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 }