001: /*
002: * Copyright 2002-2007 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: * http://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:
017: package org.springframework.beans.factory.xml;
018:
019: import org.w3c.dom.Element;
020:
021: import org.springframework.beans.factory.BeanDefinitionStoreException;
022: import org.springframework.beans.factory.config.BeanDefinition;
023: import org.springframework.beans.factory.config.BeanDefinitionHolder;
024: import org.springframework.beans.factory.parsing.BeanComponentDefinition;
025: import org.springframework.beans.factory.support.AbstractBeanDefinition;
026: import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
027: import org.springframework.beans.factory.support.BeanDefinitionRegistry;
028: import org.springframework.util.StringUtils;
029:
030: /**
031: * Abstract {@link BeanDefinitionParser} implementation providing
032: * a number of convenience methods and a
033: * {@link AbstractBeanDefinitionParser#parseInternal template method}
034: * that subclasses must override to provide the actual parsing logic.
035: *
036: * <p>Use this {@link BeanDefinitionParser} implementation when you want
037: * to parse some arbitrarily complex XML into one or more
038: * {@link BeanDefinition BeanDefinitions}. If you just want to parse some
039: * XML into a single <code>BeanDefinition</code>, you may wish to consider
040: * the simpler convenience extensions of this class, namely
041: * {@link AbstractSingleBeanDefinitionParser} and
042: * {@link AbstractSimpleBeanDefinitionParser}.
043: *
044: * @author Rob Harrop
045: * @author Juergen Hoeller
046: * @author Rick Evans
047: * @since 2.0
048: */
049: public abstract class AbstractBeanDefinitionParser implements
050: BeanDefinitionParser {
051:
052: /** Constant for the id attribute */
053: public static final String ID_ATTRIBUTE = "id";
054:
055: public final BeanDefinition parse(Element element,
056: ParserContext parserContext) {
057: AbstractBeanDefinition definition = parseInternal(element,
058: parserContext);
059: if (!parserContext.isNested()) {
060: try {
061: String id = resolveId(element, definition,
062: parserContext);
063: if (!StringUtils.hasText(id)) {
064: parserContext.getReaderContext().error(
065: "Id is required for element '"
066: + element.getLocalName()
067: + "' when used as a top-level tag",
068: element);
069: }
070: BeanDefinitionHolder holder = new BeanDefinitionHolder(
071: definition, id);
072: registerBeanDefinition(holder, parserContext
073: .getRegistry());
074: if (shouldFireEvents()) {
075: BeanComponentDefinition componentDefinition = new BeanComponentDefinition(
076: holder);
077: postProcessComponentDefinition(componentDefinition);
078: parserContext
079: .registerComponent(componentDefinition);
080: }
081: } catch (BeanDefinitionStoreException ex) {
082: parserContext.getReaderContext().error(ex.getMessage(),
083: element);
084: return null;
085: }
086: }
087: return definition;
088: }
089:
090: /**
091: * Resolve the ID for the supplied {@link BeanDefinition}.
092: * <p>When using {@link #shouldGenerateId generation}, a name is generated automatically.
093: * Otherwise, the ID is extracted from the "id" attribute, potentially with a
094: * {@link #shouldGenerateIdAsFallback() fallback} to a generated id.
095: * @param element the element that the bean definition has been built from
096: * @param definition the bean definition to be registered
097: * @param parserContext the object encapsulating the current state of the parsing process;
098: * provides access to a {@link org.springframework.beans.factory.support.BeanDefinitionRegistry}
099: * @return the resolved id
100: * @throws BeanDefinitionStoreException if no unique name could be generated
101: * for the given bean definition
102: */
103: protected String resolveId(Element element,
104: AbstractBeanDefinition definition,
105: ParserContext parserContext)
106: throws BeanDefinitionStoreException {
107:
108: if (shouldGenerateId()) {
109: return parserContext.getReaderContext().generateBeanName(
110: definition);
111: } else {
112: String id = element.getAttribute(ID_ATTRIBUTE);
113: if (!StringUtils.hasText(id)
114: && shouldGenerateIdAsFallback()) {
115: id = parserContext.getReaderContext().generateBeanName(
116: definition);
117: }
118: return id;
119: }
120: }
121:
122: /**
123: * Register the supplied {@link BeanDefinitionHolder bean} with the supplied
124: * {@link BeanDefinitionRegistry registry}.
125: * <p>Subclasses can override this method to control whether or not the supplied
126: * {@link BeanDefinitionHolder bean} is actually even registered, or to
127: * register even more beans.
128: * <p>The default implementation registers the supplied {@link BeanDefinitionHolder bean}
129: * with the supplied {@link BeanDefinitionRegistry registry} only if the <code>isNested</code>
130: * parameter is <code>false</code>, because one typically does not want inner beans
131: * to be registered as top level beans.
132: * @param definition the bean definition to be registered
133: * @param registry the registry that the bean is to be registered with
134: * @see BeanDefinitionReaderUtils#registerBeanDefinition(BeanDefinitionHolder, BeanDefinitionRegistry)
135: */
136: protected void registerBeanDefinition(
137: BeanDefinitionHolder definition,
138: BeanDefinitionRegistry registry) {
139: BeanDefinitionReaderUtils.registerBeanDefinition(definition,
140: registry);
141: }
142:
143: /**
144: * Central template method to actually parse the supplied {@link Element}
145: * into one or more {@link BeanDefinition BeanDefinitions}.
146: * @param element the element that is to be parsed into one or more {@link BeanDefinition BeanDefinitions}
147: * @param parserContext the object encapsulating the current state of the parsing process;
148: * provides access to a {@link org.springframework.beans.factory.support.BeanDefinitionRegistry}
149: * @return the primary {@link BeanDefinition} resulting from the parsing of the supplied {@link Element}
150: * @see #parse(org.w3c.dom.Element, ParserContext)
151: * @see #postProcessComponentDefinition(org.springframework.beans.factory.parsing.BeanComponentDefinition)
152: */
153: protected abstract AbstractBeanDefinition parseInternal(
154: Element element, ParserContext parserContext);
155:
156: /**
157: * Should an ID be generated instead of read from the passed in {@link Element}?
158: * <p>Disabled by default; subclasses can override this to enable ID generation.
159: * Note that this flag is about <i>always</i> generating an ID; the parser
160: * won't even check for an "id" attribute in this case.
161: * @return whether the parser should always generate an id
162: */
163: protected boolean shouldGenerateId() {
164: return false;
165: }
166:
167: /**
168: * Should an ID be generated instead if the passed in {@link Element} does not
169: * specify an "id" attribute explicitly?
170: * <p>Disabled by default; subclasses can override this to enable ID generation
171: * as fallback: The parser will first check for an "id" attribute in this case,
172: * only falling back to a generated ID if no value was specified.
173: * @return whether the parser should generate an id if no id was specified
174: */
175: protected boolean shouldGenerateIdAsFallback() {
176: return false;
177: }
178:
179: /**
180: * Controls whether this parser is supposed to fire a
181: * {@link org.springframework.beans.factory.parsing.BeanComponentDefinition}
182: * event after parsing the bean definition.
183: * <p>This implementation returns <code>true</code> by default; that is,
184: * an event will be fired when a bean definition has been completely parsed.
185: * Override this to return <code>false</code> in order to suppress the event.
186: * @return <code>true</code> in order to fire a component registration event
187: * after parsing the bean definition; <code>false</code> to suppress the event
188: * @see #postProcessComponentDefinition
189: * @see org.springframework.beans.factory.parsing.ReaderContext#fireComponentRegistered
190: */
191: protected boolean shouldFireEvents() {
192: return true;
193: }
194:
195: /**
196: * Hook method called after the primary parsing of a
197: * {@link BeanComponentDefinition} but before the
198: * {@link BeanComponentDefinition} has been registered with a
199: * {@link org.springframework.beans.factory.support.BeanDefinitionRegistry}.
200: * <p>Derived classes can override this method to supply any custom logic that
201: * is to be executed after all the parsing is finished.
202: * <p>The default implementation is a no-op.
203: * @param componentDefinition the {@link BeanComponentDefinition} that is to be processed
204: */
205: protected void postProcessComponentDefinition(
206: BeanComponentDefinition componentDefinition) {
207: }
208:
209: }
|