From 2f28c948c02c1b3bb7961565e1d64a1684bc018c Mon Sep 17 00:00:00 2001
From: sin <sin@localhost>
Date: Sat, 31 Jan 2009 07:06:11 +0000
Subject: [PATCH] Integrating the changes related to Collation indexing support

---
 opendj-sdk/opends/src/server/org/opends/server/api/ExtensibleMatchingRule.java                         |   34 
 opendj-sdk/opends/src/server/org/opends/server/admin/PropertyDefinitionUsageBuilder.java               |   13 
 opendj-sdk/opends/src/server/org/opends/server/core/DirectoryServer.java                               |   38 
 opendj-sdk/opends/src/server/org/opends/server/types/Schema.java                                       |  199 ++
 opendj-sdk/opends/src/server/org/opends/server/backends/jeb/IndexFilter.java                           |   33 
 opendj-sdk/opends/resource/admin/property-types.xsl                                                    |    3 
 opendj-sdk/opends/resource/admin/property-types/extensible-matching-rule-type.xsl                      |   42 
 opendj-sdk/opends/src/server/org/opends/server/backends/jeb/AttributeIndex.java                        |  711 ++++++++++
 opendj-sdk/opends/src/server/org/opends/server/admin/ExtensibleMatchingRuleTypePropertyDefinition.java |  185 ++
 opendj-sdk/opends/src/server/org/opends/server/admin/PropertyValueVisitor.java                         |   22 
 opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/LocalDBIndexConfiguration.xml             |   41 
 opendj-sdk/opends/src/server/org/opends/server/backends/jeb/IndexQuery.java                            |  209 +++
 opendj-sdk/opends/src/server/org/opends/server/types/IndexConfig.java                                  |   41 
 opendj-sdk/opends/src/server/org/opends/server/backends/jeb/BackendImpl.java                           |   24 
 opendj-sdk/opends/src/server/org/opends/server/api/IndexQueryFactory.java                              |  118 +
 opendj-sdk/opends/resource/admin/admin.xsd                                                             |    8 
 opendj-sdk/opends/src/server/org/opends/server/schema/CollationMatchingRuleFactory.java                | 1802 ++++++++++++++-----------
 opendj-sdk/opends/src/server/org/opends/server/backends/jeb/JEExtensibleIndexer.java                   |  221 +++
 opendj-sdk/opends/src/server/org/opends/server/backends/jeb/IndexQueryFactoryImpl.java                 |  152 ++
 opendj-sdk/opends/src/server/org/opends/server/backends/jeb/importLDIF/WorkThread.java                 |   31 
 opendj-sdk/opends/src/server/org/opends/server/util/ServerConstants.java                               |   18 
 opendj-sdk/opends/src/server/org/opends/server/api/ExtensibleIndexer.java                              |   92 +
 opendj-sdk/opends/resource/schema/02-config.ldif                                                       |    7 
 opendj-sdk/opends/src/server/org/opends/server/admin/PropertyDefinitionVisitor.java                    |   19 
 24 files changed, 3,227 insertions(+), 836 deletions(-)

diff --git a/opendj-sdk/opends/resource/admin/admin.xsd b/opendj-sdk/opends/resource/admin/admin.xsd
index 121c9fd..ace9c9c 100644
--- a/opendj-sdk/opends/resource/admin/admin.xsd
+++ b/opendj-sdk/opends/resource/admin/admin.xsd
@@ -1330,6 +1330,14 @@
         </xsd:annotation>
         <xsd:complexType />
       </xsd:element>
+      <xsd:element name="extensible-matching-rule-type">
+       <xsd:annotation>
+          <xsd:documentation>
+            Used for properties which contain Extensible matching rule type names.
+          </xsd:documentation>
+        </xsd:annotation>
+        <xsd:complexType />
+      </xsd:element>
       <xsd:element name="boolean">
         <xsd:annotation>
           <xsd:documentation>
diff --git a/opendj-sdk/opends/resource/admin/property-types.xsl b/opendj-sdk/opends/resource/admin/property-types.xsl
index 0c2f5f5..b32a657 100644
--- a/opendj-sdk/opends/resource/admin/property-types.xsl
+++ b/opendj-sdk/opends/resource/admin/property-types.xsl
@@ -22,7 +22,7 @@
   ! CDDL HEADER END
   !
   !
-  !      Copyright 2008 Sun Microsystems, Inc.
+  !      Copyright 2008-2009 Sun Microsystems, Inc.
   ! -->
 <xsl:stylesheet version="1.0" xmlns:adm="http://www.opends.org/admin"
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
@@ -54,6 +54,7 @@
   <xsl:include href="property-types/password.xsl" />
   <xsl:include href="property-types/size.xsl" />
   <xsl:include href="property-types/string.xsl" />
+  <xsl:include href="property-types/extensible-matching-rule-type.xsl" />
   <!--
     
     
diff --git a/opendj-sdk/opends/resource/admin/property-types/extensible-matching-rule-type.xsl b/opendj-sdk/opends/resource/admin/property-types/extensible-matching-rule-type.xsl
new file mode 100644
index 0000000..60cf61c
--- /dev/null
+++ b/opendj-sdk/opends/resource/admin/property-types/extensible-matching-rule-type.xsl
@@ -0,0 +1,42 @@
+<!--
+  ! CDDL HEADER START
+  !
+  ! The contents of this file are subject to the terms of the
+  ! Common Development and Distribution License, Version 1.0 only
+  ! (the "License").  You may not use this file except in compliance
+  ! with the License.
+  !
+  ! You can obtain a copy of the license at
+  ! trunk/opends/resource/legal-notices/OpenDS.LICENSE
+  ! or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+  ! See the License for the specific language governing permissions
+  ! and limitations under the License.
+  !
+  ! When distributing Covered Code, include this CDDL HEADER in each
+  ! file and include the License file at
+  ! trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+  ! add the following below this CDDL HEADER, with the fields enclosed
+  ! by brackets "[]" replaced with your own identifying information:
+  !      Portions Copyright [yyyy] [name of copyright owner]
+  !
+  ! CDDL HEADER END
+  !
+  !
+  !      Copyright 2009 Sun Microsystems, Inc.
+  ! -->
+<xsl:stylesheet version="1.0" xmlns:adm="http://www.opends.org/admin"
+  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+  <!-- 
+    Templates for processing extensible matching rule type properties.
+  -->
+  <xsl:template match="adm:extensible-matching-rule-type" mode="java-value-imports">
+    <import>org.opends.server.api.ExtensibleMatchingRule</import>
+  </xsl:template>
+  <xsl:template match="adm:extensible-matching-rule-type" mode="java-value-type">
+    <xsl:value-of select="'ExtensibleMatchingRule'" />
+  </xsl:template>
+  <xsl:template match="adm:extensible-matching-rule-type"
+    mode="java-definition-type">
+    <xsl:value-of select="'ExtensibleMatchingRuleTypePropertyDefinition'" />
+  </xsl:template>
+</xsl:stylesheet>
diff --git a/opendj-sdk/opends/resource/schema/02-config.ldif b/opendj-sdk/opends/resource/schema/02-config.ldif
index bb2b9f1..93aafcb 100644
--- a/opendj-sdk/opends/resource/schema/02-config.ldif
+++ b/opendj-sdk/opends/resource/schema/02-config.ldif
@@ -2387,6 +2387,10 @@
   SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
   SINGLE-VALUE
   X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.518
+  NAME 'ds-cfg-index-extensible-matching-rule'
+  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+  X-ORIGIN 'OpenDS Directory Server' )
 objectClasses: ( 1.3.6.1.4.1.26027.1.2.1
   NAME 'ds-cfg-access-control-handler'
   SUP top
@@ -2465,7 +2469,8 @@
   MUST ( ds-cfg-attribute $
          ds-cfg-index-type )
   MAY ( ds-cfg-index-entry-limit $
-        ds-cfg-substring-length )
+              ds-cfg-substring-length $
+              ds-cfg-index-extensible-matching-rule )
   X-ORIGIN 'OpenDS Directory Server' )
 objectClasses: ( 1.3.6.1.4.1.26027.1.2.8
   NAME 'ds-cfg-schema-backend'
diff --git a/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/LocalDBIndexConfiguration.xml b/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/LocalDBIndexConfiguration.xml
index 187669e..cfb2ad9 100644
--- a/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/LocalDBIndexConfiguration.xml
+++ b/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/LocalDBIndexConfiguration.xml
@@ -23,7 +23,7 @@
   ! CDDL HEADER END
   !
   !
-  !      Copyright 2007-2008 Sun Microsystems, Inc.
+  !      Copyright 2007-2009 Sun Microsystems, Inc.
   ! -->
 <adm:managed-object name="local-db-index" plural-name="local-db-indexes"
   package="org.opends.server.admin.std"
@@ -110,7 +110,7 @@
     <adm:requires-admin-action>
       <adm:other>
         <adm:synopsis>
-          If any new index types are added for an attribute, and 
+          If any new index types are added for an attribute, and
           values for that attribute already exist in the
           database, the index must be rebuilt before it
           will be accurate.
@@ -150,6 +150,12 @@
             of searches using approximate matching search filters.
           </adm:synopsis>
         </adm:value>
+        <adm:value name="extensible">
+         <adm:synopsis>
+            This index type is used to improve the efficiency
+            of searches using extensible matching search filters.
+         </adm:synopsis>
+      </adm:value>
       </adm:enumeration>
     </adm:syntax>
     <adm:profile name="ldap">
@@ -184,4 +190,35 @@
       </ldap:attribute>
     </adm:profile>
   </adm:property>
+  <adm:property name="index-extensible-matching-rule" multi-valued="true">
+    <adm:synopsis>
+      The extensible matching rule in an extensible index.
+    </adm:synopsis>
+    <adm:description>
+      An extensible matching rule must be specified using either LOCALE or OID of the matching rule.
+    </adm:description>
+    <adm:requires-admin-action>
+      <adm:other>
+        <adm:synopsis>
+          The index must be rebuilt before it will reflect the
+          new value.
+        </adm:synopsis>
+      </adm:other>
+    </adm:requires-admin-action>
+    <adm:default-behavior>
+      <adm:alias>
+        <adm:synopsis>
+          No extensible matching rules will be indexed.
+        </adm:synopsis>
+      </adm:alias>
+    </adm:default-behavior>
+    <adm:syntax>
+      <adm:extensible-matching-rule-type/>
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:name>ds-cfg-index-extensible-matching-rule</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
 </adm:managed-object>
diff --git a/opendj-sdk/opends/src/server/org/opends/server/admin/ExtensibleMatchingRuleTypePropertyDefinition.java b/opendj-sdk/opends/src/server/org/opends/server/admin/ExtensibleMatchingRuleTypePropertyDefinition.java
new file mode 100644
index 0000000..25dbde0
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/admin/ExtensibleMatchingRuleTypePropertyDefinition.java
@@ -0,0 +1,185 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Copyright 2009 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+import java.util.EnumSet;
+import org.opends.server.api.ExtensibleMatchingRule;
+
+import org.opends.server.core.DirectoryServer;
+import static org.opends.server.util.Validator.ensureNotNull;
+
+/**
+ * Extensible Matching Rule Type Propertiy Definition.
+ */
+public final class ExtensibleMatchingRuleTypePropertyDefinition
+        extends PropertyDefinition<ExtensibleMatchingRule>
+{
+
+  /**
+   * An interface for incrementally constructing attribute type
+   * property definitions.
+   */
+  public static class Builder extends
+      AbstractBuilder<ExtensibleMatchingRule,
+                                ExtensibleMatchingRuleTypePropertyDefinition> {
+
+    // Private constructor
+    private Builder(AbstractManagedObjectDefinition<?, ?> d,
+        String propertyName) {
+      super(d, propertyName);
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected ExtensibleMatchingRuleTypePropertyDefinition buildInstance(
+        AbstractManagedObjectDefinition<?, ?> d, String propertyName,
+        EnumSet<PropertyOption> options,
+        AdministratorAction adminAction,
+        DefaultBehaviorProvider<ExtensibleMatchingRule> defaultBehavior) {
+      return new ExtensibleMatchingRuleTypePropertyDefinition(d, propertyName,
+          options, adminAction, defaultBehavior);
+    }
+  }
+
+
+  /**
+   * Create an extensible matching rule type property definition builder.
+   *
+   * @param d
+   *          The managed object definition associated with this
+   *          property definition.
+   * @param propertyName
+   *          The property name.
+   * @return Returns the new attribute type property definition
+   *         builder.
+   */
+  public static Builder createBuilder(
+      AbstractManagedObjectDefinition<?, ?> d, String propertyName) {
+    return new Builder(d, propertyName);
+  }
+
+
+
+  /**
+   * Creates a new insantce of this class.
+   */
+  private ExtensibleMatchingRuleTypePropertyDefinition(
+      AbstractManagedObjectDefinition<?, ?> d, String propertyName,
+      EnumSet<PropertyOption> options,
+      AdministratorAction adminAction,
+      DefaultBehaviorProvider<ExtensibleMatchingRule> defaultBehavior) {
+    super(d, ExtensibleMatchingRule.class, propertyName, options,
+        adminAction, defaultBehavior);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public <R, P> R accept(PropertyDefinitionVisitor<R, P> v, P p) {
+    return v.visitExtensibleMatchingRuleType(this, p);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public <R, P> R accept(PropertyValueVisitor<R, P> v,
+      ExtensibleMatchingRule value, P p) {
+    return v.visitExtensibleMatchingRuleType(this, value, p);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public int compare(ExtensibleMatchingRule o1, ExtensibleMatchingRule o2) {
+    return o1.getOID().compareToIgnoreCase(o2.getOID());
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public ExtensibleMatchingRule decodeValue(String value)
+      throws IllegalPropertyValueStringException {
+    ensureNotNull(value);
+
+    String name = value.trim().toLowerCase();
+    //Check if the name is a valid Matching rule OID or a Locale value.
+    ExtensibleMatchingRule rule =
+            DirectoryServer.getExtensibleMatchingRule(name);
+
+    if (rule == null) {
+      throw new IllegalPropertyValueStringException(this, value);
+    } else {
+      try {
+        validateValue(rule);
+        return rule;
+      } catch (IllegalPropertyValueException e) {
+        throw new IllegalPropertyValueStringException(this, value);
+      }
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String encodeValue(ExtensibleMatchingRule value)
+      throws IllegalPropertyValueException {
+    return value.getNameOrOID();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void validateValue(ExtensibleMatchingRule value)
+      throws IllegalPropertyValueException {
+    ensureNotNull(value);
+
+    // No implementation required.
+  }
+}
\ No newline at end of file
diff --git a/opendj-sdk/opends/src/server/org/opends/server/admin/PropertyDefinitionUsageBuilder.java b/opendj-sdk/opends/src/server/org/opends/server/admin/PropertyDefinitionUsageBuilder.java
index 6af6cff..ee38963 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/admin/PropertyDefinitionUsageBuilder.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/admin/PropertyDefinitionUsageBuilder.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2008 Sun Microsystems, Inc.
+ *      Copyright 2008-2009 Sun Microsystems, Inc.
  */
 package org.opends.server.admin;
 import org.opends.messages.Message;
@@ -338,6 +338,17 @@
      * {@inheritDoc}
      */
     @Override
+     public Message visitExtensibleMatchingRuleType(
+            ExtensibleMatchingRuleTypePropertyDefinition d, Void p) {
+        return Message.raw("LOCALE | OID");
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
     public <T> Message visitUnknown(PropertyDefinition<T> d, Void p)
         throws UnknownPropertyDefinitionException {
       return Message.raw("?");
diff --git a/opendj-sdk/opends/src/server/org/opends/server/admin/PropertyDefinitionVisitor.java b/opendj-sdk/opends/src/server/org/opends/server/admin/PropertyDefinitionVisitor.java
index fa728de..67ab9a9 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/admin/PropertyDefinitionVisitor.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/admin/PropertyDefinitionVisitor.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2008 Sun Microsystems, Inc.
+ *      Copyright 2008-2009 Sun Microsystems, Inc.
  */
 
 package org.opends.server.admin;
@@ -268,6 +268,23 @@
 
 
 
+ /**
+   * Visit an extensible matching rule property definition.
+   *
+   * @param pd
+   *          The string property definition to visit.
+   * @param p
+   *          A visitor specified parameter.
+   * @return Returns a visitor specified result.
+   */
+  public R visitExtensibleMatchingRuleType(
+          ExtensibleMatchingRuleTypePropertyDefinition pd, P p) {
+    return visitUnknown(pd, p);
+  }
+
+
+
+
   /**
    * Visit an unknown type of property definition. Implementations of
    * this method can provide default behavior for unknown property
diff --git a/opendj-sdk/opends/src/server/org/opends/server/admin/PropertyValueVisitor.java b/opendj-sdk/opends/src/server/org/opends/server/admin/PropertyValueVisitor.java
index 9293048..c7b42be 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/admin/PropertyValueVisitor.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/admin/PropertyValueVisitor.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2008 Sun Microsystems, Inc.
+ *      Copyright 2008-2009 Sun Microsystems, Inc.
  */
 
 package org.opends.server.admin;
@@ -35,6 +35,7 @@
 import org.opends.server.types.AttributeType;
 import org.opends.server.types.DN;
 import org.opends.server.authorization.dseecompat.Aci;
+import org.opends.server.api.ExtensibleMatchingRule;
 
 
 /**
@@ -308,6 +309,25 @@
 
 
   /**
+   * Visit an extensible matching rule type.
+   *
+   * @param pd
+   *          The extensible matching rule type property definition.
+   * @param v
+   *          The property value to visit.
+   * @param p
+   *          A visitor specified parameter.
+   * @return Returns a visitor specified result.
+   */
+  public R visitExtensibleMatchingRuleType(
+          ExtensibleMatchingRuleTypePropertyDefinition pd,
+          ExtensibleMatchingRule v, P p) {
+    return visitUnknown(pd, v, p);
+  }
+
+
+
+  /**
    * Visit an unknown type of property value. Implementations of this
    * method can provide default behavior for unknown types of
    * property.
diff --git a/opendj-sdk/opends/src/server/org/opends/server/api/ExtensibleIndexer.java b/opendj-sdk/opends/src/server/org/opends/server/api/ExtensibleIndexer.java
new file mode 100644
index 0000000..47edcc2
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/api/ExtensibleIndexer.java
@@ -0,0 +1,92 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Copyright 2009 Sun Microsystems, Inc.
+ */
+package org.opends.server.api;
+
+import java.util.Map;
+import java.util.Set;
+import org.opends.server.types.AttributeValue;
+
+/**
+ * This class is  registered with a Backend  and it provides call-
+ * backs for indexing attribute values. An index implementation will
+ * use this interface to create the keys for an attribute value.
+ */
+@org.opends.server.types.PublicAPI(
+     stability=org.opends.server.types.StabilityLevel.VOLATILE,
+     mayInstantiate=false,
+     mayExtend=true,
+     mayInvoke=false)
+public abstract class ExtensibleIndexer
+{
+  /**
+   * Returns the index name preferred by this indexer. This name
+   * appended with the identifier returned from
+   * {@link #getExtensibleIndexID()} will be used as the index
+   * database name.
+   * @return  The name of the index for this indexer.
+   */
+  public abstract String getPreferredIndexName();
+
+
+  /**
+   * Returns an index identifier associated with this indexer. An
+   * identifier should be selected based on the matching rule type.
+   * A unique identifier will map to  a unique index database in
+   * the backend implementation. If multiple matching rules
+   * need to share the index database, the corresponding indexers
+   * should always use the same identifier.
+   * @return index ID A String containing the ID associated with
+   *                          this indexer.
+   */
+  public abstract String getExtensibleIndexID();
+
+
+  /**
+   * Generates the set of index keys for an attribute.
+   * @param value The attribute value  for which  keys are required.
+   * @param keys The set into which the generated keys will be
+   *                          inserted.
+   */
+  public abstract void getKeys(AttributeValue value,
+                                                  Set<byte[]> keys);
+
+
+  /**
+   * Generates a map of index keys and a boolean flag indicating
+   * whether the corresponding key will be inserted or deleted.
+   * @param value The attribute for which keys are required.
+   * @param modifiedKeys A map containing the keys and a boolean.
+   *              Keys corresponding to the boolean value <code>true
+   *              </code> should be inserted and <code>false</code>
+   *              should be deleted.
+   * @param insert <code>true</code> if generated keys should
+   *            be inserted or <code>false</code> otherwise.
+   */
+  public abstract void getKeys(AttributeValue value,
+                                  Map<byte[], Boolean> modifiedKeys,
+                                  Boolean insert);
+}
\ No newline at end of file
diff --git a/opendj-sdk/opends/src/server/org/opends/server/api/ExtensibleMatchingRule.java b/opendj-sdk/opends/src/server/org/opends/server/api/ExtensibleMatchingRule.java
index 6b25a58..798da0d 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/api/ExtensibleMatchingRule.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/api/ExtensibleMatchingRule.java
@@ -22,10 +22,14 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2008 Sun Microsystems, Inc.
+ *      Copyright 2008-2009 Sun Microsystems, Inc.
  */
 package org.opends.server.api;
 
+import java.util.Collection;
+import org.opends.server.types.ByteString;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.IndexConfig;
 
 
 /**
@@ -40,7 +44,33 @@
      mayInvoke=false)
 public abstract class ExtensibleMatchingRule extends MatchingRule
 {
+  /**
+  * Returns a collection of extensible indexers associated with this
+  * matching rule.
+  * @param config The index configuration to be used by this
+  *                      matching rule.
+  * @return ExtensibleIndexer associated with this matching rule.
+  */
+  public  abstract Collection<ExtensibleIndexer> getIndexers(
+          IndexConfig config);
 
-  //TODO: Index Implementation.
 
+
+
+  /**
+   * Queries the index using factory of type T and returns
+   * a query of type T for the provided assertion value.
+   * @param  <T>  The type of IndexQueryFactory.
+   * @param  assertionValue  An assertion value which needs to be
+   *                                               queried.
+   * @param factory  An IndexQueryFactory which will be used for
+   *                                creating  queries.
+   * @return T  The generated index query.
+   * @throws DirectoryException  If an  error occurs while generating
+   *                the query.
+   */
+  public  abstract <T> T createIndexQuery(
+                   ByteString assertionValue,
+                   IndexQueryFactory<T> factory)
+                                          throws DirectoryException;
 }
\ No newline at end of file
diff --git a/opendj-sdk/opends/src/server/org/opends/server/api/IndexQueryFactory.java b/opendj-sdk/opends/src/server/org/opends/server/api/IndexQueryFactory.java
new file mode 100644
index 0000000..a4f173d
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/api/IndexQueryFactory.java
@@ -0,0 +1,118 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Copyright 2009 Sun Microsystems, Inc.
+ */
+package org.opends.server.api;
+
+import java.util.Collection;
+
+/**
+ * This class acts as a factory for creating index queries. This
+ * interface is implemented by the underlying backend implementation
+ * and passed to extensible matching rules so that they can construct
+ * arbitrarily complex index queries.
+ *
+ * @param  <T>  The type of Results  returned by the factory.
+ */
+@org.opends.server.types.PublicAPI(
+     stability=org.opends.server.types.StabilityLevel.VOLATILE,
+     mayInstantiate=false,
+     mayExtend=true,
+     mayInvoke=false)
+public interface IndexQueryFactory<T>
+{
+
+  /**
+   * Returns a query requesting an index record matching the
+   * provided key.
+   * @param indexID An identifier of the index type.
+   * @param key A byte array containing the key.
+   * @return A query requesting the index record matching the key.
+   */
+  T createExactMatchQuery(String indexID,byte[] key);
+
+
+
+  /**
+   * Returns a query requesting all  index records. A backend
+   * implementation may choose to return all or no records as
+   * part of the optimization.
+   * @return A query requesting all index records.
+   */
+  T createMatchAllQuery();
+
+
+
+  /**
+   * Rreturns a query requesting all index records in the specified
+   * range.
+   * @param indexID An identifier of the index type.
+   * @param lower  The lower bound of the range.   A 0 length byte
+   *                      array indicates  no lower bound and the
+   *                      range will start from the smallest key.
+   * @param upper The upper bound of the range. A 0 length byte array
+   *                      indicates no upper bound and the range will
+   *                      end at  the largest key.
+   * @param lowerIncluded true if a key exactly matching the lower
+   *                       bound is included in the range, false if
+   *                       only keys strictly greater than the lower
+   *                       bound are included.This value is ignored if
+   *                       the lower bound is not specified.
+   * @param upperIncluded true if a key exactly matching the upper
+   *                      bound is included in the range, false if
+   *                      only keys strictly less than the upper
+   *                      bound are included. This value is ignored if
+   *                      the upper bound is not specified.
+   * @return A query requesting all index records in the specified
+   * range.
+   */
+  T createRangeMatchQuery(
+                              String indexID,
+                              byte[]  lower,
+                              byte[] upper,
+                              boolean lowerIncluded,
+                              boolean upperIncluded);
+
+
+
+  /**
+   * Returns a query requesting  intersection from a Collection of
+   * sub-queries.
+   *@param  subquery  A Collection of sub-queries.
+   *@return A query requesting intersection of  the records.
+   */
+  T createIntersectionQuery(Collection<T> subquery);
+
+
+
+
+  /**
+   * Returns a query requesting union from a Collection of
+   * sub-queries.
+   * @param  subquery  A Collection of sub-queries.
+   * @return A query requesting union of the records.
+   */
+  T createUnionQuery(Collection<T> subquery);
+}
\ No newline at end of file
diff --git a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/AttributeIndex.java b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/AttributeIndex.java
index 6b90b13..75fe149 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/AttributeIndex.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/AttributeIndex.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2006-2008 Sun Microsystems, Inc.
+ *      Copyright 2006-2009 Sun Microsystems, Inc.
  */
 package org.opends.server.backends.jeb;
 import org.opends.messages.Message;
@@ -42,8 +42,12 @@
 import org.opends.server.admin.std.server.LocalDBIndexCfg;
 import org.opends.server.admin.std.meta.LocalDBIndexCfgDefn;
 import org.opends.server.admin.server.ConfigurationChangeListener;
+import org.opends.server.api.ExtensibleIndexer;
+import org.opends.server.api.ExtensibleMatchingRule;
+import org.opends.server.api.IndexQueryFactory;
 import org.opends.server.config.ConfigException;
 import static org.opends.messages.JebMessages.*;
+import static org.opends.server.util.ServerConstants.*;
 
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.util.StaticUtils;
@@ -113,6 +117,12 @@
    */
   Index approximateIndex = null;
 
+  /**
+   * The ExtensibleMatchingRuleIndex instance for ExtensibleMatchingRule
+   * indexes.
+   */
+  private  ExtensibleMatchingRuleIndex extensibleIndexes = null;
+
   private State state;
 
   private int cursorEntryLimit = 100000;
@@ -238,7 +248,61 @@
                                         env,
                                         entryContainer);
     }
-
+    if (indexConfig.getIndexType().contains(
+        LocalDBIndexCfgDefn.IndexType.EXTENSIBLE))
+    {
+      Set<ExtensibleMatchingRule> extensibleRules =
+              indexConfig.getIndexExtensibleMatchingRule();
+      if(extensibleRules == null || extensibleRules.size() == 0)
+      {
+        Message message = ERR_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE.get(
+            String.valueOf(attrType), "extensible");
+        throw new ConfigException(message);
+      }
+      extensibleIndexes = new ExtensibleMatchingRuleIndex();
+      //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.
+      IndexConfig config = new JEIndexConfig(indexConfig.getSubstringLength());
+      for(ExtensibleMatchingRule rule:extensibleRules)
+      {
+        Map<String,Index> indexMap = new HashMap<String,Index>();
+        for(ExtensibleIndexer indexer : rule.getIndexers(config))
+        {
+          String indexerId = indexer.getExtensibleIndexID();
+          String indexID =
+                  attrType.getNameOrOID()  + "."
+                    + indexer.getPreferredIndexName()
+                    + "." + indexerId;
+          if(!extensibleIndexes.isIndexPresent(indexID))
+          {
+            //There is no index available for this index id. Create a new index.
+            Indexer extensibleIndexer =
+                    new JEExtensibleIndexer(attrType,
+                                               rule,
+                                               indexer);
+            String indexName = entryContainer.getDatabasePrefix() + "_"
+                                                  + indexID;
+            Index extensibleIndex = new Index(indexName,
+                                      extensibleIndexer,
+                                      state,
+                                      indexEntryLimit,
+                                      cursorEntryLimit,
+                                      false,
+                                      env,
+                                      entryContainer);
+              extensibleIndexes.addIndex(extensibleIndex,indexID);
+          }
+        extensibleIndexes.addRule(indexID, rule);
+        indexMap.put(indexer.getExtensibleIndexID(),
+                extensibleIndexes.getIndex(indexID));
+      }
+      IndexQueryFactory<IndexQuery> factory =
+              new IndexQueryFactoryImpl(indexMap);
+      extensibleIndexes.addQueryFactory(rule, factory);
+      }
+    }
     this.indexConfig.addChangeListener(this);
   }
 
@@ -274,6 +338,14 @@
     {
       approximateIndex.open();
     }
+
+    if(extensibleIndexes!=null)
+    {
+      for(Index extensibleIndex:extensibleIndexes.getIndexes())
+      {
+        extensibleIndex.open();
+      }
+    }
   }
 
   /**
@@ -309,6 +381,14 @@
       approximateIndex.close();
     }
 
+    if(extensibleIndexes!=null)
+    {
+      for(Index extensibleIndex:extensibleIndexes.getIndexes())
+      {
+        extensibleIndex.close();
+      }
+    }
+
     indexConfig.removeChangeListener(this);
     // The entryContainer is responsible for closing the JE databases.
   }
@@ -388,6 +468,17 @@
       }
     }
 
+    if(extensibleIndexes!=null)
+    {
+      for(Index extensibleIndex:extensibleIndexes.getIndexes())
+      {
+        if(!extensibleIndex.addEntry(buffer, entryID,entry))
+        {
+          success = false;
+        }
+      }
+    }
+
     return success;
   }
 
@@ -448,6 +539,17 @@
       }
     }
 
+    if(extensibleIndexes!=null)
+    {
+      for(Index extensibleIndex:extensibleIndexes.getIndexes())
+      {
+        if(!extensibleIndex.addEntry(txn, entryID,entry))
+        {
+          success = false;
+        }
+      }
+    }
+
     return success;
   }
 
@@ -488,6 +590,14 @@
     {
       approximateIndex.removeEntry(buffer, entryID, entry);
     }
+
+    if(extensibleIndexes!=null)
+    {
+      for(Index extensibleIndex:extensibleIndexes.getIndexes())
+      {
+        extensibleIndex.removeEntry(buffer, entryID, entry);
+      }
+    }
   }
 
   /**
@@ -526,6 +636,14 @@
     {
       approximateIndex.removeEntry(txn, entryID, entry);
     }
+
+    if(extensibleIndexes!=null)
+    {
+      for(Index extensibleIndex:extensibleIndexes.getIndexes())
+      {
+        extensibleIndex.removeEntry(txn, entryID, entry);
+      }
+    }
   }
 
   /**
@@ -571,6 +689,14 @@
     {
       approximateIndex.modifyEntry(txn, entryID, oldEntry, newEntry, mods);
     }
+
+    if(extensibleIndexes!=null)
+    {
+      for(Index extensibleIndex:extensibleIndexes.getIndexes())
+      {
+        extensibleIndex.modifyEntry(txn, entryID, oldEntry, newEntry, mods);
+      }
+    }
   }
 
   /**
@@ -616,6 +742,14 @@
     {
       approximateIndex.modifyEntry(buffer, entryID, oldEntry, newEntry, mods);
     }
+
+    if(extensibleIndexes!=null)
+    {
+      for(Index extensibleIndex:extensibleIndexes.getIndexes())
+      {
+        extensibleIndex.modifyEntry(buffer, entryID, oldEntry, newEntry, mods);
+      }
+    }
   }
 
   /**
@@ -1249,6 +1383,14 @@
     {
       approximateIndex.closeCursor();
     }
+
+    if(extensibleIndexes!=null)
+    {
+      for(Index extensibleIndex:extensibleIndexes.getIndexes())
+      {
+        extensibleIndex.closeCursor();
+      }
+    }
   }
 
   /**
@@ -1287,6 +1429,14 @@
           approximateIndex.getEntryLimitExceededCount();
     }
 
+     if(extensibleIndexes!=null)
+    {
+      for(Index extensibleIndex:extensibleIndexes.getIndexes())
+      {
+        entryLimitExceededCount +=
+                extensibleIndex.getEntryLimitExceededCount();
+      }
+    }
     return entryLimitExceededCount;
   }
 
@@ -1320,6 +1470,14 @@
     {
       dbList.add(approximateIndex);
     }
+
+    if(extensibleIndexes!=null)
+    {
+      for(Index extensibleIndex:extensibleIndexes.getIndexes())
+      {
+        dbList.add(extensibleIndex);
+      }
+    }
   }
 
   /**
@@ -1384,6 +1542,18 @@
         return false;
       }
     }
+    if (cfg.getIndexType().contains(LocalDBIndexCfgDefn.IndexType.EXTENSIBLE))
+    {
+      Set<ExtensibleMatchingRule> newRules =
+              cfg.getIndexExtensibleMatchingRule();
+      if (newRules == null || newRules.size() == 0)
+      {
+        Message message = ERR_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE.get(
+                String.valueOf(attrType), "extensible");
+        unacceptableReasons.add(message);
+        return false;
+      }
+    }
 
     return true;
   }
@@ -1721,6 +1891,171 @@
         }
       }
 
+      if (cfg.getIndexType().contains(
+              LocalDBIndexCfgDefn.IndexType.EXTENSIBLE))
+      {
+        Set<ExtensibleMatchingRule> extensibleRules =
+            cfg.getIndexExtensibleMatchingRule();
+        if(extensibleIndexes == null)
+        {
+          extensibleIndexes = new ExtensibleMatchingRuleIndex();
+        }
+        IndexConfig config = new JEIndexConfig(cfg.getSubstringLength());
+        for(ExtensibleMatchingRule rule:extensibleRules)
+        {
+          Map<String,Index> indexMap = new HashMap<String,Index>();
+          for(ExtensibleIndexer indexer: rule.getIndexers(config))
+          {
+            String indexerId = indexer.getExtensibleIndexID();
+            String indexID =
+                  attrType.getNameOrOID()  + "."
+                   + indexer.getPreferredIndexName()
+                   + "." + indexerId;
+            if(!extensibleIndexes.isIndexPresent(indexID))
+            {
+              Indexer extensibleIndexer =
+                      new JEExtensibleIndexer(attrType,
+                                                 rule,
+                                                 indexer);
+              String indexName =  entryContainer.getDatabasePrefix() + "_"
+                      + indexID;
+              Index extensibleIndex = new Index(indexName,
+                                        extensibleIndexer,
+                                        state,
+                                        indexEntryLimit,
+                                        cursorEntryLimit,
+                                        false,
+                                        env,
+                                        entryContainer);
+              extensibleIndexes.addIndex(extensibleIndex,indexID);
+            }
+            else
+            {
+              Index extensibleIndex = extensibleIndexes.getIndex(indexID);
+              if(extensibleIndex.setIndexEntryLimit(indexEntryLimit))
+              {
+                adminActionRequired = true;
+                Message message =
+                      NOTE_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD.get(
+                              extensibleIndex.getName());
+                messages.add(message);
+              }
+              if(indexConfig.getSubstringLength() !=
+              cfg.getSubstringLength())
+              {
+                Indexer extensibleIndexer =
+                      new JEExtensibleIndexer(attrType,
+                                                 rule,
+                                                 indexer);
+                extensibleIndex.setIndexer(extensibleIndexer);
+              }
+            }
+            extensibleIndexes.addRule(indexID, rule);
+            indexMap.put(indexerId,extensibleIndexes.getIndex(indexID));
+          }
+          IndexQueryFactory<IndexQuery> factory =
+                  new IndexQueryFactoryImpl(indexMap);
+          extensibleIndexes.addQueryFactory(rule, factory);
+        }
+        //Some rules might have been removed from the configuration.
+        Set<ExtensibleMatchingRule> deletedRules =
+                new HashSet<ExtensibleMatchingRule>();
+        for(ExtensibleMatchingRule r:extensibleIndexes.getRules())
+        {
+          if(!extensibleRules.contains(r))
+          {
+            deletedRules.add(r);
+          }
+        }
+        if(deletedRules.size() > 0)
+        {
+          entryContainer.exclusiveLock.lock();
+          try
+          {
+            for(ExtensibleMatchingRule rule:deletedRules)
+            {
+              Set<ExtensibleMatchingRule> rules =
+                      new HashSet<ExtensibleMatchingRule>();
+              List<String> ids = new ArrayList<String>();
+              for(ExtensibleIndexer indexer: rule.getIndexers(config))
+              {
+                String id = attrType.getNameOrOID()  + "."
+                 + indexer.getPreferredIndexName()
+                 + "." + indexer.getExtensibleIndexID();
+                rules.addAll(extensibleIndexes.getRules(id));
+                ids.add(id);
+              }
+              if(rules.isEmpty())
+              {
+                //Rule has been already deleted.
+                continue;
+              }
+              //If all the rules are part of the deletedRules, delete
+              //this index.
+              if(deletedRules.containsAll(rules))
+              {
+                //it is safe to delete this index as it is not shared.
+                for(String indexID : ids)
+                {
+                  Index extensibleIndex = extensibleIndexes.getIndex(indexID);
+                  entryContainer.deleteDatabase(extensibleIndex);
+                  extensibleIndex = null;
+                  extensibleIndexes.deleteIndex(indexID);
+                  extensibleIndexes.deleteRule(indexID);
+                }
+              }
+              else
+              {
+                for(String indexID : ids)
+                {
+                  extensibleIndexes.deleteRule(rule, indexID);
+                }
+              }
+            }
+          }
+          catch(DatabaseException de)
+          {
+            messages.add(
+                  Message.raw(StaticUtils.stackTraceToSingleLineString(de)));
+            ccr = new ConfigChangeResult(
+              DirectoryServer.getServerErrorResultCode(), false, messages);
+            return ccr;
+          }
+          finally
+          {
+            entryContainer.exclusiveLock.unlock();
+          }
+        }
+      }
+      else
+      {
+        if(extensibleIndexes != null)
+        {
+          entryContainer.exclusiveLock.lock();
+          try
+          {
+            for(Index extensibleIndex:extensibleIndexes.getIndexes())
+            {
+              entryContainer.deleteDatabase(extensibleIndex);
+              extensibleIndex =  null;
+            }
+            extensibleIndexes.deleteAll();
+          }
+          catch(DatabaseException de)
+          {
+            messages.add(
+                  Message.raw(StaticUtils.stackTraceToSingleLineString(de)));
+            ccr = new ConfigChangeResult(
+              DirectoryServer.getServerErrorResultCode(), false, messages);
+            return ccr;
+          }
+          finally
+          {
+            entryContainer.exclusiveLock.unlock();
+          }
+        }
+      }
+
       indexConfig = cfg;
 
       return new ConfigChangeResult(ResultCode.SUCCESS, adminActionRequired,
@@ -1770,6 +2105,14 @@
     {
       approximateIndex.setTrusted(txn, trusted);
     }
+
+    if(extensibleIndexes!=null)
+    {
+      for(Index extensibleIndex:extensibleIndexes.getIndexes())
+      {
+        extensibleIndex.setTrusted(txn, trusted);
+      }
+    }
   }
 
   /**
@@ -1798,11 +2141,22 @@
       return false;
     }
 
-    if (approximateIndex != null && approximateIndex.isTrusted())
+    if (approximateIndex != null && !approximateIndex.isTrusted())
     {
       return false;
     }
 
+    if(extensibleIndexes!=null)
+    {
+      for(Index extensibleIndex:extensibleIndexes.getIndexes())
+      {
+        if(extensibleIndex !=null && !extensibleIndex.isTrusted())
+        {
+          return false;
+        }
+      }
+    }
+
     return true;
   }
 
@@ -1837,6 +2191,14 @@
     {
       approximateIndex.setRebuildStatus(rebuildRunning);
     }
+
+    if(extensibleIndexes!=null)
+    {
+      for(Index extensibleIndex:extensibleIndexes.getIndexes())
+      {
+        extensibleIndex.setRebuildStatus(rebuildRunning);
+      }
+    }
   }
 
   /**
@@ -1900,6 +2262,21 @@
   }
 
   /**
+   * Return the mapping of  extensible index types and indexes.
+   *
+   * @return The Map of extensible index types and indexes.
+   */
+  public Map<String,Collection<Index>> getExtensibleIndexes()
+  {
+    if(extensibleIndexes == null)
+    {
+      return Collections.emptyMap();
+    }
+    return extensibleIndexes.getIndexMap();
+  }
+
+
+  /**
    * Retrieves all the indexes used by this attribute index.
    *
    * @return A collection of all indexes in use by this attribute
@@ -1933,6 +2310,332 @@
       indexes.add(approximateIndex);
     }
 
+    if(extensibleIndexes!=null)
+    {
+      for(Index extensibleIndex:extensibleIndexes.getIndexes())
+      {
+        indexes.add(extensibleIndex);
+      }
+    }
     return indexes;
   }
-}
+
+
+  /**
+   * Retrieve the entry IDs that might match an extensible filter.
+   *
+   * @param extensibleFilter The extensible filter.
+   * @param debugBuffer If not null, a diagnostic string will be written
+   *                     which will help determine how the indexes contributed
+   *                     to this search.
+   * @return The candidate entry IDs that might contain the filter
+   *         assertion value.
+   */
+  public EntryIDSet evaluateExtensibleFilter(SearchFilter extensibleFilter,
+                                              StringBuilder debugBuffer)
+  {
+    //Get the Matching Rule OID of the filter.
+    String nOID  = extensibleFilter.getMatchingRuleID();
+    ExtensibleMatchingRule rule =
+            DirectoryServer.getExtensibleMatchingRule(nOID);
+    IndexQueryFactory<IndexQuery> factory = null;
+    if(extensibleIndexes == null
+            || (factory = extensibleIndexes.getQueryFactory(rule))==null)
+    {
+      // There is no index on this matching rule.
+      return IndexQuery.createNullIndexQuery().evaluate();
+    }
+
+    try
+    {
+
+      if(debugBuffer != null)
+      {
+        debugBuffer.append("[INDEX:");
+        IndexConfig config =
+                new JEIndexConfig(indexConfig.getSubstringLength());
+        for(ExtensibleIndexer indexer :  rule.getIndexers(config))
+        {
+          String indexerID = indexer.getExtensibleIndexID();
+          String indexName = indexer.getPreferredIndexName();
+          String indexID = " "
+                         + extensibleFilter.getAttributeType().getNameOrOID()
+                         + "." + indexName
+                         + "." +indexerID;
+          debugBuffer.append(indexID);
+        }
+        debugBuffer.append("]");
+      }
+      ByteString assertionValue =
+              extensibleFilter.getAssertionValue().getValue();
+      IndexQuery expression = rule.createIndexQuery(assertionValue, factory);
+      return expression.evaluate();
+    }
+    catch (DirectoryException e)
+    {
+      if (debugEnabled())
+      {
+        TRACER.debugCaught(DebugLogLevel.ERROR, e);
+      }
+      return IndexQuery.createNullIndexQuery().evaluate();
+    }
+  }
+
+  /**
+   * This class manages all the configured extensible matching rules and
+   * their corresponding indexes.
+   */
+  private class ExtensibleMatchingRuleIndex
+  {
+    /**
+      * The mapping of index ID and Index database.
+      */
+    private final Map<String,Index> id2IndexMap;
+
+    /**
+     * The mapping of Index ID and Set the matching rules.
+     */
+    private final Map<String,Set<ExtensibleMatchingRule>> id2RulesMap;
+
+    /**
+     * The Map of configured ExtensibleMatchingRule and the corresponding
+     * IndexQueryFactory.
+     */
+    private final Map<ExtensibleMatchingRule,
+            IndexQueryFactory<IndexQuery>> rule2FactoryMap;
+
+    /**
+     * Creates a new instance of ExtensibleMatchingRuleIndex.
+     */
+    private ExtensibleMatchingRuleIndex()
+    {
+      id2IndexMap = new HashMap<String,Index>();
+      id2RulesMap = new HashMap<String,Set<ExtensibleMatchingRule>>();
+      rule2FactoryMap = new HashMap<ExtensibleMatchingRule,
+              IndexQueryFactory<IndexQuery>>();
+    }
+
+    /**
+     * Returns all configured ExtensibleMatchingRule instances.
+     * @return A Set  of extensible matching rules.
+     */
+    private Set<ExtensibleMatchingRule> getRules()
+    {
+      return rule2FactoryMap.keySet();
+    }
+
+    /**
+     * Returns  ExtensibleMatchingRule instances for an index.
+     * @param indexID The index ID of an extensible matching rule index.
+     * @return A Set of extensible matching rules corresponding to
+     *                 an index ID.
+     */
+    private Set<ExtensibleMatchingRule>
+            getRules(String indexID)
+    {
+      Set<ExtensibleMatchingRule> rules = id2RulesMap.get(indexID);
+      if(rules == null)
+      {
+        return Collections.emptySet();
+      }
+      else
+      {
+        return Collections.unmodifiableSet(id2RulesMap.get(indexID));
+      }
+    }
+
+    /**
+     * Returns whether an index is present or not.
+     * @param indexID The index ID of an extensible matching rule index.
+     * @return True if an index is present. False if there is no matching index.
+     */
+    private boolean isIndexPresent(String indexID)
+    {
+      return id2IndexMap.containsKey(indexID);
+    }
+
+
+    /**
+     * Returns the index corresponding to an index ID.
+     * @param indexID The ID of an index.
+     * @return The extensible rule index corresponding to the index ID.
+     */
+    private Index getIndex(String indexID)
+    {
+      return id2IndexMap.get(indexID);
+    }
+
+
+    /**
+     * Adds a new matching Rule and the name of the associated index.
+     * @indexName Name of the index.
+     * @rule An ExtensibleMatchingRule instance that needs to be indexed.
+     */
+    private void addRule(String indexName,ExtensibleMatchingRule rule)
+    {
+      Set<ExtensibleMatchingRule> rules = id2RulesMap.get(indexName);
+      if(rules == null)
+      {
+        rules = new HashSet<ExtensibleMatchingRule>();
+        id2RulesMap.put(indexName, rules);
+      }
+      rules.add(rule);
+    }
+
+    /**
+     * Adds a new Index and its name.
+     * @param index The extensible matching rule index.
+     * @indexName The name of the index.
+     */
+    private void addIndex(Index index,String indexName)
+    {
+      id2IndexMap.put(indexName, index);
+    }
+
+    /**
+     * Returns all the configured extensible indexes.
+     * @return All the available extensible matching rule indexes.
+     */
+    private Collection<Index> getIndexes()
+    {
+      return Collections.unmodifiableCollection(id2IndexMap.values());
+    }
+
+
+    /**
+     * Returns a map of all the configured extensible indexes and their types.
+     * @return A map of all the available extensible matching rule indexes
+     *             and their types.
+     */
+    private Map<String,Collection<Index>> getIndexMap()
+    {
+      if(id2IndexMap.isEmpty())
+      {
+        return Collections.emptyMap();
+      }
+      Collection<Index> substring = new ArrayList<Index>();
+      Collection<Index> shared = new ArrayList<Index>();
+      for(Map.Entry<String,Index> entry :  id2IndexMap.entrySet())
+      {
+        String indexID = entry.getKey();
+        if(indexID.endsWith(EXTENSIBLE_INDEXER_ID_SUBSTRING))
+        {
+          substring.add(entry.getValue());
+        }
+        else
+        {
+          shared.add(entry.getValue());
+        }
+      }
+      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);
+    }
+
+
+    /**
+     * Deletes an index corresponding to the index ID.
+     * @param indexID Name of the index.
+     */
+    private void deleteIndex(String indexID)
+    {
+      id2IndexMap.remove(indexID);
+    }
+
+
+    /**
+     * Deletes an extensible matching rule from the list of available rules.
+     * @param rule The ExtensibleMatchingRule that needs to be removed.
+     * @param indexID The name of the index corresponding to the rule.
+     */
+    private void deleteRule(ExtensibleMatchingRule rule,String indexID)
+    {
+      Set<ExtensibleMatchingRule> rules = id2RulesMap.get(indexID);
+      rules.remove(rule);
+      if(rules.size() == 0)
+      {
+        id2RulesMap.remove(indexID);
+      }
+      rule2FactoryMap.remove(rule);
+    }
+
+
+    /**
+     * Adds an ExtensibleMatchingRule and its corresponding IndexQueryFactory.
+     * @param rule An ExtensibleMatchingRule that needs to be added.
+     * @param query A query factory matching the rule.
+     */
+    private void addQueryFactory(ExtensibleMatchingRule rule,
+            IndexQueryFactory<IndexQuery> query)
+    {
+      rule2FactoryMap.put(rule, query);
+    }
+
+
+    /**
+     * Returns the query factory associated with the rule.
+     * @param rule An ExtensibleMatchingRule that needs to be searched.
+     * @return An IndexQueryFactory corresponding to the matching rule.
+     */
+    private IndexQueryFactory<IndexQuery> getQueryFactory(
+            ExtensibleMatchingRule rule)
+    {
+      return rule2FactoryMap.get(rule);
+    }
+
+
+    /**
+     * Deletes  extensible matching rules from the list of available rules.
+     * @param indexID The name of the index corresponding to the rules.
+     */
+    private void deleteRule(String indexID)
+    {
+      Set<ExtensibleMatchingRule> rules  = id2RulesMap.get(indexID);
+      rule2FactoryMap.remove(rules);
+      rules.clear();
+      id2RulesMap.remove(indexID);
+    }
+
+
+    /**
+     * Deletes all references to matching rules and the indexes.
+     */
+    private void deleteAll()
+    {
+      id2IndexMap.clear();
+      id2RulesMap.clear();
+      rule2FactoryMap.clear();
+    }
+  }
+
+  /**
+   * This class extends the IndexConfig for JE Backend.
+   */
+  private class JEIndexConfig extends IndexConfig
+  {
+    //The length of the substring index.
+    private int substringLength;
+
+
+    /**
+     * Creates a new JEIndexConfig instance.
+     * @param substringLength The length of the substring.
+     */
+    private JEIndexConfig(int substringLength)
+    {
+      this.substringLength = substringLength;
+    }
+
+
+    /**
+     * Returns the length of the substring.
+     * @return the length of the substring.
+     */
+   public int getSubstringLength()
+   {
+     return substringLength;
+   }
+  }
+}
\ No newline at end of file
diff --git a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/BackendImpl.java b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/BackendImpl.java
index 759789d..ed68cc0 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/BackendImpl.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/BackendImpl.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2007-2008 Sun Microsystems, Inc.
+ *      Copyright 2007-2009 Sun Microsystems, Inc.
  */
 package org.opends.server.backends.jeb;
 import org.opends.messages.Message;
@@ -69,9 +69,10 @@
 import org.opends.server.admin.std.server.LocalDBIndexCfg;
 import org.opends.server.admin.Configuration;
 import org.opends.server.admin.server.ConfigurationChangeListener;
+import org.opends.server.api.ExtensibleIndexer;
 import org.opends.server.types.DN;
 import org.opends.server.backends.jeb.importLDIF.Importer;
-
+import org.opends.server.api.ExtensibleMatchingRule;
 /**
  * This is an implementation of a Directory Server Backend which stores entries
  * locally in a Berkeley DB JE database.
@@ -1131,13 +1132,30 @@
       envConfig.setConfigParam("je.env.runCheckpointer", "false");
       //Loop through local indexes and see if any are substring.
       boolean hasSubIndex = false;
+subIndex:
       for (String idx : cfg.listLocalDBIndexes()) {
-        LocalDBIndexCfg indexCfg = cfg.getLocalDBIndex(idx);
+        final LocalDBIndexCfg indexCfg = cfg.getLocalDBIndex(idx);
         Set<org.opends.server.admin.std.meta.LocalDBIndexCfgDefn.IndexType>
                                             indexType = indexCfg.getIndexType();
         if(indexType.contains(org.opends.server.admin.std.
                 meta.LocalDBIndexCfgDefn.IndexType.SUBSTRING)) {
           hasSubIndex = true;
+          break;
+        }
+        Set<ExtensibleMatchingRule> matchingRules =
+                              indexCfg.getIndexExtensibleMatchingRule();
+        for(ExtensibleMatchingRule rule: matchingRules)
+        {
+          for(ExtensibleIndexer indexer: rule.getIndexers(null))
+          {
+            String indexID = indexer.getExtensibleIndexID();
+            if(indexID.equals(EXTENSIBLE_INDEXER_ID_SUBSTRING))
+            {
+              //The ExtensibelMatchingRule is of substring type.
+              hasSubIndex = true;
+              break subIndex;
+            }
+          }
         }
       }
       Importer importer = new Importer(importConfig, hasSubIndex);
diff --git a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/IndexFilter.java b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/IndexFilter.java
index 14e7eb9..fd46847 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/IndexFilter.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/IndexFilter.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2006-2008 Sun Microsystems, Inc.
+ *      Copyright 2006-2009 Sun Microsystems, Inc.
  */
 package org.opends.server.backends.jeb;
 
@@ -180,8 +180,15 @@
         candidates = evaluateApproximateFilter(filter);
         break;
 
-      case NOT:
       case EXTENSIBLE_MATCH:
+         if (buffer!= null)
+        {
+          filter.toString(buffer);
+        }
+        candidates = evaluateExtensibleFilter(filter);
+        break;
+
+      case NOT:
       default:
         if (buffer != null)
         {
@@ -537,4 +544,26 @@
     return candidates;
   }
 
+  /**
+   * Evaluate an extensible filter against the indexes.
+   *
+   * @param extensibleFilter The extensible filter to be evaluated.
+   * @return A set of entry IDs representing candidate entries.
+   */
+  private EntryIDSet evaluateExtensibleFilter(SearchFilter extensibleFilter)
+  {
+    EntryIDSet candidates;
+    AttributeIndex attributeIndex =
+         entryContainer.getAttributeIndex(extensibleFilter.getAttributeType());
+    if (attributeIndex == null)
+    {
+      candidates = IndexQuery.createNullIndexQuery().evaluate();
+    }
+    else
+    {
+      candidates =
+          attributeIndex.evaluateExtensibleFilter(extensibleFilter, buffer);
+    }
+    return candidates;
+  }
 }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/IndexQuery.java b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/IndexQuery.java
new file mode 100644
index 0000000..23e3e82
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/IndexQuery.java
@@ -0,0 +1,209 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Copyright 2009 Sun Microsystems, Inc.
+ */
+
+
+package org.opends.server.backends.jeb;
+
+import java.util.Collection;
+import static org.opends.server.backends.jeb.IndexFilter.*;
+
+
+/**
+ * This class represents a JE Backend Query.
+ */
+@org.opends.server.types.PublicAPI(
+     stability=org.opends.server.types.StabilityLevel.VOLATILE,
+     mayInstantiate=false,
+     mayExtend=true,
+     mayInvoke=false)
+public abstract class IndexQuery
+{
+  /**
+  * Evaluates the index query and returns the EntryIDSet.
+  * @return The EntryIDSet as a result of evaulation of this query.
+  */
+  public abstract EntryIDSet evaluate();
+
+
+
+   /**
+    * Creates an IntersectionIndexQuery object from a collection of IndexQuery
+    * objects.
+    * @param subIndexQueries A collection of IndexQuery objects.
+    * @return An IntersectionIndexQuery object.
+    */
+   public static IndexQuery createIntersectionIndexQuery(
+           Collection<IndexQuery> subIndexQueries)
+   {
+     return new IntersectionIndexQuery(subIndexQueries);
+   }
+
+
+
+   /**
+    * Creates a union IndexQuery object from a collection of IndexQuery
+    * objects.
+    * @param subIndexQueries Collection of IndexQuery objects.
+    * @return A UnionIndexQuery object.
+    */
+   public static IndexQuery createUnionIndexQuery(
+           Collection<IndexQuery> subIndexQueries)
+   {
+     return new UnionIndexQuery(subIndexQueries);
+   }
+
+
+
+   /**
+    * Creates an empty IndexQuery object.
+    * @return A NullIndexQuery object.
+    */
+   public static IndexQuery createNullIndexQuery()
+   {
+     return new NullIndexQuery();
+   }
+}
+
+
+
+/**
+* This class creates a Null  IndexQuery. It is used when there
+*  is no record in the index. It may also be used when the
+ * index contains all the records but an empty EntryIDSet should be
+ * returned as part of the optimization.
+*/
+final class NullIndexQuery extends IndexQuery
+{
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public EntryIDSet evaluate()
+  {
+    return new EntryIDSet();
+  }
+}
+
+
+
+
+
+/**
+ * This class creates an intersection IndexQuery from a collection of
+ * IndexQuery objects.
+ */
+final class IntersectionIndexQuery extends IndexQuery
+{
+  /**
+   * Collection of IndexQuery objects.
+   */
+  private final  Collection<IndexQuery> subIndexQueries;
+
+
+  /**
+   * Creates an instance of IntersectionIndexQuery.
+   * @param subIndexQueries Collection of IndexQuery objects.
+   */
+  IntersectionIndexQuery(Collection<IndexQuery> subIndexQueries)
+  {
+    this.subIndexQueries = subIndexQueries;
+  }
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public EntryIDSet evaluate()
+  {
+   EntryIDSet entryIDs = null;
+   for (IndexQuery query : subIndexQueries)
+   {
+     if (entryIDs == null)
+     {
+       entryIDs = query.evaluate();
+     }
+     else
+     {
+       entryIDs.retainAll(query.evaluate());
+     }
+     if (entryIDs.isDefined() &&
+         entryIDs.size() <= FILTER_CANDIDATE_THRESHOLD)
+     {
+       break;
+     }
+   }
+   return entryIDs;
+  }
+}
+
+/**
+ * This class creates a union of IndexQuery objects.
+ */
+final class UnionIndexQuery extends IndexQuery
+{
+  /**
+   * Collection containing IndexQuery objects.
+   */
+  private final Collection<IndexQuery> subIndexQueries;
+
+  /**
+   * Creates an instance of UnionIndexQuery.
+   * @param subIndexQueries The Collection of IndexQuery objects.
+   */
+  UnionIndexQuery(Collection<IndexQuery> subIndexQueries)
+  {
+    this.subIndexQueries = subIndexQueries;
+  }
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public EntryIDSet evaluate()
+  {
+    EntryIDSet entryIDs = null;
+    for (IndexQuery query : subIndexQueries)
+    {
+      if (entryIDs == null)
+      {
+        entryIDs = query.evaluate();
+      }
+      else
+      {
+        entryIDs.addAll(query.evaluate());
+      }
+      if (entryIDs.isDefined() &&
+          entryIDs.size() <= FILTER_CANDIDATE_THRESHOLD)
+      {
+        break;
+      }
+    }
+    return entryIDs;
+  }
+}
\ No newline at end of file
diff --git a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/IndexQueryFactoryImpl.java b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/IndexQueryFactoryImpl.java
new file mode 100644
index 0000000..55efb41
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/IndexQueryFactoryImpl.java
@@ -0,0 +1,152 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Copyright 2009 Sun Microsystems, Inc.
+ */
+
+
+package org.opends.server.backends.jeb;
+
+import com.sleepycat.je.DatabaseEntry;
+import com.sleepycat.je.LockMode;
+import java.util.Collection;
+import java.util.Map;
+import org.opends.server.api.IndexQueryFactory;
+
+/**
+ * This class is an implementation of IndexQueryFactory which creates
+ * IndexQuery objects as part of the query of the JEB index.
+*/
+public final class IndexQueryFactoryImpl
+        implements IndexQueryFactory<IndexQuery>
+{
+  /**
+   * The Map containing the string type identifier and the corresponding index.
+   */
+  private Map<String,Index> indexMap;
+
+
+
+  /**
+   * Creates a new IndexQueryFactoryImpl object.
+   * @param indexMap A map containing the index id and the corresponding index.
+   */
+  public IndexQueryFactoryImpl(Map<String,Index> indexMap)
+  {
+    this.indexMap = indexMap;
+  }
+
+
+
+  /**
+   *{@inheritDoc}
+   */
+  public IndexQuery createExactMatchQuery(final String indexID,
+          final byte[] value)
+  {
+    return new IndexQuery()
+    {
+
+      @Override
+      public EntryIDSet evaluate()
+      {
+        //Read the database and get Record for the key.
+        DatabaseEntry key = new DatabaseEntry(value);
+        //Select the right index to be used.
+        Index index = indexMap.get(indexID);
+        EntryIDSet entrySet = index.readKey(key,null,LockMode.DEFAULT);
+        return entrySet;
+      }
+    };
+  }
+
+
+
+  /**
+   *{@inheritDoc}
+   */
+  public IndexQuery createRangeMatchQuery(
+                                              final String indexID,
+                                              final byte[] lowerBound,
+                                              final byte[] upperBound,
+                                              final boolean includeLowerBound,
+                                              final boolean includeUpperBound)
+  {
+    return new IndexQuery()
+    {
+
+      @Override
+      public EntryIDSet evaluate()
+      {
+        //Find the right index.
+        Index index = indexMap.get(indexID);
+        EntryIDSet entrySet =   index.readRange(lowerBound,upperBound,
+                includeLowerBound,
+            includeUpperBound);
+        return entrySet;
+      }
+    };
+  }
+
+
+
+  /**
+   *{@inheritDoc}
+   */
+  public IndexQuery  createIntersectionQuery(Collection<IndexQuery>
+                                                                subqueries)
+  {
+    return IndexQuery.createIntersectionIndexQuery(subqueries);
+  }
+
+
+
+  /**
+   *{@inheritDoc}
+   */
+  public IndexQuery createUnionQuery(Collection<IndexQuery> subqueries)
+  {
+    return IndexQuery.createUnionIndexQuery(subqueries);
+  }
+
+
+
+  /**
+   *{@inheritDoc}
+   * It returns an empty EntryIDSet object  when either all or no record sets
+   * are requested.
+   */
+  public IndexQuery createMatchAllQuery()
+  {
+    return new IndexQuery()
+    {
+
+      @Override
+      public EntryIDSet evaluate()
+      {
+        return new EntryIDSet();
+      }
+    };
+  }
+}
\ No newline at end of file
diff --git a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/JEExtensibleIndexer.java b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/JEExtensibleIndexer.java
new file mode 100644
index 0000000..6e2e9d7
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/JEExtensibleIndexer.java
@@ -0,0 +1,221 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Copyright 2009 Sun Microsystems, Inc.
+ */
+
+
+package org.opends.server.backends.jeb;
+
+import org.opends.server.api.ExtensibleIndexer;
+import org.opends.server.types.AttributeType;
+import org.opends.server.types.AttributeValue;
+import org.opends.server.types.Entry;
+import org.opends.server.types.Modification;
+import org.opends.server.api.ExtensibleMatchingRule;
+import org.opends.server.types.Attribute;
+
+
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ *This class implements an Indexer for extensible matching rules in JE Backend.
+ */
+public final class JEExtensibleIndexer extends Indexer
+{
+  /**
+   * The comparator for index keys generated by this class.
+   */
+  private static final Comparator<byte[]> comparator =
+       new AttributeIndex.KeyComparator();
+
+
+
+  /**
+   * The attribute type for which this instance will
+   * generate index keys.
+   */
+  private final AttributeType attributeType;
+
+
+
+  /**
+   * The extensible indexer which will generate the keys
+   * for the associated  extensible matching rule.
+   */
+  private final ExtensibleIndexer extensibleIndexer;
+
+
+
+  /**
+   * The extensible matching rule which needs to be indexed.
+   */
+  private final ExtensibleMatchingRule matchingRule;
+
+
+
+  /**
+   * Creates a new extensible indexer for JE backend.
+   *
+   * @param attributeType The attribute type for which an indexer is
+   *                                            required.
+   * @param matchingRule  The extensible matching rule to be indexed.
+   * @param extensibleIndexer The extensible indexer to be used.
+   */
+  public JEExtensibleIndexer(AttributeType attributeType,
+          ExtensibleMatchingRule matchingRule,
+          ExtensibleIndexer extensibleIndexer)
+  {
+    this.attributeType = attributeType;
+    this.matchingRule = matchingRule;
+    this.extensibleIndexer = extensibleIndexer;
+  }
+
+
+
+   /**
+   * Gets a string representation of this object.  The returned value is
+   * used to name an index created using this object.
+   * @return A string representation of this object.
+   */
+  @Override
+  public String toString()
+  {
+    return attributeType.getNameOrOID() + "."
+            + extensibleIndexer.getExtensibleIndexID();
+  }
+
+
+
+  /**
+   * Gets the comparator that must be used to compare index keys
+   * generated by this class.
+   *
+   * @return A byte array comparator.
+   */
+  public Comparator<byte[]> getComparator()
+  {
+    return comparator;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void indexEntry(Entry entry, Set<byte[]> keys)
+  {
+    List<Attribute> attrList =
+         entry.getAttribute(attributeType);
+    if (attrList != null)
+    {
+      indexAttribute(attrList, keys);
+    }
+  }
+
+
+
+ /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void replaceEntry(Entry oldEntry, Entry newEntry,
+                           Map<byte[], Boolean> modifiedKeys)
+  {
+    List<Attribute> newAttributes = newEntry.getAttribute(attributeType, true);
+    List<Attribute> oldAttributes = oldEntry.getAttribute(attributeType, true);
+
+    indexAttribute(oldAttributes, modifiedKeys, false);
+    indexAttribute(newAttributes, modifiedKeys, true);
+  }
+
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void modifyEntry(Entry oldEntry, Entry newEntry,
+                          List<Modification> mods,
+                          Map<byte[], Boolean> modifiedKeys)
+  {
+    List<Attribute> newAttributes = newEntry.getAttribute(attributeType, true);
+    List<Attribute> oldAttributes = oldEntry.getAttribute(attributeType, true);
+
+    indexAttribute(oldAttributes, modifiedKeys, false);
+    indexAttribute(newAttributes, modifiedKeys, true);
+  }
+
+
+
+  /**
+   * Generates the set of extensible  index keys for an attribute.
+   * @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<byte[]> keys)
+  {
+    if (attrList == null) return;
+
+    for (Attribute attr : attrList)
+    {
+      for (AttributeValue value : attr)
+      {
+        extensibleIndexer.getKeys(value, keys);
+      }
+    }
+  }
+
+
+
+  /**
+   * Generates the set of index keys for an attribute.
+   * @param attrList The attribute to be indexed.
+   * @param modifiedKeys The map into which the modified
+   * keys will be inserted.
+   * @param insert <code>true</code> if generated keys should
+   * be inserted or <code>false</code> otherwise.
+   */
+  private void indexAttribute(List<Attribute> attrList,
+                              Map<byte[], Boolean> modifiedKeys,
+                              Boolean insert)
+  {
+    if (attrList == null) return;
+
+    for (Attribute attr : attrList)
+    {
+      for (AttributeValue value : attr)
+      {
+        extensibleIndexer.getKeys(value,modifiedKeys,insert);
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/importLDIF/WorkThread.java b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/importLDIF/WorkThread.java
index 8b8f16e..651134e 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/importLDIF/WorkThread.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/importLDIF/WorkThread.java
@@ -22,12 +22,13 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2008 Sun Microsystems, Inc.
+ *      Copyright 2008-2009 Sun Microsystems, Inc.
  */
 
 package org.opends.server.backends.jeb.importLDIF;
 
 import static org.opends.server.loggers.debug.DebugLogger.*;
+import  static org.opends.server.util.ServerConstants.*;
 import org.opends.server.loggers.debug.DebugTracer;
 import org.opends.server.types.*;
 import org.opends.server.api.DirectoryThread;
@@ -37,7 +38,6 @@
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.TimeUnit;
 import java.util.*;
-import org.opends.server.util.*;
 
 import com.sleepycat.je.*;
 
@@ -127,6 +127,7 @@
    * to flush and exit.
    *
    */
+  @Override
   public void run()
   {
     try {
@@ -222,6 +223,12 @@
         if((index=attributeIndex.getApproximateIndex()) != null) {
           delete(index, existingEntry, entryID);
         }
+        for(Collection<Index> indexes :
+                      attributeIndex.getExtensibleIndexes().values()) {
+          for(Index extensibleIndex: indexes) {
+            delete(extensibleIndex,existingEntry,entryID);
+          }
+        }
       }
       for(VLVIndex vlvIdx : context.getEntryContainer().getVLVIndexes()) {
           vlvIdx.removeEntry(txn, entryID, existingEntry);
@@ -276,6 +283,26 @@
         for(VLVIndex vlvIdx : context.getEntryContainer().getVLVIndexes()) {
             vlvIdx.addEntry(txn, entryID, entry);
         }
+        Map<String,Collection<Index>> extensibleMap =
+                attributeIndex.getExtensibleIndexes();
+        if(!extensibleMap.isEmpty()) {
+          Collection<Index> subIndexes =
+                attributeIndex.getExtensibleIndexes().get(
+                EXTENSIBLE_INDEXER_ID_SUBSTRING);
+          if(subIndexes != null) {
+            for(Index subIndex: subIndexes) {
+              bufferMgr.insert(subIndex, entry, entryID, insertKeySet);
+            }
+          }
+          Collection<Index> sharedIndexes =
+                attributeIndex.getExtensibleIndexes().get(
+                EXTENSIBLE_INDEXER_ID_SHARED);
+          if(sharedIndexes !=null) {
+            for(Index sharedIndex:sharedIndexes) {
+              insert(sharedIndex,entry,entryID);
+            }
+          }
+        }
       }
     }
   }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/DirectoryServer.java b/opendj-sdk/opends/src/server/org/opends/server/core/DirectoryServer.java
index bd3e97a..9551fdc 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/DirectoryServer.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/DirectoryServer.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2006-2008 Sun Microsystems, Inc.
+ *      Copyright 2006-2009 Sun Microsystems, Inc.
  */
 package org.opends.server.core;
 
@@ -75,6 +75,7 @@
 import org.opends.server.api.AccessControlHandler;
 import org.opends.server.api.plugin.PluginType;
 import org.opends.server.api.plugin.PluginResult;
+import org.opends.server.api.ExtensibleMatchingRule;
 import org.opends.server.backends.RootDSEBackend;
 import static org.opends.server.config.ConfigConstants.DN_CONFIG_ROOT;
 import static org.opends.server.config.ConfigConstants.DN_MONITOR_ROOT;
@@ -3515,6 +3516,41 @@
 
 
   /**
+   * Retrieves the set of extensible matching rules registered with the
+   * Directory Server.  The mapping will be between the lowercase name or OID
+   * for each extensible matching rule and the matching rule implementation. The
+   * same extensible matching rule instance may be included multiple times with
+   * different keys.
+   *
+   * @return  The set of extensible matching rules registered with the Directory
+   *          Server.
+   */
+  public static Map<String,ExtensibleMatchingRule>
+                     getExtensibleMatchingRules()
+  {
+    return directoryServer.schema.getExtensibleMatchingRules();
+  }
+
+
+
+  /**
+   * Retrieves the extensible matching rule with the specified name or OID.
+   *
+   * @param  lowerName  The lowercase name or OID for the extensible matching
+   *                rule  to retrieve.
+   *
+   * @return  The requested extensible matching rule, or <CODE>null</CODE> if no
+   *          such matching rule has been defined in the server.
+   */
+  public static ExtensibleMatchingRule
+          getExtensibleMatchingRule(String lowerName)
+  {
+    return directoryServer.schema.getExtensibleMatchingRule(lowerName);
+  }
+
+
+
+  /**
    * Retrieves the set of objectclasses defined in the Directory Server.
    *
    * @return  The set of objectclasses defined in the Directory Server.
diff --git a/opendj-sdk/opends/src/server/org/opends/server/schema/CollationMatchingRuleFactory.java b/opendj-sdk/opends/src/server/org/opends/server/schema/CollationMatchingRuleFactory.java
index 8868702..0237d4a 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/schema/CollationMatchingRuleFactory.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/schema/CollationMatchingRuleFactory.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2008 Sun Microsystems, Inc.
+ *      Copyright 2008-2009 Sun Microsystems, Inc.
  */
 
 
@@ -44,21 +44,27 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.SortedSet;
+import java.util.TreeSet;
 import org.opends.messages.Message;
 import org.opends.server.admin.server.ConfigurationChangeListener;
 import org.opends.server.admin.std.meta.CollationMatchingRuleCfgDefn.
         MatchingRuleType;
+import org.opends.server.api.ExtensibleIndexer;
+import org.opends.server.api.IndexQueryFactory;
 import org.opends.server.api.MatchingRuleFactory;
 import org.opends.server.admin.std.server.CollationMatchingRuleCfg;
 import org.opends.server.api.ExtensibleMatchingRule;
 import org.opends.server.api.MatchingRule;
+import org.opends.server.backends.jeb.AttributeIndex;
 import org.opends.server.config.ConfigException;
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.protocols.asn1.ASN1OctetString;
+import org.opends.server.types.AttributeValue;
 import org.opends.server.types.ByteString;
 import org.opends.server.types.ConditionResult;
 import org.opends.server.types.ConfigChangeResult;
 import org.opends.server.types.DirectoryException;
+import org.opends.server.types.IndexConfig;
 import org.opends.server.types.InitializationException;
 
 import org.opends.server.types.ResultCode;
@@ -68,6 +74,8 @@
 import static org.opends.messages.CoreMessages.*;
 import static org.opends.messages.ConfigMessages.*;
 import static org.opends.server.util.StaticUtils.*;
+import static org.opends.server.api.ExtensibleIndexer.*;
+import static org.opends.server.util.ServerConstants.*;
 
 
 
@@ -118,9 +126,9 @@
   }
 
 
-/**
-  * Creates a new instance of CollationMatchingRuleFactory.
- */
+  /**
+   * Creates a new instance of CollationMatchingRuleFactory.
+   */
   public CollationMatchingRuleFactory()
   {
     //Initialize the matchingRules.
@@ -130,8 +138,8 @@
 
 
   /**
-  * {@inheritDoc}
-  */
+   * {@inheritDoc}
+   */
   @Override
   public final Collection<MatchingRule> getMatchingRules()
   {
@@ -140,16 +148,16 @@
 
 
 
- /**
-  * Adds a new mapping of OID and MatchingRule.
-  *
-  * @param oid OID of the matching rule
-  * @param matchingRule instance of a MatchingRule.
-  */
+  /**
+   * Adds a new mapping of OID and MatchingRule.
+   *
+   * @param oid OID of the matching rule
+   * @param matchingRule instance of a MatchingRule.
+   */
   private final void addMatchingRule(String oid,
           MatchingRule matchingRule)
   {
-     matchingRules.put(oid, matchingRule);
+    matchingRules.put(oid, matchingRule);
   }
 
 
@@ -179,10 +187,10 @@
 
 
   /**
-  * Reads the configuration and initializes matching rule types.
-  *
-  * @param  ruleTypes  The Set containing allowed matching rule types.
-  */
+   * Reads the configuration and initializes matching rule types.
+   *
+   * @param  ruleTypes  The Set containing allowed matching rule types.
+   */
   private void initializeMatchingRuleTypes(SortedSet<MatchingRuleType>
           ruleTypes)
   {
@@ -209,19 +217,19 @@
           substringMatchingRuleType = true;
           break;
         default:
-          //No default values allowed.
+        //No default values allowed.
       }
     }
   }
 
 
 
- /**
-  * Creates a new Collator instance.
-  *
-  * @param locale Locale for the collator
-  * @return Returns a new Collator instance
-  */
+  /**
+   * Creates a new Collator instance.
+   *
+   * @param locale Locale for the collator
+   * @return Returns a new Collator instance
+   */
   private Collator createCollator(Locale locale)
   {
     Collator collator = Collator.getInstance(locale);
@@ -232,9 +240,9 @@
 
 
 
- /**
-  * {@inheritDoc}
-  */
+  /**
+   * {@inheritDoc}
+   */
   @Override
   public void initializeMatchingRule(CollationMatchingRuleCfg configuration)
   throws ConfigException, InitializationException
@@ -271,7 +279,7 @@
         Message msg =
               WARN_ATTR_INVALID_COLLATION_MATCHING_RULE_LOCALE.
               get(collation,configuration.dn().toNormalizedString(),
-                  languageTag);
+                languageTag);
 
         logError(msg);
       }
@@ -332,7 +340,6 @@
     for(String collation:configuration.getCollation())
     {
       CollationMapper mapper = new CollationMapper(collation);
-      String nOID = mapper.getNumericOID();
       String languageTag = mapper.getLanguageTag();
       Locale locale = getLocale(languageTag);
       createLessThanMatchingRule(mapper,locale);
@@ -352,10 +359,10 @@
     }
     catch (DirectoryException de)
     {
-        Message message = WARN_CONFIG_SCHEMA_MR_CONFLICTING_MR.get(
-          String.valueOf(configuration.dn()), de.getMessageObject());
-        adminActionRequired = true;
-        messages.add(message);
+      Message message = WARN_CONFIG_SCHEMA_MR_CONFLICTING_MR.get(
+              String.valueOf(configuration.dn()), de.getMessageObject());
+      adminActionRequired = true;
+      messages.add(message);
     }
     currentConfig = configuration;
     return new ConfigChangeResult(resultCode, adminActionRequired, messages);
@@ -405,7 +412,7 @@
         Message msg =
               WARN_ATTR_INVALID_COLLATION_MATCHING_RULE_LOCALE.
               get(collation,configuration.dn().toNormalizedString(),
-                  languageTag);
+                languageTag);
         unacceptableReasons.add(msg);
         configAcceptable = false;
         continue;
@@ -417,11 +424,11 @@
 
 
   /**
-  * Creates Less-than Matching Rule.
-  *
-  * @param mapper CollationMapper containing OID and the language Tag.
-  * @param locale  Locale value
-  */
+   * Creates Less-than Matching Rule.
+   *
+   * @param mapper CollationMapper containing OID and the language Tag.
+   * @param locale  Locale value
+   */
   private void createLessThanMatchingRule(CollationMapper mapper,Locale locale)
   {
     if(!lessThanMatchingRuleType)
@@ -449,12 +456,12 @@
 
 
 
- /**
-  * Creates Less-Than-Equal-To Matching Rule.
-  *
-  * @param mapper CollationMapper containing OID and the language Tag.
-  * @param locale  Locale value
-  */
+  /**
+   * Creates Less-Than-Equal-To Matching Rule.
+   *
+   * @param mapper CollationMapper containing OID and the language Tag.
+   * @param locale  Locale value
+   */
   private void createLessThanOrEqualToMatchingRule(CollationMapper mapper,
           Locale locale)
   {
@@ -484,12 +491,12 @@
 
 
 
- /**
-  * Creates Equality Matching Rule.
-  *
-  * @param mapper CollationMapper containing OID and the language Tag.
-  * @param locale  Locale value
-  */
+  /**
+   * Creates Equality Matching Rule.
+   *
+   * @param mapper CollationMapper containing OID and the language Tag.
+   * @param locale  Locale value
+   */
   private void createEqualityMatchingRule(CollationMapper mapper,Locale locale)
   {
     if(!equalityMatchingRuleType)
@@ -510,37 +517,37 @@
 
     names.add(lTag);
     matchingRule =
-          new CollationEqualityMatchingRule(nOID,names,locale);
+          new CollationEqualityMatchingRule(nOID,
+                  Collections.<String>emptySet(),locale);
     addMatchingRule(nOID, matchingRule);
 
     // Register OID.3 as the equality matching rule.
     String OID = mapper.getNumericOID() + ".3";
     MatchingRule equalityMatchingRule = getMatchingRule(OID);
-    Collection<String> equalityNames = new HashSet<String>();
     if(equalityMatchingRule!=null)
     {
       for(String name: equalityMatchingRule.getAllNames())
       {
-        equalityNames.add(name);
+        names.add(name);
       }
     }
 
-    equalityNames.add(lTag+".eq");
-    equalityNames.add(lTag+".3");
+    names.add(lTag+".eq");
+    names.add(lTag+".3");
 
     equalityMatchingRule =
-          new CollationEqualityMatchingRule(OID,equalityNames,locale);
+          new CollationEqualityMatchingRule(OID,names,locale);
     addMatchingRule(OID, equalityMatchingRule);
   }
 
 
 
   /**
-  * Creates Greater-than-equal-to Matching Rule.
-  *
-  * @param mapper CollationMapper containing OID and the language Tag.
-  * @param locale  Locale value
-  */
+   * Creates Greater-than-equal-to Matching Rule.
+   *
+   * @param mapper CollationMapper containing OID and the language Tag.
+   * @param locale  Locale value
+   */
   private void createGreaterThanOrEqualToMatchingRule(CollationMapper mapper,
           Locale locale)
   {
@@ -569,12 +576,12 @@
 
 
 
- /**
-  * Creates Greater-than Matching Rule.
-  *
-  * @param mapper CollationMapper containing OID and the language Tag.
-  * @param locale  Locale value
-  */
+  /**
+   * Creates Greater-than Matching Rule.
+   *
+   * @param mapper CollationMapper containing OID and the language Tag.
+   * @param locale  Locale value
+   */
   private void createGreaterThanMatchingRule(CollationMapper mapper,
           Locale locale)
   {
@@ -603,12 +610,12 @@
 
 
 
- /**
-  * Creates substring Matching Rule.
-  *
-  * @param mapper CollationMapper containing OID and the language Tag.
-  * @param locale  Locale value
-  */
+  /**
+   * Creates substring Matching Rule.
+   *
+   * @param mapper CollationMapper containing OID and the language Tag.
+   * @param locale  Locale value
+   */
   private void createSubstringMatchingRule(CollationMapper mapper,Locale locale)
   {
     if(!substringMatchingRuleType)
@@ -636,12 +643,12 @@
 
 
 
- /**
-  * Verifies if the locale is supported by the JVM.
-  *
-  * @param  lTag  The language tag specified in the configuration.
-  * @return  Locale The locale correspoding to the languageTag.
-  */
+  /**
+   * Verifies if the locale is supported by the JVM.
+   *
+   * @param  lTag  The language tag specified in the configuration.
+   * @return  Locale The locale correspoding to the languageTag.
+   */
   private Locale getLocale(String lTag)
   {
     //Separates the language and the country from the locale.
@@ -685,35 +692,51 @@
 
 
 
- /**
-  *Collation rule for Equality matching rule.
-  */
-  private final class CollationEqualityMatchingRule
-         extends ExtensibleMatchingRule
+  /**
+   * Collation Extensible matching rule.
+   */
+  private abstract class CollationMatchingRule
+          extends ExtensibleMatchingRule
   {
     //Names for this class.
     private final Collection<String> names;
 
+
+
     //Collator for performing equality match.
-    private final Collator collator;
+    protected final Collator collator;
+
+
 
     //Numeric OID of the rule.
     private final String nOID;
 
 
+
+    //Locale associated with this rule.
+    private final Locale locale;
+
+
+
+    //Indexer of this rule.
+    protected ExtensibleIndexer indexer;
+
+
+
     /**
-     * Constructs a new CollationEqualityMatchingRule.
+     * Constructs a new CollationMatchingRule.
      *
      * @param nOID OID of the collation matching rule
      * @param names names of this matching rule
      * @param locale Locale of the collation matching rule
      */
-    private CollationEqualityMatchingRule(String nOID,Collection<String> names,
+    private CollationMatchingRule(String nOID,Collection<String> names,
             Locale locale)
     {
       super();
       this.names = names;
-      this.collator =createCollator(locale);
+      this.collator = createCollator(locale);
+      this.locale = locale;
       this.nOID = nOID;
     }
 
@@ -747,6 +770,7 @@
     }
 
 
+
     /**
      * {@inheritDoc}
      */
@@ -782,6 +806,74 @@
 
 
     /**
+    * Returns the name of the index database for this matching rule.
+    * An index name for this rule will be based upon the Locale. This will
+    * ensure that multiple collation matching rules corresponding to the same
+    * Locale can share the same index database.
+    * @return  The name of the index for this matching rule.
+    */
+    public String getIndexName()
+    {
+      String language = locale.getLanguage();
+      String country = locale.getCountry();
+      String variant = locale.getVariant();
+      StringBuilder builder = new StringBuilder(language);
+      if (country != null && country.length() > 0)
+      {
+        builder.append("_");
+        builder.append(locale.getCountry());
+      }
+      if (variant != null && variant.length() > 0)
+      {
+        builder.append("_");
+        builder.append(locale.getVariant());
+      }
+      return builder.toString();
+    }
+
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Collection<ExtensibleIndexer> getIndexers(IndexConfig config)
+    {
+      if(indexer == null)
+      {
+        //The default implementation contains shared indexer and doesn't use the
+        //config.
+        indexer = new CollationSharedExtensibleIndexer(this);
+      }
+      return Collections.singletonList(indexer);
+    }
+  }
+
+
+
+  /**
+   *Collation rule for Equality matching rule.
+   */
+  private final class CollationEqualityMatchingRule
+          extends CollationMatchingRule
+  {
+    /**
+     * Constructs a new CollationEqualityMatchingRule.
+     *
+     * @param nOID OID of the collation matching rule
+     * @param names names of this matching rule
+     * @param locale Locale of the collation matching rule
+     */
+    private CollationEqualityMatchingRule(String nOID,Collection<String> names,
+            Locale locale)
+    {
+      super(nOID,names,locale);
+    }
+
+
+
+    /**
      * {@inheritDoc}
      */
     @Override
@@ -794,18 +886,18 @@
 
 
 
-   /**
-    * Indicates whether the two provided normalized values are equal to
-    * each other.
-    *
-    * @param  value1  The normalized form of the first value to
-    *                 compare.
-    * @param  value2  The normalized form of the second value to
-    *                 compare.
-    *
-    * @return  {@code true} if the provided values are equal, or
-    *          {@code false} if not.
-    */
+    /**
+     * Indicates whether the two provided normalized values are equal to
+     * each other.
+     *
+     * @param  value1  The normalized form of the first value to
+     *                 compare.
+     * @param  value2  The normalized form of the second value to
+     *                 compare.
+     *
+     * @return  {@code true} if the provided values are equal, or
+     *          {@code false} if not.
+     */
     private boolean areEqual(ByteString value1, ByteString value2)
     {
       return Arrays.equals(value1.value(), value2.value());
@@ -829,23 +921,35 @@
         return ConditionResult.FALSE;
       }
     }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public <T> T createIndexQuery(ByteString assertionValue,
+            IndexQueryFactory<T> factory) throws DirectoryException
+    {
+      //Normalize the assertion value.
+      ByteString normalValue = normalizeValue(assertionValue);
+      return factory.createExactMatchQuery(
+                indexer.getExtensibleIndexID(),
+                normalValue.value());
+    }
   }
 
 
- /**
-  * Collation rule for Substring matching rule.
-  */
+
+  /**
+   * Collation rule for Substring matching rule.
+   */
   private final class CollationSubstringMatchingRule
-         extends ExtensibleMatchingRule
+          extends CollationMatchingRule
   {
-    //Names for this class.
-    private final Collection<String> names;
+    //Substring Indexer associated with this instance.
+    private CollationSubstringExtensibleIndexer subIndexer;
 
-    //Collator for performing equality match.
-    private final Collator collator;
-
-    //Numeric OID of the rule.
-    private final String nOID;
 
 
     /**
@@ -858,10 +962,7 @@
     private CollationSubstringMatchingRule(String nOID,
             Collection<String> names,Locale locale)
     {
-      super();
-      this.names = names;
-      this.collator =createCollator(locale);
-      this.nOID = nOID;
+      super(nOID,names,locale);
     }
 
 
@@ -870,67 +971,6 @@
      * {@inheritDoc}
      */
     @Override
-    public String getName()
-    {
-      //Concatenate all the names and return.
-      StringBuilder builder = new StringBuilder();
-      for(String name: getAllNames())
-      {
-        builder.append(name);
-        builder.append("\b");
-      }
-      return builder.toString();
-    }
-
-
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public Collection<String> getAllNames()
-    {
-      return Collections.unmodifiableCollection(names);
-    }
-
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public String getOID()
-    {
-      return nOID;
-    }
-
-
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public String getDescription()
-    {
-      return null;
-    }
-
-
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public String getSyntaxOID()
-    {
-      return SYNTAX_DIRECTORY_STRING_OID;
-    }
-
-
-
-   /**
-    * {@inheritDoc}
-    */
-    @Override
     public ByteString normalizeValue(ByteString value)
            throws DirectoryException
     {
@@ -940,12 +980,81 @@
 
 
 
-   /**
-    * {@inheritDoc}
-    */
-    @Override
-    public  ByteString normalizeAssertionValue(ByteString value)
-           throws DirectoryException
+    /**
+     * Utility class which abstracts a substring assertion value.
+     */
+    private final class Assertion
+    {
+      //Initial part of the substring filter.
+      private String subInitial;
+
+
+      //any parts of the substring filter.
+      private List<String> subAny;
+
+
+      //Final part of the substring filter.
+      private String subFinal;
+
+
+
+      /**
+       * Creates a new instance of Assertion.
+       * @param subInitial Initial part of the filter.
+       * @param subAny  Any part of the filter.
+       * @param subFinal Final part of the filter.
+       */
+      Assertion(String subInitial, List<String> subAny, String subFinal)
+      {
+        this.subInitial = subInitial;
+        this.subAny = subAny;
+        this.subFinal = subFinal;
+      }
+
+
+
+      /**
+       * Returns the Initial part of the assertion.
+       * @return Initial part of assertion.
+       */
+      private String getInitial()
+      {
+        return subInitial;
+      }
+
+
+
+      /**
+       * Returns the any part of the assertion.
+       * @return Any part of the assertion.
+       */
+      private List<String> getAny()
+      {
+        return subAny;
+      }
+
+
+
+      /**
+       * Returns the final part of the assertion.
+       * @return Final part of the assertion.
+       */
+      private String getFinal()
+      {
+        return subFinal;
+      }
+    }
+
+
+
+    /**
+     * Parses the assertion from a given value.
+     * @param value The value that needs to be parsed.
+     * @return The parsed Assertion object containing the
+     * @throws org.opends.server.types.DirectoryException
+     */
+    private Assertion parseAssertion(ByteString value)
+            throws DirectoryException
     {
       // Get a string  representation of the value.
       String filterString = value.stringValue();
@@ -973,7 +1082,7 @@
       if (asteriskPositions.isEmpty())
       {
         Message message = ERR_SEARCH_FILTER_SUBSTRING_NO_ASTERISKS.get(
-            filterString, 0, endPos);
+                filterString, 0, endPos);
         throw new DirectoryException(
                 ResultCode.PROTOCOL_ERROR, message);
       }
@@ -1094,7 +1203,19 @@
           subFinal = filterString.substring(firstPos+1, length + firstPos + 1);
         }
       }
+      return new Assertion(subInitial, subAny, subFinal);
+    }
 
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public ByteString normalizeAssertionValue(ByteString value)
+            throws DirectoryException {
+      Assertion assertion = parseAssertion(value);
+      String subInitial = assertion.getInitial();
       // Normalize the Values in the following format:
       // initialLength, initial, numberofany, anyLength1, any1, anyLength2,
       // any2, ..., anyLengthn, anyn, finalLength, final
@@ -1117,8 +1238,8 @@
           normalizedList.add((int)initialBytes[i]);
         }
       }
-      if(subAny.size()==0)
-      {
+      List<String> subAny = assertion.getAny();
+      if (subAny.size() == 0) {
         normalizedList.add(0);
       }
       else
@@ -1136,7 +1257,8 @@
           }
         }
       }
-      if(subFinal ==null)
+      String subFinal = assertion.getFinal();
+      if (subFinal == null)
       {
         normalizedList.add(0);
       }
@@ -1238,7 +1360,7 @@
           }
           assertPos = assertPos + anyLength;
         }
-       }
+      }
 
       int finalLength = 0xFF & assertionBytes[assertPos++];
       if(finalLength!=0)
@@ -1267,23 +1389,371 @@
       }
 
       return ConditionResult.TRUE;
-     }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public final Collection<ExtensibleIndexer> getIndexers(IndexConfig config)
+    {
+      Collection<ExtensibleIndexer> indexers =
+              new ArrayList<ExtensibleIndexer>();
+      int substrLength = 6; //Default substring length;
+      if(subIndexer == null)
+      {
+        if(config != null)
+        {
+          substrLength = config.getSubstringLength();
+        }
+        subIndexer = new CollationSubstringExtensibleIndexer(this,
+                substrLength);
+      }
+      else
+      {
+        if(config !=null)
+        {
+          if(config.getSubstringLength() !=subIndexer.gerSubstringLength())
+          {
+            subIndexer.setSubstringLength(substrLength);
+          }
+        }
+      }
+
+      if(indexer == null)
+      {
+        indexer = new CollationSharedExtensibleIndexer(this);
+      }
+      indexers.add(subIndexer);
+      indexers.add(indexer);
+      return indexers;
+    }
+
+
+
+    /**
+     * Decomposes an attribute value into a set of substring index keys.
+     *
+     * @param attValue Tthe normalized attribute value
+     * @param set A set into which the keys will be inserted.
+     */
+    private void subtringKeys(ByteString attValue,
+            Set<byte[]> keys)
+    {
+      String value = attValue.stringValue();
+      int keyLength = subIndexer.gerSubstringLength();
+      for (int i = 0, remain = value.length(); remain > 0; i++, remain--)
+      {
+        int len = Math.min(keyLength, remain);
+        byte[] keyBytes = makeSubstringKey(value, i, len);
+        keys.add(keyBytes);
+      }
+    }
+
+
+
+    /**
+     * Decomposes an attribute value into a set of substring index keys.
+     *
+     * @param value The normalized attribute value
+     * @param modifiedKeys The map into which the modified
+     *  keys will be inserted.
+     * @param insert <code>true</code> if generated keys should
+     * be inserted or <code>false</code> otherwise.
+     */
+    private void substringKeys(ByteString attValue,
+            Map<byte[], Boolean> modifiedKeys,
+            Boolean insert)
+    {
+      String value = attValue.stringValue();
+      int keyLength = subIndexer.gerSubstringLength();
+      for (int i = 0, remain = value.length(); remain > 0; i++, remain--)
+      {
+        int len = Math.min(keyLength, remain);
+        byte[] keyBytes = makeSubstringKey(value, i, len);
+        Boolean cinsert = modifiedKeys.get(keyBytes);
+        if (cinsert == null)
+        {
+          modifiedKeys.put(keyBytes, insert);
+        }
+        else if (!cinsert.equals(insert))
+        {
+          modifiedKeys.remove(keyBytes);
+        }
+      }
+    }
+
+
+
+    /**
+     * Makes a byte array representing a substring index key for
+     * one substring of a value.
+     *
+     * @param value  The String containing the value.
+     * @param pos The starting position of the substring.
+     * @param len The length of the substring.
+     * @return A byte array containing a substring key.
+     */
+    private byte[] makeSubstringKey(String value, int pos, int len)
+    {
+      String sub = value.substring(pos, pos + len);
+      CollationKey col = collator.getCollationKey(sub);
+      byte[] origKey = col.toByteArray();
+      byte[] newKey = new byte[origKey.length - 4];
+      System.arraycopy(origKey, 0, newKey, 0, newKey.length);
+      return newKey;
+    }
+
+
+
+    /**
+     * Uses an equality index to retrieve the entry IDs that might contain a
+     * given initial substring.
+     * @param bytes A normalized initial substring of an attribute value.
+     * @return The candidate entry IDs.
+     */
+    private <T> T matchInitialSubstring(String value,
+            IndexQueryFactory<T> factory)
+    {
+      byte[] lower = makeSubstringKey(value, 0, value.length());
+      byte[] upper = new byte[lower.length];
+      System.arraycopy(lower, 0, upper, 0, lower.length);
+
+      for (int i = upper.length - 1; i >= 0; i--)
+      {
+        if (upper[i] == 0xFF)
+        {
+          // We have to carry the overflow to the more significant byte.
+          upper[i] = 0;
+        }
+        else
+        {
+          // No overflow, we can stop.
+          upper[i] = (byte) (upper[i] + 1);
+          break;
+        }
+      }
+      //Use the shared equality indexer.
+      return factory.createRangeMatchQuery(
+                              indexer.getExtensibleIndexID(),
+                              lower,
+                              upper,
+                              true,
+                              false);
+    }
+
+
+
+    /**
+     * Retrieves the Index Records that might contain a given substring.
+     * @param value A String representing  the attribute value.
+     * @param factory An IndexQueryFactory which issues calls to the backend.
+     * @param substrLength The length of the substring.
+     * @return The candidate entry IDs.
+     */
+    private <T> T matchSubstring(String value,
+            IndexQueryFactory<T> factory)
+    {
+      T intersectionQuery = null;
+      int substrLength = subIndexer.gerSubstringLength();
+
+      if (value.length() < substrLength)
+      {
+        byte[] lower = makeSubstringKey(value, 0, value.length());
+        byte[] upper = makeSubstringKey(value, 0, value.length());
+        for (int i = upper.length - 1; i >= 0; i--)
+        {
+          if (upper[i] == 0xFF)
+          {
+            // We have to carry the overflow to the more significant byte.
+            upper[i] = 0;
+          } else
+          {
+            // No overflow, we can stop.
+            upper[i] = (byte) (upper[i] + 1);
+            break;
+          }
+        }
+        // Read the range: lower <= keys < upper.
+        intersectionQuery =
+                factory.createRangeMatchQuery(
+                subIndexer.getExtensibleIndexID(),
+                lower,
+                upper,
+                true,
+                false);
+      }
+      else
+      {
+        List<T> queryList = new ArrayList<T>();
+        Set<byte[]> set =
+                new TreeSet<byte[]>(new AttributeIndex.KeyComparator());
+        for (int first = 0, last = substrLength;
+                last <= value.length(); first++, last++)
+        {
+          byte[] keyBytes;
+          keyBytes = makeSubstringKey(value, first, substrLength);
+          set.add(keyBytes);
+        }
+
+        for (byte[] keyBytes : set)
+        {
+          T single = factory.createExactMatchQuery(
+                  subIndexer.getExtensibleIndexID(),
+                  keyBytes);
+          queryList.add(single);
+        }
+        intersectionQuery =
+                factory.createIntersectionQuery(queryList);
+      }
+      return intersectionQuery;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public <T> T createIndexQuery(ByteString assertionValue,
+            IndexQueryFactory<T> factory) throws DirectoryException
+    {
+      Assertion assertion = parseAssertion(assertionValue);
+      String subInitial = assertion.getInitial();
+      List<String> subAny = assertion.getAny();
+      String subFinal = assertion.getFinal();
+      List<T> queries = new ArrayList<T>();
+
+      if (subInitial == null && subAny.size() == 0 && subFinal == null)
+      {
+        //Can happen with a filter like "cn:en.6:=*".
+        //Just return an empty record.
+        return factory.createMatchAllQuery();
+      }
+      List<String> elements = new ArrayList<String>();
+      if (subInitial != null)
+      {
+        //Always use the shared indexer for initial match.
+        T query = matchInitialSubstring(subInitial, factory);
+        queries.add(query);
+      }
+
+      if (subAny != null && subAny.size() > 0)
+      {
+        elements.addAll(subAny);
+      }
+
+      if (subFinal != null)
+      {
+        elements.add(subFinal);
+      }
+
+
+      for (String element : elements)
+      {
+        queries.add(matchSubstring(element, factory));
+      }
+      return factory.createIntersectionQuery(queries);
+    }
   }
 
- /**
-  *Collation rule for less-than matching rule.
-  */
-  private final class CollationLessThanMatchingRule
-         extends ExtensibleMatchingRule
+
+
+  /**
+   *An abstract Collation rule for Ordering  matching rule.
+   */
+  private abstract class CollationOrderingMatchingRule
+          extends CollationMatchingRule
   {
-    //Names for this class.
-    private final Collection<String> names;
+    /**
+     * Constructs a new CollationOrderingMatchingRule.
+     *
+     * @param nOID OID of the collation matching rule
+     * @param names names of this matching rule
+     * @param locale Locale of the collation matching rule
+     */
+    private CollationOrderingMatchingRule(String nOID,
+            Collection<String> names, Locale locale)
+    {
+      super(nOID,names,locale);
+    }
 
-    //Collator for performing equality match.
-    private final Collator collator;
 
-    //Numeric OID of the rule.
-    private final String nOID;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public ByteString normalizeValue(ByteString value)
+           throws DirectoryException
+    {
+      CollationKey key = collator.getCollationKey(value.stringValue());
+      return new ASN1OctetString(key.toByteArray());
+    }
+
+
+
+    /**
+     * Compares the first value to the second and returns a value that
+     * indicates their relative order.
+     *
+     * @param  b1  The normalized form of the first value to
+     *                 compare.
+     * @param  b2  The normalized form of the second value to
+     *                 compare.
+     *
+     * @return  A negative integer if {@code value1} should come before
+     *          {@code value2} in ascending order, a positive integer if
+     *          {@code value1} should come after {@code value2} in
+     *          ascending order, or zero if there is no difference
+     *          between the values with regard to ordering.
+     */
+    protected int compare(byte[] b1, byte[] b2) {
+      //Compare values using byte arrays.
+      int minLength = Math.min(b1.length, b2.length);
+
+      for (int i=0; i < minLength; i++)
+      {
+        int firstByte = 0xFF & ((int)b1[i]);
+        int secondByte = 0xFF & ((int)b2[i]);
+
+        if (firstByte == secondByte)
+        {
+          continue;
+        }
+        else if (firstByte < secondByte)
+        {
+          return -1;
+        }
+        else if (firstByte > secondByte)
+        {
+          return 1;
+        }
+      }
+
+      if (b1.length == b2.length)
+      {
+        return 0;
+      }
+      else if (b1.length < b2.length)
+      {
+        return -1;
+      }
+      else
+      {
+        return 1;
+      }
+    }
+  }
+
+  /**
+   * Collation matching rule for Less-than matching rule.
+   */
+  private final class CollationLessThanMatchingRule
+          extends CollationOrderingMatchingRule
+  {
 
     /**
      * Constructs a new CollationLessThanMatchingRule.
@@ -1293,160 +1763,24 @@
      * @param locale Locale of the collation matching rule
      */
     private CollationLessThanMatchingRule(String nOID,
-            Collection<String> names,Locale locale)
+            Collection<String> names, Locale locale)
     {
-      super();
-      this.names = names;
-      this.collator =createCollator(locale);
-      this.nOID = nOID;
+      super(nOID, names, locale);
     }
 
 
 
 
-   /**
-    * {@inheritDoc}
-    */
-    @Override
-    public String getName()
-    {
-       //Concatenate all the names and return.
-      StringBuilder builder = new StringBuilder();
-      for(String name: getAllNames())
-      {
-        builder.append(name);
-        builder.append("\b");
-      }
-      return builder.toString();
-    }
-
-
-
-
-   /**
-    * {@inheritDoc}
-    */
-    @Override
-    public Collection<String> getAllNames()
-    {
-      return Collections.unmodifiableCollection(names);
-    }
-
-
-
-
-    /**
-    * {@inheritDoc}
-    */
-    @Override
-    public String getOID()
-    {
-      return nOID;
-    }
-
-
-
-    /**
-    * {@inheritDoc}
-    */
-    @Override
-    public String getDescription()
-    {
-      // There is no standard description for this matching rule.
-      return null;
-    }
-
-
-
-    /**
-    * {@inheritDoc}
-    */
-    @Override
-    public String getSyntaxOID()
-    {
-      return SYNTAX_DIRECTORY_STRING_OID;
-    }
-
-
-
-    /**
-    * {@inheritDoc}
-    */
-    @Override
-    public ByteString normalizeValue(ByteString value)
-           throws DirectoryException
-    {
-      CollationKey key = collator.getCollationKey(value.stringValue());
-      return new ASN1OctetString(key.toByteArray());
-    }
-
-
-
-   /**
-    * Compares the first value to the second and returns a value that
-    * indicates their relative order.
-    *
-    * @param  b1  The normalized form of the first value to
-    *                 compare.
-    * @param  b2  The normalized form of the second value to
-    *                 compare.
-    *
-    * @return  A negative integer if {@code value1} should come before
-    *          {@code value2} in ascending order, a positive integer if
-    *          {@code value1} should come after {@code value2} in
-    *          ascending order, or zero if there is no difference
-    *          between the values with regard to ordering.
-    */
-    private int compare(byte[] b1, byte[] b2)
-    {
-      //Compare values using byte arrays.
-      int minLength = Math.min(b1.length, b2.length);
-
-      for (int i=0; i < minLength; i++)
-      {
-        int firstByte = 0xFF & ((int)b1[i]);
-        int secondByte = 0xFF & ((int)b2[i]);
-
-        if (firstByte == secondByte)
-        {
-          continue;
-        }
-        else if (firstByte < secondByte)
-        {
-          return -1;
-        }
-        else if (firstByte > secondByte)
-        {
-          return 1;
-        }
-      }
-
-      if (b1.length == b2.length)
-      {
-        return 0;
-      }
-      else if (b1.length < b2.length)
-      {
-        return -1;
-      }
-      else
-      {
-        return 1;
-      }
-    }
-
-
-
     /**
      * {@inheritDoc}
      */
     @Override
     public ConditionResult valuesMatch(ByteString attributeValue,
-                                       ByteString assertionValue)
+            ByteString assertionValue)
     {
-      int ret = compare(attributeValue.value(),assertionValue.value());
+      int ret = compare(attributeValue.value(), assertionValue.value());
 
-      if(ret <0)
+      if (ret < 0)
       {
         return ConditionResult.TRUE;
       }
@@ -1455,24 +1789,35 @@
         return ConditionResult.FALSE;
       }
     }
+
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public <T> T createIndexQuery(ByteString assertionValue,
+            IndexQueryFactory<T> factory) throws DirectoryException
+    {
+      byte[] lower = new byte[0];
+      byte[] upper = normalizeValue(assertionValue).value();
+      return factory.createRangeMatchQuery(indexer.getExtensibleIndexID(),
+              lower,
+              upper,
+              false,
+              false);
+    }
   }
 
 
+
   /**
-  * Collation rule for less-than-equal-to matching rule.
-  */
+   * Collation rule for less-than-equal-to matching rule.
+   */
   private final class CollationLessThanOrEqualToMatchingRule
-         extends ExtensibleMatchingRule
+          extends CollationOrderingMatchingRule
   {
-    //Names for this class.
-    private final Collection<String> names;
-
-    //Collator for performing equality match.
-    private final Collator collator;
-
-    //Numeric OID of the rule.
-    private final String nOID;
-
 
     /**
      * Constructs a new CollationLessThanOrEqualToMatchingRule.
@@ -1482,161 +1827,25 @@
      * @param locale Locale of the collation matching rule
      */
     private CollationLessThanOrEqualToMatchingRule(String nOID,
-                                    Collection<String> names,
-                                    Locale locale)
+            Collection<String> names,
+            Locale locale)
     {
-      super();
-      this.names = names;
-      this.collator =createCollator(locale);
-      this.nOID = nOID;
+      super(nOID, names, locale);
     }
 
 
 
-   /**
-    * {@inheritDoc}
-    */
-    @Override
-    public String getName()
-    {
-      //Concatenate all the names and return.
-      StringBuilder builder = new StringBuilder();
-      for(String name: getAllNames())
-      {
-        builder.append(name);
-        builder.append("\b");
-      }
-      return builder.toString();
-    }
-
-
-
-   /**
-    * {@inheritDoc}
-    */
-    @Override
-    public Collection<String> getAllNames()
-    {
-      return Collections.unmodifiableCollection(names);
-    }
-
-
-
-
-   /**
-    * {@inheritDoc}
-    */
-    @Override
-    public String getOID()
-    {
-      return nOID;
-    }
-
-
-
-
-   /**
-    * {@inheritDoc}
-    */
-    @Override
-    public String getDescription()
-    {
-      // There is no standard description for this matching rule.
-      return null;
-    }
-
-
-
-
-    /**
-    * {@inheritDoc}
-    */
-    @Override
-    public String getSyntaxOID()
-    {
-      return SYNTAX_DIRECTORY_STRING_OID;
-    }
-
-
-
-   /**
-    * {@inheritDoc}
-    */
-    @Override
-    public ByteString normalizeValue(ByteString value)
-           throws DirectoryException
-    {
-      CollationKey key = collator.getCollationKey(value.stringValue());
-      return new ASN1OctetString(key.toByteArray());
-    }
-
-
-
-   /**
-    * Compares the first value to the second and returns a value that
-    * indicates their relative order.
-    *
-    * @param  b1  The normalized form of the first value to
-    *                 compare.
-    * @param  b2  The normalized form of the second value to
-    *                 compare.
-    *
-    * @return  A negative integer if {@code value1} should come before
-    *          {@code value2} in ascending order, a positive integer if
-    *          {@code value1} should come after {@code value2} in
-    *          ascending order, or zero if there is no difference
-    *          between the values with regard to ordering.
-    */
-    public int compare(byte[] b1, byte[] b2)
-    {
-      //Compare values using byte arrays.
-      int minLength = Math.min(b1.length, b2.length);
-
-      for (int i=0; i < minLength; i++)
-      {
-        int firstByte = 0xFF & ((int)b1[i]);
-        int secondByte = 0xFF & ((int)b2[i]);
-
-        if (firstByte == secondByte)
-        {
-          continue;
-        }
-        else if (firstByte < secondByte)
-        {
-          return -1;
-        }
-        else if (firstByte > secondByte)
-        {
-          return 1;
-        }
-      }
-
-      if (b1.length == b2.length)
-      {
-        return 0;
-      }
-      else if (b1.length < b2.length)
-      {
-        return -1;
-      }
-      else
-      {
-        return 1;
-      }
-    }
-
-
 
     /**
      * {@inheritDoc}
      */
     @Override
     public ConditionResult valuesMatch(ByteString attributeValue,
-                                       ByteString assertionValue)
+            ByteString assertionValue)
     {
-      int ret = compare(attributeValue.value(),assertionValue.value());
+      int ret = compare(attributeValue.value(), assertionValue.value());
 
-      if(ret <= 0)
+      if (ret <= 0)
       {
         return ConditionResult.TRUE;
       }
@@ -1645,25 +1854,37 @@
         return ConditionResult.FALSE;
       }
     }
+
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public <T> T createIndexQuery(ByteString assertionValue,
+            IndexQueryFactory<T> factory)
+            throws DirectoryException
+    {
+      byte[] lower = new byte[0];
+      byte[] upper = normalizeValue(assertionValue).value();
+      // Read the range: lower < keys <= upper.
+      return factory.createRangeMatchQuery(indexer.getExtensibleIndexID(),
+              lower,
+              upper,
+              false,
+              true);
+    }
   }
 
 
 
- /**
-  * Collation rule for greater-than matching rule.
-  */
+  /**
+   * Collation rule for greater-than matching rule.
+   */
   private final class CollationGreaterThanMatchingRule
-         extends ExtensibleMatchingRule
+          extends CollationOrderingMatchingRule
   {
-    //Names for this class.
-    private final Collection<String> names;
-
-    //Collator for performing equality match.
-    private final Collator collator;
-
-    //Numeric OID of the rule.
-    private final String nOID;
-
 
     /**
      * Constructs a new CollationGreaterThanMatchingRule.
@@ -1673,147 +1894,9 @@
      * @param locale Locale of the collation matching rule
      */
     private CollationGreaterThanMatchingRule(String nOID,
-            Collection<String> names,Locale locale)
+            Collection<String> names, Locale locale)
     {
-      super();
-      this.names = names;
-      this.collator =createCollator(locale);
-      this.nOID = nOID;
-    }
-
-
-
-
-   /**
-    * {@inheritDoc}
-    */
-    @Override
-    public String getName()
-    {
-     //Concatenate all the names and return.
-      StringBuilder builder = new StringBuilder();
-      for(String name: getAllNames())
-      {
-        builder.append(name);
-        builder.append("\b");
-      }
-      return builder.toString();
-    }
-
-
-
-
-   /**
-    * {@inheritDoc}
-    */
-    @Override
-    public Collection<String> getAllNames()
-    {
-      return Collections.unmodifiableCollection(names);
-    }
-
-
-
-
-   /**
-    * {@inheritDoc}
-    */
-    @Override
-    public String getOID()
-    {
-      return nOID;
-    }
-
-
-
-   /**
-    * {@inheritDoc}
-    */
-    @Override
-    public String getDescription()
-    {
-      // There is no standard description for this matching rule.
-      return null;
-    }
-
-
-
-   /**
-    * {@inheritDoc}
-    */
-    @Override
-    public String getSyntaxOID()
-    {
-      return SYNTAX_DIRECTORY_STRING_OID;
-    }
-
-
-
-
-   /**
-    * {@inheritDoc}
-    */
-    @Override
-    public ByteString normalizeValue(ByteString value)
-           throws DirectoryException
-    {
-      CollationKey key = collator.getCollationKey(value.stringValue());
-      return new ASN1OctetString(key.toByteArray());
-    }
-
-
-
-   /**
-    * Compares the first value to the second and returns a value that
-    * indicates their relative order.
-    *
-    * @param  b1  The normalized form of the first value to
-    *                 compare.
-    * @param  b2  The normalized form of the second value to
-    *                 compare.
-    *
-    * @return  A negative integer if {@code value1} should come before
-    *          {@code value2} in ascending order, a positive integer if
-    *          {@code value1} should come after {@code value2} in
-    *          ascending order, or zero if there is no difference
-    *          between the values with regard to ordering.
-    */
-    public int compare(byte[] b1, byte[] b2)
-    {
-      //Compare values using byte arrays.
-      int minLength = Math.min(b1.length, b2.length);
-
-      for (int i=0; i < minLength; i++)
-      {
-        int firstByte = 0xFF & ((int)b1[i]);
-        int secondByte = 0xFF & ((int)b2[i]);
-
-        if (firstByte == secondByte)
-        {
-          continue;
-        }
-        else if (firstByte < secondByte)
-        {
-          return -1;
-        }
-        else if (firstByte > secondByte)
-        {
-          return 1;
-        }
-      }
-
-      if (b1.length == b2.length)
-      {
-        return 0;
-      }
-      else if (b1.length < b2.length)
-      {
-        return -1;
-      }
-      else
-      {
-        return 1;
-      }
+      super(nOID, names, locale);
     }
 
 
@@ -1823,39 +1906,45 @@
      */
     @Override
     public ConditionResult valuesMatch(ByteString attributeValue,
-                                       ByteString assertionValue)
+            ByteString assertionValue)
     {
-      int ret = compare(attributeValue.value(),assertionValue.value());
+      int ret = compare(attributeValue.value(), assertionValue.value());
 
-      if(ret > 0)
-      {
+      if (ret > 0) {
         return ConditionResult.TRUE;
-      }
-      else
-      {
+      } else {
         return ConditionResult.FALSE;
       }
     }
+
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public <T> T createIndexQuery(ByteString assertionValue,
+            IndexQueryFactory<T> factory)
+            throws DirectoryException
+    {
+      byte[] lower = normalizeValue(assertionValue).value();
+      byte[] upper = new byte[0];
+      return factory.createRangeMatchQuery(indexer.getExtensibleIndexID(),
+              lower,
+              upper,
+              false,
+              false);
+    }
   }
 
-
- /**
-  * Collation rule for greater-than-equal-to matching rule.
-  */
+  /**
+   * Collation rule for greater-than-equal-to matching rule.
+   */
   private final class CollationGreaterThanOrEqualToMatchingRule
-         extends ExtensibleMatchingRule
+          extends CollationOrderingMatchingRule
   {
 
-    //Names for this class.
-    private final Collection<String> names;
-
-    //Collator for performing equality match.
-    private final Collator collator;
-
-    //Numeric OID of the rule.
-    private final String nOID;
-
-
     /**
      * Constructs a new CollationGreaterThanOrEqualToMatchingRule.
      *
@@ -1864,143 +1953,31 @@
      * @param locale Locale of the collation matching rule
      */
     private CollationGreaterThanOrEqualToMatchingRule(String nOID,
-                                    Collection<String> names,
-                                    Locale locale)
+            Collection<String> names,
+            Locale locale)
     {
-      super();
-      this.names = names;
-      this.collator =createCollator(locale);
-      this.nOID = nOID;
+      super(nOID, names, locale);
     }
 
 
 
-   /**
-    * {@inheritDoc}
-    */
+
+    /**
+     * {@inheritDoc}
+     */
     @Override
-    public String getName()
+    public ConditionResult valuesMatch(ByteString attributeValue,
+            ByteString assertionValue)
     {
-       //Concatenate all the names and return.
-      StringBuilder builder = new StringBuilder();
-      for(String name: getAllNames())
+      int ret = compare(attributeValue.value(),assertionValue.value());
+
+      if (ret >= 0)
       {
-        builder.append(name);
-        builder.append("\b");
-      }
-      return builder.toString();
-    }
-
-
-
-   /**
-    * {@inheritDoc}
-    */
-    @Override
-    public Collection<String> getAllNames()
-    {
-      return Collections.unmodifiableCollection(names);
-    }
-
-
-
-   /**
-    * {@inheritDoc}
-    */
-    @Override
-    public String getOID()
-    {
-      return nOID;
-    }
-
-
-
-   /**
-    * {@inheritDoc}
-    */
-    @Override
-    public String getDescription()
-    {
-      // There is no standard description for this matching rule.
-      return null;
-    }
-
-
-
-   /**
-    * {@inheritDoc}
-    */
-    @Override
-    public String getSyntaxOID()
-    {
-      return SYNTAX_DIRECTORY_STRING_OID;
-    }
-
-
-
-   /**
-    * {@inheritDoc}
-    */
-    @Override
-    public ByteString normalizeValue(ByteString value)
-           throws DirectoryException
-    {
-      CollationKey key = collator.getCollationKey(value.stringValue());
-      return new ASN1OctetString(key.toByteArray());
-    }
-
-
-
-   /**
-    * Compares the first value to the second and returns a value that
-    * indicates their relative order.
-    *
-    * @param  b1  The normalized form of the first value to
-    *                 compare.
-    * @param  b2  The normalized form of the second value to
-    *                 compare.
-    *
-    * @return  A negative integer if {@code value1} should come before
-    *          {@code value2} in ascending order, a positive integer if
-    *          {@code value1} should come after {@code value2} in
-    *          ascending order, or zero if there is no difference
-    *          between the values with regard to ordering.
-    */
-    public int compare(byte[] b1, byte[] b2)
-    {
-      //Compare values using byte arrays.
-      int minLength = Math.min(b1.length, b2.length);
-
-      for (int i=0; i < minLength; i++)
-      {
-        int firstByte = 0xFF & ((int)b1[i]);
-        int secondByte = 0xFF & ((int)b2[i]);
-
-        if (firstByte == secondByte)
-        {
-          continue;
-        }
-        else if (firstByte < secondByte)
-        {
-          return -1;
-        }
-        else if (firstByte > secondByte)
-        {
-          return 1;
-        }
-      }
-
-      if (b1.length == b2.length)
-      {
-        return 0;
-      }
-      else if (b1.length < b2.length)
-      {
-        return -1;
+        return ConditionResult.TRUE;
       }
       else
       {
-        return 1;
+        return ConditionResult.FALSE;
       }
     }
 
@@ -2010,27 +1987,228 @@
      * {@inheritDoc}
      */
     @Override
-    public ConditionResult valuesMatch(ByteString attributeValue,
-                                       ByteString assertionValue)
+    public <T> T createIndexQuery(ByteString assertionValue,
+            IndexQueryFactory<T> factory)
+            throws DirectoryException
     {
-      int ret = compare(attributeValue.value(),assertionValue.value());
-
-      if(ret >= 0)
-      {
-        return ConditionResult.TRUE;
-      }
-      else
-      {
-        return ConditionResult.FALSE;
-      }
+      byte[] lower = normalizeValue(assertionValue).value();
+      byte[] upper = new byte[0];
+      // Read the range: lower <= keys < upper.
+      return factory.createRangeMatchQuery(indexer.getExtensibleIndexID(),
+              lower,
+              upper,
+              true,
+              false);
     }
   }
 
 
- /**
-  * A utility class for extracting the OID and Language Tag from the
-  * configuration entry.
-  */
+  /**
+   * Extensible Indexer class for Collation Matching rules which share the
+   * same index. This Indexer is shared by Equality and Ordering Collation
+   * Matching Rules.
+   */
+  private final class CollationSharedExtensibleIndexer
+          extends ExtensibleIndexer
+  {
+
+    /**
+     * The Extensible Matching Rule.
+     */
+    private final CollationMatchingRule matchingRule;
+
+
+
+    /**
+     * Creates a new instance of CollationSharedExtensibleIndexer.
+     *
+     * @param matchingRule The Collation Matching Rule.
+     */
+    private CollationSharedExtensibleIndexer(
+            CollationMatchingRule matchingRule)
+    {
+      this.matchingRule = matchingRule;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String getExtensibleIndexID()
+    {
+      return EXTENSIBLE_INDEXER_ID_SHARED;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public final void getKeys(AttributeValue value,
+            Set<byte[]> keys)
+    {
+      ByteString key;
+      try
+      {
+        key = matchingRule.normalizeValue(value.getValue());
+        keys.add(key.value());
+      }
+      catch (DirectoryException de)
+      {
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public final void getKeys(AttributeValue value,
+            Map<byte[], Boolean> modifiedKeys,
+            Boolean insert)
+    {
+      Set<byte[]> keys = new HashSet<byte[]>();
+      getKeys(value, keys);
+      for (byte[] key : keys)
+      {
+        Boolean cInsert = modifiedKeys.get(key);
+        if (cInsert == null)
+        {
+          modifiedKeys.put(key, insert);
+        }
+        else if (!cInsert.equals(insert))
+        {
+          modifiedKeys.remove(key);
+        }
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String getPreferredIndexName()
+    {
+      return matchingRule.getIndexName();
+    }
+  }
+
+  /**
+   * Extensible Indexer class for Collation Substring Matching rules.
+   * This Indexer is used  by Substring Collation Matching Rules.
+   */
+  private final class CollationSubstringExtensibleIndexer
+          extends ExtensibleIndexer
+  {
+    //The CollationSubstringMatching Rule.
+    private final CollationSubstringMatchingRule matchingRule;
+
+
+
+    //The substring length.
+    private int substringLen;
+
+
+
+    /**
+     * Creates a new instance of CollationSubstringExtensibleIndexer.
+     *
+     * @param matchingRule The CollationSubstringMatching Rule.
+     * @param substringLen The substring length.
+     */
+    private CollationSubstringExtensibleIndexer(
+            CollationSubstringMatchingRule matchingRule,
+            int substringLen)
+    {
+      this.matchingRule = matchingRule;
+      this.substringLen = substringLen;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void getKeys(AttributeValue value,
+                                Set<byte[]> keys)
+    {
+      matchingRule.subtringKeys(value.getValue(),
+                                keys);
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void getKeys(AttributeValue attValue,
+            Map<byte[], Boolean> modifiedKeys,
+            Boolean insert)
+    {
+      matchingRule.substringKeys(attValue.getValue(),
+              modifiedKeys,
+              insert);
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String getPreferredIndexName()
+    {
+      return matchingRule.getIndexName();
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String getExtensibleIndexID()
+    {
+      return EXTENSIBLE_INDEXER_ID_SUBSTRING;
+    }
+
+
+
+    /**
+     * Returns the substring length.
+     * @return The length of the substring.
+     */
+    private int gerSubstringLength()
+    {
+      return substringLen;
+    }
+
+
+
+    /**
+     * Sets the substring length.
+     * @param substringLen The substring length.
+     */
+    private void setSubstringLength(int substringLen)
+    {
+      this.substringLen = substringLen;
+    }
+  }
+
+
+
+  /**
+   * A utility class for extracting the OID and Language Tag from the
+   * configuration entry.
+   */
   private final class CollationMapper
   {
     //OID of the collation rule.
diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/IndexConfig.java b/opendj-sdk/opends/src/server/org/opends/server/types/IndexConfig.java
new file mode 100644
index 0000000..377a386
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/types/IndexConfig.java
@@ -0,0 +1,41 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Copyright 2009 Sun Microsystems, Inc.
+ */
+
+
+package org.opends.server.types;
+
+/**
+ * This class represents the configuration of an index.
+ */
+public abstract class IndexConfig
+{
+  /**
+   * Returns the length of a substring.
+   * @return the length of the substring.
+   */
+  public abstract int getSubstringLength();
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/Schema.java b/opendj-sdk/opends/src/server/org/opends/server/types/Schema.java
index f969e45..52216f3 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/types/Schema.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/types/Schema.java
@@ -20,9 +20,9 @@
  *      Portions Copyright [yyyy] [name of copyright owner]
  *
  * CDDL HEADER END
+ *©
  *
- *
- *      Copyright 2006-2008 Sun Microsystems, Inc.
+ *      Copyright 2006-2009 Sun Microsystems, Inc.
  */
 package org.opends.server.types;
 
@@ -47,6 +47,7 @@
 import org.opends.server.api.ApproximateMatchingRule;
 import org.opends.server.api.AttributeSyntax;
 import org.opends.server.api.EqualityMatchingRule;
+import org.opends.server.api.ExtensibleMatchingRule;
 import org.opends.server.api.MatchingRule;
 import org.opends.server.api.OrderingMatchingRule;
 import org.opends.server.api.SubstringMatchingRule;
@@ -149,6 +150,12 @@
   private ConcurrentHashMap<String,SubstringMatchingRule>
                substringMatchingRules;
 
+  // The set of extensible matching rules for this schema, mapped
+  // between the lowercase names and OID for the definition and the
+  // matching rule itself.
+  private ConcurrentHashMap<String,ExtensibleMatchingRule>
+               extensibleMatchingRules;
+
   // The set of matching rule uses for this schema, mapped between the
   // matching rule for the definition and the matching rule use
   // itself.
@@ -242,6 +249,8 @@
          new ConcurrentHashMap<String,OrderingMatchingRule>();
     substringMatchingRules =
          new ConcurrentHashMap<String,SubstringMatchingRule>();
+    extensibleMatchingRules =
+        new ConcurrentHashMap<String,ExtensibleMatchingRule>();
     matchingRuleUses =
          new ConcurrentHashMap<MatchingRule,MatchingRuleUse>();
     ditContentRules =
@@ -1001,6 +1010,11 @@
       registerSubstringMatchingRule(
            (SubstringMatchingRule) matchingRule, overwriteExisting);
     }
+   else if(matchingRule instanceof ExtensibleMatchingRule)
+   {
+      registerExtensibleMatchingRule(
+           (ExtensibleMatchingRule) matchingRule,overwriteExisting);
+   }
     else
     {
       synchronized (matchingRules)
@@ -1834,6 +1848,179 @@
 
 
   /**
+   * Retrieves the extensible matching rule definitions for this
+   * schema, as a mapping between the lowercase names and OIDs for the
+   * matching rule and the matching rule itself.  Each matching rule
+   * may be associated with multiple keys (once for the OID and again
+   * for each name).
+   *
+   * @return  The extensible matching rule definitions for this
+   *          schema.
+   */
+  public Map<String,ExtensibleMatchingRule>
+              getExtensibleMatchingRules()
+  {
+    return Collections.unmodifiableMap(extensibleMatchingRules);
+  }
+
+
+
+  /**
+   * Retrieves the extensible matching rule definition with the
+   * specified name or OID.
+   *
+   * @param  lowerName  The name or OID of the matching rule to
+   *                    retrieve, formatted in all lowercase
+   *                    characters.
+   *
+   * @return  The requested matching rule, or <CODE>null</CODE> if no
+   *          extensible matching rule is registered with the
+   *          provided name or OID.
+   */
+  public ExtensibleMatchingRule getExtensibleMatchingRule(
+                                      String lowerName)
+  {
+    return extensibleMatchingRules.get(lowerName);
+  }
+
+
+
+  /**
+   * Registers the provided extensible matching rule with this
+   * schema.
+   *
+   * @param  matchingRule       The extensible matching rule to
+   *                            register.
+   * @param  overwriteExisting  Indicates whether to overwrite an
+   *                            existing mapping if there are any
+   *                            conflicts (i.e., another matching rule
+   *                            with the same OID or name).
+   *
+   * @throws  DirectoryException  If a conflict is encountered and the
+   *                              <CODE>overwriteExisting</CODE> flag
+   *                              is set to <CODE>false</CODE>
+   */
+  public void registerExtensibleMatchingRule(
+                   ExtensibleMatchingRule matchingRule,
+                   boolean overwriteExisting)
+         throws DirectoryException
+  {
+    synchronized (matchingRules)
+    {
+      if (! overwriteExisting)
+      {
+        String oid = toLowerCase(matchingRule.getOID());
+        if (matchingRules.containsKey(oid))
+        {
+          MatchingRule conflictingRule = matchingRules.get(oid);
+
+          Message message = ERR_SCHEMA_CONFLICTING_MR_OID.
+              get(matchingRule.getNameOrOID(), oid,
+                  conflictingRule.getNameOrOID());
+          throw new DirectoryException(
+                         ResultCode.CONSTRAINT_VIOLATION, message);
+        }
+
+       for(String name:matchingRule.getAllNames())
+       {
+        if (name != null)
+        {
+          name = toLowerCase(name);
+          if (matchingRules.containsKey(name))
+          {
+            MatchingRule conflictingRule = matchingRules.get(name);
+
+            Message message = ERR_SCHEMA_CONFLICTING_MR_NAME.
+                get(matchingRule.getOID(), name,
+                    conflictingRule.getOID());
+            throw new DirectoryException(
+                           ResultCode.CONSTRAINT_VIOLATION, message);
+          }
+        }
+       }
+      }
+
+      String oid = toLowerCase(matchingRule.getOID());
+      extensibleMatchingRules.put(oid, matchingRule);
+      matchingRules.put(oid, matchingRule);
+
+      for(String name:matchingRule.getAllNames())
+      {
+        if (name != null)
+        {
+          name = toLowerCase(name);
+          extensibleMatchingRules.put(name, matchingRule);
+          matchingRules.put(name, matchingRule);
+        }
+      }
+      // We'll use an attribute value including the normalized value
+      // rather than the attribute type because otherwise it would use
+      // a very expensive matching rule (OID first component match)
+      // that would kill performance.
+      String valueString = matchingRule.toString();
+      ASN1OctetString rawValue  = new ASN1OctetString(valueString);
+      ByteString normValue = normalizationMatchingRule.normalizeValue(
+                                  new ASN1OctetString(valueString));
+      matchingRuleSet.add(new AttributeValue(rawValue, normValue));
+    }
+  }
+
+
+
+  /**
+   * Deregisters the provided extensible  matching rule definition
+   * with this schema.
+   *
+   * @param  matchingRule  The extensible matching rule to deregister
+   *                       with this schema.
+   */
+    public void deregisterExtensibleMatchingRule(
+                   ExtensibleMatchingRule matchingRule)
+   {
+    synchronized (matchingRules)
+    {
+      String oid = matchingRule.getOID();
+      extensibleMatchingRules.remove(oid, matchingRule);
+      matchingRules.remove(oid, matchingRule);
+
+      for(String name:matchingRule.getAllNames())
+      {
+        if (name != null)
+        {
+          name = toLowerCase(name);
+          extensibleMatchingRules.remove(name, matchingRule);
+          matchingRules.remove(name, matchingRule);
+        }
+      }
+      // We'll use an attribute value including the normalized value
+      // rather than the attribute type because otherwise it would use
+      // a very expensive matching rule (OID first component match)
+      // that would kill performance.
+      try
+      {
+        String valueString = matchingRule.toString();
+        ASN1OctetString rawValue = new ASN1OctetString(valueString);
+        ByteString normValue =
+             normalizationMatchingRule.normalizeValue(
+                  new ASN1OctetString(valueString));
+        matchingRuleSet.remove(new AttributeValue(rawValue,
+                                                  normValue));
+      }
+      catch (Exception e)
+      {
+        String valueString = matchingRule.toString();
+        ASN1OctetString rawValue = new ASN1OctetString(valueString);
+        ASN1OctetString normValue =
+             new ASN1OctetString(toLowerCase(valueString));
+        matchingRuleSet.remove(new AttributeValue(rawValue,
+                                                  normValue));
+      }
+    }
+  }
+
+
+
+  /**
    * Retrieves the matching rule use definitions for this schema, as a
    * mapping between the matching rule for the matching rule use
    * definition and the matching rule use itself.  Each matching rule
@@ -2942,6 +3129,7 @@
     dupSchema.equalityMatchingRules.putAll(equalityMatchingRules);
     dupSchema.orderingMatchingRules.putAll(orderingMatchingRules);
     dupSchema.substringMatchingRules.putAll(substringMatchingRules);
+    dupSchema.extensibleMatchingRules.putAll(extensibleMatchingRules);
     dupSchema.matchingRuleUses.putAll(matchingRuleUses);
     dupSchema.ditContentRules.putAll(ditContentRules);
     dupSchema.ditStructureRulesByID.putAll(ditStructureRulesByID);
@@ -3579,6 +3767,13 @@
       syntaxSet.clear();
       syntaxSet = null;
     }
+
+    if (extensibleMatchingRules != null)
+    {
+      extensibleMatchingRules.clear();
+      extensibleMatchingRules = null;
+    }
+
   }
 }
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/util/ServerConstants.java b/opendj-sdk/opends/src/server/org/opends/server/util/ServerConstants.java
index d18352c..084cb82 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/util/ServerConstants.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/util/ServerConstants.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2006-2008 Sun Microsystems, Inc.
+ *      Copyright 2006-2009 Sun Microsystems, Inc.
  */
 package org.opends.server.util;
 
@@ -2926,6 +2926,22 @@
 
 
   /**
+   * The extensible indexer identifier string that will be used for a substring
+   * type.
+   */
+  public static final String EXTENSIBLE_INDEXER_ID_SUBSTRING="substring";
+
+
+
+  /**
+   * The extensible indexer identifier string that will be used for a shared
+   * type.
+   */
+  public static final String EXTENSIBLE_INDEXER_ID_SHARED="shared";
+
+
+
+  /**
    * The lines that make up the CDDL header.  They will not have any prefix, so
    * an appropriate prefix may need to be added for some cases (e.g., "# " for
    * shell scripts, "rem " for batch files, etc.).

--
Gitblit v1.10.0