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