[evolution-data-server/wip/mcrha/libical-glib] Add ECalComponentPropertyBag to store ICalProperty objects



commit 02cfcd8b15104d1f3522b9b39eb06cdd155dee25
Author: Milan Crha <mcrha redhat com>
Date:   Thu Feb 28 18:53:41 2019 +0100

    Add ECalComponentPropertyBag to store ICalProperty objects

 .../evolution-data-server-docs.sgml.in             |   1 +
 src/calendar/libecal/CMakeLists.txt                |   2 +
 src/calendar/libecal/e-cal-component-alarm.c       |  30 ++
 src/calendar/libecal/e-cal-component-alarm.h       |   4 +
 .../libecal/e-cal-component-property-bag.c         | 354 +++++++++++++++++++++
 .../libecal/e-cal-component-property-bag.h         |  97 ++++++
 src/calendar/libecal/libecal.h                     |   1 +
 tests/libecal/test-cal-component.c                 | 270 +++++++++++++++-
 8 files changed, 750 insertions(+), 9 deletions(-)
---
diff --git a/docs/reference/evolution-data-server/evolution-data-server-docs.sgml.in 
b/docs/reference/evolution-data-server/evolution-data-server-docs.sgml.in
index 68d276800..c0ef85e2a 100644
--- a/docs/reference/evolution-data-server/evolution-data-server-docs.sgml.in
+++ b/docs/reference/evolution-data-server/evolution-data-server-docs.sgml.in
@@ -158,6 +158,7 @@
       <xi:include href="xml/e-cal-component-id.xml"/>
       <xi:include href="xml/e-cal-component-organizer.xml"/>
       <xi:include href="xml/e-cal-component-period.xml"/>
+      <xi:include href="xml/e-cal-component-property-bag.xml"/>
       <xi:include href="xml/e-cal-component-range.xml"/>
       <xi:include href="xml/e-cal-component-text.xml"/>
     </chapter>
diff --git a/src/calendar/libecal/CMakeLists.txt b/src/calendar/libecal/CMakeLists.txt
index 83ad92bb5..e2b318931 100644
--- a/src/calendar/libecal/CMakeLists.txt
+++ b/src/calendar/libecal/CMakeLists.txt
@@ -21,6 +21,7 @@ set(SOURCES
        e-cal-component-id.c
        e-cal-component-organizer.c
        e-cal-component-period.c
+       e-cal-component-property-bag.c
        e-cal-component-range.c
        e-cal-component-text.c
        e-cal-recur.c
@@ -48,6 +49,7 @@ set(HEADERS
        e-cal-component-id.h
        e-cal-component-organizer.h
        e-cal-component-period.h
+       e-cal-component-property-bag.h
        e-cal-component-range.h
        e-cal-component-text.h
        e-cal-enums.h
diff --git a/src/calendar/libecal/e-cal-component-alarm.c b/src/calendar/libecal/e-cal-component-alarm.c
index 0e7ca3609..cb9de1b98 100644
--- a/src/calendar/libecal/e-cal-component-alarm.c
+++ b/src/calendar/libecal/e-cal-component-alarm.c
@@ -46,6 +46,7 @@ struct _ECalComponentAlarm {
        ECalComponentAlarmTrigger *trigger;
        GSList *attendees; /* ECalComponentAttendee * */
        GSList *attachments; /* ICalAttach * */
+       ECalComponentPropertyBag *property_bag;
 };
 
 /**
@@ -66,6 +67,7 @@ e_cal_component_alarm_new (void)
        alarm = g_new0 (ECalComponentAlarm, 1);
        alarm->uid = e_util_generate_uid ();
        alarm->action = E_CAL_COMPONENT_ALARM_UNKNOWN;
+       alarm->property_bag = e_cal_component_property_bag_new ();
 
        return alarm;
 }
@@ -178,6 +180,8 @@ e_cal_component_alarm_copy (const ECalComponentAlarm *alarm)
                alrm->attachments = g_slist_reverse (alrm->attachments);
        }
 
+       e_cal_component_property_bag_assign (alrm->property_bag, alarm->property_bag);
+
        return alrm;
 }
 
@@ -202,6 +206,7 @@ e_cal_component_alarm_free (gpointer alarm)
                e_cal_component_text_free (alrm->description);
                e_cal_component_alarm_repeat_free (alrm->repeat);
                e_cal_component_alarm_trigger_free (alrm->trigger);
+               e_cal_component_property_bag_free (alrm->property_bag);
                g_slist_free_full (alrm->attendees, e_cal_component_attendee_free);
                g_slist_free_full (alrm->attachments, g_object_unref);
                g_free (alrm);
@@ -245,6 +250,8 @@ e_cal_component_alarm_set_from_component (ECalComponentAlarm *alarm,
        alarm->attendees = NULL;
        alarm->attachments = NULL;
 
+       e_cal_component_property_bag_clear (alarm->property_bag);
+
        for (prop = i_cal_component_get_first_property (comp, I_CAL_ANY_PROPERTY);
             prop;
             g_object_unref (prop), prop = i_cal_component_get_next_property (comp, I_CAL_ANY_PROPERTY)) {
@@ -323,10 +330,13 @@ e_cal_component_alarm_set_from_component (ECalComponentAlarm *alarm,
                        if (g_strcmp0 (xname, E_CAL_EVOLUTION_ALARM_UID_PROPERTY) == 0) {
                                g_free (alarm->uid);
                                alarm->uid = g_strdup (i_cal_property_get_x (prop));
+                       } else {
+                               e_cal_component_property_bag_add (alarm->property_bag, prop);
                        }
                        break;
 
                default:
+                       e_cal_component_property_bag_add (alarm->property_bag, prop);
                        break;
                }
        }
@@ -535,6 +545,8 @@ e_cal_component_alarm_fill_component (ECalComponentAlarm *alarm,
                if (prop)
                        i_cal_component_take_property (component, prop);
        }
+
+       e_cal_component_property_bag_fill_component (alarm->property_bag, component);
 }
 
 /**
@@ -987,3 +999,21 @@ e_cal_component_alarm_take_attachments (ECalComponentAlarm *alarm,
                alarm->attachments = attachments;
        }
 }
+
+/**
+ * e_cal_component_alarm_get_property_bag:
+ * @alarm: an #ECalComponentAlarm
+ *
+ * Returns: (transfer none): an #ECalComponentPropertyBag with additional
+ *    properties stored with an alarm component, other than those accessible
+ *    with the other functions of the @alarm.
+ *
+ * Since: 3.36
+ **/
+ECalComponentPropertyBag *
+e_cal_component_alarm_get_property_bag (const ECalComponentAlarm *alarm)
+{
+       g_return_val_if_fail (alarm != NULL, NULL);
+
+       return alarm->property_bag;
+}
diff --git a/src/calendar/libecal/e-cal-component-alarm.h b/src/calendar/libecal/e-cal-component-alarm.h
index 21299d839..f65c1f7b9 100644
--- a/src/calendar/libecal/e-cal-component-alarm.h
+++ b/src/calendar/libecal/e-cal-component-alarm.h
@@ -29,6 +29,7 @@
 #include <libecal/e-cal-component-alarm-repeat.h>
 #include <libecal/e-cal-component-alarm-trigger.h>
 #include <libecal/e-cal-component-attendee.h>
+#include <libecal/e-cal-component-property-bag.h>
 #include <libecal/e-cal-component-text.h>
 #include <libecal/e-cal-enums.h>
 
@@ -120,6 +121,9 @@ void                e_cal_component_alarm_set_attachments
 void           e_cal_component_alarm_take_attachments
                                                (ECalComponentAlarm *alarm,
                                                 GSList *attachments); /* ICalAttach * */
+ECalComponentPropertyBag *
+               e_cal_component_alarm_get_property_bag
+                                               (const ECalComponentAlarm *alarm);
 
 G_END_DECLS
 
diff --git a/src/calendar/libecal/e-cal-component-property-bag.c 
b/src/calendar/libecal/e-cal-component-property-bag.c
new file mode 100644
index 000000000..8a7107e69
--- /dev/null
+++ b/src/calendar/libecal/e-cal-component-property-bag.c
@@ -0,0 +1,354 @@
+/*
+ * Copyright (C) 2019 Red Hat, Inc. (www.redhat.com)
+ *
+ * This 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.
+ *
+ * This 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 this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "evolution-data-server-config.h"
+
+/**
+ * SECTION:e-cal-component-property-bag
+ * @short_description: An ECalComponentPropertyBag structure
+ * @include: libecal/libecal.h
+ *
+ * Contains functions to work with the #ECalComponentPropertyBag structure.
+ **/
+
+#include "e-cal-component-property-bag.h"
+
+G_DEFINE_BOXED_TYPE (ECalComponentPropertyBag, e_cal_component_property_bag, 
e_cal_component_property_bag_copy, e_cal_component_property_bag_free)
+
+struct _ECalComponentPropertyBag {
+       GPtrArray *properties; /* ICalProperty * */
+};
+
+/**
+ * e_cal_component_property_bag_new:
+ *
+ * Creates a new #ECalComponentPropertyBag. Free the structure
+ * with e_cal_component_property_bag_free(), when no longer needed.
+ *
+ * Returns: (transfer full): a newly allocated #ECalComponentPropertyBag
+ *
+ * Since: 3.36
+ **/
+ECalComponentPropertyBag *
+e_cal_component_property_bag_new (void)
+{
+       ECalComponentPropertyBag *bag;
+
+       bag = g_new0 (ECalComponentPropertyBag, 1);
+       bag->properties = g_ptr_array_new_with_free_func (g_object_unref);
+
+       return bag;
+}
+
+/**
+ * e_cal_component_property_bag_new_from_component:
+ * @component: an #ICalComponent containing the properties to fill the bag with
+ * @func: (nullable) (scope call): an optional %ECalComponentPropertyBagFilterFunc callback
+ * @user_data: (closure func): user data for the @func
+ *
+ * Created a new #ECalComponentPropertyBag, filled with properties
+ * from the @component, for which the @func returned %TRUE. When
+ * the @func is %NULL, all the properties are included.
+ *
+ * Free the structure with e_cal_component_property_bag_free(), when no longer needed.
+ *
+ * Returns: (transfer full): a newly allocated #ECalComponentPropertyBag
+ *
+ * Since: 3.36
+ **/
+ECalComponentPropertyBag *
+e_cal_component_property_bag_new_from_component (const ICalComponent *component,
+                                                ECalComponentPropertyBagFilterFunc func,
+                                                gpointer user_data)
+{
+       ECalComponentPropertyBag *bag;
+
+       bag = e_cal_component_property_bag_new ();
+
+       e_cal_component_property_bag_set_from_component (bag, component, func, user_data);
+
+       return bag;
+}
+
+/**
+ * e_cal_component_property_bag_copy:
+ * @bag: (not nullable): an #ECalComponentPropertyBag
+ *
+ * Returns a newly allocated copy of @bag, which should be freed with
+ * e_cal_component_property_bag_free(), when no longer needed.
+ *
+ * Returns: (transfer full): a newly allocated copy of @bag
+ *
+ * Since: 3.36
+ **/
+ECalComponentPropertyBag *
+e_cal_component_property_bag_copy (const ECalComponentPropertyBag *bag)
+{
+       ECalComponentPropertyBag *copy;
+
+       g_return_val_if_fail (bag != NULL, NULL);
+
+       copy = e_cal_component_property_bag_new ();
+
+       e_cal_component_property_bag_assign (copy, bag);
+
+       return copy;
+}
+
+/**
+ * e_cal_component_property_bag_free: (skip)
+ * @bag: (type ECalComponentPropertyBag) (nullable): an #ECalComponentPropertyBag to free
+ *
+ * Free @bag, previously created by e_cal_component_property_bag_new(),
+ * e_cal_component_property_bag_new_from_component() or
+ * e_cal_component_property_bag_copy(). The function does nothing, if @bag
+ * is %NULL.
+ *
+ * Since: 3.36
+ **/
+void
+e_cal_component_property_bag_free (gpointer bag)
+{
+       ECalComponentPropertyBag *bg = bag;
+
+       if (bg) {
+               g_ptr_array_unref (bg->properties);
+               g_free (bg);
+       }
+}
+
+/**
+ * e_cal_component_property_bag_set_from_component:
+ * @bag: an #ECalComponentPropertyBag
+ * @component: an #ICalComponent containing the properties to fill the @bag with
+ * @func: (nullable) (scope call): an optional %ECalComponentPropertyBagFilterFunc callback
+ * @user_data: (closure func): user data for the @func
+ *
+ * Fills the @bag with properties from the @component, for which the @func
+ * returned %TRUE. When the @func is %NULL, all the properties are included.
+ * The @bag content is cleared before any property is added.
+ *
+ * Since: 3.36
+ **/
+void
+e_cal_component_property_bag_set_from_component (ECalComponentPropertyBag *bag,
+                                                const ICalComponent *component,
+                                                ECalComponentPropertyBagFilterFunc func,
+                                                gpointer user_data)
+{
+       ICalComponent *comp = (ICalComponent *) component;
+       ICalProperty *prop;
+
+       g_return_if_fail (bag != NULL);
+       g_return_if_fail (I_CAL_IS_COMPONENT (component));
+
+       e_cal_component_property_bag_clear (bag);
+
+       for (prop = i_cal_component_get_first_property (comp, I_CAL_ANY_PROPERTY);
+            prop;
+            g_object_unref (prop), prop = i_cal_component_get_next_property (comp, I_CAL_ANY_PROPERTY)) {
+               if (!func || func (prop, user_data)) {
+                       e_cal_component_property_bag_add (bag, prop);
+               }
+       }
+}
+
+/**
+ * e_cal_component_property_bag_fill_component:
+ * @bag: an #ECalComponentPropertyBag
+ * @component: an #ICalComponent
+ *
+ * Adds all the stores properties in the @bag to the @component.
+ * The function doesn't verify whether the @component contains
+ * the same property already.
+ *
+ * Since: 3.36
+ **/
+void
+e_cal_component_property_bag_fill_component (const ECalComponentPropertyBag *bag,
+                                            ICalComponent *component)
+{
+       guint ii;
+
+       g_return_if_fail (bag != NULL);
+       g_return_if_fail (I_CAL_IS_COMPONENT (component));
+
+       for (ii = 0; ii < bag->properties->len; ii++) {
+               ICalProperty *prop = g_ptr_array_index (bag->properties, ii);
+
+               if (prop)
+                       i_cal_component_take_property (component, i_cal_property_new_clone (prop));
+       }
+}
+
+/**
+ * e_cal_component_property_bag_assign:
+ * @bag: a destination #ECalComponentPropertyBag
+ * @src_bag: a source #ECalComponentPropertyBag
+ *
+ * Assigns content of the @src_bag into the @bag.
+ *
+ * Since: 3.36
+ **/
+void
+e_cal_component_property_bag_assign (ECalComponentPropertyBag *bag,
+                                    const ECalComponentPropertyBag *src_bag)
+{
+       guint count, ii;
+
+       g_return_if_fail (bag != NULL);
+       g_return_if_fail (src_bag != NULL);
+
+       e_cal_component_property_bag_clear (bag);
+       count = e_cal_component_property_bag_get_count (src_bag);
+
+       if (count) {
+               for (ii = 0; ii < count; ii++) {
+                       ICalProperty *prop;
+
+                       prop = e_cal_component_property_bag_get (src_bag, ii);
+
+                       e_cal_component_property_bag_add (bag, prop);
+               }
+       }
+}
+
+/**
+ * e_cal_component_property_bag_add:
+ * @bag: an #ECalComponentPropertyBag
+ * @prop: an #ICalProperty
+ *
+ * Adds a copy of the @prop into the @bag.
+ *
+ * Since: 3.36
+ **/
+void
+e_cal_component_property_bag_add (ECalComponentPropertyBag *bag,
+                                 const ICalProperty *prop)
+{
+       g_return_if_fail (bag != NULL);
+       g_return_if_fail (I_CAL_IS_PROPERTY (prop));
+
+       e_cal_component_property_bag_take (bag,
+               i_cal_property_new_clone ((ICalProperty *) prop));
+}
+
+/**
+ * e_cal_component_property_bag_take:
+ * @bag: an #ECalComponentPropertyBag
+ * @prop: an #ICalProperty
+ *
+ * Adds the @prop into the @bag and assumes ownership of the @prop.
+ *
+ * Since: 3.36
+ **/
+void
+e_cal_component_property_bag_take (ECalComponentPropertyBag *bag,
+                                  ICalProperty *prop)
+{
+       g_return_if_fail (bag != NULL);
+       g_return_if_fail (I_CAL_IS_PROPERTY (prop));
+
+       g_ptr_array_add (bag->properties, prop);
+}
+
+/**
+ * e_cal_component_property_bag_get_count:
+ * @bag: an #ECalComponentPropertyBag
+ *
+ * Returns: how many properties are stored in the @bag
+ *
+ * Since: 3.36
+ **/
+guint
+e_cal_component_property_bag_get_count (const ECalComponentPropertyBag *bag)
+{
+       g_return_val_if_fail (bag != NULL, 0);
+       g_return_val_if_fail (bag->properties != NULL, 0);
+
+       return bag->properties->len;
+}
+
+/**
+ * e_cal_component_property_bag_get:
+ * @bag: an #ECalComponentPropertyBag
+ * @index: an index of the property to get
+ *
+ * Returns the #ICalProperty at the given @index. If the @index is
+ * out of bounds (not lower than e_cal_component_property_bag_get_count()),
+ * then %NULL is returned.
+ *
+ * The returned property is owned by the @bag and should not be freed
+ * by the caller.
+ *
+ * Returns: (transfer none) (nullable): the #ICalProperty at the given @index,
+ *    or %NULL on error
+ *
+ * Since: 3.36
+ **/
+ICalProperty *
+e_cal_component_property_bag_get (const ECalComponentPropertyBag *bag,
+                                 guint index)
+{
+       g_return_val_if_fail (bag != NULL, NULL);
+       g_return_val_if_fail (bag->properties != NULL, NULL);
+
+       if (index >= bag->properties->len)
+               return NULL;
+
+       return g_ptr_array_index (bag->properties, index);
+}
+
+/**
+ * e_cal_component_property_bag_remove:
+ * @bag: an #ECalComponentPropertyBag
+ * @index: an index of the property to remove
+ *
+ * Removes the #ICalProperty at the given @index. If the @index is
+ * out of bounds (not lower than e_cal_component_property_bag_get_count()),
+ * then the function does nothing.
+ *
+ * Since: 3.36
+ **/
+void
+e_cal_component_property_bag_remove (ECalComponentPropertyBag *bag,
+                                    guint index)
+{
+       g_return_if_fail (bag != NULL);
+       g_return_if_fail (bag->properties != NULL);
+
+       if (index < bag->properties->len)
+               g_ptr_array_remove_index (bag->properties, index);
+}
+
+/**
+ * e_cal_component_property_bag_clear:
+ * @bag: an #ECalComponentPropertyBag
+ *
+ * Removes all properties from the @bag, thus it doesn't contain any
+ * property after this function returns.
+ *
+ * Since: 3.36
+ **/
+void
+e_cal_component_property_bag_clear (ECalComponentPropertyBag *bag)
+{
+       g_return_if_fail (bag != NULL);
+       g_return_if_fail (bag->properties != NULL);
+
+       g_ptr_array_set_size (bag->properties, 0);
+}
diff --git a/src/calendar/libecal/e-cal-component-property-bag.h 
b/src/calendar/libecal/e-cal-component-property-bag.h
new file mode 100644
index 000000000..c029e2dcd
--- /dev/null
+++ b/src/calendar/libecal/e-cal-component-property-bag.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2019 Red Hat, Inc. (www.redhat.com)
+ *
+ * This 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.
+ *
+ * This 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 this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#if !defined (__LIBECAL_H_INSIDE__) && !defined (LIBECAL_COMPILATION)
+#error "Only <libecal/libecal.h> should be included directly."
+#endif
+
+#ifndef E_CAL_COMPONENT_PROPERTY_BAG_H
+#define E_CAL_COMPONENT_PROPERTY_BAG_H
+
+#include <glib-object.h>
+#include <libical-glib/libical-glib.h>
+
+G_BEGIN_DECLS
+
+/**
+ * ECalComponentPropertyBag:
+ *
+ * Opaque structure, which represents a bad (list) of #ICalProperty objects.
+ * Use the functions below to work with it.
+ **/
+typedef struct _ECalComponentPropertyBag ECalComponentPropertyBag;
+
+/**
+ * ECalComponentPropertyBagFilterFunc:
+ * @property: an #ICalProperty
+ * @user_data: user data for the callback
+ *
+ * A function used to filter which properties should be added to the bag,
+ * when filling it with e_cal_component_property_bag_new_from_component()
+ * and e_cal_component_property_bag_set_from_component().
+ *
+ * Returns: %TRUE, to add the property to the bag; %FALSE, to not add it to the bag
+ *
+ * Since: 3.36
+ **/
+typedef gboolean (* ECalComponentPropertyBagFilterFunc)
+                                               (ICalProperty *property,
+                                                gpointer user_data);
+
+GType          e_cal_component_property_bag_get_type
+                                               (void);
+ECalComponentPropertyBag *
+               e_cal_component_property_bag_new(void);
+ECalComponentPropertyBag *
+               e_cal_component_property_bag_new_from_component
+                                               (const ICalComponent *component,
+                                                ECalComponentPropertyBagFilterFunc func,
+                                                gpointer user_data);
+ECalComponentPropertyBag *
+               e_cal_component_property_bag_copy
+                                               (const ECalComponentPropertyBag *bag);
+void           e_cal_component_property_bag_free
+                                               (gpointer bag); /* ECalComponentPropertyBag * */
+void           e_cal_component_property_bag_set_from_component
+                                               (ECalComponentPropertyBag *bag,
+                                                const ICalComponent *component,
+                                                ECalComponentPropertyBagFilterFunc func,
+                                                gpointer user_data);
+void           e_cal_component_property_bag_fill_component
+                                               (const ECalComponentPropertyBag *bag,
+                                                ICalComponent *component);
+void           e_cal_component_property_bag_assign
+                                               (ECalComponentPropertyBag *bag,
+                                                const ECalComponentPropertyBag *src_bag);
+void           e_cal_component_property_bag_add(ECalComponentPropertyBag *bag,
+                                                const ICalProperty *prop);
+void           e_cal_component_property_bag_take
+                                               (ECalComponentPropertyBag *bag,
+                                                ICalProperty *prop);
+guint          e_cal_component_property_bag_get_count
+                                               (const ECalComponentPropertyBag *bag);
+ICalProperty * e_cal_component_property_bag_get(const ECalComponentPropertyBag *bag,
+                                                guint index);
+void           e_cal_component_property_bag_remove
+                                               (ECalComponentPropertyBag *bag,
+                                                guint index);
+void           e_cal_component_property_bag_clear
+                                               (ECalComponentPropertyBag *bag);
+
+G_END_DECLS
+
+#endif /* E_CAL_COMPONENT_PROPERTY_BAG_H */
diff --git a/src/calendar/libecal/libecal.h b/src/calendar/libecal/libecal.h
index f3abf2bb8..7af321a04 100644
--- a/src/calendar/libecal/libecal.h
+++ b/src/calendar/libecal/libecal.h
@@ -39,6 +39,7 @@
 #include <libecal/e-cal-component-id.h>
 #include <libecal/e-cal-component-organizer.h>
 #include <libecal/e-cal-component-period.h>
+#include <libecal/e-cal-component-property-bag.h>
 #include <libecal/e-cal-component-range.h>
 #include <libecal/e-cal-component-text.h>
 #include <libecal/e-cal-enums.h>
diff --git a/tests/libecal/test-cal-component.c b/tests/libecal/test-cal-component.c
index 143d78db1..9ea8d5c51 100644
--- a/tests/libecal/test-cal-component.c
+++ b/tests/libecal/test-cal-component.c
@@ -321,6 +321,43 @@ verify_struct_alarm_trigger_equal (const ECalComponentAlarmTrigger *expected,
        }
 }
 
+static void
+verify_property_bag_equal (const ECalComponentPropertyBag *expected,
+                          const ECalComponentPropertyBag *received)
+{
+       gint ii, count;
+
+       if (!expected) {
+               g_assert_null (received);
+               return;
+       }
+
+       g_assert_nonnull (received);
+
+       g_assert_cmpint (e_cal_component_property_bag_get_count (expected), ==, 
e_cal_component_property_bag_get_count (received));
+
+       count = e_cal_component_property_bag_get_count (expected);
+       for (ii = 0; ii < count; ii++) {
+               ICalProperty *prop_expected, *prop_received;
+               gchar *value_expected, *value_received;
+
+               prop_expected = e_cal_component_property_bag_get (expected, ii);
+               prop_received = e_cal_component_property_bag_get (received, ii);
+
+               g_assert_nonnull (prop_expected);
+               g_assert_nonnull (prop_received);
+               g_assert_cmpint (i_cal_property_isa (prop_expected), ==, i_cal_property_isa (prop_received));
+
+               value_expected = i_cal_property_as_ical_string_r (prop_expected);
+               value_received = i_cal_property_as_ical_string_r (prop_received);
+
+               g_assert_cmpstr (value_expected, ==, value_received);
+
+               g_free (value_expected);
+               g_free (value_received);
+       }
+}
+
 static void
 verify_struct_alarm_equal (const ECalComponentAlarm *expected,
                           const ECalComponentAlarm *received)
@@ -353,6 +390,9 @@ verify_struct_alarm_equal (const ECalComponentAlarm *expected,
 
        verify_ical_attach_list_equal (e_cal_component_alarm_get_attachments (expected),
                                       e_cal_component_alarm_get_attachments (received));
+
+       verify_property_bag_equal (e_cal_component_alarm_get_property_bag (expected),
+                                  e_cal_component_alarm_get_property_bag (received));
 }
 
 static void
@@ -420,17 +460,18 @@ test_component_struct_alarm (void)
                gboolean with_trigger;
                gboolean with_attendees;
                gboolean with_attachments;
+               gboolean with_properties;
        } values[] = {
-               { "1", E_CAL_COMPONENT_ALARM_AUDIO, NULL, FALSE, FALSE, FALSE, FALSE },
-               { "2", E_CAL_COMPONENT_ALARM_DISPLAY, "display text", FALSE, FALSE, FALSE, FALSE },
-               { "3", E_CAL_COMPONENT_ALARM_EMAIL, NULL, TRUE, FALSE, FALSE, FALSE },
-               { "4", E_CAL_COMPONENT_ALARM_PROCEDURE, "procedure", FALSE, TRUE, FALSE, FALSE },
-               { "5", E_CAL_COMPONENT_ALARM_AUDIO, NULL, FALSE, FALSE, TRUE, FALSE },
-               { "6", E_CAL_COMPONENT_ALARM_DISPLAY, "display text", FALSE, FALSE, FALSE, TRUE },
-               { "7", E_CAL_COMPONENT_ALARM_EMAIL, NULL, TRUE, FALSE, TRUE, FALSE },
-               { "8", E_CAL_COMPONENT_ALARM_PROCEDURE, "procedure", TRUE, TRUE, TRUE, TRUE }
+               { "1", E_CAL_COMPONENT_ALARM_AUDIO, NULL, FALSE, FALSE, FALSE, FALSE, FALSE },
+               { "2", E_CAL_COMPONENT_ALARM_DISPLAY, "display text", FALSE, FALSE, FALSE, FALSE, FALSE },
+               { "3", E_CAL_COMPONENT_ALARM_EMAIL, NULL, TRUE, FALSE, FALSE, FALSE, FALSE },
+               { "4", E_CAL_COMPONENT_ALARM_PROCEDURE, "procedure", FALSE, TRUE, FALSE, FALSE, TRUE },
+               { "5", E_CAL_COMPONENT_ALARM_AUDIO, NULL, FALSE, FALSE, TRUE, FALSE, TRUE },
+               { "6", E_CAL_COMPONENT_ALARM_DISPLAY, "display text", FALSE, FALSE, FALSE, TRUE, TRUE },
+               { "7", E_CAL_COMPONENT_ALARM_EMAIL, NULL, TRUE, FALSE, TRUE, FALSE, TRUE },
+               { "8", E_CAL_COMPONENT_ALARM_PROCEDURE, "procedure", TRUE, TRUE, TRUE, TRUE, TRUE }
        };
-       gint ii, nth_description = 0, nth_repeat = 0, nth_trigger = 0, nth_attendees = 0, nth_attachments = 0;
+       gint ii, nth_description = 0, nth_repeat = 0, nth_trigger = 0, nth_attendees = 0, nth_attachments = 
0, nth_properties = 0;
 
        for (ii = 0; ii < G_N_ELEMENTS (values); ii++) {
                ECalComponentAlarm *expected, *received;
@@ -554,6 +595,40 @@ test_component_struct_alarm (void)
                        }
                }
 
+               if (values[ii].with_properties) {
+                       ECalComponentPropertyBag *bag;
+                       gint ii;
+
+                       nth_properties++;
+
+                       bag = e_cal_component_alarm_get_property_bag (expected);
+
+                       g_assert_nonnull (bag);
+
+                       for (ii = nth_properties; ii > 0; ii--) {
+                               ICalProperty *prop;
+
+                               if (ii == 0) {
+                                       prop = i_cal_property_new_url ("https://www.gnome.org";);
+                               } else if (ii == 1) {
+                                       prop = i_cal_property_new_voter ("MAILTO:user@no.where");
+                               } else {
+                                       gchar *x_name;
+
+                                       x_name = g_strdup_printf ("X-CUSTOM-PROP-%d", ii);
+                                       prop = i_cal_property_new_x (x_name + 2);
+                                       i_cal_property_set_x_name (prop, x_name);
+                                       g_free (x_name);
+                               }
+
+                               g_assert_nonnull (prop);
+
+                               e_cal_component_property_bag_take (bag, prop);
+                       }
+
+                       g_assert_cmpint (e_cal_component_property_bag_get_count (bag), ==, nth_properties);
+               }
+
                received = e_cal_component_alarm_copy (expected);
                verify_struct_alarm_equal (expected, received);
                e_cal_component_alarm_free (received);
@@ -582,6 +657,7 @@ test_component_struct_alarm (void)
        g_assert_cmpint (nth_trigger, >, 1);
        g_assert_cmpint (nth_attendees, >, 1);
        g_assert_cmpint (nth_attachments, >, 1);
+       g_assert_cmpint (nth_properties, >, 1);
 }
 
 static void
@@ -1382,6 +1458,181 @@ test_component_struct_period (void)
        g_assert_cmpint (flipflop2, >, 2);
 }
 
+#define X_PROP_NAME "X-PROP"
+#define X_PROP_VALUE "xVaLuE"
+
+static gboolean
+test_property_bag_filter_cb (ICalProperty *prop,
+                            gpointer user_data)
+{
+       ICalPropertyKind *expected = user_data, kind;
+       gint ii;
+
+       g_return_val_if_fail (expected != NULL, FALSE);
+
+       kind = i_cal_property_isa (prop);
+
+       for (ii = 0; expected[ii] != I_CAL_ANY_PROPERTY; ii++) {
+               if (kind == expected[ii])
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
+static void
+test_check_property_bag (const ECalComponentPropertyBag *bag,
+                        const ICalPropertyKind *expected)
+{
+       ICalProperty *prop;
+       gint ii;
+
+       g_assert_nonnull (bag);
+       g_assert_nonnull (expected);
+
+       for (ii = 0; expected[ii] != I_CAL_ANY_PROPERTY; ii++) {
+               prop = e_cal_component_property_bag_get (bag, ii);
+
+               g_assert_nonnull (prop);
+               g_assert_cmpint (i_cal_property_isa (prop), ==, expected[ii]);
+               if (i_cal_property_isa (prop) == I_CAL_X_PROPERTY) {
+                       g_assert_cmpstr (i_cal_property_get_x_name (prop), ==, X_PROP_NAME);
+                       g_assert_cmpstr (i_cal_property_get_x (prop), ==, X_PROP_VALUE);
+               }
+       }
+
+       /* Out of bounds */
+       prop = e_cal_component_property_bag_get (bag, ii);
+       g_assert_null (prop);
+}
+
+static void
+test_component_struct_property_bag (void)
+{
+       const gchar *comp_str =
+               "BEGIN:VTODO\r\n"
+               "UID:1\r\n"
+               "STATUS:CANCELLED\r\n"
+               X_PROP_NAME ":" X_PROP_VALUE "\r\n"
+               "END:VTODO\r\n";
+       ICalPropertyKind expected_unfiltered[] = {
+               I_CAL_UID_PROPERTY,
+               I_CAL_STATUS_PROPERTY,
+               I_CAL_X_PROPERTY,
+               I_CAL_ANY_PROPERTY /* sentinel */
+       },
+       expected_filtered[] = {
+               I_CAL_STATUS_PROPERTY,
+               I_CAL_X_PROPERTY,
+               I_CAL_ANY_PROPERTY /* sentinel */
+       };
+       ICalComponent *icomp;
+       ICalProperty *prop;
+       ECalComponentPropertyBag *bag, *bag2;
+       gint ii;
+
+       icomp = i_cal_component_new_from_string (comp_str);
+       g_assert_nonnull (icomp);
+
+       bag = e_cal_component_property_bag_new ();
+       g_assert_nonnull (bag);
+       e_cal_component_property_bag_set_from_component (bag, icomp, NULL, NULL);
+       g_assert_cmpint (e_cal_component_property_bag_get_count (bag), ==, G_N_ELEMENTS (expected_unfiltered) 
- 1);
+       test_check_property_bag (bag, expected_unfiltered);
+
+       bag2 = e_cal_component_property_bag_copy (bag);
+       g_assert_nonnull (bag2);
+       g_assert_cmpint (e_cal_component_property_bag_get_count (bag2), ==, G_N_ELEMENTS 
(expected_unfiltered) - 1);
+       test_check_property_bag (bag2, expected_unfiltered);
+       e_cal_component_property_bag_free (bag2);
+       e_cal_component_property_bag_free (bag);
+
+       bag = e_cal_component_property_bag_new_from_component (icomp, NULL, NULL);
+       g_assert_nonnull (bag);
+       g_assert_cmpint (e_cal_component_property_bag_get_count (bag), ==, G_N_ELEMENTS (expected_unfiltered) 
- 1);
+       test_check_property_bag (bag, expected_unfiltered);
+       e_cal_component_property_bag_free (bag);
+
+       bag = e_cal_component_property_bag_new ();
+       g_assert_nonnull (bag);
+       e_cal_component_property_bag_set_from_component (bag, icomp, test_property_bag_filter_cb, 
expected_filtered);
+       g_assert_cmpint (e_cal_component_property_bag_get_count (bag), ==, G_N_ELEMENTS (expected_filtered) - 
1);
+       test_check_property_bag (bag, expected_filtered);
+       e_cal_component_property_bag_free (bag);
+
+       bag = e_cal_component_property_bag_new_from_component (icomp, test_property_bag_filter_cb, 
expected_filtered);
+       g_assert_nonnull (bag);
+       g_assert_cmpint (e_cal_component_property_bag_get_count (bag), ==, G_N_ELEMENTS (expected_filtered) - 
1);
+       test_check_property_bag (bag, expected_filtered);
+
+       g_object_unref (icomp);
+
+       icomp = i_cal_component_new_vevent ();
+       g_assert_nonnull (icomp);
+       e_cal_component_property_bag_fill_component (bag, icomp);
+       g_assert_cmpint (i_cal_component_count_properties (icomp, I_CAL_ANY_PROPERTY), ==, 
e_cal_component_property_bag_get_count (bag));
+       g_object_unref (icomp);
+
+       bag2 = e_cal_component_property_bag_copy (bag);
+
+       while (e_cal_component_property_bag_get_count (bag) > 1) {
+               e_cal_component_property_bag_remove (bag, 1);
+       }
+
+       e_cal_component_property_bag_assign (bag2, bag);
+       g_assert_cmpint (e_cal_component_property_bag_get_count (bag), ==, 
e_cal_component_property_bag_get_count (bag2));
+       e_cal_component_property_bag_free (bag2);
+
+       icomp = i_cal_component_new_vevent ();
+       g_assert_nonnull (icomp);
+       e_cal_component_property_bag_fill_component (bag, icomp);
+       g_assert_cmpint (i_cal_component_count_properties (icomp, I_CAL_ANY_PROPERTY), ==, 1);
+       g_object_unref (icomp);
+
+       e_cal_component_property_bag_clear (bag);
+
+       icomp = i_cal_component_new_vevent ();
+       g_assert_nonnull (icomp);
+       e_cal_component_property_bag_fill_component (bag, icomp);
+       g_assert_cmpint (i_cal_component_count_properties (icomp, I_CAL_ANY_PROPERTY), ==, 0);
+       g_object_unref (icomp);
+
+       prop = i_cal_property_new_uid ("234");
+       e_cal_component_property_bag_add (bag, prop);
+       g_object_unref (prop);
+
+       prop = i_cal_property_new_status (I_CAL_STATUS_CANCELLED);
+       e_cal_component_property_bag_take (bag, prop);
+
+       g_assert_cmpint (e_cal_component_property_bag_get_count (bag), ==, 2);
+
+       for (ii = 0; ii < 2; ii++) {
+               ICalProperty *prop2;
+
+               prop2 = e_cal_component_property_bag_get (bag, ii);
+               if (ii == 0) {
+                       g_assert (prop != prop2);
+                       g_assert_cmpint (i_cal_property_isa (prop2), ==, I_CAL_UID_PROPERTY);
+                       g_assert_cmpstr (i_cal_property_get_uid (prop2), ==, "234");
+               } else {
+                       g_assert (prop == prop2);
+                       g_assert_cmpint (i_cal_property_isa (prop2), ==, I_CAL_STATUS_PROPERTY);
+                       g_assert_cmpint (i_cal_property_get_status (prop2), ==, I_CAL_STATUS_CANCELLED);
+               }
+       }
+
+       icomp = i_cal_component_new_vevent ();
+       g_assert_nonnull (icomp);
+       e_cal_component_property_bag_fill_component (bag, icomp);
+       g_assert_cmpint (i_cal_component_count_properties (icomp, I_CAL_ANY_PROPERTY), ==, 
e_cal_component_property_bag_get_count (bag));
+       g_object_unref (icomp);
+
+       e_cal_component_property_bag_free (bag);
+}
+
+#undef X_PROP_NAME
+#undef X_PROP_VALUE
+
 static void
 test_component_struct_range (void)
 {
@@ -3367,6 +3618,7 @@ main (gint argc,
        g_test_add_func ("/ECalComponent/struct/Id", test_component_struct_id);
        g_test_add_func ("/ECalComponent/struct/Organizer", test_component_struct_organizer);
        g_test_add_func ("/ECalComponent/struct/Period", test_component_struct_period);
+       g_test_add_func ("/ECalComponent/struct/PropertyBag", test_component_struct_property_bag);
        g_test_add_func ("/ECalComponent/struct/Range", test_component_struct_range);
        g_test_add_func ("/ECalComponent/struct/Text", test_component_struct_text);
        g_test_add_func ("/ECalComponent/vtype", test_component_vtype);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]