KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > cojen > util > BeanIntrospector


1 /*
2  * Copyright 2004 Brian S O'Neill
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.cojen.util;
18
19 import java.lang.ref.SoftReference JavaDoc;
20 import java.lang.reflect.Method JavaDoc;
21 import java.lang.reflect.Modifier JavaDoc;
22 import java.util.Collections JavaDoc;
23 import java.util.HashMap JavaDoc;
24 import java.util.Map JavaDoc;
25
26 /**
27  * Alternative to the standard Bean introspector. One key difference is that
28  * this introspector ensures interface properties are properly
29  * discovered. Also, indexed properties can have an index of any type.
30  *
31  * @author Brian S O'Neill
32  */

33 public class BeanIntrospector {
34     // Weakly maps Class objects to softly referenced BeanProperty maps.
35
private static Map JavaDoc cPropertiesCache = new WeakIdentityMap();
36
37     public static void main(String JavaDoc[] args) throws Exception JavaDoc {
38         System.out.println(getAllProperties(Class.forName(args[0])));
39     }
40
41     /**
42      * Returns a Map of all the available properties on a given class including
43      * write-only and indexed properties.
44      *
45      * @return Map<String, BeanProperty> an unmodifiable mapping of property
46      * names (Strings) to BeanProperty objects.
47      *
48      */

49     public static Map JavaDoc getAllProperties(Class JavaDoc clazz) {
50         synchronized (cPropertiesCache) {
51             Map JavaDoc properties;
52             SoftReference JavaDoc ref = (SoftReference JavaDoc) cPropertiesCache.get(clazz);
53             if (ref != null) {
54                 properties = (Map JavaDoc) ref.get();
55                 if (properties != null) {
56                     return properties;
57                 }
58             }
59             properties = createProperties(clazz);
60             cPropertiesCache.put(clazz, new SoftReference JavaDoc(properties));
61             return properties;
62         }
63     }
64
65     private static Map JavaDoc createProperties(Class JavaDoc clazz) {
66         if (clazz == null || clazz.isPrimitive()) {
67             return Collections.EMPTY_MAP;
68         }
69
70         Map JavaDoc properties = new HashMap JavaDoc();
71         fillInProperties(clazz, properties);
72
73         // Properties defined in Object are also available to interfaces.
74
if (clazz.isInterface()) {
75             fillInProperties(Object JavaDoc.class, properties);
76         }
77
78         // Ensure that all implemented interfaces are properly analyzed.
79
Class JavaDoc[] interfaces = clazz.getInterfaces();
80         for (int i=0; i<interfaces.length; i++) {
81             fillInProperties(interfaces[i], properties);
82         }
83
84         return Collections.unmodifiableMap(properties);
85     }
86
87     /**
88      * @param clazz Class to introspect
89      * @param properties Receives properties as name->property entries
90      */

91     private static void fillInProperties(Class JavaDoc clazz, Map JavaDoc properties) {
92         Method JavaDoc[] methods = clazz.getMethods();
93
94         Method JavaDoc method;
95         String JavaDoc name;
96         Class JavaDoc type;
97         Class JavaDoc[] params;
98         SimpleProperty property;
99         IndexedProperty indexedProperty;
100
101         // Gather non-conflicting "get" accessors
102
for (int i=0; i<methods.length; i++) {
103             method = methods[i];
104             if (Modifier.isStatic(method.getModifiers()) ||
105                 (type = method.getReturnType()) == void.class ||
106                 method.getParameterTypes().length > 0 ||
107                 (name = extractPropertyName(method, "get")) == null) {
108                 continue;
109             }
110             if (properties.containsKey(name)) {
111                 property = (SimpleProperty) properties.get(name);
112                 if (type != property.getType() || property.getReadMethod() != null) {
113                     continue;
114                 }
115             } else {
116                 property = new SimpleProperty(name, type);
117                 properties.put(name, property);
118             }
119             property.setReadMethod(method);
120         }
121
122         // Gather non-conflicting "is" accessors.
123
for (int i=0; i<methods.length; i++) {
124             method = methods[i];
125             if (Modifier.isStatic(method.getModifiers()) ||
126                 (type = method.getReturnType()) != boolean.class ||
127                 method.getParameterTypes().length > 0 ||
128                 (name = extractPropertyName(method, "is")) == null) {
129                 continue;
130             }
131             if (properties.containsKey(name)) {
132                 property = (SimpleProperty) properties.get(name);
133                 if (type != property.getType() || property.getReadMethod() != null) {
134                     continue;
135                 }
136             } else {
137                 property = new SimpleProperty(name, type);
138                 properties.put(name, property);
139             }
140             property.setReadMethod(method);
141         }
142
143         // Gather non-conflicting mutators.
144
for (int i=0; i<methods.length; i++) {
145             method = methods[i];
146             if (Modifier.isStatic(method.getModifiers()) ||
147                 method.getReturnType() != void.class ||
148                 (params = method.getParameterTypes()).length != 1 ||
149                 (name = extractPropertyName(method, "set")) == null) {
150                 continue;
151             }
152             type = params[0];
153             if (properties.containsKey(name)) {
154                 property = (SimpleProperty) properties.get(name);
155                 if (type != property.getType() || property.getWriteMethod() != null) {
156                     continue;
157                 }
158             } else {
159                 property = new SimpleProperty(name, type);
160                 properties.put(name, property);
161             }
162             property.setWriteMethod(method);
163         }
164
165         // Gather non-conflicting indexed property accessors.
166
for (int i=0; i<methods.length; i++) {
167             method = methods[i];
168             if (Modifier.isStatic(method.getModifiers()) ||
169                 (type = method.getReturnType()) == void.class ||
170                 (params = method.getParameterTypes()).length != 1 ||
171                 (name = extractPropertyName(method, "get")) == null) {
172                 continue;
173             }
174             if (properties.containsKey(name)) {
175                 property = (SimpleProperty) properties.get(name);
176                 if (type != property.getType()) {
177                     continue;
178                 }
179                 if (property instanceof IndexedProperty) {
180                     indexedProperty = (IndexedProperty) property;
181                 } else {
182                     indexedProperty = new IndexedProperty(property);
183                     properties.put(name, indexedProperty);
184                 }
185             } else {
186                 indexedProperty = new IndexedProperty(name, type);
187                 properties.put(name, indexedProperty);
188             }
189             indexedProperty.addIndexedReadMethod(method);
190         }
191
192         // Gather non-conflicting indexed property mutators.
193
for (int i=0; i<methods.length; i++) {
194             method = methods[i];
195             if (Modifier.isStatic(method.getModifiers()) ||
196                 method.getReturnType() != void.class ||
197                 (params = method.getParameterTypes()).length != 2 ||
198                 (name = extractPropertyName(method, "set")) == null) {
199                 continue;
200             }
201             type = params[1];
202             if (properties.containsKey(name)) {
203                 property = (SimpleProperty) properties.get(name);
204                 if (type != property.getType()) {
205                     continue;
206                 }
207                 if (property instanceof IndexedProperty) {
208                     indexedProperty = (IndexedProperty) property;
209                 } else {
210                     indexedProperty = new IndexedProperty(property);
211                     properties.put(name, indexedProperty);
212                 }
213             } else {
214                 indexedProperty = new IndexedProperty(name, type);
215                 properties.put(name, indexedProperty);
216             }
217             indexedProperty.addIndexedWriteMethod(method);
218         }
219     }
220
221     /**
222      * Returns null if prefix pattern doesn't match
223      */

224     private static String JavaDoc extractPropertyName(Method JavaDoc method, String JavaDoc prefix) {
225         String JavaDoc name = method.getName();
226         if (!name.startsWith(prefix)) {
227             return null;
228         }
229         if (name.length() == prefix.length()) {
230             return "";
231         }
232         name = name.substring(prefix.length());
233         if (!Character.isUpperCase(name.charAt(0)) || name.indexOf('$') >= 0) {
234             return null;
235         }
236
237         // Decapitalize the name only if it doesn't begin with two uppercase
238
// letters.
239

240         if (name.length() == 1 || !Character.isUpperCase(name.charAt(1))) {
241             char chars[] = name.toCharArray();
242             chars[0] = Character.toLowerCase(chars[0]);
243             name = new String JavaDoc(chars);
244         }
245
246         return name.intern();
247     }
248
249     private static class SimpleProperty implements BeanProperty {
250         private final String JavaDoc mName;
251         private final Class JavaDoc mType;
252
253         private Method JavaDoc mReadMethod;
254         private Method JavaDoc mWriteMethod;
255
256         SimpleProperty(String JavaDoc name, Class JavaDoc type) {
257             mName = name;
258             mType = type;
259         }
260
261         public String JavaDoc getName() {
262             return mName;
263         }
264
265         public Class JavaDoc getType() {
266             return mType;
267         }
268
269         public Method JavaDoc getReadMethod() {
270             return mReadMethod;
271         }
272
273         public Method JavaDoc getWriteMethod() {
274             return mWriteMethod;
275         }
276
277         public int getIndexTypesCount() {
278             return 0;
279         }
280
281         public Class JavaDoc getIndexType(int index) {
282             throw new IndexOutOfBoundsException JavaDoc();
283         }
284
285         public Method JavaDoc getIndexedReadMethod(int index) {
286             throw new IndexOutOfBoundsException JavaDoc();
287         }
288
289         public Method JavaDoc getIndexedWriteMethod(int index) {
290             throw new IndexOutOfBoundsException JavaDoc();
291         }
292
293         public String JavaDoc toString() {
294             return "BeanProperty[name=" + getName() +
295                 ", type=" + getType().getName() + ']';
296         }
297
298         void setReadMethod(Method JavaDoc method) {
299             mReadMethod = method;
300         }
301
302         void setWriteMethod(Method JavaDoc method) {
303             mWriteMethod = method;
304         }
305     }
306
307     private static class IndexedProperty extends SimpleProperty {
308         private Method JavaDoc[] mIndexedReadMethods;
309         private Method JavaDoc[] mIndexedWriteMethods;
310
311         IndexedProperty(String JavaDoc name, Class JavaDoc type) {
312             super(name, type);
313         }
314
315         IndexedProperty(BeanProperty property) {
316             super(property.getName(), property.getType());
317             setReadMethod(property.getReadMethod());
318             setWriteMethod(property.getWriteMethod());
319         }
320
321         public int getIndexTypesCount() {
322             Method JavaDoc[] methods = mIndexedReadMethods;
323             if (methods != null) {
324                 return methods.length;
325             }
326             methods = mIndexedWriteMethods;
327             return methods == null ? 0 : methods.length;
328         }
329
330         public Class JavaDoc getIndexType(int index) {
331             Method JavaDoc[] methods = mIndexedReadMethods;
332             if (methods != null) {
333                 Method JavaDoc method = methods[0];
334                 if (method != null) {
335                     return method.getParameterTypes()[0];
336                 }
337             }
338             methods = mIndexedWriteMethods;
339             if (methods != null) {
340                 Method JavaDoc method = methods[index];
341                 if (method != null) {
342                     return method.getParameterTypes()[0];
343                 }
344             }
345             if (index >= getIndexTypesCount()) {
346                 throw new IndexOutOfBoundsException JavaDoc();
347             }
348             return null;
349         }
350
351         public Method JavaDoc getIndexedReadMethod(int index) {
352             return mIndexedReadMethods[index];
353         }
354
355         public Method JavaDoc getIndexedWriteMethod(int index) {
356             return mIndexedWriteMethods[index];
357         }
358
359         public String JavaDoc toString() {
360             StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
361             buf.append("BeanProperty[name=");
362             buf.append(getName());
363             buf.append(", type=");
364             buf.append(getType().getName());
365             buf.append(", ");
366             int count = getIndexTypesCount();
367             for (int i=0; i<count; i++) {
368                 if (i > 0) {
369                     buf.append(", ");
370                 }
371                 buf.append("indexType[");
372                 buf.append(i);
373                 buf.append("]=");
374                 buf.append(getIndexType(0));
375             }
376             buf.append(']');
377             return buf.toString();
378         }
379
380         void addIndexedReadMethod(Method JavaDoc method) {
381             Class JavaDoc indexType = method.getParameterTypes()[0];
382             int count = getIndexTypesCount();
383             int i;
384             for (i=0; i<count; i++) {
385                 if (getIndexType(i) == indexType) {
386                     break;
387                 }
388             }
389             if (i >= count) {
390                 expandCapactity();
391             }
392             if (mIndexedReadMethods[i] == null) {
393                 mIndexedReadMethods[i] = method;
394             }
395         }
396
397         void addIndexedWriteMethod(Method JavaDoc method) {
398             Class JavaDoc indexType = method.getParameterTypes()[0];
399             int count = getIndexTypesCount();
400             int i;
401             for (i=0; i<count; i++) {
402                 if (getIndexType(i) == indexType) {
403                     break;
404                 }
405             }
406             if (i >= count) {
407                 expandCapactity();
408             }
409             if (mIndexedWriteMethods[i] == null) {
410                 mIndexedWriteMethods[i] = method;
411             }
412         }
413
414         private void expandCapactity() {
415             int count = getIndexTypesCount();
416             Method JavaDoc[] methods = new Method JavaDoc[count + 1];
417             if (mIndexedReadMethods != null) {
418                 System.arraycopy(mIndexedReadMethods, 0, methods, 0, count);
419             }
420             mIndexedReadMethods = methods;
421             methods = new Method JavaDoc[count + 1];
422             if (mIndexedWriteMethods != null) {
423                 System.arraycopy(mIndexedWriteMethods, 0, methods, 0, count);
424             }
425             mIndexedWriteMethods = methods;
426         }
427     }
428 }
429
Popular Tags