1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  package org.kuali.rice.krad.uif.util;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.apache.commons.logging.Log;
20  import org.apache.commons.logging.LogFactory;
21  import org.kuali.rice.core.api.exception.RiceRuntimeException;
22  import org.kuali.rice.krad.datadictionary.uif.UifDictionaryBean;
23  
24  import java.util.ArrayList;
25  import java.util.HashMap;
26  import java.util.List;
27  import java.util.Map;
28  
29  
30  
31  
32  
33  
34  public class ExpressionUtils {
35      private static final Log LOG = LogFactory.getLog(ExpressionUtils.class);
36  
37      
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53      public static void populatePropertyExpressionsFromGraph(UifDictionaryBean expressionConfigurable, boolean buildRefreshGraphs) {
54          if (expressionConfigurable == null || expressionConfigurable.getExpressionGraph() == null) {
55              return;
56          }
57  
58          
59          
60          Map<String, Map<String, String>> refreshExpressionGraphs = new HashMap<String, Map<String, String>>();
61  
62          Map<String, String> expressionGraph = expressionConfigurable.getExpressionGraph();
63          for (Map.Entry<String, String> expressionEntry : expressionGraph.entrySet()) {
64              String propertyName = expressionEntry.getKey();
65              String expression = expressionEntry.getValue();
66  
67              
68              UifDictionaryBean configurableWithExpression = expressionConfigurable;
69  
70              
71              String adjustedPropertyName = propertyName;
72              if (StringUtils.contains(propertyName, ".")) {
73                  String configurablePath = StringUtils.substringBeforeLast(propertyName, ".");
74                  adjustedPropertyName = StringUtils.substringAfterLast(propertyName, ".");
75  
76                  Object nestedObject = ObjectPropertyUtils.getPropertyValue(expressionConfigurable, configurablePath);
77                  if ((nestedObject == null) || !(nestedObject instanceof UifDictionaryBean)) {
78                      throw new RiceRuntimeException(
79                              "Object for which expression is configured on is null or does not implement UifDictionaryBean: '"
80                                      + configurablePath
81                                      + "'");
82                  }
83  
84                  
85                  configurableWithExpression = (UifDictionaryBean) nestedObject;
86  
87                  
88                  if (buildRefreshGraphs) {
89                      String currentPath = "";
90  
91                      String[] configurablePathNames = StringUtils.split(configurablePath, ".");
92                      for (String configurablePathName : configurablePathNames) {
93                          if (StringUtils.isNotBlank(currentPath)) {
94                              currentPath += ".";
95                          }
96                          currentPath += configurablePathName;
97  
98                          Map<String, String> graphExpressions = null;
99                          if (refreshExpressionGraphs.containsKey(currentPath)) {
100                             graphExpressions = refreshExpressionGraphs.get(currentPath);
101                         } else {
102                             graphExpressions = new HashMap<String, String>();
103                             refreshExpressionGraphs.put(currentPath, graphExpressions);
104                         }
105 
106                         
107                         String configurablePropertyName = StringUtils.substringAfter(propertyName, currentPath + ".");
108                         graphExpressions.put(configurablePropertyName, expression);
109                     }
110                 }
111             }
112 
113             configurableWithExpression.getPropertyExpressions().put(adjustedPropertyName, expression);
114         }
115 
116         
117         if (buildRefreshGraphs) {
118             for (String configurablePath : refreshExpressionGraphs.keySet()) {
119                 Object nestedObject = ObjectPropertyUtils.getPropertyValue(expressionConfigurable, configurablePath);
120                 
121                 if ((nestedObject != null) && (nestedObject instanceof UifDictionaryBean)) {
122                     ((UifDictionaryBean) nestedObject).setRefreshExpressionGraph(refreshExpressionGraphs.get(
123                             configurablePath));
124                 }
125             }
126 
127             
128             expressionConfigurable.setRefreshExpressionGraph(expressionGraph);
129         }
130     }
131 
132     
133 
134 
135 
136 
137 
138 
139 
140 
141 
142 
143 
144 
145 
146 
147 
148 
149 
150     public static String parseExpression(String exp, List<String> controlNames) {
151         
152         exp = exp.trim();
153         if (exp.startsWith("@{")) {
154             exp = StringUtils.removeStart(exp, "@{");
155             if (exp.endsWith("}")) {
156                 exp = StringUtils.removeEnd(exp, "}");
157             }
158         }
159 
160         exp = StringUtils.replace(exp, "!=", " != ");
161         exp = StringUtils.replace(exp, "==", " == ");
162         exp = StringUtils.replace(exp, ">", " > ");
163         exp = StringUtils.replace(exp, "<", " < ");
164         exp = StringUtils.replace(exp, "<=", " <= ");
165         exp = StringUtils.replace(exp, ">=", " >= ");
166 
167         String conditionJs = exp;
168         String stack = "";
169 
170         boolean expectingSingleQuote = false;
171         boolean ignoreNext = false;
172         for (int i = 0; i < exp.length(); i++) {
173             char c = exp.charAt(i);
174             if (!expectingSingleQuote && !ignoreNext && (c == '(' || c == ' ' || c == ')')) {
175                 evaluateCurrentStack(stack.trim(), controlNames);
176                 
177                 stack = "";
178                 continue;
179             } else if (!ignoreNext && c == '\'') {
180                 stack = stack + c;
181                 expectingSingleQuote = !expectingSingleQuote;
182             } else if (c == '\\') {
183                 stack = stack + c;
184                 ignoreNext = !ignoreNext;
185             } else {
186                 stack = stack + c;
187                 ignoreNext = false;
188             }
189         }
190 
191         if (StringUtils.isNotEmpty(stack)) {
192             evaluateCurrentStack(stack.trim(), controlNames);
193         }
194 
195         conditionJs = conditionJs.replaceAll("\\s(?i:ne)\\s", " != ").replaceAll("\\s(?i:eq)\\s", " == ").replaceAll(
196                 "\\s(?i:gt)\\s", " > ").replaceAll("\\s(?i:lt)\\s", " < ").replaceAll("\\s(?i:lte)\\s", " <= ")
197                 .replaceAll("\\s(?i:gte)\\s", " >= ").replaceAll("\\s(?i:and)\\s", " && ").replaceAll("\\s(?i:or)\\s",
198                         " || ").replaceAll("\\s(?i:not)\\s", " != ").replaceAll("\\s(?i:null)\\s?", " '' ").replaceAll(
199                         "\\s?(?i:#empty)\\((.*?)\\)", "isValueEmpty($1)").replaceAll("\\s?(?i:#listContains)\\((.*?)\\)",
200                         "listContains($1)").replaceAll("\\s?(?i:#emptyList)\\((.*?)\\)", "emptyList($1)");
201 
202         if (conditionJs.contains("matches")) {
203             conditionJs = conditionJs.replaceAll("\\s+(?i:matches)\\s+'.*'", ".match(/" + "$0" + "/) != null ");
204             conditionJs = conditionJs.replaceAll("\\(/\\s+(?i:matches)\\s+'", "(/");
205             conditionJs = conditionJs.replaceAll("'\\s*/\\)", "/)");
206         }
207 
208         List<String> removeControlNames = new ArrayList<String>();
209         List<String> addControlNames = new ArrayList<String>();
210         
211         for (String propertyName : controlNames) {
212             
213             if(propertyName.trim().startsWith("{") && propertyName.trim().endsWith("}")){
214                 String array = propertyName.trim().replace('{', '[');
215                 array = array.replace('}', ']');
216                 conditionJs = conditionJs.replace(propertyName, array);
217                 removeControlNames.add(propertyName);
218                 continue;
219             }
220 
221             
222             if (propertyName.startsWith("!")){
223                 String actualPropertyName = StringUtils.removeStart(propertyName, "!");
224                 conditionJs = conditionJs.replace(propertyName,
225                         "!coerceValue(\"" + actualPropertyName + "\")");
226                 removeControlNames.add(propertyName);
227                 addControlNames.add(actualPropertyName);
228             }
229             else{
230                 conditionJs = conditionJs.replace(propertyName, "coerceValue(\"" + propertyName + "\")");
231             }
232         }
233 
234         controlNames.removeAll(removeControlNames);
235         controlNames.addAll(addControlNames);
236 
237         return conditionJs;
238     }
239 
240     
241 
242 
243 
244 
245 
246 
247     public static void evaluateCurrentStack(String stack, List<String> controlNames) {
248         if (StringUtils.isNotBlank(stack)) {
249             if (!(stack.equals("==")
250                     || stack.equals("!=")
251                     || stack.equals(">")
252                     || stack.equals("<")
253                     || stack.equals(">=")
254                     || stack.equals("<=")
255                     || stack.equalsIgnoreCase("ne")
256                     || stack.equalsIgnoreCase("eq")
257                     || stack.equalsIgnoreCase("gt")
258                     || stack.equalsIgnoreCase("lt")
259                     || stack.equalsIgnoreCase("lte")
260                     || stack.equalsIgnoreCase("gte")
261                     || stack.equalsIgnoreCase("matches")
262                     || stack.equalsIgnoreCase("null")
263                     || stack.equalsIgnoreCase("false")
264                     || stack.equalsIgnoreCase("true")
265                     || stack.equalsIgnoreCase("and")
266                     || stack.equalsIgnoreCase("or")
267                     || stack.contains("#empty")
268                     || stack.equals("!")
269                     || stack.contains("#emptyList")
270                     || stack.contains("#listContains")
271                     || stack.startsWith("'")
272                     || stack.endsWith("'"))) {
273 
274                 boolean isNumber = false;
275                 if ((StringUtils.isNumeric(stack.substring(0, 1)) || stack.substring(0, 1).equals("-"))) {
276                     try {
277                         Double.parseDouble(stack);
278                         isNumber = true;
279                     } catch (NumberFormatException e) {
280                         isNumber = false;
281                     }
282                 }
283 
284                 if (!(isNumber)) {
285                     
286                     if(StringUtils.endsWith(stack, ",")){
287                         stack = StringUtils.removeEnd(stack, ",").trim();
288                     }
289 
290                     if (!controlNames.contains(stack)) {
291                         controlNames.add(stack);
292                     }
293                 }
294             }
295         }
296     }
297 
298 }