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