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.core.env;
018
019import java.util.ArrayList;
020import java.util.Arrays;
021import java.util.Collection;
022import java.util.LinkedHashSet;
023import java.util.List;
024import java.util.Set;
025
026import org.springframework.lang.Nullable;
027import org.springframework.util.StringUtils;
028
029/**
030 * Composite {@link PropertySource} implementation that iterates over a set of
031 * {@link PropertySource} instances. Necessary in cases where multiple property sources
032 * share the same name, e.g. when multiple values are supplied to {@code @PropertySource}.
033 *
034 * <p>As of Spring 4.1.2, this class extends {@link EnumerablePropertySource} instead
035 * of plain {@link PropertySource}, exposing {@link #getPropertyNames()} based on the
036 * accumulated property names from all contained sources (as far as possible).
037 *
038 * @author Chris Beams
039 * @author Juergen Hoeller
040 * @author Phillip Webb
041 * @since 3.1.1
042 */
043public class CompositePropertySource extends EnumerablePropertySource<Object> {
044
045        private final Set<PropertySource<?>> propertySources = new LinkedHashSet<>();
046
047
048        /**
049         * Create a new {@code CompositePropertySource}.
050         * @param name the name of the property source
051         */
052        public CompositePropertySource(String name) {
053                super(name);
054        }
055
056
057        @Override
058        @Nullable
059        public Object getProperty(String name) {
060                for (PropertySource<?> propertySource : this.propertySources) {
061                        Object candidate = propertySource.getProperty(name);
062                        if (candidate != null) {
063                                return candidate;
064                        }
065                }
066                return null;
067        }
068
069        @Override
070        public boolean containsProperty(String name) {
071                for (PropertySource<?> propertySource : this.propertySources) {
072                        if (propertySource.containsProperty(name)) {
073                                return true;
074                        }
075                }
076                return false;
077        }
078
079        @Override
080        public String[] getPropertyNames() {
081                Set<String> names = new LinkedHashSet<>();
082                for (PropertySource<?> propertySource : this.propertySources) {
083                        if (!(propertySource instanceof EnumerablePropertySource)) {
084                                throw new IllegalStateException(
085                                                "Failed to enumerate property names due to non-enumerable property source: " + propertySource);
086                        }
087                        names.addAll(Arrays.asList(((EnumerablePropertySource<?>) propertySource).getPropertyNames()));
088                }
089                return StringUtils.toStringArray(names);
090        }
091
092
093        /**
094         * Add the given {@link PropertySource} to the end of the chain.
095         * @param propertySource the PropertySource to add
096         */
097        public void addPropertySource(PropertySource<?> propertySource) {
098                this.propertySources.add(propertySource);
099        }
100
101        /**
102         * Add the given {@link PropertySource} to the start of the chain.
103         * @param propertySource the PropertySource to add
104         * @since 4.1
105         */
106        public void addFirstPropertySource(PropertySource<?> propertySource) {
107                List<PropertySource<?>> existing = new ArrayList<>(this.propertySources);
108                this.propertySources.clear();
109                this.propertySources.add(propertySource);
110                this.propertySources.addAll(existing);
111        }
112
113        /**
114         * Return all property sources that this composite source holds.
115         * @since 4.1.1
116         */
117        public Collection<PropertySource<?>> getPropertySources() {
118                return this.propertySources;
119        }
120
121
122        @Override
123        public String toString() {
124                return getClass().getSimpleName() + " {name='" + this.name + "', propertySources=" + this.propertySources + "}";
125        }
126
127}