001/*
002 * Copyright 2002-2015 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.core.io.support;
018
019import java.io.IOException;
020import java.util.Map;
021import java.util.Properties;
022
023import org.springframework.core.env.PropertiesPropertySource;
024import org.springframework.core.io.DefaultResourceLoader;
025import org.springframework.core.io.Resource;
026import org.springframework.util.StringUtils;
027
028/**
029 * Subclass of {@link PropertiesPropertySource} that loads a {@link Properties} object
030 * from a given {@link org.springframework.core.io.Resource} or resource location such as
031 * {@code "classpath:/com/myco/foo.properties"} or {@code "file:/path/to/file.xml"}.
032 *
033 * <p>Both traditional and XML-based properties file formats are supported; however, in
034 * order for XML processing to take effect, the underlying {@code Resource}'s
035 * {@link org.springframework.core.io.Resource#getFilename() getFilename()} method must
036 * return a non-{@code null} value that ends in {@code ".xml"}.
037 *
038 * @author Chris Beams
039 * @author Juergen Hoeller
040 * @since 3.1
041 * @see org.springframework.core.io.Resource
042 * @see org.springframework.core.io.support.EncodedResource
043 */
044public class ResourcePropertySource extends PropertiesPropertySource {
045
046        /** The original resource name, if different from the given name */
047        private final String resourceName;
048
049
050        /**
051         * Create a PropertySource having the given name based on Properties
052         * loaded from the given encoded resource.
053         */
054        public ResourcePropertySource(String name, EncodedResource resource) throws IOException {
055                super(name, PropertiesLoaderUtils.loadProperties(resource));
056                this.resourceName = getNameForResource(resource.getResource());
057        }
058
059        /**
060         * Create a PropertySource based on Properties loaded from the given resource.
061         * The name of the PropertySource will be generated based on the
062         * {@link Resource#getDescription() description} of the given resource.
063         */
064        public ResourcePropertySource(EncodedResource resource) throws IOException {
065                super(getNameForResource(resource.getResource()), PropertiesLoaderUtils.loadProperties(resource));
066                this.resourceName = null;
067        }
068
069        /**
070         * Create a PropertySource having the given name based on Properties
071         * loaded from the given encoded resource.
072         */
073        public ResourcePropertySource(String name, Resource resource) throws IOException {
074                super(name, PropertiesLoaderUtils.loadProperties(new EncodedResource(resource)));
075                this.resourceName = getNameForResource(resource);
076        }
077
078        /**
079         * Create a PropertySource based on Properties loaded from the given resource.
080         * The name of the PropertySource will be generated based on the
081         * {@link Resource#getDescription() description} of the given resource.
082         */
083        public ResourcePropertySource(Resource resource) throws IOException {
084                super(getNameForResource(resource), PropertiesLoaderUtils.loadProperties(new EncodedResource(resource)));
085                this.resourceName = null;
086        }
087
088        /**
089         * Create a PropertySource having the given name based on Properties loaded from
090         * the given resource location and using the given class loader to load the
091         * resource (assuming it is prefixed with {@code classpath:}).
092         */
093        public ResourcePropertySource(String name, String location, ClassLoader classLoader) throws IOException {
094                this(name, new DefaultResourceLoader(classLoader).getResource(location));
095        }
096
097        /**
098         * Create a PropertySource based on Properties loaded from the given resource
099         * location and use the given class loader to load the resource, assuming it is
100         * prefixed with {@code classpath:}. The name of the PropertySource will be
101         * generated based on the {@link Resource#getDescription() description} of the
102         * resource.
103         */
104        public ResourcePropertySource(String location, ClassLoader classLoader) throws IOException {
105                this(new DefaultResourceLoader(classLoader).getResource(location));
106        }
107
108        /**
109         * Create a PropertySource having the given name based on Properties loaded from
110         * the given resource location. The default thread context class loader will be
111         * used to load the resource (assuming the location string is prefixed with
112         * {@code classpath:}.
113         */
114        public ResourcePropertySource(String name, String location) throws IOException {
115                this(name, new DefaultResourceLoader().getResource(location));
116        }
117
118        /**
119         * Create a PropertySource based on Properties loaded from the given resource
120         * location. The name of the PropertySource will be generated based on the
121         * {@link Resource#getDescription() description} of the resource.
122         */
123        public ResourcePropertySource(String location) throws IOException {
124                this(new DefaultResourceLoader().getResource(location));
125        }
126
127        private ResourcePropertySource(String name, String resourceName, Map<String, Object> source) {
128                super(name, source);
129                this.resourceName = resourceName;
130        }
131
132
133        /**
134         * Return a potentially adapted variant of this {@link ResourcePropertySource},
135         * overriding the previously given (or derived) name with the specified name.
136         * @since 4.0.4
137         */
138        public ResourcePropertySource withName(String name) {
139                if (this.name.equals(name)) {
140                        return this;
141                }
142                // Store the original resource name if necessary...
143                if (this.resourceName != null) {
144                        if (this.resourceName.equals(name)) {
145                                return new ResourcePropertySource(this.resourceName, null, this.source);
146                        }
147                        else {
148                                return new ResourcePropertySource(name, this.resourceName, this.source);
149                        }
150                }
151                else {
152                        // Current name is resource name -> preserve it in the extra field...
153                        return new ResourcePropertySource(name, this.name, this.source);
154                }
155        }
156
157        /**
158         * Return a potentially adapted variant of this {@link ResourcePropertySource},
159         * overriding the previously given name (if any) with the original resource name
160         * (equivalent to the name generated by the name-less constructor variants).
161         * @since 4.1
162         */
163        public ResourcePropertySource withResourceName() {
164                if (this.resourceName == null) {
165                        return this;
166                }
167                return new ResourcePropertySource(this.resourceName, null, this.source);
168        }
169
170
171        /**
172         * Return the description for the given Resource; if the description is
173         * empty, return the class name of the resource plus its identity hash code.
174         * @see org.springframework.core.io.Resource#getDescription()
175         */
176        private static String getNameForResource(Resource resource) {
177                String name = resource.getDescription();
178                if (!StringUtils.hasText(name)) {
179                        name = resource.getClass().getSimpleName() + "@" + System.identityHashCode(resource);
180                }
181                return name;
182        }
183
184}