[california/wip/731543-attendees: 5/5] Using Attendee class rather than strings for storage
- From: Jim Nelson <jnelson src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [california/wip/731543-attendees: 5/5] Using Attendee class rather than strings for storage
- Date: Wed, 5 Nov 2014 03:21:04 +0000 (UTC)
commit 9c3a9ede326821d9a8b179062042e48442d53b14
Author: Jim Nelson <jim yorba org>
Date: Tue Nov 4 19:20:26 2014 -0800
Using Attendee class rather than strings for storage
po/POTFILES.in | 1 +
po/POTFILES.skip | 1 +
src/Makefile.am | 1 +
src/component/component-attendee.vala | 108 +++++++++++++++++++++++++++++++++
src/component/component-instance.vala | 64 ++++++++------------
5 files changed, 136 insertions(+), 39 deletions(-)
---
diff --git a/po/POTFILES.in b/po/POTFILES.in
index b458ec9..f8d396f 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -14,6 +14,7 @@ src/calendar/calendar-day-of-week.vala
src/calendar/calendar-exact-time-span.vala
src/calendar/calendar.vala
src/component/component.vala
+src/component/component-instance.vala
src/component/component-recurrence-rule.vala
src/host/host-create-update-event.vala
src/host/host-create-update-recurring.vala
diff --git a/po/POTFILES.skip b/po/POTFILES.skip
index ac5b36b..09d8111 100644
--- a/po/POTFILES.skip
+++ b/po/POTFILES.skip
@@ -12,6 +12,7 @@ src/calendar/calendar-day-of-week.c
src/calendar/calendar-exact-time-span.c
src/component/component.c
src/component/component-event.c
+src/component/component-instance.c
src/component/component-recurrence-rule.c
src/host/host-create-update-event.c
src/host/host-import-calendar.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 1bb8c3b..c4d6bdf 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -85,6 +85,7 @@ california_VALASOURCES = \
collection/collection-simple-iterable.vala \
\
component/component.vala \
+ component/component-attendee.vala \
component/component-date-time.vala \
component/component-details-parser.vala \
component/component-error.vala \
diff --git a/src/component/component-attendee.vala b/src/component/component-attendee.vala
new file mode 100644
index 0000000..7690463
--- /dev/null
+++ b/src/component/component-attendee.vala
@@ -0,0 +1,108 @@
+/* Copyright 2014 Yorba Foundation
+ *
+ * This software is licensed under the GNU Lesser General Public License
+ * (version 2.1 or later). See the COPYING file in this distribution.
+ */
+
+namespace California.Component {
+
+/**
+ * An immutable representation of an iCalendar ATTENDEE.
+ *
+ * For equality purposes, only the { link mailto} is used. All other parameters are ignored.
+ *
+ * See [[https://tools.ietf.org/html/rfc5545#section-3.8.4.1]]
+ */
+
+public class Attendee : BaseObject, Gee.Hashable<Attendee> {
+ /**
+ * The mailto: of the ATTENDEE, the only required value for the property.
+ */
+ public Soup.URI mailto { get; private set; }
+
+ /**
+ * The CN (common name) for the ATTENDEE.
+ */
+ public string? common_name { get; private set; default = null; }
+
+ /**
+ * The { link mailto} URI as a string.
+ */
+ public string mailto_string { owned get {
+ return mailto.to_string(false);
+ } }
+
+ /**
+ * The { link mailto} URI as a simple (unadorned) RFC822 mailbox (i.e. email address).
+ */
+ public string mailbox { owned get {
+ return mailto.path;
+ } }
+
+ private Gee.HashSet<string> parameters = new Gee.HashSet<string>(String.ci_hash, String.ci_equal);
+
+ /**
+ * Create an Attendee with the required { link mailto} and optional { link common_name}.
+ */
+ public Attendee(Soup.URI mailto, string? common_name) throws ComponentError {
+ validate_mailto(mailto);
+
+ this.mailto = mailto;
+ this.common_name = common_name;
+ }
+
+ internal Attendee.from_property(iCal.icalproperty prop) throws Error {
+ string attendee = prop.get_attendee();
+ if (String.is_empty(attendee))
+ throw new ComponentError.INVALID("Invalid attendee property: no value");
+
+ mailto = URI.parse(attendee);
+ validate_mailto(mailto);
+
+ unowned iCal.icalparameter? param = prop.get_first_parameter(iCal.icalparameter_kind.ANY_PARAMETER);
+ while (param != null) {
+ parameters.add(param.as_ical_string());
+
+ // parse parameter into well-known (common) property
+ switch (param.isa()) {
+ case iCal.icalparameter_kind.CN_PARAMETER:
+ common_name = param.get_cn();
+ break;
+
+ default:
+ // fall-through
+ break;
+ }
+
+ param = prop.get_next_parameter(iCal.icalparameter_kind.ANY_PARAMETER);
+ }
+ }
+
+ private static void validate_mailto(Soup.URI uri) throws ComponentError {
+ if (uri.scheme != "mailto" || String.is_empty(uri.path))
+ throw new ComponentError.INVALID("Invalid attendee mailto: %s", uri.to_string(false));
+ }
+
+ internal iCal.icalproperty as_ical_property() {
+ iCal.icalproperty prop = new iCal.icalproperty.attendee(mailto_string);
+ foreach (string parameter in parameters)
+ prop.add_parameter(new iCal.icalparameter.from_string(parameter));
+
+ return prop;
+ }
+
+ public uint hash() {
+ return String.ci_hash(mailto_string);
+ }
+
+ public bool equal_to(Attendee other) {
+ return (this != other) ? mailto.equal(other.mailto) : true;
+ }
+
+ public override string to_string() {
+ return mailto_string;
+ }
+}
+
+}
+
diff --git a/src/component/component-instance.vala b/src/component/component-instance.vala
index acdb8b3..1002fa6 100644
--- a/src/component/component-instance.vala
+++ b/src/component/component-instance.vala
@@ -174,16 +174,16 @@ public abstract class Instance : BaseObject, Gee.Hashable<Instance> {
/**
* ATTENDEEs for a VEVENT, VTODO, or VJOURNAL.
*
- * This property returns a read-only view of the list of attendees. To add or remove attendees,
- * use { link add_attendees}, { link remove_attendees}, and { link clear_attendees}.
- * Because those methods always update the property itself and not merely modify the list,
- * the property can be watched for changes with the "notify" and/or "altered" signals.
+ * To add or remove attendees, use { link add_attendees}, { link remove_attendees}, and
+ * { link clear_attendees}. Because those methods always update the property itself and not
+ * merely modify the list, the property can be watched for changes with the "notify" and/or
+ * "altered" signals.
*
* No validity checking is performed on attendee mailto's.
*
* See [[https://tools.ietf.org/html/rfc5545#section-3.8.4.1]]
*/
- public Gee.List<string> attendees { get; private set; default = new Gee.ArrayList<string>(); }
+ public Gee.Set<Attendee> attendees { get; private set; default = new Gee.HashSet<Attendee>(); }
/**
* The iCal component being represented by this { link Instance}.
@@ -371,9 +371,11 @@ public abstract class Instance : BaseObject, Gee.Hashable<Instance> {
unowned iCal.icalproperty? attendee_prop = ical_component.get_first_property(
iCal.icalproperty_kind.ATTENDEE_PROPERTY);
while (attendee_prop != null) {
- unowned string mailto = attendee_prop.get_attendee();
- if (!String.is_empty(mailto))
- attendees.add(mailto);
+ try {
+ attendees.add(new Attendee.from_property(attendee_prop));
+ } catch (ComponentError comperr) {
+ debug("Unable to parse ATTENDEE for %s: %s", to_string(), comperr.message);
+ }
attendee_prop = ical_component.get_next_property(iCal.icalproperty_kind.ATTENDEE_PROPERTY);
}
@@ -425,14 +427,9 @@ public abstract class Instance : BaseObject, Gee.Hashable<Instance> {
break;
case PROP_ATTENDEES:
- // TODO: Need to update iCal component with adjusted set of attendees, whether
- // removed or appended
- /*
- unowned iCal.icalproperty? attendee_prop = ical_component.get_first_property(
- iCal.icalproperty_kind.ATTENDEE_PROPERTY);
- while (attendee_prop != null) {
- }
- */
+ remove_all_properties(iCal.icalproperty_kind.ATTENDEE_PROPERTY);
+ foreach (Attendee attendee in attendees)
+ ical_component.add_property(attendee.as_ical_property());
break;
default:
@@ -491,9 +488,9 @@ public abstract class Instance : BaseObject, Gee.Hashable<Instance> {
* No URI-format checking is performed here; it is up to the caller to convert and deal with
* formatting issues. Also note that duplicates are allowed in the attendees list.
*/
- public void add_attendees(Gee.Collection<string> mailtos) {
- Gee.List<string> copy = traverse<string>(attendees).to_array_list();
- copy.add_all(mailtos);
+ public void add_attendees(Gee.Collection<Attendee> to_add) {
+ Gee.Set<Attendee> copy = traverse<Attendee>(attendees).to_hash_set();
+ copy.add_all(to_add);
attendees = copy;
}
@@ -503,17 +500,17 @@ public abstract class Instance : BaseObject, Gee.Hashable<Instance> {
*
* See { link add_attendees} for notes about data validity and checking.
*/
- public void remove_attendees(Gee.Collection<string> mailtos) {
- attendees = traverse<string>(attendees)
- .filter(attendee => mailtos.contains(attendee))
- .to_array_list();
+ public void remove_attendees(Gee.Collection<Attendee> to_remove) {
+ attendees = traverse<Attendee>(attendees)
+ .filter(attendee => !to_remove.contains(attendee))
+ .to_hash_set();
}
/*
* Removes all { link attendees}.
*/
public void clear_attendees() {
- attendees = new Gee.ArrayList<string>();
+ attendees = new Gee.HashSet<Attendee>();
}
/**
@@ -522,23 +519,12 @@ public abstract class Instance : BaseObject, Gee.Hashable<Instance> {
* Returns null if no attendees are associated with this { link Instance}.
*/
public string? attendees_to_string() {
- return traverse<string>(attendees).to_string(stringify_attendee);
+ return traverse<Attendee>(attendees).to_string(stringify_attendee);
}
- private static string? stringify_attendee(string attendee, bool is_first, bool is_last) {
- // Must parse correctly into URI
- Soup.URI mailto;
- try {
- mailto = URI.parse(attendee);
- } catch (Error err) {
- return null;
- }
-
- // Must be a non-empty mailto:
- if (mailto.scheme != "mailto" || String.is_empty(mailto.path))
- return null;
-
- return is_first ? mailto.path : ", %s".printf(mailto.path);
+ private static string? stringify_attendee(Attendee attendee, bool is_first, bool is_last) {
+ // A common separator for email addresses followed by an email address itself.
+ return is_first ? attendee.mailbox : _(", %s").printf(attendee.mailbox);
}
/**
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]