001/* 002 * Copyright 2002-2019 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.beans.factory.xml; 018 019import java.util.ArrayList; 020import java.util.Arrays; 021import java.util.Collection; 022import java.util.HashSet; 023import java.util.List; 024import java.util.Map; 025import java.util.Properties; 026import java.util.Set; 027 028import org.apache.commons.logging.Log; 029import org.apache.commons.logging.LogFactory; 030import org.w3c.dom.Element; 031import org.w3c.dom.NamedNodeMap; 032import org.w3c.dom.Node; 033import org.w3c.dom.NodeList; 034 035import org.springframework.beans.BeanMetadataAttribute; 036import org.springframework.beans.BeanMetadataAttributeAccessor; 037import org.springframework.beans.PropertyValue; 038import org.springframework.beans.factory.config.BeanDefinition; 039import org.springframework.beans.factory.config.BeanDefinitionHolder; 040import org.springframework.beans.factory.config.ConstructorArgumentValues; 041import org.springframework.beans.factory.config.RuntimeBeanNameReference; 042import org.springframework.beans.factory.config.RuntimeBeanReference; 043import org.springframework.beans.factory.config.TypedStringValue; 044import org.springframework.beans.factory.parsing.BeanEntry; 045import org.springframework.beans.factory.parsing.ConstructorArgumentEntry; 046import org.springframework.beans.factory.parsing.ParseState; 047import org.springframework.beans.factory.parsing.PropertyEntry; 048import org.springframework.beans.factory.parsing.QualifierEntry; 049import org.springframework.beans.factory.support.AbstractBeanDefinition; 050import org.springframework.beans.factory.support.AutowireCandidateQualifier; 051import org.springframework.beans.factory.support.BeanDefinitionDefaults; 052import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; 053import org.springframework.beans.factory.support.LookupOverride; 054import org.springframework.beans.factory.support.ManagedArray; 055import org.springframework.beans.factory.support.ManagedList; 056import org.springframework.beans.factory.support.ManagedMap; 057import org.springframework.beans.factory.support.ManagedProperties; 058import org.springframework.beans.factory.support.ManagedSet; 059import org.springframework.beans.factory.support.MethodOverrides; 060import org.springframework.beans.factory.support.ReplaceOverride; 061import org.springframework.core.env.Environment; 062import org.springframework.util.Assert; 063import org.springframework.util.ClassUtils; 064import org.springframework.util.CollectionUtils; 065import org.springframework.util.ObjectUtils; 066import org.springframework.util.PatternMatchUtils; 067import org.springframework.util.StringUtils; 068import org.springframework.util.xml.DomUtils; 069 070/** 071 * Stateful delegate class used to parse XML bean definitions. 072 * Intended for use by both the main parser and any extension 073 * {@link BeanDefinitionParser BeanDefinitionParsers} or 074 * {@link BeanDefinitionDecorator BeanDefinitionDecorators}. 075 * 076 * @author Rob Harrop 077 * @author Juergen Hoeller 078 * @author Rod Johnson 079 * @author Mark Fisher 080 * @author Gary Russell 081 * @since 2.0 082 * @see ParserContext 083 * @see DefaultBeanDefinitionDocumentReader 084 */ 085public class BeanDefinitionParserDelegate { 086 087 public static final String BEANS_NAMESPACE_URI = "http://www.springframework.org/schema/beans"; 088 089 public static final String MULTI_VALUE_ATTRIBUTE_DELIMITERS = ",; "; 090 091 /** 092 * Value of a T/F attribute that represents true. 093 * Anything else represents false. Case seNsItive. 094 */ 095 public static final String TRUE_VALUE = "true"; 096 097 public static final String FALSE_VALUE = "false"; 098 099 public static final String DEFAULT_VALUE = "default"; 100 101 public static final String DESCRIPTION_ELEMENT = "description"; 102 103 public static final String AUTOWIRE_NO_VALUE = "no"; 104 105 public static final String AUTOWIRE_BY_NAME_VALUE = "byName"; 106 107 public static final String AUTOWIRE_BY_TYPE_VALUE = "byType"; 108 109 public static final String AUTOWIRE_CONSTRUCTOR_VALUE = "constructor"; 110 111 public static final String AUTOWIRE_AUTODETECT_VALUE = "autodetect"; 112 113 public static final String DEPENDENCY_CHECK_ALL_ATTRIBUTE_VALUE = "all"; 114 115 public static final String DEPENDENCY_CHECK_SIMPLE_ATTRIBUTE_VALUE = "simple"; 116 117 public static final String DEPENDENCY_CHECK_OBJECTS_ATTRIBUTE_VALUE = "objects"; 118 119 public static final String NAME_ATTRIBUTE = "name"; 120 121 public static final String BEAN_ELEMENT = "bean"; 122 123 public static final String META_ELEMENT = "meta"; 124 125 public static final String ID_ATTRIBUTE = "id"; 126 127 public static final String PARENT_ATTRIBUTE = "parent"; 128 129 public static final String CLASS_ATTRIBUTE = "class"; 130 131 public static final String ABSTRACT_ATTRIBUTE = "abstract"; 132 133 public static final String SCOPE_ATTRIBUTE = "scope"; 134 135 private static final String SINGLETON_ATTRIBUTE = "singleton"; 136 137 public static final String LAZY_INIT_ATTRIBUTE = "lazy-init"; 138 139 public static final String AUTOWIRE_ATTRIBUTE = "autowire"; 140 141 public static final String AUTOWIRE_CANDIDATE_ATTRIBUTE = "autowire-candidate"; 142 143 public static final String PRIMARY_ATTRIBUTE = "primary"; 144 145 public static final String DEPENDENCY_CHECK_ATTRIBUTE = "dependency-check"; 146 147 public static final String DEPENDS_ON_ATTRIBUTE = "depends-on"; 148 149 public static final String INIT_METHOD_ATTRIBUTE = "init-method"; 150 151 public static final String DESTROY_METHOD_ATTRIBUTE = "destroy-method"; 152 153 public static final String FACTORY_METHOD_ATTRIBUTE = "factory-method"; 154 155 public static final String FACTORY_BEAN_ATTRIBUTE = "factory-bean"; 156 157 public static final String CONSTRUCTOR_ARG_ELEMENT = "constructor-arg"; 158 159 public static final String INDEX_ATTRIBUTE = "index"; 160 161 public static final String TYPE_ATTRIBUTE = "type"; 162 163 public static final String VALUE_TYPE_ATTRIBUTE = "value-type"; 164 165 public static final String KEY_TYPE_ATTRIBUTE = "key-type"; 166 167 public static final String PROPERTY_ELEMENT = "property"; 168 169 public static final String REF_ATTRIBUTE = "ref"; 170 171 public static final String VALUE_ATTRIBUTE = "value"; 172 173 public static final String LOOKUP_METHOD_ELEMENT = "lookup-method"; 174 175 public static final String REPLACED_METHOD_ELEMENT = "replaced-method"; 176 177 public static final String REPLACER_ATTRIBUTE = "replacer"; 178 179 public static final String ARG_TYPE_ELEMENT = "arg-type"; 180 181 public static final String ARG_TYPE_MATCH_ATTRIBUTE = "match"; 182 183 public static final String REF_ELEMENT = "ref"; 184 185 public static final String IDREF_ELEMENT = "idref"; 186 187 public static final String BEAN_REF_ATTRIBUTE = "bean"; 188 189 public static final String LOCAL_REF_ATTRIBUTE = "local"; 190 191 public static final String PARENT_REF_ATTRIBUTE = "parent"; 192 193 public static final String VALUE_ELEMENT = "value"; 194 195 public static final String NULL_ELEMENT = "null"; 196 197 public static final String ARRAY_ELEMENT = "array"; 198 199 public static final String LIST_ELEMENT = "list"; 200 201 public static final String SET_ELEMENT = "set"; 202 203 public static final String MAP_ELEMENT = "map"; 204 205 public static final String ENTRY_ELEMENT = "entry"; 206 207 public static final String KEY_ELEMENT = "key"; 208 209 public static final String KEY_ATTRIBUTE = "key"; 210 211 public static final String KEY_REF_ATTRIBUTE = "key-ref"; 212 213 public static final String VALUE_REF_ATTRIBUTE = "value-ref"; 214 215 public static final String PROPS_ELEMENT = "props"; 216 217 public static final String PROP_ELEMENT = "prop"; 218 219 public static final String MERGE_ATTRIBUTE = "merge"; 220 221 public static final String QUALIFIER_ELEMENT = "qualifier"; 222 223 public static final String QUALIFIER_ATTRIBUTE_ELEMENT = "attribute"; 224 225 public static final String DEFAULT_LAZY_INIT_ATTRIBUTE = "default-lazy-init"; 226 227 public static final String DEFAULT_MERGE_ATTRIBUTE = "default-merge"; 228 229 public static final String DEFAULT_AUTOWIRE_ATTRIBUTE = "default-autowire"; 230 231 public static final String DEFAULT_DEPENDENCY_CHECK_ATTRIBUTE = "default-dependency-check"; 232 233 public static final String DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE = "default-autowire-candidates"; 234 235 public static final String DEFAULT_INIT_METHOD_ATTRIBUTE = "default-init-method"; 236 237 public static final String DEFAULT_DESTROY_METHOD_ATTRIBUTE = "default-destroy-method"; 238 239 240 protected final Log logger = LogFactory.getLog(getClass()); 241 242 private final XmlReaderContext readerContext; 243 244 private final DocumentDefaultsDefinition defaults = new DocumentDefaultsDefinition(); 245 246 private final ParseState parseState = new ParseState(); 247 248 /** 249 * Stores all used bean names so we can enforce uniqueness on a per 250 * beans-element basis. Duplicate bean ids/names may not exist within the 251 * same level of beans element nesting, but may be duplicated across levels. 252 */ 253 private final Set<String> usedNames = new HashSet<String>(); 254 255 256 /** 257 * Create a new BeanDefinitionParserDelegate associated with the supplied 258 * {@link XmlReaderContext}. 259 */ 260 public BeanDefinitionParserDelegate(XmlReaderContext readerContext) { 261 Assert.notNull(readerContext, "XmlReaderContext must not be null"); 262 this.readerContext = readerContext; 263 } 264 265 266 /** 267 * Get the {@link XmlReaderContext} associated with this helper instance. 268 */ 269 public final XmlReaderContext getReaderContext() { 270 return this.readerContext; 271 } 272 273 /** 274 * Get the {@link Environment} associated with this helper instance. 275 * @deprecated in favor of {@link XmlReaderContext#getEnvironment()} 276 */ 277 @Deprecated 278 public final Environment getEnvironment() { 279 return this.readerContext.getEnvironment(); 280 } 281 282 /** 283 * Invoke the {@link org.springframework.beans.factory.parsing.SourceExtractor} to pull the 284 * source metadata from the supplied {@link Element}. 285 */ 286 protected Object extractSource(Element ele) { 287 return this.readerContext.extractSource(ele); 288 } 289 290 /** 291 * Report an error with the given message for the given source element. 292 */ 293 protected void error(String message, Node source) { 294 this.readerContext.error(message, source, this.parseState.snapshot()); 295 } 296 297 /** 298 * Report an error with the given message for the given source element. 299 */ 300 protected void error(String message, Element source) { 301 this.readerContext.error(message, source, this.parseState.snapshot()); 302 } 303 304 /** 305 * Report an error with the given message for the given source element. 306 */ 307 protected void error(String message, Element source, Throwable cause) { 308 this.readerContext.error(message, source, this.parseState.snapshot(), cause); 309 } 310 311 312 /** 313 * Initialize the default settings assuming a {@code null} parent delegate. 314 */ 315 public void initDefaults(Element root) { 316 initDefaults(root, null); 317 } 318 319 /** 320 * Initialize the default lazy-init, autowire, dependency check settings, 321 * init-method, destroy-method and merge settings. Support nested 'beans' 322 * element use cases by falling back to the given parent in case the 323 * defaults are not explicitly set locally. 324 * @see #populateDefaults(DocumentDefaultsDefinition, DocumentDefaultsDefinition, org.w3c.dom.Element) 325 * @see #getDefaults() 326 */ 327 public void initDefaults(Element root, BeanDefinitionParserDelegate parent) { 328 populateDefaults(this.defaults, (parent != null ? parent.defaults : null), root); 329 this.readerContext.fireDefaultsRegistered(this.defaults); 330 } 331 332 /** 333 * Populate the given DocumentDefaultsDefinition instance with the default lazy-init, 334 * autowire, dependency check settings, init-method, destroy-method and merge settings. 335 * Support nested 'beans' element use cases by falling back to {@code parentDefaults} 336 * in case the defaults are not explicitly set locally. 337 * @param defaults the defaults to populate 338 * @param parentDefaults the parent BeanDefinitionParserDelegate (if any) defaults to fall back to 339 * @param root the root element of the current bean definition document (or nested beans element) 340 */ 341 protected void populateDefaults(DocumentDefaultsDefinition defaults, DocumentDefaultsDefinition parentDefaults, Element root) { 342 String lazyInit = root.getAttribute(DEFAULT_LAZY_INIT_ATTRIBUTE); 343 if (isDefaultValue(lazyInit)) { 344 // Potentially inherited from outer <beans> sections, otherwise falling back to false. 345 lazyInit = (parentDefaults != null ? parentDefaults.getLazyInit() : FALSE_VALUE); 346 } 347 defaults.setLazyInit(lazyInit); 348 349 String merge = root.getAttribute(DEFAULT_MERGE_ATTRIBUTE); 350 if (isDefaultValue(merge)) { 351 // Potentially inherited from outer <beans> sections, otherwise falling back to false. 352 merge = (parentDefaults != null ? parentDefaults.getMerge() : FALSE_VALUE); 353 } 354 defaults.setMerge(merge); 355 356 String autowire = root.getAttribute(DEFAULT_AUTOWIRE_ATTRIBUTE); 357 if (isDefaultValue(autowire)) { 358 // Potentially inherited from outer <beans> sections, otherwise falling back to 'no'. 359 autowire = (parentDefaults != null ? parentDefaults.getAutowire() : AUTOWIRE_NO_VALUE); 360 } 361 defaults.setAutowire(autowire); 362 363 // Don't fall back to parentDefaults for dependency-check as it's no longer supported in 364 // <beans> as of 3.0. Therefore, no nested <beans> would ever need to fall back to it. 365 defaults.setDependencyCheck(root.getAttribute(DEFAULT_DEPENDENCY_CHECK_ATTRIBUTE)); 366 367 if (root.hasAttribute(DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE)) { 368 defaults.setAutowireCandidates(root.getAttribute(DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE)); 369 } 370 else if (parentDefaults != null) { 371 defaults.setAutowireCandidates(parentDefaults.getAutowireCandidates()); 372 } 373 374 if (root.hasAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE)) { 375 defaults.setInitMethod(root.getAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE)); 376 } 377 else if (parentDefaults != null) { 378 defaults.setInitMethod(parentDefaults.getInitMethod()); 379 } 380 381 if (root.hasAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE)) { 382 defaults.setDestroyMethod(root.getAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE)); 383 } 384 else if (parentDefaults != null) { 385 defaults.setDestroyMethod(parentDefaults.getDestroyMethod()); 386 } 387 388 defaults.setSource(this.readerContext.extractSource(root)); 389 } 390 391 /** 392 * Return the defaults definition object. 393 */ 394 public DocumentDefaultsDefinition getDefaults() { 395 return this.defaults; 396 } 397 398 /** 399 * Return the default settings for bean definitions as indicated within 400 * the attributes of the top-level {@code <beans/>} element. 401 */ 402 public BeanDefinitionDefaults getBeanDefinitionDefaults() { 403 BeanDefinitionDefaults bdd = new BeanDefinitionDefaults(); 404 bdd.setLazyInit(TRUE_VALUE.equalsIgnoreCase(this.defaults.getLazyInit())); 405 bdd.setAutowireMode(getAutowireMode(DEFAULT_VALUE)); 406 bdd.setDependencyCheck(getDependencyCheck(DEFAULT_VALUE)); 407 bdd.setInitMethodName(this.defaults.getInitMethod()); 408 bdd.setDestroyMethodName(this.defaults.getDestroyMethod()); 409 return bdd; 410 } 411 412 /** 413 * Return any patterns provided in the 'default-autowire-candidates' 414 * attribute of the top-level {@code <beans/>} element. 415 */ 416 public String[] getAutowireCandidatePatterns() { 417 String candidatePattern = this.defaults.getAutowireCandidates(); 418 return (candidatePattern != null ? StringUtils.commaDelimitedListToStringArray(candidatePattern) : null); 419 } 420 421 422 /** 423 * Parses the supplied {@code <bean>} element. May return {@code null} 424 * if there were errors during parse. Errors are reported to the 425 * {@link org.springframework.beans.factory.parsing.ProblemReporter}. 426 */ 427 public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) { 428 return parseBeanDefinitionElement(ele, null); 429 } 430 431 /** 432 * Parses the supplied {@code <bean>} element. May return {@code null} 433 * if there were errors during parse. Errors are reported to the 434 * {@link org.springframework.beans.factory.parsing.ProblemReporter}. 435 */ 436 public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) { 437 String id = ele.getAttribute(ID_ATTRIBUTE); 438 String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); 439 440 List<String> aliases = new ArrayList<String>(); 441 if (StringUtils.hasLength(nameAttr)) { 442 String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS); 443 aliases.addAll(Arrays.asList(nameArr)); 444 } 445 446 String beanName = id; 447 if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) { 448 beanName = aliases.remove(0); 449 if (logger.isDebugEnabled()) { 450 logger.debug("No XML 'id' specified - using '" + beanName + 451 "' as bean name and " + aliases + " as aliases"); 452 } 453 } 454 455 if (containingBean == null) { 456 checkNameUniqueness(beanName, aliases, ele); 457 } 458 459 AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); 460 if (beanDefinition != null) { 461 if (!StringUtils.hasText(beanName)) { 462 try { 463 if (containingBean != null) { 464 beanName = BeanDefinitionReaderUtils.generateBeanName( 465 beanDefinition, this.readerContext.getRegistry(), true); 466 } 467 else { 468 beanName = this.readerContext.generateBeanName(beanDefinition); 469 // Register an alias for the plain bean class name, if still possible, 470 // if the generator returned the class name plus a suffix. 471 // This is expected for Spring 1.2/2.0 backwards compatibility. 472 String beanClassName = beanDefinition.getBeanClassName(); 473 if (beanClassName != null && 474 beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && 475 !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) { 476 aliases.add(beanClassName); 477 } 478 } 479 if (logger.isDebugEnabled()) { 480 logger.debug("Neither XML 'id' nor 'name' specified - " + 481 "using generated bean name [" + beanName + "]"); 482 } 483 } 484 catch (Exception ex) { 485 error(ex.getMessage(), ele); 486 return null; 487 } 488 } 489 String[] aliasesArray = StringUtils.toStringArray(aliases); 490 return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); 491 } 492 493 return null; 494 } 495 496 /** 497 * Validate that the specified bean name and aliases have not been used already 498 * within the current level of beans element nesting. 499 */ 500 protected void checkNameUniqueness(String beanName, List<String> aliases, Element beanElement) { 501 String foundName = null; 502 503 if (StringUtils.hasText(beanName) && this.usedNames.contains(beanName)) { 504 foundName = beanName; 505 } 506 if (foundName == null) { 507 foundName = CollectionUtils.findFirstMatch(this.usedNames, aliases); 508 } 509 if (foundName != null) { 510 error("Bean name '" + foundName + "' is already used in this <beans> element", beanElement); 511 } 512 513 this.usedNames.add(beanName); 514 this.usedNames.addAll(aliases); 515 } 516 517 /** 518 * Parse the bean definition itself, without regard to name or aliases. May return 519 * {@code null} if problems occurred during the parsing of the bean definition. 520 */ 521 public AbstractBeanDefinition parseBeanDefinitionElement( 522 Element ele, String beanName, BeanDefinition containingBean) { 523 524 this.parseState.push(new BeanEntry(beanName)); 525 526 String className = null; 527 if (ele.hasAttribute(CLASS_ATTRIBUTE)) { 528 className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); 529 } 530 531 try { 532 String parent = null; 533 if (ele.hasAttribute(PARENT_ATTRIBUTE)) { 534 parent = ele.getAttribute(PARENT_ATTRIBUTE); 535 } 536 AbstractBeanDefinition bd = createBeanDefinition(className, parent); 537 538 parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); 539 bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT)); 540 541 parseMetaElements(ele, bd); 542 parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); 543 parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); 544 545 parseConstructorArgElements(ele, bd); 546 parsePropertyElements(ele, bd); 547 parseQualifierElements(ele, bd); 548 549 bd.setResource(this.readerContext.getResource()); 550 bd.setSource(extractSource(ele)); 551 552 return bd; 553 } 554 catch (ClassNotFoundException ex) { 555 error("Bean class [" + className + "] not found", ele, ex); 556 } 557 catch (NoClassDefFoundError err) { 558 error("Class that bean class [" + className + "] depends on not found", ele, err); 559 } 560 catch (Throwable ex) { 561 error("Unexpected failure during bean definition parsing", ele, ex); 562 } 563 finally { 564 this.parseState.pop(); 565 } 566 567 return null; 568 } 569 570 /** 571 * Apply the attributes of the given bean element to the given bean * definition. 572 * @param ele bean declaration element 573 * @param beanName bean name 574 * @param containingBean containing bean definition 575 * @return a bean definition initialized according to the bean element attributes 576 */ 577 public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName, 578 BeanDefinition containingBean, AbstractBeanDefinition bd) { 579 580 if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) { 581 error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele); 582 } 583 else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) { 584 bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE)); 585 } 586 else if (containingBean != null) { 587 // Take default from containing bean in case of an inner bean definition. 588 bd.setScope(containingBean.getScope()); 589 } 590 591 if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) { 592 bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE))); 593 } 594 595 String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE); 596 if (isDefaultValue(lazyInit)) { 597 lazyInit = this.defaults.getLazyInit(); 598 } 599 bd.setLazyInit(TRUE_VALUE.equals(lazyInit)); 600 601 String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE); 602 bd.setAutowireMode(getAutowireMode(autowire)); 603 604 String dependencyCheck = ele.getAttribute(DEPENDENCY_CHECK_ATTRIBUTE); 605 bd.setDependencyCheck(getDependencyCheck(dependencyCheck)); 606 607 if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) { 608 String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE); 609 bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS)); 610 } 611 612 String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE); 613 if (isDefaultValue(autowireCandidate)) { 614 String candidatePattern = this.defaults.getAutowireCandidates(); 615 if (candidatePattern != null) { 616 String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern); 617 bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName)); 618 } 619 } 620 else { 621 bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate)); 622 } 623 624 if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) { 625 bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE))); 626 } 627 628 if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) { 629 String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE); 630 if (!"".equals(initMethodName)) { 631 bd.setInitMethodName(initMethodName); 632 } 633 } 634 else { 635 if (this.defaults.getInitMethod() != null) { 636 bd.setInitMethodName(this.defaults.getInitMethod()); 637 bd.setEnforceInitMethod(false); 638 } 639 } 640 641 if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) { 642 String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE); 643 bd.setDestroyMethodName(destroyMethodName); 644 } 645 else { 646 if (this.defaults.getDestroyMethod() != null) { 647 bd.setDestroyMethodName(this.defaults.getDestroyMethod()); 648 bd.setEnforceDestroyMethod(false); 649 } 650 } 651 652 if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) { 653 bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE)); 654 } 655 if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) { 656 bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE)); 657 } 658 659 return bd; 660 } 661 662 /** 663 * Create a bean definition for the given class name and parent name. 664 * @param className the name of the bean class 665 * @param parentName the name of the bean's parent bean 666 * @return the newly created bean definition 667 * @throws ClassNotFoundException if bean class resolution was attempted but failed 668 */ 669 protected AbstractBeanDefinition createBeanDefinition(String className, String parentName) 670 throws ClassNotFoundException { 671 672 return BeanDefinitionReaderUtils.createBeanDefinition( 673 parentName, className, this.readerContext.getBeanClassLoader()); 674 } 675 676 /** 677 * Parse the meta elements underneath the given element, if any. 678 */ 679 public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) { 680 NodeList nl = ele.getChildNodes(); 681 for (int i = 0; i < nl.getLength(); i++) { 682 Node node = nl.item(i); 683 if (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) { 684 Element metaElement = (Element) node; 685 String key = metaElement.getAttribute(KEY_ATTRIBUTE); 686 String value = metaElement.getAttribute(VALUE_ATTRIBUTE); 687 BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value); 688 attribute.setSource(extractSource(metaElement)); 689 attributeAccessor.addMetadataAttribute(attribute); 690 } 691 } 692 } 693 694 /** 695 * Parse the given autowire attribute value into 696 * {@link AbstractBeanDefinition} autowire constants. 697 */ 698 @SuppressWarnings("deprecation") 699 public int getAutowireMode(String attrValue) { 700 String attr = attrValue; 701 if (isDefaultValue(attr)) { 702 attr = this.defaults.getAutowire(); 703 } 704 int autowire = AbstractBeanDefinition.AUTOWIRE_NO; 705 if (AUTOWIRE_BY_NAME_VALUE.equals(attr)) { 706 autowire = AbstractBeanDefinition.AUTOWIRE_BY_NAME; 707 } 708 else if (AUTOWIRE_BY_TYPE_VALUE.equals(attr)) { 709 autowire = AbstractBeanDefinition.AUTOWIRE_BY_TYPE; 710 } 711 else if (AUTOWIRE_CONSTRUCTOR_VALUE.equals(attr)) { 712 autowire = AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR; 713 } 714 else if (AUTOWIRE_AUTODETECT_VALUE.equals(attr)) { 715 autowire = AbstractBeanDefinition.AUTOWIRE_AUTODETECT; 716 } 717 // Else leave default value. 718 return autowire; 719 } 720 721 public int getDependencyCheck(String attValue) { 722 String att = attValue; 723 if (DEFAULT_VALUE.equals(att)) { 724 att = this.defaults.getDependencyCheck(); 725 } 726 if (DEPENDENCY_CHECK_ALL_ATTRIBUTE_VALUE.equals(att)) { 727 return AbstractBeanDefinition.DEPENDENCY_CHECK_ALL; 728 } 729 else if (DEPENDENCY_CHECK_OBJECTS_ATTRIBUTE_VALUE.equals(att)) { 730 return AbstractBeanDefinition.DEPENDENCY_CHECK_OBJECTS; 731 } 732 else if (DEPENDENCY_CHECK_SIMPLE_ATTRIBUTE_VALUE.equals(att)) { 733 return AbstractBeanDefinition.DEPENDENCY_CHECK_SIMPLE; 734 } 735 else { 736 return AbstractBeanDefinition.DEPENDENCY_CHECK_NONE; 737 } 738 } 739 740 /** 741 * Parse constructor-arg sub-elements of the given bean element. 742 */ 743 public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) { 744 NodeList nl = beanEle.getChildNodes(); 745 for (int i = 0; i < nl.getLength(); i++) { 746 Node node = nl.item(i); 747 if (isCandidateElement(node) && nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) { 748 parseConstructorArgElement((Element) node, bd); 749 } 750 } 751 } 752 753 /** 754 * Parse property sub-elements of the given bean element. 755 */ 756 public void parsePropertyElements(Element beanEle, BeanDefinition bd) { 757 NodeList nl = beanEle.getChildNodes(); 758 for (int i = 0; i < nl.getLength(); i++) { 759 Node node = nl.item(i); 760 if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) { 761 parsePropertyElement((Element) node, bd); 762 } 763 } 764 } 765 766 /** 767 * Parse qualifier sub-elements of the given bean element. 768 */ 769 public void parseQualifierElements(Element beanEle, AbstractBeanDefinition bd) { 770 NodeList nl = beanEle.getChildNodes(); 771 for (int i = 0; i < nl.getLength(); i++) { 772 Node node = nl.item(i); 773 if (isCandidateElement(node) && nodeNameEquals(node, QUALIFIER_ELEMENT)) { 774 parseQualifierElement((Element) node, bd); 775 } 776 } 777 } 778 779 /** 780 * Parse lookup-override sub-elements of the given bean element. 781 */ 782 public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) { 783 NodeList nl = beanEle.getChildNodes(); 784 for (int i = 0; i < nl.getLength(); i++) { 785 Node node = nl.item(i); 786 if (isCandidateElement(node) && nodeNameEquals(node, LOOKUP_METHOD_ELEMENT)) { 787 Element ele = (Element) node; 788 String methodName = ele.getAttribute(NAME_ATTRIBUTE); 789 String beanRef = ele.getAttribute(BEAN_ELEMENT); 790 LookupOverride override = new LookupOverride(methodName, beanRef); 791 override.setSource(extractSource(ele)); 792 overrides.addOverride(override); 793 } 794 } 795 } 796 797 /** 798 * Parse replaced-method sub-elements of the given bean element. 799 */ 800 public void parseReplacedMethodSubElements(Element beanEle, MethodOverrides overrides) { 801 NodeList nl = beanEle.getChildNodes(); 802 for (int i = 0; i < nl.getLength(); i++) { 803 Node node = nl.item(i); 804 if (isCandidateElement(node) && nodeNameEquals(node, REPLACED_METHOD_ELEMENT)) { 805 Element replacedMethodEle = (Element) node; 806 String name = replacedMethodEle.getAttribute(NAME_ATTRIBUTE); 807 String callback = replacedMethodEle.getAttribute(REPLACER_ATTRIBUTE); 808 ReplaceOverride replaceOverride = new ReplaceOverride(name, callback); 809 // Look for arg-type match elements. 810 List<Element> argTypeEles = DomUtils.getChildElementsByTagName(replacedMethodEle, ARG_TYPE_ELEMENT); 811 for (Element argTypeEle : argTypeEles) { 812 String match = argTypeEle.getAttribute(ARG_TYPE_MATCH_ATTRIBUTE); 813 match = (StringUtils.hasText(match) ? match : DomUtils.getTextValue(argTypeEle)); 814 if (StringUtils.hasText(match)) { 815 replaceOverride.addTypeIdentifier(match); 816 } 817 } 818 replaceOverride.setSource(extractSource(replacedMethodEle)); 819 overrides.addOverride(replaceOverride); 820 } 821 } 822 } 823 824 /** 825 * Parse a constructor-arg element. 826 */ 827 public void parseConstructorArgElement(Element ele, BeanDefinition bd) { 828 String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE); 829 String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE); 830 String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); 831 if (StringUtils.hasLength(indexAttr)) { 832 try { 833 int index = Integer.parseInt(indexAttr); 834 if (index < 0) { 835 error("'index' cannot be lower than 0", ele); 836 } 837 else { 838 try { 839 this.parseState.push(new ConstructorArgumentEntry(index)); 840 Object value = parsePropertyValue(ele, bd, null); 841 ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value); 842 if (StringUtils.hasLength(typeAttr)) { 843 valueHolder.setType(typeAttr); 844 } 845 if (StringUtils.hasLength(nameAttr)) { 846 valueHolder.setName(nameAttr); 847 } 848 valueHolder.setSource(extractSource(ele)); 849 if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) { 850 error("Ambiguous constructor-arg entries for index " + index, ele); 851 } 852 else { 853 bd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder); 854 } 855 } 856 finally { 857 this.parseState.pop(); 858 } 859 } 860 } 861 catch (NumberFormatException ex) { 862 error("Attribute 'index' of tag 'constructor-arg' must be an integer", ele); 863 } 864 } 865 else { 866 try { 867 this.parseState.push(new ConstructorArgumentEntry()); 868 Object value = parsePropertyValue(ele, bd, null); 869 ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value); 870 if (StringUtils.hasLength(typeAttr)) { 871 valueHolder.setType(typeAttr); 872 } 873 if (StringUtils.hasLength(nameAttr)) { 874 valueHolder.setName(nameAttr); 875 } 876 valueHolder.setSource(extractSource(ele)); 877 bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder); 878 } 879 finally { 880 this.parseState.pop(); 881 } 882 } 883 } 884 885 /** 886 * Parse a property element. 887 */ 888 public void parsePropertyElement(Element ele, BeanDefinition bd) { 889 String propertyName = ele.getAttribute(NAME_ATTRIBUTE); 890 if (!StringUtils.hasLength(propertyName)) { 891 error("Tag 'property' must have a 'name' attribute", ele); 892 return; 893 } 894 this.parseState.push(new PropertyEntry(propertyName)); 895 try { 896 if (bd.getPropertyValues().contains(propertyName)) { 897 error("Multiple 'property' definitions for property '" + propertyName + "'", ele); 898 return; 899 } 900 Object val = parsePropertyValue(ele, bd, propertyName); 901 PropertyValue pv = new PropertyValue(propertyName, val); 902 parseMetaElements(ele, pv); 903 pv.setSource(extractSource(ele)); 904 bd.getPropertyValues().addPropertyValue(pv); 905 } 906 finally { 907 this.parseState.pop(); 908 } 909 } 910 911 /** 912 * Parse a qualifier element. 913 */ 914 public void parseQualifierElement(Element ele, AbstractBeanDefinition bd) { 915 String typeName = ele.getAttribute(TYPE_ATTRIBUTE); 916 if (!StringUtils.hasLength(typeName)) { 917 error("Tag 'qualifier' must have a 'type' attribute", ele); 918 return; 919 } 920 this.parseState.push(new QualifierEntry(typeName)); 921 try { 922 AutowireCandidateQualifier qualifier = new AutowireCandidateQualifier(typeName); 923 qualifier.setSource(extractSource(ele)); 924 String value = ele.getAttribute(VALUE_ATTRIBUTE); 925 if (StringUtils.hasLength(value)) { 926 qualifier.setAttribute(AutowireCandidateQualifier.VALUE_KEY, value); 927 } 928 NodeList nl = ele.getChildNodes(); 929 for (int i = 0; i < nl.getLength(); i++) { 930 Node node = nl.item(i); 931 if (isCandidateElement(node) && nodeNameEquals(node, QUALIFIER_ATTRIBUTE_ELEMENT)) { 932 Element attributeEle = (Element) node; 933 String attributeName = attributeEle.getAttribute(KEY_ATTRIBUTE); 934 String attributeValue = attributeEle.getAttribute(VALUE_ATTRIBUTE); 935 if (StringUtils.hasLength(attributeName) && StringUtils.hasLength(attributeValue)) { 936 BeanMetadataAttribute attribute = new BeanMetadataAttribute(attributeName, attributeValue); 937 attribute.setSource(extractSource(attributeEle)); 938 qualifier.addMetadataAttribute(attribute); 939 } 940 else { 941 error("Qualifier 'attribute' tag must have a 'name' and 'value'", attributeEle); 942 return; 943 } 944 } 945 } 946 bd.addQualifier(qualifier); 947 } 948 finally { 949 this.parseState.pop(); 950 } 951 } 952 953 /** 954 * Get the value of a property element. May be a list etc. 955 * Also used for constructor arguments, "propertyName" being null in this case. 956 */ 957 public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) { 958 String elementName = (propertyName != null) ? 959 "<property> element for property '" + propertyName + "'" : 960 "<constructor-arg> element"; 961 962 // Should only have one child element: ref, value, list, etc. 963 NodeList nl = ele.getChildNodes(); 964 Element subElement = null; 965 for (int i = 0; i < nl.getLength(); i++) { 966 Node node = nl.item(i); 967 if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) && 968 !nodeNameEquals(node, META_ELEMENT)) { 969 // Child element is what we're looking for. 970 if (subElement != null) { 971 error(elementName + " must not contain more than one sub-element", ele); 972 } 973 else { 974 subElement = (Element) node; 975 } 976 } 977 } 978 979 boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE); 980 boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE); 981 if ((hasRefAttribute && hasValueAttribute) || 982 ((hasRefAttribute || hasValueAttribute) && subElement != null)) { 983 error(elementName + 984 " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele); 985 } 986 987 if (hasRefAttribute) { 988 String refName = ele.getAttribute(REF_ATTRIBUTE); 989 if (!StringUtils.hasText(refName)) { 990 error(elementName + " contains empty 'ref' attribute", ele); 991 } 992 RuntimeBeanReference ref = new RuntimeBeanReference(refName); 993 ref.setSource(extractSource(ele)); 994 return ref; 995 } 996 else if (hasValueAttribute) { 997 TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE)); 998 valueHolder.setSource(extractSource(ele)); 999 return valueHolder; 1000 } 1001 else if (subElement != null) { 1002 return parsePropertySubElement(subElement, bd); 1003 } 1004 else { 1005 // Neither child element nor "ref" or "value" attribute found. 1006 error(elementName + " must specify a ref or value", ele); 1007 return null; 1008 } 1009 } 1010 1011 /** 1012 * Parse a value, ref or collection sub-element of a property or 1013 * constructor-arg element. 1014 * @param ele subelement of property element; we don't know which yet 1015 * @param bd the current bean definition (if any) 1016 */ 1017 public Object parsePropertySubElement(Element ele, BeanDefinition bd) { 1018 return parsePropertySubElement(ele, bd, null); 1019 } 1020 1021 /** 1022 * Parse a value, ref or collection sub-element of a property or 1023 * constructor-arg element. 1024 * @param ele subelement of property element; we don't know which yet 1025 * @param bd the current bean definition (if any) 1026 * @param defaultValueType the default type (class name) for any 1027 * {@code <value>} tag that might be created 1028 */ 1029 public Object parsePropertySubElement(Element ele, BeanDefinition bd, String defaultValueType) { 1030 if (!isDefaultNamespace(ele)) { 1031 return parseNestedCustomElement(ele, bd); 1032 } 1033 else if (nodeNameEquals(ele, BEAN_ELEMENT)) { 1034 BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd); 1035 if (nestedBd != null) { 1036 nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd); 1037 } 1038 return nestedBd; 1039 } 1040 else if (nodeNameEquals(ele, REF_ELEMENT)) { 1041 // A generic reference to any name of any bean. 1042 String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE); 1043 boolean toParent = false; 1044 if (!StringUtils.hasLength(refName)) { 1045 // A reference to the id of another bean in the same XML file. 1046 refName = ele.getAttribute(LOCAL_REF_ATTRIBUTE); 1047 if (!StringUtils.hasLength(refName)) { 1048 // A reference to the id of another bean in a parent context. 1049 refName = ele.getAttribute(PARENT_REF_ATTRIBUTE); 1050 toParent = true; 1051 if (!StringUtils.hasLength(refName)) { 1052 error("'bean', 'local' or 'parent' is required for <ref> element", ele); 1053 return null; 1054 } 1055 } 1056 } 1057 if (!StringUtils.hasText(refName)) { 1058 error("<ref> element contains empty target attribute", ele); 1059 return null; 1060 } 1061 RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent); 1062 ref.setSource(extractSource(ele)); 1063 return ref; 1064 } 1065 else if (nodeNameEquals(ele, IDREF_ELEMENT)) { 1066 return parseIdRefElement(ele); 1067 } 1068 else if (nodeNameEquals(ele, VALUE_ELEMENT)) { 1069 return parseValueElement(ele, defaultValueType); 1070 } 1071 else if (nodeNameEquals(ele, NULL_ELEMENT)) { 1072 // It's a distinguished null value. Let's wrap it in a TypedStringValue 1073 // object in order to preserve the source location. 1074 TypedStringValue nullHolder = new TypedStringValue(null); 1075 nullHolder.setSource(extractSource(ele)); 1076 return nullHolder; 1077 } 1078 else if (nodeNameEquals(ele, ARRAY_ELEMENT)) { 1079 return parseArrayElement(ele, bd); 1080 } 1081 else if (nodeNameEquals(ele, LIST_ELEMENT)) { 1082 return parseListElement(ele, bd); 1083 } 1084 else if (nodeNameEquals(ele, SET_ELEMENT)) { 1085 return parseSetElement(ele, bd); 1086 } 1087 else if (nodeNameEquals(ele, MAP_ELEMENT)) { 1088 return parseMapElement(ele, bd); 1089 } 1090 else if (nodeNameEquals(ele, PROPS_ELEMENT)) { 1091 return parsePropsElement(ele); 1092 } 1093 else { 1094 error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele); 1095 return null; 1096 } 1097 } 1098 1099 /** 1100 * Return a typed String value Object for the given 'idref' element. 1101 */ 1102 public Object parseIdRefElement(Element ele) { 1103 // A generic reference to any name of any bean. 1104 String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE); 1105 if (!StringUtils.hasLength(refName)) { 1106 // A reference to the id of another bean in the same XML file. 1107 refName = ele.getAttribute(LOCAL_REF_ATTRIBUTE); 1108 if (!StringUtils.hasLength(refName)) { 1109 error("Either 'bean' or 'local' is required for <idref> element", ele); 1110 return null; 1111 } 1112 } 1113 if (!StringUtils.hasText(refName)) { 1114 error("<idref> element contains empty target attribute", ele); 1115 return null; 1116 } 1117 RuntimeBeanNameReference ref = new RuntimeBeanNameReference(refName); 1118 ref.setSource(extractSource(ele)); 1119 return ref; 1120 } 1121 1122 /** 1123 * Return a typed String value Object for the given value element. 1124 */ 1125 public Object parseValueElement(Element ele, String defaultTypeName) { 1126 // It's a literal value. 1127 String value = DomUtils.getTextValue(ele); 1128 String specifiedTypeName = ele.getAttribute(TYPE_ATTRIBUTE); 1129 String typeName = specifiedTypeName; 1130 if (!StringUtils.hasText(typeName)) { 1131 typeName = defaultTypeName; 1132 } 1133 try { 1134 TypedStringValue typedValue = buildTypedStringValue(value, typeName); 1135 typedValue.setSource(extractSource(ele)); 1136 typedValue.setSpecifiedTypeName(specifiedTypeName); 1137 return typedValue; 1138 } 1139 catch (ClassNotFoundException ex) { 1140 error("Type class [" + typeName + "] not found for <value> element", ele, ex); 1141 return value; 1142 } 1143 } 1144 1145 /** 1146 * Build a typed String value Object for the given raw value. 1147 * @see org.springframework.beans.factory.config.TypedStringValue 1148 */ 1149 protected TypedStringValue buildTypedStringValue(String value, String targetTypeName) 1150 throws ClassNotFoundException { 1151 1152 ClassLoader classLoader = this.readerContext.getBeanClassLoader(); 1153 TypedStringValue typedValue; 1154 if (!StringUtils.hasText(targetTypeName)) { 1155 typedValue = new TypedStringValue(value); 1156 } 1157 else if (classLoader != null) { 1158 Class<?> targetType = ClassUtils.forName(targetTypeName, classLoader); 1159 typedValue = new TypedStringValue(value, targetType); 1160 } 1161 else { 1162 typedValue = new TypedStringValue(value, targetTypeName); 1163 } 1164 return typedValue; 1165 } 1166 1167 /** 1168 * Parse an array element. 1169 */ 1170 public Object parseArrayElement(Element arrayEle, BeanDefinition bd) { 1171 String elementType = arrayEle.getAttribute(VALUE_TYPE_ATTRIBUTE); 1172 NodeList nl = arrayEle.getChildNodes(); 1173 ManagedArray target = new ManagedArray(elementType, nl.getLength()); 1174 target.setSource(extractSource(arrayEle)); 1175 target.setElementTypeName(elementType); 1176 target.setMergeEnabled(parseMergeAttribute(arrayEle)); 1177 parseCollectionElements(nl, target, bd, elementType); 1178 return target; 1179 } 1180 1181 /** 1182 * Parse a list element. 1183 */ 1184 public List<Object> parseListElement(Element collectionEle, BeanDefinition bd) { 1185 String defaultElementType = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE); 1186 NodeList nl = collectionEle.getChildNodes(); 1187 ManagedList<Object> target = new ManagedList<Object>(nl.getLength()); 1188 target.setSource(extractSource(collectionEle)); 1189 target.setElementTypeName(defaultElementType); 1190 target.setMergeEnabled(parseMergeAttribute(collectionEle)); 1191 parseCollectionElements(nl, target, bd, defaultElementType); 1192 return target; 1193 } 1194 1195 /** 1196 * Parse a set element. 1197 */ 1198 public Set<Object> parseSetElement(Element collectionEle, BeanDefinition bd) { 1199 String defaultElementType = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE); 1200 NodeList nl = collectionEle.getChildNodes(); 1201 ManagedSet<Object> target = new ManagedSet<Object>(nl.getLength()); 1202 target.setSource(extractSource(collectionEle)); 1203 target.setElementTypeName(defaultElementType); 1204 target.setMergeEnabled(parseMergeAttribute(collectionEle)); 1205 parseCollectionElements(nl, target, bd, defaultElementType); 1206 return target; 1207 } 1208 1209 protected void parseCollectionElements( 1210 NodeList elementNodes, Collection<Object> target, BeanDefinition bd, String defaultElementType) { 1211 1212 for (int i = 0; i < elementNodes.getLength(); i++) { 1213 Node node = elementNodes.item(i); 1214 if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT)) { 1215 target.add(parsePropertySubElement((Element) node, bd, defaultElementType)); 1216 } 1217 } 1218 } 1219 1220 /** 1221 * Parse a map element. 1222 */ 1223 public Map<Object, Object> parseMapElement(Element mapEle, BeanDefinition bd) { 1224 String defaultKeyType = mapEle.getAttribute(KEY_TYPE_ATTRIBUTE); 1225 String defaultValueType = mapEle.getAttribute(VALUE_TYPE_ATTRIBUTE); 1226 1227 List<Element> entryEles = DomUtils.getChildElementsByTagName(mapEle, ENTRY_ELEMENT); 1228 ManagedMap<Object, Object> map = new ManagedMap<Object, Object>(entryEles.size()); 1229 map.setSource(extractSource(mapEle)); 1230 map.setKeyTypeName(defaultKeyType); 1231 map.setValueTypeName(defaultValueType); 1232 map.setMergeEnabled(parseMergeAttribute(mapEle)); 1233 1234 for (Element entryEle : entryEles) { 1235 // Should only have one value child element: ref, value, list, etc. 1236 // Optionally, there might be a key child element. 1237 NodeList entrySubNodes = entryEle.getChildNodes(); 1238 Element keyEle = null; 1239 Element valueEle = null; 1240 for (int j = 0; j < entrySubNodes.getLength(); j++) { 1241 Node node = entrySubNodes.item(j); 1242 if (node instanceof Element) { 1243 Element candidateEle = (Element) node; 1244 if (nodeNameEquals(candidateEle, KEY_ELEMENT)) { 1245 if (keyEle != null) { 1246 error("<entry> element is only allowed to contain one <key> sub-element", entryEle); 1247 } 1248 else { 1249 keyEle = candidateEle; 1250 } 1251 } 1252 else { 1253 // Child element is what we're looking for. 1254 if (nodeNameEquals(candidateEle, DESCRIPTION_ELEMENT)) { 1255 // the element is a <description> -> ignore it 1256 } 1257 else if (valueEle != null) { 1258 error("<entry> element must not contain more than one value sub-element", entryEle); 1259 } 1260 else { 1261 valueEle = candidateEle; 1262 } 1263 } 1264 } 1265 } 1266 1267 // Extract key from attribute or sub-element. 1268 Object key = null; 1269 boolean hasKeyAttribute = entryEle.hasAttribute(KEY_ATTRIBUTE); 1270 boolean hasKeyRefAttribute = entryEle.hasAttribute(KEY_REF_ATTRIBUTE); 1271 if ((hasKeyAttribute && hasKeyRefAttribute) || 1272 (hasKeyAttribute || hasKeyRefAttribute) && keyEle != null) { 1273 error("<entry> element is only allowed to contain either " + 1274 "a 'key' attribute OR a 'key-ref' attribute OR a <key> sub-element", entryEle); 1275 } 1276 if (hasKeyAttribute) { 1277 key = buildTypedStringValueForMap(entryEle.getAttribute(KEY_ATTRIBUTE), defaultKeyType, entryEle); 1278 } 1279 else if (hasKeyRefAttribute) { 1280 String refName = entryEle.getAttribute(KEY_REF_ATTRIBUTE); 1281 if (!StringUtils.hasText(refName)) { 1282 error("<entry> element contains empty 'key-ref' attribute", entryEle); 1283 } 1284 RuntimeBeanReference ref = new RuntimeBeanReference(refName); 1285 ref.setSource(extractSource(entryEle)); 1286 key = ref; 1287 } 1288 else if (keyEle != null) { 1289 key = parseKeyElement(keyEle, bd, defaultKeyType); 1290 } 1291 else { 1292 error("<entry> element must specify a key", entryEle); 1293 } 1294 1295 // Extract value from attribute or sub-element. 1296 Object value = null; 1297 boolean hasValueAttribute = entryEle.hasAttribute(VALUE_ATTRIBUTE); 1298 boolean hasValueRefAttribute = entryEle.hasAttribute(VALUE_REF_ATTRIBUTE); 1299 boolean hasValueTypeAttribute = entryEle.hasAttribute(VALUE_TYPE_ATTRIBUTE); 1300 if ((hasValueAttribute && hasValueRefAttribute) || 1301 (hasValueAttribute || hasValueRefAttribute) && valueEle != null) { 1302 error("<entry> element is only allowed to contain either " + 1303 "'value' attribute OR 'value-ref' attribute OR <value> sub-element", entryEle); 1304 } 1305 if ((hasValueTypeAttribute && hasValueRefAttribute) || 1306 (hasValueTypeAttribute && !hasValueAttribute) || 1307 (hasValueTypeAttribute && valueEle != null)) { 1308 error("<entry> element is only allowed to contain a 'value-type' " + 1309 "attribute when it has a 'value' attribute", entryEle); 1310 } 1311 if (hasValueAttribute) { 1312 String valueType = entryEle.getAttribute(VALUE_TYPE_ATTRIBUTE); 1313 if (!StringUtils.hasText(valueType)) { 1314 valueType = defaultValueType; 1315 } 1316 value = buildTypedStringValueForMap(entryEle.getAttribute(VALUE_ATTRIBUTE), valueType, entryEle); 1317 } 1318 else if (hasValueRefAttribute) { 1319 String refName = entryEle.getAttribute(VALUE_REF_ATTRIBUTE); 1320 if (!StringUtils.hasText(refName)) { 1321 error("<entry> element contains empty 'value-ref' attribute", entryEle); 1322 } 1323 RuntimeBeanReference ref = new RuntimeBeanReference(refName); 1324 ref.setSource(extractSource(entryEle)); 1325 value = ref; 1326 } 1327 else if (valueEle != null) { 1328 value = parsePropertySubElement(valueEle, bd, defaultValueType); 1329 } 1330 else { 1331 error("<entry> element must specify a value", entryEle); 1332 } 1333 1334 // Add final key and value to the Map. 1335 map.put(key, value); 1336 } 1337 1338 return map; 1339 } 1340 1341 /** 1342 * Build a typed String value Object for the given raw value. 1343 * @see org.springframework.beans.factory.config.TypedStringValue 1344 */ 1345 protected final Object buildTypedStringValueForMap(String value, String defaultTypeName, Element entryEle) { 1346 try { 1347 TypedStringValue typedValue = buildTypedStringValue(value, defaultTypeName); 1348 typedValue.setSource(extractSource(entryEle)); 1349 return typedValue; 1350 } 1351 catch (ClassNotFoundException ex) { 1352 error("Type class [" + defaultTypeName + "] not found for Map key/value type", entryEle, ex); 1353 return value; 1354 } 1355 } 1356 1357 /** 1358 * Parse a key sub-element of a map element. 1359 */ 1360 protected Object parseKeyElement(Element keyEle, BeanDefinition bd, String defaultKeyTypeName) { 1361 NodeList nl = keyEle.getChildNodes(); 1362 Element subElement = null; 1363 for (int i = 0; i < nl.getLength(); i++) { 1364 Node node = nl.item(i); 1365 if (node instanceof Element) { 1366 // Child element is what we're looking for. 1367 if (subElement != null) { 1368 error("<key> element must not contain more than one value sub-element", keyEle); 1369 } 1370 else { 1371 subElement = (Element) node; 1372 } 1373 } 1374 } 1375 return parsePropertySubElement(subElement, bd, defaultKeyTypeName); 1376 } 1377 1378 /** 1379 * Parse a props element. 1380 */ 1381 public Properties parsePropsElement(Element propsEle) { 1382 ManagedProperties props = new ManagedProperties(); 1383 props.setSource(extractSource(propsEle)); 1384 props.setMergeEnabled(parseMergeAttribute(propsEle)); 1385 1386 List<Element> propEles = DomUtils.getChildElementsByTagName(propsEle, PROP_ELEMENT); 1387 for (Element propEle : propEles) { 1388 String key = propEle.getAttribute(KEY_ATTRIBUTE); 1389 // Trim the text value to avoid unwanted whitespace 1390 // caused by typical XML formatting. 1391 String value = DomUtils.getTextValue(propEle).trim(); 1392 TypedStringValue keyHolder = new TypedStringValue(key); 1393 keyHolder.setSource(extractSource(propEle)); 1394 TypedStringValue valueHolder = new TypedStringValue(value); 1395 valueHolder.setSource(extractSource(propEle)); 1396 props.put(keyHolder, valueHolder); 1397 } 1398 1399 return props; 1400 } 1401 1402 /** 1403 * Parse the merge attribute of a collection element, if any. 1404 */ 1405 public boolean parseMergeAttribute(Element collectionElement) { 1406 String value = collectionElement.getAttribute(MERGE_ATTRIBUTE); 1407 if (isDefaultValue(value)) { 1408 value = this.defaults.getMerge(); 1409 } 1410 return TRUE_VALUE.equals(value); 1411 } 1412 1413 /** 1414 * Parse a custom element (outside of the default namespace). 1415 * @param ele the element to parse 1416 * @return the resulting bean definition 1417 */ 1418 public BeanDefinition parseCustomElement(Element ele) { 1419 return parseCustomElement(ele, null); 1420 } 1421 1422 /** 1423 * Parse a custom element (outside of the default namespace). 1424 * @param ele the element to parse 1425 * @param containingBd the containing bean definition (if any) 1426 * @return the resulting bean definition 1427 */ 1428 public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) { 1429 String namespaceUri = getNamespaceURI(ele); 1430 NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); 1431 if (handler == null) { 1432 error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele); 1433 return null; 1434 } 1435 return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); 1436 } 1437 1438 /** 1439 * Decorate the given bean definition through a namespace handler, if applicable. 1440 * @param ele the current element 1441 * @param originalDef the current bean definition 1442 * @return the decorated bean definition 1443 */ 1444 public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder originalDef) { 1445 return decorateBeanDefinitionIfRequired(ele, originalDef, null); 1446 } 1447 1448 /** 1449 * Decorate the given bean definition through a namespace handler, if applicable. 1450 * @param ele the current element 1451 * @param originalDef the current bean definition 1452 * @param containingBd the containing bean definition (if any) 1453 * @return the decorated bean definition 1454 */ 1455 public BeanDefinitionHolder decorateBeanDefinitionIfRequired( 1456 Element ele, BeanDefinitionHolder originalDef, BeanDefinition containingBd) { 1457 1458 BeanDefinitionHolder finalDefinition = originalDef; 1459 1460 // Decorate based on custom attributes first. 1461 NamedNodeMap attributes = ele.getAttributes(); 1462 for (int i = 0; i < attributes.getLength(); i++) { 1463 Node node = attributes.item(i); 1464 finalDefinition = decorateIfRequired(node, finalDefinition, containingBd); 1465 } 1466 1467 // Decorate based on custom nested elements. 1468 NodeList children = ele.getChildNodes(); 1469 for (int i = 0; i < children.getLength(); i++) { 1470 Node node = children.item(i); 1471 if (node.getNodeType() == Node.ELEMENT_NODE) { 1472 finalDefinition = decorateIfRequired(node, finalDefinition, containingBd); 1473 } 1474 } 1475 return finalDefinition; 1476 } 1477 1478 /** 1479 * Decorate the given bean definition through a namespace handler, 1480 * if applicable. 1481 * @param node the current child node 1482 * @param originalDef the current bean definition 1483 * @param containingBd the containing bean definition (if any) 1484 * @return the decorated bean definition 1485 */ 1486 public BeanDefinitionHolder decorateIfRequired( 1487 Node node, BeanDefinitionHolder originalDef, BeanDefinition containingBd) { 1488 1489 String namespaceUri = getNamespaceURI(node); 1490 if (!isDefaultNamespace(namespaceUri)) { 1491 NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); 1492 if (handler != null) { 1493 return handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd)); 1494 } 1495 else if (namespaceUri != null && namespaceUri.startsWith("http://www.springframework.org/")) { 1496 error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", node); 1497 } 1498 else { 1499 // A custom namespace, not to be handled by Spring - maybe "xml:...". 1500 if (logger.isDebugEnabled()) { 1501 logger.debug("No Spring NamespaceHandler found for XML schema namespace [" + namespaceUri + "]"); 1502 } 1503 } 1504 } 1505 return originalDef; 1506 } 1507 1508 private BeanDefinitionHolder parseNestedCustomElement(Element ele, BeanDefinition containingBd) { 1509 BeanDefinition innerDefinition = parseCustomElement(ele, containingBd); 1510 if (innerDefinition == null) { 1511 error("Incorrect usage of element '" + ele.getNodeName() + "' in a nested manner. " + 1512 "This tag cannot be used nested inside <property>.", ele); 1513 return null; 1514 } 1515 String id = ele.getNodeName() + BeanDefinitionReaderUtils.GENERATED_BEAN_NAME_SEPARATOR + 1516 ObjectUtils.getIdentityHexString(innerDefinition); 1517 if (logger.isDebugEnabled()) { 1518 logger.debug("Using generated bean name [" + id + 1519 "] for nested custom element '" + ele.getNodeName() + "'"); 1520 } 1521 return new BeanDefinitionHolder(innerDefinition, id); 1522 } 1523 1524 1525 /** 1526 * Get the namespace URI for the supplied node. 1527 * <p>The default implementation uses {@link Node#getNamespaceURI}. 1528 * Subclasses may override the default implementation to provide a 1529 * different namespace identification mechanism. 1530 * @param node the node 1531 */ 1532 public String getNamespaceURI(Node node) { 1533 return node.getNamespaceURI(); 1534 } 1535 1536 /** 1537 * Get the local name for the supplied {@link Node}. 1538 * <p>The default implementation calls {@link Node#getLocalName}. 1539 * Subclasses may override the default implementation to provide a 1540 * different mechanism for getting the local name. 1541 * @param node the {@code Node} 1542 */ 1543 public String getLocalName(Node node) { 1544 return node.getLocalName(); 1545 } 1546 1547 /** 1548 * Determine whether the name of the supplied node is equal to the supplied name. 1549 * <p>The default implementation checks the supplied desired name against both 1550 * {@link Node#getNodeName()} and {@link Node#getLocalName()}. 1551 * <p>Subclasses may override the default implementation to provide a different 1552 * mechanism for comparing node names. 1553 * @param node the node to compare 1554 * @param desiredName the name to check for 1555 */ 1556 public boolean nodeNameEquals(Node node, String desiredName) { 1557 return desiredName.equals(node.getNodeName()) || desiredName.equals(getLocalName(node)); 1558 } 1559 1560 /** 1561 * Determine whether the given URI indicates the default namespace. 1562 */ 1563 public boolean isDefaultNamespace(String namespaceUri) { 1564 return (!StringUtils.hasLength(namespaceUri) || BEANS_NAMESPACE_URI.equals(namespaceUri)); 1565 } 1566 1567 /** 1568 * Determine whether the given node indicates the default namespace. 1569 */ 1570 public boolean isDefaultNamespace(Node node) { 1571 return isDefaultNamespace(getNamespaceURI(node)); 1572 } 1573 1574 private boolean isDefaultValue(String value) { 1575 return (DEFAULT_VALUE.equals(value) || "".equals(value)); 1576 } 1577 1578 private boolean isCandidateElement(Node node) { 1579 return (node instanceof Element && (isDefaultNamespace(node) || !isDefaultNamespace(node.getParentNode()))); 1580 } 1581 1582}