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

matthew_swift
05.42.2009 22094368c2865dcfb6daf8366425212b721a4657
opends/src/server/org/opends/server/schema/CollationMatchingRuleFactory.java
@@ -25,14 +25,22 @@
 *      Copyright 2008-2009 Sun Microsystems, Inc.
 */
package org.opends.server.schema;
import static org.opends.messages.ConfigMessages.*;
import static org.opends.messages.CoreMessages.*;
import static org.opends.messages.SchemaMessages.*;
import static org.opends.server.loggers.ErrorLogger.*;
import static org.opends.server.schema.SchemaConstants.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
import java.nio.CharBuffer;
import java.text.CollationKey;
import java.text.Collator;
import java.nio.CharBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -40,99 +48,90 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
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.meta.
  CollationMatchingRuleCfgDefn.MatchingRuleType;
import org.opends.server.admin.std.server.CollationMatchingRuleCfg;
import org.opends.server.api.ExtensibleIndexer;
import org.opends.server.api.ExtensibleMatchingRule;
import org.opends.server.api.IndexQueryFactory;
import org.opends.server.api.MatchingRule;
import org.opends.server.api.MatchingRuleFactory;
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.ByteSequence;
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;
import static org.opends.server.schema.SchemaConstants.*;
import static org.opends.server.loggers.ErrorLogger.logError;
import static org.opends.messages.SchemaMessages.*;
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.*;
/**
 * This class is a factory class for Collation matching rules. It creates
 * different matching rules based on the configuration entries.
 * This class is a factory class for Collation matching rules. It
 * creates different matching rules based on the configuration entries.
 */
public final class CollationMatchingRuleFactory
        extends MatchingRuleFactory<CollationMatchingRuleCfg>
        implements ConfigurationChangeListener<CollationMatchingRuleCfg>
public final class CollationMatchingRuleFactory extends
    MatchingRuleFactory<CollationMatchingRuleCfg> implements
    ConfigurationChangeListener<CollationMatchingRuleCfg>
{
  //Whether equality matching rules are enabled.
  // Whether equality matching rules are enabled.
  private boolean equalityMatchingRuleType;
  //Whether less-than matching rules are enabled.
  // Whether less-than matching rules are enabled.
  private boolean lessThanMatchingRuleType;
  //Whether less-than-equal-to matching rules are enabled.
  // Whether less-than-equal-to matching rules are enabled.
  private boolean lessThanEqualToMatchingRuleType;
  //Whether less-than-equal-to matching rules are enabled.
  // Whether less-than-equal-to matching rules are enabled.
  private boolean greaterThanMatchingRuleType;
  //Whether greater-than matching rules are enabled.
  // Whether greater-than matching rules are enabled.
  private boolean greaterThanEqualToMatchingRuleType;
  //Whether greater-than-equal-to matching rules are enabled.
  // Whether greater-than-equal-to matching rules are enabled.
  private boolean substringMatchingRuleType;
  //Stores the list of available locales on this JVM.
  // Stores the list of available locales on this JVM.
  private static final Set<Locale> supportedLocales;
  //Current Configuration.
  // Current Configuration.
  private CollationMatchingRuleCfg currentConfig;
  //Map of OID and the Matching Rule.
  private  final Map<String, MatchingRule> matchingRules;
  // Map of OID and the Matching Rule.
  private final Map<String, MatchingRule> matchingRules;
  static
  {
    supportedLocales = new HashSet<Locale>();
    for(Locale l:Locale.getAvailableLocales())
    for (Locale l : Locale.getAvailableLocales())
    {
      supportedLocales.add(l);
    }
  }
  /**
   * Creates a new instance of CollationMatchingRuleFactory.
   */
  public CollationMatchingRuleFactory()
  {
    //Initialize the matchingRules.
    matchingRules = new HashMap<String,MatchingRule>();
    // Initialize the matchingRules.
    matchingRules = new HashMap<String, MatchingRule>();
  }
@@ -151,11 +150,13 @@
  /**
   * Adds a new mapping of OID and MatchingRule.
   *
   * @param oid OID of the matching rule
   * @param matchingRule instance of a MatchingRule.
   * @param oid
   *          OID of the matching rule
   * @param matchingRule
   *          instance of a MatchingRule.
   */
  private final void addMatchingRule(String oid,
          MatchingRule matchingRule)
      MatchingRule matchingRule)
  {
    matchingRules.put(oid, matchingRule);
  }
@@ -165,8 +166,9 @@
  /**
   * Returns the Matching rule for the specified OID.
   *
   * @param oid OID of the matching rule to be searched.
   * @return  MatchingRule corresponding to an OID.
   * @param oid
   *          OID of the matching rule to be searched.
   * @return MatchingRule corresponding to an OID.
   */
  private final MatchingRule getMatchingRule(String oid)
  {
@@ -175,7 +177,6 @@
  /**
   * Clears the Map containing matching Rules.
   */
@@ -189,35 +190,36 @@
  /**
   * Reads the configuration and initializes matching rule types.
   *
   * @param  ruleTypes  The Set containing allowed matching rule types.
   * @param ruleTypes
   *          The Set containing allowed matching rule types.
   */
  private void initializeMatchingRuleTypes(SortedSet<MatchingRuleType>
          ruleTypes)
  private void initializeMatchingRuleTypes(
      SortedSet<MatchingRuleType> ruleTypes)
  {
    for(MatchingRuleType type:ruleTypes)
    for (MatchingRuleType type : ruleTypes)
    {
      switch(type)
      switch (type)
      {
        case EQUALITY:
          equalityMatchingRuleType = true;
          break;
        case LESS_THAN:
          lessThanMatchingRuleType = true;
          break;
        case LESS_THAN_OR_EQUAL_TO:
          lessThanEqualToMatchingRuleType = true;
          break;
        case GREATER_THAN:
          greaterThanMatchingRuleType = true;
          break;
        case GREATER_THAN_OR_EQUAL_TO:
          greaterThanEqualToMatchingRuleType = true;
          break;
        case SUBSTRING:
          substringMatchingRuleType = true;
          break;
        default:
        //No default values allowed.
      case EQUALITY:
        equalityMatchingRuleType = true;
        break;
      case LESS_THAN:
        lessThanMatchingRuleType = true;
        break;
      case LESS_THAN_OR_EQUAL_TO:
        lessThanEqualToMatchingRuleType = true;
        break;
      case GREATER_THAN:
        greaterThanMatchingRuleType = true;
        break;
      case GREATER_THAN_OR_EQUAL_TO:
        greaterThanEqualToMatchingRuleType = true;
        break;
      case SUBSTRING:
        substringMatchingRuleType = true;
        break;
      default:
        // No default values allowed.
      }
    }
  }
@@ -227,7 +229,8 @@
  /**
   * Creates a new Collator instance.
   *
   * @param locale Locale for the collator
   * @param locale
   *          Locale for the collator
   * @return Returns a new Collator instance
   */
  private Collator createCollator(Locale locale)
@@ -244,49 +247,52 @@
   * {@inheritDoc}
   */
  @Override
  public void initializeMatchingRule(CollationMatchingRuleCfg configuration)
  throws ConfigException, InitializationException
  public void initializeMatchingRule(
      CollationMatchingRuleCfg configuration) throws ConfigException,
      InitializationException
  {
    initializeMatchingRuleTypes(configuration.getMatchingRuleType());
    for(String collation:configuration.getCollation())
    for (String collation : configuration.getCollation())
    {
      CollationMapper mapper = new CollationMapper(collation);
      String nOID = mapper.getNumericOID();
      String languageTag = mapper.getLanguageTag();
      if(nOID==null || languageTag==null)
      if (nOID == null || languageTag == null)
      {
        Message msg =
                WARN_ATTR_INVALID_COLLATION_MATCHING_RULE_FORMAT.
                get(collation);
            WARN_ATTR_INVALID_COLLATION_MATCHING_RULE_FORMAT
                .get(collation);
        logError(msg);
        continue;
      }
      Locale locale = getLocale(languageTag);
      if(locale!=null)
      if (locale != null)
      {
        createLessThanMatchingRule(mapper,locale);
        createLessThanOrEqualToMatchingRule(mapper,locale);
        createEqualityMatchingRule(mapper,locale);
        createGreaterThanOrEqualToMatchingRule(mapper,locale);
        createGreaterThanMatchingRule(mapper,locale);
        createSubstringMatchingRule(mapper,locale);
        createLessThanMatchingRule(mapper, locale);
        createLessThanOrEqualToMatchingRule(mapper, locale);
        createEqualityMatchingRule(mapper, locale);
        createGreaterThanOrEqualToMatchingRule(mapper, locale);
        createGreaterThanMatchingRule(mapper, locale);
        createSubstringMatchingRule(mapper, locale);
      }
      else
      {
        //This locale is not supported by JVM.
        // This locale is not supported by JVM.
        Message msg =
              WARN_ATTR_INVALID_COLLATION_MATCHING_RULE_LOCALE.
              get(collation,configuration.dn().toNormalizedString(),
            WARN_ATTR_INVALID_COLLATION_MATCHING_RULE_LOCALE.get(
                collation, configuration.dn().toNormalizedString(),
                languageTag);
        logError(msg);
      }
    }
    //Save this configuration.
    // Save this configuration.
    currentConfig = configuration;
    //Register for change events.
    // Register for change events.
    currentConfig.addCollationChangeListener(this);
  }
@@ -298,7 +304,7 @@
  @Override
  public void finalizeMatchingRule()
  {
    //De-register the listener.
    // De-register the listener.
    currentConfig.removeCollationChangeListener(this);
  }
@@ -308,64 +314,68 @@
   * {@inheritDoc}
   */
  public ConfigChangeResult applyConfigurationChange(
                                 CollationMatchingRuleCfg configuration)
      CollationMatchingRuleCfg configuration)
  {
    ResultCode        resultCode          = ResultCode.SUCCESS;
    boolean           adminActionRequired = false;
    ArrayList<Message> messages            = new ArrayList<Message>();
    ResultCode resultCode = ResultCode.SUCCESS;
    boolean adminActionRequired = false;
    ArrayList<Message> messages = new ArrayList<Message>();
    if(!configuration.isEnabled() ||
            currentConfig.isEnabled()!=configuration.isEnabled())
    if (!configuration.isEnabled()
        || currentConfig.isEnabled() != configuration.isEnabled())
    {
      //Don't do anything if:
      // Don't do anything if:
      // 1. The configuration is disabled.
      // 2. There is a change in the enable status
      //  i.e. (disable->enable or enable->disable). In this case, the
      //     ConfigManager will have already created the new Factory object.
      return new ConfigChangeResult(resultCode,
              adminActionRequired, messages);
      // i.e. (disable->enable or enable->disable). In this case, the
      // ConfigManager will have already created the new Factory object.
      return new ConfigChangeResult(resultCode, adminActionRequired,
          messages);
    }
    //Since we have come here it means that this Factory is enabled and
    //there is a change in the CollationMatchingRuleFactory's configuration.
    // Deregister all the Matching Rule corresponding to this factory..
    for(MatchingRule rule: getMatchingRules())
    // Since we have come here it means that this Factory is enabled and
    // there is a change in the CollationMatchingRuleFactory's
    // configuration.
    // Deregister all the Matching Rule corresponding to this factory.
    for (MatchingRule rule : getMatchingRules())
    {
      DirectoryServer.deregisterMatchingRule(rule);
    }
    //Clear the associated matching rules.
    // Clear the associated matching rules.
    resetRules();
    initializeMatchingRuleTypes(configuration.getMatchingRuleType());
    for(String collation:configuration.getCollation())
    for (String collation : configuration.getCollation())
    {
      CollationMapper mapper = new CollationMapper(collation);
      String languageTag = mapper.getLanguageTag();
      Locale locale = getLocale(languageTag);
      createLessThanMatchingRule(mapper,locale);
      createLessThanOrEqualToMatchingRule(mapper,locale);
      createEqualityMatchingRule(mapper,locale);
      createGreaterThanOrEqualToMatchingRule(mapper,locale);
      createGreaterThanMatchingRule(mapper,locale);
      createSubstringMatchingRule(mapper,locale);
      createLessThanMatchingRule(mapper, locale);
      createLessThanOrEqualToMatchingRule(mapper, locale);
      createEqualityMatchingRule(mapper, locale);
      createGreaterThanOrEqualToMatchingRule(mapper, locale);
      createGreaterThanMatchingRule(mapper, locale);
      createSubstringMatchingRule(mapper, locale);
    }
    try
    {
      for(MatchingRule matchingRule: getMatchingRules())
      for (MatchingRule matchingRule : getMatchingRules())
      {
        DirectoryServer.registerMatchingRule(matchingRule, false);
      }
    }
    catch (DirectoryException de)
    {
      Message message = WARN_CONFIG_SCHEMA_MR_CONFLICTING_MR.get(
              String.valueOf(configuration.dn()), de.getMessageObject());
      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);
    return new ConfigChangeResult(resultCode, adminActionRequired,
        messages);
  }
@@ -374,44 +384,45 @@
   * {@inheritDoc}
   */
  public boolean isConfigurationChangeAcceptable(
          CollationMatchingRuleCfg configuration,
                      List<Message> unacceptableReasons)
      CollationMatchingRuleCfg configuration,
      List<Message> unacceptableReasons)
  {
    boolean configAcceptable = true;
    //If the new configuration disables this factory, don't do anything.
    if(!configuration.isEnabled())
    // If the new configuration disables this factory, don't do
    // anything.
    if (!configuration.isEnabled())
    {
      return configAcceptable;
    }
    //If it comes here we don't need to verify MatchingRuleType; it should be
    //okay as its syntax is verified by the admin framework. Iterate over the
    //collations and verify if the format is okay. Also, verify if the
    //locale is allowed by the JVM.
    for(String collation:configuration.getCollation())
    // If it comes here we don't need to verify MatchingRuleType; it
    // should be okay as its syntax is verified by the admin framework.
    // Iterate over the collations and verify if the format is okay.
    // Also,
    // verify if the locale is allowed by the JVM.
    for (String collation : configuration.getCollation())
    {
      CollationMapper mapper = new CollationMapper(collation);
      String nOID = mapper.getNumericOID();
      String languageTag = mapper.getLanguageTag();
      if(nOID==null || languageTag==null)
      if (nOID == null || languageTag == null)
      {
        configAcceptable = false;
        Message msg =
                WARN_ATTR_INVALID_COLLATION_MATCHING_RULE_FORMAT.
                get(collation);
            WARN_ATTR_INVALID_COLLATION_MATCHING_RULE_FORMAT
                .get(collation);
        unacceptableReasons.add(msg);
        continue;
      }
      Locale locale = getLocale(languageTag);
      if(locale==null)
      if (locale == null)
      {
        Message msg =
              WARN_ATTR_INVALID_COLLATION_MATCHING_RULE_LOCALE.
              get(collation,configuration.dn().toNormalizedString(),
            WARN_ATTR_INVALID_COLLATION_MATCHING_RULE_LOCALE.get(
                collation, configuration.dn().toNormalizedString(),
                languageTag);
        unacceptableReasons.add(msg);
        configAcceptable = false;
@@ -426,31 +437,34 @@
  /**
   * Creates Less-than Matching Rule.
   *
   * @param mapper CollationMapper containing OID and the language Tag.
   * @param locale  Locale value
   * @param mapper
   *          CollationMapper containing OID and the language Tag.
   * @param locale
   *          Locale value
   */
  private void createLessThanMatchingRule(CollationMapper mapper,Locale locale)
  private void createLessThanMatchingRule(CollationMapper mapper,
      Locale locale)
  {
    if(!lessThanMatchingRuleType)
      return;
    if (!lessThanMatchingRuleType) return;
    String oid = mapper.getNumericOID()+".1";
    String oid = mapper.getNumericOID() + ".1";
    String lTag = mapper.getLanguageTag();
    Collection<String> names = new HashSet<String>();
    MatchingRule matchingRule = getMatchingRule(oid);
    if(matchingRule!=null)
    if (matchingRule != null)
    {
      for(String name: matchingRule.getAllNames())
      for (String name : matchingRule.getAllNames())
      {
        names.add(name);
      }
    }
    names.add(lTag+".lt");
    names.add(lTag + ".lt");
    names.add(lTag + ".1");
    matchingRule = new CollationLessThanMatchingRule(oid, names, locale);
    matchingRule =
        new CollationLessThanMatchingRule(oid, names, locale);
    addMatchingRule(oid, matchingRule);
  }
@@ -459,33 +473,34 @@
  /**
   * Creates Less-Than-Equal-To Matching Rule.
   *
   * @param mapper CollationMapper containing OID and the language Tag.
   * @param locale  Locale value
   * @param mapper
   *          CollationMapper containing OID and the language Tag.
   * @param locale
   *          Locale value
   */
  private void createLessThanOrEqualToMatchingRule(CollationMapper mapper,
          Locale locale)
  private void createLessThanOrEqualToMatchingRule(
      CollationMapper mapper, Locale locale)
  {
    if(!lessThanEqualToMatchingRuleType)
      return;
    if (!lessThanEqualToMatchingRuleType) return;
    String oid = mapper.getNumericOID()+".2";
    String oid = mapper.getNumericOID() + ".2";
    String lTag = mapper.getLanguageTag();
    Collection<String> names = new HashSet<String>();
    MatchingRule matchingRule = getMatchingRule(oid);
    if(matchingRule!=null)
    if (matchingRule != null)
    {
      for(String name: matchingRule.getAllNames())
      for (String name : matchingRule.getAllNames())
      {
        names.add(name);
      }
    }
    names.add(lTag+".lte");
    names.add(lTag + ".lte");
    names.add(lTag + ".2");
    matchingRule =
            new CollationLessThanOrEqualToMatchingRule(oid,names,locale);
        new CollationLessThanOrEqualToMatchingRule(oid, names, locale);
    addMatchingRule(oid, matchingRule);
  }
@@ -494,22 +509,28 @@
  /**
   * Creates Equality Matching Rule.
   *
   * @param mapper CollationMapper containing OID and the language Tag.
   * @param locale  Locale value
   * @param mapper
   *          CollationMapper containing OID and the language Tag.
   * @param locale
   *          Locale value
   */
  private void createEqualityMatchingRule(CollationMapper mapper,Locale locale)
  private void createEqualityMatchingRule(CollationMapper mapper,
      Locale locale)
  {
    if(!equalityMatchingRuleType)
    if (!equalityMatchingRuleType)
    {
      return;
    //Register the default OID as equality matching rule.
    }
    // Register the default OID as equality matching rule.
    String lTag = mapper.getLanguageTag();
    String nOID = mapper.getNumericOID();
    MatchingRule matchingRule = getMatchingRule(nOID);
    Collection<String> names = new HashSet<String>();
    if(matchingRule!=null)
    if (matchingRule != null)
    {
      for(String name: matchingRule.getAllNames())
      for (String name : matchingRule.getAllNames())
      {
        names.add(name);
      }
@@ -517,26 +538,26 @@
    names.add(lTag);
    matchingRule =
          new CollationEqualityMatchingRule(nOID,
                  Collections.<String>emptySet(),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);
    if(equalityMatchingRule!=null)
    if (equalityMatchingRule != null)
    {
      for(String name: equalityMatchingRule.getAllNames())
      for (String name : equalityMatchingRule.getAllNames())
      {
        names.add(name);
      }
    }
    names.add(lTag+".eq");
    names.add(lTag+".3");
    names.add(lTag + ".eq");
    names.add(lTag + ".3");
    equalityMatchingRule =
          new CollationEqualityMatchingRule(OID,names,locale);
        new CollationEqualityMatchingRule(OID, names, locale);
    addMatchingRule(OID, equalityMatchingRule);
  }
@@ -545,32 +566,34 @@
  /**
   * Creates Greater-than-equal-to Matching Rule.
   *
   * @param mapper CollationMapper containing OID and the language Tag.
   * @param locale  Locale value
   * @param mapper
   *          CollationMapper containing OID and the language Tag.
   * @param locale
   *          Locale value
   */
  private void createGreaterThanOrEqualToMatchingRule(CollationMapper mapper,
          Locale locale)
  private void createGreaterThanOrEqualToMatchingRule(
      CollationMapper mapper, Locale locale)
  {
    if(!greaterThanEqualToMatchingRuleType)
      return;
    if (!greaterThanEqualToMatchingRuleType) return;
    String oid = mapper.getNumericOID()+".4";
    String oid = mapper.getNumericOID() + ".4";
    String lTag = mapper.getLanguageTag();
    Collection<String> names = new HashSet<String>();
    MatchingRule matchingRule = getMatchingRule(oid);
    if(matchingRule!=null)
    if (matchingRule != null)
    {
      for(String name: matchingRule.getAllNames())
      for (String name : matchingRule.getAllNames())
      {
        names.add(name);
      }
    }
    names.add(lTag+".gte");
    names.add(lTag + ".gte");
    names.add(lTag + ".4");
    matchingRule =
          new CollationGreaterThanOrEqualToMatchingRule(oid,names,locale);
        new CollationGreaterThanOrEqualToMatchingRule(oid, names,
            locale);
    addMatchingRule(oid, matchingRule);
  }
@@ -579,32 +602,33 @@
  /**
   * Creates Greater-than Matching Rule.
   *
   * @param mapper CollationMapper containing OID and the language Tag.
   * @param locale  Locale value
   * @param mapper
   *          CollationMapper containing OID and the language Tag.
   * @param locale
   *          Locale value
   */
  private void createGreaterThanMatchingRule(CollationMapper mapper,
          Locale locale)
      Locale locale)
  {
    if(!greaterThanMatchingRuleType)
      return;
    if (!greaterThanMatchingRuleType) return;
    String oid = mapper.getNumericOID()+".5";
    String oid = mapper.getNumericOID() + ".5";
    String lTag = mapper.getLanguageTag();
    Collection<String> names = new HashSet<String>();
    MatchingRule matchingRule = getMatchingRule(oid);
    if(matchingRule!=null)
    if (matchingRule != null)
    {
      for(String name: matchingRule.getAllNames())
      for (String name : matchingRule.getAllNames())
      {
        names.add(name);
      }
    }
    names.add(lTag+".gt");
    names.add(lTag + ".gt");
    names.add(lTag + ".5");
    matchingRule =
          new CollationGreaterThanMatchingRule(oid,names,locale);
        new CollationGreaterThanMatchingRule(oid, names, locale);
    addMatchingRule(oid, matchingRule);
  }
@@ -613,45 +637,47 @@
  /**
   * Creates substring Matching Rule.
   *
   * @param mapper CollationMapper containing OID and the language Tag.
   * @param locale  Locale value
   * @param mapper
   *          CollationMapper containing OID and the language Tag.
   * @param locale
   *          Locale value
   */
  private void createSubstringMatchingRule(CollationMapper mapper,Locale locale)
  private void createSubstringMatchingRule(CollationMapper mapper,
      Locale locale)
  {
    if(!substringMatchingRuleType)
      return;
    if (!substringMatchingRuleType) return;
    String oid = mapper.getNumericOID()+".6";
    String oid = mapper.getNumericOID() + ".6";
    String lTag = mapper.getLanguageTag();
    Collection<String> names = new HashSet<String>();
    MatchingRule matchingRule = getMatchingRule(oid);
    if(matchingRule!=null)
    if (matchingRule != null)
    {
      for(String name: matchingRule.getAllNames())
      for (String name : matchingRule.getAllNames())
      {
        names.add(name);
      }
    }
    names.add(lTag+".sub");
    names.add(lTag + ".sub");
    names.add(lTag + ".6");
    matchingRule =
          new CollationSubstringMatchingRule(oid,names,locale);
        new CollationSubstringMatchingRule(oid, names, locale);
    addMatchingRule(oid, matchingRule);
  }
  /**
   * 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.
   * @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.
    // Separates the language and the country from the locale.
    Locale locale;
    String lang = null;
    String country = null;
@@ -660,20 +686,20 @@
    int countryIndex = lTag.indexOf("-");
    int variantIndex = lTag.lastIndexOf("-");
    if(countryIndex > 0)
    if (countryIndex > 0)
    {
      lang = lTag.substring(0,countryIndex);
      lang = lTag.substring(0, countryIndex);
      if(variantIndex>countryIndex)
      if (variantIndex > countryIndex)
      {
        country = lTag.substring(countryIndex+1,variantIndex);
        variant = lTag.substring(variantIndex+1,lTag.length());
        locale = new Locale(lang,country,variant);
        country = lTag.substring(countryIndex + 1, variantIndex);
        variant = lTag.substring(variantIndex + 1, lTag.length());
        locale = new Locale(lang, country, variant);
      }
      else
      {
        country = lTag.substring(countryIndex+1,lTag.length());
        locale = new Locale(lang,country);
        country = lTag.substring(countryIndex + 1, lTag.length());
        locale = new Locale(lang, country);
      }
    }
    else
@@ -682,9 +708,9 @@
      locale = new Locale(lTag);
    }
    if(!supportedLocales.contains(locale))
    if (!supportedLocales.contains(locale))
    {
      //This locale is not supported by this JVM.
      // This locale is not supported by this JVM.
      locale = null;
    }
    return locale;
@@ -695,30 +721,22 @@
  /**
   * Collation Extensible matching rule.
   */
  private abstract class CollationMatchingRule
          extends ExtensibleMatchingRule
  private abstract class CollationMatchingRule extends
      ExtensibleMatchingRule
  {
    //Names for this class.
    // Names for this class.
    private final Collection<String> names;
    //Collator for performing equality match.
    // Collator for performing equality match.
    protected final Collator collator;
    //Numeric OID of the rule.
    // Numeric OID of the rule.
    private final String nOID;
    //Locale associated with this rule.
    // Locale associated with this rule.
    private final Locale locale;
    //Indexer of this rule.
    // Indexer of this rule.
    protected ExtensibleIndexer indexer;
@@ -726,14 +744,16 @@
    /**
     * 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
     * @param nOID
     *          OID of the collation matching rule
     * @param names
     *          names of this matching rule
     * @param locale
     *          Locale of the collation matching rule
     */
    private CollationMatchingRule(String nOID,Collection<String> names,
            Locale locale)
    private CollationMatchingRule(String nOID,
        Collection<String> names, Locale locale)
    {
      super();
      this.names = names;
      this.collator = createCollator(locale);
      this.locale = locale;
@@ -748,9 +768,9 @@
    @Override
    public String getName()
    {
      //Concatenate all the names and return.
      // Concatenate all the names and return.
      StringBuilder builder = new StringBuilder();
      for(String name: getAllNames())
      for (String name : getAllNames())
      {
        builder.append(name);
        builder.append("\b");
@@ -806,12 +826,13 @@
    /**
    * 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.
    */
     * 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();
@@ -833,42 +854,42 @@
    /**
     * {@inheritDoc}
     */
    @Override
    public Collection<ExtensibleIndexer> getIndexers(IndexConfig config)
    {
      if(indexer == null)
      if (indexer == null)
      {
        //The default implementation contains shared indexer and doesn't use the
        //config.
        // 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.
   * Collation rule for Equality matching rule.
   */
  private final class CollationEqualityMatchingRule
          extends CollationMatchingRule
  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
     * @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)
    private CollationEqualityMatchingRule(String nOID,
        Collection<String> names, Locale locale)
    {
      super(nOID,names,locale);
      super(nOID, names, locale);
    }
@@ -877,30 +898,11 @@
     * {@inheritDoc}
     */
    @Override
    public ByteString normalizeValue(ByteString value)
           throws DirectoryException
    public ByteString normalizeValue(ByteSequence value)
        throws DirectoryException
    {
      CollationKey key = collator.getCollationKey(value.stringValue());
      return new ASN1OctetString(key.toByteArray());
    }
    /**
     * 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());
      CollationKey key = collator.getCollationKey(value.toString());
      return ByteString.wrap(key.toByteArray());
    }
@@ -909,10 +911,10 @@
     * {@inheritDoc}
     */
    @Override
    public ConditionResult valuesMatch(ByteString attributeValue,
                                     ByteString assertionValue)
    public ConditionResult valuesMatch(ByteSequence attributeValue,
        ByteSequence assertionValue)
    {
      if (areEqual(attributeValue, assertionValue))
      if (assertionValue.equals(attributeValue))
      {
        return ConditionResult.TRUE;
      }
@@ -928,26 +930,22 @@
     * {@inheritDoc}
     */
    @Override
    public <T> T createIndexQuery(ByteString assertionValue,
            IndexQueryFactory<T> factory) throws DirectoryException
    public <T> T createIndexQuery(ByteSequence assertionValue,
        IndexQueryFactory<T> factory) throws DirectoryException
    {
      //Normalize the assertion value.
      ByteString normalValue = normalizeValue(assertionValue);
      return factory.createExactMatchQuery(
                indexer.getExtensibleIndexID(),
                normalValue.value());
      // Normalize the assertion value.
      return factory.createExactMatchQuery(indexer
          .getExtensibleIndexID(), normalizeValue(assertionValue));
    }
  }
  /**
   * Collation rule for Substring matching rule.
   */
  private final class CollationSubstringMatchingRule
          extends CollationMatchingRule
  private final class CollationSubstringMatchingRule extends
      CollationMatchingRule
  {
    //Substring Indexer associated with this instance.
    // Substring Indexer associated with this instance.
    private CollationSubstringExtensibleIndexer subIndexer;
@@ -955,14 +953,17 @@
    /**
     * Constructs a new CollationSubstringMatchingRule.
     *
     * @param nOID OID of the collation matching rule
     * @param names names of this matching rule
     * @param locale Locale of the collation matching rule
     * @param nOID
     *          OID of the collation matching rule
     * @param names
     *          names of this matching rule
     * @param locale
     *          Locale of the collation matching rule
     */
    private CollationSubstringMatchingRule(String nOID,
            Collection<String> names,Locale locale)
        Collection<String> names, Locale locale)
    {
      super(nOID,names,locale);
      super(nOID, names, locale);
    }
@@ -971,11 +972,11 @@
     * {@inheritDoc}
     */
    @Override
    public ByteString normalizeValue(ByteString value)
           throws DirectoryException
    public ByteString normalizeValue(ByteSequence value)
        throws DirectoryException
    {
      CollationKey key = collator.getCollationKey(value.stringValue());
      return new ASN1OctetString(key.toByteArray());
      CollationKey key = collator.getCollationKey(value.toString());
      return ByteString.wrap(key.toByteArray());
    }
@@ -985,26 +986,29 @@
     */
    private final class Assertion
    {
      //Initial part of the substring filter.
      // Initial part of the substring filter.
      private String subInitial;
      //any parts of the substring filter.
      // any parts of the substring filter.
      private List<String> subAny;
      //Final part of the substring filter.
      // 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.
       *
       * @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)
      private Assertion(String subInitial, List<String> subAny,
          String subFinal)
      {
        this.subInitial = subInitial;
        this.subAny = subAny;
@@ -1015,6 +1019,7 @@
      /**
       * Returns the Initial part of the assertion.
       *
       * @return Initial part of assertion.
       */
      private String getInitial()
@@ -1026,6 +1031,7 @@
      /**
       * Returns the any part of the assertion.
       *
       * @return Any part of the assertion.
       */
      private List<String> getAny()
@@ -1037,6 +1043,7 @@
      /**
       * Returns the final part of the assertion.
       *
       * @return Final part of the assertion.
       */
      private String getFinal()
@@ -1049,23 +1056,25 @@
    /**
     * Parses the assertion from a given value.
     * @param value The value that needs to be parsed.
     *
     * @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
    private Assertion parseAssertion(ByteSequence value)
        throws DirectoryException
    {
      // Get a string  representation of the value.
      String filterString = value.stringValue();
      // Get a string representation of the value.
      String filterString = value.toString();
      int endPos = filterString.length();
      // Find the locations of all the asterisks in the value.  Also,
      // Find the locations of all the asterisks in the value. Also,
      // check to see if there are any escaped values, since they will
      // need special treatment.
      boolean hasEscape = false;
      LinkedList<Integer> asteriskPositions = new LinkedList<Integer>();
      for (int i=0; i < endPos; i++)
      for (int i = 0; i < endPos; i++)
      {
        if (filterString.charAt(i) == 0x2A) // The asterisk.
        {
@@ -1077,18 +1086,17 @@
        }
      }
      // If there were no asterisks, then this isn't a substring filter.
      if (asteriskPositions.isEmpty())
      {
        Message message = ERR_SEARCH_FILTER_SUBSTRING_NO_ASTERISKS.get(
                filterString, 0, endPos);
        throw new DirectoryException(
                ResultCode.PROTOCOL_ERROR, message);
        Message message =
            ERR_SEARCH_FILTER_SUBSTRING_NO_ASTERISKS.get(filterString,
                0, endPos);
        throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message);
      }
      // If the value starts with an asterisk, then there is no
      // subInitial component.  Otherwise, parse out the subInitial.
      // subInitial component. Otherwise, parse out the subInitial.
      String subInitial;
      int firstPos = asteriskPositions.removeFirst();
      if (firstPos == 0)
@@ -1100,12 +1108,12 @@
        if (hasEscape)
        {
          CharBuffer buffer = CharBuffer.allocate(firstPos);
          for (int i=0; i < firstPos; i++)
          for (int i = 0; i < firstPos; i++)
          {
            if (filterString.charAt(i) == 0x5C)
            {
              char escapeValue = hexToEscapedChar(filterString, i +1);
              i +=2; //Move to the next sequence.
              char escapeValue = hexToEscapedChar(filterString, i + 1);
              i += 2; // Move to the next sequence.
              buffer.put(escapeValue);
            }
            else
@@ -1121,11 +1129,10 @@
        }
        else
        {
          subInitial = filterString.substring(0,firstPos);
          subInitial = filterString.substring(0, firstPos);
        }
      }
      // Next, process through the rest of the asterisks to get the
      // subAny values.
      List<String> subAny = new ArrayList<String>();
@@ -1136,12 +1143,12 @@
        if (hasEscape)
        {
          CharBuffer buffer = CharBuffer.allocate(length);
          for (int i=firstPos+1; i < asteriskPos; i++)
          for (int i = firstPos + 1; i < asteriskPos; i++)
          {
            if (filterString.charAt(i) == 0x5C)
            {
              char escapeValue = hexToEscapedChar(filterString, i + 1);
              i +=2; //Move to the next sequence.
              i += 2; // Move to the next sequence.
              buffer.put(escapeValue);
            }
            else
@@ -1157,18 +1164,17 @@
        }
        else
        {
          subAny.add(filterString.substring(firstPos+1, firstPos+length+1));
          subAny.add(filterString.substring(firstPos + 1, firstPos
              + length + 1));
        }
        firstPos = asteriskPos;
      }
      // Finally, see if there is anything after the last asterisk,
      // which would be the subFinal value.
      String subFinal;
      if (firstPos == (endPos-1))
      if (firstPos == (endPos - 1))
      {
        subFinal = null;
      }
@@ -1179,12 +1185,12 @@
        if (hasEscape)
        {
          CharBuffer buffer = CharBuffer.allocate(length);
          for (int i=firstPos+1; i < endPos; i++)
          for (int i = firstPos + 1; i < endPos; i++)
          {
            if (filterString.charAt(i) == 0x5C)
            {
              char escapeValue = hexToEscapedChar(filterString, i + 1);
              i +=2; //Move to the next sequence.
              i += 2; // Move to the next sequence.
              buffer.put(escapeValue);
            }
            else
@@ -1200,9 +1206,12 @@
        }
        else
        {
          subFinal = filterString.substring(firstPos+1, length + firstPos + 1);
          subFinal =
              filterString.substring(firstPos + 1, length + firstPos
                  + 1);
        }
      }
      return new Assertion(subInitial, subAny, subFinal);
    }
@@ -1212,17 +1221,20 @@
     * {@inheritDoc}
     */
    @Override
    public ByteString normalizeAssertionValue(ByteString value)
            throws DirectoryException {
    public ByteString normalizeAssertionValue(ByteSequence 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
      // initialLength, initial, numberofany, anyLength1, any1,
      // anyLength2, any2, ..., anyLengthn, anyn, finalLength,
      // final
      CollationKey key = null;
      List<Integer> normalizedList = new ArrayList<Integer>();
      if(subInitial == null)
      if (subInitial == null)
      {
        normalizedList.add(0);
      }
@@ -1230,33 +1242,37 @@
      {
        key = collator.getCollationKey(subInitial);
        byte[] initialBytes = key.toByteArray();
        // Last 4 bytes are 0s with PRIMARY strenght.
        int length = initialBytes.length - 4 ;
        // Last 4 bytes are 0s with PRIMARY strength.
        int length = initialBytes.length - 4;
        normalizedList.add(length);
        for(int i=0;i<length;i++)
        for (int i = 0; i < length; i++)
        {
          normalizedList.add((int)initialBytes[i]);
          normalizedList.add((int) initialBytes[i]);
        }
      }
      List<String> subAny = assertion.getAny();
      if (subAny.size() == 0) {
      if (subAny.size() == 0)
      {
        normalizedList.add(0);
      }
      else
      {
        normalizedList.add(subAny.size());
        for(String any:subAny)
        for (String any : subAny)
        {
          key = collator.getCollationKey(any);
          byte[] anyBytes = key.toByteArray();
          int length = anyBytes.length - 4;
          normalizedList.add(length);
          for(int i=0;i<length;i++)
          for (int i = 0; i < length; i++)
          {
            normalizedList.add((int)anyBytes[i]);
            normalizedList.add((int) anyBytes[i]);
          }
        }
      }
      String subFinal = assertion.getFinal();
      if (subFinal == null)
      {
@@ -1268,18 +1284,19 @@
        byte[] subFinalBytes = key.toByteArray();
        int length = subFinalBytes.length - 4;
        normalizedList.add(length);
        for(int i=0;i<length;i++)
        for (int i = 0; i < length; i++)
        {
          normalizedList.add((int)subFinalBytes[i]);
          normalizedList.add((int) subFinalBytes[i]);
        }
      }
      byte[] normalizedBytes = new byte[normalizedList.size()];
      for(int i=0;i<normalizedList.size();i++)
      for (int i = 0; i < normalizedList.size(); i++)
      {
        normalizedBytes[i] = normalizedList.get(i).byteValue();
      }
      return new ASN1OctetString(normalizedBytes);
      return ByteString.wrap(normalizedBytes);
    }
@@ -1288,69 +1305,68 @@
     * {@inheritDoc}
     */
    @Override
    public ConditionResult valuesMatch(ByteString attributeValue,
                                       ByteString assertionValue)
    public ConditionResult valuesMatch(ByteSequence attributeValue,
        ByteSequence assertionValue)
    {
      byte[] valueBytes = attributeValue.value();
      int valueLength = valueBytes.length - 4;
      byte[] assertionBytes = assertionValue.value();
      int valueLength = attributeValue.length() - 4;
      int valuePos = 0; // position in the value bytes array.
      int assertPos = 0; // position in the assertion bytes array.
      int subInitialLength = 0xFF & assertionBytes[0];
      //First byte is the length of subInitial.
      if(subInitialLength!= 0)
      // First byte is the length of subInitial.
      int subInitialLength = 0xFF & assertionValue.byteAt(0);
      if (subInitialLength != 0)
      {
        if(subInitialLength > valueLength)
        if (subInitialLength > valueLength)
        {
          return ConditionResult.FALSE;
        }
        for(;valuePos < subInitialLength; valuePos++)
        for (; valuePos < subInitialLength; valuePos++)
        {
          if(valueBytes[valuePos]!=assertionBytes[valuePos+1])
          if (attributeValue.byteAt(valuePos) != assertionValue
              .byteAt(valuePos + 1))
          {
            return ConditionResult.FALSE;
          }
        }
      }
      assertPos = subInitialLength + 1;
      int anySize = 0xFF & assertionBytes[assertPos++];
      if(anySize!=0)
      int anySize = 0xFF & assertionValue.byteAt(assertPos++);
      if (anySize != 0)
      {
        while(anySize-- > 0)
        while (anySize-- > 0)
        {
          int anyLength = 0xFF & assertionBytes[assertPos++];
          int anyLength = 0xFF & assertionValue.byteAt(assertPos++);
          int end = valueLength - anyLength;
          boolean match = false;
          for(; valuePos <= end; valuePos++)
          {
            if(assertionBytes[assertPos] == valueBytes[valuePos])
          for (; valuePos <= end; valuePos++)
          {
            if (assertionValue.byteAt(assertPos) == attributeValue
                .byteAt(valuePos))
            {
              boolean subMatch = true;
              for(int i=1;i<anyLength;i++)
              for (int i = 1; i < anyLength; i++)
              {
                if(assertionBytes[assertPos+i] != valueBytes[valuePos+i])
                if (assertionValue.byteAt(assertPos + i) != attributeValue
                    .byteAt(valuePos + i))
                {
                  subMatch = false;
                  break;
                }
              }
              if(subMatch)
              if (subMatch)
              {
                match = subMatch;
                break;
              }
            }
          }
          if(match)
          if (match)
          {
            valuePos += anyLength;
          }
@@ -1358,30 +1374,32 @@
          {
            return ConditionResult.FALSE;
          }
          assertPos = assertPos + anyLength;
        }
      }
      int finalLength = 0xFF & assertionBytes[assertPos++];
      if(finalLength!=0)
      int finalLength = 0xFF & assertionValue.byteAt(assertPos++);
      if (finalLength != 0)
      {
        if((valueLength - finalLength) < valuePos)
        if ((valueLength - finalLength) < valuePos)
        {
          return ConditionResult.FALSE;
        }
        valuePos = valueLength - finalLength;
        if(finalLength != assertionBytes.length - assertPos )
        if (finalLength != assertionValue.length() - assertPos)
        {
          //Some issue with the encoding.
          // Some issue with the encoding.
          return ConditionResult.FALSE;
        }
        valuePos = valueLength - finalLength;
        for (int i=0; i < finalLength; i++,valuePos++)
        for (int i = 0; i < finalLength; i++, valuePos++)
        {
          if (assertionBytes[assertPos+i] != valueBytes[valuePos])
          if (assertionValue.byteAt(assertPos + i) != attributeValue
              .byteAt(valuePos))
          {
            return ConditionResult.FALSE;
          }
@@ -1397,37 +1415,41 @@
     * {@inheritDoc}
     */
    @Override
    public final Collection<ExtensibleIndexer> getIndexers(IndexConfig config)
    public final Collection<ExtensibleIndexer> getIndexers(
        IndexConfig config)
    {
      Collection<ExtensibleIndexer> indexers =
              new ArrayList<ExtensibleIndexer>();
      int substrLength = 6; //Default substring length;
      if(subIndexer == null)
          new ArrayList<ExtensibleIndexer>();
      int substrLength = 6; // Default substring length;
      if (subIndexer == null)
      {
        if(config != null)
        if (config != null)
        {
          substrLength = config.getSubstringLength();
        }
        subIndexer = new CollationSubstringExtensibleIndexer(this,
                substrLength);
        subIndexer =
            new CollationSubstringExtensibleIndexer(this, substrLength);
      }
      else
      {
        if(config !=null)
        if (config != null)
        {
          if(config.getSubstringLength() !=subIndexer.gerSubstringLength())
          if (config.getSubstringLength() != subIndexer
              .gerSubstringLength())
          {
            subIndexer.setSubstringLength(substrLength);
          }
        }
      }
      if(indexer == null)
      if (indexer == null)
      {
        indexer = new CollationSharedExtensibleIndexer(this);
      }
      indexers.add(subIndexer);
      indexers.add(indexer);
      return indexers;
    }
@@ -1436,13 +1458,14 @@
    /**
     * 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.
     * @param attValue
     *          The normalized attribute value
     * @param set
     *          A set into which the keys will be inserted.
     */
    private void subtringKeys(ByteString attValue,
            Set<byte[]> keys)
    private void subtringKeys(ByteString attValue, Set<byte[]> keys)
    {
      String value = attValue.stringValue();
      String value = attValue.toString();
      int keyLength = subIndexer.gerSubstringLength();
      for (int i = 0, remain = value.length(); remain > 0; i++, remain--)
      {
@@ -1457,17 +1480,18 @@
    /**
     * 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.
     * @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)
        Map<byte[], Boolean> modifiedKeys, Boolean insert)
    {
      String value = attValue.stringValue();
      String value = attValue.toString();
      int keyLength = subIndexer.gerSubstringLength();
      for (int i = 0, remain = value.length(); remain > 0; i++, remain--)
      {
@@ -1488,12 +1512,15 @@
    /**
     * Makes a byte array representing a substring index key for
     * one substring of a value.
     * 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.
     * @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)
@@ -1509,13 +1536,15 @@
    /**
     * 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.
     * 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)
        IndexQueryFactory<T> factory)
    {
      byte[] lower = makeSubstringKey(value, 0, value.length());
      byte[] upper = new byte[lower.length];
@@ -1535,26 +1564,27 @@
          break;
        }
      }
      //Use the shared equality indexer.
      return factory.createRangeMatchQuery(
                              indexer.getExtensibleIndexID(),
                              lower,
                              upper,
                              true,
                              false);
      // Use the shared equality indexer.
      return factory.createRangeMatchQuery(indexer
          .getExtensibleIndexID(), ByteString.wrap(lower), ByteString
          .wrap(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.
     *
     * @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)
        IndexQueryFactory<T> factory)
    {
      T intersectionQuery = null;
      int substrLength = subIndexer.gerSubstringLength();
@@ -1567,9 +1597,11 @@
        {
          if (upper[i] == 0xFF)
          {
            // We have to carry the overflow to the more significant byte.
            // We have to carry the overflow to the more significant
            // byte.
            upper[i] = 0;
          } else
          }
          else
          {
            // No overflow, we can stop.
            upper[i] = (byte) (upper[i] + 1);
@@ -1578,20 +1610,18 @@
        }
        // Read the range: lower <= keys < upper.
        intersectionQuery =
                factory.createRangeMatchQuery(
                subIndexer.getExtensibleIndexID(),
                lower,
                upper,
                true,
                false);
            factory.createRangeMatchQuery(subIndexer
                .getExtensibleIndexID(), ByteString.wrap(lower),
                ByteString.wrap(upper), true, false);
      }
      else
      {
        List<T> queryList = new ArrayList<T>();
        Set<byte[]> set =
                new TreeSet<byte[]>(new AttributeIndex.KeyComparator());
            new TreeSet<byte[]>(new AttributeIndex.KeyComparator());
        for (int first = 0, last = substrLength;
                last <= value.length(); first++, last++)
             last <= value.length();
             first++, last++)
        {
          byte[] keyBytes;
          keyBytes = makeSubstringKey(value, first, substrLength);
@@ -1600,13 +1630,12 @@
        for (byte[] keyBytes : set)
        {
          T single = factory.createExactMatchQuery(
                  subIndexer.getExtensibleIndexID(),
                  keyBytes);
          T single =
              factory.createExactMatchQuery(subIndexer
                  .getExtensibleIndexID(), ByteString.wrap(keyBytes));
          queryList.add(single);
        }
        intersectionQuery =
                factory.createIntersectionQuery(queryList);
        intersectionQuery = factory.createIntersectionQuery(queryList);
      }
      return intersectionQuery;
    }
@@ -1617,8 +1646,8 @@
     * {@inheritDoc}
     */
    @Override
    public <T> T createIndexQuery(ByteString assertionValue,
            IndexQueryFactory<T> factory) throws DirectoryException
    public <T> T createIndexQuery(ByteSequence assertionValue,
        IndexQueryFactory<T> factory) throws DirectoryException
    {
      Assertion assertion = parseAssertion(assertionValue);
      String subInitial = assertion.getInitial();
@@ -1628,14 +1657,14 @@
      if (subInitial == null && subAny.size() == 0 && subFinal == null)
      {
        //Can happen with a filter like "cn:en.6:=*".
        //Just return an empty record.
        // 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.
        // Always use the shared indexer for initial match.
        T query = matchInitialSubstring(subInitial, factory);
        queries.add(query);
      }
@@ -1650,7 +1679,6 @@
        elements.add(subFinal);
      }
      for (String element : elements)
      {
        queries.add(matchSubstring(element, factory));
@@ -1659,126 +1687,75 @@
    }
  }
  /**
   *An abstract Collation rule for Ordering  matching rule.
   * An abstract Collation rule for Ordering matching rule.
   */
  private abstract class CollationOrderingMatchingRule
          extends CollationMatchingRule
  private abstract class CollationOrderingMatchingRule extends
      CollationMatchingRule
  {
    /**
     * 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
     * @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);
    }
    /**
     * {@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.
     *
     * @param nOID OID of the collation matching rule
     * @param names names of this matching rule
     * @param locale Locale of the collation matching rule
     */
    private CollationLessThanMatchingRule(String nOID,
            Collection<String> names, Locale locale)
        Collection<String> names, Locale locale)
    {
      super(nOID, names, locale);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public ByteString normalizeValue(ByteSequence value)
        throws DirectoryException
    {
      CollationKey key = collator.getCollationKey(value.toString());
      return ByteString.wrap(key.toByteArray());
    }
  }
  /**
   * Collation matching rule for Less-than matching rule.
   */
  private final class CollationLessThanMatchingRule extends
      CollationOrderingMatchingRule
  {
    /**
     * Constructs a new CollationLessThanMatchingRule.
     *
     * @param nOID
     *          OID of the collation matching rule
     * @param names
     *          names of this matching rule
     * @param locale
     *          Locale of the collation matching rule
     */
    private CollationLessThanMatchingRule(String nOID,
        Collection<String> names, Locale locale)
    {
      super(nOID, names, locale);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public ConditionResult valuesMatch(ByteString attributeValue,
            ByteString assertionValue)
    public ConditionResult valuesMatch(ByteSequence attributeValue,
        ByteSequence assertionValue)
    {
      int ret = compare(attributeValue.value(), assertionValue.value());
      int ret = attributeValue.compareTo(assertionValue);
      if (ret < 0)
      {
@@ -1792,58 +1769,52 @@
    /**
     * {@inheritDoc}
     */
    @Override
    public <T> T createIndexQuery(ByteString assertionValue,
            IndexQueryFactory<T> factory) throws DirectoryException
    public <T> T createIndexQuery(ByteSequence 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);
      return factory.createRangeMatchQuery(indexer
          .getExtensibleIndexID(), ByteString.empty(),
          normalizeValue(assertionValue), false, false);
    }
  }
  /**
   * Collation rule for less-than-equal-to matching rule.
   */
  private final class CollationLessThanOrEqualToMatchingRule
          extends CollationOrderingMatchingRule
  private final class CollationLessThanOrEqualToMatchingRule extends
      CollationOrderingMatchingRule
  {
    /**
     * Constructs a new CollationLessThanOrEqualToMatchingRule.
     *
     * @param nOID OID of the collation matching rule
     * @param names names of this matching rule
     * @param locale Locale of the collation matching rule
     * @param nOID
     *          OID of the collation matching rule
     * @param names
     *          names of this matching rule
     * @param locale
     *          Locale of the collation matching rule
     */
    private CollationLessThanOrEqualToMatchingRule(String nOID,
            Collection<String> names,
            Locale locale)
        Collection<String> names, Locale locale)
    {
      super(nOID, names, locale);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public ConditionResult valuesMatch(ByteString attributeValue,
            ByteString assertionValue)
    public ConditionResult valuesMatch(ByteSequence attributeValue,
        ByteSequence assertionValue)
    {
      int ret = compare(attributeValue.value(), assertionValue.value());
      int ret = attributeValue.compareTo(assertionValue);
      if (ret <= 0)
      {
@@ -1857,44 +1828,39 @@
    /**
     * {@inheritDoc}
     */
    @Override
    public <T> T createIndexQuery(ByteString assertionValue,
            IndexQueryFactory<T> factory)
            throws DirectoryException
    public <T> T createIndexQuery(ByteSequence 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);
      return factory.createRangeMatchQuery(indexer
          .getExtensibleIndexID(), ByteString.empty(),
          normalizeValue(assertionValue), false, true);
    }
  }
  /**
   * Collation rule for greater-than matching rule.
   */
  private final class CollationGreaterThanMatchingRule
          extends CollationOrderingMatchingRule
  private final class CollationGreaterThanMatchingRule extends
      CollationOrderingMatchingRule
  {
    /**
     * Constructs a new CollationGreaterThanMatchingRule.
     *
     * @param nOID OID of the collation matching rule
     * @param names names of this matching rule
     * @param locale Locale of the collation matching rule
     * @param nOID
     *          OID of the collation matching rule
     * @param names
     *          names of this matching rule
     * @param locale
     *          Locale of the collation matching rule
     */
    private CollationGreaterThanMatchingRule(String nOID,
            Collection<String> names, Locale locale)
        Collection<String> names, Locale locale)
    {
      super(nOID, names, locale);
    }
@@ -1905,71 +1871,69 @@
     * {@inheritDoc}
     */
    @Override
    public ConditionResult valuesMatch(ByteString attributeValue,
            ByteString assertionValue)
    public ConditionResult valuesMatch(ByteSequence attributeValue,
        ByteSequence assertionValue)
    {
      int ret = compare(attributeValue.value(), assertionValue.value());
      int ret = attributeValue.compareTo(assertionValue);
      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
    public <T> T createIndexQuery(ByteSequence 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);
      return factory.createRangeMatchQuery(indexer
          .getExtensibleIndexID(), normalizeValue(assertionValue),
          ByteString.empty(), false, false);
    }
  }
  /**
   * Collation rule for greater-than-equal-to matching rule.
   */
  private final class CollationGreaterThanOrEqualToMatchingRule
          extends CollationOrderingMatchingRule
  private final class CollationGreaterThanOrEqualToMatchingRule extends
      CollationOrderingMatchingRule
  {
    /**
     * Constructs a new CollationGreaterThanOrEqualToMatchingRule.
     *
     * @param nOID OID of the collation matching rule
     * @param names names of this matching rule
     * @param locale Locale of the collation matching rule
     * @param nOID
     *          OID of the collation matching rule
     * @param names
     *          names of this matching rule
     * @param locale
     *          Locale of the collation matching rule
     */
    private CollationGreaterThanOrEqualToMatchingRule(String nOID,
            Collection<String> names,
            Locale locale)
        Collection<String> names, Locale locale)
    {
      super(nOID, names, locale);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public ConditionResult valuesMatch(ByteString attributeValue,
            ByteString assertionValue)
    public ConditionResult valuesMatch(ByteSequence attributeValue,
        ByteSequence assertionValue)
    {
      int ret = compare(attributeValue.value(),assertionValue.value());
      int ret = attributeValue.compareTo(assertionValue);
      if (ret >= 0)
      {
@@ -1987,29 +1951,23 @@
     * {@inheritDoc}
     */
    @Override
    public <T> T createIndexQuery(ByteString assertionValue,
            IndexQueryFactory<T> factory)
            throws DirectoryException
    public <T> T createIndexQuery(ByteSequence assertionValue,
        IndexQueryFactory<T> factory) throws DirectoryException
    {
      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);
      return factory.createRangeMatchQuery(indexer
          .getExtensibleIndexID(), normalizeValue(assertionValue),
          ByteString.empty(), true, false);
    }
  }
  /**
   * Extensible Indexer class for Collation Matching rules which share the
   * same index. This Indexer is shared by Equality and Ordering Collation
   * Matching Rules.
   * 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
  private final class CollationSharedExtensibleIndexer extends
      ExtensibleIndexer
  {
    /**
@@ -2022,10 +1980,11 @@
    /**
     * Creates a new instance of CollationSharedExtensibleIndexer.
     *
     * @param matchingRule The Collation Matching Rule.
     * @param matchingRule
     *          The Collation Matching Rule.
     */
    private CollationSharedExtensibleIndexer(
            CollationMatchingRule matchingRule)
        CollationMatchingRule matchingRule)
    {
      this.matchingRule = matchingRule;
    }
@@ -2047,14 +2006,13 @@
     * {@inheritDoc}
     */
    @Override
    public final void getKeys(AttributeValue value,
            Set<byte[]> keys)
    public final void getKeys(AttributeValue value, Set<byte[]> keys)
    {
      ByteString key;
      try
      {
        key = matchingRule.normalizeValue(value.getValue());
        keys.add(key.value());
        keys.add(key.toByteArray());
      }
      catch (DirectoryException de)
      {
@@ -2068,8 +2026,7 @@
     */
    @Override
    public final void getKeys(AttributeValue value,
            Map<byte[], Boolean> modifiedKeys,
            Boolean insert)
        Map<byte[], Boolean> modifiedKeys, Boolean insert)
    {
      Set<byte[]> keys = new HashSet<byte[]>();
      getKeys(value, keys);
@@ -2101,17 +2058,15 @@
  /**
   * Extensible Indexer class for Collation Substring Matching rules.
   * This Indexer is used  by Substring Collation Matching Rules.
   * This Indexer is used by Substring Collation Matching Rules.
   */
  private final class CollationSubstringExtensibleIndexer
          extends ExtensibleIndexer
  private final class CollationSubstringExtensibleIndexer extends
      ExtensibleIndexer
  {
    //The CollationSubstringMatching Rule.
    // The CollationSubstringMatching Rule.
    private final CollationSubstringMatchingRule matchingRule;
    //The substring length.
    // The substring length.
    private int substringLen;
@@ -2119,12 +2074,13 @@
    /**
     * Creates a new instance of CollationSubstringExtensibleIndexer.
     *
     * @param matchingRule The CollationSubstringMatching Rule.
     * @param substringLen The substring length.
     * @param matchingRule
     *          The CollationSubstringMatching Rule.
     * @param substringLen
     *          The substring length.
     */
    private CollationSubstringExtensibleIndexer(
            CollationSubstringMatchingRule matchingRule,
            int substringLen)
        CollationSubstringMatchingRule matchingRule, int substringLen)
    {
      this.matchingRule = matchingRule;
      this.substringLen = substringLen;
@@ -2136,11 +2092,9 @@
     * {@inheritDoc}
     */
    @Override
    public void getKeys(AttributeValue value,
                                Set<byte[]> keys)
    public void getKeys(AttributeValue value, Set<byte[]> keys)
    {
      matchingRule.subtringKeys(value.getValue(),
                                keys);
      matchingRule.subtringKeys(value.getValue(), keys);
    }
@@ -2150,12 +2104,10 @@
     */
    @Override
    public void getKeys(AttributeValue attValue,
            Map<byte[], Boolean> modifiedKeys,
            Boolean insert)
        Map<byte[], Boolean> modifiedKeys, Boolean insert)
    {
      matchingRule.substringKeys(attValue.getValue(),
              modifiedKeys,
              insert);
      matchingRule.substringKeys(attValue.getValue(), modifiedKeys,
          insert);
    }
@@ -2184,6 +2136,7 @@
    /**
     * Returns the substring length.
     *
     * @return The length of the substring.
     */
    private int gerSubstringLength()
@@ -2195,7 +2148,9 @@
    /**
     * Sets the substring length.
     * @param substringLen The substring length.
     *
     * @param substringLen
     *          The substring length.
     */
    private void setSubstringLength(int substringLen)
    {
@@ -2203,33 +2158,33 @@
    }
  }
  /**
   * A utility class for extracting the OID and Language Tag from the
   * configuration entry.
   */
  private final class CollationMapper
  {
    //OID of the collation rule.
    private  String oid;
    // OID of the collation rule.
    private String oid;
    //Language Tag.
    private  String lTag;
    // Language Tag.
    private String lTag;
    /**
     * Creates a new instance of CollationMapper.
     *
     * @param collation The collation text in the LOCALE:OID format.
     * @param collation
     *          The collation text in the LOCALE:OID format.
     */
    private CollationMapper(String collation)
    {
      int index = collation.indexOf(":");
      if(index>0)
      if (index > 0)
      {
        oid = collation.substring(index+1,collation.length());
        lTag = collation.substring(0,index);
        oid = collation.substring(index + 1, collation.length());
        lTag = collation.substring(0, index);
      }
    }
@@ -2257,4 +2212,4 @@
      return lTag;
    }
  }
}
}