mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Nicolas Capponi
24.45.2014 3dc3f07607a5c720a1452cc75701554a9f3ad715
Checkpoint commit for OPENDJ-1308 Migrate schema support

Align SubstringMatchingRule class on the SDK

* Update MatchingRule interface to match SDK equivalent class
** Add getSubstringAssertion() method
* Implement getSubstringAssertion() in AbstractMatchingRule and SubstringMatchingRule
classes
* Use Assertions instead of valueMathcesSubstring() and normalizeSubstring() methods
when using a SubstringMatchingRule

* Add XX:MaxPermSize argument with value:128M when running tests to avoid OOM
14 files modified
1070 ■■■■■ changed files
opendj3-server-dev/build.xml 2 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/api/AbstractMatchingRule.java 11 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/api/MatchingRule.java 22 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/api/SubstringMatchingRule.java 260 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/api/VirtualAttributeProvider.java 87 ●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/controls/MatchedValuesFilter.java 157 ●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/types/AttributeBuilder.java 81 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/types/SearchFilter.java 258 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/schema/CaseExactIA5SubstringMatchingRuleTest.java 33 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/schema/CaseExactSubstringMatchingRuleTest.java 33 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/schema/CaseIgnoreIA5SubstringMatchingRuleTest.java 33 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/schema/CaseIgnoreSubstringMatchingRuleTest.java 33 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/schema/NumericStringSubstringMatchingRuleTest.java 25 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/schema/SubstringMatchingRuleTest.java 35 ●●●● patch | view | raw | blame | history
opendj3-server-dev/build.xml
@@ -35,6 +35,7 @@
  <!-- Build JVM properties -->
  <property name="MEM" value="512M"/>
  <property name="PERMSIZE" value="128M"/>
  <!-- Build OpenDMK properties -->
  <property file="build.properties"/>
@@ -2403,6 +2404,7 @@
      <jvmarg value="-Dtest.progress=${test.progress}" />
      <jvmarg value="-Xms${MEM}" />
      <jvmarg value="-Xmx${MEM}" />
      <jvmarg value="-XX:MaxPermSize=${PERMSIZE}" />
      <jvmarg value="${jvm.debug.arg1}" />
      <jvmarg value="${jvm.debug.arg2}" />
      <jvmarg value="${jvm.debug.arg3}" />
opendj3-server-dev/src/server/org/opends/server/api/AbstractMatchingRule.java
@@ -28,6 +28,7 @@
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import org.forgerock.opendj.ldap.Assertion;
import org.forgerock.opendj.ldap.ByteSequence;
@@ -148,6 +149,16 @@
  /** {@inheritDoc} */
  @Override
  public Assertion getSubstringAssertion(ByteSequence subInitial,
      List<? extends ByteSequence> subAnyElements, ByteSequence subFinal) throws DecodeException
  {
    return UNDEFINED_ASSERTION;
  }
  /** {@inheritDoc} */
  @Override
  public boolean isObsolete()
  {
    return false;
opendj3-server-dev/src/server/org/opends/server/api/MatchingRule.java
@@ -28,6 +28,7 @@
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import org.forgerock.opendj.ldap.Assertion;
import org.forgerock.opendj.ldap.ByteSequence;
@@ -148,6 +149,27 @@
  public Assertion getLessOrEqualAssertion(final ByteSequence assertionValue) throws DecodeException;
  /**
   * Returns the normalized form of the provided assertion substring values,
   * which is best suited for efficiently performing matching operations on
   * that value.
   *
   * @param subInitial
   *            The normalized substring value fragment that should appear at
   *            the beginning of the target value.
   * @param subAnyElements
   *            The normalized substring value fragments that should appear in
   *            the middle of the target value.
   * @param subFinal
   *            The normalized substring value fragment that should appear at
   *            the end of the target value.
   * @return The normalized version of the provided assertion value.
   * @throws DecodeException
   *             if the syntax of the value is not valid.
   */
  public Assertion getSubstringAssertion(final ByteSequence subInitial,
      final List<? extends ByteSequence> subAnyElements, final ByteSequence subFinal) throws DecodeException;
  /**
   * Indicates whether this matching rule is declared "OBSOLETE". The
   * default implementation will always return {@code false}. If that
   * is not acceptable for a particular matching rule implementation,
opendj3-server-dev/src/server/org/opends/server/api/SubstringMatchingRule.java
@@ -26,11 +26,18 @@
 */
package org.opends.server.api;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.TreeSet;
import org.forgerock.opendj.ldap.Assertion;
import org.forgerock.opendj.ldap.ByteSequence;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.ByteStringBuilder;
import org.forgerock.opendj.ldap.ConditionResult;
import org.forgerock.opendj.ldap.DecodeException;
import org.forgerock.opendj.ldap.spi.IndexQueryFactory;
/**
 * This class defines the set of methods and structures that must be
@@ -175,5 +182,258 @@
    return true;
  }
  /**
   * Default assertion implementation for substring matching rules.
   * For example, with the assertion value "initial*any1*any2*any3*final",
   * the assertion will be decomposed like this:
   * <ul>
   * <li>normInitial will contain "initial"</li>
   * <li>normAnys will contain [ "any1", "any2", "any3" ]</li>
   * <li>normFinal will contain "final"</li>
   * </ul>
   */
  static final class DefaultSubstringAssertion implements Assertion {
      /** Normalized substring for the text before the first '*' character. */
      private final ByteString normInitial;
      /** Normalized substrings for all text chunks in between '*' characters. */
      private final ByteString[] normAnys;
      /** Normalized substring for the text after the last '*' character. */
      private final ByteString normFinal;
      private DefaultSubstringAssertion(final ByteString normInitial,
              final ByteString[] normAnys, final ByteString normFinal) {
          this.normInitial = normInitial;
          this.normAnys = normAnys;
          this.normFinal = normFinal;
      }
      /** {@inheritDoc} */
      @Override
      public ConditionResult matches(final ByteSequence normalizedAttributeValue) {
          final int valueLength = normalizedAttributeValue.length();
          int pos = 0;
          if (normInitial != null) {
              final int initialLength = normInitial.length();
              if (initialLength > valueLength) {
                  return ConditionResult.FALSE;
              }
              for (; pos < initialLength; pos++) {
                  if (normInitial.byteAt(pos) != normalizedAttributeValue.byteAt(pos)) {
                      return ConditionResult.FALSE;
                  }
              }
          }
          if (normAnys != null) {
          matchEachSubstring:
              for (final ByteSequence element : normAnys) {
                  final int anyLength = element.length();
                  final int end = valueLength - anyLength;
              matchCurrentSubstring:
                  for (; pos <= end; pos++) {
                      // Try to match all characters from the substring
                      for (int i = 0; i < anyLength; i++) {
                          if (element.byteAt(i) != normalizedAttributeValue.byteAt(pos + i)) {
                              // not a match,
                              // try to find a match in the rest of this value
                              continue matchCurrentSubstring;
                          }
                      }
                      // we just matched current substring,
                      // go try to match the next substring
                      pos += anyLength;
                      continue matchEachSubstring;
                  }
                  // Could not match current substring
                  return ConditionResult.FALSE;
              }
          }
          if (normFinal != null) {
              final int finalLength = normFinal.length();
              if (valueLength - finalLength < pos) {
                  return ConditionResult.FALSE;
              }
              pos = valueLength - finalLength;
              for (int i = 0; i < finalLength; i++, pos++) {
                  if (normFinal.byteAt(i) != normalizedAttributeValue.byteAt(pos)) {
                      return ConditionResult.FALSE;
                  }
              }
          }
          return ConditionResult.TRUE;
      }
      /** {@inheritDoc} */
      @Override
      public <T> T createIndexQuery(IndexQueryFactory<T> factory) throws DecodeException {
          final Collection<T> subqueries = new LinkedList<T>();
          if (normInitial != null) {
              // relies on the fact that equality indexes are also ordered
              subqueries.add(rangeMatch(factory, "equality", normInitial));
          }
          if (normAnys != null) {
              for (ByteString normAny : normAnys) {
                  substringMatch(factory, normAny, subqueries);
              }
          }
          if (normFinal != null) {
              substringMatch(factory, normFinal, subqueries);
          }
          if (normInitial != null) {
              // Add this one last to minimize the risk to run the same search twice
              // (possible overlapping with the use of equality index at the start of this method)
              substringMatch(factory, normInitial, subqueries);
          }
          return factory.createIntersectionQuery(subqueries);
      }
      private <T> T rangeMatch(IndexQueryFactory<T> factory, String indexID, ByteSequence lower) {
          // Iterate through all the keys that have this value as the prefix.
          // Set the upper bound for a range search.
          // We need a key for the upper bound that is of equal length
          // but slightly greater than the lower bound.
          final ByteStringBuilder upper = new ByteStringBuilder(lower);
          for (int i = upper.length() - 1; i >= 0; i--) {
              if (upper.byteAt(i) == (byte) 0xFF) {
                  // We have to carry the overflow to the more significant byte.
                  upper.setByte(i, (byte) 0);
              } else {
                  // No overflow, we can stop.
                  upper.setByte(i, (byte) (upper.byteAt(i) + 1));
                  break;
              }
          }
          // Read the range: lower <= keys < upper.
          return factory.createRangeMatchQuery(indexID, lower, upper, true, false);
      }
      private <T> void substringMatch(final IndexQueryFactory<T> factory, final ByteString normSubstring,
              final Collection<T> subqueries) {
          int substrLength = factory.getIndexingOptions().substringKeySize();
          // There are two cases, depending on whether the user-provided
          // substring is smaller than the configured index substring length or not.
          if (normSubstring.length() < substrLength) {
              subqueries.add(rangeMatch(factory, "substring", normSubstring));
          } else {
              // Break the value up into fragments of length equal to the
              // index substring length, and read those keys.
              // Eliminate duplicates by putting the keys into a set.
              final TreeSet<ByteSequence> substringKeys = new TreeSet<ByteSequence>();
              // Example: The value is ABCDE and the substring length is 3.
              // We produce the keys ABC BCD CDE.
              for (int first = 0, last = substrLength;
                   last <= normSubstring.length(); first++, last++) {
                  substringKeys.add(normSubstring.subSequence(first, first + substrLength));
              }
              for (ByteSequence key : substringKeys) {
                  subqueries.add(factory.createExactMatchQuery("substring", key));
              }
          }
      }
      // TODO : reminder : should add this method in the SDK
      /** {@inheritDoc} */
      @Override
    public int hashCode()
    {
      int hashCode = 0;
      if (normInitial != null)
      {
        hashCode += normInitial.hashCode();
      }
      if (normAnys != null)
      {
        for (ByteString any : normAnys)
        {
          hashCode += any.hashCode();
        }
      }
      if (normFinal != null)
      {
        hashCode += normFinal.hashCode();
      }
      return hashCode;
    }
      // TODO : reminder : should add this method in the SDK
      /** {@inheritDoc} */
      @Override
      public boolean equals(Object obj)
      {
        if (obj == this)
        {
          return true;
        }
        if (! (obj instanceof DefaultSubstringAssertion))
        {
          return false;
        }
        DefaultSubstringAssertion other = (DefaultSubstringAssertion) obj;
        boolean initialCheck = normInitial == null ? other.normInitial == null : normInitial.equals(other.normInitial);
        if (!initialCheck)
        {
          return false;
        }
        boolean finalCheck = normFinal == null ? other.normFinal == null : normFinal.equals(other.normFinal);
        if (!finalCheck)
        {
          return false;
        }
        boolean anyCheck = normAnys == null ? other.normAnys == null : normAnys.length == other.normAnys.length;
        if (!anyCheck)
        {
          return false;
        }
        if (normAnys != null)
        {
          for (int i = 0; i < normAnys.length; i++)
          {
            if (! normAnys[i].equals(other.normAnys[i]))
            {
              return false;
            }
          }
        }
        return true;
      }
  }
  /** {@inheritDoc} */
  @Override
  public Assertion getSubstringAssertion(ByteSequence subInitial,
      List<? extends ByteSequence> subAnyElements, ByteSequence subFinal)
      throws DecodeException
  {
    final ByteString normInitial = subInitial == null ? null : normalizeSubstring(subInitial);
    ByteString[] normAnys = null;
    if (subAnyElements != null && !subAnyElements.isEmpty())
    {
      normAnys = new ByteString[subAnyElements.size()];
      for (int i = 0; i < subAnyElements.size(); i++)
      {
        normAnys[i] = normalizeSubstring(subAnyElements.get(i));
      }
    }
    final ByteString normFinal = subFinal == null ? null : normalizeSubstring(subFinal);
    return new DefaultSubstringAssertion(normInitial, normAnys, normFinal);
  }
}
opendj3-server-dev/src/server/org/opends/server/api/VirtualAttributeProvider.java
@@ -26,7 +26,6 @@
 */
package org.opends.server.api;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -34,9 +33,9 @@
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.config.server.ConfigException;
import org.forgerock.opendj.ldap.Assertion;
import org.forgerock.opendj.ldap.ByteSequence;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.ConditionResult;
import org.forgerock.opendj.ldap.DecodeException;
import org.opends.server.admin.std.server.VirtualAttributeCfg;
import org.opends.server.core.SearchOperation;
import org.opends.server.types.Attribute;
@@ -261,96 +260,28 @@
                                          List<ByteString> subAny,
                                          ByteString subFinal)
  {
    SubstringMatchingRule matchingRule =
         rule.getAttributeType().getSubstringMatchingRule();
    MatchingRule matchingRule = rule.getAttributeType().getSubstringMatchingRule();
    if (matchingRule == null)
    {
      return ConditionResult.UNDEFINED;
    }
    ByteString normalizedSubInitial;
    if (subInitial == null)
    Assertion assertion;
    try
    {
      normalizedSubInitial = null;
      assertion = matchingRule.getSubstringAssertion(subInitial, subAny, subFinal);
    }
    else
    {
      try
      {
        normalizedSubInitial =
             matchingRule.normalizeSubstring(subInitial);
      }
      catch (Exception e)
      {
        logger.traceException(e);
        // The substring couldn't be normalized => return "undefined".
        return ConditionResult.UNDEFINED;
      }
    catch(DecodeException e) {
      logger.traceException(e);
      return ConditionResult.UNDEFINED;
    }
    ArrayList<ByteSequence> normalizedSubAny;
    if (subAny == null)
    {
      normalizedSubAny = null;
    }
    else
    {
      normalizedSubAny =
           new ArrayList<ByteSequence>(subAny.size());
      for (ByteString subAnyElement : subAny)
      {
        try
        {
          normalizedSubAny.add(matchingRule.normalizeSubstring(
                                                 subAnyElement));
        }
        catch (Exception e)
        {
          logger.traceException(e);
          // The substring couldn't be normalized => return "undefined".
          return ConditionResult.UNDEFINED;
        }
      }
    }
    ByteString normalizedSubFinal;
    if (subFinal == null)
    {
      normalizedSubFinal = null;
    }
    else
    {
      try
      {
        normalizedSubFinal =
             matchingRule.normalizeSubstring(subFinal);
      }
      catch (Exception e)
      {
        logger.traceException(e);
        // The substring couldn't be normalized => return "undefined".
        return ConditionResult.UNDEFINED;
      }
    }
    ConditionResult result = ConditionResult.FALSE;
    for (ByteString value : getValues(entry, rule))
    {
      try
      {
        ByteString nv = matchingRule.normalizeAttributeValue(value);
        if (matchingRule.valueMatchesSubstring(
                              nv,
                              normalizedSubInitial,
                              normalizedSubAny,
                              normalizedSubFinal))
        if (assertion.matches(matchingRule.normalizeAttributeValue(value)).toBoolean())
        {
          return ConditionResult.TRUE;
        }
opendj3-server-dev/src/server/org/opends/server/controls/MatchedValuesFilter.java
@@ -36,7 +36,6 @@
import org.forgerock.opendj.io.ASN1Reader;
import org.forgerock.opendj.io.ASN1Writer;
import org.forgerock.opendj.ldap.Assertion;
import org.forgerock.opendj.ldap.ByteSequence;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.DecodeException;
import org.forgerock.util.Reject;
@@ -117,12 +116,6 @@
  // The approximate matching rule for this matched values filter.
  private MatchingRule approximateMatchingRule;
  // The normalized subFinal value for this matched values filter.
  private ByteString normalizedSubFinal;
  // The normalized subInitial value for this matched values filter.
  private ByteString normalizedSubInitial;
  // The raw, unprocessed assertion value for this matched values filter.
  private final ByteString rawAssertionValue;
@@ -148,9 +141,6 @@
  // The equality matching rule for this matched values filter.
  private MatchingRule equalityMatchingRule;
  // The set of normalized subAny values for this matched values filter.
  private List<ByteString> normalizedSubAny;
  // The set of subAny values for this matched values filter.
  private final List<ByteString> subAny;
@@ -169,7 +159,11 @@
  // The substring matching rule for this matched values filter.
  private SubstringMatchingRule substringMatchingRule;
  /**
   * The assertion created from substring matching rule using values of this
   * filter.
   */
  private Assertion substringAssertion;
  /**
   * Creates a new matched values filter with the provided information.
@@ -194,18 +188,6 @@
    this.subAny            = subAny;
    this.subFinal          = subFinal;
    this.matchingRuleID    = matchingRuleID;
    decoded                 = false;
    attributeType           = null;
    assertionValue          = null;
    matchingRule            = null;
    normalizedSubInitial    = null;
    normalizedSubAny        = null;
    normalizedSubFinal      = null;
    approximateMatchingRule = null;
    equalityMatchingRule    = null;
    orderingMatchingRule    = null;
    substringMatchingRule   = null;
  }
@@ -972,37 +954,20 @@
    return subInitial;
  }
  /**
   * Retrieves the normalized form of the subInitial element.
   *
   * @return  The normalized form of the subInitial element, or
   *          <CODE>null</CODE> if there is none.
   */
  public ByteString getNormalizedSubInitialElement()
  {
    if (normalizedSubInitial == null)
  private Assertion getSubstringAssertion() {
    if (substringAssertion == null)
    {
      if ((subInitial != null) && (getSubstringMatchingRule() != null))
      try
      {
        try
        {
          normalizedSubInitial =
               getSubstringMatchingRule().normalizeSubstring(subInitial);
        }
        catch (Exception e)
        {
          logger.traceException(e);
        }
        substringAssertion =
            getSubstringMatchingRule().getSubstringAssertion(subInitial, subAny, subFinal);
      }
      catch (DecodeException e)
      {
        logger.traceException(e);
      }
    }
    return normalizedSubInitial;
    return substringAssertion;
  }
@@ -1019,55 +984,6 @@
    return subAny;
  }
  /**
   * Retrieves the set of normalized subAny elements for this matched values
   * filter.
   *
   * @return  The set of subAny elements for this matched values filter.  If
   *          there are none, then an empty list will be returned.  If a
   *          problem occurs while attempting to perform the normalization, then
   *          <CODE>null</CODE> will be returned.
   */
  public List<ByteString> getNormalizedSubAnyElements()
  {
    if (normalizedSubAny == null)
    {
      if ((subAny == null) || (subAny.isEmpty()))
      {
        normalizedSubAny = new ArrayList<ByteString>(0);
      }
      else
      {
        if (getSubstringMatchingRule() == null)
        {
          return null;
        }
        normalizedSubAny = new ArrayList<ByteString>();
        try
        {
          for (ByteString s : subAny)
          {
            normalizedSubAny.add(
                 substringMatchingRule.normalizeSubstring(s));
          }
        }
        catch (Exception e)
        {
          logger.traceException(e);
          normalizedSubAny = null;
        }
      }
    }
    return normalizedSubAny;
  }
  /**
   * Retrieves the subFinal element for this matched values filter.
   *
@@ -1079,37 +995,6 @@
    return subFinal;
  }
  /**
   * Retrieves the normalized form of the subFinal element.
   *
   * @return  The normalized form of the subFinal element, or <CODE>null</CODE>
   *          if there is none.
   */
  public ByteString getNormalizedSubFinalElement()
  {
    if (normalizedSubFinal == null)
    {
      if ((subFinal != null) && (getSubstringMatchingRule() != null))
      {
        try
        {
          normalizedSubFinal =
               getSubstringMatchingRule().normalizeSubstring(subFinal);
        }
        catch (Exception e)
        {
          logger.traceException(e);
        }
      }
    }
    return normalizedSubFinal;
  }
  /**
   * Retrieves the matching rule ID for this matched values filter.
   *
@@ -1247,9 +1132,7 @@
    {
      getAttributeType();
      getAssertionValue();
      getNormalizedSubInitialElement();
      getNormalizedSubAnyElements();
      getNormalizedSubFinalElement();
      getSubstringAssertion();
      getMatchingRule();
      getApproximateMatchingRule();
      getEqualityMatchingRule();
@@ -1303,15 +1186,11 @@
      case SUBSTRINGS_TYPE:
        if (attributeType != null
            && attributeType.equals(type)
            && substringMatchingRule != null)
            && substringAssertion != null)
        {
          try
          {
            ArrayList<ByteSequence> normalizedSubAnyBS =
                 new ArrayList<ByteSequence>(normalizedSubAny);
            return substringMatchingRule.valueMatchesSubstring(
                 substringMatchingRule.normalizeAttributeValue(value),
                 normalizedSubInitial, normalizedSubAnyBS, normalizedSubFinal);
            return substringAssertion.matches(substringMatchingRule.normalizeAttributeValue(value)).toBoolean();
          }
          catch (Exception e)
          {
opendj3-server-dev/src/server/org/opends/server/types/AttributeBuilder.java
@@ -27,7 +27,6 @@
package org.opends.server.types;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
@@ -42,7 +41,6 @@
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.ldap.Assertion;
import org.forgerock.opendj.ldap.ByteSequence;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.ConditionResult;
import org.forgerock.opendj.ldap.DecodeException;
@@ -381,81 +379,22 @@
        ByteString subInitial,
        List<ByteString> subAny, ByteString subFinal)
    {
      SubstringMatchingRule matchingRule = attributeType
          .getSubstringMatchingRule();
      SubstringMatchingRule matchingRule = attributeType.getSubstringMatchingRule();
      if (matchingRule == null)
      {
        return ConditionResult.UNDEFINED;
      }
      ByteString normalizedSubInitial;
      if (subInitial == null)
      {
        normalizedSubInitial = null;
      }
      else
      {
        try
        {
          normalizedSubInitial =
            matchingRule.normalizeSubstring(subInitial);
        }
        catch (Exception e)
        {
          logger.traceException(e);
          // The substring couldn't be normalized. We have to return
          // "undefined".
          return ConditionResult.UNDEFINED;
        }
      }
      ArrayList<ByteSequence> normalizedSubAny;
      if (subAny == null)
      Assertion assertion;
      try
      {
        normalizedSubAny = null;
        assertion = matchingRule.getSubstringAssertion(subInitial, subAny, subFinal);
      }
      else
      catch (DecodeException e)
      {
        normalizedSubAny = new ArrayList<ByteSequence>(subAny.size());
        for (ByteString subAnyElement : subAny)
        {
          try
          {
            normalizedSubAny
                .add(matchingRule.normalizeSubstring(subAnyElement));
          }
          catch (Exception e)
          {
            logger.traceException(e);
            // The substring couldn't be normalized. We have to return
            // "undefined".
            return ConditionResult.UNDEFINED;
          }
        }
      }
      ByteString normalizedSubFinal;
      if (subFinal == null)
      {
        normalizedSubFinal = null;
      }
      else
      {
        try
        {
          normalizedSubFinal =
            matchingRule.normalizeSubstring(subFinal);
        }
        catch (Exception e)
        {
          logger.traceException(e);
          // The substring couldn't be normalized. We have to return
          // "undefined".
          return ConditionResult.UNDEFINED;
        }
        logger.traceException(e);
        return ConditionResult.UNDEFINED;
      }
      ConditionResult result = ConditionResult.FALSE;
@@ -463,11 +402,7 @@
      {
        try
        {
          if (matchingRule.valueMatchesSubstring(
              matchingRule.normalizeAttributeValue(value),
              normalizedSubInitial,
              normalizedSubAny,
              normalizedSubFinal))
          if (assertion.matches(matchingRule.normalizeAttributeValue(value)).toBoolean())
          {
            return ConditionResult.TRUE;
          }
opendj3-server-dev/src/server/org/opends/server/types/SearchFilter.java
@@ -3593,30 +3593,13 @@
    {
      case AND:
      case OR:
        if (filterComponents.size() != f.filterComponents.size())
        {
          return false;
        }
outerComponentLoop:
        for (SearchFilter outerFilter : filterComponents)
        {
          for (SearchFilter innerFilter : f.filterComponents)
          {
            if (outerFilter.equals(innerFilter))
            {
              continue outerComponentLoop;
            }
          }
          return false;
        }
        return true;
        return andOrEqual(f);
      case NOT:
        return notComponent.equals(f.notComponent);
      case EQUALITY:
        return typeAndOptionsAndAssertionEqual(f);
      case SUBSTRING:
        return equalsSubstring(f);
        return substringEqual(f);
      case GREATER_OR_EQUAL:
        return typeAndOptionsAndAssertionEqual(f);
      case LESS_OR_EQUAL:
@@ -3633,6 +3616,28 @@
    }
  }
  private boolean andOrEqual(SearchFilter f)
  {
    if (filterComponents.size() != f.filterComponents.size())
    {
      return false;
    }
outerComponentLoop:
    for (SearchFilter outerFilter : filterComponents)
    {
      for (SearchFilter innerFilter : f.filterComponents)
      {
        if (outerFilter.equals(innerFilter))
        {
          continue outerComponentLoop;
        }
      }
      return false;
    }
    return true;
  }
  private boolean typeAndOptionsAndAssertionEqual(SearchFilter f)
  {
@@ -3650,113 +3655,28 @@
  }
  private boolean equalsSubstring(SearchFilter f)
  private boolean substringEqual(SearchFilter f)
  {
    if (! attributeType.equals(f.attributeType))
    {
      return false;
    }
    SubstringMatchingRule smr =
         attributeType.getSubstringMatchingRule();
    if (smr == null)
    SubstringMatchingRule rule = attributeType.getSubstringMatchingRule();
    if (rule == null)
    {
      return false;
    }
    if (! optionsEqual(attributeOptions, f.attributeOptions))
    try
    {
      Assertion thisAssertion = rule.getSubstringAssertion(subInitialElement, subAnyElements, subFinalElement);
      Assertion thatAssertion = rule.getSubstringAssertion(f.subInitialElement, f.subAnyElements, f.subFinalElement);
      return thisAssertion.equals(thatAssertion);
    }
    catch (DecodeException e)
    {
      return false;
    }
    if (subInitialElement == null)
    {
      if (f.subInitialElement != null)
      {
        return false;
      }
    }
    else
    {
      if (f.subInitialElement == null)
      {
        return false;
      }
      try
      {
        ByteString nSI1 =
             smr.normalizeSubstring(subInitialElement);
        ByteString nSI2 =
             smr.normalizeSubstring(f.subInitialElement);
        if (! nSI1.equals(nSI2))
        {
          return false;
        }
      }
      catch (Exception e)
      {
        return false;
      }
    }
    if (subFinalElement == null)
    {
      if (f.subFinalElement != null)
      {
        return false;
      }
    }
    else
    {
      if (f.subFinalElement == null)
      {
        return false;
      }
      try
      {
        ByteString nSF1 =
             smr.normalizeSubstring(subFinalElement);
        ByteString nSF2 =
             smr.normalizeSubstring(f.subFinalElement);
        if (! nSF1.equals(nSF2))
        {
          return false;
        }
      }
      catch (Exception e)
      {
        return false;
      }
    }
    if (subAnyElements.size() != f.subAnyElements.size())
    {
      return false;
    }
    for (int i = 0; i < subAnyElements.size(); i++)
    {
      try
      {
        ByteString nSA1 =
             smr.normalizeSubstring(subAnyElements.get(i));
        ByteString nSA2 =
             smr.normalizeSubstring(f.subAnyElements.get(i));
        if (! nSA1.equals(nSA2))
        {
          return false;
        }
      }
      catch (Exception e)
      {
        return false;
      }
    }
    return true;
  }
@@ -3820,10 +3740,8 @@
      }
      else
      {
        MatchingRule mr =
             DirectoryServer.getMatchingRule(
                  toLowerCase(matchingRuleID));
        if (mr == null)
        MatchingRule mrule = DirectoryServer.getMatchingRule(toLowerCase(matchingRuleID));
        if (mrule == null)
        {
          return false;
        }
@@ -3831,8 +3749,8 @@
        {
          try
          {
            Assertion assertion = mr.getAssertion(f.assertionValue);
            return assertion.matches(mr.normalizeAttributeValue(assertionValue)).toBoolean();
            Assertion assertion = mrule.getAssertion(f.assertionValue);
            return assertion.matches(mrule.normalizeAttributeValue(assertionValue)).toBoolean();
          }
          catch (Exception e)
          {
@@ -3921,24 +3839,7 @@
      case EQUALITY:
        return typeAndAssertionHashCode();
      case SUBSTRING:
        hashCode = attributeType.hashCode();
        SubstringMatchingRule smr =
             attributeType.getSubstringMatchingRule();
        hashCode = hashCode(hashCode, smr, subInitialElement);
        if (subAnyElements != null)
        {
          for (ByteString e : subAnyElements)
          {
              hashCode = hashCode(hashCode, smr, e);
          }
        }
        hashCode = hashCode(hashCode, smr, subFinalElement);
        return hashCode;
        return substringHashCode();
      case GREATER_OR_EQUAL:
        return typeAndAssertionHashCode();
      case LESS_OR_EQUAL:
@@ -3948,35 +3849,41 @@
      case APPROXIMATE_MATCH:
        return typeAndAssertionHashCode();
      case EXTENSIBLE_MATCH:
        hashCode = 0;
        if (attributeType != null)
        {
          hashCode += attributeType.hashCode();
        }
        if (dnAttributes)
        {
          hashCode++;
        }
        if (matchingRuleID != null)
        {
          hashCode += matchingRuleID.hashCode();
        }
        if (assertionValue != null)
        {
          hashCode += assertionValue.hashCode();
        }
        return hashCode;
        return extensibleHashCode();
      default:
        return 1;
    }
  }
  /** Returns the hash code for extensible filter. */
  private int extensibleHashCode()
  {
    int hashCode = 0;
    if (attributeType != null)
    {
      hashCode += attributeType.hashCode();
    }
    if (dnAttributes)
    {
      hashCode++;
    }
    if (matchingRuleID != null)
    {
      hashCode += matchingRuleID.hashCode();
    }
    if (assertionValue != null)
    {
      hashCode += assertionValue.hashCode();
    }
    return hashCode;
  }
  private int typeAndAssertionHashCode()
  {
    final int hashCode = attributeType.hashCode();
@@ -3990,25 +3897,30 @@
    }
  }
  private int hashCode(int hashCode, SubstringMatchingRule smr,
      ByteString subElem)
  /** Returns hash code to use for substring filter. */
  private int substringHashCode()
  {
    if (subElem != null)
    int hashCode = attributeType.hashCode();
    final MatchingRule rule = attributeType.getSubstringMatchingRule();
    if (rule != null)
    {
      if (smr == null)
      try
      {
        hashCode += subElem.hashCode();
        return hashCode + rule.getSubstringAssertion(subInitialElement, subAnyElements, subFinalElement).hashCode();
      }
      else
      catch (DecodeException e)
      {
        try
        {
          hashCode += smr.normalizeSubstring(subElem).hashCode();
        }
        catch (Exception e) {}
        logger.traceException(e);
      }
    }
    // Fallback to hash code based on elements
    hashCode += subInitialElement.hashCode();
    for (ByteString e : subAnyElements)
    {
      hashCode += e.hashCode();
    }
    hashCode += subFinalElement.hashCode();
    return hashCode;
  }
opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/schema/CaseExactIA5SubstringMatchingRuleTest.java
@@ -22,6 +22,7 @@
 *
 *
 *      Copyright 2006-2008 Sun Microsystems, Inc.
 *      Portions Copyright 2014 ForgeRock AS
 */
package org.opends.server.schema;
@@ -35,6 +36,38 @@
    SubstringMatchingRuleTest
{
  /** {@inheritDoc} */
  @Override
  @DataProvider(name="substringMatchData")
  public Object[][] createSubstringMatchData()
  {
    return new Object[][] {
        {"this is a value", "", new String[] {"this"}, "", true },
        {"this is a value", "", new String[] {"is"}, "", true },
        {"this is a value", "th", new String[] {"is"}, "", true },
        {"this is a value", "this", new String[] {"is"}, "", true },
        {"this is a value", "this", new String[] {"is"}, "value", true },
        {"this is a value", "this", new String[] {"is"}, "ue", true },
        {"this is a value", "this", new String[] {"is"}, "e", true },
        {"this is a value", "this", new String[] {"is"}, "valu", false },
        {"this is a value", "THIS", new String[] {"is"}, "", false },
        {"this is a value", "h", new String[] {"is"}, "", false },
        {"this is a value", "", new String[] {"a"}, "", true },
        {"this is a value", "", new String[] {"value"}, "", true },
        {"this is a value", "", new String[] {" "}, "", true },
        {"this is a value", "", new String[] {"this", "is", "a", "value"}, "", true },
         // The matching rule requires ordered non overlapping substrings
         // Issue #730 was not valid.
        {"this is a value", "", new String[] {"value", "this"}, "", false },
        {"this is a value", "", new String[] {"this", "this is"}, "", false },
        {"this is a value", "", new String[] {"his is", "a val",}, "", true },
        {"this is a value", "", new String[] {"not",}, "", false },
        {"this is a value", "", new String[] {"THIS",}, "", false },
        {"this is a value", "", new String[] {"this", "not"}, "", false },
        {"this is a value", "", new String[] {"    "}, "", true },
    };
  }
  /**
   * {@inheritDoc}
   */
opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/schema/CaseExactSubstringMatchingRuleTest.java
@@ -22,6 +22,7 @@
 *
 *
 *      Copyright 2006-2008 Sun Microsystems, Inc.
 *      Portions Copyright 2014 ForgeRock AS
 */
package org.opends.server.schema;
@@ -35,6 +36,38 @@
    SubstringMatchingRuleTest
{
  /** {@inheritDoc} */
  @Override
  @DataProvider(name="substringMatchData")
  public Object[][] createSubstringMatchData()
  {
    return new Object[][] {
        {"this is a value", "", new String[] {"this"}, "", true },
        {"this is a value", "", new String[] {"is"}, "", true },
        {"this is a value", "th", new String[] {"is"}, "", true },
        {"this is a value", "this", new String[] {"is"}, "", true },
        {"this is a value", "this", new String[] {"is"}, "value", true },
        {"this is a value", "this", new String[] {"is"}, "ue", true },
        {"this is a value", "this", new String[] {"is"}, "e", true },
        {"this is a value", "this", new String[] {"is"}, "valu", false },
        {"this is a value", "THIS", new String[] {"is"}, "", false },
        {"this is a value", "h", new String[] {"is"}, "", false },
        {"this is a value", "", new String[] {"a"}, "", true },
        {"this is a value", "", new String[] {"value"}, "", true },
        {"this is a value", "", new String[] {" "}, "", true },
        {"this is a value", "", new String[] {"this", "is", "a", "value"}, "", true },
         // The matching rule requires ordered non overlapping substrings
         // Issue #730 was not valid.
        {"this is a value", "", new String[] {"value", "this"}, "", false },
        {"this is a value", "", new String[] {"this", "this is"}, "", false },
        {"this is a value", "", new String[] {"his is", "a val",}, "", true },
        {"this is a value", "", new String[] {"not",}, "", false },
        {"this is a value", "", new String[] {"THIS",}, "", false },
        {"this is a value", "", new String[] {"this", "not"}, "", false },
        {"this is a value", "", new String[] {"    "}, "", true },
    };
  }
  /**
   * {@inheritDoc}
   */
opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/schema/CaseIgnoreIA5SubstringMatchingRuleTest.java
@@ -22,6 +22,7 @@
 *
 *
 *      Copyright 2006-2008 Sun Microsystems, Inc.
 *      Portions Copyright 2014 ForgeRock AS
 */
package org.opends.server.schema;
@@ -35,6 +36,38 @@
    SubstringMatchingRuleTest
{
  /** {@inheritDoc} */
  @Override
  @DataProvider(name="substringMatchData")
  public Object[][] createSubstringMatchData()
  {
    return new Object[][] {
        {"this is a value", "", new String[] {"this"}, "", true },
        {"this is a value", "", new String[] {"is"}, "", true },
        {"this is a value", "th", new String[] {"is"}, "", true },
        {"this is a value", "this", new String[] {"is"}, "", true },
        {"this is a value", "this", new String[] {"is"}, "value", true },
        {"this is a value", "this", new String[] {"is"}, "ue", true },
        {"this is a value", "this", new String[] {"is"}, "e", true },
        {"this is a value", "this", new String[] {"is"}, "valu", false },
        {"this is a value", "THIS", new String[] {"is"}, "", true },
        {"this is a value", "h", new String[] {"is"}, "", false },
        {"this is a value", "", new String[] {"a"}, "", true },
        {"this is a value", "", new String[] {"value"}, "", true },
        {"this is a value", "", new String[] {" "}, "", true },
        {"this is a value", "", new String[] {"this", "is", "a", "value"}, "", true },
         // The matching rule requires ordered non overlapping substrings
         // Issue #730 was not valid.
        {"this is a value", "", new String[] {"value", "this"}, "", false },
        {"this is a value", "", new String[] {"this", "this is"}, "", false },
        {"this is a value", "", new String[] {"his is", "a val",}, "", true },
        {"this is a value", "", new String[] {"not",}, "", false },
        {"this is a value", "", new String[] {"THIS",}, "", true },
        {"this is a value", "", new String[] {"this", "not"}, "", false },
        {"this is a value", "", new String[] {"    "}, "", true },
    };
  }
  /**
   * {@inheritDoc}
   */
opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/schema/CaseIgnoreSubstringMatchingRuleTest.java
@@ -22,6 +22,7 @@
 *
 *
 *      Copyright 2006-2008 Sun Microsystems, Inc.
 *      Portions Copyright 2014 ForgeRock AS
 */
package org.opends.server.schema;
@@ -35,6 +36,38 @@
    SubstringMatchingRuleTest
{
  /** {@inheritDoc} */
  @Override
  @DataProvider(name="substringMatchData")
  public Object[][] createSubstringMatchData()
  {
    return new Object[][] {
        {"this is a value", "", new String[] {"this"}, "", true },
        {"this is a value", "", new String[] {"is"}, "", true },
        {"this is a value", "th", new String[] {"is"}, "", true },
        {"this is a value", "this", new String[] {"is"}, "", true },
        {"this is a value", "this", new String[] {"is"}, "value", true },
        {"this is a value", "this", new String[] {"is"}, "ue", true },
        {"this is a value", "this", new String[] {"is"}, "e", true },
        {"this is a value", "this", new String[] {"is"}, "valu", false },
        {"this is a value", "THIS", new String[] {"is"}, "", true },
        {"this is a value", "h", new String[] {"is"}, "", false },
        {"this is a value", "", new String[] {"a"}, "", true },
        {"this is a value", "", new String[] {"value"}, "", true },
        {"this is a value", "", new String[] {" "}, "", true },
        {"this is a value", "", new String[] {"this", "is", "a", "value"}, "", true },
         // The matching rule requires ordered non overlapping substrings
         // Issue #730 was not valid.
        {"this is a value", "", new String[] {"value", "this"}, "", false },
        {"this is a value", "", new String[] {"this", "this is"}, "", false },
        {"this is a value", "", new String[] {"his is", "a val",}, "", true },
        {"this is a value", "", new String[] {"not",}, "", false },
        {"this is a value", "", new String[] {"THIS",}, "", true },
        {"this is a value", "", new String[] {"this", "not"}, "", false },
        {"this is a value", "", new String[] {"    "}, "", true },
    };
  }
  /**
   * {@inheritDoc}
   */
opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/schema/NumericStringSubstringMatchingRuleTest.java
@@ -22,6 +22,7 @@
 *
 *
 *      Copyright 2006-2008 Sun Microsystems, Inc.
 *      Portions Copyright 2014 ForgeRock AS
 */
package org.opends.server.schema;
@@ -35,6 +36,30 @@
    SubstringMatchingRuleTest
{
  /** {@inheritDoc} */
  @Override
  @DataProvider(name="substringMatchData")
  public Object[][] createSubstringMatchData()
  {
    return new Object[][] {
      // The matching rule requires ordered non overlapping substrings.
      // Issue #730 was not valid.
     {"123456789", "", new String[] {"123", "234", "567", "789"}, "",  false },
     {"123456789", "", new String[] {"123", "234"}, "",  false },
     {"123456789", "", new String[] {"567", "234"}, "",  false },
     {"123456789", "", new String[] {"123", "456"}, "",  true },
     {"123456789", "", new String[] {"123"}, "",  true },
     {"123456789", "", new String[] {"456"}, "",  true },
     {"123456789", "", new String[] {"789"}, "",  true },
     {"123456789", "", new String[] {"123456789"}, "",  true },
     {"123456789", "", new String[] {"1234567890"}, "",  false },
     {"123456789", "", new String[] {"9"}, "",  true },
     {"123456789", "", new String[] {"1"}, "",  true },
     {"123456789", "", new String[] {"0"}, "",  false },
     {"123456789", "", new String[] {"    "}, "",  true },
     {"123456789", "", new String[] {"0123"}, "",  false },
    };
  }
  /**
   * {@inheritDoc}
   */
opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/schema/SubstringMatchingRuleTest.java
@@ -30,6 +30,7 @@
import java.util.List;
import org.opends.server.api.SubstringMatchingRule;
import org.forgerock.opendj.ldap.Assertion;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.ByteSequence;
import org.testng.annotations.DataProvider;
@@ -42,9 +43,16 @@
 * This class is intended to be extended by one class for each substring
 * matching rules.
 */
@SuppressWarnings("javadoc")
public abstract class SubstringMatchingRuleTest extends SchemaTestCase
{
  /**
   * Generate data for the test of the assertion match.
   */
  @DataProvider(name="substringMatchData")
  public abstract Object[][] createSubstringMatchData();
  /**
   * Generate data for the test of the middle string match.
   *
   * @return the data for the test of the middle string match.
@@ -114,8 +122,7 @@
   * Test the normalization and the initial substring match.
   */
  @Test(dataProvider= "substringInitialMatchData")
  public void initialMatchingRules(
      String value, String initial, Boolean result) throws Exception
  public void initialMatchingRules(String value, String initial, Boolean result) throws Exception
  {
    SubstringMatchingRule rule = getRule();
@@ -140,8 +147,7 @@
   * Test the normalization and the final substring match.
   */
  @Test(dataProvider= "substringFinalMatchData")
  public void finalMatchingRules(
      String value, String finalValue, Boolean result) throws Exception
  public void finalMatchingRules(String value, String finalValue, Boolean result) throws Exception
  {
    SubstringMatchingRule rule = getRule();
@@ -160,4 +166,25 @@
          value + " and " + finalValue);
    }
  }
  @Test(dataProvider= "substringMatchData")
  public void testSubstringAssertion(String value, String initialSub, String[] middleSubs, String finalSub,
      Boolean expectedResult) throws Exception
  {
    SubstringMatchingRule rule = getRule();
    ByteString normalizedValue = rule.normalizeAttributeValue(ByteString.valueOf(value));
    ArrayList<ByteSequence> anySubs = new ArrayList<ByteSequence>(middleSubs.length);
    for (String sub : middleSubs)
    {
      anySubs.add(ByteString.valueOf(sub));
    }
    Assertion assertion = rule.getSubstringAssertion(
        ByteString.valueOf(initialSub),
        anySubs,
        ByteString.valueOf(finalSub));
    Boolean result = assertion.matches(normalizedValue).toBoolean();
    assertEquals(result,  expectedResult);
  }
}