From 30f5175327333dcfaaeafb02560697bcdce6166d Mon Sep 17 00:00:00 2001
From: neil_a_wilson <neil_a_wilson@localhost>
Date: Mon, 30 Apr 2007 16:24:15 +0000
Subject: [PATCH] Implement support for the virtual list view (VLV) control as defined in draft-ietf-ldapext-ldapv3-vlv.  This can be used to retrieve a specified page of a search result set.  Any result set that can be used with server-side sorting can also be used with VLV.  The ldapsearch tool has also been updated to support this control.

---
 opends/src/server/org/opends/server/backends/jeb/EntryIDSetSorter.java |  181 ++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 176 insertions(+), 5 deletions(-)

diff --git a/opends/src/server/org/opends/server/backends/jeb/EntryIDSetSorter.java b/opends/src/server/org/opends/server/backends/jeb/EntryIDSetSorter.java
index 768b3b2..5b685a5 100644
--- a/opends/src/server/org/opends/server/backends/jeb/EntryIDSetSorter.java
+++ b/opends/src/server/org/opends/server/backends/jeb/EntryIDSetSorter.java
@@ -28,13 +28,21 @@
 
 
 
+import java.util.Map;
+import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.TreeMap;
 
+import org.opends.server.controls.VLVRequestControl;
+import org.opends.server.controls.VLVResponseControl;
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.core.SearchOperation;
+import org.opends.server.protocols.ldap.LDAPResultCode;
+import org.opends.server.types.AttributeValue;
 import org.opends.server.types.DirectoryException;
 import org.opends.server.types.DN;
 import org.opends.server.types.Entry;
+import org.opends.server.types.ResultCode;
 import org.opends.server.types.SearchFilter;
 import org.opends.server.types.SearchScope;
 import org.opends.server.types.SortOrder;
@@ -60,6 +68,8 @@
    * @param  entryIDSet       The entry ID set to be sorted.
    * @param  searchOperation  The search operation being processed.
    * @param  sortOrder        The sort order to use for the entry ID set.
+   * @param  vlvRequest       The VLV request control included in the search
+   *                          request, or {@code null} if there was none.
    *
    * @return  A new entry ID set which is a sorted representation of the
    *          provided set using the given sort order.
@@ -69,7 +79,8 @@
   public static EntryIDSet sort(EntryContainer entryContainer,
                                 EntryIDSet entryIDSet,
                                 SearchOperation searchOperation,
-                                SortOrder sortOrder)
+                                SortOrder sortOrder,
+                                VLVRequestControl vlvRequest)
          throws DirectoryException
   {
     if (! entryIDSet.isDefined())
@@ -107,11 +118,171 @@
       }
     }
 
-    long[] sortedIDs = new long[sortMap.size()];
-    int i=0;
-    for (EntryID id : sortMap.values())
+
+    // See if there is a VLV request to further pare down the set of results,
+    // and if there is where it should be processed by offset or assertion
+    // value.
+    long[] sortedIDs;
+    if (vlvRequest != null)
     {
-      sortedIDs[i++] = id.longValue();
+      int beforeCount = vlvRequest.getBeforeCount();
+      int afterCount  = vlvRequest.getAfterCount();
+
+      if (vlvRequest.getTargetType() == VLVRequestControl.TYPE_TARGET_BYOFFSET)
+      {
+        int targetOffset = vlvRequest.getOffset();
+        int listOffset = targetOffset - 1; // VLV offsets start at 1, not 0.
+        int startPos = listOffset - beforeCount;
+        if (startPos < 0)
+        {
+          searchOperation.addResponseControl(
+               new VLVResponseControl(targetOffset, sortMap.size(),
+                                      LDAPResultCode.OFFSET_RANGE_ERROR));
+
+          int    msgID   = MSGID_ENTRYIDSORTER_NEGATIVE_START_POS;
+          String message = getMessage(msgID);
+          throw new DirectoryException(ResultCode.VIRTUAL_LIST_VIEW_ERROR,
+                                       message, msgID);
+        }
+        else if (startPos >= sortMap.size())
+        {
+          searchOperation.addResponseControl(
+               new VLVResponseControl(targetOffset, sortMap.size(),
+                                      LDAPResultCode.OFFSET_RANGE_ERROR));
+
+          int    msgID   = MSGID_ENTRYIDSORTER_OFFSET_TOO_LARGE;
+          String message = getMessage(msgID, vlvRequest.getOffset(),
+                                      sortMap.size());
+          throw new DirectoryException(ResultCode.VIRTUAL_LIST_VIEW_ERROR,
+                                       message, msgID);
+        }
+
+        int count = 1 + beforeCount + afterCount;
+        sortedIDs = new long[count];
+
+        int treePos = 0;
+        int arrayPos = 0;
+        Iterator<EntryID> idIterator = sortMap.values().iterator();
+        while (idIterator.hasNext())
+        {
+          EntryID id = idIterator.next();
+          if (treePos++ < startPos)
+          {
+            continue;
+          }
+
+          sortedIDs[arrayPos++] = id.longValue();
+          if (arrayPos >= count)
+          {
+            break;
+          }
+        }
+
+        if (arrayPos < count)
+        {
+          // We don't have enough entries in the set to meet the requested
+          // page size, so we'll need to shorten the array.
+          long[] newIDArray = new long[arrayPos];
+          System.arraycopy(sortedIDs, 0, newIDArray, 0, arrayPos);
+          sortedIDs = newIDArray;
+        }
+
+        searchOperation.addResponseControl(
+             new VLVResponseControl(targetOffset, sortMap.size(),
+                                    LDAPResultCode.SUCCESS));
+      }
+      else
+      {
+        AttributeValue assertionValue = new
+             AttributeValue(sortOrder.getSortKeys()[0].getAttributeType(),
+                            vlvRequest.getGreaterThanOrEqualAssertion());
+
+        boolean targetFound     = false;
+        int targetOffset        = 0;
+        int includedBeforeCount = 0;
+        int includedAfterCount  = 0;
+        int listSize            = 0;
+        LinkedList<EntryID> idList = new LinkedList<EntryID>();
+        Iterator<Map.Entry<SortValues,EntryID>> mapIterator =
+             sortMap.entrySet().iterator();
+        while (mapIterator.hasNext())
+        {
+          Map.Entry<SortValues,EntryID> entry = mapIterator.next();
+          SortValues sortValues = entry.getKey();
+          EntryID id = entry.getValue();
+
+          if (targetFound)
+          {
+            idList.add(id);
+            listSize++;
+            includedAfterCount++;
+            if (includedAfterCount >= afterCount)
+            {
+              break;
+            }
+          }
+          else
+          {
+            targetFound = (sortValues.compareTo(assertionValue) >= 0);
+            targetOffset++;
+
+            if (targetFound)
+            {
+              idList.add(id);
+              listSize++;
+            }
+            else if (beforeCount > 0)
+            {
+              if (beforeCount > 0)
+              {
+                idList.add(id);
+                includedBeforeCount++;
+                if (includedBeforeCount > beforeCount)
+                {
+                  idList.removeFirst();
+                  includedBeforeCount--;
+                }
+                else
+                {
+                  listSize++;
+                }
+              }
+            }
+          }
+        }
+
+        if (! targetFound)
+        {
+          searchOperation.addResponseControl(
+               new VLVResponseControl(sortMap.size(), sortMap.size(),
+                                      LDAPResultCode.OFFSET_RANGE_ERROR));
+
+          int    msgID   = MSGID_ENTRYIDSORTER_TARGET_VALUE_NOT_FOUND;
+          String message = getMessage(msgID);
+          throw new DirectoryException(ResultCode.VIRTUAL_LIST_VIEW_ERROR,
+                                       message, msgID);
+        }
+
+        sortedIDs = new long[listSize];
+        Iterator<EntryID> idIterator = idList.iterator();
+        for (int i=0; i < listSize; i++)
+        {
+          sortedIDs[i] = idIterator.next().longValue();
+        }
+
+        searchOperation.addResponseControl(
+             new VLVResponseControl(targetOffset, sortMap.size(),
+                                    LDAPResultCode.SUCCESS));
+      }
+    }
+    else
+    {
+      sortedIDs = new long[sortMap.size()];
+      int i=0;
+      for (EntryID id : sortMap.values())
+      {
+        sortedIDs[i++] = id.longValue();
+      }
     }
 
     return new EntryIDSet(sortedIDs, 0, sortedIDs.length);

--
Gitblit v1.10.0