Source Code Cross Referenced for ConstraintsMatchingStrategy.java in  » Database-ORM » MMBase » org » mmbase » cache » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Database ORM » MMBase » org.mmbase.cache 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:
0003:         This software is OSI Certified Open Source Software.
0004:         OSI Certified is a certification mark of the Open Source Initiative.
0005:
0006:         The license (Mozilla version 1.0) can be read at the MMBase site.
0007:         See http://www.MMBase.org/license
0008:
0009:         */
0010:        package org.mmbase.cache;
0011:
0012:        import java.lang.reflect.*;
0013:        import java.util.*;
0014:
0015:        import org.mmbase.bridge.Node;
0016:        import org.mmbase.bridge.implementation.BasicQuery;
0017:        import org.mmbase.core.CoreField;
0018:        import org.mmbase.core.event.*;
0019:        import org.mmbase.datatypes.DataType;
0020:        import org.mmbase.module.core.*;
0021:        import org.mmbase.util.LinkMap;
0022:        import org.mmbase.storage.search.*;
0023:        import org.mmbase.storage.search.implementation.*;
0024:        import org.mmbase.storage.search.implementation.database.BasicSqlHandler;
0025:        import org.mmbase.util.Casting;
0026:        import org.mmbase.util.logging.*;
0027:
0028:        /**
0029:         * This strategy will evaluate the constraint on a a query object against a NodeEvent. It will apply the following rules:<br>
0030:         * <b>new node/delete node</b><br>
0031:         * <ul>
0032:         * <li>If the step of a constraint matches the type of the event, and the values of the node don't fall within the
0033:         * constraint: don't flush.</li>
0034:         * <li>If the step of a constraint matches the type of the event, and the values of the node don't fall within the
0035:         * constraint: flush.</li>
0036:         * <li>If no constraints have a step matching the type of the event: flush.
0037:         * </ul>
0038:         * <b>change node</b> Like the above, but an extra check has to be made:
0039:         * <ul>
0040:         * <li>if the node previously fell within the constraints but now doesn't: flush</li>
0041:         * <li>if the node previously didn't fall within the constraints but now does: flush</li>
0042:         * </ul>
0043:         *
0044:         * @author Ernst Bunders
0045:         * @since MMBase-1.8
0046:         * @version $Id: ConstraintsMatchingStrategy.java,v 1.36 2008/02/03 17:33:56 nklasens Exp $
0047:         *
0048:         */
0049:        public class ConstraintsMatchingStrategy extends ReleaseStrategy {
0050:
0051:            private static final Logger log = Logging
0052:                    .getLoggerInstance(ConstraintsMatchingStrategy.class);
0053:            private static final BasicSqlHandler sqlHandler = new BasicSqlHandler();
0054:            private final static String escapeChars = ".\\?+*$()[]{}^|&";
0055:
0056:            /**
0057:             * This field contains the characters that are being escaped for the 'like' comparison of strings,
0058:             * where, the string that should match the other is converted to a regexp
0059:             **/
0060:            private static final Cache<SearchQuery, AbstractConstraintMatcher> constraintWrapperCache;
0061:
0062:            static {
0063:                constraintWrapperCache = new Cache<SearchQuery, AbstractConstraintMatcher>(
0064:                        1000) {
0065:                    public String getName() {
0066:                        return "ConstraintMatcherCache";
0067:                    }
0068:
0069:                    public String getDescription() {
0070:                        return "Caches query constraint wrappers used by ConstraintsMatchingStrategy";
0071:                    }
0072:                };
0073:                CacheManager.putCache(constraintWrapperCache);
0074:            }
0075:
0076:            private static final Map<String, Constructor> constraintMatcherConstructors = new HashMap<String, Constructor>();
0077:            static {
0078:                Class[] innerClasses = ConstraintsMatchingStrategy.class
0079:                        .getDeclaredClasses();
0080:                for (Class innerClass : innerClasses) {
0081:                    if (innerClass.getName().endsWith("Matcher")
0082:                            && !Modifier.isAbstract(innerClass.getModifiers())) {
0083:                        String matcherClassName = innerClass.getName();
0084:                        matcherClassName = matcherClassName
0085:                                .substring(matcherClassName.lastIndexOf("$") + 1);
0086:                        Constructor con = null;
0087:                        Constructor[] cons = innerClass.getConstructors();
0088:                        for (Constructor element0 : cons) {
0089:                            Class[] params = element0.getParameterTypes();
0090:                            if (params.length == 1
0091:                                    && Constraint.class
0092:                                            .isAssignableFrom(params[0])) {
0093:                                con = element0;
0094:                                break;
0095:                            }
0096:                        }
0097:                        if (con == null) {
0098:                            log.error("Class " + innerClass
0099:                                    + " has no appropriate constructor");
0100:                            continue;
0101:                        }
0102:
0103:                        constraintMatcherConstructors
0104:                                .put(matcherClassName, con);
0105:                        log.debug("** found matcher: " + matcherClassName);
0106:                    }
0107:                }
0108:            }
0109:
0110:            public ConstraintsMatchingStrategy() {
0111:                super ();
0112:            }
0113:
0114:            public String getName() {
0115:                return "Constraint matching strategy";
0116:            }
0117:
0118:            public String getDescription() {
0119:                return "Checks wether a changed node has a matching step within the constraints of a query, and then checks "
0120:                        + "if the node falls within the constraint. For changed nodes a check is made if the node previously "
0121:                        + "fell within the constraint and if it does so now. Queries that exclude changed nodes by their constraints "
0122:                        + "will not be flushed.";
0123:            }
0124:
0125:            protected final boolean doEvaluate(NodeEvent event,
0126:                    SearchQuery query, List<MMObjectNode> cachedResult) {
0127:                //no constraint, we release any way
0128:                Constraint constraint = query.getConstraint();
0129:                if (constraint == null)
0130:                    return true; //should release
0131:
0132:                //try to get a wrapper from the cache
0133:                AbstractConstraintMatcher matcher = constraintWrapperCache
0134:                        .get(query);
0135:
0136:                //if not found, try to create one
0137:                if (matcher == null) {
0138:                    try {
0139:                        matcher = findMatcherForConstraint(constraint);
0140:                        if (log.isTraceEnabled()) {
0141:                            log.trace("created constraint matcher: " + matcher);
0142:                        }
0143:                        // Unwrapping BasicQuery's. This avoids unnecessary references (mainly to BasicCloud instances).
0144:                        if (query instanceof  BasicQuery) {
0145:                            constraintWrapperCache.put(((BasicQuery) query)
0146:                                    .getQuery(), matcher);
0147:                        } else {
0148:                            constraintWrapperCache.put(query, matcher);
0149:                        }
0150:                        //if anything goes wrong constraintMatches is true, which means the query should be flushed
0151:                    } catch (Exception e) {
0152:                        log.error(
0153:                                "Could not create constraint matcher for constraint: "
0154:                                        + constraint + "main reason: " + e, e);
0155:                    }
0156:                } else {
0157:                    if (log.isTraceEnabled()) {
0158:                        log.trace("found matcher for query in cache. query: "
0159:                                + query);
0160:                    }
0161:                }
0162:
0163:                //we should have a matcher now
0164:                if (matcher != null) {
0165:                    switch (event.getType()) {
0166:                    case Event.TYPE_NEW: {
0167:                        Map<String, Object> newValues = event.getNewValues();
0168:                        // we have to compare the constraint value with the new value of the changed field to see if the new
0169:                        // node falls within the constraint. it it does: flush
0170:                        if (matcher.eventApplies(newValues, event)) {
0171:                            boolean eventMatches = matcher
0172:                                    .nodeMatchesConstraint(newValues, event);
0173:                            if (log.isDebugEnabled()) {
0174:                                logResult((eventMatches ? "" : "no ")
0175:                                        + "flush: with matcher {" + matcher
0176:                                        + "}:", query, event, null);
0177:                            }
0178:                            return eventMatches;
0179:                        } else {
0180:                            if (log.isDebugEnabled()) {
0181:                                logResult(
0182:                                        "flush: event does not apply to wrapper {"
0183:                                                + matcher + "}:", query, event,
0184:                                        null);
0185:                            }
0186:                            return true;
0187:                        }
0188:                    }
0189:                    case Event.TYPE_CHANGE: {
0190:                        Map<String, Object> oldValues;
0191:                        Map<String, Object> newValues;
0192:                        //because composite constraints can also cover fields that are not in the changed field list of the node
0193:                        //let's find the node and get all the values.
0194:                        MMObjectNode node = MMBase.getMMBase().getBuilder(
0195:                                event.getBuilderName()).getNode(
0196:                                event.getNodeNumber());
0197:                        if (node != null) {
0198:                            //put all the (new) values in the value maps
0199:                            Map<String, Object> nodeValues = node.getValues();
0200:                            oldValues = new LinkMap<String, Object>(nodeValues,
0201:                                    event.getOldValues());
0202:                            newValues = new LinkMap<String, Object>(nodeValues,
0203:                                    event.getNewValues());
0204:                        } else {
0205:                            oldValues = event.getOldValues();
0206:                            newValues = event.getNewValues();
0207:                        }
0208:
0209:                        // we have to compare the old value and then the new value of the changed field to see if the status
0210:                        // has changed. if the node used to match the constraint but now doesn't or the reverse of this, flush.
0211:                        if (matcher.eventApplies(newValues, event)) {
0212:                            boolean eventMatches = matcher
0213:                                    .nodeMatchesConstraint(oldValues, event)
0214:                                    || // used to match
0215:                                    matcher.nodeMatchesConstraint(newValues,
0216:                                            event); // still matches
0217:
0218:                            // It may be important to check whether the changed fields of the node are
0219:                            // present in the field-list of the query. If they are not, and usedToMatch
0220:                            // && stillMaches, then we can still return false, if at least there is no
0221:                            // sort-order on a changed field, because even if the field itself is not in
0222:                            // the result, and it matches the constraint before and after the event, it
0223:                            // can still change the order of the result then.
0224:                            // If we can garantuee that field-values alway come from the node-cache (which we cannot, at the moment),
0225:                            // then things may become a bit different.
0226:
0227:                            if (log.isDebugEnabled()) {
0228:                                boolean usedToMatch = matcher
0229:                                        .nodeMatchesConstraint(oldValues, event);
0230:                                boolean stillMatches = matcher
0231:                                        .nodeMatchesConstraint(newValues, event);
0232:                                log.debug("** match with old values : "
0233:                                        + (usedToMatch ? "match" : "no match"));
0234:                                log
0235:                                        .debug("** match with new values : "
0236:                                                + (stillMatches ? "match"
0237:                                                        : "no match"));
0238:                                log.debug("**old values: " + oldValues);
0239:                                log.debug("**new values: " + newValues);
0240:                                logResult((eventMatches ? "" : "no ")
0241:                                        + "flush: with matcher {" + matcher
0242:                                        + "}:", query, event, node);
0243:                            }
0244:
0245:                            return eventMatches;
0246:                        } else {
0247:                            if (log.isDebugEnabled()) {
0248:                                logResult(
0249:                                        "flush: event does not apply to wrapper {"
0250:                                                + matcher + "}:", query, event,
0251:                                        node);
0252:                            }
0253:                            return true;
0254:                        }
0255:                    }
0256:                    case Event.TYPE_DELETE:
0257:                        Map<String, Object> oldValues = event.getOldValues();
0258:                        // we have to compare the old value of the field to see if the node used to fall within the
0259:                        // constraint. If it did: flush
0260:                        if (matcher.eventApplies(event.getOldValues(), event)) {
0261:                            boolean eventMatches = matcher
0262:                                    .nodeMatchesConstraint(oldValues, event);
0263:                            if (log.isDebugEnabled()) {
0264:                                logResult((eventMatches ? "" : "no ")
0265:                                        + "flush: with matcher {" + matcher
0266:                                        + "}:", query, event, null);
0267:                            }
0268:                            return eventMatches;
0269:                        } else {
0270:                            if (log.isDebugEnabled()) {
0271:                                logResult(
0272:                                        "flush: event does not apply to wrapper {"
0273:                                                + matcher + "}:", query, event,
0274:                                        null);
0275:                            }
0276:                            return true;
0277:                        }
0278:                    default:
0279:                        log.error("Unrecognized event-type " + event.getType());
0280:                        break;
0281:                    }
0282:                }
0283:                return true; //safe: should release
0284:            }
0285:
0286:            protected final boolean doEvaluate(RelationEvent event,
0287:                    SearchQuery query, List<MMObjectNode> cachedResult) {
0288:                // TODO I don't think this strategy should handle these events
0289:                //because the node event that preceeds the relation event takes care of it.
0290:                return doEvaluate(event.getNodeEvent(), query, cachedResult);
0291:            }
0292:
0293:            /**
0294:             * This method will find a constraint matcher that supports the given constraint, and will return the
0295:             * UnsupportedConstraintMatcher if none is found.
0296:             *
0297:             * @param constraint
0298:             */
0299:            protected final static AbstractConstraintMatcher findMatcherForConstraint(
0300:                    Constraint constraint) throws InvocationTargetException,
0301:                    NoSuchMethodException, InstantiationException,
0302:                    IllegalAccessException {
0303:                String constraintClassName = constraint.getClass().getName();
0304:                constraintClassName = constraintClassName
0305:                        .substring(constraintClassName.lastIndexOf(".") + 1);
0306:
0307:                // MM: I think the idea behind this is questionable.
0308:                // How expensive is it?
0309:                Constructor matcherConstructor = constraintMatcherConstructors
0310:                        .get(constraintClassName + "Matcher");
0311:                if (matcherConstructor == null) {
0312:                    log.error("Could not match constraint of type "
0313:                            + constraintClassName);
0314:                    matcherConstructor = UnsupportedConstraintMatcher.class
0315:                            .getConstructors()[0];
0316:                }
0317:                if (log.isDebugEnabled()) {
0318:                    log.debug("finding matcher for constraint class name: "
0319:                            + constraintClassName + "Matcher");
0320:                    log.trace("matcher class found: "
0321:                            + matcherConstructor.getDeclaringClass().getName());
0322:                }
0323:
0324:                return (AbstractConstraintMatcher) matcherConstructor
0325:                        .newInstance(new Object[] { constraint });
0326:
0327:            }
0328:
0329:            private static abstract class AbstractConstraintMatcher {
0330:
0331:                /**
0332:                 * @param valuesToMatch the field values that the constraint value will have to be matched against.
0333:                 * this will sometimes be the 'oldValues' and sometimes be the 'newValues' from the event.
0334:                 * @return true if the values of event falls within the limits of the constraint
0335:                 */
0336:                abstract public boolean nodeMatchesConstraint(
0337:                        Map<String, Object> valuesToMatch, NodeEvent event);
0338:
0339:                /**
0340:                 * @param valuesToMatch map of (changed) fields with their values
0341:                 * @param event the event that has occured
0342:                 * @return true if the wrapped constraint matches the node event
0343:                 */
0344:                abstract public boolean eventApplies(
0345:                        Map<String, Object> valuesToMatch, NodeEvent event);
0346:
0347:                abstract public String toString();
0348:            }
0349:
0350:            private static class BasicCompositeConstraintMatcher extends
0351:                    AbstractConstraintMatcher {
0352:                private final List<AbstractConstraintMatcher> wrappedConstraints;
0353:                private final BasicCompositeConstraint wrappedCompositeConstraint;
0354:
0355:                public BasicCompositeConstraintMatcher(
0356:                        BasicCompositeConstraint constraint)
0357:                        throws NoSuchMethodException, InstantiationException,
0358:                        InvocationTargetException, IllegalAccessException {
0359:                    wrappedCompositeConstraint = constraint;
0360:                    wrappedConstraints = new ArrayList<AbstractConstraintMatcher>();
0361:                    for (Constraint c : wrappedCompositeConstraint.getChilds()) {
0362:                        wrappedConstraints.add(findMatcherForConstraint(c));
0363:                    }
0364:                }
0365:
0366:                public boolean nodeMatchesConstraint(
0367:                        Map<String, Object> valuesToMatch, NodeEvent event) {
0368:                    int matches = 0;
0369:                    for (AbstractConstraintMatcher acm : findRelevantConstraints(
0370:                            valuesToMatch, event)) {
0371:                        if (log.isDebugEnabled()) {
0372:                            log.debug("** relevant constraint found: " + acm);
0373:                        }
0374:                        if (acm.nodeMatchesConstraint(valuesToMatch, event)) {
0375:                            matches++;
0376:                            if (log.isDebugEnabled()) {
0377:                                log.debug("** constraint created a match on "
0378:                                        + valuesToMatch);
0379:                            }
0380:                        } else if (log.isDebugEnabled()) {
0381:                            log.debug("** constraint created _NO_ match on "
0382:                                    + valuesToMatch);
0383:                        }
0384:                    }
0385:                    if (wrappedCompositeConstraint.getLogicalOperator() == CompositeConstraint.LOGICAL_AND) {
0386:                        return (matches == wrappedConstraints.size()) != wrappedCompositeConstraint
0387:                                .isInverse();
0388:                    } else {
0389:                        return (matches > 0) != wrappedCompositeConstraint
0390:                                .isInverse();
0391:                    }
0392:                }
0393:
0394:                public String toString() {
0395:                    StringBuffer sb = new StringBuffer(
0396:                            "Composite Wrapper. type: ");
0397:                    sb
0398:                            .append(wrappedCompositeConstraint
0399:                                    .getLogicalOperator() == CompositeConstraint.LOGICAL_AND ? "AND"
0400:                                    : "OR");
0401:                    sb.append(" [");
0402:                    for (Iterator<AbstractConstraintMatcher> i = wrappedConstraints
0403:                            .iterator(); i.hasNext();) {
0404:                        sb.append("{");
0405:                        sb.append(i.next().toString());
0406:                        if (i.hasNext())
0407:                            sb.append("} {");
0408:                    }
0409:                    sb.append("}]");
0410:                    return sb.toString();
0411:                }
0412:
0413:                /**
0414:                 * for composite constraint wrappers the rule is that if the operator is AND and all
0415:                 * of it's constraints are relevant it is relevant, and if the opreator is OR and one or more of it's
0416:                 * constraints are relevant it is relevant
0417:                 */
0418:                public boolean eventApplies(Map<String, Object> valuesToMatch,
0419:                        NodeEvent event) {
0420:                    List<AbstractConstraintMatcher> relevantConstraints = findRelevantConstraints(
0421:                            valuesToMatch, event);
0422:                    if (log.isDebugEnabled()) {
0423:                        log.debug("** relevant constraints:  "
0424:                                + relevantConstraints);
0425:                    }
0426:                    if (wrappedCompositeConstraint.getLogicalOperator() == CompositeConstraint.LOGICAL_AND) {
0427:                        if (wrappedConstraints.size() == relevantConstraints
0428:                                .size()) {
0429:                            log
0430:                                    .debug("** composite AND: all constraints match, event applies to query");
0431:                            return true;
0432:                        } else {
0433:                            log
0434:                                    .debug("** composite AND: not all constraints match, so the event does not apply to this constraint");
0435:                        }
0436:                    } else {
0437:                        if (relevantConstraints.size() > 0) {
0438:                            log
0439:                                    .debug("** composite OR: more than zero constraints match, so event applies to query");
0440:                            return true;
0441:                        } else {
0442:                            log
0443:                                    .debug("** composite OR: zero constraints match, so event does not apply to query.");
0444:                        }
0445:
0446:                    }
0447:                    return false;
0448:                }
0449:
0450:                private List<AbstractConstraintMatcher> findRelevantConstraints(
0451:                        Map<String, Object> valuesToMatch, NodeEvent event) {
0452:                    List<AbstractConstraintMatcher> relevantConstraints = new ArrayList<AbstractConstraintMatcher>();
0453:                    for (AbstractConstraintMatcher matcher : wrappedConstraints) {
0454:                        if (matcher.eventApplies(valuesToMatch, event))
0455:                            relevantConstraints.add(matcher);
0456:                    }
0457:                    return relevantConstraints;
0458:                }
0459:
0460:            }
0461:
0462:            private static class UnsupportedConstraintMatcher extends
0463:                    AbstractConstraintMatcher {
0464:
0465:                final Constraint wrappedConstraint;
0466:
0467:                public UnsupportedConstraintMatcher(Constraint constraint) {
0468:                    wrappedConstraint = constraint;
0469:                }
0470:
0471:                /**
0472:                 * Return true here, to make sure the query gets flushed.
0473:                 */
0474:                public boolean nodeMatchesConstraint(
0475:                        Map<String, Object> valuesToMatch, NodeEvent event) {
0476:                    return true;
0477:                }
0478:
0479:                public String toString() {
0480:                    return "Unsupported Matcher. masking for constraint: "
0481:                            + wrappedConstraint.getClass().getName();
0482:                }
0483:
0484:                public boolean eventApplies(Map<String, Object> valuesToMatch,
0485:                        NodeEvent event) {
0486:                    return true;
0487:                }
0488:            }
0489:
0490:            private static class BasicLegacyConstraintMatcher extends
0491:                    UnsupportedConstraintMatcher {
0492:                public BasicLegacyConstraintMatcher(Constraint constraint) {
0493:                    super (constraint);
0494:                }
0495:            }
0496:
0497:            /**
0498:             * This class is a base for the field comparison constraints. it provides the means to perform all supported
0499:             * comparisons on all supported data types.
0500:             *
0501:             * @author ebunders
0502:             */
0503:            private static abstract class FieldCompareConstraintMatcher extends
0504:                    AbstractConstraintMatcher {
0505:
0506:                protected abstract int getOperator();
0507:
0508:                protected boolean valueMatches(final Class<Object> fieldType,
0509:                        Object constraintValue, Object valueToCompare,
0510:                        final boolean isCaseSensitive) {
0511:                    if (log.isDebugEnabled()) {
0512:                        log.debug("**method: valueMatches() fieldtype: "
0513:                                + fieldType);
0514:                    }
0515:                    if (constraintValue == null)
0516:                        return valueToCompare == null;
0517:
0518:                    int operator = getOperator();
0519:
0520:                    if (fieldType.equals(Boolean.class)) {
0521:                        boolean constraintBoolean = Casting
0522:                                .toBoolean(constraintValue);
0523:                        boolean booleanToCompare = Casting
0524:                                .toBoolean(valueToCompare);
0525:                        switch (operator) {
0526:                        case FieldCompareConstraint.EQUAL:
0527:                            return booleanToCompare == constraintBoolean;
0528:                        case FieldCompareConstraint.NOT_EQUAL:
0529:                            return booleanToCompare != constraintBoolean;
0530:                        default:
0531:                            log
0532:                                    .warn("operator "
0533:                                            + FieldCompareConstraint.OPERATOR_DESCRIPTIONS[operator]
0534:                                            + "is not supported for type Boolean");
0535:                            return true;
0536:                        }
0537:                    } else if (fieldType.equals(Float.class)) {
0538:                        float constraintFloat = Casting.toFloat(
0539:                                constraintValue, Float.MAX_VALUE);
0540:                        float floatToCompare = Casting.toFloat(valueToCompare,
0541:                                Float.MAX_VALUE);
0542:                        //if either value could not be cast to an int, return true, which is safe
0543:                        if (constraintFloat == Float.MAX_VALUE
0544:                                || floatToCompare == Float.MAX_VALUE) {
0545:                            log
0546:                                    .warn("either "
0547:                                            + constraintValue
0548:                                            + " or "
0549:                                            + valueToCompare
0550:                                            + " could not be cast to type float (while that is supposed to be their type)");
0551:                            return true;
0552:                        }
0553:                        return floatMatches(constraintFloat, floatToCompare,
0554:                                operator);
0555:                    } else if (fieldType.equals(Double.class)) {
0556:                        double constraintDouble = Casting.toDouble(
0557:                                constraintValue, Double.MAX_VALUE);
0558:                        double doubleToCompare = Casting.toDouble(
0559:                                valueToCompare, Double.MAX_VALUE);
0560:                        //if either value could not be cast to an int, return true, which is safe
0561:                        if (constraintDouble == Double.MAX_VALUE
0562:                                || doubleToCompare == Double.MAX_VALUE) {
0563:                            log
0564:                                    .warn("either "
0565:                                            + constraintValue
0566:                                            + " or "
0567:                                            + valueToCompare
0568:                                            + " could not be cast to type double (while that is supposed to be their type)");
0569:                            return true;
0570:                        }
0571:                        return floatMatches(constraintDouble, doubleToCompare,
0572:                                operator);
0573:                    } else if (fieldType.equals(Date.class)) {
0574:                        long constraintLong = Casting.toLong(constraintValue,
0575:                                Long.MAX_VALUE);
0576:                        long longToCompare = Casting.toLong(valueToCompare,
0577:                                Long.MAX_VALUE);
0578:
0579:                        //if either value could not be cast to an int, return true, which is safe
0580:                        if (constraintLong == Long.MAX_VALUE
0581:                                || longToCompare == Long.MAX_VALUE) {
0582:                            log
0583:                                    .warn("either "
0584:                                            + constraintValue
0585:                                            + " or "
0586:                                            + valueToCompare
0587:                                            + " could not be cast to type long (while they are supposed to be of type Date supposed to be their type)");
0588:                            return true;
0589:                        }
0590:                        return intMatches(constraintLong, longToCompare,
0591:                                operator);
0592:                    } else if (fieldType.equals(Integer.class)) {
0593:                        int constraintInt = Casting.toInt(constraintValue,
0594:                                Integer.MAX_VALUE);
0595:                        int intToCompare = Casting.toInt(valueToCompare,
0596:                                Integer.MAX_VALUE);
0597:
0598:                        //if either value could not be cast to an int, return true, which is safe
0599:                        if (constraintInt == Integer.MAX_VALUE
0600:                                || intToCompare == Integer.MAX_VALUE) {
0601:                            log
0602:                                    .warn("either "
0603:                                            + constraintValue
0604:                                            + " or "
0605:                                            + valueToCompare
0606:                                            + " could not be cast to type int (while that is supposed to be their type)");
0607:                            return true;
0608:                        }
0609:                        return intMatches(constraintInt, intToCompare, operator);
0610:                    } else if (fieldType.equals(Long.class)) {
0611:                        long constraintLong = Casting.toLong(constraintValue,
0612:                                Long.MAX_VALUE);
0613:                        long longToCompare = Casting.toLong(valueToCompare,
0614:                                Long.MAX_VALUE);
0615:                        //              if either value could not be cast to a long, return true, which is safe
0616:                        if (constraintLong == Long.MAX_VALUE
0617:                                || longToCompare == Long.MAX_VALUE) {
0618:                            // how can this ever happen?
0619:                            log
0620:                                    .warn("either ["
0621:                                            + constraintValue
0622:                                            + "] "
0623:                                            + (constraintValue == null ? ""
0624:                                                    : "of type "
0625:                                                            + constraintValue
0626:                                                                    .getClass()
0627:                                                                    .getName())
0628:                                            + " or ["
0629:                                            + valueToCompare
0630:                                            + "] of type "
0631:                                            + (valueToCompare == null ? ""
0632:                                                    : "of type "
0633:                                                            + valueToCompare
0634:                                                                    .getClass()
0635:                                                                    .getName())
0636:                                            + " could not be cast to type long (while that is supposed to be their type)");
0637:                            return true;
0638:
0639:                        }
0640:                        return intMatches(constraintLong, longToCompare,
0641:                                operator);
0642:                    } else if (fieldType.equals(Node.class)) {
0643:                        if (constraintValue instanceof  MMObjectNode)
0644:                            constraintValue = ((MMObjectNode) constraintValue)
0645:                                    .getNumber();
0646:                        if (valueToCompare instanceof  MMObjectNode)
0647:                            valueToCompare = ((MMObjectNode) valueToCompare)
0648:                                    .getNumber();
0649:                        int constraintInt = Casting.toInt(constraintValue,
0650:                                Integer.MAX_VALUE);
0651:                        int intToCompare = Casting.toInt(valueToCompare,
0652:                                Integer.MAX_VALUE);
0653:                        //              if either value could not be cast to a Node, return true, which is safe
0654:                        if (constraintInt == Integer.MAX_VALUE
0655:                                || intToCompare == Integer.MAX_VALUE) {
0656:                            log
0657:                                    .warn("either ["
0658:                                            + constraintValue
0659:                                            + "] "
0660:                                            + (constraintValue == null ? ""
0661:                                                    : "of type "
0662:                                                            + constraintValue
0663:                                                                    .getClass()
0664:                                                                    .getName())
0665:                                            + " or ["
0666:                                            + valueToCompare
0667:                                            + "] of type "
0668:                                            + (valueToCompare == null ? ""
0669:                                                    : "of type "
0670:                                                            + valueToCompare
0671:                                                                    .getClass()
0672:                                                                    .getName())
0673:                                            + " could not be cast to type int  (while they should be type node)");
0674:                            return true;
0675:
0676:                        }
0677:                        return intMatches(constraintInt, intToCompare, operator);
0678:                    } else if (fieldType.equals(String.class)
0679:                            || fieldType.equals(org.w3c.dom.Document.class)) {
0680:                        String constraintString = Casting
0681:                                .toString(constraintValue);
0682:                        String stringToCompare = Casting
0683:                                .toString(valueToCompare);
0684:                        return stringMatches(constraintString, stringToCompare,
0685:                                operator, isCaseSensitive);
0686:                    }
0687:
0688:                    return false;
0689:                }
0690:
0691:                private boolean floatMatches(double constraintDouble,
0692:                        double doubleTocompare, int operator) {
0693:                    switch (operator) {
0694:                    case FieldCompareConstraint.EQUAL:
0695:                        return doubleTocompare == constraintDouble;
0696:                    case FieldCompareConstraint.GREATER:
0697:                        return doubleTocompare > constraintDouble;
0698:                    case FieldCompareConstraint.GREATER_EQUAL:
0699:                        return doubleTocompare >= constraintDouble;
0700:                    case FieldCompareConstraint.LESS:
0701:                        return doubleTocompare < constraintDouble;
0702:                    case FieldCompareConstraint.LESS_EQUAL:
0703:                        return doubleTocompare <= constraintDouble;
0704:                    case FieldCompareConstraint.NOT_EQUAL:
0705:                        return doubleTocompare != constraintDouble;
0706:                    default:
0707:                        log
0708:                                .warn("operator "
0709:                                        + FieldCompareConstraint.OPERATOR_DESCRIPTIONS[operator]
0710:                                        + "for any numeric type");
0711:                        return true;
0712:
0713:                    }
0714:                }
0715:
0716:                private boolean intMatches(long constraintLong,
0717:                        long longToCompare, int operator) {
0718:                    switch (operator) {
0719:                    case FieldCompareConstraint.EQUAL:
0720:                        return longToCompare == constraintLong;
0721:                    case FieldCompareConstraint.GREATER:
0722:                        return longToCompare > constraintLong;
0723:                    case FieldCompareConstraint.GREATER_EQUAL:
0724:                        return longToCompare >= constraintLong;
0725:                    case FieldCompareConstraint.LESS:
0726:                        return longToCompare < constraintLong;
0727:                    case FieldCompareConstraint.LESS_EQUAL:
0728:                        return longToCompare <= constraintLong;
0729:                    case FieldCompareConstraint.NOT_EQUAL:
0730:                        return longToCompare != constraintLong;
0731:                    default:
0732:                        log
0733:                                .warn("operator "
0734:                                        + FieldCompareConstraint.OPERATOR_DESCRIPTIONS[operator]
0735:                                        + "for any numeric type");
0736:                        return true;
0737:                    }
0738:                }
0739:
0740:                private boolean stringMatches(String constraintString,
0741:                        String stringToCompare, int operator,
0742:                        boolean isCaseSensitive) {
0743:                    switch (operator) {
0744:                    case FieldCompareConstraint.EQUAL:
0745:                        return stringToCompare.equals(constraintString);
0746:                        // TODO: MM: I think depending on the database configuration the case-sensitivity may be important in the following 4:
0747:                    case FieldCompareConstraint.GREATER:
0748:                        return stringToCompare.compareTo(constraintString) > 0;
0749:                    case FieldCompareConstraint.LESS:
0750:                        return stringToCompare.compareTo(constraintString) < 0;
0751:                    case FieldCompareConstraint.LESS_EQUAL:
0752:                        return stringToCompare.compareTo(constraintString) <= 0;
0753:                    case FieldCompareConstraint.GREATER_EQUAL:
0754:                        return stringToCompare.compareTo(constraintString) >= 0;
0755:                    case FieldCompareConstraint.LIKE:
0756:                        return likeMatches(constraintString, stringToCompare,
0757:                                isCaseSensitive);
0758:                    case FieldCompareConstraint.NOT_EQUAL:
0759:                        return !stringToCompare.equals(constraintString);
0760:                    default:
0761:                        log
0762:                                .warn("operator "
0763:                                        + FieldCompareConstraint.OPERATOR_DESCRIPTIONS[operator]
0764:                                        + "is not supported for type String");
0765:                        return true;
0766:                    }
0767:                }
0768:
0769:                private boolean likeMatches(String constraintString,
0770:                        String stringToCompare, boolean isCaseSensitive) {
0771:                    if (log.isTraceEnabled()) {
0772:                        log.trace("** method: likeMatches() stringToCompare: "
0773:                                + stringToCompare + ", constraintString: "
0774:                                + constraintString);
0775:                    }
0776:                    if (isCaseSensitive) {
0777:                        constraintString = constraintString.toLowerCase();
0778:                        stringToCompare = stringToCompare.toLowerCase();
0779:                    }
0780:                    char[] chars = constraintString.toCharArray();
0781:                    StringBuffer sb = new StringBuffer();
0782:
0783:                    for (char element : chars) {
0784:                        if (element == '?') {
0785:                            sb.append(".");
0786:                        } else if (element == '%') {
0787:                            sb.append(".*");
0788:                        } else if (escapeChars.indexOf(element) > -1) {
0789:                            sb.append("\\");
0790:                            sb.append(element);
0791:                        } else {
0792:                            sb.append(element);
0793:                        }
0794:                    }
0795:                    if (log.isDebugEnabled()) {
0796:                        log.trace("** new pattern: " + sb.toString());
0797:                    }
0798:                    return stringToCompare.matches(sb.toString());
0799:                }
0800:
0801:                protected Class<Object> getFieldTypeClass(StepField stepField) {
0802:                    MMBase mmbase = MMBase.getMMBase();
0803:                    // why it this checked anyway?
0804:                    CoreField field = mmbase.getBuilder(
0805:                            stepField.getStep().getTableName()).getField(
0806:                            stepField.getFieldName());
0807:                    DataType<Object> fieldType = field.getDataType();
0808:                    Class<Object> fieldTypeClass = fieldType.getTypeAsClass();
0809:                    if (fieldTypeClass.equals(Boolean.class)
0810:                            || fieldTypeClass.equals(Date.class)
0811:                            || fieldTypeClass.equals(Integer.class)
0812:                            || fieldTypeClass.equals(Long.class)
0813:                            || fieldTypeClass.equals(Float.class)
0814:                            || fieldTypeClass.equals(Double.class)
0815:                            || fieldTypeClass.equals(Node.class)
0816:                            || fieldTypeClass.equals(String.class)
0817:                            || fieldTypeClass
0818:                                    .equals(org.w3c.dom.Document.class)) {
0819:                        if (log.isDebugEnabled()) {
0820:                            log.debug("** found field type: "
0821:                                    + fieldTypeClass.getName());
0822:                        }
0823:                    } else {
0824:                        throw new RuntimeException("Field type "
0825:                                + fieldTypeClass + " is not supported");
0826:                    }
0827:                    return fieldTypeClass;
0828:                }
0829:
0830:            }
0831:
0832:            private static class BasicFieldValueConstraintMatcher extends
0833:                    FieldCompareConstraintMatcher {
0834:                private final Class<Object> fieldTypeClass;
0835:                protected final StepField stepField;
0836:                protected final BasicFieldValueConstraint wrappedFieldValueConstraint;
0837:
0838:                public BasicFieldValueConstraintMatcher(
0839:                        BasicFieldValueConstraint constraint) {
0840:                    stepField = constraint.getField();
0841:                    if (log.isDebugEnabled()) {
0842:                        log.debug("** builder: "
0843:                                + stepField.getStep().getTableName()
0844:                                + ". field: " + stepField.getFieldName());
0845:                    }
0846:                    fieldTypeClass = getFieldTypeClass(stepField);
0847:                    wrappedFieldValueConstraint = constraint;
0848:
0849:                }
0850:
0851:                protected int getOperator() {
0852:                    return wrappedFieldValueConstraint.getOperator();
0853:                }
0854:
0855:                /**
0856:                 * Check the values to see if the values of the node matches the constraint.
0857:                 */
0858:                public boolean nodeMatchesConstraint(
0859:                        Map<String, Object> valuesToMatch, NodeEvent event) {
0860:                    log.debug("**method: nodeMatchesConstraint");
0861:                    //if(! eventApplies(valuesToMatch, event)) throw new FieldComparisonException("constraint " + wrappedFieldCompareConstraint.toString() + "does not match event of type " +event.getBuilderName());
0862:                    boolean matches = valueMatches(fieldTypeClass,
0863:                            wrappedFieldValueConstraint.getValue(),
0864:                            valuesToMatch.get(stepField.getFieldName()),
0865:                            wrappedFieldValueConstraint.isCaseSensitive());
0866:                    return matches != wrappedFieldValueConstraint.isInverse();
0867:                }
0868:
0869:                public String toString() {
0870:                    return "Field Value Matcher.  operator: "
0871:                            + FieldCompareConstraint.OPERATOR_DESCRIPTIONS[wrappedFieldValueConstraint
0872:                                    .getOperator()] + ", value: "
0873:                            + wrappedFieldValueConstraint.getValue().toString()
0874:                            + ", step: " + stepField.getStep().getTableName()
0875:                            + ", field name: " + stepField.getFieldName();
0876:                }
0877:
0878:                /**
0879:                 * An event applies to a field value constraint wrapper if the wrapper is of the same type as the event, and the field
0880:                 * that is being checked is in the 'changed' fields map (valuesToMatch)
0881:                 */
0882:                public boolean eventApplies(Map<String, Object> valuesToMatch,
0883:                        NodeEvent event) {
0884:                    return wrappedFieldValueConstraint.getField().getStep()
0885:                            .getTableName().equals(event.getBuilderName())
0886:                            && valuesToMatch
0887:                                    .containsKey(wrappedFieldValueConstraint
0888:                                            .getField().getFieldName());
0889:                }
0890:
0891:            }
0892:
0893:            /**
0894:             * @since MMBase-1.8.1
0895:             */
0896:            private static class BasicFieldValueBetweenConstraintMatcher extends
0897:                    AbstractConstraintMatcher {
0898:
0899:                protected final StepField stepField;
0900:                protected final BasicFieldValueBetweenConstraint wrappedFieldConstraint;
0901:
0902:                public BasicFieldValueBetweenConstraintMatcher(
0903:                        BasicFieldValueBetweenConstraint constraint) {
0904:                    stepField = constraint.getField();
0905:                    wrappedFieldConstraint = constraint;
0906:                }
0907:
0908:                public boolean nodeMatchesConstraint(
0909:                        Map<String, Object> valuesToMatch, NodeEvent event) {
0910:                    return true;
0911:                }
0912:
0913:                public boolean eventApplies(Map<String, Object> valuesToMatch,
0914:                        NodeEvent event) {
0915:                    return true;
0916:                }
0917:
0918:                public String toString() {
0919:                    return "Field Value Between Matcher.  operator: "
0920:                            + ", step: " + stepField.getStep().getTableName()
0921:                            + ", field name: " + stepField.getFieldName();
0922:                }
0923:
0924:            }
0925:
0926:            /**
0927:             * @since MMBase-1.8.1
0928:             */
0929:            private static class BasicFieldValueInConstraintMatcher extends
0930:                    FieldCompareConstraintMatcher {
0931:                private final Class<Object> fieldTypeClass;
0932:                protected final StepField stepField;
0933:                protected final BasicFieldValueInConstraint wrappedFieldValueInConstraint;
0934:
0935:                public BasicFieldValueInConstraintMatcher(
0936:                        BasicFieldValueInConstraint constraint) {
0937:                    stepField = constraint.getField();
0938:                    fieldTypeClass = getFieldTypeClass(stepField);
0939:                    wrappedFieldValueInConstraint = constraint;
0940:                }
0941:
0942:                protected int getOperator() {
0943:                    return FieldCompareConstraint.EQUAL;
0944:                }
0945:
0946:                /**
0947:                 * Check the values to see if the node's value matches the constraint.
0948:                 */
0949:                public boolean nodeMatchesConstraint(
0950:                        Map<String, Object> valuesToMatch, NodeEvent event) {
0951:                    log.debug("**method: nodeMatchesConstraint");
0952:                    SortedSet<Object> values = wrappedFieldValueInConstraint
0953:                            .getValues();
0954:                    boolean matches = false;
0955:                    Iterator<Object> i = values.iterator();
0956:                    while (i.hasNext() && !matches) {
0957:                        Object value = i.next();
0958:                        matches = valueMatches(fieldTypeClass, value,
0959:                                valuesToMatch.get(stepField.getFieldName()),
0960:                                wrappedFieldValueInConstraint.isCaseSensitive());
0961:                    }
0962:                    return matches != wrappedFieldValueInConstraint.isInverse();
0963:                }
0964:
0965:                public String toString() {
0966:                    return "Field Value IN Matcher.  operator: "
0967:                            + ", value: "
0968:                            + wrappedFieldValueInConstraint.getValues()
0969:                                    .toString() + ", step: "
0970:                            + stepField.getStep().getTableName()
0971:                            + ", field name: " + stepField.getFieldName();
0972:                }
0973:
0974:                public boolean eventApplies(Map<String, Object> valuesToMatch,
0975:                        NodeEvent event) {
0976:                    return wrappedFieldValueInConstraint.getField().getStep()
0977:                            .getTableName().equals(event.getBuilderName())
0978:                            && valuesToMatch
0979:                                    .containsKey(wrappedFieldValueInConstraint
0980:                                            .getField().getFieldName());
0981:                }
0982:            }
0983:
0984:            private void logResult(String comment, SearchQuery query,
0985:                    Event event, MMObjectNode node) {
0986:                if (log.isDebugEnabled()) {
0987:                    String role = "";
0988:                    // a small hack to limit the output
0989:                    if (event instanceof  RelationEvent) {
0990:                        //get the role name
0991:                        RelationEvent revent = (RelationEvent) event;
0992:                        MMObjectNode relDef = MMBase.getMMBase().getBuilder(
0993:                                "reldef").getNode(revent.getRole());
0994:                        role = " role: " + relDef.getStringValue("sname") + "/"
0995:                                + relDef.getStringValue("dname");
0996:                        //filter the 'object' events
0997:                        if (revent.getRelationSourceType().equals("object")
0998:                                || revent.getRelationDestinationType().equals(
0999:                                        "object"))
1000:                            return;
1001:                    }
1002:                    try {
1003:                        log.debug("\n******** \n**"
1004:                                + comment
1005:                                + "\n**"
1006:                                + event.toString()
1007:                                + role
1008:                                + "\n**nodevalues: "
1009:                                + (node == null ? "NODE NULL" : ""
1010:                                        + node.getValues()) + "\n**"
1011:                                + sqlHandler.toSql(query, sqlHandler)
1012:                                + "\n******");
1013:                    } catch (SearchQueryException e) {
1014:                        log.error(e);
1015:                    }
1016:                }
1017:            }
1018:
1019:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.