KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cayenne > reflect > PropertyUtils


1 /*****************************************************************
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied. See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  ****************************************************************/

19
20 package org.apache.cayenne.reflect;
21
22 import java.beans.BeanInfo JavaDoc;
23 import java.beans.IntrospectionException JavaDoc;
24 import java.beans.Introspector JavaDoc;
25 import java.beans.PropertyDescriptor JavaDoc;
26 import java.lang.reflect.InvocationTargetException JavaDoc;
27 import java.lang.reflect.Method JavaDoc;
28 import java.util.Map JavaDoc;
29 import java.util.StringTokenizer JavaDoc;
30
31 import org.apache.cayenne.CayenneRuntimeException;
32 import org.apache.cayenne.map.Entity;
33 import org.apache.cayenne.util.Util;
34
35 /**
36  * Utility methods to quickly access object properties. This class supports simple and
37  * nested properties and also conversion of property values to match property type. No
38  * converter customization is provided yet, so only basic converters for Strings, Numbers
39  * and primitives are available.
40  *
41  * @since 1.2
42  * @author Andrus Adamchik
43  */

44 public class PropertyUtils {
45
46     /**
47      * Returns object property using JavaBean-compatible introspection with one addition -
48      * a property can be a dot-separated property name path.
49      */

50     public static Object JavaDoc getProperty(Object JavaDoc object, String JavaDoc nestedPropertyName)
51             throws CayenneRuntimeException {
52
53         if (object == null) {
54             throw new IllegalArgumentException JavaDoc("Null object.");
55         }
56
57         if (Util.isEmptyString(nestedPropertyName)) {
58             throw new IllegalArgumentException JavaDoc("Null or empty property name.");
59         }
60
61         StringTokenizer JavaDoc path = new StringTokenizer JavaDoc(
62                 nestedPropertyName,
63                 Entity.PATH_SEPARATOR);
64         int len = path.countTokens();
65
66         Object JavaDoc value = object;
67         String JavaDoc pathSegment = null;
68
69         try {
70             for (int i = 1; i <= len; i++) {
71                 pathSegment = path.nextToken();
72
73                 if (value == null) {
74                     // null value in the middle....
75
throw new CayenneRuntimeException(
76                             "Null value in the middle of the path");
77                 }
78
79                 value = getSimpleProperty(value, pathSegment);
80             }
81
82             return value;
83         }
84         catch (Exception JavaDoc e) {
85             String JavaDoc objectType = value != null ? value.getClass().getName() : "<null>";
86             throw new CayenneRuntimeException("Error reading property segment '"
87                     + pathSegment
88                     + "' in path '"
89                     + nestedPropertyName
90                     + "' for type "
91                     + objectType, e);
92         }
93     }
94
95     /**
96      * Sets object property using JavaBean-compatible introspection with one addition - a
97      * property can be a dot-separated property name path. Before setting a value attempts
98      * to convert it to a type compatible with the object property. Automatic conversion
99      * is supported between strings and basic types like numbers or primitives.
100      */

101     public static void setProperty(Object JavaDoc object, String JavaDoc nestedPropertyName, Object JavaDoc value)
102             throws CayenneRuntimeException {
103
104         if (object == null) {
105             throw new IllegalArgumentException JavaDoc("Null object.");
106         }
107
108         if (Util.isEmptyString(nestedPropertyName)) {
109             throw new IllegalArgumentException JavaDoc("Null or invalid property name.");
110         }
111
112         int dot = nestedPropertyName.lastIndexOf(Entity.PATH_SEPARATOR);
113         String JavaDoc lastSegment;
114         if (dot > 0) {
115             lastSegment = nestedPropertyName.substring(dot + 1);
116             String JavaDoc pathSegment = nestedPropertyName.substring(0, dot);
117             object = getProperty(object, pathSegment);
118
119             if (object == null) {
120                 throw new IllegalArgumentException JavaDoc(
121                         "Null object at the end of the segment '" + pathSegment + "'");
122             }
123         }
124         else {
125             lastSegment = nestedPropertyName;
126         }
127
128         try {
129             setSimpleProperty(object, lastSegment, value);
130         }
131         catch (Exception JavaDoc e) {
132             throw new CayenneRuntimeException("Error setting property segment '"
133                     + lastSegment
134                     + "' in path '"
135                     + nestedPropertyName
136                     + "'", e);
137         }
138
139     }
140
141     static Object JavaDoc getSimpleProperty(Object JavaDoc object, String JavaDoc pathSegment)
142             throws IntrospectionException JavaDoc, IllegalArgumentException JavaDoc,
143             IllegalAccessException JavaDoc, InvocationTargetException JavaDoc {
144
145         PropertyDescriptor JavaDoc descriptor = getPropertyDescriptor(
146                 object.getClass(),
147                 pathSegment);
148
149         if (descriptor != null) {
150             Method JavaDoc reader = descriptor.getReadMethod();
151
152             if (reader == null) {
153                 throw new IntrospectionException JavaDoc("Unreadable property '"
154                         + pathSegment
155                         + "'");
156             }
157
158             return reader.invoke(object, null);
159         }
160         // note that Map has two traditional bean properties - 'empty' and 'class', so
161
// do a check here only after descriptor lookup failed.
162
else if (object instanceof Map JavaDoc) {
163             return ((Map JavaDoc) object).get(pathSegment);
164         }
165         else {
166             throw new IntrospectionException JavaDoc("No property '"
167                     + pathSegment
168                     + "' found in class "
169                     + object.getClass().getName());
170         }
171     }
172
173     static void setSimpleProperty(Object JavaDoc object, String JavaDoc pathSegment, Object JavaDoc value)
174             throws IntrospectionException JavaDoc, IllegalArgumentException JavaDoc,
175             IllegalAccessException JavaDoc, InvocationTargetException JavaDoc {
176
177         PropertyDescriptor JavaDoc descriptor = getPropertyDescriptor(
178                 object.getClass(),
179                 pathSegment);
180
181         if (descriptor != null) {
182             Method JavaDoc writer = descriptor.getWriteMethod();
183
184             if (writer == null) {
185                 throw new IntrospectionException JavaDoc("Unwritable property '"
186                         + pathSegment
187                         + "'");
188             }
189
190             // do basic conversions
191

192             value = ConverterFactory.factory
193                     .getConverter(descriptor.getPropertyType())
194                     .convert(value, descriptor.getPropertyType());
195
196             // set
197
writer.invoke(object, new Object JavaDoc[] {
198                 value
199             });
200         }
201         // note that Map has two traditional bean properties - 'empty' and 'class', so
202
// do a check here only after descriptor lookup failed.
203
else if (object instanceof Map JavaDoc) {
204             ((Map JavaDoc) object).put(pathSegment, value);
205         }
206         else {
207             throw new IntrospectionException JavaDoc("No property '"
208                     + pathSegment
209                     + "' found in class "
210                     + object.getClass().getName());
211         }
212     }
213
214     static PropertyDescriptor JavaDoc getPropertyDescriptor(Class JavaDoc beanClass, String JavaDoc propertyName)
215             throws IntrospectionException JavaDoc {
216         // bean info is cached by introspector, so this should have reasonable
217
// performance...
218
BeanInfo JavaDoc info = Introspector.getBeanInfo(beanClass);
219         PropertyDescriptor JavaDoc[] descriptors = info.getPropertyDescriptors();
220
221         for (int i = 0; i < descriptors.length; i++) {
222             if (propertyName.equals(descriptors[i].getName())) {
223                 return descriptors[i];
224             }
225         }
226
227         return null;
228     }
229
230     /**
231      * "Normalizes" passed type, converting primitive types to their object counterparts.
232      */

233     static Class JavaDoc normalizeType(Class JavaDoc type) {
234         if (type.isPrimitive()) {
235
236             String JavaDoc className = type.getName();
237             if ("byte".equals(className)) {
238                 return Byte JavaDoc.class;
239             }
240             else if ("int".equals(className)) {
241                 return Integer JavaDoc.class;
242             }
243             else if ("short".equals(className)) {
244                 return Short JavaDoc.class;
245             }
246             else if ("char".equals(className)) {
247                 return Character JavaDoc.class;
248             }
249             else if ("double".equals(className)) {
250                 return Double JavaDoc.class;
251             }
252             else if ("float".equals(className)) {
253                 return Float JavaDoc.class;
254             }
255             else if ("boolean".equals(className)) {
256                 return Boolean JavaDoc.class;
257             }
258         }
259
260         return type;
261     }
262
263     /**
264      * Returns default value that should be used for nulls. For non-primitive types, null
265      * is returned. For primitive types a default such as zero or false is returned.
266      */

267     static Object JavaDoc defaultNullValueForType(Class JavaDoc type) {
268         if (type.isPrimitive()) {
269
270             String JavaDoc className = type.getName();
271             if ("byte".equals(className)) {
272                 return new Byte JavaDoc((byte) 0);
273             }
274             else if ("int".equals(className)) {
275                 return new Integer JavaDoc(0);
276             }
277             else if ("short".equals(className)) {
278                 return new Short JavaDoc((short) 0);
279             }
280             else if ("char".equals(className)) {
281                 return new Character JavaDoc((char) 0);
282             }
283             else if ("double".equals(className)) {
284                 return new Double JavaDoc(0d);
285             }
286             else if ("float".equals(className)) {
287                 return new Float JavaDoc(0f);
288             }
289             else if ("boolean".equals(className)) {
290                 return Boolean.FALSE;
291             }
292         }
293
294         return null;
295     }
296
297     private PropertyUtils() {
298         super();
299     }
300 }
301
Popular Tags