View Javadoc

1   /**
2    * Copyright 2005-2013 The Kuali Foundation
3    *
4    * Licensed under the Educational Community 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.opensource.org/licenses/ecl2.php
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  package org.kuali.rice.krad.uif.util;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.apache.commons.logging.Log;
20  import org.apache.commons.logging.LogFactory;
21  import org.kuali.rice.krad.datadictionary.uif.UifDictionaryBean;
22  import org.kuali.rice.krad.uif.UifConstants;
23  import org.kuali.rice.krad.uif.UifPropertyPaths;
24  import org.kuali.rice.krad.uif.service.ExpressionEvaluatorService;
25  import org.springframework.beans.BeansException;
26  import org.springframework.beans.MutablePropertyValues;
27  import org.springframework.beans.PropertyValue;
28  import org.springframework.beans.factory.config.BeanDefinition;
29  import org.springframework.beans.factory.config.BeanDefinitionHolder;
30  import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
31  import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
32  import org.springframework.beans.factory.config.TypedStringValue;
33  import org.springframework.beans.factory.support.BeanDefinitionRegistry;
34  import org.springframework.beans.factory.support.ManagedArray;
35  import org.springframework.beans.factory.support.ManagedList;
36  import org.springframework.beans.factory.support.ManagedMap;
37  import org.springframework.beans.factory.support.ManagedSet;
38  
39  import java.util.HashMap;
40  import java.util.HashSet;
41  import java.util.List;
42  import java.util.Map;
43  import java.util.Set;
44  
45  /**
46   * Post processes the bean factory to handle UIF property expressions and IDs on inner beans
47   *
48   * <p>
49   * Conditional logic can be implemented with the UIF dictionary by means of property expressions. These are
50   * expressions that follow SPEL and can be given as the value for a property using the @{} placeholder. Since such
51   * a value would cause an exception when creating the object if the property is a non-string type (value cannot be
52   * converted), we need to move those expressions to a Map for processing, and then remove the original property
53   * configuration containing the expression. The expressions are then evaluated during the view apply model phase and
54   * the result is set as the value for the corresponding property.
55   * </p>
56   *
57   * <p>
58   * Spring will not register inner beans with IDs so that the bean definition can be retrieved through the factory,
59   * therefore this post processor adds them as top level registered beans
60   * </p>
61   *
62   * TODO: convert to dictionary bean processor
63   *
64   * @author Kuali Rice Team (rice.collab@kuali.org)
65   */
66  public class UifBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
67      private static final Log LOG = LogFactory.getLog(UifBeanFactoryPostProcessor.class);
68  
69      /**
70       * Iterates through all beans in the factory and invokes processing
71       *
72       * @param beanFactory - bean factory instance to process
73       * @throws org.springframework.beans.BeansException
74       */
75      public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
76          Set<String> processedBeanNames = new HashSet<String>();
77  
78          LOG.info("Beginning post processing of bean factory for UIF expressions");
79  
80          String[] beanNames = beanFactory.getBeanDefinitionNames();
81          for (int i = 0; i < beanNames.length; i++) {
82              String beanName = beanNames[i];
83              BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
84  
85              processBeanDefinition(beanName, beanDefinition, beanFactory, processedBeanNames);
86          }
87  
88          LOG.info("Finished post processing of bean factory for UIF expressions");
89      }
90  
91      /**
92       * Processes a top level (non nested) bean definition for expressions
93       *
94       * <p>
95       * A bean that is non nested (or top of a collection) will hold all the expressions for the graph. A new
96       * expression graph is initialized and expressions are collected as the bean and all its children are processed.
97       * The expression graph is then set as a property on the top bean definition
98       * </p>
99       *
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<?> 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 Map<String, String> 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 boolean 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 Object 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 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 }