View Javadoc
1 /*** 2 * ExpressionCompiler.java 3 * 4 * $Author: mballesteros $ 5 * $Date: 2003/11/28 19:18:03 $ 6 * $Revision: 1.1 $ 7 */ 8 package net.sf.jec; 9 10 import java.util.ArrayList; 11 import java.util.HashMap; 12 import java.util.List; 13 import java.util.Map; 14 15 import net.sf.jec.functions.CurrencyFunction; 16 import net.sf.jec.functions.DateFunction; 17 import net.sf.jec.functions.PercentFunction; 18 import net.sf.jec.namedop.AvgOperator; 19 import net.sf.jec.namedop.CountOperator; 20 import net.sf.jec.namedop.GroupByOperator; 21 import net.sf.jec.namedop.IfOperator; 22 import net.sf.jec.namedop.MaxOperator; 23 import net.sf.jec.namedop.MinOperator; 24 import net.sf.jec.namedop.OrderByOperator; 25 import net.sf.jec.namedop.SelectOperator; 26 import net.sf.jec.namedop.SumOperator; 27 import net.sf.jec.namedop.TableOperator; 28 29 /*** This class precompiles bean expressions so their evaluation becomes faster. 30 * 31 * @author mballesteros 32 */ 33 public class ExpressionCompiler { 34 35 /*** A map of defined functions 36 */ 37 private Map namedOperatorMap; 38 private Map functionMap; 39 40 private boolean throwExceptions; 41 42 /*** Creates a new ExpressionCompiler that uses the specified ObjectConverter 43 */ 44 public ExpressionCompiler(boolean throwExceptions) { 45 this.throwExceptions = throwExceptions; 46 this.namedOperatorMap = new HashMap(8); 47 this.functionMap = new HashMap(3); 48 } 49 50 /*** 51 * <p>Returns an {@link ExpressionCompiler} with default operators and 52 * functions. It is a convenience method that creates a new ExpressionCompiler 53 * and adds all the basic operators and functions with compehensive names. 54 * The complete list of added operators and functions is:<p> 55 * <ul> 56 * <li><b>Operators:</b> 57 * <ol> 58 * <li><b>$avg</b>: See {@link AvgOperator}</li> 59 * <li><b>$count</b>: See {@link CountOperator}</li> 60 * <li><b>$groupby</b>: See {@link GroupByOperator}</li> 61 * <li><b>$max</b>: See {@link MaxOperator}</li> 62 * <li><b>$min</b>: See {@link MinOperator}</li> 63 * <li><b>$orderby</b>: See {@link OrderByOperator}</li> 64 * <li><b>$select</b>: See {@link SelectOperator}</li> 65 * <li><b>$sum</b>: See {@link SumOperator}</li> 66 * <li><b>$table</b>: See {@link TableOperator}</li> 67 * </ol> 68 * </li> 69 * <li><b>Functions:</b> 70 * <ol> 71 * <li><b>$currency</b>: See {@link CurrencyOperator}</li> 72 * <li><b>$date</b>: See {@link DateFunction} with "dd/MM/yyyy"</li> 73 * <li><b>$percent</b>: See {@link PercentOperator}</li> 74 * </ol> 75 * </li> 76 * </ul> 77 * @param throwExceptions 78 * @return ExpressionCompiler 79 */ 80 public static ExpressionCompiler getDefaultInstance(boolean throwExceptions) { 81 ExpressionCompiler ec = new ExpressionCompiler(throwExceptions); 82 ec.addOperator("$avg", AvgOperator.class); 83 ec.addOperator("$count", CountOperator.class); 84 ec.addOperator("$groupby", GroupByOperator.class); 85 ec.addOperator("$if", IfOperator.class); 86 ec.addOperator("$max", MaxOperator.class); 87 ec.addOperator("$min", MinOperator.class); 88 ec.addOperator("$orderby", OrderByOperator.class); 89 ec.addOperator("$sum", SumOperator.class); 90 ec.addOperator("$select", SelectOperator.class); 91 ec.addOperator("$table", TableOperator.class); 92 ec.addFunction("$currency", new CurrencyFunction()); 93 ec.addFunction("$date", new DateFunction("dd/MM/yyyy")); 94 ec.addFunction("$percent", new PercentFunction()); 95 return ec; 96 } 97 98 /*** Adds a new function to the compiler 99 */ 100 public void addOperator(String name, Class operatorClass) { 101 this.namedOperatorMap.put(name, operatorClass); 102 } 103 104 /*** Adds a new function to the compiler 105 */ 106 public void addFunction(String name, Function function) { 107 this.functionMap.put(name, function); 108 } 109 110 /*** Compiles an expression into an operator 111 */ 112 public Operator compileOperator(String expr) throws ParseException { 113 // Build an expression lexer 114 Lexer lex = new Lexer(expr); 115 116 // Parse expression 117 return parse(lex); 118 } 119 120 // Root expression parse level 121 private Operator parse(Lexer lex) throws ParseException { 122 return parse5(lex); 123 } 124 125 // Parse level 5: '=' 126 private Operator parse5(Lexer lex) throws ParseException { 127 Operator out = parse4(lex); 128 129 lex.next(); 130 String token = lex.getToken(); 131 132 // Operator: '=' 133 if (token.equals("=")) { 134 if (!(out instanceof InvertibleOperator)) 135 throw new ParseException( 136 "Cannot assign to a non invertible operator: " + out); 137 lex.next(); 138 Operator nextOp = parse5(lex); 139 out = 140 new AssignOperator( 141 (InvertibleOperator) out, 142 nextOp, 143 throwExceptions); 144 } else { 145 lex.previous(); 146 return out; 147 } 148 return out; 149 } 150 151 // Parse level 4: boolean operators 152 private Operator parse4(Lexer lex) throws ParseException { 153 Operator out = parse3(lex); 154 155 lex.next(); 156 String token = lex.getToken(); 157 158 // Operator: '==' 159 if (token.equals("==") 160 || token.equals("!=") 161 || token.equals("<") 162 || token.equals(">") 163 || token.equals("&&") 164 || token.equals("||")) { 165 lex.next(); 166 Operator nextOp = parse4(lex); 167 out = new BooleanOperator(token, out, nextOp, throwExceptions); 168 } else { 169 lex.previous(); 170 return out; 171 } 172 return out; 173 } 174 175 // Parse level 3: Arithmetic operators +.- 176 private Operator parse3(Lexer lex) throws ParseException { 177 Operator out = parse2(lex); 178 179 lex.next(); 180 String token = lex.getToken(); 181 182 // Operator: '+', '-' 183 if (token.equals("+") || token.equals("-")) { 184 lex.next(); 185 Operator nextOp = parse3(lex); 186 out = new ArithmeticOperator(token, out, nextOp, throwExceptions); 187 } else { 188 lex.previous(); 189 return out; 190 } 191 return out; 192 } 193 194 // Parse level 2: Arithmetic operators *,/,% 195 private Operator parse2(Lexer lex) throws ParseException { 196 Operator out = parse1(lex); 197 198 lex.next(); 199 String token = lex.getToken(); 200 201 // Operator: '*', '/', '%' 202 if (token.equals("*") || token.equals("/") || token.equals("%")) { 203 lex.next(); 204 Operator nextOp = parse2(lex); 205 out = new ArithmeticOperator(token, out, nextOp, throwExceptions); 206 } else { 207 lex.previous(); 208 return out; 209 } 210 return out; 211 } 212 213 // Parse level 1: '.', '[]' 214 private Operator parse1(Lexer lex) throws ParseException { 215 Operator out = parse0(lex); 216 217 while (lex.next()) { 218 String token = lex.getToken(); 219 220 // Accessor or functions... 221 if (out == null && (Character.isLetter(token.charAt(0)) || 222 token.charAt(0)=='_') ) { 223 out = new AccessorOperator(token, out, false); 224 } else if (token.equals(".") || token.startsWith("$")) { 225 if (token.equals(".")) lex.next(); 226 token = lex.getToken(); 227 lex.next(); 228 if (lex.getToken().equals("(")) { 229 // Found operator... 230 Operator nestedOp = out; 231 Class operatorClass = (Class) namedOperatorMap.get(token); 232 if (operatorClass != null) { 233 try { 234 out = (Operator) operatorClass.newInstance(); 235 out.setNestedOperator(nestedOp); 236 } catch (Exception ex) { 237 throw new ParseException( 238 "Couldn't instantiate operator: " 239 + operatorClass); 240 } 241 } else { 242 Function function = (Function) functionMap.get(token); 243 if (function != null) { 244 out = new FunctionOperator(function); 245 out.setNestedOperator(nestedOp); 246 } else { 247 out = new MethodOperator(token); 248 out.setNestedOperator(nestedOp); 249 } 250 } 251 252 List argOperators = new ArrayList(); 253 boolean keep = true; 254 lex.next(); 255 while (true) { 256 Operator argOp = parse(lex); 257 if (argOp != null) argOperators.add(argOp); 258 lex.next(); 259 if (lex.getToken().equals(")")) 260 break; 261 else if (lex.getToken().equals(",")) 262 lex.next(); 263 else 264 throw new ParseException("Expected: ) or , tokens"); 265 } 266 if (argOperators.size() > 0) { 267 Operator[] argOperatorsArray = 268 new Operator[argOperators.size()]; 269 for (int i = 0; i < argOperators.size(); i++) { 270 argOperatorsArray[i] = 271 (Operator) argOperators.get(i); 272 } 273 out.setArgumentOperators(argOperatorsArray); 274 } 275 } else { 276 // Not a function, just an accessor 277 lex.previous(); 278 out = new AccessorOperator(token, out, false); 279 } 280 } 281 282 // Operator: '[' 283 else if (token.equals("[")) { 284 lex.next(); 285 Operator indexOperator = parse(lex); 286 out = new IndexerOperator(indexOperator, out, throwExceptions); 287 lex.next(); 288 if (!lex.getToken().equals("]")) 289 throw new ParseException("Expected: ] token"); 290 } else { 291 lex.previous(); 292 return out; 293 } 294 } 295 return out; 296 } 297 298 // Parse level 0: constants, accessor, and () 299 private Operator parse0(Lexer lex) throws ParseException { 300 Operator out = null; 301 String token = lex.getToken(); 302 303 if (token.equals("'")) { 304 // Constant string... 305 lex.next(); 306 out = new ConstantOperator(lex.getToken()); 307 lex.next(); 308 if (!lex.getToken().equals("'")) 309 throw new ParseException("Expected: ' token"); 310 } else if (Character.isDigit(token.charAt(0))) { 311 // Constant integer... 312 out = new ConstantOperator(new Integer(lex.getToken())); 313 } else if (token.equals("(")) { 314 // () 315 lex.next(); 316 out = parse(lex); 317 lex.next(); 318 if (!lex.getToken().equals(")")) 319 throw new ParseException( 320 "Expected token ')' found token '" + lex.getToken() + "'"); 321 } else { 322 lex.previous(); 323 } 324 return out; 325 } 326 }

This page was automatically generated by Maven