blob: 920a8d52787ce532eccce2d2836e3087ded1e5c4 [file] [log] [blame]
// 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();
}
@Override
protected void handleEntry(Entry entry) throws XmlPullParserException,
IOException, ParseException {
hasSeenReminder = false; // Reset the state for the new entry
super.handleEntry(entry);
}
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 ("sendEventNotifications".equals(name)) {
// TODO: check that the namespace is gCal
String value = parser.getAttributeValue(null /* ns */, "value");
if ("true".equals(value)) {
eventEntry.setSendEventNotifications(true);
}
} 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();
}
}
}