1 | package tuffy.ra; |
2 | |
3 | import java.util.ArrayList; |
4 | import java.util.HashSet; |
5 | import java.util.Map; |
6 | |
7 | |
8 | |
9 | import tuffy.db.SQLMan; |
10 | import tuffy.mln.Type; |
11 | import tuffy.util.ExceptionMan; |
12 | import tuffy.util.StringMan; |
13 | |
14 | /** |
15 | * An expression to a function is like a literal to a predicate. |
16 | * The interesting part is that expressions can be nested. |
17 | * The value of an expression can be numeric/string/boolean. |
18 | * |
19 | * @author Feng Niu |
20 | * |
21 | */ |
22 | final public class Expression implements Cloneable{ |
23 | private Function func_ = null; |
24 | private ArrayList<Expression> args_ = new ArrayList<Expression>(); |
25 | private String val = null; |
26 | private String valBinding = null; |
27 | |
28 | public boolean changeName = true; |
29 | |
30 | public ArrayList<Expression> getArgs(){ |
31 | return args_; |
32 | } |
33 | |
34 | public Function getFunction(){ |
35 | return func_; |
36 | } |
37 | |
38 | /** |
39 | * Get the variables referenced by this expression. |
40 | * |
41 | */ |
42 | public HashSet<String> getVars(){ |
43 | HashSet<String> set = new HashSet<String>(); |
44 | if(func_ == Function.VariableBinding){ |
45 | set.add(val); |
46 | }else{ |
47 | for(Expression e : args_){ |
48 | set.addAll(e.getVars()); |
49 | } |
50 | } |
51 | return set; |
52 | } |
53 | |
54 | /** |
55 | * For aesthetics, check if we need a pair of parenthses for this experession. |
56 | * |
57 | */ |
58 | private boolean needEnclosure(){ |
59 | return func_.isOperator(); |
60 | } |
61 | |
62 | /** |
63 | * Construct a new expression based on the function {@link #func_} |
64 | * |
65 | * @param func the underlying function |
66 | */ |
67 | public Expression(Function func){ |
68 | func_ = func; |
69 | } |
70 | |
71 | /** |
72 | * Boolean negation |
73 | * @param e |
74 | * |
75 | */ |
76 | public static Expression not(Expression e){ |
77 | if(e.func_.equals(Function.NOT)){ |
78 | return e.args_.get(0); |
79 | } |
80 | Expression ne = new Expression(Function.NOT); |
81 | ne.addArgument(e); |
82 | return ne; |
83 | } |
84 | |
85 | /** |
86 | * Boolean AND |
87 | * @param e1 |
88 | * @param e2 |
89 | * |
90 | */ |
91 | public static Expression and(Expression e1, Expression e2){ |
92 | Expression ne = new Expression(Function.AND); |
93 | ne.addArgument(e1); |
94 | ne.addArgument(e2); |
95 | return ne; |
96 | } |
97 | |
98 | /** |
99 | * Boolean OR |
100 | * @param e1 |
101 | * @param e2 |
102 | * |
103 | */ |
104 | public static Expression or(Expression e1, Expression e2){ |
105 | Expression ne = new Expression(Function.OR); |
106 | ne.addArgument(e1); |
107 | ne.addArgument(e2); |
108 | return ne; |
109 | } |
110 | |
111 | /** |
112 | * Test if this expression returns a boolean value |
113 | * |
114 | */ |
115 | public boolean isBoolean(){ |
116 | return func_.getRetType() == Type.Bool; |
117 | } |
118 | |
119 | /** |
120 | * Test if this expression returns a string value |
121 | * |
122 | */ |
123 | public boolean isString(){ |
124 | return func_.getRetType() == Type.String; |
125 | } |
126 | |
127 | |
128 | /** |
129 | * Test if this expression returns a numeric value |
130 | * |
131 | */ |
132 | public boolean isNumeric(){ |
133 | return func_.getRetType() == Type.Integer |
134 | || func_.getRetType() == Type.Float; |
135 | } |
136 | |
137 | /** |
138 | * Append an argument to the underlying function |
139 | * @param expr the new argument |
140 | */ |
141 | public void addArgument(Expression expr){ |
142 | if(args_.size() >= func_.arity()){ |
143 | ExceptionMan.die("Function " + func_.getName() + |
144 | " expected " + func_.arity() + " arguments, but " + |
145 | "received more"); |
146 | }else{ |
147 | args_.add(expr); |
148 | } |
149 | } |
150 | |
151 | /** |
152 | * Atomic expression representing a constant integer |
153 | * @param n |
154 | * |
155 | */ |
156 | public static Expression exprConstInteger(int n){ |
157 | Expression ex = new Expression(Function.ConstantNumber); |
158 | ex.val = Integer.toString(n); |
159 | return ex; |
160 | } |
161 | |
162 | /** |
163 | * Atomic expression representing a constant number |
164 | * |
165 | */ |
166 | public static Expression exprConstNum(double num){ |
167 | Expression ex = new Expression(Function.ConstantNumber); |
168 | ex.val = Double.toString(num); |
169 | return ex; |
170 | } |
171 | |
172 | /** |
173 | * Atomic expression representing a constant string |
174 | * |
175 | */ |
176 | public static Expression exprConstString(String str){ |
177 | Expression ex = new Expression(Function.ConstantString); |
178 | ex.val = str; |
179 | return ex; |
180 | } |
181 | |
182 | /** |
183 | * Atomic expression representing a variable binding |
184 | * |
185 | */ |
186 | public static Expression exprVariableBinding(String var){ |
187 | Expression ex = new Expression(Function.VariableBinding); |
188 | ex.val = var; |
189 | return ex; |
190 | } |
191 | |
192 | |
193 | @SuppressWarnings("unchecked") |
194 | public Expression clone(){ |
195 | Expression ret; |
196 | |
197 | if(this.val != null){ |
198 | ret = new Expression(this.func_); |
199 | ret.val = this.val; |
200 | ret.valBinding = this.valBinding; |
201 | ret.args_ = (ArrayList<Expression>) this.args_.clone(); |
202 | return ret; |
203 | } |
204 | |
205 | ret = new Expression(this.func_); |
206 | for(Expression sub : this.args_){ |
207 | ret.addArgument(sub.clone()); |
208 | } |
209 | ret.val = this.val; |
210 | ret.valBinding = this.valBinding; |
211 | ret.changeName = this.changeName; |
212 | |
213 | return ret; |
214 | } |
215 | |
216 | /** |
217 | * Bind variable references to their values in the symbol table. |
218 | * Variable name in the clause -> attribute name in SQL representing |
219 | * the value column of the symbol table. |
220 | * |
221 | * @param mapVarVar |
222 | */ |
223 | public String renameVariables(Map<String, String> mapVarVar){ |
224 | String es = null; |
225 | if(func_ == Function.VariableBinding){ |
226 | String nvar = mapVarVar.get(val); |
227 | if(nvar == null){ |
228 | return val; |
229 | }else{ |
230 | val = nvar; |
231 | } |
232 | }else{ |
233 | for(Expression e : args_){ |
234 | String aaa = e.renameVariables(mapVarVar); |
235 | if(aaa != null){ |
236 | return aaa; |
237 | } |
238 | } |
239 | } |
240 | return es; |
241 | } |
242 | |
243 | |
244 | /** |
245 | * Bind variable references to their values in the symbol table. |
246 | * Variable name in the clause -> attribute name in SQL representing |
247 | * the value column of the symbol table. |
248 | * |
249 | * @param mapVarVal |
250 | */ |
251 | public void bindVariables(Map<String, String> mapVarVal){ |
252 | if(func_ == Function.VariableBinding){ |
253 | String attr = mapVarVal.get(val); |
254 | if(attr == null){ |
255 | ExceptionMan.die("Encountered a dangling variable: " + val); |
256 | }else{ |
257 | valBinding = attr; |
258 | } |
259 | }else{ |
260 | for(Expression e : args_){ |
261 | e.bindVariables(mapVarVal); |
262 | } |
263 | } |
264 | } |
265 | |
266 | /** |
267 | * Get the SQL snippet for this expression |
268 | * |
269 | */ |
270 | public String toSQL(){ |
271 | return getStringForm(true); |
272 | } |
273 | |
274 | /** |
275 | * Compute the SQL representation of this expression. |
276 | * Call {@link #bindVariables(Map)} first. |
277 | * |
278 | */ |
279 | private String getStringForm(boolean inSQL){ |
280 | // atomic |
281 | if(func_ == Function.ConstantNumber){ |
282 | return val; |
283 | } |
284 | if(func_ == Function.VariableBinding){ |
285 | return inSQL ? valBinding : val; |
286 | } |
287 | if(func_ == Function.ConstantString){ |
288 | return inSQL ? SQLMan.escapeString(val) : |
289 | "\"" + StringMan.escapeJavaString(val) + "\""; |
290 | } |
291 | |
292 | // cast argument into correct types |
293 | ArrayList<String> sterms = new ArrayList<String>(); |
294 | |
295 | if(inSQL){ |
296 | for(int i=0; i < func_.arity(); i++){ |
297 | Type t = func_.getArgTypes().get(i); |
298 | String s = args_.get(i).toSQL(); |
299 | if(t == Type.Integer){ |
300 | s = "(CASE WHEN (" + s + ") IS NULL THEN NULL " + |
301 | "ELSE (" + s + ")::INT END)"; |
302 | }else if(t == Type.Float){ |
303 | s = "(CASE WHEN (" + s + ") IS NULL THEN NULL " + |
304 | "ELSE (" + s + ")::FLOAT END)"; |
305 | }else if(t == Type.String){ |
306 | s = "CAST(" + s + " AS TEXT)"; |
307 | }else if(t == Type.Bool){ |
308 | s = "(CASE WHEN (" + s + ") IS NULL THEN NULL " + |
309 | "ELSE (" + s + ")::BOOL END)"; |
310 | }else{ |
311 | s= "(" + s + ")"; |
312 | } |
313 | |
314 | if(func_ == Function.Eq || func_ == Function.Neq){ |
315 | |
316 | //TODO: NEQ'S BUG!!!! ENRON680_TUFFY |
317 | // if(func_.getArgTypes().get(0) != func_.getArgTypes().get(1)){ |
318 | sterms.add(" CAST(" + s + " AS TEXT) "); |
319 | // }else{ |
320 | // sterms.add(s); |
321 | // } |
322 | |
323 | }else{ |
324 | sterms.add(s); |
325 | } |
326 | } |
327 | }else{ |
328 | for(int i=0; i < func_.arity(); i++){ |
329 | String s = args_.get(i).toString(); |
330 | if(args_.get(i).needEnclosure()){ |
331 | s = "(" + s + ")"; |
332 | } |
333 | sterms.add(s); |
334 | } |
335 | } |
336 | |
337 | // map to operators |
338 | if(func_ == Function.BitNeg){ |
339 | return "~" + sterms.get(0); |
340 | } |
341 | |
342 | if(func_ == Function.Factorial){ |
343 | return sterms.get(0) + "!"; |
344 | } |
345 | |
346 | if(func_ == Function.NOT){ |
347 | return "NOT " + sterms.get(0) + ""; |
348 | } |
349 | |
350 | if(func_ == Function.AND){ |
351 | return sterms.get(0) + " AND " + sterms.get(1); |
352 | } |
353 | |
354 | if(func_ == Function.OR){ |
355 | return sterms.get(0) + " OR " + sterms.get(1); |
356 | } |
357 | |
358 | if(func_ == Function.Add){ |
359 | return sterms.get(0) + " + " + sterms.get(1); |
360 | } |
361 | |
362 | if(func_ == Function.Subtract){ |
363 | return sterms.get(0) + " - " + sterms.get(1); |
364 | } |
365 | |
366 | if(func_ == Function.Multiply){ |
367 | return sterms.get(0) + " * " + sterms.get(1); |
368 | } |
369 | |
370 | if(func_ == Function.Divide){ |
371 | return sterms.get(0) + " / " + sterms.get(1); |
372 | } |
373 | |
374 | if(func_ == Function.Modulo){ |
375 | return sterms.get(0) + " % " + sterms.get(1); |
376 | } |
377 | |
378 | if(func_ == Function.BitAnd){ |
379 | return sterms.get(0) + " & " + sterms.get(1); |
380 | } |
381 | |
382 | if(func_ == Function.BitOr){ |
383 | return sterms.get(0) + " | " + sterms.get(1); |
384 | } |
385 | |
386 | if(func_ == Function.BitXor){ |
387 | return sterms.get(0) + (inSQL ? " # " : " ^ ") + sterms.get(1); |
388 | } |
389 | |
390 | if(func_ == Function.BitShiftLeft){ |
391 | return sterms.get(0) + " << " + sterms.get(1); |
392 | } |
393 | |
394 | if(func_ == Function.BitShiftRight){ |
395 | return sterms.get(0) + " >> " + sterms.get(1); |
396 | } |
397 | |
398 | if(func_ == Function.Concat){ |
399 | return sterms.get(0) + " || " + sterms.get(1); |
400 | } |
401 | |
402 | if(func_ == Function.Eq){ |
403 | //System.err.println(sterms.get(0) + " = " + sterms.get(1)); |
404 | return sterms.get(0) + " = " + sterms.get(1); |
405 | } |
406 | |
407 | if(func_ == Function.Neq){ |
408 | return sterms.get(0) + " <> " + sterms.get(1); |
409 | } |
410 | |
411 | if(func_ == Function.GreaterThan){ |
412 | return sterms.get(0) + " > " + sterms.get(1); |
413 | } |
414 | |
415 | if(func_ == Function.GreaterThanEq){ |
416 | return sterms.get(0) + " >= " + sterms.get(1); |
417 | } |
418 | |
419 | if(func_ == Function.LessThan){ |
420 | return sterms.get(0) + " < " + sterms.get(1); |
421 | } |
422 | |
423 | if(func_ == Function.LessThanEq){ |
424 | return sterms.get(0) + " <= " + sterms.get(1); |
425 | } |
426 | |
427 | // map to complex expressions |
428 | if(func_ == Function.StrContains){ |
429 | return "strpos(" + sterms.get(0) + ", " + sterms.get(1) + ") > 0"; |
430 | } |
431 | |
432 | if(func_ == Function.StrStartsWith){ |
433 | return "strpos(" + sterms.get(0) + ", " + sterms.get(1) + ") = 1"; |
434 | } |
435 | |
436 | if(func_ == Function.StrEndsWith){ |
437 | return "substr(" + sterms.get(0) + ", length(" + sterms.get(0) + |
438 | ") - length(" + sterms.get(1) + ") + 1) = " + sterms.get(1); |
439 | } |
440 | |
441 | // map to direct functions |
442 | StringBuilder sb = new StringBuilder(); |
443 | sb.append((inSQL ? func_.getPgFunction() : func_.getName()) + "("); |
444 | for(int i=0; i<func_.arity(); i++){ |
445 | sb.append(sterms.get(i)); |
446 | if(i != func_.arity() - 1){ |
447 | sb.append(", "); |
448 | } |
449 | } |
450 | sb.append(")"); |
451 | |
452 | |
453 | return sb.toString(); |
454 | } |
455 | |
456 | public String toString(){ |
457 | return getStringForm(false); |
458 | } |
459 | } |