KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > thoughtworks > xstream > converters > javabean > PropertyDictionary


1 package com.thoughtworks.xstream.converters.javabean;
2
3 import java.beans.Introspector JavaDoc;
4 import java.lang.reflect.Method JavaDoc;
5 import java.lang.reflect.Modifier JavaDoc;
6 import java.util.ArrayList JavaDoc;
7 import java.util.Collection JavaDoc;
8 import java.util.Collections JavaDoc;
9 import java.util.Comparator JavaDoc;
10 import java.util.HashMap JavaDoc;
11 import java.util.Iterator JavaDoc;
12 import java.util.List JavaDoc;
13 import java.util.Map JavaDoc;
14
15 import com.thoughtworks.xstream.converters.reflection.ObjectAccessException;
16
17 /**
18  * Builds the serializable properties maps for each bean and caches them.
19  */

20 public class PropertyDictionary {
21
22     private final Map JavaDoc keyedByPropertyNameCache = Collections.synchronizedMap(new HashMap JavaDoc());
23
24     public Iterator JavaDoc serializablePropertiesFor(Class JavaDoc cls) {
25         return buildMap(cls).values().iterator();
26     }
27
28     /**
29      * Locates a serializable property
30      *
31      * @param cls
32      * @param name
33      * @param definedIn
34      * @return
35      */

36     public BeanProperty property(Class JavaDoc cls, String JavaDoc name) {
37         Map JavaDoc properties = buildMap(cls);
38         BeanProperty property = (BeanProperty) properties.get(name);
39         if (property == null) {
40             throw new ObjectAccessException("No such property " + cls.getName() + "." + name);
41         } else {
42             return property;
43         }
44     }
45
46     /**
47      * Builds the map of all serializable properties for the the provided bean
48      *
49      * @param cls
50      * @param tupleKeyed
51      * @return
52      */

53     private Map JavaDoc buildMap(Class JavaDoc cls) {
54         final String JavaDoc clsName = cls.getName();
55         if (!keyedByPropertyNameCache.containsKey(clsName)) {
56             synchronized (keyedByPropertyNameCache) {
57                 if (!keyedByPropertyNameCache.containsKey(clsName)) { // double check
58
// Gather all the properties, using only the keyed map. It
59
// is possible that a class have two writable only
60
// properties that have the same name
61
// but different types
62
final Map JavaDoc propertyMap = new HashMap JavaDoc();
63                     Method JavaDoc[] methods = cls.getMethods();
64
65                     for (int i = 0; i < methods.length; i++) {
66                         if (!Modifier.isPublic(methods[i].getModifiers())
67                                 || Modifier.isStatic(methods[i].getModifiers()))
68                             continue;
69
70                         String JavaDoc methodName = methods[i].getName();
71                         Class JavaDoc[] parameters = methods[i].getParameterTypes();
72                         Class JavaDoc returnType = methods[i].getReturnType();
73                         String JavaDoc propertyName = null;
74                         if ((methodName.startsWith("get") || methodName.startsWith("is"))
75                                 && parameters.length == 0 && returnType != null) {
76                             if (methodName.startsWith("get"))
77                                 propertyName = Introspector.decapitalize(methodName.substring(3));
78                             else
79                                 propertyName = Introspector.decapitalize(methodName.substring(2));
80                             BeanProperty property = getBeanProperty(propertyMap, cls, propertyName,
81                                     returnType);
82                             property.setGetterMethod(methods[i]);
83                         } else if (methodName.startsWith("set") && parameters.length == 1
84                                 && returnType.equals(void.class)) {
85                             propertyName = Introspector.decapitalize(methodName.substring(3));
86                             BeanProperty property = getBeanProperty(propertyMap, cls, propertyName,
87                                     parameters[0]);
88                             property.setSetterMethod(methods[i]);
89                         }
90                     }
91
92                     // retain only those that can be both read and written and
93
// sort them by name
94
List JavaDoc serializableProperties = new ArrayList JavaDoc();
95                     for (Iterator JavaDoc it = propertyMap.values().iterator(); it.hasNext();) {
96                         BeanProperty property = (BeanProperty) it.next();
97                         if (property.isReadable() && property.isWritable()) {
98                             serializableProperties.add(property);
99                         }
100                     }
101                     Collections.sort(serializableProperties, new BeanPropertyComparator());
102
103                     // build the maps and return
104
final Map JavaDoc keyedByFieldName = new OrderRetainingMap();
105                     for (Iterator JavaDoc it = serializableProperties.iterator(); it.hasNext();) {
106                         BeanProperty property = (BeanProperty) it.next();
107                         keyedByFieldName.put(property.getName(), property);
108                     }
109
110                     keyedByPropertyNameCache.put(clsName, keyedByFieldName);
111                 }
112             }
113         }
114         return (Map JavaDoc) keyedByPropertyNameCache.get(clsName);
115     }
116
117     private BeanProperty getBeanProperty(Map JavaDoc propertyMap, Class JavaDoc cls, String JavaDoc propertyName, Class JavaDoc type) {
118         PropertyKey key = new PropertyKey(propertyName, type);
119         BeanProperty property = (BeanProperty) propertyMap.get(key);
120         if (property == null) {
121             property = new BeanProperty(cls, propertyName, type);
122             propertyMap.put(key, property);
123         }
124         return property;
125     }
126
127     /**
128      * Needed to avoid problems with multiple setters with the same name, but
129      * referred to different types
130      */

131     private static class PropertyKey {
132         private String JavaDoc propertyName;
133
134         private Class JavaDoc propertyType;
135
136         public PropertyKey(String JavaDoc propertyName, Class JavaDoc propertyType) {
137             this.propertyName = propertyName;
138             this.propertyType = propertyType;
139         }
140
141         public boolean equals(Object JavaDoc o) {
142             if (this == o)
143                 return true;
144             if (!(o instanceof PropertyKey))
145                 return false;
146
147             final PropertyKey propertyKey = (PropertyKey) o;
148
149             if (propertyName != null ? !propertyName.equals(propertyKey.propertyName)
150                     : propertyKey.propertyName != null)
151                 return false;
152             if (propertyType != null ? !propertyType.equals(propertyKey.propertyType)
153                     : propertyKey.propertyType != null)
154                 return false;
155
156             return true;
157         }
158
159         public int hashCode() {
160             int result;
161             result = (propertyName != null ? propertyName.hashCode() : 0);
162             result = 29 * result + (propertyType != null ? propertyType.hashCode() : 0);
163             return result;
164         }
165
166         public String JavaDoc toString() {
167             return "PropertyKey{propertyName='" + propertyName + "'" + ", propertyType="
168                     + propertyType + "}";
169         }
170
171     }
172
173     /**
174      * Compares properties by name
175      */

176     private static class BeanPropertyComparator implements Comparator JavaDoc {
177
178         public int compare(Object JavaDoc o1, Object JavaDoc o2) {
179             return ((BeanProperty) o1).getName().compareTo(((BeanProperty) o2).getName());
180         }
181
182     }
183
184     private static class OrderRetainingMap extends HashMap JavaDoc {
185
186         private List JavaDoc valueOrder = new ArrayList JavaDoc();
187
188         public Object JavaDoc put(Object JavaDoc key, Object JavaDoc value) {
189             valueOrder.add(value);
190             return super.put(key, value);
191         }
192
193         public Collection JavaDoc values() {
194             return Collections.unmodifiableList(valueOrder);
195         }
196     }
197
198 }
Popular Tags