0001: /*
0002: * Copyright 2002-2007 the original author or authors.
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016:
0017: package org.springframework.beans.factory.xml;
0018:
0019: import java.util.ArrayList;
0020: import java.util.Arrays;
0021: import java.util.HashSet;
0022: import java.util.Iterator;
0023: import java.util.List;
0024: import java.util.Map;
0025: import java.util.Properties;
0026: import java.util.Set;
0027:
0028: import org.apache.commons.logging.Log;
0029: import org.apache.commons.logging.LogFactory;
0030: import org.w3c.dom.Element;
0031: import org.w3c.dom.NamedNodeMap;
0032: import org.w3c.dom.Node;
0033: import org.w3c.dom.NodeList;
0034:
0035: import org.springframework.beans.BeanMetadataAttribute;
0036: import org.springframework.beans.BeanMetadataAttributeAccessor;
0037: import org.springframework.beans.PropertyValue;
0038: import org.springframework.beans.factory.BeanDefinitionStoreException;
0039: import org.springframework.beans.factory.config.BeanDefinition;
0040: import org.springframework.beans.factory.config.BeanDefinitionHolder;
0041: import org.springframework.beans.factory.config.ConstructorArgumentValues;
0042: import org.springframework.beans.factory.config.RuntimeBeanNameReference;
0043: import org.springframework.beans.factory.config.RuntimeBeanReference;
0044: import org.springframework.beans.factory.config.TypedStringValue;
0045: import org.springframework.beans.factory.parsing.BeanEntry;
0046: import org.springframework.beans.factory.parsing.ConstructorArgumentEntry;
0047: import org.springframework.beans.factory.parsing.ParseState;
0048: import org.springframework.beans.factory.parsing.PropertyEntry;
0049: import org.springframework.beans.factory.parsing.QualifierEntry;
0050: import org.springframework.beans.factory.support.AbstractBeanDefinition;
0051: import org.springframework.beans.factory.support.AutowireCandidateQualifier;
0052: import org.springframework.beans.factory.support.BeanDefinitionDefaults;
0053: import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
0054: import org.springframework.beans.factory.support.LookupOverride;
0055: import org.springframework.beans.factory.support.ManagedList;
0056: import org.springframework.beans.factory.support.ManagedMap;
0057: import org.springframework.beans.factory.support.ManagedProperties;
0058: import org.springframework.beans.factory.support.ManagedSet;
0059: import org.springframework.beans.factory.support.MethodOverrides;
0060: import org.springframework.beans.factory.support.ReplaceOverride;
0061: import org.springframework.util.Assert;
0062: import org.springframework.util.ClassUtils;
0063: import org.springframework.util.CollectionUtils;
0064: import org.springframework.util.ObjectUtils;
0065: import org.springframework.util.PatternMatchUtils;
0066: import org.springframework.util.StringUtils;
0067: import org.springframework.util.xml.DomUtils;
0068:
0069: /**
0070: * Stateful delegate class used to parse XML bean definitions.
0071: * Intended for use by both the main parser and any extension
0072: * {@link BeanDefinitionParser BeanDefinitionParsers}
0073: * or {@link BeanDefinitionDecorator BeanDefinitionDecorators}.
0074: *
0075: * @author Rob Harrop
0076: * @author Juergen Hoeller
0077: * @author Rod Johnson
0078: * @author Mark Fisher
0079: * @since 2.0
0080: * @see ParserContext
0081: * @see DefaultBeanDefinitionDocumentReader
0082: */
0083: public class BeanDefinitionParserDelegate {
0084:
0085: public static final String BEANS_NAMESPACE_URI = "http://www.springframework.org/schema/beans";
0086:
0087: public static final String BEAN_NAME_DELIMITERS = ",; ";
0088:
0089: /**
0090: * Value of a T/F attribute that represents true.
0091: * Anything else represents false. Case seNsItive.
0092: */
0093: public static final String TRUE_VALUE = "true";
0094:
0095: public static final String DEFAULT_VALUE = "default";
0096:
0097: public static final String DESCRIPTION_ELEMENT = "description";
0098:
0099: public static final String AUTOWIRE_BY_NAME_VALUE = "byName";
0100:
0101: public static final String AUTOWIRE_BY_TYPE_VALUE = "byType";
0102:
0103: public static final String AUTOWIRE_CONSTRUCTOR_VALUE = "constructor";
0104:
0105: public static final String AUTOWIRE_AUTODETECT_VALUE = "autodetect";
0106:
0107: public static final String DEPENDENCY_CHECK_ALL_ATTRIBUTE_VALUE = "all";
0108:
0109: public static final String DEPENDENCY_CHECK_SIMPLE_ATTRIBUTE_VALUE = "simple";
0110:
0111: public static final String DEPENDENCY_CHECK_OBJECTS_ATTRIBUTE_VALUE = "objects";
0112:
0113: public static final String NAME_ATTRIBUTE = "name";
0114:
0115: public static final String BEAN_ELEMENT = "bean";
0116:
0117: public static final String META_ELEMENT = "meta";
0118:
0119: public static final String ID_ATTRIBUTE = "id";
0120:
0121: public static final String PARENT_ATTRIBUTE = "parent";
0122:
0123: public static final String CLASS_ATTRIBUTE = "class";
0124:
0125: public static final String ABSTRACT_ATTRIBUTE = "abstract";
0126:
0127: public static final String SCOPE_ATTRIBUTE = "scope";
0128:
0129: public static final String SINGLETON_ATTRIBUTE = "singleton";
0130:
0131: public static final String LAZY_INIT_ATTRIBUTE = "lazy-init";
0132:
0133: public static final String AUTOWIRE_ATTRIBUTE = "autowire";
0134:
0135: public static final String AUTOWIRE_CANDIDATE_ATTRIBUTE = "autowire-candidate";
0136:
0137: public static final String PRIMARY_ATTRIBUTE = "primary";
0138:
0139: public static final String DEPENDENCY_CHECK_ATTRIBUTE = "dependency-check";
0140:
0141: public static final String DEPENDS_ON_ATTRIBUTE = "depends-on";
0142:
0143: public static final String INIT_METHOD_ATTRIBUTE = "init-method";
0144:
0145: public static final String DESTROY_METHOD_ATTRIBUTE = "destroy-method";
0146:
0147: public static final String FACTORY_METHOD_ATTRIBUTE = "factory-method";
0148:
0149: public static final String FACTORY_BEAN_ATTRIBUTE = "factory-bean";
0150:
0151: public static final String CONSTRUCTOR_ARG_ELEMENT = "constructor-arg";
0152:
0153: public static final String INDEX_ATTRIBUTE = "index";
0154:
0155: public static final String TYPE_ATTRIBUTE = "type";
0156:
0157: public static final String VALUE_TYPE_ATTRIBUTE = "value-type";
0158:
0159: public static final String KEY_TYPE_ATTRIBUTE = "key-type";
0160:
0161: public static final String PROPERTY_ELEMENT = "property";
0162:
0163: public static final String REF_ATTRIBUTE = "ref";
0164:
0165: public static final String VALUE_ATTRIBUTE = "value";
0166:
0167: public static final String LOOKUP_METHOD_ELEMENT = "lookup-method";
0168:
0169: public static final String REPLACED_METHOD_ELEMENT = "replaced-method";
0170:
0171: public static final String REPLACER_ATTRIBUTE = "replacer";
0172:
0173: public static final String ARG_TYPE_ELEMENT = "arg-type";
0174:
0175: public static final String ARG_TYPE_MATCH_ATTRIBUTE = "match";
0176:
0177: public static final String REF_ELEMENT = "ref";
0178:
0179: public static final String IDREF_ELEMENT = "idref";
0180:
0181: public static final String BEAN_REF_ATTRIBUTE = "bean";
0182:
0183: public static final String LOCAL_REF_ATTRIBUTE = "local";
0184:
0185: public static final String PARENT_REF_ATTRIBUTE = "parent";
0186:
0187: public static final String VALUE_ELEMENT = "value";
0188:
0189: public static final String NULL_ELEMENT = "null";
0190:
0191: public static final String LIST_ELEMENT = "list";
0192:
0193: public static final String SET_ELEMENT = "set";
0194:
0195: public static final String MAP_ELEMENT = "map";
0196:
0197: public static final String ENTRY_ELEMENT = "entry";
0198:
0199: public static final String KEY_ELEMENT = "key";
0200:
0201: public static final String KEY_ATTRIBUTE = "key";
0202:
0203: public static final String KEY_REF_ATTRIBUTE = "key-ref";
0204:
0205: public static final String VALUE_REF_ATTRIBUTE = "value-ref";
0206:
0207: public static final String PROPS_ELEMENT = "props";
0208:
0209: public static final String PROP_ELEMENT = "prop";
0210:
0211: public static final String MERGE_ATTRIBUTE = "merge";
0212:
0213: public static final String QUALIFIER_ELEMENT = "qualifier";
0214:
0215: public static final String QUALIFIER_ATTRIBUTE_ELEMENT = "attribute";
0216:
0217: public static final String DEFAULT_LAZY_INIT_ATTRIBUTE = "default-lazy-init";
0218:
0219: public static final String DEFAULT_MERGE_ATTRIBUTE = "default-merge";
0220:
0221: public static final String DEFAULT_AUTOWIRE_ATTRIBUTE = "default-autowire";
0222:
0223: public static final String DEFAULT_DEPENDENCY_CHECK_ATTRIBUTE = "default-dependency-check";
0224:
0225: public static final String DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE = "default-autowire-candidates";
0226:
0227: public static final String DEFAULT_INIT_METHOD_ATTRIBUTE = "default-init-method";
0228:
0229: public static final String DEFAULT_DESTROY_METHOD_ATTRIBUTE = "default-destroy-method";
0230:
0231: protected final Log logger = LogFactory.getLog(getClass());
0232:
0233: private final XmlReaderContext readerContext;
0234:
0235: private DocumentDefaultsDefinition defaults;
0236:
0237: private ParseState parseState = new ParseState();
0238:
0239: /**
0240: * Stores all used bean names so we can enforce uniqueness on a per file basis.
0241: */
0242: private final Set usedNames = new HashSet();
0243:
0244: /**
0245: * Create a new BeanDefinitionParserDelegate associated with the
0246: * supplied {@link XmlReaderContext}.
0247: */
0248: public BeanDefinitionParserDelegate(XmlReaderContext readerContext) {
0249: Assert.notNull(readerContext,
0250: "XmlReaderContext must not be null");
0251: this .readerContext = readerContext;
0252: }
0253:
0254: /**
0255: * Get the {@link XmlReaderContext} associated with this helper instance.
0256: */
0257: public final XmlReaderContext getReaderContext() {
0258: return this .readerContext;
0259: }
0260:
0261: /**
0262: * Invoke the {@link org.springframework.beans.factory.parsing.SourceExtractor} to pull the
0263: * source metadata from the supplied {@link Element}.
0264: */
0265: protected Object extractSource(Element ele) {
0266: return this .readerContext.extractSource(ele);
0267: }
0268:
0269: /**
0270: * Report an error with the given message for the given source element.
0271: */
0272: protected void error(String message, Element source) {
0273: this .readerContext.error(message, source, this .parseState
0274: .snapshot());
0275: }
0276:
0277: /**
0278: * Report an error with the given message for the given source element.
0279: */
0280: protected void error(String message, Element source, Throwable cause) {
0281: this .readerContext.error(message, source, this .parseState
0282: .snapshot(), cause);
0283: }
0284:
0285: /**
0286: * Initialize the default lazy-init, autowire, dependency check settings,
0287: * init-method, destroy-method and merge settings.
0288: * @see #getDefaults()
0289: */
0290: public void initDefaults(Element root) {
0291: DocumentDefaultsDefinition defaults = new DocumentDefaultsDefinition();
0292: defaults.setLazyInit(root
0293: .getAttribute(DEFAULT_LAZY_INIT_ATTRIBUTE));
0294: defaults.setMerge(root.getAttribute(DEFAULT_MERGE_ATTRIBUTE));
0295: defaults.setAutowire(root
0296: .getAttribute(DEFAULT_AUTOWIRE_ATTRIBUTE));
0297: defaults.setDependencyCheck(root
0298: .getAttribute(DEFAULT_DEPENDENCY_CHECK_ATTRIBUTE));
0299: if (root.hasAttribute(DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE)) {
0300: defaults
0301: .setAutowireCandidates(root
0302: .getAttribute(DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE));
0303: }
0304: if (root.hasAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE)) {
0305: defaults.setInitMethod(root
0306: .getAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE));
0307: }
0308: if (root.hasAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE)) {
0309: defaults.setDestroyMethod(root
0310: .getAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE));
0311: }
0312: defaults.setSource(this .readerContext.extractSource(root));
0313:
0314: this .defaults = defaults;
0315: this .readerContext.fireDefaultsRegistered(defaults);
0316: }
0317:
0318: /**
0319: * Return the defaults definition object, or <code>null</code> if the
0320: * defaults have been initialized yet.
0321: */
0322: public DocumentDefaultsDefinition getDefaults() {
0323: return this .defaults;
0324: }
0325:
0326: /**
0327: * Return the default settings for bean definitions as indicated within
0328: * the attributes of the top-level <code><beans/></code> element.
0329: */
0330: public BeanDefinitionDefaults getBeanDefinitionDefaults() {
0331: BeanDefinitionDefaults bdd = new BeanDefinitionDefaults();
0332: if (this .defaults != null) {
0333: bdd.setLazyInit("TRUE".equalsIgnoreCase(this .defaults
0334: .getLazyInit()));
0335: bdd.setDependencyCheck(this
0336: .getDependencyCheck(DEFAULT_VALUE));
0337: bdd.setAutowireMode(this .getAutowireMode(DEFAULT_VALUE));
0338: bdd.setInitMethodName(this .defaults.getInitMethod());
0339: bdd.setDestroyMethodName(this .defaults.getDestroyMethod());
0340: }
0341: return bdd;
0342: }
0343:
0344: /**
0345: * Return any patterns provided in the 'default-autowire-candidates'
0346: * attribute of the top-level <code><beans/></code> element.
0347: */
0348: public String[] getAutowireCandidatePatterns() {
0349: String candidatePattern = this .defaults.getAutowireCandidates();
0350: return candidatePattern == null ? null : StringUtils
0351: .commaDelimitedListToStringArray(candidatePattern);
0352: }
0353:
0354: /**
0355: * Parses the supplied <code><bean></code> element. May return <code>null</code>
0356: * if there were errors during parse. Errors are reported to the
0357: * {@link org.springframework.beans.factory.parsing.ProblemReporter}.
0358: */
0359: public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
0360: return parseBeanDefinitionElement(ele, null);
0361: }
0362:
0363: /**
0364: * Parses the supplied <code><bean></code> element. May return <code>null</code>
0365: * if there were errors during parse. Errors are reported to the
0366: * {@link org.springframework.beans.factory.parsing.ProblemReporter}.
0367: */
0368: public BeanDefinitionHolder parseBeanDefinitionElement(Element ele,
0369: BeanDefinition containingBean) {
0370: String id = ele.getAttribute(ID_ATTRIBUTE);
0371: String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
0372:
0373: List aliases = new ArrayList();
0374: if (StringUtils.hasLength(nameAttr)) {
0375: String[] nameArr = StringUtils.tokenizeToStringArray(
0376: nameAttr, BEAN_NAME_DELIMITERS);
0377: aliases.addAll(Arrays.asList(nameArr));
0378: }
0379:
0380: String beanName = id;
0381: if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
0382: beanName = (String) aliases.remove(0);
0383: if (logger.isDebugEnabled()) {
0384: logger.debug("No XML 'id' specified - using '"
0385: + beanName + "' as bean name and " + aliases
0386: + " as aliases");
0387: }
0388: }
0389:
0390: if (containingBean == null) {
0391: checkNameUniqueness(beanName, aliases, ele);
0392: }
0393:
0394: AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(
0395: ele, beanName, containingBean);
0396: if (beanDefinition != null) {
0397: if (!StringUtils.hasText(beanName)) {
0398: try {
0399: if (containingBean != null) {
0400: beanName = BeanDefinitionReaderUtils
0401: .generateBeanName(beanDefinition,
0402: this .readerContext
0403: .getRegistry(), true);
0404: } else {
0405: beanName = this .readerContext
0406: .generateBeanName(beanDefinition);
0407: // Register an alias for the plain bean class name, if still possible,
0408: // if the generator returned the class name plus a suffix.
0409: // This is expected for Spring 1.2/2.0 backwards compatibility.
0410: String beanClassName = beanDefinition
0411: .getBeanClassName();
0412: if (beanClassName != null
0413: && beanName.startsWith(beanClassName)
0414: && beanName.length() > beanClassName
0415: .length()
0416: && !this .readerContext.getRegistry()
0417: .isBeanNameInUse(beanClassName)) {
0418: this .readerContext.getRegistry()
0419: .registerAlias(beanName,
0420: beanClassName);
0421: }
0422: }
0423: if (logger.isDebugEnabled()) {
0424: logger
0425: .debug("Neither XML 'id' nor 'name' specified - "
0426: + "using generated bean name ["
0427: + beanName + "]");
0428: }
0429: } catch (BeanDefinitionStoreException ex) {
0430: error(ex.getMessage(), ele);
0431: return null;
0432: }
0433: }
0434: String[] aliasesArray = StringUtils.toStringArray(aliases);
0435: return new BeanDefinitionHolder(beanDefinition, beanName,
0436: aliasesArray);
0437: }
0438:
0439: return null;
0440: }
0441:
0442: private void checkNameUniqueness(String beanName, List aliases,
0443: Element beanElement) {
0444: String foundName = null;
0445:
0446: if (StringUtils.hasText(beanName)
0447: && this .usedNames.contains(beanName)) {
0448: foundName = beanName;
0449: }
0450: if (foundName == null) {
0451: foundName = (String) CollectionUtils.findFirstMatch(
0452: this .usedNames, aliases);
0453: }
0454: if (foundName != null) {
0455: error("Bean name '" + foundName
0456: + "' is already used in this file.", beanElement);
0457: }
0458:
0459: this .usedNames.add(beanName);
0460: this .usedNames.addAll(aliases);
0461: }
0462:
0463: /**
0464: * Parse the bean definition itself, without regard to name or aliases. May return
0465: * <code>null</code> if problems occured during the parse of the bean definition.
0466: */
0467: public AbstractBeanDefinition parseBeanDefinitionElement(
0468: Element ele, String beanName, BeanDefinition containingBean) {
0469:
0470: String className = null;
0471: if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
0472: className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
0473: }
0474: String parent = null;
0475: if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
0476: parent = ele.getAttribute(PARENT_ATTRIBUTE);
0477: }
0478:
0479: try {
0480: this .parseState.push(new BeanEntry(beanName));
0481:
0482: AbstractBeanDefinition bd = BeanDefinitionReaderUtils
0483: .createBeanDefinition(parent, className,
0484: this .readerContext.getBeanClassLoader());
0485:
0486: if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
0487: // Spring 2.0 "scope" attribute
0488: bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
0489: if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
0490: error(
0491: "Specify either 'scope' or 'singleton', not both",
0492: ele);
0493: }
0494: } else if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
0495: // Spring 1.x "singleton" attribute
0496: bd.setSingleton(TRUE_VALUE.equals(ele
0497: .getAttribute(SINGLETON_ATTRIBUTE)));
0498: } else if (containingBean != null) {
0499: // Take default from containing bean in case of an inner bean definition.
0500: bd.setScope(containingBean.getScope());
0501: }
0502:
0503: if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
0504: bd.setAbstract(TRUE_VALUE.equals(ele
0505: .getAttribute(ABSTRACT_ATTRIBUTE)));
0506: }
0507:
0508: String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
0509: if (DEFAULT_VALUE.equals(lazyInit) && bd.isSingleton()) {
0510: // Just apply default to singletons, as lazy-init has no meaning for prototypes.
0511: lazyInit = this .defaults.getLazyInit();
0512: }
0513: bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
0514:
0515: String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
0516: bd.setAutowireMode(getAutowireMode(autowire));
0517:
0518: String dependencyCheck = ele
0519: .getAttribute(DEPENDENCY_CHECK_ATTRIBUTE);
0520: bd.setDependencyCheck(getDependencyCheck(dependencyCheck));
0521:
0522: if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
0523: String dependsOn = ele
0524: .getAttribute(DEPENDS_ON_ATTRIBUTE);
0525: bd.setDependsOn(StringUtils.tokenizeToStringArray(
0526: dependsOn, BEAN_NAME_DELIMITERS));
0527: }
0528:
0529: String autowireCandidate = ele
0530: .getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);
0531: if ("".equals(autowireCandidate)
0532: || DEFAULT_VALUE.equals(autowireCandidate)) {
0533: String candidatePattern = this .defaults
0534: .getAutowireCandidates();
0535: if (candidatePattern != null) {
0536: String[] patterns = StringUtils
0537: .commaDelimitedListToStringArray(candidatePattern);
0538: bd.setAutowireCandidate(PatternMatchUtils
0539: .simpleMatch(patterns, beanName));
0540: }
0541: } else {
0542: bd.setAutowireCandidate(TRUE_VALUE
0543: .equals(autowireCandidate));
0544: }
0545:
0546: if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
0547: bd.setPrimary(TRUE_VALUE.equals(ele
0548: .getAttribute(PRIMARY_ATTRIBUTE)));
0549: }
0550:
0551: if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
0552: String initMethodName = ele
0553: .getAttribute(INIT_METHOD_ATTRIBUTE);
0554: if (!"".equals(initMethodName)) {
0555: bd.setInitMethodName(initMethodName);
0556: }
0557: } else {
0558: if (this .defaults.getInitMethod() != null) {
0559: bd.setInitMethodName(this .defaults.getInitMethod());
0560: bd.setEnforceInitMethod(false);
0561: }
0562: }
0563:
0564: if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
0565: String destroyMethodName = ele
0566: .getAttribute(DESTROY_METHOD_ATTRIBUTE);
0567: if (!"".equals(destroyMethodName)) {
0568: bd.setDestroyMethodName(destroyMethodName);
0569: }
0570: } else {
0571: if (this .defaults.getDestroyMethod() != null) {
0572: bd.setDestroyMethodName(this .defaults
0573: .getDestroyMethod());
0574: bd.setEnforceDestroyMethod(false);
0575: }
0576: }
0577:
0578: if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
0579: bd.setFactoryMethodName(ele
0580: .getAttribute(FACTORY_METHOD_ATTRIBUTE));
0581: }
0582: if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
0583: bd.setFactoryBeanName(ele
0584: .getAttribute(FACTORY_BEAN_ATTRIBUTE));
0585: }
0586:
0587: parseMetaElements(ele, bd);
0588: parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
0589: parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
0590:
0591: parseConstructorArgElements(ele, bd);
0592: parsePropertyElements(ele, bd);
0593: parseQualifierElements(ele, bd);
0594:
0595: bd.setResourceDescription(this .readerContext.getResource()
0596: .getDescription());
0597: bd.setSource(extractSource(ele));
0598:
0599: return bd;
0600: } catch (ClassNotFoundException ex) {
0601: error("Bean class [" + className + "] not found", ele, ex);
0602: } catch (NoClassDefFoundError err) {
0603: error("Class that bean class [" + className
0604: + "] depends on not found", ele, err);
0605: } catch (Throwable ex) {
0606: error("Unexpected failure during bean definition parsing",
0607: ele, ex);
0608: } finally {
0609: this .parseState.pop();
0610: }
0611:
0612: return null;
0613: }
0614:
0615: public void parseMetaElements(Element ele,
0616: BeanMetadataAttributeAccessor attributeAccessor) {
0617: NodeList nl = ele.getChildNodes();
0618: for (int i = 0; i < nl.getLength(); i++) {
0619: Node node = nl.item(i);
0620: if (node instanceof Element
0621: && DomUtils.nodeNameEquals(node, META_ELEMENT)) {
0622: Element metaElement = (Element) node;
0623: String key = metaElement.getAttribute(KEY_ATTRIBUTE);
0624: String value = metaElement
0625: .getAttribute(VALUE_ATTRIBUTE);
0626: BeanMetadataAttribute attribute = new BeanMetadataAttribute(
0627: key, value);
0628: attribute.setSource(extractSource(metaElement));
0629: attributeAccessor.addMetadataAttribute(attribute);
0630: }
0631: }
0632: }
0633:
0634: public int getAutowireMode(String attValue) {
0635: String att = attValue;
0636: if (DEFAULT_VALUE.equals(att)) {
0637: att = this .defaults.getAutowire();
0638: }
0639: int autowire = AbstractBeanDefinition.AUTOWIRE_NO;
0640: if (AUTOWIRE_BY_NAME_VALUE.equals(att)) {
0641: autowire = AbstractBeanDefinition.AUTOWIRE_BY_NAME;
0642: } else if (AUTOWIRE_BY_TYPE_VALUE.equals(att)) {
0643: autowire = AbstractBeanDefinition.AUTOWIRE_BY_TYPE;
0644: } else if (AUTOWIRE_CONSTRUCTOR_VALUE.equals(att)) {
0645: autowire = AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR;
0646: } else if (AUTOWIRE_AUTODETECT_VALUE.equals(att)) {
0647: autowire = AbstractBeanDefinition.AUTOWIRE_AUTODETECT;
0648: }
0649: // Else leave default value.
0650: return autowire;
0651: }
0652:
0653: public int getDependencyCheck(String attValue) {
0654: String att = attValue;
0655: if (DEFAULT_VALUE.equals(att)) {
0656: att = this .defaults.getDependencyCheck();
0657: }
0658: int dependencyCheckCode = AbstractBeanDefinition.DEPENDENCY_CHECK_NONE;
0659: if (DEPENDENCY_CHECK_ALL_ATTRIBUTE_VALUE.equals(att)) {
0660: dependencyCheckCode = AbstractBeanDefinition.DEPENDENCY_CHECK_ALL;
0661: } else if (DEPENDENCY_CHECK_SIMPLE_ATTRIBUTE_VALUE.equals(att)) {
0662: dependencyCheckCode = AbstractBeanDefinition.DEPENDENCY_CHECK_SIMPLE;
0663: } else if (DEPENDENCY_CHECK_OBJECTS_ATTRIBUTE_VALUE.equals(att)) {
0664: dependencyCheckCode = AbstractBeanDefinition.DEPENDENCY_CHECK_OBJECTS;
0665: }
0666: // Else leave default value.
0667: return dependencyCheckCode;
0668: }
0669:
0670: /**
0671: * Parse constructor-arg sub-elements of the given bean element.
0672: */
0673: public void parseConstructorArgElements(Element beanEle,
0674: BeanDefinition bd) {
0675: NodeList nl = beanEle.getChildNodes();
0676: for (int i = 0; i < nl.getLength(); i++) {
0677: Node node = nl.item(i);
0678: if (node instanceof Element
0679: && DomUtils.nodeNameEquals(node,
0680: CONSTRUCTOR_ARG_ELEMENT)) {
0681: parseConstructorArgElement((Element) node, bd);
0682: }
0683: }
0684: }
0685:
0686: /**
0687: * Parse property sub-elements of the given bean element.
0688: */
0689: public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
0690: NodeList nl = beanEle.getChildNodes();
0691: for (int i = 0; i < nl.getLength(); i++) {
0692: Node node = nl.item(i);
0693: if (node instanceof Element
0694: && DomUtils.nodeNameEquals(node, PROPERTY_ELEMENT)) {
0695: parsePropertyElement((Element) node, bd);
0696: }
0697: }
0698: }
0699:
0700: /**
0701: * Parse qualifier sub-elements of the given bean element.
0702: */
0703: public void parseQualifierElements(Element beanEle,
0704: AbstractBeanDefinition bd) {
0705: NodeList nl = beanEle.getChildNodes();
0706: for (int i = 0; i < nl.getLength(); i++) {
0707: Node node = nl.item(i);
0708: if (node instanceof Element
0709: && DomUtils.nodeNameEquals(node, QUALIFIER_ELEMENT)) {
0710: parseQualifierElement((Element) node, bd);
0711: }
0712: }
0713: }
0714:
0715: /**
0716: * Parse lookup-override sub-elements of the given bean element.
0717: */
0718: public void parseLookupOverrideSubElements(Element beanEle,
0719: MethodOverrides overrides) {
0720: NodeList nl = beanEle.getChildNodes();
0721: for (int i = 0; i < nl.getLength(); i++) {
0722: Node node = nl.item(i);
0723: if (node instanceof Element
0724: && DomUtils.nodeNameEquals(node,
0725: LOOKUP_METHOD_ELEMENT)) {
0726: Element ele = (Element) node;
0727: String methodName = ele.getAttribute(NAME_ATTRIBUTE);
0728: String beanRef = ele.getAttribute(BEAN_ELEMENT);
0729: LookupOverride override = new LookupOverride(
0730: methodName, beanRef);
0731: override.setSource(extractSource(ele));
0732: overrides.addOverride(override);
0733: }
0734: }
0735: }
0736:
0737: /**
0738: * Parse replaced-method sub-elements of the given bean element.
0739: */
0740: public void parseReplacedMethodSubElements(Element beanEle,
0741: MethodOverrides overrides) {
0742: NodeList nl = beanEle.getChildNodes();
0743: for (int i = 0; i < nl.getLength(); i++) {
0744: Node node = nl.item(i);
0745: if (node instanceof Element
0746: && DomUtils.nodeNameEquals(node,
0747: REPLACED_METHOD_ELEMENT)) {
0748: Element replacedMethodEle = (Element) node;
0749: String name = replacedMethodEle
0750: .getAttribute(NAME_ATTRIBUTE);
0751: String callback = replacedMethodEle
0752: .getAttribute(REPLACER_ATTRIBUTE);
0753: ReplaceOverride replaceOverride = new ReplaceOverride(
0754: name, callback);
0755: // Look for arg-type match elements.
0756: List argTypeEles = DomUtils.getChildElementsByTagName(
0757: replacedMethodEle, ARG_TYPE_ELEMENT);
0758: for (Iterator it = argTypeEles.iterator(); it.hasNext();) {
0759: Element argTypeEle = (Element) it.next();
0760: replaceOverride.addTypeIdentifier(argTypeEle
0761: .getAttribute(ARG_TYPE_MATCH_ATTRIBUTE));
0762: }
0763: replaceOverride
0764: .setSource(extractSource(replacedMethodEle));
0765: overrides.addOverride(replaceOverride);
0766: }
0767: }
0768: }
0769:
0770: /**
0771: * Parse a constructor-arg element.
0772: */
0773: public void parseConstructorArgElement(Element ele,
0774: BeanDefinition bd) {
0775: String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);
0776: String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE);
0777: if (StringUtils.hasLength(indexAttr)) {
0778: try {
0779: int index = Integer.parseInt(indexAttr);
0780: if (index < 0) {
0781: error("'index' cannot be lower than 0", ele);
0782: } else {
0783: try {
0784: this .parseState
0785: .push(new ConstructorArgumentEntry(
0786: index));
0787: Object value = parsePropertyValue(ele, bd, null);
0788: ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(
0789: value);
0790: if (StringUtils.hasLength(typeAttr)) {
0791: valueHolder.setType(typeAttr);
0792: }
0793: valueHolder.setSource(extractSource(ele));
0794: bd.getConstructorArgumentValues()
0795: .addIndexedArgumentValue(index,
0796: valueHolder);
0797: } finally {
0798: this .parseState.pop();
0799: }
0800: }
0801: } catch (NumberFormatException ex) {
0802: error(
0803: "Attribute 'index' of tag 'constructor-arg' must be an integer",
0804: ele);
0805: }
0806: } else {
0807: try {
0808: this .parseState.push(new ConstructorArgumentEntry());
0809: Object value = parsePropertyValue(ele, bd, null);
0810: ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(
0811: value);
0812: if (StringUtils.hasLength(typeAttr)) {
0813: valueHolder.setType(typeAttr);
0814: }
0815: valueHolder.setSource(extractSource(ele));
0816: bd.getConstructorArgumentValues()
0817: .addGenericArgumentValue(valueHolder);
0818: } finally {
0819: this .parseState.pop();
0820: }
0821: }
0822: }
0823:
0824: /**
0825: * Parse a property element.
0826: */
0827: public void parsePropertyElement(Element ele, BeanDefinition bd) {
0828: String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
0829: if (!StringUtils.hasLength(propertyName)) {
0830: error("Tag 'property' must have a 'name' attribute", ele);
0831: return;
0832: }
0833: this .parseState.push(new PropertyEntry(propertyName));
0834: try {
0835: if (bd.getPropertyValues().contains(propertyName)) {
0836: error("Multiple 'property' definitions for property '"
0837: + propertyName + "'", ele);
0838: return;
0839: }
0840: Object val = parsePropertyValue(ele, bd, propertyName);
0841: PropertyValue pv = new PropertyValue(propertyName, val);
0842: parseMetaElements(ele, pv);
0843: pv.setSource(extractSource(ele));
0844: bd.getPropertyValues().addPropertyValue(pv);
0845: } finally {
0846: this .parseState.pop();
0847: }
0848: }
0849:
0850: /**
0851: * Parse a qualifier element.
0852: */
0853: public void parseQualifierElement(Element ele,
0854: AbstractBeanDefinition bd) {
0855: String typeName = ele.getAttribute(TYPE_ATTRIBUTE);
0856: if (!StringUtils.hasLength(typeName)) {
0857: error("Tag 'qualifier' must have a 'type' attribute", ele);
0858: return;
0859: }
0860: this .parseState.push(new QualifierEntry(typeName));
0861: try {
0862: AutowireCandidateQualifier qualifier = new AutowireCandidateQualifier(
0863: typeName);
0864: qualifier.setSource(extractSource(ele));
0865: String value = ele.getAttribute(VALUE_ATTRIBUTE);
0866: if (StringUtils.hasLength(value)) {
0867: qualifier.setAttribute(
0868: AutowireCandidateQualifier.VALUE_KEY, value);
0869: }
0870: NodeList nl = ele.getChildNodes();
0871: for (int i = 0; i < nl.getLength(); i++) {
0872: Node node = nl.item(i);
0873: if (node instanceof Element
0874: && DomUtils.nodeNameEquals(node,
0875: QUALIFIER_ATTRIBUTE_ELEMENT)) {
0876: Element attributeEle = (Element) node;
0877: String attributeName = attributeEle
0878: .getAttribute(KEY_ATTRIBUTE);
0879: String attributeValue = attributeEle
0880: .getAttribute(VALUE_ATTRIBUTE);
0881: if (StringUtils.hasLength(attributeName)
0882: && StringUtils.hasLength(attributeValue)) {
0883: BeanMetadataAttribute attribute = new BeanMetadataAttribute(
0884: attributeName, attributeValue);
0885: attribute
0886: .setSource(extractSource(attributeEle));
0887: qualifier.addMetadataAttribute(attribute);
0888: } else {
0889: error(
0890: "Qualifier 'attribute' tag must have a 'name' and 'value'",
0891: attributeEle);
0892: return;
0893: }
0894: }
0895: }
0896: bd.addQualifier(qualifier);
0897: } finally {
0898: this .parseState.pop();
0899: }
0900: }
0901:
0902: /**
0903: * Get the value of a property element. May be a list etc.
0904: * Also used for constructor arguments, "propertyName" being null in this case.
0905: */
0906: public Object parsePropertyValue(Element ele, BeanDefinition bd,
0907: String propertyName) {
0908: String elementName = (propertyName != null) ? "<property> element for property '"
0909: + propertyName + "'"
0910: : "<constructor-arg> element";
0911:
0912: // Should only have one child element: ref, value, list, etc.
0913: NodeList nl = ele.getChildNodes();
0914: Element subElement = null;
0915: for (int i = 0; i < nl.getLength(); i++) {
0916: if (nl.item(i) instanceof Element) {
0917: Element candidateEle = (Element) nl.item(i);
0918: if (DomUtils.nodeNameEquals(candidateEle,
0919: DESCRIPTION_ELEMENT)
0920: || DomUtils.nodeNameEquals(candidateEle,
0921: META_ELEMENT)) {
0922: // Keep going: we don't use these values for now.
0923: } else {
0924: // Child element is what we're looking for.
0925: if (subElement != null) {
0926: error(
0927: elementName
0928: + " must not contain more than one sub-element",
0929: ele);
0930: } else {
0931: subElement = candidateEle;
0932: }
0933: }
0934: }
0935: }
0936:
0937: boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
0938: boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
0939: if ((hasRefAttribute && hasValueAttribute)
0940: || ((hasRefAttribute || hasValueAttribute))
0941: && subElement != null) {
0942: error(
0943: elementName
0944: + " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element",
0945: ele);
0946: }
0947:
0948: if (hasRefAttribute) {
0949: String refName = ele.getAttribute(REF_ATTRIBUTE);
0950: if (!StringUtils.hasText(refName)) {
0951: error(elementName + " contains empty 'ref' attribute",
0952: ele);
0953: }
0954: RuntimeBeanReference ref = new RuntimeBeanReference(refName);
0955: ref.setSource(extractSource(ele));
0956: return ref;
0957: } else if (hasValueAttribute) {
0958: TypedStringValue valueHolder = new TypedStringValue(ele
0959: .getAttribute(VALUE_ATTRIBUTE));
0960: valueHolder.setSource(extractSource(ele));
0961: return valueHolder;
0962: } else if (subElement != null) {
0963: return parsePropertySubElement(subElement, bd);
0964: } else {
0965: // Neither child element nor "ref" or "value" attribute found.
0966: error(elementName + " must specify a ref or value", ele);
0967: return null;
0968: }
0969: }
0970:
0971: public Object parsePropertySubElement(Element ele, BeanDefinition bd) {
0972: return parsePropertySubElement(ele, bd, null);
0973: }
0974:
0975: /**
0976: * Parse a value, ref or collection sub-element of a property or
0977: * constructor-arg element.
0978: * @param ele subelement of property element; we don't know which yet
0979: * @param defaultTypeClassName the default type (class name) for any
0980: * <code><value></code> tag that might be created
0981: */
0982: public Object parsePropertySubElement(Element ele,
0983: BeanDefinition bd, String defaultTypeClassName) {
0984: if (!isDefaultNamespace(ele.getNamespaceURI())) {
0985: return parseNestedCustomElement(ele, bd);
0986: } else if (DomUtils.nodeNameEquals(ele, BEAN_ELEMENT)) {
0987: BeanDefinitionHolder bdHolder = parseBeanDefinitionElement(
0988: ele, bd);
0989: if (bdHolder != null) {
0990: bdHolder = decorateBeanDefinitionIfRequired(ele,
0991: bdHolder);
0992: }
0993: return bdHolder;
0994: } else if (DomUtils.nodeNameEquals(ele, REF_ELEMENT)) {
0995: // A generic reference to any name of any bean.
0996: String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
0997: boolean toParent = false;
0998: if (!StringUtils.hasLength(refName)) {
0999: // A reference to the id of another bean in the same XML file.
1000: refName = ele.getAttribute(LOCAL_REF_ATTRIBUTE);
1001: if (!StringUtils.hasLength(refName)) {
1002: // A reference to the id of another bean in a parent context.
1003: refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);
1004: toParent = true;
1005: if (!StringUtils.hasLength(refName)) {
1006: error(
1007: "'bean', 'local' or 'parent' is required for <ref> element",
1008: ele);
1009: return null;
1010: }
1011: }
1012: }
1013: if (!StringUtils.hasText(refName)) {
1014: error("<ref> element contains empty target attribute",
1015: ele);
1016: return null;
1017: }
1018: RuntimeBeanReference ref = new RuntimeBeanReference(
1019: refName, toParent);
1020: ref.setSource(extractSource(ele));
1021: return ref;
1022: } else if (DomUtils.nodeNameEquals(ele, IDREF_ELEMENT)) {
1023: // A generic reference to any name of any bean.
1024: String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
1025: if (!StringUtils.hasLength(refName)) {
1026: // A reference to the id of another bean in the same XML file.
1027: refName = ele.getAttribute(LOCAL_REF_ATTRIBUTE);
1028: if (!StringUtils.hasLength(refName)) {
1029: error(
1030: "Either 'bean' or 'local' is required for <idref> element",
1031: ele);
1032: return null;
1033: }
1034: }
1035: if (!StringUtils.hasText(refName)) {
1036: error(
1037: "<idref> element contains empty target attribute",
1038: ele);
1039: return null;
1040: }
1041: RuntimeBeanNameReference ref = new RuntimeBeanNameReference(
1042: refName);
1043: ref.setSource(extractSource(ele));
1044: return ref;
1045: } else if (DomUtils.nodeNameEquals(ele, VALUE_ELEMENT)) {
1046: // It's a literal value.
1047: String value = DomUtils.getTextValue(ele);
1048: String typeClassName = ele.getAttribute(TYPE_ATTRIBUTE);
1049: if (!StringUtils.hasText(typeClassName)) {
1050: typeClassName = defaultTypeClassName;
1051: }
1052: try {
1053: return buildTypedStringValue(value, typeClassName, ele);
1054: } catch (ClassNotFoundException ex) {
1055: error("Type class [" + typeClassName
1056: + "] not found for <value> element", ele, ex);
1057: return value;
1058: }
1059: } else if (DomUtils.nodeNameEquals(ele, NULL_ELEMENT)) {
1060: // It's a distinguished null value. Let's wrap it in a TypedStringValue
1061: // object in order to preserve the source location.
1062: TypedStringValue nullHolder = new TypedStringValue(null);
1063: nullHolder.setSource(extractSource(ele));
1064: return nullHolder;
1065: } else if (DomUtils.nodeNameEquals(ele, LIST_ELEMENT)) {
1066: return parseListElement(ele, bd);
1067: } else if (DomUtils.nodeNameEquals(ele, SET_ELEMENT)) {
1068: return parseSetElement(ele, bd);
1069: } else if (DomUtils.nodeNameEquals(ele, MAP_ELEMENT)) {
1070: return parseMapElement(ele, bd);
1071: } else if (DomUtils.nodeNameEquals(ele, PROPS_ELEMENT)) {
1072: return parsePropsElement(ele);
1073: }
1074: error("Unknown property sub-element: [" + ele.getNodeName()
1075: + "]", ele);
1076: return null;
1077: }
1078:
1079: /**
1080: * Build a typed String value Object for the given raw value.
1081: * @see org.springframework.beans.factory.config.TypedStringValue
1082: */
1083: protected Object buildTypedStringValue(String value,
1084: String targetTypeName, Element ele)
1085: throws ClassNotFoundException {
1086:
1087: ClassLoader classLoader = this .readerContext
1088: .getBeanClassLoader();
1089: TypedStringValue typedValue = null;
1090: if (!StringUtils.hasText(targetTypeName)) {
1091: typedValue = new TypedStringValue(value);
1092: } else if (classLoader != null) {
1093: Class targetType = ClassUtils.forName(targetTypeName,
1094: classLoader);
1095: typedValue = new TypedStringValue(value, targetType);
1096: } else {
1097: typedValue = new TypedStringValue(value, targetTypeName);
1098: }
1099: typedValue.setSource(extractSource(ele));
1100: return typedValue;
1101: }
1102:
1103: /**
1104: * Parse a list element.
1105: */
1106: public List parseListElement(Element collectionEle,
1107: BeanDefinition bd) {
1108: String defaultTypeClassName = collectionEle
1109: .getAttribute(VALUE_TYPE_ATTRIBUTE);
1110: NodeList nl = collectionEle.getChildNodes();
1111: ManagedList list = new ManagedList(nl.getLength());
1112: list.setSource(extractSource(collectionEle));
1113: list.setMergeEnabled(parseMergeAttribute(collectionEle));
1114: for (int i = 0; i < nl.getLength(); i++) {
1115: if (nl.item(i) instanceof Element) {
1116: Element ele = (Element) nl.item(i);
1117: list.add(parsePropertySubElement(ele, bd,
1118: defaultTypeClassName));
1119: }
1120: }
1121: return list;
1122: }
1123:
1124: /**
1125: * Parse a set element.
1126: */
1127: public Set parseSetElement(Element collectionEle, BeanDefinition bd) {
1128: String defaultTypeClassName = collectionEle
1129: .getAttribute(VALUE_TYPE_ATTRIBUTE);
1130: NodeList nl = collectionEle.getChildNodes();
1131: ManagedSet set = new ManagedSet(nl.getLength());
1132: set.setSource(extractSource(collectionEle));
1133: set.setMergeEnabled(parseMergeAttribute(collectionEle));
1134: for (int i = 0; i < nl.getLength(); i++) {
1135: if (nl.item(i) instanceof Element) {
1136: Element ele = (Element) nl.item(i);
1137: set.add(parsePropertySubElement(ele, bd,
1138: defaultTypeClassName));
1139: }
1140: }
1141: return set;
1142: }
1143:
1144: /**
1145: * Parse a map element.
1146: */
1147: public Map parseMapElement(Element mapEle, BeanDefinition bd) {
1148: String defaultKeyTypeClassName = mapEle
1149: .getAttribute(KEY_TYPE_ATTRIBUTE);
1150: String defaultValueTypeClassName = mapEle
1151: .getAttribute(VALUE_TYPE_ATTRIBUTE);
1152:
1153: List entryEles = DomUtils.getChildElementsByTagName(mapEle,
1154: ENTRY_ELEMENT);
1155: ManagedMap map = new ManagedMap(entryEles.size());
1156: map.setMergeEnabled(parseMergeAttribute(mapEle));
1157: map.setSource(extractSource(mapEle));
1158:
1159: for (Iterator it = entryEles.iterator(); it.hasNext();) {
1160: Element entryEle = (Element) it.next();
1161: // Should only have one value child element: ref, value, list, etc.
1162: // Optionally, there might be a key child element.
1163: NodeList entrySubNodes = entryEle.getChildNodes();
1164:
1165: Element keyEle = null;
1166: Element valueEle = null;
1167: for (int j = 0; j < entrySubNodes.getLength(); j++) {
1168: if (entrySubNodes.item(j) instanceof Element) {
1169: Element candidateEle = (Element) entrySubNodes
1170: .item(j);
1171: if (DomUtils.nodeNameEquals(candidateEle,
1172: KEY_ELEMENT)) {
1173: if (keyEle != null) {
1174: error(
1175: "<entry> element is only allowed to contain one <key> sub-element",
1176: entryEle);
1177: } else {
1178: keyEle = candidateEle;
1179: }
1180: } else {
1181: // Child element is what we're looking for.
1182: if (valueEle != null) {
1183: error(
1184: "<entry> element must not contain more than one value sub-element",
1185: entryEle);
1186: } else {
1187: valueEle = candidateEle;
1188: }
1189: }
1190: }
1191: }
1192:
1193: // Extract key from attribute or sub-element.
1194: Object key = null;
1195: boolean hasKeyAttribute = entryEle
1196: .hasAttribute(KEY_ATTRIBUTE);
1197: boolean hasKeyRefAttribute = entryEle
1198: .hasAttribute(KEY_REF_ATTRIBUTE);
1199: if ((hasKeyAttribute && hasKeyRefAttribute)
1200: || ((hasKeyAttribute || hasKeyRefAttribute))
1201: && keyEle != null) {
1202: error(
1203: "<entry> element is only allowed to contain either "
1204: + "a 'key' attribute OR a 'key-ref' attribute OR a <key> sub-element",
1205: entryEle);
1206: }
1207: if (hasKeyAttribute) {
1208: key = buildTypedStringValueForMap(entryEle
1209: .getAttribute(KEY_ATTRIBUTE),
1210: defaultKeyTypeClassName, entryEle);
1211: } else if (hasKeyRefAttribute) {
1212: String refName = entryEle
1213: .getAttribute(KEY_REF_ATTRIBUTE);
1214: if (!StringUtils.hasText(refName)) {
1215: error(
1216: "<entry> element contains empty 'key-ref' attribute",
1217: entryEle);
1218: }
1219: RuntimeBeanReference ref = new RuntimeBeanReference(
1220: refName);
1221: ref.setSource(extractSource(entryEle));
1222: key = ref;
1223: } else if (keyEle != null) {
1224: key = parseKeyElement(keyEle, bd,
1225: defaultKeyTypeClassName);
1226: } else {
1227: error("<entry> element must specify a key", entryEle);
1228: }
1229:
1230: // Extract value from attribute or sub-element.
1231: Object value = null;
1232: boolean hasValueAttribute = entryEle
1233: .hasAttribute(VALUE_ATTRIBUTE);
1234: boolean hasValueRefAttribute = entryEle
1235: .hasAttribute(VALUE_REF_ATTRIBUTE);
1236: if ((hasValueAttribute && hasValueRefAttribute)
1237: || ((hasValueAttribute || hasValueRefAttribute))
1238: && valueEle != null) {
1239: error(
1240: "<entry> element is only allowed to contain either "
1241: + "'value' attribute OR 'value-ref' attribute OR <value> sub-element",
1242: entryEle);
1243: }
1244: if (hasValueAttribute) {
1245: value = buildTypedStringValueForMap(entryEle
1246: .getAttribute(VALUE_ATTRIBUTE),
1247: defaultValueTypeClassName, entryEle);
1248: } else if (hasValueRefAttribute) {
1249: String refName = entryEle
1250: .getAttribute(VALUE_REF_ATTRIBUTE);
1251: if (!StringUtils.hasText(refName)) {
1252: error(
1253: "<entry> element contains empty 'value-ref' attribute",
1254: entryEle);
1255: }
1256: RuntimeBeanReference ref = new RuntimeBeanReference(
1257: refName);
1258: ref.setSource(extractSource(entryEle));
1259: value = ref;
1260: } else if (valueEle != null) {
1261: value = parsePropertySubElement(valueEle, bd,
1262: defaultValueTypeClassName);
1263: } else {
1264: error("<entry> element must specify a value", entryEle);
1265: }
1266:
1267: // Add final key and value to the Map.
1268: map.put(key, value);
1269: }
1270:
1271: return map;
1272: }
1273:
1274: /**
1275: * Build a typed String value Object for the given raw value.
1276: * @see org.springframework.beans.factory.config.TypedStringValue
1277: */
1278: protected final Object buildTypedStringValueForMap(String value,
1279: String defaultTypeClassName, Element entryEle) {
1280: try {
1281: return buildTypedStringValue(value, defaultTypeClassName,
1282: entryEle);
1283: } catch (ClassNotFoundException ex) {
1284: error("Type class [" + defaultTypeClassName
1285: + "] not found for Map key/value type", entryEle,
1286: ex);
1287: return value;
1288: }
1289: }
1290:
1291: /**
1292: * Parse a key sub-element of a map element.
1293: */
1294: public Object parseKeyElement(Element keyEle, BeanDefinition bd,
1295: String defaultKeyTypeClassName) {
1296: NodeList nl = keyEle.getChildNodes();
1297: Element subElement = null;
1298: for (int i = 0; i < nl.getLength(); i++) {
1299: if (nl.item(i) instanceof Element) {
1300: Element candidateEle = (Element) nl.item(i);
1301: // Child element is what we're looking for.
1302: if (subElement != null) {
1303: error(
1304: "<key> element must not contain more than one value sub-element",
1305: keyEle);
1306: } else {
1307: subElement = candidateEle;
1308: }
1309: }
1310: }
1311: return parsePropertySubElement(subElement, bd,
1312: defaultKeyTypeClassName);
1313: }
1314:
1315: /**
1316: * Parse a props element.
1317: */
1318: public Properties parsePropsElement(Element propsEle) {
1319: ManagedProperties props = new ManagedProperties();
1320: props.setSource(extractSource(propsEle));
1321: props.setMergeEnabled(parseMergeAttribute(propsEle));
1322:
1323: List propEles = DomUtils.getChildElementsByTagName(propsEle,
1324: PROP_ELEMENT);
1325: for (Iterator it = propEles.iterator(); it.hasNext();) {
1326: Element propEle = (Element) it.next();
1327: String key = propEle.getAttribute(KEY_ATTRIBUTE);
1328: // Trim the text value to avoid unwanted whitespace
1329: // caused by typical XML formatting.
1330: String value = DomUtils.getTextValue(propEle).trim();
1331:
1332: TypedStringValue keyHolder = new TypedStringValue(key);
1333: keyHolder.setSource(extractSource(propEle));
1334: TypedStringValue valueHolder = new TypedStringValue(value);
1335: valueHolder.setSource(extractSource(propEle));
1336: props.put(keyHolder, valueHolder);
1337: }
1338:
1339: return props;
1340: }
1341:
1342: /**
1343: * Parse the merge attribute of a collection element, if any.
1344: */
1345: public boolean parseMergeAttribute(Element collectionElement) {
1346: String value = collectionElement.getAttribute(MERGE_ATTRIBUTE);
1347: if (DEFAULT_VALUE.equals(value)) {
1348: value = this .defaults.getMerge();
1349: }
1350: return TRUE_VALUE.equals(value);
1351: }
1352:
1353: public BeanDefinition parseCustomElement(Element ele) {
1354: return parseCustomElement(ele, null);
1355: }
1356:
1357: public BeanDefinition parseCustomElement(Element ele,
1358: BeanDefinition containingBd) {
1359: String namespaceUri = ele.getNamespaceURI();
1360: NamespaceHandler handler = this .readerContext
1361: .getNamespaceHandlerResolver().resolve(namespaceUri);
1362: if (handler == null) {
1363: error(
1364: "Unable to locate Spring NamespaceHandler for XML schema namespace ["
1365: + namespaceUri + "]", ele);
1366: return null;
1367: }
1368: return handler.parse(ele, new ParserContext(this .readerContext,
1369: this , containingBd));
1370: }
1371:
1372: public BeanDefinitionHolder decorateBeanDefinitionIfRequired(
1373: Element ele, BeanDefinitionHolder definitionHolder) {
1374: BeanDefinitionHolder finalDefinition = definitionHolder;
1375:
1376: // Decorate based on custom attributes first.
1377: NamedNodeMap attributes = ele.getAttributes();
1378: for (int i = 0; i < attributes.getLength(); i++) {
1379: Node node = attributes.item(i);
1380: finalDefinition = decorateIfRequired(node, finalDefinition);
1381: }
1382:
1383: // Decorate based on custom nested elements.
1384: NodeList children = ele.getChildNodes();
1385: for (int i = 0; i < children.getLength(); i++) {
1386: Node node = children.item(i);
1387: if (node.getNodeType() == Node.ELEMENT_NODE) {
1388: finalDefinition = decorateIfRequired(node,
1389: finalDefinition);
1390: }
1391: }
1392: return finalDefinition;
1393: }
1394:
1395: private BeanDefinitionHolder decorateIfRequired(Node node,
1396: BeanDefinitionHolder originalDefinition) {
1397: String namespaceUri = node.getNamespaceURI();
1398: if (!isDefaultNamespace(namespaceUri)) {
1399: NamespaceHandler handler = this .readerContext
1400: .getNamespaceHandlerResolver()
1401: .resolve(namespaceUri);
1402: if (handler != null) {
1403: return handler.decorate(node, originalDefinition,
1404: new ParserContext(this .readerContext, this ));
1405: } else {
1406: // A custom namespace, not to be handled by Spring - maybe "xml:...".
1407: if (logger.isDebugEnabled()) {
1408: logger
1409: .debug("No Spring NamespaceHandler found for XML schema namespace ["
1410: + namespaceUri + "]");
1411: }
1412: }
1413: }
1414: return originalDefinition;
1415: }
1416:
1417: public boolean isDefaultNamespace(String namespaceUri) {
1418: return (!StringUtils.hasLength(namespaceUri) || BEANS_NAMESPACE_URI
1419: .equals(namespaceUri));
1420: }
1421:
1422: private BeanDefinitionHolder parseNestedCustomElement(Element ele,
1423: BeanDefinition containingBd) {
1424: BeanDefinition innerDefinition = parseCustomElement(ele,
1425: containingBd);
1426: if (innerDefinition == null) {
1427: error(
1428: "Incorrect usage of element '"
1429: + ele.getNodeName()
1430: + "' in a nested manner. "
1431: + "This tag cannot be used nested inside <property>.",
1432: ele);
1433: return null;
1434: }
1435: String id = ele.getNodeName()
1436: + BeanDefinitionReaderUtils.GENERATED_BEAN_NAME_SEPARATOR
1437: + ObjectUtils.getIdentityHexString(innerDefinition);
1438: if (logger.isDebugEnabled()) {
1439: logger.debug("Using generated bean name [" + id
1440: + "] for nested custom element '"
1441: + ele.getNodeName() + "'");
1442: }
1443: return new BeanDefinitionHolder(innerDefinition, id);
1444: }
1445:
1446: }
|