1 /*
2  * Copyright 2002-2011 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package org.springframework.beans.factory.config;
18 
19 import java.util.LinkedHashMap;
20 import java.util.LinkedHashSet;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.Set;
24 
25 import org.springframework.beans.MutablePropertyValues;
26 import org.springframework.beans.PropertyValue;
27 import org.springframework.util.Assert;
28 import org.springframework.util.ObjectUtils;
29 import org.springframework.util.StringValueResolver;
30 
31 /**
32  * Visitor class for traversing {@link BeanDefinition} objects, in particular
33  * the property values and constructor argument values contained in them,
34  * resolving bean metadata values.
35  *
36  * <p>Used by {@link PropertyPlaceholderConfigurer} to parse all String values
37  * contained in a BeanDefinition, resolving any placeholders found.
38  *
39  * @author Juergen Hoeller
40  * @author Sam Brannen
41  * @since 1.2
42  * @see BeanDefinition
43  * @see BeanDefinition#getPropertyValues
44  * @see BeanDefinition#getConstructorArgumentValues
45  * @see PropertyPlaceholderConfigurer
46  */
47 public class BeanDefinitionVisitor {
48 
49 	private StringValueResolver valueResolver;
50 
51 
52 	/**
53 	 * Create a new BeanDefinitionVisitor, applying the specified
54 	 * value resolver to all bean metadata values.
55 	 * @param valueResolver the StringValueResolver to apply
56 	 */
BeanDefinitionVisitor(StringValueResolver valueResolver)57 	public BeanDefinitionVisitor(StringValueResolver valueResolver) {
58 		Assert.notNull(valueResolver, "StringValueResolver must not be null");
59 		this.valueResolver = valueResolver;
60 	}
61 
62 	/**
63 	 * Create a new BeanDefinitionVisitor for subclassing.
64 	 * Subclasses need to override the {@link #resolveStringValue} method.
65 	 */
BeanDefinitionVisitor()66 	protected BeanDefinitionVisitor() {
67 	}
68 
69 
70 	/**
71 	 * Traverse the given BeanDefinition object and the MutablePropertyValues
72 	 * and ConstructorArgumentValues contained in them.
73 	 * @param beanDefinition the BeanDefinition object to traverse
74 	 * @see #resolveStringValue(String)
75 	 */
visitBeanDefinition(BeanDefinition beanDefinition)76 	public void visitBeanDefinition(BeanDefinition beanDefinition) {
77 		visitParentName(beanDefinition);
78 		visitBeanClassName(beanDefinition);
79 		visitFactoryBeanName(beanDefinition);
80 		visitFactoryMethodName(beanDefinition);
81 		visitScope(beanDefinition);
82 		visitPropertyValues(beanDefinition.getPropertyValues());
83 		ConstructorArgumentValues cas = beanDefinition.getConstructorArgumentValues();
84 		visitIndexedArgumentValues(cas.getIndexedArgumentValues());
85 		visitGenericArgumentValues(cas.getGenericArgumentValues());
86 	}
87 
visitParentName(BeanDefinition beanDefinition)88 	protected void visitParentName(BeanDefinition beanDefinition) {
89 		String parentName = beanDefinition.getParentName();
90 		if (parentName != null) {
91 			String resolvedName = resolveStringValue(parentName);
92 			if (!parentName.equals(resolvedName)) {
93 				beanDefinition.setParentName(resolvedName);
94 			}
95 		}
96 	}
97 
visitBeanClassName(BeanDefinition beanDefinition)98 	protected void visitBeanClassName(BeanDefinition beanDefinition) {
99 		String beanClassName = beanDefinition.getBeanClassName();
100 		if (beanClassName != null) {
101 			String resolvedName = resolveStringValue(beanClassName);
102 			if (!beanClassName.equals(resolvedName)) {
103 				beanDefinition.setBeanClassName(resolvedName);
104 			}
105 		}
106 	}
107 
visitFactoryBeanName(BeanDefinition beanDefinition)108 	protected void visitFactoryBeanName(BeanDefinition beanDefinition) {
109 		String factoryBeanName = beanDefinition.getFactoryBeanName();
110 		if (factoryBeanName != null) {
111 			String resolvedName = resolveStringValue(factoryBeanName);
112 			if (!factoryBeanName.equals(resolvedName)) {
113 				beanDefinition.setFactoryBeanName(resolvedName);
114 			}
115 		}
116 	}
117 
visitFactoryMethodName(BeanDefinition beanDefinition)118 	protected void visitFactoryMethodName(BeanDefinition beanDefinition) {
119 		String factoryMethodName = beanDefinition.getFactoryMethodName();
120 		if (factoryMethodName != null) {
121 			String resolvedName = resolveStringValue(factoryMethodName);
122 			if (!factoryMethodName.equals(resolvedName)) {
123 				beanDefinition.setFactoryMethodName(resolvedName);
124 			}
125 		}
126 	}
127 
visitScope(BeanDefinition beanDefinition)128 	protected void visitScope(BeanDefinition beanDefinition) {
129 		String scope = beanDefinition.getScope();
130 		if (scope != null) {
131 			String resolvedScope = resolveStringValue(scope);
132 			if (!scope.equals(resolvedScope)) {
133 				beanDefinition.setScope(resolvedScope);
134 			}
135 		}
136 	}
137 
visitPropertyValues(MutablePropertyValues pvs)138 	protected void visitPropertyValues(MutablePropertyValues pvs) {
139 		PropertyValue[] pvArray = pvs.getPropertyValues();
140 		for (PropertyValue pv : pvArray) {
141 			Object newVal = resolveValue(pv.getValue());
142 			if (!ObjectUtils.nullSafeEquals(newVal, pv.getValue())) {
143 				pvs.add(pv.getName(), newVal);
144 			}
145 		}
146 	}
147 
visitIndexedArgumentValues(Map<Integer, ConstructorArgumentValues.ValueHolder> ias)148 	protected void visitIndexedArgumentValues(Map<Integer, ConstructorArgumentValues.ValueHolder> ias) {
149 		for (ConstructorArgumentValues.ValueHolder valueHolder : ias.values()) {
150 			Object newVal = resolveValue(valueHolder.getValue());
151 			if (!ObjectUtils.nullSafeEquals(newVal, valueHolder.getValue())) {
152 				valueHolder.setValue(newVal);
153 			}
154 		}
155 	}
156 
visitGenericArgumentValues(List<ConstructorArgumentValues.ValueHolder> gas)157 	protected void visitGenericArgumentValues(List<ConstructorArgumentValues.ValueHolder> gas) {
158 		for (ConstructorArgumentValues.ValueHolder valueHolder : gas) {
159 			Object newVal = resolveValue(valueHolder.getValue());
160 			if (!ObjectUtils.nullSafeEquals(newVal, valueHolder.getValue())) {
161 				valueHolder.setValue(newVal);
162 			}
163 		}
164 	}
165 
166 	@SuppressWarnings("rawtypes")
resolveValue(Object value)167 	protected Object resolveValue(Object value) {
168 		if (value instanceof BeanDefinition) {
169 			visitBeanDefinition((BeanDefinition) value);
170 		}
171 		else if (value instanceof BeanDefinitionHolder) {
172 			visitBeanDefinition(((BeanDefinitionHolder) value).getBeanDefinition());
173 		}
174 		else if (value instanceof RuntimeBeanReference) {
175 			RuntimeBeanReference ref = (RuntimeBeanReference) value;
176 			String newBeanName = resolveStringValue(ref.getBeanName());
177 			if (!newBeanName.equals(ref.getBeanName())) {
178 				return new RuntimeBeanReference(newBeanName);
179 			}
180 		}
181 		else if (value instanceof RuntimeBeanNameReference) {
182 			RuntimeBeanNameReference ref = (RuntimeBeanNameReference) value;
183 			String newBeanName = resolveStringValue(ref.getBeanName());
184 			if (!newBeanName.equals(ref.getBeanName())) {
185 				return new RuntimeBeanNameReference(newBeanName);
186 			}
187 		}
188 		else if (value instanceof Object[]) {
189 			visitArray((Object[]) value);
190 		}
191 		else if (value instanceof List) {
192 			visitList((List) value);
193 		}
194 		else if (value instanceof Set) {
195 			visitSet((Set) value);
196 		}
197 		else if (value instanceof Map) {
198 			visitMap((Map) value);
199 		}
200 		else if (value instanceof TypedStringValue) {
201 			TypedStringValue typedStringValue = (TypedStringValue) value;
202 			String stringValue = typedStringValue.getValue();
203 			if (stringValue != null) {
204 				String visitedString = resolveStringValue(stringValue);
205 				typedStringValue.setValue(visitedString);
206 			}
207 		}
208 		else if (value instanceof String) {
209 			return resolveStringValue((String) value);
210 		}
211 		return value;
212 	}
213 
visitArray(Object[] arrayVal)214 	protected void visitArray(Object[] arrayVal) {
215 		for (int i = 0; i < arrayVal.length; i++) {
216 			Object elem = arrayVal[i];
217 			Object newVal = resolveValue(elem);
218 			if (!ObjectUtils.nullSafeEquals(newVal, elem)) {
219 				arrayVal[i] = newVal;
220 			}
221 		}
222 	}
223 
224 	@SuppressWarnings({"unchecked", "rawtypes"})
visitList(List listVal)225 	protected void visitList(List listVal) {
226 		for (int i = 0; i < listVal.size(); i++) {
227 			Object elem = listVal.get(i);
228 			Object newVal = resolveValue(elem);
229 			if (!ObjectUtils.nullSafeEquals(newVal, elem)) {
230 				listVal.set(i, newVal);
231 			}
232 		}
233 	}
234 
235 	@SuppressWarnings({"unchecked", "rawtypes"})
visitSet(Set setVal)236 	protected void visitSet(Set setVal) {
237 		Set newContent = new LinkedHashSet();
238 		boolean entriesModified = false;
239 		for (Object elem : setVal) {
240 			int elemHash = (elem != null ? elem.hashCode() : 0);
241 			Object newVal = resolveValue(elem);
242 			int newValHash = (newVal != null ? newVal.hashCode() : 0);
243 			newContent.add(newVal);
244 			entriesModified = entriesModified || (newVal != elem || newValHash != elemHash);
245 		}
246 		if (entriesModified) {
247 			setVal.clear();
248 			setVal.addAll(newContent);
249 		}
250 	}
251 
252 	@SuppressWarnings({"unchecked", "rawtypes"})
visitMap(Map<?, ?> mapVal)253 	protected void visitMap(Map<?, ?> mapVal) {
254 		Map newContent = new LinkedHashMap();
255 		boolean entriesModified = false;
256 		for (Map.Entry entry : mapVal.entrySet()) {
257 			Object key = entry.getKey();
258 			int keyHash = (key != null ? key.hashCode() : 0);
259 			Object newKey = resolveValue(key);
260 			int newKeyHash = (newKey != null ? newKey.hashCode() : 0);
261 			Object val = entry.getValue();
262 			Object newVal = resolveValue(val);
263 			newContent.put(newKey, newVal);
264 			entriesModified = entriesModified || (newVal != val || newKey != key || newKeyHash != keyHash);
265 		}
266 		if (entriesModified) {
267 			mapVal.clear();
268 			mapVal.putAll(newContent);
269 		}
270 	}
271 
272 	/**
273 	 * Resolve the given String value, for example parsing placeholders.
274 	 * @param strVal the original String value
275 	 * @return the resolved String value
276 	 */
resolveStringValue(String strVal)277 	protected String resolveStringValue(String strVal) {
278 		if (this.valueResolver == null) {
279 			throw new IllegalStateException("No StringValueResolver specified - pass a resolver " +
280 					"object into the constructor or override the 'resolveStringValue' method");
281 		}
282 		String resolvedValue = this.valueResolver.resolveStringValue(strVal);
283 		// Return original String if not modified.
284 		return (strVal.equals(resolvedValue) ? strVal : resolvedValue);
285 	}
286 
287 }
288