001 /*
002 * $Id: MetaBeanProperty.java,v 1.8 2005/10/17 08:36:21 tug Exp $
003 *
004 * Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
005 *
006 * Redistribution and use of this software and associated documentation
007 * ("Software"), with or without modification, are permitted provided that the
008 * following conditions are met:
009 * 1. Redistributions of source code must retain copyright statements and
010 * notices. Redistributions must also contain a copy of this document.
011 * 2. Redistributions in binary form must reproduce the above copyright
012 * notice, this list of conditions and the following disclaimer in the
013 * documentation and/or other materials provided with the distribution.
014 * 3. The name "groovy" must not be used to endorse or promote products
015 * derived from this Software without prior written permission of The Codehaus.
016 * For written permission, please contact info@codehaus.org.
017 * 4. Products derived from this Software may not be called "groovy" nor may
018 * "groovy" appear in their names without prior written permission of The
019 * Codehaus. "groovy" is a registered trademark of The Codehaus.
020 * 5. Due credit should be given to The Codehaus - http://groovy.codehaus.org/
021 *
022 * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY
023 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
024 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
025 * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR
026 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
027 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
028 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
029 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
030 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
031 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
032 * DAMAGE.
033 *
034 */
035 package groovy.lang;
036
037
038 import java.math.BigDecimal;
039 import java.math.BigInteger;
040
041 import org.codehaus.groovy.runtime.InvokerHelper;
042 import org.codehaus.groovy.runtime.MetaClassHelper;
043
044 /**
045 * Represents a property on a bean which may have a getter and/or a setter
046 *
047 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
048 * @author Pilho Kim
049 * @version $Revision: 1.8 $
050 */
051 public class MetaBeanProperty extends MetaProperty {
052
053 private MetaMethod getter;
054 private MetaMethod setter;
055
056 public MetaBeanProperty(String name, Class type, MetaMethod getter, MetaMethod setter) {
057 super(name, type);
058 this.getter = getter;
059 this.setter = setter;
060 }
061
062 /**
063 * Get the property of the given object.
064 *
065 * @param object which to be got
066 * @return the property of the given object
067 * @throws Exception if the property could not be evaluated
068 */
069 public Object getProperty(Object object) throws Exception {
070 if (getter == null) {
071 //@todo we probably need a WriteOnlyException class
072 throw new GroovyRuntimeException("Cannot read write-only property: " + name);
073 }
074 return getter.invoke(object, MetaClassHelper.EMPTY_ARRAY);
075 }
076
077 /**
078 * Set the property on the given object to the new value.
079 *
080 * @param object on which to set the property
081 * @param newValue the new value of the property
082 * @throws Exception if the property could not be set
083 */
084 public void setProperty(Object object, Object newValue) {
085 if (setter == null) {
086 throw new GroovyRuntimeException("Cannot set read-only property: " + name);
087 }
088
089 try {
090 // we'll convert a GString to String if needed
091 if (getType() == String.class && !(newValue instanceof String)) {
092 newValue = newValue.toString();
093 }
094 else {
095 // Set property for primitive types
096 newValue = coercePrimitiveValue(newValue, getType());
097 }
098
099 setter.invoke(object, new Object[] { newValue });
100 }
101 catch (IllegalArgumentException e) { // exception for executing as scripts
102 try {
103 newValue = InvokerHelper.asType(newValue, getType());
104 setter.invoke(object, new Object[] { newValue });
105 }
106 catch (Exception ex) {
107 throw new TypeMismatchException("The property '" + toName(object.getClass()) + "." + name
108 + "' can not refer to the value '"
109 + newValue + "' (type " + toName(newValue.getClass())
110 + "), because it is of the type " + toName(getType())
111 + ". The reason is from java.lang.IllegalArgumentException.");
112 }
113 }
114 catch (ClassCastException e) { // exception for executing as compiled classes
115 try {
116 newValue = InvokerHelper.asType(newValue, getType());
117 setter.invoke(object, new Object[]{newValue});
118 }
119 catch (Exception ex) {
120 throw new TypeMismatchException("The property '" + toName(object.getClass()) + "." + name
121 + "' can not refer to the value '"
122 + newValue + "' (type " + toName(newValue.getClass())
123 + "), because it is of the type " + toName(getType())
124 + ". The reason is from java.lang.ClassCastException.");
125 }
126 }
127 catch (Exception e) {
128 throw new GroovyRuntimeException("Cannot set property: " + name +
129 " reason: " + e.getMessage(), e);
130 }
131 }
132
133 /**
134 * Coerce the object <code>src</code> to the target class.
135 */
136 protected static Object coercePrimitiveValue(Object src, Class target) {
137 Object newValue = src;
138
139 if (newValue instanceof BigDecimal) {
140 if (target == java.math.BigInteger.class) {
141 newValue = ((BigDecimal) newValue).unscaledValue();
142 }
143 else if (target == Double.class) {
144 newValue = new Double(((BigDecimal) newValue).doubleValue());
145 }
146 else if (target == Float.class) {
147 newValue = new Float(((BigDecimal) newValue).floatValue());
148 }
149 else if (target == Long.class) {
150 newValue = new Long(((BigDecimal) newValue).longValue());
151 }
152 else if (target == Integer.class) {
153 newValue = new Integer(((BigDecimal) newValue).intValue());
154 }
155 else if (target == Short.class) {
156 newValue = new Short((short) ((BigDecimal) newValue).intValue());
157 }
158 else if (target == Byte.class) {
159 newValue = new Byte((byte) ((BigDecimal) newValue).intValue());
160 }
161 else if (target == Character.class) {
162 newValue = new Character((char) ((BigDecimal) newValue).intValue());
163 }
164 }
165 else if (newValue instanceof BigInteger) {
166 if (target == BigDecimal.class) {
167 newValue = new BigDecimal((BigInteger) newValue);
168 }
169 else if (target == Double.class) {
170 newValue = new Double(((java.math.BigInteger) newValue).doubleValue());
171 }
172 else if (target == Float.class) {
173 newValue = new Float(((java.math.BigInteger) newValue).floatValue());
174 }
175 else if (target == Long.class) {
176 newValue = new Long(((java.math.BigInteger) newValue).longValue());
177 }
178 else if (target == Integer.class) {
179 newValue = new Integer(((java.math.BigInteger) newValue).intValue());
180 }
181 else if (target == Short.class) {
182 newValue = new Short((short) ((java.math.BigInteger) newValue).intValue());
183 }
184 else if (target == Byte.class) {
185 newValue = new Byte((byte) ((java.math.BigInteger) newValue).intValue());
186 }
187 else if (target == Character.class) {
188 newValue = new Character((char) ((java.math.BigInteger) newValue).intValue());
189 }
190 }
191 else if (newValue instanceof java.lang.Long) {
192 if (target == Integer.class) {
193 newValue = new Integer(((Long) newValue).intValue());
194 }
195 else if (target == Short.class) {
196 newValue = new Short(((Long) newValue).shortValue());
197 }
198 else if (target == Byte.class) {
199 newValue = new Byte(((Long) newValue).byteValue());
200 }
201 else if (target == Character.class) {
202 newValue = new Character((char) ((Long) newValue).intValue());
203 }
204 else if (target == BigInteger.class) {
205 newValue = new BigInteger("" + newValue);
206 }
207 else if (target == BigDecimal.class) {
208 newValue = new BigDecimal("" + newValue);
209 }
210 }
211 else if (newValue instanceof java.lang.Integer) {
212 if (target == Double.class) {
213 newValue = new Double(((Integer) newValue).intValue());
214 }
215 else if (target == Float.class) {
216 newValue = new Float(((Integer) newValue).floatValue());
217 }
218 else if (target == Long.class) {
219 newValue = new Long(((Integer) newValue).intValue());
220 }
221 else if (target == Short.class) {
222 newValue = new Short(((Integer) newValue).shortValue());
223 }
224 else if (target == Byte.class) {
225 newValue = new Byte(((Integer) newValue).byteValue());
226 }
227 else if (target == Character.class) {
228 newValue = new Character((char) ((Integer) newValue).intValue());
229 }
230 else if (target == BigDecimal.class) {
231 newValue = new BigDecimal("" + newValue);
232 }
233 else if (target == BigInteger.class) {
234 newValue = new BigInteger("" + newValue);
235 }
236 }
237 else if (newValue instanceof java.lang.Short) {
238 if (target == Double.class) {
239 newValue = new Double(((Short) newValue).shortValue());
240 }
241 else if (target == Float.class) {
242 newValue = new Float(((Short) newValue).shortValue());
243 }
244 else if (target == Long.class) {
245 newValue = new Long(((Short) newValue).shortValue());
246 }
247 else if (target == Integer.class) {
248 newValue = new Integer(((Short) newValue).shortValue());
249 }
250 else if (target == Byte.class) {
251 newValue = new Byte((byte) ((Short) newValue).shortValue());
252 }
253 else if (target == Character.class) {
254 newValue = new Character((char) ((Short) newValue).shortValue());
255 }
256 else if (target == BigDecimal.class) {
257 newValue = new BigDecimal("" + newValue);
258 }
259 else if (target == BigInteger.class) {
260 newValue = new BigInteger("" + newValue);
261 }
262 }
263 else if (newValue instanceof java.lang.Byte) {
264 if (target == Double.class) {
265 newValue = new Double(((Byte) newValue).byteValue());
266 }
267 else if (target == Float.class) {
268 newValue = new Float(((Byte) newValue).byteValue());
269 }
270 else if (target == Long.class) {
271 newValue = new Long(((Byte) newValue).byteValue());
272 }
273 else if (target == Integer.class) {
274 newValue = new Integer(((Byte) newValue).byteValue());
275 }
276 else if (target == Short.class) {
277 newValue = new Short(((Byte) newValue).byteValue());
278 }
279 else if (target == Character.class) {
280 newValue = new Character((char) ((Byte) newValue).byteValue());
281 }
282 else if (target == BigDecimal.class) {
283 newValue = new BigDecimal("" + newValue);
284 }
285 else if (target == BigInteger.class) {
286 newValue = new BigInteger("" + newValue);
287 }
288 }
289 else if (newValue instanceof java.lang.Character) {
290 if (target == Double.class) {
291 newValue = new Double(((int) ((Character) newValue).charValue() & 0xFFFF));
292 }
293 else if (target == Long.class) {
294 newValue = new Long((long) ((Character) newValue).charValue());
295 }
296 else if (target == Integer.class) {
297 newValue = new Integer((int) ((Character) newValue).charValue());
298 }
299 else if (target == Short.class) {
300 newValue = new Short((short) ((Character) newValue).charValue());
301 }
302 else if (target == BigDecimal.class) {
303 newValue = new BigDecimal("" + ((int) ((Character) newValue).charValue() & 0xFFFF));
304 }
305 else if (target == BigInteger.class) {
306 newValue = new BigInteger("" + ((int) ((Character) newValue).charValue() & 0xFFFF));
307 }
308 else if (target == String.class) {
309 newValue = new String("" + newValue);
310 }
311 }
312 return newValue;
313 }
314
315 private String toName(Class c) {
316 String s = c.toString();
317 if (s.startsWith("class ") && s.length() > 6) {
318 return s.substring(6);
319 }
320 else {
321 return s;
322 }
323 }
324
325
326 /**
327 * Get the getter method.
328 */
329 public MetaMethod getGetter() {
330 return getter;
331 }
332
333 /**
334 * Get the setter method.
335 */
336 public MetaMethod getSetter() {
337 return setter;
338 }
339
340 /**
341 * This is for MetaClass to patch up the object later when looking for get*() methods.
342 */
343 void setGetter(MetaMethod getter) {
344 this.getter = getter;
345 }
346
347 /**
348 * This is for MetaClass to patch up the object later when looking for set*() methods.
349 */
350 void setSetter(MetaMethod setter) {
351 this.setter = setter;
352 }
353 }