| 1 package org.apache.commons.betwixt; 2 3 18 19 import java.beans.BeanDescriptor ; 20 import java.beans.BeanInfo ; 21 import java.beans.IntrospectionException ; 22 import java.beans.Introspector ; 23 import java.beans.PropertyDescriptor ; 24 import java.lang.reflect.Method ; 25 import java.net.URL ; 26 import java.util.ArrayList ; 27 import java.util.HashMap ; 28 import java.util.Iterator ; 29 import java.util.List ; 30 import java.util.Map ; 31 32 import org.apache.commons.beanutils.DynaBean; 33 import org.apache.commons.beanutils.DynaClass; 34 import org.apache.commons.beanutils.DynaProperty; 35 import org.apache.commons.betwixt.digester.XMLBeanInfoDigester; 36 import org.apache.commons.betwixt.digester.XMLIntrospectorHelper; 37 import org.apache.commons.betwixt.expression.EmptyExpression; 38 import org.apache.commons.betwixt.expression.IteratorExpression; 39 import org.apache.commons.betwixt.expression.MapEntryAdder; 40 import org.apache.commons.betwixt.expression.MethodUpdater; 41 import org.apache.commons.betwixt.expression.StringExpression; 42 import org.apache.commons.betwixt.registry.DefaultXMLBeanInfoRegistry; 43 import org.apache.commons.betwixt.registry.XMLBeanInfoRegistry; 44 import org.apache.commons.betwixt.strategy.ClassNormalizer; 45 import org.apache.commons.betwixt.strategy.DefaultNameMapper; 46 import org.apache.commons.betwixt.strategy.DefaultPluralStemmer; 47 import org.apache.commons.betwixt.strategy.NameMapper; 48 import org.apache.commons.betwixt.strategy.PluralStemmer; 49 import org.apache.commons.betwixt.strategy.TypeBindingStrategy; 50 import org.apache.commons.logging.Log; 51 import org.apache.commons.logging.LogFactory; 52 53 72 public class XMLIntrospector { 73 77 protected Log log = LogFactory.getLog( XMLIntrospector.class ); 78 79 80 private XMLBeanInfoRegistry registry = new DefaultXMLBeanInfoRegistry(); 81 82 83 private XMLBeanInfoDigester digester; 84 85 86 private IntrospectionConfiguration configuration; 87 88 89 public XMLIntrospector() { 90 this(new IntrospectionConfiguration()); 91 } 92 93 99 public XMLIntrospector(IntrospectionConfiguration configuration) { 100 setConfiguration(configuration); 101 } 102 103 104 107 111 public Log getLog() { 112 return getConfiguration().getIntrospectionLog(); 113 } 114 115 119 public void setLog(Log log) { 120 getConfiguration().setIntrospectionLog(log); 121 } 122 123 135 public XMLBeanInfoRegistry getRegistry() { 136 return registry; 137 } 138 139 151 public void setRegistry(XMLBeanInfoRegistry registry) { 152 this.registry = registry; 153 } 154 155 163 public IntrospectionConfiguration getConfiguration() { 164 return configuration; 165 } 166 167 175 public void setConfiguration(IntrospectionConfiguration configuration) { 176 this.configuration = configuration; 177 } 178 179 180 190 public ClassNormalizer getClassNormalizer() { 191 return getConfiguration().getClassNormalizer(); 192 } 193 194 205 public void setClassNormalizer(ClassNormalizer classNormalizer) { 206 getConfiguration().setClassNormalizer(classNormalizer); 207 } 208 209 215 public boolean isCachingEnabled() { 216 return true; 217 } 218 219 225 public void setCachingEnabled(boolean cachingEnabled) { 226 } 228 229 230 235 public boolean isAttributesForPrimitives() { 236 return getConfiguration().isAttributesForPrimitives(); 237 } 238 239 245 public void setAttributesForPrimitives(boolean attributesForPrimitives) { 246 getConfiguration().setAttributesForPrimitives(attributesForPrimitives); 247 } 248 249 255 public boolean isWrapCollectionsInElement() { 256 return getConfiguration().isWrapCollectionsInElement(); 257 } 258 259 266 public void setWrapCollectionsInElement(boolean wrapCollectionsInElement) { 267 getConfiguration().setWrapCollectionsInElement(wrapCollectionsInElement); 268 } 269 270 276 public PluralStemmer getPluralStemmer() { 277 return getConfiguration().getPluralStemmer(); 278 } 279 280 286 public void setPluralStemmer(PluralStemmer pluralStemmer) { 287 getConfiguration().setPluralStemmer(pluralStemmer); 288 } 289 290 297 public NameMapper getNameMapper() { 298 return getElementNameMapper(); 299 } 300 301 307 public void setNameMapper(NameMapper nameMapper) { 308 setElementNameMapper(nameMapper); 309 } 310 311 312 319 public NameMapper getElementNameMapper() { 320 return getConfiguration().getElementNameMapper(); 321 } 322 323 328 public void setElementNameMapper(NameMapper nameMapper) { 329 getConfiguration().setElementNameMapper( nameMapper ); 330 } 331 332 333 340 public NameMapper getAttributeNameMapper() { 341 return getConfiguration().getAttributeNameMapper(); 342 } 343 344 345 350 public void setAttributeNameMapper(NameMapper nameMapper) { 351 getConfiguration().setAttributeNameMapper( nameMapper ); 352 } 353 354 361 public boolean useBeanInfoSearchPath() { 362 return getConfiguration().useBeanInfoSearchPath(); 363 } 364 365 371 public void setUseBeanInfoSearchPath(boolean useBeanInfoSearchPath) { 372 getConfiguration().setUseBeanInfoSearchPath( useBeanInfoSearchPath ); 373 } 374 375 378 383 public void flushCache() {} 384 385 386 394 public XMLBeanInfo introspect(Object bean) throws IntrospectionException { 395 if (getLog().isDebugEnabled()) { 396 getLog().debug( "Introspecting..." ); 397 getLog().debug(bean); 398 } 399 400 if ( bean instanceof DynaBean ) { 401 XMLBeanInfo xmlBeanInfo = findByXMLDescriptor( bean.getClass() ); 403 if (xmlBeanInfo != null) { 404 return xmlBeanInfo; 405 } 406 return introspect( ((DynaBean) bean).getDynaClass() ); 408 409 } else { 410 Class normalClass = getClassNormalizer().getNormalizedClass( bean ); 412 return introspect( normalClass ); 413 } 414 } 415 416 424 public XMLBeanInfo introspect(DynaClass dynaClass) { 425 426 431 XMLBeanInfo xmlInfo = createXMLBeanInfo( dynaClass ); 433 434 DynaClassBeanType beanClass = new DynaClassBeanType( dynaClass ); 436 populate( xmlInfo, beanClass ); 437 438 return xmlInfo; 439 } 440 441 449 public XMLBeanInfo introspect(Class aClass) throws IntrospectionException { 450 String [] searchPath = null; 452 if ( !getConfiguration().useBeanInfoSearchPath() ) { 453 searchPath = Introspector.getBeanInfoSearchPath(); 454 Introspector.setBeanInfoSearchPath(new String [] { }); 455 } 456 457 XMLBeanInfo xmlInfo = registry.get( aClass ); 458 459 if ( xmlInfo == null ) { 460 if ( getLog().isDebugEnabled() ) { 462 getLog().debug( "Attempting to lookup an XML descriptor for class: " + aClass ); 463 } 464 465 xmlInfo = findByXMLDescriptor( aClass ); 466 if ( xmlInfo == null ) { 467 BeanInfo info = Introspector.getBeanInfo( aClass ); 468 xmlInfo = introspect( info ); 469 } 470 471 if ( xmlInfo != null ) { 472 registry.put( aClass, xmlInfo ); 473 } 474 } else { 475 getLog().trace( "Used cached XMLBeanInfo." ); 476 } 477 478 if ( getLog().isTraceEnabled() ) { 479 getLog().trace( xmlInfo ); 480 } 481 if ( !getConfiguration().useBeanInfoSearchPath() ) { 482 Introspector.setBeanInfoSearchPath( searchPath ); 484 } 485 486 return xmlInfo; 487 } 488 489 497 public XMLBeanInfo introspect(BeanInfo beanInfo) throws IntrospectionException { 498 XMLBeanInfo xmlBeanInfo = createXMLBeanInfo( beanInfo ); 499 populate( xmlBeanInfo, new JavaBeanType( beanInfo ) ); 500 return xmlBeanInfo; 501 } 502 503 509 private void populate(XMLBeanInfo xmlBeanInfo, BeanType bean) { 510 String name = bean.getBeanName(); 511 512 ElementDescriptor elementDescriptor = new ElementDescriptor(); 513 elementDescriptor.setLocalName( 514 getElementNameMapper().mapTypeToElementName( name ) ); 515 elementDescriptor.setPropertyType( bean.getElementType() ); 516 517 if (getLog().isTraceEnabled()) { 518 getLog().trace("Populating:" + bean); 519 } 520 521 if ( bean.isPrimitiveType() ) { 523 getLog().trace("Bean is primitive"); 524 elementDescriptor.setTextExpression( StringExpression.getInstance() ); 525 526 } else if ( bean.isLoopType() ) { 527 getLog().trace("Bean is loop"); 528 ElementDescriptor loopDescriptor = new ElementDescriptor(); 529 loopDescriptor.setContextExpression( 530 new IteratorExpression( EmptyExpression.getInstance() ) 531 ); 532 if ( bean.isMapType() ) { 533 loopDescriptor.setQualifiedName( "entry" ); 534 } 535 elementDescriptor.setElementDescriptors( new ElementDescriptor[] { loopDescriptor } ); 536 537 } else { 538 getLog().trace("Bean is standard type"); 539 List elements = new ArrayList (); 540 List attributes = new ArrayList (); 541 List contents = new ArrayList (); 542 543 addProperties( bean.getProperties(), elements, attributes, contents ); 544 545 int size = elements.size(); 546 if ( size > 0 ) { 547 ElementDescriptor[] descriptors = new ElementDescriptor[size]; 548 elements.toArray( descriptors ); 549 elementDescriptor.setElementDescriptors( descriptors ); 550 } 551 size = attributes.size(); 552 if ( size > 0 ) { 553 AttributeDescriptor[] descriptors = new AttributeDescriptor[size]; 554 attributes.toArray( descriptors ); 555 elementDescriptor.setAttributeDescriptors( descriptors ); 556 } 557 size = contents.size(); 558 if ( size > 0 ) { 559 if ( size > 0 ) { 560 Descriptor[] descriptors = new Descriptor[size]; 561 contents.toArray( descriptors ); 562 elementDescriptor.setContentDescriptors( descriptors ); 563 } 564 } 565 } 566 567 xmlBeanInfo.setElementDescriptor( elementDescriptor ); 568 569 defaultAddMethods( elementDescriptor, bean.getElementType() ); 571 572 if (getLog().isTraceEnabled()) { 573 getLog().trace("Populated descriptor:"); 574 getLog().trace(elementDescriptor); 575 } 576 } 577 578 579 587 protected XMLBeanInfo createXMLBeanInfo(DynaClass dynaClass) { 588 XMLBeanInfo beanInfo = new XMLBeanInfo(dynaClass.getClass()); 590 return beanInfo; 591 } 592 593 594 595 596 607 public Descriptor createDescriptor( 608 PropertyDescriptor propertyDescriptor, 609 boolean useAttributesForPrimitives 610 ) throws IntrospectionException { 611 return createXMLDescriptor( new BeanProperty( propertyDescriptor ) ); 612 } 613 614 623 public Descriptor createXMLDescriptor( BeanProperty beanProperty ) { 624 return beanProperty.createXMLDescriptor( configuration ); 625 } 626 627 628 655 public void defaultAddMethods( 656 ElementDescriptor rootDescriptor, 657 Class beanClass ) { 658 659 if ( beanClass != null ) { 662 ArrayList singleParameterAdders = new ArrayList (); 663 ArrayList twinParameterAdders = new ArrayList (); 664 665 Method [] methods = beanClass.getMethods(); 666 for ( int i = 0, size = methods.length; i < size; i++ ) { 667 Method method = methods[i]; 668 String name = method.getName(); 669 if ( name.startsWith( "add" )) { 670 Class [] types = method.getParameterTypes(); 673 if ( types != null) { 674 if ( getLog().isTraceEnabled() ) { 675 getLog().trace("Searching for match for " + method); 676 } 677 678 switch (types.length) 679 { 680 case 1: 681 singleParameterAdders.add(method); 682 break; 683 case 2: 684 twinParameterAdders.add(method); 685 break; 686 default: 687 break; 689 } 690 } 691 } 692 } 693 694 Map elementsByPropertyName = makeElementDescriptorMap( rootDescriptor ); 695 696 for (Iterator it=singleParameterAdders.iterator();it.hasNext();) { 697 Method singleParameterAdder = (Method ) it.next(); 698 setIteratorAdder(elementsByPropertyName, singleParameterAdder); 699 } 700 701 for (Iterator it=twinParameterAdders.iterator();it.hasNext();) { 702 Method twinParameterAdder = (Method ) it.next(); 703 setMapAdder(elementsByPropertyName, twinParameterAdder); 704 } 705 } 706 } 707 708 713 private void setIteratorAdder( 714 Map elementsByPropertyName, 715 Method singleParameterAdderMethod) { 716 717 String adderName = singleParameterAdderMethod.getName(); 718 String propertyName = Introspector.decapitalize(adderName.substring(3)); 719 ElementDescriptor matchingDescriptor = getMatchForAdder(propertyName, elementsByPropertyName); 720 if (matchingDescriptor != null) { 721 723 Class singularType = singleParameterAdderMethod.getParameterTypes()[0]; 724 if (getLog().isTraceEnabled()) { 725 getLog().trace(adderName + "->" + propertyName); 726 } 727 getLog().trace("Matching collection or iteration"); 729 730 matchingDescriptor.setUpdater( new MethodUpdater( singleParameterAdderMethod ) ); 731 matchingDescriptor.setSingularPropertyType( singularType ); 732 matchingDescriptor.setHollow(!isPrimitiveType(singularType)); 733 String localName = matchingDescriptor.getLocalName(); 734 if ( localName == null || localName.length() == 0 ) { 735 matchingDescriptor.setLocalName( 736 getElementNameMapper() 737 .mapTypeToElementName( propertyName ) ); 738 } 739 740 if ( getLog().isDebugEnabled() ) { 741 getLog().debug( "!! " + singleParameterAdderMethod); 742 getLog().debug( "!! " + singularType); 743 } 744 } 745 } 746 747 752 private void setMapAdder( 753 Map elementsByPropertyName, 754 Method twinParameterAdderMethod) { 755 String adderName = twinParameterAdderMethod.getName(); 756 String propertyName = Introspector.decapitalize(adderName.substring(3)); 757 ElementDescriptor matchingDescriptor = getMatchForAdder(propertyName, elementsByPropertyName); 758 if ( matchingDescriptor != null 759 && Map .class.isAssignableFrom( matchingDescriptor.getPropertyType() )) { 760 getLog().trace("Matching map"); 762 ElementDescriptor[] children 763 = matchingDescriptor.getElementDescriptors(); 764 if ( children.length == 0 ) { 766 getLog().info( 767 "'entry' descriptor is missing for map. " 768 + "Updaters cannot be set"); 769 770 } else { 771 Class [] types = twinParameterAdderMethod.getParameterTypes(); 772 Class keyType = types[0]; 773 Class valueType = types[1]; 774 775 MapEntryAdder adder = new MapEntryAdder(twinParameterAdderMethod); 778 for ( 779 int n=0, 780 noOfGrandChildren = children.length; 781 n < noOfGrandChildren; 782 n++ ) { 783 if ( "key".equals( children[n].getLocalName() ) ) { 784 785 children[n].setUpdater( adder.getKeyUpdater() ); 786 children[n].setSingularPropertyType( keyType ); 787 if (children[n].getPropertyType() == null) { 788 children[n].setPropertyType( valueType ); 789 } 790 if ( isPrimitiveType(keyType) ) { 791 children[n].setHollow(false); 792 } 793 if ( getLog().isTraceEnabled() ) { 794 getLog().trace( "Key descriptor: " + children[n]); 795 } 796 797 } else if ( "value".equals( children[n].getLocalName() ) ) { 798 799 children[n].setUpdater( adder.getValueUpdater() ); 800 children[n].setSingularPropertyType( valueType ); 801 if (children[n].getPropertyType() == null) { 802 children[n].setPropertyType( valueType ); 803 } 804 if ( isPrimitiveType( valueType) ) { 805 children[n].setHollow(false); 806 } 807 if ( isLoopType( valueType )) { 808 ElementDescriptor loopDescriptor = new ElementDescriptor(); 812 loopDescriptor.setHollow(true); 813 loopDescriptor.setSingularPropertyType( valueType ); 814 loopDescriptor.setPropertyType( valueType ); 815 &n
|