1 16 17 package org.springframework.beans.factory.xml; 18 19 import java.util.ArrayList ; 20 import java.util.Arrays ; 21 import java.util.HashSet ; 22 import java.util.Iterator ; 23 import java.util.List ; 24 import java.util.Map ; 25 import java.util.Properties ; 26 import java.util.Set ; 27 28 import org.apache.commons.logging.Log; 29 import org.apache.commons.logging.LogFactory; 30 import org.w3c.dom.Element ; 31 import org.w3c.dom.NamedNodeMap ; 32 import org.w3c.dom.Node ; 33 import org.w3c.dom.NodeList ; 34 35 import org.springframework.beans.PropertyValue; 36 import org.springframework.beans.factory.BeanDefinitionStoreException; 37 import org.springframework.beans.factory.config.BeanDefinition; 38 import org.springframework.beans.factory.config.BeanDefinitionHolder; 39 import org.springframework.beans.factory.config.ConstructorArgumentValues; 40 import org.springframework.beans.factory.config.RuntimeBeanNameReference; 41 import org.springframework.beans.factory.config.RuntimeBeanReference; 42 import org.springframework.beans.factory.config.TypedStringValue; 43 import org.springframework.beans.factory.parsing.BeanEntry; 44 import org.springframework.beans.factory.parsing.ConstructorArgumentEntry; 45 import org.springframework.beans.factory.parsing.ParseState; 46 import org.springframework.beans.factory.parsing.PropertyEntry; 47 import org.springframework.beans.factory.support.AbstractBeanDefinition; 48 import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; 49 import org.springframework.beans.factory.support.LookupOverride; 50 import org.springframework.beans.factory.support.ManagedList; 51 import org.springframework.beans.factory.support.ManagedMap; 52 import org.springframework.beans.factory.support.ManagedProperties; 53 import org.springframework.beans.factory.support.ManagedSet; 54 import org.springframework.beans.factory.support.MethodOverrides; 55 import org.springframework.beans.factory.support.ReplaceOverride; 56 import org.springframework.core.AttributeAccessor; 57 import org.springframework.util.Assert; 58 import org.springframework.util.ClassUtils; 59 import org.springframework.util.CollectionUtils; 60 import org.springframework.util.ObjectUtils; 61 import org.springframework.util.StringUtils; 62 import org.springframework.util.xml.DomUtils; 63 64 77 public class BeanDefinitionParserDelegate { 78 79 public static final String BEANS_NAMESPACE_URI = "http://www.springframework.org/schema/beans"; 80 81 public static final String BEAN_NAME_DELIMITERS = ",; "; 82 83 87 public static final String TRUE_VALUE = "true"; 88 89 public static final String DEFAULT_VALUE = "default"; 90 91 public static final String DESCRIPTION_ELEMENT = "description"; 92 93 public static final String AUTOWIRE_BY_NAME_VALUE = "byName"; 94 95 public static final String AUTOWIRE_BY_TYPE_VALUE = "byType"; 96 97 public static final String AUTOWIRE_CONSTRUCTOR_VALUE = "constructor"; 98 99 public static final String AUTOWIRE_AUTODETECT_VALUE = "autodetect"; 100 101 public static final String DEPENDENCY_CHECK_ALL_ATTRIBUTE_VALUE = "all"; 102 103 public static final String DEPENDENCY_CHECK_SIMPLE_ATTRIBUTE_VALUE = "simple"; 104 105 public static final String DEPENDENCY_CHECK_OBJECTS_ATTRIBUTE_VALUE = "objects"; 106 107 public static final String NAME_ATTRIBUTE = "name"; 108 109 public static final String BEAN_ELEMENT = "bean"; 110 111 public static final String META_ELEMENT = "meta"; 112 113 public static final String ID_ATTRIBUTE = "id"; 114 115 public static final String PARENT_ATTRIBUTE = "parent"; 116 117 public static final String CLASS_ATTRIBUTE = "class"; 118 119 public static final String ABSTRACT_ATTRIBUTE = "abstract"; 120 121 public static final String SCOPE_ATTRIBUTE = "scope"; 122 123 public static final String SINGLETON_ATTRIBUTE = "singleton"; 124 125 public static final String LAZY_INIT_ATTRIBUTE = "lazy-init"; 126 127 public static final String AUTOWIRE_ATTRIBUTE = "autowire"; 128 129 public static final String AUTOWIRE_CANDIDATE_ATTRIBUTE = "autowire-candidate"; 130 131 public static final String DEPENDENCY_CHECK_ATTRIBUTE = "dependency-check"; 132 133 public static final String DEPENDS_ON_ATTRIBUTE = "depends-on"; 134 135 public static final String INIT_METHOD_ATTRIBUTE = "init-method"; 136 137 public static final String DESTROY_METHOD_ATTRIBUTE = "destroy-method"; 138 139 public static final String FACTORY_METHOD_ATTRIBUTE = "factory-method"; 140 141 public static final String FACTORY_BEAN_ATTRIBUTE = "factory-bean"; 142 143 public static final String CONSTRUCTOR_ARG_ELEMENT = "constructor-arg"; 144 145 public static final String INDEX_ATTRIBUTE = "index"; 146 147 public static final String TYPE_ATTRIBUTE = "type"; 148 149 public static final String VALUE_TYPE_ATTRIBUTE = "value-type"; 150 151 public static final String KEY_TYPE_ATTRIBUTE = "key-type"; 152 153 public static final String PROPERTY_ELEMENT = "property"; 154 155 public static final String REF_ATTRIBUTE = "ref"; 156 157 public static final String VALUE_ATTRIBUTE = "value"; 158 159 public static final String LOOKUP_METHOD_ELEMENT = "lookup-method"; 160 161 public static final String REPLACED_METHOD_ELEMENT = "replaced-method"; 162 163 public static final String REPLACER_ATTRIBUTE = "replacer"; 164 165 public static final String ARG_TYPE_ELEMENT = "arg-type"; 166 167 public static final String ARG_TYPE_MATCH_ATTRIBUTE = "match"; 168 169 public static final String REF_ELEMENT = "ref"; 170 171 public static final String IDREF_ELEMENT = "idref"; 172 173 public static final String BEAN_REF_ATTRIBUTE = "bean"; 174 175 public static final String LOCAL_REF_ATTRIBUTE = "local"; 176 177 public static final String PARENT_REF_ATTRIBUTE = "parent"; 178 179 public static final String VALUE_ELEMENT = "value"; 180 181 public static final String NULL_ELEMENT = "null"; 182 183 public static final String LIST_ELEMENT = "list"; 184 185 public static final String SET_ELEMENT = "set"; 186 187 public static final String MAP_ELEMENT = "map"; 188 189 public static final String ENTRY_ELEMENT = "entry"; 190 191 public static final String KEY_ELEMENT = "key"; 192 193 public static final String KEY_ATTRIBUTE = "key"; 194 195 public static final String KEY_REF_ATTRIBUTE = "key-ref"; 196 197 public static final String VALUE_REF_ATTRIBUTE = "value-ref"; 198 199 public static final String PROPS_ELEMENT = "props"; 200 201 public static final String PROP_ELEMENT = "prop"; 202 203 public static final String MERGE_ATTRIBUTE = "merge"; 204 205 public static final String DEFAULT_LAZY_INIT_ATTRIBUTE = "default-lazy-init"; 206 207 public static final String DEFAULT_AUTOWIRE_ATTRIBUTE = "default-autowire"; 208 209 public static final String DEFAULT_DEPENDENCY_CHECK_ATTRIBUTE = "default-dependency-check"; 210 211 public static final String DEFAULT_INIT_METHOD_ATTRIBUTE = "default-init-method"; 212 213 public static final String DEFAULT_DESTROY_METHOD_ATTRIBUTE = "default-destroy-method"; 214 215 public static final String DEFAULT_MERGE_ATTRIBUTE = "default-merge"; 216 217 218 protected final Log logger = LogFactory.getLog(getClass()); 219 220 private final XmlReaderContext readerContext; 221 222 private DocumentDefaultsDefinition defaults; 223 224 private ParseState parseState = new ParseState(); 225 226 229 private final Set usedNames = new HashSet (); 230 231 232 236 public BeanDefinitionParserDelegate(XmlReaderContext readerContext) { 237 Assert.notNull(readerContext, "XmlReaderContext must not be null"); 238 this.readerContext = readerContext; 239 } 240 241 244 public final XmlReaderContext getReaderContext() { 245 return this.readerContext; 246 } 247 248 249 253 protected Object extractSource(Element ele) { 254 return this.readerContext.extractSource(ele); 255 } 256 257 260 protected void error(String message, Element source) { 261 this.readerContext.error(message, source, this.parseState.snapshot()); 262 } 263 264 267 protected void error(String message, Element source, Throwable cause) { 268 this.readerContext.error(message, source, this.parseState.snapshot(), cause); 269 } 270 271 272 277 public void initDefaults(Element root) { 278 DocumentDefaultsDefinition defaults = new DocumentDefaultsDefinition(); 279 defaults.setLazyInit(root.getAttribute(DEFAULT_LAZY_INIT_ATTRIBUTE)); 280 defaults.setAutowire(root.getAttribute(DEFAULT_AUTOWIRE_ATTRIBUTE)); 281 defaults.setDependencyCheck(root.getAttribute(DEFAULT_DEPENDENCY_CHECK_ATTRIBUTE)); 282 if (root.hasAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE)) { 283 defaults.setInitMethod(root.getAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE)); 284 } 285 if (root.hasAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE)) { 286 defaults.setDestroyMethod(root.getAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE)); 287 } 288 defaults.setMerge(root.getAttribute(DEFAULT_MERGE_ATTRIBUTE)); 289 defaults.setSource(this.readerContext.extractSource(root)); 290 291 this.defaults = defaults; 292 this.readerContext.fireDefaultsRegistered(defaults); 293 } 294 295 299 public DocumentDefaultsDefinition getDefaults() { 300 return this.defaults; 301 } 302 303 304 309 public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) { 310 return parseBeanDefinitionElement(ele, null); 311 } 312 313 318 public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) { 319 String id = ele.getAttribute(ID_ATTRIBUTE); 320 String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); 321 322 List aliases = new ArrayList (); 323 if (StringUtils.hasLength(nameAttr)) { 324 String [] nameArr = StringUtils.tokenizeToStringArray(nameAttr, BEAN_NAME_DELIMITERS); 325 aliases.addAll(Arrays.asList(nameArr)); 326 } 327 328 String beanName = id; 329 if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) { 330 beanName = (String ) aliases.remove(0); 331 if (logger.isDebugEnabled()) { 332 logger.debug("No XML 'id' specified - using '" + beanName + 333 "' as bean name and " + aliases + " as aliases"); 334 } 335 } 336 337 if (containingBean == null) { 338 checkNameUniqueness(beanName, aliases, ele); 339 } 340 341 AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); 342 if (beanDefinition != null) { 343 if (!StringUtils.hasText(beanName)) { 344 try { 345 if (containingBean != null) { 346 beanName = BeanDefinitionReaderUtils.generateBeanName( 347 beanDefinition, this.readerContext.getRegistry(), true); 348 } 349 else { 350 beanName = this.readerContext.generateBeanName(beanDefinition); 351 } 352 if (logger.isDebugEnabled()) { 353 logger.debug("Neither XML 'id' nor 'name' specified - " + 354 "using generated bean name [" + beanName + "]"); 355 } 356 } 357 catch (BeanDefinitionStoreException ex) { 358 error(ex.getMessage(), ele); 359 return null; 360 } 361 } 362 String [] aliasesArray = StringUtils.toStringArray(aliases); 363 return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); 364 } 365 366 return null; 367 } 368 369 private void checkNameUniqueness(String beanName, List aliases, Element beanElement) { 370 String foundName = null; 371 372 if (StringUtils.hasText(beanName) && this.usedNames.contains(beanName)) { 373 foundName = beanName; 374 } 375 if (foundName == null) { 376 foundName = (String ) CollectionUtils.findFirstMatch(this.usedNames, aliases); 377 } 378 if (foundName != null) { 379 error("Bean name '" + foundName + "' is already used in this file.", beanElement); 380 } 381 382 this.usedNames.add(beanName); 383 this.usedNames.addAll(aliases); 384 } 385 386 390 public AbstractBeanDefinition parseBeanDefinitionElement( 391 Element ele, String beanName, BeanDefinition containingBean) { 392 393 String className = null; 394 if (ele.hasAttribute(CLASS_ATTRIBUTE)) { 395 className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); 396 } 397 String parent = null; 398 if (ele.hasAttribute(PARENT_ATTRIBUTE)) { 399 parent = ele.getAttribute(PARENT_ATTRIBUTE); 400 } 401 402 try { 403 this.parseState.push(new BeanEntry(beanName)); 404 405 AbstractBeanDefinition bd = BeanDefinitionReaderUtils.createBeanDefinition( 406 parent, className, this.readerContext.getReader().getBeanClassLoader()); 407 408 if (ele.hasAttribute(SCOPE_ATTRIBUTE)) { 409 bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE)); 411 if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) { 412 error("Specify either 'scope' or 'singleton', not both", ele); 413 } 414 } 415 else if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) { 416 bd.setSingleton(TRUE_VALUE.equals(ele.getAttribute(SINGLETON_ATTRIBUTE))); 418 } 419 else if (containingBean != null) { 420 bd.setSingleton(containingBean.isSingleton()); 422 } 423 424 if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) { 425 bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE))); 426 } 427 428 String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE); 429 if (DEFAULT_VALUE.equals(lazyInit) && bd.isSingleton()) { 430 lazyInit = this.defaults.getLazyInit(); 432 } 433 bd.setLazyInit(TRUE_VALUE.equals(lazyInit)); 434 435 if (ele.hasAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE)) { 436 bd.setAutowireCandidate(TRUE_VALUE.equals(ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE))); 437 } 438 439 String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE); 440 if (DEFAULT_VALUE.equals(autowire)) { 441 autowire = this.defaults.getAutowire(); 442 } 443 bd.setAutowireMode(getAutowireMode(autowire)); 444 445 String dependencyCheck = ele.getAttribute(DEPENDENCY_CHECK_ATTRIBUTE); 446 if (DEFAULT_VALUE.equals(dependencyCheck)) { 447 dependencyCheck = this.defaults.getDependencyCheck(); 448 } 449 bd.setDependencyCheck(getDependencyCheck(dependencyCheck)); 450 451 if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) { 452 String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE); 453 bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, BEAN_NAME_DELIMITERS)); 454 } 455 456 if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) { 457 bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE)); 458 } 459 if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) { 460 bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE)); 461 } 462 463 if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) { 464 String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE); 465 if (!"".equals(initMethodName)) { 466 bd.setInitMethodName(initMethodName); 467 } 468 } 469 else { 470 if (this.defaults.getInitMethod() != null) { 471 bd.setInitMethodName(this.defaults.getInitMethod()); 472 bd.setEnforceInitMethod(false); 473 } 474 } 475 476 if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) { 477 String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE); 478 if (!"".equals(destroyMethodName)) { 479 bd.setDestroyMethodName(destroyMethodName); 480 } 481 } 482 else { 483 if (this.defaults.getDestroyMethod() != null) { 484 bd.setDestroyMethodName(this.defaults.getDestroyMethod()); 485 bd.setEnforceDestroyMethod(false); 486 } 487 } 488 489 parseMetaElements(ele, bd); 490 parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); 491 parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); 492 493 parseConstructorArgElements(ele, bd); 494 parsePropertyElements(ele, bd); 495 496 bd.setResourceDescription(this.readerContext.getResource().getDescription()); 497 bd.setSource(extractSource(ele)); 498 499 return bd; 500 } 501 catch (ClassNotFoundException ex) { 502 error("Bean class [" + className + "] not found", ele, ex); 503 } 504 catch (NoClassDefFoundError err) { 505 error("Class that bean class [" + className + "] depends on not found", ele, err); 506 } 507 catch (Throwable ex) { 508 error("Unexpected failure during bean definition parsing", ele, ex); 509 } 510 finally { 511 this.parseState.pop(); 512 } 513 514 return null; 515 } 516 517 public void parseMetaElements(Element ele, AttributeAccessor attributeAccessor) { 518 NodeList nl = ele.getChildNodes(); 519 for (int i = 0; i < nl.getLength(); i++) { 520 Node node = nl.item(i); 521 if (node instanceof Element && DomUtils.nodeNameEquals(node, META_ELEMENT)) { 522 Element metaElement = (Element ) node; 523 String key = metaElement.getAttribute(KEY_ATTRIBUTE); 524 String value = metaElement.getAttribute(VALUE_ATTRIBUTE); 525 attributeAccessor.setAttribute(key, value); 526 } 527 } 528 } 529 530 public int getDependencyCheck(String att) { 531 int dependencyCheckCode = AbstractBeanDefinition.DEPENDENCY_CHECK_NONE; 532 if (DEPENDENCY_CHECK_ALL_ATTRIBUTE_VALUE.equals(att)) { 533 dependencyCheckCode = AbstractBeanDefinition.DEPENDENCY_CHECK_ALL; 534 } 535 else if (DEPENDENCY_CHECK_SIMPLE_ATTRIBUTE_VALUE.equals(att)) { 536 dependencyCheckCode = AbstractBeanDefinition.DEPENDENCY_CHECK_SIMPLE; 537 } 538 else if (DEPENDENCY_CHECK_OBJECTS_ATTRIBUTE_VALUE.equals(att)) { 539 dependencyCheckCode = AbstractBeanDefinition.DEPENDENCY_CHECK_OBJECTS; 540 } 541 return dependencyCheckCode; 543 } 544 545 public int getAutowireMode(String att) { 546 int autowire = AbstractBeanDefinition.AUTOWIRE_NO; 547 if (AUTOWIRE_BY_NAME_VALUE.equals(att)) { 548 autowire = AbstractBeanDefinition.AUTOWIRE_BY_NAME; 549 } 550 else if (AUTOWIRE_BY_TYPE_VALUE.equals(att)) { 551 autowire = AbstractBeanDefinition.AUTOWIRE_BY_TYPE; 552 } 553 else if (AUTOWIRE_CONSTRUCTOR_VALUE.equals(att)) { 554 autowire = AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR; 555 } 556 else if (AUTOWIRE_AUTODETECT_VALUE.equals(att)) { 557 autowire = AbstractBeanDefinition.AUTOWIRE_AUTODETECT; 558 } 559 return autowire; 561 } 562 563 566 public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) { 567 NodeList nl = beanEle.getChildNodes(); 568 for (int i = 0; i < nl.getLength(); i++) { 569 Node node = nl.item(i); 570 if (node instanceof Element && DomUtils.nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) { 571 parseConstructorArgElement((Element ) node, bd); 572 } 573 } 574 } 575 576 579 public void parsePropertyElements(Element beanEle, BeanDefinition bd) { 580 NodeList nl = beanEle.getChildNodes(); 581 for (int i = 0; i < nl.getLength(); i++) { 582 Node node = nl.item(i); 583 if (node instanceof Element && DomUtils.nodeNameEquals(node, PROPERTY_ELEMENT)) { 584 parsePropertyElement((Element ) node, bd); 585 } 586 } 587 } 588 589 592 public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) { 593 NodeList nl = beanEle.getChildNodes(); 594 for (int i = 0; i < nl.getLength(); i++) { 595 Node node = nl.item(i); 596 if (node instanceof Element && DomUtils.nodeNameEquals(node, LOOKUP_METHOD_ELEMENT)) { 597 Element ele = (Element ) node; 598 String methodName = ele.getAttribute(NAME_ATTRIBUTE); 599 String beanRef = ele.getAttribute(BEAN_ELEMENT); 600 LookupOverride override = new LookupOverride(methodName, beanRef); 601 override.setSource(extractSource(ele)); 602 overrides.addOverride(override); 603 } 604 } 605 } 606 607 610 public void parseReplacedMethodSubElements(Element beanEle, MethodOverrides overrides) { 611 NodeList nl = beanEle.getChildNodes(); 612 for (int i = 0; i < nl.getLength(); i++) { 613 Node node = nl.item(i); 614 if (node instanceof Element && DomUtils.nodeNameEquals(node, REPLACED_METHOD_ELEMENT)) { 615 Element replacedMethodEle = (Element ) node; 616 String name = replacedMethodEle.getAttribute(NAME_ATTRIBUTE); 617 String callback = replacedMethodEle.getAttribute(REPLACER_ATTRIBUTE); 618 ReplaceOverride replaceOverride = new ReplaceOverride(name, callback); 619 List argTypeEles = DomUtils.getChildElementsByTagName(replacedMethodEle, ARG_TYPE_ELEMENT); 621 for (Iterator it = argTypeEles.iterator(); it.hasNext();) { 622 Element argTypeEle = (Element ) it.next(); 623 replaceOverride.addTypeIdentifier(argTypeEle.getAttribute(ARG_TYPE_MATCH_ATTRIBUTE)); 624 } 625 replaceOverride.setSource(extractSource(replacedMethodEle)); 626 overrides.addOverride(replaceOverride); 627 } 628 } 629 } 630 631 634 public void parseConstructorArgElement(Element ele, BeanDefinition bd) { 635 String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE); 636 String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE); 637 if (StringUtils.hasLength(indexAttr)) { 638 try { 639 int index = Integer.parseInt(indexAttr); 640 if (index < 0) { 641 error("'index' cannot be lower than 0", ele); 642 } 643 else { 644 try { 645 this.parseState.push(new ConstructorArgumentEntry(index)); 646 Object value = parsePropertyValue(ele, bd, null); 647 ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value); 648 if (StringUtils.hasLength(typeAttr)) { 649 valueHolder.setType(typeAttr); 650 } 651 valueHolder.setSource(extractSource(ele)); 652 bd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder); 653 } 654 finally { 655 this.parseState.pop(); 656 } 657 } 658 } 659 catch (NumberFormatException ex) { 660 error("Attribute 'index' of tag 'constructor-arg' must be an integer", ele); 661 } 662 } 663 else { 664 try { 665 this.parseState.push(new ConstructorArgumentEntry()); 666 Object value = parsePropertyValue(ele, bd, null); 667 ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value); 668 if (StringUtils.hasLength(typeAttr)) { 669 valueHolder.setType(typeAttr); 670 } 671 valueHolder.setSource(extractSource(ele)); 672 bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder); 673 } 674 finally { 675 this.parseState.pop(); 676 } 677 } 678 } 679 680 683 public void parsePropertyElement(Element ele, BeanDefinition bd) { 684 String propertyName = ele.getAttribute(NAME_ATTRIBUTE); 685 if (!StringUtils.hasLength(propertyName)) { 686 error("Tag 'property' must have a 'name' attribute", ele); 687 return; 688 } 689 this.parseState.push(new PropertyEntry(propertyName)); 690 try { 691 if (bd.getPropertyValues().contains(propertyName)) { 692 error("Multiple 'property' definitions for property '" + propertyName + "'", ele); 693 return; 694 } 695 Object val = parsePropertyValue(ele, bd, propertyName); 696 PropertyValue pv = new PropertyValue(propertyName, val); 697 parseMetaElements(ele, pv); 698 pv.setSource(extractSource(ele)); 699 bd.getPropertyValues().addPropertyValue(pv); 700 } 701 finally { 702 this.parseState.pop(); 703 } 704 } 705 706 710 public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) { 711 String elementName = (propertyName != null) ? 712 "<property> element for property '" + propertyName + "'" : 713 "<constructor-arg> element"; 714 715 NodeList nl = ele.getChildNodes(); 717 Element subElement = null; 718 for (int i = 0; i < nl.getLength(); i++) { 719 if (nl.item(i) instanceof Element ) { 720 Element candidateEle = (Element ) nl.item(i); 721 if (DESCRIPTION_ELEMENT.equals(candidateEle.getTagName())) { 722 } 724 else { 725 if (subElement != null && !META_ELEMENT.equals(subElement.getTagName())) { 727 error(elementName + " must not contain more than one sub-element", ele); 728 } 729 else { 730 subElement = candidateEle; 731 } 732 } 733 } 734 } 735 736 boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE); 737 boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE); 738 if ((hasRefAttribute && hasValueAttribute) || 739 ((hasRefAttribute || hasValueAttribute)) && subElement != null) { 740 error(elementName + 741 " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele); 742 } 743 744 if (hasRefAttribute) { 745 String refName = ele.getAttribute(REF_ATTRIBUTE); 746 if (!StringUtils.hasText(refName)) { 747 error(elementName + " contains empty 'ref' attribute", ele); 748 } 749 RuntimeBeanReference ref = new RuntimeBeanReference(refName); 750 ref.setSource(extractSource(ele)); 751 return ref; 752 } 753 else if (hasValueAttribute) { 754 TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE)); 755 valueHolder.setSource(extractSource(ele)); 756 return valueHolder; 757 } 758 else if (subElement != null) { 759 return parsePropertySubElement(subElement, bd); 760 } 761 else { 762 error(elementName + " must specify a ref or value", ele); 764 return null; 765 } 766 } 767 768 public Object parsePropertySubElement(Element ele, BeanDefinition bd) { 769 return parsePropertySubElement(ele, bd, null); 770 } 771 772 779 public Object parsePropertySubElement(Element ele, BeanDefinition bd, String defaultTypeClassName) { 780 if (!isDefaultNamespace(ele.getNamespaceURI())) { 781 return parseNestedCustomElement(ele, bd); 782 } 783 else if (DomUtils.nodeNameEquals(ele, BEAN_ELEMENT)) { 784 BeanDefinitionHolder bdHolder = parseBeanDefinitionElement(ele, bd); 785 if (bdHolder != null) { 786 bdHolder = decorateBeanDefinitionIfRequired(ele, bdHolder); 787 } 788 return bdHolder; 789 } 790 else if (DomUtils.nodeNameEquals(ele, REF_ELEMENT)) { 791 String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE); 793 boolean toParent = false; 794 if (!StringUtils.hasLength(refName)) { 795 refName = ele.getAttribute(LOCAL_REF_ATTRIBUTE); 797 if (!StringUtils.hasLength(refName)) { 798 refName = ele.getAttribute(PARENT_REF_ATTRIBUTE); 800 toParent = true; 801 if (!StringUtils.hasLength(refName)) { 802 error("'bean', 'local' or 'parent' is required for <ref> element", ele); 803 return null; 804 } 805 } 806 } 807 if (!StringUtils.hasText(refName)) { 808 error("<ref> element contains empty target attribute", ele); 809 return null; 810 } 811 RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent); 812 ref.setSource(extractSource(ele)); 813 return ref; 814 } 815 else if (DomUtils.nodeNameEquals(ele, IDREF_ELEMENT)) { 816 String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE); 818 if (!StringUtils.hasLength(refName)) { 819 refName = ele.getAttribute(LOCAL_REF_ATTRIBUTE); 821 if (!StringUtils.hasLength(refName)) { 822 error("Either 'bean' or 'local' is required for <idref> element", ele); 823 return null; 824 } 825 } 826 if (!StringUtils.hasText(refName)) { 827 error("<idref> element contains empty target attribute", ele); 828 return null; 829 } 830 RuntimeBeanNameReference ref = new RuntimeBeanNameReference(refName); 831 ref.setSource(extractSource(ele)); 832 return ref; 833 } 834 else if (DomUtils.nodeNameEquals(ele, VALUE_ELEMENT)) { 835 String value = DomUtils.getTextValue(ele); 837 String typeClassName = ele.getAttribute(TYPE_ATTRIBUTE); 838 if (!StringUtils.hasText(typeClassName)) { 839 typeClassName = defaultTypeClassName; 840 } 841 try { 842 return buildTypedStringValue(value, typeClassName, ele); 843 } 844 catch (ClassNotFoundException ex) { 845 error("Type class [" + typeClassName + "] not found for <value> element", ele, ex); 846 return value; 847 } 848 } 849 else if (DomUtils.nodeNameEquals(ele, NULL_ELEMENT)) { 850 TypedStringValue nullHolder = new TypedStringValue(null); 853 nullHolder.setSource(extractSource(ele)); 854 return nullHolder; 855 } 856 else if (DomUtils.nodeNameEquals(ele, LIST_ELEMENT)) { 857 return parseListElement(ele, bd); 858 } 859 else if (DomUtils.nodeNameEquals(ele, SET_ELEMENT)) { 860 return parseSetElement(ele, bd); 861 } 862 else if (DomUtils.nodeNameEquals(ele, MAP_ELEMENT)) { 863 return parseMapElement(ele, bd); 864 } 865 else if (DomUtils.nodeNameEquals(ele, PROPS_ELEMENT)) { 866 return parsePropsElement(ele); 867 } 868 error("Unknown property sub-element: [" + ele.getTagName() + "]", ele); 869 return null; 870 } 871 872 private Object buildTypedStringValue(String value, String targetTypeName, Element ele) 873 throws ClassNotFoundException { 874 875 ClassLoader classLoader = this.readerContext.getReader().getBeanClassLoader(); 876 TypedStringValue typedValue = null; 877 if (!StringUtils.hasText(targetTypeName)) { 878 typedValue = new TypedStringValue(value); 879 } 880 else if (classLoader != null) { 881 Class targetType = ClassUtils.forName(targetTypeName, classLoader); 882 typedValue = new TypedStringValue(value, targetType); 883 } 884 else { 885 typedValue = new TypedStringValue(value, targetTypeName); 886 } 887 typedValue.setSource(extractSource(ele)); 888 return typedValue; 889 } 890 891 894 public List parseListElement(Element collectionEle, BeanDefinition bd) { 895 String defaultTypeClassName = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE); 896 NodeList nl = collectionEle.getChildNodes(); 897 ManagedList list = new ManagedList(nl.getLength()); 898 list.setSource(extractSource(collectionEle)); 899 list.setMergeEnabled(parseMergeAttribute(collectionEle)); 900 for (int i = 0; i < nl.getLength(); i++) { 901 if (nl.item(i) instanceof Element ) { 902 Element ele = (Element ) nl.item(i); 903 list.add(parsePropertySubElement(ele, bd, defaultTypeClassName)); 904 } 905 } 906 return list; 907 } 908 909 912 public Set parseSetElement(Element collectionEle, BeanDefinition bd) { 913 String defaultTypeClassName = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE); 914 NodeList nl = collectionEle.getChildNodes(); 915 ManagedSet set = new ManagedSet(nl.getLength()); 916 set.setSource(extractSource(collectionEle)); 917 set.setMergeEnabled(parseMergeAttribute(collectionEle)); 918 for (int i = 0; i < nl.getLength(); i++) { 919 if (nl.item(i) instanceof Element ) { 920 Element ele = (Element ) nl.item(i); 921 set.add(parsePropertySubElement(ele, bd, defaultTypeClassName)); 922 } 923 } 924 return set; 925 } 926 927 930 public Map parseMapElement(Element mapEle, BeanDefinition bd) { 931 String defaultKeyTypeClassName = mapEle.getAttribute(KEY_TYPE_ATTRIBUTE); 932 String defaultValueTypeClassName = mapEle.getAttribute(VALUE_TYPE_ATTRIBUTE); 933 934 List entryEles = DomUtils.getChildElementsByTagName(mapEle, ENTRY_ELEMENT); 935 ManagedMap map = new ManagedMap(entryEles.size()); 936 map.setMergeEnabled(parseMergeAttribute(mapEle)); 937 map.setSource(extractSource(mapEle)); 938 939 for (Iterator it = entryEles.iterator(); it.hasNext();) { 940 Element entryEle = (Element ) it.next(); 941 NodeList entrySubNodes = entryEle.getChildNodes(); 944 945 Element keyEle = null; 946 Element valueEle = null; 947 for (int j = 0; j < entrySubNodes.getLength(); j++) { 948 if (entrySubNodes.item(j) instanceof Element ) { 949 Element candidateEle = (Element ) entrySubNodes.item(j); 950 if (DomUtils.nodeNameEquals(candidateEle, KEY_ELEMENT)) { 951 if (keyEle != null) { 952 error("<entry> element is only allowed to contain one <key> sub-element", entryEle); 953 } 954 else { 955 keyEle = candidateEle; 956 } 957 } 958 else { 959 if (valueEle != null) { 961 error("<entry> element must not contain more than one value sub-element", entryEle); 962 } 963 else { 964 valueEle = candidateEle; 965 } 966 } 967 } 968 } 969 970 Object key = null; 972 boolean hasKeyAttribute = entryEle.hasAttribute(KEY_ATTRIBUTE); 973 boolean hasKeyRefAttribute = entryEle.hasAttribute(KEY_REF_ATTRIBUTE); 974 if ((hasKeyAttribute && hasKeyRefAttribute) || 975 ((hasKeyAttribute || hasKeyRefAttribute)) && keyEle != null) { 976 error("<entry> element is only allowed to contain either " + 977 "a 'key' attribute OR a 'key-ref' attribute OR a <key> sub-element", entryEle); 978 } 979 if (hasKeyAttribute) { 980 key = buildTypedStringValueForMap( 981 entryEle.getAttribute(KEY_ATTRIBUTE), defaultKeyTypeClassName, entryEle); 982 } 983 else if (hasKeyRefAttribute) { 984 String refName = entryEle.getAttribute(KEY_REF_ATTRIBUTE); 985 if (!StringUtils.hasText(refName)) { 986 error("<entry> element contains empty 'key-ref' attribute", entryEle); 987 } 988 RuntimeBeanReference ref = new RuntimeBeanReference(refName); 989 ref.setSource(extractSource(keyEle)); 990 key = ref; 991 } 992 else if (keyEle != null) { 993 key = parseKeyElement(keyEle, bd, defaultKeyTypeClassName); 994 } 995 else { 996 error("<entry> element must specify a key", entryEle); 997 } 998 999 Object value = null; 1001 boolean hasValueAttribute = entryEle.hasAttribute(VALUE_ATTRIBUTE); 1002 boolean hasValueRefAttribute = entryEle.hasAttribute(VALUE_REF_ATTRIBUTE); 1003 if ((hasValueAttribute && hasValueRefAttribute) || 1004 ((hasValueAttribute || hasValueRefAttribute)) && valueEle != null) { 1005 error("<entry> element is only allowed to contain either " + 1006 "'value' attribute OR 'value-ref' attribute OR <value> sub-element", entryEle); 1007 } 1008 if (hasValueAttribute) { 1009 value = buildTypedStringValueForMap( 1010 entryEle.getAttribute(VALUE_ATTRIBUTE), defaultValueTypeClassName, entryEle); 1011 } 1012 else if (hasValueRefAttribute) { 1013 String refName = entryEle.getAttribute(VALUE_REF_ATTRIBUTE); 1014 if (!StringUtils.hasText(refName)) { 1015 error("<entry> element contains empty 'value-ref' attribute", entryEle); 1016 } 1017 RuntimeBeanReference ref = new RuntimeBeanReference(refName); 1018 ref.setSource(extractSource(valueEle)); 1019 value = ref; 1020 } 1021 else if (valueEle != null) { 1022 value = parsePropertySubElement(valueEle, bd, defaultValueTypeClassName); 1023 } 1024 else { 1025 error("<entry> element must specify a value", entryEle); 1026 } 1027 1028 map.put(key, value); 1030 } 1031 1032 return map; 1033 } 1034 1035 private Object buildTypedStringValueForMap(String value, String defaultTypeClassName, Element entryEle) { 1036 try { 1037 return buildTypedStringValue(value, defaultTypeClassName, entryEle); 1038 } 1039 catch (ClassNotFoundException ex) { 1040 error("Type class [" + defaultTypeClassName + "] not found for Map key/value type", entryEle, ex); 1041 return value; 1042 } 1043 } 1044 1045 1048 public Object parseKeyElement(Element keyEle, BeanDefinition bd, String defaultKeyTypeClassName) { 1049 NodeList nl = keyEle.getChildNodes(); 1050 Element subElement = null; 1051 for (int i = 0; i < nl.getLength(); i++) { 1052 if (nl.item(i) instanceof Element ) { 1053 Element candidateEle = (Element ) nl.item(i); 1054 if (subElement != null) { 1056 error("<key> element must not contain more than one value sub-element", keyEle); 1057 } 1058 else { 1059 subElement = candidateEle; 1060 } 1061 } 1062 } 1063 return parsePropertySubElement(subElement, bd, defaultKeyTypeClassName); 1064 } 1065 1066 1069 public Properties parsePropsElement(Element propsEle) { 1070 ManagedProperties props = new ManagedProperties(); 1071 props.setSource(extractSource(propsEle)); 1072 props.setMergeEnabled(parseMergeAttribute(propsEle)); 1073 1074 List propEles = DomUtils.getChildElementsByTagName(propsEle, PROP_ELEMENT); 1075 for (Iterator it = propEles.iterator(); it.hasNext();) { 1076 Element propEle = (Element ) it.next(); 1077 String key = propEle.getAttribute(KEY_ATTRIBUTE); 1078 String value = DomUtils.getTextValue(propEle).trim(); 1081 1082 TypedStringValue keyHolder = new TypedStringValue(key); 1083 keyHolder.setSource(extractSource(propEle)); 1084 TypedStringValue valueHolder = new TypedStringValue(value); 1085 valueHolder.setSource(extractSource(propEle)); 1086 props.put(keyHolder, valueHolder); 1087 } 1088 1089 return props; 1090 } 1091 1092 1095 public boolean parseMergeAttribute(Element collectionElement) { 1096 String value = collectionElement.getAttribute(MERGE_ATTRIBUTE); 1097 if (DEFAULT_VALUE.equals(value)) { 1098 value = this.defaults.getMerge(); 1099 } 1100 return TRUE_VALUE.equals(value); 1101 } 1102 1103 public BeanDefinition parseCustomElement(Element ele) { 1104 return parseCustomElement(ele, null); 1105 } 1106 1107 public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) { 1108 String namespaceUri = ele.getNamespaceURI(); 1109 NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); 1110 if (handler == null) { 1111 error("Unable to locate NamespaceHandler for namespace [" + namespaceUri + "]", ele); 1112 return null; 1113 } 1114 return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); 1115 } 1116 1117 public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder definitionHolder) { 1118 BeanDefinitionHolder finalDefinition = definitionHolder; 1119 1120 NamedNodeMap attributes = ele.getAttributes(); 1122 for (int i = 0; i < attributes.getLength(); i++) { 1123 Node node = attributes.item(i); 1124 finalDefinition = decorateIfRequired(node, finalDefinition); 1125 } 1126 1127 NodeList children = ele.getChildNodes(); 1129 for (int i = 0; i < children.getLength(); i++) { 1130 Node node = children.item(i); 1131 if (node.getNodeType() == Node.ELEMENT_NODE) { 1132 finalDefinition = decorateIfRequired(node, finalDefinition); 1133 } 1134 } 1135 return finalDefinition; 1136 } 1137 1138 private BeanDefinitionHolder decorateIfRequired(Node node, BeanDefinitionHolder finalDefinition) { 1139 String uri = node.getNamespaceURI(); 1140 if (!isDefaultNamespace(uri)) { 1141 NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(uri); 1142 finalDefinition = handler.decorate(node, finalDefinition, new ParserContext(this.readerContext, this)); 1143 } 1144 return finalDefinition; 1145 } 1146 1147 public boolean isDefaultNamespace(String namespaceUri) { 1148 return (!StringUtils.hasLength(namespaceUri) || BEANS_NAMESPACE_URI.equals(namespaceUri)); 1149 } 1150 1151 private BeanDefinitionHolder parseNestedCustomElement(Element ele, BeanDefinition containingBd) { 1152 BeanDefinition innerDefinition = parseCustomElement(ele, containingBd); 1153 if (innerDefinition == null) { 1154 error("Incorrect usage of element '" + ele.getNodeName() + "' in a nested manner. " + 1155 "This tag cannot be used nested inside <property>.", ele); 1156 return null; 1157 } 1158 String id = ele.getNodeName() + BeanDefinitionReaderUtils.GENERATED_BEAN_NAME_SEPARATOR + 1159 ObjectUtils.getIdentityHexString(innerDefinition); 1160 if (logger.isDebugEnabled()) { 1161 logger.debug("Using generated bean name [" + id + 1162 "] for nested custom element '" + ele.getNodeName() + "'"); 1163 } 1164 return new BeanDefinitionHolder(innerDefinition, id); 1165 } 1166 1167} 1168 | Popular Tags |