From cd57b969289f568da79e41fffe451aa0ed837722 Mon Sep 17 00:00:00 2001
From: matthew_swift <matthew_swift@localhost>
Date: Wed, 28 Jan 2009 23:43:17 +0000
Subject: [PATCH] Fix issue 3446 and improve fix for issue 3726:

---
 opends/src/server/org/opends/server/extensions/EntryDNVirtualAttributeProvider.java                                           |   18 
 opends/src/server/org/opends/server/extensions/MemberVirtualAttributeProvider.java                                            |   27 
 opends/src/server/org/opends/server/types/Entry.java                                                                          |  351 +++---------------
 opends/src/server/org/opends/server/extensions/EntryUUIDVirtualAttributeProvider.java                                         |   20 
 opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/EntryUUIDVirtualAttributeProviderTestCase.java         |    5 
 opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/EntryDNVirtualAttributeProviderTestCase.java           |    7 
 opends/src/server/org/opends/server/extensions/HasSubordinatesVirtualAttributeProvider.java                                   |   15 
 opends/src/server/org/opends/server/extensions/SubschemaSubentryVirtualAttributeProvider.java                                 |   29 -
 opends/src/server/org/opends/server/extensions/NumSubordinatesVirtualAttributeProvider.java                                   |   15 
 opends/src/server/org/opends/server/core/SearchOperationBasis.java                                                            |  201 +++++++--
 opends/src/server/org/opends/server/types/VirtualAttribute.java                                                               |   16 
 opends/tests/unit-tests-testng/src/server/org/opends/server/core/SearchOperationTestCase.java                                 |  264 +++++++++++++
 opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/IsMemberOfVirtualAttributeProviderTestCase.java        |   18 
 opends/src/server/org/opends/server/api/VirtualAttributeProvider.java                                                         |   39 -
 opends/src/server/org/opends/server/extensions/UserDefinedVirtualAttributeProvider.java                                       |   32 +
 opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/VirtualStaticGroupTestCase.java                        |    7 
 opends/tests/unit-tests-testng/src/server/org/opends/server/types/VirtualAttributeTestCase.java                               |    6 
 opends/src/server/org/opends/server/extensions/IsMemberOfVirtualAttributeProvider.java                                        |   14 
 opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/SubschemaSubentryVirtualAttributeProviderTestCase.java |    8 
 19 files changed, 591 insertions(+), 501 deletions(-)

diff --git a/opends/src/server/org/opends/server/api/VirtualAttributeProvider.java b/opends/src/server/org/opends/server/api/VirtualAttributeProvider.java
index 64ef428..9b1c147 100644
--- a/opends/src/server/org/opends/server/api/VirtualAttributeProvider.java
+++ b/opends/src/server/org/opends/server/api/VirtualAttributeProvider.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2006-2008 Sun Microsystems, Inc.
+ *      Copyright 2006-2009 Sun Microsystems, Inc.
  */
 package org.opends.server.api;
 import org.opends.messages.Message;
@@ -31,8 +31,8 @@
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Set;
 
 import org.opends.server.admin.std.server.VirtualAttributeCfg;
 import org.opends.server.config.ConfigException;
@@ -147,19 +147,18 @@
 
 
   /**
-   * Generates a set of values for the provided entry.
+   * Generates an unmodifiable set of values for the provided entry.
    *
-   * @param  entry  The entry for which the values are to be
-   *                generated.
-   * @param  rule   The virtual attribute rule which defines the
-   *                constraints for the virtual attribute.
-   *
-   * @return  The set of values generated for the provided entry.  It
-   *          may be empty, but it must not be {@code null}.
+   * @param entry
+   *          The entry for which the values are to be generated.
+   * @param rule
+   *          The virtual attribute rule which defines the constraints
+   *          for the virtual attribute.
+   * @return The unmodifiable set of values generated for the provided
+   *         entry. It may be empty, but it must not be {@code null}.
    */
-  public abstract LinkedHashSet<AttributeValue>
-                       getValues(Entry entry,
-                                 VirtualAttributeRule rule);
+  public abstract Set<AttributeValue> getValues(
+      Entry entry, VirtualAttributeRule rule);
 
 
 
@@ -221,15 +220,8 @@
   public boolean hasAllValues(Entry entry, VirtualAttributeRule rule,
                               Collection<AttributeValue> values)
   {
-    for (AttributeValue value : values)
-    {
-      if (! getValues(entry, rule).contains(value))
-      {
-        return false;
-      }
-    }
-
-    return true;
+    Set<AttributeValue> virtualValues = getValues(entry, rule);
+    return virtualValues.containsAll(values);
   }
 
 
@@ -251,9 +243,10 @@
   public boolean hasAnyValue(Entry entry, VirtualAttributeRule rule,
                              Collection<AttributeValue> values)
   {
+    Set<AttributeValue> virtualValues = getValues(entry, rule);
     for (AttributeValue value : values)
     {
-      if (getValues(entry, rule).contains(value))
+      if (virtualValues.contains(value))
       {
         return true;
       }
diff --git a/opends/src/server/org/opends/server/core/SearchOperationBasis.java b/opends/src/server/org/opends/server/core/SearchOperationBasis.java
index b4aad32..456aa04 100644
--- a/opends/src/server/org/opends/server/core/SearchOperationBasis.java
+++ b/opends/src/server/org/opends/server/core/SearchOperationBasis.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2006-2008 Sun Microsystems, Inc.
+ *      Copyright 2006-2009 Sun Microsystems, Inc.
  */
 package org.opends.server.core;
 
@@ -35,6 +35,7 @@
 import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 import org.opends.server.api.ClientConnection;
 import org.opends.server.api.plugin.PluginResult;
@@ -714,10 +715,13 @@
     // Make a copy of the entry and pare it down to only include the set
     // of requested attributes.
     Entry entryToReturn;
+    boolean omitReal = isVirtualAttributesOnly();
+    boolean omitVirtual = isRealAttributesOnly();
     if ((getAttributes() == null) || getAttributes().isEmpty())
     {
-      entryToReturn = entry.duplicateWithoutOperationalAttributes(typesOnly,
-                                                                  true);
+      entryToReturn =
+          entry.duplicateWithoutOperationalAttributes(typesOnly,
+              omitReal, omitVirtual);
     }
     else
     {
@@ -729,22 +733,26 @@
         {
           // This is a special placeholder indicating that all user attributes
           // should be returned.
-          if (typesOnly)
+          if (!omitReal)
           {
-            // First, add the placeholder for the objectclass attribute.
-            AttributeType ocType =
-                 DirectoryServer.getObjectClassAttributeType();
-            List<Attribute> ocList = new ArrayList<Attribute>(1);
-            ocList.add(Attributes.empty(ocType));
-            entryToReturn.putAttribute(ocType, ocList);
-          }
-          else
-          {
-            // First, add the objectclass attribute.
-            Attribute ocAttr = entry.getObjectClassAttribute();
-            if (ocAttr != null)
+            if (typesOnly)
             {
-              entryToReturn.replaceAttribute(ocAttr);
+              // First, add the placeholder for the objectclass
+              // attribute.
+              AttributeType ocType =
+                  DirectoryServer.getObjectClassAttributeType();
+              List<Attribute> ocList = new ArrayList<Attribute>(1);
+              ocList.add(Attributes.empty(ocType));
+              entryToReturn.putAttribute(ocType, ocList);
+            }
+            else
+            {
+              // First, add the objectclass attribute.
+              Attribute ocAttr = entry.getObjectClassAttribute();
+              if (ocAttr != null)
+              {
+                entryToReturn.replaceAttribute(ocAttr);
+              }
             }
           }
 
@@ -752,8 +760,12 @@
           for (AttributeType t : entry.getUserAttributes().keySet())
           {
             List<Attribute> attrList =
-                 entry.duplicateUserAttribute(t, null, typesOnly);
-            entryToReturn.putAttribute(t, attrList);
+                duplicateUserAttribute(entry, t, null, typesOnly,
+                    omitReal, omitVirtual);
+            if (attrList != null)
+            {
+              entryToReturn.putAttribute(t, attrList);
+            }
           }
 
           continue;
@@ -765,8 +777,12 @@
           for (AttributeType t : entry.getOperationalAttributes().keySet())
           {
             List<Attribute> attrList =
-                 entry.duplicateOperationalAttribute(t, null, typesOnly);
-            entryToReturn.putAttribute(t, attrList);
+                duplicateOperationalAttribute(entry, t, null,
+                    typesOnly, omitReal, omitVirtual);
+            if (attrList != null)
+            {
+              entryToReturn.putAttribute(t, attrList);
+            }
           }
 
           continue;
@@ -787,7 +803,6 @@
             semicolonPos = nextPos;
             nextPos = attrName.indexOf(';', semicolonPos+1);
           }
-
           options.add(attrName.substring(semicolonPos+1));
         }
         else
@@ -796,7 +811,6 @@
           options = null;
         }
 
-
         AttributeType attrType = DirectoryServer.getAttributeType(lowerName);
         if (attrType == null)
         {
@@ -806,11 +820,11 @@
             if (t.hasNameOrOID(lowerName))
             {
               List<Attribute> attrList =
-                   entry.duplicateUserAttribute(t, options, typesOnly);
+                  duplicateUserAttribute(entry, t, options, typesOnly,
+                      omitReal, omitVirtual);
               if (attrList != null)
               {
                 entryToReturn.putAttribute(t, attrList);
-
                 added = true;
                 break;
               }
@@ -827,11 +841,11 @@
             if (t.hasNameOrOID(lowerName))
             {
               List<Attribute> attrList =
-                   entry.duplicateOperationalAttribute(t, options, typesOnly);
+                  duplicateOperationalAttribute(entry, t, options,
+                      typesOnly, omitReal, omitVirtual);
               if (attrList != null)
               {
                 entryToReturn.putAttribute(t, attrList);
-
                 break;
               }
             }
@@ -840,30 +854,34 @@
         else
         {
           if (attrType.isObjectClassType()) {
-            if (typesOnly)
+            if (!omitReal)
             {
-              AttributeType ocType =
-                   DirectoryServer.getObjectClassAttributeType();
-              List<Attribute> ocList = new ArrayList<Attribute>(1);
-              ocList.add(Attributes.empty(ocType));
-              entryToReturn.putAttribute(ocType, ocList);
-            }
-            else
-            {
-              List<Attribute> attrList = new ArrayList<Attribute>(1);
-              attrList.add(entry.getObjectClassAttribute());
-              entryToReturn.putAttribute(attrType, attrList);
+              if (typesOnly)
+              {
+                AttributeType ocType =
+                    DirectoryServer.getObjectClassAttributeType();
+                List<Attribute> ocList = new ArrayList<Attribute>(1);
+                ocList.add(Attributes.empty(ocType));
+                entryToReturn.putAttribute(ocType, ocList);
+              }
+              else
+              {
+                List<Attribute> attrList = new ArrayList<Attribute>(1);
+                attrList.add(entry.getObjectClassAttribute());
+                entryToReturn.putAttribute(attrType, attrList);
+              }
             }
           }
           else
           {
             List<Attribute> attrList =
-                 entry.duplicateOperationalAttribute(attrType, options,
-                                                     typesOnly);
+                duplicateOperationalAttribute(entry, attrType, options,
+                    typesOnly, omitReal, omitVirtual);
             if (attrList == null)
             {
-              attrList = entry.duplicateUserAttribute(attrType, options,
-                                                      typesOnly);
+              attrList =
+                  duplicateUserAttribute(entry, attrType, options,
+                      typesOnly, omitReal, omitVirtual);
             }
             if (attrList != null)
             {
@@ -874,15 +892,6 @@
       }
     }
 
-    if (isRealAttributesOnly())
-    {
-      entryToReturn.stripVirtualAttributes();
-    }
-    else if (isVirtualAttributesOnly())
-    {
-      entryToReturn.stripRealAttributes();
-    }
-
     // If there is a matched values control, then further pare down the entry
     // based on the filters that it contains.
     MatchedValuesControl matchedValuesControl = getMatchedValuesControl();
@@ -1466,6 +1475,7 @@
   /**
    * {@inheritDoc}
    */
+  @Override
   public final void run()
   {
     setResultCode(ResultCode.UNDEFINED);
@@ -1617,4 +1627,91 @@
     appendErrorMessage(message);
   }
 
+
+  // Copies non-empty attributes.
+  private List<Attribute> duplicateAttribute(
+       List<Attribute> attrList,
+       Set<String> options,
+       boolean omitValues,
+       boolean omitReal,
+       boolean omitVirtual)
+  {
+    if (attrList == null)
+    {
+      return null;
+    }
+
+    ArrayList<Attribute> duplicateList =
+         new ArrayList<Attribute>(attrList.size());
+    for (Attribute a : attrList)
+    {
+      if (a.hasAllOptions(options))
+      {
+        if (omitReal && !a.isVirtual())
+        {
+          continue;
+        }
+        else if (omitVirtual && a.isVirtual())
+        {
+          continue;
+        }
+        else if (a.isEmpty())
+        {
+          continue;
+        }
+        else if (omitValues)
+        {
+          duplicateList.add(Attributes.empty(a));
+        }
+        else
+        {
+          duplicateList.add(a);
+        }
+      }
+    }
+
+    if (duplicateList.isEmpty())
+    {
+      return null;
+    }
+    else
+    {
+      return duplicateList;
+    }
+  }
+
+
+
+  // Copy a user attribute - may return null if the attribute was
+  // not found or if it was empty.
+  private List<Attribute> duplicateUserAttribute(
+       Entry entry,
+       AttributeType attributeType,
+       Set<String> options,
+       boolean omitValues,
+       boolean omitReal,
+       boolean omitVirtual)
+  {
+    List<Attribute> currentList = entry.getUserAttribute(attributeType);
+    return duplicateAttribute(currentList, options, omitValues,
+        omitReal, omitVirtual);
+  }
+
+
+
+  // Copy an operational attribute - may return null if the
+  // attribute was not found or if it was empty.
+  private List<Attribute> duplicateOperationalAttribute(
+       Entry entry,
+       AttributeType attributeType,
+       Set<String> options,
+       boolean omitValues,
+       boolean omitReal,
+       boolean omitVirtual)
+  {
+    List<Attribute> currentList =
+         entry.getOperationalAttribute(attributeType);
+    return duplicateAttribute(currentList, options, omitValues,
+        omitReal, omitVirtual);
+  }
 }
diff --git a/opends/src/server/org/opends/server/extensions/EntryDNVirtualAttributeProvider.java b/opends/src/server/org/opends/server/extensions/EntryDNVirtualAttributeProvider.java
index 59242e4..31ada60 100644
--- a/opends/src/server/org/opends/server/extensions/EntryDNVirtualAttributeProvider.java
+++ b/opends/src/server/org/opends/server/extensions/EntryDNVirtualAttributeProvider.java
@@ -22,15 +22,17 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2008 Sun Microsystems, Inc.
+ *      Copyright 2008-2009 Sun Microsystems, Inc.
  */
 package org.opends.server.extensions;
 
 
 
 import java.util.Collection;
+import java.util.Collections;
 import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Set;
 
 import org.opends.server.admin.std.server.EntryDNVirtualAttributeCfg;
 import org.opends.server.api.VirtualAttributeProvider;
@@ -109,16 +111,14 @@
    * {@inheritDoc}
    */
   @Override()
-  public LinkedHashSet<AttributeValue> getValues(Entry entry,
-                                                 VirtualAttributeRule rule)
+  public Set<AttributeValue> getValues(Entry entry,
+                                       VirtualAttributeRule rule)
   {
-    LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>(1);
-
     String normDNString = entry.getDN().toNormalizedString();
-    values.add(new AttributeValue(ByteStringFactory.create(normDNString),
-                                  ByteStringFactory.create(normDNString)));
-
-    return values;
+    AttributeValue value = new AttributeValue(
+                                  ByteStringFactory.create(normDNString),
+                                  ByteStringFactory.create(normDNString));
+    return Collections.singleton(value);
   }
 
 
diff --git a/opends/src/server/org/opends/server/extensions/EntryUUIDVirtualAttributeProvider.java b/opends/src/server/org/opends/server/extensions/EntryUUIDVirtualAttributeProvider.java
index f1a3b8c..036a995 100644
--- a/opends/src/server/org/opends/server/extensions/EntryUUIDVirtualAttributeProvider.java
+++ b/opends/src/server/org/opends/server/extensions/EntryUUIDVirtualAttributeProvider.java
@@ -22,14 +22,15 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2008 Sun Microsystems, Inc.
+ *      Copyright 2008-2009 Sun Microsystems, Inc.
  */
 package org.opends.server.extensions;
 
 
 
-import java.util.LinkedHashSet;
+import java.util.Collections;
 import java.util.List;
+import java.util.Set;
 import java.util.UUID;
 
 import org.opends.messages.Message;
@@ -50,7 +51,6 @@
 
 import static org.opends.messages.ExtensionMessages.*;
 import static org.opends.server.loggers.debug.DebugLogger.*;
-import static org.opends.server.util.ServerConstants.*;
 import static org.opends.server.util.StaticUtils.*;
 
 
@@ -112,18 +112,16 @@
    * {@inheritDoc}
    */
   @Override()
-  public LinkedHashSet<AttributeValue> getValues(Entry entry,
-                                                 VirtualAttributeRule rule)
+  public Set<AttributeValue> getValues(Entry entry,
+                                       VirtualAttributeRule rule)
   {
-    LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>(1);
-
     String normDNString = entry.getDN().toNormalizedString();
     String uuidString =
          UUID.nameUUIDFromBytes(getBytes(normDNString)).toString();
-    values.add(new AttributeValue(ByteStringFactory.create(uuidString),
-                                  ByteStringFactory.create(uuidString)));
-
-    return values;
+    AttributeValue value = new AttributeValue(
+        ByteStringFactory.create(uuidString),
+        ByteStringFactory.create(uuidString));
+    return Collections.singleton(value);
   }
 
 
diff --git a/opends/src/server/org/opends/server/extensions/HasSubordinatesVirtualAttributeProvider.java b/opends/src/server/org/opends/server/extensions/HasSubordinatesVirtualAttributeProvider.java
index 30d024a..130b741 100644
--- a/opends/src/server/org/opends/server/extensions/HasSubordinatesVirtualAttributeProvider.java
+++ b/opends/src/server/org/opends/server/extensions/HasSubordinatesVirtualAttributeProvider.java
@@ -22,14 +22,15 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2008 Sun Microsystems, Inc.
+ *      Copyright 2008-2009 Sun Microsystems, Inc.
  */
 package org.opends.server.extensions;
 
 
 
-import java.util.LinkedHashSet;
+import java.util.Collections;
 import java.util.List;
+import java.util.Set;
 
 import org.opends.messages.Message;
 import org.opends.server.admin.std.server.HasSubordinatesVirtualAttributeCfg;
@@ -109,11 +110,9 @@
    * {@inheritDoc}
    */
   @Override()
-  public LinkedHashSet<AttributeValue> getValues(Entry entry,
-                                                 VirtualAttributeRule rule)
+  public Set<AttributeValue> getValues(Entry entry,
+                                       VirtualAttributeRule rule)
   {
-    LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>(1);
-
     Backend backend = DirectoryServer.getBackend(entry.getDN());
 
     try
@@ -124,7 +123,7 @@
         AttributeValue value =
             new AttributeValue(ByteStringFactory.create(ret.toString()),
                                ByteStringFactory.create(ret.toString()));
-        values.add(value);
+        return Collections.singleton(value);
       }
     }
     catch(DirectoryException de)
@@ -135,7 +134,7 @@
       }
     }
 
-    return values;
+    return Collections.emptySet();
   }
 
 
diff --git a/opends/src/server/org/opends/server/extensions/IsMemberOfVirtualAttributeProvider.java b/opends/src/server/org/opends/server/extensions/IsMemberOfVirtualAttributeProvider.java
index 02e1cdc..8cf32ea 100644
--- a/opends/src/server/org/opends/server/extensions/IsMemberOfVirtualAttributeProvider.java
+++ b/opends/src/server/org/opends/server/extensions/IsMemberOfVirtualAttributeProvider.java
@@ -22,15 +22,17 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2008 Sun Microsystems, Inc.
+ *      Copyright 2008-2009 Sun Microsystems, Inc.
  */
 package org.opends.server.extensions;
 
 
 
 import java.util.Collection;
-import java.util.LinkedHashSet;
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 import org.opends.server.admin.std.server.IsMemberOfVirtualAttributeCfg;
 import org.opends.server.api.Group;
@@ -112,11 +114,11 @@
    * {@inheritDoc}
    */
   @Override()
-  public LinkedHashSet<AttributeValue> getValues(Entry entry,
-                                                 VirtualAttributeRule rule)
+  public Set<AttributeValue> getValues(Entry entry,
+                                       VirtualAttributeRule rule)
   {
     // FIXME -- This probably isn't the most efficient implementation.
-    LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>();
+    HashSet<AttributeValue> values = new HashSet<AttributeValue>();
     for (Group g : DirectoryServer.getGroupManager().getGroupInstances())
     {
       try
@@ -136,7 +138,7 @@
       }
     }
 
-    return values;
+    return Collections.unmodifiableSet(values);
   }
 
 
diff --git a/opends/src/server/org/opends/server/extensions/MemberVirtualAttributeProvider.java b/opends/src/server/org/opends/server/extensions/MemberVirtualAttributeProvider.java
index 4310cb5..d9f57ac 100644
--- a/opends/src/server/org/opends/server/extensions/MemberVirtualAttributeProvider.java
+++ b/opends/src/server/org/opends/server/extensions/MemberVirtualAttributeProvider.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2008 Sun Microsystems, Inc.
+ *      Copyright 2008-2009 Sun Microsystems, Inc.
  */
 package org.opends.server.extensions;
 import org.opends.messages.Message;
@@ -30,8 +30,10 @@
 
 
 import java.util.Collection;
-import java.util.LinkedHashSet;
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 import org.opends.server.admin.server.ConfigurationChangeListener;
 import org.opends.server.admin.std.server.MemberVirtualAttributeCfg;
@@ -40,7 +42,6 @@
 import org.opends.server.config.ConfigException;
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.core.SearchOperation;
-import org.opends.server.types.AttributeType;
 import org.opends.server.types.AttributeValue;
 import org.opends.server.types.ByteString;
 import org.opends.server.types.ConditionResult;
@@ -56,7 +57,6 @@
 
 import static org.opends.server.loggers.debug.DebugLogger.*;
 import org.opends.server.loggers.debug.DebugTracer;
-import static org.opends.server.util.ServerConstants.*;
 
 
 
@@ -74,10 +74,6 @@
    */
   private static final DebugTracer TRACER = getTracer();
 
-  // The attribute type used to indicate which target group should be used to
-  // obtain the member list.
-  private AttributeType targetGroupType;
-
   // The current configuration for this member virtual attribute.
   private MemberVirtualAttributeCfg currentConfig;
 
@@ -106,9 +102,6 @@
   {
     configuration.addMemberChangeListener(this);
     currentConfig = configuration;
-
-    targetGroupType =
-         DirectoryServer.getAttributeType(ATTR_TARGET_GROUP_DN, true);
   }
 
 
@@ -128,21 +121,21 @@
    * {@inheritDoc}
    */
   @Override()
-  public LinkedHashSet<AttributeValue> getValues(Entry entry,
-                                                 VirtualAttributeRule rule)
+  public Set<AttributeValue> getValues(Entry entry,
+                                       VirtualAttributeRule rule)
   {
     if (! currentConfig.isAllowRetrievingMembership())
     {
-      return new LinkedHashSet<AttributeValue>(0);
+      return Collections.emptySet();
     }
 
     Group g = DirectoryServer.getGroupManager().getGroupInstance(entry.getDN());
     if (g == null)
     {
-      return new LinkedHashSet<AttributeValue>(0);
+      return Collections.emptySet();
     }
 
-    LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>();
+    HashSet<AttributeValue> values = new HashSet<AttributeValue>();
     try
     {
       MemberList memberList = g.getMembers();
@@ -174,7 +167,7 @@
       }
     }
 
-    return values;
+    return Collections.unmodifiableSet(values);
   }
 
 
diff --git a/opends/src/server/org/opends/server/extensions/NumSubordinatesVirtualAttributeProvider.java b/opends/src/server/org/opends/server/extensions/NumSubordinatesVirtualAttributeProvider.java
index 8e69f35..727d220 100644
--- a/opends/src/server/org/opends/server/extensions/NumSubordinatesVirtualAttributeProvider.java
+++ b/opends/src/server/org/opends/server/extensions/NumSubordinatesVirtualAttributeProvider.java
@@ -22,14 +22,15 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2008 Sun Microsystems, Inc.
+ *      Copyright 2008-2009 Sun Microsystems, Inc.
  */
 package org.opends.server.extensions;
 
 
 
-import java.util.LinkedHashSet;
+import java.util.Collections;
 import java.util.List;
+import java.util.Set;
 
 import org.opends.messages.Message;
 import org.opends.server.admin.std.server.NumSubordinatesVirtualAttributeCfg;
@@ -110,11 +111,9 @@
    * {@inheritDoc}
    */
   @Override()
-  public LinkedHashSet<AttributeValue> getValues(Entry entry,
-                                                 VirtualAttributeRule rule)
+  public Set<AttributeValue> getValues(Entry entry,
+                                       VirtualAttributeRule rule)
   {
-    LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>(1);
-
     Backend backend = DirectoryServer.getBackend(entry.getDN());
 
     try
@@ -125,7 +124,7 @@
         AttributeValue value =
             new AttributeValue(ByteStringFactory.create(String.valueOf(count)),
                                ByteStringFactory.create(String.valueOf(count)));
-        values.add(value);
+        return Collections.singleton(value);
       }
     }
     catch(DirectoryException de)
@@ -136,7 +135,7 @@
       }
     }
 
-    return values;
+    return Collections.emptySet();
   }
 
 
diff --git a/opends/src/server/org/opends/server/extensions/SubschemaSubentryVirtualAttributeProvider.java b/opends/src/server/org/opends/server/extensions/SubschemaSubentryVirtualAttributeProvider.java
index 0902170..f8e58ca 100644
--- a/opends/src/server/org/opends/server/extensions/SubschemaSubentryVirtualAttributeProvider.java
+++ b/opends/src/server/org/opends/server/extensions/SubschemaSubentryVirtualAttributeProvider.java
@@ -22,14 +22,15 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2006-2008 Sun Microsystems, Inc.
+ *      Copyright 2006-2009 Sun Microsystems, Inc.
  */
 package org.opends.server.extensions;
 
 
 
-import java.util.LinkedHashSet;
+import java.util.Collections;
 import java.util.List;
+import java.util.Set;
 
 import org.opends.messages.Message;
 import org.opends.server.admin.std.server.SubschemaSubentryVirtualAttributeCfg;
@@ -37,7 +38,6 @@
 import org.opends.server.config.ConfigException;
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.core.SearchOperation;
-import org.opends.server.loggers.debug.DebugTracer;
 import org.opends.server.types.AttributeValue;
 import org.opends.server.types.ByteString;
 import org.opends.server.types.ConditionResult;
@@ -46,9 +46,7 @@
 import org.opends.server.types.ResultCode;
 import org.opends.server.types.VirtualAttributeRule;
 
-import static org.opends.server.loggers.debug.DebugLogger.*;
 import static org.opends.messages.ExtensionMessages.*;
-import static org.opends.server.util.ServerConstants.*;
 
 
 
@@ -60,13 +58,6 @@
        extends VirtualAttributeProvider<SubschemaSubentryVirtualAttributeCfg>
 {
   /**
-   * The tracer object for the debug logger.
-   */
-  private static final DebugTracer TRACER = getTracer();
-
-
-
-  /**
    * Creates a new instance of this subschemaSubentry virtual attribute
    * provider.
    */
@@ -108,15 +99,13 @@
    * {@inheritDoc}
    */
   @Override()
-  public LinkedHashSet<AttributeValue> getValues(Entry entry,
-                                                 VirtualAttributeRule rule)
+  public Set<AttributeValue> getValues(Entry entry,
+                                       VirtualAttributeRule rule)
   {
-    LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>(1);
-
-    values.add(new AttributeValue(rule.getAttributeType(),
-                                  DirectoryServer.getSchemaDN().toString()));
-
-    return values;
+    AttributeValue value =
+        new AttributeValue(rule.getAttributeType(),
+                           DirectoryServer.getSchemaDN().toString());
+    return Collections.singleton(value);
   }
 
 
diff --git a/opends/src/server/org/opends/server/extensions/UserDefinedVirtualAttributeProvider.java b/opends/src/server/org/opends/server/extensions/UserDefinedVirtualAttributeProvider.java
index 33ed8d2..f893cd7 100644
--- a/opends/src/server/org/opends/server/extensions/UserDefinedVirtualAttributeProvider.java
+++ b/opends/src/server/org/opends/server/extensions/UserDefinedVirtualAttributeProvider.java
@@ -22,14 +22,15 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2008 Sun Microsystems, Inc.
+ *      Copyright 2008-2009 Sun Microsystems, Inc.
  */
 package org.opends.server.extensions;
 import org.opends.messages.Message;
 
 
 
-import java.util.LinkedHashSet;
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
@@ -124,20 +125,27 @@
    * {@inheritDoc}
    */
   @Override()
-  public LinkedHashSet<AttributeValue> getValues(Entry entry,
-                                                 VirtualAttributeRule rule)
+  public Set<AttributeValue> getValues(Entry entry,
+                                       VirtualAttributeRule rule)
   {
     AttributeType attributeType = rule.getAttributeType();
     Set<String> userDefinedValues = currentConfig.getValue();
-
-    LinkedHashSet<AttributeValue> values =
-         new LinkedHashSet<AttributeValue>(userDefinedValues.size());
-    for (String valueString : userDefinedValues)
-    {
-      values.add(new AttributeValue(attributeType, valueString));
+    switch (userDefinedValues.size()) {
+    case 0:
+      return Collections.emptySet();
+    case 1:
+      String valueString = userDefinedValues.iterator().next();
+      AttributeValue value = new AttributeValue(attributeType, valueString);
+      return Collections.singleton(value);
+    default:
+      HashSet<AttributeValue> values =
+          new HashSet<AttributeValue>(userDefinedValues.size());
+      for (String valueString2 : userDefinedValues)
+      {
+        values.add(new AttributeValue(attributeType, valueString2));
+      }
+      return Collections.unmodifiableSet(values);
     }
-
-    return values;
   }
 
 
diff --git a/opends/src/server/org/opends/server/types/Entry.java b/opends/src/server/org/opends/server/types/Entry.java
index 2538631..46a274f 100644
--- a/opends/src/server/org/opends/server/types/Entry.java
+++ b/opends/src/server/org/opends/server/types/Entry.java
@@ -96,10 +96,6 @@
    */
   private static final DebugTracer TRACER = getTracer();
 
-  // Indicates whether virtual attribute processing has been performed
-  // for this entry.
-  private boolean virtualAttributeProcessingPerformed;
-
   // The set of operational attributes for this entry.
   private Map<AttributeType,List<Attribute>> operationalAttributes;
 
@@ -120,7 +116,7 @@
   private transient Object attachment;
 
   // The schema used to govern this entry.
-  private Schema schema;
+  private final Schema schema;
 
 
 
@@ -149,13 +145,9 @@
   {
     attachment                          = null;
     schema                              = DirectoryServer.getSchema();
-    virtualAttributeProcessingPerformed = false;
-
-
     suppressedAttributes =
          new LinkedHashMap<AttributeType,List<Attribute>>();
 
-
     if (dn == null)
     {
       this.dn = DN.nullDN();
@@ -1214,114 +1206,6 @@
 
 
   /**
-   * Makes a copy of attributes matching the specified options.
-   *
-   * @param  attrList       The attributes to be copied.
-   * @param  options        The set of attribute options to include in
-   *                        matching elements.
-   * @param  omitValues     <CODE>true</CODE> if the values are to be
-   *                        omitted.
-   *
-   * @return  A copy of the attributes matching the specified options,
-   *          or <CODE>null</CODE> if there is no such attribute with
-   *          the specified set of options.
-   */
-  private static List<Attribute> duplicateAttribute(
-       List<Attribute> attrList,
-       Set<String> options,
-       boolean omitValues)
-  {
-    if (attrList == null)
-    {
-      return null;
-    }
-
-    ArrayList<Attribute> duplicateList =
-         new ArrayList<Attribute>(attrList.size());
-    for (Attribute a : attrList)
-    {
-      if (a.hasAllOptions(options))
-      {
-        if (omitValues && !a.isVirtual())
-        {
-          duplicateList.add(Attributes.empty(a));
-        }
-        else
-        {
-          duplicateList.add(a);
-        }
-      }
-    }
-
-    if (duplicateList.isEmpty())
-    {
-      return null;
-    }
-    else
-    {
-      return duplicateList;
-    }
-  }
-
-
-
-  /**
-   * Retrieves a copy of the requested user 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  options        The set of attribute options to include in
-   *                        matching elements.
-   * @param  omitValues     <CODE>true</CODE> if the values are to be
-   *                        omitted.
-   *
-   * @return  A copy of the requested attribute element(s) for the
-   *          specified attribute type, or <CODE>null</CODE> if there
-   *          is no such user attribute with the specified set of
-   *          options.
-   */
-  public List<Attribute> duplicateUserAttribute(
-       AttributeType attributeType,
-       Set<String> options,
-       boolean omitValues)
-  {
-    List<Attribute> currentList = getUserAttribute(attributeType);
-    return duplicateAttribute(currentList, options, omitValues);
-  }
-
-
-
-  /**
-   * Retrieves a copy of the requested operational 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  options        The set of attribute options to include in
-   *                        matching elements.
-   * @param  omitValues     <CODE>true</CODE> if the values are to be
-   *                        omitted.
-   *
-   * @return  A copy of the requested attribute element(s) for the
-   *          specified attribute type, or <CODE>null</CODE> if there
-   *          is no such user attribute with the specified set of
-   *          options.
-   */
-  public List<Attribute> duplicateOperationalAttribute(
-       AttributeType attributeType,
-       Set<String> options,
-       boolean omitValues)
-  {
-    List<Attribute> currentList =
-         getOperationalAttribute(attributeType);
-    return duplicateAttribute(currentList, options, omitValues);
-  }
-
-
-  /**
    * Indicates whether this entry contains the specified operational
    * attribute.
    *
@@ -2966,12 +2850,14 @@
     HashMap<AttributeType,List<Attribute>> userAttrsCopy =
          new HashMap<AttributeType,List<Attribute>>(
               userAttributes.size());
-    deepCopy(userAttributes, userAttrsCopy, false);
+    deepCopy(userAttributes, userAttrsCopy, false, false, false,
+        true);
 
     HashMap<AttributeType,List<Attribute>> operationalAttrsCopy =
          new HashMap<AttributeType,List<Attribute>>(
                   operationalAttributes.size());
-    deepCopy(operationalAttributes, operationalAttrsCopy, false);
+    deepCopy(operationalAttributes, operationalAttrsCopy, false,
+        false, false, true);
 
     for (AttributeType t : suppressedAttributes.keySet())
     {
@@ -2999,24 +2885,29 @@
 
   /**
    * Creates a duplicate of this entry without any operational
-   * attributes that may be altered without impacting the information
-   * in this entry.
+   * attributes that may be altered without impacting the
+   * information in this entry.
+   * <p>
+   * TODO: this method is very specific to search result
+   * processing but we are forced to have it here due to tight
+   * coupling and performance reasons.
    *
-   * @param  typesOnly       Indicates whether to include attribute
-   *                         types only without values.
-   * @param  processVirtual  Indicates whether virtual attribute
-   *                         processing should be performed for the
-   *                         entry.
-   *
-   * @return  A duplicate of this entry that may be altered without
-   *          impacting the information in this entry and that does
-   *          not contain any operational attributes.
+   * @param typesOnly
+   *          Indicates whether to include attribute types only
+   *          without values.
+   * @param omitReal
+   *          Indicates whether to exclude real attributes.
+   * @param omitVirtual
+   *          Indicates whether to exclude virtual attributes.
+   * @return A duplicate of this entry that may be altered without
+   *         impacting the information in this entry and that does not
+   *         contain any operational attributes.
    */
   public Entry duplicateWithoutOperationalAttributes(
-                    boolean typesOnly, boolean processVirtual)
+      boolean typesOnly, boolean omitReal, boolean omitVirtual)
   {
     HashMap<ObjectClass,String> objectClassesCopy;
-    if (typesOnly)
+    if (typesOnly || omitReal)
     {
       objectClassesCopy = new HashMap<ObjectClass,String>(0);
     }
@@ -3029,7 +2920,8 @@
     HashMap<AttributeType,List<Attribute>> userAttrsCopy =
          new HashMap<AttributeType,List<Attribute>>(
               userAttributes.size());
-    if (typesOnly)
+
+    if (typesOnly && !omitReal)
     {
       // Make sure to include the objectClass attribute here because
       // it won't make it in otherwise.
@@ -3040,49 +2932,47 @@
       userAttrsCopy.put(ocType, ocList);
     }
 
-    deepCopy(userAttributes, userAttrsCopy, typesOnly);
+    deepCopy(userAttributes, userAttrsCopy, typesOnly, true,
+        omitReal, omitVirtual);
 
     HashMap<AttributeType,List<Attribute>> operationalAttrsCopy =
          new HashMap<AttributeType,List<Attribute>>(0);
 
-    for (AttributeType t : suppressedAttributes.keySet())
-    {
-      List<Attribute> attrList = suppressedAttributes.get(t);
-      if (! t.isOperational())
-      {
-        userAttributes.put(t, attrList);
-      }
-    }
-
     Entry e = new Entry(dn, objectClassesCopy, userAttrsCopy,
                         operationalAttrsCopy);
 
-    if (processVirtual)
-    {
-      e.processVirtualAttributes(false);
-    }
-
     return e;
   }
 
 
 
   /**
-   * Performs a deep copy from the source map to the target map.  In
-   * this case, the attributes in the list will be duplicates rather
-   * than re-using the same reference.  Virtual attributes will not be
-   * included when making the copy.
+   * Performs a deep copy from the source map to the target map.
+   * In this case, the attributes in the list will be duplicates
+   * rather than re-using the same reference.
    *
-   * @param  source      The source map from which to obtain the
-   *                     information.
-   * @param  target      The target map into which to place the
-   *                     copied information.
-   * @param  omitValues  Indicates whether to omit attribute values
-   *                     when processing.
+   * @param source
+   *          The source map from which to obtain the information.
+   * @param target
+   *          The target map into which to place the copied
+   *          information.
+   * @param omitValues
+   *          Indicates whether to omit attribute values when
+   *          processing.
+   * @param omitEmpty
+   *          Indicates whether to omit empty attributes when
+   *          processing.
+   * @param omitReal
+   *          Indicates whether to exclude real attributes.
+   * @param omitVirtual
+   *          Indicates whether to exclude virtual attributes.
    */
   private void deepCopy(Map<AttributeType,List<Attribute>> source,
                         Map<AttributeType,List<Attribute>> target,
-                        boolean omitValues)
+                        boolean omitValues,
+                        boolean omitEmpty,
+                        boolean omitReal,
+                        boolean omitVirtual)
   {
     for (AttributeType t : source.keySet())
     {
@@ -3092,12 +2982,19 @@
 
       for (Attribute a : sourceList)
       {
-        if (a.isVirtual())
+        if (omitReal && !a.isVirtual())
         {
           continue;
         }
-
-        if (omitValues)
+        else if (omitVirtual && a.isVirtual())
+        {
+          continue;
+        }
+        else if (omitEmpty && a.isEmpty())
+        {
+          continue;
+        }
+        else if (omitValues)
         {
           targetList.add(Attributes.empty(a));
         }
@@ -3107,7 +3004,7 @@
         }
       }
 
-      if (! targetList.isEmpty())
+      if (!targetList.isEmpty())
       {
         target.put(t, targetList);
       }
@@ -3569,133 +3466,6 @@
         }
       }
     }
-
-    virtualAttributeProcessingPerformed = true;
-  }
-
-
-
-  /**
-   * Indicates whether virtual attribute processing has been performed
-   * for this entry.
-   *
-   * @return  {@code true} if virtual attribute processing has been
-   *          performed for this entry, or {@code false} if not.
-   */
-  public boolean virtualAttributeProcessingPerformed()
-  {
-    return virtualAttributeProcessingPerformed;
-  }
-
-
-
-  /**
-   * Strips out all real attributes from this entry so that it only
-   * contains virtual attributes.
-   */
-  public void stripRealAttributes()
-  {
-    // The objectClass attribute will always be a real attribute.
-    objectClasses.clear();
-
-    Iterator<Map.Entry<AttributeType,List<Attribute>>>
-         attrListIterator = userAttributes.entrySet().iterator();
-    while (attrListIterator.hasNext())
-    {
-      Map.Entry<AttributeType,List<Attribute>> mapEntry =
-           attrListIterator.next();
-      Iterator<Attribute> attrIterator =
-           mapEntry.getValue().iterator();
-      while (attrIterator.hasNext())
-      {
-        Attribute a = attrIterator.next();
-        if (! a.isVirtual())
-        {
-          attrIterator.remove();
-        }
-      }
-
-      if (mapEntry.getValue().isEmpty())
-      {
-        attrListIterator.remove();
-      }
-    }
-
-    attrListIterator = operationalAttributes.entrySet().iterator();
-    while (attrListIterator.hasNext())
-    {
-      Map.Entry<AttributeType,List<Attribute>> mapEntry =
-           attrListIterator.next();
-      Iterator<Attribute> attrIterator =
-           mapEntry.getValue().iterator();
-      while (attrIterator.hasNext())
-      {
-        Attribute a = attrIterator.next();
-        if (! a.isVirtual())
-        {
-          attrIterator.remove();
-        }
-      }
-
-      if (mapEntry.getValue().isEmpty())
-      {
-        attrListIterator.remove();
-      }
-    }
-  }
-
-
-
-  /**
-   * Strips out all virtual attributes from this entry so that it only
-   * contains real attributes.
-   */
-  public void stripVirtualAttributes()
-  {
-    Iterator<Map.Entry<AttributeType,List<Attribute>>>
-         attrListIterator = userAttributes.entrySet().iterator();
-    while (attrListIterator.hasNext())
-    {
-      Map.Entry<AttributeType,List<Attribute>> mapEntry =
-           attrListIterator.next();
-      Iterator<Attribute> attrIterator =
-           mapEntry.getValue().iterator();
-      while (attrIterator.hasNext())
-      {
-        Attribute a = attrIterator.next();
-        if (a.isVirtual())
-        {
-          attrIterator.remove();
-        }
-      }
-
-      if (mapEntry.getValue().isEmpty())
-      {
-        attrListIterator.remove();
-      }
-    }
-
-    attrListIterator = operationalAttributes.entrySet().iterator();
-    while (attrListIterator.hasNext())
-    {
-      Map.Entry<AttributeType,List<Attribute>> mapEntry =
-           attrListIterator.next();
-      Iterator<Attribute> attrIterator =
-           mapEntry.getValue().iterator();
-      while (attrIterator.hasNext())
-      {
-        Attribute a = attrIterator.next();
-        if (a.isVirtual())
-        {
-          attrIterator.remove();
-        }
-      }
-
-      if (mapEntry.getValue().isEmpty())
-      {
-        attrListIterator.remove();
-      }
-    }
   }
 
 
@@ -5652,6 +5422,7 @@
    *
    * @return  A string representation of this protocol element.
    */
+  @Override
   public String toString()
   {
     StringBuilder buffer = new StringBuilder();
diff --git a/opends/src/server/org/opends/server/types/VirtualAttribute.java b/opends/src/server/org/opends/server/types/VirtualAttribute.java
index ef11179..3f9dbe6 100644
--- a/opends/src/server/org/opends/server/types/VirtualAttribute.java
+++ b/opends/src/server/org/opends/server/types/VirtualAttribute.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2008 Sun Microsystems, Inc.
+ *      Copyright 2008-2009 Sun Microsystems, Inc.
  */
 package org.opends.server.types;
 
@@ -131,18 +131,6 @@
 
 
   /**
-   * Retrieves the entry in which this virtual attribute exists.
-   *
-   * @return The entry in which this virtual attribute exists.
-   */
-  public Entry getEntry()
-  {
-    return entry;
-  }
-
-
-
-  /**
    * {@inheritDoc}
    */
   @Override
@@ -247,7 +235,7 @@
   public Iterator<AttributeValue> iterator()
   {
     Set<AttributeValue> values = provider.getValues(entry, rule);
-    return Collections.unmodifiableSet(values).iterator();
+    return values.iterator();
   }
 
 
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/core/SearchOperationTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/SearchOperationTestCase.java
index 521effb..adb8175 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/core/SearchOperationTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/SearchOperationTestCase.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2006-2008 Sun Microsystems, Inc.
+ *      Copyright 2006-2009 Sun Microsystems, Inc.
  */
 
 package org.opends.server.core;
@@ -37,18 +37,20 @@
 import org.opends.server.protocols.ldap.*;
 import org.opends.server.types.*;
 import org.opends.server.TestCaseUtils;
+import org.opends.server.util.ServerConstants;
 import org.opends.server.util.StaticUtils;
 import org.opends.server.controls.MatchedValuesFilter;
 import org.opends.server.controls.MatchedValuesControl;
 import org.opends.server.plugins.InvocationCounterPlugin;
 import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.*;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.LinkedHashSet;
+import java.util.LinkedList;
 import java.util.List;
 import java.net.Socket;
 import java.io.IOException;
@@ -418,6 +420,7 @@
     Entry resultEntry = searchInternalForSingleEntry(searchOperation);
 
     assertEquals(resultEntry.getObjectClasses().size(), 0);
+
     assertEquals(resultEntry.getUserAttributes().size(),
                  testEntry.getUserAttributes().size() + 1);
     assertEquals(resultEntry.getOperationalAttributes().size(), 0);
@@ -887,4 +890,257 @@
     assertEquals(searchOperation.getResultCode(), ResultCode.NO_SUCH_OBJECT);
     assertNotNull(searchOperation.getMatchedDN());
   }
+
+
+
+  /**
+   * Determines how attributes should be filtered in search operations.
+   */
+  private enum AttributeFilterType {
+    DEFAULT, WILDCARDS, ENUMERATED;
+  }
+
+
+
+  /**
+   * Returns test data for testSearchInternalAttributeFilters.
+   *
+   * @return The test data.
+   */
+  @DataProvider(name = "testSearchInternalAttributeFilters")
+  public Object[][] createTestSearchInternalAttributeFiltersData()
+  {
+    // It was quicker to cut n paste...
+    return new Object[][] {
+        {AttributeFilterType.DEFAULT,     false, false, false},
+        {AttributeFilterType.DEFAULT,     false, false, true},
+        {AttributeFilterType.DEFAULT,     false, true,  false},
+        {AttributeFilterType.DEFAULT,     false, true,  true},
+        {AttributeFilterType.DEFAULT,     true,  false, false},
+        {AttributeFilterType.DEFAULT,     true,  false, true},
+        {AttributeFilterType.DEFAULT,     true,  true,  false},
+        {AttributeFilterType.DEFAULT,     true,  true,  true},
+        {AttributeFilterType.WILDCARDS,   false, false, false},
+        {AttributeFilterType.WILDCARDS,   false, false, true},
+        {AttributeFilterType.WILDCARDS,   false, true,  false},
+        {AttributeFilterType.WILDCARDS,   false, true,  true},
+        {AttributeFilterType.WILDCARDS,   true,  false, false},
+        {AttributeFilterType.WILDCARDS,   true,  false, true},
+        {AttributeFilterType.WILDCARDS,   true,  true,  false},
+        {AttributeFilterType.WILDCARDS,   true,  true,  true},
+        {AttributeFilterType.ENUMERATED,  false, false, false},
+        {AttributeFilterType.ENUMERATED,  false, false, true},
+        {AttributeFilterType.ENUMERATED,  false, true,  false},
+        {AttributeFilterType.ENUMERATED,  false, true,  true},
+        {AttributeFilterType.ENUMERATED,  true,  false, false},
+        {AttributeFilterType.ENUMERATED,  true,  false, true},
+        {AttributeFilterType.ENUMERATED,  true,  true,  false},
+        {AttributeFilterType.ENUMERATED,  true,  true,  true},
+    };
+  }
+
+
+
+  /**
+   * Tests that attribute filtering is performed correctly for real and
+   * virtual attributes when various combinations of typesOnly, and the
+   * real-attributes-only and virtual-attributes-only controls are used
+   * (issues 3446 and 3726).
+   *
+   * @param filterType
+   *          Specifies how attributes should be filtered.
+   * @param typesOnly
+   *          Strip attribute values.
+   * @param stripVirtualAttributes
+   *          Strip virtual attributes.
+   * @param stripRealAttributes
+   *          Strip real attributes.
+   * @throws Exception
+   *           If an unexpected problem occurs.
+   */
+  @Test(dataProvider = "testSearchInternalAttributeFilters")
+  public void testSearchInternalAttributeFilters(
+      AttributeFilterType filterType, boolean typesOnly,
+      boolean stripVirtualAttributes, boolean stripRealAttributes)
+      throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    // Real attributes (these are all user attributes).
+    List<String> realAttrTypes =
+        Arrays.asList("objectclass", "uid", "cn", "sn", "givenname",
+            "userpassword");
+
+    // Virtual attributes (these are all operational attributes).
+    List<String> virtualAttrTypes =
+        Arrays.asList("numsubordinates", "hassubordinates",
+            "subschemasubentry", "entrydn", "ismemberof");
+
+    String userDNString = "uid=test.user,o=test";
+    DN userDN = DN.decode(userDNString);
+
+    TestCaseUtils.addEntry("dn: " + userDNString,
+        "objectClass: top",
+        "objectClass: person",
+        "objectClass: organizationalPerson",
+        "objectClass: inetOrgPerson",
+        "uid: test.user",
+        "givenName: Test",
+        "sn: User",
+        "cn: Test User",
+        "userPassword: password");
+
+    Entry userEntry = DirectoryServer.getEntry(userDN);
+    assertNotNull(userEntry);
+
+    LinkedHashSet<String> attributes = new LinkedHashSet<String>();
+    switch (filterType)
+    {
+    case DEFAULT:
+      // Only user attributes.
+      attributes = null;
+      break;
+    case WILDCARDS:
+      attributes.add("*");
+      attributes.add("+");
+      break;
+    case ENUMERATED:
+      attributes.addAll(realAttrTypes);
+      attributes.addAll(virtualAttrTypes);
+      break;
+    }
+
+    List<Control> controls = new LinkedList<Control>();
+
+    if (stripRealAttributes)
+    {
+      controls.add(new Control(ServerConstants.OID_VIRTUAL_ATTRS_ONLY,
+          false));
+    }
+
+    if (stripVirtualAttributes)
+    {
+      controls.add(new Control(ServerConstants.OID_REAL_ATTRS_ONLY,
+          false));
+    }
+
+    InternalClientConnection conn =
+        InternalClientConnection.getRootConnection();
+
+    InternalSearchOperation search =
+        conn.processSearch(userDNString, SearchScope.BASE_OBJECT,
+            DereferencePolicy.NEVER_DEREF_ALIASES, 0, // Size limit
+            0, // Time limit
+            typesOnly, // Types only
+            "(objectClass=*)", attributes, controls, null);
+
+    assertEquals(search.getResultCode(), ResultCode.SUCCESS);
+
+    LinkedList<SearchResultEntry> entries = search.getSearchEntries();
+    assertEquals(entries.size(), 1);
+
+    Entry entry = entries.getFirst();
+    assertEquals(entry.getDN(), userDN);
+
+    // Check real attributes.
+    List<String> messages = new LinkedList<String>();
+    for (String attrType : realAttrTypes)
+    {
+      List<Attribute> attrList = entry.getAttribute(attrType);
+
+      if (stripRealAttributes)
+      {
+        if (attrList != null)
+        {
+          messages.add("Unexpected real attribute: " + attrType);
+        }
+      }
+      else
+      {
+        if (attrList == null)
+        {
+          messages.add("Missing real attribute: " + attrType);
+        }
+        else
+        {
+          Attribute attr = attrList.get(0);
+          if (typesOnly)
+          {
+            if (!attr.isEmpty())
+            {
+              messages.add("Unexpected non-empty real attribute: "
+                  + attrType);
+            }
+          }
+          else
+          {
+            if (attr.isEmpty())
+            {
+              messages.add("Unexpected empty real attribute: "
+                  + attrType);
+            }
+          }
+        }
+      }
+    }
+
+    // Check virtual (operational) attributes.
+    for (String attrType : virtualAttrTypes)
+    {
+      List<Attribute> attrList = entry.getAttribute(attrType);
+
+      if (stripVirtualAttributes)
+      {
+        if (attrList != null)
+        {
+          messages.add("Unexpected virtual attribute: " + attrType);
+        }
+      }
+      else if (filterType == AttributeFilterType.DEFAULT)
+      {
+        if (attrList != null)
+        {
+          messages.add("Unexpected operational attribute: " + attrType);
+        }
+      }
+      else if (attrType.equals("ismemberof"))
+      {
+        // isMemberOf should never be returned as user is not in any
+        // groups.
+        if (attrList != null)
+        {
+          messages.add("Unexpected isMemberOf attribute");
+        }
+      }
+      else
+      {
+        if (attrList == null)
+        {
+          messages.add("Missing virtual attribute: " + attrType);
+        }
+        else
+        {
+          Attribute attr = attrList.get(0);
+          if (typesOnly)
+          {
+            if (!attr.isEmpty())
+            {
+              messages.add("Unexpected non-empty virtual attribute: "
+                  + attrType);
+            }
+          }
+          else
+          {
+            if (attr.isEmpty())
+            {
+              messages.add("Unexpected empty virtual attribute: "
+                  + attrType);
+            }
+          }
+        }
+      }
+    }
+
+    assertTrue(messages.isEmpty(), "Entry invalid: " + messages);
+  }
 }
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/EntryDNVirtualAttributeProviderTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/EntryDNVirtualAttributeProviderTestCase.java
index 1b82082..cfdf382 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/EntryDNVirtualAttributeProviderTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/EntryDNVirtualAttributeProviderTestCase.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2008 Sun Microsystems, Inc.
+ *      Copyright 2008-2009 Sun Microsystems, Inc.
  */
 package org.opends.server.extensions;
 
@@ -32,6 +32,7 @@
 import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Set;
 
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.DataProvider;
@@ -560,7 +561,7 @@
                   VirtualAttributeCfgDefn.ConflictBehavior.
                        VIRTUAL_OVERRIDES_REAL);
 
-    LinkedHashSet<AttributeValue> values = provider.getValues(entry, rule);
+    Set<AttributeValue> values = provider.getValues(entry, rule);
     assertNotNull(values);
     assertEquals(values.size(), 1);
     assertTrue(values.contains(new AttributeValue(entryDNType, "o=test")));
@@ -1104,7 +1105,7 @@
                                      0, false, filter, null, null);
     LocalBackendSearchOperation localSearch =
       new LocalBackendSearchOperation(searchOperation);
-    
+
     provider.processSearch(rule, localSearch);
 
     if (shouldMatch)
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/EntryUUIDVirtualAttributeProviderTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/EntryUUIDVirtualAttributeProviderTestCase.java
index 2c674e2..3fba399 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/EntryUUIDVirtualAttributeProviderTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/EntryUUIDVirtualAttributeProviderTestCase.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2008 Sun Microsystems, Inc.
+ *      Copyright 2008-2009 Sun Microsystems, Inc.
  */
 package org.opends.server.extensions;
 
@@ -32,6 +32,7 @@
 import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Set;
 import java.util.UUID;
 
 import org.testng.annotations.BeforeClass;
@@ -574,7 +575,7 @@
                   VirtualAttributeCfgDefn.ConflictBehavior.
                        VIRTUAL_OVERRIDES_REAL);
 
-    LinkedHashSet<AttributeValue> values = provider.getValues(entry, rule);
+    Set<AttributeValue> values = provider.getValues(entry, rule);
     assertNotNull(values);
     assertEquals(values.size(), 1);
     assertTrue(values.contains(new AttributeValue(entryUUIDType, uuidString)));
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/IsMemberOfVirtualAttributeProviderTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/IsMemberOfVirtualAttributeProviderTestCase.java
index 93736f1..30f15a4 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/IsMemberOfVirtualAttributeProviderTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/IsMemberOfVirtualAttributeProviderTestCase.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2008 Sun Microsystems, Inc.
+ *      Copyright 2008-2009 Sun Microsystems, Inc.
  */
 package org.opends.server.extensions;
 
@@ -31,7 +31,6 @@
 import java.util.Collections;
 import java.util.LinkedHashSet;
 import java.util.LinkedList;
-import java.util.List;
 
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.DataProvider;
@@ -49,7 +48,6 @@
 import org.opends.server.types.ByteString;
 import org.opends.server.types.ByteStringFactory;
 import org.opends.server.types.ConditionResult;
-import org.opends.server.types.Control;
 import org.opends.server.types.DereferencePolicy;
 import org.opends.server.types.DN;
 import org.opends.server.types.Entry;
@@ -61,8 +59,6 @@
 
 import static org.testng.Assert.*;
 
-import static org.opends.server.util.ServerConstants.*;
-
 
 
 /**
@@ -1107,8 +1103,10 @@
     InternalClientConnection conn =
          InternalClientConnection.getRootConnection();
     InternalSearchOperation searchOperation =
-         new InternalSearchOperation(conn, conn.nextOperationID(),
-                                     conn.nextMessageID(), null,
+         new InternalSearchOperation(conn,
+                                     InternalClientConnection.nextOperationID(),
+                                     InternalClientConnection.nextMessageID(),
+                                     null,
                                      DN.decode("o=test"),
                                      SearchScope.WHOLE_SUBTREE,
                                      DereferencePolicy.NEVER_DEREF_ALIASES, 0,
@@ -1232,8 +1230,10 @@
     InternalClientConnection conn =
          InternalClientConnection.getRootConnection();
     InternalSearchOperation searchOperation =
-         new InternalSearchOperation(conn, conn.nextOperationID(),
-                                     conn.nextMessageID(), null,
+         new InternalSearchOperation(conn,
+                                     InternalClientConnection.nextOperationID(),
+                                     InternalClientConnection.nextMessageID(),
+                                     null,
                                      DN.decode("o=test"),
                                      SearchScope.WHOLE_SUBTREE,
                                      DereferencePolicy.NEVER_DEREF_ALIASES, 0,
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/SubschemaSubentryVirtualAttributeProviderTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/SubschemaSubentryVirtualAttributeProviderTestCase.java
index c3fba30..518524b 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/SubschemaSubentryVirtualAttributeProviderTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/SubschemaSubentryVirtualAttributeProviderTestCase.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2008 Sun Microsystems, Inc.
+ *      Copyright 2008-2009 Sun Microsystems, Inc.
  */
 package org.opends.server.extensions;
 
@@ -32,6 +32,7 @@
 import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Set;
 
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.DataProvider;
@@ -45,9 +46,6 @@
 import org.opends.server.types.Attribute;
 import org.opends.server.types.AttributeType;
 import org.opends.server.types.AttributeValue;
-import org.opends.server.types.ByteString;
-import org.opends.server.types.ByteStringFactory;
-import org.opends.server.types.ConditionResult;
 import org.opends.server.types.Control;
 import org.opends.server.types.DereferencePolicy;
 import org.opends.server.types.DN;
@@ -559,7 +557,7 @@
                   VirtualAttributeCfgDefn.ConflictBehavior.
                        VIRTUAL_OVERRIDES_REAL);
 
-    LinkedHashSet<AttributeValue> values = provider.getValues(entry, rule);
+    Set<AttributeValue> values = provider.getValues(entry, rule);
     assertNotNull(values);
     assertEquals(values.size(), 1);
     assertTrue(values.contains(new AttributeValue(subschemaSubentryType,
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/VirtualStaticGroupTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/VirtualStaticGroupTestCase.java
index bcd5967..704d06d 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/VirtualStaticGroupTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/VirtualStaticGroupTestCase.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2008 Sun Microsystems, Inc.
+ *      Copyright 2008-2009 Sun Microsystems, Inc.
  */
 package org.opends.server.extensions;
 
@@ -32,6 +32,7 @@
 import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Set;
 
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.DataProvider;
@@ -658,7 +659,7 @@
 
     assertTrue(provider.isMultiValued());
 
-    LinkedHashSet<AttributeValue> values = provider.getValues(entry, rule);
+    Set<AttributeValue> values = provider.getValues(entry, rule);
     assertNotNull(values);
     assertFalse(values.isEmpty());
     assertTrue(provider.hasValue(entry, rule));
@@ -737,7 +738,7 @@
 
     assertTrue(provider.isMultiValued());
 
-    LinkedHashSet<AttributeValue> values = provider.getValues(entry, rule);
+    Set<AttributeValue> values = provider.getValues(entry, rule);
     assertNotNull(values);
     assertTrue(values.isEmpty());
     assertFalse(provider.hasValue(entry, rule));
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/types/VirtualAttributeTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/types/VirtualAttributeTestCase.java
index 95527d8..7c75fe8 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/types/VirtualAttributeTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/types/VirtualAttributeTestCase.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2008 Sun Microsystems, Inc.
+ *      Copyright 2008-2009 Sun Microsystems, Inc.
  */
 package org.opends.server.types;
 
@@ -102,10 +102,6 @@
   public void testGetters()
          throws Exception
   {
-    assertNotNull(virtualAttribute.getEntry());
-    assertEquals(virtualAttribute.getEntry().getDN(),
-                 DN.decode("o=test"));
-
     assertEquals(virtualAttribute.getVirtualAttributeRule(),
                  virtualAttributeRule);
 

--
Gitblit v1.10.0