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

Yannick Lecaillez
18.52.2015 6e92d4259d25e53c653e646c3e393b1a74a064ee
OPENDJ-1864: Ordering matching rules should reuse equality indexes where possible
82 files modified
2448 ■■■■ changed files
opendj-sdk/opendj-core/clirr-ignored-api-changes.xml 6 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AbstractApproximateMatchingRuleImpl.java 18 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AbstractEqualityMatchingRuleImpl.java 20 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AbstractMatchingRuleImpl.java 32 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AbstractOrderingMatchingRuleImpl.java 37 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AbstractSubstringMatchingRuleImpl.java 31 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AuthPasswordExactEqualityMatchingRuleImpl.java 10 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/BitStringEqualityMatchingRuleImpl.java 9 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/BooleanEqualityMatchingRuleImpl.java 9 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CaseExactEqualityMatchingRuleImpl.java 8 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CaseExactIA5EqualityMatchingRuleImpl.java 9 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CaseExactIA5SubstringMatchingRuleImpl.java 8 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CaseExactOrderingMatchingRuleImpl.java 10 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CaseExactSubstringMatchingRuleImpl.java 9 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CaseIgnoreEqualityMatchingRuleImpl.java 8 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CaseIgnoreIA5EqualityMatchingRuleImpl.java 8 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CaseIgnoreIA5SubstringMatchingRuleImpl.java 9 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CaseIgnoreListEqualityMatchingRuleImpl.java 10 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CaseIgnoreListSubstringMatchingRuleImpl.java 9 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CaseIgnoreOrderingMatchingRuleImpl.java 9 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CaseIgnoreSubstringMatchingRuleImpl.java 9 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CertificateExactMatchingRuleImpl.java 11 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CollationMatchingRulesImpl.java 18 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DITContentRuleSyntaxImpl.java 4 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DirectoryStringFirstComponentEqualityMatchingRuleImpl.java 10 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DistinguishedNameEqualityMatchingRuleImpl.java 8 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DoubleMetaphoneApproximateMatchingRuleImpl.java 8 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/EnumOrderingMatchingRule.java 4 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/GeneralizedTimeEqualityMatchingRuleImpl.java 9 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/GeneralizedTimeOrderingMatchingRuleImpl.java 10 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/IntegerEqualityMatchingRuleImpl.java 6 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/IntegerFirstComponentEqualityMatchingRuleImpl.java 9 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/IntegerOrderingMatchingRuleImpl.java 6 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/KeywordEqualityMatchingRuleImpl.java 11 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/MatchingRule.java 9 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/MatchingRuleImpl.java 14 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/NumericStringEqualityMatchingRuleImpl.java 9 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/NumericStringOrderingMatchingRuleImpl.java 10 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/NumericStringSubstringMatchingRuleImpl.java 9 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/ObjectIdentifierEqualityMatchingRuleImpl.java 5 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/ObjectIdentifierFirstComponentEqualityMatchingRuleImpl.java 7 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/OctetStringEqualityMatchingRuleImpl.java 9 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/OctetStringOrderingMatchingRuleImpl.java 9 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/OctetStringSubstringMatchingRuleImpl.java 8 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/PresentationAddressEqualityMatchingRuleImpl.java 8 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/ProtocolInformationEqualityMatchingRuleImpl.java 8 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/TelephoneNumberEqualityMatchingRuleImpl.java 9 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/TelephoneNumberSubstringMatchingRuleImpl.java 8 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/TimeBasedMatchingRulesImpl.java 22 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/UUIDEqualityMatchingRuleImpl.java 8 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/UUIDOrderingMatchingRuleImpl.java 8 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/UniqueMemberEqualityMatchingRuleImpl.java 8 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/UserPasswordExactEqualityMatchingRuleImpl.java 9 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/Indexer.java 7 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/AbstractSubstringMatchingRuleImplTest.java 35 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/PartialDateAndTimeMatchingRuleTestCase.java 7 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/RelativeTimeGreaterThanMatchingRuleTest.java 6 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/RelativeTimeLessThanMatchingRuleTest.java 5 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/AttributeIndex.java 568 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/AttributeIndexer.java 29 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/EqualityIndexer.java 7 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/ID2CIndexer.java 7 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/ID2SIndexer.java 7 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/Importer.java 150 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/Index.java 30 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/Indexer.java 10 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/NullIndex.java 9 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/PresenceIndexer.java 5 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/Suffix.java 25 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/VerifyJob.java 106 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/AbstractTree.java 2 ●●● patch | view | raw | blame | history
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/AttributeIndex.java 508 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/DefaultIndex.java 17 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/EntryContainer.java 21 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/Importer.java 28 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/VLVIndex.java 2 ●●● patch | view | raw | blame | history
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/VerifyJob.java 12 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/HistoricalCsnOrderingMatchingRuleImpl.java 14 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/schema/AbstractPasswordEqualityMatchingRuleImpl.java 15 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-server-legacy/src/test/java/org/opends/server/backends/SchemaTestMatchingRuleImpl.java 10 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-server-legacy/src/test/java/org/opends/server/backends/jeb/TestBackendImpl.java 210 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-server-legacy/src/test/java/org/opends/server/backends/jeb/TestVerifyJob.java 27 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/clirr-ignored-api-changes.xml
@@ -421,6 +421,12 @@
    <justification>OPENDJ-1689 method has been removed because all matching rules should support the default comparator</justification>
  </difference>
  <difference>
    <className>org/forgerock/opendj/ldap/schema/MatchingRuleImpl</className>
    <differenceType>7012</differenceType>
    <method>java.util.Collection createIndexers(org.forgerock.opendj.ldap.spi.IndexingOptions)</method>
    <justification>Doesn't really seem correct to call createKeys() with different options each time.</justification>
  </difference>
  <difference>
    <className>org/forgerock/opendj/ldap/ByteSequence</className>
    <differenceType>7012</differenceType>
    <method>boolean startsWith(org.forgerock.opendj.ldap.ByteSequence)</method>
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AbstractApproximateMatchingRuleImpl.java
@@ -21,7 +21,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2014 ForgeRock AS
 *      Copyright 2014-2015 ForgeRock AS
 */
package org.forgerock.opendj.ldap.schema;
@@ -32,6 +32,7 @@
import org.forgerock.opendj.ldap.ByteSequence;
import org.forgerock.opendj.ldap.DecodeException;
import org.forgerock.opendj.ldap.spi.Indexer;
import org.forgerock.opendj.ldap.spi.IndexingOptions;
/**
 * This class implements an approximate matching rule that matches normalized
@@ -39,22 +40,21 @@
 */
abstract class AbstractApproximateMatchingRuleImpl extends AbstractMatchingRuleImpl {
    private final Collection<? extends Indexer> indexers =
            Collections.singleton(new DefaultIndexer("approximate"));
    private final Indexer indexer;
    AbstractApproximateMatchingRuleImpl() {
        // Nothing to do.
    AbstractApproximateMatchingRuleImpl(String indexID) {
        indexer = new DefaultIndexer(indexID);
    }
    @Override
    public Assertion getAssertion(final Schema schema, final ByteSequence assertionValue)
    public final Assertion getAssertion(final Schema schema, final ByteSequence assertionValue)
            throws DecodeException {
        return DefaultAssertion.approximate(normalizeAttributeValue(schema, assertionValue));
        return named(indexer.getIndexID(), normalizeAttributeValue(schema, assertionValue));
    }
    /** {@inheritDoc} */
    @Override
    public Collection<? extends Indexer> getIndexers() {
        return indexers;
    public final Collection<? extends Indexer> createIndexers(IndexingOptions options) {
        return Collections.singleton(indexer);
    }
}
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AbstractEqualityMatchingRuleImpl.java
@@ -21,7 +21,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2014 ForgeRock AS
 *      Copyright 2014-2015 ForgeRock AS
 */
package org.forgerock.opendj.ldap.schema;
@@ -32,6 +32,7 @@
import org.forgerock.opendj.ldap.ByteSequence;
import org.forgerock.opendj.ldap.DecodeException;
import org.forgerock.opendj.ldap.spi.Indexer;
import org.forgerock.opendj.ldap.spi.IndexingOptions;
/**
 * This class implements an equality matching rule that matches normalized
@@ -39,21 +40,26 @@
 */
abstract class AbstractEqualityMatchingRuleImpl extends AbstractMatchingRuleImpl {
    private final Collection<? extends Indexer> indexers = Collections.singleton(new DefaultIndexer("equality"));
    private final Indexer indexer;
    AbstractEqualityMatchingRuleImpl() {
        // Nothing to do.
    AbstractEqualityMatchingRuleImpl(String indexID) {
        this.indexer = new DefaultIndexer(indexID);
    }
    @Override
    public Assertion getAssertion(final Schema schema, final ByteSequence assertionValue)
            throws DecodeException {
        return DefaultAssertion.equality(normalizeAttributeValue(schema, assertionValue));
        return defaultAssertion(normalizeAttributeValue(schema, assertionValue));
    }
    /** {@inheritDoc} */
    @Override
    public Collection<? extends Indexer> getIndexers() {
        return indexers;
    public Collection<? extends Indexer> createIndexers(IndexingOptions options) {
        return Collections.singleton(indexer);
    }
    Assertion defaultAssertion(final ByteSequence normalizedAssertionValue) {
        return named(indexer.getIndexID(), normalizedAssertionValue);
    }
}
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AbstractMatchingRuleImpl.java
@@ -36,7 +36,6 @@
import org.forgerock.opendj.ldap.DecodeException;
import org.forgerock.opendj.ldap.spi.IndexQueryFactory;
import org.forgerock.opendj.ldap.spi.Indexer;
import org.forgerock.opendj.ldap.spi.IndexingOptions;
import static org.forgerock.opendj.ldap.Assertion.*;
@@ -46,23 +45,15 @@
 */
abstract class AbstractMatchingRuleImpl implements MatchingRuleImpl {
    static final class DefaultAssertion implements Assertion {
    static DefaultAssertion named(final String indexID, final ByteSequence normalizedAssertionValue) {
        return new DefaultAssertion(indexID, normalizedAssertionValue);
    }
    private static final class DefaultAssertion implements Assertion {
        /** The ID of the DB index to use with this assertion.*/
        private final String indexID;
        private final ByteSequence normalizedAssertionValue;
        static DefaultAssertion equality(final ByteSequence normalizedAssertionValue) {
            return named("equality", normalizedAssertionValue);
        }
        static DefaultAssertion approximate(final ByteSequence normalizedAssertionValue) {
            return named("approximate", normalizedAssertionValue);
        }
        static DefaultAssertion named(final String indexID, final ByteSequence normalizedAssertionValue) {
            return new DefaultAssertion(indexID, normalizedAssertionValue);
        }
        private DefaultAssertion(final String indexID, final ByteSequence normalizedAssertionValue) {
            this.indexID = indexID;
            this.normalizedAssertionValue = normalizedAssertionValue;
@@ -88,8 +79,7 @@
        }
        @Override
        public void createKeys(Schema schema, ByteSequence value, IndexingOptions options, Collection<ByteString> keys)
                throws DecodeException {
        public void createKeys(Schema schema, ByteSequence value, Collection<ByteString> keys) throws DecodeException {
            keys.add(normalizeAttributeValue(schema, value));
        }
@@ -99,10 +89,6 @@
        }
    }
    AbstractMatchingRuleImpl() {
        // Nothing to do.
    }
    @Override
    public Assertion getAssertion(final Schema schema, final ByteSequence assertionValue)
            throws DecodeException {
@@ -127,10 +113,4 @@
            throws DecodeException {
        return UNDEFINED_ASSERTION;
    }
    @Override
    public boolean isIndexingSupported() {
        return !getIndexers().isEmpty();
    }
}
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AbstractOrderingMatchingRuleImpl.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2014 ForgeRock AS
 *      Portions copyright 2014-2015 ForgeRock AS
 */
package org.forgerock.opendj.ldap.schema;
@@ -36,6 +36,7 @@
import org.forgerock.opendj.ldap.DecodeException;
import org.forgerock.opendj.ldap.spi.IndexQueryFactory;
import org.forgerock.opendj.ldap.spi.Indexer;
import org.forgerock.opendj.ldap.spi.IndexingOptions;
/**
 * This class implements a default ordering matching rule that matches
@@ -46,23 +47,16 @@
 * this assumption does not hold true.
 */
abstract class AbstractOrderingMatchingRuleImpl extends AbstractMatchingRuleImpl {
    private final Collection<? extends Indexer> indexers;
    private final String indexId;
    /** Constructor for default matching rules. */
    AbstractOrderingMatchingRuleImpl() {
        this("ordering");
    }
    private final Indexer indexer;
    /** Constructor for non-default matching rules. */
    AbstractOrderingMatchingRuleImpl(String indexId) {
        this.indexId = indexId;
        this.indexers = Collections.singleton(new DefaultIndexer(indexId));
        this.indexer = new DefaultIndexer(indexId);
    }
    /** {@inheritDoc} */
    @Override
    public Assertion getAssertion(final Schema schema, final ByteSequence value)
    public final Assertion getAssertion(final Schema schema, final ByteSequence value)
            throws DecodeException {
        final ByteString normAssertion = normalizeAttributeValue(schema, value);
        return new Assertion() {
@@ -73,14 +67,16 @@
            @Override
            public <T> T createIndexQuery(IndexQueryFactory<T> factory) throws DecodeException {
                return factory.createRangeMatchQuery(indexId, ByteString.empty(), normAssertion, false, false);
                return factory.createRangeMatchQuery(indexer.getIndexID(), ByteString.empty(), normAssertion, false,
                        false);
            }
        };
    }
    /** {@inheritDoc} */
    @Override
    public Assertion getGreaterOrEqualAssertion(final Schema schema, final ByteSequence value) throws DecodeException {
    public final Assertion getGreaterOrEqualAssertion(final Schema schema, final ByteSequence value)
            throws DecodeException {
        final ByteString normAssertion = normalizeAttributeValue(schema, value);
        return new Assertion() {
            @Override
@@ -90,14 +86,15 @@
            @Override
            public <T> T createIndexQuery(IndexQueryFactory<T> factory) throws DecodeException {
                return factory.createRangeMatchQuery(indexId, normAssertion, ByteString.empty(), true, false);
                return factory.createRangeMatchQuery(indexer.getIndexID(), normAssertion, ByteString.empty(), true,
                        false);
            }
        };
    }
    /** {@inheritDoc} */
    @Override
    public Assertion getLessOrEqualAssertion(final Schema schema, final ByteSequence value)
    public final Assertion getLessOrEqualAssertion(final Schema schema, final ByteSequence value)
            throws DecodeException {
        final ByteString normAssertion = normalizeAttributeValue(schema, value);
        return new Assertion() {
@@ -108,18 +105,16 @@
            @Override
            public <T> T createIndexQuery(IndexQueryFactory<T> factory) throws DecodeException {
                return factory.createRangeMatchQuery(indexId, ByteString.empty(), normAssertion, false, true);
                return factory.createRangeMatchQuery(indexer.getIndexID(), ByteString.empty(), normAssertion, false,
                        true);
            }
        };
    }
    /** {@inheritDoc} */
    @Override
    public Collection<? extends Indexer> getIndexers() {
        return indexers;
    public final Collection<? extends Indexer> createIndexers(IndexingOptions options) {
        return Collections.singleton(indexer);
    }
    final String getIndexId() {
        return indexId;
    }
}
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AbstractSubstringMatchingRuleImpl.java
@@ -225,14 +225,20 @@
    }
    final class SubstringIndexer implements Indexer {
    private final class SubstringIndexer implements Indexer {
        private final String indexID;
        private final int substringKeySize;
        private SubstringIndexer(int substringKeySize) {
            this.substringKeySize = substringKeySize;
            this.indexID = substringIndexId + ":" + this.substringKeySize;
        }
        /** {@inheritDoc} */
        @Override
        public void createKeys(Schema schema, ByteSequence value, IndexingOptions options, Collection<ByteString> keys)
                throws DecodeException {
        public void createKeys(Schema schema, ByteSequence value, Collection<ByteString> keys) throws DecodeException {
            final ByteString normValue = normalizeAttributeValue(schema, value);
            final int substringKeySize = options.substringKeySize();
            // Example: The value is ABCDE and the substring length is 3.
            // We produce the keys ABC BCD CDE DE E
@@ -248,23 +254,16 @@
        /** {@inheritDoc} */
        @Override
        public String getIndexID() {
            return substringIndexId;
            return indexID;
        }
    }
    private final Collection<? extends Indexer> indexers = Collections.singleton(new SubstringIndexer());
    /** Identifier of the substring index. */
    private final String substringIndexId;
    /** Identifier of the equality index. */
    private final String equalityIndexId;
    /** Constructor for default matching rules. */
    AbstractSubstringMatchingRuleImpl() {
        this("substring", "equality");
    }
    /** Constructor for non-default matching rules. */
    AbstractSubstringMatchingRuleImpl(String substringIndexId, String equalityIndexId) {
        this.substringIndexId = substringIndexId;
@@ -273,7 +272,7 @@
    /** {@inheritDoc} */
    @Override
    public Assertion getAssertion(final Schema schema, final ByteSequence assertionValue)
    public final Assertion getAssertion(final Schema schema, final ByteSequence assertionValue)
            throws DecodeException {
        if (assertionValue.length() == 0) {
            throw DecodeException.error(WARN_ATTR_SYNTAX_SUBSTRING_EMPTY.get());
@@ -323,7 +322,7 @@
    /** {@inheritDoc} */
    @Override
    public Assertion getSubstringAssertion(final Schema schema, final ByteSequence subInitial,
    public final Assertion getSubstringAssertion(final Schema schema, final ByteSequence subInitial,
            final List<? extends ByteSequence> subAnyElements, final ByteSequence subFinal)
            throws DecodeException {
        final ByteString normInitial =
@@ -543,8 +542,8 @@
    /** {@inheritDoc} */
    @Override
    public Collection<? extends Indexer> getIndexers() {
        return indexers;
    public final Collection<? extends Indexer> createIndexers(IndexingOptions options) {
        return Collections.singleton(new SubstringIndexer(options.substringKeySize()));
    }
}
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AuthPasswordExactEqualityMatchingRuleImpl.java
@@ -22,10 +22,12 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2014 ForgeRock AS
 *      Portions copyright 2014-2015 ForgeRock AS
 */
package org.forgerock.opendj.ldap.schema;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
import org.forgerock.opendj.ldap.ByteSequence;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.DecodeException;
@@ -35,6 +37,12 @@
 * 3112.
 */
final class AuthPasswordExactEqualityMatchingRuleImpl extends AbstractEqualityMatchingRuleImpl {
    AuthPasswordExactEqualityMatchingRuleImpl() {
        super(EMR_AUTH_PASSWORD_EXACT_NAME);
    }
    @Override
    public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value)
            throws DecodeException {
        final StringBuilder[] authPWComponents =
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/BitStringEqualityMatchingRuleImpl.java
@@ -22,13 +22,14 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2014 ForgeRock AS
 *      Portions copyright 2014-2015 ForgeRock AS
 */
package org.forgerock.opendj.ldap.schema;
import static com.forgerock.opendj.ldap.CoreMessages.WARN_ATTR_SYNTAX_BIT_STRING_INVALID_BIT;
import static com.forgerock.opendj.ldap.CoreMessages.WARN_ATTR_SYNTAX_BIT_STRING_NOT_QUOTED;
import static com.forgerock.opendj.ldap.CoreMessages.WARN_ATTR_SYNTAX_BIT_STRING_TOO_SHORT;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.opendj.ldap.ByteSequence;
@@ -40,6 +41,12 @@
 * referenced in RFC 2252.
 */
final class BitStringEqualityMatchingRuleImpl extends AbstractEqualityMatchingRuleImpl {
    BitStringEqualityMatchingRuleImpl() {
        super(EMR_BIT_STRING_NAME);
    }
    @Override
    public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value)
            throws DecodeException {
        final String valueString = value.toString().toUpperCase();
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/BooleanEqualityMatchingRuleImpl.java
@@ -22,11 +22,12 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2014 ForgeRock AS
 *      Portions copyright 2014-2015 ForgeRock AS
 */
package org.forgerock.opendj.ldap.schema;
import static com.forgerock.opendj.ldap.CoreMessages.WARN_ATTR_SYNTAX_ILLEGAL_BOOLEAN;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
import org.forgerock.opendj.ldap.ByteSequence;
import org.forgerock.opendj.ldap.ByteString;
@@ -37,6 +38,12 @@
 * referenced in RFC 4519.
 */
final class BooleanEqualityMatchingRuleImpl extends AbstractEqualityMatchingRuleImpl {
    BooleanEqualityMatchingRuleImpl() {
        super(EMR_BOOLEAN_NAME);
    }
    @Override
    public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value)
            throws DecodeException {
        final String valueString = value.toString().toUpperCase();
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CaseExactEqualityMatchingRuleImpl.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2014 ForgeRock AS
 *      Portions copyright 2014-2015 ForgeRock AS
 */
package org.forgerock.opendj.ldap.schema;
@@ -30,12 +30,18 @@
import org.forgerock.opendj.ldap.ByteString;
import static com.forgerock.opendj.util.StringPrepProfile.*;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
/**
 * This class defines the caseExactMatch matching rule defined in X.520 and
 * referenced in RFC 4519.
 */
final class CaseExactEqualityMatchingRuleImpl extends AbstractEqualityMatchingRuleImpl {
    CaseExactEqualityMatchingRuleImpl() {
        super(EMR_CASE_EXACT_NAME);
    }
    @Override
    public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value) {
        return SchemaUtils.normalizeStringAttributeValue(value, TRIM, NO_CASE_FOLD);
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CaseExactIA5EqualityMatchingRuleImpl.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2014 ForgeRock AS
 *      Portions copyright 2014-2015 ForgeRock AS
 */
package org.forgerock.opendj.ldap.schema;
@@ -31,12 +31,19 @@
import org.forgerock.opendj.ldap.DecodeException;
import static com.forgerock.opendj.util.StringPrepProfile.*;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
/**
 * This class implements the caseExactIA5Match matching rule defined in RFC
 * 2252.
 */
final class CaseExactIA5EqualityMatchingRuleImpl extends AbstractEqualityMatchingRuleImpl {
    CaseExactIA5EqualityMatchingRuleImpl() {
        super(EMR_CASE_EXACT_IA5_NAME);
    }
    @Override
    public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value)
            throws DecodeException {
        return SchemaUtils.normalizeIA5StringAttributeValue(value, TRIM, NO_CASE_FOLD);
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CaseExactIA5SubstringMatchingRuleImpl.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2014 ForgeRock AS
 *      Portions copyright 2014-2015 ForgeRock AS
 */
package org.forgerock.opendj.ldap.schema;
@@ -31,6 +31,7 @@
import org.forgerock.opendj.ldap.DecodeException;
import static com.forgerock.opendj.util.StringPrepProfile.*;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
/**
 * This class implements the caseExactIA5SubstringsMatch matching rule. This
@@ -39,6 +40,11 @@
 * private namespace.
 */
final class CaseExactIA5SubstringMatchingRuleImpl extends AbstractSubstringMatchingRuleImpl {
    CaseExactIA5SubstringMatchingRuleImpl() {
        super(SMR_CASE_EXACT_IA5_NAME, EMR_CASE_EXACT_IA5_NAME);
    }
    @Override
    public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value)
            throws DecodeException {
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CaseExactOrderingMatchingRuleImpl.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2014 ForgeRock AS
 *      Portions copyright 2014-2015 ForgeRock AS
 */
package org.forgerock.opendj.ldap.schema;
@@ -30,12 +30,20 @@
import org.forgerock.opendj.ldap.ByteString;
import static com.forgerock.opendj.util.StringPrepProfile.*;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
/**
 * This class defines the caseExactOrderingMatch matching rule defined in X.520
 * and referenced in RFC 4519.
 */
final class CaseExactOrderingMatchingRuleImpl extends AbstractOrderingMatchingRuleImpl {
    public CaseExactOrderingMatchingRuleImpl() {
        // Reusing equality index since OPENDJ-1864
        super(EMR_CASE_EXACT_NAME);
    }
    @Override
    public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value) {
        return SchemaUtils.normalizeStringAttributeValue(value, TRIM, NO_CASE_FOLD);
    }
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CaseExactSubstringMatchingRuleImpl.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2014 ForgeRock AS
 *      Portions copyright 2014-2015 ForgeRock AS
 */
package org.forgerock.opendj.ldap.schema;
@@ -31,12 +31,19 @@
import org.forgerock.opendj.ldap.DecodeException;
import static com.forgerock.opendj.util.StringPrepProfile.*;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
/**
 * This class defines the caseExactSubstringsMatch matching rule defined in
 * X.520 and referenced in RFC 2252.
 */
final class CaseExactSubstringMatchingRuleImpl extends AbstractSubstringMatchingRuleImpl {
    CaseExactSubstringMatchingRuleImpl() {
        super(SMR_CASE_EXACT_NAME, EMR_CASE_EXACT_NAME);
    }
    @Override
    public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value) {
        return normalize(TRIM, value);
    }
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CaseIgnoreEqualityMatchingRuleImpl.java
@@ -22,11 +22,12 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2014 ForgeRock AS
 *      Portions copyright 2014-2015 ForgeRock AS
 */
package org.forgerock.opendj.ldap.schema;
import static com.forgerock.opendj.util.StringPrepProfile.*;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
import org.forgerock.opendj.ldap.ByteSequence;
import org.forgerock.opendj.ldap.ByteString;
@@ -36,6 +37,11 @@
 * referenced in RFC 2252.
 */
final class CaseIgnoreEqualityMatchingRuleImpl extends AbstractEqualityMatchingRuleImpl {
    CaseIgnoreEqualityMatchingRuleImpl() {
        super(EMR_CASE_IGNORE_NAME);
    }
    @Override
    public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value) {
        return SchemaUtils.normalizeStringAttributeValue(value, TRIM, CASE_FOLD);
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CaseIgnoreIA5EqualityMatchingRuleImpl.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2014 ForgeRock AS
 *      Portions copyright 2014-2015 ForgeRock AS
 */
package org.forgerock.opendj.ldap.schema;
@@ -31,12 +31,18 @@
import org.forgerock.opendj.ldap.DecodeException;
import static com.forgerock.opendj.util.StringPrepProfile.*;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
/**
 * This class implements the caseIgnoreIA5Match matching rule defined in RFC
 * 2252.
 */
final class CaseIgnoreIA5EqualityMatchingRuleImpl extends AbstractEqualityMatchingRuleImpl {
    CaseIgnoreIA5EqualityMatchingRuleImpl() {
        super(EMR_CASE_IGNORE_IA5_NAME);
    }
    @Override
    public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value)
            throws DecodeException {
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CaseIgnoreIA5SubstringMatchingRuleImpl.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2014 ForgeRock AS
 *      Portions copyright 2014-2015 ForgeRock AS
 */
package org.forgerock.opendj.ldap.schema;
@@ -31,12 +31,19 @@
import org.forgerock.opendj.ldap.DecodeException;
import static com.forgerock.opendj.util.StringPrepProfile.*;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
/**
 * This class implements the caseIgnoreIA5SubstringsMatch matching rule defined
 * in RFC 2252.
 */
final class CaseIgnoreIA5SubstringMatchingRuleImpl extends AbstractSubstringMatchingRuleImpl {
    CaseIgnoreIA5SubstringMatchingRuleImpl() {
        super(SMR_CASE_IGNORE_IA5_NAME, EMR_CASE_IGNORE_IA5_NAME);
    }
    @Override
    public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value)
            throws DecodeException {
        return normalize(TRIM, value);
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CaseIgnoreListEqualityMatchingRuleImpl.java
@@ -22,21 +22,29 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2014 ForgeRock AS
 *      Portions copyright 2014-2015 ForgeRock AS
 */
package org.forgerock.opendj.ldap.schema;
import static com.forgerock.opendj.util.StringPrepProfile.CASE_FOLD;
import org.forgerock.opendj.ldap.ByteSequence;
import org.forgerock.opendj.ldap.ByteString;
import static com.forgerock.opendj.util.StringPrepProfile.*;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
/**
 * This class implements the caseIgnoreListMatch matching rule defined in X.520
 * and referenced in RFC 2252.
 */
final class CaseIgnoreListEqualityMatchingRuleImpl extends AbstractEqualityMatchingRuleImpl {
    CaseIgnoreListEqualityMatchingRuleImpl() {
        super(EMR_CASE_IGNORE_LIST_NAME);
    }
    @Override
    public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value) {
        return SchemaUtils.normalizeStringListAttributeValue(value, TRIM, CASE_FOLD);
    }
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CaseIgnoreListSubstringMatchingRuleImpl.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2014 ForgeRock AS
 *      Portions copyright 2014-2015 ForgeRock AS
 */
package org.forgerock.opendj.ldap.schema;
@@ -31,12 +31,19 @@
import org.forgerock.opendj.ldap.DecodeException;
import static com.forgerock.opendj.util.StringPrepProfile.*;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
/**
 * This class implements the caseIgnoreListSubstringsMatch matching rule defined
 * in X.520 and referenced in RFC 2252.
 */
final class CaseIgnoreListSubstringMatchingRuleImpl extends AbstractSubstringMatchingRuleImpl {
    CaseIgnoreListSubstringMatchingRuleImpl() {
        super(SMR_CASE_IGNORE_LIST_NAME, EMR_CASE_IGNORE_LIST_NAME);
    }
    @Override
    public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value) {
        return SchemaUtils.normalizeStringListAttributeValue(value, TRIM, CASE_FOLD);
    }
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CaseIgnoreOrderingMatchingRuleImpl.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2014 ForgeRock AS
 *      Portions copyright 2014-2015 ForgeRock AS
 */
package org.forgerock.opendj.ldap.schema;
@@ -30,12 +30,19 @@
import org.forgerock.opendj.ldap.ByteString;
import static com.forgerock.opendj.util.StringPrepProfile.*;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
/**
 * This class defines the caseIgnoreOrderingMatch matching rule defined in X.520
 * and referenced in RFC 2252.
 */
final class CaseIgnoreOrderingMatchingRuleImpl extends AbstractOrderingMatchingRuleImpl {
    public CaseIgnoreOrderingMatchingRuleImpl() {
        // Reusing equality index since OPENDJ-1864
        super(EMR_CASE_IGNORE_NAME);
    }
    @Override
    public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value) {
        return SchemaUtils.normalizeStringAttributeValue(value, TRIM, CASE_FOLD);
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CaseIgnoreSubstringMatchingRuleImpl.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2014 ForgeRock AS
 *      Portions copyright 2014-2015 ForgeRock AS
 */
package org.forgerock.opendj.ldap.schema;
@@ -31,12 +31,19 @@
import org.forgerock.opendj.ldap.DecodeException;
import static com.forgerock.opendj.util.StringPrepProfile.*;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
/**
 * This class defines the caseIgnoreSubstringsMatch matching rule defined in
 * X.520 and referenced in RFC 2252.
 */
final class CaseIgnoreSubstringMatchingRuleImpl extends AbstractSubstringMatchingRuleImpl {
    CaseIgnoreSubstringMatchingRuleImpl() {
        super(SMR_CASE_IGNORE_NAME, EMR_CASE_IGNORE_NAME);
    }
    @Override
    public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value) {
        return normalize(TRIM, value);
    }
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CertificateExactMatchingRuleImpl.java
@@ -23,7 +23,7 @@
 *
 *      Copyright 2006-2009 Sun Microsystems, Inc.
 *      Portions Copyright 2013-2014 Manuel Gaupp
 *      Copyright 2014 ForgeRock AS
 *      Copyright 2014-2015 ForgeRock AS
 */
package org.forgerock.opendj.ldap.schema;
@@ -49,6 +49,7 @@
import com.forgerock.opendj.util.StaticUtils;
import static com.forgerock.opendj.ldap.CoreMessages.*;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
/**
 * This class implements the certificateExactMatch matching rule defined in
@@ -74,6 +75,10 @@
     */
    private static final String GSER_ID_RDNSEQUENCE = "rdnSequence";
    CertificateExactMatchingRuleImpl() {
        super(EMR_CERTIFICATE_EXACT_NAME);
    }
    /**
     * Retrieves the normalized form of the provided value, which is best suited
     * for efficiently performing matching operations on that value.
@@ -129,7 +134,7 @@
            // Assume the assertion value is a certificate and parse issuer and
            // serial number. If the value is not even a certificate then the
            // raw bytes will be returned.
            return DefaultAssertion.equality(normalizeAttributeValue(schema, value));
            return defaultAssertion(normalizeAttributeValue(schema, value));
        }
        final BigInteger serialNumber;
@@ -181,7 +186,7 @@
        }
        final ByteString certificateIssuer = normalizeDN(schema, dnstring);
        return DefaultAssertion.equality(createEncodedValue(serialNumber, certificateIssuer));
        return defaultAssertion(createEncodedValue(serialNumber, certificateIssuer));
    }
    private ByteString normalizeDN(final Schema schema, final String dnstring) throws DecodeException {
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CollationMatchingRulesImpl.java
@@ -21,12 +21,12 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2014 ForgeRock AS.
 *      Copyright 2014-2015 ForgeRock AS.
 */
package org.forgerock.opendj.ldap.schema;
import java.text.Collator;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@@ -40,6 +40,7 @@
import org.forgerock.opendj.ldap.DecodeException;
import org.forgerock.opendj.ldap.spi.IndexQueryFactory;
import org.forgerock.opendj.ldap.spi.Indexer;
import org.forgerock.opendj.ldap.spi.IndexingOptions;
/**
 * Implementations of collation matching rules. Each matching rule is created
@@ -183,7 +184,7 @@
        /** {@inheritDoc} */
        @Override
        public Collection<? extends Indexer> getIndexers() {
        public Collection<? extends Indexer> createIndexers(IndexingOptions options) {
            return Collections.singletonList(indexer);
        }
@@ -219,7 +220,7 @@
        @Override
        public Assertion getAssertion(final Schema schema, final ByteSequence assertionValue)
                throws DecodeException {
            return DefaultAssertion.named(indexName, normalizeAttributeValue(schema, assertionValue));
            return named(indexName, normalizeAttributeValue(schema, assertionValue));
        }
    }
@@ -230,7 +231,6 @@
    private static final class CollationSubstringMatchingRuleImpl extends AbstractCollationMatchingRuleImpl {
        private final AbstractSubstringMatchingRuleImpl substringMatchingRule;
        private final Indexer subIndexer;
        /**
         * Creates the matching rule with the provided locale.
@@ -248,8 +248,6 @@
                    return CollationSubstringMatchingRuleImpl.this.normalizeAttributeValue(schema, value);
                }
            };
            // default substring matching rule has exactly one indexer
            subIndexer = substringMatchingRule.getIndexers().iterator().next();
        }
        @Override
@@ -266,8 +264,10 @@
        /** {@inheritDoc} */
        @Override
        public final Collection<? extends Indexer> getIndexers() {
            return Arrays.asList(subIndexer, indexer);
        public final Collection<? extends Indexer> createIndexers(IndexingOptions options) {
            final Collection<Indexer> indexers = new ArrayList<>(substringMatchingRule.createIndexers(options));
            indexers.add(indexer);
            return indexers;
        }
    }
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DITContentRuleSyntaxImpl.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2011-2014 ForgeRock AS
 *      Portions copyright 2011-2015 ForgeRock AS
 */
package org.forgerock.opendj.ldap.schema;
@@ -50,7 +50,7 @@
    @Override
    public String getEqualityMatchingRule() {
        return EMR_OID_FIRST_COMPONENT_OID;
        return EMR_OID_FIRST_COMPONENT_NAME;
    }
    public String getName() {
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DirectoryStringFirstComponentEqualityMatchingRuleImpl.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2014 ForgeRock AS
 *      Portions copyright 2014-2015 ForgeRock AS
 */
package org.forgerock.opendj.ldap.schema;
@@ -30,6 +30,7 @@
import static com.forgerock.opendj.util.StringPrepProfile.TRIM;
import static com.forgerock.opendj.ldap.CoreMessages.ERR_ATTR_SYNTAX_EMPTY_VALUE;
import static com.forgerock.opendj.ldap.CoreMessages.ERR_ATTR_SYNTAX_EXPECTED_OPEN_PARENTHESIS;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.EMR_DIRECTORY_STRING_FIRST_COMPONENT_NAME;
import org.forgerock.opendj.ldap.Assertion;
import org.forgerock.opendj.ldap.ByteSequence;
@@ -48,9 +49,14 @@
 */
final class DirectoryStringFirstComponentEqualityMatchingRuleImpl extends AbstractEqualityMatchingRuleImpl {
    DirectoryStringFirstComponentEqualityMatchingRuleImpl() {
      super(EMR_DIRECTORY_STRING_FIRST_COMPONENT_NAME);
    }
    @Override
    public Assertion getAssertion(final Schema schema, final ByteSequence assertionValue) {
        return DefaultAssertion.equality(SchemaUtils.normalizeStringAttributeValue(assertionValue, TRIM, CASE_FOLD));
        return named(EMR_DIRECTORY_STRING_FIRST_COMPONENT_NAME,
                SchemaUtils.normalizeStringAttributeValue(assertionValue, TRIM, CASE_FOLD));
    }
    public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value)
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DistinguishedNameEqualityMatchingRuleImpl.java
@@ -22,10 +22,12 @@
 *
 *
 *      Copyright 2009-2010 Sun Microsystems, Inc.
 *      Portions copyright 2011-2014 ForgeRock AS.
 *      Portions copyright 2011-2015 ForgeRock AS.
 */
package org.forgerock.opendj.ldap.schema;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
import org.forgerock.i18n.LocalizedIllegalArgumentException;
import org.forgerock.opendj.ldap.ByteSequence;
import org.forgerock.opendj.ldap.ByteString;
@@ -38,6 +40,10 @@
 */
final class DistinguishedNameEqualityMatchingRuleImpl extends AbstractEqualityMatchingRuleImpl {
    DistinguishedNameEqualityMatchingRuleImpl() {
        super(EMR_DN_NAME);
    }
    /** {@inheritDoc} */
    public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value)
            throws DecodeException {
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DoubleMetaphoneApproximateMatchingRuleImpl.java
@@ -22,10 +22,12 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2014 ForgeRock AS
 *      Portions copyright 2014-2015 ForgeRock AS
 */
package org.forgerock.opendj.ldap.schema;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.ldap.ByteSequence;
@@ -53,6 +55,10 @@
    private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
    DoubleMetaphoneApproximateMatchingRuleImpl() {
      super(AMR_DOUBLE_METAPHONE_NAME);
    }
    /** {@inheritDoc} */
    public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value) {
        String valueString = value.toString();
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/EnumOrderingMatchingRule.java
@@ -22,10 +22,12 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions Copyright 2015 ForgeRock AS.
 */
package org.forgerock.opendj.ldap.schema;
import static com.forgerock.opendj.ldap.CoreMessages.WARN_ATTR_SYNTAX_LDAPSYNTAX_ENUM_INVALID_VALUE;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
import org.forgerock.opendj.ldap.ByteSequence;
import org.forgerock.opendj.ldap.ByteString;
@@ -42,10 +44,12 @@
    private final EnumSyntaxImpl syntax;
    EnumOrderingMatchingRule(final EnumSyntaxImpl syntax) {
        super(OMR_GENERIC_ENUM_NAME);
        Reject.ifNull(syntax);
        this.syntax = syntax;
    }
    @Override
    public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value)
            throws DecodeException {
        final int index = syntax.indexOf(value);
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/GeneralizedTimeEqualityMatchingRuleImpl.java
@@ -22,10 +22,12 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2012-2014 ForgeRock AS.
 *      Portions copyright 2012-2015 ForgeRock AS.
 */
package org.forgerock.opendj.ldap.schema;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
import org.forgerock.i18n.LocalizedIllegalArgumentException;
import org.forgerock.opendj.ldap.ByteSequence;
import org.forgerock.opendj.ldap.ByteString;
@@ -37,6 +39,11 @@
 * and referenced in RFC 2252.
 */
final class GeneralizedTimeEqualityMatchingRuleImpl extends AbstractEqualityMatchingRuleImpl {
    GeneralizedTimeEqualityMatchingRuleImpl() {
        super(EMR_GENERALIZED_TIME_NAME);
    }
    public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value)
            throws DecodeException {
        try {
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/GeneralizedTimeOrderingMatchingRuleImpl.java
@@ -22,10 +22,12 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2012 ForgeRock AS.
 *      Portions copyright 2012-2015 ForgeRock AS.
 */
package org.forgerock.opendj.ldap.schema;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
import org.forgerock.i18n.LocalizedIllegalArgumentException;
import org.forgerock.opendj.ldap.ByteSequence;
import org.forgerock.opendj.ldap.ByteString;
@@ -37,6 +39,12 @@
 * X.520 and referenced in RFC 2252.
 */
final class GeneralizedTimeOrderingMatchingRuleImpl extends AbstractOrderingMatchingRuleImpl {
    GeneralizedTimeOrderingMatchingRuleImpl() {
        // Reusing equality index since OPENDJ-1864
        super(EMR_GENERALIZED_TIME_NAME);
    }
    public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value)
            throws DecodeException {
        try {
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/IntegerEqualityMatchingRuleImpl.java
@@ -27,6 +27,7 @@
package org.forgerock.opendj.ldap.schema;
import static org.forgerock.opendj.ldap.schema.IntegerOrderingMatchingRuleImpl.normalizeValueAndEncode;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
import org.forgerock.opendj.ldap.ByteSequence;
import org.forgerock.opendj.ldap.ByteString;
@@ -39,6 +40,11 @@
 * potentially share the same index.
 */
final class IntegerEqualityMatchingRuleImpl extends AbstractEqualityMatchingRuleImpl {
    IntegerEqualityMatchingRuleImpl() {
        super(EMR_INTEGER_NAME);
    }
    @Override
    public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value)
            throws DecodeException {
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/IntegerFirstComponentEqualityMatchingRuleImpl.java
@@ -22,11 +22,12 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2013-2014 ForgeRock AS
 *      Portions copyright 2013-2015 ForgeRock AS
 */
package org.forgerock.opendj.ldap.schema;
import static com.forgerock.opendj.ldap.CoreMessages.*;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.slf4j.LocalizedLogger;
@@ -48,6 +49,10 @@
    private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
    IntegerFirstComponentEqualityMatchingRuleImpl() {
        super(EMR_INTEGER_FIRST_COMPONENT_NAME);
    }
    @Override
    public Assertion getAssertion(final Schema schema, final ByteSequence assertionValue)
            throws DecodeException {
@@ -55,7 +60,7 @@
            final String definition = assertionValue.toString();
            final SubstringReader reader = new SubstringReader(definition);
            final int intValue = SchemaUtils.readRuleID(reader);
            return DefaultAssertion.equality(ByteString.valueOf(intValue));
            return defaultAssertion(ByteString.valueOf(intValue));
        } catch (final Exception e) {
            logger.debug(LocalizableMessage.raw("%s", e));
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/IntegerOrderingMatchingRuleImpl.java
@@ -27,6 +27,7 @@
package org.forgerock.opendj.ldap.schema;
import static com.forgerock.opendj.ldap.CoreMessages.WARN_ATTR_SYNTAX_ILLEGAL_INTEGER;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
import java.math.BigInteger;
@@ -136,6 +137,11 @@
        }
    }
    public IntegerOrderingMatchingRuleImpl() {
        // Reusing equality index since OPENDJ-1864
        super(EMR_INTEGER_NAME);
    }
    @Override
    public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value)
            throws DecodeException {
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/KeywordEqualityMatchingRuleImpl.java
@@ -22,12 +22,13 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2014 ForgeRock AS
 *      Portions copyright 2014-2015 ForgeRock AS
 */
package org.forgerock.opendj.ldap.schema;
import static com.forgerock.opendj.util.StringPrepProfile.CASE_FOLD;
import static com.forgerock.opendj.util.StringPrepProfile.TRIM;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
import java.util.Collection;
import java.util.Collections;
@@ -39,6 +40,7 @@
import org.forgerock.opendj.ldap.DecodeException;
import org.forgerock.opendj.ldap.spi.IndexQueryFactory;
import org.forgerock.opendj.ldap.spi.Indexer;
import org.forgerock.opendj.ldap.spi.IndexingOptions;
/**
 * This class implements the keywordMatch matching rule defined in X.520. That
@@ -60,6 +62,11 @@
 * </UL>
 */
final class KeywordEqualityMatchingRuleImpl extends AbstractEqualityMatchingRuleImpl {
    KeywordEqualityMatchingRuleImpl() {
        super(EMR_KEYWORD_NAME);
    }
    @Override
    public Assertion getAssertion(final Schema schema, final ByteSequence assertionValue)
            throws DecodeException {
@@ -122,7 +129,7 @@
    /** {@inheritDoc} */
    @Override
    public Collection<? extends Indexer> getIndexers() {
    public Collection<? extends Indexer> createIndexers(IndexingOptions options) {
        return Collections.emptySet();
    }
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/MatchingRule.java
@@ -41,6 +41,7 @@
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.DecodeException;
import org.forgerock.opendj.ldap.spi.Indexer;
import org.forgerock.opendj.ldap.spi.IndexingOptions;
/**
 * This class defines a data structure for storing and interacting with matching
@@ -348,12 +349,14 @@
    }
    /**
     * Returns the indexers for this matching rule.
     * Returns the indexers for this matching rule configured using the provided indexing options.
     *
     * @param options
     *            The indexing options
     * @return the collection of indexers for this matching rule.
     */
    public Collection<? extends Indexer> getIndexers() {
        return impl.getIndexers();
    public Collection<? extends Indexer> createIndexers(IndexingOptions options) {
        return impl.createIndexers(options);
    }
    /**
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/MatchingRuleImpl.java
@@ -34,6 +34,7 @@
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.DecodeException;
import org.forgerock.opendj.ldap.spi.Indexer;
import org.forgerock.opendj.ldap.spi.IndexingOptions;
/**
 * This interface defines the set of methods that must be implemented to define
@@ -133,16 +134,9 @@
    /**
     * Returns the indexers for this matching rule.
     *
     * @param options
     *          The indexing options
     * @return a non null collection of indexers for this matching rule.
     */
    Collection<? extends Indexer> getIndexers();
    /**
     * Returns whether a backend can build an index for this matching rule.
     *
     * @return true a backend can build an index for this matching rule,
     *         false otherwise.
     */
    boolean isIndexingSupported();
    Collection<? extends Indexer> createIndexers(IndexingOptions options);
}
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/NumericStringEqualityMatchingRuleImpl.java
@@ -22,10 +22,12 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2014 ForgeRock AS
 *      Portions copyright 2014-2015 ForgeRock AS
 */
package org.forgerock.opendj.ldap.schema;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
import org.forgerock.opendj.ldap.ByteSequence;
import org.forgerock.opendj.ldap.ByteString;
@@ -35,6 +37,11 @@
 * spaces, but ignores spaces when performing matching.
 */
final class NumericStringEqualityMatchingRuleImpl extends AbstractEqualityMatchingRuleImpl {
    NumericStringEqualityMatchingRuleImpl() {
        super(EMR_NUMERIC_STRING_NAME);
    }
    public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value) {
        return SchemaUtils.normalizeNumericStringAttributeValue(value);
    }
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/NumericStringOrderingMatchingRuleImpl.java
@@ -22,10 +22,12 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2014 ForgeRock AS
 *      Portions copyright 2014-2015 ForgeRock AS
 */
package org.forgerock.opendj.ldap.schema;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
import org.forgerock.opendj.ldap.ByteSequence;
import org.forgerock.opendj.ldap.ByteString;
@@ -34,6 +36,12 @@
 * in X.520 and referenced in RFC 2252.
 */
final class NumericStringOrderingMatchingRuleImpl extends AbstractOrderingMatchingRuleImpl {
    NumericStringOrderingMatchingRuleImpl() {
        // Reusing equality index since OPENDJ-1864
        super(EMR_NUMERIC_STRING_NAME);
    }
    public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value) {
        return SchemaUtils.normalizeNumericStringAttributeValue(value);
    }
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/NumericStringSubstringMatchingRuleImpl.java
@@ -22,10 +22,12 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2014 ForgeRock AS
 *      Portions copyright 2014-2015 ForgeRock AS
 */
package org.forgerock.opendj.ldap.schema;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
import org.forgerock.opendj.ldap.ByteSequence;
import org.forgerock.opendj.ldap.ByteString;
@@ -34,6 +36,11 @@
 * in X.520 and referenced in RFC 2252.
 */
final class NumericStringSubstringMatchingRuleImpl extends AbstractSubstringMatchingRuleImpl {
    NumericStringSubstringMatchingRuleImpl() {
        super(SMR_NUMERIC_STRING_NAME, EMR_NUMERIC_STRING_NAME);
    }
    public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value) {
        return SchemaUtils.normalizeNumericStringAttributeValue(value);
    }
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/ObjectIdentifierEqualityMatchingRuleImpl.java
@@ -26,6 +26,7 @@
 */
package org.forgerock.opendj.ldap.schema;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
import static org.forgerock.opendj.ldap.schema.SchemaOptions.*;
import static org.forgerock.opendj.ldap.schema.SchemaUtils.*;
@@ -44,6 +45,10 @@
 */
final class ObjectIdentifierEqualityMatchingRuleImpl extends AbstractEqualityMatchingRuleImpl {
    ObjectIdentifierEqualityMatchingRuleImpl() {
        super(EMR_OID_NAME);
    }
    static String resolveNames(final Schema schema, final String oid) {
        if (!StaticUtils.isDigit(oid.charAt(0))) {
            // Do an best effort attempt to normalize names to OIDs.
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/ObjectIdentifierFirstComponentEqualityMatchingRuleImpl.java
@@ -29,6 +29,7 @@
import static com.forgerock.opendj.ldap.CoreMessages.*;
import static org.forgerock.opendj.ldap.schema.ObjectIdentifierEqualityMatchingRuleImpl.*;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
import static org.forgerock.opendj.ldap.schema.SchemaOptions.*;
import static org.forgerock.opendj.ldap.schema.SchemaUtils.*;
@@ -50,9 +51,13 @@
 */
final class ObjectIdentifierFirstComponentEqualityMatchingRuleImpl extends AbstractEqualityMatchingRuleImpl {
    ObjectIdentifierFirstComponentEqualityMatchingRuleImpl() {
        super(EMR_OID_FIRST_COMPONENT_NAME);
    }
    @Override
    public Assertion getAssertion(final Schema schema, final ByteSequence assertionValue) throws DecodeException {
        return DefaultAssertion.equality(normalizeAttributeValuePrivate(schema, assertionValue));
        return defaultAssertion(normalizeAttributeValuePrivate(schema, assertionValue));
    }
    @Override
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/OctetStringEqualityMatchingRuleImpl.java
@@ -22,10 +22,12 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2014 ForgeRock AS
 *      Portions copyright 2014-2015 ForgeRock AS
 */
package org.forgerock.opendj.ldap.schema;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
import org.forgerock.opendj.ldap.ByteSequence;
import org.forgerock.opendj.ldap.ByteString;
@@ -35,6 +37,11 @@
 * string syntaxes.
 */
final class OctetStringEqualityMatchingRuleImpl extends AbstractEqualityMatchingRuleImpl {
    OctetStringEqualityMatchingRuleImpl() {
        super(EMR_OCTET_STRING_NAME);
    }
    public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value) {
        return value.toByteString();
    }
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/OctetStringOrderingMatchingRuleImpl.java
@@ -22,9 +22,12 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2015 ForgeRock AS.
 */
package org.forgerock.opendj.ldap.schema;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
import org.forgerock.opendj.ldap.ByteSequence;
import org.forgerock.opendj.ldap.ByteString;
@@ -34,6 +37,12 @@
 * octet string syntaxes.
 */
final class OctetStringOrderingMatchingRuleImpl extends AbstractOrderingMatchingRuleImpl {
    OctetStringOrderingMatchingRuleImpl() {
        // Reusing equality index since OPENDJ-1864
        super(EMR_OCTET_STRING_NAME);
    }
    public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value) {
        return value.toByteString();
    }
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/OctetStringSubstringMatchingRuleImpl.java
@@ -22,9 +22,12 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions Copyright 2015 ForgeRock AS.
 */
package org.forgerock.opendj.ldap.schema;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
import org.forgerock.opendj.ldap.ByteSequence;
import org.forgerock.opendj.ldap.ByteString;
@@ -34,6 +37,11 @@
 * and octet string syntaxes.
 */
final class OctetStringSubstringMatchingRuleImpl extends AbstractSubstringMatchingRuleImpl {
    OctetStringSubstringMatchingRuleImpl() {
        super(SMR_OCTET_STRING_NAME, EMR_OCTET_STRING_NAME);
    }
    public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value) {
        return value.toByteString();
    }
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/PresentationAddressEqualityMatchingRuleImpl.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2014 ForgeRock AS
 *      Portions copyright 2014-2015 ForgeRock AS
 */
package org.forgerock.opendj.ldap.schema;
@@ -30,6 +30,7 @@
import org.forgerock.opendj.ldap.ByteString;
import static com.forgerock.opendj.util.StringPrepProfile.*;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
/**
 * This class implements the presentationAddressMatch matching rule defined in
@@ -38,6 +39,11 @@
 * like the caseIgnoreMatch rule.
 */
final class PresentationAddressEqualityMatchingRuleImpl extends AbstractEqualityMatchingRuleImpl {
    PresentationAddressEqualityMatchingRuleImpl() {
        super(EMR_PRESENTATION_ADDRESS_NAME);
    }
    @Override
    public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value) {
        return SchemaUtils.normalizeStringAttributeValue(value, TRIM, CASE_FOLD);
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/ProtocolInformationEqualityMatchingRuleImpl.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2014 ForgeRock AS
 *      Portions copyright 2014-2015 ForgeRock AS
 */
package org.forgerock.opendj.ldap.schema;
@@ -30,6 +30,7 @@
import org.forgerock.opendj.ldap.ByteString;
import static com.forgerock.opendj.util.StringPrepProfile.*;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
/**
 * This class implements the protocolInformationMatch matching rule defined in
@@ -38,6 +39,11 @@
 * like the caseIgnoreMatch rule.
 */
final class ProtocolInformationEqualityMatchingRuleImpl extends AbstractEqualityMatchingRuleImpl {
    ProtocolInformationEqualityMatchingRuleImpl() {
        super(EMR_PROTOCOL_INFORMATION_NAME);
    }
    @Override
    public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value) {
        return SchemaUtils.normalizeStringAttributeValue(value, TRIM, CASE_FOLD);
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/TelephoneNumberEqualityMatchingRuleImpl.java
@@ -22,10 +22,12 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2014 ForgeRock AS
 *      Portions copyright 2014-2015 ForgeRock AS
 */
package org.forgerock.opendj.ldap.schema;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
import org.forgerock.opendj.ldap.ByteSequence;
import org.forgerock.opendj.ldap.ByteString;
@@ -38,6 +40,11 @@
 * only numeric digits and strip out everything else.
 */
final class TelephoneNumberEqualityMatchingRuleImpl extends AbstractEqualityMatchingRuleImpl {
    TelephoneNumberEqualityMatchingRuleImpl() {
        super(EMR_TELEPHONE_NAME);
    }
    public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value) {
        final String valueString = value.toString();
        final int valueLength = valueString.length();
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/TelephoneNumberSubstringMatchingRuleImpl.java
@@ -22,9 +22,12 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions Copyright 2015 ForgeRock AS.
 */
package org.forgerock.opendj.ldap.schema;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
import org.forgerock.opendj.ldap.ByteSequence;
import org.forgerock.opendj.ldap.ByteString;
@@ -37,6 +40,11 @@
 * this matching will compare only numeric digits and strip out everything else.
 */
final class TelephoneNumberSubstringMatchingRuleImpl extends AbstractSubstringMatchingRuleImpl {
    TelephoneNumberSubstringMatchingRuleImpl() {
        super(SMR_TELEPHONE_NAME, EMR_TELEPHONE_NAME);
    }
    public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value) {
        final String valueString = value.toString();
        final int valueLength = valueString.length();
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/TimeBasedMatchingRulesImpl.java
@@ -46,23 +46,18 @@
import org.forgerock.opendj.ldap.spi.IndexQueryFactory;
import org.forgerock.opendj.ldap.spi.Indexer;
import org.forgerock.opendj.ldap.spi.IndexingOptions;
import org.forgerock.util.time.TimeService;
import static com.forgerock.opendj.ldap.CoreMessages.*;
import static com.forgerock.opendj.util.StaticUtils.*;
import static org.forgerock.opendj.ldap.DecodeException.*;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
/**
 * Implementations of time-based matching rules.
 */
final class TimeBasedMatchingRulesImpl {
    private static final String RELATIVE_TIME_INDEX_ID = "rt";
    private static final String PARTIAL_DATE_TIME_INDEX_ID = "pdt";
    private static final String EXTENSIBLE_INDEX_ID = "ext";
    private static final TimeZone TIME_ZONE_UTC = TimeZone.getTimeZone("UTC");
    /** Constants for generating keys. */
@@ -114,7 +109,7 @@
        /** {@inheritDoc} */
        @Override
        public ByteString normalizeAttributeValue(Schema schema, ByteSequence value) throws DecodeException {
        public final ByteString normalizeAttributeValue(Schema schema, ByteSequence value) throws DecodeException {
            try {
                return ByteString.valueOf(GeneralizedTime.valueOf(value.toString()).getTimeInMillis());
            } catch (final LocalizedIllegalArgumentException e) {
@@ -133,11 +128,11 @@
     */
    private static abstract class RelativeTimeOrderingMatchingRuleImpl extends TimeBasedMatchingRuleImpl {
        final Indexer indexer = new DefaultIndexer(RELATIVE_TIME_INDEX_ID + "." + EXTENSIBLE_INDEX_ID);
        final Indexer indexer = new DefaultIndexer(EMR_GENERALIZED_TIME_NAME);
        /** {@inheritDoc} */
        @Override
        public Collection<? extends Indexer> getIndexers() {
        public Collection<? extends Indexer> createIndexers(IndexingOptions options) {
            return Collections.singletonList(indexer);
        }
@@ -291,11 +286,11 @@
     */
    private static final class PartialDateAndTimeMatchingRuleImpl extends TimeBasedMatchingRuleImpl {
        final Indexer indexer = new PartialDateAndTimeIndexer(this);
        private final Indexer indexer = new PartialDateAndTimeIndexer(this);
        /** {@inheritDoc} */
        @Override
        public Collection<? extends Indexer> getIndexers() {
        public Collection<? extends Indexer> createIndexers(IndexingOptions options) {
            return Collections.singletonList(indexer);
        }
@@ -585,15 +580,14 @@
        /** {@inheritDoc} */
        @Override
        public void createKeys(Schema schema, ByteSequence value, IndexingOptions options,
                Collection<ByteString> keys) {
        public void createKeys(Schema schema, ByteSequence value, Collection<ByteString> keys) {
            matchingRule.timeKeys(value, keys);
        }
        /** {@inheritDoc} */
        @Override
        public String getIndexID() {
            return PARTIAL_DATE_TIME_INDEX_ID + "." + EXTENSIBLE_INDEX_ID;
            return MR_PARTIAL_DATE_AND_TIME_NAME;
        }
    }
}
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/UUIDEqualityMatchingRuleImpl.java
@@ -22,13 +22,14 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2014 ForgeRock AS
 *      Portions copyright 2014-2015 ForgeRock AS
 */
package org.forgerock.opendj.ldap.schema;
import static com.forgerock.opendj.ldap.CoreMessages.WARN_ATTR_SYNTAX_UUID_EXPECTED_DASH;
import static com.forgerock.opendj.ldap.CoreMessages.WARN_ATTR_SYNTAX_UUID_EXPECTED_HEX;
import static com.forgerock.opendj.ldap.CoreMessages.WARN_ATTR_SYNTAX_UUID_INVALID_LENGTH;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.opendj.ldap.ByteSequence;
@@ -40,6 +41,11 @@
 * be used as the default equality matching rule for the UUID syntax.
 */
final class UUIDEqualityMatchingRuleImpl extends AbstractEqualityMatchingRuleImpl {
    UUIDEqualityMatchingRuleImpl() {
        super(EMR_UUID_NAME);
    }
    public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value)
            throws DecodeException {
        if (value.length() != 36) {
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/UUIDOrderingMatchingRuleImpl.java
@@ -22,12 +22,14 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2015 ForgeRock AS.
 */
package org.forgerock.opendj.ldap.schema;
import static com.forgerock.opendj.ldap.CoreMessages.WARN_ATTR_SYNTAX_UUID_EXPECTED_DASH;
import static com.forgerock.opendj.ldap.CoreMessages.WARN_ATTR_SYNTAX_UUID_EXPECTED_HEX;
import static com.forgerock.opendj.ldap.CoreMessages.WARN_ATTR_SYNTAX_UUID_INVALID_LENGTH;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.opendj.ldap.ByteSequence;
@@ -39,6 +41,12 @@
 * This will be the default ordering matching rule for the UUID syntax.
 */
final class UUIDOrderingMatchingRuleImpl extends AbstractOrderingMatchingRuleImpl {
    UUIDOrderingMatchingRuleImpl() {
        // Reusing equality index since OPENDJ-1864
        super(EMR_UUID_NAME);
    }
    public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value)
            throws DecodeException {
        if (value.length() != 36) {
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/UniqueMemberEqualityMatchingRuleImpl.java
@@ -22,10 +22,12 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2014 ForgeRock AS
 *      Portions copyright 2014-2015 ForgeRock AS
 */
package org.forgerock.opendj.ldap.schema;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
import org.forgerock.i18n.LocalizedIllegalArgumentException;
import org.forgerock.opendj.ldap.ByteSequence;
import org.forgerock.opendj.ldap.ByteString;
@@ -41,6 +43,10 @@
 */
final class UniqueMemberEqualityMatchingRuleImpl extends AbstractEqualityMatchingRuleImpl {
    UniqueMemberEqualityMatchingRuleImpl() {
        super(EMR_UNIQUE_MEMBER_NAME);
    }
    public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value) throws DecodeException {
        // Separate value into normalized DN and "optional uid" portion.
        final String stringValue = value.toString().trim();
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/UserPasswordExactEqualityMatchingRuleImpl.java
@@ -22,10 +22,12 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2014 ForgeRock AS
 *      Portions copyright 2014-2015 ForgeRock AS
 */
package org.forgerock.opendj.ldap.schema;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
import org.forgerock.opendj.ldap.ByteSequence;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.DecodeException;
@@ -38,6 +40,11 @@
 * equal to each other.
 */
final class UserPasswordExactEqualityMatchingRuleImpl extends AbstractEqualityMatchingRuleImpl {
    UserPasswordExactEqualityMatchingRuleImpl() {
        super(EMR_USER_PASSWORD_EXACT_NAME);
    }
    public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value)
            throws DecodeException {
        // The normalized form of this matching rule is exactly equal to the
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/Indexer.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2014 ForgeRock AS
 *      Portions copyright 2014-2015 ForgeRock AS
 */
package org.forgerock.opendj.ldap.spi;
@@ -58,13 +58,10 @@
     *          The schema in which the associated matching rule is defined.
     * @param value
     *          The attribute value for which keys are required.
     * @param options
     *          The indexing options
     * @param keys
     *          A collection where to add the created keys.
     * @throws DecodeException if an error occurs while normalizing the value
     */
    void createKeys(Schema schema, ByteSequence value, IndexingOptions options,
            Collection<ByteString> keys) throws DecodeException;
    void createKeys(Schema schema, ByteSequence value, Collection<ByteString> keys) throws DecodeException;
}
opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/AbstractSubstringMatchingRuleImplTest.java
@@ -43,6 +43,7 @@
import org.testng.annotations.Test;
import static org.forgerock.opendj.ldap.ByteString.*;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
import static org.mockito.Mockito.*;
import static org.testng.Assert.*;
@@ -54,6 +55,10 @@
    private static class FakeSubstringMatchingRuleImpl extends AbstractSubstringMatchingRuleImpl {
        FakeSubstringMatchingRuleImpl() {
            super(SMR_CASE_EXACT_OID, EMR_CASE_EXACT_OID);
        }
        /** {@inheritDoc} */
        @Override
        public ByteString normalizeAttributeValue(Schema schema, ByteSequence value) throws DecodeException {
@@ -210,8 +215,8 @@
        assertEquals(
            assertion.createIndexQuery(new FakeIndexQueryFactory(newIndexingOptions())),
            "intersect["
                    + "exactMatch(substring, value=='his'), "
                    + "exactMatch(substring, value=='thi')"
                    + "exactMatch(" + SMR_CASE_EXACT_OID + ", value=='his'), "
                    + "exactMatch(" + SMR_CASE_EXACT_OID + ", value=='thi')"
                    + "]");
    }
@@ -223,11 +228,11 @@
        assertEquals(
            assertion.createIndexQuery(new FakeIndexQueryFactory(newIndexingOptions())),
            "intersect["
                    + "rangeMatch(equality, 'abc' <= value < 'abd'), "
                    + "exactMatch(substring, value=='def'), "
                    + "exactMatch(substring, value=='ghi'), "
                    + "exactMatch(substring, value=='jkl'), "
                    + "exactMatch(substring, value=='abc')"
                    + "rangeMatch(" + EMR_CASE_EXACT_OID + ", 'abc' <= value < 'abd'), "
                    + "exactMatch(" + SMR_CASE_EXACT_OID + ", value=='def'), "
                    + "exactMatch(" + SMR_CASE_EXACT_OID + ", value=='ghi'), "
                    + "exactMatch(" + SMR_CASE_EXACT_OID + ", value=='jkl'), "
                    + "exactMatch(" + SMR_CASE_EXACT_OID + ", value=='abc')"
                    + "]");
    }
@@ -240,8 +245,8 @@
        assertEquals(
            assertion.createIndexQuery(new FakeIndexQueryFactory(newIndexingOptions())),
            "intersect["
                    + "rangeMatch(equality, 'aa' <= value < 'ab'), "
                    + "rangeMatch(substring, 'aa' <= value < 'ab')"
                    + "rangeMatch(" + EMR_CASE_EXACT_OID + ", 'aa' <= value < 'ab'), "
                    + "rangeMatch(" + SMR_CASE_EXACT_OID + ", 'aa' <= value < 'ab')"
                    + "]");
    }
@@ -257,19 +262,19 @@
            // 0x00 is the nul byte, a.k.a. string terminator
            // so everything after it is not part of the string
            "intersect["
                    + "rangeMatch(equality, '" + lower + "' <= value < 'b\u0000'), "
                    + "rangeMatch(substring, '" + lower + "' <= value < 'b\u0000')"
                    + "rangeMatch(" + EMR_CASE_EXACT_OID + ", '" + lower + "' <= value < 'b\u0000'), "
                    + "rangeMatch(" + SMR_CASE_EXACT_OID + ", '" + lower + "' <= value < 'b\u0000')"
                    + "]");
    }
    @Test
    public void testIndexer() throws Exception {
        final Indexer indexer = getRule().getIndexers().iterator().next();
        Assertions.assertThat(indexer.getIndexID()).isEqualTo("substring");
        final IndexingOptions options = newIndexingOptions();
        final Indexer indexer = getRule().createIndexers(options).iterator().next();
        Assertions.assertThat(indexer.getIndexID()).isEqualTo(SMR_CASE_EXACT_OID + ":" + options.substringKeySize());
        final TreeSet<ByteString> keys = new TreeSet<>();
        indexer.createKeys(Schema.getCoreSchema(), valueOf("ABCDE"), options, keys);
        indexer.createKeys(Schema.getCoreSchema(), valueOf("ABCDE"), keys);
        Assertions.assertThat(keys).containsOnly((Object[]) toByteStrings("ABC", "BCD", "CDE", "DE", "E"));
    }
opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/PartialDateAndTimeMatchingRuleTestCase.java
@@ -21,7 +21,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2014 ForgeRock AS.
 *      Copyright 2014-2015 ForgeRock AS.
 */
package org.forgerock.opendj.ldap.schema;
@@ -35,11 +35,11 @@
import org.forgerock.opendj.ldap.schema.AbstractSubstringMatchingRuleImplTest.FakeIndexQueryFactory;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import org.forgerock.util.time.TimeService;
import static org.fest.assertions.Assertions.*;
import static org.forgerock.opendj.ldap.schema.AbstractSubstringMatchingRuleImplTest.*;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
@SuppressWarnings("javadoc")
@Test
@@ -187,6 +187,7 @@
        Assertion assertion = getRule().getAssertion(ByteString.valueOf("2012Y"));
        String indexQuery = assertion.createIndexQuery(new FakeIndexQueryFactory(newIndexingOptions(), false));
        assertThat(indexQuery).matches("intersect\\[exactMatch\\(pdt\\.ext, value=='[0-9A-Z ]*'\\)\\]");
        assertThat(indexQuery).matches(
                "intersect\\[exactMatch\\(" + MR_PARTIAL_DATE_AND_TIME_NAME + ", value=='[0-9A-Z ]*'\\)\\]");
    }
}
opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/RelativeTimeGreaterThanMatchingRuleTest.java
@@ -21,7 +21,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2014 ForgeRock AS.
 *      Copyright 2014-2015 ForgeRock AS.
 */
package org.forgerock.opendj.ldap.schema;
@@ -39,6 +39,7 @@
import static org.fest.assertions.Assertions.*;
import static org.forgerock.opendj.ldap.schema.AbstractSubstringMatchingRuleImplTest.*;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
@SuppressWarnings("javadoc")
@Test
@@ -120,6 +121,7 @@
        Assertion assertion = getRule().getAssertion(ByteString.valueOf("+5m"));
        String indexQuery = assertion.createIndexQuery(new FakeIndexQueryFactory(newIndexingOptions(), false));
        assertThat(indexQuery).startsWith("rangeMatch(rt.ext, '").endsWith("' < value < '')");
        assertThat(indexQuery).startsWith("rangeMatch(" + EMR_GENERALIZED_TIME_NAME + ", '")
                .endsWith("' < value < '')");
    }
}
opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/RelativeTimeLessThanMatchingRuleTest.java
@@ -21,7 +21,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2014 ForgeRock AS.
 *      Copyright 2014-2015 ForgeRock AS.
 */
package org.forgerock.opendj.ldap.schema;
@@ -39,6 +39,7 @@
import static org.fest.assertions.Assertions.*;
import static org.forgerock.opendj.ldap.schema.AbstractSubstringMatchingRuleImplTest.*;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
@SuppressWarnings("javadoc")
@Test
@@ -121,6 +122,6 @@
        Assertion assertion = getRule().getAssertion(ByteString.valueOf("+5m"));
        String indexQuery = assertion.createIndexQuery(new FakeIndexQueryFactory(newIndexingOptions(), false));
        assertThat(indexQuery).startsWith("rangeMatch(rt.ext, '' < value < '");
        assertThat(indexQuery).startsWith("rangeMatch(" + EMR_GENERALIZED_TIME_NAME + ", '' < value < '");
    }
}
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/AttributeIndex.java
@@ -28,7 +28,6 @@
package org.opends.server.backends.jeb;
import static org.opends.messages.BackendMessages.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
import java.io.Closeable;
@@ -45,6 +44,7 @@
import org.forgerock.opendj.ldap.DecodeException;
import org.forgerock.opendj.ldap.schema.MatchingRule;
import org.forgerock.opendj.ldap.spi.IndexQueryFactory;
import org.forgerock.opendj.ldap.spi.Indexer;
import org.forgerock.opendj.ldap.spi.IndexingOptions;
import org.forgerock.util.Utils;
import org.opends.server.admin.server.ConfigurationChangeListener;
@@ -116,15 +116,11 @@
  /** The attribute index configuration. */
  private LocalDBIndexCfg indexConfig;
  private IndexingOptions indexingOptions;
  /** The mapping from names to indexes. */
  private final Map<String, Index> nameToIndexes = new HashMap<String, Index>();
  private final IndexQueryFactory<IndexQuery> indexQueryFactory;
  /**
   * The mapping from extensible index types (e.g. "substring" or "shared") to list of indexes.
   */
  private Map<String, Collection<Index>> extensibleIndexesMapping;
  private Map<String, Index> indexIdToIndexes;
  private IndexQueryFactory<IndexQuery> indexQueryFactory;
  /**
   * Create a new attribute index object.
@@ -137,94 +133,119 @@
  {
    this.entryContainer = entryContainer;
    this.indexConfig = indexConfig;
    buildPresenceIndex();
    buildIndexes(IndexType.EQUALITY);
    buildIndexes(IndexType.SUBSTRING);
    buildIndexes(IndexType.ORDERING);
    buildIndexes(IndexType.APPROXIMATE);
    buildExtensibleIndexes();
    final JEIndexConfig config = new JEIndexConfig(indexConfig.getSubstringLength());
    indexQueryFactory = new IndexQueryFactoryImpl(nameToIndexes, config, indexConfig.getAttribute());
    extensibleIndexesMapping = computeExtensibleIndexesMapping();
    this.indexingOptions = new JEIndexingOptions(indexConfig.getSubstringLength());
    this.indexIdToIndexes = Collections.unmodifiableMap(buildIndexes(entryContainer, indexConfig, indexingOptions));
    this.indexQueryFactory = new IndexQueryFactoryImpl(indexIdToIndexes, indexingOptions, indexConfig.getAttribute());
  }
  private void buildPresenceIndex()
  private static Map<String, Index> buildIndexes(EntryContainer entryContainer,
                                                 LocalDBIndexCfg config,
                                                 IndexingOptions options) throws ConfigException
  {
    final IndexType indexType = IndexType.PRESENCE;
    if (indexConfig.getIndexType().contains(indexType))
    {
      String indexID = indexType.toString();
      nameToIndexes.put(indexID, newPresenceIndex(indexConfig));
    final Map<String, Index> indexes = new HashMap<>();
    final AttributeType attributeType = config.getAttribute();
    final int indexEntryLimit = config.getIndexEntryLimit();
    for(IndexType indexType : config.getIndexType()) {
      Collection<? extends Indexer> indexers;
      switch (indexType)
      {
      case PRESENCE:
        indexes.put(indexType.toString(), newPresenceIndex(entryContainer, config));
        indexers = Collections.emptyList();
        break;
      case EXTENSIBLE:
        indexers = getExtensibleIndexers(config.getAttribute(), config.getIndexExtensibleMatchingRule(), options);
        break;
      case APPROXIMATE:
        indexers =
            throwIfNoMatchingRule(attributeType, indexType, attributeType.getApproximateMatchingRule())
              .createIndexers(options);
        break;
      case EQUALITY:
        indexers =
            throwIfNoMatchingRule(attributeType, indexType, attributeType.getEqualityMatchingRule())
              .createIndexers(options);
        break;
      case ORDERING:
        indexers =
            throwIfNoMatchingRule(attributeType, indexType, attributeType.getOrderingMatchingRule())
              .createIndexers(options);
        break;
      case SUBSTRING:
        indexers =
            throwIfNoMatchingRule(attributeType, indexType, attributeType.getSubstringMatchingRule())
              .createIndexers(options);
        break;
      default:
       throw new ConfigException(ERR_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE.get(attributeType, indexType.toString()));
      }
      buildAndRegisterIndexesWithIndexers(entryContainer, attributeType, indexEntryLimit, indexers, indexes);
    }
    return indexes;
  }
  private Index newPresenceIndex(LocalDBIndexCfg cfg)
  private static Index newPresenceIndex(EntryContainer entryContainer, LocalDBIndexCfg cfg)
  {
    final AttributeType attrType = cfg.getAttribute();
    final String indexName = getIndexName(attrType, IndexType.PRESENCE.toString());
    final String indexName = getIndexName(entryContainer, attrType, IndexType.PRESENCE.toString());
    final PresenceIndexer indexer = new PresenceIndexer(attrType);
    return entryContainer.newIndexForAttribute(indexName, indexer, cfg.getIndexEntryLimit());
  }
  private void buildExtensibleIndexes() throws ConfigException
  private static MatchingRule throwIfNoMatchingRule(AttributeType attributeType, IndexType type, MatchingRule rule)
      throws ConfigException
  {
    final IndexType indexType = IndexType.EXTENSIBLE;
    if (indexConfig.getIndexType().contains(indexType))
    if (rule == null)
    {
      final AttributeType attrType = indexConfig.getAttribute();
      Set<String> extensibleRules = indexConfig.getIndexExtensibleMatchingRule();
      if (extensibleRules == null || extensibleRules.isEmpty())
      {
        throw new ConfigException(ERR_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE.get(attrType, indexType.toString()));
      }
      throw new ConfigException(ERR_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE.get(attributeType, type.toString()));
    }
    return rule;
  }
      // Iterate through the Set and create the index only if necessary.
      // Collation equality and Ordering matching rules share the same indexer and index
      // A Collation substring matching rule is treated differently
      // as it uses a separate indexer and index.
      for (final String ruleName : extensibleRules)
  private static void buildAndRegisterIndexesWithIndexers(EntryContainer entryContainer,
                                                          AttributeType attributeType,
                                                          int indexEntryLimit,
                                                          Collection<? extends Indexer> indexers,
                                                          Map<String, Index> indexes)
  {
    for (Indexer indexer : indexers)
    {
      final String indexID = indexer.getIndexID();
      if (!indexes.containsKey(indexID))
      {
        MatchingRule rule = DirectoryServer.getMatchingRule(toLowerCase(ruleName));
        if (rule == null)
        {
          logger.error(ERR_CONFIG_INDEX_TYPE_NEEDS_VALID_MATCHING_RULE, attrType, ruleName);
          continue;
        }
        for (org.forgerock.opendj.ldap.spi.Indexer indexer : rule.getIndexers())
        {
          final String indexId = indexer.getIndexID();
          if (!nameToIndexes.containsKey(indexId))
          {
            // There is no index available for this index id. Create a new index
            nameToIndexes.put(indexId, newAttributeIndex(indexConfig, indexer));
          }
        }
        final Index index = newAttributeIndex(entryContainer, attributeType, indexer, indexEntryLimit);
        indexes.put(indexID, index);
      }
    }
  }
  private void buildIndexes(IndexType indexType) throws ConfigException
  private static Collection<Indexer> getExtensibleIndexers(AttributeType attributeType, Set<String> extensibleRules,
      IndexingOptions options) throws ConfigException
  {
    if (indexConfig.getIndexType().contains(indexType))
    if (extensibleRules == null || extensibleRules.isEmpty())
    {
      final AttributeType attrType = indexConfig.getAttribute();
      final String indexID = indexType.toString();
      final MatchingRule rule = getMatchingRule(indexType, attrType);
      throw new ConfigException(
          ERR_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE.get(attributeType, IndexType.EXTENSIBLE.toString()));
    }
    final Collection<Indexer> indexers = new ArrayList<>();
    for (final String ruleName : extensibleRules)
    {
      final MatchingRule rule = DirectoryServer.getMatchingRule(toLowerCase(ruleName));
      if (rule == null)
      {
        throw new ConfigException(ERR_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE.get(attrType, indexID));
        logger.error(ERR_CONFIG_INDEX_TYPE_NEEDS_VALID_MATCHING_RULE, attributeType, ruleName);
        continue;
      }
      for (org.forgerock.opendj.ldap.spi.Indexer indexer : rule.getIndexers())
      {
        nameToIndexes.put(indexID, newAttributeIndex(indexConfig, indexer));
      }
      indexers.addAll(rule.createIndexers(options));
    }
    return indexers;
  }
  private MatchingRule getMatchingRule(IndexType indexType, AttributeType attrType)
  private static MatchingRule getMatchingRule(IndexType indexType, AttributeType attrType)
  {
    switch (indexType)
    {
@@ -241,15 +262,15 @@
    }
  }
  private Index newAttributeIndex(LocalDBIndexCfg indexConfig, org.forgerock.opendj.ldap.spi.Indexer indexer)
  private static Index newAttributeIndex(EntryContainer entryContainer, AttributeType attributeType,
      org.forgerock.opendj.ldap.spi.Indexer indexer, int indexEntryLimit)
  {
    final AttributeType attrType = indexConfig.getAttribute();
    final String indexName = getIndexName(attrType, indexer.getIndexID());
    final AttributeIndexer attrIndexer = new AttributeIndexer(attrType, indexer);
    return entryContainer.newIndexForAttribute(indexName, attrIndexer, indexConfig.getIndexEntryLimit());
    final String indexName = getIndexName(entryContainer, attributeType, indexer.getIndexID());
    final AttributeIndexer attrIndexer = new AttributeIndexer(attributeType, indexer);
    return entryContainer.newIndexForAttribute(indexName, attrIndexer, indexEntryLimit);
  }
  private String getIndexName(AttributeType attrType, String indexID)
  private static String getIndexName(EntryContainer entryContainer, AttributeType attrType, String indexID)
  {
    return entryContainer.getDatabasePrefix() + "_" + attrType.getNameOrOID() + "." + indexID;
  }
@@ -262,7 +283,7 @@
   */
  public void open() throws DatabaseException
  {
    for (Index index : nameToIndexes.values())
    for (Index index : indexIdToIndexes.values())
    {
      index.open();
    }
@@ -273,7 +294,7 @@
  @Override
  public void close()
  {
    Utils.closeSilently(nameToIndexes.values());
    Utils.closeSilently(indexIdToIndexes.values());
    indexConfig.removeChangeListener(this);
    // The entryContainer is responsible for closing the JE databases.
  }
@@ -315,13 +336,11 @@
   * @throws DatabaseException If an error occurs in the JE database.
   * @throws DirectoryException If a Directory Server error occurs.
   */
  public void addEntry(IndexBuffer buffer, EntryID entryID, Entry entry)
       throws DatabaseException, DirectoryException
  public void addEntry(IndexBuffer buffer, EntryID entryID, Entry entry) throws DatabaseException, DirectoryException
  {
    final IndexingOptions options = indexQueryFactory.getIndexingOptions();
    for (Index index : nameToIndexes.values())
    for (Index index : indexIdToIndexes.values())
    {
      index.addEntry(buffer, entryID, entry, options);
      index.addEntry(buffer, entryID, entry);
    }
  }
@@ -337,10 +356,9 @@
  public void removeEntry(IndexBuffer buffer, EntryID entryID, Entry entry)
       throws DatabaseException, DirectoryException
  {
    final IndexingOptions options = indexQueryFactory.getIndexingOptions();
    for (Index index : nameToIndexes.values())
    for (Index index : indexIdToIndexes.values())
    {
      index.removeEntry(buffer, entryID, entry, options);
      index.removeEntry(buffer, entryID, entry);
    }
  }
@@ -363,10 +381,9 @@
                          List<Modification> mods)
       throws DatabaseException
  {
    final IndexingOptions options = indexQueryFactory.getIndexingOptions();
    for (Index index : nameToIndexes.values())
    for (Index index : indexIdToIndexes.values())
    {
      index.modifyEntry(buffer, entryID, oldEntry, newEntry, mods, options);
      index.modifyEntry(buffer, entryID, oldEntry, newEntry, mods);
    }
  }
@@ -379,7 +396,7 @@
   * @param len The length of the substring.
   * @return A byte string containing a substring key.
   */
  private ByteString makeSubstringKey(byte[] bytes, int pos, int len)
  private static ByteString makeSubstringKey(byte[] bytes, int pos, int len)
  {
    byte[] keyBytes = new byte[len];
    System.arraycopy(bytes, pos, keyBytes, 0, len);
@@ -602,7 +619,7 @@
  {
    long entryLimitExceededCount = 0;
    for (Index index : nameToIndexes.values())
    for (Index index : indexIdToIndexes.values())
    {
      entryLimitExceededCount += index.getEntryLimitExceededCount();
    }
@@ -615,7 +632,7 @@
   */
  public void listDatabases(List<DatabaseContainer> dbList)
  {
    dbList.addAll(nameToIndexes.values());
    dbList.addAll(indexIdToIndexes.values());
  }
  /**
@@ -654,16 +671,14 @@
    return true;
  }
  private boolean isIndexAcceptable(LocalDBIndexCfg cfg, IndexType indexType,
  private static boolean isIndexAcceptable(LocalDBIndexCfg cfg, IndexType indexType,
      List<LocalizableMessage> unacceptableReasons)
  {
    final String indexId = indexType.toString();
    final AttributeType attrType = cfg.getAttribute();
    if (cfg.getIndexType().contains(indexType)
        && nameToIndexes.get(indexId) == null
        && getMatchingRule(indexType, attrType) == null)
    {
      unacceptableReasons.add(ERR_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE.get(attrType, indexId));
      unacceptableReasons.add(ERR_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE.get(attrType, indexType.toString()));
      return false;
    }
    return true;
@@ -671,218 +686,61 @@
  /** {@inheritDoc} */
  @Override
  public synchronized ConfigChangeResult applyConfigurationChange(LocalDBIndexCfg cfg)
  public synchronized ConfigChangeResult applyConfigurationChange(final LocalDBIndexCfg newConfiguration)
  {
    final ConfigChangeResult ccr = new ConfigChangeResult();
    final IndexingOptions newIndexingOptions = new JEIndexingOptions(newConfiguration.getSubstringLength());
    try
    {
      applyChangeToPresenceIndex(cfg, ccr);
      applyChangeToIndex(IndexType.EQUALITY, cfg, ccr);
      applyChangeToIndex(IndexType.SUBSTRING, cfg, ccr);
      applyChangeToIndex(IndexType.ORDERING, cfg, ccr);
      applyChangeToIndex(IndexType.APPROXIMATE, cfg, ccr);
      applyChangeToExtensibleIndexes(cfg, ccr);
      Map<String, Index> newIndexIdToIndexes = buildIndexes(entryContainer, newConfiguration, newIndexingOptions);
      extensibleIndexesMapping = computeExtensibleIndexesMapping();
      indexConfig = cfg;
      final Map<String, Index> removedIndexes = new HashMap<>(indexIdToIndexes);
      removedIndexes.keySet().removeAll(newIndexIdToIndexes.keySet());
      return ccr;
      final Map<String, Index> addedIndexes = new HashMap<>(newIndexIdToIndexes);
      addedIndexes.keySet().removeAll(indexIdToIndexes.keySet());
      final Map<String, Index> updatedIndexes = new HashMap<>(indexIdToIndexes);
      updatedIndexes.keySet().retainAll(newIndexIdToIndexes.keySet());
      // Replace instances of Index created by buildIndexes() with the one already opened and present in the actual
      // indexIdToIndexes
      newIndexIdToIndexes.putAll(updatedIndexes);
      // Open added indexes *before* adding them to indexIdToIndexes
      for (Index addedIndex : addedIndexes.values())
      {
        openIndex(addedIndex, ccr);
      }
      indexConfig = newConfiguration;
      indexingOptions = newIndexingOptions;
      indexIdToIndexes =  Collections.unmodifiableMap(newIndexIdToIndexes);
      indexQueryFactory = new IndexQueryFactoryImpl(indexIdToIndexes, indexingOptions, indexConfig.getAttribute());
      // FIXME: There is no guarantee here that deleted index are not currently involved in a query
      for (Index removedIndex : removedIndexes.values())
      {
        deleteIndex(entryContainer, removedIndex);
      }
      for (Index updatedIndex : updatedIndexes.values())
      {
        updateIndex(updatedIndex, newConfiguration.getIndexEntryLimit(), ccr);
      }
    }
    catch(Exception e)
    catch (Exception e)
    {
      ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
      ccr.addMessage(LocalizableMessage.raw(StaticUtils.stackTraceToSingleLineString(e)));
      return ccr;
    }
    return ccr;
  }
  private void applyChangeToExtensibleIndexes(LocalDBIndexCfg cfg, final ConfigChangeResult ccr)
  {
    final AttributeType attrType = cfg.getAttribute();
    if (!cfg.getIndexType().contains(IndexType.EXTENSIBLE))
    {
      final Set<MatchingRule> validRules = Collections.emptySet();
      final Set<String> validIndexIds = Collections.emptySet();
      removeIndexesForExtensibleMatchingRules(validRules, validIndexIds);
      return;
    }
    final Set<String> extensibleRules = cfg.getIndexExtensibleMatchingRule();
    final Set<MatchingRule> validRules = new HashSet<MatchingRule>();
    final Set<String> validIndexIds = new HashSet<String>();
    final int indexEntryLimit = cfg.getIndexEntryLimit();
    for (String ruleName : extensibleRules)
    {
      MatchingRule rule = DirectoryServer.getMatchingRule(toLowerCase(ruleName));
      if (rule == null)
      {
        logger.error(ERR_CONFIG_INDEX_TYPE_NEEDS_VALID_MATCHING_RULE, attrType, ruleName);
        continue;
      }
      validRules.add(rule);
      for (org.forgerock.opendj.ldap.spi.Indexer indexer : rule.getIndexers())
      {
        String indexId = indexer.getIndexID();
        validIndexIds.add(indexId);
        if (!nameToIndexes.containsKey(indexId))
        {
          Index index = newAttributeIndex(cfg, indexer);
          openIndex(index, ccr);
          nameToIndexes.put(indexId, index);
        }
        else
        {
          Index index = nameToIndexes.get(indexId);
          if (index.setIndexEntryLimit(indexEntryLimit))
          {
            ccr.setAdminActionRequired(true);
            ccr.addMessage(NOTE_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD.get(index.getName()));
          }
          if (indexConfig.getSubstringLength() != cfg.getSubstringLength())
          {
            index.setIndexer(new AttributeIndexer(attrType, indexer));
          }
        }
      }
    }
    removeIndexesForExtensibleMatchingRules(validRules, validIndexIds);
  }
  /** Remove indexes which do not correspond to valid rules. */
  private void removeIndexesForExtensibleMatchingRules(Set<MatchingRule> validRules, Set<String> validIndexIds)
  {
    final Set<MatchingRule> rulesToDelete = getCurrentExtensibleMatchingRules();
    rulesToDelete.removeAll(validRules);
    if (!rulesToDelete.isEmpty())
    {
      entryContainer.exclusiveLock.lock();
      try
      {
        for (MatchingRule rule: rulesToDelete)
        {
          final List<String> indexIdsToRemove = new ArrayList<String>();
          for (org.forgerock.opendj.ldap.spi.Indexer indexer : rule.getIndexers())
          {
            final String indexId = indexer.getIndexID();
            if (!validIndexIds.contains(indexId))
            {
              indexIdsToRemove.add(indexId);
            }
          }
          // Delete indexes which are not used
          for (String indexId : indexIdsToRemove)
          {
            Index index = nameToIndexes.get(indexId);
            if (index != null)
            {
              entryContainer.deleteDatabase(index);
              nameToIndexes.remove(index);
            }
          }
        }
      }
      finally
      {
        entryContainer.exclusiveLock.unlock();
      }
    }
  }
  private Set<MatchingRule> getCurrentExtensibleMatchingRules()
  {
    final Set<MatchingRule> rules = new HashSet<MatchingRule>();
    for (String ruleName : indexConfig.getIndexExtensibleMatchingRule())
    {
        final MatchingRule rule = DirectoryServer.getMatchingRule(toLowerCase(ruleName));
        if (rule != null)
        {
          rules.add(rule);
        }
    }
    return rules;
  }
  private void applyChangeToIndex(IndexType indexType, LocalDBIndexCfg cfg, final ConfigChangeResult ccr)
  {
    String indexId = indexType.toString();
    Index index = nameToIndexes.get(indexId);
    if (!cfg.getIndexType().contains(indexType))
    {
      removeIndex(index, indexType);
      return;
    }
    if (index == null)
    {
      final MatchingRule matchingRule = getMatchingRule(indexType, cfg.getAttribute());
      for (org.forgerock.opendj.ldap.spi.Indexer indexer : matchingRule.getIndexers())
      {
        index = newAttributeIndex(cfg, indexer);
        openIndex(index, ccr);
        nameToIndexes.put(indexId, index);
      }
    }
    else
    {
      // already exists. Just update index entry limit.
      if (index.setIndexEntryLimit(cfg.getIndexEntryLimit()))
      {
        ccr.setAdminActionRequired(true);
        ccr.addMessage(NOTE_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD.get(index.getName()));
      }
    }
  }
  private void applyChangeToPresenceIndex(LocalDBIndexCfg cfg, final ConfigChangeResult ccr)
  {
    final IndexType indexType = IndexType.PRESENCE;
    final String indexID = indexType.toString();
    Index index = nameToIndexes.get(indexID);
    if (!cfg.getIndexType().contains(indexType))
    {
      removeIndex(index, indexType);
      return;
    }
    if (index == null)
    {
      index = newPresenceIndex(cfg);
      openIndex(index, ccr);
      nameToIndexes.put(indexID, index);
    }
    else
    {
      // already exists. Just update index entry limit.
      if (index.setIndexEntryLimit(cfg.getIndexEntryLimit()))
      {
        ccr.setAdminActionRequired(true);
        ccr.addMessage(NOTE_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD.get(index.getName()));
      }
    }
  }
  private void removeIndex(Index index, IndexType indexType)
  {
    if (index != null)
    {
      entryContainer.exclusiveLock.lock();
      try
      {
        nameToIndexes.remove(indexType.toString());
        entryContainer.deleteDatabase(index);
      }
      finally
      {
        entryContainer.exclusiveLock.unlock();
      }
    }
  }
  private void openIndex(Index index, final ConfigChangeResult ccr)
  private static void openIndex(Index index, ConfigChangeResult ccr)
  {
    index.open();
    if (!index.isTrusted())
    {
      ccr.setAdminActionRequired(true);
@@ -890,13 +748,36 @@
    }
  }
  private static void updateIndex(Index updatedIndex, int newIndexEntryLimit, ConfigChangeResult ccr)
  {
    if (updatedIndex.setIndexEntryLimit(newIndexEntryLimit))
    {
      // This index can still be used since index size limit doesn't impact validity of the results.
      ccr.setAdminActionRequired(true);
      ccr.addMessage(NOTE_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD.get(updatedIndex.getName()));
    }
  }
  private static void deleteIndex(EntryContainer entryContainer, Index index)
  {
    entryContainer.exclusiveLock.lock();
    try
    {
      entryContainer.deleteDatabase(index);
    }
    finally
    {
      entryContainer.exclusiveLock.unlock();
    }
  }
  /**
   * Return true iff this index is trusted.
   * @return the trusted state of this index
   */
  public boolean isTrusted()
  {
    for (Index index : nameToIndexes.values())
    for (Index index : indexIdToIndexes.values())
    {
      if (!index.isTrusted())
      {
@@ -918,103 +799,18 @@
        + indexConfig.getAttribute().getNameOrOID();
  }
  /**
   * Return the equality index.
   *
   * @return The equality index.
   */
  public Index getEqualityIndex() {
    return nameToIndexes.get(IndexType.EQUALITY.toString());
  }
  /**
   * Return the approximate index.
   *
   * @return The approximate index.
   */
  public Index getApproximateIndex() {
    return nameToIndexes.get(IndexType.APPROXIMATE.toString());
  }
  /**
   * Return the ordering index.
   *
   * @return  The ordering index.
   */
  public Index getOrderingIndex() {
    return nameToIndexes.get(IndexType.ORDERING.toString());
  }
  /**
   * Return the substring index.
   *
   * @return The substring index.
   */
  public Index getSubstringIndex() {
    return nameToIndexes.get(IndexType.SUBSTRING.toString());
  }
  /**
   * Return the presence index.
   *
   * @return The presence index.
   */
  public Index getPresenceIndex() {
    return nameToIndexes.get(IndexType.PRESENCE.toString());
  }
  /**
   * Return the mapping of extensible index types and indexes.
   *
   * @return The map containing entries (extensible index type, list of indexes)
   */
  public Map<String, Collection<Index>> getExtensibleIndexes()
  {
    return extensibleIndexesMapping;
  }
  private Map<String, Collection<Index>> computeExtensibleIndexesMapping()
  {
    final Collection<Index> substring = new ArrayList<Index>();
    final Collection<Index> shared = new ArrayList<Index>();
    for (Map.Entry<String, Index> entry : nameToIndexes.entrySet())
    {
      final String indexId = entry.getKey();
      if (isDefaultIndex(indexId)) {
        continue;
      }
      if (indexId.endsWith(EXTENSIBLE_INDEXER_ID_SUBSTRING))
      {
        substring.add(entry.getValue());
      }
      else
      {
        shared.add(entry.getValue());
      }
    }
    final Map<String, Collection<Index>> indexMap = new HashMap<String,Collection<Index>>();
    indexMap.put(EXTENSIBLE_INDEXER_ID_SUBSTRING, substring);
    indexMap.put(EXTENSIBLE_INDEXER_ID_SHARED, shared);
    return Collections.unmodifiableMap(indexMap);
  }
  private boolean isDefaultIndex(String indexId)
  {
    return indexId.equals(IndexType.EQUALITY.toString())
        || indexId.equals(IndexType.PRESENCE.toString())
        || indexId.equals(IndexType.SUBSTRING.toString())
        || indexId.equals(IndexType.ORDERING.toString())
        || indexId.equals(IndexType.APPROXIMATE.toString());
  Index getIndex(String indexID) {
    return indexIdToIndexes.get(indexID);
  }
  /**
   * Retrieves all the indexes used by this attribute index.
   *
   * @return A collection of all indexes in use by this attribute
   * @return An immutable collection of all indexes in use by this attribute
   * index.
   */
  public Collection<Index> getAllIndexes() {
    return new LinkedHashSet<Index>(nameToIndexes.values());
    return indexIdToIndexes.values();
  }
  /**
@@ -1065,7 +861,7 @@
      if (debugBuffer != null)
      {
        debugBuffer.append("[INDEX:");
        for (org.forgerock.opendj.ldap.spi.Indexer indexer : rule.getIndexers())
        for (org.forgerock.opendj.ldap.spi.Indexer indexer : rule.createIndexers(indexingOptions))
        {
            debugBuffer.append(" ")
              .append(filter.getAttributeType().getNameOrOID())
@@ -1100,9 +896,9 @@
  private boolean ruleHasAtLeasOneIndex(MatchingRule rule)
  {
    for (org.forgerock.opendj.ldap.spi.Indexer indexer : rule.getIndexers())
    for (org.forgerock.opendj.ldap.spi.Indexer indexer : rule.createIndexers(indexingOptions))
    {
      if (nameToIndexes.containsKey(indexer.getIndexID()))
      if (indexIdToIndexes.containsKey(indexer.getIndexID()))
      {
        return true;
      }
@@ -1111,7 +907,7 @@
  }
  /** This class extends the IndexConfig for JE Backend. */
  private final class JEIndexConfig implements IndexingOptions
  private static final class JEIndexingOptions implements IndexingOptions
  {
    /** The length of the substring index. */
    private int substringLength;
@@ -1120,7 +916,7 @@
     * Creates a new JEIndexConfig instance.
     * @param substringLength The length of the substring.
     */
    private JEIndexConfig(int substringLength)
    private JEIndexingOptions(int substringLength)
    {
      this.substringLength = substringLength;
    }
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/AttributeIndexer.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2009-2010 Sun Microsystems, Inc.
 *      Portions Copyright 2014 ForgeRock AS
 *      Portions Copyright 2014-2015 ForgeRock AS
 */
package org.opends.server.backends.jeb;
@@ -35,7 +35,6 @@
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.DecodeException;
import org.forgerock.opendj.ldap.schema.Schema;
import org.forgerock.opendj.ldap.spi.IndexingOptions;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
import org.opends.server.types.Entry;
@@ -79,26 +78,25 @@
  /** {@inheritDoc} */
  @Override
  public void indexEntry(Entry entry, Set<ByteString> keys, IndexingOptions options)
  public void indexEntry(Entry entry, Set<ByteString> keys)
  {
    List<Attribute> attrList = entry.getAttribute(attributeType);
    final List<Attribute> attrList = entry.getAttribute(attributeType);
    if (attrList != null)
    {
      indexAttribute(attrList, keys, options);
      indexAttribute(attrList, keys);
    }
  }
  /** {@inheritDoc} */
  @Override
  public void modifyEntry(Entry oldEntry, Entry newEntry,
      List<Modification> mods, Map<ByteString, Boolean> modifiedKeys,
      IndexingOptions options)
      List<Modification> mods, Map<ByteString, Boolean> modifiedKeys)
  {
    List<Attribute> newAttributes = newEntry.getAttribute(attributeType, true);
    List<Attribute> oldAttributes = oldEntry.getAttribute(attributeType, true);
    indexAttribute(oldAttributes, modifiedKeys, false, options);
    indexAttribute(newAttributes, modifiedKeys, true, options);
    indexAttribute(oldAttributes, modifiedKeys, false);
    indexAttribute(newAttributes, modifiedKeys, true);
  }
@@ -108,8 +106,7 @@
   * @param attrList The attribute for which substring keys are required.
   * @param keys The set into which the generated keys will be inserted.
   */
  private void indexAttribute(List<Attribute> attrList, Set<ByteString> keys,
      IndexingOptions options)
  private void indexAttribute(List<Attribute> attrList, Set<ByteString> keys)
  {
    if (attrList == null)
    {
@@ -124,7 +121,7 @@
        {
          try
          {
            indexer.createKeys(Schema.getDefaultSchema(), value, options, keys);
            indexer.createKeys(Schema.getDefaultSchema(), value, keys);
          }
          catch (DecodeException e)
          {
@@ -143,17 +140,15 @@
   * @param insert <code>true</code> if generated keys should
   * be inserted or <code>false</code> otherwise.
   */
  private void indexAttribute(List<Attribute> attrList,
      Map<ByteString, Boolean> modifiedKeys, Boolean insert,
      IndexingOptions options)
  private void indexAttribute(List<Attribute> attrList, Map<ByteString, Boolean> modifiedKeys, Boolean insert)
  {
    if (attrList == null)
    {
      return;
    }
    final Set<ByteString> keys = new HashSet<ByteString>();
    indexAttribute(attrList, keys, options);
    final Set<ByteString> keys = new HashSet<>();
    indexAttribute(attrList, keys);
    computeModifiedKeys(modifiedKeys, insert, keys);
  }
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/EqualityIndexer.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2006-2010 Sun Microsystems, Inc.
 *      Portions Copyright 2014 ForgeRock AS
 *      Portions Copyright 2014-2015 ForgeRock AS
 */
package org.opends.server.backends.jeb;
@@ -34,7 +34,6 @@
import org.forgerock.opendj.ldap.schema.MatchingRule;
import org.forgerock.opendj.ldap.schema.Schema;
import org.forgerock.opendj.ldap.spi.Indexer;
import org.forgerock.opendj.ldap.spi.IndexingOptions;
import org.opends.server.types.AttributeType;
/**
@@ -68,9 +67,7 @@
  /** {@inheritDoc} */
  @Override
  public void createKeys(Schema schema, ByteSequence value,
      IndexingOptions options, Collection<ByteString> keys)
      throws DecodeException
  public void createKeys(Schema schema, ByteSequence value, Collection<ByteString> keys) throws DecodeException
  {
    keys.add(equalityRule.normalizeAttributeValue(value));
  }
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/ID2CIndexer.java
@@ -22,14 +22,13 @@
 *
 *
 *      Copyright 2006-2008 Sun Microsystems, Inc.
 *      Portions Copyright 2014 ForgeRock AS
 *      Portions Copyright 2014-2015 ForgeRock AS
 */
package org.opends.server.backends.jeb;
import java.util.*;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.spi.IndexingOptions;
import org.opends.server.types.Entry;
import org.opends.server.types.Modification;
@@ -57,7 +56,7 @@
  /** {@inheritDoc} */
  @Override
  public void indexEntry(Entry entry, Set<ByteString> addKeys, IndexingOptions options)
  public void indexEntry(Entry entry, Set<ByteString> addKeys)
  {
    // The superior entry IDs are in the entry attachment.
    ArrayList<EntryID> ids = (ArrayList<EntryID>) entry.getAttachment();
@@ -78,7 +77,7 @@
  @Override
  public void modifyEntry(Entry oldEntry, Entry newEntry,
                          List<Modification> mods,
                          Map<ByteString, Boolean> modifiedKeys, IndexingOptions options)
                          Map<ByteString, Boolean> modifiedKeys)
  {
    // Nothing to do.
  }
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/ID2SIndexer.java
@@ -22,14 +22,13 @@
 *
 *
 *      Copyright 2006-2008 Sun Microsystems, Inc.
 *      Portions Copyright 2014 ForgeRock AS
 *      Portions Copyright 2014-2015 ForgeRock AS
 */
package org.opends.server.backends.jeb;
import java.util.*;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.spi.IndexingOptions;
import org.opends.server.types.Entry;
import org.opends.server.types.Modification;
@@ -56,7 +55,7 @@
  /** {@inheritDoc} */
  @Override
  public void indexEntry(Entry entry, Set<ByteString> addKeys, IndexingOptions options)
  public void indexEntry(Entry entry, Set<ByteString> addKeys)
  {
    // The superior entry IDs are in the entry attachment.
    ArrayList<EntryID> ids = (ArrayList<EntryID>) entry.getAttachment();
@@ -77,7 +76,7 @@
  @Override
  public void modifyEntry(Entry oldEntry, Entry newEntry,
                          List<Modification> mods,
                          Map<ByteString, Boolean> modifiedKeys, IndexingOptions options)
                          Map<ByteString, Boolean> modifiedKeys)
  {
    // Nothing to do.
  }
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/Importer.java
@@ -89,7 +89,6 @@
import org.forgerock.opendj.config.server.ConfigException;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.spi.IndexingOptions;
import org.forgerock.util.Utils;
import org.opends.server.admin.std.meta.LocalDBIndexCfgDefn.IndexType;
import org.opends.server.admin.std.server.LocalDBBackendCfg;
@@ -689,26 +688,7 @@
  {
    for (AttributeIndex attributeIndex : suffix.getAttrIndexMap().values())
    {
      putInIdContainerMap(attributeIndex.getEqualityIndex());
      putInIdContainerMap(attributeIndex.getPresenceIndex());
      putInIdContainerMap(attributeIndex.getSubstringIndex());
      putInIdContainerMap(attributeIndex.getOrderingIndex());
      putInIdContainerMap(attributeIndex.getApproximateIndex());
      Map<String, Collection<Index>> extensibleMap = attributeIndex.getExtensibleIndexes();
      if (!extensibleMap.isEmpty())
      {
        putInIdContainerMap(extensibleMap.get(EXTENSIBLE_INDEXER_ID_SUBSTRING));
        putInIdContainerMap(extensibleMap.get(EXTENSIBLE_INDEXER_ID_SHARED));
      }
    }
  }
  private void putInIdContainerMap(Collection<Index> indexes)
  {
    if (indexes != null)
    {
      for (Index index : indexes)
      {
      for(Index index : attributeIndex.getAllIndexes()) {
        putInIdContainerMap(index);
      }
    }
@@ -1492,20 +1472,20 @@
    }
    @Override
    void processAttribute(Index index, Entry entry, EntryID entryID, IndexingOptions options,
        IndexKey indexKey) throws DatabaseException, InterruptedException
    void processAttribute(Index index, Entry entry, EntryID entryID, IndexKey indexKey)
        throws DatabaseException, InterruptedException
    {
      if (oldEntry != null)
      {
        deleteKeySet.clear();
        index.indexEntry(oldEntry, deleteKeySet, options);
        index.indexEntry(oldEntry, deleteKeySet);
        for (ByteString delKey : deleteKeySet)
        {
          processKey(index, delKey.toByteArray(), entryID, indexKey, false);
        }
      }
      insertKeySet.clear();
      index.indexEntry(entry, insertKeySet, options);
      index.indexEntry(entry, insertKeySet);
      for (ByteString key : insertKeySet)
      {
        processKey(index, key.toByteArray(), entryID, indexKey, true);
@@ -1522,7 +1502,7 @@
    private final Map<IndexKey, IndexOutputBuffer> indexBufferMap = new HashMap<IndexKey, IndexOutputBuffer>();
    private final Set<ByteString> insertKeySet = new HashSet<ByteString>();
    private final EntryInformation entryInfo = new EntryInformation();
    private final IndexKey dnIndexKey = new IndexKey(dnType, ImportIndexType.DN, 1);
    private final IndexKey dnIndexKey = new IndexKey(dnType, ImportIndexType.DN.toString(), 1);
    private DatabaseEntry keyEntry = new DatabaseEntry();
    private DatabaseEntry valEntry = new DatabaseEntry();
@@ -1623,57 +1603,45 @@
    void fillIndexKey(Suffix suffix, AttributeIndex attrIndex, Entry entry, AttributeType attrType, EntryID entryID)
        throws DatabaseException, InterruptedException, DirectoryException, JebException
    {
      final IndexingOptions options = attrIndex.getIndexingOptions();
      processAttribute(attrIndex.getEqualityIndex(), ImportIndexType.EQUALITY, entry, attrType, entryID, options);
      processAttribute(attrIndex.getPresenceIndex(), ImportIndexType.PRESENCE, entry, attrType, entryID, options);
      processAttribute(attrIndex.getSubstringIndex(), ImportIndexType.SUBSTRING, entry, attrType, entryID, options);
      processAttribute(attrIndex.getOrderingIndex(), ImportIndexType.ORDERING, entry, attrType, entryID, options);
      processAttribute(attrIndex.getApproximateIndex(), ImportIndexType.APPROXIMATE, entry, attrType, entryID, options);
      for(Index index : attrIndex.getAllIndexes()) {
        processAttribute(index,  entry, attrType, entryID);
      }
      for (VLVIndex vlvIdx : suffix.getEntryContainer().getVLVIndexes())
      {
        Transaction transaction = null;
        vlvIdx.addEntry(transaction, entryID, entry);
      }
      Map<String, Collection<Index>> extensibleMap = attrIndex.getExtensibleIndexes();
      if (!extensibleMap.isEmpty())
      {
        Collection<Index> subIndexes = extensibleMap.get(EXTENSIBLE_INDEXER_ID_SUBSTRING);
        processAttributes(subIndexes, ImportIndexType.EX_SUBSTRING, entry, attrType, entryID, options);
        Collection<Index> sharedIndexes = extensibleMap.get(EXTENSIBLE_INDEXER_ID_SHARED);
        processAttributes(sharedIndexes, ImportIndexType.EX_SHARED, entry, attrType, entryID, options);
      }
    }
    private void processAttribute(Index index, ImportIndexType presence, Entry entry,
        AttributeType attributeType, EntryID entryID, IndexingOptions options) throws InterruptedException
    private void processAttribute(Index index, Entry entry, AttributeType attributeType, EntryID entryID)
        throws InterruptedException
    {
      if (index != null)
      {
        IndexKey indexKey = new IndexKey(attributeType, presence, index.getIndexEntryLimit());
        processAttribute(index, entry, entryID, options, indexKey);
        processAttribute(index, entry, entryID,
            new IndexKey(attributeType, index.getName(), index.getIndexEntryLimit()));
      }
    }
    private void processAttributes(Collection<Index> indexes, ImportIndexType indexType, Entry entry,
        AttributeType attributeType, EntryID entryID, IndexingOptions options) throws InterruptedException
    private void processAttributes(Collection<Index> indexes, Entry entry, AttributeType attributeType, EntryID entryID)
        throws InterruptedException
    {
      if (indexes != null)
      {
        for (Index index : indexes)
        {
          IndexKey indexKey = new IndexKey(attributeType, indexType, index.getIndexEntryLimit());
          processAttribute(index, entry, entryID, options, indexKey);
          processAttribute(index, entry, entryID,
              new IndexKey(attributeType, index.getName(), index.getIndexEntryLimit()));
        }
      }
    }
    void processAttribute(Index index, Entry entry, EntryID entryID, IndexingOptions options,
        IndexKey indexKey) throws DatabaseException, InterruptedException
    void processAttribute(Index index, Entry entry, EntryID entryID, IndexKey indexKey)
        throws DatabaseException, InterruptedException
    {
      insertKeySet.clear();
      index.indexEntry(entry, insertKeySet, options);
      index.indexEntry(entry, insertKeySet);
      for (ByteString key : insertKeySet)
      {
        processKey(index, key.toByteArray(), entryID, indexKey, true);
@@ -2754,7 +2722,7 @@
        {
          return;
        }
        boolean isDN2ID = ImportIndexType.DN.equals(indexKey.getIndexType());
        boolean isDN2ID = ImportIndexType.DN.toString().equals(indexKey.getIndexName());
        IndexManager indexMgr = new IndexManager(indexKey.getName(), isDN2ID, indexKey.getEntryLimit());
        if (isDN2ID)
        {
@@ -3178,60 +3146,19 @@
        final AttributeType attrType, final boolean onlyDegraded)
        throws DatabaseException
    {
      fillIndexMap(attrType, attrIndex.getSubstringIndex(), ImportIndexType.SUBSTRING, onlyDegraded);
      fillIndexMap(attrType, attrIndex.getOrderingIndex(), ImportIndexType.ORDERING, onlyDegraded);
      fillIndexMap(attrType, attrIndex.getEqualityIndex(), ImportIndexType.EQUALITY, onlyDegraded);
      fillIndexMap(attrType, attrIndex.getPresenceIndex(), ImportIndexType.PRESENCE, onlyDegraded);
      fillIndexMap(attrType, attrIndex.getApproximateIndex(), ImportIndexType.APPROXIMATE, onlyDegraded);
      final Map<String, Collection<Index>> extensibleMap = attrIndex.getExtensibleIndexes();
      if (!extensibleMap.isEmpty())
      {
        final Collection<Index> subIndexes = extensibleMap.get(EXTENSIBLE_INDEXER_ID_SUBSTRING);
        fillIndexMap(attrType, subIndexes, ImportIndexType.EX_SUBSTRING, onlyDegraded);
        final Collection<Index> sharedIndexes = extensibleMap.get(EXTENSIBLE_INDEXER_ID_SHARED);
        fillIndexMap(attrType, sharedIndexes, ImportIndexType.EX_SHARED, onlyDegraded);
      for(Index index : attrIndex.getAllIndexes()) {
        fillIndexMap(attrType, index, onlyDegraded);
      }
    }
    private void fillIndexMap(final AttributeType attrType, final Collection<Index> indexes,
        final ImportIndexType importIndexType, final boolean onlyDegraded)
    {
      if (indexes != null && !indexes.isEmpty())
      {
        final List<Index> mutableCopy = new LinkedList<Index>(indexes);
        for (final Iterator<Index> it = mutableCopy.iterator(); it.hasNext();)
        {
          final Index sharedIndex = it.next();
          if (!onlyDegraded || !sharedIndex.isTrusted())
          {
            if (!rebuildConfig.isClearDegradedState() || sharedIndex.getRecordCount() == 0)
            {
              putInIdContainerMap(sharedIndex);
            }
          }
          else
          {
            // This index is not a candidate for rebuilding.
            it.remove();
          }
        }
        if (!mutableCopy.isEmpty())
        {
          extensibleIndexMap.put(new IndexKey(attrType, importIndexType, 0), mutableCopy);
        }
      }
    }
    private void fillIndexMap(final AttributeType attrType, final Index index,
        final ImportIndexType importIndexType, final boolean onlyDegraded)
    private void fillIndexMap(final AttributeType attrType, final Index index, final boolean onlyDegraded)
    {
      if (index != null
          && (!onlyDegraded || !index.isTrusted())
          && (!rebuildConfig.isClearDegradedState() || index.getRecordCount() == 0))
      {
        putInIdContainerMap(index);
        final IndexKey key = new IndexKey(attrType, importIndexType, index.getIndexEntryLimit());
        final IndexKey key = new IndexKey(attrType, index.getName(), index.getIndexEntryLimit());
        indexMap.put(key, index);
      }
    }
@@ -3588,11 +3515,9 @@
        AttributeType attrType = key.getAttributeType();
        if (entry.hasAttribute(attrType))
        {
          AttributeIndex attributeIndex = entryContainer.getAttributeIndex(attrType);
          IndexingOptions options = attributeIndex.getIndexingOptions();
          for (Index index : mapEntry.getValue())
          {
            processAttribute(index, entry, entryID, options, key);
            processAttribute(index, entry, entryID, key);
          }
        }
      }
@@ -3607,10 +3532,7 @@
        AttributeType attrType = key.getAttributeType();
        if (entry.hasAttribute(attrType))
        {
          AttributeIndex attributeIndex = entryContainer.getAttributeIndex(attrType);
          IndexingOptions options = attributeIndex.getIndexingOptions();
          Index index = mapEntry.getValue();
          processAttribute(index, entry, entryID, options, key);
          processAttribute(mapEntry.getValue(), entry, entryID, key);
        }
      }
    }
@@ -4045,7 +3967,7 @@
  {
    private final AttributeType attributeType;
    private final ImportIndexType indexType;
    private final String indexName;
    private final int entryLimit;
    /**
@@ -4059,10 +3981,10 @@
     * @param entryLimit
     *          The entry limit for the index.
     */
    private IndexKey(AttributeType attributeType, ImportIndexType indexType, int entryLimit)
    private IndexKey(AttributeType attributeType, String indexName, int entryLimit)
    {
      this.attributeType = attributeType;
      this.indexType = indexType;
      this.indexName = indexName;
      this.entryLimit = entryLimit;
    }
@@ -4082,7 +4004,7 @@
      {
        IndexKey oKey = (IndexKey) obj;
        if (attributeType.equals(oKey.getAttributeType())
            && indexType.equals(oKey.getIndexType()))
            && indexName.equals(oKey.indexName))
        {
          return true;
        }
@@ -4100,7 +4022,7 @@
    @Override
    public int hashCode()
    {
      return attributeType.hashCode() + indexType.hashCode();
      return attributeType.hashCode() + indexName.hashCode();
    }
    /**
@@ -4118,9 +4040,9 @@
     *
     * @return The index type.
     */
    public ImportIndexType getIndexType()
    public String getIndexName()
    {
      return indexType;
      return indexName;
    }
    /**
@@ -4133,7 +4055,7 @@
    public String getName()
    {
      return attributeType.getPrimaryName() + "."
          + StaticUtils.toLowerCase(indexType.name());
          + StaticUtils.toLowerCase(indexName);
    }
    /**
@@ -4151,7 +4073,7 @@
    public String toString()
    {
      return getClass().getSimpleName()
          + "(index=" + attributeType.getNameOrOID() + "." + indexType
          + "(index=" + attributeType.getNameOrOID() + "." + indexName
          + ", entryLimit=" + entryLimit
          + ")";
    }
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/Index.java
@@ -35,7 +35,6 @@
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.ConditionResult;
import org.forgerock.opendj.ldap.spi.IndexingOptions;
import org.opends.server.backends.jeb.IndexBuffer.BufferedIndexValues;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.Entry;
@@ -140,9 +139,9 @@
    }
  }
  void indexEntry(Entry entry, Set<ByteString> keys, IndexingOptions options)
  void indexEntry(Entry entry, Set<ByteString> keys)
  {
    indexer.indexEntry(entry, keys, options);
    indexer.indexEntry(entry, keys);
  }
  /**
@@ -684,15 +683,13 @@
   * @param buffer The index buffer to use to store the deleted keys
   * @param entryID     The entry ID.
   * @param entry       The entry to be indexed.
   * @param options     The indexing options to use
   * @throws DatabaseException If an error occurs in the JE database.
   * @throws DirectoryException If a Directory Server error occurs.
   */
  public void addEntry(IndexBuffer buffer, EntryID entryID, Entry entry,
      IndexingOptions options) throws DatabaseException, DirectoryException
  public void addEntry(IndexBuffer buffer, EntryID entryID, Entry entry) throws DatabaseException, DirectoryException
  {
    HashSet<ByteString> addKeys = new HashSet<ByteString>();
    indexer.indexEntry(entry, addKeys, options);
    final Set<ByteString> addKeys = new HashSet<>();
    indexer.indexEntry(entry, addKeys);
    for (ByteString keyBytes : addKeys)
    {
@@ -706,15 +703,14 @@
   * @param buffer The index buffer to use to store the deleted keys
   * @param entryID     The entry ID
   * @param entry       The contents of the deleted entry.
   * @param options     The indexing options to use
   * @throws DatabaseException If an error occurs in the JE database.
   * @throws DirectoryException If a Directory Server error occurs.
   */
  public void removeEntry(IndexBuffer buffer, EntryID entryID, Entry entry,
      IndexingOptions options) throws DatabaseException, DirectoryException
  public void removeEntry(IndexBuffer buffer, EntryID entryID, Entry entry)
      throws DatabaseException, DirectoryException
  {
    HashSet<ByteString> delKeys = new HashSet<ByteString>();
    indexer.indexEntry(entry, delKeys, options);
    final Set<ByteString> delKeys = new HashSet<>();
    indexer.indexEntry(entry, delKeys);
    for (ByteString keyBytes : delKeys)
    {
@@ -731,19 +727,17 @@
   * @param oldEntry The entry before the modifications were applied.
   * @param newEntry The entry after the modifications were applied.
   * @param mods The sequence of modifications in the Modify operation.
   * @param options The indexing options to use
   * @throws DatabaseException If an error occurs in the JE database.
   */
  public void modifyEntry(IndexBuffer buffer,
                          EntryID entryID,
                          Entry oldEntry,
                          Entry newEntry,
                          List<Modification> mods, IndexingOptions options)
                          List<Modification> mods)
      throws DatabaseException
  {
    TreeMap<ByteString, Boolean> modifiedKeys =
        new TreeMap<ByteString, Boolean>(indexer.getBSComparator());
    indexer.modifyEntry(oldEntry, newEntry, mods, modifiedKeys, options);
    final Map<ByteString, Boolean> modifiedKeys = new TreeMap<ByteString, Boolean>(indexer.getBSComparator());
    indexer.modifyEntry(oldEntry, newEntry, mods, modifiedKeys);
    for (Map.Entry<ByteString, Boolean> modifiedKey : modifiedKeys.entrySet())
    {
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/Indexer.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2006-2008 Sun Microsystems, Inc.
 *      Portions copyright 2012-2014 ForgeRock AS.
 *      Portions copyright 2012-2015 ForgeRock AS.
 */
package org.opends.server.backends.jeb;
@@ -33,7 +33,6 @@
import org.forgerock.opendj.ldap.ByteSequence;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.spi.IndexingOptions;
import org.opends.server.backends.jeb.AttributeIndex.KeyComparator;
import org.opends.server.types.Entry;
import org.opends.server.types.Modification;
@@ -71,9 +70,8 @@
   *
   * @param entry The entry.
   * @param keys The set into which the generated keys will be inserted.
   * @param options The indexing options to use
   */
  public abstract void indexEntry(Entry entry, Set<ByteString> keys, IndexingOptions options);
  public abstract void indexEntry(Entry entry, Set<ByteString> keys);
  /**
   * Generate the set of index keys to be added and the set of index keys
@@ -83,11 +81,9 @@
   * @param newEntry The new entry contents.
   * @param mods The set of modifications that were applied to the entry.
   * @param modifiedKeys The map into which the modified keys will be inserted.
   * @param options The indexing options to use
   */
  public abstract void modifyEntry(Entry oldEntry, Entry newEntry,
      List<Modification> mods, Map<ByteString, Boolean> modifiedKeys,
      IndexingOptions options);
      List<Modification> mods, Map<ByteString, Boolean> modifiedKeys);
  /**
   * Get a string representation of this object.  The returned value is
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/NullIndex.java
@@ -28,7 +28,6 @@
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.ConditionResult;
import org.forgerock.opendj.ldap.spi.IndexingOptions;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.Entry;
import org.opends.server.types.Modification;
@@ -131,7 +130,7 @@
  /** {@inheritDoc} */
  @Override
  public void addEntry(IndexBuffer buffer, EntryID entryID, Entry entry, IndexingOptions options)
  public void addEntry(IndexBuffer buffer, EntryID entryID, Entry entry)
      throws DatabaseException, DirectoryException
  {
    // Do nothing.
@@ -139,7 +138,7 @@
  /** {@inheritDoc} */
  @Override
  public void removeEntry(IndexBuffer buffer, EntryID entryID, Entry entry, IndexingOptions options)
  public void removeEntry(IndexBuffer buffer, EntryID entryID, Entry entry)
      throws DatabaseException, DirectoryException
  {
    // Do nothing.
@@ -147,8 +146,8 @@
  /** {@inheritDoc} */
  @Override
  public void modifyEntry(IndexBuffer buffer, EntryID entryID, Entry oldEntry, Entry newEntry, List<Modification> mods,
      IndexingOptions options) throws DatabaseException
  public void modifyEntry(IndexBuffer buffer, EntryID entryID, Entry oldEntry, Entry newEntry, List<Modification> mods)
      throws DatabaseException
  {
    // Do nothing.
  }
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/PresenceIndexer.java
@@ -31,7 +31,6 @@
import java.util.Set;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.spi.IndexingOptions;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
import org.opends.server.types.Entry;
@@ -70,7 +69,7 @@
  /** {@inheritDoc} */
  @Override
  public void indexEntry(Entry entry, Set<ByteString> keys, IndexingOptions options)
  public void indexEntry(Entry entry, Set<ByteString> keys)
  {
    List<Attribute> attrList = entry.getAttribute(attributeType);
    if (attrList != null && !attrList.isEmpty())
@@ -83,7 +82,7 @@
  @Override
  public void modifyEntry(Entry oldEntry, Entry newEntry,
                          List<Modification> mods,
                          Map<ByteString, Boolean> modifiedKeys, IndexingOptions options)
                          Map<ByteString, Boolean> modifiedKeys)
  {
    List<Attribute> newAttributes = newEntry.getAttribute(attributeType, true);
    List<Attribute> oldAttributes = oldEntry.getAttribute(attributeType, true);
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/Suffix.java
@@ -266,19 +266,13 @@
    entryContainer.getID2Subtree().setTrusted(null, trusted);
    for (AttributeIndex attributeIndex : entryContainer.getAttributeIndexes())
    {
      setTrusted(attributeIndex.getEqualityIndex(), trusted);
      setTrusted(attributeIndex.getPresenceIndex(), trusted);
      setTrusted(attributeIndex.getSubstringIndex(), trusted);
      setTrusted(attributeIndex.getOrderingIndex(), trusted);
      setTrusted(attributeIndex.getApproximateIndex(), trusted);
      Map<String, Collection<Index>> exIndexes = attributeIndex.getExtensibleIndexes();
      if(!exIndexes.isEmpty())
      for (Index index : attributeIndex.getAllIndexes())
      {
        setTrusted(exIndexes.get(EXTENSIBLE_INDEXER_ID_SUBSTRING), trusted);
        setTrusted(exIndexes.get(EXTENSIBLE_INDEXER_ID_SHARED), trusted);
        setTrusted(index, trusted);
      }
    }
    for(VLVIndex vlvIdx : entryContainer.getVLVIndexes()) {
    for (VLVIndex vlvIdx : entryContainer.getVLVIndexes())
    {
      vlvIdx.setTrusted(null, trusted);
    }
  }
@@ -291,17 +285,6 @@
    }
  }
  private void setTrusted(Collection<Index> subIndexes, boolean trusted)
  {
    if (subIndexes != null)
    {
      for (Index subIndex : subIndexes)
      {
        subIndex.setTrusted(null, trusted);
      }
    }
  }
  /**
   * Return a src entry container.
   *
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/VerifyJob.java
@@ -38,7 +38,6 @@
import org.forgerock.opendj.ldap.DecodeException;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.schema.MatchingRule;
import org.forgerock.opendj.ldap.spi.IndexingOptions;
import org.opends.server.backends.VerifyConfig;
import org.opends.server.core.DirectoryServer;
import org.opends.server.types.*;
@@ -436,14 +435,10 @@
    }
    else if (attrIndexList.size() > 0)
    {
      AttributeIndex attrIndex = attrIndexList.get(0);
      final IndexingOptions options = attrIndex.getIndexingOptions();
      iterateAttrIndex(attrIndex.getEqualityIndex(), options);
      iterateAttrIndex(attrIndex.getPresenceIndex(), options);
      iterateAttrIndex(attrIndex.getSubstringIndex(), options);
      iterateAttrIndex(attrIndex.getOrderingIndex(), options);
      iterateAttrIndex(attrIndex.getApproximateIndex(), options);
     // TODO: Need to iterate through ExtendedMatchingRules indexes.
      for (Index index : attrIndexList.get(0).getAllIndexes())
      {
        iterateAttrIndex(index);
      }
    }
    else if (vlvIndexList.size() > 0)
    {
@@ -962,8 +957,7 @@
   * @throws JebException If an error occurs in the JE backend.
   * @throws DatabaseException If an error occurs in the JE database.
   */
  private void iterateAttrIndex(Index index, IndexingOptions options)
      throws JebException, DatabaseException
  private void iterateAttrIndex(Index index) throws JebException, DatabaseException
  {
    if (index == null)
    {
@@ -1078,7 +1072,7 @@
            };
            index.indexEntry(entry, dummySet, options);
            index.indexEntry(entry, dummySet);
            if (!foundMatchingKey.get())
            {
@@ -1386,12 +1380,7 @@
    {
      try
      {
        List<Attribute> attrList =
             entry.getAttribute(attrIndex.getAttributeType());
        if (attrList != null)
        {
          verifyAttribute(attrIndex, entryID, attrList);
        }
        verifyAttribute(attrIndex, entryID, entry);
      }
      catch (DirectoryException e)
      {
@@ -1462,86 +1451,37 @@
   * @param attrList The attribute to be checked.
   * @throws DirectoryException If a Directory Server error occurs.
   */
  private void verifyAttribute(AttributeIndex attrIndex, EntryID entryID,
                              List<Attribute> attrList)
       throws DirectoryException
  private void verifyAttribute(AttributeIndex attrIndex, EntryID entryID, Entry entry) throws DirectoryException
  {
    if (attrList == null || attrList.isEmpty())
    for (Index index : attrIndex.getAllIndexes())
    {
      return;
    }
    Transaction txn = null;
    Index equalityIndex = attrIndex.getEqualityIndex();
    Index presenceIndex = attrIndex.getPresenceIndex();
    Index substringIndex = attrIndex.getSubstringIndex();
    Index orderingIndex = attrIndex.getOrderingIndex();
    Index approximateIndex = attrIndex.getApproximateIndex();
    // TODO: Add support for Extended Matching Rules indexes.
    if (presenceIndex != null)
    {
      verifyAttributeInIndex(presenceIndex, txn, JEBUtils.presenceKey, entryID);
    }
    final DatabaseEntry key = new DatabaseEntry();
    for (Attribute attr : attrList)
    {
      final AttributeType attrType = attr.getAttributeType();
      MatchingRule equalityRule = attrType.getEqualityMatchingRule();
      for (ByteString value : attr)
      final Set<ByteString> keys = new HashSet<>();
      index.indexEntry(entry, keys);
      for (ByteString key : keys)
      {
        byte[] normalizedBytes = normalize(equalityRule, value);
        if (equalityIndex != null)
        {
          key.setData(normalizedBytes);
          verifyAttributeInIndex(equalityIndex, txn, key, entryID);
        }
        if (substringIndex != null)
        {
          for (ByteString keyBytes : attrIndex.substringKeys(normalizedBytes))
          {
            key.setData(keyBytes.toByteArray());
            verifyAttributeInIndex(substringIndex, txn, key, entryID);
          }
        }
        if (orderingIndex != null)
        {
          key.setData(normalize(attrType.getOrderingMatchingRule(), value));
          verifyAttributeInIndex(orderingIndex, txn, key, entryID);
        }
        if (approximateIndex != null)
        {
          key.setData(normalize(attrType.getApproximateMatchingRule(), value));
          verifyAttributeInIndex(approximateIndex, txn, key, entryID);
        }
        verifyAttributeInIndex(index, null, key, entryID);
      }
    }
  }
  private void verifyAttributeInIndex(Index index, Transaction txn,
      DatabaseEntry key, EntryID entryID)
  private void verifyAttributeInIndex(Index index, Transaction txn, ByteString key, EntryID entryID)
  {
    try
    {
      ConditionResult cr = index.containsID(txn, key, entryID);
      final ConditionResult cr = index.containsID(txn, new DatabaseEntry(key.toByteArray()), entryID);
      if (cr == ConditionResult.FALSE)
      {
        if (logger.isTraceEnabled())
        {
          logger.trace("Missing ID %d%n%s",
                     entryID.longValue(),
                     keyDump(index, key.getData()));
                     keyDump(index, key.toByteArray()));
        }
        errorCount++;
      }
      else if (cr == ConditionResult.UNDEFINED)
      {
        incrEntryLimitStats(index, key.getData());
        incrEntryLimitStats(index, key.toByteArray());
      }
    }
    catch (DatabaseException e)
@@ -1552,7 +1492,7 @@
        logger.trace("Error reading database: %s%n%s",
                   e.getMessage(),
                   keyDump(index, key.getData()));
                   keyDump(index, key.toByteArray()));
      }
      errorCount++;
    }
@@ -1640,12 +1580,10 @@
        {
          AttributeIndex attrIndex = attrIndexList.get(0);
          totalCount = 0;
          totalCount += getRecordCount(attrIndex.getEqualityIndex());
          totalCount += getRecordCount(attrIndex.getPresenceIndex());
          totalCount += getRecordCount(attrIndex.getSubstringIndex());
          totalCount += getRecordCount(attrIndex.getOrderingIndex());
          totalCount += getRecordCount(attrIndex.getApproximateIndex());
          // TODO: Add support for Extended Matching Rules indexes.
          for (Index index : attrIndex.getAllIndexes())
          {
            totalCount += getRecordCount(index);
          }
        }
        else if (vlvIndexList.size() > 0)
        {
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/AbstractTree.java
@@ -55,7 +55,7 @@
  /**
   * Override in order to perform any additional initialization after the index has opened.
   */
  void open0(ReadableTransaction txn) throws StorageRuntimeException
  void open0(WriteableTransaction txn) throws StorageRuntimeException
  {
    // Do nothing by default.
  }
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/AttributeIndex.java
@@ -111,36 +111,35 @@
  /**
   * This class implements an attribute indexer for matching rules in a Backend.
   */
  final class MatchingRuleIndex extends DefaultIndex
  static final class MatchingRuleIndex extends DefaultIndex
  {
    /**
     * The matching rule's indexer.
     */
    private final AttributeType attributeType;
    private final Indexer indexer;
    private MatchingRuleIndex(WriteableTransaction txn, BackendIndexCfg cfg, Indexer indexer)
    private MatchingRuleIndex(EntryContainer entryContainer, AttributeType attributeType, State state, Indexer indexer,
        int indexEntryLimit)
    {
      super(getIndexName(attributeType, indexer.getIndexID()), state, cfg.getIndexEntryLimit(), txn, entryContainer);
      super(getIndexName(entryContainer, attributeType, indexer.getIndexID()), state, indexEntryLimit, entryContainer);
      this.attributeType = attributeType;
      this.indexer = indexer;
    }
    void indexEntry(Entry entry, Set<ByteString> keys, IndexingOptions options)
    void indexEntry(Entry entry, Set<ByteString> keys)
    {
      List<Attribute> attributes = entry.getAttribute(attributeType, true);
      if (attributes != null)
      {
        indexAttribute(attributes, keys, options);
        indexAttribute(attributes, keys);
      }
    }
    private void modifyEntry(Entry oldEntry, Entry newEntry, Map<ByteString, Boolean> modifiedKeys,
        IndexingOptions options)
    private void modifyEntry(Entry oldEntry, Entry newEntry, Map<ByteString, Boolean> modifiedKeys)
    {
      List<Attribute> oldAttributes = oldEntry.getAttribute(attributeType, true);
      if (oldAttributes != null)
      {
        final Set<ByteString> keys = new HashSet<ByteString>();
        indexAttribute(oldAttributes, keys, options);
        indexAttribute(oldAttributes, keys);
        for (ByteString key : keys)
        {
          modifiedKeys.put(key, false);
@@ -151,7 +150,7 @@
      if (newAttributes != null)
      {
        final Set<ByteString> keys = new HashSet<ByteString>();
        indexAttribute(newAttributes, keys, options);
        indexAttribute(newAttributes, keys);
        for (ByteString key : keys)
        {
          final Boolean needsAdding = modifiedKeys.get(key);
@@ -169,7 +168,7 @@
      }
    }
    private void indexAttribute(List<Attribute> attributes, Set<ByteString> keys, IndexingOptions options)
    private void indexAttribute(List<Attribute> attributes, Set<ByteString> keys)
    {
      for (Attribute attr : attributes)
      {
@@ -179,7 +178,7 @@
          {
            try
            {
              indexer.createKeys(Schema.getDefaultSchema(), value, options, keys);
              indexer.createKeys(Schema.getDefaultSchema(), value, keys);
              /*
               * Optimization for presence: return immediately after first value since all values
@@ -209,8 +208,7 @@
  private static final Indexer PRESENCE_INDEXER = new Indexer()
  {
    @Override
    public void createKeys(Schema schema, ByteSequence value, IndexingOptions options, Collection<ByteString> keys)
        throws DecodeException
    public void createKeys(Schema schema, ByteSequence value, Collection<ByteString> keys) throws DecodeException
    {
      keys.add(PRESENCE_KEY);
    }
@@ -235,118 +233,113 @@
  private BackendIndexCfg config;
  /** The mapping from names to indexes. */
  private final Map<String, MatchingRuleIndex> nameToIndexes = new HashMap<String, MatchingRuleIndex>();
  private final IndexingOptions indexingOptions;
  private Map<String, MatchingRuleIndex> indexIdToIndexes;
  private IndexingOptions indexingOptions;
  private final State state;
  /** The attribute type for which this instance will generate index keys. */
  private final AttributeType attributeType;
  AttributeIndex(BackendIndexCfg config, State state, EntryContainer entryContainer, WriteableTransaction txn)
      throws ConfigException
  AttributeIndex(BackendIndexCfg config, State state, EntryContainer entryContainer) throws ConfigException
  {
    this.entryContainer = entryContainer;
    this.config = config;
    this.state = state;
    this.attributeType = config.getAttribute();
    buildPresenceIndex(txn);
    buildIndexes(txn, IndexType.EQUALITY);
    buildIndexes(txn, IndexType.SUBSTRING);
    buildIndexes(txn, IndexType.ORDERING);
    buildIndexes(txn, IndexType.APPROXIMATE);
    buildExtensibleIndexes(txn);
    indexingOptions = new IndexingOptionsImpl(config.getSubstringLength());
    this.indexingOptions = new IndexingOptionsImpl(config.getSubstringLength());
    this.indexIdToIndexes = Collections.unmodifiableMap(buildIndexes(entryContainer, state, config));
  }
  private void buildPresenceIndex(WriteableTransaction txn)
  private static Map<String, MatchingRuleIndex> buildIndexes(EntryContainer entryContainer, State state,
      BackendIndexCfg config) throws ConfigException
  {
    final IndexType indexType = IndexType.PRESENCE;
    if (config.getIndexType().contains(indexType))
    {
      String indexID = indexType.toString();
      nameToIndexes.put(indexID, new MatchingRuleIndex(txn, config, PRESENCE_INDEXER));
    }
  }
    final AttributeType attributeType = config.getAttribute();
    final int indexEntryLimit = config.getIndexEntryLimit();
    final IndexingOptions indexingOptions = new IndexingOptionsImpl(config.getSubstringLength());
  private void buildExtensibleIndexes(WriteableTransaction txn) throws ConfigException
  {
    final IndexType indexType = IndexType.EXTENSIBLE;
    if (config.getIndexType().contains(indexType))
    {
      final AttributeType attrType = config.getAttribute();
      Set<String> extensibleRules = config.getIndexExtensibleMatchingRule();
      if (extensibleRules == null || extensibleRules.isEmpty())
    final Map<String, MatchingRuleIndex> indexes = new HashMap<>();
    for(IndexType indexType : config.getIndexType()) {
      Collection<? extends Indexer> indexers;
      switch (indexType)
      {
        throw new ConfigException(ERR_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE.get(attrType, indexType.toString()));
      case PRESENCE:
        indexers = Collections.singleton(PRESENCE_INDEXER);
        break;
      case EXTENSIBLE:
        indexers =
            getExtensibleIndexers(config.getAttribute(), config.getIndexExtensibleMatchingRule(), indexingOptions);
        break;
      case APPROXIMATE:
        indexers =
            throwIfNoMatchingRule(attributeType, indexType, attributeType.getApproximateMatchingRule()).createIndexers(
                indexingOptions);
        break;
      case EQUALITY:
        indexers =
            throwIfNoMatchingRule(attributeType, indexType, attributeType.getEqualityMatchingRule()).createIndexers(
                indexingOptions);
        break;
      case ORDERING:
        indexers =
            throwIfNoMatchingRule(attributeType, indexType, attributeType.getOrderingMatchingRule()).createIndexers(
                indexingOptions);
        break;
      case SUBSTRING:
        indexers =
            throwIfNoMatchingRule(attributeType, indexType, attributeType.getSubstringMatchingRule()).createIndexers(
                indexingOptions);
        break;
      default:
       throw new ConfigException(ERR_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE.get(attributeType, indexType.toString()));
      }
      buildIndexesForIndexers(entryContainer, attributeType, state, indexes, indexEntryLimit, indexers);
    }
    return indexes;
  }
      // Iterate through the Set and create the index only if necessary.
      // Collation equality and Ordering matching rules share the same indexer and index
      // A Collation substring matching rule is treated differently
      // as it uses a separate indexer and index.
      for (final String ruleName : extensibleRules)
  private static MatchingRule throwIfNoMatchingRule(AttributeType attributeType, IndexType type, MatchingRule rule)
      throws ConfigException
  {
    if (rule == null)
    {
      throw new ConfigException(ERR_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE.get(attributeType, type.toString()));
    }
    return rule;
  }
  private static void buildIndexesForIndexers(EntryContainer entryContainer, AttributeType attributeType, State state,
      Map<String, MatchingRuleIndex> indexes, int indexEntryLimit, Collection<? extends Indexer> indexers)
  {
    for (Indexer indexer : indexers)
    {
      final String indexID = indexer.getIndexID();
      if (!indexes.containsKey(indexID))
      {
        MatchingRule rule = DirectoryServer.getMatchingRule(toLowerCase(ruleName));
        if (rule == null)
        {
          logger.error(ERR_CONFIG_INDEX_TYPE_NEEDS_VALID_MATCHING_RULE, attrType, ruleName);
          continue;
        }
        for (Indexer indexer : rule.getIndexers())
        {
          final String indexId = indexer.getIndexID();
          if (!nameToIndexes.containsKey(indexId))
          {
            // There is no index available for this index id. Create a new index
            nameToIndexes.put(indexId, new MatchingRuleIndex(txn, config, indexer));
          }
        }
        indexes.put(indexID, new MatchingRuleIndex(entryContainer, attributeType, state, indexer, indexEntryLimit));
      }
    }
  }
  private void buildIndexes(WriteableTransaction txn, IndexType indexType) throws ConfigException
  private static Collection<Indexer> getExtensibleIndexers(AttributeType attributeType, Set<String> extensibleRules,
      IndexingOptions options) throws ConfigException
  {
    if (config.getIndexType().contains(indexType))
    if (extensibleRules == null || extensibleRules.isEmpty())
    {
      final AttributeType attrType = config.getAttribute();
      final String indexID = indexType.toString();
      final MatchingRule rule = getMatchingRule(indexType, attrType);
      if (rule == null)
      {
        throw new ConfigException(ERR_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE.get(attrType, indexID));
      }
      for (Indexer indexer : rule.getIndexers())
      {
        nameToIndexes.put(indexer.getIndexID(), new MatchingRuleIndex(txn, config, indexer));
      }
      throw new ConfigException(
          ERR_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE.get(attributeType, IndexType.EXTENSIBLE.toString()));
    }
    final Collection<Indexer> indexers = new ArrayList<>();
    for (final String ruleName : extensibleRules)
    {
      final MatchingRule rule = DirectoryServer.getMatchingRule(toLowerCase(ruleName));
      throwIfNoMatchingRule(attributeType, IndexType.EXTENSIBLE, rule);
      indexers.addAll(rule.createIndexers(options));
    }
    return indexers;
  }
  private MatchingRule getMatchingRule(IndexType indexType, AttributeType attrType)
  private static TreeName getIndexName(EntryContainer entryContainer, AttributeType attrType, String indexID)
  {
    switch (indexType)
    {
    case APPROXIMATE:
      return attrType.getApproximateMatchingRule();
    case EQUALITY:
      return attrType.getEqualityMatchingRule();
    case ORDERING:
      return attrType.getOrderingMatchingRule();
    case SUBSTRING:
      return attrType.getSubstringMatchingRule();
    default:
      throw new IllegalArgumentException("Not implemented for index type " + indexType);
    }
  }
  private TreeName getIndexName(AttributeType attrType, String indexID)
  {
    final String attrIndexId = attrType.getNameOrOID() + "." + indexID;
    return new TreeName(entryContainer.getTreePrefix(), attrIndexId);
    return new TreeName(entryContainer.getTreePrefix(), attrType.getNameOrOID() + "." + indexID);
  }
  /**
@@ -357,7 +350,7 @@
   */
  void open(WriteableTransaction txn) throws StorageRuntimeException
  {
    for (Index index : nameToIndexes.values())
    for (Index index : indexIdToIndexes.values())
    {
      index.open(txn);
    }
@@ -436,10 +429,10 @@
   */
  void addEntry(IndexBuffer buffer, EntryID entryID, Entry entry) throws StorageRuntimeException, DirectoryException
  {
    for (MatchingRuleIndex index : nameToIndexes.values())
    for (MatchingRuleIndex index : indexIdToIndexes.values())
    {
      HashSet<ByteString> keys = new HashSet<ByteString>();
      index.indexEntry(entry, keys, indexingOptions);
      final Set<ByteString> keys = new HashSet<>();
      index.indexEntry(entry, keys);
      for (ByteString key : keys)
      {
        buffer.put(index, key, entryID);
@@ -458,10 +451,10 @@
   */
  void removeEntry(IndexBuffer buffer, EntryID entryID, Entry entry) throws StorageRuntimeException, DirectoryException
  {
    for (MatchingRuleIndex index : nameToIndexes.values())
    for (MatchingRuleIndex index : indexIdToIndexes.values())
    {
      HashSet<ByteString> keys = new HashSet<ByteString>();
      index.indexEntry(entry, keys, indexingOptions);
      index.indexEntry(entry, keys);
      for (ByteString key : keys)
      {
        buffer.remove(index, key, entryID);
@@ -482,10 +475,10 @@
   */
  void modifyEntry(IndexBuffer buffer, EntryID entryID, Entry oldEntry, Entry newEntry) throws StorageRuntimeException
  {
    for (MatchingRuleIndex index : nameToIndexes.values())
    for (MatchingRuleIndex index : indexIdToIndexes.values())
    {
      TreeMap<ByteString, Boolean> modifiedKeys = new TreeMap<ByteString, Boolean>();
      index.modifyEntry(oldEntry, newEntry, modifiedKeys, indexingOptions);
      index.modifyEntry(oldEntry, newEntry, modifiedKeys);
      for (Map.Entry<ByteString, Boolean> modifiedKey : modifiedKeys.entrySet())
      {
        if (modifiedKey.getValue())
@@ -694,226 +687,143 @@
    return true;
  }
  private boolean isIndexAcceptable(BackendIndexCfg cfg, IndexType indexType,
  private static boolean isIndexAcceptable(BackendIndexCfg cfg, IndexType indexType,
      List<LocalizableMessage> unacceptableReasons)
  {
    final String indexId = indexType.toString();
    final AttributeType attrType = cfg.getAttribute();
    if (cfg.getIndexType().contains(indexType)
        && nameToIndexes.get(indexId) == null
        && getMatchingRule(indexType, attrType) == null)
    {
      unacceptableReasons.add(ERR_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE.get(attrType, indexId));
      unacceptableReasons.add(ERR_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE.get(attrType, indexType));
      return false;
    }
    return true;
  }
  private static MatchingRule getMatchingRule(IndexType indexType, AttributeType attrType)
  {
    switch (indexType)
    {
    case APPROXIMATE:
      return attrType.getApproximateMatchingRule();
    case EQUALITY:
      return attrType.getEqualityMatchingRule();
    case ORDERING:
      return attrType.getOrderingMatchingRule();
    case SUBSTRING:
      return attrType.getSubstringMatchingRule();
    default:
      throw new IllegalArgumentException("Not implemented for index type " + indexType);
    }
  }
  /** {@inheritDoc} */
  @Override
  public synchronized ConfigChangeResult applyConfigurationChange(final BackendIndexCfg cfg)
  public synchronized ConfigChangeResult applyConfigurationChange(final BackendIndexCfg newConfiguration)
  {
    final ConfigChangeResult ccr = new ConfigChangeResult();
    final IndexingOptions newIndexingOptions = new IndexingOptionsImpl(newConfiguration.getSubstringLength());
    try
    {
      final Map<String, MatchingRuleIndex> newIndexIdToIndexes = buildIndexes(entryContainer, state, newConfiguration);
      final Map<String, MatchingRuleIndex> removedIndexes = new HashMap<>(indexIdToIndexes);
      removedIndexes.keySet().removeAll(newIndexIdToIndexes.keySet());
      final Map<String, MatchingRuleIndex> addedIndexes = new HashMap<>(newIndexIdToIndexes);
      addedIndexes.keySet().removeAll(indexIdToIndexes.keySet());
      final Map<String, MatchingRuleIndex> updatedIndexes = new HashMap<>(indexIdToIndexes);
      updatedIndexes.keySet().retainAll(newIndexIdToIndexes.keySet());
      // Replace instances of Index created by buildIndexes() with the one already opened and present in the actual
      // indexIdToIndexes
      newIndexIdToIndexes.putAll(updatedIndexes);
      // Open added indexes *before* adding them to indexIdToIndexes
      entryContainer.getRootContainer().getStorage().write(new WriteOperation()
      {
        @Override
        public void run(WriteableTransaction txn) throws Exception
        {
          applyChangeToIndex(txn, IndexType.PRESENCE, cfg, ccr);
          applyChangeToIndex(txn, IndexType.EQUALITY, cfg, ccr);
          applyChangeToIndex(txn, IndexType.SUBSTRING, cfg, ccr);
          applyChangeToIndex(txn, IndexType.ORDERING, cfg, ccr);
          applyChangeToIndex(txn, IndexType.APPROXIMATE, cfg, ccr);
          applyChangeToExtensibleIndexes(txn, cfg, ccr);
          for (MatchingRuleIndex addedIndex : addedIndexes.values())
          {
            openIndex(txn, addedIndex, ccr);
          }
        }
      });
      config = cfg;
      config = newConfiguration;
      indexingOptions = newIndexingOptions;
      indexIdToIndexes = Collections.unmodifiableMap(newIndexIdToIndexes);
      // We get exclusive lock to ensure that no query is actually using the indexes that will be deleted.
      entryContainer.lock();
      try
      {
        entryContainer.getRootContainer().getStorage().write(new WriteOperation()
        {
          @Override
          public void run(WriteableTransaction txn) throws Exception
          {
            for (MatchingRuleIndex removedIndex : removedIndexes.values())
            {
              deleteIndex(txn, entryContainer, removedIndex);
            }
          }
        });
      }
      finally
      {
        entryContainer.unlock();
      }
      for (Index updatedIndex : updatedIndexes.values())
      {
        updateIndex(updatedIndex, newConfiguration.getIndexEntryLimit(), ccr);
      }
    }
    catch(Exception e)
    catch (Exception e)
    {
      ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
      ccr.addMessage(LocalizableMessage.raw(StaticUtils.stackTraceToSingleLineString(e)));
    }
    return ccr;
  }
  private void applyChangeToExtensibleIndexes(WriteableTransaction txn, BackendIndexCfg cfg, ConfigChangeResult ccr)
  private static void openIndex(WriteableTransaction txn, MatchingRuleIndex index, ConfigChangeResult ccr)
  {
    final AttributeType attrType = cfg.getAttribute();
    if (!cfg.getIndexType().contains(IndexType.EXTENSIBLE))
    {
      final Set<MatchingRule> validRules = Collections.emptySet();
      final Set<String> validIndexIds = Collections.emptySet();
      removeIndexesForExtensibleMatchingRules(txn, validRules, validIndexIds);
      return;
    }
    final Set<String> extensibleRules = cfg.getIndexExtensibleMatchingRule();
    final Set<MatchingRule> validRules = new HashSet<MatchingRule>();
    final Set<String> validIndexIds = new HashSet<String>();
    final int indexEntryLimit = cfg.getIndexEntryLimit();
    for (String ruleName : extensibleRules)
    {
      MatchingRule rule = DirectoryServer.getMatchingRule(toLowerCase(ruleName));
      if (rule == null)
      {
        logger.error(ERR_CONFIG_INDEX_TYPE_NEEDS_VALID_MATCHING_RULE, attrType, ruleName);
        continue;
      }
      validRules.add(rule);
      for (Indexer indexer : rule.getIndexers())
      {
        String indexId = indexer.getIndexID();
        validIndexIds.add(indexId);
        if (!nameToIndexes.containsKey(indexId))
        {
          nameToIndexes.put(indexId, openNewIndex(txn, cfg, indexer, ccr));
        }
        else
        {
          Index index = nameToIndexes.get(indexId);
          if (index.setIndexEntryLimit(indexEntryLimit))
          {
            ccr.setAdminActionRequired(true);
            ccr.addMessage(NOTE_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD.get(index.getName()));
          }
        }
      }
    }
    removeIndexesForExtensibleMatchingRules(txn, validRules, validIndexIds);
  }
  /** Remove indexes which do not correspond to valid rules. */
  private void removeIndexesForExtensibleMatchingRules(WriteableTransaction txn, Set<MatchingRule> validRules,
      Set<String> validIndexIds)
  {
    final Set<MatchingRule> rulesToDelete = getCurrentExtensibleMatchingRules();
    rulesToDelete.removeAll(validRules);
    if (!rulesToDelete.isEmpty())
    {
      entryContainer.exclusiveLock.lock();
      try
      {
        for (MatchingRule rule: rulesToDelete)
        {
          final List<String> indexIdsToRemove = new ArrayList<String>();
          for (Indexer indexer : rule.getIndexers())
          {
            final String indexId = indexer.getIndexID();
            if (!validIndexIds.contains(indexId))
            {
              indexIdsToRemove.add(indexId);
            }
          }
          // Delete indexes which are not used
          for (String indexId : indexIdsToRemove)
          {
            Index index = nameToIndexes.get(indexId);
            if (index != null)
            {
              entryContainer.deleteTree(txn, index);
              nameToIndexes.remove(index);
            }
          }
        }
      }
      finally
      {
        entryContainer.exclusiveLock.unlock();
      }
    }
  }
  private Set<MatchingRule> getCurrentExtensibleMatchingRules()
  {
    final Set<MatchingRule> rules = new HashSet<MatchingRule>();
    for (String ruleName : config.getIndexExtensibleMatchingRule())
    {
        final MatchingRule rule = DirectoryServer.getMatchingRule(toLowerCase(ruleName));
        if (rule != null)
        {
          rules.add(rule);
        }
    }
    return rules;
  }
  private void applyChangeToIndex(final WriteableTransaction txn, final IndexType indexType, final BackendIndexCfg cfg,
      final ConfigChangeResult ccr)
  {
    String indexId = indexType.toString();
    MatchingRuleIndex index = nameToIndexes.get(indexId);
    if (!cfg.getIndexType().contains(indexType))
    {
      removeIndex(txn, index, indexType);
      return;
    }
    if (index == null)
    {
      if (indexType == IndexType.PRESENCE)
      {
        nameToIndexes.put(indexId, openNewIndex(txn, cfg, PRESENCE_INDEXER, ccr));
      }
      else
      {
        final MatchingRule matchingRule = getMatchingRule(indexType, cfg.getAttribute());
        for (Indexer indexer : matchingRule.getIndexers())
        {
          nameToIndexes.put(indexId, openNewIndex(txn, cfg, indexer, ccr));
        }
      }
    }
    else
    {
      // already exists. Just update index entry limit.
      if (index.setIndexEntryLimit(cfg.getIndexEntryLimit()))
      {
        ccr.setAdminActionRequired(true);
        ccr.addMessage(NOTE_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD.get(index.getName()));
      }
      if (indexType == IndexType.SUBSTRING && config.getSubstringLength() != cfg.getSubstringLength())
      {
        ccr.setAdminActionRequired(true);
        ccr.addMessage(NOTE_CONFIG_INDEX_SUBSTRING_LENGTH_REQUIRES_REBUILD.get(index.getName()));
      }
    }
  }
  private void removeIndex(WriteableTransaction txn, Index index, IndexType indexType)
  {
    if (index != null)
    {
      entryContainer.exclusiveLock.lock();
      try
      {
        nameToIndexes.remove(indexType.toString());
        entryContainer.deleteTree(txn, index);
      }
      finally
      {
        entryContainer.exclusiveLock.unlock();
      }
    }
  }
  private MatchingRuleIndex openNewIndex(WriteableTransaction txn, BackendIndexCfg cfg, Indexer indexer,
      ConfigChangeResult ccr)
  {
    final MatchingRuleIndex index = new MatchingRuleIndex(txn, cfg, indexer);
    index.open(txn);
    if (!index.isTrusted())
    {
      ccr.setAdminActionRequired(true);
      ccr.addMessage(NOTE_INDEX_ADD_REQUIRES_REBUILD.get(index.getName()));
    }
    return index;
  }
  private static void updateIndex(Index updatedIndex, int newIndexEntryLimit, ConfigChangeResult ccr)
  {
    if (updatedIndex.setIndexEntryLimit(newIndexEntryLimit))
    {
      // This index can still be used since index size limit doesn't impact validity of the results.
      ccr.setAdminActionRequired(true);
      ccr.addMessage(NOTE_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD.get(updatedIndex.getName()));
    }
  }
  private static void deleteIndex(WriteableTransaction txn, EntryContainer entryContainer, Index index)
  {
    entryContainer.exclusiveLock.lock();
    try
    {
      entryContainer.deleteTree(txn, index);
    }
    finally
    {
      entryContainer.exclusiveLock.unlock();
    }
  }
  /**
@@ -922,7 +832,7 @@
   */
  boolean isTrusted()
  {
    for (Index index : nameToIndexes.values())
    for (Index index : indexIdToIndexes.values())
    {
      if (!index.isTrusted())
      {
@@ -946,7 +856,7 @@
  Map<String, MatchingRuleIndex> getNameToIndexes()
  {
    return Collections.unmodifiableMap(nameToIndexes);
    return indexIdToIndexes;
  }
  /**
@@ -997,7 +907,7 @@
      if (debugBuffer != null)
      {
        debugBuffer.append("[INDEX:");
        for (Indexer indexer : rule.getIndexers())
        for (Indexer indexer : rule.createIndexers(indexingOptions))
        {
            debugBuffer.append(" ")
              .append(filter.getAttributeType().getNameOrOID())
@@ -1032,9 +942,9 @@
  private boolean ruleHasAtLeastOneIndex(MatchingRule rule)
  {
    for (Indexer indexer : rule.getIndexers())
    for (Indexer indexer : rule.createIndexers(indexingOptions))
    {
      if (nameToIndexes.containsKey(indexer.getIndexID()))
      if (indexIdToIndexes.containsKey(indexer.getIndexID()))
      {
        return true;
      }
@@ -1043,7 +953,7 @@
  }
  /** Indexing options implementation. */
  private final class IndexingOptionsImpl implements IndexingOptions
  private static final class IndexingOptionsImpl implements IndexingOptions
  {
    /** The length of substring keys used in substring indexes. */
    private int substringKeySize;
@@ -1063,7 +973,7 @@
  void closeAndDelete(WriteableTransaction txn)
  {
    close();
    for (Index index : nameToIndexes.values())
    for (Index index : indexIdToIndexes.values())
    {
      index.delete(txn);
      state.deleteRecord(txn, index.getName());
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/DefaultIndex.java
@@ -64,7 +64,9 @@
  private final State state;
  private final EntryIDSetCodec codec;
  private final EntryContainer entryContainer;
  private EntryIDSetCodec codec;
  /**
   * A flag to indicate if this index should be trusted to be consistent with the entries tree.
@@ -84,23 +86,26 @@
   *          The state tree to persist index state info.
   * @param indexEntryLimit
   *          The configured limit on the number of entry IDs that may be indexed by one key.
   * @param txn
   *          a non null transaction
   * @param entryContainer
   *          The entryContainer holding this index.
   * @throws StorageRuntimeException
   *           If an error occurs in the storage.
   */
  DefaultIndex(TreeName name, State state, int indexEntryLimit, WriteableTransaction txn, EntryContainer entryContainer)
  DefaultIndex(TreeName name, State state, int indexEntryLimit, EntryContainer entryContainer)
      throws StorageRuntimeException
  {
    super(name);
    this.indexEntryLimit = indexEntryLimit;
    this.state = state;
    this.entryContainer = entryContainer;
  }
  @Override
  final void open0(WriteableTransaction txn)
  {
    final EnumSet<IndexFlag> flags = state.getIndexFlags(txn, getName());
    this.codec = flags.contains(COMPACTED) ? CODEC_V2 : CODEC_V1;
    this.trusted = flags.contains(TRUSTED);
    codec = flags.contains(COMPACTED) ? CODEC_V2 : CODEC_V1;
    trusted = flags.contains(TRUSTED);
    if (!trusted && entryContainer.getHighestEntryID(txn).longValue() == 0)
    {
      // If there are no entries in the entry container then there
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/EntryContainer.java
@@ -195,17 +195,7 @@
    {
      try
      {
        // FIXME this should be a read operation, but I cannot change it
        // because of AttributeIndex ctor.
        storage.write(new WriteOperation()
        {
          @Override
          public void run(WriteableTransaction txn) throws Exception
          {
            //Try creating all the indexes before confirming they are valid ones.
            new AttributeIndex(cfg, state, EntryContainer.this, txn);
          }
        });
        new AttributeIndex(cfg, state, EntryContainer.this);
        return true;
      }
      catch(Exception e)
@@ -222,12 +212,12 @@
      final ConfigChangeResult ccr = new ConfigChangeResult();
      try
      {
        final AttributeIndex index = new AttributeIndex(cfg, state, EntryContainer.this);
        storage.write(new WriteOperation()
        {
          @Override
          public void run(WriteableTransaction txn) throws Exception
          {
            final AttributeIndex index = new AttributeIndex(cfg, state, EntryContainer.this, txn);
            index.open(txn);
            if (!index.isTrusted())
            {
@@ -496,7 +486,7 @@
      {
        BackendIndexCfg indexCfg = config.getBackendIndex(idx);
        AttributeIndex index = new AttributeIndex(indexCfg, state, this, txn);
        final AttributeIndex index = new AttributeIndex(indexCfg, state, this);
        index.open(txn);
        if(!index.isTrusted())
        {
@@ -2194,8 +2184,7 @@
                }
                // Move this entry.
                removeSubordinateEntry(txn, buffer, oldSuperiorDN, oldID, newID, oldEntry, newDN, isApexEntryMoved,
                    modifyDNOperation, current);
                removeSubordinateEntry(txn, buffer, oldID, newID, oldEntry, newDN, modifyDNOperation, current);
                current = current.next;
                if (modifyDNOperation != null)
@@ -2364,10 +2353,8 @@
  }
  private void removeSubordinateEntry(WriteableTransaction txn, IndexBuffer buffer,
      DN oldSuperiorDN,
      EntryID oldID, EntryID newID,
      Entry oldEntry, DN newDN,
      boolean isApexEntryMoved,
      ModifyDNOperation modifyDNOperation,
      MovedEntry tail)
  throws DirectoryException, StorageRuntimeException
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/Importer.java
@@ -93,7 +93,6 @@
import org.forgerock.opendj.ldap.ByteSequenceReader;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.ByteStringBuilder;
import org.forgerock.opendj.ldap.spi.IndexingOptions;
import org.forgerock.util.Utils;
import org.opends.server.admin.std.meta.BackendIndexCfgDefn.IndexType;
import org.opends.server.admin.std.server.BackendIndexCfg;
@@ -1436,20 +1435,20 @@
    }
    @Override
    void processAttribute(MatchingRuleIndex index, Entry entry, EntryID entryID, IndexingOptions options,
        IndexKey indexKey) throws StorageRuntimeException, InterruptedException
    void processAttribute(MatchingRuleIndex index, Entry entry, EntryID entryID, IndexKey indexKey)
        throws StorageRuntimeException, InterruptedException
    {
      if (oldEntry != null)
      {
        deleteKeySet.clear();
        index.indexEntry(oldEntry, deleteKeySet, options);
        index.indexEntry(oldEntry, deleteKeySet);
        for (ByteString delKey : deleteKeySet)
        {
          processKey(index, delKey, entryID, indexKey, false);
        }
      }
      insertKeySet.clear();
      index.indexEntry(entry, insertKeySet, options);
      index.indexEntry(entry, insertKeySet);
      for (ByteString key : insertKeySet)
      {
        processKey(index, key, entryID, indexKey, true);
@@ -1581,11 +1580,9 @@
    void fillIndexKey(AttributeIndex attrIndex, Entry entry, AttributeType attrType, EntryID entryID)
        throws InterruptedException, StorageRuntimeException
    {
      final IndexingOptions options = attrIndex.getIndexingOptions();
      for (Map.Entry<String, MatchingRuleIndex> mapEntry : attrIndex.getNameToIndexes().entrySet())
      {
        processAttribute(mapEntry.getValue(), mapEntry.getKey(), entry, attrType, entryID, options);
        processAttribute(mapEntry.getValue(), mapEntry.getKey(), entry, attrType, entryID);
      }
    }
@@ -1602,20 +1599,20 @@
    }
    private void processAttribute(MatchingRuleIndex index, String indexID, Entry entry,
        AttributeType attributeType, EntryID entryID, IndexingOptions options) throws InterruptedException
        AttributeType attributeType, EntryID entryID) throws InterruptedException
    {
      if (index != null)
      {
        IndexKey indexKey = new IndexKey(attributeType, indexID, index.getIndexEntryLimit());
        processAttribute(index, entry, entryID, options, indexKey);
        processAttribute(index, entry, entryID, indexKey);
      }
    }
    void processAttribute(MatchingRuleIndex index, Entry entry, EntryID entryID, IndexingOptions options,
        IndexKey indexKey) throws StorageRuntimeException, InterruptedException
    void processAttribute(MatchingRuleIndex index, Entry entry, EntryID entryID, IndexKey indexKey)
        throws StorageRuntimeException, InterruptedException
    {
      insertKeySet.clear();
      index.indexEntry(entry, insertKeySet, options);
      index.indexEntry(entry, insertKeySet);
      for (ByteString key : insertKeySet)
      {
        processKey(index, key, entryID, indexKey, true);
@@ -3167,10 +3164,7 @@
        AttributeType attrType = indexKey.getAttributeType();
        if (entry.hasAttribute(attrType))
        {
          AttributeIndex attributeIndex = entryContainer.getAttributeIndex(attrType);
          IndexingOptions options = attributeIndex.getIndexingOptions();
          MatchingRuleIndex index = mapEntry.getValue();
          processAttribute(index, entry, entryID, options, indexKey);
          processAttribute(mapEntry.getValue(), entry, entryID, indexKey);
        }
      }
    }
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/VLVIndex.java
@@ -164,7 +164,7 @@
  }
  @Override
  void open0(final ReadableTransaction txn) throws StorageRuntimeException
  void open0(final WriteableTransaction txn) throws StorageRuntimeException
  {
    count.set((int) txn.getRecordCount(getName()));
  }
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/VerifyJob.java
@@ -51,7 +51,6 @@
import org.forgerock.opendj.ldap.ByteSequence;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.ConditionResult;
import org.forgerock.opendj.ldap.spi.IndexingOptions;
import org.opends.server.backends.VerifyConfig;
import org.opends.server.backends.pluggable.AttributeIndex.MatchingRuleIndex;
import org.opends.server.backends.pluggable.spi.Cursor;
@@ -431,10 +430,9 @@
    else if (attrIndexList.size() > 0)
    {
      AttributeIndex attrIndex = attrIndexList.get(0);
      final IndexingOptions options = attrIndex.getIndexingOptions();
      for (MatchingRuleIndex index : attrIndex.getNameToIndexes().values())
      {
        iterateAttrIndex(txn, index, options);
        iterateAttrIndex(txn, index);
      }
    }
    else if (vlvIndexList.size() > 0)
@@ -672,8 +670,7 @@
   * @param index The index tree to be checked.
   * @throws StorageRuntimeException If an error occurs in the storage.
   */
  private void iterateAttrIndex(ReadableTransaction txn, MatchingRuleIndex index, IndexingOptions options)
      throws StorageRuntimeException
  private void iterateAttrIndex(ReadableTransaction txn, MatchingRuleIndex index) throws StorageRuntimeException
  {
    if (index == null)
    {
@@ -780,7 +777,7 @@
            };
            index.indexEntry(entry, dummySet, options);
            index.indexEntry(entry, dummySet);
            if (!foundMatchingKey.get())
            {
@@ -958,11 +955,10 @@
   */
  private void verifyAttribute(ReadableTransaction txn, EntryID entryID, Entry entry, AttributeIndex attrIndex)
  {
    IndexingOptions options = attrIndex.getIndexingOptions();
    for (MatchingRuleIndex index : attrIndex.getNameToIndexes().values())
    {
      Set<ByteString> keys = new HashSet<ByteString>();
      index.indexEntry(entry, keys, options);
      index.indexEntry(entry, keys);
      for (ByteString key : keys)
      {
        verifyAttributeInIndex(index, txn, key, entryID);
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/HistoricalCsnOrderingMatchingRuleImpl.java
@@ -51,7 +51,7 @@
 */
public final class HistoricalCsnOrderingMatchingRuleImpl implements MatchingRuleImpl
{
  private static final String ORDERING_ID = "ordering";
  private static final String ORDERING_ID = "changeSequenceNumberOrderingMatch";
  private final Collection<? extends Indexer> indexers = Collections.singleton(new HistoricalIndexer());
@@ -59,8 +59,7 @@
  private final class HistoricalIndexer implements Indexer
  {
    @Override
    public void createKeys(Schema schema, ByteSequence value, IndexingOptions options, Collection<ByteString> keys)
        throws DecodeException
    public void createKeys(Schema schema, ByteSequence value, Collection<ByteString> keys) throws DecodeException
    {
      keys.add(normalizeAttributeValue(schema, value));
    }
@@ -172,16 +171,9 @@
  /** {@inheritDoc} */
  @Override
  public Collection<? extends Indexer> getIndexers()
  public Collection<? extends Indexer> createIndexers(IndexingOptions options)
  {
    return indexers;
  }
  /** {@inheritDoc} */
  @Override
  public boolean isIndexingSupported()
  {
    return !indexers.isEmpty();
  }
}
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/schema/AbstractPasswordEqualityMatchingRuleImpl.java
@@ -42,6 +42,7 @@
import org.forgerock.opendj.ldap.spi.IndexingOptions;
import static org.forgerock.opendj.ldap.Assertion.*;
import static org.opends.server.schema.SchemaConstants.*;
/**
 * Abstract implementation for password matching rules.
@@ -49,13 +50,12 @@
abstract class AbstractPasswordEqualityMatchingRuleImpl implements MatchingRuleImpl
{
  private static final String EQUALITY_ID = "equality";
  private static final String EQUALITY_ID =  EMR_AUTH_PASSWORD_NAME;
  private final Collection<? extends Indexer> indexers = Collections.singleton(new Indexer()
  {
    @Override
    public void createKeys(Schema schema, ByteSequence value, IndexingOptions options, Collection<ByteString> keys)
        throws DecodeException
    public void createKeys(Schema schema, ByteSequence value, Collection<ByteString> keys) throws DecodeException
    {
      keys.add(normalizeAttributeValue(schema, value));
    }
@@ -131,18 +131,11 @@
  /** {@inheritDoc} */
  @Override
  public Collection<? extends Indexer> getIndexers()
  public Collection<? extends Indexer> createIndexers(IndexingOptions options)
  {
    return indexers;
  }
  /** {@inheritDoc} */
  @Override
  public boolean isIndexingSupported()
  {
    return !indexers.isEmpty();
  }
  /**
   * Indicates whether the provided attribute value should be considered a match
   * for the given assertion value.  This will only be used for the purpose of
opendj-sdk/opendj-server-legacy/src/test/java/org/opends/server/backends/SchemaTestMatchingRuleImpl.java
@@ -40,6 +40,7 @@
import org.forgerock.opendj.ldap.schema.MatchingRuleImpl;
import org.forgerock.opendj.ldap.schema.Schema;
import org.forgerock.opendj.ldap.spi.Indexer;
import org.forgerock.opendj.ldap.spi.IndexingOptions;
/**
@@ -94,15 +95,10 @@
  }
  @Override
  public Collection<? extends Indexer> getIndexers()
  public Collection<? extends Indexer> createIndexers(IndexingOptions options)
  {
    return caseIgnoreMatchingRule.getIndexers();
    return caseIgnoreMatchingRule.createIndexers(options);
  }
  @Override
  public boolean isIndexingSupported()
  {
    return !getIndexers().isEmpty();
  }
}
opendj-sdk/opendj-server-legacy/src/test/java/org/opends/server/backends/jeb/TestBackendImpl.java
@@ -26,11 +26,14 @@
 */
package org.opends.server.backends.jeb;
import static org.opends.server.schema.SchemaConstants.*;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
@@ -95,10 +98,15 @@
 */
@SuppressWarnings("javadoc")
public class TestBackendImpl extends JebTestCase {
  private String homeDirName;
  private BackendImpl backend;
  private AttributeType givenName;
  private AttributeType title;
  private AttributeType name;
  private List<Entry> topEntries;
  private List<Entry> entries;
  private List<Entry> additionalEntries;
@@ -565,6 +573,9 @@
        "ou: People"
    );
    givenName = DirectoryServer.getAttributeType("givenname");
    title = DirectoryServer.getAttributeType("title");
    name = DirectoryServer.getAttributeType("name");
  }
  @AfterClass
@@ -803,18 +814,17 @@
      AttributeIndex index = ec.getAttributeIndex(attribute);
      AttributeType attrType = index.getAttributeType();
      List<? extends Indexer> indexers;
      indexers = singletonList(new PresenceIndexer(index.getAttributeType()));
      assertIndexContainsID(indexers, entry, index.getPresenceIndex(), entryID, FALSE);
      List<? extends Indexer> indexers = singletonList(new PresenceIndexer(index.getAttributeType()));
      assertIndexContainsID(indexers, entry, index.getIndex("presence"), entryID, FALSE);
      indexers = newAttributeIndexers(attrType, attrType.getEqualityMatchingRule());
      assertIndexContainsID(indexers, entry, index.getEqualityIndex(), entryID, FALSE);
      assertIndexContainsID(indexers, entry, index.getIndex(EMR_CASE_IGNORE_NAME), entryID, FALSE);
      indexers = newAttributeIndexers(attrType, attrType.getSubstringMatchingRule());
      assertIndexContainsID(indexers, entry, index.getSubstringIndex(), entryID, FALSE);
      assertIndexContainsID(indexers, entry, index.getIndex(substringIndexId()), entryID, FALSE);
      indexers = newAttributeIndexers(attrType, attrType.getOrderingMatchingRule());
      assertIndexContainsID(indexers, entry, index.getOrderingIndex(), entryID, FALSE);
      // OrderingIndex is now handled by EqualityIndex (OPENDJ-1864)
      assertThat(index.getIndex(OMR_CASE_IGNORE_NAME)).isNull();
    }
    finally
    {
@@ -825,7 +835,7 @@
  private static List<AttributeIndexer> newAttributeIndexers(AttributeType attrType, MatchingRule matchingRule)
  {
    List<AttributeIndexer> indexers = new ArrayList<AttributeIndexer>();
    for (org.forgerock.opendj.ldap.spi.Indexer indexer : matchingRule.getIndexers())
    for (org.forgerock.opendj.ldap.spi.Indexer indexer : matchingRule.createIndexers(getOptions()))
    {
      indexers.add(new AttributeIndexer(attrType, indexer));
    }
@@ -844,7 +854,7 @@
    for (Indexer indexer : indexers)
    {
      Set<ByteString> addKeys = new HashSet<ByteString>();
      indexer.indexEntry(entry, addKeys, getOptions());
      indexer.indexEntry(entry, addKeys);
      DatabaseEntry key = new DatabaseEntry();
      for (ByteString keyBytes : addKeys)
@@ -861,7 +871,7 @@
    for (Indexer indexer : indexers)
    {
      Set<ByteString> addKeys = new HashSet<ByteString>();
      indexer.indexEntry(entry, addKeys, getOptions());
      indexer.indexEntry(entry, addKeys);
      assertIndexContainsID(addKeys, index, entryID, expected);
    }
@@ -915,16 +925,16 @@
      List<? extends Indexer> indexers;
      indexers = newAttributeIndexers(attrType, attrType.getOrderingMatchingRule());
      assertIndexContainsID(indexers, entry, index.getOrderingIndex(), entryID, TRUE);
      assertIndexContainsID(indexers, oldEntry, index.getOrderingIndex(), entryID, FALSE);
      // OrderingIndex is now handled by EqualityIndex (OPENDJ-1864)
      assertThat(index.getIndex(OMR_CASE_IGNORE_NAME)).isNull();
      indexers = newAttributeIndexers(attrType, attrType.getSubstringMatchingRule());
      assertIndexContainsID(indexers, entry, index.getSubstringIndex(), entryID, TRUE);
      assertIndexContainsID(indexers, oldEntry, index.getSubstringIndex(), entryID, FALSE);
      assertIndexContainsID(indexers, entry, index.getIndex(substringIndexId()), entryID, TRUE);
      assertIndexContainsID(indexers, oldEntry, index.getIndex(substringIndexId()), entryID, FALSE);
      indexers = newAttributeIndexers(attrType, attrType.getEqualityMatchingRule());
      assertIndexContainsID(indexers, entry, index.getEqualityIndex(), entryID, TRUE);
      assertIndexContainsID(indexers, oldEntry, index.getEqualityIndex(), entryID, FALSE);
      assertIndexContainsID(indexers, entry, index.getIndex(EMR_CASE_IGNORE_NAME), entryID, TRUE);
      assertIndexContainsID(indexers, oldEntry, index.getIndex(EMR_CASE_IGNORE_NAME), entryID, FALSE);
    }
    finally
    {
@@ -987,20 +997,18 @@
      assertNotNull(entryID);
      attribute = DirectoryServer.getAttributeType("title");
      titleIndex = ec.getAttributeIndex(attribute);
      attribute = DirectoryServer.getAttributeType("name");
      nameIndex = ec.getAttributeIndex(attribute);
      titleIndex = ec.getAttributeIndex(title);
      nameIndex = ec.getAttributeIndex(name);
      // This current entry in the DB shouldn't be in the presence titleIndex.
      addKeys = new HashSet<ByteString>();
      addKeys.add(PresenceIndexer.presenceKey);
      assertIndexContainsID(addKeys, titleIndex.getPresenceIndex(), entryID, FALSE);
      assertIndexContainsID(addKeys, titleIndex.getIndex("presence"), entryID, FALSE);
      // This current entry should be in the presence nameIndex.
      addKeys = new HashSet<ByteString>();
      addKeys.add(PresenceIndexer.presenceKey);
      assertIndexContainsID(addKeys, nameIndex.getPresenceIndex(), entryID, TRUE);
      assertIndexContainsID(addKeys, nameIndex.getIndex("presence"), entryID, TRUE);
      List<Control> noControls = new ArrayList<Control>(0);
      ModifyOperationBasis modifyOp = new ModifyOperationBasis(getRootConnection(), nextOperationID(), nextMessageID(),
@@ -1039,24 +1047,30 @@
      AttributeType nameIndexAttrType = nameIndex.getAttributeType();
      indexers = singletonList(new PresenceIndexer(titleIndexAttrType));
      assertIndexContainsID(indexers, entry, titleIndex.getPresenceIndex(), entryID);
      assertIndexContainsID(indexers, entry, titleIndex.getIndex("presence"), entryID);
      indexers = singletonList(new PresenceIndexer(nameIndexAttrType));
      assertIndexContainsID(indexers, entry, nameIndex.getPresenceIndex(), entryID);
      assertIndexContainsID(indexers, entry, nameIndex.getIndex("presence"), entryID);
      indexers = newAttributeIndexers(titleIndexAttrType, titleIndexAttrType.getOrderingMatchingRule());
      assertIndexContainsID(indexers, entry, titleIndex.getOrderingIndex(), entryID);
      indexers = newAttributeIndexers(nameIndexAttrType, nameIndexAttrType.getOrderingMatchingRule());
      assertIndexContainsID(indexers, entry, nameIndex.getOrderingIndex(), entryID);
      // OrderingIndex is now handled by EqualityIndex (OPENDJ-1864)
      assertThat(getIndexNames(titleIndex)).containsOnly(
          indexName(ec, titleIndexAttrType, "presence"),
          indexName(ec, titleIndexAttrType, EMR_CASE_IGNORE_NAME),
          indexName(ec, titleIndexAttrType, SMR_CASE_IGNORE_NAME + ":6"));
      assertThat(getIndexNames(nameIndex)).containsOnly(
          indexName(ec, nameIndexAttrType, "presence"),
          indexName(ec, nameIndexAttrType, AMR_DOUBLE_METAPHONE_NAME),
          indexName(ec, nameIndexAttrType, EMR_CASE_IGNORE_NAME),
          indexName(ec, nameIndexAttrType, SMR_CASE_IGNORE_NAME + ":6"));
      indexers = newAttributeIndexers(titleIndexAttrType, titleIndexAttrType.getEqualityMatchingRule());
      assertIndexContainsID(indexers, entry, titleIndex.getEqualityIndex(), entryID);
      assertIndexContainsID(indexers, entry, titleIndex.getIndex(EMR_CASE_IGNORE_NAME), entryID);
      indexers = newAttributeIndexers(nameIndexAttrType, nameIndexAttrType.getEqualityMatchingRule());
      assertIndexContainsID(indexers, entry, nameIndex.getEqualityIndex(), entryID);
      assertIndexContainsID(indexers, entry, nameIndex.getIndex(EMR_CASE_IGNORE_NAME), entryID);
      indexers = newAttributeIndexers(titleIndexAttrType, titleIndexAttrType.getSubstringMatchingRule());
      assertIndexContainsID(indexers, entry, titleIndex.getSubstringIndex(), entryID);
      assertIndexContainsID(indexers, entry, titleIndex.getIndex(substringIndexId()), entryID);
      indexers = newAttributeIndexers(nameIndexAttrType, nameIndexAttrType.getSubstringMatchingRule());
      assertIndexContainsID(indexers, entry, nameIndex.getSubstringIndex(), entryID);
      assertIndexContainsID(indexers, entry, nameIndex.getIndex(substringIndexId()), entryID);
    }
    finally
    {
@@ -1176,6 +1190,39 @@
    assertNotNull(rootContainer.getEntryContainer(DN.valueOf("dc=newsuffix,dc=com")));
  }
  /** @since OPENDJ-1864 Equality and Ordering indexes share the same database */
  @Test
  public void testRemovingOrderingIndexDoesNotRemoveEquality() throws Exception
  {
    int resultCode = TestCaseUtils.applyModifications(true,
        "dn: ds-cfg-attribute=givenName,cn=Index,ds-cfg-backend-id=indexRoot,cn=Backends,cn=config",
        "changetype: modify",
        "replace: ds-cfg-index-type",
        "ds-cfg-index-type: equality",
        "ds-cfg-index-type: ordering");
    assertEquals(resultCode, 0);
    RootContainer rootContainer = backend.getRootContainer();
    EntryContainer ec = rootContainer.getEntryContainer(DN.valueOf("dc=test,dc=com"));
    Collection<String> containerNames = getContainerNames(ec);
    assertThat(containerNames).contains(indexName(ec, givenName, EMR_CASE_IGNORE_NAME));
    assertThat(containerNames).doesNotContain(indexName(ec, givenName, OMR_CASE_IGNORE_NAME));
    // Remove equality indexes
    resultCode = TestCaseUtils.applyModifications(true,
        "dn: ds-cfg-attribute=givenName,cn=Index,ds-cfg-backend-id=indexRoot,cn=Backends,cn=config",
        "changetype: modify",
        "replace: ds-cfg-index-type",
        "ds-cfg-index-type: ordering");
    assertEquals(resultCode, 0);
    // Since ordering is using the same equality db, it must remain
    containerNames = getContainerNames(ec);
    assertThat(containerNames).contains(indexName(ec, givenName, EMR_CASE_IGNORE_NAME));
    assertThat(containerNames).doesNotContain(indexName(ec, givenName, OMR_CASE_IGNORE_NAME));
  }
  @Test(dependsOnMethods = {"testModifyDN",
      "testSearchScope", "testSearchIndex", "testReplaceEntry",
      "testModifyEntry", "testModifyDN", "testDeleteSubtree",
@@ -1184,8 +1231,7 @@
      "testModifyDNNewSuperior", "testMatchedDN"})
  public void testApplyIndexConfig() throws Exception {
    int resultCode = TestCaseUtils.applyModifications(true,
        "dn: ds-cfg-attribute=givenName,cn=Index," +
            "ds-cfg-backend-id=indexRoot,cn=Backends,cn=config",
        "dn: ds-cfg-attribute=givenName,cn=Index,ds-cfg-backend-id=indexRoot,cn=Backends,cn=config",
        "changetype: modify",
        "replace: ds-cfg-index-type",
        "ds-cfg-index-type: approximate");
@@ -1195,20 +1241,13 @@
    RootContainer rootContainer = backend.getRootContainer();
    EntryContainer ec = rootContainer.getEntryContainer(DN.valueOf("dc=test,dc=com"));
    AttributeType givennameAttr = DirectoryServer.getAttributeType("givenname");
    AttributeIndex attributeIndex = ec.getAttributeIndex(givennameAttr);
    assertNull(attributeIndex.getEqualityIndex());
    assertNull(attributeIndex.getPresenceIndex());
    assertNull(attributeIndex.getSubstringIndex());
    assertNull(attributeIndex.getOrderingIndex());
    assertNotNull(attributeIndex.getApproximateIndex());
    List<DatabaseContainer> databases = new ArrayList<DatabaseContainer>();
    ec.listDatabases(databases);
    assertFalse(findContainer(databases, "givenname.equality"));
    assertFalse(findContainer(databases, "givenname.presence"));
    assertFalse(findContainer(databases, "givenname.substring"));
    assertFalse(findContainer(databases, "givenname.ordering"));
    assertTrue(findContainer(databases, "givenname.approximate"));
    Collection<String> containerNames = getContainerNames(ec);
    assertThat(containerNames).doesNotContain(
        indexName(ec, givenName, EMR_CASE_IGNORE_NAME),
        indexName(ec, givenName, "presence"),
        indexName(ec, givenName, substringIndexId()),
        indexName(ec, givenName, OMR_CASE_IGNORE_NAME));
    assertThat(containerNames).contains(indexName(ec, givenName, AMR_DOUBLE_METAPHONE_NAME));
    final SearchRequest request = newSearchRequest("dc=test,dc=com", SearchScope.SUBORDINATES, "(givenName~=Aaccf)")
        .addAttribute(ATTR_DEBUG_SEARCH_INDEX);
@@ -1221,8 +1260,7 @@
    assertThat(debugString).contains("not-indexed");
    resultCode = TestCaseUtils.applyModifications(true,
        "dn: ds-cfg-attribute=givenName,cn=Index," +
            "ds-cfg-backend-id=indexRoot,cn=Backends,cn=config",
        "dn: ds-cfg-attribute=givenName,cn=Index, ds-cfg-backend-id=indexRoot,cn=Backends,cn=config",
        "changetype: modify",
        "replace: ds-cfg-index-type",
        "ds-cfg-index-type: equality",
@@ -1232,18 +1270,15 @@
    assertEquals(resultCode, 0);
    assertNotNull(attributeIndex.getEqualityIndex());
    assertNotNull(attributeIndex.getPresenceIndex());
    assertNotNull(attributeIndex.getSubstringIndex());
    assertNotNull(attributeIndex.getOrderingIndex());
    assertNull(attributeIndex.getApproximateIndex());
    databases = new ArrayList<DatabaseContainer>();
    ec.listDatabases(databases);
    assertTrue(findContainer(databases, "givenname.equality"));
    assertTrue(findContainer(databases, "givenname.presence"));
    assertTrue(findContainer(databases, "givenname.substring"));
    assertTrue(findContainer(databases, "givenname.ordering"));
    assertFalse(findContainer(databases, "givenname.approximate"));
    containerNames = getContainerNames(ec);
    assertThat(containerNames).contains(
        indexName(ec, givenName, EMR_CASE_IGNORE_NAME),
        indexName(ec, givenName, "presence"),
        indexName(ec, givenName, substringIndexId()));
    // Ordering is also handled by equality since OPENDJ-1864
    assertThat(containerNames).doesNotContain(
        indexName(ec, givenName, OMR_CASE_IGNORE_NAME),
        indexName(ec, givenName, AMR_DOUBLE_METAPHONE_NAME));
    // Delete the entries attribute index.
    resultCode = TestCaseUtils.applyModifications(true,
@@ -1253,8 +1288,10 @@
    assertEquals(resultCode, 0);
    AttributeType givennameAttr = DirectoryServer.getAttributeType("givenname");
    assertNull(ec.getAttributeIndex(givennameAttr));
    databases = new ArrayList<DatabaseContainer>();
    List<DatabaseContainer> databases = new ArrayList<>();
    ec.listDatabases(databases);
    for(DatabaseContainer dc : databases)
    {
@@ -1277,13 +1314,16 @@
    assertEquals(resultCode, 0);
    assertNotNull(ec.getAttributeIndex(givennameAttr));
    databases = new ArrayList<DatabaseContainer>();
    ec.listDatabases(databases);
    assertTrue(findContainer(databases, "givenname.equality"));
    assertTrue(findContainer(databases, "givenname.presence"));
    assertTrue(findContainer(databases, "givenname.substring"));
    assertTrue(findContainer(databases, "givenname.ordering"));
    assertFalse(findContainer(databases, "givenname.approximate"));
    containerNames = getContainerNames(ec);
    assertThat(containerNames).contains(
        indexName(ec, givenName, EMR_CASE_IGNORE_NAME),
        indexName(ec, givenName, "presence"),
        indexName(ec, givenName, substringIndexId()));
    // Equality and Ordering indexes share the same database since OPENDJ-1864
    assertThat(containerNames).doesNotContain(
        indexName(ec, givenName, OMR_CASE_IGNORE_NAME),
        indexName(ec, givenName, AMR_DOUBLE_METAPHONE_NAME));
    // Make sure changing the index entry limit on an index where the limit
    // is already exceeded causes warnings.
@@ -1307,16 +1347,31 @@
    assertEquals(resultCode, 0);
  }
  private static boolean findContainer(List<DatabaseContainer> databases, String lowercaseName)
  private static Collection<String> getContainerNames(EntryContainer container)
  {
    final List<DatabaseContainer> databases = new ArrayList<>();
    container.listDatabases(databases);
    final Collection<String> names = new ArrayList<>(databases.size());
    for (DatabaseContainer dc : databases)
    {
      if (dc.getName().toLowerCase().contains(lowercaseName))
      {
        return true;
      }
      names.add(dc.getName().toLowerCase());
    }
    return false;
    return names;
  }
  private static Collection<String> getIndexNames(AttributeIndex index)
  {
    final Collection<String> names = new ArrayList<>();
    for (Index idx : index.getAllIndexes())
    {
      names.add(idx.getName().toLowerCase());
    }
    return names;
  }
  private static String indexName(EntryContainer entryContainer, AttributeType attrType, String indexID)
  {
    return entryContainer.getDatabasePrefix() + "_" + attrType.getPrimaryName().toLowerCase() + "." + indexID.toLowerCase();
  }
  @Test(dependsOnMethods = {"testDeleteEntry", "testSearchScope",
@@ -1519,5 +1574,8 @@
    }
  }
  private static String substringIndexId() {
    return SMR_CASE_IGNORE_NAME + ":" + getOptions().substringKeySize();
  }
}
opendj-sdk/opendj-server-legacy/src/test/java/org/opends/server/backends/jeb/TestVerifyJob.java
@@ -26,6 +26,7 @@
 */
package org.opends.server.backends.jeb;
import static org.opends.server.schema.SchemaConstants.*;
import static org.opends.server.util.ServerConstants.*;
import static org.testng.Assert.*;
@@ -54,6 +55,12 @@
@SuppressWarnings("javadoc")
public class TestVerifyJob extends JebTestCase
{
  private static final String EQUALITY_CASE_IGNORE = EMR_CASE_IGNORE_NAME;
  private static final String EQUALITY_TELEPHONE = EMR_TELEPHONE_NAME;
  private static final String ORDERING_CASE_IGNORE = OMR_CASE_IGNORE_NAME;
  private static final String SUBSTRING_CASE_IGNORE_IA5 = SMR_CASE_IGNORE_IA5_NAME;
  /** Root suffix for verify backend. */
  private static String suffix="dc=verify,dc=jeb";
  private static  String vBranch="ou=verify tests," + suffix;
@@ -344,7 +351,7 @@
    try
    {
      AttributeType attributeType = DirectoryServer.getAttributeType(phoneType);
      Index index = eContainer.getAttributeIndex(attributeType).getEqualityIndex();
      Index index = eContainer.getAttributeIndex(attributeType).getIndex(EQUALITY_TELEPHONE);
      //Add entry with bad JEB format Version
      addID2EntryReturnKey(junkDN, 4, true);
      //Add phone number with various bad id list entryIDs
@@ -636,10 +643,12 @@
          DirectoryServer.getAttributeType(mailType);
      //Get db handles to each index.
      AttributeIndex attributeIndex = eContainer.getAttributeIndex(attributeType);
      Index eqIndex = attributeIndex.getEqualityIndex();
      Index presIndex = attributeIndex.getPresenceIndex();
      Index subIndex = attributeIndex.getSubstringIndex();
      Index ordIndex = attributeIndex.getOrderingIndex();
      Index eqIndex = attributeIndex.getIndex(EQUALITY_CASE_IGNORE);
      Index presIndex = attributeIndex.getIndex("presence");
      Index subIndex = attributeIndex.getIndex(SUBSTRING_CASE_IGNORE_IA5 + ":6");
      // Ordering is processed by equality since OPENDJ-1864
      assertNull(attributeIndex.getIndex(ORDERING_CASE_IGNORE));
      //Add invalid idlist ids to both equality and ordering indexes.
      DatabaseEntry key=
           new DatabaseEntry(StaticUtils.getBytes("user.0@example.com"));
@@ -650,15 +659,11 @@
      DatabaseEntry data= new DatabaseEntry(dataBytes);
      OperationStatus status = eqIndex.put(txn, key, data);
      assertEquals(status, OperationStatus.SUCCESS);
      status = ordIndex.put(txn, key, data);
      assertEquals(status, OperationStatus.SUCCESS);
      //Add null idlist to both equality and ordering indexes.
      //Add null idlist to equality index.
      key = new DatabaseEntry(StaticUtils.getBytes("user.1@example.com"));
      data= new DatabaseEntry(new EntryIDSet().toDatabase());
      status = eqIndex.put(txn, key, data);
      assertEquals(status, OperationStatus.SUCCESS);
      status = ordIndex.put(txn, key, data);
      assertEquals(status, OperationStatus.SUCCESS);
      //Add invalid idlist ids to presence index.
      key = new DatabaseEntry(StaticUtils.getBytes("+"));
      data = new DatabaseEntry(dataBytes);
@@ -669,7 +674,7 @@
      data = new DatabaseEntry(dataBytes);
      status = subIndex.put(txn, key, data);
      assertEquals(status, OperationStatus.SUCCESS);
      performBECompleteVerify(mailType, 6);
      performBECompleteVerify(mailType, 5);
    }
    finally
    {