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