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