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}