001/* 002 * Copyright 2002-2020 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.Iterator; 020import java.util.List; 021import java.util.Spliterator; 022import java.util.Spliterators; 023import java.util.concurrent.CopyOnWriteArrayList; 024import java.util.stream.Stream; 025 026import org.springframework.lang.Nullable; 027 028/** 029 * The default implementation of the {@link PropertySources} interface. 030 * Allows manipulation of contained property sources and provides a constructor 031 * for copying an existing {@code PropertySources} instance. 032 * 033 * <p>Where <em>precedence</em> is mentioned in methods such as {@link #addFirst} 034 * and {@link #addLast}, this is with regard to the order in which property sources 035 * will be searched when resolving a given property with a {@link PropertyResolver}. 036 * 037 * @author Chris Beams 038 * @author Juergen Hoeller 039 * @since 3.1 040 * @see PropertySourcesPropertyResolver 041 */ 042public class MutablePropertySources implements PropertySources { 043 044 private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<>(); 045 046 047 /** 048 * Create a new {@link MutablePropertySources} object. 049 */ 050 public MutablePropertySources() { 051 } 052 053 /** 054 * Create a new {@code MutablePropertySources} from the given propertySources 055 * object, preserving the original order of contained {@code PropertySource} objects. 056 */ 057 public MutablePropertySources(PropertySources propertySources) { 058 this(); 059 for (PropertySource<?> propertySource : propertySources) { 060 addLast(propertySource); 061 } 062 } 063 064 065 @Override 066 public Iterator<PropertySource<?>> iterator() { 067 return this.propertySourceList.iterator(); 068 } 069 070 @Override 071 public Spliterator<PropertySource<?>> spliterator() { 072 return Spliterators.spliterator(this.propertySourceList, 0); 073 } 074 075 @Override 076 public Stream<PropertySource<?>> stream() { 077 return this.propertySourceList.stream(); 078 } 079 080 @Override 081 public boolean contains(String name) { 082 for (PropertySource<?> propertySource : this.propertySourceList) { 083 if (propertySource.getName().equals(name)) { 084 return true; 085 } 086 } 087 return false; 088 } 089 090 @Override 091 @Nullable 092 public PropertySource<?> get(String name) { 093 for (PropertySource<?> propertySource : this.propertySourceList) { 094 if (propertySource.getName().equals(name)) { 095 return propertySource; 096 } 097 } 098 return null; 099 } 100 101 102 /** 103 * Add the given property source object with highest precedence. 104 */ 105 public void addFirst(PropertySource<?> propertySource) { 106 synchronized (this.propertySourceList) { 107 removeIfPresent(propertySource); 108 this.propertySourceList.add(0, propertySource); 109 } 110 } 111 112 /** 113 * Add the given property source object with lowest precedence. 114 */ 115 public void addLast(PropertySource<?> propertySource) { 116 synchronized (this.propertySourceList) { 117 removeIfPresent(propertySource); 118 this.propertySourceList.add(propertySource); 119 } 120 } 121 122 /** 123 * Add the given property source object with precedence immediately higher 124 * than the named relative property source. 125 */ 126 public void addBefore(String relativePropertySourceName, PropertySource<?> propertySource) { 127 assertLegalRelativeAddition(relativePropertySourceName, propertySource); 128 synchronized (this.propertySourceList) { 129 removeIfPresent(propertySource); 130 int index = assertPresentAndGetIndex(relativePropertySourceName); 131 addAtIndex(index, propertySource); 132 } 133 } 134 135 /** 136 * Add the given property source object with precedence immediately lower 137 * than the named relative property source. 138 */ 139 public void addAfter(String relativePropertySourceName, PropertySource<?> propertySource) { 140 assertLegalRelativeAddition(relativePropertySourceName, propertySource); 141 synchronized (this.propertySourceList) { 142 removeIfPresent(propertySource); 143 int index = assertPresentAndGetIndex(relativePropertySourceName); 144 addAtIndex(index + 1, propertySource); 145 } 146 } 147 148 /** 149 * Return the precedence of the given property source, {@code -1} if not found. 150 */ 151 public int precedenceOf(PropertySource<?> propertySource) { 152 return this.propertySourceList.indexOf(propertySource); 153 } 154 155 /** 156 * Remove and return the property source with the given name, {@code null} if not found. 157 * @param name the name of the property source to find and remove 158 */ 159 @Nullable 160 public PropertySource<?> remove(String name) { 161 synchronized (this.propertySourceList) { 162 int index = this.propertySourceList.indexOf(PropertySource.named(name)); 163 return (index != -1 ? this.propertySourceList.remove(index) : null); 164 } 165 } 166 167 /** 168 * Replace the property source with the given name with the given property source object. 169 * @param name the name of the property source to find and replace 170 * @param propertySource the replacement property source 171 * @throws IllegalArgumentException if no property source with the given name is present 172 * @see #contains 173 */ 174 public void replace(String name, PropertySource<?> propertySource) { 175 synchronized (this.propertySourceList) { 176 int index = assertPresentAndGetIndex(name); 177 this.propertySourceList.set(index, propertySource); 178 } 179 } 180 181 /** 182 * Return the number of {@link PropertySource} objects contained. 183 */ 184 public int size() { 185 return this.propertySourceList.size(); 186 } 187 188 @Override 189 public String toString() { 190 return this.propertySourceList.toString(); 191 } 192 193 194 /** 195 * Ensure that the given property source is not being added relative to itself. 196 */ 197 protected void assertLegalRelativeAddition(String relativePropertySourceName, PropertySource<?> propertySource) { 198 String newPropertySourceName = propertySource.getName(); 199 if (relativePropertySourceName.equals(newPropertySourceName)) { 200 throw new IllegalArgumentException( 201 "PropertySource named '" + newPropertySourceName + "' cannot be added relative to itself"); 202 } 203 } 204 205 /** 206 * Remove the given property source if it is present. 207 */ 208 protected void removeIfPresent(PropertySource<?> propertySource) { 209 this.propertySourceList.remove(propertySource); 210 } 211 212 /** 213 * Add the given property source at a particular index in the list. 214 */ 215 private void addAtIndex(int index, PropertySource<?> propertySource) { 216 removeIfPresent(propertySource); 217 this.propertySourceList.add(index, propertySource); 218 } 219 220 /** 221 * Assert that the named property source is present and return its index. 222 * @param name {@linkplain PropertySource#getName() name of the property source} to find 223 * @throws IllegalArgumentException if the named property source is not present 224 */ 225 private int assertPresentAndGetIndex(String name) { 226 int index = this.propertySourceList.indexOf(PropertySource.named(name)); 227 if (index == -1) { 228 throw new IllegalArgumentException("PropertySource named '" + name + "' does not exist"); 229 } 230 return index; 231 } 232 233}