View Javadoc

1   /**
2    * Copyright 2005-2011 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         } catch (NoSuchMethodException e) {
139             LOG.error("Custom function for el expressions not found: " + e.getMessage());
140             throw new RuntimeException("Custom function for el expressions not found: " + e.getMessage(), e);
141         }
142     }
143 
144     /**
145      * Iterates through any configured <code>PropertyReplacer</code> instances for the component and
146      * evaluates the given condition. If the condition is met, the replacement value is set on the
147      * corresponding property
148      *
149      * @param object - object instance with property replacers list, should be either a component or layout manager
150      * @param contextObject - context for el evaluation
151      * @param evaluationParameters - parameters for el evaluation
152      */
153     protected void evaluatePropertyReplacers(Object object, Object contextObject,
154             Map<String, Object> evaluationParameters) {
155         List<PropertyReplacer> replacers = null;
156         if (Component.class.isAssignableFrom(object.getClass())) {
157             replacers = ((Component) object).getPropertyReplacers();
158         } else if (LayoutManager.class.isAssignableFrom(object.getClass())) {
159             replacers = ((LayoutManager) object).getPropertyReplacers();
160         }
161 
162         for (PropertyReplacer propertyReplacer : replacers) {
163             String conditionEvaluation = evaluateExpressionTemplate(contextObject, evaluationParameters,
164                     propertyReplacer.getCondition());
165             boolean conditionSuccess = Boolean.parseBoolean(conditionEvaluation);
166             if (conditionSuccess) {
167                 ObjectPropertyUtils.setPropertyValue(object, propertyReplacer.getPropertyName(),
168                         propertyReplacer.getReplacement());
169             }
170         }
171     }
172 
173     /**
174      * Retrieves the Map from the given object that containing the property expressions that should
175      * be evaluated. Each expression is then evaluated and the result is used to set the property value
176      *
177      * <p>
178      * If the expression is an el template (part static text and part expression), only the expression
179      * part will be replaced with the result. More than one expressions may be contained within the template
180      * </p>
181      *
182      * @param object - object instance to evaluate expressions for
183      * @param contextObject - object providing the default context for expressions
184      * @param evaluationParameters - map of additional parameters that may be used within the expressions
185      */
186     protected void evaluatePropertyExpressions(Object object, Object contextObject,
187             Map<String, Object> evaluationParameters) {
188         Map<String, String> propertyExpressions = new HashMap<String, String>();
189         if (Configurable.class.isAssignableFrom(object.getClass())) {
190             propertyExpressions = ((Configurable) object).getPropertyExpressions();
191         }
192 
193         for (Entry<String, String> propertyExpression : propertyExpressions.entrySet()) {
194             String propertyName = propertyExpression.getKey();
195             String expression = propertyExpression.getValue();
196 
197             // check whether expression should be evaluated or property should retain the expression
198             if (CloneUtils.fieldHasAnnotation(object.getClass(), propertyName, KeepExpression.class)) {
199                 // set expression as property value to be handled by the component
200                 ObjectPropertyUtils.setPropertyValue(object, propertyName, expression);
201                 continue;
202             }
203 
204             Object propertyValue = null;
205 
206             // determine whether the expression is a string template, or evaluates to another object type
207             if (StringUtils.startsWith(expression, UifConstants.EL_PLACEHOLDER_PREFIX) && StringUtils.endsWith(
208                     expression, UifConstants.EL_PLACEHOLDER_SUFFIX) && (StringUtils.countMatches(expression,
209                     UifConstants.EL_PLACEHOLDER_PREFIX) == 1)) {
210                 propertyValue = evaluateExpression(contextObject, evaluationParameters, expression);
211             } else {
212                 // treat as string template
213                 propertyValue = evaluateExpressionTemplate(contextObject, evaluationParameters, expression);
214             }
215 
216             ObjectPropertyUtils.setPropertyValue(object, propertyName, propertyValue);
217         }
218     }
219 
220     /**
221      * @see org.kuali.rice.krad.uif.service.ExpressionEvaluatorService#containsElPlaceholder(java.lang.String)
222      */
223     public boolean containsElPlaceholder(String value) {
224         boolean containsElPlaceholder = false;
225 
226         if (StringUtils.isNotBlank(value)) {
227             String elPlaceholder = StringUtils.substringBetween(value, UifConstants.EL_PLACEHOLDER_PREFIX,
228                     UifConstants.EL_PLACEHOLDER_SUFFIX);
229             if (StringUtils.isNotBlank(elPlaceholder)) {
230                 containsElPlaceholder = true;
231             }
232         }
233 
234         return containsElPlaceholder;
235     }
236 
237 }