>From c637236b6bf7c565a05fd81304259ed635aca81f Mon Sep 17 00:00:00 2001
From: Dmitri Pal <dpal@redhat.com>
Date: Mon, 14 Sep 2009 12:52:23 -0400
Subject: [PATCH] COLLECTION Adding item comparison and sorting

Needed item comparison functions and realized
that the easiest way to test them would be using
sorting. Since there already been a ticket #73
to do that I added function to sort collection
based on different properties of the item.
---
 common/collection/Makefile.am        |    1 +
 common/collection/collection.c       |    2 +-
 common/collection/collection.h       |  107 ++++++++++++++++++++++++
 common/collection/collection_tools.c |    2 +-
 common/collection/collection_ut.c    |  147 +++++++++++++++++++++++++++++++++-
 common/collection/configure.ac       |    7 ++
 6 files changed, 261 insertions(+), 5 deletions(-)

diff --git a/common/collection/Makefile.am b/common/collection/Makefile.am
index 292a9a8..b7cfa60 100644
--- a/common/collection/Makefile.am
+++ b/common/collection/Makefile.am
@@ -26,6 +26,7 @@ libcollection_la_SOURCES = \
     collection_cnv.c \
     collection_queue.c \
     collection_stack.c \
+    collection_cmp.c \
     collection.h \
     collection_tools.h \
     collection_priv.h \
diff --git a/common/collection/collection.c b/common/collection/collection.c
index bccb50d..c116f48 100644
--- a/common/collection/collection.c
+++ b/common/collection/collection.c
@@ -28,7 +28,7 @@
 #include "config.h"
 #include "trace.h"
 
-/* The collection should use the teal structures */
+/* The collection should use the real structures */
 #include "collection_priv.h"
 #include "collection.h"
 
diff --git a/common/collection/collection.h b/common/collection/collection.h
index dc59ea2..b4bbb2a 100644
--- a/common/collection/collection.h
+++ b/common/collection/collection.h
@@ -501,6 +501,113 @@ uint64_t col_get_item_hash(struct collection_item *ci);
  */
 uint64_t col_make_hash(const char *string, int *length);
 
+/* Compare two items.
+ * The second item is evaluated against the first.
+ * Function returns 0 if two items are the same
+ * and non-zero otherwise.
+ * The in_flags is a bit mask that defines
+ * how the items should be compared.
+ * See below the list of conbstants
+ * defined for this purpose.
+ * If items are different they might be orderable
+ * or not. For example one can order items by name
+ * but not by type.
+ * If the result of the function is non-zero
+ * the out_flags (if provided) will be
+ * set to indicate if the second item is greater
+ * then the first.
+ */
+int col_compare_items(struct collection_item *first,
+                      struct collection_item *second,
+                      unsigned in_flags,
+                      unsigned *out_flags);
+
+/********* Possible valies for input flags ********/
+/* How to compare properties?
+ * The following 4 flags are mutually exclusive
+ */
+#define COL_CMPIN_PROP_EQU    0x000000004 /* Properties should be same */
+#define COL_CMPIN_PROP_BEG    0x000000005 /* Properties start same */
+#define COL_CMPIN_PROP_MID    0x000000006 /* One is substring of another */
+#define COL_CMPIN_PROP_END    0x000000007 /* One property ends with another */
+
+/* Make sure that there is a dot.
+ * Useful with _BEG, _MID and _END flags to check that the there is
+ * a dot (if present) in the right place (before, after or both).
+ * For example the first item is named "foo.bar" and the second
+ * is "bar". Using _END the "bar" will be found but if _DOT flag is
+ * used too the function will also check if there was a "." before the found
+ * string in this case.
+ * Ignored in case of _EQU.
+ */
+#define COL_CMPIN_PROP_DOT     0x000000008
+
+/* Compare property lenghts */
+#define COL_CMPIN_PROP_LEN     0x000000010
+
+/* Compare types */
+#define COL_CMPIN_TYPE         0x000000020
+
+/* Compare data len */
+#define COL_CMPIN_DATA_LEN     0x000000040
+
+/* Compare data (up to the length of the second one)
+ * if type is the same. If type is different
+ * function will assume data is different
+ * without performing actual comparison.
+ */
+#define COL_CMPIN_DATA         0x000000080
+
+/********* Possible values for output flags *********/
+/* If _EQU was specified and the property of the second item
+ * is greater the following bit will be set
+ */
+#define COL_CMPOUT_PROP_STR    0x00000001
+/* If we were told to compare property lengths
+ * and second is longer this bit will be set
+ */
+#define COL_CMPOUT_PROP_LEN    0x00000002
+/* If we were told to compare data lengths
+ * and second is longer this bit will be set
+ */
+#define COL_CMPOUT_DATA_LEN    0x00000004
+/* If we were told to compare data
+ * and types are the same then
+ * if the second one is greater this bit will
+ * be set. If data is binary flag is never set
+ */
+#define COL_CMPOUT_DATA    0x00000008
+
+
+/* Sort collection.
+ * cmp_flags are the same as in_flags for the compare
+ * function. The sort_flags is an OR of the costants
+ * defined below.
+ * If the subcollections are included in sorting
+ * each collection is sorted separately (this is not a global sort).
+ * It might be dangerous to sort subcollections if
+ * subcollection is not owned by current collection.
+ * If it is a reference to an external collection
+ * there might be an issue. To skip the collections that
+ * externally referenced use MYSUB flag.
+ * Keep in mind that if the collection
+ * has two references to the same other
+ * collection it is impossible to detect
+ * this situation. If MYSUB is used in this
+ * case such collection will be ignored
+ * If MYSUB is not used the collection
+ * will be sorted more than once.
+ */
+int col_sort_collection(struct collection_item *col,
+                        unsigned cmp_flags,
+                        unsigned sort_flags);
+
+/* Sort flags */
+#define COL_SORT_ASC    0x00000000
+#define COL_SORT_DESC   0x00000001
+#define COL_SORT_SUB    0x00000002
+#define COL_SORT_MYSUB  0x00000004
+
 /* If you want to modify the item that you got as a result of iterating through collection
  * or by calling col_get_item(). If you want to rename item provide a new name in the property
  * argument. If you want the data to remain unchanged use 0 as length parameter.
diff --git a/common/collection/collection_tools.c b/common/collection/collection_tools.c
index c3f00ea..503a1a8 100644
--- a/common/collection/collection_tools.c
+++ b/common/collection/collection_tools.c
@@ -117,7 +117,7 @@ int col_debug_handle(const char *property,
                (nest_level -1) * 4, "",
                property,
                length,
-               (*((unsigned char *)(data)) == '\0') ? "flase" : "true",
+               (*((unsigned char *)(data)) == '\0') ? "false" : "true",
                nest_level);
         break;
     case COL_TYPE_COLLECTION:
diff --git a/common/collection/collection_ut.c b/common/collection/collection_ut.c
index 050c6ef..c17f5bc 100644
--- a/common/collection/collection_ut.c
+++ b/common/collection/collection_ut.c
@@ -1323,9 +1323,9 @@ int search_test(void)
         (error = col_add_collection_to_collection(level1, NULL, NULL, level2, COL_ADD_MODE_REFERENCE)) ||
         (error = col_create_collection(&level3, "level3", 0)) ||
         (error = col_add_collection_to_collection(level1, "level2", NULL, level3, COL_ADD_MODE_REFERENCE)) ||
-        (error = col_create_collection(&level4, "level4", 0)) ||
+        (error = col_create_collection(&level4, "leveL4", 0)) ||
         (error = col_add_collection_to_collection(level1, "level3", NULL, level4, COL_ADD_MODE_REFERENCE)) ||
-        (error = col_add_int_property(level1, "level4", "id", 1)) ||
+        (error = col_add_int_property(level1, "leveL4", "id", 1)) ||
         (error = col_add_long_property(level1, "level3", "packets", 100000000L)) ||
         (error = col_add_binary_property(level1, "level2", "stack", binary_dump, sizeof(binary_dump)))) {
         col_destroy_collection(level1);
@@ -1424,6 +1424,146 @@ int search_test(void)
     return EOK;
 }
 
+/* Sort test */
+int sort_test(void)
+{
+    struct collection_item *level1 = NULL;
+    struct collection_item *level2a = NULL;
+    struct collection_item *level2b = NULL;
+    struct collection_item *level3 = NULL;
+    int error = 0;
+
+    printf("\n\n==== SORT TEST ====\n\n");
+
+    if ((error = col_create_collection(&level1, "level1", 0)) ||
+        (error = col_create_collection(&level2a, "level2a", 0)) ||
+        (error = col_add_collection_to_collection(level1, NULL, NULL, level2a, COL_ADD_MODE_REFERENCE)) ||
+        (error = col_create_collection(&level2b, "level2b", 0)) ||
+        (error = col_add_collection_to_collection(level1, NULL, NULL, level2b, COL_ADD_MODE_REFERENCE)) ||
+        (error = col_create_collection(&level3, "level3", 0)) ||
+        (error = col_add_collection_to_collection(level1, "level2a", NULL, level3, COL_ADD_MODE_REFERENCE)) ||
+        (error = col_add_collection_to_collection(level1, "level2b", NULL, level3, COL_ADD_MODE_REFERENCE)) ||
+        (error = col_add_int_property(level1, NULL, "int3", 1)) ||
+        (error = col_add_int_property(level1, NULL, "int2", 2)) ||
+        (error = col_add_int_property(level1, NULL, "int1", 3)) ||
+        (error = col_add_bool_property(level1, NULL, "bool3", 1)) ||
+        (error = col_add_bool_property(level1, NULL, "bool2", 1)) ||
+        (error = col_add_bool_property(level1, NULL, "bool1", 0)) ||
+        (error = col_add_unsigned_property(level1, NULL, "unsigned1", 2)) ||
+        (error = col_add_unsigned_property(level1, NULL, "unsigned3", 1)) ||
+        (error = col_add_unsigned_property(level1, NULL, "unsigned2", 3)) ||
+        (error = col_add_long_property(level1, NULL, "long3", 1)) ||
+        (error = col_add_long_property(level1, NULL, "long2", 2)) ||
+        (error = col_add_long_property(level1, NULL, "long1", 3)) ||
+        (error = col_add_ulong_property(level1, NULL, "ulong1", 2)) ||
+        (error = col_add_ulong_property(level1, NULL, "ulong3", 1)) ||
+        (error = col_add_ulong_property(level1, NULL, "ulong2", 3)) ||
+        (error = col_add_double_property(level1, NULL, "double1", 2.2)) ||
+        (error = col_add_double_property(level1, NULL, "double3", 1.1)) ||
+        (error = col_add_double_property(level1, NULL, "double2", 3.3)) ||
+        (error = col_add_int_property(level3, NULL, "int3L3", 1)) ||
+        (error = col_add_int_property(level3, NULL, "int2L3", 2)) ||
+        (error = col_add_int_property(level3, NULL, "int1L3", 3)) ||
+        (error = col_add_unsigned_property(level1, "level2a!level3", "unsigned1L3", 2)) ||
+        (error = col_add_unsigned_property(level1, "level2a!level3", "unsigned3L3", 1)) ||
+        (error = col_add_unsigned_property(level1, "level2a!level3", "unsigned2L3", 3)) ||
+        (error = col_add_long_property(level1, "level2b!level3", "long3L3", 1)) ||
+        (error = col_add_long_property(level1, "level2b!level3", "long2L3", 2)) ||
+        (error = col_add_long_property(level1, "level2b!level3", "long1L3", 3)) ||
+        (error = col_add_ulong_property(level1, "level3", "ulong1L3", 2)) ||
+        (error = col_add_ulong_property(level1, "level3", "ulong3L3", 1)) ||
+        (error = col_add_ulong_property(level1, "level3", "ulong2L3", 3)) ||
+        (error = col_add_bool_property(level3, NULL, "bool3", 1)) ||
+        (error = col_add_bool_property(level3, NULL, "bool2", 1)) ||
+        (error = col_add_bool_property(level3, NULL, "bool1", 0)) ||
+        (error = col_add_double_property(level3, NULL, "double1L3", 2.2)) ||
+        (error = col_add_double_property(level3, NULL, "double3L3", 1.1)) ||
+        (error = col_add_double_property(level3, NULL, "double2L3", 3.3))) {
+        col_destroy_collection(level1);
+        col_destroy_collection(level2a);
+        col_destroy_collection(level2b);
+        col_destroy_collection(level3);
+        printf("Failed to build test. Error %d\n", error);
+        return error;
+    }
+
+    printf("\nUNSORTED COLLECTION\n\n");
+    col_debug_collection(level1, COL_TRAVERSE_DEFAULT);
+
+    error = col_sort_collection(level1, COL_CMPIN_PROP_EQU, COL_SORT_SUB | COL_SORT_MYSUB);
+    if (error) {
+        col_destroy_collection(level1);
+        col_destroy_collection(level2a);
+        col_destroy_collection(level2b);
+        col_destroy_collection(level3);
+        printf("Failed sort. Error %d\n", error);
+        return error;
+    }
+
+    printf("\nSORTED BUT SKIPPING REFERENCES\n\n");
+    col_debug_collection(level1, COL_TRAVERSE_DEFAULT);
+
+    error = col_sort_collection(level1, COL_CMPIN_PROP_EQU, COL_SORT_SUB);
+    if (error) {
+        col_destroy_collection(level1);
+        col_destroy_collection(level2a);
+        col_destroy_collection(level2b);
+        col_destroy_collection(level3);
+        printf("Failed sort. Error %d\n", error);
+        return error;
+    }
+
+    printf("\nSORTED BUT NOT SKIPPING REFERENCES\n\n");
+    col_debug_collection(level1, COL_TRAVERSE_DEFAULT);
+
+    error = col_sort_collection(level1, COL_CMPIN_DATA_LEN, COL_SORT_SUB | COL_SORT_DESC);
+    if (error) {
+        col_destroy_collection(level1);
+        col_destroy_collection(level2a);
+        col_destroy_collection(level2b);
+        col_destroy_collection(level3);
+        printf("Failed sort. Error %d\n", error);
+        return error;
+    }
+
+    printf("\nSORTED DESC NOT SKIPPING BY LENGTH OF DATA\n\n");
+    col_debug_collection(level1, COL_TRAVERSE_DEFAULT);
+
+    error = col_sort_collection(level1, COL_CMPIN_PROP_LEN, COL_SORT_SUB | COL_SORT_DESC);
+    if (error) {
+        col_destroy_collection(level1);
+        col_destroy_collection(level2a);
+        col_destroy_collection(level2b);
+        col_destroy_collection(level3);
+        printf("Failed sort. Error %d\n", error);
+        return error;
+    }
+
+    printf("\nSORTED DESC NOT SKIPPING BY LENGTH OF PROPERTY\n\n");
+    col_debug_collection(level1, COL_TRAVERSE_DEFAULT);
+
+    error = col_sort_collection(level1, COL_CMPIN_DATA, COL_SORT_SUB | COL_SORT_DESC);
+    if (error) {
+        col_destroy_collection(level1);
+        col_destroy_collection(level2a);
+        col_destroy_collection(level2b);
+        col_destroy_collection(level3);
+        printf("Failed sort. Error %d\n", error);
+        return error;
+    }
+
+    printf("\nSORTED DESC NOT SKIPPING BY DATA\n\n");
+    col_debug_collection(level1, COL_TRAVERSE_DEFAULT);
+
+    col_destroy_collection(level1);
+    col_destroy_collection(level2a);
+    col_destroy_collection(level2b);
+    col_destroy_collection(level3);
+
+    printf("\n\n==== SORT TEST END ====\n\n");
+
+    return EOK;
+}
 
 /* Main function of the unit test */
 
@@ -1439,7 +1579,8 @@ int main(int argc, char *argv[])
         (error = iterator_test()) ||
         (error = insert_extract_test()) ||
         (error = delete_test()) ||
-        (error = search_test())) {
+        (error = search_test()) ||
+        (error = sort_test())) {
         printf("Failed!\n");
     }
     else printf("Success!\n");
diff --git a/common/collection/configure.ac b/common/collection/configure.ac
index 4858bc6..cf7a1ff 100644
--- a/common/collection/configure.ac
+++ b/common/collection/configure.ac
@@ -24,5 +24,12 @@ AS_IF([test ["$trace_level" -gt "0"] -a ["$trace_level" -lt "8"] ],[AC_SUBST([TR
 AC_CHECK_SIZEOF([long])
 AC_CHECK_SIZEOF([long long])
 
+AC_CHECK_FUNC([strcasestr],
+              AC_DEFINE([HAVE_STRCASESTR],
+                        [1],
+                        [Define if strcasestr exists]),
+              AC_MSG_ERROR("Platform must support strcasestr"))
+
+
 AC_CONFIG_FILES([Makefile collection.pc])
 AC_OUTPUT
-- 
1.5.5.6

