001    /*
002     * $Id: MetaBeanProperty.java,v 1.6 2005/03/31 06:15:11 jstrachan 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 org.codehaus.groovy.runtime.InvokerHelper;
039    
040    /**
041     * Represents a property on a bean which may have a getter and/or a setter
042     *
043     * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
044     * @author Kim Pilho
045     * @version $Revision: 1.6 $
046     */
047    public class MetaBeanProperty extends MetaProperty {
048    
049        private MetaMethod getter;
050        private MetaMethod setter;
051    
052        public MetaBeanProperty(String name, Class type, MetaMethod getter, MetaMethod setter) {
053            super(name, type);
054            this.getter = getter;
055            this.setter = setter;
056        }
057    
058        /**
059         * @return the property of the given object
060         * @throws Exception if the property could not be evaluated
061         */
062        public Object getProperty(Object object) throws Exception {
063            if (getter == null) {
064                //@todo we probably need a WriteOnlyException class
065                throw new GroovyRuntimeException("Cannot read write-only property: " + name);
066            }
067            return getter.invoke(object, MetaClass.EMPTY_ARRAY);
068        }
069    
070        /**
071         * Sets the property on the given object to the new value
072         *
073         * @param object   on which to set the property
074         * @param newValue the new value of the property
075         * @throws Exception if the property could not be set
076         */
077        public void setProperty(Object object, Object newValue) {
078            if (setter == null) {
079                throw new GroovyRuntimeException("Cannot set read-only property: " + name);
080            }
081    
082            try {
083                // we'll convert a GString to String if needed
084                if (getType() == String.class && !(newValue instanceof String)) {
085                    newValue = newValue.toString();
086                }
087    
088                // Set property for primitive types
089                if (newValue instanceof java.math.BigDecimal) {
090                    if (getType() == Double.class) {
091                        newValue = new Double(((java.math.BigDecimal) newValue).doubleValue());
092                    }
093                    else if (getType() == Float.class) {
094                        newValue = new Float(((java.math.BigDecimal) newValue).floatValue());
095                    }
096                    else if (getType() == Long.class) {
097                        newValue = new Long(((java.math.BigDecimal) newValue).longValue());
098                    }
099                    else if (getType() == Integer.class) {
100                        newValue = new Integer(((java.math.BigDecimal) newValue).intValue());
101                    }
102                    else if (getType() == Short.class) {
103                        newValue = new Short((short) ((java.math.BigDecimal) newValue).intValue());
104                    }
105                    else if (getType() == Byte.class) {
106                        newValue = new Byte((byte) ((java.math.BigDecimal) newValue).intValue());
107                    }
108                    else if (getType() == Character.class) {
109                        newValue = new Character((char) ((java.math.BigDecimal) newValue).intValue());
110                    }
111                }
112                else if (newValue instanceof java.math.BigInteger) {
113                    if (getType() == Long.class) {
114                        newValue = new Long(((java.math.BigInteger) newValue).longValue());
115                    }
116                    else if (getType() == Integer.class) {
117                        newValue = new Integer(((java.math.BigInteger) newValue).intValue());
118                    }
119                    else if (getType() == Short.class) {
120                        newValue = new Short((short) ((java.math.BigInteger) newValue).intValue());
121                    }
122                    else if (getType() == Byte.class) {
123                        newValue = new Byte((byte) ((java.math.BigInteger) newValue).intValue());
124                    }
125                    else if (getType() == Character.class) {
126                        newValue = new Character((char) ((java.math.BigInteger) newValue).intValue());
127                    }
128                }
129                else if (newValue instanceof java.lang.Long) {
130                    if (getType() == Integer.class) {
131                        newValue = new Integer(((Long) newValue).intValue());
132                    }
133                    else if (getType() == Short.class) {
134                        newValue = new Short(((Long) newValue).shortValue());
135                    }
136                    else if (getType() == Byte.class) {
137                        newValue = new Byte(((Long) newValue).byteValue());
138                    }
139                    else if (getType() == Character.class) {
140                        newValue = new Character((char) ((Long) newValue).intValue());
141                    }
142                }
143                else if (newValue instanceof java.lang.Integer) {
144                    if (getType() == Short.class) {
145                        newValue = new Short(((Integer) newValue).shortValue());
146                    }
147                    else if (getType() == Byte.class) {
148                        newValue = new Byte(((Integer) newValue).byteValue());
149                    }
150                    else if (getType() == Character.class) {
151                        newValue = new Character((char) ((Integer) newValue).intValue());
152                    }
153                }
154    
155                setter.invoke(object, new Object[]{newValue});
156            }
157            catch (IllegalArgumentException e) {    // exception for executing as scripts
158                try {
159                    newValue = InvokerHelper.asType(newValue, getType());
160                    setter.invoke(object, new Object[]{newValue});
161                }
162                catch (Exception ex) {
163                    throw new TypeMismatchException("The property '" + toName(object.getClass()) + "." + name
164                            + "' can not refer to the value '"
165                            + newValue + "' (type " + toName(newValue.getClass())
166                            + "), because it is of the type " + toName(getType())
167                            + ". The reason is from java.lang.IllegalArgumentException.");
168                }
169            }
170            catch (ClassCastException e) {    // exception for executing as compiled classes
171                try {
172                    newValue = InvokerHelper.asType(newValue, getType());
173                    setter.invoke(object, new Object[]{newValue});
174                }
175                catch (Exception ex) {
176                    throw new TypeMismatchException("The property '" + toName(object.getClass()) + "." + name
177                            + "' can not refer to the value '"
178                            + newValue + "' (type " + toName(newValue.getClass())
179                            + "), because it is of the type " + toName(getType())
180                            + ". The reason is from java.lang.ClassCastException.");
181                }
182            }
183            catch (Exception e) {
184                throw new GroovyRuntimeException("Cannot set property: " + name +
185                        " reason: " + e.getMessage(), e);
186            }
187        }
188    
189        private String toName(Class c) {
190            String s = c.toString();
191            if (s.startsWith("class ") && s.length() > 6) {
192                return s.substring(6);
193            }
194            else {
195                return s;
196            }
197        }
198    
199        public MetaMethod getGetter() {
200            return getter;
201        }
202    
203        public MetaMethod getSetter() {
204            return setter;
205        }
206    
207        /**
208         * this is for MetaClass to patch up the object later when looking for get*() methods
209         */
210        void setGetter(MetaMethod getter) {
211            this.getter = getter;
212        }
213    
214        /**
215         * this is for MetaClass to patch up the object later when looking for set*() methods
216         */
217        void setSetter(MetaMethod setter) {
218            this.setter = setter;
219        }
220    }