View Javadoc

1   /*
2    * $Id: MetaBeanProperty.java,v 1.7 2005/08/04 21:55:45 phk Exp $
3    *
4    * Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
5    *
6    * Redistribution and use of this software and associated documentation
7    * ("Software"), with or without modification, are permitted provided that the
8    * following conditions are met:
9    *  1. Redistributions of source code must retain copyright statements and
10   * notices. Redistributions must also contain a copy of this document.
11   *  2. Redistributions in binary form must reproduce the above copyright
12   * notice, this list of conditions and the following disclaimer in the
13   * documentation and/or other materials provided with the distribution.
14   *  3. The name "groovy" must not be used to endorse or promote products
15   * derived from this Software without prior written permission of The Codehaus.
16   * For written permission, please contact info@codehaus.org.
17   *  4. Products derived from this Software may not be called "groovy" nor may
18   * "groovy" appear in their names without prior written permission of The
19   * Codehaus. "groovy" is a registered trademark of The Codehaus.
20   *  5. Due credit should be given to The Codehaus - http://groovy.codehaus.org/
21   *
22   * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY
23   * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25   * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR
26   * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
28   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
32   * DAMAGE.
33   *
34   */
35  package groovy.lang;
36  
37  
38  import java.math.BigDecimal;
39  import java.math.BigInteger;
40  
41  import org.codehaus.groovy.runtime.InvokerHelper;
42  
43  /***
44   * Represents a property on a bean which may have a getter and/or a setter
45   *
46   * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
47   * @author Pilho Kim
48   * @version $Revision: 1.7 $
49   */
50  public class MetaBeanProperty extends MetaProperty {
51  
52      private MetaMethod getter;
53      private MetaMethod setter;
54  
55      public MetaBeanProperty(String name, Class type, MetaMethod getter, MetaMethod setter) {
56          super(name, type);
57          this.getter = getter;
58          this.setter = setter;
59      }
60  
61      /***
62       * Get the property of the given object.
63       *
64       * @param object which to be got
65       * @return the property of the given object
66       * @throws Exception if the property could not be evaluated
67       */
68      public Object getProperty(Object object) throws Exception {
69          if (getter == null) {
70              //@todo we probably need a WriteOnlyException class
71              throw new GroovyRuntimeException("Cannot read write-only property: " + name);
72          }
73          return getter.invoke(object, MetaClass.EMPTY_ARRAY);
74      }
75  
76      /***
77       * Set the property on the given object to the new value.
78       *
79       * @param object   on which to set the property
80       * @param newValue the new value of the property
81       * @throws Exception if the property could not be set
82       */
83      public void setProperty(Object object, Object newValue) {
84          if (setter == null) {
85              throw new GroovyRuntimeException("Cannot set read-only property: " + name);
86          }
87  
88          try {
89              // we'll convert a GString to String if needed
90              if (getType() == String.class && !(newValue instanceof String)) {
91                  newValue = newValue.toString();
92              }
93              else {
94                  // Set property for primitive types
95                  newValue = coercePrimitiveValue(newValue, getType());
96              }
97  
98              setter.invoke(object, new Object[] { newValue });
99          }
100         catch (IllegalArgumentException e) {    // exception for executing as scripts
101             try {
102                 newValue = InvokerHelper.asType(newValue, getType());
103                 setter.invoke(object, new Object[] { newValue });
104             }
105             catch (Exception ex) {
106                 throw new TypeMismatchException("The property '" + toName(object.getClass()) + "." + name
107                         + "' can not refer to the value '"
108                         + newValue + "' (type " + toName(newValue.getClass())
109                         + "), because it is of the type " + toName(getType())
110                         + ". The reason is from java.lang.IllegalArgumentException.");
111             }
112         }
113         catch (ClassCastException e) {    // exception for executing as compiled classes
114             try {
115                 newValue = InvokerHelper.asType(newValue, getType());
116                 setter.invoke(object, new Object[]{newValue});
117             }
118             catch (Exception ex) {
119                 throw new TypeMismatchException("The property '" + toName(object.getClass()) + "." + name
120                         + "' can not refer to the value '"
121                         + newValue + "' (type " + toName(newValue.getClass())
122                         + "), because it is of the type " + toName(getType())
123                         + ". The reason is from java.lang.ClassCastException.");
124             }
125         }
126         catch (Exception e) {
127             throw new GroovyRuntimeException("Cannot set property: " + name +
128                     " reason: " + e.getMessage(), e);
129         }
130     }
131 
132     /***
133      * Coerce the object <code>src</code> to the target class.
134      */
135     protected static Object coercePrimitiveValue(Object src, Class target) {
136         Object newValue = src;
137 
138         if (newValue instanceof BigDecimal) {
139             if (target == java.math.BigInteger.class) {
140                 newValue = ((BigDecimal) newValue).unscaledValue();
141             }
142             else if (target == Double.class) {
143                 newValue = new Double(((BigDecimal) newValue).doubleValue());
144             }
145             else if (target == Float.class) {
146                 newValue = new Float(((BigDecimal) newValue).floatValue());
147             }
148             else if (target == Long.class) {
149                 newValue = new Long(((BigDecimal) newValue).longValue());
150             }
151             else if (target == Integer.class) {
152                 newValue = new Integer(((BigDecimal) newValue).intValue());
153             }
154             else if (target == Short.class) {
155                 newValue = new Short((short) ((BigDecimal) newValue).intValue());
156             }
157             else if (target == Byte.class) {
158                 newValue = new Byte((byte) ((BigDecimal) newValue).intValue());
159             }
160             else if (target == Character.class) {
161                 newValue = new Character((char) ((BigDecimal) newValue).intValue());
162             }
163         }
164         else if (newValue instanceof BigInteger) {
165             if (target == BigDecimal.class) {
166                 newValue = new BigDecimal((BigInteger) newValue);
167             }
168             else if (target == Double.class) {
169                 newValue = new Double(((java.math.BigInteger) newValue).doubleValue());
170             }
171             else if (target == Float.class) {
172                 newValue = new Float(((java.math.BigInteger) newValue).floatValue());
173             }
174             else if (target == Long.class) {
175                 newValue = new Long(((java.math.BigInteger) newValue).longValue());
176             }
177             else if (target == Integer.class) {
178                 newValue = new Integer(((java.math.BigInteger) newValue).intValue());
179             }
180             else if (target == Short.class) {
181                 newValue = new Short((short) ((java.math.BigInteger) newValue).intValue());
182             }
183             else if (target == Byte.class) {
184                 newValue = new Byte((byte) ((java.math.BigInteger) newValue).intValue());
185             }
186             else if (target == Character.class) {
187                 newValue = new Character((char) ((java.math.BigInteger) newValue).intValue());
188             }
189         }
190         else if (newValue instanceof java.lang.Long) {
191             if (target == Integer.class) {
192                 newValue = new Integer(((Long) newValue).intValue());
193             }
194             else if (target == Short.class) {
195                 newValue = new Short(((Long) newValue).shortValue());
196             }
197             else if (target == Byte.class) {
198                 newValue = new Byte(((Long) newValue).byteValue());
199             }
200             else if (target == Character.class) {
201                 newValue = new Character((char) ((Long) newValue).intValue());
202             }
203             else if (target == BigInteger.class) {
204                 newValue = new BigInteger("" + newValue);
205             }
206             else if (target == BigDecimal.class) {
207                 newValue = new BigDecimal("" + newValue);
208             }
209         }
210         else if (newValue instanceof java.lang.Integer) {
211             if (target == Double.class) {
212                 newValue = new Double(((Integer) newValue).intValue());
213             }
214             else if (target == Float.class) {
215                 newValue = new Float(((Integer) newValue).floatValue());
216             }
217             else if (target == Long.class) {
218                 newValue = new Long(((Integer) newValue).intValue());
219             }
220             else if (target == Short.class) {
221                 newValue = new Short(((Integer) newValue).shortValue());
222             }
223             else if (target == Byte.class) {
224                 newValue = new Byte(((Integer) newValue).byteValue());
225             }
226             else if (target == Character.class) {
227                 newValue = new Character((char) ((Integer) newValue).intValue());
228             }
229             else if (target == BigDecimal.class) {
230                 newValue = new BigDecimal("" + newValue);
231             }
232             else if (target == BigInteger.class) {
233                 newValue = new BigInteger("" + newValue);
234             }
235         }
236         else if (newValue instanceof java.lang.Short) {
237             if (target == Double.class) {
238                 newValue = new Double(((Short) newValue).shortValue());
239             }
240             else if (target == Float.class) {
241                 newValue = new Float(((Short) newValue).shortValue());
242             }
243             else if (target == Long.class) {
244                 newValue = new Long(((Short) newValue).shortValue());
245             }
246             else if (target == Integer.class) {
247                 newValue = new Integer(((Short) newValue).shortValue());
248             }
249             else if (target == Byte.class) {
250                 newValue = new Byte((byte) ((Short) newValue).shortValue());
251             }
252             else if (target == Character.class) {
253                 newValue = new Character((char) ((Short) newValue).shortValue());
254             }
255             else if (target == BigDecimal.class) {
256                 newValue = new BigDecimal("" + newValue);
257             }
258             else if (target == BigInteger.class) {
259                 newValue = new BigInteger("" + newValue);
260             }
261         }
262         else if (newValue instanceof java.lang.Byte) {
263             if (target == Double.class) {
264                 newValue = new Double(((Byte) newValue).byteValue());
265             }
266             else if (target == Float.class) {
267                 newValue = new Float(((Byte) newValue).byteValue());
268             }
269             else if (target == Long.class) {
270                 newValue = new Long(((Byte) newValue).byteValue());
271             }
272             else if (target == Integer.class) {
273                 newValue = new Integer(((Byte) newValue).byteValue());
274             }
275             else if (target == Short.class) {
276                 newValue = new Short(((Byte) newValue).byteValue());
277             }
278             else if (target == Character.class) {
279                 newValue = new Character((char) ((Byte) newValue).byteValue());
280             }
281             else if (target == BigDecimal.class) {
282                 newValue = new BigDecimal("" + newValue);
283             }
284             else if (target == BigInteger.class) {
285                 newValue = new BigInteger("" + newValue);
286             }
287         }
288         else if (newValue instanceof java.lang.Character) {
289             if (target == Double.class) {
290                 newValue = new Double(((int) ((Character) newValue).charValue() & 0xFFFF));
291             }
292             else if (target == Long.class) {
293                 newValue = new Long((long) ((Character) newValue).charValue());
294             }
295             else if (target == Integer.class) {
296                 newValue = new Integer((int) ((Character) newValue).charValue());
297             }
298             else if (target == Short.class) {
299                 newValue = new Short((short) ((Character) newValue).charValue());
300             }
301             else if (target == BigDecimal.class) {
302                 newValue = new BigDecimal("" + ((int) ((Character) newValue).charValue() & 0xFFFF));
303             }
304             else if (target == BigInteger.class) {
305                 newValue = new BigInteger("" + ((int) ((Character) newValue).charValue() & 0xFFFF));
306             }
307             else if (target == String.class) {
308                 newValue = new String("" + newValue);
309             }
310         }
311         return newValue;
312     }
313 
314     private String toName(Class c) {
315         String s = c.toString();
316         if (s.startsWith("class ") && s.length() > 6) {
317             return s.substring(6);
318         }
319         else {
320             return s;
321         }
322     }
323 
324 
325     /***
326      * Get the getter method.
327      */
328     public MetaMethod getGetter() {
329         return getter;
330     }
331 
332     /***
333      * Get the setter method.
334      */
335     public MetaMethod getSetter() {
336         return setter;
337     }
338 
339     /***
340      * This is for MetaClass to patch up the object later when looking for get*() methods.
341      */
342     void setGetter(MetaMethod getter) {
343         this.getter = getter;
344     }
345 
346     /***
347      * This is for MetaClass to patch up the object later when looking for set*() methods.
348      */
349     void setSetter(MetaMethod setter) {
350         this.setter = setter;
351     }
352 }