001    // Copyright 2004, 2005 The Apache Software Foundation
002    //
003    // Licensed under the Apache License, Version 2.0 (the "License");
004    // you may not use this file except in compliance with the License.
005    // You may obtain a copy of the License at
006    //
007    //     http://www.apache.org/licenses/LICENSE-2.0
008    //
009    // Unless required by applicable law or agreed to in writing, software
010    // distributed under the License is distributed on an "AS IS" BASIS,
011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    // See the License for the specific language governing permissions and
013    // limitations under the License.
014    
015    package org.apache.hivemind.util;
016    
017    import java.beans.BeanInfo;
018    import java.beans.Introspector;
019    import java.util.HashMap;
020    import java.util.List;
021    import java.util.Map;
022    
023    import org.apache.hivemind.ApplicationRuntimeException;
024    import org.apache.hivemind.HiveMind;
025    
026    /**
027     * A collection of static methods used to perform property-level access on arbitrary objects.
028     * 
029     * @author Howard Lewis Ship
030     */
031    public class PropertyUtils
032    {
033        private static final Map _classAdaptors = new HashMap();
034    
035        // Prevent instantiation
036        private PropertyUtils()
037        {
038        }
039    
040        /**
041         * Updates the property of the target object.
042         * 
043         * @param target
044         *            the object to update
045         * @param propertyName
046         *            the name of the property to be updated
047         * @param value
048         *            the value to be stored into the target object property
049         */
050        public static void write(Object target, String propertyName, Object value)
051        {
052            ClassAdaptor a = getAdaptor(target);
053    
054            a.write(target, propertyName, value);
055        }
056    
057        /**
058         * An improved version of {@link #write(Object, String, Object)} where the value starts as a
059         * string and is converted to the correct property type before being assigned.
060         * 
061         * @since 1.1
062         */
063        public static void smartWrite(Object target, String propertyName, String value)
064        {
065            ClassAdaptor a = getAdaptor(target);
066    
067            a.smartWrite(target, propertyName, value);
068        }
069    
070        /**
071         * Initializes the properties of an object from a string. The string is a comma-seperated
072         * sequence of property names and values. Property names are seperated from values be an equals
073         * sign. Spaces before and after the property names are trimmed.
074         * For boolean properties, the equals sign and value may be omitted (a value of true is
075         * assumed), or the property name may be prefixed with an exclamation point to indicated false
076         * value. Example: <code>validate,maxLength=10,displayName=User Id</code>.
077         * 
078         * @param target
079         *            the object to be configured
080         * @param initializer
081         *            the string encoding the properties and values to be configured in the target
082         *            object
083         * @since 1.1
084         */
085    
086        public static void configureProperties(Object target, String initializer)
087        {
088            ClassAdaptor a = getAdaptor(target);
089    
090            a.configureProperties(target, initializer);
091        }
092    
093        /**
094         * Returns true of the instance contains a writable property of the given type.
095         * 
096         * @param target
097         *            the object to inspect
098         * @param propertyName
099         *            the name of the property to check
100         */
101    
102        public static boolean isWritable(Object target, String propertyName)
103        {
104            return getAdaptor(target).isWritable(propertyName);
105        }
106    
107        public static boolean isReadable(Object target, String propertyName)
108        {
109            return getAdaptor(target).isReadable(propertyName);
110        }
111    
112        /**
113         * Updates the property of the target object.
114         * 
115         * @param target
116         *            the object to update
117         * @param propertyName
118         *            the name of a property toread
119         */
120    
121        public static Object read(Object target, String propertyName)
122        {
123            ClassAdaptor a = getAdaptor(target);
124    
125            return a.read(target, propertyName);
126        }
127    
128        /**
129         * Returns the type of the named property.
130         * 
131         * @param target
132         *            the object to examine
133         * @param propertyName
134         *            the name of the property to check
135         */
136        public static Class getPropertyType(Object target, String propertyName)
137        {
138            ClassAdaptor a = getAdaptor(target);
139    
140            return a.getPropertyType(target, propertyName);
141        }
142    
143        /**
144         * Returns the {@link PropertyAdaptor} for the given target object and property name.
145         * 
146         * @throws ApplicationRuntimeException
147         *             if the property does not exist.
148         */
149        public static PropertyAdaptor getPropertyAdaptor(Object target, String propertyName)
150        {
151            ClassAdaptor a = getAdaptor(target);
152    
153            return a.getPropertyAdaptor(target, propertyName);
154        }
155    
156        /**
157         * Returns an unordered List of the names of all readable properties of the target.
158         */
159        public static List getReadableProperties(Object target)
160        {
161            return getAdaptor(target).getReadableProperties();
162        }
163    
164        /**
165         * Returns an unordered List of the names of all writable properties of the target.
166         */
167        public static List getWriteableProperties(Object target)
168        {
169            return getAdaptor(target).getWriteableProperties();
170        }
171    
172        private static ClassAdaptor getAdaptor(Object target)
173        {
174            if (target == null)
175                throw new ApplicationRuntimeException(UtilMessages.nullObject());
176    
177            Class targetClass = target.getClass();
178    
179            synchronized (HiveMind.INTROSPECTOR_MUTEX)
180            {
181                ClassAdaptor result = (ClassAdaptor) _classAdaptors.get(targetClass);
182    
183                if (result == null)
184                {
185                    result = buildClassAdaptor(target, targetClass);
186                    _classAdaptors.put(targetClass, result);
187                }
188    
189                return result;
190            }
191        }
192    
193        private static ClassAdaptor buildClassAdaptor(Object target, Class targetClass)
194        {
195            try
196            {
197                BeanInfo info = Introspector.getBeanInfo(targetClass);
198    
199                return new ClassAdaptor(info.getPropertyDescriptors());
200            }
201            catch (Exception ex)
202            {
203                throw new ApplicationRuntimeException(UtilMessages.unableToIntrospect(targetClass, ex),
204                        target, null, ex);
205            }
206        }
207    
208        /**
209         * Clears all cached information. Invokes {@link Introspector#flushCaches()}.
210         */
211        public static void clearCache()
212        {
213            synchronized (HiveMind.INTROSPECTOR_MUTEX)
214            {
215                _classAdaptors.clear();
216                Introspector.flushCaches();
217            }
218        }
219    
220    }