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
015package org.apache.hivemind.util;
016
017import java.beans.BeanInfo;
018import java.beans.Introspector;
019import java.util.HashMap;
020import java.util.List;
021import java.util.Map;
022
023import org.apache.hivemind.ApplicationRuntimeException;
024import 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 */
031public 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}