1 package org.apache.commons.betwixt.digester;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import java.beans.PropertyDescriptor;
20 import java.lang.reflect.Method;
21 import java.lang.reflect.Modifier;
22 import java.util.Map;
23
24 import org.apache.commons.betwixt.ElementDescriptor;
25 import org.apache.commons.betwixt.XMLBeanInfo;
26 import org.apache.commons.betwixt.XMLUtils;
27 import org.apache.commons.betwixt.expression.ConstantExpression;
28 import org.apache.commons.betwixt.expression.Expression;
29 import org.apache.commons.betwixt.expression.IteratorExpression;
30 import org.apache.commons.betwixt.expression.MethodExpression;
31 import org.apache.commons.betwixt.expression.MethodUpdater;
32 import org.apache.commons.logging.Log;
33 import org.apache.commons.logging.LogFactory;
34 import org.xml.sax.Attributes;
35 import org.xml.sax.SAXException;
36
37
38
39
40
41
42
43
44
45 public class ElementRule extends MappedPropertyRule {
46
47
48 private static Log log = LogFactory.getLog(ElementRule.class);
49
50
51
52
53
54
55
56
57 public static final void setLog(Log newLog) {
58 log = newLog;
59 }
60
61
62 private Class beanClass;
63
64
65 public ElementRule() {
66 }
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82 public void begin(String name, String namespace, Attributes attributes)
83 throws SAXException {
84 String nameAttributeValue = attributes.getValue("name");
85
86 ElementDescriptor descriptor = new ElementDescriptor();
87 descriptor.setLocalName(nameAttributeValue);
88 String uri = attributes.getValue("uri");
89 String qName = nameAttributeValue;
90 if (uri != null && nameAttributeValue != null) {
91 descriptor.setURI(uri);
92 String prefix = getXMLIntrospector().getConfiguration()
93 .getPrefixMapper().getPrefix(uri);
94 qName = prefix + ":" + nameAttributeValue;
95 }
96 descriptor.setQualifiedName(qName);
97
98 String propertyName = attributes.getValue("property");
99 descriptor.setPropertyName(propertyName);
100
101 String propertyType = attributes.getValue("type");
102
103 if (log.isTraceEnabled()) {
104 log.trace("(BEGIN) name=" + nameAttributeValue + " uri=" + uri
105 + " property=" + propertyName + " type=" + propertyType);
106 }
107
108
109 String mappingDerivation = attributes.getValue("mappingDerivation");
110 if ("introspection".equals(mappingDerivation)) {
111 descriptor.setUseBindTimeTypeForMapping(false);
112 } else if ("bind".equals(mappingDerivation)) {
113 descriptor.setUseBindTimeTypeForMapping(true);
114 }
115
116
117 descriptor.setPropertyType(getPropertyType(propertyType, beanClass,
118 propertyName));
119
120 boolean isCollective = getXMLIntrospector().getConfiguration()
121 .isLoopType(descriptor.getPropertyType());
122
123 descriptor.setCollective(isCollective);
124
125
126 if (!isCollective
127 && (nameAttributeValue == null || nameAttributeValue.trim()
128 .equals(""))) {
129
130 log
131 .info("No name attribute has been specified. This element will be polymorphic.");
132 }
133
134
135 if (nameAttributeValue != null
136 && !XMLUtils.isWellFormedXMLName(nameAttributeValue)) {
137 throw new SAXException("'" + nameAttributeValue
138 + "' would not be a well formed xml element name.");
139 }
140
141 String implementationClass = attributes.getValue("class");
142 if (log.isTraceEnabled()) {
143 log.trace("'class' attribute=" + implementationClass);
144 }
145 if (implementationClass != null) {
146 try {
147
148 Class clazz = loadClass(implementationClass);
149 descriptor.setImplementationClass(clazz);
150
151 } catch (Exception e) {
152 if (log.isDebugEnabled()) {
153 log.debug(
154 "Cannot load class named: " + implementationClass,
155 e);
156 }
157 throw new SAXException("Cannot load class named: "
158 + implementationClass);
159 }
160 }
161
162 if (propertyName != null && propertyName.length() > 0) {
163 boolean forceAccessible = "true".equals(attributes
164 .getValue("forceAccessible"));
165 configureDescriptor(descriptor, attributes.getValue("updater"),
166 forceAccessible);
167
168 } else {
169 String value = attributes.getValue("value");
170 if (value != null) {
171 descriptor.setTextExpression(new ConstantExpression(value));
172 }
173 }
174
175 Object top = digester.peek();
176 if (top instanceof XMLBeanInfo) {
177 XMLBeanInfo beanInfo = (XMLBeanInfo) top;
178 beanInfo.setElementDescriptor(descriptor);
179 beanClass = beanInfo.getBeanClass();
180 descriptor.setPropertyType(beanClass);
181
182 } else if (top instanceof ElementDescriptor) {
183 ElementDescriptor parent = (ElementDescriptor) top;
184 parent.addElementDescriptor(descriptor);
185
186 } else {
187 throw new SAXException("Invalid use of <element>. It should "
188 + "be nested inside <info> or other <element> nodes");
189 }
190
191 digester.push(descriptor);
192 }
193
194
195
196
197 public void end(String name, String namespace) {
198 ElementDescriptor descriptor = (ElementDescriptor)digester.pop();
199
200 final Object peek = digester.peek();
201
202 if(peek instanceof ElementDescriptor) {
203 ElementDescriptor parent = (ElementDescriptor)digester.peek();
204
205
206 if( getXMLIntrospector().getConfiguration().getElementSuppressionStrategy().suppress(descriptor)) {
207 parent.removeElementDescriptor(descriptor);
208 }
209 }
210 }
211
212
213
214
215
216
217
218
219
220
221
222
223 protected void configureDescriptor(ElementDescriptor elementDescriptor) {
224 configureDescriptor(elementDescriptor, null);
225 }
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243 protected void configureDescriptor(ElementDescriptor elementDescriptor,
244 String updateMethodName) {
245 configureDescriptor(elementDescriptor, null, false);
246 }
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261 private void configureDescriptor(ElementDescriptor elementDescriptor,
262 String updateMethodName, boolean forceAccessible) {
263 Class beanClass = getBeanClass();
264 if (beanClass != null) {
265 String name = elementDescriptor.getPropertyName();
266 PropertyDescriptor descriptor = getPropertyDescriptor(beanClass,
267 name);
268
269 if (descriptor == null) {
270 if (log.isDebugEnabled()) {
271 log.debug("Cannot find property matching " + name);
272 }
273 } else {
274 configureProperty(elementDescriptor, descriptor,
275 updateMethodName, forceAccessible, beanClass);
276
277 getProcessedPropertyNameSet().add(name);
278 }
279 }
280 }
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302 private void configureProperty(ElementDescriptor elementDescriptor,
303 PropertyDescriptor propertyDescriptor, String updateMethodName,
304 boolean forceAccessible, Class beanClass) {
305
306 Class type = propertyDescriptor.getPropertyType();
307 Method readMethod = propertyDescriptor.getReadMethod();
308 Method writeMethod = propertyDescriptor.getWriteMethod();
309
310 elementDescriptor.setPropertyType(type);
311
312
313
314
315
316
317 if (readMethod == null) {
318 log.trace("No read method");
319 return;
320 }
321
322 if (log.isTraceEnabled()) {
323 log.trace("Read method=" + readMethod.getName());
324 }
325
326
327
328 final MethodExpression methodExpression = new MethodExpression(readMethod);
329 if (getXMLIntrospector().isPrimitiveType(type)) {
330 elementDescriptor
331 .setTextExpression(methodExpression);
332
333 } else if (getXMLIntrospector().isLoopType(type)) {
334 log.trace("Loop type ??");
335
336
337
338 Expression expression = methodExpression;
339
340
341
342 boolean standardProperty = false;
343 if (updateMethodName != null && writeMethod != null && writeMethod.getName().equals(updateMethodName)) {
344 final Class[] parameters = writeMethod.getParameterTypes();
345 if (parameters.length == 1) {
346 Class setterType = parameters[0];
347 if (type.equals(setterType)) {
348 standardProperty = true;
349 }
350 }
351 }
352 if (!standardProperty) {
353 expression = new IteratorExpression(methodExpression);
354 }
355 elementDescriptor.setContextExpression(expression);
356 elementDescriptor.setHollow(true);
357
358 writeMethod = null;
359
360 if (Map.class.isAssignableFrom(type)) {
361 elementDescriptor.setLocalName("entry");
362
363 ElementDescriptor keyDescriptor = new ElementDescriptor("key");
364 keyDescriptor.setHollow(true);
365 elementDescriptor.addElementDescriptor(keyDescriptor);
366
367 ElementDescriptor valueDescriptor = new ElementDescriptor(
368 "value");
369 valueDescriptor.setHollow(true);
370 elementDescriptor.addElementDescriptor(valueDescriptor);
371 }
372
373 } else {
374 log.trace("Standard property");
375 elementDescriptor.setHollow(true);
376 elementDescriptor.setContextExpression(methodExpression);
377 }
378
379
380 if (updateMethodName == null) {
381
382 if (writeMethod != null) {
383 elementDescriptor.setUpdater(new MethodUpdater(writeMethod));
384 }
385
386 } else {
387
388 if (log.isTraceEnabled()) {
389 log.trace("Finding custom method: ");
390 log.trace(" on:" + beanClass);
391 log.trace(" name:" + updateMethodName);
392 }
393
394 Method updateMethod;
395 boolean isMapTypeProperty = Map.class.isAssignableFrom(type);
396 if (forceAccessible) {
397 updateMethod = findAnyMethod(updateMethodName, beanClass, isMapTypeProperty);
398 } else {
399 updateMethod = findPublicMethod(updateMethodName, beanClass, isMapTypeProperty);
400 }
401
402 if (updateMethod == null) {
403 if (log.isInfoEnabled()) {
404
405 log.info("No method with name '" + updateMethodName
406 + "' found for update");
407 }
408 } else {
409
410 if (Map.class.isAssignableFrom(type)) {
411
412 getXMLIntrospector().assignAdder(updateMethod, elementDescriptor);
413
414 } else {
415 elementDescriptor
416 .setUpdater(new MethodUpdater(updateMethod));
417 Class singularType = updateMethod.getParameterTypes()[0];
418 elementDescriptor.setSingularPropertyType(singularType);
419 if (singularType != null)
420 {
421 boolean isPrimitive = getXMLIntrospector().isPrimitiveType(singularType);
422 if (isPrimitive)
423 {
424 log.debug("Primitive collective: setting hollow to false");
425 elementDescriptor.setHollow(false);
426 }
427 }
428 if (log.isTraceEnabled()) {
429 log.trace("Set custom updater on " + elementDescriptor);
430 }
431 }
432 }
433 }
434 }
435
436 private Method findPublicMethod(String updateMethodName, Class beanType, boolean isMapTypeProperty) {
437 Method[] methods = beanType.getMethods();
438 Method updateMethod = searchMethodsForMatch(updateMethodName, methods, isMapTypeProperty);
439 return updateMethod;
440 }
441
442 private Method searchMethodsForMatch(String updateMethodName,
443 Method[] methods, boolean isMapType) {
444 Method updateMethod = null;
445 for (int i = 0, size = methods.length; i < size; i++) {
446 Method method = methods[i];
447 if (updateMethodName.equals(method.getName())) {
448
449
450 int numParams = 1;
451 if (isMapType) {
452
453 numParams = 2;
454 }
455
456
457
458 if (methods[i].getParameterTypes().length == numParams) {
459
460 updateMethod = methods[i];
461 if (log.isTraceEnabled()) {
462 log.trace("Matched method:" + updateMethod);
463 }
464
465 break;
466 }
467 }
468 }
469 return updateMethod;
470 }
471
472 private Method findAnyMethod(String updateMethodName, Class beanType, boolean isMapTypeProperty) {
473
474
475
476 Method updateMethod = null;
477 Class classToTry = beanType;
478 do {
479 Method[] methods = classToTry.getDeclaredMethods();
480 updateMethod = searchMethodsForMatch(updateMethodName, methods, isMapTypeProperty);
481
482
483
484 classToTry = classToTry.getSuperclass();
485 } while (updateMethod == null && classToTry != null);
486
487 if (updateMethod != null) {
488 boolean isPublic = Modifier.isPublic(updateMethod.getModifiers())
489 && Modifier.isPublic(beanType.getModifiers());
490 if (!isPublic) {
491 updateMethod.setAccessible(true);
492 }
493 }
494 return updateMethod;
495 }
496 }