View Javadoc

1   /**
2    * Copyright 2005-2015 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.service.impl;
17  
18  import java.util.HashMap;
19  import java.util.List;
20  import java.util.Map;
21  import java.util.Map.Entry;
22  
23  import org.apache.commons.lang.StringUtils;
24  import org.kuali.rice.krad.uif.UifConstants;
25  import org.kuali.rice.krad.uif.component.Component;
26  import org.kuali.rice.krad.uif.component.Configurable;
27  import org.kuali.rice.krad.uif.component.KeepExpression;
28  import org.kuali.rice.krad.uif.component.PropertyReplacer;
29  import org.kuali.rice.krad.uif.layout.LayoutManager;
30  import org.kuali.rice.krad.uif.service.ExpressionEvaluatorService;
31  import org.kuali.rice.krad.uif.util.CloneUtils;
32  import org.kuali.rice.krad.uif.util.ExpressionFunctions;
33  import org.kuali.rice.krad.uif.util.ObjectPropertyUtils;
34  import org.springframework.expression.Expression;
35  import org.springframework.expression.ExpressionParser;
36  import org.springframework.expression.common.TemplateParserContext;
37  import org.springframework.expression.spel.standard.SpelExpressionParser;
38  import org.springframework.expression.spel.support.StandardEvaluationContext;
39  
40  /**
41   * Evaluates expression language statements using the Spring EL engine TODO:
42   * Look into using Rice KRMS for evaluation
43   *
44   * @author Kuali Rice Team (rice.collab@kuali.org)
45   */
46  public class ExpressionEvaluatorServiceImpl implements ExpressionEvaluatorService {
47      private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(
48              ExpressionEvaluatorServiceImpl.class);
49  
50      /**
51       * @see org.kuali.rice.krad.uif.service.ExpressionEvaluatorService#evaluateObjectExpressions(java.lang.Object,
52       *      java.lang.Object, java.util.Map)
53       */
54      public void evaluateObjectExpressions(Object object, Object contextObject,
55              Map<String, Object> evaluationParameters) {
56          if ((object instanceof Component) || (object instanceof LayoutManager)) {
57              evaluatePropertyReplacers(object, contextObject, evaluationParameters);
58          }
59          evaluatePropertyExpressions(object, contextObject, evaluationParameters);
60      }
61  
62      /**
63       * @see org.kuali.rice.krad.uif.service.ExpressionEvaluatorService#evaluateExpressionTemplate(java.lang.Object,
64       *      java.util.Map, java.lang.String)
65       */
66      public String evaluateExpressionTemplate(Object contextObject, Map<String, Object> evaluationParameters,
67              String expressionTemplate) {
68          StandardEvaluationContext context = new StandardEvaluationContext(contextObject);
69          context.setVariables(evaluationParameters);
70          addCustomFunctions(context);
71  
72          ExpressionParser parser = new SpelExpressionParser();
73  
74          String result = null;
75          try {
76              Expression expression = null;
77              if (StringUtils.contains(expressionTemplate, UifConstants.EL_PLACEHOLDER_PREFIX)) {
78                  expression = parser.parseExpression(expressionTemplate, new TemplateParserContext(
79                          UifConstants.EL_PLACEHOLDER_PREFIX, UifConstants.EL_PLACEHOLDER_SUFFIX));
80              } else {
81                  expression = parser.parseExpression(expressionTemplate);
82              }
83  
84              result = expression.getValue(context, String.class);
85          } catch (Exception e) {
86              LOG.error("Exception evaluating expression: " + expressionTemplate);
87              throw new RuntimeException("Exception evaluating expression: " + expressionTemplate, e);
88          }
89  
90          return result;
91      }
92  
93      /**
94       * @see org.kuali.rice.krad.uif.service.ExpressionEvaluatorService#evaluateExpression(java.lang.Object,
95       *      java.util.Map, java.lang.String)
96       */
97      public Object evaluateExpression(Object contextObject, Map<String, Object> evaluationParameters,
98              String expressionStr) {
99          StandardEvaluationContext context = new StandardEvaluationContext(contextObject);
100         context.setVariables(evaluationParameters);
101         addCustomFunctions(context);
102 
103         // if expression contains placeholders remove before evaluating
104         if (StringUtils.startsWith(expressionStr, UifConstants.EL_PLACEHOLDER_PREFIX) && StringUtils.endsWith(
105                 expressionStr, UifConstants.EL_PLACEHOLDER_SUFFIX)) {
106             expressionStr = StringUtils.removeStart(expressionStr, UifConstants.EL_PLACEHOLDER_PREFIX);
107             expressionStr = StringUtils.removeEnd(expressionStr, UifConstants.EL_PLACEHOLDER_SUFFIX);
108         }
109 
110         ExpressionParser parser = new SpelExpressionParser();
111         Object result = null;
112         try {
113             Expression expression = parser.parseExpression(expressionStr);
114 
115             result = expression.getValue(context);
116         } catch (Exception e) {
117             LOG.error("Exception evaluating expression: " + expressionStr);
118             throw new RuntimeException("Exception evaluating expression: " + expressionStr, e);
119         }
120 
121         return result;
122     }
123 
124     /**
125      * Registers custom functions for el expressions with the given context
126      *
127      * @param context - context instance to register functions to
128      */
129     protected void addCustomFunctions(StandardEvaluationContext context) {
130         try {
131             // TODO: possibly reflect ExpressionFunctions and add automatically
132             context.registerFunction("isAssignableFrom", ExpressionFunctions.class.getDeclaredMethod("isAssignableFrom",
133                     new Class[]{Class.class, Class.class}));
134             context.registerFunction("empty", ExpressionFunctions.class.getDeclaredMethod("empty",
135                     new Class[]{Object.class}));
136             context.registerFunction("getName", ExpressionFunctions.class.getDeclaredMethod("getName",
137                     new Class[]{Class.class}));
138             context.registerFunction("getParm", ExpressionFunctions.class.getDeclaredMethod("getParm",
139                     new Class[]{String.class, String.class, String.class}));
140             context.registerFunction("getParmInd", ExpressionFunctions.class.getDeclaredMethod("getParmInd",
141                     new Class[]{String.class, String.class, String.class}));
142             context.registerFunction("hasPerm", ExpressionFunctions.class.getDeclaredMethod("hasPerm",
143                     new Class[]{String.class, String.class}));
144             context.registerFunction("hasPermDtls", ExpressionFunctions.class.getDeclaredMethod("hasPermDtls",
145                     new Class[]{String.class, String.class, Map.class, Map.class}));
146             context.registerFunction("hasPermTmpl", ExpressionFunctions.class.getDeclaredMethod("hasPermTmpl",
147                     new Class[]{String.class, String.class, Map.class, Map.class}));
148         } catch (NoSuchMethodException e) {
149             LOG.error("Custom function for el expressions not found: " + e.getMessage());
150             throw new RuntimeException("Custom function for el expressions not found: " + e.getMessage(), e);
151         }
152     }
153 
154     /**
155      * Iterates through any configured <code>PropertyReplacer</code> instances for the component and
156      * evaluates the given condition. If the condition is met, the replacement value is set on the
157      * corresponding property
158      *
159      * @param object - object instance with property replacers list, should be either a component or layout manager
160      * @param contextObject - context for el evaluation
161      * @param evaluationParameters - parameters for el evaluation
162      */
163     protected void evaluatePropertyReplacers(Object object, Object contextObject,
164             Map<String, Object> evaluationParameters) {
165         List<PropertyReplacer> replacers = null;
166         if (Component.class.isAssignableFrom(object.getClass())) {
167             replacers = ((Component) object).getPropertyReplacers();
168         } else if (LayoutManager.class.isAssignableFrom(object.getClass())) {
169             replacers = ((LayoutManager) object).getPropertyReplacers();
170         }
171 
172         for (PropertyReplacer propertyReplacer : replacers) {
173             String conditionEvaluation = evaluateExpressionTemplate(contextObject, evaluationParameters,
174                     propertyReplacer.getCondition());
175             boolean conditionSuccess = Boolean.parseBoolean(conditionEvaluation);
176             if (conditionSuccess) {
177                 ObjectPropertyUtils.setPropertyValue(object, propertyReplacer.getPropertyName(),
178                         propertyReplacer.getReplacement());
179             }
180         }
181     }
182 
183     /**
184      * Retrieves the Map from the given object that containing the property expressions that should
185      * be evaluated. Each expression is then evaluated and the result is used to set the property value
186      *
187      * <p>
188      * If the expression is an el template (part static text and part expression), only the expression
189      * part will be replaced with the result. More than one expressions may be contained within the template
190      * </p>
191      *
192      * @param object - object instance to evaluate expressions for
193      * @param contextObject - object providing the default context for expressions
194      * @param evaluationParameters - map of additional parameters that may be used within the expressions
195      */
196     protected void evaluatePropertyExpressions(Object object, Object contextObject,
197             Map<String, Object> evaluationParameters) {
198         Map<String, String> propertyExpressions = new HashMap<String, String>();
199         if (Configurable.class.isAssignableFrom(object.getClass())) {
200             propertyExpressions = ((Configurable) object).getPropertyExpressions();
201         }
202 
203         for (Entry<String, String> propertyExpression : propertyExpressions.entrySet()) {
204             String propertyName = propertyExpression.getKey();
205             String expression = propertyExpression.getValue();
206 
207             // check whether expression should be evaluated or property should retain the expression
208             if (CloneUtils.fieldHasAnnotation(object.getClass(), propertyName, KeepExpression.class)) {
209                 // set expression as property value to be handled by the component
210                 ObjectPropertyUtils.setPropertyValue(object, propertyName, expression);
211                 continue;
212             }
213 
214             Object propertyValue = null;
215 
216             // determine whether the expression is a string template, or evaluates to another object type
217             if (StringUtils.startsWith(expression, UifConstants.EL_PLACEHOLDER_PREFIX) && StringUtils.endsWith(
218                     expression, UifConstants.EL_PLACEHOLDER_SUFFIX) && (StringUtils.countMatches(expression,
219                     UifConstants.EL_PLACEHOLDER_PREFIX) == 1)) {
220                 propertyValue = evaluateExpression(contextObject, evaluationParameters, expression);
221             } else {
222                 // treat as string template
223                 propertyValue = evaluateExpressionTemplate(contextObject, evaluationParameters, expression);
224             }
225 
226             ObjectPropertyUtils.setPropertyValue(object, propertyName, propertyValue);
227         }
228     }
229 
230     /**
231      * @see org.kuali.rice.krad.uif.service.ExpressionEvaluatorService#containsElPlaceholder(java.lang.String)
232      */
233     public boolean containsElPlaceholder(String value) {
234         boolean containsElPlaceholder = false;
235 
236         if (StringUtils.isNotBlank(value)) {
237             String elPlaceholder = StringUtils.substringBetween(value, UifConstants.EL_PLACEHOLDER_PREFIX,
238                     UifConstants.EL_PLACEHOLDER_SUFFIX);
239             if (StringUtils.isNotBlank(elPlaceholder)) {
240                 containsElPlaceholder = true;
241             }
242         }
243 
244         return containsElPlaceholder;
245     }
246 
247 }