1 56 package org.objectstyle.cayenne.exp; 57 58 import java.util.Collection ; 59 60 import org.apache.commons.beanutils.PropertyUtils; 61 import org.apache.log4j.Logger; 62 import org.apache.oro.text.perl.Perl5Util; 63 import org.objectstyle.cayenne.DataObject; 64 import org.objectstyle.cayenne.map.Entity; 65 import org.objectstyle.cayenne.util.Util; 66 67 79 abstract class ASTNode { 80 private static Logger logObj = Logger.getLogger(ASTNode.class); 81 82 private static final Perl5Util regexUtil = new Perl5Util(); 85 86 protected ASTNode nextNode; 87 88 static ASTNode buildObjectNode(Object object, ASTNode parent) { 89 ASTNode node = new PushNode(object); 90 return parent != null ? parent.wrapChildNode(node) : node; 91 } 92 93 static ASTNode buildExpressionNode(Expression expression, ASTNode parent) { 94 ASTNode node; 95 96 switch (expression.getType()) { 97 case Expression.OBJ_PATH : 98 case Expression.DB_PATH : 99 node = new PropertyNode(expression); 100 break; 101 case Expression.EQUAL_TO : 102 node = new EqualsNode(); 103 break; 104 case Expression.OR : 105 node = new OrNode(); 106 break; 107 case Expression.AND : 108 node = new AndNode(); 109 break; 110 case Expression.NOT : 111 node = new NotNode(); 112 break; 113 case Expression.LIST : 114 node = new ListNode(expression.getOperand(0)); 115 break; 116 case Expression.NOT_EQUAL_TO : 117 node = new NotEqualsNode(); 118 break; 119 case Expression.LESS_THAN : 120 node = new LessThanNode(); 121 break; 122 case Expression.LESS_THAN_EQUAL_TO : 123 node = new LessThanEqualsToNode(); 124 break; 125 case Expression.GREATER_THAN : 126 node = new GreaterThanNode(); 127 break; 128 case Expression.GREATER_THAN_EQUAL_TO : 129 node = new GreaterThanEqualsToNode(); 130 break; 131 case Expression.BETWEEN : 132 node = new BetweenNode(false); 133 break; 134 case Expression.NOT_BETWEEN : 135 node = new BetweenNode(true); 136 break; 137 case Expression.IN : 138 node = new InNode(false); 139 break; 140 case Expression.NOT_IN : 141 node = new InNode(true); 142 break; 143 case Expression.LIKE : 144 node = new LikeNode((String ) expression.getOperand(1), false, false); 145 break; 146 case Expression.NOT_LIKE : 147 node = new LikeNode((String ) expression.getOperand(1), true, false); 148 break; 149 case Expression.LIKE_IGNORE_CASE : 150 node = new LikeNode((String ) expression.getOperand(1), false, true); 151 break; 152 case Expression.NOT_LIKE_IGNORE_CASE : 153 node = new LikeNode((String ) expression.getOperand(1), true, true); 154 break; 155 default : 156 throw new ExpressionException( 157 "Unsupported expression type: " 158 + expression.getType() 159 + " (" 160 + expression.expName() 161 + ")"); 162 } 163 164 return parent != null ? parent.wrapChildNode(node) : node; 165 } 166 167 private static boolean contains(Object [] objects, Object object) { 168 int size = objects.length; 169 170 for (int i = 0; i < size; i++) { 171 if (Util.nullSafeEquals(objects[i], object)) { 172 return true; 173 } 174 } 175 176 return false; 177 } 178 179 183 ASTNode wrapChildNode(ASTNode childNode) { 184 return childNode; 185 } 186 187 ASTNode getNextNode() { 188 return nextNode; 189 } 190 191 void setNextNode(ASTNode nextNode) { 192 this.nextNode = nextNode; 193 } 194 195 abstract void appendString(StringBuffer buffer); 196 197 202 boolean evaluateBooleanASTChain(Object bean) throws ExpressionException { 203 return ASTStack.booleanFromObject(evaluateASTChain(bean)); 204 } 205 206 210 Object evaluateASTChain(Object bean) throws ExpressionException { 211 ASTNode currentNode = this; 212 ASTStack stack = new ASTStack(); 213 214 try { 216 while ((currentNode = currentNode.evaluateWithObject(stack, bean)) != null) { 217 } 219 220 return stack.pop(); 221 } 222 catch (Throwable th) { 223 if (th instanceof ExpressionException) { 224 throw (ExpressionException) th; 225 } 226 else { 227 throw new ExpressionException( 228 "Error evaluating expression.", 229 this.toString(), 230 Util.unwindException(th)); 231 } 232 } 233 } 234 235 240 abstract ASTNode evaluateWithObject(ASTStack stack, Object bean); 241 242 public String toString() { 243 StringBuffer buffer = new StringBuffer (); 245 246 for (ASTNode node = this; node != null; node = node.getNextNode()) { 247 node.appendString(buffer); 248 } 249 250 return buffer.toString(); 251 } 252 253 255 final static class PushNode extends ASTNode { 256 Object value; 257 258 PushNode(Object value) { 259 this.value = value; 260 } 261 262 ASTNode evaluateWithObject(ASTStack stack, Object bean) { 263 stack.push(value); 264 return nextNode; 265 } 266 267 void appendString(StringBuffer buffer) { 268 buffer.append("%@"); 269 } 270 } 271 272 final static class ListNode extends ASTNode { 273 Object [] value; 274 275 ListNode(Object object) { 276 if (object instanceof Collection ) { 279 value = ((Collection ) object).toArray(); 280 } 281 else if (object instanceof Object []) { 282 value = (Object []) object; 283 } 284 else { 285 value = new Object [] { object }; 288 } 289 } 290 291 ASTNode evaluateWithObject(ASTStack stack, Object bean) { 292 stack.push(value); 293 return nextNode; 294 } 295 296 void appendString(StringBuffer buffer) { 297 buffer.append("(%@)"); 298 } 299 } 300 301 final static class PropertyNode extends ASTNode { 302 Expression pathExp; 303 String propertyPath; 304 305 PropertyNode(Expression pathExp) { 306 this.pathExp = pathExp; 307 this.propertyPath = (String ) pathExp.getOperand(0); 308 } 309 310 ASTNode evaluateWithObject(ASTStack stack, Object bean) { 311 try { 313 stack.push( 318 (bean instanceof DataObject) 319 ? ((DataObject) bean).readNestedProperty(propertyPath) 320 : (bean instanceof Entity) 321 ? ((Entity) bean).resolvePathComponents(pathExp) 322 : PropertyUtils.getProperty(bean, propertyPath)); 323 } 324 catch (Exception ex) { 325 String beanClass = (bean != null) ? bean.getClass().getName() : "<null>"; 326 String msg = 327 "Error reading property '" + beanClass + "." + propertyPath + "'."; 328 logObj.warn(msg, ex); 329 throw new ExpressionException(msg, ex); 330 } 331 return nextNode; 332 } 333 334 void appendString(StringBuffer buffer) { 335 buffer.append("'").append(propertyPath).append("'"); 336 } 337 } 338 339 final static class EqualsNode extends ASTNode { 340 ASTNode evaluateWithObject(ASTStack stack, Object bean) { 341 stack.push(Util.nullSafeEquals(stack.pop(), stack.pop())); 343 return nextNode; 344 } 345 346 void appendString(StringBuffer buffer) { 347 buffer.append(" = "); 348 } 349 } 350 351 final static class AndNode extends ASTNode { 352 ASTNode evaluateWithObject(ASTStack stack, Object bean) { 353 stack.push(stack.popBoolean() && stack.popBoolean()); 355 return nextNode; 356 } 357 358 ASTNode wrapChildNode(ASTNode childNode) { 359 return new AndOperandWrapper(childNode, this); 360 } 361 362 void appendString(StringBuffer buffer) { 363 buffer.append(" and "); 364 } 365 } 366 367 final static class OrNode extends ASTNode { 368 ASTNode evaluateWithObject(ASTStack stack, Object bean) { 369 stack.push(stack.popBoolean() || stack.popBoolean()); 371 return nextNode; 372 } 373 374 ASTNode wrapChildNode(ASTNode childNode) { 375 return new OrOperandWrapper(childNode, this); 376 } 377 378 void appendString(StringBuffer buffer) { 379 buffer.append(" or "); 380 } 381 } 382 383 final static class NotNode extends ASTNode { 384 ASTNode evaluateWithObject(ASTStack stack, Object bean) { 385 stack.push(!stack.popBoolean()); 387 return nextNode; 388 } 389 390 void appendString(StringBuffer buffer) { 391 buffer.append(" not "); 392 } 393 } 394 395 final static class NotEqualsNode extends ASTNode { 396 ASTNode evaluateWithObject(ASTStack stack, Object bean) { 397 stack.push(!Util.nullSafeEquals(stack.pop(), stack.pop())); 399 return nextNode; 400 } 401 402 void appendString(StringBuffer buffer) { 403 buffer.append(" != "); 404 } 405 } 406 407 final static class LessThanNode extends ASTNode { 408 ASTNode evaluateWithObject(ASTStack stack, Object bean) { 409 boolean result = false; 411 412 Object c1 = stack.pop(); 413 Comparable c2 = stack.popComparable(); 414 415 if (c1 != null && c2 != null) { 417 result = c2.compareTo(c1) < 0; 420 } 421 422 stack.push(result); 423 return nextNode; 424 } 425 426 void appendString(StringBuffer buffer) { 427 buffer.append(" < "); 428 } 429 } 430 431 final static class LessThanEqualsToNode extends ASTNode { 432 ASTNode evaluateWithObject(ASTStack stack, Object bean) { 433 boolean result = false; 435 436 Object c1 = stack.pop(); 437 Comparable c2 = stack.popComparable(); 438 439 if (c1 != null && c2 != null) { 441 result = c2.compareTo(c1) <= 0; 444 } 445 446 stack.push(result); 447 return nextNode; 448 } 449 450 void appendString(StringBuffer buffer) { 451 buffer.append(" <= "); 452 } 453 } 454 455 final static class GreaterThanNode extends ASTNode { 456 ASTNode evaluateWithObject(ASTStack stack, Object bean) { 457 boolean result = false; 459 460 Object c1 = stack.pop(); 461 Comparable c2 = stack.popComparable(); 462 463 if (c1 != null && c2 != null) { 465 result = c2.compareTo(c1) > 0; 468 } 469 470 stack.push(result); 471 return nextNode; 472 } 473 474 void appendString(StringBuffer buffer) { 475 buffer.append(" > "); 476 } 477 } 478 479 final static class GreaterThanEqualsToNode extends ASTNode { 480 ASTNode evaluateWithObject(ASTStack stack, Object bean) { 481 boolean result = false; 483 484 Object c1 = stack.pop(); 485 Comparable c2 = stack.popComparable(); 486 487 if (c1 != null && c2 != null) { 489 result = c2.compareTo(c1) >= 0; 492 } 493 494 stack.push(result); 495 return nextNode; 496 } 497 498 void appendString(StringBuffer buffer) { 499 buffer.append(" >= "); 500 } 501 } 502 503 final static class BetweenNode extends ASTNode { 504 boolean negate; 505 506 BetweenNode(boolean negate) { 507 this.negate = negate; 508 } 509 510 ASTNode evaluateWithObject(ASTStack stack, Object bean) { 511 boolean result = false; 512 513 515 Comparable c1 = stack.popComparable(); 516 Comparable c2 = stack.popComparable(); 517 Object c3 = stack.pop(); 518 519 if (c1 != null && c2 != null && c3 != null) { 521 result = c2.compareTo(c3) <= 0 && c1.compareTo(c3) >= 0; 524 } 525 526 stack.push((negate) ? !result : result); 527 return nextNode; 528 } 529 530 void appendString(StringBuffer buffer) { 531 if (negate) { 532 buffer.append(" NOT"); 533 } 534 buffer.append(" BETWEEN "); 535 } 536 } 537 538 final static class InNode extends ASTNode { 539 boolean negate; 540 541 InNode(boolean negate) { 542 this.negate = negate; 543 } 544 545 ASTNode evaluateWithObject(ASTStack stack, Object bean) { 546 547 Object [] o1 = (Object []) stack.pop(); 549 Object o2 = stack.pop(); 550 551 boolean result = contains(o1, o2); 552 stack.push((negate) ? !result : result); 553 return nextNode; 554 } 555 556 void appendString(StringBuffer buffer) { 557 if (negate) { 558 buffer.append(" NOT"); 559 } 560 buffer.append(" IN "); 561 } 562 } 563 564 final static class LikeNode extends ASTNode { 565 String regex; 566 boolean negate; 567 568 LikeNode(String pattern, boolean negate, boolean ignoreCase) { 569 this.regex = Util.sqlPatternToRegex(pattern, ignoreCase); 570 this.negate = negate; 571 } 572 573 ASTNode evaluateWithObject(ASTStack stack, Object bean) { 574 Object o = stack.pop(); 576 String string = (o != null) ? o.toString() : null; 577 578 boolean match = regexUtil.match(regex, string); 579 stack.push((negate) ? !match : match); 580 return nextNode; 581 } 582 583 void appendString(StringBuffer buffer) { 584 if (negate) { 585 buffer.append(" NOT"); 586 } 587 588 buffer.append(" LIKE ").append(regex); 589 } 590 } 591 592 594 599 abstract static class ConditionalJumpNode extends ASTNode { 600 ASTNode wrappedNode; 601 ASTNode altNode; 602 603 ConditionalJumpNode(ASTNode wrappedNode, ASTNode altNode) { 604 this.wrappedNode = wrappedNode; 605 this.altNode = altNode; 606 } 607 608 ASTNode evaluateWithObject(ASTStack stack, Object bean) { 609 ASTNode next = wrappedNode.evaluateWithObject(stack, bean); 610 611 return jumpPastAltNode(stack) 614 ? ((altNode != null) ? altNode.getNextNode() : null) 615 : next; 616 } 617 618 ASTNode getNextNode() { 619 return (wrappedNode != null) ? wrappedNode.getNextNode() : null; 620 } 621 622 void setNextNode(ASTNode nextNode) { 623 if (wrappedNode != null) { 624 wrappedNode.setNextNode(nextNode); 625 } 626 } 627 628 void appendString(StringBuffer buffer) { 629 buffer.append("("); 630 if (wrappedNode != null) { 631 wrappedNode.appendString(buffer); 632 } 633 else { 634 buffer.append("?"); 635 } 636 buffer.append(")"); 637 } 638 639 abstract boolean jumpPastAltNode(ASTStack stack); 640 } 641 642 final static class AndOperandWrapper extends ConditionalJumpNode { 643 AndOperandWrapper(ASTNode wrappedNode, ASTNode altNode) { 644 super(wrappedNode, altNode); 645 } 646 647 boolean jumpPastAltNode(ASTStack stack) { 648 return !stack.peekBoolean(); 649 } 650 } 651 652 final static class OrOperandWrapper extends ConditionalJumpNode { 653 OrOperandWrapper(ASTNode wrappedNode, ASTNode altNode) { 654 super(wrappedNode, altNode); 655 } 656 657 boolean jumpPastAltNode(ASTStack stack) { 658 return stack.peekBoolean(); 659 } 660 } 661 } 662 | Popular Tags |