001package scpc.model.support; 002 003import java.util.HashMap; 004import java.util.Map; 005import java.util.regex.Pattern; 006import java.util.regex.Matcher; 007import javax.script.ScriptException; 008import org.slf4j.Logger; 009import org.slf4j.LoggerFactory; 010import org.springframework.expression.EvaluationContext; 011import org.springframework.expression.Expression; 012import org.springframework.expression.spel.SpelCompilerMode; 013import org.springframework.expression.spel.SpelParserConfiguration; 014import org.springframework.expression.spel.standard.SpelExpressionParser; 015import org.springframework.expression.spel.support.StandardEvaluationContext; 016import scpc.model.IChainRule; 017import scpc.model.IRule; 018import scpc.model.SingleItem; 019 020/** 021 * A Spring Expression Language formula evaluator. 022 * <br>使用Spring Expression Language做為公式解譯工具 023 * 024 * @author Kent Yeh 025 * @param <T> type of real cart item. 026 */ 027public abstract class SpelEvalHelper<T> extends EvalHelper<T> { 028 029 private static final Logger logger = LoggerFactory.getLogger(SpelEvalHelper.class); 030 031 private static final EvaluationContext context = new StandardEvaluationContext(); 032 private static final SpelExpressionParser sep = new SpelExpressionParser(new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE, SpelEvalHelper.class.getClassLoader(), false, true, Integer.MAX_VALUE)); 033 private static final Pattern escapePtn = Pattern.compile("[\\\\\\^\\$\\.\\|\\?\\*\\+\\(\\)\\[\\{]"); 034 private BindHolder binder = null; 035 private IRule<T> bindRule = null; 036 private SingleItem<T> bindItem = null; 037 038 public Expression getExpression(String spel) { 039 return sep.parseExpression(spel); 040 } 041 042 private String escape(String src) { 043 Matcher m = escapePtn.matcher(src); 044 StringBuilder sb = new StringBuilder(); 045 int pos = 0; 046 while (m.find()) { 047 sb.append(src.substring(pos, m.start())).append("\\").append(src.substring(m.start(), m.start() + 1)); 048 pos = m.start() + 1; 049 } 050 return sb.append(src.substring(pos, src.length())).toString(); 051 } 052 053 /** 054 * @see EvalHelper#bindVaribles(scpc.model.IRule, scpc.model.SingleItem) 055 * @param rule 056 * @param item 057 * @return 058 */ 059 @Override 060 public EvalHelper<T> bindVaribles(IRule<T> rule, SingleItem<T> item) { 061 binder = new BindHolder(rule, item); 062 bindRule = rule; 063 bindItem = item; 064 return this; 065 } 066 067 /** 068 * @see EvalHelper#bindVarValue(java.lang.String, java.lang.Object) 069 * @param variable <br>Must be a javascript qualified variable name.</b> 070 * @param value 071 * @return 072 */ 073 @Override 074 public EvalHelper<T> bindVarValue(String variable, Object value) { 075 return this; 076 } 077 078 /** 079 * @see EvalHelper#eval(java.lang.String) 080 * @param <R> 081 * @param formula 082 * @return 083 * @throws ScriptException 084 */ 085 @Override 086 public <R> R eval(String formula) throws ScriptException { 087 if (binder == null) { 088 throw new ScriptException("Not any binder bind (IRule,SingleItem) yet"); 089 } 090 Map<String, String> prefix = new HashMap<>(); 091 Map<String, String> replacers = new HashMap<>(); 092 IChainRule<T> prule = bindRule.getPrevious(); 093 String rpfx = ""; 094 String rrpfx = "rule"; 095 while (prule != null) { 096 rpfx += getPreviousRulePrefix(); 097 rrpfx += ".previous"; 098 prefix.put(rpfx, rrpfx); 099 prule = prule.getPrevious(); 100 } 101 for (Map.Entry<String, String> entry : prefix.entrySet()) { 102 String varname = validJSVarName(entry.getKey() + getVarContainsCount()); 103 if (null != replacers.put(varname, entry.getValue() + ".containsCount")) { 104 throw new ScriptException(String.format(" 2.Variable[%s] already set!", varname)); 105 } 106 varname = validJSVarName(entry.getKey() + getVarSumOfContainsRegularPrice()); 107 if (null != replacers.put(varname, entry.getValue() + ".sumOfContainsRegularPrice")) { 108 throw new ScriptException(String.format(" 3.Variable[%s] already set!", varname)); 109 } 110 varname = validJSVarName(entry.getKey() + getVarSumOfContainsSalePrice()); 111 if (null != replacers.put(varname, entry.getValue() + ".sumOfContainsSalePrice")) { 112 throw new ScriptException(String.format(" 4.Variable[%s] already set!", varname)); 113 } 114 varname = validJSVarName(entry.getKey() + getVarSumOfSerialRegularPrice()); 115 if (null != replacers.put(varname, entry.getValue() + ".sumOfSerialRegularPrice")) { 116 throw new ScriptException(String.format(" 5.Variable[%s] already set!", varname)); 117 } 118 varname = validJSVarName(entry.getKey() + getVarSumOfSerialSalePrice()); 119 if (null != replacers.put(varname, entry.getValue() + ".sumOfSerialSalePrice")) { 120 throw new ScriptException(String.format(" 6.Variable[%s] already set!", varname)); 121 } 122 } 123 if (bindRule != null) { 124 String varname = validJSVarName(getVarContainsCount()); 125 if (null != replacers.put(varname, "rule.containsCount")) { 126 throw new ScriptException(String.format(" 8.Variable[%s] already set!", varname)); 127 } 128 varname = validJSVarName(getVarSerialNum()); 129 if (null != replacers.put(varname, "rule.serialNum")) { 130 throw new ScriptException(String.format(" 9.Variable[%s] already set!", varname)); 131 } 132 varname = validJSVarName(getVarSumOfContainsRegularPrice()); 133 if (null != replacers.put(varname, "rule.sumOfContainsRegularPrice")) { 134 throw new ScriptException(String.format("10.Variable[%s] already set!", varname)); 135 } 136 varname = validJSVarName(getVarSumOfContainsSalePrice()); 137 if (null != replacers.put(varname, "rule.sumOfContainsSalePrice")) { 138 throw new ScriptException(String.format("11.Variable[%s] already set!", varname)); 139 } 140 varname = validJSVarName(getVarSumOfSerialRegularPrice()); 141 if (null != replacers.put(varname, "rule.sumOfSerialRegularPrice")) { 142 throw new ScriptException(String.format("12,Variable[%s] already set!", varname)); 143 } 144 varname = validJSVarName(getVarSumOfSerialSalePrice()); 145 if (null != replacers.put(varname, "rule.sumOfSerialSalePrice")) { 146 throw new ScriptException(String.format("13.Variable[%s] already set!", varname)); 147 } 148 } 149 if (bindItem != null) { 150 String varname = validJSVarName(getVarSalePrice()); 151 if (null != replacers.put(varname, "item.salePrice")) { 152 throw new ScriptException(String.format("14.Variable[%s] already set!", varname)); 153 } 154 varname = validJSVarName(getVarRegularPrice()); 155 if (null != replacers.put(varname, "item.regularPrice")) { 156 throw new ScriptException(String.format("15.Variable[%s] already set!", varname)); 157 } 158 } 159 Matcher m = JS_NAME_LOCATOR.matcher(formula); 160 int pos = 0; 161 StringBuilder sb = new StringBuilder(); 162 while (m.find()) { 163 String rep = replacers.get(m.group()); 164 if (rep != null) { 165 sb.append(formula.substring(pos, m.start())).append(rep); 166 } 167 pos = m.end(); 168 } 169 formula = sb.append(formula.substring(pos)).toString(); 170 logger.debug("Spel.eval(\"{}\")", formula); 171 Expression exp = sep.parseExpression(formula); 172 return (R) exp.getValue(context, binder); 173 } 174 175 @Override 176 protected void purgeBind() { 177 binder = null; 178 bindRule = null; 179 bindItem = null; 180 } 181 182 private static class BindHolder<T> { 183 184 private final IRule<T> rule; 185 private final SingleItem<T> item; 186 187 public BindHolder(IRule<T> rule, SingleItem<T> item) { 188 this.rule = rule; 189 this.item = item; 190 } 191 192 public IRule<T> getRule() { 193 return rule; 194 } 195 196 public SingleItem<T> getItem() { 197 return item; 198 } 199 200 } 201}