>From a57d1eb89a87a60984ea30b51b676b818183ca15 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_cmp.c   |  471 ++++++++++++++++++++++++++++++++++
 common/collection/collection_tools.c |    2 +-
 common/collection/collection_ut.c    |  147 +++++++++++-
 common/collection/configure.ac       |    7 +
 7 files changed, 732 insertions(+), 5 deletions(-)
 create mode 100644 common/collection/collection_cmp.c

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_cmp.c b/common/collection/collection_cmp.c
new file mode 100644
index 0000000..8c2acc2
--- /dev/null
+++ b/common/collection/collection_cmp.c
@@ -0,0 +1,471 @@
+/*
+    COLLECTION LIBRARY
+
+    Function to compare items.
+
+    Copyright (C) Dmitri Pal <dpal@redhat.com> 2009
+
+    Collection Library is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Collection Library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with Collection Library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+#include <time.h>
+#include "config.h"
+#include "trace.h"
+
+/* The collection should use the real structures */
+#include "collection_priv.h"
+#include "collection.h"
+
+#define NONZERO 1
+#define PROP_MSK    0x000000007
+
+
+/* Function to compare two items */
+int col_compare_items(struct collection_item *first,
+                      struct collection_item *second,
+                      unsigned in_flags,
+                      unsigned *out_flags)
+{
+    int result = 0;
+    unsigned mode;
+    int cmpres = 0;
+    char *substr;
+
+    TRACE_FLOW_STRING("col_compare_items", "Entry.");
+
+    /* If any of the arguments is NULL return
+     * that they are different.
+     */
+    if ((first == NULL) || (second == NULL)) {
+        TRACE_INFO_STRING("One of the items is NULL", "");
+        return NONZERO;
+    }
+
+    /* Check if we are told to compare something */
+    if (!in_flags) {
+        TRACE_INFO_NUMBER("No flags specified", in_flags);
+        return NONZERO;
+    }
+
+    if (out_flags) *out_flags = 0;
+
+    /* Start comparison */
+    mode = in_flags & PROP_MSK;
+    if (mode > 0 ) {
+        /* We are told to compare the properties */
+        switch(mode) {
+
+        case COL_CMPIN_PROP_EQU: /* looking for exact match */
+
+            /* Compare hashes and lengths first */
+            if ((first->phash == first->phash) &&
+                (first->property_len == second->property_len)) {
+                /* Collections are case insensitive, sorry... */
+                cmpres = strncasecmp(first->property,
+                                     second->property,
+                                     second->property_len);
+                if (cmpres != 0) {
+                    result = NONZERO;
+                    if (cmpres < 0) {
+                        /* Second is greater */
+                        if (out_flags) *out_flags |= COL_CMPOUT_PROP_STR;
+                    }
+                }
+            }
+            else {
+                result = NONZERO;
+                /* They are different so check if we need to compare? */
+                if (out_flags) {
+                    cmpres = strncasecmp(first->property,
+                                         second->property,
+                                         second->property_len);
+                    if (cmpres < 0) {
+                        /* Second is greater */
+                            *out_flags |= COL_CMPOUT_PROP_STR;
+                    }
+                }
+            }
+            break;
+
+        case COL_CMPIN_PROP_BEG: /* looking for beginning */
+
+            /* Compare lengths first */
+            if (first->property_len >= second->property_len) {
+                cmpres = strncasecmp(first->property,
+                                     second->property,
+                                     second->property_len);
+	            if (cmpres == 0) {
+                    /* Check we need to validate for dot */
+                    if (in_flags & COL_CMPIN_PROP_DOT) {
+                        if ((first->property[second->property_len] != '\0') &&
+                            (first->property[second->property_len] != '.')) {
+                            result = NONZERO;
+                        }
+                    }
+                }
+                else result = NONZERO;
+            }
+            else result = NONZERO;
+            break;
+
+        case COL_CMPIN_PROP_MID: /* looking for middle */
+
+            /* Compare lengths first */
+            if (first->property_len >= second->property_len) {
+                substr = strcasestr(first->property, second->property);
+                if (substr != NULL) {
+                    /* Check we need to validate for dot */
+                    if (in_flags & COL_CMPIN_PROP_DOT) {
+                        /* Check if we have a dot before or after */
+                        if (((substr != first->property) &&
+                             (first->property[(substr - first->property) - 1] != '.')) ||
+                            ((substr[second->property_len] != '\0') &&
+                             (substr[second->property_len] != '.'))) {
+                            result = NONZERO;
+                        }
+                    }
+                }
+                else result = NONZERO;
+            }
+            else result = NONZERO;
+            break;
+
+        case COL_CMPIN_PROP_END: /* looking for end */
+
+            /* Compare lengths first */
+            if (first->property_len >= second->property_len) {
+                substr = first->property + (first->property_len - second->property_len);
+                cmpres = strncasecmp(substr,
+                                     second->property,
+                                     second->property_len);
+	            if (cmpres == 0) {
+                    /* Check we need to validate for dot */
+                    if (in_flags & COL_CMPIN_PROP_DOT) {
+                        if ((substr != first->property) &&
+                            (first->property[(substr - first->property) - 1] != '.')) {
+                            result = NONZERO;
+                        }
+                    }
+                }
+                else result = NONZERO;
+            }
+            else result = NONZERO;
+            break;
+
+        default: result = NONZERO;
+            break;
+        }
+    }
+
+    /* Check if we are told to compare property lengths */
+    if (in_flags & COL_CMPIN_PROP_LEN) {
+        if (first->property_len != second->property_len) {
+            result = NONZERO;
+            /* Do we need to tell who is greater? */
+            if ((out_flags) && (first->property_len < second->property_len)) {
+                    *out_flags |= COL_CMPOUT_PROP_LEN;
+            }
+        }
+    }
+
+    /* Check if we are told to compare types */
+    if (in_flags & COL_CMPIN_TYPE) {
+        if (first->type != second->type) result = NONZERO;
+    }
+
+    /* Check if we need to compare data length */
+    if (in_flags & COL_CMPIN_DATA_LEN) {
+        if (first->length != second->length) {
+            result = NONZERO;
+            /* Do we need to tell who is greater? */
+            if ((out_flags) && (first->length < second->length)) {
+                    *out_flags |= COL_CMPOUT_DATA_LEN;
+            }
+        }
+    }
+
+    /* Check if we need to compare data */
+    if (in_flags & COL_CMPIN_DATA) {
+        if (first->type == second->type) {
+            switch(first->type) {
+
+            case COL_TYPE_STRING:
+                if (first->length == second->length) {
+                    cmpres = strncmp((const char *)first->data,
+                                     (const char *)second->data,
+                                     first->length);
+
+                    if (cmpres != 0) {
+                        result = NONZERO;
+                        if (cmpres < 0) {
+                            /* Second is greater */
+                            if (out_flags) *out_flags |= COL_CMPOUT_DATA;
+                        }
+                    }
+
+                }
+                else result = NONZERO;
+                break;
+
+            case COL_TYPE_BINARY:
+                if (first->length == second->length) {
+                    cmpres = memcmp(first->data,
+                                    second->data,
+                                    first->length);
+
+                    if (cmpres != 0) result = NONZERO;
+                }
+                break;
+
+            case COL_TYPE_INTEGER:
+                /* Cast and compare values */
+                if (*((int *)(first->data)) != *((int *)(second->data))) {
+                    result = NONZERO;
+                    if ((out_flags) &&
+                        (*((int *)(first->data)) < *((int *)(second->data)))) {
+                        /* Second is greater */
+                        *out_flags |= COL_CMPOUT_DATA;
+                    }
+                }
+                break;
+
+            case COL_TYPE_UNSIGNED:
+                /* Cast and compare values */
+                if (*((unsigned *)(first->data)) != *((unsigned *)(second->data))) {
+                    result = NONZERO;
+                    if ((out_flags) &&
+                        (*((unsigned *)(first->data)) < *((unsigned *)(second->data)))) {
+                        /* Second is greater */
+                        *out_flags |= COL_CMPOUT_DATA;
+                    }
+                }
+                break;
+
+            case COL_TYPE_LONG:
+                /* Cast and compare values */
+                if (*((long *)(first->data)) != *((long *)(second->data))) {
+                    result = NONZERO;
+                    if ((out_flags) &&
+                        (*((long *)(first->data)) < *((long *)(second->data)))) {
+                        /* Second is greater */
+                        *out_flags |= COL_CMPOUT_DATA;
+                    }
+                }
+                break;
+
+            case COL_TYPE_ULONG:
+                /* Cast and compare values */
+                if (*((unsigned long *)(first->data)) !=
+                    *((unsigned long *)(second->data))) {
+                    result = NONZERO;
+                    if ((out_flags) &&
+                        (*((unsigned long *)(first->data)) <
+                         *((unsigned long *)(second->data)))) {
+                        /* Second is greater */
+                        *out_flags |= COL_CMPOUT_DATA;
+                    }
+                }
+                break;
+
+            case COL_TYPE_DOUBLE:
+                /* Cast and compare values */
+                if (*((double *)(first->data)) !=
+                    *((double *)(second->data))) {
+                    result = NONZERO;
+                    if ((out_flags) &&
+                        (*((double *)(first->data)) <
+                         *((double *)(second->data)))) {
+                        /* Second is greater */
+                        *out_flags |= COL_CMPOUT_DATA;
+                    }
+                }
+                break;
+
+            case COL_TYPE_BOOL:
+                /* Cast and compare values */
+                if (*((unsigned char *)(first->data)) !=
+                    *((unsigned char *)(second->data))) {
+                    result = NONZERO;
+                    if ((out_flags) &&
+                        (*((unsigned char *)(first->data)) <
+                         *((unsigned char *)(second->data)))) {
+                        /* Second is greater */
+                        *out_flags |= COL_CMPOUT_DATA;
+                    }
+                }
+                break;
+
+            /* These are never same */
+            case COL_TYPE_COLLECTION:
+            case COL_TYPE_COLLECTIONREF:
+            case COL_TYPE_END:
+            default:
+                result = NONZERO;
+                break;
+            }
+
+        }
+        else result = NONZERO;
+    }
+
+    TRACE_FLOW_NUMBER("col_compare_items. Exit. Returning:", result);
+    return result;
+}
+
+/* Sort collection */
+int col_sort_collection(struct collection_item *col,
+                        unsigned cmp_flags,
+                        unsigned sort_flags)
+{
+    int error = EOK;
+
+    struct collection_item *current;
+    struct collection_header *header;
+    struct collection_item **array;
+    struct collection_item *temp_item;
+    struct collection_item *other;
+    size_t size;
+    int ind, last;
+    int i, j;
+    int res;
+    unsigned out_flags;
+
+    TRACE_FLOW_STRING("col_sort_collection", "Entry.");
+
+    TRACE_INFO_NUMBER("Comparison flags:", cmp_flags);
+    TRACE_INFO_NUMBER("Sort flags:", sort_flags);
+
+    if ((col == NULL) || (col->type != COL_TYPE_COLLECTION)) {
+        TRACE_ERROR_STRING("Collecton must not ne NULL", "");
+        return EINVAL;
+    }
+
+    /* This will be a fast and simple implementation for now */
+    header = (struct collection_header *)(col->data);
+
+    if ((sort_flags & COL_SORT_SUB) &&
+        (sort_flags & COL_SORT_MYSUB) &&
+        (header->reference_count > 1)) {
+        TRACE_FLOW_STRING("col_sort_collection", "Exit.");
+        return error;
+    }
+
+    size = sizeof(struct collection_item *) * (header->count - 1);
+    array = (struct collection_item **)malloc(size);
+    if (array == NULL) {
+        TRACE_ERROR_NUMBER("Failed to allocate memory", ENOMEM);
+        return ENOMEM;
+    }
+
+    /* Fill array */
+    current = col->next;
+    ind = 0;
+    while (current != NULL) {
+        TRACE_INFO_STRING("Item:", current->property);
+        array[ind] = current;
+        if ((sort_flags & COL_SORT_SUB) &&
+            (array[ind]->type == COL_TYPE_COLLECTIONREF)) {
+            /* If we found a subcollection and we need to sort it
+             * then sort it.
+             */
+            other = *((struct collection_item **)(array[ind]->data));
+            error = col_sort_collection(other, cmp_flags, sort_flags);
+            if (error) {
+                TRACE_ERROR_NUMBER("Subcollection sort failed", error);
+                free(array);
+                return error;
+            }
+        }
+        ind++;
+        current = current->next;
+    }
+
+    last = ind - 1;
+
+    for (i = 0; i < last; i++) {
+
+        TRACE_INFO_STRING("Arg1:", array[i]->property);
+        TRACE_INFO_STRING("Arg2:", array[i + 1]->property);
+
+        res = col_compare_items(array[i],
+                                array[i + 1],
+                                cmp_flags,
+                                &out_flags);
+
+        TRACE_INFO_STRING("Result:", ((res == 0) ? "same" : "different"));
+        TRACE_INFO_NUMBER("Out flags", out_flags);
+
+        /* If they are not same and second is not greater
+         * in any way then we need to swap them */
+        if ((res =! 0) && (out_flags == 0)) {
+            /* Swap */
+            TRACE_INFO_STRING("Swapping:", "");
+            TRACE_INFO_STRING("Item:", array[i]->property);
+            TRACE_INFO_STRING("Item:", array[i + 1]->property);
+
+            temp_item = array[i];
+            array[i] = array[i + 1];
+            array[i + 1] = temp_item;
+
+            /* But we need to go up bubbling this item
+             */
+            j = i;
+            while (j > 0) {
+                res = col_compare_items(array[j - 1],
+                                        array[j],
+                                        cmp_flags,
+                                        &out_flags);
+                /* If they are not same and second is not greater
+                 * in any way then we need to swap them */
+                if ((res =! 0) && (out_flags == 0)) {
+                    /* Swap */
+                    temp_item = array[j - 1];
+                    array[j - 1] = array[j];
+                    array[j] = temp_item;
+                }
+                else break;
+                j--;
+            }
+        }
+    }
+
+    /* Build the chain back */
+    if (sort_flags & COL_SORT_DESC) {
+        col->next = array[last];
+        for (i = last; i > 0 ; i--) {
+            array[i]->next = array[i - 1];
+        }
+        array[0]->next = NULL;
+        header->last = array[0];
+    }
+    else {
+        col->next = array[0];
+        for (i = 0; i < last ; i++) {
+            array[i]->next = array[i + 1];
+        }
+        array[last]->next = NULL;
+        header->last = array[last];
+    }
+
+    free(array);
+
+    TRACE_FLOW_STRING("col_sort_collection", "Exit.");
+    return error;
+
+}
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

