001 /* 002 * $Id: MetaBeanProperty.java,v 1.7 2005/08/04 21:55:45 phk 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 043 /** 044 * Represents a property on a bean which may have a getter and/or a setter 045 * 046 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a> 047 * @author Pilho Kim 048 * @version $Revision: 1.7 $ 049 */ 050 public class MetaBeanProperty extends MetaProperty { 051 052 private MetaMethod getter; 053 private MetaMethod setter; 054 055 public MetaBeanProperty(String name, Class type, MetaMethod getter, MetaMethod setter) { 056 super(name, type); 057 this.getter = getter; 058 this.setter = setter; 059 } 060 061 /** 062 * Get the property of the given object. 063 * 064 * @param object which to be got 065 * @return the property of the given object 066 * @throws Exception if the property could not be evaluated 067 */ 068 public Object getProperty(Object object) throws Exception { 069 if (getter == null) { 070 //@todo we probably need a WriteOnlyException class 071 throw new GroovyRuntimeException("Cannot read write-only property: " + name); 072 } 073 return getter.invoke(object, MetaClass.EMPTY_ARRAY); 074 } 075 076 /** 077 * Set the property on the given object to the new value. 078 * 079 * @param object on which to set the property 080 * @param newValue the new value of the property 081 * @throws Exception if the property could not be set 082 */ 083 public void setProperty(Object object, Object newValue) { 084 if (setter == null) { 085 throw new GroovyRuntimeException("Cannot set read-only property: " + name); 086 } 087 088 try { 089 // we'll convert a GString to String if needed 090 if (getType() == String.class && !(newValue instanceof String)) { 091 newValue = newValue.toString(); 092 } 093 else { 094 // Set property for primitive types 095 newValue = coercePrimitiveValue(newValue, getType()); 096 } 097 098 setter.invoke(object, new Object[] { newValue }); 099 } 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 }