From 702b6351ed3207102d0bf4a152f45cf74452eeb3 Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Mon, 12 Dec 2011 17:33:59 +0000
Subject: [PATCH] Additional fixes for OPENDJ-381: Implement LDIF diff, patch, and search API support in the SDK

---
 opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/LDIF.java |  247 +++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 237 insertions(+), 10 deletions(-)

diff --git a/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/LDIF.java b/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/LDIF.java
index d3061ec..17d8b4d 100644
--- a/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/LDIF.java
+++ b/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/LDIF.java
@@ -28,11 +28,18 @@
 
 
 
+import static org.forgerock.opendj.ldap.CoreMessages.REJECTED_CHANGE_FAIL_DELETE;
+import static org.forgerock.opendj.ldap.CoreMessages.REJECTED_CHANGE_FAIL_MODIFY;
+import static org.forgerock.opendj.ldap.CoreMessages.REJECTED_CHANGE_FAIL_MODIFYDN;
+
 import java.io.IOException;
 import java.util.*;
 
 import org.forgerock.opendj.ldap.*;
+import org.forgerock.opendj.ldap.controls.SubtreeDeleteRequestControl;
 import org.forgerock.opendj.ldap.requests.*;
+import org.forgerock.opendj.ldap.schema.AttributeUsage;
+import org.forgerock.opendj.ldap.schema.Schema;
 
 
 
@@ -66,7 +73,7 @@
    * @throws IOException
    *           If an unexpected IO error occurred.
    */
-  public static final ChangeRecordWriter copyTo(final ChangeRecordReader input,
+  public static ChangeRecordWriter copyTo(final ChangeRecordReader input,
       final ChangeRecordWriter output) throws IOException
   {
     while (input.hasNext())
@@ -90,7 +97,7 @@
    * @throws IOException
    *           If an unexpected IO error occurred.
    */
-  public static final EntryWriter copyTo(final EntryReader input,
+  public static EntryWriter copyTo(final EntryReader input,
       final EntryWriter output) throws IOException
   {
     while (input.hasNext())
@@ -278,7 +285,7 @@
   public static EntryReader patch(final EntryReader input,
       final ChangeRecordReader patch) throws IOException
   {
-    return patch(input, patch, RejectedChangeListener.OVERWRITE);
+    return patch(input, patch, RejectedChangeRecordListener.OVERWRITE);
   }
 
 
@@ -305,8 +312,8 @@
    *           If an unexpected IO error occurred.
    */
   public static EntryReader patch(final EntryReader input,
-      final ChangeRecordReader patch, final RejectedChangeListener listener)
-      throws IOException
+      final ChangeRecordReader patch,
+      final RejectedChangeRecordListener listener) throws IOException
   {
     final SortedMap<DN, Entry> entries = readEntries(input);
 
@@ -353,7 +360,9 @@
               {
                 try
                 {
-                  listener.handleMissingEntry(change);
+                  listener.handleRejectedChangeRecord(change,
+                      REJECTED_CHANGE_FAIL_DELETE.get(change.getName()
+                          .toString()));
                 }
                 catch (final DecodeException e)
                 {
@@ -362,7 +371,24 @@
               }
               else
               {
-                entries.remove(change.getName());
+                try
+                {
+                  if (change.getControl(SubtreeDeleteRequestControl.DECODER,
+                      new DecodeOptions()) != null)
+                  {
+                    entries.subMap(change.getName(),
+                        change.getName().child(RDN.maxValue())).clear();
+                  }
+                  else
+                  {
+                    entries.remove(change.getName());
+                  }
+                }
+                catch (final DecodeException e)
+                {
+                  return e;
+                }
+
               }
               return null;
             }
@@ -377,7 +403,9 @@
               {
                 try
                 {
-                  listener.handleMissingEntry(change);
+                  listener.handleRejectedChangeRecord(change,
+                      REJECTED_CHANGE_FAIL_MODIFYDN.get(change.getName()
+                          .toString()));
                 }
                 catch (final DecodeException e)
                 {
@@ -404,7 +432,9 @@
                 // avoid cases where the renamed subtree overlaps.
                 final SortedMap<DN, Entry> renamedEntries = new TreeMap<DN, Entry>();
                 final Iterator<Map.Entry<DN, Entry>> i = entries
-                    .tailMap(change.getName()).entrySet().iterator();
+                    .subMap(change.getName(),
+                        change.getName().child(RDN.maxValue())).entrySet()
+                    .iterator();
                 while (i.hasNext())
                 {
                   final Map.Entry<DN, Entry> e = i.next();
@@ -466,7 +496,9 @@
               {
                 try
                 {
-                  listener.handleMissingEntry(change);
+                  listener.handleRejectedChangeRecord(change,
+                      REJECTED_CHANGE_FAIL_MODIFY.get(change.getName()
+                          .toString()));
                 }
                 catch (final DecodeException e)
                 {
@@ -551,6 +583,201 @@
 
 
 
+  /**
+   * Returns a filtered view of {@code input} containing only those entries
+   * which match the search base DN, scope, and filtered defined in
+   * {@code search}. In addition, returned entries will be filtered according to
+   * any attribute filtering criteria defined in the search request.
+   * <p>
+   * The filter and attribute descriptions will be decoded using the default
+   * schema.
+   *
+   * @param input
+   *          The entry reader containing the set of entries to be filtered.
+   * @param search
+   *          The search request defining the filtering criteria.
+   * @return A filtered view of {@code input} containing only those entries
+   *         which match the provided search request.
+   */
+  public static EntryReader search(final EntryReader input,
+      final SearchRequest search)
+  {
+    return search(input, search, Schema.getDefaultSchema());
+  }
+
+
+
+  /**
+   * Returns a filtered view of {@code input} containing only those entries
+   * which match the search base DN, scope, and filtered defined in
+   * {@code search}. In addition, returned entries will be filtered according to
+   * any attribute filtering criteria defined in the search request.
+   * <p>
+   * The filter and attribute descriptions will be decoded using the provided
+   * schema.
+   *
+   * @param input
+   *          The entry reader containing the set of entries to be filtered.
+   * @param search
+   *          The search request defining the filtering criteria.
+   * @param schema
+   *          The schema which should be used to decode the search filter and
+   *          attribute descriptions.
+   * @return A filtered view of {@code input} containing only those entries
+   *         which match the provided search request.
+   */
+  public static EntryReader search(final EntryReader input,
+      final SearchRequest search, final Schema schema)
+  {
+    final Matcher matcher = search.getFilter().matcher(schema);
+
+    return new EntryReader()
+    {
+      private Entry nextEntry = null;
+      private int entryCount = 0;
+
+
+
+      public void close() throws IOException
+      {
+        input.close();
+      }
+
+
+
+      public boolean hasNext() throws IOException
+      {
+        if (nextEntry == null)
+        {
+          final int sizeLimit = search.getSizeLimit();
+          if (sizeLimit == 0 || entryCount < sizeLimit)
+          {
+            final DN baseDN = search.getName();
+            final SearchScope scope = search.getScope();
+            while (input.hasNext())
+            {
+              final Entry entry = input.readEntry();
+              if (entry.getName().isInScopeOf(baseDN, scope)
+                  && matcher.matches(entry).toBoolean())
+              {
+                nextEntry = filterEntry(entry);
+                break;
+              }
+            }
+          }
+        }
+        return nextEntry != null;
+      }
+
+
+
+      public Entry readEntry() throws IOException, NoSuchElementException
+      {
+        if (hasNext())
+        {
+          final Entry entry = nextEntry;
+          nextEntry = null;
+          entryCount++;
+          return entry;
+        }
+        else
+        {
+          throw new NoSuchElementException();
+        }
+      }
+
+
+
+      private Entry filterEntry(final Entry entry)
+      {
+        // TODO: rename attributes; move functionality to Entries.
+        if (search.getAttributes().isEmpty())
+        {
+          if (search.isTypesOnly())
+          {
+            final Entry filteredEntry = new LinkedHashMapEntry(entry.getName());
+            for (final Attribute attribute : entry.getAllAttributes())
+            {
+              filteredEntry.addAttribute(Attributes.emptyAttribute(attribute
+                  .getAttributeDescription()));
+            }
+            return filteredEntry;
+          }
+          else
+          {
+            return entry;
+          }
+        }
+        else
+        {
+          final Entry filteredEntry = new LinkedHashMapEntry(entry.getName());
+          for (final String atd : search.getAttributes())
+          {
+            if (atd.equals("*"))
+            {
+              for (final Attribute attribute : entry.getAllAttributes())
+              {
+                if (attribute.getAttributeDescription().getAttributeType()
+                    .getUsage() == AttributeUsage.USER_APPLICATIONS)
+                {
+                  if (search.isTypesOnly())
+                  {
+                    filteredEntry.addAttribute(Attributes
+                        .emptyAttribute(attribute.getAttributeDescription()));
+                  }
+                  else
+                  {
+                    filteredEntry.addAttribute(attribute);
+                  }
+                }
+              }
+            }
+            else if (atd.equals("+"))
+            {
+              for (final Attribute attribute : entry.getAllAttributes())
+              {
+                if (attribute.getAttributeDescription().getAttributeType()
+                    .getUsage() != AttributeUsage.USER_APPLICATIONS)
+                {
+                  if (search.isTypesOnly())
+                  {
+                    filteredEntry.addAttribute(Attributes
+                        .emptyAttribute(attribute.getAttributeDescription()));
+                  }
+                  else
+                  {
+                    filteredEntry.addAttribute(attribute);
+                  }
+                }
+              }
+            }
+            else
+            {
+              final AttributeDescription ad = AttributeDescription.valueOf(atd,
+                  schema);
+              for (final Attribute attribute : entry.getAllAttributes(ad))
+              {
+                if (search.isTypesOnly())
+                {
+                  filteredEntry.addAttribute(Attributes
+                      .emptyAttribute(attribute.getAttributeDescription()));
+                }
+                else
+                {
+                  filteredEntry.addAttribute(attribute);
+                }
+              }
+            }
+          }
+          return filteredEntry;
+        }
+      }
+
+    };
+  }
+
+
+
   private static SortedMap<DN, Entry> readEntries(final EntryReader reader)
       throws IOException
   {

--
Gitblit v1.10.0