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