1 10 package org.mmbase.cache; 11 12 import java.lang.reflect.*; 13 import java.util.*; 14 15 import org.mmbase.bridge.Node; 16 import org.mmbase.bridge.implementation.BasicQuery; 17 import org.mmbase.core.CoreField; 18 import org.mmbase.core.event.*; 19 import org.mmbase.datatypes.DataType; 20 import org.mmbase.module.core.*; 21 import org.mmbase.storage.search.*; 22 import org.mmbase.storage.search.implementation.*; 23 import org.mmbase.storage.search.implementation.database.BasicSqlHandler; 24 import org.mmbase.util.Casting; 25 import org.mmbase.util.logging.*; 26 27 48 public class ConstraintsMatchingStrategy extends ReleaseStrategy { 49 50 private static final Logger log = Logging.getLoggerInstance(ConstraintsMatchingStrategy.class); 51 private static final BasicSqlHandler sqlHandler = new BasicSqlHandler(); 52 private final static String escapeChars=".\\?+*$()[]{}^|&"; 53 54 58 private static final Cache constraintWrapperCache; 59 60 61 static { 62 constraintWrapperCache = new Cache(1000) { 63 public String getName(){ return "ConstraintMatcherCache";} 64 public String getDescription() {return "Caches query constraint wrappers used by ConstraintsMatchingStrategy";} 65 }; 66 Cache.putCache(constraintWrapperCache); 67 } 68 69 private static final Map constraintMatcherConstructors = new HashMap(); 70 static { 71 Class [] innerClasses = ConstraintsMatchingStrategy.class.getDeclaredClasses(); 72 for (int i = 0; i < innerClasses.length; i++) { 73 Class innerClass = innerClasses[i]; 74 if (innerClass.getName().endsWith("Matcher") && ! Modifier.isAbstract(innerClass.getModifiers())) { 75 String matcherClassName = innerClass.getName(); 76 matcherClassName = matcherClassName.substring(matcherClassName.lastIndexOf("$") + 1); 77 Constructor con = null; 78 Constructor[] cons = innerClass.getConstructors(); 79 for (int j = 0; j < cons.length; j++) { 80 Class [] params = cons[j].getParameterTypes(); 81 if(params.length == 1 && Constraint.class.isAssignableFrom(params[0])) { 82 con = cons[j]; 83 break; 84 } 85 } 86 if (con == null) { 87 log.error("Class " + innerClass + " has no appropriate constructor"); 88 continue; 89 } 90 91 constraintMatcherConstructors.put(matcherClassName, con); 92 log.debug("** found matcher: " + matcherClassName); 93 } 94 } 95 } 96 97 public ConstraintsMatchingStrategy() { 98 super(); 99 } 100 101 public String getName() { 102 return "Constraint matching strategy"; 103 } 104 105 public String getDescription() { 106 return "Checks wether a changed node has a matching step within the constraints of a query, and then checks " 107 + "if the node falls within the constraint. For changed nodes a check is made if the node previously " 108 + "fell within the constraint and if it does so now. Queries that exclude changed nodes by their constraints " 109 + "will not be flushed."; 110 } 111 112 protected final boolean doEvaluate(NodeEvent event, SearchQuery query, List cachedResult) { 113 Constraint constraint = query.getConstraint(); 115 if(constraint == null) return true; 117 AbstractConstraintMatcher matcher = (AbstractConstraintMatcher) constraintWrapperCache.get(query); 119 120 if(matcher == null){ 122 try { 123 matcher = findMatcherForConstraint(constraint); 124 if (log.isTraceEnabled()) { 125 log.trace("created constraint matcher: " + matcher); 126 } 127 if (query instanceof BasicQuery) { 129 constraintWrapperCache.put(((BasicQuery) query).getQuery(), matcher); 130 } else { 131 constraintWrapperCache.put(query, matcher); 132 } 133 } catch (Exception e) { 135 log.error("Could not create constraint matcher for constraint: " + constraint + "main reason: " + e, e); 136 } 137 } else { 138 if(log.isTraceEnabled()){ 139 log.trace("found matcher for query in cache. query: " + query); 140 } 141 } 142 143 if(matcher != null){ 145 switch(event.getType()) { 146 case Event.TYPE_NEW: { 147 Map newValues = event.getNewValues(); 148 if(matcher.eventApplies(newValues, event)){ 151 boolean eventMatches = matcher.nodeMatchesConstraint(newValues, event); 152 if (log.isDebugEnabled()) { 153 logResult((eventMatches ? "" : "no ") + "flush: with matcher {" + matcher + "}:", query, event, null); 154 } 155 return eventMatches; 156 } else { 157 if (log.isDebugEnabled()) { 158 logResult("flush: event does not apply to wrapper {" + matcher + "}:", query, event, null); 159 } 160 return true; 161 } 162 } 163 case Event.TYPE_CHANGE: { 164 Map oldValues; 165 Map newValues; 166 MMObjectNode node = MMBase.getMMBase().getBuilder(event.getBuilderName()).getNode(event.getNodeNumber()); 169 if(node != null){ 170 Map nodeValues = node.getValues(); 172 oldValues = new LinkMap(nodeValues, event.getOldValues()); 173 newValues = new LinkMap(nodeValues, event.getNewValues()); 174 } else { 175 oldValues = event.getOldValues(); 176 newValues = event.getNewValues(); 177 } 178 179 if(matcher.eventApplies(newValues, event)){ 182 boolean eventMatches = 183 matcher.nodeMatchesConstraint(oldValues, event) || matcher.nodeMatchesConstraint(newValues, event); 186 195 if (log.isDebugEnabled()) { 196 boolean usedToMatch = matcher.nodeMatchesConstraint(oldValues, event); 197 boolean stillMatches = matcher.nodeMatchesConstraint(newValues, event); 198 log.debug("** match with old values : " + (usedToMatch ? "match" : "no match")); 199 log.debug("** match with new values : " + (stillMatches ? "match" : "no match")); 200 log.debug("**old values: " + oldValues); 201 log.debug("**new values: " + newValues); 202 logResult((eventMatches ? "" : "no ") + "flush: with matcher {" + matcher + "}:", query, event, node); 203 } 204 205 return eventMatches; 206 } else { 207 if (log.isDebugEnabled()) { 208 logResult("flush: event does not apply to wrapper {" + matcher + "}:", query, event, node); 209 } 210 return true; 211 } 212 } 213 case Event.TYPE_DELETE: 214 Map oldValues = event.getOldValues(); 215 if(matcher.eventApplies(event.getOldValues(), event)){ 218 boolean eventMatches = matcher.nodeMatchesConstraint(oldValues, event); 219 if (log.isDebugEnabled()) { 220 logResult( (eventMatches ? "" : "no ") + "flush: with matcher {"+matcher+"}:", query, event, null); 221 } 222 return eventMatches; 223 } else { 224 if (log.isDebugEnabled()) { 225 logResult("flush: event does not apply to wrapper {" + matcher + "}:", query, event, null); 226 } 227 return true; 228 } 229 default: 230 log.error("Unrecognized event-type " + event.getType()); 231 break; 232 } 233 } 234 return true; } 236 237 protected final boolean doEvaluate(RelationEvent event, SearchQuery query, List cachedResult) { 238 return doEvaluate(event.getNodeEvent(), query, cachedResult); 241 } 242 243 249 protected final static AbstractConstraintMatcher findMatcherForConstraint(Constraint constraint) throws InvocationTargetException, NoSuchMethodException , InstantiationException , IllegalAccessException { 250 String constraintClassName = constraint.getClass().getName(); 251 constraintClassName = constraintClassName.substring(constraintClassName.lastIndexOf(".") + 1); 252 253 254 Constructor matcherConstructor = (Constructor) constraintMatcherConstructors.get(constraintClassName + "Matcher"); 257 if (matcherConstructor == null) { 258 log.error("Could not match constraint of type " + constraintClassName); 259 matcherConstructor = UnsupportedConstraintMatcher.class.getConstructors()[0]; 260 } 261 if (log.isDebugEnabled()) { 262 log.debug("finding matcher for constraint class name: " + constraintClassName + "Matcher"); 263 log.trace("matcher class found: " + matcherConstructor.getDeclaringClass().getName()); 264 } 265 266 return (AbstractConstraintMatcher) matcherConstructor.newInstance(new Object [] { constraint }); 267 268 } 269 270 273 public static void main(String [] args) { 274 Logging.getLoggerInstance(ConstraintsMatchingStrategy.class).setLevel(Level.DEBUG); 275 276 Class cl = UnsupportedConstraintMatcher.class; 277 try { 278 Constructor c = cl.getConstructor(new Class [] { Constraint.class }); 279 AbstractConstraintMatcher matcherInstance; 280 } catch (Exception e) { 282 System.out.println(e.toString()); 283 } 284 285 } 286 287 private static abstract class AbstractConstraintMatcher { 288 289 294 abstract public boolean nodeMatchesConstraint(Map valuesToMatch, NodeEvent event); 295 300 abstract public boolean eventApplies(Map valuesToMatch, NodeEvent event); 301 abstract public String toString(); 302 } 303 304 305 306 307 308 309 private static class BasicCompositeConstraintMatcher extends AbstractConstraintMatcher { 310 private final List wrappedConstraints; 311 private final BasicCompositeConstraint wrappedCompositeConstraint; 312 313 public BasicCompositeConstraintMatcher(BasicCompositeConstraint constraint) throws NoSuchMethodException , InstantiationException , InvocationTargetException, IllegalAccessException { 314 wrappedCompositeConstraint = constraint; 315 wrappedConstraints = new ArrayList(); 316 for (Iterator i = wrappedCompositeConstraint.getChilds().iterator(); i.hasNext();) { 317 Constraint c = (Constraint) i.next(); 318 wrappedConstraints.add(findMatcherForConstraint(c)); 319 } 320 } 321 322 public boolean nodeMatchesConstraint(Map valuesToMatch, NodeEvent event) { 323 int matches = 0; 324 for (Iterator i = findRelevantConstraints(valuesToMatch, event).iterator(); i.hasNext();) { 325 AbstractConstraintMatcher acm = (AbstractConstraintMatcher) i.next(); 326 if (log.isDebugEnabled()) { 327 log.debug("** relevant constraint found: " + acm); 328 } 329 if (acm.nodeMatchesConstraint(valuesToMatch, event)){ 330 matches ++; 331 if (log.isDebugEnabled()) { 332 log.debug("** constraint created a match on " + valuesToMatch); 333 } 334 } else if (log.isDebugEnabled()) { 335 log.debug("** constraint created _NO_ match on " + valuesToMatch); 336 } 337 } 338 if (wrappedCompositeConstraint.getLogicalOperator() == BasicCompositeConstraint.LOGICAL_AND) { 339 return (matches == wrappedConstraints.size()) != wrappedCompositeConstraint.isInverse(); 340 } else { 341 return (matches > 0) != wrappedCompositeConstraint.isInverse(); 342 } 343 } 344 345 public String toString(){ 346 StringBuffer sb = new StringBuffer ("Composite Wrapper. type: "); 347 sb.append(wrappedCompositeConstraint.getLogicalOperator() == BasicCompositeConstraint.LOGICAL_AND ? "AND" : "OR"); 348 sb.append(" ["); 349 for (Iterator i = wrappedConstraints.iterator(); i.hasNext();) { 350 sb.append("{"); 351 sb.append(((AbstractConstraintMatcher)i.next()).toString()); 352 if(i.hasNext()) sb.append("} {"); 353 } 354 sb.append("}]"); 355 return sb.toString(); 356 } 357 358 363 public boolean eventApplies(Map valuesToMatch, NodeEvent event) { 364 List relevantConstraints = findRelevantConstraints(valuesToMatch, event); 365 if (log.isDebugEnabled()) { 366 log.debug("** relevant constraints: " + relevantConstraints); 367 } 368 if(wrappedCompositeConstraint.getLogicalOperator() == BasicCompositeConstraint.LOGICAL_AND){ 369 if(wrappedConstraints.size() == relevantConstraints.size()) { 370 log.debug("** composite AND: all constraints match, event applies to query"); 371 return true; 372 } else { 373 log.debug("** composite AND: not all constraints match, so the event does not apply to this constraint"); 374 } 375 } else { 376 if(relevantConstraints.size() > 0){ 377 log.debug("** composite OR: more than zero constraints match, so event applies to query"); 378 return true; 379 }else{ 380 log.debug("** composite OR: zero constraints match, so event does not apply to query."); 381 } 382 383 } 384 return false; 385 } 386 387 388 private List findRelevantConstraints(Map valuesToMatch, NodeEvent event){ 389 List relevantConstraints = new ArrayList(); 390 for (Iterator i = wrappedConstraints.iterator(); i.hasNext();) { 391 AbstractConstraintMatcher matcher = (AbstractConstraintMatcher ) i.next(); 392 if(matcher.eventApplies(valuesToMatch, event))relevantConstraints.add(matcher); 393 } 394 return relevantConstraints; 395 } 396 397 } 398 399 400 401 402 403 private static class UnsupportedConstraintMatcher extends AbstractConstraintMatcher { 404 405 final Constraint wrappedConstraint; 406 public UnsupportedConstraintMatcher(Constraint constraint) { 407 wrappedConstraint = constraint; 408 } 409 410 413 public boolean nodeMatchesConstraint(Map valuesToMatch, NodeEvent event) { 414 return true; 415 } 416 417 public String toString(){ 418 return "Unsupported Matcher. masking for constraint: " + wrappedConstraint.getClass().getName(); 419 } 420 421 public boolean eventApplies(Map valuesToMatch, NodeEvent event) { 422 return true; 423 } 424 } 425 426 427 private static class BasicLegacyConstraintMatcher extends UnsupportedConstraintMatcher { 428 public BasicLegacyConstraintMatcher(Constraint constraint) { 429 super(constraint); 430 } 431 } 432 433 434 435 436 442 private static abstract class FieldCompareConstraintMatcher extends AbstractConstraintMatcher { 443 444 protected abstract int getOperator(); 445 446 protected boolean valueMatches(final Class fieldType, Object constraintValue, Object valueToCompare, final boolean isCaseSensitive) { 447 if (log.isDebugEnabled()) { 448 log.debug("**method: valueMatches() fieldtype: " + fieldType); 449 } 450 if (constraintValue == null) return valueToCompare == null; 451 452 int operator = getOperator(); 453 454 if (fieldType.equals(Boolean .class)) { 455 boolean constraintBoolean = Casting.toBoolean(constraintValue); 456 boolean booleanToCompare = Casting.toBoolean(valueToCompare); 457 switch(operator) { 458 case FieldCompareConstraint.EQUAL: return booleanToCompare == constraintBoolean; 459 case FieldCompareConstraint.NOT_EQUAL: return booleanToCompare != constraintBoolean; 460 default: 461 log.warn("operator " + FieldCompareConstraint.OPERATOR_DESCRIPTIONS[operator] + "is not supported for type Boolean"); 462 return true; 463 } 464 } else if (fieldType.equals(Float .class)) { 465 float constraintFloat = Casting.toFloat(constraintValue, Float.MAX_VALUE); 466 float floatToCompare = Casting.toFloat(valueToCompare, Float.MAX_VALUE); 467 if(constraintFloat == Float.MAX_VALUE || floatToCompare == Float.MAX_VALUE) { 469 log.warn("either " + constraintValue + " or " + valueToCompare + " could not be cast to type float (while that is supposed to be their type)"); 470 return true; 471 } 472 return floatMatches(constraintFloat, floatToCompare, operator); 473 } else if (fieldType.equals(Double .class)) { 474 double constraintDouble = Casting.toDouble(constraintValue, Double.MAX_VALUE); 475 double doubleToCompare = Casting.toDouble(valueToCompare, Double.MAX_VALUE); 476 if(constraintDouble == Double.MAX_VALUE || doubleToCompare == Double.MAX_VALUE) { 478 log.warn("either " + constraintValue + " or " + valueToCompare + " could not be cast to type double (while that is supposed to be their type)"); 479 return true; 480 } 481 return floatMatches(constraintDouble, doubleToCompare, operator); 482 } else if (fieldType.equals(Date.class)) { 483 long constraintLong = Casting.toLong(constraintValue, Long.MAX_VALUE); 484 long longToCompare = Casting.toLong(valueToCompare, Long.MAX_VALUE); 485 486 if(constraintLong == Long.MAX_VALUE || longToCompare == Long.MAX_VALUE) { 488 log.warn("either " + constraintValue + " or " + valueToCompare + " could not be cast to type long (while they are supposed to be of type Date supposed to be their type)"); 489 return true; 490 } 491 return intMatches(constraintLong, longToCompare, operator); 492 } else if (fieldType.equals(Integer .class)) { 493 int constraintInt = Casting.toInt(constraintValue, Integer.MAX_VALUE); 494 int intToCompare = Casting.toInt(valueToCompare, Integer.MAX_VALUE); 495 496 if(constraintInt == Integer.MAX_VALUE || intToCompare == Integer.MAX_VALUE) { 498 log.warn("either " + constraintValue + " or " + valueToCompare + " could not be cast to type int (while that is supposed to be their type)"); 499 return true; 500 } 501 return intMatches(constraintInt, intToCompare, operator); 502 } else if (fieldType.equals(Long .class)) { 503 long constraintLong = Casting.toLong(constraintValue, Long.MAX_VALUE); 504 long longToCompare = Casting.toLong(valueToCompare, Long.MAX_VALUE); 505 if(constraintLong == Long.MAX_VALUE || longToCompare == Long.MAX_VALUE) { 507 log.warn("either [" + constraintValue +"] " + (constraintValue == null ? "": "of type " + constraintValue.getClass().getName()) + 509 " or [" + valueToCompare + "] of type " + (valueToCompare == null ? "": "of type " + valueToCompare.getClass().getName()) + 510 " could not be cast to type long (while that is supposed to be their type)"); 511 return true; 512 513 } 514 return intMatches(constraintLong, longToCompare, operator); 515 } else if (fieldType.equals(Node.class)) { 516 if(constraintValue instanceof MMObjectNode) constraintValue = new Integer (((MMObjectNode)constraintValue).getNumber()); 517 if(valueToCompare instanceof MMObjectNode) valueToCompare = new Integer (((MMObjectNode)valueToCompare).getNumber()); 518 int constraintInt = Casting.toInt(constraintValue, Integer.MAX_VALUE); 519 int intToCompare = Casting.toInt(valueToCompare, Integer.MAX_VALUE); 520 if(constraintInt == Integer.MAX_VALUE || intToCompare == Integer.MAX_VALUE) { 522 log.warn("either [" + constraintValue +"] " + (constraintValue == null ? "": "of type " + constraintValue.getClass().getName()) + 523 " or [" + valueToCompare + "] of type " + (valueToCompare == null ? "": "of type " + valueToCompare.getClass().getName()) + 524 " could not be cast to type int (while they should be type node)"); 525 return true; 526 527 } 528 return intMatches(constraintInt, intToCompare, operator); 529 } else if (fieldType.equals(String .class) || fieldType.equals(org.w3c.dom.Document .class)) { 530 String constraintString = Casting.toString(constraintValue); 531 String stringToCompare = Casting.toString(valueToCompare); 532 return stringMatches(constraintString, stringToCompare, operator, isCaseSensitive); 533 } 534 535 return false; 536 } 537 538 private boolean floatMatches(double constraintDouble, double doubleTocompare, int operator) { 539 switch(operator) { 540 case FieldCompareConstraint.EQUAL: return doubleTocompare == constraintDouble; 541 case FieldCompareConstraint.GREATER: return doubleTocompare > constraintDouble; 542 case FieldCompareConstraint.GREATER_EQUAL: return doubleTocompare >= constraintDouble; 543 case FieldCompareConstraint.LESS: return doubleTocompare < constraintDouble; 544 case FieldCompareConstraint.LESS_EQUAL: return doubleTocompare <= constraintDouble; 545 case FieldCompareConstraint.NOT_EQUAL: return doubleTocompare != constraintDouble; 546 default: 547 log.warn("operator " + FieldCompareConstraint.OPERATOR_DESCRIPTIONS[operator] + "for any numeric type"); 548 return true; 549 550 } 551 } 552 553 private boolean intMatches(long constraintLong, long longToCompare, int operator) { 554 switch(operator) { 555 case FieldCompareConstraint.EQUAL: return longToCompare == constraintLong; 556 case FieldCompareConstraint.GREATER: return longToCompare > constraintLong; 557 case FieldCompareConstraint.GREATER_EQUAL: return longToCompare >= constraintLong; 558 case FieldCompareConstraint.LESS: return longToCompare < constraintLong; 559 case FieldCompareConstraint.LESS_EQUAL: return longToCompare <= constraintLong; 560 case FieldCompareConstraint.NOT_EQUAL: return longToCompare != constraintLong; 561 default: 562 log.warn("operator " + FieldCompareConstraint.OPERATOR_DESCRIPTIONS[operator] + "for any numeric type"); 563 return true; 564 } 565 } 566 567 private boolean stringMatches(String constraintString, String stringToCompare, int operator, boolean isCaseSensitive) { 568 switch(operator) { 569 case FieldCompareConstraint.EQUAL: return stringToCompare.equals(constraintString); 570 case FieldCompareConstraint.GREATER: return stringToCompare.compareTo(constraintString) > 0; 572 case FieldCompareConstraint.LESS: return stringToCompare.compareTo(constraintString) < 0; 573 case FieldCompareConstraint.LESS_EQUAL: return stringToCompare.compareTo(constraintString) <= 0; 574 case FieldCompareConstraint.GREATER_EQUAL: return stringToCompare.compareTo(constraintString) >= 0; 575 case FieldCompareConstraint.LIKE: return likeMatches(constraintString, stringToCompare, isCaseSensitive); 576 case FieldCompareConstraint.NOT_EQUAL: return ! stringToCompare.equals(constraintString); 577 default: 578 log.warn("operator " + FieldCompareConstraint.OPERATOR_DESCRIPTIONS[operator] + "is not supported for type String"); 579 return true; 580 } 581 } 582 583 private boolean likeMatches(String constraintString, String stringToCompare, boolean isCaseSensitive){ 584 if (log.isTraceEnabled()) { 585 log.trace("** method: likeMatches() stringToCompare: " + stringToCompare + ", constraintString: " + constraintString ); 586 } 587 if(isCaseSensitive){ 588 constraintString = constraintString.toLowerCase(); 589 stringToCompare = stringToCompare.toLowerCase(); 590 } 591 char[] chars = constraintString.toCharArray(); 592 StringBuffer sb = new StringBuffer (); 593 594 for(int i = 0; i < chars.length; i++){ 595 if(chars[i] == '?'){ 596 sb.append("."); 597 } else if(chars[i] == '%'){ 598 sb.append(".*"); 599 } else if(escapeChars.indexOf(chars[i]) > -1){ 600 sb.append("\\"); 601 sb.append(chars[i]); 602 } else{ 603 sb.append(chars[i]); 604 } 605 } 606 if (log.isDebugEnabled()) { 607 log.trace("** new pattern: " + sb.toString()); 608 } 609 return stringToCompare.matches(sb.toString()); 610 } 611 612 protected Class getFieldTypeClass(StepField stepField) { 613 MMBase mmbase = MMBase.getMMBase(); 614 CoreField field = mmbase.getBuilder(stepField.getStep().getTableName()).getField(stepField.getFieldName()); 616 DataType fieldType = field.getDataType(); 617 Class fieldTypeClass = fieldType.getTypeAsClass(); 618 if( fieldTypeClass.equals(Boolean .class) || 619 fieldTypeClass.equals(Date.class) || 620 fieldTypeClass.equals(Integer .class) || 621 fieldTypeClass.equals(Long .class) || 622 fieldTypeClass.equals(Float .class) || 623 fieldTypeClass.equals(Double .class) || 624 fieldTypeClass.equals(Node.class) || 625 fieldTypeClass.equals(String .class) || 626 fieldTypeClass.equals(org.w3c.dom.Document .class)) { 627 if (log.isDebugEnabled()) { 628 log.debug("** found field type: " + fieldTypeClass.getName()); 629 } 630 } else { 631 throw new RuntimeException ("Field type " + fieldTypeClass + " is not supported"); 632 } 633 return fieldTypeClass; 634 } 635 636 } 637 638 639 640 private static class BasicFieldValueConstraintMatcher extends FieldCompareConstraintMatcher { 641 private final Class fieldTypeClass; 642 protected final StepField stepField; 643 protected final BasicFieldValueConstraint wrappedFieldValueConstraint; 644 645 public BasicFieldValueConstraintMatcher(BasicFieldValueConstraint constraint) { 646 stepField = constraint.getField(); 647 if (log.isDebugEnabled()) { 648 log.debug("** builder: " + stepField.getStep().getTableName()+". field: " + stepField.getFieldName()); 649 } 650 fieldTypeClass = getFieldTypeClass(stepField); 651 wrappedFieldValueConstraint = constraint; 652 653 } 654 655 protected int getOperator() { 656 return wrappedFieldValueConstraint.getOperator(); 657 } 658 661 public boolean nodeMatchesConstraint(Map valuesToMatch, NodeEvent event) { 662 log.debug("**method: nodeMatchesConstraint"); 663 boolean matches = valueMatches(fieldTypeClass, 665 wrappedFieldValueConstraint.getValue(), 666 valuesToMatch.get(stepField.getFieldName()), 667 wrappedFieldValueConstraint.isCaseSensitive()); 668 return matches != wrappedFieldValueConstraint.isInverse(); 669 } 670 671 public String toString(){ 672 return "Field Value Matcher. operator: " + FieldCompareConstraint.OPERATOR_DESCRIPTIONS[wrappedFieldValueConstraint.getOperator()] + 673 ", value: " + wrappedFieldValueConstraint.getValue().toString() + ", step: " +stepField.getStep().getTableName() + 674 ", field name: " + stepField.getFieldName(); 675 } 676 677 678 682 public boolean eventApplies(Map valuesToMatch, NodeEvent event) { 683 return 684 wrappedFieldValueConstraint.getField().getStep().getTableName().equals(event.getBuilderName()) && 685 valuesToMatch.containsKey(wrappedFieldValueConstraint.getField().getFieldName()); 686 } 687 688 } 689 690 691 692 693 696 private static class BasicFieldValueBetweenConstraintMatcher extends AbstractConstraintMatcher { 697 698 protected final StepField stepField; 699 protected final BasicFieldValueBetweenConstraint wrappedFieldConstraint; 700 701 public BasicFieldValueBetweenConstraintMatcher(BasicFieldValueBetweenConstraint constraint) { 702 stepField = constraint.getField(); 703 wrappedFieldConstraint = constraint; 704 } 705 706 public boolean nodeMatchesConstraint(Map valuesToMatch, NodeEvent event) { 707 return true; 708 } 709 710 public boolean eventApplies(Map valuesToMatch, NodeEvent event) { 711 return true; 712 } 713 714 public String toString() { 715 return "Field Value Between Matcher. operator: " + 716 ", step: " +stepField.getStep().getTableName() + 717 ", field name: " + stepField.getFieldName(); 718 } 719 720 } 721 722 725 private static class BasicFieldValueInConstraintMatcher extends FieldCompareConstraintMatcher { 726 private final Class fieldTypeClass; 727 protected final StepField stepField; 728 protected final BasicFieldValueInConstraint wrappedFieldValueInConstraint; 729 730 public BasicFieldValueInConstraintMatcher(BasicFieldValueInConstraint constraint) { 731 stepField = constraint.getField(); 732 fieldTypeClass = getFieldTypeClass(stepField); 733 wrappedFieldValueInConstraint = constraint; 734 } 735 736 protected int getOperator() { 737 return FieldCompareConstraint.EQUAL; 738 } 739 740 743 public boolean nodeMatchesConstraint(Map valuesToMatch, NodeEvent event) { 744 log.debug("**method: nodeMatchesConstraint"); 745 SortedSet values = wrappedFieldValueInConstraint.getValues(); 746 boolean matches = false; 747 Iterator i = values.iterator(); 748 while (i.hasNext() && !matches) { 749 Object value = i.next(); 750 matches = valueMatches(fieldTypeClass, 751 value, 752 valuesToMatch.get(stepField.getFieldName()), 753 wrappedFieldValueInConstraint.isCaseSensitive()); 754 } 755 return matches != wrappedFieldValueInConstraint.isInverse(); 756 } 757 758 public String toString(){ 759 return "Field Value IN Matcher. operator: " + 760 ", value: " + wrappedFieldValueInConstraint.getValues().toString() + ", step: " +stepField.getStep().getTableName() + 761 ", field name: " + stepField.getFieldName(); 762 } 763 764 765 766 public boolean eventApplies(Map valuesToMatch, NodeEvent event) { 767 return 768 wrappedFieldValueInConstraint.getField().getStep().getTableName().equals(event.getBuilderName()) && 769 valuesToMatch.containsKey(wrappedFieldValueInConstraint.getField().getFieldName()); 770 } 771 } 772 773 774 private void logResult(String comment, SearchQuery query, Event event, MMObjectNode node){ 775 if(log.isDebugEnabled()){ 776 String role=""; 777 if (event instanceof RelationEvent) { 779 RelationEvent revent = (RelationEvent) event; 781 MMObjectNode relDef = MMBase.getMMBase().getBuilder("reldef").getNode(revent.getRole()); 782 role = " role: " + relDef.getStringValue("sname") + "/" + relDef.getStringValue("dname"); 783 if (revent.getRelationSourceType().equals("object") 785 || revent.getRelationDestinationType().equals("object")) 786 return; 787 } 788 try { 789 log.debug("\n******** \n**" + comment + "\n**" + event.toString() + role + "\n**nodevalues: " + (node == null ? "NODE NULL" : "" + node.getValues()) + "\n**" 790 + sqlHandler.toSql(query, sqlHandler) + "\n******"); 791 } catch (SearchQueryException e) { 792 log.error(e); 793 } 794 } 795 } 796 797 801 private static class LinkMap extends AbstractMap { 802 private final Map map1; 803 private final Map map2; 804 LinkMap(Map m1, Map m2) { 805 map1 = m1; map2 = m2; 806 } 807 public Set entrySet() { 808 return new AbstractSet() { 809 public Iterator iterator() { 810 final Iterator i = map1.entrySet().iterator(); 811 return new Iterator() { 812 public boolean hasNext() { 813 return i.hasNext(); 814 } 815 public Object next() { 816 final Map.Entry entry1 = (Map.Entry) i.next(); 817 final Object key = entry1.getKey(); 818 return new Map.Entry() { 819 public Object getKey() { 820 return key; 821 } 822 public Object getValue() { 823 if (map2.containsKey(key)) { 824 return map2.get(key); 825 } else { 826 return entry1.getValue(); 827 } 828 } 829 public Object setValue(Object v) { 830 throw new UnsupportedOperationException (); 831 } 832 }; 833 } 834 public void remove() { 835 throw new UnsupportedOperationException (); 836 } 837 }; 838 } 839 public int size() { 840 return map1.size(); 841 } 842 }; 843 } 844 public int size() { 845 return map1.size(); 846 } 847 public Object get(Object key) { 848 if (map2.containsKey(key)) { 849 return map2.get(key); 850 } else { 851 return map1.get(key); 852 } 853 } 854 public boolean containsKey(Object key) { 855 return map1.containsKey(key); 856 } 857 } 858 859 } 860 | Popular Tags |