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
20 import java.beans.IntrospectionException;
21 import java.beans.Introspector;
22 import java.beans.PropertyDescriptor;
23 import java.lang.reflect.Method;
24 import java.util.Collection;
25 import java.util.Date;
26 import java.util.Enumeration;
27 import java.util.HashMap;
28 import java.util.Iterator;
29 import java.util.Map;
30
31 import org.apache.commons.betwixt.AttributeDescriptor;
32 import org.apache.commons.betwixt.ElementDescriptor;
33 import org.apache.commons.betwixt.NodeDescriptor;
34 import org.apache.commons.betwixt.XMLIntrospector;
35 import org.apache.commons.betwixt.expression.IteratorExpression;
36 import org.apache.commons.betwixt.expression.MapEntryAdder;
37 import org.apache.commons.betwixt.expression.MethodExpression;
38 import org.apache.commons.betwixt.expression.MethodUpdater;
39 import org.apache.commons.betwixt.strategy.PluralStemmer;
40 import org.apache.commons.logging.Log;
41 import org.apache.commons.logging.LogFactory;
42
43
44
45
46
47
48
49
50
51
52
53
54
55 public class XMLIntrospectorHelper {
56
57
58 protected static Log log = LogFactory.getLog( XMLIntrospectorHelper.class );
59
60
61 public XMLIntrospectorHelper() {
62 }
63
64
65
66
67
68
69 public static Log getLog() {
70 return log;
71 }
72
73
74
75
76
77
78 public static void setLog(Log aLog) {
79 log = aLog;
80 }
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96 public static NodeDescriptor createDescriptor(
97 PropertyDescriptor propertyDescriptor,
98 boolean useAttributesForPrimitives,
99 XMLIntrospector introspector
100 ) throws IntrospectionException {
101 String name = propertyDescriptor.getName();
102 Class type = propertyDescriptor.getPropertyType();
103
104 if (log.isTraceEnabled()) {
105 log.trace("Creating descriptor for property: name="
106 + name + " type=" + type);
107 }
108
109 NodeDescriptor nodeDescriptor = null;
110 Method readMethod = propertyDescriptor.getReadMethod();
111 Method writeMethod = propertyDescriptor.getWriteMethod();
112
113 if ( readMethod == null ) {
114 if (log.isTraceEnabled()) {
115 log.trace( "No read method for property: name="
116 + name + " type=" + type);
117 }
118 return null;
119 }
120
121 if ( log.isTraceEnabled() ) {
122 log.trace( "Read method=" + readMethod.getName() );
123 }
124
125
126
127
128 if ( Class.class.equals( type ) && "class".equals( name ) ) {
129 log.trace( "Ignoring class property" );
130 return null;
131 }
132 if ( isPrimitiveType( type ) ) {
133 if (log.isTraceEnabled()) {
134 log.trace( "Primitive type: " + name);
135 }
136 if ( useAttributesForPrimitives ) {
137 if (log.isTraceEnabled()) {
138 log.trace( "Adding property as attribute: " + name );
139 }
140 nodeDescriptor = new AttributeDescriptor();
141 } else {
142 if (log.isTraceEnabled()) {
143 log.trace( "Adding property as element: " + name );
144 }
145 nodeDescriptor = new ElementDescriptor(true);
146 }
147 nodeDescriptor.setTextExpression( new MethodExpression( readMethod ) );
148
149 if ( writeMethod != null ) {
150 nodeDescriptor.setUpdater( new MethodUpdater( writeMethod ) );
151 }
152 } else if ( isLoopType( type ) ) {
153 if (log.isTraceEnabled()) {
154 log.trace("Loop type: " + name);
155 log.trace("Wrap in collections? " + introspector.isWrapCollectionsInElement());
156 }
157 ElementDescriptor loopDescriptor = new ElementDescriptor();
158 loopDescriptor.setContextExpression(
159 new IteratorExpression( new MethodExpression( readMethod ) )
160 );
161 loopDescriptor.setWrapCollectionsInElement(
162 introspector.isWrapCollectionsInElement());
163
164
165 if ( Map.class.isAssignableFrom( type ) ) {
166 loopDescriptor.setQualifiedName( "entry" );
167
168 loopDescriptor.addElementDescriptor( new ElementDescriptor( "key" ) );
169 loopDescriptor.addElementDescriptor( new ElementDescriptor( "value" ) );
170 }
171
172 ElementDescriptor elementDescriptor = new ElementDescriptor();
173 elementDescriptor.setWrapCollectionsInElement(
174 introspector.isWrapCollectionsInElement());
175 elementDescriptor.setElementDescriptors( new ElementDescriptor[] { loopDescriptor } );
176
177 nodeDescriptor = elementDescriptor;
178 } else {
179 if (log.isTraceEnabled()) {
180 log.trace( "Standard property: " + name);
181 }
182 ElementDescriptor elementDescriptor = new ElementDescriptor();
183 elementDescriptor.setContextExpression( new MethodExpression( readMethod ) );
184 if ( writeMethod != null ) {
185 elementDescriptor.setUpdater( new MethodUpdater( writeMethod ) );
186 }
187
188 nodeDescriptor = elementDescriptor;
189 }
190
191 if (nodeDescriptor instanceof AttributeDescriptor) {
192
193 nodeDescriptor.setLocalName(
194 introspector.getAttributeNameMapper().mapTypeToElementName( name ) );
195 } else {
196 nodeDescriptor.setLocalName(
197 introspector.getElementNameMapper().mapTypeToElementName( name ) );
198 }
199
200 nodeDescriptor.setPropertyName( propertyDescriptor.getName() );
201 nodeDescriptor.setPropertyType( type );
202
203
204
205
206
207 if (log.isTraceEnabled()) {
208 log.trace("Created descriptor:");
209 log.trace(nodeDescriptor);
210 }
211 return nodeDescriptor;
212 }
213
214
215
216
217
218
219
220
221
222 public static void configureProperty(
223 ElementDescriptor elementDescriptor,
224 PropertyDescriptor propertyDescriptor ) {
225
226 configureProperty( elementDescriptor, propertyDescriptor, null, null);
227 }
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242 public static void configureProperty(
243 ElementDescriptor elementDescriptor,
244 PropertyDescriptor propertyDescriptor,
245 String updateMethodName,
246 Class beanClass ) {
247
248 Class type = propertyDescriptor.getPropertyType();
249 Method readMethod = propertyDescriptor.getReadMethod();
250 Method writeMethod = propertyDescriptor.getWriteMethod();
251
252 elementDescriptor.setLocalName( propertyDescriptor.getName() );
253 elementDescriptor.setPropertyType( type );
254
255
256
257
258
259 if ( readMethod == null ) {
260 log.trace( "No read method" );
261 return;
262 }
263
264 if ( log.isTraceEnabled() ) {
265 log.trace( "Read method=" + readMethod.getName() );
266 }
267
268
269
270
271 if ( Class.class.equals( type ) && "class".equals( propertyDescriptor.getName() ) ) {
272 log.trace( "Ignoring class property" );
273 return;
274 }
275 if ( isPrimitiveType( type ) ) {
276 elementDescriptor.setTextExpression( new MethodExpression( readMethod ) );
277
278 } else if ( isLoopType( type ) ) {
279 log.trace("Loop type ??");
280
281
282
283 elementDescriptor.setContextExpression(
284 new IteratorExpression( new MethodExpression( readMethod ) )
285 );
286
287 writeMethod = null;
288 } else {
289 log.trace( "Standard property" );
290 elementDescriptor.setContextExpression( new MethodExpression( readMethod ) );
291 }
292
293
294 if (updateMethodName == null) {
295
296 if ( writeMethod != null ) {
297 elementDescriptor.setUpdater( new MethodUpdater( writeMethod ) );
298 }
299
300 } else {
301
302 if ( log.isTraceEnabled() ) {
303 log.trace( "Finding custom method: " );
304 log.trace( " on:" + beanClass );
305 log.trace( " name:" + updateMethodName );
306 }
307
308 Method updateMethod = null;
309 Method[] methods = beanClass.getMethods();
310 for ( int i = 0, size = methods.length; i < size; i++ ) {
311 Method method = methods[i];
312 if ( updateMethodName.equals( method.getName() ) ) {
313
314
315 if (methods[i].getParameterTypes().length == 1) {
316
317 updateMethod = methods[i];
318 if ( log.isTraceEnabled() ) {
319 log.trace("Matched method:" + updateMethod);
320 }
321
322 break;
323 }
324 }
325 }
326
327 if (updateMethod == null) {
328 if ( log.isInfoEnabled() ) {
329
330 log.info("No method with name '" + updateMethodName + "' found for update");
331 }
332 } else {
333
334 elementDescriptor.setUpdater( new MethodUpdater( updateMethod ) );
335 elementDescriptor.setSingularPropertyType( updateMethod.getParameterTypes()[0] );
336 if ( log.isTraceEnabled() ) {
337 log.trace( "Set custom updater on " + elementDescriptor);
338 }
339 }
340 }
341 }
342
343
344
345
346
347
348
349
350 public static void configureProperty(
351 AttributeDescriptor attributeDescriptor,
352 PropertyDescriptor propertyDescriptor ) {
353 Class type = propertyDescriptor.getPropertyType();
354 Method readMethod = propertyDescriptor.getReadMethod();
355 Method writeMethod = propertyDescriptor.getWriteMethod();
356
357 if ( readMethod == null ) {
358 log.trace( "No read method" );
359 return;
360 }
361
362 if ( log.isTraceEnabled() ) {
363 log.trace( "Read method=" + readMethod );
364 }
365
366
367
368
369 if ( Class.class.equals( type ) && "class".equals( propertyDescriptor.getName() ) ) {
370 log.trace( "Ignoring class property" );
371 return;
372 }
373 if ( isLoopType( type ) ) {
374 log.warn( "Using loop type for an attribute. Type = "
375 + type.getName() + " attribute: " + attributeDescriptor.getQualifiedName() );
376 }
377
378 log.trace( "Standard property" );
379 attributeDescriptor.setTextExpression( new MethodExpression( readMethod ) );
380
381 if ( writeMethod != null ) {
382 attributeDescriptor.setUpdater( new MethodUpdater( writeMethod ) );
383 }
384
385 attributeDescriptor.setLocalName( propertyDescriptor.getName() );
386 attributeDescriptor.setPropertyType( type );
387
388
389
390
391 }
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413 public static void defaultAddMethods(
414 XMLIntrospector introspector,
415 ElementDescriptor rootDescriptor,
416 Class beanClass ) {
417
418
419 if ( beanClass != null ) {
420 Method[] methods = beanClass.getMethods();
421 for ( int i = 0, size = methods.length; i < size; i++ ) {
422 Method method = methods[i];
423 String name = method.getName();
424 if ( name.startsWith( "add" ) ) {
425
426
427 Class[] types = method.getParameterTypes();
428 if ( types != null) {
429 if ( log.isTraceEnabled() ) {
430 log.trace("Searching for match for " + method);
431 }
432
433 if ( ( types.length == 1 ) || types.length == 2 ) {
434 String propertyName = Introspector.decapitalize( name.substring(3) );
435 if (propertyName.length() == 0)
436 continue;
437 if ( log.isTraceEnabled() ) {
438 log.trace( name + "->" + propertyName );
439 }
440
441
442
443
444
445 ElementDescriptor descriptor =
446 findGetCollectionDescriptor(
447 introspector,
448 rootDescriptor,
449 propertyName );
450
451 if ( log.isDebugEnabled() ) {
452 log.debug( "!! " + propertyName + " -> " + descriptor );
453 log.debug( "!! " + name + " -> "
454 + (descriptor!=null?descriptor.getPropertyName():"") );
455 }
456 if ( descriptor != null ) {
457 boolean isMapDescriptor
458 = Map.class.isAssignableFrom( descriptor.getPropertyType() );
459 if ( !isMapDescriptor && types.length == 1 ) {
460
461 log.trace("Matching collection or iteration");
462
463 descriptor.setUpdater( new MethodUpdater( method ) );
464 descriptor.setSingularPropertyType( types[0] );
465
466 if ( log.isDebugEnabled() ) {
467 log.debug( "!! " + method);
468 log.debug( "!! " + types[0]);
469 }
470
471
472 ElementDescriptor[] children
473 = descriptor.getElementDescriptors();
474 if ( children != null && children.length > 0 ) {
475 ElementDescriptor child = children[0];
476 String localName = child.getLocalName();
477 if ( localName == null || localName.length() == 0 ) {
478 child.setLocalName(
479 introspector.getElementNameMapper()
480 .mapTypeToElementName( propertyName ) );
481 }
482 }
483
484 } else if ( isMapDescriptor && types.length == 2 ) {
485
486 log.trace("Matching map");
487 ElementDescriptor[] children
488 = descriptor.getElementDescriptors();
489
490 if ( children.length == 0 ) {
491
492 log.info(
493 "'entry' descriptor is missing for map. "
494 + "Updaters cannot be set");
495
496 } else {
497
498
499 ElementDescriptor[] grandchildren
500 = children[0].getElementDescriptors();
501 MapEntryAdder adder = new MapEntryAdder(method);
502 for (
503 int n=0,
504 noOfGrandChildren = grandchildren.length;
505 n < noOfGrandChildren;
506 n++ ) {
507 if ( "key".equals(
508 grandchildren[n].getLocalName() ) ) {
509
510 grandchildren[n].setUpdater(
511 adder.getKeyUpdater() );
512 grandchildren[n].setSingularPropertyType(
513 types[0] );
514 if ( log.isTraceEnabled() ) {
515 log.trace(
516 "Key descriptor: " + grandchildren[n]);
517 }
518
519 } else if (
520 "value".equals(
521 grandchildren[n].getLocalName() ) ) {
522
523 grandchildren[n].setUpdater(
524 adder.getValueUpdater() );
525 grandchildren[n].setSingularPropertyType(
526 types[1] );
527 if ( log.isTraceEnabled() ) {
528 log.trace(
529 "Value descriptor: " + grandchildren[n]);
530 }
531 }
532 }
533 }
534 }
535 } else {
536 if ( log.isDebugEnabled() ) {
537 log.debug(
538 "Could not find an ElementDescriptor with property name: "
539 + propertyName + " to attach the add method: " + method
540 );
541 }
542 }
543 }
544 }
545 }
546 }
547 }
548 }
549
550
551
552
553
554
555
556
557 public static boolean isLoopType(Class type) {
558
559 if (type == null) {
560 log.trace("isLoopType: type is null");
561 return false;
562 }
563 return type.isArray()
564 || Map.class.isAssignableFrom( type )
565 || Collection.class.isAssignableFrom( type )
566 || Enumeration.class.isAssignableFrom( type )
567 || Iterator.class.isAssignableFrom( type );
568 }
569
570
571
572
573
574
575
576
577
578
579
580
581
582 public static boolean isPrimitiveType(Class type) {
583 if ( type == null ) {
584 return false;
585
586 } else if ( type.isPrimitive() ) {
587 return true;
588
589 } else if ( type.equals( Object.class ) ) {
590 return false;
591 }
592 return type.getName().startsWith( "java.lang." )
593 || Number.class.isAssignableFrom( type )
594 || String.class.isAssignableFrom( type )
595 || Date.class.isAssignableFrom( type )
596 || java.sql.Date.class.isAssignableFrom( type )
597 || java.sql.Time.class.isAssignableFrom( type )
598 || java.sql.Timestamp.class.isAssignableFrom( type )
599 || java.math.BigDecimal.class.isAssignableFrom( type )
600 || java.math.BigInteger.class.isAssignableFrom( type );
601 }
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619 protected static ElementDescriptor findGetCollectionDescriptor(
620 XMLIntrospector introspector,
621 ElementDescriptor rootDescriptor,
622 String propertyName ) {
623
624 Map map = new HashMap();
625
626 if ( log.isTraceEnabled() ) {
627 log.trace( "findPluralDescriptor( " + propertyName
628 + " ):root property name=" + rootDescriptor.getPropertyName() );
629 }
630
631 if (rootDescriptor.getPropertyName() != null) {
632 map.put(propertyName, rootDescriptor);
633 }
634 makeElementDescriptorMap( rootDescriptor, map );
635
636 PluralStemmer stemmer = introspector.getPluralStemmer();
637 ElementDescriptor elementDescriptor = stemmer.findPluralDescriptor( propertyName, map );
638
639 if ( log.isTraceEnabled() ) {
640 log.trace(
641 "findPluralDescriptor( " + propertyName
642 + " ):ElementDescriptor=" + elementDescriptor );
643 }
644
645 return elementDescriptor;
646 }
647
648
649
650
651
652
653
654
655
656 protected static void makeElementDescriptorMap( ElementDescriptor rootDescriptor, Map map ) {
657 ElementDescriptor[] children = rootDescriptor.getElementDescriptors();
658 if ( children != null ) {
659 for ( int i = 0, size = children.length; i < size; i++ ) {
660 ElementDescriptor child = children[i];
661 String propertyName = child.getPropertyName();
662 if ( propertyName != null ) {
663 map.put( propertyName, child );
664 }
665 makeElementDescriptorMap( child, map );
666 }
667 }
668 }
669
670
671
672
673
674
675
676
677
678
679 protected static void swapDescriptor(
680 ElementDescriptor rootDescriptor,
681 ElementDescriptor oldValue,
682 ElementDescriptor newValue ) {
683 ElementDescriptor[] children = rootDescriptor.getElementDescriptors();
684 if ( children != null ) {
685 for ( int i = 0, size = children.length; i < size; i++ ) {
686 ElementDescriptor child = children[i];
687 if ( child == oldValue ) {
688 children[i] = newValue;
689 break;
690 }
691 swapDescriptor( child, oldValue, newValue );
692 }
693 }
694 }
695 }