001/*
002 * Copyright 2002-2016 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.List;
020import java.util.Map;
021import java.util.Properties;
022import java.util.Set;
023
024import org.w3c.dom.Element;
025
026import org.springframework.beans.factory.config.FieldRetrievingFactoryBean;
027import org.springframework.beans.factory.config.ListFactoryBean;
028import org.springframework.beans.factory.config.MapFactoryBean;
029import org.springframework.beans.factory.config.PropertiesFactoryBean;
030import org.springframework.beans.factory.config.PropertyPathFactoryBean;
031import org.springframework.beans.factory.config.SetFactoryBean;
032import org.springframework.beans.factory.support.AbstractBeanDefinition;
033import org.springframework.beans.factory.support.BeanDefinitionBuilder;
034import org.springframework.util.StringUtils;
035
036/**
037 * {@link NamespaceHandler} for the {@code util} namespace.
038 *
039 * @author Rob Harrop
040 * @author Juergen Hoeller
041 * @since 2.0
042 */
043public class UtilNamespaceHandler extends NamespaceHandlerSupport {
044
045        private static final String SCOPE_ATTRIBUTE = "scope";
046
047
048        @Override
049        public void init() {
050                registerBeanDefinitionParser("constant", new ConstantBeanDefinitionParser());
051                registerBeanDefinitionParser("property-path", new PropertyPathBeanDefinitionParser());
052                registerBeanDefinitionParser("list", new ListBeanDefinitionParser());
053                registerBeanDefinitionParser("set", new SetBeanDefinitionParser());
054                registerBeanDefinitionParser("map", new MapBeanDefinitionParser());
055                registerBeanDefinitionParser("properties", new PropertiesBeanDefinitionParser());
056        }
057
058
059        private static class ConstantBeanDefinitionParser extends AbstractSimpleBeanDefinitionParser {
060
061                @Override
062                protected Class<?> getBeanClass(Element element) {
063                        return FieldRetrievingFactoryBean.class;
064                }
065
066                @Override
067                protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) {
068                        String id = super.resolveId(element, definition, parserContext);
069                        if (!StringUtils.hasText(id)) {
070                                id = element.getAttribute("static-field");
071                        }
072                        return id;
073                }
074        }
075
076
077        private static class PropertyPathBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
078
079                @Override
080                protected Class<?> getBeanClass(Element element) {
081                        return PropertyPathFactoryBean.class;
082                }
083
084                @Override
085                protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
086                        String path = element.getAttribute("path");
087                        if (!StringUtils.hasText(path)) {
088                                parserContext.getReaderContext().error("Attribute 'path' must not be empty", element);
089                                return;
090                        }
091                        int dotIndex = path.indexOf('.');
092                        if (dotIndex == -1) {
093                                parserContext.getReaderContext().error(
094                                                "Attribute 'path' must follow pattern 'beanName.propertyName'", element);
095                                return;
096                        }
097                        String beanName = path.substring(0, dotIndex);
098                        String propertyPath = path.substring(dotIndex + 1);
099                        builder.addPropertyValue("targetBeanName", beanName);
100                        builder.addPropertyValue("propertyPath", propertyPath);
101                }
102
103                @Override
104                protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) {
105                        String id = super.resolveId(element, definition, parserContext);
106                        if (!StringUtils.hasText(id)) {
107                                id = element.getAttribute("path");
108                        }
109                        return id;
110                }
111        }
112
113
114        private static class ListBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
115
116                @Override
117                protected Class<?> getBeanClass(Element element) {
118                        return ListFactoryBean.class;
119                }
120
121                @Override
122                protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
123                        List<Object> parsedList = parserContext.getDelegate().parseListElement(element, builder.getRawBeanDefinition());
124                        builder.addPropertyValue("sourceList", parsedList);
125
126                        String listClass = element.getAttribute("list-class");
127                        if (StringUtils.hasText(listClass)) {
128                                builder.addPropertyValue("targetListClass", listClass);
129                        }
130
131                        String scope = element.getAttribute(SCOPE_ATTRIBUTE);
132                        if (StringUtils.hasLength(scope)) {
133                                builder.setScope(scope);
134                        }
135                }
136        }
137
138
139        private static class SetBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
140
141                @Override
142                protected Class<?> getBeanClass(Element element) {
143                        return SetFactoryBean.class;
144                }
145
146                @Override
147                protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
148                        Set<Object> parsedSet = parserContext.getDelegate().parseSetElement(element, builder.getRawBeanDefinition());
149                        builder.addPropertyValue("sourceSet", parsedSet);
150
151                        String setClass = element.getAttribute("set-class");
152                        if (StringUtils.hasText(setClass)) {
153                                builder.addPropertyValue("targetSetClass", setClass);
154                        }
155
156                        String scope = element.getAttribute(SCOPE_ATTRIBUTE);
157                        if (StringUtils.hasLength(scope)) {
158                                builder.setScope(scope);
159                        }
160                }
161        }
162
163
164        private static class MapBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
165
166                @Override
167                protected Class<?> getBeanClass(Element element) {
168                        return MapFactoryBean.class;
169                }
170
171                @Override
172                protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
173                        Map<Object, Object> parsedMap = parserContext.getDelegate().parseMapElement(element, builder.getRawBeanDefinition());
174                        builder.addPropertyValue("sourceMap", parsedMap);
175
176                        String mapClass = element.getAttribute("map-class");
177                        if (StringUtils.hasText(mapClass)) {
178                                builder.addPropertyValue("targetMapClass", mapClass);
179                        }
180
181                        String scope = element.getAttribute(SCOPE_ATTRIBUTE);
182                        if (StringUtils.hasLength(scope)) {
183                                builder.setScope(scope);
184                        }
185                }
186        }
187
188
189        private static class PropertiesBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
190
191                @Override
192                protected Class<?> getBeanClass(Element element) {
193                        return PropertiesFactoryBean.class;
194                }
195
196                @Override
197                protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
198                        Properties parsedProps = parserContext.getDelegate().parsePropsElement(element);
199                        builder.addPropertyValue("properties", parsedProps);
200
201                        String location = element.getAttribute("location");
202                        if (StringUtils.hasLength(location)) {
203                                location = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(location);
204                                String[] locations = StringUtils.commaDelimitedListToStringArray(location);
205                                builder.addPropertyValue("locations", locations);
206                        }
207
208                        builder.addPropertyValue("ignoreResourceNotFound",
209                                        Boolean.valueOf(element.getAttribute("ignore-resource-not-found")));
210
211                        builder.addPropertyValue("localOverride",
212                                        Boolean.valueOf(element.getAttribute("local-override")));
213
214                        String scope = element.getAttribute(SCOPE_ATTRIBUTE);
215                        if (StringUtils.hasLength(scope)) {
216                                builder.setScope(scope);
217                        }
218                }
219        }
220
221}