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