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

neil_a_wilson
18.00.2007 a9b31a7fd6f665f10673721758e5dedc025135c7
Update the build script to temporarily allow up to 256MB of memory for the JVM
used to run the unit tests. Also, re-enable the JMX and search filter test
cases that had been disabled.
4 files modified
2181 ■■■■ changed files
opends/build.xml 2 ●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/jmx/JmxConnectTest.java 12 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/jmx/postConnectedDisconnectTest.java 2 ●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/types/SearchFilterTests.java 2165 ●●●● patch | view | raw | blame | history
opends/build.xml
@@ -923,8 +923,8 @@
      <jvmarg value="-Demma.coverage.out.merge=false" />
      <jvmarg value="-Dorg.opends.server.BuildRoot=${basedir}" />
      <jvmarg value="-Dorg.opends.test.suppressOutput=${org.opends.test.suppressOutput}" />
      <jvmarg value="-Xmx${MEM}" />
      <jvmarg value="-Xms${MEM}" />
      <jvmarg value="-Xmx256M" />
      <xmlfileset dir="${unittest.resource.dir}" includes="testng.xml" />
    </testng>
opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/jmx/JmxConnectTest.java
@@ -111,7 +111,7 @@
   * credentials are OK and refused when the credentials are invalid.
   *
   */
  @Test(enabled = false, dataProvider="simpleConnect")
  @Test(enabled = true, dataProvider="simpleConnect")
  public void simpleConnect(String user, String password,
      boolean expectedResult) throws Exception
  {
@@ -147,7 +147,7 @@
   * Test simple JMX get.
   *
   */
  @Test(enabled = false, dataProvider="simpleGet")
  @Test(enabled = true, dataProvider="simpleGet")
  public void simpleGet(String dn, String attributeName, Object value)
     throws Exception
  {
@@ -173,7 +173,7 @@
  /**
   * Test setting some config attribute through jmx.
   */
  @Test(enabled = false)
  @Test(enabled = true)
  public void simpleSet() throws Exception
  {
    OpendsJmxConnector connector = connect("cn=directory manager", "password",
@@ -208,7 +208,7 @@
   *
   * @throws Exception
   */
  @Test(enabled = false)
  @Test(enabled = true)
  public void disable() throws Exception
  {
    
@@ -277,7 +277,7 @@
   * Test changing JMX port through LDAP
   * @throws Exception
   */
  @Test(enabled=false)
  @Test(enabled=true)
  public void changePort() throws Exception
  {
    // Connect to the JMX service and get the current port
@@ -352,7 +352,7 @@
   * credentials are OK and refused when the credentials are invalid.
   *
   */
  @Test(enabled=false)
  @Test(enabled=true)
  public void sslConnect() throws Exception
  {
    // configure the JMX ssl key manager
opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/jmx/postConnectedDisconnectTest.java
@@ -62,7 +62,7 @@
   * Perform a simple connect.
   * @throws Exception If something wrong occurs.
   */
  @Test(enabled = false)
  @Test(enabled = true)
  public void checkPostconnectDisconnectPlugin() throws Exception
  {
    // Before the test, how many time postconnect and postdisconnect
opends/tests/unit-tests-testng/src/server/org/opends/server/types/SearchFilterTests.java
@@ -53,1089 +53,1088 @@
 * with extensible match, attribute options, and there is a lot of code
 * that is not reachable because it's in exception handling code that
 * is not exercisable externally.
 */
// These are commented out because they make TestNG run out of memory.
   */
public class SearchFilterTests extends DirectoryServerTestCase {
//
//  @BeforeClass
//  public void setupClass() throws Exception {
//    TestCaseUtils.startServer();
//  }
//
//  ////////////////////////////////////////////////////////////////////////////
//  ////////////////////////////////////////////////////////////////////////////
//  //
//  // createFilterFromString
//  //
//  ////////////////////////////////////////////////////////////////////////////
//  ////////////////////////////////////////////////////////////////////////////
//
//  // -------------------------------------------------------------------------
//  //
//  // Test valid filters.
//  //
//  // -------------------------------------------------------------------------
//
//  // These are valid filters.
//  @DataProvider(name = "paramsCreateFilterFromStringValidFilters")
//  public Object[][] paramsCreateFilterFromStringValidFilters() {
//    return new Object[][]{
//            {"(&)", "(&)"},
//            {"(|)", "(|)"},
//            {"(sn=test)", "(sn=test)"},
//            {"(sn=*)", "(sn=*)"},
//            {"(sn=)", "(sn=)"},
//            {"(sn=*test*)", "(sn=*test*)"},
//
//            {"(!(sn=test))", "(!(sn=test))"},
//            {"(|(sn=test)(sn=test2))", "(|(sn=test)(sn=test2))"},
//
//            {"(&(sn=test))", "(&(sn=test))"},
//            {"(|(sn=test))", "(|(sn=test))"},
//    };
//  }
//
//  @Test(dataProvider = "paramsCreateFilterFromStringValidFilters")
//  public void testCreateFilterFromStringValidFilters(
//          String originalFilter,
//          String expectedToStringFilter
//  ) throws DirectoryException {
//    runRecreateFilterTest(originalFilter, expectedToStringFilter);
//  }
//
//  private void runRecreateFilterTest(
//          String originalFilter,
//          String expectedToStringFilter
//  ) throws DirectoryException {
//    String regenerated = SearchFilter.createFilterFromString(originalFilter).toString();
//    Assert.assertEquals(regenerated, expectedToStringFilter, "original=" + originalFilter + ", expected=" + expectedToStringFilter);
//  }
//
//  // These are valid filters.
//  @DataProvider(name = "escapeSequenceFilters")
//  public Object[][] escapeSequenceFilters() {
//    final char[] CHAR_NIBBLES = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
//                                 'a', 'b', 'c', 'd', 'e', 'f',
//                                 'A', 'B', 'C', 'D', 'E', 'F'};
//
//    final byte[] BYTE_NIBBLES = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
//                                 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
//                                 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};
//
//    List<String[]> allParameters = new ArrayList<String[]>();
//    for (int i = 0; i < CHAR_NIBBLES.length; i++) {
//      char highNibble = CHAR_NIBBLES[i];
//      byte highByteNibble = BYTE_NIBBLES[i];
//      for (int j = 0; j < CHAR_NIBBLES.length; j++) {
//        char lowNibble = CHAR_NIBBLES[j];
//        byte lowByteNibble = BYTE_NIBBLES[j];
//        String inputChar = "\\" + highNibble + lowNibble;
//        byte byteValue = (byte)((((int)highByteNibble) << 4) | lowByteNibble);
//        String outputChar = getFilterValueForChar(byteValue);
//
//        // Exact match
//        String inputFilter = "(sn=" + inputChar + ")";
//        String outputFilter = "(sn=" + outputChar + ")";
//        allParameters.add(new String[]{inputFilter, outputFilter});
//
//        // Substring
//        inputFilter = "(sn=" + inputChar + "*" + inputChar + "*" + inputChar + ")";
//        outputFilter = "(sn=" + outputChar + "*" + outputChar + "*" + outputChar + ")";
//        allParameters.add(new String[]{inputFilter, outputFilter});
//
//        // <=
//        inputFilter = "(sn<=" + inputChar + ")";
//        outputFilter = "(sn<=" + outputChar + ")";
//        allParameters.add(new String[]{inputFilter, outputFilter});
//
//        // >=
//        inputFilter = "(sn>=" + inputChar + ")";
//        outputFilter = "(sn>=" + outputChar + ")";
//        allParameters.add(new String[]{inputFilter, outputFilter});
//
//        // =~
//        inputFilter = "(sn>=" + inputChar + ")";
//        outputFilter = "(sn>=" + outputChar + ")";
//        allParameters.add(new String[]{inputFilter, outputFilter});
//
//        // =~
//        inputFilter = "(sn:caseExactMatch:=" + inputChar + ")";
//        outputFilter = "(sn:caseExactMatch:=" + outputChar + ")";
//        allParameters.add(new String[]{inputFilter, outputFilter});
//      }
//    }
//
//    return (Object[][]) allParameters.toArray(new String[][]{});
//  }
//
//
//  // These are filters with invalid escape sequences.
//  @DataProvider(name = "invalidEscapeSequenceFilters")
//  public Object[][] invalidEscapeSequenceFilters() {
//    final char[] VALID_NIBBLES = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
//                                 'a', 'b', 'c', 'd', 'e', 'f',
//                                 'A', 'B', 'C', 'D', 'E', 'F'};
//
//    final char[] INVALID_NIBBBLES = {'g', 'z', 'G', 'Z', '-', '=', '+', '\00', ')',
//                                     'n', 't', '\\'};
//
//    List<String> invalidEscapeSequences = new ArrayList<String>();
//
//    for (int i = 0; i < VALID_NIBBLES.length; i++) {
//      char validNibble = VALID_NIBBLES[i];
//      for (int j = 0; j < INVALID_NIBBBLES.length; j++) {
//        char invalidNibble = INVALID_NIBBBLES[j];
//
//        invalidEscapeSequences.add("\\" + validNibble + invalidNibble);
//        invalidEscapeSequences.add("\\" + invalidNibble + validNibble);
//      }
//      // Also do a test case where we only have one character in the escape sequence.
//      invalidEscapeSequences.add("\\" + validNibble);
//    }
//
//    List<String[]> allParameters = new ArrayList<String[]>();
//    for (String invalidEscape : invalidEscapeSequences) {
//      // Exact match
//      allParameters.add(new String[]{"(sn=" + invalidEscape + ")"});
//      allParameters.add(new String[]{"(sn=" + invalidEscape});
//
//      // Substring
//      allParameters.add(new String[]{"(sn=" + invalidEscape + "*" + invalidEscape + "*" + invalidEscape + ")"});
//      allParameters.add(new String[]{"(sn=" + invalidEscape + "*" + invalidEscape + "*" + invalidEscape});
//
//      // <=
//      allParameters.add(new String[]{"(sn<=" + invalidEscape + ")"});
//      allParameters.add(new String[]{"(sn<=" + invalidEscape});
//
//      // >=
//      allParameters.add(new String[]{"(sn>=" + invalidEscape + ")"});
//      allParameters.add(new String[]{"(sn>=" + invalidEscape});
//
//      // =~
//      allParameters.add(new String[]{"(sn>=" + invalidEscape + ")"});
//      allParameters.add(new String[]{"(sn>=" + invalidEscape});
//
//      // =~
//      allParameters.add(new String[]{"(sn:caseExactMatch:=" + invalidEscape + ")"});
//      allParameters.add(new String[]{"(sn:caseExactMatch:=" + invalidEscape});
//    }
//
//    return (Object[][]) allParameters.toArray(new String[][]{});
//  }
//
//
//  /**
//   * @return a value that can be used in an LDAP filter.
//   */
//  private String getFilterValueForChar(byte value) {
//    if (((value & 0x7F) != value) ||  // Not 7-bit clean
//         (value <= 0x1F) ||           // Below the printable character range
//         (value == 0x28) ||           // Open parenthesis
//         (value == 0x29) ||           // Close parenthesis
//         (value == 0x2A) ||           // Asterisk
//         (value == 0x5C) ||           // Backslash
//         (value == 0x7F))             // Delete character
//    {
//      return "\\" + StaticUtils.byteToHex(value);
//    } else {
//      return "" + ((char)value);
//    }
//  }
//
//  @Test(dataProvider = "escapeSequenceFilters")
//  public void testRecreateFilterWithEscape(
//          String originalFilter,
//          String expectedToStringFilter
//  ) throws DirectoryException {
//    runRecreateFilterTest(originalFilter, expectedToStringFilter);
//  }
//
//  @Test(dataProvider = "invalidEscapeSequenceFilters",
//        expectedExceptions = DirectoryException.class)
//  public void testFilterWithInvalidEscape(
//          String filterWithInvalidEscape)
//          throws DirectoryException {
//    // This should fail with a parse error.
//    SearchFilter.createFilterFromString(filterWithInvalidEscape);
//  }
//
//
//  // -------------------------------------------------------------------------
//  //
//  // Test invalid filters.
//  //
//  // -------------------------------------------------------------------------
//
//  //
//  // Invalid filters that are detected.
//  //
//
//  @DataProvider(name = "invalidFilters")
//  public Object[][] invalidFilters() {
//    return new Object[][]{
//            {null},
//            {"(cn)"},
//            {"()"},
//            {"("},
//            {"(&(sn=test)"},
//            {"(|(sn=test)"},
//            {"(!(sn=test)"},
//            {"(&(sn=test)))"},
//            {"(|(sn=test)))"},
//            // TODO: open a bug for this.
////            {"(!(sn=test)))"},
//            {"(sn=\\A)"},
//            {"(sn=\\1H)"},
//            {"(sn=\\H1)"},
//    };
//  }
//
//  @Test(dataProvider = "invalidFilters",
//        expectedExceptions = DirectoryException.class)
//  public void testCreateFilterFromStringInvalidFilters(String invalidFilter)
//          throws DirectoryException {
//    SearchFilter.createFilterFromString(invalidFilter).toString();
//  }
//
//  //
//  // This is more or less the same as what's above, but it's for invalid
//  // filters that are not currently detected by the parser.  To turn these
//  // on, remove them from the broken group.  As the code is modified to handle
//  // these cases, please add these test cases to the
//  // paramsCreateFilterFromStringInvalidFilters DataProvider.
//  //
//
//  @DataProvider(name = "uncaughtInvalidFilters")
//  public Object[][] paramsCreateFilterFromStringUncaughtInvalidFilters() {
//    return new Object[][]{
//            {"(cn=**)"},
//            {"( sn = test )"},
//            {"&(cn=*)"},
//            {"(!(sn=test)(sn=test2))"},
//            {"(objectclass=**)"},
//    };
//  }
//
//  @Test(dataProvider = "uncaughtInvalidFilters",
//        expectedExceptions = DirectoryException.class,
//        // FIXME:  These currently aren't detected
//        enabled = false)
//  public void testCreateFilterFromStringUncaughtInvalidFilters(String invalidFilter)
//          throws DirectoryException {
//    SearchFilter.createFilterFromString(invalidFilter).toString();
//  }
//
//
//  ////////////////////////////////////////////////////////////////////////////
//  ////////////////////////////////////////////////////////////////////////////
//  //
//  // matches
//  //
//  ////////////////////////////////////////////////////////////////////////////
//  ////////////////////////////////////////////////////////////////////////////
//
//  private static final String JOHN_SMITH_LDIF = TestCaseUtils.makeLdif(
//          "dn: cn=John Smith,dc=example,dc=com",
//          "objectclass: inetorgperson",
//          "cn: John Smith",
//          "cn;lang-en: Jonathan Smith",
//          "sn: Smith",
//          "givenname: John",
//          "internationaliSDNNumber: 12345",
//          "displayName: *",
//          "title: tattoos",
//          "labeledUri: http://opends.org/john"
//          );
//
//  @DataProvider(name = "matchesParams")
//  public Object[][] matchesParams() {
//    return new Object[][]{
//            {JOHN_SMITH_LDIF, "(objectclass=inetorgperson)", true},
//            {JOHN_SMITH_LDIF, "(objectclass=iNetOrgPeRsOn)", true},
//            {JOHN_SMITH_LDIF, "(objectclass=*)", true},
//            {JOHN_SMITH_LDIF, "(objectclass=person)", false},
//
//            {JOHN_SMITH_LDIF, "(cn=John Smith)", true},
//            {JOHN_SMITH_LDIF, "(cn=Jonathan Smith)", true},
//            {JOHN_SMITH_LDIF, "(cn=JOHN SmITh)", true},
//            {JOHN_SMITH_LDIF, "(cn=*)", true},
//            {JOHN_SMITH_LDIF, "(cn=*John Smith*)", true},
//            {JOHN_SMITH_LDIF, "(cn=*Jo*ith*)", true},
//            {JOHN_SMITH_LDIF, "(cn=*Jo*i*th*)", true},
//            {JOHN_SMITH_LDIF, "(cn=*Joh*ohn*)", false},  // this shouldn't match
//            {JOHN_SMITH_LDIF, "(internationaliSDNNumber=*23*34*)", false},  // this shouldn't match
//
//            {JOHN_SMITH_LDIF, "(cn=*o*n*)", true},
//            {JOHN_SMITH_LDIF, "(cn=*n*o*)", false},
//
//            // attribute options
//            {JOHN_SMITH_LDIF, "(cn;lang-en=Jonathan Smith)", true},
//            {JOHN_SMITH_LDIF, "(cn;lang-en=Jonathan Smithe)", false},
//            {JOHN_SMITH_LDIF, "(cn;lang-fr=Jonathan Smith)", false},
//            {JOHN_SMITH_LDIF, "(cn;lang-en=*jon*an*)", true},
//
//            // attribute subtypes.  Enable this once 593 is fixed.
////            {JOHN_SMITH_LDIF, "(name=John Smith)", true},
////            {JOHN_SMITH_LDIF, "(name=*Smith*)", true},
////            {JOHN_SMITH_LDIF, "(name;lang-en=Jonathan Smith)", true},  // ? maybe not
////            {JOHN_SMITH_LDIF, "(name;lang-en=*Jonathan*)", true},  // ? maybe not
//
//            // Enable this once
////            {JOHN_SMITH_LDIF, "(cn=*Jo**i*th*)", true},
//
//            {JOHN_SMITH_LDIF, "(cn=\\4Aohn*)", true}, // \4A = J
//            {JOHN_SMITH_LDIF, "(|(cn=Jane Smith)(cn=John Smith))", true},
//
//            {JOHN_SMITH_LDIF, "(title~=tattoos)", true},
//            {JOHN_SMITH_LDIF, "(title~=tattos)", true},
//
//            {JOHN_SMITH_LDIF, "(labeledUri=http://opends.org/john)", true},
//            {JOHN_SMITH_LDIF, "(labeledUri=http://opends.org/JOHN)", false},
//            {JOHN_SMITH_LDIF, "(labeledUri=http://*/john)", true},
//            {JOHN_SMITH_LDIF, "(labeledUri=http://*/JOHN)", false},
//
//            {JOHN_SMITH_LDIF, "(cn>=John Smith)", true},
//            {JOHN_SMITH_LDIF, "(cn>=J)", true},
//            {JOHN_SMITH_LDIF, "(cn<=J)", false},
//
//            {JOHN_SMITH_LDIF, "(cn=Jane Smith)", false},
//
//            {JOHN_SMITH_LDIF, "(displayName=\\2A)", true}, // \2A = *
//
//            // 2.5.4.4 is Smith
//            {JOHN_SMITH_LDIF, "(2.5.4.4=Smith)", true},
//
//            {JOHN_SMITH_LDIF, "(sn:caseExactMatch:=Smith)", true},
//            {JOHN_SMITH_LDIF, "(sn:caseExactMatch:=smith)", false},
//
//            // Test cases for 730
//            {JOHN_SMITH_LDIF, "(internationaliSDNNumber=*12*45*)", true},
//            {JOHN_SMITH_LDIF, "(internationaliSDNNumber=*45*12*)", false},
//
//            // TODO: open a bug for all of these.
////            {JOHN_SMITH_LDIF, "(:caseExactMatch:=Smith)", true},
////            {JOHN_SMITH_LDIF, "(:caseExactMatch:=NotSmith)", false},
//
//            // Look at 4515 for some more examples.  Ask Neil.
////            {JOHN_SMITH_LDIF, "(:dn:caseExactMatch:=example)", true},
////            {JOHN_SMITH_LDIF, "(:dn:caseExactMatch:=notexample)", false},
//    };
//  }
//
//  @Test(dataProvider = "matchesParams")
//  public void testMatches(String ldifEntry, String filterStr, boolean expectMatch) throws Exception {
//    runMatchTest(ldifEntry, filterStr, expectMatch);
//  }
//
//  private void runMatchTest(String ldifEntry, String filterStr, boolean expectMatch) throws Exception {
//    Entry entry = TestCaseUtils.entryFromLdifString(ldifEntry);
//
//    runSingleMatchTest(entry, filterStr, expectMatch);
//    runSingleMatchTest(entry, "(|" + filterStr + ")", expectMatch);
//    runSingleMatchTest(entry, "(&" + filterStr + ")", expectMatch);
//    runSingleMatchTest(entry, "(!" + filterStr + ")", !expectMatch);
//  }
//
//  private void runSingleMatchTest(Entry entry, String filterStr, boolean expectMatch) throws Exception {
//    final SearchFilter filter = SearchFilter.createFilterFromString(filterStr);
//    boolean matches = filter.matchesEntry(entry);
//    Assert.assertEquals(matches, expectMatch, "Filter=" + filter + "\nEntry=" + entry);
//  }
//
//  ////////////////////////////////////////////////////////////////////////////
//  ////////////////////////////////////////////////////////////////////////////
//  //
//  // Filter construction
//  //
//  ////////////////////////////////////////////////////////////////////////////
//  ////////////////////////////////////////////////////////////////////////////
//
//
//  /**
//   *
//   */
//  private static final String makeSimpleLdif(String givenname, String sn) {
//    String cn = givenname + " " + sn;
//    return TestCaseUtils.makeLdif(
//          "dn: cn=" + cn + ",dc=example,dc=com",
//          "objectclass: inetorgperson",
//          "cn: " + cn,
//          "sn: " + sn,
//          "givenname: " + givenname
//          );
//  }
//
//  private static final String JANE_SMITH_LDIF = makeSimpleLdif("Jane", "Smith");
//  private static final String JANE_AUSTIN_LDIF = makeSimpleLdif("Jane", "Austin");
//  private static final String JOE_SMITH_LDIF = makeSimpleLdif("Joe", "Smith");
//  private static final String JOE_AUSTIN_LDIF = makeSimpleLdif("Joe", "Austin");
//
//  private static final List<String> ALL_ENTRIES_LDIF =
//          Collections.unmodifiableList(asList(JANE_SMITH_LDIF,
//                                              JANE_AUSTIN_LDIF,
//                                              JOE_SMITH_LDIF,
//                                              JOE_AUSTIN_LDIF));
//
//
//  /**
//   *
//   */
//  private List<String> getEntriesExcluding(List<String> matchedEntries) {
//    List<String> unmatched = new ArrayList<String>(ALL_ENTRIES_LDIF);
//    unmatched.removeAll(matchedEntries);
//    return unmatched;
//  }
//
//
//  /**
//   *
//   */
//  private static class FilterDescription {
//    private SearchFilter searchFilter;
//
//    private List<String> matchedEntriesLdif;
//    private List<String> unmatchedEntriesLdif;
//
//    private FilterType filterType;
//    private LinkedHashSet<SearchFilter> filterComponents = new LinkedHashSet<SearchFilter>();
//    private SearchFilter notComponent;
//    private AttributeValue assertionValue;
//    private AttributeType attributeType;
//    private ByteString subInitialElement;
//    private List<ByteString> subAnyElements = new ArrayList<ByteString>();
//    private ByteString subFinalElement;
//    private String matchingRuleId;
//    private boolean dnAttributes;
//
//
//    /**
//     *
//     */
//    public void validateFilterFields() throws AssertionError {
//      if (!searchFilter.getFilterType().equals(filterType)) {
//        throwUnequalError("filterTypes");
//      }
//
//      if (!searchFilter.getFilterComponents().equals(filterComponents)) {
//        throwUnequalError("filterComponents");
//      }
//
//      if (!objectsAreEqual(searchFilter.getNotComponent(), notComponent)) {
//        throwUnequalError("notComponent");
//      }
//
//      if (!objectsAreEqual(searchFilter.getAssertionValue(), assertionValue)) {
//        throwUnequalError("assertionValue");
//      }
//
//      if (!objectsAreEqual(searchFilter.getAttributeType(), attributeType)) {
//        throwUnequalError("attributeType");
//      }
//
//      if (!objectsAreEqual(searchFilter.getSubInitialElement(), subInitialElement)) {
//        throwUnequalError("subInitial");
//      }
//
//      if (!objectsAreEqual(searchFilter.getSubAnyElements(), subAnyElements)) {
//        throwUnequalError("subAny");
//      }
//
//      if (!objectsAreEqual(searchFilter.getSubFinalElement(), subFinalElement)) {
//        throwUnequalError("subFinal");
//      }
//
//      if (!objectsAreEqual(searchFilter.getMatchingRuleID(), matchingRuleId)) {
//        throwUnequalError("matchingRuleId");
//      }
//
//      if (searchFilter.getDNAttributes() != dnAttributes) {
//        throwUnequalError("dnAttributes");
//      }
//    }
//
//
//    /**
//     *
//     */
//    private void throwUnequalError(String message) throws AssertionError {
//      throw new AssertionError("Filter differs from what is expected '" + message + "' differ.\n" + toString());
//    }
//
//
//    /**
//     *
//     */
//    @Override
//    public String toString() {
//      return "FilterDescription: \n" +
//              "\tsearchFilter=" + searchFilter + "\n" +
//              "\tfilterType = " + filterType + "\n" +
//              "\tfilterComponents = " + filterComponents + "\n" +
//              "\tnotComponent = " + notComponent + "\n" +
//              "\tassertionValue = " + assertionValue + "\n" +
//              "\tattributeType = " + attributeType + "\n" +
//              "\tsubInitialElement = " + subInitialElement + "\n" +
//              "\tsubAnyElements = " + subAnyElements + "\n" +
//              "\tsubFinalElement = " + subFinalElement + "\n" +
//              "\tmatchingRuleId = " + dnAttributes + "\n";
//    }
//
//
//    /**
//     *
//     */
//    private FilterDescription negate() {
//      FilterDescription negation = new FilterDescription();
//      negation.searchFilter = SearchFilter.createNOTFilter(searchFilter);
//
//      // Flip-flop these
//      negation.matchedEntriesLdif = unmatchedEntriesLdif;
//      negation.unmatchedEntriesLdif = matchedEntriesLdif;
//
//      negation.filterType = FilterType.NOT;
//      negation.notComponent = searchFilter;
//
//      return negation;
//    }
//
//
//    /**
//     *
//     */
//    public FilterDescription clone() {
//      FilterDescription that = new FilterDescription();
//
//      that.searchFilter = this.searchFilter;
//      that.matchedEntriesLdif = this.matchedEntriesLdif;
//      that.unmatchedEntriesLdif = this.unmatchedEntriesLdif;
//      that.filterType = this.filterType;
//      that.filterComponents = this.filterComponents;
//      that.notComponent = this.notComponent;
//      that.assertionValue = this.assertionValue;
//      that.attributeType = this.attributeType;
//      that.subInitialElement = this.subInitialElement;
//      that.subAnyElements = this.subAnyElements;
//      that.subFinalElement = this.subFinalElement;
//      that.matchingRuleId = this.matchingRuleId;
//      that.dnAttributes = this.dnAttributes;
//
//      return that;
//    }
//  }
//
//
//  /**
//   *
//   */
//  private FilterDescription assertionFilterDescription(FilterType filterType,
//                                                       String attributeType,
//                                                       String attributeValue,
//                                                       List<String> matchedEntries) {
//    FilterDescription description = new FilterDescription();
//
//    description.filterType = filterType;
//    description.attributeType = DirectoryServer.getAttributeType(attributeType);
//    description.assertionValue = new AttributeValue(description.attributeType, attributeValue);
//
//    if (filterType == FilterType.EQUALITY) {
//      description.searchFilter = SearchFilter.createEqualityFilter(description.attributeType,
//                                                                   description.assertionValue);
//    } else if (filterType == FilterType.LESS_OR_EQUAL) {
//      description.searchFilter = SearchFilter.createLessOrEqualFilter(description.attributeType,
//                                                                      description.assertionValue);
//    } else if (filterType == FilterType.GREATER_OR_EQUAL) {
//      description.searchFilter = SearchFilter.createGreaterOrEqualFilter(description.attributeType,
//                                                                         description.assertionValue);
//    } else if (filterType == FilterType.APPROXIMATE_MATCH) {
//      description.searchFilter = SearchFilter.createApproximateFilter(description.attributeType,
//                                                                      description.assertionValue);
//    } else {
//      fail(filterType + " is not handled.");
//    }
//
//    description.matchedEntriesLdif = matchedEntries;
//    description.unmatchedEntriesLdif = getEntriesExcluding(matchedEntries);
//
//    return description;
//  }
//
//
//  /**
//   *
//   */
//  private FilterDescription equalityFilterDescription(String attributeType,
//                                                      String attributeValue,
//                                                      List<String> matchedEntries) {
//    return assertionFilterDescription(FilterType.EQUALITY, attributeType, attributeValue, matchedEntries);
//  }
//
//
//  /**
//   *
//   */
//  private FilterDescription lessEqualFilterDescription(String attributeType,
//                                                       String attributeValue,
//                                                       List<String> matchedEntries) {
//    return assertionFilterDescription(FilterType.LESS_OR_EQUAL, attributeType, attributeValue, matchedEntries);
//  }
//
//
//  /**
//   *
//   */
//  private FilterDescription greaterEqualFilterDescription(String attributeType,
//                                                          String attributeValue,
//                                                          List<String> matchedEntries) {
//    return assertionFilterDescription(FilterType.GREATER_OR_EQUAL, attributeType, attributeValue, matchedEntries);
//  }
//
//
//  /**
//   *
//   */
//  private FilterDescription approximateFilterDescription(String attributeType,
//                                                         String attributeValue,
//                                                         List<String> matchedEntries) {
//    return assertionFilterDescription(FilterType.APPROXIMATE_MATCH, attributeType, attributeValue, matchedEntries);
//  }
//
//
//  /**
//   *
//   */
//  private FilterDescription substringFilterDescription(String attributeType,
//                                                       String subInitial,
//                                                       List<String> subAny,
//                                                       String subFinal,
//                                                       List<String> matchedEntries) {
//    FilterDescription description = new FilterDescription();
//
//    description.filterType = FilterType.SUBSTRING;
//    description.attributeType = DirectoryServer.getAttributeType(attributeType);
//
//    description.subInitialElement = new ASN1OctetString(subInitial);
//    description.subAnyElements = new ArrayList<ByteString>();
//    for (int i = 0; (subAny != null) && (i < subAny.size()); i++) {
//      String s = subAny.get(i);
//      description.subAnyElements.add(new ASN1OctetString(s));
//    }
//    description.subFinalElement = new ASN1OctetString(subFinal);
//
//    description.searchFilter = SearchFilter.createSubstringFilter(description.attributeType,
//            description.subInitialElement,
//            description.subAnyElements,
//            description.subFinalElement);
//
//
//    description.matchedEntriesLdif = matchedEntries;
//    description.unmatchedEntriesLdif = getEntriesExcluding(matchedEntries);
//
//    return description;
//  }
//
//
//  /**
//   *
//   */
//  private List<FilterDescription> getNotFilters(List<FilterDescription> filters) {
//    List<FilterDescription> notFilters = new ArrayList<FilterDescription>();
//
//    for (FilterDescription filter: filters) {
//      notFilters.add(filter.negate());
//    }
//
//    return notFilters;
//  }
//
//
//  /**
//   *
//   */
//  private FilterDescription getAndFilter(List<FilterDescription> filters) {
//    FilterDescription andFilter = new FilterDescription();
//
//    List<String> matchedEntries = new ArrayList<String>(ALL_ENTRIES_LDIF);
//    List<SearchFilter> filterComponents = new ArrayList<SearchFilter>();
//
//    for (FilterDescription filter: filters) {
//      matchedEntries.retainAll(filter.matchedEntriesLdif);
//      filterComponents.add(filter.searchFilter);
//    }
//
//    andFilter.searchFilter = SearchFilter.createANDFilter(filterComponents);
//    andFilter.filterComponents = new LinkedHashSet<SearchFilter>(filterComponents);
//
//    andFilter.filterType = FilterType.AND;
//
//    andFilter.matchedEntriesLdif = matchedEntries;
//    andFilter.unmatchedEntriesLdif = getEntriesExcluding(matchedEntries);
//
//    return andFilter;
//  }
//
//
//  /**
//   *
//   */
//  private List<FilterDescription> getAndFilters(List<FilterDescription> filters) {
//    List<FilterDescription> andFilters = new ArrayList<FilterDescription>();
//
//    for (FilterDescription first: filters) {
//      for (FilterDescription second: filters) {
//        andFilters.add(getAndFilter(asList(first, second)));
//      }
//    }
//
//    return andFilters;
//  }
//
//
//  /**
//   *
//   */
//  private FilterDescription getOrFilter(List<FilterDescription> filters) {
//    FilterDescription orFilter = new FilterDescription();
//
//    List<String> unmatchedEntries = new ArrayList<String>(ALL_ENTRIES_LDIF);
//    List<SearchFilter> filterComponents = new ArrayList<SearchFilter>();
//
//    for (FilterDescription filter: filters) {
//      unmatchedEntries.retainAll(filter.unmatchedEntriesLdif);
//      filterComponents.add(filter.searchFilter);
//    }
//
//    orFilter.searchFilter = SearchFilter.createORFilter(filterComponents);
//    orFilter.filterComponents = new LinkedHashSet<SearchFilter>(filterComponents);
//
//    orFilter.filterType = FilterType.OR;
//
//    // Since we're not using Sets, we've whittled down unmatched entries from
//    // the full set instead of adding to matchedEntries, which would lead
//    // to duplicates.
//    orFilter.unmatchedEntriesLdif = unmatchedEntries;
//    orFilter.matchedEntriesLdif = getEntriesExcluding(unmatchedEntries);
//
//    return orFilter;
//  }
//
//
//  /**
//   *
//   */
//  private List<FilterDescription> getOrFilters(List<FilterDescription> filters) {
//    List<FilterDescription> orFilters = new ArrayList<FilterDescription>();
//
//    for (FilterDescription first: filters) {
//      for (FilterDescription second: filters) {
//        orFilters.add(getOrFilter(asList(first, second)));
//      }
//    }
//
//    return orFilters;
//  }
//
//
//  /**
//   *
//   */
//  private List<FilterDescription> getEqualityFilters() throws Exception {
//    List<FilterDescription> descriptions = new ArrayList<FilterDescription>();
//
//    descriptions.add(equalityFilterDescription("sn", "Smith",
//            asList(JANE_SMITH_LDIF, JOE_SMITH_LDIF)));
//
//    descriptions.add(equalityFilterDescription("givenname", "Jane",
//            asList(JANE_SMITH_LDIF, JANE_AUSTIN_LDIF)));
//
//    return descriptions;
//  }
//
//
//  /**
//   *
//   */
//  private List<FilterDescription> getApproximateFilters() throws Exception {
//    List<FilterDescription> descriptions = new ArrayList<FilterDescription>();
//
//    descriptions.add(approximateFilterDescription("sn", "Smythe",
//            asList(JANE_SMITH_LDIF, JOE_SMITH_LDIF)));
//
//    return descriptions;
//  }
//
//
//  /**
//   *
//   */
//  private List<FilterDescription> getSubstringFilters() throws Exception {
//    List<FilterDescription> descriptions = new ArrayList<FilterDescription>();
//
//    descriptions.add(substringFilterDescription(
//            "sn",
//            "S", asList("i"), "th", // S*i*th
//            asList(JANE_SMITH_LDIF, JOE_SMITH_LDIF)));
//
//    return descriptions;
//  }
//
//
//  /**
//   *
//   */
//  private List<FilterDescription> getInequalityFilters() throws Exception {
//    List<FilterDescription> descriptions = new ArrayList<FilterDescription>();
//
//    descriptions.add(lessEqualFilterDescription("sn", "Aus",
//            (List<String>)(new ArrayList<String>())));
//
//    descriptions.add(greaterEqualFilterDescription("sn", "Aus",
//            asList(JANE_AUSTIN_LDIF, JOE_AUSTIN_LDIF,
//                   JANE_SMITH_LDIF, JOE_SMITH_LDIF)));
//
//
//    descriptions.add(lessEqualFilterDescription("sn", "Smi",
//            asList(JANE_AUSTIN_LDIF, JOE_AUSTIN_LDIF)));
//
//    descriptions.add(greaterEqualFilterDescription("sn", "Smi",
//            asList(JANE_SMITH_LDIF, JOE_SMITH_LDIF)));
//
//
//    descriptions.add(lessEqualFilterDescription("sn", "Smith",
//            asList(JANE_AUSTIN_LDIF, JOE_AUSTIN_LDIF,
//                   JANE_SMITH_LDIF, JOE_SMITH_LDIF)));
//
//    descriptions.add(greaterEqualFilterDescription("sn", "Smith",
//            asList(JANE_SMITH_LDIF, JOE_SMITH_LDIF)));
//
//
//    return descriptions;
//  }
//
//
//  /**
//   * Updates to this should also be made in getMinimalFilterDescriptionList.
//   * @see #getMinimalFilterDescriptionList
//   */
//  private List<FilterDescription> getFilterDescriptionList() throws Exception {
//    List<FilterDescription> baseDescriptions = new ArrayList<FilterDescription>();
//
//    baseDescriptions.addAll(getEqualityFilters());
//    baseDescriptions.addAll(getInequalityFilters());
//    baseDescriptions.addAll(getApproximateFilters());
//    baseDescriptions.addAll(getSubstringFilters());
//    baseDescriptions.addAll(getNotFilters(baseDescriptions));
//
//    List<FilterDescription> allDescriptions = new ArrayList<FilterDescription>();
//
//    allDescriptions.addAll(getAndFilters(baseDescriptions));
//    allDescriptions.addAll(getOrFilters(baseDescriptions));
//    allDescriptions.addAll(baseDescriptions);
//
//    return allDescriptions;
//  }
//
//
//  /**
//   *
//   */
//  public List<FilterDescription> getMinimalFilterDescriptionList() throws Exception {
//    List<FilterDescription> baseDescriptions = new ArrayList<FilterDescription>();
//    List<FilterDescription> allDescriptions = new ArrayList<FilterDescription>();
//
//    baseDescriptions.addAll(getEqualityFilters().subList(0, 1));
//    baseDescriptions.addAll(getInequalityFilters().subList(0, 2));
//    baseDescriptions.addAll(getSubstringFilters().subList(0, 1));
//    baseDescriptions.addAll(getNotFilters(baseDescriptions).subList(0, 1));
//
//    allDescriptions.addAll(baseDescriptions);
//    allDescriptions.addAll(getAndFilters(baseDescriptions).subList(0, 2));
//    allDescriptions.addAll(getOrFilters(baseDescriptions).subList(0, 2));
//
//    return allDescriptions;
//  }
//
//
//
//  /**
//   *
//   */
//  @DataProvider(name = "filterDescriptions")
//  public Object[][] getFilterDescriptions() throws Exception {
//    List<FilterDescription> allDescriptions = getFilterDescriptionList();
//
//    // Now convert to [][]
//    FilterDescription[][] descriptionArray = new FilterDescription[allDescriptions.size()][];
//    for (int i = 0; i < allDescriptions.size(); i++) {
//      FilterDescription description = allDescriptions.get(i);
//      descriptionArray[i] = new FilterDescription[]{description};
//    }
//
//    return descriptionArray;
//  }
//
//
//  @Test(dataProvider = "filterDescriptions")
//  public void testFilterConstruction(FilterDescription description) throws Exception {
//    description.validateFilterFields();
//
//    for (String ldif: description.matchedEntriesLdif) {
//      Entry entry = TestCaseUtils.entryFromLdifString(ldif);
//      if (!description.searchFilter.matchesEntry(entry)) {
//        fail("Expected to match entry. " + description + entry);
//      }
//    }
//
//    for (String ldif: description.unmatchedEntriesLdif) {
//      Entry entry = TestCaseUtils.entryFromLdifString(ldif);
//      if (description.searchFilter.matchesEntry(entry)) {
//        fail("Should not have matched entry. " + description + entry);
//      }
//    }
//  }
//
//  // TODO: test more on extensible match and attribute options
//  // TODO: test that we fail when creating filters without specifying all of the parameters
//  // TODO: we need to test attribute options!
//  // TODO: test the audio attribute since it's octetStringMatch
//  // TODO: test the homePhone attribute since   EQUALITY telephoneNumberMatch SUBSTR telephoneNumberSubstringsMatch
//  // TODO: test labeledURI since it's  caseExactMatch SUBSTR caseExactSubstringsMatch
//  // TODO: test mail since it's EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5SubstringsMatch
//  // TODO: test secretary since it's distinguishedNameMatch
//  // TODO: test x500UniqueIdentifier since it's bitStringMatch
//
//
//  private static final Object[][] TEST_EQUALS_PARAMS = new Object[][]{
//          // These have duplicates, and their String representation should even reflect that.
//          {"(&(sn=Smith))", "(&(sn=Smith)(sn=Smith))", true, true},
//          {"(|(sn=Smith))", "(|(sn=Smith)(sn=Smith))", true, true},
//
//          // These are reordered, so they are equivalent, but their String representations will differ
//          {"(&(sn=Smith)(sn<=Aus))", "(&(sn<=Aus)(sn=Smith))", true, false},
//          {"(|(sn=Smith)(sn<=Aus))", "(|(sn<=Aus)(sn=Smith))", true, false},
//
//          // These should be case insensitive
//          {"(SN=Smith)", "(sn=Smith)", true, true},
//          {"(sn=smith)", "(sn=Smith)", true, false},
//          {"(SN=S*th)", "(sn=S*th)", true, true},
//
//          {"(sn:caseExactMatch:=Smith)", "(sn:caseExactMatch:=Smith)", true, true},
//
//          // This demonstrates bug 704.
////          {"(sn:caseExactMatch:=Smith)", "(sn:caseExactMatch:=smith)", false, false},
//
//          // TODO: open a bug for this.
////          {"(:dn:caseExactMatch:=example)", "(:DN:caseExactMatch:=example)", true, true}, // ? String not match
//
//          // 2.5.4.4 is 'sn'
//          {"(2.5.4.4=Smith)", "(2.5.4.4=Smith)", true, true},
//          {"(2.5.4.4=Smith)", "(sn=Smith)", true, true},
//
//          {"(sn;lang-en=Smith)", "(sn;lang-en=Smith)", true, true},
//
//          // This demonstrates bug 706
////          {"(sn;lang-en=Smith)", "(sn=Smith)", false, false},
//
//
//          // This demonstrates bug 705.
////          {"(sn=s*t*h)", "(sn=S*T*H)", true, false},
//
//          // These should be case sensitive
//          {"(labeledURI=http://opends.org)", "(labeledURI=http://OpenDS.org)", false, false},
//          {"(labeledURI=http://opends*)", "(labeledURI=http://OpenDS*)", false, false},
//
//          // These are WYSIWIG
//          {"(sn=*)", "(sn=*)", true, true},
//          {"(sn=S*)", "(sn=S*th)", false, false},
//          {"(sn=*S)", "(sn=S*th)", false, false},
//          {"(sn=S*t)", "(sn=S*th)", false, false},
//          {"(sn=*i*t*)", "(sn=*i*t*)", true, true},
//          {"(sn=*t*i*)", "(sn=*i*t*)", false, false},  // Test case for 695
//          {"(sn=S*i*t)", "(sn=S*th)", false, false},
//          {"(sn=Smith)", "(sn=Smith)", true, true},
//          {"(sn=Smith)", "(sn<=Aus)", false, false},
//          {"(sn=Smith)", "(sn>=Aus)", false, false},
//          {"(sn=Smith)", "(sn=S*i*th)", false, false},
//          {"(sn=Smith)", "(!(sn=Smith))", false, false},
//          {"(sn=Smith)", "(&(sn=Smith)(sn<=Aus))", false, false},
//          {"(sn=Smith)", "(|(sn=Smith)(sn<=Aus))", false, false},
//          {"(sn<=Aus)", "(sn<=Aus)", true, true},
//          {"(sn<=Aus)", "(sn>=Aus)", false, false},
//          {"(sn<=Aus)", "(sn=S*i*th)", false, false},
//          {"(sn<=Aus)", "(!(sn=Smith))", false, false},
//          {"(sn<=Aus)", "(&(sn=Smith)(sn=Smith))", false, false},
//          {"(sn<=Aus)", "(&(sn=Smith)(sn<=Aus))", false, false},
//          {"(sn<=Aus)", "(|(sn=Smith)(sn=Smith))", false, false},
//          {"(sn<=Aus)", "(|(sn=Smith)(sn<=Aus))", false, false},
//          {"(sn>=Aus)", "(sn>=Aus)", true, true},
//          {"(sn>=Aus)", "(sn=S*i*th)", false, false},
//          {"(sn>=Aus)", "(!(sn=Smith))", false, false},
//          {"(sn>=Aus)", "(&(sn=Smith)(sn=Smith))", false, false},
//          {"(sn>=Aus)", "(&(sn=Smith)(sn<=Aus))", false, false},
//          {"(sn>=Aus)", "(|(sn=Smith)(sn=Smith))", false, false},
//          {"(sn>=Aus)", "(|(sn=Smith)(sn<=Aus))", false, false},
//          {"(sn=S*i*th)", "(sn=S*i*th)", true, true},
//          {"(sn=S*i*th)", "(!(sn=Smith))", false, false},
//          {"(sn=S*i*th)", "(&(sn=Smith)(sn=Smith))", false, false},
//          {"(sn=S*i*th)", "(&(sn=Smith)(sn<=Aus))", false, false},
//          {"(sn=S*i*th)", "(|(sn=Smith)(sn=Smith))", false, false},
//          {"(sn=S*i*th)", "(|(sn=Smith)(sn<=Aus))", false, false},
//          {"(!(sn=Smith))", "(!(sn=Smith))", true, true},
//          {"(!(sn=Smith))", "(&(sn=Smith)(sn=Smith))", false, false},
//          {"(!(sn=Smith))", "(&(sn=Smith)(sn<=Aus))", false, false},
//          {"(!(sn=Smith))", "(|(sn=Smith)(sn=Smith))", false, false},
//          {"(!(sn=Smith))", "(|(sn=Smith)(sn<=Aus))", false, false},
//          {"(&(sn=Smith)(sn=Smith))", "(&(sn=Smith)(sn=Smith))", true, true},
//          {"(&(sn=Smith)(sn=Smith))", "(&(sn=Smith)(sn<=Aus))", false, false},
//          {"(&(sn=Smith)(sn=Smith))", "(|(sn=Smith)(sn=Smith))", false, false},
//          {"(&(sn=Smith)(sn=Smith))", "(|(sn=Smith)(sn<=Aus))", false, false},
//          {"(&(sn=Smith)(sn<=Aus))", "(&(sn=Smith)(sn<=Aus))", true, true},
//          {"(&(sn=Smith)(sn<=Aus))", "(|(sn=Smith)(sn=Smith))", false, false},
//          {"(&(sn=Smith)(sn<=Aus))", "(|(sn=Smith)(sn<=Aus))", false, false},
//          {"(|(sn=Smith)(sn=Smith))", "(|(sn=Smith)(sn=Smith))", true, true},
//          {"(|(sn=Smith)(sn=Smith))", "(|(sn=Smith)(sn<=Aus))", false, false},
//          {"(|(sn=Smith)(sn<=Aus))", "(|(sn=Smith)(sn<=Aus))", true, true},
//          {"(&(sn=Smith)(sn<=Aus))", "(&(sn=Smith)(sn>=Aus))", false, false},
//          {"(|(sn=Smith)(sn<=Aus))", "(|(sn=Smith)(sn>=Aus))", false, false},
//
//
//  };
//
//
//  /**
//   *
//   */
//  @DataProvider(name = "equalsTest")
//  public Object[][] getEqualsTests() throws Exception {
//    return TEST_EQUALS_PARAMS;
//  }
//
//
//  /**
//   *
//   */
//  @Test(dataProvider = "equalsTest")
//  public void testEquals(String stringFilter1, String stringFilter2, boolean expectEquals, boolean expectStringEquals) throws Exception {
//    SearchFilter filter1 = SearchFilter.createFilterFromString(stringFilter1);
//    SearchFilter filter2 = SearchFilter.createFilterFromString(stringFilter2);
//
//    boolean actualEquals = filter1.equals(filter2);
//    assertEquals(actualEquals, expectEquals,
//                 "Expected " + filter1 + (expectEquals ? " == " : " != ") + filter2);
//
//    // Test symmetry
//    actualEquals = filter2.equals(filter1);
//    assertEquals(actualEquals, expectEquals,
//                 "Expected " + filter1 + (expectEquals ? " == " : " != ") + filter2);
//
//    if (expectEquals) {
//      assertEquals(filter1.hashCode(), filter2.hashCode(),
//                   "Hash codes differ for " + filter1 + " and " + filter2);
//    }
//
//    // Test toString
//    actualEquals = filter2.toString().equals(filter1.toString());
//    assertEquals(actualEquals, expectStringEquals,
//                 "Expected " + filter1 + (expectStringEquals ? " == " : " != ") + filter2);
//  }
  @BeforeClass
  public void setupClass() throws Exception {
    TestCaseUtils.startServer();
  }
  ////////////////////////////////////////////////////////////////////////////
  ////////////////////////////////////////////////////////////////////////////
  //
  // createFilterFromString
  //
  ////////////////////////////////////////////////////////////////////////////
  ////////////////////////////////////////////////////////////////////////////
  // -------------------------------------------------------------------------
  //
  // Test valid filters.
  //
  // -------------------------------------------------------------------------
  // These are valid filters.
  @DataProvider(name = "paramsCreateFilterFromStringValidFilters")
  public Object[][] paramsCreateFilterFromStringValidFilters() {
    return new Object[][]{
            {"(&)", "(&)"},
            {"(|)", "(|)"},
            {"(sn=test)", "(sn=test)"},
            {"(sn=*)", "(sn=*)"},
            {"(sn=)", "(sn=)"},
            {"(sn=*test*)", "(sn=*test*)"},
            {"(!(sn=test))", "(!(sn=test))"},
            {"(|(sn=test)(sn=test2))", "(|(sn=test)(sn=test2))"},
            {"(&(sn=test))", "(&(sn=test))"},
            {"(|(sn=test))", "(|(sn=test))"},
    };
  }
  @Test(dataProvider = "paramsCreateFilterFromStringValidFilters")
  public void testCreateFilterFromStringValidFilters(
          String originalFilter,
          String expectedToStringFilter
  ) throws DirectoryException {
    runRecreateFilterTest(originalFilter, expectedToStringFilter);
  }
  private void runRecreateFilterTest(
          String originalFilter,
          String expectedToStringFilter
  ) throws DirectoryException {
    String regenerated = SearchFilter.createFilterFromString(originalFilter).toString();
    Assert.assertEquals(regenerated, expectedToStringFilter, "original=" + originalFilter + ", expected=" + expectedToStringFilter);
  }
  // These are valid filters.
  @DataProvider(name = "escapeSequenceFilters")
  public Object[][] escapeSequenceFilters() {
    final char[] CHAR_NIBBLES = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
                                 'a', 'b', 'c', 'd', 'e', 'f',
                                 'A', 'B', 'C', 'D', 'E', 'F'};
    final byte[] BYTE_NIBBLES = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
                                 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
                                 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};
    List<String[]> allParameters = new ArrayList<String[]>();
    for (int i = 0; i < CHAR_NIBBLES.length; i++) {
      char highNibble = CHAR_NIBBLES[i];
      byte highByteNibble = BYTE_NIBBLES[i];
      for (int j = 0; j < CHAR_NIBBLES.length; j++) {
        char lowNibble = CHAR_NIBBLES[j];
        byte lowByteNibble = BYTE_NIBBLES[j];
        String inputChar = "\\" + highNibble + lowNibble;
        byte byteValue = (byte)((((int)highByteNibble) << 4) | lowByteNibble);
        String outputChar = getFilterValueForChar(byteValue);
        // Exact match
        String inputFilter = "(sn=" + inputChar + ")";
        String outputFilter = "(sn=" + outputChar + ")";
        allParameters.add(new String[]{inputFilter, outputFilter});
        // Substring
        inputFilter = "(sn=" + inputChar + "*" + inputChar + "*" + inputChar + ")";
        outputFilter = "(sn=" + outputChar + "*" + outputChar + "*" + outputChar + ")";
        allParameters.add(new String[]{inputFilter, outputFilter});
        // <=
        inputFilter = "(sn<=" + inputChar + ")";
        outputFilter = "(sn<=" + outputChar + ")";
        allParameters.add(new String[]{inputFilter, outputFilter});
        // >=
        inputFilter = "(sn>=" + inputChar + ")";
        outputFilter = "(sn>=" + outputChar + ")";
        allParameters.add(new String[]{inputFilter, outputFilter});
        // =~
        inputFilter = "(sn>=" + inputChar + ")";
        outputFilter = "(sn>=" + outputChar + ")";
        allParameters.add(new String[]{inputFilter, outputFilter});
        // =~
        inputFilter = "(sn:caseExactMatch:=" + inputChar + ")";
        outputFilter = "(sn:caseExactMatch:=" + outputChar + ")";
        allParameters.add(new String[]{inputFilter, outputFilter});
      }
    }
    return (Object[][]) allParameters.toArray(new String[][]{});
  }
  // These are filters with invalid escape sequences.
  @DataProvider(name = "invalidEscapeSequenceFilters")
  public Object[][] invalidEscapeSequenceFilters() {
    final char[] VALID_NIBBLES = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
                                 'a', 'b', 'c', 'd', 'e', 'f',
                                 'A', 'B', 'C', 'D', 'E', 'F'};
    final char[] INVALID_NIBBBLES = {'g', 'z', 'G', 'Z', '-', '=', '+', '\00', ')',
                                     'n', 't', '\\'};
    List<String> invalidEscapeSequences = new ArrayList<String>();
    for (int i = 0; i < VALID_NIBBLES.length; i++) {
      char validNibble = VALID_NIBBLES[i];
      for (int j = 0; j < INVALID_NIBBBLES.length; j++) {
        char invalidNibble = INVALID_NIBBBLES[j];
        invalidEscapeSequences.add("\\" + validNibble + invalidNibble);
        invalidEscapeSequences.add("\\" + invalidNibble + validNibble);
      }
      // Also do a test case where we only have one character in the escape sequence.
      invalidEscapeSequences.add("\\" + validNibble);
    }
    List<String[]> allParameters = new ArrayList<String[]>();
    for (String invalidEscape : invalidEscapeSequences) {
      // Exact match
      allParameters.add(new String[]{"(sn=" + invalidEscape + ")"});
      allParameters.add(new String[]{"(sn=" + invalidEscape});
      // Substring
      allParameters.add(new String[]{"(sn=" + invalidEscape + "*" + invalidEscape + "*" + invalidEscape + ")"});
      allParameters.add(new String[]{"(sn=" + invalidEscape + "*" + invalidEscape + "*" + invalidEscape});
      // <=
      allParameters.add(new String[]{"(sn<=" + invalidEscape + ")"});
      allParameters.add(new String[]{"(sn<=" + invalidEscape});
      // >=
      allParameters.add(new String[]{"(sn>=" + invalidEscape + ")"});
      allParameters.add(new String[]{"(sn>=" + invalidEscape});
      // =~
      allParameters.add(new String[]{"(sn>=" + invalidEscape + ")"});
      allParameters.add(new String[]{"(sn>=" + invalidEscape});
      // =~
      allParameters.add(new String[]{"(sn:caseExactMatch:=" + invalidEscape + ")"});
      allParameters.add(new String[]{"(sn:caseExactMatch:=" + invalidEscape});
    }
    return (Object[][]) allParameters.toArray(new String[][]{});
  }
  /**
   * @return a value that can be used in an LDAP filter.
   */
  private String getFilterValueForChar(byte value) {
    if (((value & 0x7F) != value) ||  // Not 7-bit clean
         (value <= 0x1F) ||           // Below the printable character range
         (value == 0x28) ||           // Open parenthesis
         (value == 0x29) ||           // Close parenthesis
         (value == 0x2A) ||           // Asterisk
         (value == 0x5C) ||           // Backslash
         (value == 0x7F))             // Delete character
    {
      return "\\" + StaticUtils.byteToHex(value);
    } else {
      return "" + ((char)value);
    }
  }
  @Test(dataProvider = "escapeSequenceFilters")
  public void testRecreateFilterWithEscape(
          String originalFilter,
          String expectedToStringFilter
  ) throws DirectoryException {
    runRecreateFilterTest(originalFilter, expectedToStringFilter);
  }
  @Test(dataProvider = "invalidEscapeSequenceFilters",
        expectedExceptions = DirectoryException.class)
  public void testFilterWithInvalidEscape(
          String filterWithInvalidEscape)
          throws DirectoryException {
    // This should fail with a parse error.
    SearchFilter.createFilterFromString(filterWithInvalidEscape);
  }
  // -------------------------------------------------------------------------
  //
  // Test invalid filters.
  //
  // -------------------------------------------------------------------------
  //
  // Invalid filters that are detected.
  //
  @DataProvider(name = "invalidFilters")
  public Object[][] invalidFilters() {
    return new Object[][]{
            {null},
            {"(cn)"},
            {"()"},
            {"("},
            {"(&(sn=test)"},
            {"(|(sn=test)"},
            {"(!(sn=test)"},
            {"(&(sn=test)))"},
            {"(|(sn=test)))"},
            // TODO: open a bug for this.
//            {"(!(sn=test)))"},
            {"(sn=\\A)"},
            {"(sn=\\1H)"},
            {"(sn=\\H1)"},
    };
  }
  @Test(dataProvider = "invalidFilters",
        expectedExceptions = DirectoryException.class)
  public void testCreateFilterFromStringInvalidFilters(String invalidFilter)
          throws DirectoryException {
    SearchFilter.createFilterFromString(invalidFilter).toString();
  }
  //
  // This is more or less the same as what's above, but it's for invalid
  // filters that are not currently detected by the parser.  To turn these
  // on, remove them from the broken group.  As the code is modified to handle
  // these cases, please add these test cases to the
  // paramsCreateFilterFromStringInvalidFilters DataProvider.
  //
  @DataProvider(name = "uncaughtInvalidFilters")
  public Object[][] paramsCreateFilterFromStringUncaughtInvalidFilters() {
    return new Object[][]{
            {"(cn=**)"},
            {"( sn = test )"},
            {"&(cn=*)"},
            {"(!(sn=test)(sn=test2))"},
            {"(objectclass=**)"},
    };
  }
  @Test(dataProvider = "uncaughtInvalidFilters",
        expectedExceptions = DirectoryException.class,
        // FIXME:  These currently aren't detected
        enabled = false)
  public void testCreateFilterFromStringUncaughtInvalidFilters(String invalidFilter)
          throws DirectoryException {
    SearchFilter.createFilterFromString(invalidFilter).toString();
  }
  ////////////////////////////////////////////////////////////////////////////
  ////////////////////////////////////////////////////////////////////////////
  //
  // matches
  //
  ////////////////////////////////////////////////////////////////////////////
  ////////////////////////////////////////////////////////////////////////////
  private static final String JOHN_SMITH_LDIF = TestCaseUtils.makeLdif(
          "dn: cn=John Smith,dc=example,dc=com",
          "objectclass: inetorgperson",
          "cn: John Smith",
          "cn;lang-en: Jonathan Smith",
          "sn: Smith",
          "givenname: John",
          "internationaliSDNNumber: 12345",
          "displayName: *",
          "title: tattoos",
          "labeledUri: http://opends.org/john"
          );
  @DataProvider(name = "matchesParams")
  public Object[][] matchesParams() {
    return new Object[][]{
            {JOHN_SMITH_LDIF, "(objectclass=inetorgperson)", true},
            {JOHN_SMITH_LDIF, "(objectclass=iNetOrgPeRsOn)", true},
            {JOHN_SMITH_LDIF, "(objectclass=*)", true},
            {JOHN_SMITH_LDIF, "(objectclass=person)", false},
            {JOHN_SMITH_LDIF, "(cn=John Smith)", true},
            {JOHN_SMITH_LDIF, "(cn=Jonathan Smith)", true},
            {JOHN_SMITH_LDIF, "(cn=JOHN SmITh)", true},
            {JOHN_SMITH_LDIF, "(cn=*)", true},
            {JOHN_SMITH_LDIF, "(cn=*John Smith*)", true},
            {JOHN_SMITH_LDIF, "(cn=*Jo*ith*)", true},
            {JOHN_SMITH_LDIF, "(cn=*Jo*i*th*)", true},
            {JOHN_SMITH_LDIF, "(cn=*Joh*ohn*)", false},  // this shouldn't match
            {JOHN_SMITH_LDIF, "(internationaliSDNNumber=*23*34*)", false},  // this shouldn't match
            {JOHN_SMITH_LDIF, "(cn=*o*n*)", true},
            {JOHN_SMITH_LDIF, "(cn=*n*o*)", false},
            // attribute options
            {JOHN_SMITH_LDIF, "(cn;lang-en=Jonathan Smith)", true},
            {JOHN_SMITH_LDIF, "(cn;lang-en=Jonathan Smithe)", false},
            {JOHN_SMITH_LDIF, "(cn;lang-fr=Jonathan Smith)", false},
            {JOHN_SMITH_LDIF, "(cn;lang-en=*jon*an*)", true},
            // attribute subtypes.  Enable this once 593 is fixed.
//            {JOHN_SMITH_LDIF, "(name=John Smith)", true},
//            {JOHN_SMITH_LDIF, "(name=*Smith*)", true},
//            {JOHN_SMITH_LDIF, "(name;lang-en=Jonathan Smith)", true},  // ? maybe not
//            {JOHN_SMITH_LDIF, "(name;lang-en=*Jonathan*)", true},  // ? maybe not
            // Enable this once
//            {JOHN_SMITH_LDIF, "(cn=*Jo**i*th*)", true},
            {JOHN_SMITH_LDIF, "(cn=\\4Aohn*)", true}, // \4A = J
            {JOHN_SMITH_LDIF, "(|(cn=Jane Smith)(cn=John Smith))", true},
            {JOHN_SMITH_LDIF, "(title~=tattoos)", true},
            {JOHN_SMITH_LDIF, "(title~=tattos)", true},
            {JOHN_SMITH_LDIF, "(labeledUri=http://opends.org/john)", true},
            {JOHN_SMITH_LDIF, "(labeledUri=http://opends.org/JOHN)", false},
            {JOHN_SMITH_LDIF, "(labeledUri=http://*/john)", true},
            {JOHN_SMITH_LDIF, "(labeledUri=http://*/JOHN)", false},
            {JOHN_SMITH_LDIF, "(cn>=John Smith)", true},
            {JOHN_SMITH_LDIF, "(cn>=J)", true},
            {JOHN_SMITH_LDIF, "(cn<=J)", false},
            {JOHN_SMITH_LDIF, "(cn=Jane Smith)", false},
            {JOHN_SMITH_LDIF, "(displayName=\\2A)", true}, // \2A = *
            // 2.5.4.4 is Smith
            {JOHN_SMITH_LDIF, "(2.5.4.4=Smith)", true},
            {JOHN_SMITH_LDIF, "(sn:caseExactMatch:=Smith)", true},
            {JOHN_SMITH_LDIF, "(sn:caseExactMatch:=smith)", false},
            // Test cases for 730
            {JOHN_SMITH_LDIF, "(internationaliSDNNumber=*12*45*)", true},
            {JOHN_SMITH_LDIF, "(internationaliSDNNumber=*45*12*)", false},
            // TODO: open a bug for all of these.
//            {JOHN_SMITH_LDIF, "(:caseExactMatch:=Smith)", true},
//            {JOHN_SMITH_LDIF, "(:caseExactMatch:=NotSmith)", false},
            // Look at 4515 for some more examples.  Ask Neil.
//            {JOHN_SMITH_LDIF, "(:dn:caseExactMatch:=example)", true},
//            {JOHN_SMITH_LDIF, "(:dn:caseExactMatch:=notexample)", false},
    };
  }
  @Test(dataProvider = "matchesParams")
  public void testMatches(String ldifEntry, String filterStr, boolean expectMatch) throws Exception {
    runMatchTest(ldifEntry, filterStr, expectMatch);
  }
  private void runMatchTest(String ldifEntry, String filterStr, boolean expectMatch) throws Exception {
    Entry entry = TestCaseUtils.entryFromLdifString(ldifEntry);
    runSingleMatchTest(entry, filterStr, expectMatch);
    runSingleMatchTest(entry, "(|" + filterStr + ")", expectMatch);
    runSingleMatchTest(entry, "(&" + filterStr + ")", expectMatch);
    runSingleMatchTest(entry, "(!" + filterStr + ")", !expectMatch);
  }
  private void runSingleMatchTest(Entry entry, String filterStr, boolean expectMatch) throws Exception {
    final SearchFilter filter = SearchFilter.createFilterFromString(filterStr);
    boolean matches = filter.matchesEntry(entry);
    Assert.assertEquals(matches, expectMatch, "Filter=" + filter + "\nEntry=" + entry);
  }
  ////////////////////////////////////////////////////////////////////////////
  ////////////////////////////////////////////////////////////////////////////
  //
  // Filter construction
  //
  ////////////////////////////////////////////////////////////////////////////
  ////////////////////////////////////////////////////////////////////////////
  /**
   *
   */
  private static final String makeSimpleLdif(String givenname, String sn) {
    String cn = givenname + " " + sn;
    return TestCaseUtils.makeLdif(
          "dn: cn=" + cn + ",dc=example,dc=com",
          "objectclass: inetorgperson",
          "cn: " + cn,
          "sn: " + sn,
          "givenname: " + givenname
          );
  }
  private static final String JANE_SMITH_LDIF = makeSimpleLdif("Jane", "Smith");
  private static final String JANE_AUSTIN_LDIF = makeSimpleLdif("Jane", "Austin");
  private static final String JOE_SMITH_LDIF = makeSimpleLdif("Joe", "Smith");
  private static final String JOE_AUSTIN_LDIF = makeSimpleLdif("Joe", "Austin");
  private static final List<String> ALL_ENTRIES_LDIF =
          Collections.unmodifiableList(asList(JANE_SMITH_LDIF,
                                              JANE_AUSTIN_LDIF,
                                              JOE_SMITH_LDIF,
                                              JOE_AUSTIN_LDIF));
  /**
   *
   */
  private List<String> getEntriesExcluding(List<String> matchedEntries) {
    List<String> unmatched = new ArrayList<String>(ALL_ENTRIES_LDIF);
    unmatched.removeAll(matchedEntries);
    return unmatched;
  }
  /**
   *
   */
  private static class FilterDescription {
    private SearchFilter searchFilter;
    private List<String> matchedEntriesLdif;
    private List<String> unmatchedEntriesLdif;
    private FilterType filterType;
    private LinkedHashSet<SearchFilter> filterComponents = new LinkedHashSet<SearchFilter>();
    private SearchFilter notComponent;
    private AttributeValue assertionValue;
    private AttributeType attributeType;
    private ByteString subInitialElement;
    private List<ByteString> subAnyElements = new ArrayList<ByteString>();
    private ByteString subFinalElement;
    private String matchingRuleId;
    private boolean dnAttributes;
    /**
     *
     */
    public void validateFilterFields() throws AssertionError {
      if (!searchFilter.getFilterType().equals(filterType)) {
        throwUnequalError("filterTypes");
      }
      if (!searchFilter.getFilterComponents().equals(filterComponents)) {
        throwUnequalError("filterComponents");
      }
      if (!objectsAreEqual(searchFilter.getNotComponent(), notComponent)) {
        throwUnequalError("notComponent");
      }
      if (!objectsAreEqual(searchFilter.getAssertionValue(), assertionValue)) {
        throwUnequalError("assertionValue");
      }
      if (!objectsAreEqual(searchFilter.getAttributeType(), attributeType)) {
        throwUnequalError("attributeType");
      }
      if (!objectsAreEqual(searchFilter.getSubInitialElement(), subInitialElement)) {
        throwUnequalError("subInitial");
      }
      if (!objectsAreEqual(searchFilter.getSubAnyElements(), subAnyElements)) {
        throwUnequalError("subAny");
      }
      if (!objectsAreEqual(searchFilter.getSubFinalElement(), subFinalElement)) {
        throwUnequalError("subFinal");
      }
      if (!objectsAreEqual(searchFilter.getMatchingRuleID(), matchingRuleId)) {
        throwUnequalError("matchingRuleId");
      }
      if (searchFilter.getDNAttributes() != dnAttributes) {
        throwUnequalError("dnAttributes");
      }
    }
    /**
     *
     */
    private void throwUnequalError(String message) throws AssertionError {
      throw new AssertionError("Filter differs from what is expected '" + message + "' differ.\n" + toString());
    }
    /**
     *
     */
    @Override
    public String toString() {
      return "FilterDescription: \n" +
              "\tsearchFilter=" + searchFilter + "\n" +
              "\tfilterType = " + filterType + "\n" +
              "\tfilterComponents = " + filterComponents + "\n" +
              "\tnotComponent = " + notComponent + "\n" +
              "\tassertionValue = " + assertionValue + "\n" +
              "\tattributeType = " + attributeType + "\n" +
              "\tsubInitialElement = " + subInitialElement + "\n" +
              "\tsubAnyElements = " + subAnyElements + "\n" +
              "\tsubFinalElement = " + subFinalElement + "\n" +
              "\tmatchingRuleId = " + dnAttributes + "\n";
    }
    /**
     *
     */
    private FilterDescription negate() {
      FilterDescription negation = new FilterDescription();
      negation.searchFilter = SearchFilter.createNOTFilter(searchFilter);
      // Flip-flop these
      negation.matchedEntriesLdif = unmatchedEntriesLdif;
      negation.unmatchedEntriesLdif = matchedEntriesLdif;
      negation.filterType = FilterType.NOT;
      negation.notComponent = searchFilter;
      return negation;
    }
    /**
     *
     */
    public FilterDescription clone() {
      FilterDescription that = new FilterDescription();
      that.searchFilter = this.searchFilter;
      that.matchedEntriesLdif = this.matchedEntriesLdif;
      that.unmatchedEntriesLdif = this.unmatchedEntriesLdif;
      that.filterType = this.filterType;
      that.filterComponents = this.filterComponents;
      that.notComponent = this.notComponent;
      that.assertionValue = this.assertionValue;
      that.attributeType = this.attributeType;
      that.subInitialElement = this.subInitialElement;
      that.subAnyElements = this.subAnyElements;
      that.subFinalElement = this.subFinalElement;
      that.matchingRuleId = this.matchingRuleId;
      that.dnAttributes = this.dnAttributes;
      return that;
    }
  }
  /**
   *
   */
  private FilterDescription assertionFilterDescription(FilterType filterType,
                                                       String attributeType,
                                                       String attributeValue,
                                                       List<String> matchedEntries) {
    FilterDescription description = new FilterDescription();
    description.filterType = filterType;
    description.attributeType = DirectoryServer.getAttributeType(attributeType);
    description.assertionValue = new AttributeValue(description.attributeType, attributeValue);
    if (filterType == FilterType.EQUALITY) {
      description.searchFilter = SearchFilter.createEqualityFilter(description.attributeType,
                                                                   description.assertionValue);
    } else if (filterType == FilterType.LESS_OR_EQUAL) {
      description.searchFilter = SearchFilter.createLessOrEqualFilter(description.attributeType,
                                                                      description.assertionValue);
    } else if (filterType == FilterType.GREATER_OR_EQUAL) {
      description.searchFilter = SearchFilter.createGreaterOrEqualFilter(description.attributeType,
                                                                         description.assertionValue);
    } else if (filterType == FilterType.APPROXIMATE_MATCH) {
      description.searchFilter = SearchFilter.createApproximateFilter(description.attributeType,
                                                                      description.assertionValue);
    } else {
      fail(filterType + " is not handled.");
    }
    description.matchedEntriesLdif = matchedEntries;
    description.unmatchedEntriesLdif = getEntriesExcluding(matchedEntries);
    return description;
  }
  /**
   *
   */
  private FilterDescription equalityFilterDescription(String attributeType,
                                                      String attributeValue,
                                                      List<String> matchedEntries) {
    return assertionFilterDescription(FilterType.EQUALITY, attributeType, attributeValue, matchedEntries);
  }
  /**
   *
   */
  private FilterDescription lessEqualFilterDescription(String attributeType,
                                                       String attributeValue,
                                                       List<String> matchedEntries) {
    return assertionFilterDescription(FilterType.LESS_OR_EQUAL, attributeType, attributeValue, matchedEntries);
  }
  /**
   *
   */
  private FilterDescription greaterEqualFilterDescription(String attributeType,
                                                          String attributeValue,
                                                          List<String> matchedEntries) {
    return assertionFilterDescription(FilterType.GREATER_OR_EQUAL, attributeType, attributeValue, matchedEntries);
  }
  /**
   *
   */
  private FilterDescription approximateFilterDescription(String attributeType,
                                                         String attributeValue,
                                                         List<String> matchedEntries) {
    return assertionFilterDescription(FilterType.APPROXIMATE_MATCH, attributeType, attributeValue, matchedEntries);
  }
  /**
   *
   */
  private FilterDescription substringFilterDescription(String attributeType,
                                                       String subInitial,
                                                       List<String> subAny,
                                                       String subFinal,
                                                       List<String> matchedEntries) {
    FilterDescription description = new FilterDescription();
    description.filterType = FilterType.SUBSTRING;
    description.attributeType = DirectoryServer.getAttributeType(attributeType);
    description.subInitialElement = new ASN1OctetString(subInitial);
    description.subAnyElements = new ArrayList<ByteString>();
    for (int i = 0; (subAny != null) && (i < subAny.size()); i++) {
      String s = subAny.get(i);
      description.subAnyElements.add(new ASN1OctetString(s));
    }
    description.subFinalElement = new ASN1OctetString(subFinal);
    description.searchFilter = SearchFilter.createSubstringFilter(description.attributeType,
            description.subInitialElement,
            description.subAnyElements,
            description.subFinalElement);
    description.matchedEntriesLdif = matchedEntries;
    description.unmatchedEntriesLdif = getEntriesExcluding(matchedEntries);
    return description;
  }
  /**
   *
   */
  private List<FilterDescription> getNotFilters(List<FilterDescription> filters) {
    List<FilterDescription> notFilters = new ArrayList<FilterDescription>();
    for (FilterDescription filter: filters) {
      notFilters.add(filter.negate());
    }
    return notFilters;
  }
  /**
   *
   */
  private FilterDescription getAndFilter(List<FilterDescription> filters) {
    FilterDescription andFilter = new FilterDescription();
    List<String> matchedEntries = new ArrayList<String>(ALL_ENTRIES_LDIF);
    List<SearchFilter> filterComponents = new ArrayList<SearchFilter>();
    for (FilterDescription filter: filters) {
      matchedEntries.retainAll(filter.matchedEntriesLdif);
      filterComponents.add(filter.searchFilter);
    }
    andFilter.searchFilter = SearchFilter.createANDFilter(filterComponents);
    andFilter.filterComponents = new LinkedHashSet<SearchFilter>(filterComponents);
    andFilter.filterType = FilterType.AND;
    andFilter.matchedEntriesLdif = matchedEntries;
    andFilter.unmatchedEntriesLdif = getEntriesExcluding(matchedEntries);
    return andFilter;
  }
  /**
   *
   */
  private List<FilterDescription> getAndFilters(List<FilterDescription> filters) {
    List<FilterDescription> andFilters = new ArrayList<FilterDescription>();
    for (FilterDescription first: filters) {
      for (FilterDescription second: filters) {
        andFilters.add(getAndFilter(asList(first, second)));
      }
    }
    return andFilters;
  }
  /**
   *
   */
  private FilterDescription getOrFilter(List<FilterDescription> filters) {
    FilterDescription orFilter = new FilterDescription();
    List<String> unmatchedEntries = new ArrayList<String>(ALL_ENTRIES_LDIF);
    List<SearchFilter> filterComponents = new ArrayList<SearchFilter>();
    for (FilterDescription filter: filters) {
      unmatchedEntries.retainAll(filter.unmatchedEntriesLdif);
      filterComponents.add(filter.searchFilter);
    }
    orFilter.searchFilter = SearchFilter.createORFilter(filterComponents);
    orFilter.filterComponents = new LinkedHashSet<SearchFilter>(filterComponents);
    orFilter.filterType = FilterType.OR;
    // Since we're not using Sets, we've whittled down unmatched entries from
    // the full set instead of adding to matchedEntries, which would lead
    // to duplicates.
    orFilter.unmatchedEntriesLdif = unmatchedEntries;
    orFilter.matchedEntriesLdif = getEntriesExcluding(unmatchedEntries);
    return orFilter;
  }
  /**
   *
   */
  private List<FilterDescription> getOrFilters(List<FilterDescription> filters) {
    List<FilterDescription> orFilters = new ArrayList<FilterDescription>();
    for (FilterDescription first: filters) {
      for (FilterDescription second: filters) {
        orFilters.add(getOrFilter(asList(first, second)));
      }
    }
    return orFilters;
  }
  /**
   *
   */
  private List<FilterDescription> getEqualityFilters() throws Exception {
    List<FilterDescription> descriptions = new ArrayList<FilterDescription>();
    descriptions.add(equalityFilterDescription("sn", "Smith",
            asList(JANE_SMITH_LDIF, JOE_SMITH_LDIF)));
    descriptions.add(equalityFilterDescription("givenname", "Jane",
            asList(JANE_SMITH_LDIF, JANE_AUSTIN_LDIF)));
    return descriptions;
  }
  /**
   *
   */
  private List<FilterDescription> getApproximateFilters() throws Exception {
    List<FilterDescription> descriptions = new ArrayList<FilterDescription>();
    descriptions.add(approximateFilterDescription("sn", "Smythe",
            asList(JANE_SMITH_LDIF, JOE_SMITH_LDIF)));
    return descriptions;
  }
  /**
   *
   */
  private List<FilterDescription> getSubstringFilters() throws Exception {
    List<FilterDescription> descriptions = new ArrayList<FilterDescription>();
    descriptions.add(substringFilterDescription(
            "sn",
            "S", asList("i"), "th", // S*i*th
            asList(JANE_SMITH_LDIF, JOE_SMITH_LDIF)));
    return descriptions;
  }
  /**
   *
   */
  private List<FilterDescription> getInequalityFilters() throws Exception {
    List<FilterDescription> descriptions = new ArrayList<FilterDescription>();
    descriptions.add(lessEqualFilterDescription("sn", "Aus",
            (List<String>)(new ArrayList<String>())));
    descriptions.add(greaterEqualFilterDescription("sn", "Aus",
            asList(JANE_AUSTIN_LDIF, JOE_AUSTIN_LDIF,
                   JANE_SMITH_LDIF, JOE_SMITH_LDIF)));
    descriptions.add(lessEqualFilterDescription("sn", "Smi",
            asList(JANE_AUSTIN_LDIF, JOE_AUSTIN_LDIF)));
    descriptions.add(greaterEqualFilterDescription("sn", "Smi",
            asList(JANE_SMITH_LDIF, JOE_SMITH_LDIF)));
    descriptions.add(lessEqualFilterDescription("sn", "Smith",
            asList(JANE_AUSTIN_LDIF, JOE_AUSTIN_LDIF,
                   JANE_SMITH_LDIF, JOE_SMITH_LDIF)));
    descriptions.add(greaterEqualFilterDescription("sn", "Smith",
            asList(JANE_SMITH_LDIF, JOE_SMITH_LDIF)));
    return descriptions;
  }
  /**
   * Updates to this should also be made in getMinimalFilterDescriptionList.
   * @see #getMinimalFilterDescriptionList
   */
  private List<FilterDescription> getFilterDescriptionList() throws Exception {
    List<FilterDescription> baseDescriptions = new ArrayList<FilterDescription>();
    baseDescriptions.addAll(getEqualityFilters());
    baseDescriptions.addAll(getInequalityFilters());
    baseDescriptions.addAll(getApproximateFilters());
    baseDescriptions.addAll(getSubstringFilters());
    baseDescriptions.addAll(getNotFilters(baseDescriptions));
    List<FilterDescription> allDescriptions = new ArrayList<FilterDescription>();
    allDescriptions.addAll(getAndFilters(baseDescriptions));
    allDescriptions.addAll(getOrFilters(baseDescriptions));
    allDescriptions.addAll(baseDescriptions);
    return allDescriptions;
  }
  /**
   *
   */
  public List<FilterDescription> getMinimalFilterDescriptionList() throws Exception {
    List<FilterDescription> baseDescriptions = new ArrayList<FilterDescription>();
    List<FilterDescription> allDescriptions = new ArrayList<FilterDescription>();
    baseDescriptions.addAll(getEqualityFilters().subList(0, 1));
    baseDescriptions.addAll(getInequalityFilters().subList(0, 2));
    baseDescriptions.addAll(getSubstringFilters().subList(0, 1));
    baseDescriptions.addAll(getNotFilters(baseDescriptions).subList(0, 1));
    allDescriptions.addAll(baseDescriptions);
    allDescriptions.addAll(getAndFilters(baseDescriptions).subList(0, 2));
    allDescriptions.addAll(getOrFilters(baseDescriptions).subList(0, 2));
    return allDescriptions;
  }
  /**
   *
   */
  @DataProvider(name = "filterDescriptions")
  public Object[][] getFilterDescriptions() throws Exception {
    List<FilterDescription> allDescriptions = getFilterDescriptionList();
    // Now convert to [][]
    FilterDescription[][] descriptionArray = new FilterDescription[allDescriptions.size()][];
    for (int i = 0; i < allDescriptions.size(); i++) {
      FilterDescription description = allDescriptions.get(i);
      descriptionArray[i] = new FilterDescription[]{description};
    }
    return descriptionArray;
  }
  @Test(dataProvider = "filterDescriptions")
  public void testFilterConstruction(FilterDescription description) throws Exception {
    description.validateFilterFields();
    for (String ldif: description.matchedEntriesLdif) {
      Entry entry = TestCaseUtils.entryFromLdifString(ldif);
      if (!description.searchFilter.matchesEntry(entry)) {
        fail("Expected to match entry. " + description + entry);
      }
    }
    for (String ldif: description.unmatchedEntriesLdif) {
      Entry entry = TestCaseUtils.entryFromLdifString(ldif);
      if (description.searchFilter.matchesEntry(entry)) {
        fail("Should not have matched entry. " + description + entry);
      }
    }
  }
  // TODO: test more on extensible match and attribute options
  // TODO: test that we fail when creating filters without specifying all of the parameters
  // TODO: we need to test attribute options!
  // TODO: test the audio attribute since it's octetStringMatch
  // TODO: test the homePhone attribute since   EQUALITY telephoneNumberMatch SUBSTR telephoneNumberSubstringsMatch
  // TODO: test labeledURI since it's  caseExactMatch SUBSTR caseExactSubstringsMatch
  // TODO: test mail since it's EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5SubstringsMatch
  // TODO: test secretary since it's distinguishedNameMatch
  // TODO: test x500UniqueIdentifier since it's bitStringMatch
  private static final Object[][] TEST_EQUALS_PARAMS = new Object[][]{
          // These have duplicates, and their String representation should even reflect that.
          {"(&(sn=Smith))", "(&(sn=Smith)(sn=Smith))", true, true},
          {"(|(sn=Smith))", "(|(sn=Smith)(sn=Smith))", true, true},
          // These are reordered, so they are equivalent, but their String representations will differ
          {"(&(sn=Smith)(sn<=Aus))", "(&(sn<=Aus)(sn=Smith))", true, false},
          {"(|(sn=Smith)(sn<=Aus))", "(|(sn<=Aus)(sn=Smith))", true, false},
          // These should be case insensitive
          {"(SN=Smith)", "(sn=Smith)", true, true},
          {"(sn=smith)", "(sn=Smith)", true, false},
          {"(SN=S*th)", "(sn=S*th)", true, true},
          {"(sn:caseExactMatch:=Smith)", "(sn:caseExactMatch:=Smith)", true, true},
          // This demonstrates bug 704.
//          {"(sn:caseExactMatch:=Smith)", "(sn:caseExactMatch:=smith)", false, false},
          // TODO: open a bug for this.
//          {"(:dn:caseExactMatch:=example)", "(:DN:caseExactMatch:=example)", true, true}, // ? String not match
          // 2.5.4.4 is 'sn'
          {"(2.5.4.4=Smith)", "(2.5.4.4=Smith)", true, true},
          {"(2.5.4.4=Smith)", "(sn=Smith)", true, true},
          {"(sn;lang-en=Smith)", "(sn;lang-en=Smith)", true, true},
          // This demonstrates bug 706
//          {"(sn;lang-en=Smith)", "(sn=Smith)", false, false},
          // This demonstrates bug 705.
//          {"(sn=s*t*h)", "(sn=S*T*H)", true, false},
          // These should be case sensitive
          {"(labeledURI=http://opends.org)", "(labeledURI=http://OpenDS.org)", false, false},
          {"(labeledURI=http://opends*)", "(labeledURI=http://OpenDS*)", false, false},
          // These are WYSIWIG
          {"(sn=*)", "(sn=*)", true, true},
          {"(sn=S*)", "(sn=S*th)", false, false},
          {"(sn=*S)", "(sn=S*th)", false, false},
          {"(sn=S*t)", "(sn=S*th)", false, false},
          {"(sn=*i*t*)", "(sn=*i*t*)", true, true},
          {"(sn=*t*i*)", "(sn=*i*t*)", false, false},  // Test case for 695
          {"(sn=S*i*t)", "(sn=S*th)", false, false},
          {"(sn=Smith)", "(sn=Smith)", true, true},
          {"(sn=Smith)", "(sn<=Aus)", false, false},
          {"(sn=Smith)", "(sn>=Aus)", false, false},
          {"(sn=Smith)", "(sn=S*i*th)", false, false},
          {"(sn=Smith)", "(!(sn=Smith))", false, false},
          {"(sn=Smith)", "(&(sn=Smith)(sn<=Aus))", false, false},
          {"(sn=Smith)", "(|(sn=Smith)(sn<=Aus))", false, false},
          {"(sn<=Aus)", "(sn<=Aus)", true, true},
          {"(sn<=Aus)", "(sn>=Aus)", false, false},
          {"(sn<=Aus)", "(sn=S*i*th)", false, false},
          {"(sn<=Aus)", "(!(sn=Smith))", false, false},
          {"(sn<=Aus)", "(&(sn=Smith)(sn=Smith))", false, false},
          {"(sn<=Aus)", "(&(sn=Smith)(sn<=Aus))", false, false},
          {"(sn<=Aus)", "(|(sn=Smith)(sn=Smith))", false, false},
          {"(sn<=Aus)", "(|(sn=Smith)(sn<=Aus))", false, false},
          {"(sn>=Aus)", "(sn>=Aus)", true, true},
          {"(sn>=Aus)", "(sn=S*i*th)", false, false},
          {"(sn>=Aus)", "(!(sn=Smith))", false, false},
          {"(sn>=Aus)", "(&(sn=Smith)(sn=Smith))", false, false},
          {"(sn>=Aus)", "(&(sn=Smith)(sn<=Aus))", false, false},
          {"(sn>=Aus)", "(|(sn=Smith)(sn=Smith))", false, false},
          {"(sn>=Aus)", "(|(sn=Smith)(sn<=Aus))", false, false},
          {"(sn=S*i*th)", "(sn=S*i*th)", true, true},
          {"(sn=S*i*th)", "(!(sn=Smith))", false, false},
          {"(sn=S*i*th)", "(&(sn=Smith)(sn=Smith))", false, false},
          {"(sn=S*i*th)", "(&(sn=Smith)(sn<=Aus))", false, false},
          {"(sn=S*i*th)", "(|(sn=Smith)(sn=Smith))", false, false},
          {"(sn=S*i*th)", "(|(sn=Smith)(sn<=Aus))", false, false},
          {"(!(sn=Smith))", "(!(sn=Smith))", true, true},
          {"(!(sn=Smith))", "(&(sn=Smith)(sn=Smith))", false, false},
          {"(!(sn=Smith))", "(&(sn=Smith)(sn<=Aus))", false, false},
          {"(!(sn=Smith))", "(|(sn=Smith)(sn=Smith))", false, false},
          {"(!(sn=Smith))", "(|(sn=Smith)(sn<=Aus))", false, false},
          {"(&(sn=Smith)(sn=Smith))", "(&(sn=Smith)(sn=Smith))", true, true},
          {"(&(sn=Smith)(sn=Smith))", "(&(sn=Smith)(sn<=Aus))", false, false},
          {"(&(sn=Smith)(sn=Smith))", "(|(sn=Smith)(sn=Smith))", false, false},
          {"(&(sn=Smith)(sn=Smith))", "(|(sn=Smith)(sn<=Aus))", false, false},
          {"(&(sn=Smith)(sn<=Aus))", "(&(sn=Smith)(sn<=Aus))", true, true},
          {"(&(sn=Smith)(sn<=Aus))", "(|(sn=Smith)(sn=Smith))", false, false},
          {"(&(sn=Smith)(sn<=Aus))", "(|(sn=Smith)(sn<=Aus))", false, false},
          {"(|(sn=Smith)(sn=Smith))", "(|(sn=Smith)(sn=Smith))", true, true},
          {"(|(sn=Smith)(sn=Smith))", "(|(sn=Smith)(sn<=Aus))", false, false},
          {"(|(sn=Smith)(sn<=Aus))", "(|(sn=Smith)(sn<=Aus))", true, true},
          {"(&(sn=Smith)(sn<=Aus))", "(&(sn=Smith)(sn>=Aus))", false, false},
          {"(|(sn=Smith)(sn<=Aus))", "(|(sn=Smith)(sn>=Aus))", false, false},
  };
  /**
   *
   */
  @DataProvider(name = "equalsTest")
  public Object[][] getEqualsTests() throws Exception {
    return TEST_EQUALS_PARAMS;
  }
  /**
   *
   */
  @Test(dataProvider = "equalsTest")
  public void testEquals(String stringFilter1, String stringFilter2, boolean expectEquals, boolean expectStringEquals) throws Exception {
    SearchFilter filter1 = SearchFilter.createFilterFromString(stringFilter1);
    SearchFilter filter2 = SearchFilter.createFilterFromString(stringFilter2);
    boolean actualEquals = filter1.equals(filter2);
    assertEquals(actualEquals, expectEquals,
                 "Expected " + filter1 + (expectEquals ? " == " : " != ") + filter2);
    // Test symmetry
    actualEquals = filter2.equals(filter1);
    assertEquals(actualEquals, expectEquals,
                 "Expected " + filter1 + (expectEquals ? " == " : " != ") + filter2);
    if (expectEquals) {
      assertEquals(filter1.hashCode(), filter2.hashCode(),
                   "Hash codes differ for " + filter1 + " and " + filter2);
    }
    // Test toString
    actualEquals = filter2.toString().equals(filter1.toString());
    assertEquals(actualEquals, expectStringEquals,
                 "Expected " + filter1 + (expectStringEquals ? " == " : " != ") + filter2);
  }
}