19
19
*/
20
20
package org .broadleafcommerce .common .rule ;
21
21
22
+ import org .apache .commons .collections4 .MapUtils ;
22
23
import org .apache .commons .logging .Log ;
23
24
import org .apache .commons .logging .LogFactory ;
24
25
import org .broadleafcommerce .common .RequestDTO ;
36
37
import java .text .ParseException ;
37
38
import java .util .HashMap ;
38
39
import java .util .Map ;
40
+ import java .util .Map .Entry ;
39
41
40
42
import javax .servlet .http .HttpServletRequest ;
41
43
@@ -62,7 +64,7 @@ public class MvelHelper {
62
64
63
65
// The following attribute is set in BroadleafProcessURLFilter
64
66
public static final String REQUEST_DTO = "blRequestDTO" ;
65
-
67
+
66
68
/**
67
69
* Converts a field to the specified type. Useful when
68
70
* @param type
@@ -122,16 +124,38 @@ public static boolean evaluateRule(String rule, Map<String, Object> ruleParamete
122
124
*/
123
125
public static boolean evaluateRule (String rule , Map <String , Object > ruleParameters ,
124
126
Map <String , Serializable > expressionCache ) {
127
+ return evaluateRule (rule , ruleParameters , expressionCache , null );
128
+ }
129
+
130
+ /**
131
+ * @param rule
132
+ * @param ruleParameters
133
+ * @param expressionCache
134
+ * @param additionalContextImports additional imports to give to the {@link ParserContext} besides "MVEL" ({@link MVEL} and
135
+ * "MvelHelper" ({@link MvelHelper}) since they are automatically added
136
+ * @return
137
+ */
138
+ public static boolean evaluateRule (String rule , Map <String , Object > ruleParameters ,
139
+ Map <String , Serializable > expressionCache , Map <String , Class <?>> additionalContextImports ) {
140
+
125
141
// Null or empty is a match
126
142
if (rule == null || "" .equals (rule )) {
127
143
return true ;
128
144
} else {
129
145
// MVEL expression compiling can be expensive so let's cache the expression
130
- Serializable exp = ( Serializable ) expressionCache .get (rule );
146
+ Serializable exp = expressionCache .get (rule );
131
147
if (exp == null ) {
132
148
ParserContext context = new ParserContext ();
133
149
context .addImport ("MVEL" , MVEL .class );
134
150
context .addImport ("MvelHelper" , MvelHelper .class );
151
+ if (MapUtils .isNotEmpty (additionalContextImports )) {
152
+ for (Entry <String , Class <?>> entry : additionalContextImports .entrySet ()) {
153
+ context .addImport (entry .getKey (), entry .getValue ());
154
+ }
155
+ }
156
+
157
+ rule = modifyExpression (rule , ruleParameters , context );
158
+
135
159
exp = MVEL .compileExpression (rule , context );
136
160
expressionCache .put (rule , exp );
137
161
}
@@ -161,6 +185,45 @@ public static boolean evaluateRule(String rule, Map<String, Object> ruleParamete
161
185
}
162
186
}
163
187
}
188
+
189
+ /**
190
+ * <p>
191
+ * Provides a hook point to modify the final expression before it's built. By default, this looks for attribute
192
+ * maps and replaces them such that it does string comparison.
193
+ *
194
+ * <p>
195
+ * For example, given an expression like getProductAttributes()['somekey'] == 'someval', getProductAttributes()['somekey']
196
+ * actually returns a ProductAttribute object, not a String, so the comparison is wrong. Instead, we actually want
197
+ * to do this: getProductAttributes()['somekey'].?value == 'someval'. This function performs that replacement
198
+ *
199
+ * @param rule the rule to replace
200
+ * @return a modified version of <b>rule</b>
201
+ * @see {@link #getRuleAttributeMaps()}
202
+ */
203
+ protected static String modifyExpression (String rule , Map <String , Object > ruleParameters , ParserContext context ) {
204
+ String modifiedExpression = rule ;
205
+ for (String attributeMap : getRuleAttributeMaps ()) {
206
+ modifiedExpression = modifiedExpression .replaceAll (attributeMap + "\\ (\\ )\\ [(.*)\\ ](?!\\ .\\ ?value)" , attributeMap + "()[$1].?value" );
207
+ }
208
+ return modifiedExpression ;
209
+ }
210
+
211
+ /**
212
+ * Returns an array of attribute map field names that we need to do replacements for in
213
+ * {@link #modifyExpression(String, Map, ParserContext)}
214
+ */
215
+ protected static String [] getRuleAttributeMaps () {
216
+ // intentionally left out pricing context getPricingContextAttributes because that's a Map<String, String>
217
+ return new String []{ "getProductAttributes" ,
218
+ "getCategoryAttributesMap" ,
219
+ "getSkuAttributes" ,
220
+ "getOrderItemAttributes" ,
221
+ "getCustomerAttributes" ,
222
+ // Map<String, PageAttribute>
223
+ "getAdditionalAttributes" ,
224
+ // Map<String, AdminUserAttribute>
225
+ "getAdditionalFields" };
226
+ }
164
227
165
228
/**
166
229
* When true, LOG.info statement will be suppressed. Should only be set from within MvelHelperTest.
@@ -185,7 +248,7 @@ public static Map<String, Object> buildMvelParameters() {
185
248
if (brc != null && brc .getRequest () != null ) {
186
249
TimeDTO timeDto = new TimeDTO (SystemTime .asCalendar ());
187
250
HttpServletRequest request = brc .getRequest ();
188
- RequestDTO requestDto = ( RequestDTO ) brc .getRequestDTO ();
251
+ RequestDTO requestDto = brc .getRequestDTO ();
189
252
mvelParameters .put ("time" , timeDto );
190
253
mvelParameters .put ("request" , requestDto );
191
254
0 commit comments