Coverage Report - org.kuali.rice.krad.uif.service.impl.ExpressionEvaluatorServiceImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
ExpressionEvaluatorServiceImpl
0%
0/83
0%
0/40
4.714
 
 1  
 /*
 2  
  * Copyright 2011 The Kuali Foundation Licensed under the Educational Community
 3  
  * License, Version 1.0 (the "License"); you may not use this file except in
 4  
  * compliance with the License. You may obtain a copy of the License at
 5  
  * http://www.opensource.org/licenses/ecl1.php Unless required by applicable law
 6  
  * or agreed to in writing, software distributed under the License is
 7  
  * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 8  
  * KIND, either express or implied. See the License for the specific language
 9  
  * governing permissions and limitations under the License.
 10  
  */
 11  
 package org.kuali.rice.krad.uif.service.impl;
 12  
 
 13  
 import java.beans.PropertyDescriptor;
 14  
 import java.util.List;
 15  
 import java.util.Map;
 16  
 import java.util.Map.Entry;
 17  
 
 18  
 import org.apache.commons.lang.StringUtils;
 19  
 import org.kuali.rice.krad.uif.UifConstants;
 20  
 import org.kuali.rice.krad.uif.core.Component;
 21  
 import org.kuali.rice.krad.uif.core.PropertyReplacer;
 22  
 import org.kuali.rice.krad.uif.layout.LayoutManager;
 23  
 import org.kuali.rice.krad.uif.service.ExpressionEvaluatorService;
 24  
 import org.kuali.rice.krad.uif.util.ExpressionFunctions;
 25  
 import org.kuali.rice.krad.uif.util.ObjectPropertyUtils;
 26  
 import org.springframework.expression.EvaluationException;
 27  
 import org.springframework.expression.Expression;
 28  
 import org.springframework.expression.ExpressionParser;
 29  
 import org.springframework.expression.common.TemplateParserContext;
 30  
 import org.springframework.expression.spel.standard.SpelExpressionParser;
 31  
 import org.springframework.expression.spel.support.StandardEvaluationContext;
 32  
 
 33  
 /**
 34  
  * Evaluates expression language statements using the Spring EL engine TODO:
 35  
  * Look into using Rice KRMS for evaluation
 36  
  * 
 37  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 38  
  */
 39  0
 public class ExpressionEvaluatorServiceImpl implements ExpressionEvaluatorService {
 40  0
     private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger
 41  
             .getLogger(ExpressionEvaluatorServiceImpl.class);
 42  
 
 43  
     /**
 44  
      * @see org.kuali.rice.krad.uif.service.ExpressionEvaluatorService#evaluateObjectProperties(java.lang.Object,
 45  
      *      java.lang.Object, java.util.Map)
 46  
      */
 47  
     public void evaluateObjectProperties(Object object, Object contextObject, Map<String, Object> evaluationParameters) {
 48  0
         if (object instanceof Component) {
 49  0
             evaluatePropertyReplacers(object, contextObject, evaluationParameters);
 50  
         }
 51  0
         visitPropertiesAndEvaluateExpressions(object, contextObject, evaluationParameters);
 52  0
     }
 53  
 
 54  
     /**
 55  
      * @see org.kuali.rice.krad.uif.service.ExpressionEvaluatorService#evaluateExpressionTemplate(java.lang.Object,
 56  
      *      java.util.Map, java.lang.String)
 57  
      */
 58  
     public String evaluateExpressionTemplate(Object contextObject, Map<String, Object> evaluationParameters,
 59  
             String expressionTemplate) {
 60  0
         StandardEvaluationContext context = new StandardEvaluationContext(contextObject);
 61  0
         context.setVariables(evaluationParameters);
 62  0
         addCustomFunctions(context);
 63  
 
 64  0
         ExpressionParser parser = new SpelExpressionParser();
 65  
         
 66  0
         String result = null;
 67  
         try {
 68  0
             Expression expression = null;
 69  0
             if (StringUtils.contains(expressionTemplate, UifConstants.EL_PLACEHOLDER_PREFIX)) {
 70  0
                 expression = parser.parseExpression(expressionTemplate, new TemplateParserContext(
 71  
                         UifConstants.EL_PLACEHOLDER_PREFIX, UifConstants.EL_PLACEHOLDER_SUFFIX));
 72  
             } else {
 73  0
                 expression = parser.parseExpression(expressionTemplate);
 74  
             }
 75  
 
 76  0
             result = expression.getValue(context, String.class);
 77  0
         } catch (Exception e) {
 78  0
             LOG.error("Exception evaluating expression: " + expressionTemplate);
 79  0
             throw new RuntimeException("Exception evaluating expression: " + expressionTemplate, e);
 80  0
         }
 81  
 
 82  0
         return result;
 83  
     }
 84  
 
 85  
     /**
 86  
      * @see org.kuali.rice.krad.uif.service.ExpressionEvaluatorService#evaluateExpression(java.lang.Object,
 87  
      *      java.util.Map, java.lang.String)
 88  
      */
 89  
     public Object evaluateExpression(Object contextObject, Map<String, Object> evaluationParameters,
 90  
             String expressionStr) {
 91  0
         StandardEvaluationContext context = new StandardEvaluationContext(contextObject);
 92  0
         context.setVariables(evaluationParameters);
 93  0
         addCustomFunctions(context);
 94  
 
 95  0
         ExpressionParser parser = new SpelExpressionParser();
 96  0
         Expression expression = parser.parseExpression(expressionStr);
 97  
 
 98  0
         Object result = null;
 99  
         try {
 100  0
             result = expression.getValue(context);
 101  
         }
 102  0
         catch (EvaluationException e) {
 103  0
             LOG.error("Exception evaluating expression: " + expressionStr);
 104  0
             throw new RuntimeException("Exception evaluating expression: " + expressionStr, e);
 105  0
         }
 106  
 
 107  0
         return result;
 108  
     }
 109  
 
 110  
     /**
 111  
      * Registers custom functions for el expressions with the given context
 112  
      *
 113  
      * @param context - context instance to register functions to
 114  
      */
 115  
     protected void addCustomFunctions(StandardEvaluationContext context) {
 116  
         try {
 117  
             // TODO: possibly reflect ExpressionFunctions and add automatically
 118  0
             context.registerFunction("isAssignableFrom", ExpressionFunctions.class
 119  
                     .getDeclaredMethod("isAssignableFrom", new Class[]{Class.class, Class.class}));
 120  0
             context.registerFunction("empty", ExpressionFunctions.class
 121  
                     .getDeclaredMethod("empty", new Class[]{Object.class}));
 122  0
             context.registerFunction("getName", ExpressionFunctions.class
 123  
                     .getDeclaredMethod("getName", new Class[]{Class.class}));
 124  0
         } catch (NoSuchMethodException e) {
 125  0
             LOG.error("Custom function for el expressions not found: " + e.getMessage());
 126  0
             throw new RuntimeException("Custom function for el expressions not found: " + e.getMessage(), e);
 127  0
         }
 128  0
     }
 129  
 
 130  
     /**
 131  
      * Iterates through any configured <code>PropertyReplacer</code> instances for the component and
 132  
      * evaluates the given condition. If the condition is met, the replacement value is set on the
 133  
      * corresponding property
 134  
      *
 135  
      * @param object - object instance with property replacers list, should be either a component or layout manager
 136  
      * @param contextObject - context for el evaluation
 137  
      * @param evaluationParameters - parameters for el evaluation
 138  
      */
 139  
     protected void evaluatePropertyReplacers(Object object, Object contextObject,
 140  
                                              Map<String, Object> evaluationParameters) {
 141  0
         List<PropertyReplacer> replacers = null;
 142  0
         if (Component.class.isAssignableFrom(object.getClass())) {
 143  0
             replacers = ((Component) object).getPropertyReplacers();
 144  0
         } else if (LayoutManager.class.isAssignableFrom(object.getClass())) {
 145  0
             replacers = ((LayoutManager) object).getPropertyReplacers();
 146  
         }
 147  
 
 148  0
         for (PropertyReplacer propertyReplacer : replacers) {
 149  0
             String conditionEvaluation =
 150  
                     evaluateExpressionTemplate(contextObject, evaluationParameters, propertyReplacer.getCondition());
 151  0
             boolean conditionSuccess = Boolean.parseBoolean(conditionEvaluation);
 152  0
             if (conditionSuccess) {
 153  0
                 ObjectPropertyUtils.setPropertyValue(object, propertyReplacer.getPropertyName(),
 154  
                         propertyReplacer.getReplacement());
 155  
             }
 156  0
         }
 157  0
     }
 158  
 
 159  
     /**
 160  
      * Iterates through the properties of the given object and checks for property values that contain
 161  
      * an el expression. If an expression is found it will be evaluated and the result of that evaluation
 162  
      * set back into the property value
 163  
      *
 164  
      * <p>
 165  
      *  If the property contains an el template (part static text and part expression), only the expression
 166  
      *  part will be replaced with the result. More than one expressions may be contained within the template
 167  
      * </p>
 168  
      *
 169  
      * <p>
 170  
      *  A special check is done for Map property types. When found the Map is iterated over and expressions
 171  
      *  contained in the map value are evaluated
 172  
      * </p>
 173  
      *
 174  
      * @param object - object to evaluate properties for
 175  
      * @param contextObject - context for el evaluation
 176  
      * @param evaluationParameters - parameters for el evaluation
 177  
      */
 178  
     protected void visitPropertiesAndEvaluateExpressions(Object object, Object contextObject,
 179  
             Map<String, Object> evaluationParameters) {
 180  
         // iterate through object properties and check for expressions
 181  0
         PropertyDescriptor[] propertyDescriptors = ObjectPropertyUtils.getPropertyDescriptors(object);
 182  0
         for (int i = 0; i < propertyDescriptors.length; i++) {
 183  0
             PropertyDescriptor descriptor = propertyDescriptors[i];
 184  
 
 185  0
             if (descriptor.getWriteMethod() == null) {
 186  0
                 continue;
 187  
             }
 188  
 
 189  0
             String propertyName = descriptor.getName();
 190  0
             if (String.class.isAssignableFrom(descriptor.getPropertyType())) {
 191  0
                 String propertyValue = ObjectPropertyUtils.getPropertyValue(object, propertyName);
 192  
 
 193  0
                 if (StringUtils.isNotBlank(propertyValue)
 194  
                         && (containsElPlaceholder(propertyValue) || StringUtils.startsWith(propertyName,
 195  
                                 UifConstants.EL_CONDITIONAL_PROPERTY_PREFIX))) {
 196  
 
 197  
                     // evaluate any expressions and reset property value
 198  0
                     propertyValue = evaluateExpressionTemplate(contextObject, evaluationParameters, propertyValue);
 199  
 
 200  0
                     String propertyNameToSet = propertyName;
 201  0
                     if (StringUtils.startsWith(propertyName, UifConstants.EL_CONDITIONAL_PROPERTY_PREFIX)) {
 202  
                         // get the target property by convention
 203  0
                         propertyNameToSet = StringUtils.removeStart(propertyName,
 204  
                                 UifConstants.EL_CONDITIONAL_PROPERTY_PREFIX);
 205  0
                         propertyNameToSet = StringUtils.substring(propertyNameToSet, 0, 1).toLowerCase()
 206  
                                 + StringUtils.substring(propertyNameToSet, 1, propertyNameToSet.length());
 207  
                     }
 208  
 
 209  0
                     ObjectPropertyUtils.setPropertyValue(object, propertyNameToSet, propertyValue);
 210  
                 }
 211  0
             }
 212  0
             else if (Map.class.isAssignableFrom(descriptor.getPropertyType())) {
 213  0
                 Map<Object, Object> propertyValue = ObjectPropertyUtils.getPropertyValue(object, propertyName);
 214  
 
 215  0
                 if (propertyValue != null) {
 216  0
                     for (Entry<Object, Object> entry : propertyValue.entrySet()) {
 217  0
                         if ((entry.getValue() != null) && String.class.isAssignableFrom(entry.getValue().getClass())
 218  
                                 && containsElPlaceholder((String) entry.getValue())) {
 219  0
                             String entryValue = evaluateExpressionTemplate(contextObject, evaluationParameters,
 220  
                                     (String) entry.getValue());
 221  0
                             propertyValue.put(entry.getKey(), entryValue);
 222  0
                         }
 223  
                     }
 224  
                 }
 225  
             }
 226  
         }
 227  0
     }
 228  
 
 229  
     /**
 230  
      * @see org.kuali.rice.krad.uif.service.ExpressionEvaluatorService#containsElPlaceholder(java.lang.String)
 231  
      */
 232  
     public boolean containsElPlaceholder(String value) {
 233  0
         boolean containsElPlaceholder = false;
 234  
 
 235  0
         String elPlaceholder = StringUtils.substringBetween(value, UifConstants.EL_PLACEHOLDER_PREFIX,
 236  
                 UifConstants.EL_PLACEHOLDER_SUFFIX);
 237  0
         if (elPlaceholder != null) {
 238  0
             containsElPlaceholder = true;
 239  
         }
 240  
 
 241  0
         return containsElPlaceholder;
 242  
     }
 243  
 
 244  
 }