From e25d897908d93ab4f9cabadb1029ad3352602d5d Mon Sep 17 00:00:00 2001
From: boli <boli@localhost>
Date: Wed, 31 Oct 2007 22:19:14 +0000
Subject: [PATCH] This set of fixes mainly address the issues where the JE backend does not handle attributes with options and subtypes correctly when they are being indexed.  With this fix: - All values of an indexed attribute type will be indexed correctly on modifies, adds, and deletes.  - Updates to subordinate types will now update the superior type if its indexed.  - Adding and deleting superior attribute types that are not allowed by any object classes (ie. name) will be correctly handled - Deleting all values from an attribute with no options will no longer delete the values from the same attribute but with options.  

---
 opends/src/server/org/opends/server/backends/jeb/VLVIndex.java                                     |   15 
 opends/src/server/org/opends/server/backends/jeb/EntryContainer.java                               |   14 
 opends/src/server/org/opends/server/backends/jeb/OrderingIndexer.java                              |   93 ++----
 opends/src/server/org/opends/server/types/Entry.java                                               |  243 +++++++++++-----
 opends/tests/unit-tests-testng/src/server/org/opends/server/types/EntrySchemaCheckingTestCase.java |   30 ++
 opends/tests/unit-tests-testng/src/server/org/opends/server/types/TestEntry.java                   |   18 +
 opends/src/server/org/opends/server/backends/jeb/SubstringIndexer.java                             |   75 +++-
 opends/src/server/org/opends/server/backends/jeb/EqualityIndexer.java                              |  159 ++--------
 opends/src/server/org/opends/server/backends/jeb/ApproximateIndexer.java                           |   32 ++
 opends/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestBackendImpl.java      |  154 +++++++++-
 opends/src/server/org/opends/server/backends/jeb/PresenceIndexer.java                              |   19 +
 opends/tests/unit-tests-testng/resource/config-changes.ldif                                        |   12 
 12 files changed, 558 insertions(+), 306 deletions(-)

diff --git a/opends/src/server/org/opends/server/backends/jeb/ApproximateIndexer.java b/opends/src/server/org/opends/server/backends/jeb/ApproximateIndexer.java
index bf42103..84ffd2a 100644
--- a/opends/src/server/org/opends/server/backends/jeb/ApproximateIndexer.java
+++ b/opends/src/server/org/opends/server/backends/jeb/ApproximateIndexer.java
@@ -176,7 +176,37 @@
                           Set<ASN1OctetString> addKeys,
                           Set<ASN1OctetString> delKeys)
   {
-    replaceEntry(txn, oldEntry, newEntry, addKeys, delKeys);
+    List<Attribute> newAttributes = newEntry.getAttribute(attributeType, true);
+    List<Attribute> oldAttributes = oldEntry.getAttribute(attributeType, true);
+    HashSet<AttributeValue> newValues;
+    HashSet<AttributeValue> oldValues;
+
+    if(newAttributes == null)
+    {
+      indexAttribute(oldAttributes, delKeys);
+    }
+    else
+    {
+      if(oldAttributes == null)
+      {
+        indexAttribute(newAttributes, addKeys);
+      }
+      else
+      {
+        HashSet<ASN1OctetString> newKeys =
+            new HashSet<ASN1OctetString>();
+        HashSet<ASN1OctetString> oldKeys =
+            new HashSet<ASN1OctetString>();
+        indexAttribute(newAttributes, newKeys);
+        indexAttribute(oldAttributes, oldKeys);
+
+        addKeys.addAll(newKeys);
+        addKeys.removeAll(oldKeys);
+
+        delKeys.addAll(oldKeys);
+        delKeys.removeAll(newKeys);
+      }
+    }
   }
 
   /**
diff --git a/opends/src/server/org/opends/server/backends/jeb/EntryContainer.java b/opends/src/server/org/opends/server/backends/jeb/EntryContainer.java
index 556ebbf..7073463 100644
--- a/opends/src/server/org/opends/server/backends/jeb/EntryContainer.java
+++ b/opends/src/server/org/opends/server/backends/jeb/EntryContainer.java
@@ -3789,15 +3789,27 @@
     {
       // Check whether any modifications apply to this indexed attribute.
       boolean attributeModified = false;
+      AttributeType indexAttributeType = index.getAttributeType();
+      Iterable<AttributeType> subTypes =
+          DirectoryServer.getSchema().getSubTypes(indexAttributeType);
+
       for (Modification mod : mods)
       {
         Attribute modAttr = mod.getAttribute();
         AttributeType modAttrType = modAttr.getAttributeType();
-        if (modAttrType.equals(index.getAttributeType()))
+        if (modAttrType.equals(indexAttributeType))
         {
           attributeModified = true;
           break;
         }
+        for(AttributeType subType : subTypes)
+        {
+          if(modAttrType.equals(subType))
+          {
+            attributeModified = true;
+            break;
+          }
+        }
       }
       if (attributeModified)
       {
diff --git a/opends/src/server/org/opends/server/backends/jeb/EqualityIndexer.java b/opends/src/server/org/opends/server/backends/jeb/EqualityIndexer.java
index 83f2079..e684c0a 100644
--- a/opends/src/server/org/opends/server/backends/jeb/EqualityIndexer.java
+++ b/opends/src/server/org/opends/server/backends/jeb/EqualityIndexer.java
@@ -45,7 +45,6 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
-import java.util.ArrayList;
 
 /**
  * An implementation of an Indexer for attribute equality.
@@ -191,140 +190,44 @@
                           Set<ASN1OctetString> delKeys)
        throws DatabaseException
   {
-    // Optimize for the case where there are no attribute options
-    // involved and only simple addition and deletion of individual values.
-    // An issue with attribute options is that the values can not be assumed
-    // to be unique.
+    List<Attribute> newAttributes = newEntry.getAttribute(attributeType, true);
+    List<Attribute> oldAttributes = oldEntry.getAttribute(attributeType, true);
+    HashSet<AttributeValue> newValues;
+    HashSet<AttributeValue> oldValues;
 
-    List<Attribute> beforeList;
-    List<Attribute> afterList;
-    beforeList = oldEntry.getAttribute(attributeType);
-    afterList = newEntry.getAttribute(attributeType);
-
-    boolean hasOptions = false;
-
-    if (beforeList != null)
+    if(newAttributes == null)
     {
-      for (Attribute a : beforeList)
-      {
-        if (a.hasOptions())
-        {
-          hasOptions = true;
-          break;
-        }
-      }
-    }
-
-    if (afterList != null)
-    {
-      for (Attribute a : afterList)
-      {
-        if (a.hasOptions())
-        {
-          hasOptions = true;
-          break;
-        }
-      }
-    }
-
-    boolean hasOnlySimpleMods = true;
-    List<Modification> simpleMods = new ArrayList<Modification>();
-    for (Modification mod : mods)
-    {
-      Attribute modAttr = mod.getAttribute();
-      AttributeType modAttrType = modAttr.getAttributeType();
-      if (modAttrType.equals(attributeType))
-      {
-        if (modAttr.hasOptions())
-        {
-          hasOptions = true;
-        }
-        switch (mod.getModificationType())
-        {
-          case ADD:
-            simpleMods.add(mod);
-            break;
-
-          case DELETE:
-            if (!modAttr.hasValue())
-            {
-              hasOnlySimpleMods = false;
-            }
-            else
-            {
-              simpleMods.add(mod);
-            }
-            break;
-
-          default:
-            hasOnlySimpleMods = false;
-            break;
-        }
-      }
-    }
-
-    if (hasOnlySimpleMods && !hasOptions)
-    {
-      // This is the optimized case where there are no attribute options
-      // involved and only simple addition and deletion of individual values.
-      // It should be efficient for adding and/or deleting a few values of an
-      // attribute with lots of values.
-      for (Modification mod : simpleMods)
-      {
-        Set<AttributeValue> values = mod.getAttribute().getValues();
-        Set<ASN1OctetString> keys =
-             new HashSet<ASN1OctetString>(values.size());
-        indexValues(values, keys);
-
-        switch (mod.getModificationType())
-        {
-          case ADD:
-            for (ASN1OctetString key : keys)
-            {
-              if (delKeys.contains(key))
-              {
-                delKeys.remove(key);
-              }
-              else
-              {
-                addKeys.add(key);
-              }
-            }
-            break;
-
-          case DELETE:
-            for (ASN1OctetString key : keys)
-            {
-              if (addKeys.contains(key))
-              {
-                addKeys.remove(key);
-              }
-              else
-              {
-                delKeys.add(key);
-              }
-            }
-            break;
-        }
-      }
+      indexAttribute(oldAttributes, delKeys);
     }
     else
     {
-      // This is the non-optimized case that should be correct in all cases.
+      if(oldAttributes == null)
+      {
+        indexAttribute(newAttributes, addKeys);
+      }
+      else
+      {
+        newValues = new HashSet<AttributeValue>();
+        oldValues = new HashSet<AttributeValue>();
+        for(Attribute a : newAttributes)
+        {
+          newValues.addAll(a.getValues());
+        }
+        for(Attribute a : oldAttributes)
+        {
+          oldValues.addAll(a.getValues());
+        }
 
-      Set<ASN1OctetString> oldSet = new HashSet<ASN1OctetString>();
-      indexAttribute(beforeList, oldSet);
+        HashSet<AttributeValue> valuesToAdd =
+            new HashSet<AttributeValue>(newValues);
+        HashSet<AttributeValue> valuesToDel =
+            new HashSet<AttributeValue>(oldValues);
+        valuesToAdd.removeAll(oldValues);
+        valuesToDel.removeAll(newValues);
 
-      Set<ASN1OctetString> newSet = new HashSet<ASN1OctetString>();
-      indexAttribute(afterList, newSet);
-
-      HashSet<ASN1OctetString> removeSet = new HashSet<ASN1OctetString>(oldSet);
-      removeSet.removeAll(newSet);
-      delKeys.addAll(removeSet);
-
-      HashSet<ASN1OctetString> addSet = new HashSet<ASN1OctetString>(newSet);
-      addSet.removeAll(oldSet);
-      addKeys.addAll(addSet);
+        indexValues(valuesToDel, delKeys);
+        indexValues(valuesToAdd, addKeys);
+      }
     }
   }
 
diff --git a/opends/src/server/org/opends/server/backends/jeb/OrderingIndexer.java b/opends/src/server/org/opends/server/backends/jeb/OrderingIndexer.java
index 70d9b15..4c17058 100644
--- a/opends/src/server/org/opends/server/backends/jeb/OrderingIndexer.java
+++ b/opends/src/server/org/opends/server/backends/jeb/OrderingIndexer.java
@@ -180,72 +180,43 @@
                           Set<ASN1OctetString> delKeys)
        throws DatabaseException
   {
-    List<Attribute> beforeList;
-    beforeList = oldEntry.getAttribute(attributeType);
+    List<Attribute> newAttributes = newEntry.getAttribute(attributeType, true);
+    List<Attribute> oldAttributes = oldEntry.getAttribute(attributeType, true);
+    HashSet<AttributeValue> newValues;
+    HashSet<AttributeValue> oldValues;
 
-    // Pick out the modifications that apply to this indexed attribute
-
-    /**
-     * FIXME unusual modifications can insert spurious index values
-     * The following sequence of modifications will insert A into the
-     * index, yet A is not a resulting value for the attribute.
-     *
-     * add: cn
-     * cn: A
-     * -
-     * replace: cn
-     * cn: B
-     * -
-     *
-     */
-
-    for (Modification mod : mods)
+    if(newAttributes == null)
     {
-      Attribute modAttr = mod.getAttribute();
-      AttributeType modAttrType = modAttr.getAttributeType();
-      if (modAttrType.equals(attributeType))
+      indexAttribute(oldAttributes, delKeys);
+    }
+    else
+    {
+      if(oldAttributes == null)
       {
-        switch (mod.getModificationType())
+        indexAttribute(newAttributes, addKeys);
+      }
+      else
+      {
+        newValues = new HashSet<AttributeValue>();
+        oldValues = new HashSet<AttributeValue>();
+        for(Attribute a : newAttributes)
         {
-          case REPLACE:
-          case INCREMENT:
-            if (beforeList != null)
-            {
-              for (Attribute attr : beforeList)
-              {
-                if (attr.hasOptions(modAttr.getOptions()))
-                {
-                  indexValues(attr.getValues(), delKeys);
-                }
-              }
-            }
-            indexValues(modAttr.getValues(), addKeys);
-            break;
-
-          case ADD:
-            indexValues(modAttr.getValues(), addKeys);
-            break;
-
-          case DELETE:
-            if (!modAttr.hasValue())
-            {
-              if (beforeList != null)
-              {
-                for (Attribute attr : beforeList)
-                {
-                  if (attr.hasOptions(modAttr.getOptions()))
-                  {
-                    indexValues(attr.getValues(), delKeys);
-                  }
-                }
-              }
-            }
-            else
-            {
-              indexValues(modAttr.getValues(), delKeys);
-            }
-            break;
+          newValues.addAll(a.getValues());
         }
+        for(Attribute a : oldAttributes)
+        {
+          oldValues.addAll(a.getValues());
+        }
+
+        HashSet<AttributeValue> valuesToAdd =
+            new HashSet<AttributeValue>(newValues);
+        HashSet<AttributeValue> valuesToDel =
+            new HashSet<AttributeValue>(oldValues);
+        valuesToAdd.removeAll(oldValues);
+        valuesToDel.removeAll(newValues);
+
+        indexValues(valuesToDel, delKeys);
+        indexValues(valuesToAdd, addKeys);
       }
     }
   }
diff --git a/opends/src/server/org/opends/server/backends/jeb/PresenceIndexer.java b/opends/src/server/org/opends/server/backends/jeb/PresenceIndexer.java
index 887746f..c1fa5a6 100644
--- a/opends/src/server/org/opends/server/backends/jeb/PresenceIndexer.java
+++ b/opends/src/server/org/opends/server/backends/jeb/PresenceIndexer.java
@@ -171,6 +171,23 @@
                           Set<ASN1OctetString> delKeys)
        throws DatabaseException
   {
-    replaceEntry(txn, oldEntry, newEntry, addKeys, delKeys);
+    List<Attribute> newAttributes = newEntry.getAttribute(attributeType, true);
+    List<Attribute> oldAttributes = oldEntry.getAttribute(attributeType, true);
+    if(oldAttributes == null)
+    {
+      if(newAttributes != null)
+      {
+        addKeys.add(
+              new ASN1OctetString(AttributeIndex.presenceKey.getData()));
+      }
+    }
+    else
+    {
+      if(newAttributes == null)
+      {
+        delKeys.add(
+              new ASN1OctetString(AttributeIndex.presenceKey.getData()));
+      }
+    }
   }
 }
diff --git a/opends/src/server/org/opends/server/backends/jeb/SubstringIndexer.java b/opends/src/server/org/opends/server/backends/jeb/SubstringIndexer.java
index a9d04c9..9024da4 100644
--- a/opends/src/server/org/opends/server/backends/jeb/SubstringIndexer.java
+++ b/opends/src/server/org/opends/server/backends/jeb/SubstringIndexer.java
@@ -34,7 +34,6 @@
 
 import java.util.Comparator;
 import java.util.HashSet;
-import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
 
@@ -179,7 +178,37 @@
                           Set<ASN1OctetString> addKeys,
                           Set<ASN1OctetString> delKeys)
   {
-    replaceEntry(txn, oldEntry, newEntry, addKeys, delKeys);
+    List<Attribute> newAttributes = newEntry.getAttribute(attributeType, true);
+    List<Attribute> oldAttributes = oldEntry.getAttribute(attributeType, true);
+    HashSet<AttributeValue> newValues;
+    HashSet<AttributeValue> oldValues;
+
+    if(newAttributes == null)
+    {
+      indexAttribute(oldAttributes, delKeys);
+    }
+    else
+    {
+      if(oldAttributes == null)
+      {
+        indexAttribute(newAttributes, addKeys);
+      }
+      else
+      {
+        HashSet<ASN1OctetString> newKeys =
+            new HashSet<ASN1OctetString>();
+        HashSet<ASN1OctetString> oldKeys =
+            new HashSet<ASN1OctetString>();
+        indexAttribute(newAttributes, newKeys);
+        indexAttribute(oldAttributes, oldKeys);
+
+        addKeys.addAll(newKeys);
+        addKeys.removeAll(oldKeys);
+
+        delKeys.addAll(oldKeys);
+        delKeys.removeAll(newKeys);
+      }
+    }
   }
 
 
@@ -187,30 +216,42 @@
   /**
    * Generate the set of substring index keys for an attribute.
    * @param attrList The attribute for which substring keys are required.
-   * @param addKeys The set into which the generated keys will be inserted.
+   * @param keys The set into which the generated keys will be inserted.
    */
   private void indexAttribute(List<Attribute> attrList,
-                              Set<ASN1OctetString> addKeys)
+                              Set<ASN1OctetString> keys)
   {
     if (attrList == null) return;
 
     for (Attribute attr : attrList)
     {
-      LinkedHashSet<AttributeValue> values = attr.getValues();
-      for (AttributeValue value : values)
-      {
-        try
-        {
-          byte[] normalizedBytes = value.getNormalizedValue().value();
+      indexValues(attr.getValues(), keys);
+    }
+  }
 
-          substringKeys(normalizedBytes, addKeys);
-        }
-        catch (DirectoryException e)
+  /**
+   * Generate the set of index keys for a set of attribute values.
+   * @param values The set of attribute values to be indexed.
+   * @param keys The set into which the keys will be inserted.
+   */
+  private void indexValues(Set<AttributeValue> values,
+                           Set<ASN1OctetString> keys)
+  {
+    if (values == null) return;
+
+    for (AttributeValue value : values)
+    {
+      try
+      {
+        byte[] normalizedBytes = value.getNormalizedValue().value();
+
+        substringKeys(normalizedBytes, keys);
+      }
+      catch (DirectoryException e)
+      {
+        if (debugEnabled())
         {
-          if (debugEnabled())
-          {
-            TRACER.debugCaught(DebugLogLevel.ERROR, e);
-          }
+          TRACER.debugCaught(DebugLogLevel.ERROR, e);
         }
       }
     }
diff --git a/opends/src/server/org/opends/server/backends/jeb/VLVIndex.java b/opends/src/server/org/opends/server/backends/jeb/VLVIndex.java
index 0f24189..00de153 100644
--- a/opends/src/server/org/opends/server/backends/jeb/VLVIndex.java
+++ b/opends/src/server/org/opends/server/backends/jeb/VLVIndex.java
@@ -383,14 +383,25 @@
         SortKey[] sortKeys = sortOrder.getSortKeys();
         for(SortKey sortKey : sortKeys)
         {
+          AttributeType attributeType = sortKey.getAttributeType();
+          Iterable<AttributeType> subTypes =
+              DirectoryServer.getSchema().getSubTypes(attributeType);
           for(Modification mod : mods)
           {
-            if(mod.getAttribute().getAttributeType().
-                equals(sortKey.getAttributeType()))
+            AttributeType modAttrType = mod.getAttribute().getAttributeType();
+            if(modAttrType.equals(attributeType))
             {
               sortAttributeModified = true;
               break;
             }
+            for(AttributeType subType : subTypes)
+            {
+              if(modAttrType.equals(subType))
+              {
+                sortAttributeModified = true;
+                break;
+              }
+            }
           }
           if(sortAttributeModified)
           {
diff --git a/opends/src/server/org/opends/server/types/Entry.java b/opends/src/server/org/opends/server/types/Entry.java
index 461143e..52f7b9c 100644
--- a/opends/src/server/org/opends/server/types/Entry.java
+++ b/opends/src/server/org/opends/server/types/Entry.java
@@ -608,25 +608,47 @@
   }
 
 
-
   /**
    * Indicates whether this entry contains the specified attribute.
+   * Any subordinate attribute of the specified attribute will also
+   * be used in the determination.
    *
-   * @param  attributeType  The attribute type for which to make the
-   *                        determination.
+   *
+   * @param  attributeType       The attribute type for which to
+   *                             make the determination.
    *
    * @return  <CODE>true</CODE> if this entry contains the specified
    *          attribute, or <CODE>false</CODE> if not.
    */
   public boolean hasAttribute(AttributeType attributeType)
   {
+    return hasAttribute(attributeType, true);
+  }
+
+
+  /**
+   * Indicates whether this entry contains the specified attribute.
+   *
+   * @param  attributeType       The attribute type for which to
+   *                             make the determination.
+   * @param  includeSubordinates Whether to include any subordinate
+   *                             attributes of the attribute type
+   *                             being retrieved.
+   *
+   * @return  <CODE>true</CODE> if this entry contains the specified
+   *          attribute, or <CODE>false</CODE> if not.
+   */
+  public boolean hasAttribute(AttributeType attributeType,
+                              boolean includeSubordinates)
+  {
     if (userAttributes.containsKey(attributeType) ||
         operationalAttributes.containsKey(attributeType))
     {
       return true;
     }
 
-    if (attributeType.mayHaveSubordinateTypes())
+    if (includeSubordinates &&
+        attributeType.mayHaveSubordinateTypes())
     {
       for (AttributeType at : schema.getSubTypes(attributeType))
       {
@@ -643,15 +665,16 @@
   }
 
 
-
   /**
    * Indicates whether this entry contains the specified attribute
-   * with all of the options in the provided set.
+   * with all of the options in the provided set. Any subordinate
+   * attribute of the specified attribute will also be used in
+   * the determination.
    *
-   * @param  attributeType     The attribute type for which to make
-   *                           the determination.
-   * @param  attributeOptions  The set of options to use in the
-   *                           determination.
+   * @param  attributeType       The attribute type for which to
+   *                             make the determination.
+   * @param  attributeOptions    The set of options to use in the
+   *                             determination.
    *
    * @return  <CODE>true</CODE> if this entry contains the specified
    *          attribute, or <CODE>false</CODE> if not.
@@ -659,8 +682,32 @@
   public boolean hasAttribute(AttributeType attributeType,
                               Set<String> attributeOptions)
   {
+    return hasAttribute(attributeType, true, attributeOptions);
+  }
+
+
+  /**
+   * Indicates whether this entry contains the specified attribute
+   * with all of the options in the provided set.
+   *
+   * @param  attributeType       The attribute type for which to
+   *                             make the determination.
+   * @param  includeSubordinates Whether to include any subordinate
+   *                             attributes of the attribute type
+   *                             being retrieved.
+   * @param  attributeOptions    The set of options to use in the
+   *                             determination.
+   *
+   * @return  <CODE>true</CODE> if this entry contains the specified
+   *          attribute, or <CODE>false</CODE> if not.
+   */
+  public boolean hasAttribute(AttributeType attributeType,
+                              boolean includeSubordinates,
+                              Set<String> attributeOptions)
+  {
     List<Attribute> attributes;
-    if (attributeType.mayHaveSubordinateTypes())
+    if (includeSubordinates &&
+        attributeType.mayHaveSubordinateTypes())
     {
       attributes = new LinkedList<Attribute>();
       List<Attribute> attrs = userAttributes.get(attributeType);
@@ -726,13 +773,12 @@
     return false;
   }
 
-
-
   /**
    * Retrieves the requested attribute element(s) for the specified
    * attribute type.  The list returned may include multiple elements
    * if the same attribute exists in the entry multiple times with
-   * different sets of options.
+   * different sets of options. It may also include any subordinate
+   * attributes of the attribute being retrieved.
    *
    * @param  attributeType  The attribute type to retrieve.
    *
@@ -742,7 +788,30 @@
    */
   public List<Attribute> getAttribute(AttributeType attributeType)
   {
-    if (attributeType.mayHaveSubordinateTypes())
+    return getAttribute(attributeType, true);
+  }
+
+
+  /**
+   * Retrieves the requested attribute element(s) for the specified
+   * attribute type.  The list returned may include multiple elements
+   * if the same attribute exists in the entry multiple times with
+   * different sets of options.
+   *
+   * @param  attributeType       The attribute type to retrieve.
+   * @param  includeSubordinates Whether to include any subordinate
+   *                             attributes of the attribute type
+   *                             being retrieved.
+   *
+   * @return  The requested attribute element(s) for the specified
+   *          attribute type, or <CODE>null</CODE> if the specified
+   *          attribute type is not present in this entry.
+   */
+  public List<Attribute> getAttribute(AttributeType attributeType,
+                                      boolean includeSubordinates)
+  {
+    if (includeSubordinates &&
+        attributeType.mayHaveSubordinateTypes())
     {
       List<Attribute> attributes = new LinkedList<Attribute>();
 
@@ -821,7 +890,9 @@
    * Retrieves the requested attribute element(s) for the attribute
    * with the specified name or OID.  The list returned may include
    * multiple elements if the same attribute exists in the entry
-   * multiple times with different sets of options.
+   * multiple times with different sets of options. It may also
+   * include any subordinate attributes of the attribute being
+   * retrieved.
    * <BR><BR>
    * Note that this method should only be used in cases in which the
    * Directory Server schema has no reference of an attribute type
@@ -842,7 +913,7 @@
     {
       if (attr.hasNameOrOID(lowerName))
       {
-        return getAttribute(attr);
+        return getAttribute(attr, true);
       }
     }
 
@@ -850,7 +921,7 @@
     {
       if (attr.hasNameOrOID(lowerName))
       {
-        return getAttribute(attr);
+        return getAttribute(attr, true);
       }
     }
 
@@ -865,17 +936,16 @@
     return null;
   }
 
-
-
   /**
    * Retrieves the requested attribute element(s) for the specified
    * attribute type.  The list returned may include multiple elements
    * if the same attribute exists in the entry multiple times with
-   * different sets of options.
+   * different sets of options. It may also include any subordinate
+   * attributes of the attribute being retrieved.
    *
-   * @param  attributeType  The attribute type to retrieve.
-   * @param  options        The set of attribute options to include in
-   *                        matching elements.
+   * @param  attributeType       The attribute type to retrieve.
+   * @param  options             The set of attribute options to
+   *                             include in matching elements.
    *
    * @return  The requested attribute element(s) for the specified
    *          attribute type, or <CODE>null</CODE> if the specified
@@ -885,8 +955,34 @@
   public List<Attribute> getAttribute(AttributeType attributeType,
                                       Set<String> options)
   {
+    return getAttribute(attributeType, true, options);
+  }
+
+  /**
+   * Retrieves the requested attribute element(s) for the specified
+   * attribute type.  The list returned may include multiple elements
+   * if the same attribute exists in the entry multiple times with
+   * different sets of options.
+   *
+   * @param  attributeType       The attribute type to retrieve.
+   * @param  includeSubordinates Whether to include any subordinate
+   *                             attributes of the attribute type
+   *                             being retrieved.
+   * @param  options             The set of attribute options to
+   *                             include in matching elements.
+   *
+   * @return  The requested attribute element(s) for the specified
+   *          attribute type, or <CODE>null</CODE> if the specified
+   *          attribute type is not present in this entry with the
+   *          provided set of options.
+   */
+  public List<Attribute> getAttribute(AttributeType attributeType,
+                                      boolean includeSubordinates,
+                                      Set<String> options)
+  {
     List<Attribute> attributes = new LinkedList<Attribute>();
-    if (attributeType.mayHaveSubordinateTypes())
+    if (includeSubordinates &&
+        attributeType.mayHaveSubordinateTypes())
     {
       List<Attribute> attrs = userAttributes.get(attributeType);
       if (attrs != null)
@@ -1053,7 +1149,7 @@
   public final <T> T getAttributeValue(AttributeType attributeType,
       AttributeValueDecoder<T> decoder) throws DirectoryException
   {
-    List<Attribute> attributes = getAttribute(attributeType);
+    List<Attribute> attributes = getAttribute(attributeType, true);
     AttributeValueIterable values =
          new AttributeValueIterable(attributes);
     Iterator<AttributeValue> iterator = values.iterator();
@@ -1100,7 +1196,7 @@
       Collection<T> collection)
       throws DirectoryException
   {
-    List<Attribute> attributes = getAttribute(attributeType);
+    List<Attribute> attributes = getAttribute(attributeType, true);
     AttributeValueIterable values =
          new AttributeValueIterable(attributes);
 
@@ -1664,7 +1760,7 @@
     attachment = null;
 
     List<Attribute> attrList =
-         getAttribute(attribute.getAttributeType());
+         getAttribute(attribute.getAttributeType(), false);
     if (attrList == null)
     {
       // There are no instances of the specified attribute in this
@@ -1721,10 +1817,11 @@
 
   /**
    * Removes all instances of the specified attribute type from this
-   * entry.  If the provided attribute type is the objectclass type,
-   * then all objectclass values will be removed (but must be replaced
-   * for the entry to be valid).  If the specified attribute type is
-   * not present in this entry, then this method will have no effect.
+   * entry, including any instances with options.  If the provided
+   * attribute type is the objectclass type, then all objectclass
+   * values will be removed (but must be replaced for the entry to
+   * be valid).  If the specified attribute type is not present in
+   * this entry, then this method will have no effect.
    *
    * @param  attributeType  The attribute type for the attribute to
    *                        remove from this entry.
@@ -1753,12 +1850,10 @@
 
   /**
    * Removes the attribute with the provided type and set of options
-   * from this entry.  If the provided set of options is
-   * <CODE>null</CODE> or empty, then all occurrences of the specified
-   * attribute will be removed.  Otherwise, only the instance with the
-   * exact set of options provided will be removed.  This has no
-   * effect if the specified attribute is not present in this entry
-   * with the given set of options.
+   * from this entry.  Only the instance with the exact set of
+   * options provided will be removed.  This has no effect if the
+   * specified attribute is not present in this entry with the given
+   * set of options.
    *
    * @param  attributeType  The attribute type for the attribute to
    *                        remove from this entry.
@@ -1774,44 +1869,37 @@
   {
     attachment = null;
 
-    if ((options == null) || options.isEmpty())
+    List<Attribute> attrList = userAttributes.get(attributeType);
+    if (attrList == null)
     {
-      return removeAttribute(attributeType);
-    }
-    else
-    {
-      List<Attribute> attrList = userAttributes.get(attributeType);
+      attrList = operationalAttributes.get(attributeType);
       if (attrList == null)
       {
-        attrList = operationalAttributes.get(attributeType);
-        if (attrList == null)
-        {
-          return false;
-        }
+        return false;
       }
-
-      boolean removed = false;
-
-      Iterator<Attribute> iterator = attrList.iterator();
-      while (iterator.hasNext())
-      {
-        Attribute a = iterator.next();
-        if (a.optionsEqual(options))
-        {
-          iterator.remove();
-          removed = true;
-          break;
-        }
-      }
-
-      if (attrList.isEmpty())
-      {
-        userAttributes.remove(attributeType);
-        operationalAttributes.remove(attributeType);
-      }
-
-      return removed;
     }
+
+    boolean removed = false;
+
+    Iterator<Attribute> iterator = attrList.iterator();
+    while (iterator.hasNext())
+    {
+      Attribute a = iterator.next();
+      if (a.optionsEqual(options))
+      {
+        iterator.remove();
+        removed = true;
+        break;
+      }
+    }
+
+    if (attrList.isEmpty())
+    {
+      userAttributes.remove(attributeType);
+      operationalAttributes.remove(attributeType);
+    }
+
+    return removed;
   }
 
 
@@ -1906,7 +1994,7 @@
       }
 
       List<Attribute> attrList =
-           getAttribute(attribute.getAttributeType());
+           getAttribute(attribute.getAttributeType(), false);
       if (attrList == null)
       {
         return false;
@@ -1944,11 +2032,11 @@
       LinkedHashSet<AttributeValue> valueSet = attribute.getValues();
       if ((valueSet == null) || valueSet.isEmpty())
       {
-        return removeAttribute(attribute.getAttributeType());
+        return removeAttribute(attribute.getAttributeType(), null);
       }
 
       List<Attribute> attrList =
-           getAttribute(attribute.getAttributeType());
+           getAttribute(attribute.getAttributeType(), false);
       if (attrList == null)
       {
         return false;
@@ -1971,7 +2059,8 @@
 
           if (existingValueSet.isEmpty())
           {
-            return removeAttribute(attribute.getAttributeType());
+            return removeAttribute(attribute.getAttributeType(),
+                null);
           }
 
           return true;
@@ -2050,7 +2139,7 @@
   public boolean hasValue(AttributeType attributeType,
                           Set<String> options, AttributeValue value)
   {
-    List<Attribute> attrList = getAttribute(attributeType);
+    List<Attribute> attrList = getAttribute(attributeType, true);
     if ((attrList == null) || attrList.isEmpty())
     {
       return false;
@@ -2196,7 +2285,7 @@
         break;
 
       case INCREMENT:
-        List<Attribute> attrList = getAttribute(t);
+        List<Attribute> attrList = getAttribute(t, false);
         if ((attrList == null) || attrList.isEmpty())
         {
           Message message =
diff --git a/opends/tests/unit-tests-testng/resource/config-changes.ldif b/opends/tests/unit-tests-testng/resource/config-changes.ldif
index 9ee7b7c..2ca81f4 100644
--- a/opends/tests/unit-tests-testng/resource/config-changes.ldif
+++ b/opends/tests/unit-tests-testng/resource/config-changes.ldif
@@ -1246,6 +1246,18 @@
 objectClass: ds-cfg-branch
 cn: Index
 
+dn: ds-cfg-attribute=name,cn=Index,ds-cfg-backend-id=indexRoot,cn=Backends,cn=config
+changetype: add
+objectClass: top
+objectClass: ds-cfg-local-db-index
+ds-cfg-attribute: name
+ds-cfg-index-type: presence
+ds-cfg-index-type: equality
+ds-cfg-index-type: substring
+ds-cfg-index-type: ordering
+ds-cfg-index-type: approximate
+ds-cfg-index-entry-limit: 4000
+
 dn: ds-cfg-attribute=cn,cn=Index,ds-cfg-backend-id=indexRoot,cn=Backends,cn=config
 changetype: add
 objectClass: top
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestBackendImpl.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestBackendImpl.java
index 8595001..10f0180 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestBackendImpl.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestBackendImpl.java
@@ -129,6 +129,9 @@
         "objectClass: organizationalPerson",
         "objectClass: inetOrgPerson",
         "givenName: Aaren",
+        "givenName;lang-fr: test2",
+        "givenName;lang-cn: test2",
+        "givenName;lang-es: test3",
         "sn: Atp",
         "cn: Aaren Atp",
         "initials: APA",
@@ -1019,7 +1022,8 @@
     Entry newEntry;
     EntryID entryID;
     AttributeType attribute;
-    AttributeIndex index;
+    AttributeIndex titleIndex;
+    AttributeIndex nameIndex;
     HashSet<ASN1OctetString> addKeys;
     DatabaseEntry key;
     PresenceIndexer presenceIndexer;
@@ -1036,12 +1040,46 @@
       ArrayList<Modification> modifications = new ArrayList<Modification>();
       modifications.add(new Modification(ModificationType.ADD, new
           Attribute("title", "debugger")));
+
+      attribute = DirectoryServer.getAttributeType("title");
+      LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>();
+      values.add(new AttributeValue(attribute, "debugger2"));
+      LinkedHashSet<String> options = new LinkedHashSet<String>(1);
+      options.add("lang-en");
+      Attribute attr = new Attribute(attribute, "title", options, values);
+      modifications.add(new Modification(ModificationType.ADD, attr));
+
       modifications.add(new Modification(ModificationType.DELETE, new
           Attribute("cn", "Aaren Atp")));
       modifications.add(new Modification(ModificationType.ADD, new
           Attribute("cn", "Aaren Rigor")));
       modifications.add(new Modification(ModificationType.ADD, new
           Attribute("cn", "Aarenister Rigor")));
+
+      attribute = DirectoryServer.getAttributeType("givenname");
+      values = new LinkedHashSet<AttributeValue>();
+      values.add(new AttributeValue(attribute, "test"));
+      options = new LinkedHashSet<String>(1);
+      options.add("lang-de");
+      attr = new Attribute(attribute, "givenName", options, values);
+      modifications.add(new Modification(ModificationType.ADD, attr));
+
+      attribute = DirectoryServer.getAttributeType("givenname");
+      values = new LinkedHashSet<AttributeValue>();
+      values.add(new AttributeValue(attribute, "test2"));
+      options = new LinkedHashSet<String>(1);
+      options.add("lang-cn");
+      attr = new Attribute(attribute, "givenName", options, values);
+      modifications.add(new Modification(ModificationType.DELETE, attr));
+
+      attribute = DirectoryServer.getAttributeType("givenname");
+      values = new LinkedHashSet<AttributeValue>();
+      values.add(new AttributeValue(attribute, "newtest3"));
+      options = new LinkedHashSet<String>(1);
+      options.add("lang-es");
+      attr = new Attribute(attribute, "givenName", options, values);
+      modifications.add(new Modification(ModificationType.REPLACE, attr));
+
       modifications.add(new Modification(ModificationType.REPLACE, new
           Attribute("employeenumber", "222")));
 
@@ -1053,19 +1091,31 @@
       assertNotNull(entryID);
 
 
-      attribute = newEntry.getAttribute("title").get(0).getAttributeType();
-      index = ec.getAttributeIndex(attribute);
+      attribute = DirectoryServer.getAttributeType("title");
+      titleIndex = ec.getAttributeIndex(attribute);
+      attribute = DirectoryServer.getAttributeType("name");
+      nameIndex = ec.getAttributeIndex(attribute);
 
-      //This current entry in the DB shouldn't be in the presence index.
+      //This current entry in the DB shouldn't be in the presence titleIndex.
       addKeys = new HashSet<ASN1OctetString>();
       addKeys.add(new ASN1OctetString(AttributeIndex.presenceKey.getData()));
       key = new DatabaseEntry();
       for (ASN1OctetString keyBytes : addKeys) {
         key.setData(keyBytes.value());
       }
-      assertEquals(index.presenceIndex.containsID(null, key, entryID),
+      assertEquals(titleIndex.presenceIndex.containsID(null, key, entryID),
           ConditionResult.FALSE);
 
+      //This current entry should be in the presence nameIndex.
+      addKeys = new HashSet<ASN1OctetString>();
+      addKeys.add(new ASN1OctetString(AttributeIndex.presenceKey.getData()));
+      key = new DatabaseEntry();
+      for (ASN1OctetString keyBytes : addKeys) {
+        key.setData(keyBytes.value());
+      }
+      assertEquals(nameIndex.presenceIndex.containsID(null, key, entryID),
+          ConditionResult.TRUE);
+
       ArrayList<Control> noControls = new ArrayList<Control>(0);
       InternalClientConnection conn =
           InternalClientConnection.getRootConnection();
@@ -1098,55 +1148,123 @@
               entry.getAttribute("cn").get(0).getAttributeType(),
               "Aaren Atp")));
 
+      options = new LinkedHashSet<String>();
+      options.add("lang-de");
+      assertTrue(entry.getAttribute("givenname", options).get(0).getValues().contains(
+          new AttributeValue(
+              entry.getAttribute("givenname", options).get(0).getAttributeType(),
+              "test")));
+      options = new LinkedHashSet<String>();
+      options.add("lang-cn");
+      assertNull
+          (entry.getAttribute("givenname", options));
+      options = new LinkedHashSet<String>();
+      options.add("lang-es");
+      assertTrue(entry.getAttribute("givenname", options).get(0).getValues().contains(
+          new AttributeValue(
+              entry.getAttribute("givenname", options).get(0).getAttributeType(),
+              "newtest3")));
+      options = new LinkedHashSet<String>();
+      options.add("lang-fr");
+      assertTrue(entry.getAttribute("givenname", options).get(0).getValues().contains(
+          new AttributeValue(
+              entry.getAttribute("givenname", options).get(0).getAttributeType(),
+              "test2")));
+
       assertTrue(entry.getAttribute("employeenumber").contains(new
           Attribute("employeenumber", "222")));
       assertFalse(entry.getAttribute("employeenumber").contains(new
           Attribute("employeenumber", "1")));
 
       addKeys = new HashSet<ASN1OctetString>();
-      presenceIndexer = new PresenceIndexer(index.getAttributeType());
+      presenceIndexer = new PresenceIndexer(titleIndex.getAttributeType());
       presenceIndexer.indexEntry(null, entry, addKeys);
 
       key = new DatabaseEntry();
       for (ASN1OctetString keyBytes : addKeys) {
         key.setData(keyBytes.value());
-      }
-      assertEquals(index.presenceIndex.containsID(null, key, entryID),
+        assertEquals(titleIndex.presenceIndex.containsID(null, key, entryID),
           ConditionResult.TRUE);
+      }
 
       addKeys = new HashSet<ASN1OctetString>();
-      orderingIndexer = new OrderingIndexer(index.getAttributeType());
+      presenceIndexer = new PresenceIndexer(nameIndex.getAttributeType());
+      presenceIndexer.indexEntry(null, entry, addKeys);
+
+      key = new DatabaseEntry();
+      for (ASN1OctetString keyBytes : addKeys) {
+        key.setData(keyBytes.value());
+        assertEquals(nameIndex.presenceIndex.containsID(null, key, entryID),
+          ConditionResult.TRUE);
+      }
+
+      addKeys = new HashSet<ASN1OctetString>();
+      orderingIndexer = new OrderingIndexer(titleIndex.getAttributeType());
       orderingIndexer.indexEntry(null, entry, addKeys);
 
       key = new DatabaseEntry();
       for (ASN1OctetString keyBytes : addKeys) {
         key.setData(keyBytes.value());
-      }
-      assertEquals(index.orderingIndex.containsID(null, key, entryID),
+        assertEquals(titleIndex.orderingIndex.containsID(null, key, entryID),
           ConditionResult.TRUE);
+      }
 
       addKeys = new HashSet<ASN1OctetString>();
-      equalityIndexer = new EqualityIndexer(index.getAttributeType());
+      orderingIndexer = new OrderingIndexer(nameIndex.getAttributeType());
+      orderingIndexer.indexEntry(null, entry, addKeys);
+
+      key = new DatabaseEntry();
+      for (ASN1OctetString keyBytes : addKeys) {
+        key.setData(keyBytes.value());
+        assertEquals(nameIndex.orderingIndex.containsID(null, key, entryID),
+          ConditionResult.TRUE);
+      }
+
+      addKeys = new HashSet<ASN1OctetString>();
+      equalityIndexer = new EqualityIndexer(titleIndex.getAttributeType());
       equalityIndexer.indexEntry(null, entry, addKeys);
 
       key = new DatabaseEntry();
       for (ASN1OctetString keyBytes : addKeys) {
         key.setData(keyBytes.value());
-      }
-      assertEquals(index.equalityIndex.containsID(null, key, entryID),
+        assertEquals(titleIndex.equalityIndex.containsID(null, key, entryID),
           ConditionResult.TRUE);
+      }
 
       addKeys = new HashSet<ASN1OctetString>();
-      substringIndexer = new SubstringIndexer(index.getAttributeType(),
-                   index.getConfiguration().getSubstringLength());
+      equalityIndexer = new EqualityIndexer(nameIndex.getAttributeType());
+      equalityIndexer.indexEntry(null, entry, addKeys);
+
+      key = new DatabaseEntry();
+      for (ASN1OctetString keyBytes : addKeys) {
+        key.setData(keyBytes.value());
+        assertEquals(nameIndex.equalityIndex.containsID(null, key, entryID),
+          ConditionResult.TRUE);
+      }
+
+      addKeys = new HashSet<ASN1OctetString>();
+      substringIndexer = new SubstringIndexer(titleIndex.getAttributeType(),
+                   titleIndex.getConfiguration().getSubstringLength());
       substringIndexer.indexEntry(null, entry, addKeys);
 
       key = new DatabaseEntry();
       for (ASN1OctetString keyBytes : addKeys) {
         key.setData(keyBytes.value());
-      }
-      assertEquals(index.substringIndex.containsID(null, key, entryID),
+        assertEquals(titleIndex.substringIndex.containsID(null, key, entryID),
           ConditionResult.TRUE);
+      }
+
+      addKeys = new HashSet<ASN1OctetString>();
+      substringIndexer = new SubstringIndexer(nameIndex.getAttributeType(),
+                   nameIndex.getConfiguration().getSubstringLength());
+      substringIndexer.indexEntry(null, entry, addKeys);
+
+      key = new DatabaseEntry();
+      for (ASN1OctetString keyBytes : addKeys) {
+        key.setData(keyBytes.value());
+        assertEquals(nameIndex.substringIndex.containsID(null, key, entryID),
+          ConditionResult.TRUE);
+      }
     }
     finally
     {
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/types/EntrySchemaCheckingTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/types/EntrySchemaCheckingTestCase.java
index dbb81a2..3f07c6f 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/types/EntrySchemaCheckingTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/types/EntrySchemaCheckingTestCase.java
@@ -1430,5 +1430,35 @@
     invalidReason = new MessageBuilder();
     failOnlyForStrictEvaluation(e);
   }
+
+  /**
+   * Tests schema checking for an entry that includes an attribute not
+   * defined in any objectClasses but the subtypes of the attribute are.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testInvalidSuperiorAttribute()
+         throws Exception
+  {
+    // The LDIF reader won't let us do this directly, so we have to hack around
+    // it.
+    Entry e = TestCaseUtils.makeEntry(
+         "dn: uid=test.user,o=test",
+         "objectClass: top",
+         "objectClass: person",
+         "objectClass: organizationalPerson",
+         "objectClass: inetOrgPerson",
+         "objectClass: account",
+         "uid: test.user",
+         "givenName: Test",
+         "sn: User",
+         "cn: Test User");
+
+    e.addAttribute(new Attribute("name", "foo"),
+                   new LinkedList<AttributeValue>());
+
+    assertFalse(e.conformsToSchema(null, false, true, true, new MessageBuilder()));
+  }
 }
 
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/types/TestEntry.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/types/TestEntry.java
index ccb0e3e..d3e4465 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/types/TestEntry.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/types/TestEntry.java
@@ -343,6 +343,7 @@
     assertTrue(e.hasAttribute(ocType));
     assertTrue(e.hasAttribute(cnType));
     assertTrue(e.hasAttribute(nameType));
+    assertFalse(e.hasAttribute(nameType, false));
     assertFalse(e.hasAttribute(uidType));
     assertTrue(e.hasAttribute(mnType));
 
@@ -350,6 +351,7 @@
     assertTrue(e.hasAttribute(ocType, options));
     assertTrue(e.hasAttribute(cnType, options));
     assertTrue(e.hasAttribute(nameType, options));
+    assertFalse(e.hasAttribute(nameType, false, options));
     assertFalse(e.hasAttribute(uidType, options));
     assertTrue(e.hasAttribute(mnType, options));
 
@@ -357,6 +359,7 @@
     assertTrue(e.hasAttribute(ocType, options));
     assertTrue(e.hasAttribute(cnType, options));
     assertTrue(e.hasAttribute(nameType, options));
+    assertFalse(e.hasAttribute(nameType, false, options));
     assertFalse(e.hasAttribute(uidType, options));
     assertTrue(e.hasAttribute(mnType, options));
 
@@ -364,6 +367,7 @@
     assertFalse(e.hasAttribute(ocType, options));
     assertTrue(e.hasAttribute(cnType, options));
     assertTrue(e.hasAttribute(nameType, options));
+    assertFalse(e.hasAttribute(nameType, false, options));
     assertFalse(e.hasAttribute(uidType, options));
     assertFalse(e.hasAttribute(mnType, options));
 
@@ -371,6 +375,7 @@
     assertFalse(e.hasAttribute(ocType, options));
     assertFalse(e.hasAttribute(cnType, options));
     assertFalse(e.hasAttribute(nameType, options));
+    assertFalse(e.hasAttribute(nameType, false, options));
     assertFalse(e.hasAttribute(uidType, options));
     assertFalse(e.hasAttribute(mnType, options));
 
@@ -379,6 +384,7 @@
     assertFalse(e.hasAttribute(ocType, options));
     assertFalse(e.hasAttribute(cnType, options));
     assertFalse(e.hasAttribute(nameType, options));
+    assertFalse(e.hasAttribute(nameType, false, options));
     assertFalse(e.hasAttribute(uidType, options));
     assertFalse(e.hasAttribute(mnType, options));
   }
@@ -526,6 +532,9 @@
     assertNotNull(attrs);
     assertEquals(attrs.size(), 6);
 
+    attrs = e.getAttribute(nameType, false);
+    assertNull(attrs);
+
     attrs = e.getAttribute(uidType);
     assertNull(attrs);
 
@@ -563,6 +572,9 @@
     assertNotNull(attrs);
     assertEquals(attrs.size(), 6);
 
+    attrs = e.getAttribute(nameType, false, options);
+    assertNull(attrs);
+
     attrs = e.getAttribute(uidType, options);
     assertNull(attrs);
 
@@ -600,6 +612,9 @@
     assertNotNull(attrs);
     assertEquals(attrs.size(), 6);
 
+    attrs = e.getAttribute(nameType, false, options);
+    assertNull(attrs);
+
     attrs = e.getAttribute(uidType, options);
     assertNull(attrs);
 
@@ -636,6 +651,9 @@
     assertNotNull(attrs);
     assertEquals(attrs.size(), 3);
 
+    attrs = e.getAttribute(nameType, false, options);
+    assertNull(attrs);
+
     attrs = e.getAttribute(uidType, options);
     assertNull(attrs);
 

--
Gitblit v1.10.0