From c430f8b3da724d0a117eadd172b92179b9a0b5a9 Mon Sep 17 00:00:00 2001
From: Jean-Noel Rouvignac <jean-noel.rouvignac@forgerock.com>
Date: Fri, 21 Mar 2014 11:59:48 +0000
Subject: [PATCH] OPENDJ-1368 (CR-3232) Remove AttributeValue

---
 opendj3-server-dev/src/server/org/opends/server/api/VirtualAttributeProvider.java                       |   39 +
 opendj3-server-dev/src/server/org/opends/server/types/Attribute.java                                    |   34 
 opendj3-server-dev/src/server/org/opends/server/admin/server/ServerManagementContext.java               |   14 
 opendj3-server-dev/src/server/org/opends/server/backends/jeb/AttributeIndex.java                        |   24 
 opendj3-server-dev/src/server/org/opends/server/types/AttributeBuilder.java                             |   67 +
 opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestVerifyJob.java |    6 
 opendj3-server-dev/src/guitools/org/opends/guitools/controlpanel/task/ModifyEntryTask.java              |    5 
 opendj3-server-dev/src/server/org/opends/server/types/SmallMap.java                                     |  257 ++++++++
 opendj3-server-dev/src/server/org/opends/server/types/CollectiveVirtualAttribute.java                   |   19 
 opendj3-server-dev/src/server/org/opends/server/backends/jeb/JebFormat.java                             |   31 
 opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/types/TestEntry.java            |    8 
 opendj3-server-dev/src/server/org/opends/server/backends/jeb/VLVIndex.java                              |  449 ++++-----------
 /dev/null                                                                                               |  198 ------
 opendj3-server-dev/src/server/org/opends/server/authorization/dseecompat/UserDN.java                    |    8 
 opendj3-server-dev/src/server/org/opends/server/types/SearchFilter.java                                 |   35 
 opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/types/SearchFilterTests.java    |  141 +--
 opendj3-server-dev/src/server/org/opends/server/backends/jeb/VLVKeyComparator.java                      |   35 
 opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/types/SmallMapTest.java         |  249 ++++++++
 opendj3-server-dev/src/server/org/opends/server/types/AbstractAttribute.java                            |   23 
 opendj3-server-dev/src/server/org/opends/server/types/VirtualAttribute.java                             |   41 -
 opendj3-server-dev/src/server/org/opends/server/backends/jeb/SortValuesSet.java                         |   14 
 21 files changed, 900 insertions(+), 797 deletions(-)

diff --git a/opendj3-server-dev/src/guitools/org/opends/guitools/controlpanel/task/ModifyEntryTask.java b/opendj3-server-dev/src/guitools/org/opends/guitools/controlpanel/task/ModifyEntryTask.java
index 28cf7f9..6822449 100644
--- a/opendj3-server-dev/src/guitools/org/opends/guitools/controlpanel/task/ModifyEntryTask.java
+++ b/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)
   {
diff --git a/opendj3-server-dev/src/server/org/opends/server/admin/server/ServerManagementContext.java b/opendj3-server-dev/src/server/org/opends/server/admin/server/ServerManagementContext.java
index e8dd49d..f29d94b 100644
--- a/opendj3-server-dev/src/server/org/opends/server/admin/server/ServerManagementContext.java
+++ b/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;
   }
diff --git a/opendj3-server-dev/src/server/org/opends/server/api/VirtualAttributeProvider.java b/opendj3-server-dev/src/server/org/opends/server/api/VirtualAttributeProvider.java
index 8a33e70..1a83583 100644
--- a/opendj3-server-dev/src/server/org/opends/server/api/VirtualAttributeProvider.java
+++ b/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,
diff --git a/opendj3-server-dev/src/server/org/opends/server/authorization/dseecompat/UserDN.java b/opendj3-server-dev/src/server/org/opends/server/authorization/dseecompat/UserDN.java
index e299cc8..a3d78b1 100644
--- a/opendj3-server-dev/src/server/org/opends/server/authorization/dseecompat/UserDN.java
+++ b/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
diff --git a/opendj3-server-dev/src/server/org/opends/server/backends/jeb/AttributeIndex.java b/opendj3-server-dev/src/server/org/opends/server/backends/jeb/AttributeIndex.java
index 6d0874d..d9b9f34 100644
--- a/opendj3-server-dev/src/server/org/opends/server/backends/jeb/AttributeIndex.java
+++ b/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);
 
diff --git a/opendj3-server-dev/src/server/org/opends/server/backends/jeb/JebFormat.java b/opendj3-server-dev/src/server/org/opends/server/backends/jeb/JebFormat.java
index 458df29..9813c65 100644
--- a/opendj3-server-dev/src/server/org/opends/server/backends/jeb/JebFormat.java
+++ b/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;
   }
diff --git a/opendj3-server-dev/src/server/org/opends/server/backends/jeb/SortValuesSet.java b/opendj3-server-dev/src/server/org/opends/server/backends/jeb/SortValuesSet.java
index cd04d26..4e9432f 100644
--- a/opendj3-server-dev/src/server/org/opends/server/backends/jeb/SortValuesSet.java
+++ b/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;
diff --git a/opendj3-server-dev/src/server/org/opends/server/backends/jeb/VLVIndex.java b/opendj3-server-dev/src/server/org/opends/server/backends/jeb/VLVIndex.java
index cc8038e..1f90e1a 100644
--- a/opendj3-server-dev/src/server/org/opends/server/backends/jeb/VLVIndex.java
+++ b/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);
diff --git a/opendj3-server-dev/src/server/org/opends/server/backends/jeb/VLVKeyComparator.java b/opendj3-server-dev/src/server/org/opends/server/backends/jeb/VLVKeyComparator.java
index c3a61db..6fd1ec4 100644
--- a/opendj3-server-dev/src/server/org/opends/server/backends/jeb/VLVKeyComparator.java
+++ b/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)
         {
diff --git a/opendj3-server-dev/src/server/org/opends/server/types/AbstractAttribute.java b/opendj3-server-dev/src/server/org/opends/server/types/AbstractAttribute.java
index 1ec9b2a..265ce82 100644
--- a/opendj3-server-dev/src/server/org/opends/server/types/AbstractAttribute.java
+++ b/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;
   }
 
diff --git a/opendj3-server-dev/src/server/org/opends/server/types/Attribute.java b/opendj3-server-dev/src/server/org/opends/server/types/Attribute.java
index db29e7f..29369d4 100644
--- a/opendj3-server-dev/src/server/org/opends/server/types/Attribute.java
+++ b/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);
 
 
 
diff --git a/opendj3-server-dev/src/server/org/opends/server/types/AttributeBuilder.java b/opendj3-server-dev/src/server/org/opends/server/types/AttributeBuilder.java
index 8c38d96..515cf56 100644
--- a/opendj3-server-dev/src/server/org/opends/server/types/AttributeBuilder.java
+++ b/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.
diff --git a/opendj3-server-dev/src/server/org/opends/server/types/AttributeValueIterable.java b/opendj3-server-dev/src/server/org/opends/server/types/AttributeValueIterable.java
deleted file mode 100644
index 73f59df..0000000
--- a/opendj3-server-dev/src/server/org/opends/server/types/AttributeValueIterable.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * 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 2006-2008 Sun Microsystems, Inc.
- *      Portions Copyright 2014 ForgeRock AS
- */
-package org.opends.server.types;
-
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.NoSuchElementException;
-
-import org.forgerock.opendj.ldap.ByteString;
-
-/**
- * An iterable read-only view of of a set of attribute values returned
- * from methods such as
- * {@link org.opends.server.types.Entry#getAttribute(AttributeType)}.
- * <p>
- * Using instances of this class it is possible to filter out
- * attribute values which do not have the correct options set. This is
- * achieved without having to duplicate the set of attributes.
- */
-@org.opends.server.types.PublicAPI(
-     stability=org.opends.server.types.StabilityLevel.VOLATILE,
-     mayInstantiate=false,
-     mayExtend=false,
-     mayInvoke=true)
-public final class AttributeValueIterable implements Iterable<ByteString> {
-
-  // The set of attributes having the same type and options.
-  private Iterable<Attribute> attributes;
-
-  // The set of options which all values must contain.
-  private HashSet<String> options;
-
-
-
-  /**
-   * Create a new attribute value iterable object.
-   *
-   * @param  attributes  The set of attributes having the same type.
-   *                     Can be {@code null}.
-   */
-  public AttributeValueIterable(Iterable<Attribute> attributes) {
-    this(attributes, null);
-
-  }
-
-  /**
-   * Create a new attribute value iterable object.
-   *
-   * @param  attributes  The set of attributes having the same type.
-   *                     Can be {@code null}.
-   * @param  options     The set of options which all values must
-   *                     contain, or {@code null} if no options are
-   *                     required.
-   */
-  public AttributeValueIterable(Iterable<Attribute> attributes,
-                                HashSet<String> options) {
-
-    this.attributes = attributes;
-    this.options = options;
-  }
-
-  /**
-   * Retrieves an iterator that can be used to cursor through the set
-   * of attribute values.
-   *
-   * @return  An iterator that can be used to cursor through the set
-   *          of attribute values.
-   */
-  public Iterator<ByteString> iterator() {
-    return new AttributeValueIterator();
-  }
-
-  /**
-   * Private iterator implementation.
-   */
-  private class AttributeValueIterator implements Iterator<ByteString> {
-    // Flag indicating whether iteration can proceed.
-    private boolean hasNext;
-
-    // The current attribute iterator.
-    private Iterator<Attribute> attributeIterator;
-
-    // The current value iterator.
-    private Iterator<ByteString> valueIterator;
-
-    /**
-     * Create a new attribute value iterator over the attribute set.
-     */
-    private AttributeValueIterator() {
-
-      this.valueIterator = null;
-
-      if (attributes != null) {
-        this.attributeIterator = attributes.iterator();
-        this.hasNext = skipNonMatchingAttributes();
-      } else {
-        this.attributeIterator = null;
-        this.hasNext = false;
-      }
-    }
-
-    /**
-     * Indicates whether there are more attribute values to return.
-     *
-     * @return  {@code true} if there are more attribute values to
-     *          return, or {@code false} if not.
-     */
-    public boolean hasNext() {
-      return hasNext;
-    }
-
-    /**
-     * Retrieves the next attribute value in the set.
-     *
-     * @return  The next attribute value in the set.
-     *
-     * @throws  NoSuchElementException  If there are no more values to
-     *                                  return.
-     */
-    public ByteString next() throws NoSuchElementException
-    {
-      if (hasNext == false) {
-        throw new NoSuchElementException();
-      }
-
-      ByteString value = valueIterator.next();
-
-      // We've reached the end of this array list, so skip to the next
-      // non-empty one.
-      if (valueIterator.hasNext() == false) {
-        hasNext = skipNonMatchingAttributes();
-      }
-
-      return value;
-    }
-
-    /**
-     * Removes the last attribute value retrieved from the set.  Note
-     * that this operation is not supported and will always cause an
-     * {@code UnsupportedOperationException} to be thrown.
-     *
-     * @throws  UnsupportedOperationException  If the last value
-     *                                         cannot be removed.
-     */
-    public void remove() throws UnsupportedOperationException
-    {
-      throw new UnsupportedOperationException();
-    }
-
-    /**
-     * Skip past any empty attributes or attributes that do not have
-     * the correct set of options until we find one that contains some
-     * values.
-     *
-     * @return  {@code true} if iteration can continue, or
-     *          {@code false} if not.
-     */
-    private boolean skipNonMatchingAttributes() {
-
-      while (attributeIterator.hasNext()) {
-        Attribute attribute = attributeIterator.next();
-
-        if (attribute.hasAllOptions(options)) {
-          valueIterator = attribute.iterator();
-          if (valueIterator.hasNext()) {
-            return true;
-          }
-        }
-      }
-
-      return false;
-    }
-  }
-}
diff --git a/opendj3-server-dev/src/server/org/opends/server/types/CollectiveVirtualAttribute.java b/opendj3-server-dev/src/server/org/opends/server/types/CollectiveVirtualAttribute.java
index 5c7b99b..e1929bc 100644
--- a/opendj3-server-dev/src/server/org/opends/server/types/CollectiveVirtualAttribute.java
+++ b/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);
   }
 
   /**
diff --git a/opendj3-server-dev/src/server/org/opends/server/types/SearchFilter.java b/opendj3-server-dev/src/server/org/opends/server/types/SearchFilter.java
index e19558d..d3a5406 100644
--- a/opendj3-server-dev/src/server/org/opends/server/types/SearchFilter.java
+++ b/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;
   }
 
 
diff --git a/opendj3-server-dev/src/server/org/opends/server/types/SmallMap.java b/opendj3-server-dev/src/server/org/opends/server/types/SmallMap.java
new file mode 100644
index 0000000..88769c8
--- /dev/null
+++ b/opendj3-server-dev/src/server/org/opends/server/types/SmallMap.java
@@ -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;
+      }
+    };
+  }
+
+}
diff --git a/opendj3-server-dev/src/server/org/opends/server/types/VirtualAttribute.java b/opendj3-server-dev/src/server/org/opends/server/types/VirtualAttribute.java
index 03f7c30..77021b9 100644
--- a/opendj3-server-dev/src/server/org/opends/server/types/VirtualAttribute.java
+++ b/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(");
diff --git a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestVerifyJob.java b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestVerifyJob.java
index c85ae42..93b7b2c 100644
--- a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestVerifyJob.java
+++ b/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);
diff --git a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/types/SearchFilterTests.java b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/types/SearchFilterTests.java
index 3ee6816..3b2d643 100644
--- a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/types/SearchFilterTests.java
+++ b/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")
diff --git a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/types/SmallMapTest.java b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/types/SmallMapTest.java
new file mode 100644
index 0000000..3086943
--- /dev/null
+++ b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/types/SmallMapTest.java
@@ -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);
+  }
+}
diff --git a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/types/TestEntry.java b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/types/TestEntry.java
index eddb4fdd..e5a2cbb 100644
--- a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/types/TestEntry.java
+++ b/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);
   }
 

--
Gitblit v1.10.0