diff options
Diffstat (limited to 'src/com/google/wireless/gdata/calendar')
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> |