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

Jean-Noel Rouvignac
21.59.2014 c430f8b3da724d0a117eadd172b92179b9a0b5a9
OPENDJ-1368 (CR-3232) Remove AttributeValue

Adressed comments from post commit review for r10554.

Code review: Matthew Swift



JebFormat.java:
In entryIDFromDatabase(), extracted toLong() for reuse outside of this class.
In entryIDUndefinedSizeFromDatabase(), called entryIDFromDatabase().



VlVIndex.java:
Extracted methods getSortValuesSet(), getSearchKeyRange(), logSearchKeyResult(), put(), moveToNextSortValues() to factorize code.
In evaluate(), regrouped guard clauses.
In getSortValues(), regrouped if statements.
In decodeKey(), called JebFormat.toLong().

VlVKeyComparator.java:
In compare(), called JebFormat.toLong().
In compare(), removed AttributeType[] parameter, no longer needed.

SortValuesSet.java:
In binarySearch(), removed AttributeType[] parameter, no longer needed (consequence of the change to VlVKeyComparator.compare()).

TestVerifyJob.java:
Consequence of removing AttributeType[] parameter from a few methods.

VirtualAttributeProvider.java:
Added matchesEqualityAssertion().
Fixed javadocs to speak about assertion values.



AbstractAttribute.java:
Pulled hashCode() implementation here from VirtualAttribute.

Attribute.java, CollectiveVirtualAttribute.java:
Added matchesEqualityAssertion().
Fixed javadocs + parameter names to speak about assertion values.

AttributeBuilder.java:
Used SmallMap instead of LinkedHashMap.
Added matchesEqualityAssertion().
Fixed javadocs + parameter names to speak about assertion values.

SmallMap.java, SmallMapTest.java: ADDED

VirtualAttribute.java:
Added matchesEqualityAssertion().
Fixed javadocs + parameter names to speak about assertion values.
Pulled up hashCode() implementation.

SearchFilter.java:
In processEquality(), changed implementation and called Attribute.matchesEqualityAssertion().



AttributeValueIterable.java: REMOVED

ServerManagementContext.java, TestEntry.java:
Consequence of removing AttributeValueIterable.



AttributeIndex.java:
Used normalizeAssertionValue() instead of normalizeAttributeValue() where appropriate.

ModifyEntryTask.java, UserDN.java:
Fixed small typo.

SearchFilterTests.java:
Code cleanup.
1 files deleted
2 files added
18 files modified
1697 ■■■■ changed files
opendj3-server-dev/src/guitools/org/opends/guitools/controlpanel/task/ModifyEntryTask.java 5 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/admin/server/ServerManagementContext.java 14 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/api/VirtualAttributeProvider.java 39 ●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/authorization/dseecompat/UserDN.java 8 ●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/backends/jeb/AttributeIndex.java 24 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/backends/jeb/JebFormat.java 31 ●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/backends/jeb/SortValuesSet.java 14 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/backends/jeb/VLVIndex.java 449 ●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/backends/jeb/VLVKeyComparator.java 35 ●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/types/AbstractAttribute.java 23 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/types/Attribute.java 34 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/types/AttributeBuilder.java 67 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/types/AttributeValueIterable.java 198 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/types/CollectiveVirtualAttribute.java 19 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/types/SearchFilter.java 35 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/types/SmallMap.java 257 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/types/VirtualAttribute.java 41 ●●●● patch | view | raw | blame | history
opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestVerifyJob.java 6 ●●●● patch | view | raw | blame | history
opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/types/SearchFilterTests.java 141 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/types/SmallMapTest.java 249 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/types/TestEntry.java 8 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/src/guitools/org/opends/guitools/controlpanel/task/ModifyEntryTask.java
@@ -690,10 +690,9 @@
  }
  /**
   * Creates an ByteString for an attribute and a value (the one we got
   * using JNDI).
   * Creates a ByteString for an attribute and a value (the one we got using JNDI).
   * @param value the value found using JNDI.
   * @return an ByteString object.
   * @return a ByteString object.
   */
  private static ByteString createAttributeValue(Object value)
  {
opendj3-server-dev/src/server/org/opends/server/admin/server/ServerManagementContext.java
@@ -24,11 +24,8 @@
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions Copyright 2014 ForgeRock AS
 */
package org.opends.server.admin.server;
import static org.opends.messages.AdminMessages.*;
import static org.opends.server.admin.PropertyException.*;
import static org.opends.server.util.StaticUtils.*;
@@ -77,7 +74,6 @@
import org.opends.server.config.ConfigEntry;
import org.opends.server.core.DirectoryServer;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeValueIterable;
import org.opends.server.types.AttributeType;
import org.forgerock.opendj.ldap.ByteString;
import org.opends.server.types.DN;
@@ -905,9 +901,15 @@
    List<Attribute> attributes = configEntry.getEntry().getAttribute(type, true);
    List<ByteString> results = new LinkedList<ByteString>();
    for (ByteString v : new AttributeValueIterable(attributes))
    if (attributes != null)
    {
      results.add(v);
      for (Attribute a : attributes)
      {
        for (ByteString v : a)
        {
          results.add(v);
        }
      }
    }
    return results;
  }
opendj3-server-dev/src/server/org/opends/server/api/VirtualAttributeProvider.java
@@ -190,6 +190,26 @@
    return getValues(entry, rule).contains(value);
  }
  /**
   * Indicates whether this virtual attribute provider matches the assertion
   * value.
   *
   * @param entry
   *          The entry for which to make the determination.
   * @param rule
   *          The virtual attribute rule which defines the constraints for the
   *          virtual attribute.
   * @param assertionValue
   *          The assertion value for which to make the determination.
   * @return {@code true} if this virtual attribute provider matches the
   *         specified assertion value for the provided entry, or {@code false}
   *         if not.
   */
  public ConditionResult matchesEqualityAssertion(Entry entry,
      VirtualAttributeRule rule, ByteString assertionValue)
  {
    return getValues(entry, rule).matchesEqualityAssertion(assertionValue);
  }
  /**
@@ -359,12 +379,12 @@
   *          The virtual attribute rule which defines the constraints for the
   *          virtual attribute.
   * @param assertionValue
   *          The value for which to make the determination.
   *          The assertion value for which to make the determination.
   * @return {@code UNDEFINED} if the associated attribute type does not have an
   *         ordering matching rule, {@code TRUE} if at least one of the
   *         generated values will be greater than or equal to the specified
   *         value, or {@code FALSE} if none of the generated values will be
   *         greater than or equal to the specified value.
   *         assertion value, or {@code FALSE} if none of the generated values
   *         will be greater than or equal to the specified value.
   */
  public ConditionResult greaterThanOrEqualTo(Entry entry,
                              VirtualAttributeRule rule,
@@ -421,12 +441,12 @@
   *          The virtual attribute rule which defines the constraints for the
   *          virtual attribute.
   * @param assertionValue
   *          The value for which to make the determination.
   *          The assertion value for which to make the determination.
   * @return {@code UNDEFINED} if the associated attribute type does not have an
   *         ordering matching rule, {@code TRUE} if at least one of the
   *         generated values will be less than or equal to the specified value,
   *         or {@code FALSE} if none of the generated values will be greater
   *         than or equal to the specified value.
   *         generated values will be less than or equal to the specified
   *         assertion value, or {@code FALSE} if none of the generated values
   *         will be greater than or equal to the specified value.
   */
  public ConditionResult lessThanOrEqualTo(Entry entry,
                              VirtualAttributeRule rule,
@@ -482,14 +502,15 @@
   * @param  entry  The entry for which to make the determination.
   * @param  rule   The virtual attribute rule which defines the
   *                constraints for the virtual attribute.
   * @param  assertionValue  The value for which to make the determination.
   * @param  assertionValue
   *          The assertion value for which to make the determination.
   *
   * @return  {@code UNDEFINED} if the associated attribute type does
   *          not have an approximate matching rule, {@code TRUE} if at
   *          least one of the generated values will be approximately
   *          equal to the specified value, or {@code FALSE} if none
   *          of the generated values will be approximately equal to
   *          the specified value.
   *          the specified assertion value.
   */
  public ConditionResult approximatelyEqualTo(Entry entry,
                              VirtualAttributeRule rule,
opendj3-server-dev/src/server/org/opends/server/authorization/dseecompat/UserDN.java
@@ -346,10 +346,10 @@
    /*
     * TODO Evaluate making this method more efficient.
     *
     * The evalDNEntryAttr method isn't as efficient as it could be.  It would
     * probably be faster to to convert the clientDN to an ByteString and
     * see if the entry has that value than to decode each value as a DN and
     * see if it matches the clientDN.
     * The evalDNEntryAttr method isn't as efficient as it could be.
     * It would probably be faster to to convert the clientDN to a ByteString
     * and see if the entry has that value than to decode each value as a DN
     * and see if it matches the clientDN.
     */
    /**
     * This method searches an entry for an attribute value that is
opendj3-server-dev/src/server/org/opends/server/backends/jeb/AttributeIndex.java
@@ -32,6 +32,7 @@
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.config.server.ConfigException;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.DecodeException;
import org.forgerock.opendj.ldap.ResultCode;
@@ -42,7 +43,6 @@
import org.opends.server.admin.std.meta.LocalDBIndexCfgDefn;
import org.opends.server.admin.std.server.LocalDBIndexCfg;
import org.opends.server.api.*;
import org.forgerock.opendj.config.server.ConfigException;
import org.opends.server.core.DirectoryServer;
import org.opends.server.monitors.DatabaseEnvironmentMonitor;
import org.opends.server.types.*;
@@ -975,10 +975,8 @@
    try
    {
      // Use the ordering matching rule to normalize the value.
      MatchingRule orderingRule =
           filter.getAttributeType().getOrderingMatchingRule();
      // FIXME JNR this looks wrong, it should use normalizeAssertionValue()
      byte[] normalizedValue = orderingRule.normalizeAttributeValue(
      MatchingRule orderingRule = filter.getAttributeType().getOrderingMatchingRule();
      byte[] normalizedValue = orderingRule.normalizeAssertionValue(
          filter.getAssertionValue()).toByteArray();
      // Set the lower and upper bounds for a range search.
@@ -1229,8 +1227,8 @@
   * equal to the lower bound value, and less than or equal to the
   * upper bound value.
   *
   * @param lowerValue The lower bound value
   * @param upperValue The upper bound value
   * @param lowerValue The lower bound assertion value
   * @param upperValue The upper bound assertion value
   * @return The candidate entry IDs.
   */
  public EntryIDSet evaluateBoundedRange(ByteString lowerValue, ByteString upperValue)
@@ -1242,13 +1240,10 @@
    try
    {
      // Use the ordering matching rule to normalize the values.
      MatchingRule orderingRule =
           getAttributeType().getOrderingMatchingRule();
      // Set the lower and upper bounds for a range search.
      byte[] lower = orderingRule.normalizeAttributeValue(lowerValue).toByteArray();
      byte[] upper = orderingRule.normalizeAttributeValue(upperValue).toByteArray();
      MatchingRule orderingRule = getAttributeType().getOrderingMatchingRule();
      byte[] lower = orderingRule.normalizeAssertionValue(lowerValue).toByteArray();
      byte[] upper = orderingRule.normalizeAssertionValue(upperValue).toByteArray();
      // Read the range: lower <= keys <= upper.
      return orderingIndex.readRange(lower, upper, true, true);
@@ -1392,8 +1387,7 @@
      MatchingRule approximateMatchingRule =
          approximateFilter.getAttributeType().getApproximateMatchingRule();
      // Make a key from the normalized assertion value.
      // FIXME JNR this looks wrong, it should use normalizeAssertionValue()
      byte[] keyBytes = approximateMatchingRule.normalizeAttributeValue(
      byte[] keyBytes = approximateMatchingRule.normalizeAssertionValue(
          approximateFilter.getAssertionValue()).toByteArray();
      DatabaseEntry key = new DatabaseEntry(keyBytes);
opendj3-server-dev/src/server/org/opends/server/backends/jeb/JebFormat.java
@@ -66,8 +66,28 @@
   */
  public static long entryIDFromDatabase(byte[] bytes)
  {
    return toLong(bytes, 0, 8);
  }
  /**
   * Decode a long from a byte array, starting at start index and ending at end
   * index.
   *
   * @param bytes
   *          The bytes value of the long.
   * @param start
   *          the array index where to start computing the long
   * @param end
   *          the array index exclusive where to end computing the long
   * @return the long representation of the read bytes.
   * @throws ArrayIndexOutOfBoundsException
   *           if the bytes array length is less than end.
   */
  public static long toLong(byte[] bytes, int start, int end)
      throws ArrayIndexOutOfBoundsException
  {
    long v = 0;
    for (int i = 0; i < 8; i++)
    for (int i = start; i < end; i++)
    {
      v <<= 8;
      v |= (bytes[i] & 0xFF);
@@ -90,14 +110,7 @@
    if(bytes.length == 8)
    {
      long v = 0;
      v |= (bytes[0] & 0x7F);
      for (int i = 1; i < 8; i++)
      {
        v <<= 8;
        v |= (bytes[i] & 0xFF);
      }
      return v;
      return entryIDFromDatabase(bytes);
    }
    return Long.MAX_VALUE;
  }
opendj3-server-dev/src/server/org/opends/server/backends/jeb/SortValuesSet.java
@@ -125,7 +125,7 @@
      return true;
    }
    if (vlvIndex.comparator.compare(
        this, entryIDs.length - 1, entryID, values, types) < 0)
        this, entryIDs.length - 1, entryID, values) < 0)
    {
      long[] updatedEntryIDs = new long[entryIDs.length + 1];
      System.arraycopy(entryIDs, 0, updatedEntryIDs, 0, entryIDs.length);
@@ -157,7 +157,7 @@
    }
    else
    {
      int pos = binarySearch(entryID, values, types);
      int pos = binarySearch(entryID, values);
      if(pos >= 0)
      {
        if(entryIDs[pos] == entryID)
@@ -220,13 +220,12 @@
   *
   * @param entryID The entry ID to remove.
   * @param values The values to remove.
   * @param types The types of the values to remove.
   * @return True if the information was successfully removed or False
   * otherwise.
   * @throws DirectoryException If a Directory Server error occurs.
   * @throws DatabaseException If an error occurs in the JE database.
   */
  public boolean remove(long entryID, ByteString[] values, AttributeType[] types)
  public boolean remove(long entryID, ByteString[] values)
      throws DatabaseException, DirectoryException
  {
    if(entryIDs == null || entryIDs.length == 0)
@@ -239,7 +238,7 @@
      updateValuesBytesOffsets();
    }
    int pos = binarySearch(entryID, values, types);
    int pos = binarySearch(entryID, values);
    if(pos < 0)
    {
      // Not found.
@@ -413,13 +412,12 @@
   *
   * @param entryID The entry ID to match or -1 if not matching on entry ID.
   * @param values The values to match.
   * @param types The types of the values to match.
   * @return Index of the entry matching the values and optionally the entry ID
   * if it is found or a negative index if its not found.
   * @throws DirectoryException If a Directory Server error occurs.
   * @throws DatabaseException If an error occurs in the JE database.
   */
  int binarySearch(long entryID, ByteString[] values, AttributeType[] types)
  int binarySearch(long entryID, ByteString... values)
      throws DatabaseException, DirectoryException
  {
    if(entryIDs == null || entryIDs.length == 0)
@@ -431,7 +429,7 @@
    for(int j = entryIDs.length - 1; i <= j;)
    {
      int k = i + j >> 1;
      int l = vlvIndex.comparator.compare(this, k, entryID, values, types);
      int l = vlvIndex.comparator.compare(this, k, entryID, values);
      if (l < 0)
      {
        i = k + 1;
opendj3-server-dev/src/server/org/opends/server/backends/jeb/VLVIndex.java
@@ -149,8 +149,7 @@
    try
    {
      this.filter =
          SearchFilter.createFilterFromString(config.getFilter());
      this.filter = SearchFilter.createFilterFromString(config.getFilter());
    }
    catch(Exception e)
    {
@@ -161,8 +160,7 @@
    String[] sortAttrs = config.getSortOrder().split(" ");
    SortKey[] sortKeys = new SortKey[sortAttrs.length];
    MatchingRule[] orderingRules =
        new MatchingRule[sortAttrs.length];
    MatchingRule[] orderingRules = new MatchingRule[sortAttrs.length];
    boolean[] ascending = new boolean[sortAttrs.length];
    for(int i = 0; i < sortAttrs.length; i++)
    {
@@ -666,53 +664,45 @@
      ByteString[] values, AttributeType[] types) throws DatabaseException,
      DirectoryException
  {
    SortValuesSet sortValuesSet = null;
    DatabaseEntry key = new DatabaseEntry();
    OperationStatus status;
    LockMode lockMode = LockMode.DEFAULT;
    DatabaseEntry data = new DatabaseEntry();
    key.setData(encodeKey(entryID, values, types));
    return getSortValuesSet(txn, key, data, LockMode.DEFAULT);
  }
    Cursor cursor = openCursor(txn, CursorConfig.READ_COMMITTED);
    try
  private SortValuesSet getSortValuesSet(Transaction txn, DatabaseEntry key,
      DatabaseEntry data, LockMode lockMode)
  {
    OperationStatus status = getSearchKeyRange(txn, key, data, lockMode);
    if (status != OperationStatus.SUCCESS)
    {
      key.setData(encodeKey(entryID, values, types));
      status = cursor.getSearchKeyRange(key, data,lockMode);
      if(status != OperationStatus.SUCCESS)
      // There are no records in the database
      if (logger.isTraceEnabled())
      {
        // There are no records in the database
        if(logger.isTraceEnabled())
        {
          logger.trace("No sort values set exist in VLV vlvIndex %s. " +
              "Creating unbound set.", config.getName());
        }
        sortValuesSet = new SortValuesSet(this);
        logger.trace("No sort values set exist in VLV vlvIndex %s. "
            + "Creating unbound set.", config.getName());
      }
      else
      {
        if(logger.isTraceEnabled())
        {
          StringBuilder searchKeyHex = new StringBuilder();
          StaticUtils.byteArrayToHexPlusAscii(searchKeyHex, key.getData(), 4);
          StringBuilder foundKeyHex = new StringBuilder();
          StaticUtils.byteArrayToHexPlusAscii(foundKeyHex, key.getData(), 4);
          logger.trace("Retrieved a sort values set in VLV vlvIndex " +
              "%s\nSearch Key:%s\nFound Key:%s\n",
                              config.getName(),
                              searchKeyHex,
                              foundKeyHex);
        }
        sortValuesSet = new SortValuesSet(key.getData(), data.getData(),
                                          this);
      }
    }
    finally
    {
      cursor.close();
      // this could not be found, so clean the key for later reuse
      key.setData(new byte[0]);
      return new SortValuesSet(this);
    }
    return sortValuesSet;
    if (logger.isTraceEnabled())
    {
      logSearchKeyResult(key);
    }
    return new SortValuesSet(key.getData(), data.getData(), this);
  }
  private void logSearchKeyResult(DatabaseEntry key)
  {
    StringBuilder searchKeyHex = new StringBuilder();
    StaticUtils.byteArrayToHexPlusAscii(searchKeyHex, key.getData(), 4);
    StringBuilder foundKeyHex = new StringBuilder();
    StaticUtils.byteArrayToHexPlusAscii(foundKeyHex, key.getData(), 4);
    logger.trace("Retrieved a sort values set in VLV vlvIndex %s\n" +
        "Search Key:%s\nFound Key:%s\n",
        config.getName(), searchKeyHex, foundKeyHex);
  }
  /**
@@ -736,7 +726,7 @@
      DatabaseException, DirectoryException
  {
    SortValuesSet valuesSet = getSortValuesSet(txn, entryID, values, types);
    int pos = valuesSet.binarySearch(entryID, values, types);
    int pos = valuesSet.binarySearch(entryID, values);
    return pos >= 0;
  }
@@ -746,68 +736,19 @@
    ByteString[] values = getSortValues(entry);
    AttributeType[] types = getSortTypes();
    DatabaseEntry key = new DatabaseEntry();
    OperationStatus status;
    LockMode lockMode = LockMode.RMW;
    DatabaseEntry data = new DatabaseEntry();
    Cursor cursor = openCursor(txn, CursorConfig.READ_COMMITTED);
    try
    {
      key.setData(encodeKey(entryID, values, types));
      status = cursor.getSearchKeyRange(key, data,lockMode);
    }
    finally
    {
      cursor.close();
    }
    SortValuesSet sortValuesSet;
    if(status != OperationStatus.SUCCESS)
    {
      // There are no records in the database
      if(logger.isTraceEnabled())
      {
        logger.trace("No sort values set exist in VLV vlvIndex %s. " +
            "Creating unbound set.", config.getName());
      }
      sortValuesSet = new SortValuesSet(this);
      key.setData(new byte[0]);
    }
    else
    {
      if(logger.isTraceEnabled())
      {
        StringBuilder searchKeyHex = new StringBuilder();
        StaticUtils.byteArrayToHexPlusAscii(searchKeyHex, key.getData(), 4);
        StringBuilder foundKeyHex = new StringBuilder();
        StaticUtils.byteArrayToHexPlusAscii(foundKeyHex, key.getData(), 4);
        logger.trace("Retrieved a sort values set in VLV vlvIndex " +
            "%s\nSearch Key:%s\nFound Key:%s\n",
                            config.getName(),
                            searchKeyHex,
                            foundKeyHex);
      }
      sortValuesSet = new SortValuesSet(key.getData(), data.getData(),
                                        this);
    }
    key.setData(encodeKey(entryID, values, types));
    SortValuesSet sortValuesSet =
        getSortValuesSet(txn, key, data, LockMode.RMW);
    boolean success = sortValuesSet.add(entryID, values, types);
    int newSize = sortValuesSet.size();
    if(newSize >= sortedSetCapacity)
    {
      SortValuesSet splitSortValuesSet = sortValuesSet.split(newSize / 2);
      byte[] splitAfter = splitSortValuesSet.toDatabase();
      key.setData(splitSortValuesSet.getKeyBytes());
      data.setData(splitAfter);
      put(txn, key, data);
      byte[] after = sortValuesSet.toDatabase();
      key.setData(sortValuesSet.getKeyBytes());
      data.setData(after);
      put(txn, key, data);
      put(txn, key, data, splitSortValuesSet); // splitAfter
      put(txn, key, data, sortValuesSet); // after
      if(logger.isTraceEnabled())
      {
@@ -820,8 +761,7 @@
    }
    else
    {
      byte[] after = sortValuesSet.toDatabase();
      data.setData(after);
      data.setData(sortValuesSet.toDatabase()); // after
      put(txn, key, data);
      // TODO: What about phantoms?
    }
@@ -834,6 +774,14 @@
    return success;
  }
  private void put(Transaction txn, DatabaseEntry key, DatabaseEntry data,
      SortValuesSet set) throws DirectoryException
  {
    key.setData(set.getKeyBytes());
    data.setData(set.toDatabase());
    put(txn, key, data);
  }
  /**
   * Gets the types of the attribute values to sort.
   *
@@ -845,8 +793,7 @@
    AttributeType[] types = new AttributeType[sortKeys.length];
    for (int i = 0; i < sortKeys.length; i++)
    {
      SortKey sortKey = sortKeys[i];
      types[i] = sortKey.getAttributeType();
      types[i] = sortKeys[i].getAttributeType();
    }
    return types;
  }
@@ -857,38 +804,18 @@
    ByteString[] values = getSortValues(entry);
    AttributeType[] types = getSortTypes();
    DatabaseEntry key = new DatabaseEntry();
    OperationStatus status;
    LockMode lockMode = LockMode.RMW;
    DatabaseEntry data = new DatabaseEntry();
    Cursor cursor = openCursor(txn, CursorConfig.READ_COMMITTED);
    try
    {
      key.setData(encodeKey(entryID, values, types));
      status = cursor.getSearchKeyRange(key, data,lockMode);
    }
    finally
    {
      cursor.close();
    }
    key.setData(encodeKey(entryID, values, types));
    OperationStatus status = getSearchKeyRange(txn, key, data, LockMode.RMW);
    if(status == OperationStatus.SUCCESS)
    {
      if(logger.isTraceEnabled())
      {
        StringBuilder searchKeyHex = new StringBuilder();
        StaticUtils.byteArrayToHexPlusAscii(searchKeyHex, key.getData(), 4);
        StringBuilder foundKeyHex = new StringBuilder();
        StaticUtils.byteArrayToHexPlusAscii(foundKeyHex, key.getData(), 4);
        logger.trace("Retrieved a sort values set in VLV vlvIndex " +
            "%s\nSearch Key:%s\nFound Key:%s\n",
                            config.getName(),
                            searchKeyHex,
                            foundKeyHex);
        logSearchKeyResult(key);
      }
      SortValuesSet sortValuesSet = new SortValuesSet(key.getData(), data.getData(), this);
      boolean success = sortValuesSet.remove(entryID, values, types);
      boolean success = sortValuesSet.remove(entryID, values);
      byte[] after = sortValuesSet.toDatabase();
      if(after == null)
@@ -911,6 +838,20 @@
    return false;
  }
  private OperationStatus getSearchKeyRange(Transaction txn, DatabaseEntry key,
      DatabaseEntry data, LockMode lockMode)
  {
    Cursor cursor = openCursor(txn, CursorConfig.READ_COMMITTED);
    try
    {
      return cursor.getSearchKeyRange(key, data, lockMode);
    }
    finally
    {
      cursor.close();
    }
  }
  /**
   * Update the vlvIndex with the specified values to add and delete.
   *
@@ -935,10 +876,7 @@
    }
    DatabaseEntry key = new DatabaseEntry();
    OperationStatus status;
    LockMode lockMode = LockMode.RMW;
    DatabaseEntry data = new DatabaseEntry();
    SortValuesSet sortValuesSet;
    Iterator<SortValues> aValues = null;
    Iterator<SortValues> dValues = null;
    SortValues av = null;
@@ -985,46 +923,7 @@
        break;
      }
      Cursor cursor = openCursor(txn, CursorConfig.READ_COMMITTED);
      try
      {
        status = cursor.getSearchKeyRange(key, data,lockMode);
      }
      finally
      {
        cursor.close();
      }
      if(status != OperationStatus.SUCCESS)
      {
        // There are no records in the database
        if(logger.isTraceEnabled())
        {
          logger.trace("No sort values set exist in VLV vlvIndex %s. " +
              "Creating unbound set.", config.getName());
        }
        sortValuesSet = new SortValuesSet(this);
        key.setData(new byte[0]);
      }
      else
      {
        if(logger.isTraceEnabled())
        {
          StringBuilder searchKeyHex = new StringBuilder();
          StaticUtils.byteArrayToHexPlusAscii(searchKeyHex, key.getData(), 4);
          StringBuilder foundKeyHex = new StringBuilder();
          StaticUtils.byteArrayToHexPlusAscii(foundKeyHex, key.getData(), 4);
          logger.trace("Retrieved a sort values set in VLV vlvIndex " +
              "%s\nSearch Key:%s\nFound Key:%s\n",
              config.getName(),
              searchKeyHex,
              foundKeyHex);
        }
        sortValuesSet = new SortValuesSet(key.getData(), data.getData(),
            this);
      }
      final SortValuesSet sortValuesSet = getSortValuesSet(txn, data, data, LockMode.RMW);
      int oldSize = sortValuesSet.size();
      if(key.getData().length == 0)
      {
@@ -1032,29 +931,13 @@
        while(av != null)
        {
          sortValuesSet.add(av.getEntryID(), av.getValues(), av.getTypes());
          aValues.remove();
          if(aValues.hasNext())
          {
            av = aValues.next();
          }
          else
          {
            av = null;
          }
          av = moveToNextSortValues(aValues);
        }
        while(dv != null)
        {
          sortValuesSet.remove(dv.getEntryID(), dv.getValues(), dv.getTypes());
          dValues.remove();
          if(dValues.hasNext())
          {
            dv = dValues.next();
          }
          else
          {
            dv = null;
          }
          sortValuesSet.remove(dv.getEntryID(), dv.getValues());
          dv = moveToNextSortValues(dValues);
        }
      }
      else
@@ -1064,29 +947,13 @@
        while(av != null && av.compareTo(maxValues) <= 0)
        {
          sortValuesSet.add(av.getEntryID(), av.getValues(), av.getTypes());
          aValues.remove();
          if(aValues.hasNext())
          {
            av = aValues.next();
          }
          else
          {
            av = null;
          }
          av = moveToNextSortValues(aValues);
        }
        while(dv != null && dv.compareTo(maxValues) <= 0)
        {
          sortValuesSet.remove(dv.getEntryID(), dv.getValues(), dv.getTypes());
          dValues.remove();
          if(dValues.hasNext())
          {
            dv = dValues.next();
          }
          else
          {
            dv = null;
          }
          sortValuesSet.remove(dv.getEntryID(), dv.getValues());
          dv = moveToNextSortValues(dValues);
        }
      }
@@ -1094,14 +961,8 @@
      if(newSize >= sortedSetCapacity)
      {
        SortValuesSet splitSortValuesSet = sortValuesSet.split(newSize / 2);
        byte[] splitAfter = splitSortValuesSet.toDatabase();
        key.setData(splitSortValuesSet.getKeyBytes());
        data.setData(splitAfter);
        put(txn, key, data);
        byte[] after = sortValuesSet.toDatabase();
        key.setData(sortValuesSet.getKeyBytes());
        data.setData(after);
        put(txn, key, data);
        put(txn, key, data, splitSortValuesSet); // splitAfter
        put(txn, key, data, sortValuesSet); // after
        if(logger.isTraceEnabled())
        {
@@ -1127,6 +988,16 @@
    }
  }
  private SortValues moveToNextSortValues(Iterator<SortValues> sortValues)
  {
    sortValues.remove();
    if (sortValues.hasNext())
    {
      return sortValues.next();
    }
    return null;
  }
  private byte[] encodeKey(SortValues sv) throws DirectoryException
  {
    return encodeKey(sv.getEntryID(), sv.getValues(), sv.getTypes());
@@ -1156,23 +1027,11 @@
                             StringBuilder debugBuilder)
      throws DirectoryException, DatabaseException
  {
    if(!trusted || rebuildRunning)
    {
      return null;
    }
    if(!searchOperation.getBaseDN().equals(baseDN))
    {
      return null;
    }
    if(!searchOperation.getScope().equals(scope))
    {
      return null;
    }
    if(!searchOperation.getFilter().equals(filter))
    {
      return null;
    }
    if(!sortControl.getSortOrder().equals(this.sortOrder))
    if (!trusted || rebuildRunning
        || !searchOperation.getBaseDN().equals(baseDN)
        || !searchOperation.getScope().equals(scope)
        || !searchOperation.getFilter().equals(filter)
        || !sortControl.getSortOrder().equals(sortOrder))
    {
      return null;
    }
@@ -1241,34 +1100,21 @@
        int count = 1 + beforeCount + afterCount;
        selectedIDs = new long[count];
        DatabaseEntry key = new DatabaseEntry();
        OperationStatus status;
        LockMode lockMode = LockMode.DEFAULT;
        DatabaseEntry data = new DatabaseEntry();
        Cursor cursor = openCursor(txn, CursorConfig.READ_COMMITTED);
        try
        {
          DatabaseEntry key = new DatabaseEntry();
          DatabaseEntry data = new DatabaseEntry();
          LockMode lockMode = LockMode.DEFAULT;
          //Locate the set that contains the target entry.
          int cursorCount = 0;
          int selectedPos = 0;
          status = cursor.getFirst(key, data,lockMode);
          OperationStatus status = cursor.getFirst(key, data, lockMode);
          while(status == OperationStatus.SUCCESS)
          {
            if(logger.isTraceEnabled())
            {
              StringBuilder searchKeyHex = new StringBuilder();
              StaticUtils.byteArrayToHexPlusAscii(searchKeyHex, key.getData(),
                                                  4);
              StringBuilder foundKeyHex = new StringBuilder();
              StaticUtils.byteArrayToHexPlusAscii(foundKeyHex, key.getData(),
                                                  4);
              logger.trace("Retrieved a sort values set in VLV " +
                  "vlvIndex %s\nSearch Key:%s\nFound Key:%s\n",
                                  config.getName(),
                                  searchKeyHex,
                                  foundKeyHex);
              logSearchKeyResult(key);
            }
            long[] IDs = SortValuesSet.getEncodedIDs(data.getData(), 0);
            for(int i = startPos + selectedPos - cursorCount;
@@ -1313,14 +1159,12 @@
        int includedAfterCount  = 0;
        LinkedList<EntryID> idList = new LinkedList<EntryID>();
        DatabaseEntry key = new DatabaseEntry();
        OperationStatus status;
        LockMode lockMode = LockMode.DEFAULT;
        DatabaseEntry data = new DatabaseEntry();
        Cursor cursor = openCursor(txn, CursorConfig.READ_COMMITTED);
        try
        {
          LockMode lockMode = LockMode.DEFAULT;
          ByteSequence vBytes = vlvRequest.getGreaterThanOrEqualAssertion();
          ByteStringBuilder keyBytes =
              new ByteStringBuilder(vBytes.length() + 4);
@@ -1328,34 +1172,18 @@
          vBytes.copyTo(keyBytes);
          key.setData(keyBytes.getBackingArray(), 0, keyBytes.length());
          status = cursor.getSearchKeyRange(key, data, lockMode);
          OperationStatus status = cursor.getSearchKeyRange(key, data, lockMode);
          if(status == OperationStatus.SUCCESS)
          {
            if(logger.isTraceEnabled())
            {
              StringBuilder searchKeyHex = new StringBuilder();
              StaticUtils.byteArrayToHexPlusAscii(searchKeyHex, key.getData(),
                                                  4);
              StringBuilder foundKeyHex = new StringBuilder();
              StaticUtils.byteArrayToHexPlusAscii(foundKeyHex, key.getData(),
                                                  4);
              logger.trace("Retrieved a sort values set in VLV " +
                  "vlvIndex %s\nSearch Key:%s\nFound Key:%s\n",
                                  config.getName(),
                                  searchKeyHex,
                                  foundKeyHex);
              logSearchKeyResult(key);
            }
            SortValuesSet sortValuesSet =
                new SortValuesSet(key.getData(), data.getData(), this);
            ByteString[] assertionValue = new ByteString[] {
                vlvRequest.getGreaterThanOrEqualAssertion()
            };
            AttributeType[] assertionType = new AttributeType[] {
                sortOrder.getSortKeys()[0].getAttributeType()
            };
            int adjustedTargetOffset =
                sortValuesSet.binarySearch(-1, assertionValue, assertionType);
            int adjustedTargetOffset = sortValuesSet.binarySearch(
                -1, vlvRequest.getGreaterThanOrEqualAssertion());
            if(adjustedTargetOffset < 0)
            {
              // For a negative return value r, the vlvIndex -(r+1) gives the
@@ -1379,7 +1207,6 @@
              }
              status = cursor.getPrev(key, data, lockMode);
              if(status != OperationStatus.SUCCESS)
              {
                break;
@@ -1387,8 +1214,7 @@
              if(includedBeforeCount < beforeCount)
              {
                lastIDs =
                    SortValuesSet.getEncodedIDs(data.getData(), 0);
                lastIDs = SortValuesSet.getEncodedIDs(data.getData(), 0);
                lastOffset = lastIDs.length - 1;
                targetOffset += lastIDs.length;
              }
@@ -1423,14 +1249,12 @@
              }
              status = cursor.getNext(key, data, lockMode);
              if(status != OperationStatus.SUCCESS)
              {
                break;
              }
              lastIDs =
                  SortValuesSet.getEncodedIDs(data.getData(), 0);
              lastIDs = SortValuesSet.getEncodedIDs(data.getData(), 0);
              lastOffset = 0;
              afterIDCount += lastIDs.length;
            }
@@ -1465,28 +1289,18 @@
      LinkedList<long[]> idSets = new LinkedList<long[]>();
      int currentCount = 0;
      DatabaseEntry key = new DatabaseEntry();
      OperationStatus status;
      LockMode lockMode = LockMode.RMW;
      DatabaseEntry data = new DatabaseEntry();
      Cursor cursor = openCursor(txn, CursorConfig.READ_COMMITTED);
      try
      {
        status = cursor.getFirst(key, data, lockMode);
        LockMode lockMode = LockMode.RMW;
        OperationStatus status = cursor.getFirst(key, data, lockMode);
        while(status == OperationStatus.SUCCESS)
        {
          if(logger.isTraceEnabled())
          {
            StringBuilder searchKeyHex = new StringBuilder();
            StaticUtils.byteArrayToHexPlusAscii(searchKeyHex, key.getData(), 4);
            StringBuilder foundKeyHex = new StringBuilder();
            StaticUtils.byteArrayToHexPlusAscii(foundKeyHex, key.getData(), 4);
            logger.trace("Retrieved a sort values set in VLV vlvIndex " +
                "%s\nSearch Key:%s\nFound Key:%s\n",
                                config.getName(),
                                searchKeyHex,
                                foundKeyHex);
            logSearchKeyResult(key);
          }
          long[] ids = SortValuesSet.getEncodedIDs(data.getData(), 0);
          idSets.add(ids);
@@ -1563,12 +1377,9 @@
    for (int i=0; i < sortKeys.length; i++)
    {
      SortKey sortKey = sortKeys[i];
      AttributeType attrType = sortKey.getAttributeType();
      List<Attribute> attrList = entry.getAttribute(attrType);
      List<Attribute> attrList = entry.getAttribute(sortKey.getAttributeType());
      if (attrList != null)
      {
        ByteString sortValue = null;
        // There may be multiple versions of this attribute in the target entry
        // (e.g., with different sets of options), and it may also be a
        // multivalued attribute.  In that case, we need to find the value that
@@ -1576,15 +1387,12 @@
        // in ascending order, we want to find the lowest value; for sorting in
        // descending order, we want to find the highest value).  This is
        // handled by the SortKey.compareValues method.
        ByteString sortValue = null;
        for (Attribute a : attrList)
        {
          for (ByteString v : a)
          {
            if (sortValue == null)
            {
              sortValue = v;
            }
            else if (sortKey.compareValues(v, sortValue) < 0)
            if (sortValue == null || sortKey.compareValues(v, sortValue) < 0)
            {
              sortValue = v;
            }
@@ -1647,8 +1455,7 @@
   * @return The sort values represented by the key bytes.
   * @throws DirectoryException If a Directory Server error occurs.
   */
  SortValues decodeKey(byte[] keyBytes)
      throws DirectoryException
  SortValues decodeKey(byte[] keyBytes) throws DirectoryException
  {
    if(keyBytes == null || keyBytes.length == 0)
    {
@@ -1685,16 +1492,8 @@
      vBytesPos += valueLength;
    }
    // FIXME: Should pos+offset method for decoding IDs be added to
    // JebFormat?
    long v = 0;
    for (int i = vBytesPos; i < keyBytes.length; i++)
    {
      v <<= 8;
      v |= (keyBytes[i] & 0xFF);
    }
    return new SortValues(new EntryID(v), attributeValues, sortOrder);
    final long id = JebFormat.toLong(keyBytes, vBytesPos, keyBytes.length);
    return new SortValues(new EntryID(id), attributeValues, sortOrder);
  }
  /**
@@ -1732,8 +1531,7 @@
  {
    try
    {
      this.filter =
          SearchFilter.createFilterFromString(cfg.getFilter());
      this.filter = SearchFilter.createFilterFromString(cfg.getFilter());
    }
    catch(Exception e)
    {
@@ -1746,8 +1544,7 @@
    String[] sortAttrs = cfg.getSortOrder().split(" ");
    SortKey[] sortKeys = new SortKey[sortAttrs.length];
    MatchingRule[] orderingRules =
        new MatchingRule[sortAttrs.length];
    MatchingRule[] orderingRules = new MatchingRule[sortAttrs.length];
    boolean[] ascending = new boolean[sortAttrs.length];
    for(int i = 0; i < sortAttrs.length; i++)
    {
@@ -1815,15 +1612,13 @@
    }
    // Update sort set capacity only if changed.
    if(config.getMaxBlockSize() !=
        cfg.getMaxBlockSize())
    if (config.getMaxBlockSize() != cfg.getMaxBlockSize())
    {
      this.sortedSetCapacity = cfg.getMaxBlockSize();
      // Require admin action only if the new capacity is larger. Otherwise,
      // we will lazyly update the sorted sets.
      if(config.getMaxBlockSize() <
          cfg.getMaxBlockSize())
      if (config.getMaxBlockSize() < cfg.getMaxBlockSize())
      {
        adminActionRequired = true;
      }
@@ -1834,8 +1629,7 @@
    {
      try
      {
        this.filter =
            SearchFilter.createFilterFromString(cfg.getFilter());
        this.filter = SearchFilter.createFilterFromString(cfg.getFilter());
        adminActionRequired = true;
      }
      catch(Exception e)
@@ -1852,13 +1646,11 @@
    }
    // Update the sort order only if changed.
    if(!config.getSortOrder().equals(
        cfg.getSortOrder()))
    if (!config.getSortOrder().equals(cfg.getSortOrder()))
    {
      String[] sortAttrs = cfg.getSortOrder().split(" ");
      SortKey[] sortKeys = new SortKey[sortAttrs.length];
      MatchingRule[] orderingRules =
          new MatchingRule[sortAttrs.length];
      MatchingRule[] orderingRules = new MatchingRule[sortAttrs.length];
      boolean[] ascending = new boolean[sortAttrs.length];
      for(int i = 0; i < sortAttrs.length; i++)
      {
@@ -1935,8 +1727,7 @@
    if(adminActionRequired)
    {
      trusted = false;
      LocalizableMessage message = NOTE_JEB_INDEX_ADD_REQUIRES_REBUILD.get(name);
      messages.add(message);
      messages.add(NOTE_JEB_INDEX_ADD_REQUIRES_REBUILD.get(name));
      try
      {
        state.putIndexTrustState(null, this, false);
opendj3-server-dev/src/server/org/opends/server/backends/jeb/VLVKeyComparator.java
@@ -34,7 +34,6 @@
import org.forgerock.opendj.ldap.DecodeException;
import org.forgerock.opendj.ldap.ResultCode;
import org.opends.server.api.MatchingRule;
import org.opends.server.types.AttributeType;
import org.opends.server.types.DirectoryException;
import com.sleepycat.je.DatabaseException;
@@ -66,8 +65,7 @@
   * @param ascending     The array of booleans indicating the ordering for
   *                      each value.
   */
  public VLVKeyComparator(MatchingRule[] orderingRules,
                          boolean[] ascending)
  public VLVKeyComparator(MatchingRule[] orderingRules, boolean[] ascending)
  {
    this.orderingRules = orderingRules;
    this.ascending = ascending;
@@ -205,19 +203,8 @@
    if(b1Pos + 8 <= b1.length && b2Pos + 8 <= b2.length)
    {
      long b1ID = 0;
      for (int i = b1Pos; i < b1Pos + 8; i++)
      {
        b1ID <<= 8;
        b1ID |= (b1[i] & 0xFF);
      }
      long b2ID = 0;
      for (int i = b2Pos; i < b2Pos + 8; i++)
      {
        b2ID <<= 8;
        b2ID |= (b2[i] & 0xFF);
      }
      long b1ID = JebFormat.toLong(b1, b1Pos, b1Pos + 8);
      long b2ID = JebFormat.toLong(b2, b2Pos, b2Pos + 8);
      long idDifference = (b1ID - b2ID);
      if (idDifference < 0)
@@ -235,10 +222,8 @@
    }
    // If we've gotten here, then we can't tell the difference between the sets
    // of available values and entry IDs are not all available, so just return
    // 0
    // of available values and entry IDs are not all available, so just return 0
    return 0;
  }
  /**
@@ -256,10 +241,8 @@
   *
   * @param  set  The sort values set to containing the values.
   * @param  index The index of the values in the set.
   * @param  entryID The entry ID to use in the comparasion.
   * @param  values The values to use in the comparasion.
   * @param  types The types of the values to use in the comparasion.
   *
   * @param  entryID The entry ID to use in the comparison.
   * @param  values The values to use in the comparison.
   * @return  A negative integer if the values in the set should come before
   *          the given values in ascending order, a positive integer if
   *          the values in the set should come after the given values in
@@ -273,8 +256,7 @@
   *                              associated equality matching rule).
   */
  public int compare(SortValuesSet set, int index, long entryID,
      ByteString[] values, AttributeType[] types) throws DatabaseException,
      DirectoryException
      ByteString[] values) throws DatabaseException, DirectoryException
  {
    for (int j=0; j < orderingRules.length; j++)
    {
@@ -290,8 +272,7 @@
      {
        try
        {
          final MatchingRule eqRule = types[j].getEqualityMatchingRule();
          b2Bytes = eqRule.normalizeAttributeValue(values[j]);
          b2Bytes = orderingRules[j].normalizeAttributeValue(values[j]);
        }
        catch (DecodeException e)
        {
opendj3-server-dev/src/server/org/opends/server/types/AbstractAttribute.java
@@ -30,6 +30,8 @@
import java.util.Set;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.DecodeException;
import org.opends.server.api.MatchingRule;
import static org.opends.server.util.StaticUtils.*;
@@ -201,16 +203,22 @@
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public int hashCode()
  {
    int hashCode = getAttributeType().hashCode();
    for (ByteString value : this)
    {
      hashCode += value.hashCode();
      try
      {
        MatchingRule eqRule = getAttributeType().getEqualityMatchingRule();
        hashCode += eqRule.normalizeAttributeValue(value).hashCode();
      }
      catch (DecodeException e)
      {
        hashCode += value.hashCode();
      }
    }
    return hashCode;
  }
@@ -231,8 +239,7 @@
  {
    String noption = toLowerCase(option);
    // Cannot use Set.contains() because the options are not
    // normalized.
    // Cannot use Set.contains() because the options are not normalized.
    for (String o : getOptions())
    {
      String no = toLowerCase(o);
@@ -299,8 +306,7 @@
      return false;
    }
    // Cannot use Set.containsAll() because the set of options are
    // not normalized.
    // Cannot use Set.containsAll() because the set of options are not normalized.
    for (String option : options)
    {
      if (!hasOption(option))
@@ -308,7 +314,6 @@
        return false;
      }
    }
    return true;
  }
opendj3-server-dev/src/server/org/opends/server/types/Attribute.java
@@ -62,14 +62,14 @@
   * Indicates whether this attribute has any value(s) that are
   * approximately equal to the provided value.
   *
   * @param value
   *          The value for which to make the determination.
   * @param assertionValue
   *          The assertion value for which to make the determination.
   * @return <CODE>UNDEFINED</CODE> if this attribute does not have
   *         an approximate matching rule, <CODE>TRUE</CODE> if at
   *         least one value is approximately equal to the provided
   *         value, or <CODE>false</CODE> otherwise.
   */
  ConditionResult approximatelyEqualTo(ByteString value);
  ConditionResult approximatelyEqualTo(ByteString assertionValue);
@@ -100,6 +100,18 @@
  /**
   * Indicates whether this attribute matches the specified assertion value.
   *
   * @param assertionValue
   *          The assertion value for which to make the determination.
   * @return <CODE>true</CODE> if this attribute matches the specified assertion
   *         value, or <CODE>false</CODE> if not.
   */
  ConditionResult matchesEqualityAssertion(ByteString assertionValue);
  /**
   * Indicates whether the provided object is an attribute that is
   * equal to this attribute. It will be considered equal if the
   * attribute type, set of values, and set of options are equal.
@@ -159,14 +171,14 @@
   * Indicates whether this attribute has any value(s) that are
   * greater than or equal to the provided value.
   *
   * @param value
   *          The value for which to make the determination.
   * @param assertionValue
   *          The assertion value for which to make the determination.
   * @return <CODE>UNDEFINED</CODE> if this attribute does not have
   *         an ordering matching rule, <CODE>TRUE</CODE> if at
   *         least one value is greater than or equal to the provided
   *         value, or <CODE>false</CODE> otherwise.
   *         assertion value, or <CODE>false</CODE> otherwise.
   */
  ConditionResult greaterThanOrEqualTo(ByteString value);
  ConditionResult greaterThanOrEqualTo(ByteString assertionValue);
@@ -257,14 +269,14 @@
   * Indicates whether this attribute has any value(s) that are less
   * than or equal to the provided value.
   *
   * @param value
   *          The value for which to make the determination.
   * @param assertionValue
   *          The assertion value for which to make the determination.
   * @return <CODE>UNDEFINED</CODE> if this attribute does not have
   *         an ordering matching rule, <CODE>TRUE</CODE> if at
   *         least one value is less than or equal to the provided
   *         value, or <CODE>false</CODE> otherwise.
   *         assertion value, or <CODE>false</CODE> otherwise.
   */
  ConditionResult lessThanOrEqualTo(ByteString value);
  ConditionResult lessThanOrEqualTo(ByteString assertionValue);
opendj3-server-dev/src/server/org/opends/server/types/AttributeBuilder.java
@@ -32,7 +32,6 @@
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -128,7 +127,11 @@
    // The name of this attribute as provided by the end user.
    private final String name;
    // The unmodifiable set of values for this attribute.
    /**
     * The unmodifiable map of values for this attribute. The key is the
     * attribute value normalized according to the equality matching rule and
     * the value is the non normalized value.
     */
    private final Map<ByteString, ByteString> values;
@@ -158,7 +161,7 @@
     * {@inheritDoc}
     */
    @Override
    public final ConditionResult approximatelyEqualTo(ByteString value)
    public final ConditionResult approximatelyEqualTo(ByteString assertionValue)
    {
      MatchingRule matchingRule = attributeType.getApproximateMatchingRule();
      if (matchingRule == null)
@@ -169,7 +172,7 @@
      Assertion assertion = null;
      try
      {
        assertion = matchingRule.getAssertion(value);
        assertion = matchingRule.getAssertion(assertionValue);
      }
      catch (Exception e)
      {
@@ -208,6 +211,28 @@
      return values.containsKey(normalize(attributeType, value));
    }
    /** {@inheritDoc} */
    @Override
    public ConditionResult matchesEqualityAssertion(ByteString assertionValue)
    {
      try
      {
        MatchingRule eqRule = getAttributeType().getEqualityMatchingRule();
        final Assertion assertion = eqRule.getAssertion(assertionValue);
        for (ByteString normalizedValue : values.keySet())
        {
          if (assertion.matches(normalizedValue).toBoolean())
          {
            return ConditionResult.TRUE;
          }
        }
        return ConditionResult.FALSE;
      }
      catch (DecodeException e)
      {
        return ConditionResult.UNDEFINED;
      }
    }
    /**
     * {@inheritDoc}
@@ -235,7 +260,7 @@
     * {@inheritDoc}
     */
    @Override
    public final ConditionResult greaterThanOrEqualTo(ByteString value)
    public final ConditionResult greaterThanOrEqualTo(ByteString assertionValue)
    {
      MatchingRule matchingRule = attributeType.getOrderingMatchingRule();
      if (matchingRule == null)
@@ -246,7 +271,7 @@
      Assertion assertion;
      try
      {
        assertion = matchingRule.getGreaterOrEqualAssertion(value);
        assertion = matchingRule.getGreaterOrEqualAssertion(assertionValue);
      }
      catch (DecodeException e)
      {
@@ -268,8 +293,7 @@
        {
          logger.traceException(e);
          // We couldn't normalize one of the attribute values. If we
          // can't find a definite match, then we should return
          // "undefined".
          // can't find a definite match, then we should return "undefined".
          result = ConditionResult.UNDEFINED;
        }
      }
@@ -305,7 +329,7 @@
     * {@inheritDoc}
     */
    @Override
    public final ConditionResult lessThanOrEqualTo(ByteString value)
    public final ConditionResult lessThanOrEqualTo(ByteString assertionValue)
    {
      MatchingRule matchingRule = attributeType.getOrderingMatchingRule();
      if (matchingRule == null)
@@ -316,7 +340,7 @@
      Assertion assertion;
      try
      {
        assertion = matchingRule.getLessOrEqualAssertion(value);
        assertion = matchingRule.getLessOrEqualAssertion(assertionValue);
      }
      catch (DecodeException e)
      {
@@ -1076,8 +1100,8 @@
  private final SmallSet<String> options = new SmallSet<String>();
  /** The map of normalized values => values for this attribute. */
  private LinkedHashMap<ByteString, ByteString> values =
      new LinkedHashMap<ByteString, ByteString>();
  private Map<ByteString, ByteString> values =
      new SmallMap<ByteString, ByteString>();
@@ -1213,37 +1237,38 @@
   * Adds the specified attribute value to this attribute builder if it is not
   * already present.
   *
   * @param value
   * @param attributeValue
   *          The {@link ByteString} representation of the attribute value to be
   *          added to this attribute builder.
   * @return <code>true</code> if this attribute builder did not already contain
   *         the specified attribute value.
   */
  public boolean add(ByteString value)
  public boolean add(ByteString attributeValue)
  {
    return values.put(normalize(value), value) == null;
    return values.put(normalize(attributeValue), attributeValue) == null;
  }
  private ByteString normalize(ByteString value)
  private ByteString normalize(ByteString attributeValue)
  {
    return normalize(attributeType, value);
    return normalize(attributeType, attributeValue);
  }
  private static ByteString normalize(AttributeType attributeType, ByteString value)
  private static ByteString normalize(AttributeType attributeType,
      ByteString attributeValue)
  {
    try
    {
      if (attributeType != null)
      {
        final MatchingRule eqRule = attributeType.getEqualityMatchingRule();
        return eqRule.normalizeAttributeValue(value);
        return eqRule.normalizeAttributeValue(attributeValue);
      }
    }
    catch (DecodeException e)
    {
      // nothing to do here
    }
    return value;
    return attributeValue;
  }
  /**
@@ -1765,7 +1790,7 @@
    else
    {
      newValues = Collections.unmodifiableMap(values);
      values = new LinkedHashMap<ByteString, ByteString>();
      values = new SmallMap<ByteString, ByteString>();
    }
    // Now create the appropriate attribute based on the options.
opendj3-server-dev/src/server/org/opends/server/types/AttributeValueIterable.java
File was deleted
opendj3-server-dev/src/server/org/opends/server/types/CollectiveVirtualAttribute.java
@@ -58,8 +58,8 @@
   * {@inheritDoc}
   */
  @Override
  public ConditionResult approximatelyEqualTo(ByteString value) {
    return attribute.approximatelyEqualTo(value);
  public ConditionResult approximatelyEqualTo(ByteString assertionValue) {
    return attribute.approximatelyEqualTo(assertionValue);
  }
  /**
@@ -70,6 +70,13 @@
    return attribute.contains(value);
  }
  /** {@inheritDoc} */
  @Override
  public ConditionResult matchesEqualityAssertion(ByteString assertionValue)
  {
    return attribute.matchesEqualityAssertion(assertionValue);
  }
  /**
   * {@inheritDoc}
   */
@@ -90,8 +97,8 @@
   * {@inheritDoc}
   */
  @Override
  public ConditionResult greaterThanOrEqualTo(ByteString value) {
    return attribute.greaterThanOrEqualTo(value);
  public ConditionResult greaterThanOrEqualTo(ByteString assertionValue) {
    return attribute.greaterThanOrEqualTo(assertionValue);
  }
  /**
@@ -114,8 +121,8 @@
   * {@inheritDoc}
   */
  @Override
  public ConditionResult lessThanOrEqualTo(ByteString value) {
    return attribute.lessThanOrEqualTo(value);
  public ConditionResult lessThanOrEqualTo(ByteString assertionValue) {
    return attribute.lessThanOrEqualTo(assertionValue);
  }
  /**
opendj3-server-dev/src/server/org/opends/server/types/SearchFilter.java
@@ -2723,44 +2723,35 @@
      return ConditionResult.UNDEFINED;
    }
    ByteString normAssertionValue;
    try
    {
      normAssertionValue = matchingRule.normalizeAssertionValue(assertionValue);
    }
    catch (Exception e)
    {
      logger.traceException(e);
      // We can't normalize the assertion value, so the result must be
      // undefined.
      return ConditionResult.UNDEFINED;
    }
    // Iterate through all the attributes and see if we can find a match.
    ConditionResult result = ConditionResult.FALSE;
    for (Attribute a : attrs)
    {
      // FIXME next if is incorrect
      if (a.contains(normAssertionValue))
      final ConditionResult cr = a.matchesEqualityAssertion(assertionValue);
      if (cr == ConditionResult.TRUE)
      {
        if (logger.isTraceEnabled())
        {
          logger.trace(
            "Returning TRUE for equality component %s in " +
            "filter %s for entry %s", this, completeFilter, entry.getName());
              "Returning TRUE for equality component %s in filter %s " +
                  "for entry %s", this, completeFilter, entry.getName());
        }
        return ConditionResult.TRUE;
      }
      else if (cr == ConditionResult.UNDEFINED)
      {
        result = ConditionResult.UNDEFINED;
      }
    }
    if (logger.isTraceEnabled())
    {
      logger.trace(
          "Returning FALSE for equality component %s in filter " +
          "%s because entry %s didn't have attribute type %s with value %s",
          this, completeFilter, entry.getName(), attributeType.getNameOrOID(), assertionValue);
          "Returning %s for equality component %s in filter %s " +
              "because entry %s didn't have attribute type %s with value %s",
          result, this, completeFilter, entry.getName(), attributeType.getNameOrOID(), assertionValue);
    }
    return ConditionResult.FALSE;
    return result;
  }
opendj3-server-dev/src/server/org/opends/server/types/SmallMap.java
New file
@@ -0,0 +1,257 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *      Copyright 2014 ForgeRock AS
 */
package org.opends.server.types;
import java.util.*;
/**
 * A small map of values. This map implementation is optimized to use as little
 * memory as possible in the case where there zero or one elements. In addition,
 * any normalization of entriess is delayed until the second entry is added
 * (normalization may be triggered by invoking {@link Object#hashCode()} or
 * {@link Object#equals(Object)}.
 * <p>
 * Null keys are not supported by this map.
 *
 * @param <K>
 *          the type of keys maintained by this map
 * @param <V>
 *          the type of mapped values
 */
public class SmallMap<K, V> extends AbstractMap<K, V>
{
  /** The map of entries if there are more than one. */
  private LinkedHashMap<K, V> entries;
  /** The first key of the Map. */
  private K firstKey;
  /** The first value of the Map. */
  private V firstValue;
  /** Creates a new small map which is initially empty. */
  public SmallMap()
  {
    // No implementation required.
  }
  private void rejectIfNull(Object key)
  {
    if (key == null)
    {
      throw new NullPointerException("null keys are not allowed");
    }
  }
  /** {@inheritDoc} */
  @Override
  public V get(Object key)
  {
    rejectIfNull(key);
    if (entries != null)
    { // >= 2 entries case
      return entries.get(key);
    }
    // 0 and 1 case
    if (firstKey != null && firstKey.equals(key))
    {
      return firstValue;
    }
    return null;
  }
  /** {@inheritDoc} */
  @Override
  public V put(K key, V value)
  {
    rejectIfNull(key);
    if (entries != null)
    { // >= 2 entries case
      return entries.put(key, value);
    }
    if (firstKey == null)
    { // 0 entries case
      firstKey = key;
      firstValue = value;
      return null;
    }
    // 1 entry case
    if (firstKey.equals(key))
    { // replace value
      V oldValue = firstValue;
      firstValue = value;
      return oldValue;
    }
    // overflow to the underlying map
    entries = new LinkedHashMap<K, V>(2);
    entries.put(firstKey, firstValue);
    firstKey = null;
    firstValue = null;
    return entries.put(key, value);
  }
  /** {@inheritDoc} */
  @Override
  public void putAll(Map<? extends K, ? extends V> m)
  {
    for (Entry<? extends K, ? extends V> entry : m.entrySet())
    {
      put(entry.getKey(), entry.getValue());
    }
  }
  /** {@inheritDoc} */
  @Override
  public V remove(Object key)
  {
    if (entries != null)
    {
      // Note: if there is one or zero values left we could stop using the map.
      // However, lets assume that if the map was multi-valued before
      // then it may become multi-valued again.
      return entries.remove(key);
    }
    if (firstKey != null && firstKey.equals(key))
    {
      V oldV = firstValue;
      firstKey = null;
      firstValue = null;
      return oldV;
    }
    return null;
  }
  /** {@inheritDoc} */
  @Override
  public boolean containsKey(Object key)
  {
    rejectIfNull(key);
    if (entries != null)
    {
      return entries.containsKey(key);
    }
    return firstKey != null && firstKey.equals(key);
  }
  /** {@inheritDoc} */
  @Override
  public boolean containsValue(Object value)
  {
    if (entries != null)
    {
      return entries.containsValue(value);
    }
    if (firstKey == null)
    {
      return false;
    }
    if (firstValue == null)
    {
      return value == null;
    }
    return firstValue.equals(value);
  }
  /** {@inheritDoc} */
  @Override
  public void clear()
  {
    firstKey = null;
    firstValue = null;
    entries = null;
  }
  /** {@inheritDoc} */
  @Override
  public int size()
  {
    if (entries != null)
    {
      return entries.size();
    }
    return firstKey != null ? 1 : 0;
  }
  /** {@inheritDoc} */
  @Override
  public Set<Entry<K, V>> entrySet()
  {
    if (entries != null)
    {
      return entries.entrySet();
    }
    if (firstKey == null)
    {
      return Collections.emptySet();
    }
    return new AbstractSet<Entry<K, V>>()
    {
      @Override
      public Iterator<Entry<K, V>> iterator()
      {
        return new Iterator<Entry<K, V>>()
        {
          private boolean isFirst = true;
          @Override
          public boolean hasNext()
          {
            return isFirst;
          }
          @Override
          public Entry<K, V> next()
          {
            if (!isFirst)
            {
              throw new NoSuchElementException();
            }
            isFirst = false;
            return new SimpleEntry<K, V>(firstKey, firstValue);
          }
          @Override
          public void remove()
          {
            firstKey = null;
            firstValue = null;
          }
        };
      }
      @Override
      public int size()
      {
        return 1;
      }
    };
  }
}
opendj3-server-dev/src/server/org/opends/server/types/VirtualAttribute.java
@@ -35,9 +35,7 @@
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.ConditionResult;
import org.forgerock.opendj.ldap.DecodeException;
import org.forgerock.util.Utils;
import org.opends.server.api.MatchingRule;
import org.opends.server.api.VirtualAttributeProvider;
/**
@@ -93,9 +91,9 @@
  /** {@inheritDoc} */
  @Override
  public ConditionResult approximatelyEqualTo(ByteString value)
  public ConditionResult approximatelyEqualTo(ByteString assertionValue)
  {
    return provider.approximatelyEqualTo(entry, rule, value);
    return provider.approximatelyEqualTo(entry, rule, assertionValue);
  }
@@ -116,6 +114,12 @@
    return provider.hasAllValues(entry, rule, values);
  }
  /** {@inheritDoc} */
  @Override
  public ConditionResult matchesEqualityAssertion(ByteString assertionValue)
  {
    return provider.matchesEqualityAssertion(entry, rule, assertionValue);
  }
  /** {@inheritDoc} */
@@ -161,9 +165,9 @@
  /** {@inheritDoc} */
  @Override
  public ConditionResult greaterThanOrEqualTo(ByteString value)
  public ConditionResult greaterThanOrEqualTo(ByteString assertionValue)
  {
    return provider.greaterThanOrEqualTo(entry, rule, value);
    return provider.greaterThanOrEqualTo(entry, rule, assertionValue);
  }
@@ -224,9 +228,9 @@
  /** {@inheritDoc} */
  @Override
  public ConditionResult lessThanOrEqualTo(ByteString value)
  public ConditionResult lessThanOrEqualTo(ByteString assertionValue)
  {
    return provider.lessThanOrEqualTo(entry, rule, value);
    return provider.lessThanOrEqualTo(entry, rule, assertionValue);
  }
@@ -263,27 +267,6 @@
  /** {@inheritDoc} */
  @Override
  public int hashCode()
  {
    int hashCode = getAttributeType().hashCode();
    for (ByteString value : this)
    {
      try
      {
        final MatchingRule eqRule = attributeType.getEqualityMatchingRule();
        final ByteString nv = eqRule.normalizeAttributeValue(value);
        hashCode += nv.hashCode();
      }
      catch (DecodeException e)
      {
        logger.traceException(e);
      }
    }
    return hashCode;
  }
  /** {@inheritDoc} */
  @Override
  public void toString(StringBuilder buffer)
  {
    buffer.append("VirtualAttribute(");
opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestVerifyJob.java
@@ -408,7 +408,7 @@
      ByteString[] badValues = new ByteString[values.length];
      System.arraycopy(values, 1, badValues, 0, values.length - 1);
      badValues[badValues.length-1] = values[0];
      svs.remove(id, values, types);
      svs.remove(id, values);
      svs.add(id, badValues, types);
      vlvIndex.putSortValuesSet(null, svs);
@@ -697,7 +697,7 @@
      SortValuesSet svs = vlvIndex.getSortValuesSet(null, 0, new ByteString[3], types);
      long id = svs.getEntryIDs()[0];
      Entry entry = eContainer.getID2Entry().get(null, new EntryID(id), LockMode.DEFAULT);
      svs.remove(id, vlvIndex.getSortValues(entry), types);
      svs.remove(id, vlvIndex.getSortValues(entry));
      // Add an incorrectly ordered values.
      SortValuesSet svs2 = svs.split(2);
@@ -710,7 +710,7 @@
      ByteString[] badValues = new ByteString[values.length];
      System.arraycopy(values, 1, badValues, 0, values.length - 1);
      badValues[badValues.length-1] = values[0];
      svs.remove(id, values, types);
      svs.remove(id, values);
      svs.add(id, badValues, types);
      vlvIndex.putSortValuesSet(null, svs);
opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/types/SearchFilterTests.java
@@ -27,26 +27,25 @@
 */
package org.opends.server.types;
import org.forgerock.opendj.ldap.ByteString;
import org.opends.server.DirectoryServerTestCase;
import org.opends.server.TestCaseUtils;
import org.opends.server.util.StaticUtils;
import org.opends.server.util.Base64;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.RawFilter;
import org.opends.server.core.DirectoryServer;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import org.testng.annotations.BeforeClass;
import org.testng.Assert;
import java.util.List;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.text.ParseException;
import java.util.List;
import static java.util.Arrays.asList;
import org.forgerock.opendj.ldap.ByteString;
import org.opends.server.DirectoryServerTestCase;
import org.opends.server.TestCaseUtils;
import org.opends.server.core.DirectoryServer;
import org.opends.server.util.Base64;
import org.opends.server.util.StaticUtils;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import static java.util.Arrays.*;
import static org.opends.server.util.StaticUtils.*;
import static org.testng.Assert.*;
@@ -611,7 +610,7 @@
  private FilterDescription assertionFilterDescription(FilterType filterType,
                                                       String attributeType,
                                                       String attributeValue,
                                                       List<String> matchedEntries) {
                                                       String... matchedEntries) {
    FilterDescription description = new FilterDescription();
    description.filterType = filterType;
@@ -634,8 +633,8 @@
      fail(filterType + " is not handled.");
    }
    description.matchedEntriesLdif = matchedEntries;
    description.unmatchedEntriesLdif = getEntriesExcluding(matchedEntries);
    description.matchedEntriesLdif = asList(matchedEntries);
    description.unmatchedEntriesLdif = getEntriesExcluding(description.matchedEntriesLdif);
    return description;
  }
@@ -643,28 +642,28 @@
  private FilterDescription equalityFilterDescription(String attributeType,
                                                      String attributeValue,
                                                      List<String> matchedEntries) {
                                                      String... matchedEntries) {
    return assertionFilterDescription(FilterType.EQUALITY, attributeType, attributeValue, matchedEntries);
  }
  private FilterDescription lessEqualFilterDescription(String attributeType,
                                                       String attributeValue,
                                                       List<String> matchedEntries) {
                                                       String... matchedEntries) {
    return assertionFilterDescription(FilterType.LESS_OR_EQUAL, attributeType, attributeValue, matchedEntries);
  }
  private FilterDescription greaterEqualFilterDescription(String attributeType,
                                                          String attributeValue,
                                                          List<String> matchedEntries) {
                                                          String... matchedEntries) {
    return assertionFilterDescription(FilterType.GREATER_OR_EQUAL, attributeType, attributeValue, matchedEntries);
  }
  private FilterDescription approximateFilterDescription(String attributeType,
                                                         String attributeValue,
                                                         List<String> matchedEntries) {
                                                         String... matchedEntries) {
    return assertionFilterDescription(FilterType.APPROXIMATE_MATCH, attributeType, attributeValue, matchedEntries);
  }
@@ -681,9 +680,12 @@
    description.subInitialElement = ByteString.valueOf(subInitial);
    description.subAnyElements = new ArrayList<ByteString>();
    for (int i = 0; (subAny != null) && (i < subAny.size()); i++) {
      String s = subAny.get(i);
      description.subAnyElements.add(ByteString.valueOf(s));
    if (subAny != null)
    {
      for (String s : subAny)
      {
        description.subAnyElements.add(ByteString.valueOf(s));
      }
    }
    description.subFinalElement = ByteString.valueOf(subFinal);
@@ -787,67 +789,38 @@
  private List<FilterDescription> getEqualityFilters() throws Exception {
    List<FilterDescription> descriptions = new ArrayList<FilterDescription>();
    descriptions.add(equalityFilterDescription("sn", "Smith",
            asList(JANE_SMITH_LDIF, JOE_SMITH_LDIF)));
    descriptions.add(equalityFilterDescription("givenname", "Jane",
            asList(JANE_SMITH_LDIF, JANE_AUSTIN_LDIF)));
    return descriptions;
    return asList(
        equalityFilterDescription("sn", "Smith", JANE_SMITH_LDIF, JOE_SMITH_LDIF),
        equalityFilterDescription("givenname", "Jane", JANE_SMITH_LDIF, JANE_AUSTIN_LDIF));
  }
  private List<FilterDescription> getApproximateFilters() throws Exception {
    List<FilterDescription> descriptions = new ArrayList<FilterDescription>();
    descriptions.add(approximateFilterDescription("sn", "Smythe",
            asList(JANE_SMITH_LDIF, JOE_SMITH_LDIF)));
    return descriptions;
    return asList(approximateFilterDescription("sn", "Smythe", JANE_SMITH_LDIF, JOE_SMITH_LDIF));
  }
  private List<FilterDescription> getSubstringFilters() throws Exception {
    List<FilterDescription> descriptions = new ArrayList<FilterDescription>();
    descriptions.add(substringFilterDescription(
    return asList(substringFilterDescription(
            "sn",
            "S", asList("i"), "th", // S*i*th
            asList(JANE_SMITH_LDIF, JOE_SMITH_LDIF)));
    return descriptions;
  }
  private List<FilterDescription> getInequalityFilters() throws Exception {
    List<FilterDescription> descriptions = new ArrayList<FilterDescription>();
    descriptions.add(lessEqualFilterDescription("sn", "Aus",
            new ArrayList<String>()));
    descriptions.add(greaterEqualFilterDescription("sn", "Aus",
            asList(JANE_AUSTIN_LDIF, JOE_AUSTIN_LDIF,
                   JANE_SMITH_LDIF, JOE_SMITH_LDIF)));
    descriptions.add(lessEqualFilterDescription("sn", "Smi",
            asList(JANE_AUSTIN_LDIF, JOE_AUSTIN_LDIF)));
    descriptions.add(greaterEqualFilterDescription("sn", "Smi",
            asList(JANE_SMITH_LDIF, JOE_SMITH_LDIF)));
    descriptions.add(lessEqualFilterDescription("sn", "Smith",
            asList(JANE_AUSTIN_LDIF, JOE_AUSTIN_LDIF,
                   JANE_SMITH_LDIF, JOE_SMITH_LDIF)));
    descriptions.add(greaterEqualFilterDescription("sn", "Smith",
            asList(JANE_SMITH_LDIF, JOE_SMITH_LDIF)));
    return descriptions;
    return asList(
        lessEqualFilterDescription("sn", "Aus"),
        greaterEqualFilterDescription("sn", "Aus",
            JANE_AUSTIN_LDIF, JOE_AUSTIN_LDIF, JANE_SMITH_LDIF, JOE_SMITH_LDIF),
        lessEqualFilterDescription("sn", "Smi",
            JANE_AUSTIN_LDIF, JOE_AUSTIN_LDIF),
        greaterEqualFilterDescription("sn", "Smi",
            JANE_SMITH_LDIF, JOE_SMITH_LDIF),
        lessEqualFilterDescription("sn", "Smith",
            JANE_AUSTIN_LDIF, JOE_AUSTIN_LDIF, JANE_SMITH_LDIF, JOE_SMITH_LDIF),
        greaterEqualFilterDescription("sn", "Smith",
            JANE_SMITH_LDIF, JOE_SMITH_LDIF));
  }
@@ -899,8 +872,7 @@
    // Now convert to [][]
    FilterDescription[][] descriptionArray = new FilterDescription[allDescriptions.size()][];
    for (int i = 0; i < allDescriptions.size(); i++) {
      FilterDescription description = allDescriptions.get(i);
      descriptionArray[i] = new FilterDescription[]{description};
      descriptionArray[i] = new FilterDescription[]{ allDescriptions.get(i) };
    }
    return descriptionArray;
@@ -913,16 +885,14 @@
    for (String ldif: description.matchedEntriesLdif) {
      Entry entry = TestCaseUtils.entryFromLdifString(ldif);
      if (!description.searchFilter.matchesEntry(entry)) {
        fail("Expected to match entry. " + description + entry);
      }
      assertTrue(description.searchFilter.matchesEntry(entry),
          "Expected to match entry. " + description + " " + entry);
    }
    for (String ldif: description.unmatchedEntriesLdif) {
      Entry entry = TestCaseUtils.entryFromLdifString(ldif);
      if (description.searchFilter.matchesEntry(entry)) {
        fail("Should not have matched entry. " + description + entry);
      }
      assertFalse(description.searchFilter.matchesEntry(entry),
          "Should not have matched entry. " + description + " " + entry);
    }
  }
@@ -1075,7 +1045,7 @@
  @DataProvider(name = "differentNormalization")
  public Object[][] differentNormalizationData() throws ParseException
  {
    final String BASE64_CERT_VALUE =
    final String BASE64_CERT_VALUE =
      "MIICpTCCAg6gAwIBAgIJALeoA6I3ZC/cMA0GCSqGSIb3DQEBBQUAMFYxCzAJBgNV" +
      "BAYTAlVTMRMwEQYDVQQHEwpDdXBlcnRpb25lMRwwGgYDVQQLExNQcm9kdWN0IERl" +
      "dmVsb3BtZW50MRQwEgYDVQQDEwtCYWJzIEplbnNlbjAeFw0xMjA1MDIxNjM0MzVa" +
@@ -1091,10 +1061,10 @@
      "6CD0WRmc2pBeYX2z94/PWO5L3Fx+eIZh2wTxScF+FdRWJzLbUaBuClrxuy0Y5ifj" +
      "axuJ8LFNbZtsp1ldW3i84+F5+SYT+xI67ZcoAtwx/VFVI9s5I/Gkmu9f9nxjPpK7" +
      "1AIUXiE3Qcck";
    final String CERT_EXACT_ASSERTION =
    final String CERT_EXACT_ASSERTION =
      "{ serialNumber 13233831500277100508, issuer rdnSequence:\""+
      "CN=Babs Jensen,OU=Product Development,L=Cupertione,C=US\" }";
    final String CERTIFICATE_LDIF = TestCaseUtils.makeLdif(
    final String LDIF_ENTRY = TestCaseUtils.makeLdif(
          "dn: cn=John Smith,dc=example,dc=com",
          "objectclass: inetorgperson",
          "cn: John Smith",
@@ -1106,8 +1076,9 @@
    final String CERTIFICATE_ENCODED = builder.toString();
    return new Object[][]{
            {CERTIFICATE_LDIF, "userCertificate="+CERT_EXACT_ASSERTION, true},
            {CERTIFICATE_LDIF, "userCertificate="+CERTIFICATE_ENCODED, true}};
      { LDIF_ENTRY, "userCertificate=" + CERT_EXACT_ASSERTION, true },
      { LDIF_ENTRY, "userCertificate=" + CERTIFICATE_ENCODED, true },
    };
  }
  @Test(dataProvider = "differentNormalization")
opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/types/SmallMapTest.java
New file
@@ -0,0 +1,249 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *      Copyright 2014 ForgeRock AS
 */
package org.opends.server.types;
import java.util.*;
import java.util.Map.Entry;
import org.testng.annotations.Test;
import static org.assertj.core.api.Assertions.*;
import static org.testng.Assert.*;
@SuppressWarnings("javadoc")
public class SmallMapTest
{
  @Test
  public void testPutAndSize() throws Exception
  {
    SmallMap<Integer, String> map = new SmallMap<Integer, String>();
    assertEquals(map.size(), 0);
    assertEquals(map.put(1, "one"), null);
    assertEquals(map.size(), 1);
    assertEquals(map.put(1, "ONE"), "one");
    assertEquals(map.size(), 1);
    assertEquals(map.put(2, "two"), null);
    assertEquals(map.size(), 2);
    assertEquals(map.put(3, "three"), null);
    assertEquals(map.size(), 3);
  }
  @Test(dependsOnMethods = { "testPutAndSize" })
  public void testGet() throws Exception
  {
    SmallMap<Integer, String> map = new SmallMap<Integer, String>();
    assertEquals(map.get(1), null);
    assertEquals(map.get(2), null);
    map.put(1, "one");
    assertEquals(map.get(1), "one");
    assertEquals(map.get(2), null);
    map.put(1, "ONE");
    assertEquals(map.get(1), "ONE");
    assertEquals(map.get(2), null);
    map.put(2, "two");
    assertEquals(map.get(1), "ONE");
    assertEquals(map.get(2), "two");
  }
  @Test(dependsOnMethods = { "testPutAndSize" })
  public void testPutAll() throws Exception
  {
    final SmallMap<Integer, String> map = new SmallMap<Integer, String>();
    final HashMap<Integer, String> hashMap = new HashMap<Integer, String>();
    map.putAll(hashMap);
    assertEquals(map.size(), 0);
    hashMap.put(1, "one");
    map.putAll(hashMap);
    assertEquals(map.size(), 1);
    assertEquals(map.get(1), "one");
  }
  @Test(dependsOnMethods = { "testPutAndSize" })
  public void testRemove() throws Exception
  {
    SmallMap<Integer, String> map = new SmallMap<Integer, String>();
    assertEquals(map.size(), 0);
    assertEquals(map.remove(2), null);
    assertEquals(map.remove(1), null);
    map.put(1, "one");
    assertEquals(map.size(), 1);
    assertEquals(map.remove(2), null);
    assertEquals(map.remove(1), "one");
    assertEquals(map.size(), 0);
    map.put(1, "one");
    map.put(2, "two");
    assertEquals(map.size(), 2);
    assertEquals(map.remove(1), "one");
    assertEquals(map.size(), 1);
  }
  @Test(dependsOnMethods = { "testPutAndSize" })
  public void testContains() throws Exception
  {
    SmallMap<Integer, String> map = new SmallMap<Integer, String>();
    assertDoesNotContain(map, entry(2, "two"));
    map.put(1, null);
    assertContains(map, entry(1, (String) null));
    assertDoesNotContain(map, entry(2, "two"));
    map.put(1, "one");
    assertContains(map, entry(1, "one"));
    assertDoesNotContain(map, entry(2, "two"));
    map.put(2, "two");
    assertContains(map, entry(1, "one"));
    assertContains(map, entry(2, "two"));
    assertDoesNotContain(map, entry(3, "three"));
  }
  @Test(dependsOnMethods = { "testPutAndSize" })
  public void testClear() throws Exception
  {
    SmallMap<Integer, String> map = new SmallMap<Integer, String>();
    map.clear();
    assertEquals(map.size(), 0);
    map.put(1, "one");
    map.clear();
    assertEquals(map.size(), 0);
    map.put(1, "one");
    map.put(2, "two");
    map.clear();
    assertEquals(map.size(), 0);
  }
  @Test(dependsOnMethods = { "testPutAndSize" })
  public void testEntrySetSize() throws Exception
  {
    SmallMap<Integer, String> map = new SmallMap<Integer, String>();
    assertEquals(map.entrySet().size(), 0);
    map.put(1, "one");
    assertEquals(map.entrySet().size(), 1);
    map.put(1, "one");
    map.put(2, "two");
    assertEquals(map.entrySet().size(), 2);
  }
  @SuppressWarnings("unchecked")
  @Test(dependsOnMethods = { "testEntrySetSize" })
  public void testEntrySetIterator() throws Exception
  {
    SmallMap<Integer, String> map = new SmallMap<Integer, String>();
    assertThat(map.entrySet().iterator()).isEmpty();
    map.put(1, "one");
    assertThat(map.entrySet().iterator()).containsExactly(
        entry(1, "one"));
    map.put(1, "one");
    map.put(2, "two");
    assertThat(map.entrySet().iterator()).containsExactly(
        entry(1, "one"), entry(2, "two"));
  }
  @Test(dependsOnMethods = { "testEntrySetIterator" })
  public void testEntrySetIteratorNextRemove() throws Exception
  {
    SmallMap<Integer, String> map = new SmallMap<Integer, String>();
    map.put(1, "one");
    Iterator<Entry<Integer, String>> iter = map.entrySet().iterator();
    assertTrue(iter.hasNext());
    assertNotNull(iter.next());
    iter.remove();
    assertFalse(iter.hasNext());
    assertTrue(map.isEmpty());
  }
  @Test(dependsOnMethods = { "testEntrySetIterator" },
      expectedExceptions = { NoSuchElementException.class })
  public void testEntrySetIteratorNextThrowsNoSuchElementException() throws Exception
  {
    SmallMap<Integer, String> map = new SmallMap<Integer, String>();
    map.put(1, "one");
    Iterator<Entry<Integer, String>> iter = map.entrySet().iterator();
    assertTrue(iter.hasNext());
    iter.next();
    assertFalse(iter.hasNext());
    iter.next(); // throw an exception
  }
  private <K, V> Entry<K, V> entry(K key, V value)
  {
    return new AbstractMap.SimpleImmutableEntry<K, V>(key, value);
  }
  private void assertContains(SmallMap<Integer, String> map,
      Entry<Integer, String> entry)
  {
    assertContains(map, entry, true);
  }
  private void assertDoesNotContain(SmallMap<Integer, String> map,
      Entry<Integer, String> entry)
  {
    assertContains(map, entry, false);
  }
  private void assertContains(SmallMap<Integer, String> map,
      Entry<Integer, String> entry, boolean expected)
  {
    assertEquals(map.containsKey(entry.getKey()), expected);
    assertEquals(map.containsValue(entry.getValue()), expected);
  }
  @Test(expectedExceptions = { NullPointerException.class })
  public void testGetRejectsNull() throws Exception
  {
    new SmallMap<Integer, String>().get(null);
  }
  @Test(expectedExceptions = { NullPointerException.class })
  public void testContainsKeyRejectsNull() throws Exception
  {
    new SmallMap<Integer, String>().containsKey(null);
  }
  @Test(expectedExceptions = { NullPointerException.class })
  public void testPutRejectsNull() throws Exception
  {
    new SmallMap<Integer, String>().put(null, null);
  }
  @Test(expectedExceptions = { NullPointerException.class })
  public void testPutAllRejectsNull() throws Exception
  {
    final HashMap<Integer, String> map = new HashMap<Integer, String>();
    map.put(null, null);
    new SmallMap<Integer, String>().putAll(map);
  }
}
opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/types/TestEntry.java
@@ -257,11 +257,13 @@
    Entry entry = createTestEntry(type, values);
    Set<SubtreeSpecification> result = new HashSet<SubtreeSpecification>();
    List<Attribute> attributes = entry.getAttribute(type, true);
    for (ByteString value : new AttributeValueIterable(attributes))
    for (Attribute a : attributes)
    {
      result.add(SubtreeSpecification.valueOf(rootDN, value.toString()));
      for (ByteString value : a)
      {
        result.add(SubtreeSpecification.valueOf(rootDN, value.toString()));
      }
    }
    assertEquals(expected, result);
  }