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.config.BeanDefinition;
022import org.springframework.beans.factory.support.AbstractBeanDefinition;
023import org.springframework.beans.factory.support.BeanDefinitionBuilder;
024import org.springframework.lang.Nullable;
025
026/**
027 * Base class for those {@link BeanDefinitionParser} implementations that
028 * need to parse and define just a <i>single</i> {@code BeanDefinition}.
029 *
030 * <p>Extend this parser class when you want to create a single bean definition
031 * from an arbitrarily complex XML element. You may wish to consider extending
032 * the {@link AbstractSimpleBeanDefinitionParser} when you want to create a
033 * single bean definition from a relatively simple custom XML element.
034 *
035 * <p>The resulting {@code BeanDefinition} will be automatically registered
036 * with the {@link org.springframework.beans.factory.support.BeanDefinitionRegistry}.
037 * Your job simply is to {@link #doParse parse} the custom XML {@link Element}
038 * into a single {@code BeanDefinition}.
039 *
040 * @author Rob Harrop
041 * @author Juergen Hoeller
042 * @author Rick Evans
043 * @since 2.0
044 * @see #getBeanClass
045 * @see #getBeanClassName
046 * @see #doParse
047 */
048public abstract class AbstractSingleBeanDefinitionParser extends AbstractBeanDefinitionParser {
049
050        /**
051         * Creates a {@link BeanDefinitionBuilder} instance for the
052         * {@link #getBeanClass bean Class} and passes it to the
053         * {@link #doParse} strategy method.
054         * @param element the element that is to be parsed into a single BeanDefinition
055         * @param parserContext the object encapsulating the current state of the parsing process
056         * @return the BeanDefinition resulting from the parsing of the supplied {@link Element}
057         * @throws IllegalStateException if the bean {@link Class} returned from
058         * {@link #getBeanClass(org.w3c.dom.Element)} is {@code null}
059         * @see #doParse
060         */
061        @Override
062        protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
063                BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
064                String parentName = getParentName(element);
065                if (parentName != null) {
066                        builder.getRawBeanDefinition().setParentName(parentName);
067                }
068                Class<?> beanClass = getBeanClass(element);
069                if (beanClass != null) {
070                        builder.getRawBeanDefinition().setBeanClass(beanClass);
071                }
072                else {
073                        String beanClassName = getBeanClassName(element);
074                        if (beanClassName != null) {
075                                builder.getRawBeanDefinition().setBeanClassName(beanClassName);
076                        }
077                }
078                builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
079                BeanDefinition containingBd = parserContext.getContainingBeanDefinition();
080                if (containingBd != null) {
081                        // Inner bean definition must receive same scope as containing bean.
082                        builder.setScope(containingBd.getScope());
083                }
084                if (parserContext.isDefaultLazyInit()) {
085                        // Default-lazy-init applies to custom bean definitions as well.
086                        builder.setLazyInit(true);
087                }
088                doParse(element, parserContext, builder);
089                return builder.getBeanDefinition();
090        }
091
092        /**
093         * Determine the name for the parent of the currently parsed bean,
094         * in case of the current bean being defined as a child bean.
095         * <p>The default implementation returns {@code null},
096         * indicating a root bean definition.
097         * @param element the {@code Element} that is being parsed
098         * @return the name of the parent bean for the currently parsed bean,
099         * or {@code null} if none
100         */
101        @Nullable
102        protected String getParentName(Element element) {
103                return null;
104        }
105
106        /**
107         * Determine the bean class corresponding to the supplied {@link Element}.
108         * <p>Note that, for application classes, it is generally preferable to
109         * override {@link #getBeanClassName} instead, in order to avoid a direct
110         * dependence on the bean implementation class. The BeanDefinitionParser
111         * and its NamespaceHandler can be used within an IDE plugin then, even
112         * if the application classes are not available on the plugin's classpath.
113         * @param element the {@code Element} that is being parsed
114         * @return the {@link Class} of the bean that is being defined via parsing
115         * the supplied {@code Element}, or {@code null} if none
116         * @see #getBeanClassName
117         */
118        @Nullable
119        protected Class<?> getBeanClass(Element element) {
120                return null;
121        }
122
123        /**
124         * Determine the bean class name corresponding to the supplied {@link Element}.
125         * @param element the {@code Element} that is being parsed
126         * @return the class name of the bean that is being defined via parsing
127         * the supplied {@code Element}, or {@code null} if none
128         * @see #getBeanClass
129         */
130        @Nullable
131        protected String getBeanClassName(Element element) {
132                return null;
133        }
134
135        /**
136         * Parse the supplied {@link Element} and populate the supplied
137         * {@link BeanDefinitionBuilder} as required.
138         * <p>The default implementation delegates to the {@code doParse}
139         * version without ParserContext argument.
140         * @param element the XML element being parsed
141         * @param parserContext the object encapsulating the current state of the parsing process
142         * @param builder used to define the {@code BeanDefinition}
143         * @see #doParse(Element, BeanDefinitionBuilder)
144         */
145        protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
146                doParse(element, builder);
147        }
148
149        /**
150         * Parse the supplied {@link Element} and populate the supplied
151         * {@link BeanDefinitionBuilder} as required.
152         * <p>The default implementation does nothing.
153         * @param element the XML element being parsed
154         * @param builder used to define the {@code BeanDefinition}
155         */
156        protected void doParse(Element element, BeanDefinitionBuilder builder) {
157        }
158
159}