1 package de.keepondreaming.xml;
2
3 import java.lang.reflect.Method;
4 import java.util.HashMap;
5 import java.util.HashSet;
6 import java.util.Map;
7 import java.util.Properties;
8 import java.util.Set;
9
10 import de.keepondreaming.xml.util.Util;
11
12 /***
13 * Resolves interfaces and attributes via a {@link java.util.Properties} mapping.
14 * <p>
15 * If the mapping does not resolve the strategy can be configured via the constructor
16 * {@link #ClassNameMappingObjectStrategy(Properties, boolean)} to use a backup
17 * strategy, usually {@link de.keepondreaming.xml.ProxyObjectStrategy}
18 * <p>
19 * Property mappings are as follows:
20 * <ul>
21 * <li>Interface to class mapping: <Interface-name>=<Class-name>, <p>i.e. <code>de.keepondreaming.xml.example.Root=de.keepondreaming.xml.example.impl.RootImpl</code></li>
22 * <li>Map XML-tagname to getter method:<interface-name>.<tag-name>=<getter-method>, <p>i.e. <code>de.package.MyClass.UGLY_XML_TAG_NAME=getNiceNamedMethod</code></li>
23 * <li>Map XML-tagname to setter method:<b><class-name></b>.<tag-name>=<setter-method>, <p>i.e. <code>de.package.MyClassImpl.UGLY_XML_TAG_NAME=setNiceNamedMethodWithOtherName</code></li>
24 * </ul>
25 * Note that the mappings of XML tagnames to setter/getter methods is only needed if the methods can not get obtained via reflection
26 *
27 * <p>
28 * $Author: wintermond $
29 * $Date: 2005/07/10 18:38:43 $
30 * $Log: ClassNameMappingObjectStrategy.java,v $
31 * Revision 1.1 2005/07/10 18:38:43 wintermond
32 * Renamed ImplObjectStrategy to ClassNameMappingStrategy
33 * Performance improvements
34 *
35 * Revision 1.2 2005/07/09 09:56:38 wintermond
36 * Javadoc and implemented ProxyClassCreator. Backupstrategy now more generic
37 *
38 */
39 public class ClassNameMappingObjectStrategy implements ObjectStrategy
40 {
41 /***
42 * Backup strategy if mapping information is insufficient.
43 */
44 private ObjectStrategy backupStrategyM = null;
45
46 /***
47 * lookup cache, stores mappings of interfaces to classes
48 */
49 private Map<Class, Class> classCacheM = new HashMap<Class, Class>();
50
51 /***
52 * reverse cache, caches the implementation classes to the interfaces
53 */
54 private Map<Class, Class> interfaceCacheM = new HashMap<Class, Class>();
55
56 /***
57 * Stores all classes already instanciated by this instance
58 */
59 private Set<Class> cachedClassesM = new HashSet<Class>();
60
61 /***
62 * To prevent spamming of messages unresolved attributes get logged here
63 */
64 private Set<String> unresolvedAttributes = new HashSet<String>();
65
66 /***
67 * Mapping information, tells the strategy how to map
68 * interfaces to classes and the name of setter methods.
69 */
70 private Properties mappingsM = null;
71
72 /***
73 * Performance optimisation: method lookup for {@link #setAttribute(Object, String, Object))}
74 */
75 private Map<String, Method> methodCacheM = new HashMap<String, Method>();
76
77 /***
78 * Default constructor
79 *
80 * @param mappings Must not be null;
81 */
82 public ClassNameMappingObjectStrategy(Properties mappings)
83 {
84 this(mappings, true);
85 }
86
87 /***
88 * Constructor, specifying if in case of insufficient mapping information
89 * the call should get passed to the backup strategy
90 *
91 * @param mappings
92 * @param useProxyMapping
93 */
94 public ClassNameMappingObjectStrategy(Properties mappings, boolean useProxyMapping)
95 {
96 if(useProxyMapping)
97 {
98 backupStrategyM = new ProxyObjectStrategy();
99 }
100 if(mappings == null)
101 {
102 throw new NullPointerException("maping is null");
103 }
104 mappingsM = mappings;
105 }
106
107
108
109
110 public Object createInstance(Class clazz)
111 {
112 Object result = null;
113
114
115
116
117 Class creatorClass = classCacheM.get(clazz);
118
119
120
121
122 if(creatorClass == null)
123 {
124 String className = mappingsM.getProperty(clazz.getName());
125 if(className != null)
126 {
127 try
128 {
129 creatorClass = Class.forName(className);
130 classCacheM.put(clazz, creatorClass);
131 cachedClassesM.add(creatorClass);
132 interfaceCacheM.put(creatorClass, clazz);
133 }
134 catch (ClassNotFoundException e)
135 {
136 throw new IllegalArgumentException("Unkown class [" + className + "] specified in mapping!");
137 }
138 }
139
140
141
142 else if(backupStrategyM == null)
143 {
144 throw new IllegalArgumentException("No mapping specified for class [" + className + "]!");
145 }
146 else
147 {
148 result = backupStrategyM.createInstance(clazz);
149 }
150 }
151
152
153
154
155 if(result == null)
156 {
157 try
158 {
159 result = creatorClass.newInstance();
160 }
161 catch (Throwable e)
162 {
163 throw new IllegalArgumentException("Error while instanciating class [" + creatorClass.getName() + "]", e);
164 }
165 }
166
167 return result;
168 }
169
170
171
172
173
174
175
176 public void setAttribute(Object target, String attribute, Object value)
177 {
178 Method method = getMethod(target, attribute, value);
179
180 if(method != null)
181 {
182 try
183 {
184 method.invoke(target, new Object[]{value});
185 }
186 catch (Throwable e)
187 {
188 throw new IllegalArgumentException("Could not set value [" + value + "] of attribute [" + attribute + "] in object [" + target + "] using method [" + method + "]. Value class is [" + value.getClass() + "]", e);
189 }
190 }
191 else if(backupStrategyM != null && !cachedClassesM.contains(target.getClass()))
192 {
193 backupStrategyM.setAttribute(target, attribute, value);
194 }
195 else
196 {
197 String key = target.getClass().getName()
198 + "."
199 + attribute;
200
201 if(!unresolvedAttributes.contains(key))
202 {
203 unresolvedAttributes.add(key);
204 System.err.println("Could not resolve setter for attribute " + attribute + " of class " + target.getClass());
205 }
206 }
207
208 }
209
210 private Method getMethod(Object target, String attribute, Object value)
211 {
212
213
214
215
216 String key = target.getClass().getName()
217 + "."
218 + attribute;
219
220 Method result = methodCacheM.get(key);
221
222 if(result == null)
223 {
224 String backupKey = "set"
225 + Util.capitalize(attribute);
226 String methodName = mappingsM.getProperty(key, backupKey);
227
228 try
229 {
230 Class valueClass = value.getClass();
231
232
233
234 if (value instanceof ProxyClassCreator)
235 {
236 valueClass = ((ProxyClassCreator) value).getWrappedClass(value);
237 }
238 result = target.getClass().getMethod(methodName, new Class[]{valueClass});
239 }
240 catch (NoSuchMethodException checkForPrimitive)
241 {
242
243
244
245
246
247
248 Class primitive = Util.computePrimitiveClass(value);
249 if(primitive != null)
250 {
251 try
252 {
253 result = target.getClass().getMethod(methodName, new Class[]{primitive});
254 }
255 catch (NoSuchMethodException ignore)
256 {
257
258 }
259 }
260 }
261
262 if(result != null)
263 {
264 methodCacheM.put(key, result);
265 }
266 }
267 return result;
268 }
269
270
271
272
273 public void init()
274 {
275 classCacheM.clear();
276 cachedClassesM.clear();
277 interfaceCacheM.clear();
278 unresolvedAttributes.clear();
279 methodCacheM.clear();
280 }
281
282
283
284
285 public Class resolveInterface(Object object)
286 {
287 Class result = interfaceCacheM.get(object.getClass());
288 if(result == null)
289 {
290 result = backupStrategyM.resolveInterface(object);
291 }
292 return result;
293 }
294
295
296
297
298 public String getMethodName(Class clazz, String attribute, boolean set)
299 {
300 String key = clazz.getName() + "." + attribute;
301 String result = mappingsM.getProperty(key);
302 return result;
303 }
304
305
306
307
308 }