/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2009 Sun Microsystems, Inc. */ package org.opends.sdk; import static com.sun.opends.sdk.util.StaticUtils.*; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import org.opends.sdk.schema.MatchingRule; import org.opends.sdk.schema.MatchingRuleUse; import org.opends.sdk.schema.Schema; import org.opends.sdk.schema.UnknownSchemaElementException; import com.sun.opends.sdk.util.StaticUtils; /** * An interface for determining whether entries match a {@code Filter}. */ public final class Matcher { private static class AndMatcherImpl extends MatcherImpl { private final List subMatchers; private AndMatcherImpl(List subMatchers) { this.subMatchers = subMatchers; } @Override public ConditionResult matches(Entry entry) { ConditionResult r = ConditionResult.TRUE; for (final MatcherImpl m : subMatchers) { final ConditionResult p = m.matches(entry); if (p == ConditionResult.FALSE) { return p; } r = ConditionResult.and(r, p); } return r; } } private static class AssertionMatcherImpl extends MatcherImpl { private final Assertion assertion; private final AttributeDescription attributeDescription; private final boolean dnAttributes; private final MatchingRule rule; private final MatchingRuleUse ruleUse; private AssertionMatcherImpl( AttributeDescription attributeDescription, MatchingRule rule, MatchingRuleUse ruleUse, Assertion assertion, boolean dnAttributes) { this.attributeDescription = attributeDescription; this.rule = rule; this.ruleUse = ruleUse; this.assertion = assertion; this.dnAttributes = dnAttributes; } @Override public ConditionResult matches(Entry entry) { ConditionResult r = ConditionResult.FALSE; if (attributeDescription != null) { // If the matchingRule field is absent, the type field will be // present and the default equality matching rule is used, // and an equality match is performed for that type. // If the type field is present and the matchingRule is present, // the matchValue is compared against the specified attribute // type and its subtypes. final ConditionResult p = Matcher.matches(entry .getAttribute(attributeDescription), rule, assertion); if (p == ConditionResult.TRUE) { return p; } r = ConditionResult.or(r, p); } else { // If the type field is absent and the matchingRule is present, // the matchValue is compared against all attributes in an entry // that support that matchingRule. for (final Attribute a : entry.getAttributes()) { if (ruleUse.hasAttribute(a.getAttributeDescription() .getAttributeType())) { final ConditionResult p = Matcher.matches(a, rule, assertion); if (p == ConditionResult.TRUE) { return p; } r = ConditionResult.or(r, p); } } } if (dnAttributes) { // If the dnAttributes field is set to TRUE, the match is // additionally applied against all the AttributeValueAssertions // in an entry's distinguished name, and it evaluates to TRUE if // there is at least one attribute or subtype in the // distinguished name for which the filter item evaluates to // TRUE. final DN dn = entry.getName(); for (final RDN rdn : dn) { for (final RDN.AVA ava : rdn) { if (ruleUse.hasAttribute(ava.getAttributeType())) { final ConditionResult p = Matcher.matches(ava .getAttributeValue(), rule, assertion); if (p == ConditionResult.TRUE) { return p; } r = ConditionResult.or(r, p); } } } } return r; } } private static class FalseMatcherImpl extends MatcherImpl { @Override public ConditionResult matches(Entry entry) { return ConditionResult.FALSE; } } private static abstract class MatcherImpl { public abstract ConditionResult matches(Entry entry); } private static class NotMatcherImpl extends MatcherImpl { private final MatcherImpl subFilter; private NotMatcherImpl(MatcherImpl subFilter) { this.subFilter = subFilter; } @Override public ConditionResult matches(Entry entry) { return ConditionResult.not(subFilter.matches(entry)); } } private static class OrMatcherImpl extends MatcherImpl { private final List subMatchers; private OrMatcherImpl(List subMatchers) { this.subMatchers = subMatchers; } @Override public ConditionResult matches(Entry entry) { ConditionResult r = ConditionResult.FALSE; for (final MatcherImpl m : subMatchers) { final ConditionResult p = m.matches(entry); if (p == ConditionResult.TRUE) { return p; } r = ConditionResult.or(r, p); } return r; } } private static class PresentMatcherImpl extends MatcherImpl { private final AttributeDescription attribute; private PresentMatcherImpl(AttributeDescription attribute) { this.attribute = attribute; } @Override public ConditionResult matches(Entry entry) { return entry.getAttribute(attribute) == null ? ConditionResult.FALSE : ConditionResult.TRUE; } } private static class TrueMatcherImpl extends MatcherImpl { @Override public ConditionResult matches(Entry entry) { return ConditionResult.TRUE; } } private static class UndefinedMatcherImpl extends MatcherImpl { @Override public ConditionResult matches(Entry entry) { return ConditionResult.UNDEFINED; } } /** * A visitor which is used to transform a filter into a matcher. */ private static final class Visitor implements FilterVisitor { public MatcherImpl visitAndFilter(Schema schema, List subFilters) { if (subFilters.isEmpty()) { if (DEBUG_LOG.isLoggable(Level.FINER)) { DEBUG_LOG.finer("Empty add filter component. " + "Will always return TRUE"); } return TRUE; } final List subMatchers = new ArrayList( subFilters.size()); for (final Filter f : subFilters) { subMatchers.add(f.accept(this, schema)); } return new AndMatcherImpl(subMatchers); } public MatcherImpl visitApproxMatchFilter(Schema schema, String attributeDescription, ByteString assertionValue) { AttributeDescription ad; MatchingRule rule; Assertion assertion; try { ad = AttributeDescription.valueOf(attributeDescription, schema); } catch (final LocalizedIllegalArgumentException e) { if (DEBUG_LOG.isLoggable(Level.WARNING)) { DEBUG_LOG.warning("Attribute description " + attributeDescription + " is not recognized: " + e.toString()); } return UNDEFINED; } if ((rule = ad.getAttributeType().getApproximateMatchingRule()) == null) { if (DEBUG_LOG.isLoggable(Level.WARNING)) { DEBUG_LOG.warning("The attribute type " + attributeDescription + " does not define an approximate matching rule"); } return UNDEFINED; } try { assertion = rule.getAssertion(assertionValue); } catch (final DecodeException de) { if (DEBUG_LOG.isLoggable(Level.WARNING)) { DEBUG_LOG.warning("The assertion value " + assertionValue + " is invalid: " + de.toString()); } return UNDEFINED; } return new AssertionMatcherImpl(ad, rule, null, assertion, false); } public MatcherImpl visitEqualityMatchFilter(Schema schema, String attributeDescription, ByteString assertionValue) { AttributeDescription ad; MatchingRule rule; Assertion assertion; try { ad = AttributeDescription.valueOf(attributeDescription, schema); } catch (final LocalizedIllegalArgumentException e) { if (DEBUG_LOG.isLoggable(Level.WARNING)) { DEBUG_LOG.warning("Attribute description " + attributeDescription + " is not recognized: " + e.toString()); } return UNDEFINED; } if ((rule = ad.getAttributeType().getEqualityMatchingRule()) == null) { if (DEBUG_LOG.isLoggable(Level.WARNING)) { DEBUG_LOG.warning("The attribute type " + attributeDescription + " does not define an equality matching rule"); } return UNDEFINED; } try { assertion = rule.getAssertion(assertionValue); } catch (final DecodeException de) { if (DEBUG_LOG.isLoggable(Level.WARNING)) { DEBUG_LOG.warning("The assertion value " + assertionValue + " is invalid: " + de.toString()); } return UNDEFINED; } return new AssertionMatcherImpl(ad, rule, null, assertion, false); } public MatcherImpl visitExtensibleMatchFilter(Schema schema, String matchingRule, String attributeDescription, ByteString assertionValue, boolean dnAttributes) { AttributeDescription ad = null; MatchingRule rule = null; MatchingRuleUse ruleUse = null; Assertion assertion; if (matchingRule != null) { try { rule = schema.getMatchingRule(matchingRule); } catch (final UnknownSchemaElementException e) { if (DEBUG_LOG.isLoggable(Level.WARNING)) { DEBUG_LOG.warning("Matching rule " + matchingRule + " is not recognized: " + e.toString()); } return UNDEFINED; } } if (attributeDescription != null) { try { ad = AttributeDescription.valueOf(attributeDescription, schema); } catch (final LocalizedIllegalArgumentException e) { if (DEBUG_LOG.isLoggable(Level.WARNING)) { DEBUG_LOG.warning("Attribute description " + attributeDescription + " is not recognized: " + e.toString()); } return UNDEFINED; } if (rule == null) { if ((rule = ad.getAttributeType().getEqualityMatchingRule()) == null) { if (DEBUG_LOG.isLoggable(Level.WARNING)) { DEBUG_LOG.warning("The attribute type " + attributeDescription + " does not define an equality matching rule"); } return UNDEFINED; } } else { try { ruleUse = schema.getMatchingRuleUse(rule); } catch (final UnknownSchemaElementException e) { if (DEBUG_LOG.isLoggable(Level.WARNING)) { DEBUG_LOG.warning("No matching rule use is defined for " + "matching rule " + matchingRule); return UNDEFINED; } } if (!ruleUse.hasAttribute(ad.getAttributeType())) { if (DEBUG_LOG.isLoggable(Level.WARNING)) { DEBUG_LOG.warning("The matching rule " + matchingRule + " is not valid for attribute type " + attributeDescription); } return UNDEFINED; } } } else { try { ruleUse = schema.getMatchingRuleUse(rule); } catch (final UnknownSchemaElementException e) { if (DEBUG_LOG.isLoggable(Level.WARNING)) { DEBUG_LOG.warning("No matching rule use is defined for " + "matching rule " + matchingRule); } return UNDEFINED; } } try { assertion = rule.getAssertion(assertionValue); } catch (final DecodeException de) { if (DEBUG_LOG.isLoggable(Level.WARNING)) { DEBUG_LOG.warning("The assertion value " + assertionValue + " is invalid: " + de.toString()); } return UNDEFINED; } return new AssertionMatcherImpl(ad, rule, ruleUse, assertion, dnAttributes); } public MatcherImpl visitGreaterOrEqualFilter(Schema schema, String attributeDescription, ByteString assertionValue) { AttributeDescription ad; MatchingRule rule; Assertion assertion; try { ad = AttributeDescription.valueOf(attributeDescription, schema); } catch (final LocalizedIllegalArgumentException e) { if (DEBUG_LOG.isLoggable(Level.WARNING)) { DEBUG_LOG.warning("Attribute description " + attributeDescription + " is not recognized: " + e.toString()); } return UNDEFINED; } if ((rule = ad.getAttributeType().getOrderingMatchingRule()) == null) { if (DEBUG_LOG.isLoggable(Level.WARNING)) { DEBUG_LOG.warning("The attribute type " + attributeDescription + " does not define an ordering matching rule"); } return UNDEFINED; } try { assertion = rule.getGreaterOrEqualAssertion(assertionValue); } catch (final DecodeException de) { if (DEBUG_LOG.isLoggable(Level.WARNING)) { DEBUG_LOG.warning("The assertion value " + assertionValue + " is invalid: " + de.toString()); } return UNDEFINED; } return new AssertionMatcherImpl(ad, rule, null, assertion, false); } public MatcherImpl visitLessOrEqualFilter(Schema schema, String attributeDescription, ByteString assertionValue) { AttributeDescription ad; MatchingRule rule; Assertion assertion; try { ad = AttributeDescription.valueOf(attributeDescription, schema); } catch (final LocalizedIllegalArgumentException e) { if (DEBUG_LOG.isLoggable(Level.WARNING)) { DEBUG_LOG.warning("Attribute description " + attributeDescription + " is not recognized: " + e.toString()); } return UNDEFINED; } if ((rule = ad.getAttributeType().getOrderingMatchingRule()) == null) { if (DEBUG_LOG.isLoggable(Level.WARNING)) { DEBUG_LOG.warning("The attribute type " + attributeDescription + " does not define an ordering matching rule"); } return UNDEFINED; } try { assertion = rule.getLessOrEqualAssertion(assertionValue); } catch (final DecodeException de) { if (DEBUG_LOG.isLoggable(Level.WARNING)) { DEBUG_LOG.warning("The assertion value " + assertionValue + " is invalid: " + de.toString()); } return UNDEFINED; } return new AssertionMatcherImpl(ad, rule, null, assertion, false); } public MatcherImpl visitNotFilter(Schema schema, Filter subFilter) { final MatcherImpl subMatcher = subFilter.accept(this, schema); return new NotMatcherImpl(subMatcher); } public MatcherImpl visitOrFilter(Schema schema, List subFilters) { if (subFilters.isEmpty()) { if (DEBUG_LOG.isLoggable(Level.FINER)) { DEBUG_LOG.finer("Empty or filter component. " + "Will always return FALSE"); } return FALSE; } final List subMatchers = new ArrayList( subFilters.size()); for (final Filter f : subFilters) { subMatchers.add(f.accept(this, schema)); } return new OrMatcherImpl(subMatchers); } public MatcherImpl visitPresentFilter(Schema schema, String attributeDescription) { AttributeDescription ad; try { ad = AttributeDescription.valueOf(attributeDescription, schema); } catch (final LocalizedIllegalArgumentException e) { if (DEBUG_LOG.isLoggable(Level.WARNING)) { DEBUG_LOG.warning("Attribute description " + attributeDescription + " is not recognized: " + e.toString()); } return UNDEFINED; } return new PresentMatcherImpl(ad); } public MatcherImpl visitSubstringsFilter(Schema schema, String attributeDescription, ByteString initialSubstring, List anySubstrings, ByteString finalSubstring) { AttributeDescription ad; MatchingRule rule; Assertion assertion; try { ad = AttributeDescription.valueOf(attributeDescription, schema); } catch (final LocalizedIllegalArgumentException e) { if (DEBUG_LOG.isLoggable(Level.WARNING)) { DEBUG_LOG.warning("Attribute description " + attributeDescription + " is not recognized: " + e.toString()); } return UNDEFINED; } if ((rule = ad.getAttributeType().getSubstringMatchingRule()) == null) { if (DEBUG_LOG.isLoggable(Level.WARNING)) { DEBUG_LOG.warning("The attribute type " + attributeDescription + " does not define an substring matching rule"); } return UNDEFINED; } try { assertion = rule.getAssertion(initialSubstring, anySubstrings, finalSubstring); } catch (final DecodeException de) { if (DEBUG_LOG.isLoggable(Level.WARNING)) { DEBUG_LOG .warning("The substring assertion values contain an invalid value: " + de.toString()); } return UNDEFINED; } return new AssertionMatcherImpl(ad, rule, null, assertion, false); } public MatcherImpl visitUnrecognizedFilter(Schema schema, byte filterTag, ByteString filterBytes) { if (DEBUG_LOG.isLoggable(Level.WARNING)) { DEBUG_LOG.warning("The type of filtering requested with tag " + StaticUtils.byteToHex(filterTag) + " is not implemented"); } return UNDEFINED; } } private static final MatcherImpl FALSE = new FalseMatcherImpl(); private static final MatcherImpl TRUE = new TrueMatcherImpl(); private static final MatcherImpl UNDEFINED = new UndefinedMatcherImpl(); private static final FilterVisitor VISITOR = new Visitor(); private static ConditionResult matches(Attribute a, MatchingRule rule, Assertion assertion) { ConditionResult r = ConditionResult.FALSE; if (a != null) { for (final ByteString v : a) { switch (matches(v, rule, assertion)) { case TRUE: return ConditionResult.TRUE; case UNDEFINED: r = ConditionResult.UNDEFINED; } } } return r; } private static ConditionResult matches(ByteString v, MatchingRule rule, Assertion assertion) { try { final ByteString normalizedValue = rule .normalizeAttributeValue(v); return assertion.matches(normalizedValue); } catch (final DecodeException de) { if (DEBUG_LOG.isLoggable(Level.WARNING)) { DEBUG_LOG.warning("The attribute value " + v.toString() + " is " + "invalid for matching rule " + rule.getNameOrOID() + ". Possible schema error? : " + de.toString()); } return ConditionResult.UNDEFINED; } } private final MatcherImpl impl; Matcher(Filter filter, Schema schema) { this.impl = filter.accept(VISITOR, schema); } /** * Indicates whether this filter {@code Matcher} matches the provided * {@code Entry}. * * @param entry * The entry to be matched. * @return {@code true} if this filter {@code Matcher} matches the * provided {@code Entry}. */ public ConditionResult matches(Entry entry) { return impl.matches(entry); } }