001 /**
002 * Copyright 2005-2013 The Kuali Foundation
003 *
004 * Licensed under the Educational Community 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 * http://www.opensource.org/licenses/ecl2.php
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 package org.kuali.rice.krad.uif.util;
017
018 import org.apache.commons.lang.StringUtils;
019 import org.apache.commons.logging.Log;
020 import org.apache.commons.logging.LogFactory;
021 import org.kuali.rice.krad.datadictionary.uif.UifDictionaryBean;
022 import org.kuali.rice.krad.uif.UifConstants;
023 import org.kuali.rice.krad.uif.UifPropertyPaths;
024 import org.kuali.rice.krad.uif.service.ExpressionEvaluatorService;
025 import org.springframework.beans.BeansException;
026 import org.springframework.beans.MutablePropertyValues;
027 import org.springframework.beans.PropertyValue;
028 import org.springframework.beans.factory.config.BeanDefinition;
029 import org.springframework.beans.factory.config.BeanDefinitionHolder;
030 import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
031 import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
032 import org.springframework.beans.factory.config.TypedStringValue;
033 import org.springframework.beans.factory.support.BeanDefinitionRegistry;
034 import org.springframework.beans.factory.support.ManagedArray;
035 import org.springframework.beans.factory.support.ManagedList;
036 import org.springframework.beans.factory.support.ManagedMap;
037 import org.springframework.beans.factory.support.ManagedSet;
038
039 import java.util.HashMap;
040 import java.util.HashSet;
041 import java.util.List;
042 import java.util.Map;
043 import java.util.Set;
044
045 /**
046 * Post processes the bean factory to handle UIF property expressions and IDs on inner beans
047 *
048 * <p>
049 * Conditional logic can be implemented with the UIF dictionary by means of property expressions. These are
050 * expressions that follow SPEL and can be given as the value for a property using the @{} placeholder. Since such
051 * a value would cause an exception when creating the object if the property is a non-string type (value cannot be
052 * converted), we need to move those expressions to a Map for processing, and then remove the original property
053 * configuration containing the expression. The expressions are then evaluated during the view apply model phase and
054 * the result is set as the value for the corresponding property.
055 * </p>
056 *
057 * <p>
058 * Spring will not register inner beans with IDs so that the bean definition can be retrieved through the factory,
059 * therefore this post processor adds them as top level registered beans
060 * </p>
061 *
062 * TODO: convert to dictionary bean processor
063 *
064 * @author Kuali Rice Team (rice.collab@kuali.org)
065 */
066 public class UifBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
067 private static final Log LOG = LogFactory.getLog(UifBeanFactoryPostProcessor.class);
068
069 /**
070 * Iterates through all beans in the factory and invokes processing
071 *
072 * @param beanFactory bean factory instance to process
073 * @throws org.springframework.beans.BeansException
074 */
075 public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
076 Set<String> processedBeanNames = new HashSet<String>();
077
078 LOG.info("Beginning post processing of bean factory for UIF expressions");
079
080 String[] beanNames = beanFactory.getBeanDefinitionNames();
081 for (int i = 0; i < beanNames.length; i++) {
082 String beanName = beanNames[i];
083 BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
084
085 processBeanDefinition(beanName, beanDefinition, beanFactory, processedBeanNames);
086 }
087
088 LOG.info("Finished post processing of bean factory for UIF expressions");
089 }
090
091 /**
092 * Processes a top level (non nested) bean definition for expressions
093 *
094 * <p>
095 * A bean that is non nested (or top of a collection) will hold all the expressions for the graph. A new
096 * expression graph is initialized and expressions are collected as the bean and all its children are processed.
097 * The expression graph is then set as a property on the top bean definition
098 * </p>
099 *
100 * @param beanName name of the bean to process
101 * @param beanDefinition bean definition to process
102 * @param beanFactory factory holding all the bean definitions
103 * @param processedBeanNames set of bean names that have already been processed
104 */
105 protected void processBeanDefinition(String beanName, BeanDefinition beanDefinition,
106 ConfigurableListableBeanFactory beanFactory, Set<String> processedBeanNames) {
107 Class<?> beanClass = getBeanClass(beanDefinition, beanFactory);
108 if ((beanClass == null) || !UifDictionaryBean.class.isAssignableFrom(beanClass) || processedBeanNames.contains(
109 beanName)) {
110 return;
111 }
112
113 // process bean definition and all nested definitions for expressions
114 ManagedMap<String, String> expressionGraph = new ManagedMap<String, String>();
115 MutablePropertyValues pvs = beanDefinition.getPropertyValues();
116 if (pvs.contains(UifPropertyPaths.EXPRESSION_GRAPH)) {
117 expressionGraph = (ManagedMap<String, String>) pvs.getPropertyValue(UifPropertyPaths.EXPRESSION_GRAPH)
118 .getValue();
119 if (expressionGraph == null) {
120 expressionGraph = new ManagedMap<String, String>();
121 }
122 }
123
124 expressionGraph.setMergeEnabled(false);
125 processNestedBeanDefinition(beanName, beanDefinition, "", expressionGraph, beanFactory, processedBeanNames);
126
127 // add property for expression graph
128 pvs = beanDefinition.getPropertyValues();
129 pvs.addPropertyValue(UifPropertyPaths.EXPRESSION_GRAPH, expressionGraph);
130 }
131
132 /**
133 * If the bean class is type UifDictionaryBean, iterate through configured property values
134 * and check for expressions
135 *
136 * @param beanName name of the bean in the factory (only set for top level beans, not nested)
137 * @param beanDefinition bean definition to process for expressions
138 * @param nestedPropertyName
139 * @param expressionGraph
140 * @param beanFactory bean factory being processed
141 * @param processedBeanNames
142 */
143 protected void processNestedBeanDefinition(String beanName, BeanDefinition beanDefinition,
144 String nestedPropertyName, Map<String, String> expressionGraph,
145 ConfigurableListableBeanFactory beanFactory, Set<String> processedBeanNames) {
146 Class<?> beanClass = getBeanClass(beanDefinition, beanFactory);
147 if ((beanClass == null) || !UifDictionaryBean.class.isAssignableFrom(beanClass) || processedBeanNames.contains(
148 beanName)) {
149 return;
150 }
151
152 LOG.debug("Processing bean name '" + beanName + "'");
153
154 Map<String, String> parentExpressionGraph = getExpressionGraphFromParent(beanDefinition.getParentName(),
155 beanFactory, processedBeanNames);
156
157 // process expressions on property values
158 MutablePropertyValues pvs = beanDefinition.getPropertyValues();
159 PropertyValue[] pvArray = pvs.getPropertyValues();
160 for (PropertyValue pv : pvArray) {
161 if (pv.getName().equals(UifPropertyPaths.EXPRESSION_GRAPH)) {
162 continue;
163 }
164
165 String propertyPath = pv.getName();
166 if (StringUtils.isNotBlank(nestedPropertyName)) {
167 propertyPath = nestedPropertyName + "." + propertyPath;
168 }
169
170 // for reloading, need to remove the property from the previously loaded bean definition
171 if (expressionGraph.containsKey(propertyPath)) {
172 expressionGraph.remove(propertyPath);
173 }
174
175 if (hasExpression(pv.getValue())) {
176 // process expression
177 String strValue = getStringValue(pv.getValue());
178 expressionGraph.put(propertyPath, strValue);
179
180 // remove property value so expression will not cause binding exception
181 pvs.removePropertyValue(pv.getName());
182 } else {
183 // process nested objects
184 Object newValue = processPropertyValue(propertyPath, pv.getName(), pv.getValue(), beanDefinition,
185 parentExpressionGraph, expressionGraph, beanFactory, processedBeanNames);
186
187 pvs.removePropertyValue(pv.getName());
188 pvs.addPropertyValue(pv.getName(), newValue);
189 }
190
191 // removed expression (if exists) from parent map since the property was set on child
192 if (parentExpressionGraph.containsKey(pv.getName())) {
193 parentExpressionGraph.remove(pv.getName());
194 }
195 }
196
197 // if nested bean set expression graph to null so it is not inherited from parent definition
198 if (StringUtils.isNotBlank(nestedPropertyName)) {
199 pvs.addPropertyValue(UifPropertyPaths.EXPRESSION_GRAPH, null);
200 }
201
202 // add remaining expressions from parent to expression graph
203 for (Map.Entry<String, String> parentExpression : parentExpressionGraph.entrySet()) {
204 String expressionPath = parentExpression.getKey();
205 if (StringUtils.isNotBlank(nestedPropertyName)) {
206 expressionPath = nestedPropertyName + "." + expressionPath;
207 }
208 expressionGraph.put(expressionPath, parentExpression.getValue());
209 }
210
211 // if bean name is given and factory does not have it registered we need to add it (inner beans that
212 // were given an id)
213 if (StringUtils.isNotBlank(beanName) && !StringUtils.contains(beanName, "$") && !StringUtils.contains(beanName,
214 "#") && !beanFactory.containsBean(beanName)) {
215 ((BeanDefinitionRegistry) beanFactory).registerBeanDefinition(beanName, beanDefinition);
216 }
217
218 if (StringUtils.isNotBlank(beanName)) {
219 processedBeanNames.add(beanName);
220 }
221 }
222
223 /**
224 * Retrieves the class for the object that will be created from the bean definition. Since the class might not
225 * be configured on the bean definition, but by a parent, each parent bean definition is recursively checked for
226 * a class until one is found
227 *
228 * @param beanDefinition bean definition to get class for
229 * @param beanFactory bean factory that contains the bean definition
230 * @return class configured for the bean definition, or null
231 */
232 protected Class<?> getBeanClass(BeanDefinition beanDefinition, ConfigurableListableBeanFactory beanFactory) {
233 if (StringUtils.isNotBlank(beanDefinition.getBeanClassName())) {
234 try {
235 return Class.forName(beanDefinition.getBeanClassName());
236 } catch (ClassNotFoundException e) {
237 // swallow exception and return null so bean is not processed
238 return null;
239 }
240 } else if (StringUtils.isNotBlank(beanDefinition.getParentName())) {
241 BeanDefinition parentBeanDefinition = beanFactory.getBeanDefinition(beanDefinition.getParentName());
242 if (parentBeanDefinition != null) {
243 return getBeanClass(parentBeanDefinition, beanFactory);
244 }
245 }
246
247 return null;
248 }
249
250 /**
251 * Retrieves the expression graph map set on the bean with given name. If the bean has not been processed
252 * by the bean factory post processor, that is done before retrieving the map
253 *
254 * @param parentBeanName name of the parent bean to retrieve map for (if empty a new map will be returned)
255 * @param beanFactory bean factory to retrieve bean definition from
256 * @param processedBeanNames set of bean names that have been processed so far
257 * @return expression graph map from parent or new instance
258 */
259 protected Map<String, String> getExpressionGraphFromParent(String parentBeanName,
260 ConfigurableListableBeanFactory beanFactory, Set<String> processedBeanNames) {
261 Map<String, String> expressionGraph = new HashMap<String, String>();
262 if (StringUtils.isBlank(parentBeanName) || !beanFactory.containsBeanDefinition(parentBeanName)) {
263 return expressionGraph;
264 }
265
266 BeanDefinition beanDefinition = beanFactory.getBeanDefinition(parentBeanName);
267 if (!processedBeanNames.contains(parentBeanName)) {
268 processBeanDefinition(parentBeanName, beanDefinition, beanFactory, processedBeanNames);
269 }
270
271 MutablePropertyValues pvs = beanDefinition.getPropertyValues();
272 PropertyValue propertyExpressionsPV = pvs.getPropertyValue(UifPropertyPaths.EXPRESSION_GRAPH);
273 if (propertyExpressionsPV != null) {
274 Object value = propertyExpressionsPV.getValue();
275 if ((value != null) && (value instanceof ManagedMap)) {
276 expressionGraph.putAll((ManagedMap) value);
277 }
278 }
279
280 return expressionGraph;
281 }
282
283 /**
284 * Checks whether the given property value is of String type, and if so whether it contains the expression
285 * placholder(s)
286 *
287 * @param propertyValue value to check for expressions
288 * @return true if the property value contains expression(s), false if it does not
289 */
290 protected boolean hasExpression(Object propertyValue) {
291 if (propertyValue != null) {
292 // if value is string, check for el expression
293 String strValue = getStringValue(propertyValue);
294 if (strValue != null) {
295 String elPlaceholder = StringUtils.substringBetween(strValue, UifConstants.EL_PLACEHOLDER_PREFIX,
296 UifConstants.EL_PLACEHOLDER_SUFFIX);
297 if (StringUtils.isNotBlank(elPlaceholder)) {
298 return true;
299 }
300 }
301 }
302
303 return false;
304 }
305
306 /**
307 * Processes the given property name/value pair for complex objects, such as bean definitions or collections,
308 * which if found will be processed for contained property expression values
309 *
310 * @param nestedPropertyName nested path of the property whose value is being processed
311 * @param propertyName name of the property in the bean definition being processed
312 * @param propertyValue value to check
313 * @param beanDefinition bean definition the property belongs to
314 * @param parentExpressionGraph map that holds property expressions for the parent bean definition, used for
315 * merging
316 * @param expressionGraph map that holds property expressions for the bean definition being processed
317 * @param beanFactory bean factory that contains the bean definition being processed
318 * @param processedBeanNames set of bean names that have been processed so far
319 * @return new value to set for property
320 */
321 protected Object processPropertyValue(String nestedPropertyName, String propertyName, Object propertyValue,
322 BeanDefinition beanDefinition, Map<String, String> parentExpressionGraph,
323 Map<String, String> expressionGraph, ConfigurableListableBeanFactory beanFactory,
324 Set<String> processedBeanNames) {
325 boolean clearExpressionsForNull = false;
326 if (propertyValue instanceof TypedStringValue) {
327 TypedStringValue typedStringValue = (TypedStringValue) propertyValue;
328
329 String value = typedStringValue.getValue();
330 if (value == null) {
331 clearExpressionsForNull = true;
332 }
333 } else if (propertyValue == null) {
334 clearExpressionsForNull = true;
335 }
336
337 // if property is object and set to null, clear any parent expressions for the property
338 if (clearExpressionsForNull) {
339 removeExpressionsByPrefix(nestedPropertyName, expressionGraph);
340 removeExpressionsByPrefix(propertyName, parentExpressionGraph);
341
342 return propertyValue;
343 }
344
345 // process nested bean definitions
346 if ((propertyValue instanceof BeanDefinition) || (propertyValue instanceof BeanDefinitionHolder)) {
347 String beanName = null;
348 BeanDefinition beanDefinitionValue;
349 if (propertyValue instanceof BeanDefinition) {
350 beanDefinitionValue = (BeanDefinition) propertyValue;
351 } else {
352 beanDefinitionValue = ((BeanDefinitionHolder) propertyValue).getBeanDefinition();
353 beanName = ((BeanDefinitionHolder) propertyValue).getBeanName();
354 }
355
356 // since overriding the entire bean, clear any expressions from parent that start with the bean property
357 removeExpressionsByPrefix(nestedPropertyName, expressionGraph);
358 removeExpressionsByPrefix(propertyName, parentExpressionGraph);
359
360 processNestedBeanDefinition(beanName, beanDefinitionValue, nestedPropertyName, expressionGraph, beanFactory,
361 processedBeanNames);
362
363 return propertyValue;
364 }
365
366 // recurse into collections
367 if (propertyValue instanceof Object[]) {
368 visitArray(nestedPropertyName, parentExpressionGraph, expressionGraph, (Object[]) propertyValue, beanFactory,
369 processedBeanNames);
370 } else if (propertyValue instanceof List) {
371 visitList(nestedPropertyName, propertyName, beanDefinition, parentExpressionGraph, expressionGraph,
372 (List) propertyValue, beanFactory, processedBeanNames);
373 } else if (propertyValue instanceof Set) {
374 visitSet(nestedPropertyName, parentExpressionGraph, expressionGraph, (Set) propertyValue, beanFactory,
375 processedBeanNames);
376 } else if (propertyValue instanceof Map) {
377 visitMap(nestedPropertyName, parentExpressionGraph, expressionGraph, (Map) propertyValue, beanFactory,
378 processedBeanNames);
379 }
380
381 // others (primitive) just return value as is
382 return propertyValue;
383 }
384
385 /**
386 * Removes entries from the given expressions map whose key starts with the given prefix
387 *
388 * @param propertyNamePrefix prefix to search for and remove
389 * @param expressionGraph map of property expressions to filter
390 */
391 protected void removeExpressionsByPrefix(String propertyNamePrefix, Map<String, String> expressionGraph) {
392 Map<String, String> adjustedExpressionGraph = new HashMap<String, String>();
393 for (String propertyName : expressionGraph.keySet()) {
394 if (!propertyName.startsWith(propertyNamePrefix + ".")) {
395 adjustedExpressionGraph.put(propertyName, expressionGraph.get(propertyName));
396 }
397 }
398
399 expressionGraph.clear();
400 expressionGraph.putAll(adjustedExpressionGraph);
401 }
402
403 /**
404 * Determines whether the given value is of String type and if so returns the string value
405 *
406 * @param value object value to check
407 * @return string value for object or null if object is not a string type
408 */
409 protected String getStringValue(Object value) {
410 if (value instanceof TypedStringValue) {
411 TypedStringValue typedStringValue = (TypedStringValue) value;
412 return typedStringValue.getValue();
413 } else if (value instanceof String) {
414 return (String) value;
415 }
416
417 return null;
418 }
419
420 @SuppressWarnings("unchecked")
421 protected void visitArray(String propertyName, Map<String, String> parentExpressionGraph,
422 Map<String, String> expressionGraph, Object array, ConfigurableListableBeanFactory beanFactory,
423 Set<String> processedBeanNames) {
424 Object newArray = null;
425 Object[] arrayVal = null;
426
427 boolean isMergeEnabled = false;
428 if (array instanceof ManagedArray) {
429 isMergeEnabled = ((ManagedArray) array).isMergeEnabled();
430 arrayVal = (Object[]) ((ManagedArray) array).getSource();
431
432 newArray = new ManagedArray(((ManagedArray) array).getElementTypeName(), arrayVal.length);
433 ((ManagedArray) newArray).setMergeEnabled(isMergeEnabled);
434 } else {
435 arrayVal = (Object[]) array;
436 newArray = new Object[arrayVal.length];
437 }
438
439 for (int i = 0; i < arrayVal.length; i++) {
440 Object elem = arrayVal[i];
441 String elemPropertyName = propertyName + "[" + i + "]";
442
443 if (hasExpression(elem)) {
444 String strValue = getStringValue(elem);
445 expressionGraph.put(elemPropertyName, strValue);
446 arrayVal[i] = null;
447 } else {
448 // process set value bean definition as a top level bean
449 if ((elem instanceof BeanDefinition) || (elem instanceof BeanDefinitionHolder)) {
450 String beanName = null;
451 BeanDefinition beanDefinition;
452 if (elem instanceof BeanDefinition) {
453 beanDefinition = (BeanDefinition) elem;
454 } else {
455 beanDefinition = ((BeanDefinitionHolder) elem).getBeanDefinition();
456 beanName = ((BeanDefinitionHolder) elem).getBeanName();
457 }
458
459 processBeanDefinition(beanName, beanDefinition, beanFactory, processedBeanNames);
460 }
461
462 arrayVal[i] = elem;
463 }
464
465 if (isMergeEnabled && parentExpressionGraph.containsKey(elemPropertyName)) {
466 parentExpressionGraph.remove(elemPropertyName);
467 }
468 }
469
470 // determine if we need to clear any parent expressions for this list
471 if (!isMergeEnabled) {
472 // clear any expressions that match the property name minus index
473 Map<String, String> adjustedParentExpressionGraph = new HashMap<String, String>();
474 for (Map.Entry<String, String> parentExpression : parentExpressionGraph.entrySet()) {
475 if (!parentExpression.getKey().startsWith(propertyName + "[")) {
476 adjustedParentExpressionGraph.put(parentExpression.getKey(), parentExpression.getValue());
477 }
478 }
479
480 parentExpressionGraph.clear();
481 parentExpressionGraph.putAll(adjustedParentExpressionGraph);
482 }
483
484 if (array instanceof ManagedArray) {
485 ((ManagedArray) array).setSource(newArray);
486 } else {
487 array = newArray;
488 }
489 }
490
491 @SuppressWarnings("unchecked")
492 protected void visitList(String nestedPropertyName, String propertyName, BeanDefinition beanDefinition,
493 Map<String, String> parentExpressionGraph, Map<String, String> expressionGraph, List listVal,
494 ConfigurableListableBeanFactory beanFactory, Set<String> processedBeanNames) {
495 boolean isMergeEnabled = false;
496 if (listVal instanceof ManagedList) {
497 isMergeEnabled = ((ManagedList) listVal).isMergeEnabled();
498 }
499
500 ManagedList newList = new ManagedList();
501 newList.setMergeEnabled(isMergeEnabled);
502
503 // if merging, need to find size of parent list so we can know which element to set
504 // when evaluating expressions
505 int parentListSize = 0;
506 if (isMergeEnabled && StringUtils.isNotBlank(beanDefinition.getParentName())) {
507 BeanDefinition parentBeanDefinition = beanFactory.getMergedBeanDefinition(beanDefinition.getParentName());
508 PropertyValue parentListPropertyValue = parentBeanDefinition.getPropertyValues().getPropertyValue(
509 propertyName);
510 if (parentListPropertyValue != null) {
511 List parentList = (List) parentListPropertyValue.getValue();
512 parentListSize = parentList.size();
513 }
514 }
515
516 for (int i = 0; i < listVal.size(); i++) {
517 Object elem = listVal.get(i);
518
519 int elementPosition = i + parentListSize;
520 String elemPropertyName = nestedPropertyName + "[" + elementPosition + "]";
521
522 if (hasExpression(elem)) {
523 String strValue = getStringValue(elem);
524
525 expressionGraph.put(elemPropertyName, strValue);
526 newList.add(i, null);
527 } else {
528 // process list value bean definition as a top level bean
529 if ((elem instanceof BeanDefinition) || (elem instanceof BeanDefinitionHolder)) {
530 String beanName = null;
531 BeanDefinition beanDefinitionValue;
532 if (elem instanceof BeanDefinition) {
533 beanDefinitionValue = (BeanDefinition) elem;
534 } else {
535 beanDefinitionValue = ((BeanDefinitionHolder) elem).getBeanDefinition();
536 beanName = ((BeanDefinitionHolder) elem).getBeanName();
537 }
538
539 processBeanDefinition(beanName, beanDefinitionValue, beanFactory, processedBeanNames);
540 }
541
542 newList.add(i, elem);
543 }
544 }
545
546 // determine if we need to clear any parent expressions for this list
547 if (!isMergeEnabled) {
548 // clear any expressions that match the property name minus index
549 Map<String, String> adjustedParentExpressionGraph = new HashMap<String, String>();
550 for (Map.Entry<String, String> parentExpression : parentExpressionGraph.entrySet()) {
551 if (!parentExpression.getKey().startsWith(nestedPropertyName + "[")) {
552 adjustedParentExpressionGraph.put(parentExpression.getKey(), parentExpression.getValue());
553 }
554 }
555
556 parentExpressionGraph.clear();
557 parentExpressionGraph.putAll(adjustedParentExpressionGraph);
558 }
559
560 listVal.clear();
561 listVal.addAll(newList);
562 }
563
564 @SuppressWarnings("unchecked")
565 protected void visitSet(String propertyName, Map<String, String> parentPropertyExpressions,
566 Map<String, String> propertyExpressions, Set setVal, ConfigurableListableBeanFactory beanFactory,
567 Set<String> processedBeanNames) {
568 boolean isMergeEnabled = false;
569 if (setVal instanceof ManagedSet) {
570 isMergeEnabled = ((ManagedSet) setVal).isMergeEnabled();
571 }
572
573 ManagedSet newSet = new ManagedSet();
574 newSet.setMergeEnabled(isMergeEnabled);
575
576 for (Object elem : setVal) {
577 if (hasExpression(elem)) {
578 String strValue = getStringValue(elem);
579 propertyExpressions.put(propertyName + ExpressionEvaluatorService.EMBEDDED_PROPERTY_NAME_ADD_INDICATOR,
580 strValue);
581 } else {
582 // process set value bean definition as a top level bean
583 if ((elem instanceof BeanDefinition) || (elem instanceof BeanDefinitionHolder)) {
584 String beanName = null;
585 BeanDefinition beanDefinition;
586 if (elem instanceof BeanDefinition) {
587 beanDefinition = (BeanDefinition) elem;
588 } else {
589 beanDefinition = ((BeanDefinitionHolder) elem).getBeanDefinition();
590 beanName = ((BeanDefinitionHolder) elem).getBeanName();
591 }
592
593 processBeanDefinition(beanName, beanDefinition, beanFactory, processedBeanNames);
594 }
595
596 newSet.add(elem);
597 }
598 }
599
600 // determine if we need to clear any parent expressions for this list
601 if (!isMergeEnabled) {
602 // clear any expressions that match the property name minus index
603 Map<String, String> adjustedParentExpressions = new HashMap<String, String>();
604 for (Map.Entry<String, String> parentExpression : parentPropertyExpressions.entrySet()) {
605 if (!parentExpression.getKey().startsWith(
606 propertyName + ExpressionEvaluatorService.EMBEDDED_PROPERTY_NAME_ADD_INDICATOR)) {
607 adjustedParentExpressions.put(parentExpression.getKey(), parentExpression.getValue());
608 }
609 }
610
611 parentPropertyExpressions.clear();
612 parentPropertyExpressions.putAll(adjustedParentExpressions);
613 }
614
615 setVal.clear();
616 setVal.addAll(newSet);
617 }
618
619 @SuppressWarnings("unchecked")
620 protected void visitMap(String propertyName, Map<String, String> parentExpressionGraph,
621 Map<String, String> expressionGraph, Map<?, ?> mapVal, ConfigurableListableBeanFactory beanFactory,
622 Set<String> processedBeanNames) {
623 boolean isMergeEnabled = false;
624 if (mapVal instanceof ManagedMap) {
625 isMergeEnabled = ((ManagedMap) mapVal).isMergeEnabled();
626 }
627
628 ManagedMap newMap = new ManagedMap();
629 newMap.setMergeEnabled(isMergeEnabled);
630
631 for (Map.Entry entry : mapVal.entrySet()) {
632 Object key = entry.getKey();
633 Object val = entry.getValue();
634
635 String keyStr = getStringValue(key);
636 String elemPropertyName = propertyName + "['" + keyStr + "']";
637
638 if (hasExpression(val)) {
639 String strValue = getStringValue(val);
640 expressionGraph.put(elemPropertyName, strValue);
641 } else {
642 // process map value bean definition as a top level bean
643 if ((val instanceof BeanDefinition) || (val instanceof BeanDefinitionHolder)) {
644 String beanName = null;
645 BeanDefinition beanDefinition;
646 if (val instanceof BeanDefinition) {
647 beanDefinition = (BeanDefinition) val;
648 } else {
649 beanDefinition = ((BeanDefinitionHolder) val).getBeanDefinition();
650 beanName = ((BeanDefinitionHolder) val).getBeanName();
651 }
652
653 processBeanDefinition(beanName, beanDefinition, beanFactory, processedBeanNames);
654 }
655
656 newMap.put(key, val);
657 }
658
659 if (isMergeEnabled && parentExpressionGraph.containsKey(elemPropertyName)) {
660 parentExpressionGraph.remove(elemPropertyName);
661 }
662 }
663
664 if (!isMergeEnabled) {
665 // clear any expressions that match the property minus key
666 Map<String, String> adjustedParentExpressionGraph = new HashMap<String, String>();
667 for (Map.Entry<String, String> parentExpression : parentExpressionGraph.entrySet()) {
668 if (!parentExpression.getKey().startsWith(propertyName + "[")) {
669 adjustedParentExpressionGraph.put(parentExpression.getKey(), parentExpression.getValue());
670 }
671 }
672
673 parentExpressionGraph.clear();
674 parentExpressionGraph.putAll(adjustedParentExpressionGraph);
675 }
676
677 mapVal.clear();
678 mapVal.putAll(newMap);
679 }
680 }