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 }