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

sin
31.06.2009 7ddf5a923866031a7034f457715d6a5e4ecbca67
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.