View Javadoc

1   /*
2    * This file is part of Domingo
3    * an Open Source Java-API to Lotus Notes/Domino
4    * hosted at http://domingo.sourceforge.net
5    *
6    * Copyright (c) 2003-2007 Beck et al. projects GmbH Munich, Germany (http://www.bea.de)
7    *
8    * This library is free software; you can redistribute it and/or
9    * modify it under the terms of the GNU Lesser General Public
10   * License as published by the Free Software Foundation; either
11   * version 2.1 of the License, or (at your option) any later version.
12   *
13   * This library is distributed in the hope that it will be useful,
14   * but WITHOUT ANY WARRANTY; without even the implied warranty of
15   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   * Lesser General Public License for more details.
17   *
18   * You should have received a copy of the GNU Lesser General Public
19   * License along with this library; if not, write to the Free Software
20   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21   */
22  
23  package de.bea.domingo.map;
24  
25  import java.lang.reflect.Method;
26  import java.util.ArrayList;
27  import java.util.Calendar;
28  import java.util.Collection;
29  import java.util.List;
30  
31  import de.bea.domingo.DDocument;
32  
33  /***
34   * Class defining and performing a direct mapping of a single attribute between
35   * a document and a business object.
36   *
37   * The following types are supported:
38   * <ul>
39   * <li>java.lang.String</li>
40   * <li>java.lang.Integer and <code>int</code></li>
41   * <li>java.lang.Double and <code>double</code></li>
42   * <li>java.util.Calendar</li>
43   * </ul>
44   *
45   * <p>Other types must be mapped e.g. with a {@link CustomMapper}.</p>
46   *
47   * @author <a href="mailto:kriede@users.sourceforge.net">Kurt Riede</a>
48   */
49  public final class DirectMapper extends BaseDMapper {
50  
51      /*** Notes item name in the Notes document. */
52      private String itemName;
53  
54      /*** Name of getter method of instance class. */
55      private String getName;
56  
57      /*** Name of setter method of instance class. */
58      private String setName;
59  
60      /***
61       * Value class, can be {@link java.lang.Number}, {@link java.lang.String}
62       * or {@link java.lang.Calendar}.
63       */
64      private Class clazz;
65  
66      /***
67       * Constructor. Creates a direct mapper where the Notes item name is equal
68       * to the <tt>itemName</tt> attribute and the names of the get/set methods
69       * in the business class are equal to <tt>"get" + itemName</tt> and
70       * <tt>"set" + itemName</tt>.
71       *
72       * @param itemName name of Notes item
73       * @param clazz the value type
74       * @throws MethodNotFoundException if the getter or setter method was not found for the attribute name
75       */
76      public DirectMapper(final String itemName, final Class clazz) throws MethodNotFoundException {
77          this(itemName, itemName, clazz);
78      }
79  
80      /***
81       * Constructor. Creates a direct mapper where the Notes item name is equal
82       * to the <tt>itemName</tt> attribute and the names of the get/set methods
83       * in the business class are equal to <tt>"get" + attributeName</tt> and
84       * <tt>"set" + attributeName</tt>. Also the prefix <tt>"is</tt>" is allowed
85       * in case of methods returning boolean values.
86       *
87       * @param itemName name of Notes item
88       * @param attributeName name of Java attribute
89       * @param clazz the value type
90       * @throws MethodNotFoundException if the getter or setter method was not found for the attribute name
91       */
92      public DirectMapper(final String itemName, final String attributeName, final Class clazz) throws MethodNotFoundException {
93          this(itemName, getGetterName(clazz, attributeName), getSetterName(clazz, attributeName), clazz);
94      }
95  
96      /***
97       * Returns the name of the setter method of a class for a given attribute
98       * name or throws an exception if no getter method exists.
99       *
100      * @param clazz the class to check
101      * @param attributeName the attribute name
102      * @return the name of the getter method
103      * @throws MethodNotFoundException if the getter or setter method was not
104      *             found for the attribute name
105      */
106     private static String getSetterName(final Class clazz, final String attributeName)
107             throws MethodNotFoundException {
108         return "set" + attributeName;
109     }
110 
111     /***
112      * Returns the name of the getter method of a class for a given attribute
113      * name or throws an exception if no getter method exists.
114      *
115      * @param clazz the class to check
116      * @param attributeName the attribute name
117      * @return the name of the getter method
118      * @throws MethodNotFoundException if the getter or setter method was not
119      *             found for the attribute name
120      */
121     private static String getGetterName(final Class clazz, final String attributeName) throws MethodNotFoundException {
122         if (clazz.equals(Boolean.TYPE)) {
123             return "is" + attributeName;
124         } else {
125             return "get" + attributeName;
126         }
127     }
128 
129     /***
130      * Constructor. Creates a direct mapper where the Notes item name is equal
131      * to the <tt>itemName</tt> attribute and the names of the get/set methods
132      * in the business class are equal to <tt>getName</tt> and
133      * <tt>setName</tt>.
134      *
135      * @param itemName name of Notes item
136      * @param getName name of get-method
137      * @param setName name of set-method
138      * @param clazz the value type
139      */
140     public DirectMapper(final String itemName, final String getName, final String setName, final Class clazz) {
141         this.itemName = itemName;
142         this.getName = getName;
143         this.setName = setName;
144         this.clazz = clazz;
145     }
146 
147     /***
148      * Performs the direct mapping from a document to a business object.
149      *
150      * @param document the Notes document
151      * @param object the business object
152      * @throws MappingException if an error occurred during mapping
153      */
154     public void map(final DDocument document, final Object object) throws MappingException {
155         List itemValueList = document.getItemValue(itemName);
156         if (itemValueList != null && itemValueList.size() > 0) {
157             Object value = itemValueList;
158             if (!Collection.class.isAssignableFrom(clazz)) {
159                 if (itemValueList.size() > 0) {
160                     value = itemValueList.get(0);
161                 } else {
162                     return;
163                 }
164             }
165             if (Boolean.class.equals(clazz) || Boolean.TYPE.equals(clazz)) {
166                 value = getBoolean(value.toString());
167             }
168             Class[] parameterTypes = { clazz };
169             if (setName != null) {
170                 String className = object.getClass().getName();
171                 try {
172                     Method method = new MethodFinder(object.getClass()).findMethod(setName, parameterTypes);
173                     Object[] args = { value };
174                     method.invoke(object, args);
175                 } catch (Exception e) {
176                     throw new MappingException("Cannot map item " + itemName + " to " + className + "." + setName, e);
177                 }
178             }
179         }
180     }
181 
182     /***
183      * Performs the direct mapping from a business object to a document.
184      *
185      * Allowed objects are of type <code>String</code>,
186      * <code>Calendar</code>, <code>Integer</code>, <code>Double</code>
187      * or <code>List</code>, where if the object is a <code>List</code>, all
188      * values in the list must be of one of the other types.
189      *
190      * @param object the business object
191      * @param document the Notes document
192      * @throws MappingException if an error occurred during mapping
193      */
194     public void map(final Object object, final DDocument document) throws MappingException {
195         if (getName != null) {
196             final Object value = getValue(object, getName);
197             if (value == null) {
198                 document.replaceItemValue(itemName, "");
199             } else if (String.class.equals(clazz)) {
200                 document.replaceItemValue(itemName, value.toString());
201             } else if (Boolean.class.equals(clazz) || Boolean.TYPE.equals(clazz)) {
202                 document.replaceItemValue(itemName, getBooleanValue(value));
203             } else if (value instanceof Calendar && Calendar.class.isAssignableFrom(clazz)) {
204                 document.replaceItemValue(itemName, (Calendar) value);
205             } else if (value instanceof Double && Number.class.isAssignableFrom(clazz) || Double.TYPE.equals(clazz)) {
206                 document.replaceItemValue(itemName, (Double) value);
207             } else if (value instanceof Integer && Number.class.isAssignableFrom(clazz) || Integer.TYPE.equals(clazz)) {
208                 document.replaceItemValue(itemName, (Integer) value);
209             } else if (value instanceof List) {
210                 // TODO test behavior with mixed type in list
211                 document.replaceItemValue(itemName, (List) value);
212             } else if (value instanceof Collection) {
213                 // TODO test behavior with mixed type in collection
214                 List list = new ArrayList();
215                 list.addAll((Collection) value);
216                 document.replaceItemValue(itemName, list);
217             } else {
218                 throw new MappingException("Invalid datatype for method " + getName + ": " + value.getClass().getName()
219                         + " is not assignable of class " + clazz.getName());
220             }
221         }
222     }
223 
224     /***
225      * Converts a value to a string representing a boolean value as stored in
226      * Lotus Notes.
227      *
228      * @param value the value
229      * @return the string representing <code>true</code> or <code>false</code>
230      */
231     private String getBooleanValue(final Object value) {
232         if (value instanceof Boolean) {
233             return ((Boolean) value).equals(Boolean.TRUE) ? "1" : "0";
234         } else if (value instanceof Number) {
235             return ((Number) value).doubleValue() != 0 ? "1" : "0";
236         }
237         return value != null ? "1" : "0";
238     }
239 
240     /***
241      * Converts a string to a boolean value; only the string <code>"1"</code>
242      * results to <code>true</code>.
243      *
244      * @param value any string
245      * @return <code>true</code> if the string is <code>"1"</code>, else <code>false</code>
246      */
247     private Boolean getBoolean(final String value) {
248         return Boolean.valueOf(value != null && value.equals("1"));
249     }
250 }