>From 5c9c5d63620ea47810023e805f29ce1246ca0d50 Mon Sep 17 00:00:00 2001
From: Dmitri Pal <dpal@redhat.com>
Date: Thu, 10 Sep 2009 10:42:43 -0400
Subject: [PATCH] COLLECTION Improvements to copy functions

This patch adds better options for
copying collections in flat mode.
It allows caller of the interface
to control prefixing of the fields
when one collection is appended to another.
It also avoids creating prefixes when the
collection is simply copied in flat mode.
Also for ELAPI I realized that the most efficient
way to deal with the "resolved" event
(event where all templeted values are actually replaced
with the real values) is to add a callback
capability to a copy collection function
so that the callback can be used to modify
the data (resolve it) while the copy operation
is in progress. This approach eliminates the need
for separate set of lookups after the event
is already copied.
---
 common/collection/collection.c       |  182 ++++++++++++++++++++++++++--------
 common/collection/collection.h       |   30 ++++--
 common/collection/collection_tools.c |    2 +-
 common/collection/collection_ut.c    |   28 +++++
 4 files changed, 191 insertions(+), 51 deletions(-)

diff --git a/common/collection/collection.c b/common/collection/collection.c
index 3075eff..e875237 100644
--- a/common/collection/collection.c
+++ b/common/collection/collection.c
@@ -86,6 +86,8 @@ struct col_copy {
     struct path_data *current_path;
     char *given_name;
     int given_len;
+    col_copy_cb copy_cb;
+    void *ext_data;
 };
 
 /******************** FUNCTION DECLARATIONS ****************************/
@@ -875,7 +877,7 @@ int col_remove_item(struct collection_item *ci,
 }
 
 /* Remove item (property) from current collection.
- * Just a simple wropper.
+ * Just a simple wrapper.
  */
 int col_remove_item_from_current(struct collection_item *ci,
                                  int disposition,
@@ -1015,6 +1017,64 @@ static int col_insert_property_with_ref_int(struct collection_item *collection,
     return EOK;
 }
 
+/* Special function used to copy item from one
+ * collection to another using caller's callback.
+ */
+static int col_copy_item_with_cb(struct collection_item *collection,
+                                 const char *property,
+                                 int type,
+                                 const void *data,
+                                 int length,
+                                 col_copy_cb copy_cb,
+                                 void *ext_data)
+{
+    struct collection_item *item = NULL;
+    int skip = 0;
+    int error = EOK;
+
+    TRACE_FLOW_STRING("col_copy_item_with_cb", "Entry point.");
+
+    /* Create a new property out of the given parameters */
+    error = col_allocate_item(&item, property, data, length, type);
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to allocate item", error);
+        return error;
+    }
+
+    /* Call callback if any */
+    if (copy_cb) {
+        TRACE_INFO_STRING("Calling callback for item:", item->property);
+        error = copy_cb(item, ext_data, &skip);
+        if (error) {
+            TRACE_ERROR_NUMBER("Callback failed", error);
+            col_delete_item(item);
+            return error;
+        }
+    }
+
+    /* Are we told to skip this item? */
+    if (skip) col_delete_item(item);
+    else {
+        /* Insted property into the collection */
+        error = col_insert_item(collection,
+                                NULL,
+                                item,
+                                COL_DSP_END,
+                                NULL,
+                                0,
+                                0);
+        if (error) {
+            TRACE_ERROR_NUMBER("Failed to insert item", error);
+            col_delete_item(item);
+            return error;
+        }
+    }
+
+    TRACE_FLOW_STRING("col_copy_item_with_cb", "Exit");
+    return EOK;
+}
+
+
 /* This is public function so we need to check the validity
  * of the arguments.
  */
@@ -1923,6 +1983,7 @@ static int col_copy_traverse_handler(struct collection_item *head,
     char *property = NULL;
     int property_len;
     struct collection_header *header;
+    char *offset;
 
     TRACE_FLOW_STRING("col_copy_traverse_handler", "Entry.");
 
@@ -1957,19 +2018,29 @@ static int col_copy_traverse_handler(struct collection_item *head,
                 length = traverse_data->current_path->length;
                 TRACE_INFO_STRING("Path:", name);
                 TRACE_INFO_NUMBER("Path len:", length);
+                if (traverse_data->given_name != NULL) {
+                    property = traverse_data->given_name;
+                    property_len = traverse_data->given_len;
+                }
+                else {
+                    property = current->property;
+                    property_len = current->property_len;
+                }
             }
             else {
+                /* Do not create prefix for top collection
+                 * if there is no given name.
+                 */
                 name = NULL;
                 length = 0;
-            }
-
-            if (traverse_data->given_name != NULL) {
-                property = traverse_data->given_name;
-                property_len = traverse_data->given_len;
-            }
-            else {
-                property = current->property;
-                property_len = current->property_len;
+                if (traverse_data->given_name != NULL) {
+                    property = traverse_data->given_name;
+                    property_len = traverse_data->given_len;
+                }
+                else {
+                    property = NULL;
+                    property_len = 0;
+                }
             }
 
             TRACE_INFO_STRING("col_copy_traverse_handler", "About to create path data.");
@@ -2081,34 +2152,34 @@ static int col_copy_traverse_handler(struct collection_item *head,
                 TRACE_ERROR_NUMBER("Failed to allocate memory for a new name:", error);
                 return error;
             }
-            memcpy(property, traverse_data->current_path->name,
-                   traverse_data->current_path->length);
-            property[traverse_data->current_path->length] = '.';
-            memcpy(property + traverse_data->current_path->length + 1,
-                   current->property, current->property_len);
-            property[traverse_data->current_path->length + 1 +
-                     current->property_len] = '\0';
+            /* Add first part and dot only if we have prefix */
+            offset = property;
+            if (traverse_data->current_path->length) {
+                memcpy(offset, traverse_data->current_path->name,
+                       traverse_data->current_path->length);
+                offset[traverse_data->current_path->length] = '.';
+                offset += traverse_data->current_path->length + 1;
+            }
+            memcpy(offset, current->property, current->property_len);
+            offset[current->property_len] = '\0';
         }
         else property = current->property;
 
         TRACE_INFO_STRING("Using property:", property);
 
-        error = col_insert_property_with_ref_int(parent,
-                                                 NULL,
-                                                 COL_DSP_END,
-                                                 NULL,
-                                                 0,
-                                                 0,
-                                                 property,
-                                                 current->type,
-                                                 current->data,
-                                                 current->length,
-                                                 NULL);
-        /* First free then check error */
+        error = col_copy_item_with_cb(parent,
+                                      property,
+                                      current->type,
+                                      current->data,
+                                      current->length,
+                                      traverse_data->copy_cb,
+                                      traverse_data->ext_data);
+
+        /* Free property if we allocated it */
         if (traverse_data->mode == COL_COPY_FLATDOT) free(property);
 
         if (error) {
-            TRACE_ERROR_NUMBER("Add property returned error:", error);
+            TRACE_ERROR_NUMBER("Failed to copy property:", error);
             return error;
         }
     }
@@ -2200,14 +2271,37 @@ void col_destroy_collection(struct collection_item *ci)
 
 
 /* COPY */
-/* Create a deep copy of the current collection. */
-/* Referenced collections of the donor are copied as sub collections. */
+
+/* Wrapper around a more advanced function */
 int col_copy_collection(struct collection_item **collection_copy,
                         struct collection_item *collection_to_copy,
                         const char *name_to_use,
                         int copy_mode)
 {
     int error = EOK;
+    TRACE_FLOW_STRING("col_copy_collection", "Entry.");
+
+    error = col_copy_collection_with_cb(collection_copy,
+                                        collection_to_copy,
+                                        name_to_use,
+                                        copy_mode,
+                                        NULL,
+                                        NULL);
+
+    TRACE_FLOW_NUMBER("col_copy_collection. Exit. Returning", error);
+    return error;
+}
+
+/* Create a deep copy of the current collection. */
+/* Referenced collections of the donor are copied as sub collections. */
+int col_copy_collection_with_cb(struct collection_item **collection_copy,
+                                struct collection_item *collection_to_copy,
+                                const char *name_to_use,
+                                int copy_mode,
+                                col_copy_cb copy_cb,
+                                void *ext_data)
+{
+    int error = EOK;
     struct collection_item *new_collection = NULL;
     const char *name;
     struct collection_header *header;
@@ -2215,9 +2309,9 @@ int col_copy_collection(struct collection_item **collection_copy,
     struct col_copy traverse_data;
     int flags;
 
-    TRACE_FLOW_STRING("col_copy_collection", "Entry.");
+    TRACE_FLOW_STRING("col_copy_collection_with_cb", "Entry.");
 
-    /* Collection is requered */
+    /* Collection is required */
     if (collection_to_copy == NULL) {
         TRACE_ERROR_NUMBER("No collection to search!", EINVAL);
         return EINVAL;
@@ -2254,6 +2348,8 @@ int col_copy_collection(struct collection_item **collection_copy,
     traverse_data.current_path = NULL;
     traverse_data.given_name = NULL;
     traverse_data.given_len = 0;
+    traverse_data.copy_cb = copy_cb;
+    traverse_data.ext_data = ext_data;
 
     if (copy_mode == COL_COPY_FLATDOT) flags = COL_TRAVERSE_DEFAULT | COL_TRAVERSE_END;
     else if (copy_mode == COL_COPY_FLAT) flags = COL_TRAVERSE_FLAT;
@@ -2266,7 +2362,7 @@ int col_copy_collection(struct collection_item **collection_copy,
     if (!error) *collection_copy = new_collection;
     else col_destroy_collection(new_collection);
 
-    TRACE_FLOW_NUMBER("col_copy_collection returning", error);
+    TRACE_FLOW_NUMBER("col_copy_collection_with_cb returning", error);
     return error;
 
 }
@@ -2518,8 +2614,10 @@ int col_add_collection_to_collection(struct collection_item *ci,
 
         traverse_data.mode = COL_COPY_FLAT;
         traverse_data.current_path = NULL;
+        traverse_data.copy_cb = NULL;
+        traverse_data.ext_data = NULL;
 
-        if ((name_to_use) && (*name_to_use)) {
+        if ((as_property) && (*as_property)) {
             /* The normal assignement generates a warning
              * becuase I am assigning const to a non const.
              * I can't make the structure member to be const
@@ -2530,8 +2628,8 @@ int col_add_collection_to_collection(struct collection_item *ci,
              * To overcome the issue I use memcpy();
              */
             memcpy(&(traverse_data.given_name),
-                   &(name_to_use), sizeof(char *));
-            traverse_data.given_len = strlen(name_to_use);
+                   &(as_property), sizeof(char *));
+            traverse_data.given_len = strlen(as_property);
         }
         else {
             traverse_data.given_name = NULL;
@@ -2550,8 +2648,10 @@ int col_add_collection_to_collection(struct collection_item *ci,
 
         traverse_data.mode = COL_COPY_FLATDOT;
         traverse_data.current_path = NULL;
+        traverse_data.copy_cb = NULL;
+        traverse_data.ext_data = NULL;
 
-        if ((name_to_use) && (*name_to_use)) {
+        if ((as_property) && (*as_property)) {
             /* The normal assignement generates a warning
              * becuase I am assigning const to a non const.
              * I can't make the structure member to be const
@@ -2562,8 +2662,8 @@ int col_add_collection_to_collection(struct collection_item *ci,
              * To overcome the issue I use memcpy();
              */
             memcpy(&(traverse_data.given_name),
-                   &(name_to_use), sizeof(char *));
-            traverse_data.given_len = strlen(name_to_use);
+                   &(as_property), sizeof(char *));
+            traverse_data.given_len = strlen(as_property);
         }
         else {
             traverse_data.given_name = NULL;
diff --git a/common/collection/collection.h b/common/collection/collection.h
index 710e33b..1ff038e 100644
--- a/common/collection/collection.h
+++ b/common/collection/collection.h
@@ -97,13 +97,6 @@
 #define COL_TRAVERSE_IGNORE   0x00000004  /* Ignore sub collections as if none
                                            * is present */
 #define COL_TRAVERSE_FLAT     0x00000008  /* Flatten the collection. */
-#define COL_TRAVERSE_FLATDOT  0x00000010  /* Flatten the collection but use
-                                           * dotted notation for property names
-                                           * For example the subcollection
-                                           * named "sub" containing "foo" and
-                                           * "bar" will be flattened as:
-                                           * "sub.foo", "sub.bar".
-                                           */
 
 
 /* Additional iterator flags
@@ -414,13 +407,32 @@ int col_add_collection_to_collection(struct collection_item *ci,            /* C
                                  struct collection_item *collection_to_add, /* Collection to add */
                                  int mode);                                 /* How this collection needs to be added */
 
-/* Create a deep copy of the current collection. */
-/* The acceptable modes are defined at the top */
+/* Create a deep copy of the current collection.
+ * Wraps the function below.
+ * The acceptable modes are defined at the top.
+ */
 int col_copy_collection(struct collection_item **collection_copy,
                         struct collection_item *collection_to_copy,
                         const char *name_to_use,
                         int copy_mode);
 
+/* Callback used in the next function */
+typedef int (*col_copy_cb)(struct collection_item *item,
+                           void *ext_data,
+                           int *skip);
+
+/* Create a deep copy of the current collection.
+ * Calls caller provided callback before
+ * copying each item's data.
+ * The acceptable modes are defined at the top.
+ */
+int col_copy_collection_with_cb(struct collection_item **collection_copy,
+                                struct collection_item *collection_to_copy,
+                                const char *name_to_use,
+                                int copy_mode,
+                                col_copy_cb copy_cb,
+                                void *ext_data);
+
 /* Signature of the callback that needs to be used when
    traversing a collection or looking for a specific item */
 typedef int (*col_item_fn)(const char *property,   /* The name of the property will be passed in this parameter. */
diff --git a/common/collection/collection_tools.c b/common/collection/collection_tools.c
index aa82134..c3f00ea 100644
--- a/common/collection/collection_tools.c
+++ b/common/collection/collection_tools.c
@@ -244,7 +244,7 @@ int col_get_data_len(int type, int length)
     case COL_TYPE_UNSIGNED:
     case COL_TYPE_LONG:
     case COL_TYPE_ULONG:
-        len = 15;
+        len = 20;
         break;
 
     case COL_TYPE_STRING:
diff --git a/common/collection/collection_ut.c b/common/collection/collection_ut.c
index 3666179..f1ec905 100644
--- a/common/collection/collection_ut.c
+++ b/common/collection/collection_ut.c
@@ -222,16 +222,30 @@ int add_collection_test(void)
     return error;
 }
 
+int copy_cb(struct collection_item *item,
+            void *ext_data,
+            int *skip)
+{
+    printf("INSIDE Copy Callback\n");
+    col_debug_item(item);
+    printf("Passed in data: %s\n", (char *) ext_data);
+    if (strcmp(col_get_item_property(item, NULL), "id") == 0) *skip = 1;
+    return EOK;
+}
+
+
 int mixed_collection_test(void)
 {
     struct collection_item *peer;
     struct collection_item *socket1;
     struct collection_item *socket2;
+    struct collection_item *socket3;
     struct collection_item *event;
     struct collection_item *host;
     char binary_dump[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
     int found = 0;
     unsigned int class = 0;
+    char foo[] = "foo";
 
     int error = EOK;
 
@@ -295,6 +309,20 @@ int mixed_collection_test(void)
     col_debug_collection(socket2, COL_TRAVERSE_DEFAULT);
     col_debug_collection(peer, COL_TRAVERSE_DEFAULT);
 
+    error = col_copy_collection_with_cb(&socket3, socket1, "socket3",
+                                        COL_COPY_FLATDOT, copy_cb, (void *)foo);
+    if (error) {
+        col_destroy_collection(peer);
+        col_destroy_collection(host);
+        col_destroy_collection(socket1);
+        col_destroy_collection(socket2);
+        printf("Failed to copy collection. Error %d\n", error);
+        return error;
+    }
+
+    col_debug_collection(socket3, COL_TRAVERSE_DEFAULT);
+    col_destroy_collection(socket3);
+
     printf("Adding PEER collection to SOCKET2 collection as a reference named PEER2\n");
 
     /* Embed peer host into the socket2 as reference */
-- 
1.5.5.6

