summaryrefslogtreecommitdiff
path: root/src/com/google/wireless/gdata/calendar
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/google/wireless/gdata/calendar')
-rw-r--r--src/com/google/wireless/gdata/calendar/client/CalendarClient.java104
-rw-r--r--src/com/google/wireless/gdata/calendar/client/package.html5
-rw-r--r--src/com/google/wireless/gdata/calendar/data/CalendarEntry.java162
-rw-r--r--src/com/google/wireless/gdata/calendar/data/CalendarsFeed.java18
-rw-r--r--src/com/google/wireless/gdata/calendar/data/EventEntry.java315
-rw-r--r--src/com/google/wireless/gdata/calendar/data/EventsFeed.java33
-rw-r--r--src/com/google/wireless/gdata/calendar/data/Recurrence.java28
-rw-r--r--src/com/google/wireless/gdata/calendar/data/Reminder.java92
-rw-r--r--src/com/google/wireless/gdata/calendar/data/When.java53
-rw-r--r--src/com/google/wireless/gdata/calendar/data/Who.java153
-rw-r--r--src/com/google/wireless/gdata/calendar/data/package.html5
-rw-r--r--src/com/google/wireless/gdata/calendar/package.html5
-rw-r--r--src/com/google/wireless/gdata/calendar/parser/package.html5
-rw-r--r--src/com/google/wireless/gdata/calendar/parser/xml/XmlCalendarGDataParserFactory.java98
-rw-r--r--src/com/google/wireless/gdata/calendar/parser/xml/XmlCalendarsGDataParser.java132
-rw-r--r--src/com/google/wireless/gdata/calendar/parser/xml/XmlEventsGDataParser.java419
-rw-r--r--src/com/google/wireless/gdata/calendar/parser/xml/package.html5
-rw-r--r--src/com/google/wireless/gdata/calendar/serializer/package.html5
-rw-r--r--src/com/google/wireless/gdata/calendar/serializer/xml/XmlEventEntryGDataSerializer.java399
-rw-r--r--src/com/google/wireless/gdata/calendar/serializer/xml/package.html5
20 files changed, 2041 insertions, 0 deletions
diff --git a/src/com/google/wireless/gdata/calendar/client/CalendarClient.java b/src/com/google/wireless/gdata/calendar/client/CalendarClient.java
new file mode 100644
index 0000000..8710079
--- /dev/null
+++ b/src/com/google/wireless/gdata/calendar/client/CalendarClient.java
@@ -0,0 +1,104 @@
+// Copyright 2007 The Android Open Source Project
+
+package com.google.wireless.gdata.calendar.client;
+
+import com.google.wireless.gdata.calendar.data.CalendarEntry;
+import com.google.wireless.gdata.client.AuthenticationException;
+import com.google.wireless.gdata.client.GDataClient;
+import com.google.wireless.gdata.client.GDataParserFactory;
+import com.google.wireless.gdata.client.GDataServiceClient;
+import com.google.wireless.gdata.client.HttpException;
+import com.google.wireless.gdata.client.QueryParams;
+import com.google.wireless.gdata.client.AllDeletedUnavailableException;
+import com.google.wireless.gdata.parser.GDataParser;
+import com.google.wireless.gdata.parser.ParseException;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * GDataServiceClient for accessing Google Calendar. This client can access and
+ * parse both the meta feed (list of calendars for a user) and events feeds
+ * (calendar entries for a specific user). The parsers this class uses handle
+ * the XML version of feeds.
+ */
+// TODO: add a method that applies projections such as cutting the attendees.
+public class CalendarClient extends GDataServiceClient {
+ /** Service value for calendar. */
+ public static final String SERVICE = "cl";
+
+ public static final String PROJECTION_PRIVATE_FULL = "/private/full";
+ public static final String PROJECTION_PRIVATE_SELF_ATTENDANCE = "/private/full-selfattendance";
+
+ /** Standard base url for a calendar feed. */
+ private static final String CALENDAR_BASE_FEED_URL =
+ "http://www.google.com/calendar/feeds/";
+
+ /**
+ * Create a new CalendarClient. Uses the standard base URL for calendar feeds.
+ * @param client The GDataClient that should be used to authenticate
+ * requests, retrieve feeds, etc.
+ * @param factory The factory that should be used to obtain {@link GDataParser}s used by this
+ * client.
+ */
+ public CalendarClient(GDataClient client, GDataParserFactory factory) {
+ super(client, factory);
+ }
+
+ /* (non-Javadoc)
+ * @see GDataServiceClient#getServiceName
+ */
+ public String getServiceName() {
+ return SERVICE;
+ }
+
+ /**
+ * Returns the url for the default feed for a user, after applying the
+ * provided QueryParams.
+ * @param username The username for this user.
+ * @param projection the projection to use
+ * @param params The QueryParams that should be applied to the default feed.
+ * @return The url that should be used to retrieve a user's default feed.
+ */
+ public String getDefaultCalendarUrl(String username, String projection, QueryParams params) {
+ String feedUrl = CALENDAR_BASE_FEED_URL + getGDataClient().encodeUri(username);
+ feedUrl += projection;
+ if (params == null) {
+ return feedUrl;
+ }
+ return params.generateQueryUrl(feedUrl);
+ }
+
+ /**
+ * Returns the url for the metafeed for user, which contains the information about
+ * the user's calendars.
+ * @param username The username for this user.
+ * @return The url that should be used to retrieve a user's default feed.
+ */
+ public String getUserCalendarsUrl(String username) {
+ return CALENDAR_BASE_FEED_URL + getGDataClient().encodeUri(username);
+ }
+
+ /**
+ * Fetches the meta feed containing the list of calendars for a user. The
+ * caller is responsible for closing the returned {@link GDataParser}.
+ *
+ * @param feedUrl the URL of the user calendars feed
+ * @param authToken The authentication token for this user
+ * @return A GDataParser with the meta feed containing the list of
+ * calendars for this user.
+ * @throws ParseException Thrown if the feed could not be fetched.
+ */
+ public GDataParser getParserForUserCalendars(String feedUrl, String authToken)
+ throws AuthenticationException, ParseException, IOException,
+ AllDeletedUnavailableException {
+ GDataClient gDataClient = getGDataClient();
+ try {
+ InputStream is = gDataClient.getFeedAsStream(feedUrl, authToken);
+ return getGDataParserFactory().createParser(CalendarEntry.class, is);
+ } catch (HttpException e) {
+ convertHttpExceptionForReads("Could not fetch calendars feed", e);
+ return null; // never reached
+ }
+ }
+}
diff --git a/src/com/google/wireless/gdata/calendar/client/package.html b/src/com/google/wireless/gdata/calendar/client/package.html
new file mode 100644
index 0000000..1c9bf9d
--- /dev/null
+++ b/src/com/google/wireless/gdata/calendar/client/package.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+ {@hide}
+</body>
+</html>
diff --git a/src/com/google/wireless/gdata/calendar/data/CalendarEntry.java b/src/com/google/wireless/gdata/calendar/data/CalendarEntry.java
new file mode 100644
index 0000000..098b88c
--- /dev/null
+++ b/src/com/google/wireless/gdata/calendar/data/CalendarEntry.java
@@ -0,0 +1,162 @@
+// Copyright 2007 The Android Open Source Project
+
+package com.google.wireless.gdata.calendar.data;
+
+import com.google.wireless.gdata.data.Entry;
+
+/**
+ * Entry containing information about a calendar.
+ */
+public class CalendarEntry extends Entry {
+
+ /**
+ * Access level constant indicating the user has no access to a calendar.
+ */
+ public static final byte ACCESS_NONE = 0;
+
+ /**
+ * Access level constant indicating the user can read (but not write) to
+ * a calendar.
+ */
+ public static final byte ACCESS_READ = 1;
+
+ /**
+ * Access level constant indicating the user can only view free-busy
+ * information for a calendar.
+ */
+ public static final byte ACCESS_FREEBUSY = 2;
+
+ /**
+ * Access level constant indicating the user can contribute (read and write)
+ * to this calendar.
+ */
+ public static final byte ACCESS_CONTRIBUTOR = 3;
+
+ /**
+ * Access level constant indicating the user owns this calendar.
+ */
+ public static final byte ACCESS_OWNER = 4;
+
+ private byte accessLevel = ACCESS_READ;
+ // TODO: rename to feed Url?
+ private String alternateLink = null;
+ private String color = null;
+ private boolean hidden = false;
+ private boolean selected = true;
+ private String timezone = null;
+
+ /**
+ * Creates a new, empty calendar entry.
+ */
+ public CalendarEntry() {
+ }
+
+ public void clear() {
+ super.clear();
+ accessLevel = ACCESS_READ;
+ alternateLink = null;
+ color = null;
+ hidden = false;
+ selected = true;
+ timezone = null;
+ }
+
+ /**
+ * @return the accessLevel
+ */
+ public byte getAccessLevel() {
+ return accessLevel;
+ }
+
+ /**
+ * @param accessLevel the accessLevel to set
+ */
+ public void setAccessLevel(byte accessLevel) {
+ this.accessLevel = accessLevel;
+ }
+
+ /**
+ * @return the alternateLink
+ */
+ public String getAlternateLink() {
+ return alternateLink;
+ }
+
+ /**
+ * @param alternateLink the alternateLink to set
+ */
+ public void setAlternateLink(String alternateLink) {
+ this.alternateLink = alternateLink;
+ }
+
+ /**
+ * @return the color
+ */
+ public String getColor() {
+ return color;
+ }
+
+ /**
+ * @param color the color to set
+ */
+ public void setColor(String color) {
+ this.color = color;
+ }
+
+ /**
+ * @return the hidden
+ */
+ public boolean isHidden() {
+ return hidden;
+ }
+
+ /**
+ * @param hidden the hidden to set
+ */
+ public void setHidden(boolean hidden) {
+ this.hidden = hidden;
+ }
+
+ /**
+ * @return the selected
+ */
+ public boolean isSelected() {
+ return selected;
+ }
+
+ /**
+ * @param selected the selected to set
+ */
+ public void setSelected(boolean selected) {
+ this.selected = selected;
+ }
+
+ /**
+ * @return the timezone
+ */
+ public String getTimezone() {
+ return timezone;
+ }
+
+ /**
+ * @param timezone the timezone to set
+ */
+ public void setTimezone(String timezone) {
+ this.timezone = timezone;
+ }
+
+ public void toString(StringBuffer sb) {
+ sb.append("ACCESS LEVEL: ");
+ sb.append(accessLevel);
+ sb.append('\n');
+ appendIfNotNull(sb, "ALTERNATE LINK", alternateLink);
+ appendIfNotNull(sb, "COLOR", color);
+ sb.append("HIDDEN: ");
+ sb.append(hidden);
+ sb.append('\n');
+ sb.append("SELECTED: ");
+ sb.append(selected);
+ sb.append('\n');
+ appendIfNotNull(sb, "TIMEZONE", timezone);
+ }
+}
diff --git a/src/com/google/wireless/gdata/calendar/data/CalendarsFeed.java b/src/com/google/wireless/gdata/calendar/data/CalendarsFeed.java
new file mode 100644
index 0000000..f792017
--- /dev/null
+++ b/src/com/google/wireless/gdata/calendar/data/CalendarsFeed.java
@@ -0,0 +1,18 @@
+// Copyright 2007 The Android Open Source Project
+
+package com.google.wireless.gdata.calendar.data;
+
+import com.google.wireless.gdata.data.Feed;
+
+/**
+ * Meta feed containing the list of calendars for a user.
+ */
+public class CalendarsFeed extends Feed {
+
+ /**
+ * Creates a new empty calendars feed.
+ */
+ public CalendarsFeed() {
+ }
+
+}
diff --git a/src/com/google/wireless/gdata/calendar/data/EventEntry.java b/src/com/google/wireless/gdata/calendar/data/EventEntry.java
new file mode 100644
index 0000000..5f2f271
--- /dev/null
+++ b/src/com/google/wireless/gdata/calendar/data/EventEntry.java
@@ -0,0 +1,315 @@
+// Copyright 2007 The Android Open Source Project
+
+package com.google.wireless.gdata.calendar.data;
+
+import com.google.wireless.gdata.data.Entry;
+
+import java.util.Hashtable;
+import java.util.Vector;
+import java.util.Enumeration;
+
+/**
+ * Entry containing information about an event in a calendar.
+ */
+public class EventEntry extends Entry {
+
+ // TODO: pack all of these enums into an int
+
+ /**
+ * Status constant indicating that a user's attendance at an event is
+ * tentative.
+ */
+ public static final byte STATUS_TENTATIVE = 0;
+
+ /**
+ * Status constant indicating that a user's attendance at an event is
+ * confirmed.
+ */
+ public static final byte STATUS_CONFIRMED = 1;
+
+ /**
+ * Status constant indicating that an event has been cancelled.
+ */
+ public static final byte STATUS_CANCELED = 2;
+
+ /**
+ * Visibility constant indicating that an event uses the user's default
+ * visibility.
+ */
+ public static final byte VISIBILITY_DEFAULT = 0;
+
+ /**
+ * Visibility constant indicating that an event has been marked
+ * confidential.
+ */
+ public static final byte VISIBILITY_CONFIDENTIAL = 1;
+
+ /**
+ * Visibility constant indicating that an event has been marked private.
+ */
+ public static final byte VISIBILITY_PRIVATE = 2;
+
+ /**
+ * Visibility constant indicating that an event has been marked public.
+ */
+ public static final byte VISIBILITY_PUBLIC = 3;
+
+ /**
+ * Transparency constant indicating that an event has been marked opaque.
+ */
+ public static final byte TRANSPARENCY_OPAQUE = 0;
+
+ /**
+ * Transparency constant indicating that an event has been marked
+ * transparent.
+ */
+ public static final byte TRANSPARENCY_TRANSPARENT = 1;
+
+ private byte status = STATUS_TENTATIVE;
+ private String recurrence = null;
+ private byte visibility = VISIBILITY_DEFAULT;
+ private byte transparency = TRANSPARENCY_OPAQUE;
+ private Vector attendees = new Vector();
+ private Vector whens = new Vector();
+ private Vector reminders = null;
+ private String originalEventId = null;
+ private String originalEventStartTime = null;
+ private String where = null;
+ private String commentsUri = null;
+ private Hashtable extendedProperties = null;
+
+ /**
+ * Creates a new empty event entry.
+ */
+ public EventEntry() {
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.google.wireless.gdata.data.Entry#clear()
+ */
+ public void clear() {
+ super.clear();
+ status = STATUS_TENTATIVE;
+ recurrence = null;
+ visibility = VISIBILITY_DEFAULT;
+ transparency = TRANSPARENCY_OPAQUE;
+ attendees.removeAllElements();
+ whens.removeAllElements();
+ reminders = null;
+ originalEventId = null;
+ originalEventStartTime = null;
+ where = null;
+ commentsUri = null;
+ extendedProperties = null;
+ }
+
+ /**
+ * @return the recurrence
+ */
+ public String getRecurrence() {
+ return recurrence;
+ }
+
+ /**
+ * @param recurrence the recurrence to set
+ */
+ public void setRecurrence(String recurrence) {
+ this.recurrence = recurrence;
+ }
+
+ /**
+ * @return the status
+ */
+ public byte getStatus() {
+ return status;
+ }
+
+ /**
+ * @param status the status to set
+ */
+ public void setStatus(byte status) {
+ this.status = status;
+ }
+
+ /**
+ * @return the transparency
+ */
+ public byte getTransparency() {
+ return transparency;
+ }
+
+ /**
+ * @param transparency the transparency to set
+ */
+ public void setTransparency(byte transparency) {
+ this.transparency = transparency;
+ }
+
+ /**
+ * @return the visibility
+ */
+ public byte getVisibility() {
+ return visibility;
+ }
+
+ /**
+ * @param visibility the visibility to set
+ */
+ public void setVisibility(byte visibility) {
+ this.visibility = visibility;
+ }
+
+ public void clearAttendees() {
+ attendees.clear();
+ }
+
+ public void addAttendee(Who attendee) {
+ attendees.add(attendee);
+ }
+
+ public Vector getAttendees() {
+ return attendees;
+ }
+
+ public void clearWhens() {
+ whens.clear();
+ }
+
+ public void addWhen(When when) {
+ whens.add(when);
+ }
+
+ public Vector getWhens() {
+ return whens;
+ }
+
+ public When getFirstWhen() {
+ if (whens.isEmpty()) {
+ return null;
+ }
+ return (When) whens.elementAt(0);
+ }
+
+ public Vector getReminders() {
+ return reminders;
+ }
+
+ public void addReminder(Reminder reminder) {
+ if (reminders == null) {
+ reminders = new Vector();
+ }
+ reminders.add(reminder);
+ }
+
+ public void clearReminders() {
+ reminders = null;
+ }
+
+ public String getOriginalEventId() {
+ return originalEventId;
+ }
+
+ public void setOriginalEventId(String originalEventId) {
+ this.originalEventId = originalEventId;
+ }
+
+ public String getOriginalEventStartTime() {
+ return originalEventStartTime;
+ }
+
+ public void setOriginalEventStartTime(String originalEventStartTime) {
+ this.originalEventStartTime = originalEventStartTime;
+ }
+
+ /**
+ * @return the where
+ */
+ public String getWhere() {
+ return where;
+ }
+
+ /**
+ * @param where the where to set
+ */
+ public void setWhere(String where) {
+ this.where = where;
+ }
+
+ public Hashtable getExtendedProperties() {
+ return extendedProperties;
+ }
+
+ public String getExtendedProperty(String name) {
+ if (extendedProperties == null) {
+ return null;
+ }
+ String value = null;
+ if (extendedProperties.containsKey(name)) {
+ value = (String) extendedProperties.get(name);
+ }
+ return value;
+ }
+
+ public void addExtendedProperty(String name, String value) {
+ if (extendedProperties == null) {
+ extendedProperties = new Hashtable();
+ }
+ extendedProperties.put(name, value);
+ }
+
+ public void clearExtendedProperties() {
+ extendedProperties = null;
+ }
+
+ public String getCommentsUri() {
+ return commentsUri;
+ }
+
+ public void setCommentsUri(String commentsUri) {
+ this.commentsUri = commentsUri;
+ }
+
+ public void toString(StringBuffer sb) {
+ super.toString(sb);
+ sb.append("STATUS: " + status + "\n");
+ appendIfNotNull(sb, "RECURRENCE", recurrence);
+ sb.append("VISIBILITY: " + visibility + "\n");
+ sb.append("TRANSPARENCY: " + transparency + "\n");
+
+ appendIfNotNull(sb, "ORIGINAL_EVENT_ID", originalEventId);
+ appendIfNotNull(sb, "ORIGINAL_START_TIME", originalEventStartTime);
+
+ Enumeration whos = this.attendees.elements();
+ while (whos.hasMoreElements()) {
+ Who who = (Who) whos.nextElement();
+ who.toString(sb);
+ }
+
+ Enumeration times = this.whens.elements();
+ while (times.hasMoreElements()) {
+ When when = (When) times.nextElement();
+ when.toString(sb);
+ }
+ if (reminders != null) {
+ Enumeration alarms = reminders.elements();
+ while (alarms.hasMoreElements()) {
+ Reminder reminder = (Reminder) alarms.nextElement();
+ reminder.toString(sb);
+ }
+ }
+ appendIfNotNull(sb, "WHERE", where);
+ appendIfNotNull(sb, "COMMENTS", commentsUri);
+ if (extendedProperties != null) {
+ Enumeration entryNames = extendedProperties.keys();
+ while (entryNames.hasMoreElements()) {
+ String name = (String) entryNames.nextElement();
+ String value = (String) extendedProperties.get(name);
+ sb.append(name);
+ sb.append(':');
+ sb.append(value);
+ sb.append('\n');
+ }
+ }
+ }
+}
diff --git a/src/com/google/wireless/gdata/calendar/data/EventsFeed.java b/src/com/google/wireless/gdata/calendar/data/EventsFeed.java
new file mode 100644
index 0000000..d5f093d
--- /dev/null
+++ b/src/com/google/wireless/gdata/calendar/data/EventsFeed.java
@@ -0,0 +1,33 @@
+// Copyright 2007 The Android Open Source Project
+
+package com.google.wireless.gdata.calendar.data;
+
+import com.google.wireless.gdata.data.Feed;
+
+/**
+ * Feed containing events in a calendar.
+ */
+public class EventsFeed extends Feed {
+
+ private String timezone = null;
+
+ /**
+ * Creates a new empty events feed.
+ */
+ public EventsFeed() {
+ }
+
+ /**
+ * @return the timezone
+ */
+ public String getTimezone() {
+ return timezone;
+ }
+
+ /**
+ * @param timezone the timezone to set
+ */
+ public void setTimezone(String timezone) {
+ this.timezone = timezone;
+ }
+}
diff --git a/src/com/google/wireless/gdata/calendar/data/Recurrence.java b/src/com/google/wireless/gdata/calendar/data/Recurrence.java
new file mode 100644
index 0000000..bae9160
--- /dev/null
+++ b/src/com/google/wireless/gdata/calendar/data/Recurrence.java
@@ -0,0 +1,28 @@
+// Copyright 2007 The Android Open Source Project
+
+package com.google.wireless.gdata.calendar.data;
+
+/**
+ * Container for information about a Recurrence.
+ */
+// TODO: get rid of this?
+public class Recurrence {
+
+ private final String recurrence;
+
+ /**
+ * Creates a new recurrence for the provide recurrence string.
+ * @param recurrence The recurrence string that should be parsed.
+ */
+ public Recurrence(String recurrence) {
+ this.recurrence = recurrence;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ public String toString() {
+ return recurrence;
+ }
+}
diff --git a/src/com/google/wireless/gdata/calendar/data/Reminder.java b/src/com/google/wireless/gdata/calendar/data/Reminder.java
new file mode 100644
index 0000000..5f87eb6
--- /dev/null
+++ b/src/com/google/wireless/gdata/calendar/data/Reminder.java
@@ -0,0 +1,92 @@
+// Copyright 2007 The Android Open Source Project
+package com.google.wireless.gdata.calendar.data;
+
+/**
+ * Contains information about a reminder for an event.
+ */
+public class Reminder {
+ /**
+ * Default reminder method as defined on the calendar server.
+ */
+ public static final byte METHOD_DEFAULT = 0;
+
+ /**
+ * Reminder that uses e-mail for notification.
+ */
+ public static final byte METHOD_EMAIL = 1;
+
+ /**
+ * Reminder that uses sms for notification.
+ */
+ public static final byte METHOD_SMS = 2;
+
+ /**
+ * Reminder that uses a local alert for notification.
+ */
+ public static final byte METHOD_ALERT = 3;
+
+ /**
+ * Reminder that uses a calendar-wide default time for the alarm.
+ */
+ public static final int MINUTES_DEFAULT = -1;
+
+ // do absolute times work with recurrences?
+ // private String absoluteTime;
+ private int minutes = MINUTES_DEFAULT;
+ private byte method = METHOD_DEFAULT;
+
+ /**
+ * Creates a new empty reminder.
+ */
+ public Reminder() {
+ }
+
+ /**
+ * Returns the method of the reminder.
+ * @return The method of the reminder.
+ */
+ public byte getMethod() {
+ return method;
+ }
+
+ /**
+ * Sets the method of the reminder.
+ * @param method The method of the reminder.
+ */
+ public void setMethod(byte method) {
+ this.method = method;
+ }
+
+ /**
+ * Gets how many minutes before an event that the reminder should be
+ * triggered.
+ * @return How many minutes before an event that the reminder should be
+ * triggered.
+ */
+ public int getMinutes() {
+ return minutes;
+ }
+
+ /**
+ * Sets how many minutes before an event that the reminder should be
+ * triggered.
+ * @param minutes How many minutes before an event that the reminder should
+ * be triggered.
+ */
+ public void setMinutes(int minutes) {
+ this.minutes = minutes;
+ }
+
+ public void toString(StringBuffer sb) {
+ sb.append("REMINDER MINUTES: " + minutes);
+ sb.append("\n");
+ sb.append("REMINDER METHOD: " + method);
+ sb.append("\n");
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ toString(sb);
+ return sb.toString();
+ }
+}
diff --git a/src/com/google/wireless/gdata/calendar/data/When.java b/src/com/google/wireless/gdata/calendar/data/When.java
new file mode 100644
index 0000000..4f9ab9e
--- /dev/null
+++ b/src/com/google/wireless/gdata/calendar/data/When.java
@@ -0,0 +1,53 @@
+// Copyright 2007 The Android Open Source Project
+package com.google.wireless.gdata.calendar.data;
+
+import com.google.wireless.gdata.data.StringUtils;
+
+/**
+ * Contains information about the start and end of an instance of an event.
+ */
+public class When {
+ private final String startTime;
+ private final String endTime;
+
+ /**
+ * Creates a new When.
+ * @param startTime The start of the event.
+ * @param endTime The end of the event.
+ */
+ public When(String startTime, String endTime) {
+ this.startTime = startTime;
+ this.endTime = endTime;
+ }
+
+ /**
+ * Returns the start time for the event.
+ * @return The start time for the event.
+ */
+ public String getStartTime() {
+ return startTime;
+ }
+
+ /**
+ * Returns the end time for the event.
+ * @return The end time for the event.
+ */
+ public String getEndTime() {
+ return endTime;
+ }
+
+ public void toString(StringBuffer sb) {
+ if (!StringUtils.isEmpty(startTime)) {
+ sb.append("START TIME: " + startTime + "\n");
+ }
+ if (!StringUtils.isEmpty(endTime)) {
+ sb.append("END TIME: " + endTime + "\n");
+ }
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ toString(sb);
+ return sb.toString();
+ }
+}
diff --git a/src/com/google/wireless/gdata/calendar/data/Who.java b/src/com/google/wireless/gdata/calendar/data/Who.java
new file mode 100644
index 0000000..2d438d6
--- /dev/null
+++ b/src/com/google/wireless/gdata/calendar/data/Who.java
@@ -0,0 +1,153 @@
+package com.google.wireless.gdata.calendar.data;
+
+import com.google.wireless.gdata.data.StringUtils;
+
+/**
+ * Contains information about a event attendee.
+ */
+public class Who {
+
+ /**
+ * No attendee relationhip set. Used in {@link #setRelationship}
+ * and {@link #getRelationship}.
+ */
+ public static final byte RELATIONSHIP_NONE = 0;
+
+ /**
+ * A general meeting/event attendee. Used in {@link #setRelationship}
+ * and {@link #getRelationship}.
+ */
+ public static final byte RELATIONSHIP_ATTENDEE = 1;
+
+ /**
+ * Event organizer. An organizer is not necessary an attendee.
+ * Used in {@link #setRelationship} and {@link #getRelationship}.
+ */
+ public static final byte RELATIONSHIP_ORGANIZER = 2;
+
+ /**
+ * Performer. Similar to {@link #RELATIONSHIP_SPEAKER}, but with more emphasis on art than
+ * speech delivery.
+ * Used in {@link #setRelationship} and {@link #getRelationship}.
+ */
+ public static final byte RELATIONSHIP_PERFORMER = 3;
+
+ /**
+ * Speaker. Used in {@link #setRelationship} and {@link #getRelationship}.
+ */
+ public static final byte RELATIONSHIP_SPEAKER = 4;
+
+ /**
+ * No attendee type set. Used in {@link #setType} and {@link #getType}.
+ */
+ public static final byte TYPE_NONE = 0;
+
+ /**
+ * Optional attendee. Used in {@link #setType} and {@link #getType}.
+ */
+ public static final byte TYPE_OPTIONAL = 1;
+
+ /**
+ * Required attendee. Used in {@link #setType} and {@link #getType}.
+ */
+ public static final byte TYPE_REQUIRED = 2;
+
+ /**
+ * No attendee status set. Used in {@link #setStatus} and {@link #getStatus}.
+ */
+ public static final byte STATUS_NONE = 0;
+
+
+ /**
+ * Attendee has accepted. Used in {@link #setStatus} and {@link #getStatus}.
+ */
+ public static final byte STATUS_ACCEPTED = 1;
+
+ /**
+ * Attendee has declined. Used in {@link #setStatus} and {@link #getStatus}.
+ */
+ public static final byte STATUS_DECLINED = 2;
+
+ /**
+ * Invitation has been sent, but the person has not accepted.
+ * Used in {@link #setStatus} and {@link #getStatus}.
+ */
+ public static final byte STATUS_INVITED = 3;
+
+ /**
+ * Attendee has accepted tentatively. Used in {@link #setStatus} and {@link #getStatus}.
+ */
+ public static final byte STATUS_TENTATIVE = 4;
+
+ private String email;
+ private String value;
+ private byte relationship = RELATIONSHIP_NONE;
+ private byte type = TYPE_NONE;
+ private byte status = STATUS_NONE;
+
+ /**
+ * Creates a new Who, representing event attendee information.
+ */
+ public Who() {
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public byte getRelationship() {
+ return relationship;
+ }
+
+ public void setRelationship(byte relationship) {
+ this.relationship = relationship;
+ }
+
+ public byte getType() {
+ return type;
+ }
+
+ public void setType(byte type) {
+ this.type = type;
+ }
+
+ public byte getStatus() {
+ return status;
+ }
+
+ public void setStatus(byte status) {
+ this.status = status;
+ }
+
+ protected void toString(StringBuffer sb) {
+ if (!StringUtils.isEmpty(email)) {
+ sb.append("EMAIL: " + email + "\n");
+ }
+
+ if (!StringUtils.isEmpty(value)) {
+ sb.append("VALUE: " + value + "\n");
+ }
+
+ sb.append("RELATIONSHIP: " + relationship + "\n");
+ sb.append("TYPE: " + type + "\n");
+ sb.append("STATUS: " + status + "\n");
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ toString(sb);
+ return sb.toString();
+ }
+}
diff --git a/src/com/google/wireless/gdata/calendar/data/package.html b/src/com/google/wireless/gdata/calendar/data/package.html
new file mode 100644
index 0000000..1c9bf9d
--- /dev/null
+++ b/src/com/google/wireless/gdata/calendar/data/package.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+ {@hide}
+</body>
+</html>
diff --git a/src/com/google/wireless/gdata/calendar/package.html b/src/com/google/wireless/gdata/calendar/package.html
new file mode 100644
index 0000000..1c9bf9d
--- /dev/null
+++ b/src/com/google/wireless/gdata/calendar/package.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+ {@hide}
+</body>
+</html>
diff --git a/src/com/google/wireless/gdata/calendar/parser/package.html b/src/com/google/wireless/gdata/calendar/parser/package.html
new file mode 100644
index 0000000..1c9bf9d
--- /dev/null
+++ b/src/com/google/wireless/gdata/calendar/parser/package.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+ {@hide}
+</body>
+</html>
diff --git a/src/com/google/wireless/gdata/calendar/parser/xml/XmlCalendarGDataParserFactory.java b/src/com/google/wireless/gdata/calendar/parser/xml/XmlCalendarGDataParserFactory.java
new file mode 100644
index 0000000..9e38ba8
--- /dev/null
+++ b/src/com/google/wireless/gdata/calendar/parser/xml/XmlCalendarGDataParserFactory.java
@@ -0,0 +1,98 @@
+// Copyright 2007 The Android Open Source Project
+
+package com.google.wireless.gdata.calendar.parser.xml;
+
+import com.google.wireless.gdata.calendar.data.CalendarEntry;
+import com.google.wireless.gdata.calendar.data.EventEntry;
+import com.google.wireless.gdata.calendar.serializer.xml.XmlEventEntryGDataSerializer;
+import com.google.wireless.gdata.client.GDataParserFactory;
+import com.google.wireless.gdata.data.Entry;
+import com.google.wireless.gdata.parser.GDataParser;
+import com.google.wireless.gdata.parser.ParseException;
+import com.google.wireless.gdata.parser.xml.XmlParserFactory;
+import com.google.wireless.gdata.serializer.GDataSerializer;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.InputStream;
+
+/**
+ * GDataParserFactory that creates XML GDataParsers and GDataSerializers for
+ * Google Calendar.
+ */
+public class XmlCalendarGDataParserFactory implements GDataParserFactory {
+
+ private final XmlParserFactory xmlFactory;
+
+ public XmlCalendarGDataParserFactory(XmlParserFactory xmlFactory) {
+ this.xmlFactory = xmlFactory;
+ }
+
+ /**
+ * Returns a parser for a calendars meta-feed.
+ *
+ * @param is The input stream to be parsed.
+ * @return A parser for the stream.
+ */
+ public GDataParser createCalendarsFeedParser(InputStream is)
+ throws ParseException {
+ XmlPullParser xmlParser;
+ try {
+ xmlParser = xmlFactory.createParser();
+ } catch (XmlPullParserException xppe) {
+ throw new ParseException("Could not create XmlPullParser", xppe);
+ }
+ return new XmlCalendarsGDataParser(is, xmlParser);
+ }
+
+ /*
+ * (non-javadoc)
+ *
+ * @see GDataParserFactory#createParser
+ */
+ public GDataParser createParser(InputStream is) throws ParseException {
+ XmlPullParser xmlParser;
+ try {
+ xmlParser = xmlFactory.createParser();
+ } catch (XmlPullParserException xppe) {
+ throw new ParseException("Could not create XmlPullParser", xppe);
+ }
+ return new XmlEventsGDataParser(is, xmlParser);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.google.wireless.gdata.client.GDataParserFactory#createParser(
+ * int, java.io.InputStream)
+ */
+ public GDataParser createParser(Class entryClass, InputStream is)
+ throws ParseException {
+ if (entryClass == CalendarEntry.class) {
+ return createCalendarsFeedParser(is);
+ } else if (entryClass == EventEntry.class) {
+ return createParser(is);
+ }
+ throw new IllegalArgumentException("Unknown entry class '" + entryClass.getName()
+ + "' specified.");
+ }
+
+ /**
+ * Creates a new {@link GDataSerializer} for the provided entry. The entry
+ * <strong>must</strong> be an instance of {@link EventEntry}.
+ *
+ * @param entry The {@link EventEntry} that should be serialized.
+ * @return The {@link GDataSerializer} that will serialize this entry.
+ * @throws IllegalArgumentException Thrown if entry is not an
+ * {@link EventEntry}.
+ * @see GDataParserFactory#createSerializer
+ */
+ public GDataSerializer createSerializer(Entry entry) {
+ if (!(entry instanceof EventEntry)) {
+ throw new IllegalArgumentException("Expected EventEntry!");
+ }
+ EventEntry eventEntry = (EventEntry) entry;
+ return new XmlEventEntryGDataSerializer(xmlFactory, eventEntry);
+ }
+}
diff --git a/src/com/google/wireless/gdata/calendar/parser/xml/XmlCalendarsGDataParser.java b/src/com/google/wireless/gdata/calendar/parser/xml/XmlCalendarsGDataParser.java
new file mode 100644
index 0000000..2879780
--- /dev/null
+++ b/src/com/google/wireless/gdata/calendar/parser/xml/XmlCalendarsGDataParser.java
@@ -0,0 +1,132 @@
+// Copyright 2007 The Android Open Source Project
+
+package com.google.wireless.gdata.calendar.parser.xml;
+
+import com.google.wireless.gdata.calendar.data.CalendarEntry;
+import com.google.wireless.gdata.calendar.data.CalendarsFeed;
+import com.google.wireless.gdata.data.Entry;
+import com.google.wireless.gdata.data.Feed;
+import com.google.wireless.gdata.parser.ParseException;
+import com.google.wireless.gdata.parser.xml.XmlGDataParser;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * GDataParser for the meta feed listing a user's calendars.
+ */
+public class XmlCalendarsGDataParser extends XmlGDataParser {
+
+ /**
+ * Creates a new XmlCalendarsGDataParser.
+ * @param is The InputStream containing the calendars feed.
+ * @throws ParseException Thrown if an XmlPullParser could not be created.
+ */
+ public XmlCalendarsGDataParser(InputStream is, XmlPullParser parser)
+ throws ParseException {
+ super(is, parser);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.google.wireless.gdata.parser.xml.XmlGDataParser#createFeed()
+ */
+ protected Feed createFeed() {
+ return new CalendarsFeed();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.google.wireless.gdata.parser.xml.XmlGDataParser#createEntry()
+ */
+ protected Entry createEntry() {
+ return new CalendarEntry();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see XmlGDataParser#handleExtraElementInEntry
+ */
+ protected void handleExtraElementInEntry(Entry entry)
+ throws XmlPullParserException, IOException {
+
+ XmlPullParser parser = getParser();
+
+ if (!(entry instanceof CalendarEntry)) {
+ throw new IllegalArgumentException("Expected CalendarEntry!");
+ }
+ CalendarEntry calendarEntry = (CalendarEntry) entry;
+
+ // NOTE: all of these names are assumed to be in the "gcal" namespace.
+ // we do not bother checking that here.
+ String name = parser.getName();
+ if ("accesslevel".equals(name)) {
+ String accesslevelStr = parser.getAttributeValue(null /* ns */,
+ "value");
+ byte accesslevel = CalendarEntry.ACCESS_READ;
+ if ("none".equals(accesslevelStr)) {
+ accesslevel = CalendarEntry.ACCESS_NONE;
+ } else if ("read".equals(accesslevelStr)) {
+ accesslevel = CalendarEntry.ACCESS_READ;
+ } else if ("freebusy".equals(accesslevelStr)) {
+ accesslevel = CalendarEntry.ACCESS_FREEBUSY;
+ } else if ("contributor".equals(accesslevelStr)) {
+ accesslevel = CalendarEntry.ACCESS_CONTRIBUTOR;
+ } else if ("owner".equals(accesslevelStr)) {
+ accesslevel = CalendarEntry.ACCESS_OWNER;
+ }
+ calendarEntry.setAccessLevel(accesslevel);
+ } else if ("color".equals(name)) {
+ String color =
+ parser.getAttributeValue(null /* ns */, "value");
+ calendarEntry.setColor(color);
+ } else if ("hidden".equals(name)) {
+ String hiddenStr =
+ parser.getAttributeValue(null /* ns */, "value");
+ boolean hidden = false;
+ if ("false".equals(hiddenStr)) {
+ hidden = false;
+ } else if ("true".equals(hiddenStr)) {
+ hidden = true;
+ }
+ calendarEntry.setHidden(hidden);
+ // if the calendar is hidden, it cannot be selected.
+ if (hidden) {
+ calendarEntry.setSelected(false);
+ }
+ } else if ("selected".equals(name)) {
+ String selectedStr =
+ parser.getAttributeValue(null /* ns */, "value");
+ boolean selected = false;
+ if ("false".equals(selectedStr)) {
+ selected = false;
+ } else if ("true".equals(selectedStr)) {
+ selected = true;
+ }
+ calendarEntry.setSelected(selected);
+ } else if ("timezone".equals(name)) {
+ String timezone =
+ parser.getAttributeValue(null /* ns */, "value");
+ calendarEntry.setTimezone(timezone);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see XmlGDataParser#handleExtraLinkInEntry
+ */
+ protected void handleExtraLinkInEntry(String rel,
+ String type,
+ String href,
+ Entry entry)
+ throws XmlPullParserException, IOException {
+ if (("alternate".equals(rel)) &&
+ ("application/atom+xml".equals(type))) {
+ CalendarEntry calendarEntry = (CalendarEntry) entry;
+ calendarEntry.setAlternateLink(href);
+ }
+ }
+}
diff --git a/src/com/google/wireless/gdata/calendar/parser/xml/XmlEventsGDataParser.java b/src/com/google/wireless/gdata/calendar/parser/xml/XmlEventsGDataParser.java
new file mode 100644
index 0000000..6135d1b
--- /dev/null
+++ b/src/com/google/wireless/gdata/calendar/parser/xml/XmlEventsGDataParser.java
@@ -0,0 +1,419 @@
+// Copyright 2007 The Android Open Source Project
+
+package com.google.wireless.gdata.calendar.parser.xml;
+
+import com.google.wireless.gdata.calendar.data.EventEntry;
+import com.google.wireless.gdata.calendar.data.EventsFeed;
+import com.google.wireless.gdata.calendar.data.When;
+import com.google.wireless.gdata.calendar.data.Reminder;
+import com.google.wireless.gdata.calendar.data.Who;
+import com.google.wireless.gdata.data.Entry;
+import com.google.wireless.gdata.data.Feed;
+import com.google.wireless.gdata.data.StringUtils;
+import com.google.wireless.gdata.data.XmlUtils;
+import com.google.wireless.gdata.parser.ParseException;
+import com.google.wireless.gdata.parser.xml.XmlGDataParser;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * GDataParser for an events feed containing events in a calendar.
+ */
+public class XmlEventsGDataParser extends XmlGDataParser {
+
+ // whether or not we've seen reminders directly under the entry.
+ // the calendar feed sends duplicate <reminder> entries in case of
+ // recurrences, if the recurrences are expanded.
+ // if the <reminder> elements precede the <when> elements, we'll only
+ // process the <reminder> elements directly under the entry and ignore
+ // the <reminder> elements within a <when>.
+ // if the <when> elements precede the <reminder> elements, we'll first
+ // process reminders under the when, and then we'll clear them and process
+ // the reminders directly under the entry (which should take precedence).
+ // if we only see <reminder> as direct children of the entry or only see
+ // <reminder> as children of <when> elements, there is no conflict.
+ private boolean hasSeenReminder = false;
+
+ /**
+ * Creates a new XmlEventsGDataParser.
+ * @param is The InputStream that should be parsed.
+ * @throws ParseException Thrown if a parser cannot be created.
+ */
+ public XmlEventsGDataParser(InputStream is, XmlPullParser parser)
+ throws ParseException {
+ super(is, parser);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.google.wireless.gdata.parser.xml.XmlGDataParser#createFeed()
+ */
+ protected Feed createFeed() {
+ return new EventsFeed();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.google.wireless.gdata.parser.xml.XmlGDataParser#createEntry()
+ */
+ protected Entry createEntry() {
+ return new EventEntry();
+ }
+
+ protected void handleExtraElementInFeed(Feed feed)
+ throws XmlPullParserException, IOException {
+ XmlPullParser parser = getParser();
+ if (!(feed instanceof EventsFeed)) {
+ throw new IllegalArgumentException("Expected EventsFeed!");
+ }
+ EventsFeed eventsFeed = (EventsFeed) feed;
+ String name = parser.getName();
+ if ("timezone".equals(name)) {
+ String timezone = parser.getAttributeValue(null /* ns */, "value");
+ if (!StringUtils.isEmpty(timezone)) {
+ eventsFeed.setTimezone(timezone);
+ }
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see XmlGDataParser#handleExtraElementInEntry
+ */
+ protected void handleExtraElementInEntry(Entry entry)
+ throws XmlPullParserException, IOException, ParseException {
+
+ XmlPullParser parser = getParser();
+
+ if (!(entry instanceof EventEntry)) {
+ throw new IllegalArgumentException("Expected EventEntry!");
+ }
+ EventEntry eventEntry = (EventEntry) entry;
+
+ // NOTE: all of these names are assumed to be in the "gd" namespace.
+ // we do not bother checking that here.
+
+ String name = parser.getName();
+ if ("eventStatus".equals(name)) {
+ String eventStatusStr = parser.getAttributeValue(null, "value");
+ byte eventStatus = EventEntry.STATUS_TENTATIVE;
+ if ("http://schemas.google.com/g/2005#event.canceled".
+ equals(eventStatusStr)) {
+ eventStatus = EventEntry.STATUS_CANCELED;
+ } else if ("http://schemas.google.com/g/2005#event.confirmed".
+ equals(eventStatusStr)) {
+ eventStatus = EventEntry.STATUS_CONFIRMED;
+ } else if ("http://schemas.google.com/g/2005#event.tentative".
+ equals(eventStatusStr)) {
+ eventStatus = EventEntry.STATUS_TENTATIVE;
+ }
+ eventEntry.setStatus(eventStatus);
+ } else if ("recurrence".equals(name)) {
+ String recurrence = XmlUtils.extractChildText(parser);
+ eventEntry.setRecurrence(recurrence);
+ } else if ("transparency".equals(name)) {
+ String transparencyStr = parser.getAttributeValue(null, "value");
+ byte transparency = EventEntry.TRANSPARENCY_OPAQUE;
+ if ("http://schemas.google.com/g/2005#event.opaque".
+ equals(transparencyStr)) {
+ transparency = EventEntry.TRANSPARENCY_OPAQUE;
+ } else if ("http://schemas.google.com/g/2005#event.transparent".
+ equals(transparencyStr)) {
+ transparency = EventEntry.TRANSPARENCY_TRANSPARENT;
+ }
+ eventEntry.setTransparency(transparency);
+ } else if ("visibility".equals(name)) {
+ String visibilityStr = parser.getAttributeValue(null, "value");
+ byte visibility = EventEntry.VISIBILITY_DEFAULT;
+ if ("http://schemas.google.com/g/2005#event.confidential".
+ equals(visibilityStr)) {
+ visibility = EventEntry.VISIBILITY_CONFIDENTIAL;
+ } else if ("http://schemas.google.com/g/2005#event.default"
+ .equals(visibilityStr)) {
+ visibility = EventEntry.VISIBILITY_DEFAULT;
+ } else if ("http://schemas.google.com/g/2005#event.private"
+ .equals(visibilityStr)) {
+ visibility = EventEntry.VISIBILITY_PRIVATE;
+ } else if ("http://schemas.google.com/g/2005#event.public"
+ .equals(visibilityStr)) {
+ visibility = EventEntry.VISIBILITY_PUBLIC;
+ }
+ eventEntry.setVisibility(visibility);
+ } else if ("who".equals(name)) {
+ handleWho(eventEntry);
+ } else if ("when".equals(name)) {
+ handleWhen(eventEntry);
+ } else if ("reminder".equals(name)) {
+ if (!hasSeenReminder) {
+ // if this is the first <reminder> we've seen directly under the
+ // entry, clear any previously seen reminders (under <when>s)
+ eventEntry.clearReminders();
+ hasSeenReminder = true;
+ }
+ handleReminder(eventEntry);
+ } else if ("originalEvent".equals(name)) {
+ handleOriginalEvent(eventEntry);
+ } else if ("where".equals(name)) {
+ String where = parser.getAttributeValue(null /* ns */,
+ "valueString");
+ String rel = parser.getAttributeValue(null /* ns */,
+ "rel");
+ if (StringUtils.isEmpty(rel) ||
+ "http://schemas.google.com/g/2005#event".equals(rel)) {
+ eventEntry.setWhere(where);
+ }
+ // TODO: handle entryLink?
+ } else if ("feedLink".equals(name)) {
+ // TODO: check that the parent is a gd:comments
+ String commentsUri = parser.getAttributeValue(null /* ns */, "href");
+ eventEntry.setCommentsUri(commentsUri);
+ } else if ("extendedProperty".equals(name)) {
+ String propertyName = parser.getAttributeValue(null /* ns */, "name");
+ String propertyValue = parser.getAttributeValue(null /* ns */, "value");
+ eventEntry.addExtendedProperty(propertyName, propertyValue);
+ }
+ }
+
+ private void handleWho(EventEntry eventEntry)
+ throws XmlPullParserException, IOException, ParseException {
+
+ XmlPullParser parser = getParser();
+
+ int eventType = parser.getEventType();
+ String name = parser.getName();
+
+ if (eventType != XmlPullParser.START_TAG ||
+ (!"who".equals(parser.getName()))) {
+ // should not happen.
+ throw new
+ IllegalStateException("Expected <who>: Actual "
+ + "element: <"
+ + name + ">");
+ }
+
+ String email =
+ parser.getAttributeValue(null /* ns */, "email");
+ String relString =
+ parser.getAttributeValue(null /* ns */, "rel");
+ String value =
+ parser.getAttributeValue(null /* ns */, "valueString");
+
+ Who who = new Who();
+ who.setEmail(email);
+ who.setValue(value);
+ byte rel = Who.RELATIONSHIP_NONE;
+ if ("http://schemas.google.com/g/2005#event.attendee".equals(relString)) {
+ rel = Who.RELATIONSHIP_ATTENDEE;
+ } else if ("http://schemas.google.com/g/2005#event.organizer".equals(relString)) {
+ rel = Who.RELATIONSHIP_ORGANIZER;
+ } else if ("http://schemas.google.com/g/2005#event.performer".equals(relString)) {
+ rel = Who.RELATIONSHIP_PERFORMER;
+ } else if ("http://schemas.google.com/g/2005#event.speaker".equals(relString)) {
+ rel = Who.RELATIONSHIP_SPEAKER;
+ } else if (StringUtils.isEmpty(relString)) {
+ rel = Who.RELATIONSHIP_ATTENDEE;
+ } else {
+ throw new ParseException("Unexpected rel: " + relString);
+ }
+ who.setRelationship(rel);
+
+ eventEntry.addAttendee(who);
+
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+ switch (eventType) {
+ case XmlPullParser.START_TAG:
+ name = parser.getName();
+ if ("attendeeStatus".equals(name)) {
+ String statusString =
+ parser.getAttributeValue(null /* ns */, "value");
+ byte status = Who.STATUS_NONE;
+ if ("http://schemas.google.com/g/2005#event.accepted".
+ equals(statusString)) {
+ status = Who.STATUS_ACCEPTED;
+ } else if ("http://schemas.google.com/g/2005#event.declined".
+ equals(statusString)) {
+ status = Who.STATUS_DECLINED;
+ } else if ("http://schemas.google.com/g/2005#event.invited".
+ equals(statusString)) {
+ status = Who.STATUS_INVITED;
+ } else if ("http://schemas.google.com/g/2005#event.tentative".
+ equals(statusString)) {
+ status = Who.STATUS_TENTATIVE;
+ } else if (StringUtils.isEmpty(statusString)) {
+ status = Who.STATUS_TENTATIVE;
+ } else {
+ throw new ParseException("Unexpected status: " + statusString);
+ }
+ who.setStatus(status);
+ } else if ("attendeeType".equals(name)) {
+ String typeString= XmlUtils.extractChildText(parser);
+ byte type = Who.TYPE_NONE;
+ if ("http://schemas.google.com/g/2005#event.optional".equals(typeString)) {
+ type = Who.TYPE_OPTIONAL;
+ } else if ("http://schemas.google.com/g/2005#event.required".
+ equals(typeString)) {
+ type = Who.TYPE_REQUIRED;
+ } else if (StringUtils.isEmpty(typeString)) {
+ type = Who.TYPE_REQUIRED;
+ } else {
+ throw new ParseException("Unexpected type: " + typeString);
+ }
+ who.setType(type);
+ }
+ break;
+ case XmlPullParser.END_TAG:
+ name = parser.getName();
+ if ("who".equals(name)) {
+ return;
+ }
+ default:
+ // ignore
+ }
+
+ eventType = parser.next();
+ }
+ }
+
+ private void handleWhen(EventEntry eventEntry)
+ throws XmlPullParserException, IOException {
+
+ XmlPullParser parser = getParser();
+
+ int eventType = parser.getEventType();
+ String name = parser.getName();
+
+ if (eventType != XmlPullParser.START_TAG ||
+ (!"when".equals(parser.getName()))) {
+ // should not happen.
+ throw new
+ IllegalStateException("Expected <when>: Actual "
+ + "element: <"
+ + name + ">");
+ }
+
+ String startTime =
+ parser.getAttributeValue(null /* ns */, "startTime");
+ String endTime =
+ parser.getAttributeValue(null /* ns */, "endTime");
+
+ When when = new When(startTime, endTime);
+ eventEntry.addWhen(when);
+ boolean firstWhen = eventEntry.getWhens().size() == 1;
+ // we only parse reminders under the when if reminders have not already
+ // been handled (directly under the entry, or in a previous when for
+ // this entry)
+ boolean handleReminders = firstWhen && !hasSeenReminder;
+
+ eventType = parser.next();
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+ switch (eventType) {
+ case XmlPullParser.START_TAG:
+ name = parser.getName();
+ if ("reminder".equals(name)) {
+ // only want to store reminders on the first when. they
+ // should have the same values for all other instances.
+ if (handleReminders) {
+ handleReminder(eventEntry);
+ }
+ }
+ break;
+ case XmlPullParser.END_TAG:
+ name = parser.getName();
+ if ("when".equals(name)) {
+ return;
+ }
+ default:
+ // ignore
+ }
+
+ eventType = parser.next();
+ }
+ }
+
+ private void handleReminder(EventEntry eventEntry) {
+ XmlPullParser parser = getParser();
+
+ Reminder reminder = new Reminder();
+ eventEntry.addReminder(reminder);
+
+ String methodStr = parser.getAttributeValue(null /* ns */,
+ "method");
+ String minutesStr = parser.getAttributeValue(null /* ns */,
+ "minutes");
+ String hoursStr = parser.getAttributeValue(null /* ns */,
+ "hours");
+ String daysStr = parser.getAttributeValue(null /* ns */,
+ "days");
+
+ if (!StringUtils.isEmpty(methodStr)) {
+ if ("alert".equals(methodStr)) {
+ reminder.setMethod(Reminder.METHOD_ALERT);
+ } else if ("email".equals(methodStr)) {
+ reminder.setMethod(Reminder.METHOD_EMAIL);
+ } else if ("sms".equals(methodStr)) {
+ reminder.setMethod(Reminder.METHOD_SMS);
+ }
+ }
+
+ int minutes = Reminder.MINUTES_DEFAULT;
+ if (!StringUtils.isEmpty(minutesStr)) {
+ minutes = StringUtils.parseInt(minutesStr, minutes);
+ } else if (!StringUtils.isEmpty(hoursStr)) {
+ minutes = 60*StringUtils.parseInt(hoursStr, minutes);
+ } else if (!StringUtils.isEmpty(daysStr)) {
+ minutes = 24*60*StringUtils.parseInt(daysStr, minutes);
+ }
+ // TODO: support absolute times?
+ if (minutes < 0) {
+ minutes = Reminder.MINUTES_DEFAULT;
+ }
+ reminder.setMinutes(minutes);
+ }
+
+ private void handleOriginalEvent(EventEntry eventEntry)
+ throws XmlPullParserException, IOException {
+
+ XmlPullParser parser = getParser();
+
+ int eventType = parser.getEventType();
+ String name = parser.getName();
+
+ if (eventType != XmlPullParser.START_TAG ||
+ (!"originalEvent".equals(parser.getName()))) {
+ // should not happen.
+ throw new
+ IllegalStateException("Expected <originalEvent>: Actual "
+ + "element: <"
+ + name + ">");
+ }
+
+ eventEntry.setOriginalEventId(
+ parser.getAttributeValue(null /* ns */, "href"));
+
+ eventType = parser.next();
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+ switch (eventType) {
+ case XmlPullParser.START_TAG:
+ name = parser.getName();
+ if ("when".equals(name)) {
+ eventEntry.setOriginalEventStartTime(
+ parser.getAttributeValue(null/*ns*/, "startTime"));
+ }
+ break;
+ case XmlPullParser.END_TAG:
+ name = parser.getName();
+ if ("originalEvent".equals(name)) {
+ return;
+ }
+ default:
+ // ignore
+ }
+
+ eventType = parser.next();
+ }
+ }
+}
diff --git a/src/com/google/wireless/gdata/calendar/parser/xml/package.html b/src/com/google/wireless/gdata/calendar/parser/xml/package.html
new file mode 100644
index 0000000..1c9bf9d
--- /dev/null
+++ b/src/com/google/wireless/gdata/calendar/parser/xml/package.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+ {@hide}
+</body>
+</html>
diff --git a/src/com/google/wireless/gdata/calendar/serializer/package.html b/src/com/google/wireless/gdata/calendar/serializer/package.html
new file mode 100644
index 0000000..1c9bf9d
--- /dev/null
+++ b/src/com/google/wireless/gdata/calendar/serializer/package.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+ {@hide}
+</body>
+</html>
diff --git a/src/com/google/wireless/gdata/calendar/serializer/xml/XmlEventEntryGDataSerializer.java b/src/com/google/wireless/gdata/calendar/serializer/xml/XmlEventEntryGDataSerializer.java
new file mode 100644
index 0000000..ccb701f
--- /dev/null
+++ b/src/com/google/wireless/gdata/calendar/serializer/xml/XmlEventEntryGDataSerializer.java
@@ -0,0 +1,399 @@
+// Copyright 2007 The Android Open Source Project
+
+package com.google.wireless.gdata.calendar.serializer.xml;
+
+import com.google.wireless.gdata.calendar.data.EventEntry;
+import com.google.wireless.gdata.calendar.data.When;
+import com.google.wireless.gdata.calendar.data.Reminder;
+import com.google.wireless.gdata.calendar.data.Who;
+import com.google.wireless.gdata.data.StringUtils;
+import com.google.wireless.gdata.parser.xml.XmlGDataParser;
+import com.google.wireless.gdata.parser.xml.XmlParserFactory;
+import com.google.wireless.gdata.parser.ParseException;
+import com.google.wireless.gdata.serializer.xml.XmlEntryGDataSerializer;
+
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+
+/**
+ * Serializes Google Calendar event entries into the Atom XML format.
+ */
+// TODO: move all strings into constants. share with parser?
+public class XmlEventEntryGDataSerializer extends XmlEntryGDataSerializer {
+
+ public static final String NAMESPACE_GCAL = "gCal";
+ public static final String NAMESPACE_GCAL_URI =
+ "http://schemas.google.com/gCal/2005";
+
+ public XmlEventEntryGDataSerializer(XmlParserFactory factory,
+ EventEntry entry) {
+ super(factory, entry);
+ }
+
+ protected EventEntry getEventEntry() {
+ return (EventEntry) getEntry();
+ }
+
+ protected void declareExtraEntryNamespaces(XmlSerializer serializer)
+ throws IOException {
+ serializer.setPrefix(NAMESPACE_GCAL, NAMESPACE_GCAL_URI);
+ }
+
+ /* (non-Javadoc)
+ * @see XmlEntryGDataSerializer#serializeExtraEntryContents
+ */
+ protected void serializeExtraEntryContents(XmlSerializer serializer,
+ int format)
+ throws IOException, ParseException {
+ EventEntry entry = getEventEntry();
+
+ serializeEventStatus(serializer, entry.getStatus());
+ serializeTransparency(serializer, entry.getTransparency());
+ serializeVisibility(serializer, entry.getVisibility());
+ Enumeration attendees = entry.getAttendees().elements();
+ while (attendees.hasMoreElements()) {
+ Who attendee = (Who) attendees.nextElement();
+ serializeWho(serializer, entry, attendee);
+ }
+
+ serializeRecurrence(serializer, entry.getRecurrence());
+ // either serialize reminders directly under the entry, or serialize
+ // whens (with reminders within the whens) -- should be just one.
+ if (entry.getRecurrence() != null) {
+ if (entry.getReminders() != null) {
+ Enumeration reminders = entry.getReminders().elements();
+ while (reminders.hasMoreElements()) {
+ Reminder reminder = (Reminder) reminders.nextElement();
+ serializeReminder(serializer, reminder);
+ }
+ }
+ } else {
+ Enumeration whens = entry.getWhens().elements();
+ while (whens.hasMoreElements()) {
+ When when = (When) whens.nextElement();
+ serializeWhen(serializer, entry, when);
+ }
+ }
+ serializeOriginalEvent(serializer,
+ entry.getOriginalEventId(),
+ entry.getOriginalEventStartTime());
+ serializeWhere(serializer, entry.getWhere());
+
+ serializeCommentsUri(serializer, entry.getCommentsUri());
+
+ Hashtable extendedProperties = entry.getExtendedProperties();
+ if (extendedProperties != null) {
+ Enumeration propertyNames = extendedProperties.keys();
+ while (propertyNames.hasMoreElements()) {
+ String propertyName = (String) propertyNames.nextElement();
+ String propertyValue = (String) extendedProperties.get(propertyName);
+ serializeExtendedProperty(serializer, propertyName, propertyValue);
+ }
+ }
+ }
+
+ private static void serializeEventStatus(XmlSerializer serializer,
+ byte status)
+ throws IOException {
+
+ String statusString;
+
+ switch (status) {
+ case EventEntry.STATUS_TENTATIVE:
+ statusString = "http://schemas.google.com/g/2005#event.tentative";
+ break;
+ case EventEntry.STATUS_CANCELED:
+ statusString = "http://schemas.google.com/g/2005#event.canceled";
+ break;
+ case EventEntry.STATUS_CONFIRMED:
+ statusString = "http://schemas.google.com/g/2005#event.confirmed";
+ break;
+ default:
+ // should not happen
+ // TODO: log this
+ statusString = "http://schemas.google.com/g/2005#event.tentative";
+ }
+
+ serializer.startTag(XmlGDataParser.NAMESPACE_GD_URI, "eventStatus");
+ serializer.attribute(null /* ns */, "value", statusString);
+ serializer.endTag(XmlGDataParser.NAMESPACE_GD_URI, "eventStatus");
+ }
+
+ private static void serializeRecurrence(XmlSerializer serializer,
+ String recurrence)
+ throws IOException {
+ if (StringUtils.isEmpty(recurrence)) {
+ return;
+ }
+ serializer.startTag(XmlGDataParser.NAMESPACE_GD_URI, "recurrence");
+ serializer.text(recurrence);
+ serializer.endTag(XmlGDataParser.NAMESPACE_GD_URI, "recurrence");
+ }
+
+ private static void serializeTransparency(XmlSerializer serializer,
+ byte transparency)
+ throws IOException {
+
+ String transparencyString;
+
+ switch (transparency) {
+ case EventEntry.TRANSPARENCY_OPAQUE:
+ transparencyString =
+ "http://schemas.google.com/g/2005#event.opaque";
+ break;
+ case EventEntry.TRANSPARENCY_TRANSPARENT:
+ transparencyString =
+ "http://schemas.google.com/g/2005#event.transparent";
+ break;
+ default:
+ // should not happen
+ // TODO: log this
+ transparencyString =
+ "http://schemas.google.com/g/2005#event.transparent";
+ }
+
+ serializer.startTag(XmlGDataParser.NAMESPACE_GD_URI, "transparency");
+ serializer.attribute(null /* ns */, "value", transparencyString);
+ serializer.endTag(XmlGDataParser.NAMESPACE_GD_URI, "transparency");
+ }
+
+
+ private static void serializeVisibility(XmlSerializer serializer,
+ byte visibility)
+ throws IOException {
+
+ String visibilityString;
+
+ switch (visibility) {
+ case EventEntry.VISIBILITY_DEFAULT:
+ visibilityString = "http://schemas.google.com/g/2005#event.default";
+ break;
+ case EventEntry.VISIBILITY_CONFIDENTIAL:
+ visibilityString =
+ "http://schemas.google.com/g/2005#event.confidential";
+ break;
+ case EventEntry.VISIBILITY_PRIVATE:
+ visibilityString = "http://schemas.google.com/g/2005#event.private";
+ break;
+ case EventEntry.VISIBILITY_PUBLIC:
+ visibilityString = "http://schemas.google.com/g/2005#event.public";
+ break;
+ default:
+ // should not happen
+ // TODO: log this
+ visibilityString = "http://schemas.google.com/g/2005#event.default";
+ }
+
+ serializer.startTag(XmlGDataParser.NAMESPACE_GD_URI, "visibility");
+ serializer.attribute(null /* ns */, "value", visibilityString);
+ serializer.endTag(XmlGDataParser.NAMESPACE_GD_URI, "visibility");
+ }
+
+ private static void serializeWho(XmlSerializer serializer,
+ EventEntry entry,
+ Who who)
+ throws IOException, ParseException {
+ serializer.startTag(XmlGDataParser.NAMESPACE_GD_URI, "who");
+ String email = who.getEmail();
+ if (!StringUtils.isEmpty(email)) {
+ serializer.attribute(null /* ns */, "email", email);
+ }
+
+ String value = who.getValue();
+ if (!StringUtils.isEmpty(value)) {
+ serializer.attribute(null /* ns */, "valueString", value);
+ }
+
+ String rel = null;
+ switch (who.getRelationship()) {
+ case Who.RELATIONSHIP_NONE:
+ break;
+ case Who.RELATIONSHIP_ATTENDEE:
+ rel = "http://schemas.google.com/g/2005#event.attendee";
+ break;
+ case Who.RELATIONSHIP_ORGANIZER:
+ rel = "http://schemas.google.com/g/2005#event.organizer";
+ break;
+ case Who.RELATIONSHIP_PERFORMER:
+ rel = "http://schemas.google.com/g/2005#event.performer";
+ break;
+ case Who.RELATIONSHIP_SPEAKER:
+ rel = "http://schemas.google.com/g/2005#event.speaker";
+ break;
+ default:
+ throw new ParseException("Unexpected rel: " + who.getRelationship());
+ }
+ if (!StringUtils.isEmpty(rel)) {
+ serializer.attribute(null /* ns */, "rel", rel);
+ }
+
+ String status = null;
+ switch (who.getStatus()) {
+ case Who.STATUS_NONE:
+ break;
+ case Who.STATUS_ACCEPTED:
+ status = "http://schemas.google.com/g/2005#event.accepted";
+ break;
+ case Who.STATUS_DECLINED:
+ status = "http://schemas.google.com/g/2005#event.declined";
+ break;
+ case Who.STATUS_INVITED:
+ status = "http://schemas.google.com/g/2005#event.invited";
+ break;
+ case Who.STATUS_TENTATIVE:
+ status = "http://schemas.google.com/g/2005#event.tentative";
+ break;
+ default:
+ throw new ParseException("Unexpected status: " + who.getStatus());
+ }
+ if (!StringUtils.isEmpty(status)) {
+ serializer.startTag(XmlGDataParser.NAMESPACE_GD_URI,
+ "attendeeStatus");
+ serializer.attribute(null /* ns */, "value", status);
+ serializer.endTag(XmlGDataParser.NAMESPACE_GD_URI,
+ "attendeeStatus");
+ }
+
+ String type = null;
+ switch (who.getType()) {
+ case Who.TYPE_NONE:
+ break;
+ case Who.TYPE_REQUIRED:
+ type = "http://schemas.google.com/g/2005#event.required";
+ break;
+ case Who.TYPE_OPTIONAL:
+ type = "http://schemas.google.com/g/2005#event.optional";
+ break;
+ default:
+ throw new ParseException("Unexpected type: " + who.getType());
+ }
+ if (!StringUtils.isEmpty(type)) {
+ serializer.startTag(XmlGDataParser.NAMESPACE_GD_URI,
+ "attendeeType");
+ serializer.attribute(null /* ns */, "value", type);
+ serializer.endTag(XmlGDataParser.NAMESPACE_GD_URI, "attendeeType");
+ }
+ serializer.endTag(XmlGDataParser.NAMESPACE_GD_URI, "who");
+ }
+
+ private static void serializeWhen(XmlSerializer serializer,
+ EventEntry entry,
+ When when)
+ throws IOException {
+ // TODO: throw exn if startTime is empty but endTime is not?
+ String startTime = when.getStartTime();
+ String endTime = when.getEndTime();
+ if (StringUtils.isEmpty(when.getStartTime())) {
+ return;
+ }
+
+ serializer.startTag(XmlGDataParser.NAMESPACE_GD_URI, "when");
+ serializer.attribute(null /* ns */, "startTime", startTime);
+ if (!StringUtils.isEmpty(endTime)) {
+ serializer.attribute(null /* ns */, "endTime", endTime);
+ }
+ if (entry.getReminders() != null) {
+ Enumeration reminders = entry.getReminders().elements();
+ while (reminders.hasMoreElements()) {
+ Reminder reminder = (Reminder) reminders.nextElement();
+ serializeReminder(serializer, reminder);
+ }
+ }
+ serializer.endTag(XmlGDataParser.NAMESPACE_GD_URI, "when");
+ }
+
+ private static void serializeReminder(XmlSerializer serializer,
+ Reminder reminder)
+ throws IOException {
+
+ serializer.startTag(XmlGDataParser.NAMESPACE_GD_URI, "reminder");
+ byte method = reminder.getMethod();
+ String methodStr = null;
+ switch (method) {
+ case Reminder.METHOD_ALERT:
+ methodStr = "alert";
+ break;
+ case Reminder.METHOD_EMAIL:
+ methodStr = "email";
+ break;
+ case Reminder.METHOD_SMS:
+ methodStr = "sms";
+ break;
+ }
+ if (methodStr != null) {
+ serializer.attribute(null /* ns */, "method", methodStr);
+ }
+
+ int minutes = reminder.getMinutes();
+ if (minutes != Reminder.MINUTES_DEFAULT) {
+ serializer.attribute(null /* ns */, "minutes",
+ Integer.toString(minutes));
+ }
+
+ serializer.endTag(XmlGDataParser.NAMESPACE_GD_URI, "reminder");
+ }
+
+ private static void serializeOriginalEvent(XmlSerializer serializer,
+ String originalEventId,
+ String originalEventTime)
+ throws IOException {
+ if (StringUtils.isEmpty(originalEventId) ||
+ StringUtils.isEmpty(originalEventTime)) {
+ return;
+ }
+
+
+ serializer.startTag(XmlGDataParser.NAMESPACE_GD_URI, "originalEvent");
+ int index = originalEventId.lastIndexOf("/");
+ if (index != -1) {
+ String id = originalEventId.substring(index + 1);
+ if (!StringUtils.isEmpty(id)) {
+ serializer.attribute(null /* ns */, "id", id);
+ }
+ }
+ serializer.attribute(null /* ns */, "href", originalEventId);
+ serializer.startTag(XmlGDataParser.NAMESPACE_GD_URI, "when");
+ serializer.attribute(null /* ns */, "startTime", originalEventTime);
+ serializer.endTag(XmlGDataParser.NAMESPACE_GD_URI, "when");
+ serializer.endTag(XmlGDataParser.NAMESPACE_GD_URI, "originalEvent");
+ }
+
+
+ private static void serializeWhere(XmlSerializer serializer,
+ String where)
+ throws IOException {
+ if (StringUtils.isEmpty(where)) {
+ return;
+ }
+
+ serializer.startTag(XmlGDataParser.NAMESPACE_GD_URI, "where");
+ serializer.attribute(null /* ns */, "valueString", where);
+ serializer.endTag(XmlGDataParser.NAMESPACE_GD_URI, "where");
+ }
+
+ private static void serializeCommentsUri(XmlSerializer serializer,
+ String commentsUri)
+ throws IOException {
+ if (commentsUri == null) {
+ return;
+ }
+
+ serializer.startTag(XmlGDataParser.NAMESPACE_GD_URI, "feedLink");
+ serializer.attribute(null /* ns */, "href", commentsUri);
+ serializer.endTag(XmlGDataParser.NAMESPACE_GD_URI, "feedLink");
+ }
+
+ private static void serializeExtendedProperty(XmlSerializer serializer,
+ String name,
+ String value)
+ throws IOException {
+ serializer.startTag(XmlGDataParser.NAMESPACE_GD_URI, "extendedProperty");
+ serializer.attribute(null /* ns */, "name", name);
+ serializer.attribute(null /* ns */, "value", value);
+ serializer.endTag(XmlGDataParser.NAMESPACE_GD_URI, "extendedProperty");
+ }
+}
diff --git a/src/com/google/wireless/gdata/calendar/serializer/xml/package.html b/src/com/google/wireless/gdata/calendar/serializer/xml/package.html
new file mode 100644
index 0000000..1c9bf9d
--- /dev/null
+++ b/src/com/google/wireless/gdata/calendar/serializer/xml/package.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+ {@hide}
+</body>
+</html>