1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.betwixt.io;
18
19 import java.util.HashMap;
20 import java.util.List;
21 import java.util.Map;
22
23 import org.apache.commons.betwixt.AttributeDescriptor;
24 import org.apache.commons.betwixt.ElementDescriptor;
25 import org.apache.commons.betwixt.XMLBeanInfo;
26 import org.apache.commons.betwixt.XMLIntrospector;
27 import org.apache.commons.betwixt.digester.XMLIntrospectorHelper;
28 import org.apache.commons.betwixt.expression.Context;
29 import org.apache.commons.betwixt.expression.MethodUpdater;
30 import org.apache.commons.betwixt.expression.Updater;
31 import org.apache.commons.digester.Rule;
32 import org.apache.commons.digester.Rules;
33 import org.apache.commons.logging.Log;
34 import org.apache.commons.logging.LogFactory;
35 import org.xml.sax.Attributes;
36
37
38
39
40
41
42
43
44
45 public class BeanCreateRule extends Rule {
46
47
48 private static Log log = LogFactory.getLog( BeanCreateRule.class );
49
50
51
52
53
54 public static void setLog(Log aLog) {
55 log = aLog;
56 }
57
58
59 private ElementDescriptor descriptor;
60
61 private Context context;
62
63 private boolean addedChildren;
64
65 private boolean createdBean;
66
67 private Class beanClass;
68
69 private String pathPrefix;
70
71 private boolean matchIDs = true;
72
73 private String classNameAttribute = "className";
74
75
76
77
78
79
80
81
82 public BeanCreateRule(
83 ElementDescriptor descriptor,
84 Class beanClass,
85 String pathPrefix ) {
86 this( descriptor, beanClass, pathPrefix, true );
87 }
88
89
90
91
92
93
94
95
96
97 public BeanCreateRule(
98 ElementDescriptor descriptor,
99 Class beanClass,
100 String pathPrefix,
101 boolean matchIDs ) {
102 this(
103 descriptor,
104 beanClass,
105 new Context(),
106 pathPrefix,
107 matchIDs);
108 }
109
110
111
112
113
114
115
116 public BeanCreateRule( ElementDescriptor descriptor, Class beanClass ) {
117 this( descriptor, beanClass, true );
118 }
119
120
121
122
123
124
125
126
127 public BeanCreateRule( ElementDescriptor descriptor, Class beanClass, boolean matchIDs ) {
128 this( descriptor, beanClass, descriptor.getQualifiedName() + "/" , matchIDs );
129 }
130
131
132
133
134
135
136
137
138 public BeanCreateRule(
139 ElementDescriptor descriptor,
140 Context context,
141 String pathPrefix ) {
142 this( descriptor, context, pathPrefix, true );
143 }
144
145
146
147
148
149
150
151
152
153 public BeanCreateRule(
154 ElementDescriptor descriptor,
155 Context context,
156 String pathPrefix,
157 boolean matchIDs ) {
158 this(
159 descriptor,
160 descriptor.getSingularPropertyType(),
161 context,
162 pathPrefix,
163 matchIDs );
164 }
165
166
167
168
169
170
171
172
173
174
175 private BeanCreateRule(
176 ElementDescriptor descriptor,
177 Class beanClass,
178 Context context,
179 String pathPrefix,
180 boolean matchIDs ) {
181 this.descriptor = descriptor;
182 this.context = context;
183 this.beanClass = beanClass;
184 this.pathPrefix = pathPrefix;
185 this.matchIDs = matchIDs;
186 if (log.isTraceEnabled()) {
187 log.trace("Created bean create rule");
188 log.trace("Descriptor=" + descriptor);
189 log.trace("Class=" + beanClass);
190 log.trace("Path prefix=" + pathPrefix);
191 }
192 }
193
194
195
196
197
198
199
200
201
202
203
204 public void begin(Attributes attributes) {
205 log.debug( "Called with descriptor: " + descriptor
206 + " propertyType: " + descriptor.getPropertyType() );
207
208 if (log.isTraceEnabled()) {
209 int attributesLength = attributes.getLength();
210 if (attributesLength > 0) {
211 log.trace("Attributes:");
212 }
213 for (int i=0, size=attributesLength; i<size; i++) {
214 log.trace("Local:" + attributes.getLocalName(i));
215 log.trace("URI:" + attributes.getURI(i));
216 log.trace("QName:" + attributes.getQName(i));
217 }
218 }
219
220
221
222
223
224
225 createdBean = false;
226
227 Object instance = null;
228 if ( beanClass != null ) {
229 instance = createBean(attributes);
230 if ( instance != null ) {
231 createdBean = true;
232
233 context.setBean( instance );
234 digester.push(instance);
235
236
237
238
239
240 ElementDescriptor typeDescriptor = getElementDescriptor( descriptor );
241
242
243
244 AttributeDescriptor[] attributeDescriptors
245 = typeDescriptor.getAttributeDescriptors();
246 if ( attributeDescriptors != null ) {
247 for ( int i = 0, size = attributeDescriptors.length; i < size; i++ ) {
248 AttributeDescriptor attributeDescriptor = attributeDescriptors[i];
249
250
251
252
253
254 String value = attributes.getValue(
255 attributeDescriptor.getURI(),
256 attributeDescriptor.getLocalName()
257 );
258
259 if (value == null) {
260 value = attributes.getValue(attributeDescriptor.getQualifiedName());
261 }
262
263 if (log.isTraceEnabled()) {
264 log.trace("Attr URL:" + attributeDescriptor.getURI());
265 log.trace("Attr LocalName:" + attributeDescriptor.getLocalName() );
266 log.trace(value);
267 }
268
269 Updater updater = attributeDescriptor.getUpdater();
270 log.trace(updater);
271 if ( updater != null && value != null ) {
272 updater.update( context, value );
273 }
274 }
275 }
276
277 addChildRules();
278
279
280 if ( matchIDs ) {
281
282
283
284 String id = attributes.getValue( "id" );
285 if ( id != null ) {
286 getBeansById().put( id, instance );
287 }
288 }
289 }
290 }
291 }
292
293
294
295
296 public void end() {
297 if ( createdBean ) {
298
299
300 Updater updater = descriptor.getUpdater();
301 Object instance = context.getBean();
302
303 Object top = digester.pop();
304 if (digester.getCount() == 0) {
305 context.setBean(null);
306 }else{
307 context.setBean( digester.peek() );
308 }
309
310 if ( updater != null ) {
311 if ( log.isDebugEnabled() ) {
312 log.debug( "Calling updater for: " + descriptor + " with: "
313 + instance + " on bean: " + context.getBean() );
314 }
315 updater.update( context, instance );
316 } else {
317 if ( log.isDebugEnabled() ) {
318 log.debug( "No updater for: " + descriptor + " with: "
319 + instance + " on bean: " + context.getBean() );
320 }
321 }
322 }
323 }
324
325
326
327
328 public void finish() {}
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343 public String getClassNameAttribute() {
344 return classNameAttribute;
345 }
346
347
348
349
350
351
352
353
354
355
356 public void setClassNameAttribute(String classNameAttribute) {
357 this.classNameAttribute = classNameAttribute;
358 }
359
360
361
362
363
364
365
366
367
368
369 protected Object createBean(Attributes attributes) {
370
371
372
373
374
375
376 if ( matchIDs ) {
377 String idref = attributes.getValue( "idref" );
378 if ( idref != null ) {
379
380
381
382 log.trace( "Found IDREF" );
383 Object bean = getBeansById().get( idref );
384 if ( bean != null ) {
385 if (log.isTraceEnabled()) {
386 log.trace( "Matched bean " + bean );
387 }
388 return bean;
389 }
390 log.trace( "No match found" );
391 }
392 }
393
394 Class theClass = beanClass;
395 try {
396
397 String className = attributes.getValue(classNameAttribute);
398 if (className != null) {
399
400 theClass = getDigester().getClassLoader().loadClass(className);
401 }
402 if (log.isTraceEnabled()) {
403 log.trace( "Creating instance of " + theClass );
404 }
405 return theClass.newInstance();
406
407 } catch (Exception e) {
408 log.warn( "Could not create instance of type: " + theClass.getName() );
409 return null;
410 }
411 }
412
413
414 protected void addChildRules() {
415 if ( ! addedChildren ) {
416 addedChildren = true;
417
418 addChildRules( pathPrefix, descriptor );
419 }
420 }
421
422
423
424
425
426
427
428 protected void addChildRules(String prefix, ElementDescriptor currentDescriptor ) {
429
430 if (log.isTraceEnabled()) {
431 log.trace("Adding child rules for " + currentDescriptor + "@" + prefix);
432 }
433
434
435
436
437 ElementDescriptor typeDescriptor = getElementDescriptor( currentDescriptor );
438
439
440
441 ElementDescriptor[] childDescriptors = typeDescriptor.getElementDescriptors();
442 if ( childDescriptors != null ) {
443 for ( int i = 0, size = childDescriptors.length; i < size; i++ ) {
444 final ElementDescriptor childDescriptor = childDescriptors[i];
445 if (log.isTraceEnabled()) {
446 log.trace("Processing child " + childDescriptor);
447 }
448
449 String qualifiedName = childDescriptor.getQualifiedName();
450 if ( qualifiedName == null ) {
451 log.trace( "Ignoring" );
452 continue;
453 }
454 String path = prefix + qualifiedName;
455
456
457
458 if ( qualifiedName.equals( currentDescriptor.getQualifiedName() )
459 && currentDescriptor.getPropertyName() != null ) {
460 log.trace("Creating generic rule for recursive elements");
461 int index = -1;
462 if (childDescriptor.isWrapCollectionsInElement()) {
463 index = prefix.indexOf(qualifiedName);
464 if (index == -1) {
465
466 log.debug( "Oops - this shouldn't happen" );
467 continue;
468 }
469 int removeSlash = prefix.endsWith("/")?1:0;
470 path = "*/" + prefix.substring(index, prefix.length()-removeSlash);
471 }else{
472
473 ElementDescriptor[] desc = currentDescriptor.getElementDescriptors();
474 if (desc.length == 1) {
475 path = "*/"+desc[0].getQualifiedName();
476 }
477 }
478 Rule rule = new BeanCreateRule( childDescriptor, context, path, matchIDs);
479 addRule(path, rule);
480 continue;
481 }
482 if ( childDescriptor.getUpdater() != null ) {
483 if (log.isTraceEnabled()) {
484 log.trace("Element has updater "
485 + ((MethodUpdater) childDescriptor.getUpdater()).getMethod().getName());
486 }
487 if ( childDescriptor.isPrimitiveType() ) {
488 addPrimitiveTypeRule(path, childDescriptor);
489
490 } else {
491
492 ElementDescriptor[] grandChildren = childDescriptor.getElementDescriptors();
493 if ( grandChildren != null && grandChildren.length > 0 ) {
494 ElementDescriptor grandChild = grandChildren[0];
495 String grandChildQName = grandChild.getQualifiedName();
496 if ( grandChildQName != null && grandChildQName.length() > 0 ) {
497 if (childDescriptor.isWrapCollectionsInElement()) {
498 path += '/' + grandChildQName;
499
500 } else {
501 path = prefix + (prefix.endsWith("/")?"":"/") + grandChildQName;
502 }
503 }
504 }
505
506
507 Class beanClass = childDescriptor.getSingularPropertyType();
508 if ( XMLIntrospectorHelper.isPrimitiveType( beanClass ) ) {
509 addPrimitiveTypeRule(path, childDescriptor);
510
511 } else {
512 Rule rule = new BeanCreateRule(
513 childDescriptor,
514 context,
515 path + '/',
516 matchIDs );
517 addRule( path, rule );
518 }
519 }
520 } else {
521 log.trace("Element does not have updater");
522 }
523
524 ElementDescriptor[] grandChildren = childDescriptor.getElementDescriptors();
525 if ( grandChildren != null && grandChildren.length > 0 ) {
526 log.trace("Adding grand children");
527 addChildRules( path + '/', childDescriptor );
528 }
529 }
530 }
531 }
532
533
534
535
536
537
538 protected BeanReader getBeanReader() {
539
540
541 return (BeanReader) getDigester();
542 }
543
544
545
546
547
548
549
550
551
552 protected ElementDescriptor getElementDescriptor( ElementDescriptor propertyDescriptor ) {
553 Class beanClass = propertyDescriptor.getSingularPropertyType();
554 if ( beanClass != null ) {
555 XMLIntrospector introspector = getBeanReader().getXMLIntrospector();
556 try {
557 XMLBeanInfo xmlInfo = introspector.introspect( beanClass );
558 return xmlInfo.getElementDescriptor();
559
560 } catch (Exception e) {
561 log.warn( "Could not introspect class: " + beanClass, e );
562 }
563 }
564
565 return propertyDescriptor;
566 }
567
568
569
570
571
572
573
574 protected void addPrimitiveTypeRule(String path, final ElementDescriptor childDescriptor) {
575 Rule rule = new Rule() {
576 public void body(String text) throws Exception {
577 childDescriptor.getUpdater().update( context, text );
578 }
579 };
580 addRule( path, rule );
581 }
582
583
584
585
586
587
588
589 protected void addRule(String path, Rule rule) {
590 Rules rules = digester.getRules();
591 List matches = rules.match(null, path);
592 if ( matches.isEmpty() ) {
593 if ( log.isDebugEnabled() ) {
594 log.debug( "Adding digester rule for path: " + path + " rule: " + rule );
595 }
596 digester.addRule( path, rule );
597
598 } else {
599 if ( log.isDebugEnabled() ) {
600 log.debug( "Ignoring duplicate digester rule for path: "
601 + path + " rule: " + rule );
602 log.debug( "New rule (not added): " + rule );
603 log.debug( "Existing rule:" + matches.get(0) );
604 }
605 }
606 }
607
608
609
610
611
612
613
614 protected Map getBeansById() {
615
616
617
618
619
620 Map beansById = (Map) context.getVariable( "beans-index" );
621 if ( beansById == null ) {
622
623 beansById = new HashMap();
624 context.setVariable( "beans-index", beansById );
625 log.trace( "Created new index-by-id map" );
626 }
627
628 return beansById;
629 }
630
631
632
633
634
635
636 public String toString() {
637 return "BeanCreateRule [path prefix=" + pathPrefix + " descriptor=" + descriptor + "]";
638 }
639
640 }