summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJean-Baptiste Queru <jbq@google.com>2009-11-12 18:45:26 -0800
committerJean-Baptiste Queru <jbq@google.com>2009-11-12 18:45:26 -0800
commit874caa518dee0a761ba5102a2b81ef84fefd2181 (patch)
treeb0417c48c9d1384c72027efe4f6d68ec686ce127
parenta8d14b5ad6306e65266c1801dabb660f8d4a04a1 (diff)
downloadgdata-874caa518dee0a761ba5102a2b81ef84fefd2181.tar.gz
eclair snapshot
-rw-r--r--src/com/google/wireless/gdata/calendar/data/CalendarEntry.java5
-rw-r--r--src/com/google/wireless/gdata/calendar/data/EventEntry.java68
-rw-r--r--src/com/google/wireless/gdata/calendar/parser/xml/XmlCalendarsGDataParser.java2
-rw-r--r--src/com/google/wireless/gdata/calendar/parser/xml/XmlEventsGDataParser.java18
-rw-r--r--src/com/google/wireless/gdata/calendar/serializer/xml/XmlEventEntryGDataSerializer.java59
-rw-r--r--src/com/google/wireless/gdata/client/HttpQueryParams.java4
-rw-r--r--src/com/google/wireless/gdata/data/XmlUtils.java24
-rw-r--r--src/com/google/wireless/gdata/parser/ConflictException.java38
-rwxr-xr-xsrc/com/google/wireless/gdata/spreadsheets/client/SpreadsheetsClient.java236
-rwxr-xr-xsrc/com/google/wireless/gdata/spreadsheets/data/CellEntry.java134
-rwxr-xr-xsrc/com/google/wireless/gdata/spreadsheets/data/CellFeed.java35
-rwxr-xr-xsrc/com/google/wireless/gdata/spreadsheets/data/ListEntry.java77
-rwxr-xr-xsrc/com/google/wireless/gdata/spreadsheets/data/ListFeed.java35
-rwxr-xr-xsrc/com/google/wireless/gdata/spreadsheets/data/SpreadsheetEntry.java38
-rwxr-xr-xsrc/com/google/wireless/gdata/spreadsheets/data/SpreadsheetFeed.java14
-rw-r--r--src/com/google/wireless/gdata/spreadsheets/data/WorksheetEntry.java105
-rwxr-xr-xsrc/com/google/wireless/gdata/spreadsheets/data/WorksheetFeed.java14
-rwxr-xr-xsrc/com/google/wireless/gdata/spreadsheets/parser/xml/XmlCellsGDataParser.java126
-rwxr-xr-xsrc/com/google/wireless/gdata/spreadsheets/parser/xml/XmlListGDataParser.java109
-rwxr-xr-xsrc/com/google/wireless/gdata/spreadsheets/parser/xml/XmlSpreadsheetsGDataParser.java68
-rw-r--r--src/com/google/wireless/gdata/spreadsheets/parser/xml/XmlSpreadsheetsGDataParserFactory.java99
-rwxr-xr-xsrc/com/google/wireless/gdata/spreadsheets/parser/xml/XmlWorksheetsGDataParser.java98
-rwxr-xr-xsrc/com/google/wireless/gdata/spreadsheets/serializer/xml/XmlCellEntryGDataSerializer.java83
-rwxr-xr-xsrc/com/google/wireless/gdata/spreadsheets/serializer/xml/XmlListEntryGDataSerializer.java67
-rw-r--r--src/com/google/wireless/gdata2/ConflictDetectedException.java32
-rw-r--r--src/com/google/wireless/gdata2/GDataException.java64
-rw-r--r--src/com/google/wireless/gdata2/client/AuthenticationException.java37
-rw-r--r--src/com/google/wireless/gdata2/client/BadRequestException.java37
-rw-r--r--src/com/google/wireless/gdata2/client/ForbiddenException.java37
-rw-r--r--src/com/google/wireless/gdata2/client/GDataClient.java209
-rw-r--r--src/com/google/wireless/gdata2/client/GDataParserFactory.java57
-rw-r--r--src/com/google/wireless/gdata2/client/GDataServiceClient.java478
-rw-r--r--src/com/google/wireless/gdata2/client/HttpException.java63
-rw-r--r--src/com/google/wireless/gdata2/client/HttpQueryParams.java73
-rw-r--r--src/com/google/wireless/gdata2/client/PreconditionFailedException.java39
-rw-r--r--src/com/google/wireless/gdata2/client/QueryParams.java258
-rw-r--r--src/com/google/wireless/gdata2/client/ResourceGoneException.java37
-rw-r--r--src/com/google/wireless/gdata2/client/ResourceNotFoundException.java37
-rw-r--r--src/com/google/wireless/gdata2/client/ResourceNotModifiedException.java39
-rw-r--r--src/com/google/wireless/gdata2/client/package.html (renamed from src/com/google/wireless/gdata/spreadsheets/client/package.html)0
-rw-r--r--src/com/google/wireless/gdata2/contacts/client/ContactsClient.java43
-rw-r--r--src/com/google/wireless/gdata2/contacts/client/package.html (renamed from src/com/google/wireless/gdata/spreadsheets/data/package.html)0
-rw-r--r--src/com/google/wireless/gdata2/contacts/data/CalendarLink.java54
-rw-r--r--src/com/google/wireless/gdata2/contacts/data/ContactEntry.java720
-rw-r--r--src/com/google/wireless/gdata2/contacts/data/ContactsElement.java33
-rw-r--r--src/com/google/wireless/gdata2/contacts/data/ContactsFeed.java16
-rw-r--r--src/com/google/wireless/gdata2/contacts/data/EmailAddress.java55
-rw-r--r--src/com/google/wireless/gdata2/contacts/data/Event.java55
-rw-r--r--src/com/google/wireless/gdata2/contacts/data/ExternalId.java67
-rw-r--r--src/com/google/wireless/gdata2/contacts/data/GeoPt.java76
-rw-r--r--src/com/google/wireless/gdata2/contacts/data/GroupEntry.java40
-rw-r--r--src/com/google/wireless/gdata2/contacts/data/GroupMembershipInfo.java53
-rw-r--r--src/com/google/wireless/gdata2/contacts/data/GroupsFeed.java16
-rw-r--r--src/com/google/wireless/gdata2/contacts/data/ImAddress.java72
-rw-r--r--src/com/google/wireless/gdata2/contacts/data/Jot.java64
-rw-r--r--src/com/google/wireless/gdata2/contacts/data/Language.java103
-rw-r--r--src/com/google/wireless/gdata2/contacts/data/Name.java167
-rw-r--r--src/com/google/wireless/gdata2/contacts/data/Organization.java135
-rw-r--r--src/com/google/wireless/gdata2/contacts/data/PhoneNumber.java55
-rw-r--r--src/com/google/wireless/gdata2/contacts/data/Relation.java69
-rw-r--r--src/com/google/wireless/gdata2/contacts/data/StructuredPostalAddress.java206
-rw-r--r--src/com/google/wireless/gdata2/contacts/data/TypedElement.java56
-rw-r--r--src/com/google/wireless/gdata2/contacts/data/UserDefinedField.java95
-rw-r--r--src/com/google/wireless/gdata2/contacts/data/WebSite.java55
-rw-r--r--src/com/google/wireless/gdata2/contacts/data/package.html (renamed from src/com/google/wireless/gdata/spreadsheets/package.html)0
-rw-r--r--src/com/google/wireless/gdata2/contacts/package.html (renamed from src/com/google/wireless/gdata/spreadsheets/parser/package.html)0
-rw-r--r--src/com/google/wireless/gdata2/contacts/parser/package.html (renamed from src/com/google/wireless/gdata/spreadsheets/parser/xml/package.html)0
-rw-r--r--src/com/google/wireless/gdata2/contacts/parser/xml/XmlContactsGDataParser.java639
-rw-r--r--src/com/google/wireless/gdata2/contacts/parser/xml/XmlContactsGDataParserFactory.java137
-rw-r--r--src/com/google/wireless/gdata2/contacts/parser/xml/XmlGroupEntryGDataParser.java62
-rw-r--r--src/com/google/wireless/gdata2/contacts/parser/xml/XmlNametable.java83
-rw-r--r--src/com/google/wireless/gdata2/contacts/parser/xml/package.html (renamed from src/com/google/wireless/gdata/spreadsheets/serializer/package.html)0
-rw-r--r--src/com/google/wireless/gdata2/contacts/serializer/package.html (renamed from src/com/google/wireless/gdata/spreadsheets/serializer/xml/package.html)0
-rw-r--r--src/com/google/wireless/gdata2/contacts/serializer/xml/XmlContactEntryGDataSerializer.java557
-rw-r--r--src/com/google/wireless/gdata2/contacts/serializer/xml/XmlGroupEntryGDataSerializer.java54
-rw-r--r--src/com/google/wireless/gdata2/contacts/serializer/xml/package.html5
-rw-r--r--src/com/google/wireless/gdata2/data/Entry.java401
-rw-r--r--src/com/google/wireless/gdata2/data/ExtendedProperty.java95
-rw-r--r--src/com/google/wireless/gdata2/data/Feed.java173
-rw-r--r--src/com/google/wireless/gdata2/data/MediaEntry.java12
-rw-r--r--src/com/google/wireless/gdata2/data/StringUtils.java78
-rw-r--r--src/com/google/wireless/gdata2/data/XmlUtils.java100
-rw-r--r--src/com/google/wireless/gdata2/data/batch/BatchInfo.java30
-rw-r--r--src/com/google/wireless/gdata2/data/batch/BatchInterrupted.java75
-rw-r--r--src/com/google/wireless/gdata2/data/batch/BatchStatus.java75
-rw-r--r--src/com/google/wireless/gdata2/data/batch/BatchUtils.java93
-rw-r--r--src/com/google/wireless/gdata2/data/package.html5
-rw-r--r--src/com/google/wireless/gdata2/package.html5
-rw-r--r--src/com/google/wireless/gdata2/parser/GDataParser.java64
-rw-r--r--src/com/google/wireless/gdata2/parser/ParseException.java38
-rw-r--r--src/com/google/wireless/gdata2/parser/package.html5
-rw-r--r--src/com/google/wireless/gdata2/parser/xml/SimplePullParser.java302
-rw-r--r--src/com/google/wireless/gdata2/parser/xml/XmlGDataParser.java770
-rw-r--r--src/com/google/wireless/gdata2/parser/xml/XmlMediaEntryGDataParser.java44
-rw-r--r--src/com/google/wireless/gdata2/parser/xml/XmlNametable.java53
-rw-r--r--src/com/google/wireless/gdata2/parser/xml/XmlParserFactory.java31
-rw-r--r--src/com/google/wireless/gdata2/parser/xml/package.html5
-rw-r--r--src/com/google/wireless/gdata2/serializer/GDataSerializer.java69
-rw-r--r--src/com/google/wireless/gdata2/serializer/package.html5
-rw-r--r--src/com/google/wireless/gdata2/serializer/xml/XmlBatchGDataSerializer.java108
-rw-r--r--src/com/google/wireless/gdata2/serializer/xml/XmlEntryGDataSerializer.java326
-rw-r--r--src/com/google/wireless/gdata2/serializer/xml/package.html5
102 files changed, 8520 insertions, 1342 deletions
diff --git a/src/com/google/wireless/gdata/calendar/data/CalendarEntry.java b/src/com/google/wireless/gdata/calendar/data/CalendarEntry.java
index 6e49a9f..0d9e8d8 100644
--- a/src/com/google/wireless/gdata/calendar/data/CalendarEntry.java
+++ b/src/com/google/wireless/gdata/calendar/data/CalendarEntry.java
@@ -35,6 +35,11 @@ public class CalendarEntry extends Entry {
* Access level constant indicating the user owns this calendar.
*/
public static final byte ACCESS_OWNER = 4;
+
+ /**
+ * Access level constant indicating the user is a domain admin.
+ */
+ public static final byte ACCESS_ROOT = 5;
private byte accessLevel = ACCESS_READ;
// TODO: rename to feed Url?
diff --git a/src/com/google/wireless/gdata/calendar/data/EventEntry.java b/src/com/google/wireless/gdata/calendar/data/EventEntry.java
index 5f2f271..329d6f4 100644
--- a/src/com/google/wireless/gdata/calendar/data/EventEntry.java
+++ b/src/com/google/wireless/gdata/calendar/data/EventEntry.java
@@ -70,6 +70,11 @@ public class EventEntry extends Entry {
private byte visibility = VISIBILITY_DEFAULT;
private byte transparency = TRANSPARENCY_OPAQUE;
private Vector attendees = new Vector();
+ private boolean sendEventNotifications = false;
+ private boolean guestsCanModify = false;
+ private boolean guestsCanInviteOthers = true;
+ private boolean guestsCanSeeGuests = true;
+ private String organizer = null;
private Vector whens = new Vector();
private Vector reminders = null;
private String originalEventId = null;
@@ -77,6 +82,7 @@ public class EventEntry extends Entry {
private String where = null;
private String commentsUri = null;
private Hashtable extendedProperties = null;
+ private boolean quickAdd = false;
/**
* Creates a new empty event entry.
@@ -94,6 +100,11 @@ public class EventEntry extends Entry {
recurrence = null;
visibility = VISIBILITY_DEFAULT;
transparency = TRANSPARENCY_OPAQUE;
+ sendEventNotifications = false;
+ guestsCanModify = false;
+ guestsCanInviteOthers = true;
+ guestsCanSeeGuests = true;
+ organizer = null;
attendees.removeAllElements();
whens.removeAllElements();
reminders = null;
@@ -102,6 +113,7 @@ public class EventEntry extends Entry {
where = null;
commentsUri = null;
extendedProperties = null;
+ quickAdd = false;
}
/**
@@ -160,6 +172,46 @@ public class EventEntry extends Entry {
this.visibility = visibility;
}
+ public boolean getSendEventNotifications() {
+ return sendEventNotifications;
+ }
+
+ public void setSendEventNotifications(boolean sendEventNotifications) {
+ this.sendEventNotifications = sendEventNotifications;
+ }
+
+ public boolean getGuestsCanModify() {
+ return guestsCanModify;
+ }
+
+ public void setGuestsCanModify(boolean guestsCanModify) {
+ this.guestsCanModify = guestsCanModify;
+ }
+
+ public boolean getGuestsCanInviteOthers() {
+ return guestsCanInviteOthers;
+ }
+
+ public void setGuestsCanInviteOthers(boolean guestsCanInviteOthers) {
+ this.guestsCanInviteOthers = guestsCanInviteOthers;
+ }
+
+ public boolean getGuestsCanSeeGuests() {
+ return guestsCanSeeGuests;
+ }
+
+ public void setGuestsCanSeeGuests(boolean guestsCanSeeGuests) {
+ this.guestsCanSeeGuests = guestsCanSeeGuests;
+ }
+
+ public String getOrganizer() {
+ return organizer;
+ }
+
+ public void setOrganizer(String organizer) {
+ this.organizer = organizer;
+ }
+
public void clearAttendees() {
attendees.clear();
}
@@ -270,15 +322,29 @@ public class EventEntry extends Entry {
this.commentsUri = commentsUri;
}
+ public boolean isQuickAdd() {
+ return quickAdd;
+ }
+
+ public void setQuickAdd(boolean quickAdd) {
+ this.quickAdd = quickAdd;
+ }
+
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);
+ sb.append("QUICK_ADD: " + (quickAdd ? "true" : "false"));
+ sb.append("SEND_EVENT_NOTIFICATIONS: " + (sendEventNotifications ? "true" : "false"));
+ sb.append("GUESTS_CAN_MODIFY: " + (guestsCanModify ? "true" : "false"));
+ sb.append("GUESTS_CAN_INVITE_OTHERS: " + (guestsCanInviteOthers ? "true" : "false"));
+ sb.append("GUESTS_CAN_SEE_GUESTS: " + (guestsCanSeeGuests ? "true" : "false"));
+ appendIfNotNull(sb, "ORGANIZER", organizer);
Enumeration whos = this.attendees.elements();
while (whos.hasMoreElements()) {
diff --git a/src/com/google/wireless/gdata/calendar/parser/xml/XmlCalendarsGDataParser.java b/src/com/google/wireless/gdata/calendar/parser/xml/XmlCalendarsGDataParser.java
index 431fd43..9619834 100644
--- a/src/com/google/wireless/gdata/calendar/parser/xml/XmlCalendarsGDataParser.java
+++ b/src/com/google/wireless/gdata/calendar/parser/xml/XmlCalendarsGDataParser.java
@@ -81,6 +81,8 @@ public class XmlCalendarsGDataParser extends XmlGDataParser {
accesslevel = CalendarEntry.ACCESS_EDITOR;
} else if ("owner".equals(accesslevelStr)) {
accesslevel = CalendarEntry.ACCESS_OWNER;
+ } else if ("root".equals(accesslevelStr)) {
+ accesslevel = CalendarEntry.ACCESS_ROOT;
}
calendarEntry.setAccessLevel(accesslevel);
} else if ("color".equals(name)) {
diff --git a/src/com/google/wireless/gdata/calendar/parser/xml/XmlEventsGDataParser.java b/src/com/google/wireless/gdata/calendar/parser/xml/XmlEventsGDataParser.java
index 7ec447e..eddbc3f 100644
--- a/src/com/google/wireless/gdata/calendar/parser/xml/XmlEventsGDataParser.java
+++ b/src/com/google/wireless/gdata/calendar/parser/xml/XmlEventsGDataParser.java
@@ -152,7 +152,23 @@ public class XmlEventsGDataParser extends XmlGDataParser {
eventEntry.setVisibility(visibility);
} else if ("who".equals(name)) {
handleWho(eventEntry);
- } else if ("when".equals(name)) {
+ } else if ("sendEventNotifications".equals(name)) {
+ // TODO: check that the namespace is gCal
+ String value = parser.getAttributeValue(null /* ns */, "value");
+ eventEntry.setSendEventNotifications("true".equals(value));
+ } else if ("guestsCanModify".equals(name)) {
+ // TODO: check that the namespace is gCal
+ String value = parser.getAttributeValue(null /* ns */, "value");
+ eventEntry.setGuestsCanModify("true".equals(value));
+ } else if ("guestsCanInviteOthers".equals(name)) {
+ // TODO: check that the namespace is gCal
+ String value = parser.getAttributeValue(null /* ns */, "value");
+ eventEntry.setGuestsCanInviteOthers("true".equals(value));
+ } else if ("guestsCanSeeGuests".equals(name)) {
+ // TODO: check that the namespace is gCal
+ String value = parser.getAttributeValue(null /* ns */, "value");
+ eventEntry.setGuestsCanSeeGuests("true".equals(value));
+ } else if ("when".equals(name)) {
handleWhen(eventEntry);
} else if ("reminder".equals(name)) {
if (!hasSeenReminder) {
diff --git a/src/com/google/wireless/gdata/calendar/serializer/xml/XmlEventEntryGDataSerializer.java b/src/com/google/wireless/gdata/calendar/serializer/xml/XmlEventEntryGDataSerializer.java
index ccb701f..3fe8c27 100644
--- a/src/com/google/wireless/gdata/calendar/serializer/xml/XmlEventEntryGDataSerializer.java
+++ b/src/com/google/wireless/gdata/calendar/serializer/xml/XmlEventEntryGDataSerializer.java
@@ -54,6 +54,20 @@ public class XmlEventEntryGDataSerializer extends XmlEntryGDataSerializer {
serializeEventStatus(serializer, entry.getStatus());
serializeTransparency(serializer, entry.getTransparency());
serializeVisibility(serializer, entry.getVisibility());
+ if (entry.getSendEventNotifications()) {
+ serializer.startTag(NAMESPACE_GCAL_URI, "sendEventNotifications");
+ serializer.attribute(null /* ns */, "value", "true");
+ serializer.endTag(NAMESPACE_GCAL_URI, "sendEventNotifications");
+ }
+
+ // TODO: sending these values can cause server crashes, e.g. if modifying attendee
+ // status while sending guestsCanModify=false
+ /*
+ serializeGuestsCanModify(serializer, entry.getGuestsCanModify());
+ serializeGuestsCanInviteOthers(serializer, entry.getGuestsCanInviteOthers());
+ serializeGuestsCanSeeGuests(serializer, entry.getGuestsCanSeeGuests());
+ */
+
Enumeration attendees = entry.getAttendees().elements();
while (attendees.hasMoreElements()) {
Who attendee = (Who) attendees.nextElement();
@@ -94,6 +108,8 @@ public class XmlEventEntryGDataSerializer extends XmlEntryGDataSerializer {
serializeExtendedProperty(serializer, propertyName, propertyValue);
}
}
+
+ serializeQuickAdd(serializer, entry.isQuickAdd());
}
private static void serializeEventStatus(XmlSerializer serializer,
@@ -193,7 +209,39 @@ public class XmlEventEntryGDataSerializer extends XmlEntryGDataSerializer {
serializer.endTag(XmlGDataParser.NAMESPACE_GD_URI, "visibility");
}
- private static void serializeWho(XmlSerializer serializer,
+ private static void serializeSendEventNotifications(XmlSerializer serializer,
+ boolean sendEventNotifications)
+ throws IOException {
+ serializer.startTag(NAMESPACE_GCAL_URI, "sendEventNotifications");
+ serializer.attribute(null /* ns */, "value", sendEventNotifications ? "true" : "false");
+ serializer.endTag(NAMESPACE_GCAL_URI, "sendEventNotifications");
+ }
+
+ private static void serializeGuestsCanModify(XmlSerializer serializer,
+ boolean guestsCanModify)
+ throws IOException {
+ serializer.startTag(NAMESPACE_GCAL_URI, "guestsCanModify");
+ serializer.attribute(null /* ns */, "value", guestsCanModify ? "true" : "false");
+ serializer.endTag(NAMESPACE_GCAL_URI, "guestsCanModify");
+ }
+
+ private static void serializeGuestsCanInviteOthers(XmlSerializer serializer,
+ boolean guestsCanInviteOthers)
+ throws IOException {
+ serializer.startTag(NAMESPACE_GCAL_URI, "guestsCanInviteOthers");
+ serializer.attribute(null /* ns */, "value", guestsCanInviteOthers ? "true" : "false");
+ serializer.endTag(NAMESPACE_GCAL_URI, "guestsCanInviteOthers");
+ }
+
+ private static void serializeGuestsCanSeeGuests(XmlSerializer serializer,
+ boolean guestsCanSeeGuests)
+ throws IOException {
+ serializer.startTag(NAMESPACE_GCAL_URI, "guestsCanSeeGuests");
+ serializer.attribute(null /* ns */, "value", guestsCanSeeGuests ? "true" : "false");
+ serializer.endTag(NAMESPACE_GCAL_URI, "guestsCanSeeGuests");
+ }
+
+ private static void serializeWho(XmlSerializer serializer,
EventEntry entry,
Who who)
throws IOException, ParseException {
@@ -396,4 +444,13 @@ public class XmlEventEntryGDataSerializer extends XmlEntryGDataSerializer {
serializer.attribute(null /* ns */, "value", value);
serializer.endTag(XmlGDataParser.NAMESPACE_GD_URI, "extendedProperty");
}
+
+ private static void serializeQuickAdd(XmlSerializer serializer,
+ boolean quickAdd) throws IOException {
+ if (quickAdd) {
+ serializer.startTag(NAMESPACE_GCAL, "quickadd");
+ serializer.attribute(null /* ns */, "value", "true");
+ serializer.endTag(NAMESPACE_GCAL, "quickadd");
+ }
+ }
}
diff --git a/src/com/google/wireless/gdata/client/HttpQueryParams.java b/src/com/google/wireless/gdata/client/HttpQueryParams.java
index 4878002..e391e27 100644
--- a/src/com/google/wireless/gdata/client/HttpQueryParams.java
+++ b/src/com/google/wireless/gdata/client/HttpQueryParams.java
@@ -43,8 +43,10 @@ public class HttpQueryParams extends QueryParams {
url.append('&');
}
String name = (String) names.elementAt(i);
+ String value = getParamValue(name);
+ if (value == null) continue;
url.append(client.encodeUri(name)).append('=');
- url.append(client.encodeUri(getParamValue(name)));
+ url.append(client.encodeUri(value));
}
return url.toString();
}
diff --git a/src/com/google/wireless/gdata/data/XmlUtils.java b/src/com/google/wireless/gdata/data/XmlUtils.java
index 1c067d3..d838372 100644
--- a/src/com/google/wireless/gdata/data/XmlUtils.java
+++ b/src/com/google/wireless/gdata/data/XmlUtils.java
@@ -77,6 +77,30 @@ public final class XmlUtils {
+ "depth " + parentDepth);
}
+ /**
+ * Supply a 'skipSubTree' API which, for some reason, the kxml2 pull parser
+ * hasn't implemented.
+ */
+ public void skipSubTree(XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ // Iterate the remaining structure for this element, discarding events
+ // until we hit the element's corresponding end tag.
+ int level = 1;
+ while (level > 0) {
+ int eventType = parser.next();
+ switch (eventType) {
+ case XmlPullParser.START_TAG:
+ ++level;
+ break;
+ case XmlPullParser.END_TAG:
+ --level;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
// public static void parseChildrenToSerializer(XmlPullParser parser, XmlSerializer serializer)
// throws XmlPullParserException, IOException {
// int parentDepth = parser.getDepth();
diff --git a/src/com/google/wireless/gdata/parser/ConflictException.java b/src/com/google/wireless/gdata/parser/ConflictException.java
new file mode 100644
index 0000000..87e7539
--- /dev/null
+++ b/src/com/google/wireless/gdata/parser/ConflictException.java
@@ -0,0 +1,38 @@
+// Copyright 2007 The Android Open Source Project
+
+package com.google.wireless.gdata.parser;
+
+import com.google.wireless.gdata.GDataException;
+
+/**
+ * Exception thrown if a GData entry cannot be modified due to a version conflict.
+ */
+public class ConflictException extends GDataException {
+
+ /**
+ * Creates a new empty ConflictException.
+ */
+ public ConflictException() {
+ super();
+ }
+
+ /**
+ * Creates a new ConflictException with the supplied message.
+ * @param message The message for this ConflictException.
+ */
+ public ConflictException(String message) {
+ super(message);
+ }
+
+ /**
+ * Creates a new ConflictException with the supplied message and underlying
+ * cause.
+ *
+ * @param message The message for this ConflictException.
+ * @param cause The underlying cause that was caught and wrapped by this
+ * ConflictException.
+ */
+ public ConflictException(String message, Throwable cause) {
+ super(message, cause);
+ }
+} \ No newline at end of file
diff --git a/src/com/google/wireless/gdata/spreadsheets/client/SpreadsheetsClient.java b/src/com/google/wireless/gdata/spreadsheets/client/SpreadsheetsClient.java
deleted file mode 100755
index 388856d..0000000
--- a/src/com/google/wireless/gdata/spreadsheets/client/SpreadsheetsClient.java
+++ /dev/null
@@ -1,236 +0,0 @@
-// Copyright 2007 The Android Open Source Project
-package com.google.wireless.gdata.spreadsheets.client;
-
-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.data.Entry;
-import com.google.wireless.gdata.data.StringUtils;
-import com.google.wireless.gdata.parser.GDataParser;
-import com.google.wireless.gdata.parser.ParseException;
-import com.google.wireless.gdata.serializer.GDataSerializer;
-import com.google.wireless.gdata.spreadsheets.data.CellEntry;
-import com.google.wireless.gdata.spreadsheets.data.ListEntry;
-import com.google.wireless.gdata.spreadsheets.data.SpreadsheetEntry;
-import com.google.wireless.gdata.spreadsheets.data.WorksheetEntry;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * GDataServiceClient for accessing Google Spreadsheets. This client can
- * access and parse all of the Spreadsheets feed types: Spreadsheets feed,
- * Worksheets feed, List feed, and Cells feed. Read operations are supported
- * on all feed types, but only the List and Cells feeds support write
- * operations. (This is a limitation of the protocol, not this API. Such write
- * access may be added to the protocol in the future, requiring changes to
- * this implementation.)
- *
- * Only 'private' visibility and 'full' projections are currently supported.
- */
-public class SpreadsheetsClient extends GDataServiceClient {
- /** The name of the service, dictated to be 'wise' by the protocol. */
- private static final String SERVICE = "wise";
-
- /** Standard base feed url for spreadsheets. */
- public static final String SPREADSHEETS_BASE_FEED_URL =
- "http://spreadsheets.google.com/feeds/spreadsheets/private/full";
-
- /** Base feed url for spreadsheets. */
- private final String baseFeedUrl;
-
- /**
- * Create a new SpreadsheetsClient.
- *
- * @param client The GDataClient that should be used to authenticate
- * requests, retrieve feeds, etc.
- * @param spreadsheetFactory The GDataParserFactory that should be used to obtain GDataParsers
- * used by this client.
- * @param baseFeedUrl The base URL for spreadsheets feeds.
- */
- public SpreadsheetsClient(GDataClient client,
- GDataParserFactory spreadsheetFactory,
- String baseFeedUrl) {
- super(client, spreadsheetFactory);
- this.baseFeedUrl = baseFeedUrl;
- }
-
- /**
- * Create a new SpreadsheetsClient. Uses the standard base URL for spreadsheets feeds.
- *
- * @param client The GDataClient that should be used to authenticate
- * requests, retrieve feeds, etc.
- */
- public SpreadsheetsClient(GDataClient client,
- GDataParserFactory spreadsheetFactory) {
- this(client, spreadsheetFactory, SPREADSHEETS_BASE_FEED_URL);
- }
-
- /* (non-Javadoc)
- * @see GDataServiceClient#getServiceName
- */
- public String getServiceName() {
- return SERVICE;
- }
-
- /**
- * Returns a parser for the specified feed type.
- *
- * @param feedEntryClass the Class of entry type that will be parsed. This lets this
- * method figure out which parser to create.
- * @param feedUri the URI of the feed to be fetched and parsed
- * @param authToken the current authToken to use for the request @return a parser for the indicated feed
- * @throws HttpException if an http error is encountered
- * @throws ParseException if the response from the server could not be
- * parsed
- */
- private GDataParser getParserForTypedFeed(Class feedEntryClass, String feedUri,
- String authToken) throws ParseException, IOException, HttpException {
- GDataClient gDataClient = getGDataClient();
- GDataParserFactory gDataParserFactory = getGDataParserFactory();
-
- InputStream is = gDataClient.getFeedAsStream(feedUri, authToken);
- return gDataParserFactory.createParser(feedEntryClass, is);
- }
-
- /* (non-javadoc)
- * @see GDataServiceClient#createEntry
- */
- public Entry createEntry(String feedUri, String authToken, Entry entry)
- throws ParseException, IOException, HttpException {
-
- GDataParserFactory factory = getGDataParserFactory();
- GDataSerializer serializer = factory.createSerializer(entry);
-
- InputStream is = getGDataClient().createEntry(feedUri, authToken, serializer);
- GDataParser parser = factory.createParser(entry.getClass(), is);
- try {
- return parser.parseStandaloneEntry();
- } finally {
- parser.close();
- }
- }
-
- /**
- * Returns a parser for a Cells-based feed.
- *
- * @param feedUri the URI of the feed to be fetched and parsed
- * @param authToken the current authToken to use for the request
- * @return a parser for the indicated feed
- * @throws HttpException if an http error is encountered
- * @throws ParseException if the response from the server could not be
- * parsed
- */
- public GDataParser getParserForCellsFeed(String feedUri, String authToken)
- throws ParseException, IOException, HttpException {
- return getParserForTypedFeed(CellEntry.class, feedUri, authToken);
- }
-
- /**
- * Fetches a GDataParser for the indicated feed. The parser can be used to
- * access the contents of URI. WARNING: because we cannot reliably infer
- * the feed type from the URI alone, this method assumes the default feed
- * type! This is probably NOT what you want. Please use the
- * getParserFor[Type]Feed methods.
- *
- * @param feedEntryClass
- * @param feedUri the URI of the feed to be fetched and parsed
- * @param authToken the current authToken to use for the request
- * @return a parser for the indicated feed
- * @throws HttpException if an http error is encountered
- * @throws ParseException if the response from the server could not be
- * parsed
- */
- public GDataParser getParserForFeed(Class feedEntryClass, String feedUri, String authToken)
- throws ParseException, IOException, HttpException {
- GDataClient gDataClient = getGDataClient();
- GDataParserFactory gDataParserFactory = getGDataParserFactory();
- InputStream is = gDataClient.getFeedAsStream(feedUri, authToken);
- return gDataParserFactory.createParser(feedEntryClass, is);
- }
-
- /**
- * Returns a parser for a List (row-based) feed.
- *
- * @param feedUri the URI of the feed to be fetched and parsed
- * @param authToken the current authToken to use for the request
- * @return a parser for the indicated feed
- * @throws HttpException if an http error is encountered
- * @throws ParseException if the response from the server could not be
- * parsed
- */
- public GDataParser getParserForListFeed(String feedUri, String authToken)
- throws ParseException, IOException, HttpException {
- return getParserForTypedFeed(ListEntry.class, feedUri, authToken);
- }
-
- /**
- * Returns a parser for a Spreadsheets meta-feed.
- *
- * @param feedUri the URI of the feed to be fetched and parsed
- * @param authToken the current authToken to use for the request
- * @return a parser for the indicated feed
- * @throws HttpException if an http error is encountered
- * @throws ParseException if the response from the server could not be
- * parsed
- */
- public GDataParser getParserForSpreadsheetsFeed(String feedUri, String authToken)
- throws ParseException, IOException, HttpException {
- return getParserForTypedFeed(SpreadsheetEntry.class, feedUri, authToken);
- }
-
- /**
- * Returns a parser for a Worksheets meta-feed.
- *
- * @param feedUri the URI of the feed to be fetched and parsed
- * @param authToken the current authToken to use for the request
- * @return a parser for the indicated feed
- * @throws HttpException if an http error is encountered
- * @throws ParseException if the response from the server could not be
- * parsed
- */
- public GDataParser getParserForWorksheetsFeed(String feedUri, String authToken)
- throws ParseException, IOException, HttpException {
- return getParserForTypedFeed(WorksheetEntry.class, feedUri, authToken);
- }
-
- /**
- * Updates an entry. The URI to be updated is taken from
- * <code>entry</code>. Note that only entries in List and Cells feeds
- * can be updated, so <code>entry</code> must be of the corresponding
- * type; other types will result in an exception.
- *
- * @param entry the entry to be updated; must include its URI
- * @param authToken the current authToken to be used for the operation
- * @return An Entry containing the re-parsed version of the entry returned
- * by the server in response to the update.
- * @throws HttpException if an http error is encountered
- * @throws ParseException if the server returned an error, if the server's
- * response was unparseable (unlikely), or if <code>entry</code>
- * is of a read-only type
- * @throws IOException on network error
- */
- public Entry updateEntry(Entry entry, String authToken)
- throws ParseException, IOException, HttpException {
- GDataParserFactory factory = getGDataParserFactory();
- GDataSerializer serializer = factory.createSerializer(entry);
-
- String editUri = entry.getEditUri();
- if (StringUtils.isEmpty(editUri)) {
- throw new ParseException("No edit URI -- cannot update.");
- }
-
- InputStream is = getGDataClient().updateEntry(editUri, authToken, serializer);
- GDataParser parser = factory.createParser(entry.getClass(), is);
- try {
- return parser.parseStandaloneEntry();
- } finally {
- parser.close();
- }
- }
-
- public String getBaseFeedUrl() {
- return baseFeedUrl;
- }
-}
diff --git a/src/com/google/wireless/gdata/spreadsheets/data/CellEntry.java b/src/com/google/wireless/gdata/spreadsheets/data/CellEntry.java
deleted file mode 100755
index 88fb6b6..0000000
--- a/src/com/google/wireless/gdata/spreadsheets/data/CellEntry.java
+++ /dev/null
@@ -1,134 +0,0 @@
-// Copyright 2007 The Android Open Source Project
-package com.google.wireless.gdata.spreadsheets.data;
-
-import com.google.wireless.gdata.data.Entry;
-
-/**
- * Represents an entry in a GData Spreadsheets Cell-based feed.
- */
-public class CellEntry extends Entry {
- /** The spreadsheet column of the cell. */
- private int col = -1;
-
- /** The cell entry's inputValue attribute */
- private String inputValue = null;
-
- /** The cell entry's numericValue attribute */
- private String numericValue = null;
-
- /** The spreadsheet row of the cell */
- private int row = -1;
-
- /** The cell entry's text sub-element */
- private String value = null;
-
- /** Default constructor. */
- public CellEntry() {
- super();
- }
-
- /**
- * Fetches the cell's spreadsheet column.
- *
- * @return the cell's spreadsheet column
- */
- public int getCol() {
- return col;
- }
-
- /**
- * Fetches the cell's inputValue attribute, which is the actual user input
- * rather (such as a formula) than computed value of the cell.
- *
- * @return the cell's inputValue
- */
- public String getInputValue() {
- return inputValue;
- }
-
- /**
- * Fetches the cell's numericValue attribute, which is a decimal
- * representation.
- *
- * @return the cell's numericValue
- */
- public String getNumericValue() {
- return numericValue;
- }
-
- /**
- * Fetches the cell's spreadsheet row.
- *
- * @return the cell's spreadsheet row
- */
- public int getRow() {
- return row;
- }
-
- /**
- * Fetches the cell's contents, after any computation. For example, if the
- * cell actually contains a formula, this will return the formula's computed
- * value.
- *
- * @return the computed value of the cell
- */
- public String getValue() {
- return value;
- }
-
- /**
- * Indicates whether the cell's contents are numeric.
- *
- * @return true if the contents are numeric, or false if not
- */
- public boolean hasNumericValue() {
- return numericValue != null;
- }
-
- /**
- * Sets the cell's spreadsheet column.
- *
- * @param col the new spreadsheet column of the cell
- */
- public void setCol(int col) {
- this.col = col;
- }
-
- /**
- * Sets the cell's actual contents (such as a formula, or a raw value.)
- *
- * @param inputValue the new inputValue of the cell
- */
- public void setInputValue(String inputValue) {
- this.inputValue = inputValue;
- }
-
- /**
- * Sets the cell's numeric value. This can be different from the actual
- * value; for instance, the actual value may be a thousands-delimited pretty
- * string, while the numeric value could be the raw decimal.
- *
- * @param numericValue the cell's new numericValue
- */
- public void setNumericValue(String numericValue) {
- this.numericValue = numericValue;
- }
-
- /**
- * Sets the cell's spreadsheet row.
- *
- * @param row the new spreadsheet row of the cell
- */
- public void setRow(int row) {
- this.row = row;
- }
-
- /**
- * Sets the cell's computed value.
- *
- * @param value the new value of the cell
- */
- public void setValue(String value) {
- this.value = value;
- }
-}
diff --git a/src/com/google/wireless/gdata/spreadsheets/data/CellFeed.java b/src/com/google/wireless/gdata/spreadsheets/data/CellFeed.java
deleted file mode 100755
index 9281e09..0000000
--- a/src/com/google/wireless/gdata/spreadsheets/data/CellFeed.java
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2007 The Android Open Source Project
-package com.google.wireless.gdata.spreadsheets.data;
-
-import com.google.wireless.gdata.data.Feed;
-
-/**
- * A feed handler for GData Spreadsheets cell-based feeds.
- */
-public class CellFeed extends Feed {
- private String editUri;
-
- /** Default constructor. */
- public CellFeed() {
- super();
- }
-
- /**
- * Fetches the URI to which edits (such as cell content updates) should
- * go.
- *
- * @return the edit URI for this feed
- */
- public String getEditUri() {
- return editUri;
- }
-
- /**
- * Sets the URI to which edits (such as cell content updates) should go.
- *
- * @param editUri the new edit URI for this feed
- */
- public void setEditUri(String editUri) {
- this.editUri = editUri;
- }
-}
diff --git a/src/com/google/wireless/gdata/spreadsheets/data/ListEntry.java b/src/com/google/wireless/gdata/spreadsheets/data/ListEntry.java
deleted file mode 100755
index c3e169f..0000000
--- a/src/com/google/wireless/gdata/spreadsheets/data/ListEntry.java
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2007 The Android Open Source Project
-package com.google.wireless.gdata.spreadsheets.data;
-
-import com.google.wireless.gdata.data.Entry;
-import com.google.wireless.gdata.data.StringUtils;
-
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.Vector;
-
-/**
- * Represents an entry in a GData Spreadsheets List feed.
- */
-public class ListEntry extends Entry {
- /** Map containing the values in the row. */
- private Hashtable values = new Hashtable();
-
- /** Caches the list of names, so they don't need to be recomputed. */
- private Vector names = null;
-
- /**
- * Retrieves the column names present in this row.
- *
- * @return a Set of Strings, one per column where data exists
- */
- public Vector getNames() {
- if (names != null) {
- return names;
- }
- names = new Vector();
- Enumeration e = values.keys();
- while (e.hasMoreElements()) {
- names.add(e.nextElement());
- }
- return names;
- }
-
- /**
- * Fetches the value for a column. Equivalent to
- * <code>getValue(name, null)</code>.
- *
- * @param name the name of the column whose row is to be fetched
- * @return the value of the column, or null if the column is not present
- */
- public String getValue(String name) {
- return getValue(name, null);
- }
-
- /**
- * Fetches the value for a column.
- *
- * @param name the name of the column whose row is to be fetched
- * @param defaultValue the value to return if the row has no value for the
- * requested column; may be null
- * @return the value of the column, or null if the column is not present
- */
- public String getValue(String name, String defaultValue) {
- if (StringUtils.isEmpty(name)) {
- return defaultValue;
- }
- String val = (String) values.get(name);
- if (val == null) {
- return defaultValue;
- }
- return val;
- }
-
- /**
- * Sets the value of a column.
- *
- * @param name the name of the column
- * @param value the value for the column
- */
- public void setValue(String name, String value) {
- values.put(name, value == null ? "" : value);
- }
-}
diff --git a/src/com/google/wireless/gdata/spreadsheets/data/ListFeed.java b/src/com/google/wireless/gdata/spreadsheets/data/ListFeed.java
deleted file mode 100755
index d18bfa8..0000000
--- a/src/com/google/wireless/gdata/spreadsheets/data/ListFeed.java
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2007 The Android Open Source Project
-package com.google.wireless.gdata.spreadsheets.data;
-
-import com.google.wireless.gdata.data.Feed;
-
-/**
- * A feed handler for GData Spreadsheets List-based feeds.
- */
-public class ListFeed extends Feed {
- private String editUri;
-
- /** Default constructor. */
- public ListFeed() {
- super();
- }
-
- /**
- * Fetches the URI to which edits (such as cell content updates) should
- * go.
- *
- * @return the edit URI for this feed
- */
- public String getEditUri() {
- return editUri;
- }
-
- /**
- * Sets the URI to which edits (such as cell content updates) should go.
- *
- * @param editUri the new edit URI for this feed
- */
- public void setEditUri(String editUri) {
- this.editUri = editUri;
- }
-}
diff --git a/src/com/google/wireless/gdata/spreadsheets/data/SpreadsheetEntry.java b/src/com/google/wireless/gdata/spreadsheets/data/SpreadsheetEntry.java
deleted file mode 100755
index a10837b..0000000
--- a/src/com/google/wireless/gdata/spreadsheets/data/SpreadsheetEntry.java
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2007 The Android Open Source Project
-package com.google.wireless.gdata.spreadsheets.data;
-
-import com.google.wireless.gdata.GDataException;
-import com.google.wireless.gdata.data.Entry;
-import com.google.wireless.gdata.data.StringUtils;
-
-/**
- * Represents an entry in a GData Spreadsheets meta-feed.
- */
-public class SpreadsheetEntry extends Entry {
- /** The URI of the worksheets meta-feed for this spreadsheet */
- private String worksheetsUri = null;
-
- /**
- * Fetches the URI of the worksheets meta-feed (that is, list of worksheets)
- * for this spreadsheet.
- *
- * @return the worksheets meta-feed URI
- * @throws GDataException if the unique key is not set
- */
- public String getWorksheetFeedUri() throws GDataException {
- if (StringUtils.isEmpty(worksheetsUri)) {
- throw new GDataException("worksheet URI is not set");
- }
- return worksheetsUri;
- }
-
- /**
- * Sets the URI of the worksheet meta-feed corresponding to this
- * spreadsheet.
- *
- * @param href
- */
- public void setWorksheetFeedUri(String href) {
- worksheetsUri = href;
- }
-}
diff --git a/src/com/google/wireless/gdata/spreadsheets/data/SpreadsheetFeed.java b/src/com/google/wireless/gdata/spreadsheets/data/SpreadsheetFeed.java
deleted file mode 100755
index bfab289..0000000
--- a/src/com/google/wireless/gdata/spreadsheets/data/SpreadsheetFeed.java
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2007 The Android Open Source Project
-package com.google.wireless.gdata.spreadsheets.data;
-
-import com.google.wireless.gdata.data.Feed;
-
-/**
- * Feed handler for a GData Spreadsheets meta-feed.
- */
-public class SpreadsheetFeed extends Feed {
- /** Default constructor. */
- public SpreadsheetFeed() {
- super();
- }
-}
diff --git a/src/com/google/wireless/gdata/spreadsheets/data/WorksheetEntry.java b/src/com/google/wireless/gdata/spreadsheets/data/WorksheetEntry.java
deleted file mode 100644
index 2c00d66..0000000
--- a/src/com/google/wireless/gdata/spreadsheets/data/WorksheetEntry.java
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright 2007 The Android Open Source Project
-package com.google.wireless.gdata.spreadsheets.data;
-
-import com.google.wireless.gdata.GDataException;
-import com.google.wireless.gdata.data.Entry;
-import com.google.wireless.gdata.data.StringUtils;
-
-/**
- * Represents an entry in a GData Worksheets meta-feed.
- */
-public class WorksheetEntry extends Entry {
- /** The URI to this entry's cells feed. */
- private String cellsUri = null;
-
- /** The number of columns in the worksheet. */
- private int colCount = -1;
-
- /** The URI to this entry's list feed. */
- private String listUri = null;
-
- /** The number of rows in the worksheet. */
- private int rowCount = -1;
-
- /**
- * Fetches the URI of this entry's Cells feed.
- *
- * @return The URI of the entry's Cells feed.
- */
- public String getCellFeedUri() {
- return cellsUri;
- }
-
- /**
- * Fetches the number of columns in the worksheet.
- *
- * @return The number of columns.
- */
- public int getColCount() {
- return colCount;
- }
-
- /**
- * Fetches the URI of this entry's List feed.
- *
- * @return The URI of the entry's List feed.
- * @throws GDataException If the URI is not set or is invalid.
- */
- public String getListFeedUri() {
- return listUri;
- }
-
- /**
- * Fetches the number of rows in the worksheet.
- *
- * @return The number of rows.
- */
- public int getRowCount() {
- return rowCount;
- }
-
- /**
- * Sets the URI of this entry's Cells feed.
- *
- * @param href The HREF attribute that should be the Cells feed URI.
- */
- public void setCellFeedUri(String href) {
- cellsUri = href;
- }
-
- /**
- * Sets the number of columns in the worksheet.
- *
- * @param colCount The new number of columns.
- */
- public void setColCount(int colCount) {
- this.colCount = colCount;
- }
-
- /**
- * Sets this entry's Atom ID.
- *
- * @param id The new ID value.
- */
- public void setId(String id) {
- super.setId(id);
- }
-
- /**
- * Sets the URI of this entry's List feed.
- *
- * @param href The HREF attribute that should be the List feed URI.
- */
- public void setListFeedUri(String href) {
- listUri = href;
- }
-
- /**
- * Sets the number of rows in the worksheet.
- *
- * @param rowCount The new number of rows.
- */
- public void setRowCount(int rowCount) {
- this.rowCount = rowCount;
- }
-}
diff --git a/src/com/google/wireless/gdata/spreadsheets/data/WorksheetFeed.java b/src/com/google/wireless/gdata/spreadsheets/data/WorksheetFeed.java
deleted file mode 100755
index d8c469d..0000000
--- a/src/com/google/wireless/gdata/spreadsheets/data/WorksheetFeed.java
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2007 The Android Open Source Project
-package com.google.wireless.gdata.spreadsheets.data;
-
-import com.google.wireless.gdata.data.Feed;
-
-/**
- * Feed handler for GData Spreadsheets Worksheet meta-feed.
- */
-public class WorksheetFeed extends Feed {
- /** Default constructor. */
- public WorksheetFeed() {
- super();
- }
-}
diff --git a/src/com/google/wireless/gdata/spreadsheets/parser/xml/XmlCellsGDataParser.java b/src/com/google/wireless/gdata/spreadsheets/parser/xml/XmlCellsGDataParser.java
deleted file mode 100755
index b64f76a..0000000
--- a/src/com/google/wireless/gdata/spreadsheets/parser/xml/XmlCellsGDataParser.java
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright 2007 The Android Open Source Project
-package com.google.wireless.gdata.spreadsheets.parser.xml;
-
-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 com.google.wireless.gdata.spreadsheets.data.CellEntry;
-import com.google.wireless.gdata.spreadsheets.data.CellFeed;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * Parser for non-Atom data in a GData Spreadsheets Cell-based feed.
- */
-public class XmlCellsGDataParser extends XmlGDataParser {
- /**
- * The rel ID used by the server to identify the URLs for Cell POSTs
- * (updates)
- */
- private static final String CELL_FEED_POST_REL =
- "http://schemas.google.com/g/2005#post";
-
- /**
- * Creates a new XmlCellsGDataParser.
- *
- * @param is the stream from which to read the data
- * @param xmlParser the XMLPullParser to use for parsing the raw XML
- * @throws ParseException if the super-class throws one
- */
- public XmlCellsGDataParser(InputStream is, XmlPullParser xmlParser)
- throws ParseException {
- super(is, xmlParser);
- }
-
- /* (non-JavaDoc)
- * Creates a new Entry that can handle the data parsed by this class.
- */
- protected Entry createEntry() {
- return new CellEntry();
- }
-
- /* (non-JavaDoc)
- * Creates a new Feed that can handle the data parsed by this class.
- */
- protected Feed createFeed() {
- return new CellFeed();
- }
-
- /* (non-JavaDoc)
- * Callback to handle non-Atom data present in an Atom entry tag.
- */
- protected void handleExtraElementInEntry(Entry entry)
- throws XmlPullParserException, IOException {
- XmlPullParser parser = getParser();
- if (!(entry instanceof CellEntry)) {
- throw new IllegalArgumentException("Expected CellEntry!");
- }
- CellEntry row = (CellEntry) entry;
-
- String name = parser.getName();
- // cells can only have row, col, inputValue, & numericValue attrs
- if ("cell".equals(name)) {
- int count = parser.getAttributeCount();
- String attrName = null;
- for (int i = 0; i < count; ++i) {
- attrName = parser.getAttributeName(i);
- if ("row".equals(attrName)) {
- row.setRow(StringUtils.parseInt(parser
- .getAttributeValue(i), 0));
- } else if ("col".equals(attrName)) {
- row.setCol(StringUtils.parseInt(parser
- .getAttributeValue(i), 0));
- } else if ("numericValue".equals(attrName)) {
- row.setNumericValue(parser.getAttributeValue(i));
- } else if ("inputValue".equals(attrName)) {
- row.setInputValue(parser.getAttributeValue(i));
- }
- }
-
- // also need the data stored in the child text node
- row.setValue(XmlUtils.extractChildText(parser));
- }
- }
-
- /* (non-JavaDoc)
- * Callback to handle non-Atom data in the feed.
- */
- protected void handleExtraElementInFeed(Feed feed)
- throws XmlPullParserException, IOException {
- XmlPullParser parser = getParser();
- if (!(feed instanceof CellFeed)) {
- throw new IllegalArgumentException("Expected CellFeed!");
- }
- CellFeed cellFeed = (CellFeed) feed;
-
- String name = parser.getName();
- if (!"link".equals(name)) {
- return;
- }
-
- int numAttrs = parser.getAttributeCount();
- String rel = null;
- String href = null;
- String attrName = null;
- for (int i = 0; i < numAttrs; ++i) {
- attrName = parser.getAttributeName(i);
- if ("rel".equals(attrName)) {
- rel = parser.getAttributeValue(i);
- } else if ("href".equals(attrName)) {
- href = parser.getAttributeValue(i);
- }
- }
- if (!(StringUtils.isEmpty(rel) || StringUtils.isEmpty(href))) {
- if (CELL_FEED_POST_REL.equals(rel)) {
- cellFeed.setEditUri(href);
- }
- }
- }
-}
diff --git a/src/com/google/wireless/gdata/spreadsheets/parser/xml/XmlListGDataParser.java b/src/com/google/wireless/gdata/spreadsheets/parser/xml/XmlListGDataParser.java
deleted file mode 100755
index b582ad9..0000000
--- a/src/com/google/wireless/gdata/spreadsheets/parser/xml/XmlListGDataParser.java
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright 2007 The Android Open Source Project
-package com.google.wireless.gdata.spreadsheets.parser.xml;
-
-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 com.google.wireless.gdata.spreadsheets.data.ListEntry;
-import com.google.wireless.gdata.spreadsheets.data.ListFeed;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * Parser for non-Atom data in a GData Spreadsheets List-based feed.
- */
-public class XmlListGDataParser extends XmlGDataParser {
- /**
- * The rel ID used by the server to identify the URLs for List POSTs
- * (updates)
- */
- private static final String LIST_FEED_POST_REL =
- "http://schemas.google.com/g/2005#post";
-
- /**
- * Creates a new XmlListGDataParser.
- *
- * @param is the stream from which to read the data
- * @param xmlParser the XmlPullParser to use to parse the raw XML
- * @throws ParseException if the super-class throws one
- */
- public XmlListGDataParser(InputStream is, XmlPullParser xmlParser)
- throws ParseException {
- super(is, xmlParser);
- }
-
- /* (non-JavaDoc)
- * Creates a new Entry that can handle the data parsed by this class.
- */
- protected Entry createEntry() {
- return new ListEntry();
- }
-
- /* (non-JavaDoc)
- * Creates a new Feed that can handle the data parsed by this class.
- */
- protected Feed createFeed() {
- return new ListFeed();
- }
-
- /* (non-JavaDoc)
- * Callback to handle non-Atom data present in an Atom entry tag.
- */
- protected void handleExtraElementInEntry(Entry entry)
- throws XmlPullParserException, IOException {
- XmlPullParser parser = getParser();
- if (!(entry instanceof ListEntry)) {
- throw new IllegalArgumentException("Expected ListEntry!");
- }
- ListEntry row = (ListEntry) entry;
-
- String name = parser.getName();
- row.setValue(name, XmlUtils.extractChildText(parser));
- }
-
- /* (non-JavaDoc)
- * Callback to handle non-Atom data in the feed.
- */
- protected void handleExtraElementInFeed(Feed feed)
- throws XmlPullParserException, IOException {
- XmlPullParser parser = getParser();
- if (!(feed instanceof ListFeed)) {
- throw new IllegalArgumentException("Expected ListFeed!");
- }
- ListFeed listFeed = (ListFeed) feed;
-
- String name = parser.getName();
- if (!"link".equals(name)) {
- return;
- }
-
- // lists store column data in the gsx namespace:
- // <gsx:columnheader>data</gsx:columnheader>
- // The columnheader tag names are the scrubbed values of the first row.
- // We extract them all and store them as keys in a Map.
- int numAttrs = parser.getAttributeCount();
- String rel = null;
- String href = null;
- String attrName = null;
- for (int i = 0; i < numAttrs; ++i) {
- attrName = parser.getAttributeName(i);
- if ("rel".equals(attrName)) {
- rel = parser.getAttributeValue(i);
- } else if ("href".equals(attrName)) {
- href = parser.getAttributeValue(i);
- }
- }
- if (!(StringUtils.isEmpty(rel) || StringUtils.isEmpty(href))) {
- if (LIST_FEED_POST_REL.equals(rel)) {
- listFeed.setEditUri(href);
- }
- }
- }
-}
diff --git a/src/com/google/wireless/gdata/spreadsheets/parser/xml/XmlSpreadsheetsGDataParser.java b/src/com/google/wireless/gdata/spreadsheets/parser/xml/XmlSpreadsheetsGDataParser.java
deleted file mode 100755
index cb1094d..0000000
--- a/src/com/google/wireless/gdata/spreadsheets/parser/xml/XmlSpreadsheetsGDataParser.java
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2007 The Android Open Source Project
-package com.google.wireless.gdata.spreadsheets.parser.xml;
-
-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 com.google.wireless.gdata.spreadsheets.data.SpreadsheetEntry;
-import com.google.wireless.gdata.spreadsheets.data.SpreadsheetFeed;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * Parser helper for non-Atom data in a GData Spreadsheets meta-feed.
- */
-public class XmlSpreadsheetsGDataParser extends XmlGDataParser {
- /**
- * The rel ID used by the server to identify the URLs for the worksheets
- * feed
- */
- protected static final String WORKSHEET_FEED_REL =
- "http://schemas.google.com/spreadsheets/2006#worksheetsfeed";
-
- /**
- * Creates a new XmlSpreadsheetsGDataParser.
- *
- * @param is the stream from which to read the data
- * @param xmlParser the XmlPullParser to use to parse the raw XML
- * @throws ParseException if the super-class throws one
- */
- public XmlSpreadsheetsGDataParser(InputStream is, XmlPullParser xmlParser)
- throws ParseException {
- super(is, xmlParser);
- }
-
- /* (non-JavaDoc)
- * Creates a new Entry that can handle the data parsed by this class.
- */
- protected Entry createEntry() {
- return new SpreadsheetEntry();
- }
-
- /* (non-JavaDoc)
- * Creates a new Feed that can handle the data parsed by this class.
- */
- protected Feed createFeed() {
- return new SpreadsheetFeed();
- }
-
- /* (non-JavaDoc)
- * Callback to handle link elements that are not recognized as Atom links.
- * Used to pick out the link tag in a Spreadsheet's entry that corresponds
- * to that spreadsheet's worksheets meta-feed.
- */
- protected void handleExtraLinkInEntry(String rel, String type, String href,
- Entry entry) throws XmlPullParserException, IOException {
- super.handleExtraLinkInEntry(rel, type, href, entry);
- if (WORKSHEET_FEED_REL.equals(rel)
- && "application/atom+xml".equals(type)) {
- SpreadsheetEntry sheet = (SpreadsheetEntry) entry;
- sheet.setWorksheetFeedUri(href);
- }
- }
-}
diff --git a/src/com/google/wireless/gdata/spreadsheets/parser/xml/XmlSpreadsheetsGDataParserFactory.java b/src/com/google/wireless/gdata/spreadsheets/parser/xml/XmlSpreadsheetsGDataParserFactory.java
deleted file mode 100644
index 4feed62..0000000
--- a/src/com/google/wireless/gdata/spreadsheets/parser/xml/XmlSpreadsheetsGDataParserFactory.java
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright 2007 The Android Open Source Project
-package com.google.wireless.gdata.spreadsheets.parser.xml;
-
-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 com.google.wireless.gdata.spreadsheets.data.CellEntry;
-import com.google.wireless.gdata.spreadsheets.data.ListEntry;
-import com.google.wireless.gdata.spreadsheets.data.SpreadsheetEntry;
-import com.google.wireless.gdata.spreadsheets.data.WorksheetEntry;
-import com.google.wireless.gdata.spreadsheets.serializer.xml.XmlCellEntryGDataSerializer;
-import com.google.wireless.gdata.spreadsheets.serializer.xml.XmlListEntryGDataSerializer;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.InputStream;
-
-/**
- * A GDataParserFactory capable of handling Spreadsheets.
- */
-public class XmlSpreadsheetsGDataParserFactory implements GDataParserFactory {
- /*
- * @see GDataParserFactory
- */
- public XmlSpreadsheetsGDataParserFactory(XmlParserFactory xmlFactory) {
- this.xmlFactory = xmlFactory;
- }
-
- /** Intentionally private. */
- private XmlSpreadsheetsGDataParserFactory() {
- }
-
- /*
- * Creates a parser for the indicated feed, assuming the default feed type.
- * The default type is specified on {@link SpreadsheetsClient#DEFAULT_FEED}.
- *
- * @param is The stream containing the feed to be parsed.
- * @return A GDataParser capable of parsing the feed as the default type.
- * @throws ParseException if the feed could not be parsed for any reason
- */
- public GDataParser createParser(InputStream is) throws ParseException {
- // attempt a default
- return createParser(SpreadsheetEntry.class, is);
- }
-
- /*
- * Creates a parser of the indicated type for the indicated feed.
- *
- * @param feedType The type of the feed; must be one of the constants on
- * {@link SpreadsheetsClient}.
- * @return A parser capable of parsing the feed as the indicated type.
- * @throws ParseException if the feed could not be parsed for any reason
- */
- public GDataParser createParser(Class entryClass, InputStream is)
- throws ParseException {
- try {
- XmlPullParser xmlParser = xmlFactory.createParser();
- if (entryClass == SpreadsheetEntry.class) {
- return new XmlSpreadsheetsGDataParser(is, xmlParser);
- } else if (entryClass == WorksheetEntry.class) {
- return new XmlWorksheetsGDataParser(is, xmlParser);
- } else if (entryClass == CellEntry.class) {
- return new XmlCellsGDataParser(is, xmlParser);
- } else if (entryClass == ListEntry.class) {
- return new XmlListGDataParser(is, xmlParser);
- } else {
- throw new ParseException("Unrecognized feed requested.");
- }
- } catch (XmlPullParserException e) {
- throw new ParseException("Failed to create parser", e);
- }
- }
-
- /*
- * Creates a serializer capable of handling the indicated entry.
- *
- * @param The Entry to be serialized to an XML string.
- * @return A GDataSerializer capable of handling the indicated entry.
- * @throws IllegalArgumentException if Entry is not a supported type (which
- * currently includes only {@link ListEntry} and {@link CellEntry}.)
- */
- public GDataSerializer createSerializer(Entry entry) {
- if (entry instanceof ListEntry) {
- return new XmlListEntryGDataSerializer(xmlFactory, entry);
- } else if (entry instanceof CellEntry) {
- return new XmlCellEntryGDataSerializer(xmlFactory, entry);
- } else {
- throw new IllegalArgumentException(
- "Expected a ListEntry or CellEntry");
- }
- }
-
- /** The XmlParserFactory to use to actually process XML streams. */
- private XmlParserFactory xmlFactory;
-}
diff --git a/src/com/google/wireless/gdata/spreadsheets/parser/xml/XmlWorksheetsGDataParser.java b/src/com/google/wireless/gdata/spreadsheets/parser/xml/XmlWorksheetsGDataParser.java
deleted file mode 100755
index e9ceee5..0000000
--- a/src/com/google/wireless/gdata/spreadsheets/parser/xml/XmlWorksheetsGDataParser.java
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright 2007 The Android Open Source Project
-package com.google.wireless.gdata.spreadsheets.parser.xml;
-
-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 com.google.wireless.gdata.spreadsheets.data.WorksheetEntry;
-import com.google.wireless.gdata.spreadsheets.data.WorksheetFeed;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * Parser helper for non-Atom data in a GData Spreadsheets Worksheets meta-feed.
- */
-public class XmlWorksheetsGDataParser extends XmlGDataParser {
- /**
- * The rel ID used by the server to identify the cells feed for a worksheet
- */
- protected static final String CELLS_FEED_REL =
- "http://schemas.google.com/spreadsheets/2006#cellsfeed";
-
- /**
- * The rel ID used by the server to identify the list feed for a worksheet
- */
- protected static final String LIST_FEED_REL =
- "http://schemas.google.com/spreadsheets/2006#listfeed";
-
- /**
- * Creates a new XmlWorksheetsGDataParser.
- *
- * @param is the stream from which to read the data
- * @param xmlParser the XmlPullParser to use to parse the raw XML
- * @throws ParseException if the super-class throws one
- */
- public XmlWorksheetsGDataParser(InputStream is, XmlPullParser xmlParser)
- throws ParseException {
- super(is, xmlParser);
- }
-
- /* (non-JavaDoc)
- * Creates a new Entry that can handle the data parsed by this class.
- */
- protected Entry createEntry() {
- return new WorksheetEntry();
- }
-
- /* (non-JavaDoc)
- * Creates a new Feed that can handle the data parsed by this class.
- */
- protected Feed createFeed() {
- return new WorksheetFeed();
- }
-
- /* (non-JavaDoc)
- * Callback to handle non-Atom data present in an Atom entry tag.
- */
- protected void handleExtraElementInEntry(Entry entry)
- throws XmlPullParserException, IOException {
- XmlPullParser parser = getParser();
- if (!(entry instanceof WorksheetEntry)) {
- throw new IllegalArgumentException("Expected WorksheetEntry!");
- }
- WorksheetEntry worksheet = (WorksheetEntry) entry;
-
- // the only custom elements are rowCount and colCount
- String name = parser.getName();
- if ("rowCount".equals(name)) {
- worksheet.setRowCount(StringUtils.parseInt(XmlUtils
- .extractChildText(parser), 0));
- } else if ("colCount".equals(name)) {
- worksheet.setColCount(StringUtils.parseInt(XmlUtils
- .extractChildText(parser), 0));
- }
- }
-
- /* (non-JavaDoc)
- * Callback to handle non-Atom links present in an Atom entry tag. Used to
- * pick out a worksheet's cells and list feeds.
- */
- protected void handleExtraLinkInEntry(String rel, String type, String href,
- Entry entry) throws XmlPullParserException, IOException {
- if (LIST_FEED_REL.equals(rel) && "application/atom+xml".equals(type)) {
- WorksheetEntry sheet = (WorksheetEntry) entry;
- sheet.setListFeedUri(href);
- } else if (CELLS_FEED_REL.equals(rel)
- && "application/atom+xml".equals(type)) {
- WorksheetEntry sheet = (WorksheetEntry) entry;
- sheet.setCellFeedUri(href);
- }
- }
-}
diff --git a/src/com/google/wireless/gdata/spreadsheets/serializer/xml/XmlCellEntryGDataSerializer.java b/src/com/google/wireless/gdata/spreadsheets/serializer/xml/XmlCellEntryGDataSerializer.java
deleted file mode 100755
index 995af2c..0000000
--- a/src/com/google/wireless/gdata/spreadsheets/serializer/xml/XmlCellEntryGDataSerializer.java
+++ /dev/null
@@ -1,83 +0,0 @@
-package com.google.wireless.gdata.spreadsheets.serializer.xml;
-
-import com.google.wireless.gdata.data.Entry;
-import com.google.wireless.gdata.data.StringUtils;
-import com.google.wireless.gdata.parser.ParseException;
-import com.google.wireless.gdata.parser.xml.XmlParserFactory;
-import com.google.wireless.gdata.serializer.xml.XmlEntryGDataSerializer;
-import com.google.wireless.gdata.spreadsheets.data.CellEntry;
-
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.IOException;
-
-/**
- * A serializer for handling GData Spreadsheets Cell entries.
- */
-public class XmlCellEntryGDataSerializer extends XmlEntryGDataSerializer {
- /** The namespace to use for the GData Cell attributes */
- public static final String NAMESPACE_GS = "gs";
-
- /** The URI of the GData Cell namespace */
- public static final String NAMESPACE_GS_URI =
- "http://schemas.google.com/spreadsheets/2006";
-
- /**
- * Creates a new XmlCellEntryGDataSerializer.
- *
- * @param entry the entry to be serialized
- */
- public XmlCellEntryGDataSerializer(XmlParserFactory xmlFactory,
- Entry entry) {
- super(xmlFactory, entry);
- }
-
- /**
- * Sets up the GData Cell namespace.
- *
- * @param serializer the serializer to use
- */
- protected void declareExtraEntryNamespaces(XmlSerializer serializer)
- throws IOException {
- serializer.setPrefix(NAMESPACE_GS, NAMESPACE_GS_URI);
- }
-
- /*
- * Handles the non-Atom data belonging to the GData Spreadsheets Cell
- * namespace.
- *
- * @param serializer the XML serializer to use
- * @param format unused
- * @throws ParseException if the data could not be serialized
- * @throws IOException on network error
- */
- protected void serializeExtraEntryContents(XmlSerializer serializer,
- int format) throws ParseException, IOException {
- CellEntry entry = (CellEntry) getEntry();
- int row = entry.getRow();
- int col = entry.getCol();
- String value = entry.getValue();
- String inputValue = entry.getInputValue();
- if (row < 0 || col < 0) {
- throw new ParseException("Negative row or column value");
- }
-
- // cells require row & col attrs, and allow inputValue and
- // numericValue
- serializer.startTag(NAMESPACE_GS_URI, "cell");
- serializer.attribute(null /* ns */, "row", "" + row);
- serializer.attribute(null /* ns */, "col", "" + col);
- if (inputValue != null) {
- serializer.attribute(null /* ns */, "inputValue", inputValue);
- }
- if (entry.hasNumericValue()) {
- serializer.attribute(null /* ns */, "numericValue", entry
- .getNumericValue());
- }
-
- // set the child text...
- value = StringUtils.isEmpty(value) ? "" : value;
- serializer.text(value);
- serializer.endTag(NAMESPACE_GS_URI, "cell");
- }
-}
diff --git a/src/com/google/wireless/gdata/spreadsheets/serializer/xml/XmlListEntryGDataSerializer.java b/src/com/google/wireless/gdata/spreadsheets/serializer/xml/XmlListEntryGDataSerializer.java
deleted file mode 100755
index 55cc59c..0000000
--- a/src/com/google/wireless/gdata/spreadsheets/serializer/xml/XmlListEntryGDataSerializer.java
+++ /dev/null
@@ -1,67 +0,0 @@
-package com.google.wireless.gdata.spreadsheets.serializer.xml;
-
-import com.google.wireless.gdata.data.Entry;
-import com.google.wireless.gdata.parser.ParseException;
-import com.google.wireless.gdata.parser.xml.XmlParserFactory;
-import com.google.wireless.gdata.serializer.xml.XmlEntryGDataSerializer;
-import com.google.wireless.gdata.spreadsheets.data.ListEntry;
-
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.IOException;
-import java.util.Iterator;
-import java.util.Vector;
-
-/**
- * A serializer for handling GData Spreadsheets List entries.
- */
-public class XmlListEntryGDataSerializer extends XmlEntryGDataSerializer {
- /** The prefix to use for the GData Spreadsheets list namespace */
- public static final String NAMESPACE_GSX = "gsx";
-
- /** The URI of the GData Spreadsheets list namespace */
- public static final String NAMESPACE_GSX_URI =
- "http://schemas.google.com/spreadsheets/2006/extended";
-
- /**
- * Creates a new XmlListEntryGDataSerializer.
- *
- * @param entry the entry to be serialized
- */
- public XmlListEntryGDataSerializer(XmlParserFactory xmlFactory, Entry entry) {
- super(xmlFactory, entry);
- }
-
- /**
- * Sets up the GData Spreadsheets list namespace.
- *
- * @param serializer the XML serializer to use
- * @throws IOException on stream errors.
- */
- protected void declareExtraEntryNamespaces(XmlSerializer serializer)
- throws IOException {
- serializer.setPrefix(NAMESPACE_GSX, NAMESPACE_GSX_URI);
- }
-
- /* (non-JavaDoc)
- * Handles the non-Atom data belonging to the GData Spreadsheets Cell
- * namespace.
- */
- protected void serializeExtraEntryContents(XmlSerializer serializer,
- int format) throws ParseException, IOException {
- ListEntry entry = (ListEntry) getEntry();
- Vector names = entry.getNames();
- String name = null;
- String value = null;
- Iterator it = names.iterator();
- while (it.hasNext()) {
- name = (String) it.next();
- value = entry.getValue(name);
- if (value != null) {
- serializer.startTag(NAMESPACE_GSX_URI, name);
- serializer.text(value);
- serializer.endTag(NAMESPACE_GSX_URI, name);
- }
- }
- }
-}
diff --git a/src/com/google/wireless/gdata2/ConflictDetectedException.java b/src/com/google/wireless/gdata2/ConflictDetectedException.java
new file mode 100644
index 0000000..fa2d84f
--- /dev/null
+++ b/src/com/google/wireless/gdata2/ConflictDetectedException.java
@@ -0,0 +1,32 @@
+// Copyright 2009 The Android Open Source Project.
+
+package com.google.wireless.gdata2;
+
+import com.google.wireless.gdata2.data.Entry;
+
+/**
+ * A ConflictDetectedException is thrown when the server detects a conflict
+ * between the Entry which the client is trying to insert or modify and an
+ * existing Entry. Typically this is because the version of the Entry being
+ * uploaded by the client is older than the version on the server, but it may
+ * also indicate the violation of some other constraint (e.g., key uniqueness).
+ */
+public class ConflictDetectedException extends GDataException {
+
+ private final Entry conflictingEntry;
+
+ /**
+ * Creates a new ConflictDetectedException with the given entry.
+ * @param conflictingEntry the conflicting entry state returned by the server.
+ */
+ public ConflictDetectedException(Entry conflictingEntry) {
+ this.conflictingEntry = conflictingEntry;
+ }
+
+ /**
+ * @return the conflicting Entry returned by the server.
+ */
+ public Entry getConflictingEntry() {
+ return conflictingEntry;
+ }
+}
diff --git a/src/com/google/wireless/gdata2/GDataException.java b/src/com/google/wireless/gdata2/GDataException.java
new file mode 100644
index 0000000..b5850f1
--- /dev/null
+++ b/src/com/google/wireless/gdata2/GDataException.java
@@ -0,0 +1,64 @@
+// Copyright 2007 The Android Open Source Project
+package com.google.wireless.gdata2;
+
+/**
+ * The base exception for GData operations.
+ */
+public class GDataException extends Exception {
+
+ private final Throwable cause;
+
+ /**
+ * Creates a new empty GDataException.
+ */
+ public GDataException() {
+ super();
+ cause = null;
+ }
+
+ /**
+ * Creates a new GDataException with the supplied message.
+ * @param message The message for this GDataException.
+ */
+ public GDataException(String message) {
+ super(message);
+ cause = null;
+ }
+
+ /**
+ * Creates a new GDataException with the supplied message and underlying
+ * cause.
+ *
+ * @param message The message for this GDataException.
+ * @param cause The underlying cause that was caught and wrapped by this
+ * GDataException.
+ */
+ public GDataException(String message, Throwable cause) {
+ super(message);
+ this.cause = cause;
+ }
+
+ /**
+ * Creates a new GDataException with the underlying cause.
+ *
+ * @param cause The underlying cause that was caught and wrapped by this
+ * GDataException.
+ */
+ public GDataException(Throwable cause) {
+ this("", cause);
+ }
+
+ /**
+ * @return the cause of this GDataException or null if the cause is unknown.
+ */
+ public Throwable getCause() {
+ return cause;
+ }
+
+ /**
+ * @return a string representation of this exception.
+ */
+ public String toString() {
+ return super.toString() + (cause != null ? " " + cause.toString() : "");
+ }
+}
diff --git a/src/com/google/wireless/gdata2/client/AuthenticationException.java b/src/com/google/wireless/gdata2/client/AuthenticationException.java
new file mode 100644
index 0000000..72caddb
--- /dev/null
+++ b/src/com/google/wireless/gdata2/client/AuthenticationException.java
@@ -0,0 +1,37 @@
+// Copyright 2009 The Android Open Source Project.
+
+package com.google.wireless.gdata2.client;
+
+import com.google.wireless.gdata2.GDataException;
+
+/**
+ * Exception thrown when a user's credentials could not be authenticated.
+ */
+public class AuthenticationException extends GDataException {
+
+ /**
+ * Creates a new AuthenticationException.
+ */
+ public AuthenticationException() {
+ }
+
+ /**
+ * Creates a new AuthenticationException with a supplied message.
+ * @param message The message for the exception.
+ */
+ public AuthenticationException(String message) {
+ super(message);
+ }
+
+ /**
+ * Creates a new AuthenticationException with a supplied message and
+ * underlying cause.
+ *
+ * @param message The message for the exception.
+ * @param cause Another throwable that was caught and wrapped in this
+ * exception.
+ */
+ public AuthenticationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/com/google/wireless/gdata2/client/BadRequestException.java b/src/com/google/wireless/gdata2/client/BadRequestException.java
new file mode 100644
index 0000000..6e2e98b
--- /dev/null
+++ b/src/com/google/wireless/gdata2/client/BadRequestException.java
@@ -0,0 +1,37 @@
+// Copyright 2009 The Android Open Source Project.
+
+package com.google.wireless.gdata2.client;
+
+import com.google.wireless.gdata2.GDataException;
+
+/**
+ * Exception thrown when the server returns a 400 Bad Request.
+ */
+public class BadRequestException extends GDataException {
+
+ /**
+ * Creates a new AuthenticationException.
+ */
+ public BadRequestException() {
+ }
+
+ /**
+ * Creates a new BadRequestException with a supplied message.
+ * @param message The message for the exception.
+ */
+ public BadRequestException(String message) {
+ super(message);
+ }
+
+ /**
+ * Creates a new BadRequestException with a supplied message and
+ * underlying cause.
+ *
+ * @param message The message for the exception.
+ * @param cause Another throwable that was caught and wrapped in this
+ * exception.
+ */
+ public BadRequestException(String message, Throwable cause) {
+ super(message, cause);
+ }
+} \ No newline at end of file
diff --git a/src/com/google/wireless/gdata2/client/ForbiddenException.java b/src/com/google/wireless/gdata2/client/ForbiddenException.java
new file mode 100644
index 0000000..49a19cb
--- /dev/null
+++ b/src/com/google/wireless/gdata2/client/ForbiddenException.java
@@ -0,0 +1,37 @@
+// Copyright 2009 The Android Open Source Project.
+
+package com.google.wireless.gdata2.client;
+
+import com.google.wireless.gdata2.GDataException;
+
+/**
+ * Exception thrown when the server returns a 403 Forbidden.
+ */
+public class ForbiddenException extends GDataException {
+
+ /**
+ * Creates a new AuthenticationException.
+ */
+ public ForbiddenException() {
+ }
+
+ /**
+ * Creates a new ForbiddenException with a supplied message.
+ * @param message The message for the exception.
+ */
+ public ForbiddenException(String message) {
+ super(message);
+ }
+
+ /**
+ * Creates a new ForbiddenException with a supplied message and
+ * underlying cause.
+ *
+ * @param message The message for the exception.
+ * @param cause Another throwable that was caught and wrapped in this
+ * exception.
+ */
+ public ForbiddenException(String message, Throwable cause) {
+ super(message, cause);
+ }
+} \ No newline at end of file
diff --git a/src/com/google/wireless/gdata2/client/GDataClient.java b/src/com/google/wireless/gdata2/client/GDataClient.java
new file mode 100644
index 0000000..4f664c5
--- /dev/null
+++ b/src/com/google/wireless/gdata2/client/GDataClient.java
@@ -0,0 +1,209 @@
+// Copyright 2007 The Android Open Source Project
+
+package com.google.wireless.gdata2.client;
+
+import com.google.wireless.gdata2.serializer.GDataSerializer;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Interface for interacting with a GData server. Specific platforms can
+ * provide their own implementations using the available networking and HTTP
+ * stack for that platform.
+ */
+public interface GDataClient {
+
+ /**
+ * Closes this GDataClient, cleaning up any resources, persistent connections, etc.,
+ * it may have.
+ */
+ void close();
+
+ /**
+ * URI encodes the supplied uri (using UTF-8).
+ * @param uri The uri that should be encoded.
+ * @return The encoded URI.
+ */
+ // TODO: get rid of this, if we write our own URI encoding library.
+ String encodeUri(String uri);
+
+ /**
+ * Creates a new QueryParams that should be used to restrict the feed
+ * contents that are fetched.
+ * @return A new QueryParams.
+ */
+ // TODO: get rid of this, if we write a generic QueryParams that can encode
+ // querystring params/values.
+ QueryParams createQueryParams();
+
+ /**
+ * Connects to a GData server (specified by the feedUrl) and fetches the
+ * specified feed as an InputStream. The caller is responsible for calling
+ * {@link InputStream#close()} on the returned {@link InputStream}.
+ *
+ * @param feedUrl The feed that should be fetched.
+ * @param authToken The authentication token that should be used when
+ * fetching the feed.
+ * @param eTag The eTag associated with this request, this will
+ * cause the GET to return a 304 if the content was
+ * not modified. The parameter can be null
+ * @param protocolVersion Identifies the protocol version to use
+ * in form of a string, e.g. "2.1". The parameter
+ * can not be null
+ * @return An InputStream for the feed.
+ * @throws IOException Thrown if an io error occurs while communicating with
+ * the service.
+ * @throws HttpException if the service returns an error response.
+ */
+ InputStream getFeedAsStream(String feedUrl,
+ String authToken,
+ String eTag,
+ String protocolVersion)
+ throws HttpException, IOException;
+
+ /**
+ * Connects to a GData server (specified by the mediaEntryUrl) and fetches the
+ * specified media entry as an InputStream. The caller is responsible for calling
+ * {@link InputStream#close()} on the returned {@link InputStream}.
+ *
+ * @param mediaEntryUrl The media entry that should be fetched.
+ * @param authToken The authentication token that should be used when
+ * fetching the media entry.
+ * @param eTag The eTag associated with this request, this will
+ * cause the GET to return a 304 if the content was
+ * not modified.
+ * @param protocolVersion Identifies the protocol version to use
+ * in form of a string, e.g. "2.1". The parameter
+ * can not be null
+ * @return An InputStream for the media entry.
+ * @throws IOException Thrown if an io error occurs while communicating with
+ * the service.
+ * @throws HttpException if the service returns an error response.
+ */
+ InputStream getMediaEntryAsStream(String mediaEntryUrl, String authToken, String eTag, String protocolVersion)
+ throws HttpException, IOException;
+
+ /**
+ * Connects to a GData server (specified by the feedUrl) and creates a new
+ * entry. The response from the server is returned as an
+ * {@link InputStream}. The caller is responsible for calling
+ * {@link InputStream#close()} on the returned {@link InputStream}.
+ *
+ * @param feedUrl The feed url where the entry should be created.
+ * @param authToken The authentication token that should be used when
+ * creating the entry.
+ * @param protocolVersion Identifies the protocol version to use
+ * in form of a string, e.g. "2.1". The parameter
+ * can not be null
+ * @param entry The entry that should be created.
+ * @throws IOException Thrown if an io error occurs while communicating with
+ * the service.
+ * @throws HttpException if the service returns an error response.
+ */
+ InputStream createEntry(String feedUrl,
+ String authToken,
+ String protocolVersion,
+ GDataSerializer entry)
+ throws HttpException, IOException;
+
+ /**
+ * Connects to a GData server (specified by the editUri) and updates an
+ * existing entry. If the entry has a partial field mask set
+ * {@link setFields}, this call will be executed as a partial
+ * PATCH instead of a full representation PUT. The response from
+ * the server is returned as an {@link InputStream}. The caller
+ * is responsible for calling {@link InputStream#close()} on the
+ * returned {@link InputStream}.
+ *
+ * @param editUri The edit uri that should be used for updating the entry.
+ * @param authToken The authentication token that should be used when
+ * updating the entry.
+ * @param eTag The eTag associated with this request, this will
+ * cause the PUT to return a conflict if the
+ * resource was already modified
+ * @param protocolVersion Identifies the protocol version to use
+ * in form of a string, e.g. "2.1". The parameter
+ * can not be null
+ * @param entry The entry that should be updated.
+ * @throws IOException Thrown if an io error occurs while communicating with
+ * the service.
+ * @throws HttpException if the service returns an error response.
+ */
+ InputStream updateEntry(String editUri,
+ String authToken,
+ String eTag,
+ String protocolVersion,
+ GDataSerializer entry)
+ throws HttpException, IOException;
+
+ /**
+ * Connects to a GData server (specified by the editUri) and deletes an
+ * existing entry. Note this does not need a protocol version,
+ * it will default to 2.0.
+ *
+ * @param editUri The edit uri that should be used for deleting the entry.
+ * @param authToken The authentication token that should be used when
+ * deleting the entry.
+ * @param eTag The eTag associated with this request, this will
+ * cause a failure if the resource was modified
+ * since retrieval.
+ * @throws IOException Thrown if an io error occurs while communicating with
+ * the service.
+ * @throws HttpException if the service returns an error response.
+ */
+ void deleteEntry(String editUri,
+ String authToken,
+ String eTag)
+ throws HttpException, IOException;
+
+ /**
+ * Connects to a GData server (specified by the editUri) and updates an
+ * existing media entry. The response from the server is returned as an
+ * {@link InputStream}. The caller is responsible for calling
+ * {@link InputStream#close()} on the returned {@link InputStream}.
+ *
+ * @param editUri The edit uri that should be used for updating the entry.
+ * @param authToken The authentication token that should be used when
+ * updating the entry
+ * @param eTag The eTag associated with this request, this will
+ * cause the PUT to return a conflict if the
+ * resource was already modified
+ * @param protocolVersion Identifies the protocol version to use
+ * in form of a string, e.g. "2.1". The parameter
+ * can not be null
+ * @param mediaEntryInputStream The {@link InputStream} that contains the new
+ * value of the resource
+ * @param contentType The contentType of the new media entry
+ * @throws IOException Thrown if an io error occurs while communicating with
+ * the service.
+ * @throws HttpException if the service returns an error response.
+ * @return The {@link InputStream} that contains the metadata associated with the
+ * new version of the media entry.
+ */
+ public InputStream updateMediaEntry(String editUri, String authToken, String eTag,
+ String protocolVersion, InputStream mediaEntryInputStream, String contentType)
+ throws HttpException, IOException;
+
+ /**
+ * Connects to a GData server (specified by the batchUrl) and submits a
+ * batch for processing. The response from the server is returned as an
+ * {@link InputStream}. The caller is responsible for calling
+ * {@link InputStream#close()} on the returned {@link InputStream}.
+ *
+ * @param batchUrl The batch url to which the batch is submitted.
+ * @param authToken the authentication token that should be used when
+ * submitting the batch.
+ * @param protocolVersion The version of the protocol that
+ * should be used for this request.
+ * @param batch The batch of entries to submit.
+ * @throws IOException Thrown if an io error occurs while communicating with
+ * the service.
+ * @throws HttpException if the service returns an error response.
+ */
+ InputStream submitBatch(String batchUrl,
+ String authToken,
+ String protocolVersion,
+ GDataSerializer batch)
+ throws HttpException, IOException;
+}
diff --git a/src/com/google/wireless/gdata2/client/GDataParserFactory.java b/src/com/google/wireless/gdata2/client/GDataParserFactory.java
new file mode 100644
index 0000000..f08b384
--- /dev/null
+++ b/src/com/google/wireless/gdata2/client/GDataParserFactory.java
@@ -0,0 +1,57 @@
+// Copyright 2007 The Android Open Source Project
+
+package com.google.wireless.gdata2.client;
+
+import com.google.wireless.gdata2.parser.GDataParser;
+import com.google.wireless.gdata2.parser.ParseException;
+import com.google.wireless.gdata2.data.Entry;
+import com.google.wireless.gdata2.serializer.GDataSerializer;
+
+import java.io.InputStream;
+import java.util.Enumeration;
+
+/**
+ * Factory that creates {@link GDataParser}s and {@link GDataSerializer}s.
+ */
+public interface GDataParserFactory {
+ /**
+ * Creates a new {@link GDataParser} for the provided InputStream.
+ *
+ * @param entryClass Specify the class of Entry objects that are to be parsed. This
+ * lets createParser know which parser to create.
+ * @param is The InputStream that should be parsed. @return The GDataParser that will parse is.
+ * @throws ParseException Thrown if the GDataParser could not be created.
+ * @throws IllegalArgumentException if the feed type is unknown.
+ */
+ GDataParser createParser(Class entryClass, InputStream is)
+ throws ParseException;
+
+ /**
+ * Creates a new {@link GDataParser} for the provided InputStream, using the
+ * default feed type for the client.
+ *
+ * @param is The InputStream that should be parsed.
+ * @return The GDataParser that will parse is.
+ * @throws ParseException Thrown if the GDataParser could not be created.
+ * Note that this can occur if the feed in the InputStream is not of
+ * the default type assumed by this method.
+ * @see #createParser(Class,InputStream)
+ */
+ GDataParser createParser(InputStream is) throws ParseException;
+
+ /**
+ * Creates a new {@link GDataSerializer} for the provided Entry.
+ *
+ * @param entry The Entry that should be serialized.
+ * @return The GDataSerializer that will serialize entry.
+ */
+ GDataSerializer createSerializer(Entry entry);
+
+ /**
+ * Creates a new {@link GDataSerializer} for the provided batch of entries.
+ *
+ * @param batch An enumeration of entries comprising the batch.
+ * @return The GDataSerializer that will serialize the batch.
+ */
+ GDataSerializer createSerializer(Enumeration batch);
+}
diff --git a/src/com/google/wireless/gdata2/client/GDataServiceClient.java b/src/com/google/wireless/gdata2/client/GDataServiceClient.java
new file mode 100644
index 0000000..5e0aea1
--- /dev/null
+++ b/src/com/google/wireless/gdata2/client/GDataServiceClient.java
@@ -0,0 +1,478 @@
+// Copyright 2008 The Android Open Source Project
+
+package com.google.wireless.gdata2.client;
+
+import com.google.wireless.gdata2.ConflictDetectedException;
+import com.google.wireless.gdata2.client.AuthenticationException;
+import com.google.wireless.gdata2.client.HttpException;
+import com.google.wireless.gdata2.client.ResourceGoneException;
+import com.google.wireless.gdata2.client.ResourceNotFoundException;
+import com.google.wireless.gdata2.data.Entry;
+import com.google.wireless.gdata2.data.MediaEntry;
+import com.google.wireless.gdata2.data.StringUtils;
+import com.google.wireless.gdata2.parser.GDataParser;
+import com.google.wireless.gdata2.parser.ParseException;
+import com.google.wireless.gdata2.serializer.GDataSerializer;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Enumeration;
+
+/**
+ * Abstract base class for service-specific clients to access GData feeds.
+ */
+public abstract class GDataServiceClient {
+
+ /**
+ * Default gdata protocol version that this library supports
+ */
+ protected static String DEFAULT_GDATA_VERSION = "2.0";
+
+ private final GDataClient gDataClient;
+ private final GDataParserFactory gDataParserFactory;
+
+ public GDataServiceClient(GDataClient gDataClient, GDataParserFactory gDataParserFactory) {
+ this.gDataClient = gDataClient;
+ this.gDataParserFactory = gDataParserFactory;
+ }
+
+ /**
+ * Returns the {@link GDataClient} being used by this GDataServiceClient.
+ *
+ * @return The {@link GDataClient} being used by this GDataServiceClient.
+ */
+ protected GDataClient getGDataClient() {
+ return gDataClient;
+ }
+
+ /**
+ * Returns the protocol version used by this GDataServiceClient, in the form
+ * of a "2.1" string
+ *
+ * @return String
+ */
+ public abstract String getProtocolVersion();
+
+ /**
+ * Returns the {@link GDataParserFactory} being used by this
+ * GDataServiceClient.
+ *
+ * @return The {@link GDataParserFactory} being used by this
+ * GDataServiceClient.
+ */
+ protected GDataParserFactory getGDataParserFactory() {
+ return gDataParserFactory;
+ }
+
+ /**
+ * Returns the name of the service. Used for authentication.
+ *
+ * @return The name of the service.
+ */
+ public abstract String getServiceName();
+
+ /**
+ * Creates {@link QueryParams} that can be used to restrict the feed contents
+ * that are fetched.
+ *
+ * @return The QueryParams that can be used with this client.
+ */
+ public QueryParams createQueryParams() {
+ return gDataClient.createQueryParams();
+ }
+
+ /**
+ * Fetches a feed for this user. The caller is responsible for closing the
+ * returned {@link GDataParser}.
+ *
+ * @param feedEntryClass the class of Entry that is contained in the feed
+ * @param feedUrl ThAe URL of the feed that should be fetched.
+ * @param authToken The authentication token for this user.
+ * @param eTag The etag used for this query. Passing null will result in an
+ * unconditional query
+ * @return A {@link GDataParser} for the requested feed.
+ * @throws AuthenticationException Thrown if the server considers the
+ * authToken invalid.
+ * @throws ResourceGoneException Thrown if the server indicates that the
+ * resource is Gone. Currently used to indicate that some Tombstones
+ * are missing.
+ * @throws ResourceNotModifiedException Thrown if the retrieval fails because
+ * the specified ETag matches the current ETag of the entry (i.e. the
+ * entry has not been modified since last retrieval).
+ * @throws HttpException Thrown if the request returns an error response.
+ * @throws ParseException Thrown if the server response cannot be parsed.
+ * @throws IOException Thrown if an error occurs while communicating with the
+ * GData service.
+ */
+ public GDataParser getParserForFeed(Class feedEntryClass, String feedUrl, String authToken,
+ String eTag) throws AuthenticationException, ResourceGoneException,
+ ResourceNotModifiedException, HttpException, ParseException, IOException,
+ ForbiddenException {
+ try {
+ InputStream is = gDataClient.getFeedAsStream(feedUrl, authToken, eTag, getProtocolVersion());
+ return gDataParserFactory.createParser(feedEntryClass, is);
+ } catch (HttpException e) {
+ convertHttpExceptionForFeedReads("Could not fetch feed " + feedUrl, e);
+ return null; // never reached
+ }
+ }
+
+ /**
+ * Fetches a media entry as an InputStream. The caller is responsible for
+ * closing the returned {@link InputStream}.
+ *
+ * @param mediaEntryUrl The URL of the media entry that should be fetched.
+ * @param authToken The authentication token for this user.
+ * @param eTag The ETag associated with this request.
+ * @return A {@link InputStream} for the requested media entry.
+ * @throws AuthenticationException Thrown if the server considers the
+ * authToken invalid.
+ * @throws ResourceGoneException Thrown if the server indicates that the
+ * resource is Gone. Currently used to indicate that some Tombstones
+ * are missing.
+ * @throws ResourceNotModifiedException Thrown if the retrieval fails because
+ * the specified ETag matches the current ETag of the entry (i.e. the
+ * entry has not been modified since last retrieval).
+ * @throws ResourceNotFoundException Thrown if the resource was not found.
+ * @throws HttpException Thrown if the request returns an error response.
+ * @throws IOException Thrown if an error occurs while communicating with the
+ * GData service.
+ */
+ public InputStream getMediaEntryAsStream(String mediaEntryUrl, String authToken, String eTag)
+ throws AuthenticationException, ResourceGoneException, ResourceNotModifiedException,
+ ResourceNotFoundException, HttpException, IOException, ForbiddenException {
+ try {
+ return gDataClient
+ .getMediaEntryAsStream(mediaEntryUrl, authToken, eTag, getProtocolVersion());
+ } catch (HttpException e) {
+ convertHttpExceptionForEntryReads("Could not fetch media entry " + mediaEntryUrl, e);
+ return null; // never reached
+ }
+ }
+
+ /**
+ * Creates a new entry at the provided feed. Parses the server response into
+ * the version of the entry stored on the server.
+ *
+ * @param feedUrl The feed where the entry should be created.
+ * @param authToken The authentication token for this user.
+ * @param entry The entry that should be created.
+ * @return The entry returned by the server as a result of creating the
+ * provided entry.
+ * @throws ConflictDetectedException Thrown if the server detects an existing
+ * entry that conflicts with this one.
+ * @throws AuthenticationException Thrown if the server considers the
+ * authToken invalid.
+ * @throws PreconditionFailedException Thrown if the update fails because the
+ * specified ETag does not match the current ETag of the
+ * @throws HttpException Thrown if the request returns an error response.
+ * @throws ParseException Thrown if the server response cannot be parsed.
+ * @throws IOException Thrown if an error occurs while communicating with the
+ * GData service.
+ * @throws BadRequestException thrown if the server returns a 400
+ * @throws ForbiddenException thrown if the server returns a 403
+ */
+ public Entry createEntry(String feedUrl, String authToken, Entry entry)
+ throws ConflictDetectedException, AuthenticationException, PreconditionFailedException,
+ HttpException, ParseException, IOException, ForbiddenException, BadRequestException {
+ GDataSerializer serializer = gDataParserFactory.createSerializer(entry);
+ try {
+ InputStream is =
+ gDataClient.createEntry(feedUrl, authToken, getProtocolVersion(), serializer);
+ return parseEntry(entry.getClass(), is);
+ } catch (HttpException e) {
+ try {
+ convertHttpExceptionForWrites(entry.getClass(),
+ "Could not create " + "entry " + feedUrl, e);
+ } catch (ResourceNotFoundException e1) {
+ // this should never happen
+ throw e;
+ }
+ return null; // never reached.
+ }
+ }
+
+ /**
+ * Fetches an existing entry.
+ *
+ * @param entryClass the type of entry to expect
+ * @param id of the entry to fetch.
+ * @param authToken The authentication token for this user
+ * @param eTag The etag used for this query. Passing null will result in an
+ * unconditional query
+ * @return The entry returned by the server.
+ * @throws AuthenticationException Thrown if the server considers the
+ * authToken invalid.
+ * @throws ResourceNotFoundException Thrown if the resource was not found.
+ * @throws ResourceNotModifiedException Thrown if the retrieval fails because
+ * the specified ETag matches the current ETag of the entry (i.e. the
+ * entry has not been modified since last retrieval).
+ * @throws HttpException Thrown if the request returns an error response.
+ * @throws ParseException Thrown if the server response cannot be parsed.
+ * @throws IOException Thrown if an error occurs while communicating with the
+ * GData service.
+ */
+ public Entry getEntry(Class entryClass, String id, String authToken, String eTag)
+ throws AuthenticationException, ResourceNotFoundException, ResourceNotModifiedException,
+ HttpException, ParseException, IOException, ForbiddenException {
+ try {
+ InputStream is = getGDataClient().getFeedAsStream(id, authToken, eTag, getProtocolVersion());
+ return parseEntry(entryClass, is);
+ } catch (HttpException e) {
+ convertHttpExceptionForEntryReads("Could not fetch entry " + id, e);
+ return null; // never reached
+ }
+ }
+
+ /**
+ * Updates an existing entry. Parses the server response into the version of
+ * the entry stored on the server.
+ *
+ * @param entry The entry that should be updated.
+ * @param authToken The authentication token for this user.
+ * @return The entry returned by the server as a result of updating the
+ * provided entry.
+ * @throws AuthenticationException Thrown if the server considers the
+ * authToken invalid.
+ * @throws ConflictDetectedException Thrown if the server detects an existing
+ * entry that conflicts with this one, or if the server version of
+ * this entry has changed since it was retrieved.
+ * @throws PreconditionFailedException Thrown if the update fails because the
+ * specified ETag does not match the current ETag of the entry.
+ * @throws HttpException Thrown if the request returns an error response.
+ * @throws ParseException Thrown if the server response cannot be parsed.
+ * @throws IOException Thrown if an error occurs while communicating with the
+ * GData service.
+ * @throws BadRequestException thrown if the server returns a 400
+ * @throws ForbiddenException thrown if the server returns a 403
+ * @throws ResourceNotFoundException Thrown if the resource was not found.
+ */
+ public Entry updateEntry(Entry entry, String authToken) throws AuthenticationException,
+ ConflictDetectedException, PreconditionFailedException, HttpException, ParseException,
+ IOException, ForbiddenException, ResourceNotFoundException, BadRequestException {
+ String editUri = entry.getEditUri();
+ if (StringUtils.isEmpty(editUri)) {
+ throw new ParseException("No edit URI -- cannot update.");
+ }
+
+ GDataSerializer serializer = gDataParserFactory.createSerializer(entry);
+ try {
+ InputStream is =
+ gDataClient.updateEntry(editUri, authToken, entry.getETag(), getProtocolVersion(),
+ serializer);
+ return parseEntry(entry.getClass(), is);
+ } catch (HttpException e) {
+ convertHttpExceptionForWrites(entry.getClass(), "Could not update " + "entry " + editUri, e);
+ return null; // never reached
+ }
+ }
+
+ /**
+ * Updates an existing entry. Parses the server response into the metadata of
+ * the entry stored on the server.
+ *
+ * @param editUri The URI of the resource that should be updated.
+ * @param inputStream The {@link java.io.InputStream} that contains the new
+ * value of the media entry
+ * @param contentType The content type of the new media entry
+ * @param authToken The authentication token for this user.
+ * @return The entry returned by the server as a result of updating the
+ * provided entry
+ * @param eTag The etag used for this query. Passing null will result in an
+ * unconditional query
+ * @throws AuthenticationException Thrown if the server considers the
+ * authToken invalid.
+ * @throws ConflictDetectedException Thrown if the server detects an existing
+ * entry that conflicts with this one, or if the server version of
+ * this entry has changed since it was retrieved.
+ * @throws PreconditionFailedException Thrown if the update fails because the
+ * specified ETag does not match the current ETag of the entry.
+ * @throws HttpException Thrown if the request returns an error response.
+ * @throws ParseException Thrown if the server response cannot be parsed.
+ * @throws IOException Thrown if an error occurs while communicating with the
+ * GData service.
+ * @throws BadRequestException thrown if the server returns a 400
+ * @throws ForbiddenException thrown if the server returns a 403
+ * @throws ResourceNotFoundException Thrown if the resource was not found.
+ */
+ public MediaEntry updateMediaEntry(String editUri, InputStream inputStream, String contentType,
+ String authToken, String eTag) throws AuthenticationException, ConflictDetectedException,
+ PreconditionFailedException, HttpException, ParseException, IOException,
+ ForbiddenException, ResourceNotFoundException, BadRequestException {
+ if (StringUtils.isEmpty(editUri)) {
+ throw new IllegalArgumentException("No edit URI -- cannot update.");
+ }
+
+ try {
+ InputStream is =
+ gDataClient.updateMediaEntry(editUri, authToken, eTag, getProtocolVersion(), inputStream,
+ contentType);
+ return (MediaEntry) parseEntry(MediaEntry.class, is);
+ } catch (HttpException e) {
+ convertHttpExceptionForWrites(MediaEntry.class, "Could not update entry " + editUri, e);
+ return null; // never reached
+ }
+ }
+
+ /**
+ * Deletes an existing entry.
+ *
+ * @param editUri The editUri for the entry that should be deleted.
+ * @param authToken The authentication token for this user.
+ * @param eTag The etag used for this query. Passing null will result in an
+ * unconditional query
+ * @throws AuthenticationException Thrown if the server considers the
+ * authToken invalid.
+ * @throws ConflictDetectedException Thrown if the server version of this
+ * entry has changed since it was retrieved.
+ * @throws PreconditionFailedException Thrown if the update fails because the
+ * specified ETag does not match the current ETag of the entry.
+ * @throws HttpException Thrown if the request returns an error response.
+ * @throws ParseException Thrown if the server response cannot be parsed.
+ * @throws IOException Thrown if an error occurs while communicating with the
+ * GData service.
+ * @throws BadRequestException thrown if the server returns a 400
+ * @throws ForbiddenException thrown if the server returns a 403
+ * @throws ResourceNotFoundException Thrown if the resource was not found.
+ */
+ public void deleteEntry(String editUri, String authToken, String eTag)
+ throws AuthenticationException, ConflictDetectedException, PreconditionFailedException,
+ HttpException, ParseException, IOException, ForbiddenException,
+ ResourceNotFoundException, BadRequestException {
+ try {
+ gDataClient.deleteEntry(editUri, authToken, eTag);
+ } catch (HttpException e) {
+ if (e.getStatusCode() == HttpException.SC_NOT_FOUND) {
+ // the server does not know about this entry.
+ // nothing to delete.
+ return;
+ }
+ convertHttpExceptionForWrites(null, "Unable to delete " + editUri, e);
+ }
+ }
+
+ private Entry parseEntry(Class entryClass, InputStream is) throws ParseException, IOException {
+ GDataParser parser = null;
+ try {
+ parser = gDataParserFactory.createParser(entryClass, is);
+ return parser.parseStandaloneEntry();
+ } finally {
+ if (parser != null) {
+ parser.close();
+ }
+ }
+ }
+
+ /**
+ * Submits a batch of operations.
+ *
+ * @param feedEntryClass the type of the entry to expect.
+ * @param batchUrl The url to which the batch is submitted.
+ * @param authToken The authentication token for this user.
+ * @param entries an enumeration of the entries to submit.
+ * @throws AuthenticationException Thrown if the server considers the
+ * authToken invalid.
+ * @throws HttpException if the service returns an error response
+ * @throws ParseException Thrown if the server response cannot be parsed.
+ * @throws IOException Thrown if an error occurs while communicating with the
+ * GData service.
+ * @throws BadRequestException thrown if the server returns a 400
+ * @throws ForbiddenException thrown if the server returns a 403
+ */
+ public GDataParser submitBatch(Class feedEntryClass, String batchUrl, String authToken,
+ Enumeration entries) throws AuthenticationException, HttpException, ParseException,
+ IOException, ForbiddenException, BadRequestException {
+ GDataSerializer serializer = gDataParserFactory.createSerializer(entries);
+ try {
+ InputStream is =
+ gDataClient.submitBatch(batchUrl, authToken, getProtocolVersion(), serializer);
+ return gDataParserFactory.createParser(feedEntryClass, is);
+ } catch (HttpException e) {
+ convertHttpExceptionsForBatches("Could not submit batch " + batchUrl, e);
+ return null; // never reached.
+ }
+ }
+
+ protected void convertHttpExceptionForFeedReads(String message, HttpException cause)
+ throws AuthenticationException, ResourceGoneException, ResourceNotModifiedException,
+ HttpException, ForbiddenException {
+ switch (cause.getStatusCode()) {
+ case HttpException.SC_FORBIDDEN:
+ throw new ForbiddenException(message, cause);
+ case HttpException.SC_UNAUTHORIZED:
+ throw new AuthenticationException(message, cause);
+ case HttpException.SC_GONE:
+ throw new ResourceGoneException(message, cause);
+ case HttpException.SC_NOT_MODIFIED:
+ throw new ResourceNotModifiedException(message, cause);
+ default:
+ throw new HttpException(message + ": " + cause.getMessage(), cause.getStatusCode(), cause
+ .getResponseStream());
+ }
+ }
+
+ protected void convertHttpExceptionForEntryReads(String message, HttpException cause)
+ throws AuthenticationException, HttpException, ResourceNotFoundException,
+ ResourceNotModifiedException, ForbiddenException {
+ switch (cause.getStatusCode()) {
+ case HttpException.SC_FORBIDDEN:
+ throw new ForbiddenException(message, cause);
+ case HttpException.SC_UNAUTHORIZED:
+ throw new AuthenticationException(message, cause);
+ case HttpException.SC_NOT_FOUND:
+ throw new ResourceNotFoundException(message, cause);
+ case HttpException.SC_NOT_MODIFIED:
+ throw new ResourceNotModifiedException(message, cause);
+ default:
+ throw new HttpException(message + ": " + cause.getMessage(), cause.getStatusCode(), cause
+ .getResponseStream());
+ }
+ }
+
+ protected void convertHttpExceptionsForBatches(String message, HttpException cause)
+ throws AuthenticationException, ParseException, HttpException, ForbiddenException,
+ BadRequestException {
+ switch (cause.getStatusCode()) {
+ case HttpException.SC_FORBIDDEN:
+ throw new ForbiddenException(message, cause);
+ case HttpException.SC_UNAUTHORIZED:
+ throw new AuthenticationException(message, cause);
+ case HttpException.SC_BAD_REQUEST:
+ throw new BadRequestException(message, cause);
+ default:
+ throw new HttpException(message + ": " + cause.getMessage(), cause.getStatusCode(), cause
+ .getResponseStream());
+ }
+ }
+
+ protected void convertHttpExceptionForWrites(Class entryClass, String message,
+ HttpException cause)
+ throws ConflictDetectedException, AuthenticationException, PreconditionFailedException,
+ ParseException, HttpException, IOException, ForbiddenException,
+ ResourceNotFoundException, BadRequestException {
+ switch (cause.getStatusCode()) {
+ case HttpException.SC_CONFLICT:
+ Entry entry = null;
+ if (entryClass != null) {
+ InputStream is = cause.getResponseStream();
+ if (is != null) {
+ entry = parseEntry(entryClass, cause.getResponseStream());
+ }
+ }
+ throw new ConflictDetectedException(entry);
+ case HttpException.SC_BAD_REQUEST:
+ throw new BadRequestException(message, cause);
+ case HttpException.SC_FORBIDDEN:
+ throw new ForbiddenException(message, cause);
+ case HttpException.SC_UNAUTHORIZED:
+ throw new AuthenticationException(message, cause);
+ case HttpException.SC_PRECONDITION_FAILED:
+ throw new PreconditionFailedException(message, cause);
+ case HttpException.SC_NOT_FOUND:
+ throw new ResourceNotFoundException(message, cause);
+ default:
+ throw new HttpException(message + ": " + cause.getMessage(), cause.getStatusCode(), cause
+ .getResponseStream());
+ }
+ }
+}
diff --git a/src/com/google/wireless/gdata2/client/HttpException.java b/src/com/google/wireless/gdata2/client/HttpException.java
new file mode 100644
index 0000000..4ca71b7
--- /dev/null
+++ b/src/com/google/wireless/gdata2/client/HttpException.java
@@ -0,0 +1,63 @@
+// Copyright 2007 The Android Open Source Project
+
+package com.google.wireless.gdata2.client;
+
+import com.google.wireless.gdata2.GDataException;
+
+import java.io.InputStream;
+
+/**
+ * A class representing exceptional (i.e., non 200) responses from an HTTP
+ * Server.
+ */
+public class HttpException extends GDataException {
+
+ public static final int SC_NOT_MODIFIED = 304;
+
+ public static final int SC_BAD_REQUEST = 400;
+
+ public static final int SC_UNAUTHORIZED = 401;
+
+ public static final int SC_FORBIDDEN = 403;
+
+ public static final int SC_NOT_FOUND = 404;
+
+ public static final int SC_CONFLICT = 409;
+
+ public static final int SC_GONE = 410;
+
+ public static final int SC_PRECONDITION_FAILED = 412;
+
+ public static final int SC_INTERNAL_SERVER_ERROR = 500;
+
+ private final int statusCode;
+
+ private final InputStream responseStream;
+
+ /**
+ * Creates an HttpException with the given message, statusCode and
+ * responseStream.
+ */
+ public HttpException(String message, int statusCode,
+ InputStream responseStream) {
+ super(message);
+ this.statusCode = statusCode;
+ this.responseStream = responseStream;
+ }
+
+ /**
+ * Gets the status code associated with this exception.
+ * @return the status code returned by the server, typically one of the SC_*
+ * constants.
+ */
+ public int getStatusCode() {
+ return statusCode;
+ }
+
+ /**
+ * @return the error response stream from the server.
+ */
+ public InputStream getResponseStream() {
+ return responseStream;
+ }
+}
diff --git a/src/com/google/wireless/gdata2/client/HttpQueryParams.java b/src/com/google/wireless/gdata2/client/HttpQueryParams.java
new file mode 100644
index 0000000..bfd9eaf
--- /dev/null
+++ b/src/com/google/wireless/gdata2/client/HttpQueryParams.java
@@ -0,0 +1,73 @@
+// Copyright 2007 The Android Open Source Project
+
+package com.google.wireless.gdata2.client;
+
+import java.util.Hashtable;
+import java.util.Vector;
+
+/**
+ * A concrete implementation of QueryParams that uses the encodeUri method of a
+ * GDataClient (passed in at construction time) to URL encode parameters.
+ *
+ * This implementation maintains the order of parameters, which is useful for
+ * testing. Instances of this class are not thread safe.
+ */
+public class HttpQueryParams extends QueryParams {
+
+ private GDataClient client;
+
+ /* Used to store the mapping of names to values */
+ private Hashtable params;
+
+ /* Used to maintain the order of parameter additions */
+ private Vector names;
+
+ /**
+ * Constructs a new, empty HttpQueryParams.
+ *
+ * @param client GDataClient whose encodeUri method is used for URL encoding.
+ */
+ public HttpQueryParams(GDataClient client) {
+ this.client = client;
+ // We expect most queries to have a relatively small number of parameters.
+ names = new Vector(4);
+ params = new Hashtable(7);
+ }
+
+ public String generateQueryUrl(String feedUrl) {
+ StringBuffer url = new StringBuffer(feedUrl);
+ url.append(feedUrl.indexOf('?') >= 0 ? '&' : '?');
+
+ for (int i = 0; i < names.size(); i++) {
+ if (i > 0) {
+ url.append('&');
+ }
+ String name = (String) names.elementAt(i);
+ url.append(client.encodeUri(name)).append('=');
+ url.append(client.encodeUri(getParamValue(name)));
+ }
+ return url.toString();
+ }
+
+ public String getParamValue(String param) {
+ return (String) params.get(param);
+ }
+
+ public void setParamValue(String param, String value) {
+ if (value != null) {
+ if (!params.containsKey(param)) {
+ names.addElement(param);
+ }
+ params.put(param, value);
+ } else {
+ if (params.remove(param) != null) {
+ names.removeElement(param);
+ }
+ }
+ }
+
+ public void clear() {
+ names.removeAllElements();
+ params.clear();
+ }
+}
diff --git a/src/com/google/wireless/gdata2/client/PreconditionFailedException.java b/src/com/google/wireless/gdata2/client/PreconditionFailedException.java
new file mode 100644
index 0000000..caa880e
--- /dev/null
+++ b/src/com/google/wireless/gdata2/client/PreconditionFailedException.java
@@ -0,0 +1,39 @@
+// Copyright 2009 The Android Open Source Project.
+
+package com.google.wireless.gdata2.client;
+
+import com.google.wireless.gdata2.GDataException;
+
+/**
+ * Exception thrown when an update fails because the specified ETag doesn't
+ * match the current ETag on the entry (which implies that the entry has changed
+ * on the server since it was last retrieved).
+ */
+public class PreconditionFailedException extends GDataException {
+
+ /**
+ * Creates a new PreconditionFailedException.
+ */
+ public PreconditionFailedException() {
+ }
+
+ /**
+ * Creates a new PreconditionFailedException with a supplied message.
+ * @param message The message for the exception.
+ */
+ public PreconditionFailedException(String message) {
+ super(message);
+ }
+
+ /**
+ * Creates a new PreconditionFailedException with a supplied message and
+ * underlying cause.
+ *
+ * @param message The message for the exception.
+ * @param cause Another throwable that was caught and wrapped in this
+ * exception.
+ */
+ public PreconditionFailedException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/com/google/wireless/gdata2/client/QueryParams.java b/src/com/google/wireless/gdata2/client/QueryParams.java
new file mode 100644
index 0000000..c3afb4a
--- /dev/null
+++ b/src/com/google/wireless/gdata2/client/QueryParams.java
@@ -0,0 +1,258 @@
+// Copyright 2007 The Android Open Source Project
+
+package com.google.wireless.gdata2.client;
+
+/**
+ * Class for specifying parameters and constraints for a GData feed.
+ * These are used to modify the feed URL and add querystring parameters to the
+ * feed URL.
+ *
+ * Note that if an entry ID has been set, no other query params can be set.
+ *
+ * @see QueryParams#generateQueryUrl(String)
+ */
+// TODO: add support for projections?
+// TODO: add support for categories?
+public abstract class QueryParams {
+
+ /**
+ * Param name constant for a search query.
+ */
+ public static final String QUERY_PARAM = "q";
+
+ /**
+ * Param name constant for filtering by author.
+ */
+ public static final String AUTHOR_PARAM = "author";
+
+ /**
+ * Param name constant for alternate representations of GData.
+ */
+ public static final String ALT_PARAM = "alt";
+ public static final String ALT_RSS = "rss";
+ public static final String ALT_JSON = "json";
+
+ /**
+ * Param name constant for the updated min.
+ */
+ public static final String UPDATED_MIN_PARAM = "updated-min";
+
+ /**
+ * Param name constant for the updated max.
+ */
+ public static final String UPDATED_MAX_PARAM = "updated-max";
+
+ /**
+ * Param name constant for the published min.
+ */
+ public static final String PUBLISHED_MIN_PARAM = "published-min";
+
+ /**
+ * Param name constant for the published max.
+ */
+ public static final String PUBLISHED_MAX_PARAM = "published-max";
+
+ /**
+ * Param name constant for the start index for results.
+ */
+ public static final String START_INDEX_PARAM = "start-index";
+
+ /**
+ * Param name constant for the max number of results that should be fetched.
+ */
+ public static final String MAX_RESULTS_PARAM = "max-results";
+
+ /**
+ * Param name constant for the fields used in partial retrievals
+ */
+ public static final String FIELDS_PARAM = "fields";
+
+ private String entryId;
+
+ /**
+ * Creates a new empty QueryParams.
+ */
+ public QueryParams() {
+ }
+
+ /**
+ * Generates the url that should be used to query a GData feed.
+ * @param feedUrl The original feed URL.
+ * @return The URL that should be used to query the GData feed.
+ */
+ public abstract String generateQueryUrl(String feedUrl);
+
+ /**
+ * Gets a parameter value from this QueryParams.
+ * @param param The parameter name.
+ * @return The parameter value. Returns null if the parameter is not
+ * defined in this QueryParams.
+ */
+ public abstract String getParamValue(String param);
+
+ /**
+ * Sets a parameter value in this QueryParams.
+ * @param param The parameter name.
+ * @param value The parameter value.
+ */
+ public abstract void setParamValue(String param, String value);
+
+ /**
+ * Clears everything in this QueryParams.
+ */
+ public abstract void clear();
+
+ /**
+ * @return the alt
+ */
+ public String getAlt() {
+ return getParamValue(ALT_PARAM);
+ }
+
+ /**
+ * @param alt the alt to set
+ */
+ public void setAlt(String alt) {
+ setParamValue(ALT_PARAM, alt);
+ }
+
+ /**
+ * @return the author
+ */
+ public String getAuthor() {
+ return getParamValue(AUTHOR_PARAM);
+ }
+
+ /**
+ * @param author the author to set
+ */
+ public void setAuthor(String author) {
+ setParamValue(AUTHOR_PARAM, author);
+ }
+
+ /**
+ * @return the entryId
+ */
+ public String getEntryId() {
+ return entryId;
+ }
+
+ /**
+ * @param entryId the entryId to set
+ */
+ public void setEntryId(String entryId) {
+ this.entryId = entryId;
+ }
+
+ /**
+ * @return the maxResults
+ */
+ public int getMaxResults() {
+ return Integer.parseInt(getParamValue(MAX_RESULTS_PARAM));
+ }
+
+ /**
+ * @param maxResults the maxResults to set
+ */
+ public void setMaxResults(int maxResults) {
+ setParamValue(MAX_RESULTS_PARAM, String.valueOf(maxResults));
+ }
+
+ /**
+ * @return the publishedMax
+ */
+ public String getPublishedMax() {
+ return getParamValue(PUBLISHED_MAX_PARAM);
+ }
+
+ /**
+ * @param publishedMax the publishedMax to set
+ */
+ public void setPublishedMax(String publishedMax) {
+ setParamValue(PUBLISHED_MAX_PARAM, publishedMax);
+ }
+
+ /**
+ * @return the publishedMin
+ */
+ public String getPublishedMin() {
+ return getParamValue(PUBLISHED_MIN_PARAM);
+ }
+
+ /**
+ * @param publishedMin the publishedMin to set
+ */
+ public void setPublishedMin(String publishedMin) {
+ setParamValue(PUBLISHED_MIN_PARAM, publishedMin);
+ }
+
+ /**
+ * @return the query
+ */
+ public String getQuery() {
+ return getParamValue(QUERY_PARAM);
+ }
+
+ /**
+ * @param query the query to set
+ */
+ public void setQuery(String query) {
+ setParamValue(QUERY_PARAM, query);
+ }
+
+ /**
+ * @return the startIndex
+ */
+ public int getStartIndex() {
+ return Integer.parseInt(getParamValue(START_INDEX_PARAM));
+ }
+
+ /**
+ * @param startIndex the startIndex to set
+ */
+ public void setStartIndex(int startIndex) {
+ setParamValue(START_INDEX_PARAM, String.valueOf(startIndex));
+ }
+
+ /**
+ * @return the updatedMax
+ */
+ public String getUpdatedMax() {
+ return getParamValue(UPDATED_MAX_PARAM);
+ }
+
+ /**
+ * @param updatedMax the updatedMax to set
+ */
+ public void setUpdatedMax(String updatedMax) {
+ setParamValue(UPDATED_MAX_PARAM, updatedMax);
+ }
+
+ /**
+ * @return the updatedMin
+ */
+ public String getUpdatedMin() {
+ return getParamValue(UPDATED_MIN_PARAM);
+ }
+
+ /**
+ * @param updatedMin the updatedMin to set
+ */
+ public void setUpdatedMin(String updatedMin) {
+ setParamValue(UPDATED_MIN_PARAM, updatedMin);
+ }
+
+ /**
+ * @return the field list used
+ */
+ public String getFields() {
+ return getParamValue(FIELDS_PARAM);
+ }
+
+ /**
+ * @param fields the fields expression to be used
+ */
+ public void setFields(String fields) {
+ setParamValue(FIELDS_PARAM, fields);
+ }
+}
diff --git a/src/com/google/wireless/gdata2/client/ResourceGoneException.java b/src/com/google/wireless/gdata2/client/ResourceGoneException.java
new file mode 100644
index 0000000..c9911da
--- /dev/null
+++ b/src/com/google/wireless/gdata2/client/ResourceGoneException.java
@@ -0,0 +1,37 @@
+// Copyright 2009 The Android Open Source Project.
+
+package com.google.wireless.gdata2.client;
+
+import com.google.wireless.gdata2.GDataException;
+
+/**
+ * Exception thrown when a specified resource is gone
+ */
+public class ResourceGoneException extends GDataException {
+
+ /**
+ * Creates a new ResourceGoneException.
+ */
+ public ResourceGoneException() {
+ }
+
+ /**
+ * Creates a new ResourceGoneException with a supplied message.
+ * @param message The message for the exception.
+ */
+ public ResourceGoneException(String message) {
+ super(message);
+ }
+
+ /**
+ * Creates a new ResourceGoneException with a supplied message and
+ * underlying cause.
+ *
+ * @param message The message for the exception.
+ * @param cause Another throwable that was caught and wrapped in this
+ * exception.
+ */
+ public ResourceGoneException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/com/google/wireless/gdata2/client/ResourceNotFoundException.java b/src/com/google/wireless/gdata2/client/ResourceNotFoundException.java
new file mode 100644
index 0000000..766abd6
--- /dev/null
+++ b/src/com/google/wireless/gdata2/client/ResourceNotFoundException.java
@@ -0,0 +1,37 @@
+// Copyright 2009 The Android Open Source Project.
+
+package com.google.wireless.gdata2.client;
+
+import com.google.wireless.gdata2.GDataException;
+
+/**
+ * Exception thrown when a specified resource does not exist
+ */
+public class ResourceNotFoundException extends GDataException {
+
+ /**
+ * Creates a new ResourceNotFoundException.
+ */
+ public ResourceNotFoundException() {
+ }
+
+ /**
+ * Creates a new ResourceNotFoundException with a supplied message.
+ * @param message The message for the exception.
+ */
+ public ResourceNotFoundException(String message) {
+ super(message);
+ }
+
+ /**
+ * Creates a new ResourceNotFoundException with a supplied message and
+ * underlying cause.
+ *
+ * @param message The message for the exception.
+ * @param cause Another throwable that was caught and wrapped in this
+ * exception.
+ */
+ public ResourceNotFoundException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/com/google/wireless/gdata2/client/ResourceNotModifiedException.java b/src/com/google/wireless/gdata2/client/ResourceNotModifiedException.java
new file mode 100644
index 0000000..2873bde
--- /dev/null
+++ b/src/com/google/wireless/gdata2/client/ResourceNotModifiedException.java
@@ -0,0 +1,39 @@
+// Copyright 2009 The Android Open Source Project.
+
+package com.google.wireless.gdata2.client;
+
+import com.google.wireless.gdata2.GDataException;
+
+/**
+ * Exception thrown when a retrieval fails because the specified ETag matches
+ * the current ETag on the entry (which implies that the entry has not changed
+ * on the server since it was last retrieved).
+ */
+public class ResourceNotModifiedException extends GDataException {
+
+ /**
+ * Creates a new ResourceNotModifiedException.
+ */
+ public ResourceNotModifiedException() {
+ }
+
+ /**
+ * Creates a new ResourceNotModifiedException with a supplied message.
+ * @param message The message for the exception.
+ */
+ public ResourceNotModifiedException(String message) {
+ super(message);
+ }
+
+ /**
+ * Creates a new ResourceNotModifiedException with a supplied message and
+ * underlying cause.
+ *
+ * @param message The message for the exception.
+ * @param cause Another throwable that was caught and wrapped in this
+ * exception.
+ */
+ public ResourceNotModifiedException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/com/google/wireless/gdata/spreadsheets/client/package.html b/src/com/google/wireless/gdata2/client/package.html
index 1c9bf9d..1c9bf9d 100644
--- a/src/com/google/wireless/gdata/spreadsheets/client/package.html
+++ b/src/com/google/wireless/gdata2/client/package.html
diff --git a/src/com/google/wireless/gdata2/contacts/client/ContactsClient.java b/src/com/google/wireless/gdata2/contacts/client/ContactsClient.java
new file mode 100644
index 0000000..f0258c8
--- /dev/null
+++ b/src/com/google/wireless/gdata2/contacts/client/ContactsClient.java
@@ -0,0 +1,43 @@
+// Copyright 2007 The Android Open Source Project
+
+package com.google.wireless.gdata2.contacts.client;
+
+import com.google.wireless.gdata2.client.GDataClient;
+import com.google.wireless.gdata2.client.GDataParserFactory;
+import com.google.wireless.gdata2.client.GDataServiceClient;
+
+/**
+ * GDataServiceClient for accessing Google Contacts. This client can access and
+ * parse the contacts feeds for specific user. The parser this class uses handle
+ * the XML version of feeds.
+ */
+public class ContactsClient extends GDataServiceClient {
+ /** Service value for contacts. */
+ public static final String SERVICE = "cp";
+
+ /**
+ * Create a new ContactsClient.
+ * @param client The GDataClient that should be used to authenticate
+ * if we are using the caribou feed
+ */
+ public ContactsClient(GDataClient client, GDataParserFactory factory) {
+ super(client, factory);
+ }
+
+ /* (non-Javadoc)
+ * @see GDataServiceClient#getServiceName
+ */
+ public String getServiceName() {
+ return SERVICE;
+ }
+
+ /**
+ * Returns the protocol version used by this GDataServiceClient,
+ * in the form of a string. Contacts is using "3.0"
+ *
+ * @return the protocolVersion
+ */
+ public String getProtocolVersion() {
+ return "3.0";
+ }
+}
diff --git a/src/com/google/wireless/gdata/spreadsheets/data/package.html b/src/com/google/wireless/gdata2/contacts/client/package.html
index 1c9bf9d..1c9bf9d 100644
--- a/src/com/google/wireless/gdata/spreadsheets/data/package.html
+++ b/src/com/google/wireless/gdata2/contacts/client/package.html
diff --git a/src/com/google/wireless/gdata2/contacts/data/CalendarLink.java b/src/com/google/wireless/gdata2/contacts/data/CalendarLink.java
new file mode 100644
index 0000000..71fef94
--- /dev/null
+++ b/src/com/google/wireless/gdata2/contacts/data/CalendarLink.java
@@ -0,0 +1,54 @@
+// Copyright 2009 The Android Open Source Project
+
+package com.google.wireless.gdata2.contacts.data;
+
+import com.google.wireless.gdata2.data.StringUtils;
+
+/**
+ * Storage for URL of the contact's calendar. The element can be
+ * repeated.
+ */
+public class CalendarLink extends ContactsElement {
+ /** The phone number type. */
+ public static final byte TYPE_HOME = 1;
+ public static final byte TYPE_WORK = 2;
+ public static final byte TYPE_FREE_BUSY = 3;
+
+ /**
+ * default empty constructor
+ */
+ public CalendarLink() {}
+
+ /**
+ * constructor that allows initialization
+ */
+ public CalendarLink(String href, byte type, String label, boolean isPrimary) {
+ super(type, label, isPrimary);
+ setHRef(href);
+ }
+
+ private String href;
+
+ /**
+ * The URL of the calendar.
+ */
+ public String getHRef() {
+ return this.href;
+ }
+
+ /**
+ * The URL of the calendar.
+ */
+ public void setHRef(String href) {
+ this.href = href;
+ }
+
+
+ public void toString(StringBuffer sb) {
+ sb.append("CalendarLink");
+ super.toString(sb);
+ if (!StringUtils.isEmpty(href)) {
+ sb.append(" href:").append(href);
+ }
+ }
+}
diff --git a/src/com/google/wireless/gdata2/contacts/data/ContactEntry.java b/src/com/google/wireless/gdata2/contacts/data/ContactEntry.java
new file mode 100644
index 0000000..00d7b4c
--- /dev/null
+++ b/src/com/google/wireless/gdata2/contacts/data/ContactEntry.java
@@ -0,0 +1,720 @@
+// Copyright 2009 The Android Open Source Project
+
+package com.google.wireless.gdata2.contacts.data;
+
+import java.util.Enumeration;
+import java.util.Vector;
+
+import com.google.wireless.gdata2.data.Entry;
+import com.google.wireless.gdata2.data.ExtendedProperty;
+import com.google.wireless.gdata2.data.StringUtils;
+import com.google.wireless.gdata2.parser.ParseException;
+
+/**
+ * Entry containing information about a contact.
+ */
+public class ContactEntry extends Entry {
+ private String linkPhotoHref;
+ private String linkPhotoType;
+ private String linkPhotoEtag;
+ private final Vector emailAddresses = new Vector();
+ private final Vector imAddresses = new Vector();
+ private final Vector phoneNumbers = new Vector();
+ private final Vector postalAddresses = new Vector();
+ private final Vector organizations = new Vector();
+ private final Vector extendedProperties = new Vector();
+ private final Vector groups = new Vector();
+
+ // new collections in Contacts v3
+ private final Vector calendarLinks = new Vector();
+ private final Vector events = new Vector();
+ private final Vector externalIds = new Vector();
+ private final Vector hobbies = new Vector();
+ private final Vector jots = new Vector();
+ private final Vector languages = new Vector();
+ private final Vector relations = new Vector();
+ private final Vector userDefinedFields = new Vector();
+ private final Vector webSites = new Vector();
+
+ // new properties in contacts v3
+ private String directoryServer;
+ private String gender;
+ private String initials;
+ private String maidenName;
+ private String mileage;
+ private String nickname;
+ private String occupation;
+ private String shortName;
+ private String subject;
+ private String birthday;
+ private String billingInformation;
+
+ public static final String GENDER_MALE = "male";
+ public static final String GENDER_FEMALE = "female";
+ public static final byte TYPE_PRIORITY_HIGH = 1;
+ public static final byte TYPE_PRIORITY_NORMAL = 2;
+ public static final byte TYPE_PRIORITY_LOW = 3;
+ private byte priority = TypedElement.TYPE_NONE;
+
+ public static final byte TYPE_SENSITIVITY_CONFIDENTIAL = 1;
+ public static final byte TYPE_SENSITIVITY_NORMAL = 2;
+ public static final byte TYPE_SENSITIVITY_PERSONAL = 3;
+ public static final byte TYPE_SENSITIVITY_PRIVATE = 4;
+ private byte sensitivity = TypedElement.TYPE_NONE;
+
+ private Name name;
+
+ /**
+ * default empty constructor
+ */
+ public ContactEntry() {
+ super();
+ }
+
+ public void setLinkPhoto(String href, String type, String photoEtag) {
+ this.linkPhotoHref = href;
+ this.linkPhotoType = type;
+ this.linkPhotoEtag = photoEtag;
+ }
+
+ public String getLinkPhotoETag() {
+ return linkPhotoEtag;
+ }
+
+ public String getLinkPhotoHref() {
+ return linkPhotoHref;
+ }
+
+ public String getLinkPhotoType() {
+ return linkPhotoType;
+ }
+
+ public void addEmailAddress(EmailAddress emailAddress) {
+ emailAddresses.addElement(emailAddress);
+ }
+
+ public Vector getEmailAddresses() {
+ return emailAddresses;
+ }
+
+ public void addImAddress(ImAddress imAddress) {
+ imAddresses.addElement(imAddress);
+ }
+
+ public Vector getImAddresses() {
+ return imAddresses;
+ }
+
+ public void addPostalAddress(StructuredPostalAddress postalAddress) {
+ postalAddresses.addElement(postalAddress);
+ }
+
+ public Vector getPostalAddresses() {
+ return postalAddresses;
+ }
+
+ public void addPhoneNumber(PhoneNumber phoneNumber) {
+ phoneNumbers.addElement(phoneNumber);
+ }
+
+ public Vector getPhoneNumbers() {
+ return phoneNumbers;
+ }
+
+ public void addOrganization(Organization organization) {
+ organizations.addElement(organization);
+ }
+
+ public Vector getExtendedProperties() {
+ return extendedProperties;
+ }
+
+ public void addExtendedProperty(ExtendedProperty extendedProperty) {
+ extendedProperties.addElement(extendedProperty);
+ }
+
+ public Vector getGroups() {
+ return groups;
+ }
+
+ public void addGroup(GroupMembershipInfo group) {
+ groups.addElement(group);
+ }
+
+ public Vector getOrganizations() {
+ return organizations;
+ }
+
+ /**
+ * Accessor to the CalendarLink Collection
+ */
+ public Vector getCalendarLinks() {
+ return calendarLinks;
+ }
+
+ /**
+ * Adds a new member to the CalendarLink collection
+ */
+ public void addCalendarLink(CalendarLink calendarLink) {
+ calendarLinks.addElement(calendarLink);
+ }
+
+ /**
+ * Accessor to the Event Collection
+ */
+ public Vector getEvents() {
+ return events;
+ }
+
+ /**
+ * Adds a new member to the Event collection
+ */
+ public void addEvent(Event event) {
+ events.addElement(event);
+ }
+
+
+
+ /**
+ * Accessor to the ExternalId Collection
+ */
+ public Vector getExternalIds() {
+ return externalIds;
+ }
+
+ /**
+ * Adds a new member to the ExternalId collection
+ */
+ public void addExternalId(ExternalId externalId) {
+ externalIds.addElement(externalId);
+ }
+
+ /**
+ * Accessor to the Hobbies Collection
+ */
+ public Vector getHobbies() {
+ return hobbies;
+ }
+
+ /**
+ * Adds a new member to the Hobbies collection
+ */
+ public void addHobby(String hobby) {
+ hobbies.addElement(hobby);
+ }
+
+ /**
+ * Accessor to the Jots Collection
+ */
+ public Vector getJots() {
+ return jots;
+ }
+
+ /**
+ * Adds a new member to the Jot collection
+ */
+ public void addJot(Jot jot) {
+ jots.addElement(jot);
+ }
+
+ /**
+ * Accessor to the Language Collection
+ */
+ public Vector getLanguages() {
+ return languages;
+ }
+
+ /**
+ * Adds a new member to the Language collection
+ */
+ public void addLanguage(Language language) {
+ languages.addElement(language);
+ }
+
+ /**
+ * Accessor to the Relation Collection
+ */
+ public Vector getRelations() {
+ return relations;
+ }
+
+ /**
+ * Adds a new member to the Relation collection
+ */
+ public void addRelation(Relation relation) {
+ relations.addElement(relation);
+ }
+
+ /**
+ * Accessor to the UserDefinedField Collection
+ */
+ public Vector getUserDefinedFields() {
+ return userDefinedFields;
+ }
+
+ /**
+ * Adds a new member to the UserDefinedField collection
+ */
+ public void addUserDefinedField(UserDefinedField userDefinedField) {
+ userDefinedFields.addElement(userDefinedField);
+ }
+
+ /**
+ * Accessor to the WebSite Collection
+ */
+ public Vector getWebSites() {
+ return webSites;
+ }
+
+ /**
+ * Adds a new member to the WebSite collection
+ */
+ public void addWebSite(WebSite webSite) {
+ webSites.addElement(webSite);
+ }
+
+
+ /**
+ * Directory server associated with the contact
+ */
+ public String getDirectoryServer() {
+ return this.directoryServer;
+ }
+ /**
+ * Directory server associated with the contact
+ */
+ public void setDirectoryServer(String directoryServer) {
+ this.directoryServer = directoryServer;
+ }
+
+ /**
+ * Gender associated with the contact.
+ */
+ public String getGender() {
+ return this.gender;
+ }
+
+ /**
+ * Gender associated with the contact.
+ */
+ public void setGender(String gender) {
+ this.gender = gender;
+ }
+
+ /**
+ * Contact's initials.
+ */
+ public String getInitials() {
+ return this.initials;
+ }
+
+ /**
+ * Contact's initials.
+ */
+ public void setInitials(String initials) {
+ this.initials = initials;
+ }
+
+ /**
+ * Maiden name associated with the contact.
+ */
+ public String getMaidenName() {
+ return this.maidenName;
+ }
+
+ /**
+ * Maiden name associated with the contact.
+ */
+ public void setMaidenName(String maidenName) {
+ this.maidenName = maidenName;
+ }
+
+ /**
+ * Mileage associated with the contact.
+ */
+ public String getMileage() {
+ return this.mileage;
+ }
+
+ /**
+ * Mileage associated with the contact.
+ */
+ public void setMileage(String mileage) {
+ this.mileage = mileage;
+ }
+
+ /**
+ * Nickname associated with this Contact
+ */
+ public String getNickname() {
+ return this.nickname;
+ }
+
+ /**
+ * Nickname associated with this Contact
+ */
+ public void setNickname(String nickname) {
+ this.nickname = nickname;
+ }
+
+ /**
+ * Occupation associated with this Contact
+ */
+ public String getOccupation() {
+ return this.occupation;
+ }
+
+ /**
+ * Occupation associated with this Contact
+ */
+ public void setOccupation(String occupation) {
+ this.occupation = occupation;
+ }
+
+ /**
+ * Priority associated with this Contact
+ */
+ public byte getPriority() {
+ return this.priority;
+ }
+
+ /**
+ * Priority associated with this Contact
+ */
+ public void setPriority(byte priority) {
+ this.priority = priority;
+ }
+
+ /**
+ * Specifies contact's sensitivity. Can be either confidential,
+ * normal, personal or private.
+ */
+ public byte getSensitivity() {
+ return this.sensitivity;
+ }
+
+ /**
+ * Specifies contact's sensitivity. Can be either confidential,
+ * normal, personal or private.
+ */
+ public void setSensitivity(byte sensitiviy) {
+ this.sensitivity = sensitiviy;
+ }
+
+ /**
+ * ShortName associated with this Contact
+ */
+ public String getShortName() {
+ return this.shortName;
+ }
+
+ /**
+ * ShortName associated with this Contact
+ */
+ public void setShortName(String shortName) {
+ this.shortName = shortName;
+ }
+
+ /**
+ * Subject associated with this Contact
+ */
+ public String getSubject() {
+ return this.subject;
+ }
+
+ /**
+ * Subject associated with this Contact
+ */
+ public void setSubject(String subject) {
+ this.subject = subject;
+ }
+
+ /**
+ * Name associated with this Contact
+ */
+ public Name getName() {
+ return this.name;
+ }
+
+ /**
+ * Name associated with this Contact
+ */
+ public void setName(Name name) {
+ this.name = name;
+ }
+
+ /**
+ * Birthday associated with this Contact
+ */
+ public String getBirthday() {
+ return this.birthday;
+ }
+
+ /**
+ * Birthday associated with this Contact
+ */
+ public void setBirthday(String birthday) {
+ this.birthday = birthday;
+ }
+
+ /**
+ * BillingInformation associated with this Contact
+ */
+ public String getBillingInformation() {
+ return this.billingInformation;
+ }
+
+ /**
+ * BillingInformation associated with this Contact
+ */
+ public void setBillingInformation(String billingInformation) {
+ this.billingInformation = billingInformation;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.google.wireless.gdata2.data.Entry#clear()
+ */
+ public void clear() {
+ super.clear();
+ linkPhotoHref = null;
+ linkPhotoType = null;
+ linkPhotoEtag = null;
+ directoryServer = null;
+ gender = null;
+ initials = null;
+ maidenName = null;
+ mileage = null;
+ nickname = null;
+ occupation = null;
+ priority = TypedElement.TYPE_NONE;
+ sensitivity = TypedElement.TYPE_NONE;
+ shortName = null;
+ subject = null;
+ birthday = null;
+ billingInformation = null;
+ name = null;
+ emailAddresses.removeAllElements();
+ imAddresses.removeAllElements();
+ phoneNumbers.removeAllElements();
+ postalAddresses.removeAllElements();
+ organizations.removeAllElements();
+ extendedProperties.removeAllElements();
+ groups.removeAllElements();
+ calendarLinks.removeAllElements();
+ events.removeAllElements();
+ externalIds.removeAllElements();
+ hobbies.removeAllElements();
+ jots.removeAllElements();
+ languages.removeAllElements();
+ relations.removeAllElements();
+ userDefinedFields.removeAllElements();
+ webSites.removeAllElements();
+
+ }
+
+ protected void toString(StringBuffer sb) {
+ super.toString(sb);
+ sb.append("\n");
+ sb.append("ContactEntry:");
+ if (!StringUtils.isEmpty(linkPhotoHref)) {
+ sb.append(" linkPhotoHref:").append(linkPhotoHref);
+ }
+ if (!StringUtils.isEmpty(linkPhotoType)) {
+ sb.append(" linkPhotoType:").append(linkPhotoType);
+ }
+ if (!StringUtils.isEmpty(linkPhotoEtag)) {
+ sb.append(" linkPhotoEtag:").append(linkPhotoEtag);
+ }
+ if (!StringUtils.isEmpty(directoryServer)) {
+ sb.append(" directoryServer:").append(directoryServer);
+ }
+ if (!StringUtils.isEmpty(gender)) {
+ sb.append(" gender:").append(gender);
+ }
+ if (!StringUtils.isEmpty(initials)) {
+ sb.append(" initials:").append(initials);
+ }
+ if (!StringUtils.isEmpty(maidenName)) {
+ sb.append(" maidenName:").append(maidenName);
+ }
+ if (!StringUtils.isEmpty(mileage)) {
+ sb.append(" mileage:").append(mileage);
+ }
+ if (!StringUtils.isEmpty(nickname)) {
+ sb.append(" nickname:").append(nickname);
+ }
+ if (!StringUtils.isEmpty(occupation)) {
+ sb.append(" occupaton:").append(occupation);
+ }
+ sb.append(" priority:").append(priority);
+
+ sb.append(" sensitivity:").append(sensitivity);
+
+ if (!StringUtils.isEmpty(shortName)) {
+ sb.append(" shortName:").append(shortName);
+ }
+ if (!StringUtils.isEmpty(subject)) {
+ sb.append(" subject:").append(subject);
+ }
+ if (!StringUtils.isEmpty(birthday)) {
+ sb.append(" birthday:").append(birthday);
+ }
+ if (!StringUtils.isEmpty(billingInformation)) {
+ sb.append(" billingInformation:").append(billingInformation);
+ }
+ sb.append("\n");
+ if (name != null) {
+ name.toString(sb);
+ sb.append("\n");
+ }
+ for (Enumeration iter = emailAddresses.elements();
+ iter.hasMoreElements(); ) {
+ sb.append(" ");
+ ((EmailAddress) iter.nextElement()).toString(sb);
+ sb.append("\n");
+ }
+ for (Enumeration iter = imAddresses.elements();
+ iter.hasMoreElements(); ) {
+ sb.append(" ");
+ ((ImAddress) iter.nextElement()).toString(sb);
+ sb.append("\n");
+ }
+ for (Enumeration iter = postalAddresses.elements();
+ iter.hasMoreElements(); ) {
+ sb.append(" ");
+ ((StructuredPostalAddress) iter.nextElement()).toString(sb);
+ sb.append("\n");
+ }
+ for (Enumeration iter = phoneNumbers.elements();
+ iter.hasMoreElements(); ) {
+ sb.append(" ");
+ ((PhoneNumber) iter.nextElement()).toString(sb);
+ sb.append("\n");
+ }
+ for (Enumeration iter = organizations.elements();
+ iter.hasMoreElements(); ) {
+ sb.append(" ");
+ ((Organization) iter.nextElement()).toString(sb);
+ sb.append("\n");
+ }
+ for (Enumeration iter = extendedProperties.elements();
+ iter.hasMoreElements(); ) {
+ sb.append(" ");
+ ((ExtendedProperty) iter.nextElement()).toString(sb);
+ sb.append("\n");
+ }
+ for (Enumeration iter = groups.elements();
+ iter.hasMoreElements(); ) {
+ sb.append(" ");
+ ((GroupMembershipInfo) iter.nextElement()).toString(sb);
+ sb.append("\n");
+ }
+ for (Enumeration iter = calendarLinks.elements();
+ iter.hasMoreElements(); ) {
+ sb.append(" ");
+ ((CalendarLink) iter.nextElement()).toString(sb);
+ sb.append("\n");
+ }
+ for (Enumeration iter = events.elements();
+ iter.hasMoreElements(); ) {
+ sb.append(" ");
+ ((Event) iter.nextElement()).toString(sb);
+ sb.append("\n");
+ }
+ for (Enumeration iter = externalIds.elements();
+ iter.hasMoreElements(); ) {
+ sb.append(" ");
+ ((ExternalId) iter.nextElement()).toString(sb);
+ sb.append("\n");
+ }
+ for (Enumeration iter = hobbies.elements();
+ iter.hasMoreElements(); ) {
+ sb.append(" ");
+ sb.append ((String) iter.nextElement());
+ sb.append("\n");
+ }
+ for (Enumeration iter = jots.elements();
+ iter.hasMoreElements(); ) {
+ sb.append(" ");
+ sb.append ((Jot) iter.nextElement());
+ sb.append("\n");
+ }
+ for (Enumeration iter = languages.elements();
+ iter.hasMoreElements(); ) {
+ sb.append(" ");
+ ((Language) iter.nextElement()).toString(sb);
+ sb.append("\n");
+ }
+ for (Enumeration iter = relations.elements();
+ iter.hasMoreElements(); ) {
+ sb.append(" ");
+ ((Relation) iter.nextElement()).toString(sb);
+ sb.append("\n");
+ }
+ for (Enumeration iter = userDefinedFields.elements();
+ iter.hasMoreElements(); ) {
+ sb.append(" ");
+ ((UserDefinedField) iter.nextElement()).toString(sb);
+ sb.append("\n");
+ }
+ for (Enumeration iter = webSites.elements();
+ iter.hasMoreElements(); ) {
+ sb.append(" ");
+ ((WebSite) iter.nextElement()).toString(sb);
+ sb.append("\n");
+ }
+ }
+
+ public void validate() throws ParseException {
+ super.validate();
+ if (gender != null && !GENDER_FEMALE.equals(gender) && !GENDER_MALE.equals(gender)) {
+ throw new ParseException(
+ String.format("invalid gender \"%s\", must be one of \"%s\" or \"%s\"",
+ gender, GENDER_FEMALE, GENDER_MALE));
+ }
+ for (Enumeration iter = emailAddresses.elements(); iter.hasMoreElements(); ) {
+ ((EmailAddress) iter.nextElement()).validate();
+ }
+ for (Enumeration iter = imAddresses.elements(); iter.hasMoreElements(); ) {
+ ((ImAddress) iter.nextElement()).validate();
+ }
+ for (Enumeration iter = postalAddresses.elements(); iter.hasMoreElements(); ) {
+ ((StructuredPostalAddress) iter.nextElement()).validate();
+ }
+ for (Enumeration iter = phoneNumbers.elements(); iter.hasMoreElements(); ) {
+ ((PhoneNumber) iter.nextElement()).validate();
+ }
+ for (Enumeration iter = organizations.elements(); iter.hasMoreElements(); ) {
+ ((Organization) iter.nextElement()).validate();
+ }
+ for (Enumeration iter = extendedProperties.elements(); iter.hasMoreElements(); ) {
+ ((ExtendedProperty) iter.nextElement()).validate();
+ }
+ for (Enumeration iter = groups.elements(); iter.hasMoreElements(); ) {
+ ((GroupMembershipInfo) iter.nextElement()).validate();
+ }
+
+ for (Enumeration iter = calendarLinks.elements(); iter.hasMoreElements(); ) {
+ ((CalendarLink) iter.nextElement()).validate();
+ }
+ for (Enumeration iter = events.elements(); iter.hasMoreElements(); ) {
+ ((Event) iter.nextElement()).validate();
+ }
+ for (Enumeration iter = externalIds.elements(); iter.hasMoreElements(); ) {
+ ((ExternalId) iter.nextElement()).validate();
+ }
+ for (Enumeration iter = languages.elements(); iter.hasMoreElements(); ) {
+ ((Language) iter.nextElement()).validate();
+ }
+ for (Enumeration iter = relations.elements(); iter.hasMoreElements(); ) {
+ ((Relation) iter.nextElement()).validate();
+ }
+ for (Enumeration iter = userDefinedFields.elements(); iter.hasMoreElements(); ) {
+ ((UserDefinedField) iter.nextElement()).validate();
+ }
+ for (Enumeration iter = webSites.elements(); iter.hasMoreElements(); ) {
+ ((WebSite) iter.nextElement()).validate();
+ }
+ }
+}
diff --git a/src/com/google/wireless/gdata2/contacts/data/ContactsElement.java b/src/com/google/wireless/gdata2/contacts/data/ContactsElement.java
new file mode 100644
index 0000000..d8e23cc
--- /dev/null
+++ b/src/com/google/wireless/gdata2/contacts/data/ContactsElement.java
@@ -0,0 +1,33 @@
+/**
+ * Copyright (C) 2009 The Android Open Source Project
+ */
+package com.google.wireless.gdata2.contacts.data;
+
+import com.google.wireless.gdata2.parser.ParseException;
+
+
+/**
+ * Contains attributes that are common to all elements in a ContactEntry.
+ */
+public abstract class ContactsElement extends TypedElement {
+ private boolean isPrimary;
+
+ public ContactsElement() {}
+ public ContactsElement(byte type, String label, boolean isPrimary) {
+ super(type, label);
+ this.isPrimary = isPrimary;
+ }
+
+ public boolean isPrimary() {
+ return isPrimary;
+ }
+
+ public void setIsPrimary(boolean primary) {
+ isPrimary = primary;
+ }
+
+ public void toString(StringBuffer sb) {
+ super.toString(sb);
+ sb.append(" isPrimary:").append(isPrimary);
+ }
+}
diff --git a/src/com/google/wireless/gdata2/contacts/data/ContactsFeed.java b/src/com/google/wireless/gdata2/contacts/data/ContactsFeed.java
new file mode 100644
index 0000000..1e879f0
--- /dev/null
+++ b/src/com/google/wireless/gdata2/contacts/data/ContactsFeed.java
@@ -0,0 +1,16 @@
+// Copyright 2007 The Android Open Source Project
+
+package com.google.wireless.gdata2.contacts.data;
+
+import com.google.wireless.gdata2.data.Feed;
+
+/**
+ * Feed containing contacts.
+ */
+public class ContactsFeed extends Feed {
+ /**
+ * Creates a new empty events feed.
+ */
+ public ContactsFeed() {
+ }
+}
diff --git a/src/com/google/wireless/gdata2/contacts/data/EmailAddress.java b/src/com/google/wireless/gdata2/contacts/data/EmailAddress.java
new file mode 100644
index 0000000..bd5fed1
--- /dev/null
+++ b/src/com/google/wireless/gdata2/contacts/data/EmailAddress.java
@@ -0,0 +1,55 @@
+// Copyright 2007 The Android Open Source Project
+
+package com.google.wireless.gdata2.contacts.data;
+
+/**
+ * The EmailAddress GData type.
+ */
+public class EmailAddress extends ContactsElement {
+ public static final byte TYPE_HOME = 1;
+ public static final byte TYPE_WORK = 2;
+ public static final byte TYPE_OTHER = 3;
+
+ private String address;
+ private String displayName;
+
+ /**
+ * default empty constructor
+ */
+ public EmailAddress() {}
+ public EmailAddress(String address, String displayName,
+ byte type, String label, boolean isPrimary) {
+ super(type, label, isPrimary);
+ this.address = address;
+ this.displayName = displayName;
+ }
+
+ public String getAddress() {
+ return address;
+ }
+
+ public void setAddress(String address) {
+ this.address = address;
+ }
+
+ /**
+ * Getter for displayName
+ */
+ public String getDisplayName() {
+ return this.displayName;
+ }
+
+ /**
+ * Setter for displayName
+ */
+ public void setDisplayName(String displayName) {
+ this.displayName = displayName;
+ }
+
+ public void toString(StringBuffer sb) {
+ sb.append("EmailAddress");
+ super.toString(sb);
+ if (address != null) sb.append(" address:").append(address);
+ if (displayName != null) sb.append(" displayName:").append(displayName);
+ }
+}
diff --git a/src/com/google/wireless/gdata2/contacts/data/Event.java b/src/com/google/wireless/gdata2/contacts/data/Event.java
new file mode 100644
index 0000000..7bac378
--- /dev/null
+++ b/src/com/google/wireless/gdata2/contacts/data/Event.java
@@ -0,0 +1,55 @@
+/**
+ * Copyright (C) 2009 The Android Open Source Project
+ */
+
+package com.google.wireless.gdata2.contacts.data;
+
+import java.util.Date;
+
+import com.google.wireless.gdata2.data.StringUtils;
+import com.google.wireless.gdata2.parser.ParseException;
+
+
+/**
+ * These elements describe events associated with a contact.
+ * They may be repeated.
+ */
+public class Event extends TypedElement {
+ public static final byte TYPE_ANNIVERSARY = 1;
+ public static final byte TYPE_OTHER = 2;
+
+ private String startDate;
+
+ /**
+ * default empty constructor
+ */
+ public Event() {}
+ public Event(String startDate, byte type, String label) {
+ super(type, label);
+ this.startDate = startDate;
+ }
+
+ /**
+ * StartDate associated with this event
+ */
+ public String getStartDate() {
+ return this.startDate;
+ }
+
+ /**
+ * StartDate associated with this event
+ */
+ public void setStartDate(String startDate) {
+ this.startDate = startDate;
+ }
+
+ public void toString(StringBuffer sb) {
+ sb.append("Event");
+ super.toString(sb);
+ sb.append(" date:").append(startDate.toString());
+ }
+}
+
+
+
+
diff --git a/src/com/google/wireless/gdata2/contacts/data/ExternalId.java b/src/com/google/wireless/gdata2/contacts/data/ExternalId.java
new file mode 100644
index 0000000..f0c1bc2
--- /dev/null
+++ b/src/com/google/wireless/gdata2/contacts/data/ExternalId.java
@@ -0,0 +1,67 @@
+/**
+ * Copyright (C) 2009 The Android Open Source Project
+ */
+
+package com.google.wireless.gdata2.contacts.data;
+
+import com.google.wireless.gdata2.parser.ParseException;
+import com.google.wireless.gdata2.data.StringUtils;
+
+/**
+ * Describes an ID of the contact in an external system of some
+ * kind. This element may be repeated..
+ */
+public class ExternalId extends TypedElement {
+ public static final byte TYPE_ACCOUNT = 1;
+ public static final byte TYPE_CUSTOMER = 2;
+ public static final byte TYPE_NETWORK = 3;
+ public static final byte TYPE_ORGANIZATION = 4;
+
+
+ private String value;
+
+ /**
+ * default empty constructor
+ */
+ public ExternalId() {}
+
+ /**
+ * constructor that allows initialization
+ */
+ public ExternalId(String value, byte type, String label) {
+ super(type, label);
+ setValue(value);
+ }
+
+ /**
+ * The value of this external ID.
+ */
+ public String getValue() {
+ return this.value;
+ }
+
+ /**
+ * The value of this external ID.
+ */
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public void toString(StringBuffer sb) {
+ sb.append("ExternalId");
+ super.toString(sb);
+ if (!StringUtils.isEmpty(value)) {
+ sb.append(" value:").append(value);
+ }
+ }
+
+ /**
+ * override default behaviour, an externalId has its own rules for type and label
+ */
+ public void validate() throws ParseException {
+ if (value == null) {
+ throw new ParseException("the value must be set");
+ }
+ }
+}
+
diff --git a/src/com/google/wireless/gdata2/contacts/data/GeoPt.java b/src/com/google/wireless/gdata2/contacts/data/GeoPt.java
new file mode 100644
index 0000000..cb509d3
--- /dev/null
+++ b/src/com/google/wireless/gdata2/contacts/data/GeoPt.java
@@ -0,0 +1,76 @@
+// Copyright 2009 The Android Open Source Project
+
+package com.google.wireless.gdata2.contacts.data;
+
+/**
+ * The GeoPt GData type.
+ */
+public class GeoPt {
+ private String label;
+ private Float latitude;
+ private Float longitude;
+ private Float elevation;
+
+ /**
+ * default empty constructor
+ */
+ public GeoPt() {}
+
+ // TODO: figure out how to store the GeoPt time
+ private String time;
+
+ public String getLabel() {
+ return label;
+ }
+
+ public void setLabel(String label) {
+ this.label = label;
+ }
+
+ public Float getLatitute() {
+ return latitude;
+ }
+
+ public void setLatitude(Float lat) {
+ this.latitude = lat;
+ }
+
+ public Float getLongitute() {
+ return longitude;
+ }
+
+ public void setLongitude(Float lon) {
+ this.longitude = lon;
+ }
+
+ public Float getElevation() {
+ return elevation;
+ }
+
+ public void setElevation(Float elev) {
+ this.elevation = elev;
+ }
+
+ public String getTime() {
+ return time;
+ }
+
+ public void setTime(String time) {
+ this.time = time;
+ }
+
+ public void toString(StringBuffer sb) {
+ sb.append("GeoPt");
+ if (latitude != null) sb.append(" latitude:").append(latitude);
+ if (longitude != null) sb.append(" longitude:").append(longitude);
+ if (elevation != null) sb.append(" elevation:").append(elevation);
+ if (time != null) sb.append(" time:").append(time);
+ if (label != null) sb.append(" label:").append(label);
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ toString(sb);
+ return sb.toString();
+ }
+}
diff --git a/src/com/google/wireless/gdata2/contacts/data/GroupEntry.java b/src/com/google/wireless/gdata2/contacts/data/GroupEntry.java
new file mode 100644
index 0000000..09b0ccc
--- /dev/null
+++ b/src/com/google/wireless/gdata2/contacts/data/GroupEntry.java
@@ -0,0 +1,40 @@
+// Copyright 2009 The Android Open Source Project
+
+package com.google.wireless.gdata2.contacts.data;
+
+import com.google.wireless.gdata2.data.Entry;
+import com.google.wireless.gdata2.data.StringUtils;
+
+/**
+ * Entry containing information about a contact group.
+ */
+public class GroupEntry extends Entry {
+ // If this is a system group then this field will be set with the name of the system group.
+ private String systemGroup = null;
+
+ public GroupEntry() {
+ super();
+ }
+
+ public String getSystemGroup() {
+ return systemGroup;
+ }
+
+ public void clear() {
+ super.clear();
+ systemGroup = null;
+ }
+
+ public void setSystemGroup(String systemGroup) {
+ this.systemGroup = systemGroup;
+ }
+
+ protected void toString(StringBuffer sb) {
+ super.toString(sb);
+ sb.append("\n");
+ sb.append("GroupEntry:");
+ if (!StringUtils.isEmpty(systemGroup)) {
+ sb.append(" systemGroup:").append(systemGroup).append("\n");
+ }
+ }
+}
diff --git a/src/com/google/wireless/gdata2/contacts/data/GroupMembershipInfo.java b/src/com/google/wireless/gdata2/contacts/data/GroupMembershipInfo.java
new file mode 100644
index 0000000..d328773
--- /dev/null
+++ b/src/com/google/wireless/gdata2/contacts/data/GroupMembershipInfo.java
@@ -0,0 +1,53 @@
+// Copyright 2009 The Android Open Source Project
+
+package com.google.wireless.gdata2.contacts.data;
+
+import com.google.wireless.gdata2.data.StringUtils;
+import com.google.wireless.gdata2.parser.ParseException;
+
+/** The groupMembershipInfo GData type. */
+public class GroupMembershipInfo {
+ private String group;
+ private boolean deleted;
+
+ /**
+ * default empty constructor
+ */
+ public GroupMembershipInfo() {}
+
+ /**
+ * constructor that allows initializing the GroupMembershipInfo
+ */
+ public GroupMembershipInfo(String groupId, boolean deleted) {
+ setGroup(groupId);
+ setDeleted(deleted);
+ }
+
+ public String getGroup() {
+ return group;
+ }
+
+ public void setGroup(String group) {
+ this.group = group;
+ }
+
+ public boolean isDeleted() {
+ return deleted;
+ }
+
+ public void setDeleted(boolean deleted) {
+ this.deleted = deleted;
+ }
+
+ public void toString(StringBuffer sb) {
+ sb.append("GroupMembershipInfo");
+ if (group != null) sb.append(" group:").append(group);
+ sb.append(" deleted:").append(deleted);
+ }
+
+ public void validate() throws ParseException {
+ if (StringUtils.isEmpty(group)) {
+ throw new ParseException("the group must be present");
+ }
+ }
+}
diff --git a/src/com/google/wireless/gdata2/contacts/data/GroupsFeed.java b/src/com/google/wireless/gdata2/contacts/data/GroupsFeed.java
new file mode 100644
index 0000000..6f6511d
--- /dev/null
+++ b/src/com/google/wireless/gdata2/contacts/data/GroupsFeed.java
@@ -0,0 +1,16 @@
+// Copyright 2009 The Android Open Source Project
+
+package com.google.wireless.gdata2.contacts.data;
+
+import com.google.wireless.gdata2.data.Feed;
+
+/**
+ * Feed containing contact groups.
+ */
+public class GroupsFeed extends Feed {
+ /**
+ * Creates a new empty contact groups feed.
+ */
+ public GroupsFeed() {
+ }
+}
diff --git a/src/com/google/wireless/gdata2/contacts/data/ImAddress.java b/src/com/google/wireless/gdata2/contacts/data/ImAddress.java
new file mode 100644
index 0000000..3f4c86c
--- /dev/null
+++ b/src/com/google/wireless/gdata2/contacts/data/ImAddress.java
@@ -0,0 +1,72 @@
+// Copyright 2007 The Android Open Source Project
+
+package com.google.wireless.gdata2.contacts.data;
+
+/**
+ * The ImAddress gdata type
+ */
+public class ImAddress extends ContactsElement {
+ public static final byte TYPE_HOME = 1;
+ public static final byte TYPE_WORK = 2;
+ public static final byte TYPE_OTHER = 3;
+
+ public static final byte PROTOCOL_CUSTOM = 1;
+ public static final byte PROTOCOL_AIM = 2;
+ public static final byte PROTOCOL_MSN = 3;
+ public static final byte PROTOCOL_YAHOO = 4;
+ public static final byte PROTOCOL_SKYPE = 5;
+ public static final byte PROTOCOL_QQ = 6;
+ public static final byte PROTOCOL_GOOGLE_TALK = 7;
+ public static final byte PROTOCOL_ICQ = 8;
+ public static final byte PROTOCOL_JABBER = 9;
+ public static final byte PROTOCOL_NETMEETING = 10;
+ public static final byte PROTOCOL_NONE = 11;
+
+ private byte protocolPredefined;
+ private String protocolCustom;
+ private String address;
+
+ /**
+ * default empty constructor
+ */
+ public ImAddress() {}
+ public ImAddress(String address, byte protocolPredefined, String protocolCustom,
+ byte type, String label, boolean isPrimary) {
+ super(type, label, isPrimary);
+ this.address = address;
+ this.protocolPredefined = protocolPredefined;
+ this.protocolCustom = protocolCustom;
+ }
+
+ public byte getProtocolPredefined() {
+ return protocolPredefined;
+ }
+
+ public void setProtocolPredefined(byte protocolPredefined) {
+ this.protocolPredefined = protocolPredefined;
+ }
+
+ public String getProtocolCustom() {
+ return protocolCustom;
+ }
+
+ public void setProtocolCustom(String protocolCustom) {
+ this.protocolCustom = protocolCustom;
+ }
+
+ public String getAddress() {
+ return address;
+ }
+
+ public void setAddress(String address) {
+ this.address = address;
+ }
+
+ public void toString(StringBuffer sb) {
+ sb.append("ImAddress");
+ super.toString(sb);
+ sb.append(" protocolPredefined:").append(protocolPredefined);
+ if (protocolCustom != null) sb.append(" protocolCustom:").append(protocolCustom);
+ if (address != null) sb.append(" address:").append(address);
+ }
+}
diff --git a/src/com/google/wireless/gdata2/contacts/data/Jot.java b/src/com/google/wireless/gdata2/contacts/data/Jot.java
new file mode 100644
index 0000000..81a92b9
--- /dev/null
+++ b/src/com/google/wireless/gdata2/contacts/data/Jot.java
@@ -0,0 +1,64 @@
+/**
+ * Copyright (C) 2009 The Android Open Source Project
+ */
+package com.google.wireless.gdata2.contacts.data;
+
+import com.google.wireless.gdata2.parser.ParseException;
+import com.google.wireless.gdata2.data.StringUtils;
+
+
+/**
+ * Storage for arbitrary pieces of information about the
+ * contact. Each jot has a type specified by the rel attribute
+ * and a text value. The element can be repeated.
+ */
+public class Jot extends TypedElement {
+ public static final byte TYPE_HOME = 1;
+ public static final byte TYPE_WORK = 2;
+ public static final byte TYPE_KEYWORDS = 3;
+ public static final byte TYPE_USER = 4;
+ public static final byte TYPE_OTHER = 5;
+
+ private String value;
+
+ /**
+ * default empty constructor
+ */
+ public Jot() {}
+
+ /**
+ * constructor that allows initialization
+ */
+ public Jot(String value, byte type, String label) {
+ super(type, label);
+ setValue(value);
+ }
+
+ /**
+ * override default behaviour, a jot is not relying on either
+ * label or type
+ */
+ public void validate() throws ParseException {}
+
+ /**
+ * The value of this Jot
+ */
+ public String getValue() {
+ return this.value;
+ }
+
+ /**
+ * The value of this Jot.
+ */
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public void toString(StringBuffer sb) {
+ sb.append("Jot");
+ super.toString(sb);
+ if (!StringUtils.isEmpty(value)) {
+ sb.append(" value:").append(value);
+ }
+ }
+}
diff --git a/src/com/google/wireless/gdata2/contacts/data/Language.java b/src/com/google/wireless/gdata2/contacts/data/Language.java
new file mode 100644
index 0000000..0ca6ae3
--- /dev/null
+++ b/src/com/google/wireless/gdata2/contacts/data/Language.java
@@ -0,0 +1,103 @@
+/**
+ * Copyright (C) 2009 The Android Open Source Project
+ */
+package com.google.wireless.gdata2.contacts.data;
+
+import com.google.wireless.gdata2.parser.ParseException;
+import com.google.wireless.gdata2.data.StringUtils;
+
+
+/**
+ * Specifies the preferred languages of the contact. The element
+ * can be repeated. The language must be specified using one of
+ * two mutually exclusive methods: using the freeform label
+ * attribute, or using the code attribute, whose value must
+ * conform to the IETF BCP 47 specification. Describes an ID of
+ * the contact in an external system of some kind. This element
+ * may be repeated.
+ */
+public class Language {
+
+ private String label;
+ private String code;
+
+ /**
+ * default empty constructor
+ */
+ public Language() {}
+
+ /**
+ * constructor that allows initialization
+ */
+ public Language(String label, String code) {
+ setLabel(label);
+ setCode(code);
+ }
+
+ /**
+ * A freeform name of a language. Must not be empty or all
+ * whitespace.
+ */
+ public String getLabel() {
+ return this.label;
+ }
+
+ /**
+ * A freeform name of a language. Must not be empty or all
+ * whitespace.
+ */
+ public void setLabel(String label) {
+ this.label = label;
+ }
+
+ /**
+ * A language code conforming to the IETF BCP 47 specification.
+ * The server returns an error if a nonconformant value is
+ * provided.
+ */
+ public String getCode() {
+ return this.code;
+ }
+
+ /**
+ * A language code conforming to the IETF BCP 47 specification.
+ * The server returns an error if a nonconformant value is
+ * provided.
+ */
+ public void setCode(String code) {
+ this.code = code;
+ }
+
+
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ toString(sb);
+ return sb.toString();
+ }
+
+
+ public void toString(StringBuffer sb) {
+ sb.append("Language");
+ if (!StringUtils.isEmpty(code)) {
+ sb.append(" code:").append(code);
+ }
+ if (!StringUtils.isEmpty(label)) {
+ sb.append(" label:").append(label);
+ }
+ }
+
+ /**
+ * A Language either has a code or a label, not both
+ */
+ public void validate() throws ParseException {
+ if ((StringUtils.isEmpty(label) &&
+ StringUtils.isEmpty(code)) ||
+ (!StringUtils.isEmpty(label) &&
+ !StringUtils.isEmpty(code))) {
+ throw new ParseException("exactly one of label or code must be set");
+ }
+ }
+}
+
+
diff --git a/src/com/google/wireless/gdata2/contacts/data/Name.java b/src/com/google/wireless/gdata2/contacts/data/Name.java
new file mode 100644
index 0000000..37ece2a
--- /dev/null
+++ b/src/com/google/wireless/gdata2/contacts/data/Name.java
@@ -0,0 +1,167 @@
+// Copyright 2009 The Android Open Source Project
+
+package com.google.wireless.gdata2.contacts.data;
+
+/**
+ * Allows storing person's name in a structured way. Consists of
+ * given name, additional name, family name, prefix, suffix and
+ * full name.
+ */
+public class Name {
+
+ private String fullName;
+ private String nameSuffix;
+ private String namePrefix;
+ private String familyName;
+ private String additionalName;
+ private String givenName;
+ private String givenNameYomi;
+ private String familyNameYomi;
+ private String additionalNameYomi;
+
+ /**
+ * default empty constructor
+ */
+ public Name() {}
+
+ /**
+ * Getter for givenName, Person's given name.
+ */
+ public String getGivenName() {
+ return this.givenName;
+ }
+
+ /**
+ * Setter for givenName, Person's given name.
+ */
+ public void setGivenName(String givenName) {
+ this.givenName = givenName;
+ }
+
+ /**
+ * Getter for addtionalName, Additional name of the person, eg.
+ * middle name.
+ */
+ public String getAdditionalName() {
+ return this.additionalName;
+ }
+
+ /**
+ * Setter for addtionalName, Additional name of the person, eg.
+ * middle name.
+ */
+ public void setAdditionalName(String addtionalName) {
+ this.additionalName = addtionalName;
+ }
+
+ /**
+ * Getter for familyName, Person's family name.
+ */
+ public String getFamilyName() {
+ return this.familyName;
+ }
+
+ /**
+ * Setter for familyName, Person's family name.
+ */
+ public void setFamilyName(String familyName) {
+ this.familyName = familyName;
+ }
+
+ /**
+ * Getter for namePrefix, Honorific prefix, eg. 'Mr' or 'Mrs'.
+ */
+ public String getNamePrefix() {
+ return this.namePrefix;
+ }
+
+ /**
+ * Setter for namePrefix, Honorific prefix, eg. 'Mr' or 'Mrs'.
+ */
+ public void setNamePrefix(String namePrefix) {
+ this.namePrefix = namePrefix;
+ }
+
+ /**
+ * Getter for nameSuffix, Honorific suffix, eg. 'san' or 'III'.
+ */
+ public String getNameSuffix() {
+ return this.nameSuffix;
+ }
+
+ /**
+ * Setter for nameSuffix, Honorific suffix, eg. 'san' or 'III'.
+ */
+ public void setNameSuffix(String nameSuffix) {
+ this.nameSuffix = nameSuffix;
+ }
+
+ /**
+ * Getter for fullName, Unstructured representation of the name.
+ */
+ public String getFullName() {
+ return this.fullName;
+ }
+
+ /**
+ * Setter for fullName, Unstructured representation of the name.
+ */
+ public void setFullName(String fullName) {
+ this.fullName = fullName;
+ }
+
+ /**
+ * Getter for addtionalNameYomi, Phonetic representation
+ */
+ public String getAdditionalNameYomi() {
+ return this.additionalNameYomi;
+ }
+
+ /**
+ * Setter for addtionalNameYomi, Phonetic representation
+ */
+ public void setAdditionalNameYomi(String addtionalNameYomi) {
+ this.additionalNameYomi = addtionalNameYomi;
+ }
+
+ /**
+ * Getter for familyNameYomi, Phonetic representation
+ */
+ public String getFamilyNameYomi() {
+ return this.familyNameYomi;
+ }
+
+ /**
+ * Setter for familyNameYomi, Phonetic representation
+ */
+ public void setFamilyNameYomi(String familyNameYomi) {
+ this.familyNameYomi = familyNameYomi;
+ }
+
+ /**
+ * Getter for givenNameYomi, Phonetic representation
+ */
+ public String getGivenNameYomi() {
+ return this.givenNameYomi;
+ }
+
+ /**
+ * Setter for givenNameYomi, Phonetic representation
+ */
+ public void setGivenNameYomi(String givenNameYomi) {
+ this.givenNameYomi = givenNameYomi;
+ }
+
+ public void toString(StringBuffer sb) {
+ sb.append("Name");
+ if (fullName != null) sb.append(" fullName:").append(fullName);
+ if (nameSuffix != null) sb.append(" nameSuffix:").append(nameSuffix);
+ if (namePrefix != null) sb.append(" namePrefix:").append(namePrefix);
+ if (familyName != null) sb.append(" familyName:").append(familyName);
+ if (additionalName != null) sb.append(" additionalName:").append(additionalName);
+ if (givenName != null) sb.append(" givenName:").append(givenName);
+ if (givenNameYomi != null) sb.append(" givenNameYomi:").append(givenNameYomi);
+ if (familyNameYomi != null) sb.append(" familyNameYomi:").append(familyNameYomi);
+ if (additionalNameYomi != null) sb.append(" additionalNameYomi:").append(additionalNameYomi);
+ }
+}
diff --git a/src/com/google/wireless/gdata2/contacts/data/Organization.java b/src/com/google/wireless/gdata2/contacts/data/Organization.java
new file mode 100644
index 0000000..ca11252
--- /dev/null
+++ b/src/com/google/wireless/gdata2/contacts/data/Organization.java
@@ -0,0 +1,135 @@
+/**
+ * Copyright (C) 2009 The Android Open Source Project
+ */
+
+package com.google.wireless.gdata2.contacts.data;
+
+/** The Organization GData type. */
+public class Organization extends ContactsElement {
+ public static final byte TYPE_WORK = 1;
+ public static final byte TYPE_OTHER = 2;
+
+ private String name;
+ private String nameYomi;
+ private String title;
+ private String orgDepartment;
+ private String orgJobDescription;
+ private String orgSymbol;
+ private String where;
+
+ /**
+ * default empty constructor
+ */
+ public Organization() {}
+ public Organization(String name, String nameYomi, String title, String orgDepartment,
+ String orgJobDescription, String orgSymbol, String where,
+ byte type, String label, boolean isPrimary) {
+ super(type, label, isPrimary);
+ this.name = name;
+ this.nameYomi = nameYomi;
+ this.title = title;
+ this.orgDepartment = orgDepartment;
+ this.orgJobDescription = orgJobDescription;
+ this.orgSymbol = orgSymbol;
+ this.where = where;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Getter for nameYomi
+ */
+ public String getNameYomi() {
+ return this.nameYomi;
+ }
+
+ /**
+ * Setter for nameYomi
+ */
+ public void setNameYomi(String nameYomi) {
+ this.nameYomi = nameYomi;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ /**
+ * Getter for orgDepartment
+ */
+ public String getOrgDepartment() {
+ return this.orgDepartment;
+ }
+
+ /**
+ * Setter for orgDepartment
+ */
+ public void setOrgDepartment(String orgDepartment) {
+ this.orgDepartment = orgDepartment;
+ }
+
+ /**
+ * Getter for orgJobDescription
+ */
+ public String getOrgJobDescription() {
+ return this.orgJobDescription;
+ }
+
+ /**
+ * Setter for orgJobDescription
+ */
+ public void setOrgJobDescription(String orgJobDescription) {
+ this.orgJobDescription = orgJobDescription;
+ }
+
+ /**
+ * Getter for orgSymbol
+ */
+ public String getOrgSymbol() {
+ return this.orgSymbol;
+ }
+
+ /**
+ * Setter for orgSymbol
+ */
+ public void setOrgSymbol(String orgSymbol) {
+ this.orgSymbol = orgSymbol;
+ }
+
+ /**
+ * A place associated with the organization, e.g. office
+ * location. In Contacts, this is just a string value.
+ */
+ public String getWhere() {
+ return this.where;
+ }
+
+ /**
+ * A place associated with the organization, e.g. office
+ * location. In Contacts, this is just a string value.
+ */
+ public void setWhere(String where) {
+ this.where = where;
+ }
+
+ public void toString(StringBuffer sb) {
+ sb.append("Organization");
+ super.toString(sb);
+ if (name != null) sb.append(" name:").append(name);
+ if (title != null) sb.append(" title:").append(title);
+ if (orgDepartment != null) sb.append(" orgDepartment:").append(orgDepartment);
+ if (orgJobDescription != null) sb.append(" orgJobDescription:").append(orgJobDescription);
+ if (orgSymbol != null) sb.append(" orgSymbol:").append(orgSymbol);
+ if (nameYomi != null) sb.append(" nameYomi:").append(nameYomi);
+ }
+}
diff --git a/src/com/google/wireless/gdata2/contacts/data/PhoneNumber.java b/src/com/google/wireless/gdata2/contacts/data/PhoneNumber.java
new file mode 100644
index 0000000..a4b4c62
--- /dev/null
+++ b/src/com/google/wireless/gdata2/contacts/data/PhoneNumber.java
@@ -0,0 +1,55 @@
+// Copyright 2009 The Android Open Source Project
+
+package com.google.wireless.gdata2.contacts.data;
+
+/**
+ * The PhoneNumber gdata type
+ */
+public class PhoneNumber extends ContactsElement {
+ /** The phone number type. */
+ public static final byte TYPE_MOBILE = 1;
+ public static final byte TYPE_HOME = 2;
+ public static final byte TYPE_WORK = 3;
+ public static final byte TYPE_WORK_FAX = 4;
+ public static final byte TYPE_HOME_FAX = 5;
+ public static final byte TYPE_PAGER = 6;
+ public static final byte TYPE_ASSISTANT = 7;
+ public static final byte TYPE_CALLBACK = 8;
+ public static final byte TYPE_CAR = 9;
+ public static final byte TYPE_COMPANY_MAIN = 10;
+ public static final byte TYPE_ISDN = 11;
+ public static final byte TYPE_MAIN = 12;
+ public static final byte TYPE_OTHER_FAX = 13;
+ public static final byte TYPE_RADIO = 14;
+ public static final byte TYPE_TELEX = 15;
+ public static final byte TYPE_TTY_TDD = 16;
+ public static final byte TYPE_WORK_MOBILE = 17;
+ public static final byte TYPE_WORK_PAGER = 18;
+ public static final byte TYPE_OTHER = 19;
+
+ private String phoneNumber;
+
+ /**
+ * default empty constructor
+ */
+ public PhoneNumber() {}
+ public PhoneNumber(String phoneNumber, byte type, String label, boolean isPrimary) {
+ super(type, label, isPrimary);
+ this.phoneNumber = phoneNumber;
+ }
+
+
+ public String getPhoneNumber() {
+ return phoneNumber;
+ }
+
+ public void setPhoneNumber(String phoneNumber) {
+ this.phoneNumber = phoneNumber;
+ }
+
+ public void toString(StringBuffer sb) {
+ sb.append("PhoneNumber");
+ super.toString(sb);
+ if (phoneNumber != null) sb.append(" phoneNumber:").append(phoneNumber);
+ }
+}
diff --git a/src/com/google/wireless/gdata2/contacts/data/Relation.java b/src/com/google/wireless/gdata2/contacts/data/Relation.java
new file mode 100644
index 0000000..1944c49
--- /dev/null
+++ b/src/com/google/wireless/gdata2/contacts/data/Relation.java
@@ -0,0 +1,69 @@
+/**
+ * Copyright (C) 2007 The Android Open Source Project
+ */
+
+package com.google.wireless.gdata2.contacts.data;
+
+import com.google.wireless.gdata2.parser.ParseException;
+import com.google.wireless.gdata2.data.StringUtils;
+
+
+/**
+ * This element describe another entity (usually a person)
+ * that is in a relation of some kind with the contact
+ */
+public class Relation extends TypedElement {
+ public static final byte TYPE_ASSISTANT = 1;
+ public static final byte TYPE_BROTHER = 2;
+ public static final byte TYPE_CHILD = 3;
+ public static final byte TYPE_DOMESTICPARTNER = 4;
+ public static final byte TYPE_FATHER = 5;
+ public static final byte TYPE_FRIEND = 6;
+ public static final byte TYPE_MANAGER = 7;
+ public static final byte TYPE_MOTHER = 8;
+ public static final byte TYPE_PARENT = 9;
+ public static final byte TYPE_PARTNER = 10;
+ public static final byte TYPE_REFERREDBY = 11;
+ public static final byte TYPE_RELATIVE = 12;
+ public static final byte TYPE_SISTER = 13;
+ public static final byte TYPE_SPOUSE = 14;
+
+ private String text;
+
+ /**
+ * default empty constructor
+ */
+ public Relation() {}
+ public Relation(String text, byte type, String label) {
+ super(type, label);
+ this.text = text;
+ }
+
+
+
+ /**
+ * The entity in relation with the contact.
+ */
+ public String getText() {
+ return this.text;
+ }
+
+ /**
+ * The entity in relation with the contact.
+ */
+ public void setText(String text) {
+ this.text = text;
+ }
+
+
+ public void toString(StringBuffer sb) {
+ sb.append("Relation");
+ super.toString(sb);
+ if (!StringUtils.isEmpty(text)) {
+ sb.append(" text:").append(text);
+ }
+ }
+}
+
+
+
diff --git a/src/com/google/wireless/gdata2/contacts/data/StructuredPostalAddress.java b/src/com/google/wireless/gdata2/contacts/data/StructuredPostalAddress.java
new file mode 100644
index 0000000..79399f5
--- /dev/null
+++ b/src/com/google/wireless/gdata2/contacts/data/StructuredPostalAddress.java
@@ -0,0 +1,206 @@
+// Copyright 2009 The Android Open Source Project
+
+package com.google.wireless.gdata2.contacts.data;
+
+/**
+ * Postal address split into components. It allows to store the
+ * address in locale independent format. The fields can be
+ * interpreted and used to generate formatted, locale dependent
+ * address.
+ * Not all the properties of gd:structuredPostalAddress are supported
+ * by the Contacts API. The unsupported properties are the
+ * gd:agent, gd:housename, and gd:subregion subelements, and the
+ * attributes mailClass and usage.
+ */
+public class StructuredPostalAddress extends ContactsElement {
+ public static final byte TYPE_HOME = 1;
+ public static final byte TYPE_WORK = 2;
+ public static final byte TYPE_OTHER = 3;
+
+ private String street;
+ private String pobox;
+ private String neighborhood;
+ private String city;
+ private String region;
+ private String postcode;
+ private String country;
+ private String formattedAddress;
+
+ /**
+ * default empty constructor
+ */
+ public StructuredPostalAddress() {}
+ public StructuredPostalAddress(String street, String pobox, String city, String postcode,
+ String country, String region, String neighborhood, String formattedAddress,
+ byte type, String label, boolean isPrimary) {
+ super(type, label, isPrimary);
+ this.street = street;
+ this.pobox = pobox;
+ this.city = city;
+ this.postcode = postcode;
+ this.country = country;
+ this.region = region;
+ this.neighborhood = neighborhood;
+ this.formattedAddress = formattedAddress;
+ }
+
+ /**
+ * Getter for street
+ * Can be street, avenue, road, etc. This element also includes
+ * the house number and room/apartment/flat/floor number
+ */
+ public String getStreet() {
+ return this.street;
+ }
+
+ /**
+ * Setter for street
+ * Can be street, avenue, road, etc. This element also includes
+ * the house number and room/apartment/flat/floor number
+ */
+ public void setStreet(String street) {
+ this.street = street;
+ }
+
+ /**
+ * Getter for pobox
+ * Covers actual P.O. boxes, drawers, locked bags, etc. This is
+ * usually but not always mutually exclusive with street.
+ */
+ public String getPobox() {
+ return this.pobox;
+ }
+
+ /**
+ * Setter for pobox
+ * Covers actual P.O. boxes, drawers, locked bags, etc. This is
+ * usually but not always mutually exclusive with street.
+ */
+ public void setPobox(String pobox) {
+ this.pobox = pobox;
+ }
+
+ /**
+ * Getter for neighborhood
+ * This is used to disambiguate a street address when a city
+ * contains more than one street with the same name, or to
+ * specify a small place whose mail is routed through a larger
+ * postal town. In China it could be a county or a minor city.
+ */
+ public String getNeighborhood() {
+ return this.neighborhood;
+ }
+
+ /**
+ * Setter for neighborhood
+ * This is used to disambiguate a street address when a city
+ * contains more than one street with the same name, or to
+ * specify a small place whose mail is routed through a larger
+ * postal town. In China it could be a county or a minor city.
+ */
+ public void setNeighborhood(String neighborhood) {
+ this.neighborhood = neighborhood;
+ }
+
+ /**
+ * Getter for city
+ * Can be city, village, town, borough, etc. This is the postal
+ * town and not necessarily the place of residence or place of
+ * business.
+ */
+ public String getCity() {
+ return this.city;
+ }
+
+ /**
+ * Setter for city
+ * Can be city, village, town, borough, etc. This is the postal
+ * town and not necessarily the place of residence or place of
+ * business.
+ */
+ public void setCity(String city) {
+ this.city = city;
+ }
+
+
+ /**
+ * Getter for region
+ * A state, province, county (in Ireland), Land (in Germany),
+ * departement (in France), etc.
+ */
+ public String getRegion() {
+ return this.region;
+ }
+
+ /**
+ * Setter for region
+ * A state, province, county (in Ireland), Land (in Germany),
+ * departement (in France), etc.
+ */
+ public void setRegion(String region) {
+ this.region = region;
+ }
+
+ /**
+ * Getter for postcode
+ * Postal code. Usually country-wide, but sometimes specific to
+ * the city (e.g. "2" in "Dublin 2, Ireland" addresses).
+ */
+ public String getPostcode() {
+ return this.postcode;
+ }
+
+ /**
+ * Setter for postcode
+ * Postal code. Usually country-wide, but sometimes specific to
+ * the city (e.g. "2" in "Dublin 2, Ireland" addresses).
+ */
+ public void setPostcode(String postcode) {
+ this.postcode = postcode;
+ }
+
+ /**
+ * Getter for country
+ * The name or code of the country.
+ */
+ public String getCountry() {
+ return this.country;
+ }
+
+ /**
+ * Setter for country
+ * The name or code of the country.
+ */
+ public void setCountry(String country) {
+ this.country = country;
+ }
+
+ /**
+ * Getter for formatedAddress
+ * The full, unstructured postal address.
+ */
+ public String getFormattedAddress() {
+ return this.formattedAddress;
+ }
+
+ /**
+ * Setter for formatedAddress
+ * The full, unstructured postal address.
+ */
+ public void setFormattedAddress(String formattedAddress) {
+ this.formattedAddress = formattedAddress;
+ }
+
+ public void toString(StringBuffer sb) {
+ sb.append("PostalAddress");
+ super.toString(sb);
+ if (street != null) sb.append(" street:").append(street);
+ if (pobox != null) sb.append(" pobox:").append(pobox);
+ if (neighborhood != null) sb.append(" neighborhood:").append(neighborhood);
+ if (city != null) sb.append(" city:").append(city);
+ if (region != null) sb.append(" region:").append(region);
+ if (postcode != null) sb.append(" postcode:").append(postcode);
+ if (country != null) sb.append(" country:").append(country);
+ if (formattedAddress != null) sb.append(" formattedAddress:").append(formattedAddress);
+ }
+}
diff --git a/src/com/google/wireless/gdata2/contacts/data/TypedElement.java b/src/com/google/wireless/gdata2/contacts/data/TypedElement.java
new file mode 100644
index 0000000..20595b7
--- /dev/null
+++ b/src/com/google/wireless/gdata2/contacts/data/TypedElement.java
@@ -0,0 +1,56 @@
+/**
+ * Copyright (C) 2009 The Android Open Source Project
+ */
+package com.google.wireless.gdata2.contacts.data;
+
+import com.google.wireless.gdata2.parser.ParseException;
+
+
+/**
+ * Contains attributes that are common to all elements in a ContactEntry.
+ */
+public abstract class TypedElement {
+ public static final byte TYPE_NONE = -1;
+ private byte type = TYPE_NONE;
+
+ private String label;
+
+ public TypedElement() {}
+ public TypedElement(byte type, String label) {
+ this.type = type;
+ this.label = label;
+ }
+
+ public byte getType() {
+ return type;
+ }
+
+ public void setType(byte rel) {
+ this.type = rel;
+ }
+
+ public String getLabel() {
+ return label;
+ }
+
+ public void setLabel(String label) {
+ this.label = label;
+ }
+
+ public void toString(StringBuffer sb) {
+ sb.append(" type:").append(type);
+ if (label != null) sb.append(" label:").append(label);
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ toString(sb);
+ return sb.toString();
+ }
+
+ public void validate() throws ParseException {
+ if ((label == null && type == TYPE_NONE) || (label != null && type != TYPE_NONE)) {
+ throw new ParseException("exactly one of label or type must be set");
+ }
+ }
+}
diff --git a/src/com/google/wireless/gdata2/contacts/data/UserDefinedField.java b/src/com/google/wireless/gdata2/contacts/data/UserDefinedField.java
new file mode 100644
index 0000000..c127a68
--- /dev/null
+++ b/src/com/google/wireless/gdata2/contacts/data/UserDefinedField.java
@@ -0,0 +1,95 @@
+/**
+ * Copyright (C) 2007 The Android Open Source Project
+ */
+
+package com.google.wireless.gdata2.contacts.data;
+
+import com.google.wireless.gdata2.parser.ParseException;
+import com.google.wireless.gdata2.data.StringUtils;
+
+
+/**
+ * Represents an arbitrary key-value pair attached to the contact.
+ */
+public class UserDefinedField {
+
+ private String key;
+ private String value;
+
+
+ /**
+ * default empty constructor
+ */
+ public UserDefinedField() {}
+
+ /**
+ * Default constructor
+ */
+ public UserDefinedField(String key, String value)
+ {
+ this.key = key;
+ this.value = value;
+ }
+
+
+ /**
+ * A simple string value used to name this field.
+ * Case-sensitive
+ */
+ public String getKey() {
+ return this.key;
+ }
+
+ /**
+ * A simple string value used to name this field. Case-sensitive
+ */
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ /**
+ * The value of this field.
+ */
+ public String getValue() {
+ return this.value;
+ }
+
+ /**
+ * The value of this field.
+ */
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ toString(sb);
+ return sb.toString();
+ }
+
+
+ public void toString(StringBuffer sb) {
+ sb.append("UserDefinedField");
+ if (!StringUtils.isEmpty(key)) {
+ sb.append(" key:").append(key);
+ }
+ if (!StringUtils.isEmpty(value)) {
+ sb.append(" value:").append(value);
+ }
+ }
+
+ /**
+ * Currently empty, will be filled when the parser is done
+ *
+ */
+ public void validate() throws ParseException {
+ if (StringUtils.isEmpty(key)) {
+ throw new ParseException("key has to be set");
+ }
+ }
+}
+
+
+
diff --git a/src/com/google/wireless/gdata2/contacts/data/WebSite.java b/src/com/google/wireless/gdata2/contacts/data/WebSite.java
new file mode 100644
index 0000000..6c3e59b
--- /dev/null
+++ b/src/com/google/wireless/gdata2/contacts/data/WebSite.java
@@ -0,0 +1,55 @@
+// Copyright 2007 The Android Open Source Project
+
+package com.google.wireless.gdata2.contacts.data;
+
+import com.google.wireless.gdata2.data.StringUtils;
+
+/**
+ * Describes websites associated with the contact, including links.
+ * May be repeated.
+ */
+public class WebSite extends ContactsElement {
+ /** The phone number type. */
+ public static final byte TYPE_HOMEPAGE = 1;
+ public static final byte TYPE_BLOG = 2;
+ public static final byte TYPE_PROFILE = 3;
+ public static final byte TYPE_HOME = 4;
+ public static final byte TYPE_WORK = 5;
+ public static final byte TYPE_OTHER = 6;
+ public static final byte TYPE_FTP = 7;
+
+ private String href;
+
+ /**
+ * default empty constructor
+ */
+ public WebSite() {}
+ public WebSite(String href, byte type, String label, boolean isPrimary) {
+ super(type, label, isPrimary);
+ this.href = href;
+ }
+
+ /**
+ * The URL of the website.
+ */
+ public String getHRef() {
+ return this.href;
+ }
+
+ /**
+ * The URL of the website.
+ */
+ public void setHRef(String href) {
+ this.href = href;
+ }
+
+
+ public void toString(StringBuffer sb) {
+ sb.append("WebSite");
+ super.toString(sb);
+ if (!StringUtils.isEmpty(href)) {
+ sb.append(" href:").append(href);
+ }
+ }
+}
+
diff --git a/src/com/google/wireless/gdata/spreadsheets/package.html b/src/com/google/wireless/gdata2/contacts/data/package.html
index 1c9bf9d..1c9bf9d 100644
--- a/src/com/google/wireless/gdata/spreadsheets/package.html
+++ b/src/com/google/wireless/gdata2/contacts/data/package.html
diff --git a/src/com/google/wireless/gdata/spreadsheets/parser/package.html b/src/com/google/wireless/gdata2/contacts/package.html
index 1c9bf9d..1c9bf9d 100644
--- a/src/com/google/wireless/gdata/spreadsheets/parser/package.html
+++ b/src/com/google/wireless/gdata2/contacts/package.html
diff --git a/src/com/google/wireless/gdata/spreadsheets/parser/xml/package.html b/src/com/google/wireless/gdata2/contacts/parser/package.html
index 1c9bf9d..1c9bf9d 100644
--- a/src/com/google/wireless/gdata/spreadsheets/parser/xml/package.html
+++ b/src/com/google/wireless/gdata2/contacts/parser/package.html
diff --git a/src/com/google/wireless/gdata2/contacts/parser/xml/XmlContactsGDataParser.java b/src/com/google/wireless/gdata2/contacts/parser/xml/XmlContactsGDataParser.java
new file mode 100644
index 0000000..47ef50a
--- /dev/null
+++ b/src/com/google/wireless/gdata2/contacts/parser/xml/XmlContactsGDataParser.java
@@ -0,0 +1,639 @@
+// Copyright 2009 The Android Open Source Project
+
+package com.google.wireless.gdata2.contacts.parser.xml;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import com.google.wireless.gdata2.contacts.data.*;
+import com.google.wireless.gdata2.data.Entry;
+import com.google.wireless.gdata2.data.ExtendedProperty;
+import com.google.wireless.gdata2.data.Feed;
+import com.google.wireless.gdata2.data.XmlUtils;
+import com.google.wireless.gdata2.parser.ParseException;
+import com.google.wireless.gdata2.parser.xml.XmlGDataParser;
+
+/**
+ * GDataParser for a contacts feed.
+ */
+public class XmlContactsGDataParser extends XmlGDataParser {
+ /** Namespace prefix for Contacts */
+ public static final String NAMESPACE_CONTACTS = "gContact";
+
+ /** Namespace URI for Contacts */
+ public static final String NAMESPACE_CONTACTS_URI =
+ "http://schemas.google.com/contact/2008";
+
+ /** The photo link rels */
+ public static final String LINK_REL_PHOTO = "http://schemas.google.com/contacts/2008/rel#photo";
+
+ /** The phone number type gdata string. */
+ private static final String GD_NAMESPACE = "http://schemas.google.com/g/2005#";
+ public static final String TYPESTRING_MOBILE = GD_NAMESPACE + "mobile";
+ public static final String TYPESTRING_HOME = GD_NAMESPACE + "home";
+ public static final String TYPESTRING_WORK = GD_NAMESPACE + "work";
+ public static final String TYPESTRING_HOME_FAX = GD_NAMESPACE + "home_fax";
+ public static final String TYPESTRING_WORK_FAX = GD_NAMESPACE + "work_fax";
+ public static final String TYPESTRING_PAGER = GD_NAMESPACE + "pager";
+ public static final String TYPESTRING_ASSISTANT = GD_NAMESPACE + "assistant";
+ public static final String TYPESTRING_CALLBACK = GD_NAMESPACE + "callback";
+ public static final String TYPESTRING_CAR = GD_NAMESPACE + "car";
+ public static final String TYPESTRING_COMPANY_MAIN = GD_NAMESPACE + "company_main";
+ public static final String TYPESTRING_ISDN = GD_NAMESPACE + "isdn";
+ public static final String TYPESTRING_MAIN = GD_NAMESPACE + "main";
+ public static final String TYPESTRING_OTHER_FAX = GD_NAMESPACE + "other_fax";
+ public static final String TYPESTRING_RADIO = GD_NAMESPACE + "radio";
+ public static final String TYPESTRING_TELEX = GD_NAMESPACE + "telex";
+ public static final String TYPESTRING_TTY_TDD = GD_NAMESPACE + "tty_tdd";
+ public static final String TYPESTRING_WORK_MOBILE = GD_NAMESPACE + "work_mobile";
+ public static final String TYPESTRING_WORK_PAGER = GD_NAMESPACE + "work_pager";
+ public static final String TYPESTRING_OTHER = GD_NAMESPACE + "other";
+
+ public static final String IM_PROTOCOL_AIM = GD_NAMESPACE + "AIM";
+ public static final String IM_PROTOCOL_MSN = GD_NAMESPACE + "MSN";
+ public static final String IM_PROTOCOL_YAHOO = GD_NAMESPACE + "YAHOO";
+ public static final String IM_PROTOCOL_SKYPE = GD_NAMESPACE + "SKYPE";
+ public static final String IM_PROTOCOL_QQ = GD_NAMESPACE + "QQ";
+ public static final String IM_PROTOCOL_GOOGLE_TALK = GD_NAMESPACE + "GOOGLE_TALK";
+ public static final String IM_PROTOCOL_ICQ = GD_NAMESPACE + "ICQ";
+ public static final String IM_PROTOCOL_JABBER = GD_NAMESPACE + "JABBER";
+ public static final String IM_PROTOCOL_NETMEETING = GD_NAMESPACE + "netmeeting";
+
+ public static final String TYPESTRING_CALENDARLINK_HOME = "home";
+ public static final String TYPESTRING_CALENDARLINK_WORK = "work";
+ public static final String TYPESTRING_CALENDARLINK_FREEBUSY = "free-busy";
+
+ public static final String TYPESTRING_EVENT_ANNIVERARY = "anniversary";
+ public static final String TYPESTRING_EVENT_OTHER = "other";
+
+ public static final String TYPESTRING_EXTERNALID_ACCOUNT = "account";
+ public static final String TYPESTRING_EXTERNALID_CUSTOMER = "customer";
+ public static final String TYPESTRING_EXTERNALID_NETWORK = "network";
+ public static final String TYPESTRING_EXTERNALID_ORGANIZATION = "organization";
+
+ public static final String TYPESTRING_JOT_HOME = TYPESTRING_CALENDARLINK_HOME;
+ public static final String TYPESTRING_JOT_WORK = TYPESTRING_CALENDARLINK_WORK;
+ public static final String TYPESTRING_JOT_OTHER = TYPESTRING_EVENT_OTHER;
+ public static final String TYPESTRING_JOT_KEYWORDS = "keywords";
+ public static final String TYPESTRING_JOT_USER = "user";
+
+ public static final String TYPESTRING_PRIORITY_HIGH = "high";
+ public static final String TYPESTRING_PRIORITY_LOW = "low";
+ public static final String TYPESTRING_PRIORITY_NORMAL = "normal";
+
+ public static final String TYPESTRING_RELATION_ASSISTANT = "assistant";
+ public static final String TYPESTRING_RELATION_BROTHER = "brother";
+ public static final String TYPESTRING_RELATION_CHILD = "child";
+ public static final String TYPESTRING_RELATION_DOMESTICPARTNER = "domestic-partner";
+ public static final String TYPESTRING_RELATION_FATHER = "father";
+ public static final String TYPESTRING_RELATION_FRIEND = "friend";
+ public static final String TYPESTRING_RELATION_MANAGER = "manager";
+ public static final String TYPESTRING_RELATION_MOTHER = "mother";
+ public static final String TYPESTRING_RELATION_PARENT = "parent";
+ public static final String TYPESTRING_RELATION_PARTNER = "partner";
+ public static final String TYPESTRING_RELATION_REFERREDBY = "referred-by";
+ public static final String TYPESTRING_RELATION_RELATIVE = "relative";
+ public static final String TYPESTRING_RELATION_SISTER = "sister";
+ public static final String TYPESTRING_RELATION_SPOUSE = "spouse";
+
+ public static final String TYPESTRING_SENSITIVITY_CONFIDENTIAL = "confidential";
+ public static final String TYPESTRING_SENSITIVITY_NORMAL = "normal";
+ public static final String TYPESTRING_SENSITIVITY_PERSONAL = "personal";
+ public static final String TYPESTRING_SENSITIVITY_PRIVATE = "private";
+
+ public static final String TYPESTRING_WEBSITE_HOMEPAGE = "home-page";
+ public static final String TYPESTRING_WEBSITE_BLOG = "blog";
+ public static final String TYPESTRING_WEBSITE_PROFILE = "profile";
+ public static final String TYPESTRING_WEBSITE_HOME = TYPESTRING_CALENDARLINK_HOME;
+ public static final String TYPESTRING_WEBSITE_WORK = TYPESTRING_CALENDARLINK_WORK;
+ public static final String TYPESTRING_WEBSITE_OTHER = TYPESTRING_EVENT_OTHER;
+ public static final String TYPESTRING_WEBSITE_FTP = "ftp";
+
+ private static final Hashtable REL_TO_TYPE_EMAIL;
+ private static final Hashtable REL_TO_TYPE_PHONE;
+ private static final Hashtable REL_TO_TYPE_POSTAL;
+ private static final Hashtable REL_TO_TYPE_IM;
+ private static final Hashtable REL_TO_TYPE_ORGANIZATION;
+ private static final Hashtable IM_PROTOCOL_STRING_TO_TYPE_MAP;
+ private static final Hashtable REL_TO_TYPE_CALENDARLINK;
+ private static final Hashtable REL_TO_TYPE_EVENT;
+ private static final Hashtable REL_TO_TYPE_EXTERNALID;
+ private static final Hashtable REL_TO_TYPE_JOT;
+ private static final Hashtable REL_TO_TYPE_PRIORITY;
+ private static final Hashtable REL_TO_TYPE_RELATION;
+ private static final Hashtable REL_TO_TYPE_SENSITIVITY;
+ private static final Hashtable REL_TO_TYPE_WEBSITE;
+
+ public static final Hashtable TYPE_TO_REL_EMAIL;
+ public static final Hashtable TYPE_TO_REL_PHONE;
+ public static final Hashtable TYPE_TO_REL_POSTAL;
+ public static final Hashtable TYPE_TO_REL_IM;
+ public static final Hashtable TYPE_TO_REL_ORGANIZATION;
+ public static final Hashtable IM_PROTOCOL_TYPE_TO_STRING_MAP;
+ public static final Hashtable TYPE_TO_REL_CALENDARLINK;
+ public static final Hashtable TYPE_TO_REL_EVENT;
+ public static final Hashtable TYPE_TO_REL_EXTERNALID;
+ public static final Hashtable TYPE_TO_REL_JOT;
+ public static final Hashtable TYPE_TO_REL_PRIORITY;
+ public static final Hashtable TYPE_TO_REL_RELATION;
+ public static final Hashtable TYPE_TO_REL_SENSITIVITY;
+ public static final Hashtable TYPE_TO_REL_WEBSITE;
+
+ static {
+ Hashtable map;
+
+ map = new Hashtable();
+ map.put(TYPESTRING_HOME, new Byte(EmailAddress.TYPE_HOME));
+ map.put(TYPESTRING_WORK, new Byte(EmailAddress.TYPE_WORK));
+ map.put(TYPESTRING_OTHER, new Byte(EmailAddress.TYPE_OTHER));
+ REL_TO_TYPE_EMAIL = map;
+ TYPE_TO_REL_EMAIL = swapMap(map);
+
+ map = new Hashtable();
+ map.put(TYPESTRING_HOME, new Byte(PhoneNumber.TYPE_HOME));
+ map.put(TYPESTRING_MOBILE, new Byte(PhoneNumber.TYPE_MOBILE));
+ map.put(TYPESTRING_PAGER, new Byte(PhoneNumber.TYPE_PAGER));
+ map.put(TYPESTRING_WORK, new Byte(PhoneNumber.TYPE_WORK));
+ map.put(TYPESTRING_HOME_FAX, new Byte(PhoneNumber.TYPE_HOME_FAX));
+ map.put(TYPESTRING_WORK_FAX, new Byte(PhoneNumber.TYPE_WORK_FAX));
+ map.put(TYPESTRING_ASSISTANT, new Byte(PhoneNumber.TYPE_ASSISTANT));
+ map.put(TYPESTRING_CALLBACK, new Byte(PhoneNumber.TYPE_CALLBACK));
+ map.put(TYPESTRING_CAR, new Byte(PhoneNumber.TYPE_CAR));
+ map.put(TYPESTRING_COMPANY_MAIN, new Byte(PhoneNumber.TYPE_COMPANY_MAIN));
+ map.put(TYPESTRING_ISDN, new Byte(PhoneNumber.TYPE_ISDN));
+ map.put(TYPESTRING_MAIN, new Byte(PhoneNumber.TYPE_MAIN));
+ map.put(TYPESTRING_OTHER_FAX, new Byte(PhoneNumber.TYPE_OTHER_FAX));
+ map.put(TYPESTRING_RADIO, new Byte(PhoneNumber.TYPE_RADIO));
+ map.put(TYPESTRING_TELEX, new Byte(PhoneNumber.TYPE_TELEX));
+ map.put(TYPESTRING_TTY_TDD, new Byte(PhoneNumber.TYPE_TTY_TDD));
+ map.put(TYPESTRING_WORK_MOBILE, new Byte(PhoneNumber.TYPE_WORK_MOBILE));
+ map.put(TYPESTRING_WORK_PAGER, new Byte(PhoneNumber.TYPE_WORK_PAGER));
+ map.put(TYPESTRING_OTHER, new Byte(PhoneNumber.TYPE_OTHER));
+ REL_TO_TYPE_PHONE = map;
+ TYPE_TO_REL_PHONE = swapMap(map);
+
+ map = new Hashtable();
+ map.put(TYPESTRING_HOME, new Byte(StructuredPostalAddress.TYPE_HOME));
+ map.put(TYPESTRING_WORK, new Byte(StructuredPostalAddress.TYPE_WORK));
+ map.put(TYPESTRING_OTHER, new Byte(StructuredPostalAddress.TYPE_OTHER));
+ REL_TO_TYPE_POSTAL = map;
+ TYPE_TO_REL_POSTAL = swapMap(map);
+
+ map = new Hashtable();
+ map.put(TYPESTRING_HOME, new Byte(ImAddress.TYPE_HOME));
+ map.put(TYPESTRING_WORK, new Byte(ImAddress.TYPE_WORK));
+ map.put(TYPESTRING_OTHER, new Byte(ImAddress.TYPE_OTHER));
+ REL_TO_TYPE_IM = map;
+ TYPE_TO_REL_IM = swapMap(map);
+
+ map = new Hashtable();
+ map.put(TYPESTRING_WORK, new Byte(Organization.TYPE_WORK));
+ map.put(TYPESTRING_OTHER, new Byte(Organization.TYPE_OTHER));
+ REL_TO_TYPE_ORGANIZATION = map;
+ TYPE_TO_REL_ORGANIZATION = swapMap(map);
+
+ map = new Hashtable();
+ map.put(IM_PROTOCOL_AIM, new Byte(ImAddress.PROTOCOL_AIM));
+ map.put(IM_PROTOCOL_MSN, new Byte(ImAddress.PROTOCOL_MSN));
+ map.put(IM_PROTOCOL_YAHOO, new Byte(ImAddress.PROTOCOL_YAHOO));
+ map.put(IM_PROTOCOL_SKYPE, new Byte(ImAddress.PROTOCOL_SKYPE));
+ map.put(IM_PROTOCOL_QQ, new Byte(ImAddress.PROTOCOL_QQ));
+ map.put(IM_PROTOCOL_GOOGLE_TALK, new Byte(ImAddress.PROTOCOL_GOOGLE_TALK));
+ map.put(IM_PROTOCOL_ICQ, new Byte(ImAddress.PROTOCOL_ICQ));
+ map.put(IM_PROTOCOL_JABBER, new Byte(ImAddress.PROTOCOL_JABBER));
+ map.put(IM_PROTOCOL_NETMEETING, new Byte(ImAddress.PROTOCOL_NETMEETING));
+
+ IM_PROTOCOL_STRING_TO_TYPE_MAP = map;
+ IM_PROTOCOL_TYPE_TO_STRING_MAP = swapMap(map);
+
+ map = new Hashtable();
+ map.put(TYPESTRING_CALENDARLINK_HOME, new Byte(CalendarLink.TYPE_HOME));
+ map.put(TYPESTRING_CALENDARLINK_WORK, new Byte(CalendarLink.TYPE_WORK));
+ map.put(TYPESTRING_CALENDARLINK_FREEBUSY, new Byte(CalendarLink.TYPE_FREE_BUSY));
+ REL_TO_TYPE_CALENDARLINK = map;
+ TYPE_TO_REL_CALENDARLINK = swapMap(map);
+
+ map = new Hashtable();
+ map.put(TYPESTRING_EVENT_ANNIVERARY, new Byte(Event.TYPE_ANNIVERSARY));
+ map.put(TYPESTRING_EVENT_OTHER, new Byte(Event.TYPE_OTHER));
+ REL_TO_TYPE_EVENT = map;
+ TYPE_TO_REL_EVENT = swapMap(map);
+
+ map = new Hashtable();
+ map.put(TYPESTRING_EXTERNALID_ACCOUNT, new Byte(ExternalId.TYPE_ACCOUNT));
+ map.put(TYPESTRING_EXTERNALID_CUSTOMER, new Byte(ExternalId.TYPE_CUSTOMER));
+ map.put(TYPESTRING_EXTERNALID_NETWORK, new Byte(ExternalId.TYPE_NETWORK));
+ map.put(TYPESTRING_EXTERNALID_ORGANIZATION, new Byte(ExternalId.TYPE_ORGANIZATION));
+ REL_TO_TYPE_EXTERNALID = map;
+ TYPE_TO_REL_EXTERNALID = swapMap(map);
+
+ map = new Hashtable();
+ map.put(TYPESTRING_JOT_HOME, new Byte(Jot.TYPE_HOME));
+ map.put(TYPESTRING_JOT_KEYWORDS, new Byte(Jot.TYPE_KEYWORDS));
+ map.put(TYPESTRING_JOT_OTHER, new Byte(Jot.TYPE_OTHER));
+ map.put(TYPESTRING_JOT_USER, new Byte(Jot.TYPE_USER));
+ map.put(TYPESTRING_JOT_WORK, new Byte(Jot.TYPE_WORK));
+ REL_TO_TYPE_JOT = map;
+ TYPE_TO_REL_JOT = swapMap(map);
+
+ map = new Hashtable();
+ map.put(TYPESTRING_PRIORITY_HIGH, new Byte(ContactEntry.TYPE_PRIORITY_HIGH));
+ map.put(TYPESTRING_PRIORITY_NORMAL, new Byte(ContactEntry.TYPE_PRIORITY_NORMAL));
+ map.put(TYPESTRING_PRIORITY_LOW, new Byte(ContactEntry.TYPE_PRIORITY_LOW));
+ REL_TO_TYPE_PRIORITY = map;
+ TYPE_TO_REL_PRIORITY = swapMap(map);
+
+ map = new Hashtable();
+ map.put(TYPESTRING_RELATION_ASSISTANT, new Byte(Relation.TYPE_ASSISTANT));
+ map.put(TYPESTRING_RELATION_BROTHER, new Byte(Relation.TYPE_BROTHER));
+ map.put(TYPESTRING_RELATION_CHILD, new Byte(Relation.TYPE_CHILD));
+ map.put(TYPESTRING_RELATION_DOMESTICPARTNER, new Byte(Relation.TYPE_DOMESTICPARTNER));
+ map.put(TYPESTRING_RELATION_FATHER, new Byte(Relation.TYPE_FATHER));
+ map.put(TYPESTRING_RELATION_FRIEND, new Byte(Relation.TYPE_FRIEND));
+ map.put(TYPESTRING_RELATION_MANAGER, new Byte(Relation.TYPE_MANAGER));
+ map.put(TYPESTRING_RELATION_MOTHER, new Byte(Relation.TYPE_MOTHER));
+ map.put(TYPESTRING_RELATION_PARENT, new Byte(Relation.TYPE_PARENT));
+ map.put(TYPESTRING_RELATION_PARTNER, new Byte(Relation.TYPE_PARTNER));
+ map.put(TYPESTRING_RELATION_REFERREDBY, new Byte(Relation.TYPE_REFERREDBY));
+ map.put(TYPESTRING_RELATION_RELATIVE, new Byte(Relation.TYPE_RELATIVE));
+ map.put(TYPESTRING_RELATION_SISTER, new Byte(Relation.TYPE_SISTER));
+ map.put(TYPESTRING_RELATION_SPOUSE, new Byte(Relation.TYPE_SPOUSE));
+ REL_TO_TYPE_RELATION = map;
+ TYPE_TO_REL_RELATION = swapMap(map);
+
+ map = new Hashtable();
+ map.put(TYPESTRING_SENSITIVITY_CONFIDENTIAL,
+ new Byte(ContactEntry.TYPE_SENSITIVITY_CONFIDENTIAL));
+ map.put(TYPESTRING_SENSITIVITY_NORMAL,
+ new Byte(ContactEntry.TYPE_SENSITIVITY_NORMAL));
+ map.put(TYPESTRING_SENSITIVITY_PERSONAL,
+ new Byte(ContactEntry.TYPE_SENSITIVITY_PERSONAL));
+ map.put(TYPESTRING_SENSITIVITY_PRIVATE,
+ new Byte(ContactEntry.TYPE_SENSITIVITY_PRIVATE));
+ REL_TO_TYPE_SENSITIVITY= map;
+ TYPE_TO_REL_SENSITIVITY = swapMap(map);
+
+ map = new Hashtable();
+ map.put(TYPESTRING_WEBSITE_BLOG, new Byte(WebSite.TYPE_BLOG));
+ map.put(TYPESTRING_WEBSITE_HOMEPAGE, new Byte(WebSite.TYPE_HOMEPAGE));
+ map.put(TYPESTRING_WEBSITE_PROFILE, new Byte(WebSite.TYPE_PROFILE));
+ map.put(TYPESTRING_WEBSITE_HOME, new Byte(WebSite.TYPE_HOME));
+ map.put(TYPESTRING_WEBSITE_WORK, new Byte(WebSite.TYPE_WORK));
+ map.put(TYPESTRING_WEBSITE_OTHER, new Byte(WebSite.TYPE_OTHER));
+ map.put(TYPESTRING_WEBSITE_FTP, new Byte(WebSite.TYPE_FTP));
+
+ REL_TO_TYPE_WEBSITE = map;
+ TYPE_TO_REL_WEBSITE = swapMap(map);
+
+ }
+
+ private static Hashtable swapMap(Hashtable originalMap) {
+ Hashtable newMap = new Hashtable();
+ Enumeration enumeration = originalMap.keys();
+ while (enumeration.hasMoreElements()) {
+ Object key = enumeration.nextElement();
+ Object value = originalMap.get(key);
+ if (newMap.containsKey(value)) {
+ throw new IllegalArgumentException("value " + value
+ + " was already encountered");
+ }
+ newMap.put(value, key);
+ }
+ return newMap;
+ }
+
+ /**
+ * Creates a new XmlEventsGDataParser.
+ * @param is The InputStream that should be parsed.
+ * @throws ParseException Thrown if a parser cannot be created.
+ */
+ public XmlContactsGDataParser(InputStream is, XmlPullParser parser)
+ throws ParseException {
+ super(is, parser);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.google.wireless.gdata2.parser.xml.XmlGDataParser#createFeed()
+ */
+ protected Feed createFeed() {
+ return new ContactsFeed();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.google.wireless.gdata2.parser.xml.XmlGDataParser#createEntry()
+ */
+ protected Entry createEntry() {
+ return new ContactEntry();
+ }
+
+ protected void handleExtraElementInEntry(Entry entry) throws XmlPullParserException, IOException {
+ XmlPullParser parser = getParser();
+
+ if (!(entry instanceof ContactEntry)) {
+ throw new IllegalArgumentException("Expected ContactEntry!");
+ }
+ ContactEntry contactEntry = (ContactEntry) entry;
+ String name = parser.getName();
+ String ns = parser.getNamespace();
+
+ if (XmlGDataParser.NAMESPACE_GD_URI.equals(ns)) {
+ if (XmlNametable.GD_EMAIL.equals(name)) {
+ EmailAddress emailAddress = new EmailAddress();
+ parseContactsElement(emailAddress, parser, REL_TO_TYPE_EMAIL);
+ emailAddress.setDisplayName(parser.getAttributeValue(null /* ns */,
+ XmlNametable.GD_EMAIL_DISPLAYNAME));
+ emailAddress.setAddress(parser.getAttributeValue(null /* ns */,
+ XmlNametable.GD_ADDRESS));
+ contactEntry.addEmailAddress(emailAddress);
+ } else if (XmlNametable.GD_DELETED.equals(name)) {
+ contactEntry.setDeleted(true);
+ } else if (XmlNametable.GD_IM.equals(name)) {
+ ImAddress imAddress = new ImAddress();
+ parseContactsElement(imAddress, parser, REL_TO_TYPE_IM);
+ imAddress.setAddress(parser.getAttributeValue(null /* ns */,
+ XmlNametable.GD_ADDRESS));
+ imAddress.setLabel(parser.getAttributeValue(null /* ns */,
+ XmlNametable.LABEL));
+ String protocolString = parser.getAttributeValue(null /* ns */,
+ XmlNametable.GD_PROTOCOL);
+ if (protocolString == null) {
+ imAddress.setProtocolPredefined(ImAddress.PROTOCOL_NONE);
+ imAddress.setProtocolCustom(null);
+ } else {
+ Byte predefinedProtocol = (Byte) IM_PROTOCOL_STRING_TO_TYPE_MAP.get(protocolString);
+ if (predefinedProtocol == null) {
+ imAddress.setProtocolPredefined(ImAddress.PROTOCOL_CUSTOM);
+ imAddress.setProtocolCustom(protocolString);
+ } else {
+ imAddress.setProtocolPredefined(predefinedProtocol.byteValue());
+ imAddress.setProtocolCustom(null);
+ }
+ }
+ contactEntry.addImAddress(imAddress);
+ } else if (XmlNametable.GD_SPA.equals(name)) {
+ StructuredPostalAddress postalAddress = new StructuredPostalAddress();
+ parseContactsElement(postalAddress, parser, REL_TO_TYPE_POSTAL);
+ handleStructuredPostalAddressSubElement(postalAddress, parser);
+ contactEntry.addPostalAddress(postalAddress);
+ } else if (XmlNametable.GD_PHONENUMBER.equals(name)) {
+ PhoneNumber phoneNumber = new PhoneNumber();
+ parseContactsElement(phoneNumber, parser, REL_TO_TYPE_PHONE);
+ phoneNumber.setPhoneNumber(XmlUtils.extractChildText(parser));
+ contactEntry.addPhoneNumber(phoneNumber);
+ } else if (XmlNametable.GD_ORGANIZATION.equals(name)) {
+ Organization organization = new Organization();
+ parseContactsElement(organization, parser, REL_TO_TYPE_ORGANIZATION);
+ handleOrganizationSubElement(organization, parser);
+ contactEntry.addOrganization(organization);
+ } else if (XmlNametable.GD_EXTENDEDPROPERTY.equals(name)) {
+ ExtendedProperty extendedProperty = new ExtendedProperty();
+ parseExtendedProperty(extendedProperty);
+ contactEntry.addExtendedProperty(extendedProperty);
+ } else if (XmlNametable.GD_NAME.equals(name)) {
+ Name n = new Name();
+ handleNameSubElement(n, parser);
+ contactEntry.setName(n);
+ }
+ } else if (XmlContactsGDataParser.NAMESPACE_CONTACTS_URI.equals(ns)) {
+ if (XmlNametable.GC_GMI.equals(name)) {
+ GroupMembershipInfo group = new GroupMembershipInfo();
+ group.setGroup(parser.getAttributeValue(null /* ns */,
+ XmlNametable.HREF));
+ group.setDeleted("true".equals(parser.getAttributeValue(null /* ns */,
+ XmlNametable.GD_DELETED)));
+ contactEntry.addGroup(group);
+ } else if (XmlNametable.GC_BIRTHDAY.equals(name)) {
+ contactEntry.setBirthday(parser.getAttributeValue(null /* ns */,
+ XmlNametable.GD_WHEN));
+ } else if (XmlNametable.GC_BILLINGINFO.equals(name)) {
+ contactEntry.setBillingInformation(XmlUtils.extractChildText(parser));
+ } else if (XmlNametable.GC_CALENDARLINK.equals(name)) {
+ CalendarLink cl = new CalendarLink();
+ parseContactsElement(cl, parser, REL_TO_TYPE_CALENDARLINK);
+ cl.setHRef(parser.getAttributeValue(null /* ns */, XmlNametable.HREF));
+ contactEntry.addCalendarLink(cl);
+ } else if (XmlNametable.GC_DIRECTORYSERVER.equals(name)) {
+ contactEntry.setDirectoryServer(XmlUtils.extractChildText(parser));
+ } else if ("event".equals(name)) {
+ Event event = new Event();
+ parseTypedElement(event, parser, REL_TO_TYPE_EVENT);
+ handleEventSubElement(event, parser);
+ contactEntry.addEvent(event);
+ } else if (XmlNametable.GC_EXTERNALID.equals(name)) {
+ ExternalId externalId = new ExternalId();
+ parseTypedElement(externalId, parser, REL_TO_TYPE_EXTERNALID);
+ externalId.setValue(parser.getAttributeValue(null /* ns */, XmlNametable.VALUE));
+ contactEntry.addExternalId(externalId);
+ } else if (XmlNametable.GC_GENDER.equals(name)) {
+ contactEntry.setGender(parser.getAttributeValue(null /* ns */, XmlNametable.VALUE));
+ } else if (XmlNametable.GC_HOBBY.equals(name)) {
+ contactEntry.addHobby(XmlUtils.extractChildText(parser));
+ } else if (XmlNametable.GC_INITIALS.equals(name)) {
+ contactEntry.setInitials(XmlUtils.extractChildText(parser));
+ } else if (XmlNametable.GC_JOT.equals(name)) {
+ Jot jot = new Jot();
+ parseTypedElement(jot, parser, REL_TO_TYPE_JOT);
+ jot.setLabel(XmlUtils.extractChildText(parser));
+ contactEntry.addJot(jot);
+ } else if (XmlNametable.GC_LANGUAGE.equals(name)) {
+ Language language = new Language();
+ language.setCode(parser.getAttributeValue(null /* ns */, XmlNametable.CODE));
+ language.setLabel(parser.getAttributeValue(null /* */, XmlNametable.LABEL));
+ contactEntry.addLanguage(language);
+ } else if (XmlNametable.GC_MAIDENNAME.equals(name)) {
+ contactEntry.setMaidenName(XmlUtils.extractChildText(parser));
+ } else if (XmlNametable.GC_MILEAGE.equals(name)) {
+ contactEntry.setMileage(XmlUtils.extractChildText(parser));
+ } else if (XmlNametable.GC_NICKNAME.equals(name)) {
+ contactEntry.setNickname(XmlUtils.extractChildText(parser));
+ } else if (XmlNametable.GC_OCCUPATION.equals(name)) {
+ contactEntry.setOccupation(XmlUtils.extractChildText(parser));
+ } else if (XmlNametable.GC_PRIORITY.equals(name)) {
+ contactEntry.setPriority(relToType(
+ parser.getAttributeValue(null /* ns */, XmlNametable.REL),
+ REL_TO_TYPE_PRIORITY));
+ } else if (XmlNametable.GC_RELATION.equals(name)) {
+ Relation relation = new Relation();
+ parseTypedElement(relation, parser, REL_TO_TYPE_RELATION);
+ relation.setText(XmlUtils.extractChildText(parser));
+ contactEntry.addRelation(relation);
+ } else if (XmlNametable.GC_SENSITIVITY.equals(name)) {
+ contactEntry.setSensitivity(relToType(
+ parser.getAttributeValue(null /* ns */, XmlNametable.REL),
+ REL_TO_TYPE_SENSITIVITY));
+ } else if (XmlNametable.GC_SHORTNAME.equals(name)) {
+ contactEntry.setShortName(XmlUtils.extractChildText(parser));
+ } else if (XmlNametable.GC_SUBJECT.equals(name)) {
+ contactEntry.setSubject(XmlUtils.extractChildText(parser));
+ } else if (XmlNametable.GC_UDF.equals(name)) {
+ UserDefinedField udf = new UserDefinedField();
+ udf.setKey(parser.getAttributeValue(null /* ns */, XmlNametable.KEY));
+ udf.setValue(parser.getAttributeValue(null /* ns */, XmlNametable.VALUE));
+ contactEntry.addUserDefinedField(udf);
+ } else if (XmlNametable.GC_WEBSITE.equals(name)) {
+ WebSite ws = new WebSite();
+ parseContactsElement(ws, parser, REL_TO_TYPE_WEBSITE);
+ ws.setHRef(parser.getAttributeValue(null /* ns */, XmlNametable.HREF));
+ contactEntry.addWebSite(ws);
+ }
+ }
+ }
+
+ protected void handleExtraLinkInEntry(String rel, String type, String href, Entry entry)
+ throws XmlPullParserException, IOException {
+ if (LINK_REL_PHOTO.equals(rel)) {
+ ContactEntry contactEntry = (ContactEntry) entry;
+ XmlPullParser parser = getParser();
+ String etag = parser.getAttributeValue(NAMESPACE_GD_URI, XmlNametable.ETAG);
+ contactEntry.setLinkPhoto(href, type, etag);
+ }
+ }
+
+ private static void parseContactsElement(ContactsElement element, XmlPullParser parser,
+ Hashtable relToTypeMap) throws XmlPullParserException {
+ parseTypedElement(element, parser, relToTypeMap);
+ element.setIsPrimary("true".equals(parser.getAttributeValue(null /* ns */, XmlNametable.PRIMARY)));
+ }
+
+ private static void parseTypedElement(TypedElement element, XmlPullParser parser,
+ Hashtable relToTypeMap) throws XmlPullParserException {
+ String rel = parser.getAttributeValue(null /* ns */, XmlNametable.REL);
+ String label = parser.getAttributeValue(null /* ns */, XmlNametable.LABEL);
+
+ if ((label == null && rel == null) || (label != null && rel != null)) {
+ // TODO: remove this once the focus feed is fixed to not send this case
+ rel = TYPESTRING_OTHER;
+ }
+
+ if (rel != null) {
+ element.setType(relToType(rel, relToTypeMap));
+ }
+ element.setLabel(label);
+ }
+
+ private static byte relToType(String rel, Hashtable relToTypeMap)
+ throws XmlPullParserException {
+ if (rel != null) {
+ final Object type = relToTypeMap.get(rel.toLowerCase());
+ if (type == null) {
+ throw new XmlPullParserException("unknown rel, " + rel);
+ }
+ return ((Byte) type).byteValue();
+ }
+ return TypedElement.TYPE_NONE;
+ }
+
+ private static void handleOrganizationSubElement(Organization element, XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ int depth = parser.getDepth();
+ while (true) {
+ String tag = XmlUtils.nextDirectChildTag(parser, depth);
+ if (tag == null) break;
+ if (XmlNametable.GD_ORG_NAME.equals(tag)) {
+ element.setName(XmlUtils.extractChildText(parser));
+ } else if (XmlNametable.GD_ORG_TITLE.equals(tag)) {
+ element.setTitle(XmlUtils.extractChildText(parser));
+ } else if (XmlNametable.GD_ORG_DEPARTMENT.equals(tag)) {
+ element.setOrgDepartment(XmlUtils.extractChildText(parser));
+ } else if (XmlNametable.GD_ORG_JOBDESC.equals(tag)) {
+ element.setOrgJobDescription(XmlUtils.extractChildText(parser));
+ } else if (XmlNametable.GD_ORG_SYMBOL.equals(tag)) {
+ element.setOrgSymbol(XmlUtils.extractChildText(parser));
+ } else if (XmlNametable.GD_WHERE.equals(tag)) {
+ String where = parser.getAttributeValue(null /* ns */,
+ XmlNametable.VALUESTRING);
+ element.setWhere(where);
+ }
+ }
+ }
+
+
+ private static void handleEventSubElement(Event element, XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ int depth = parser.getDepth();
+ while (true) {
+ String tag = XmlUtils.nextDirectChildTag(parser, depth);
+ if (tag == null) break;
+ if (XmlNametable.GD_WHEN.equals(tag)) {
+ String startDate = parser.getAttributeValue(null /* ns */,
+ XmlNametable.STARTTIME);
+ element.setStartDate(startDate);
+ }
+ }
+ }
+
+
+ private static void handleNameSubElement(Name element, XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ int depth = parser.getDepth();
+ while (true) {
+ String tag = XmlUtils.nextDirectChildTag(parser, depth);
+ if (tag == null) break;
+ if (XmlNametable.GD_NAME_GIVENNAME.equals(tag)) {
+ element.setGivenName(XmlUtils.extractChildText(parser));
+ } else if (XmlNametable.GD_NAME_ADDITIONALNAME.equals(tag)) {
+ element.setAdditionalNameYomi(
+ parser.getAttributeValue(null /* ns */, XmlNametable.GD_NAME_YOMI));
+ element.setAdditionalName(XmlUtils.extractChildText(parser));
+ } else if (XmlNametable.GD_NAME_FAMILYNAME.equals(tag)) {
+ element.setFamilyNameYomi(
+ parser.getAttributeValue(null /* ns */, XmlNametable.GD_NAME_YOMI));
+ element.setFamilyName(XmlUtils.extractChildText(parser));
+ } else if (XmlNametable.GD_NAME_PREFIX.equals(tag)) {
+ element.setNamePrefix(XmlUtils.extractChildText(parser));
+ } else if (XmlNametable.GD_NAME_SUFFIX.equals(tag)) {
+ element.setNameSuffix(XmlUtils.extractChildText(parser));
+ } else if (XmlNametable.GD_NAME_FULLNAME.equals(tag)) {
+ element.setFullName(XmlUtils.extractChildText(parser));
+ }
+ }
+ }
+
+ private static void handleStructuredPostalAddressSubElement(
+ StructuredPostalAddress element, XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ int depth = parser.getDepth();
+ while (true) {
+ String tag = XmlUtils.nextDirectChildTag(parser, depth);
+ if (tag == null) break;
+ if (XmlNametable.GD_SPA_STREET.equals(tag)) {
+ element.setStreet(XmlUtils.extractChildText(parser));
+ } else if (XmlNametable.GD_SPA_POBOX.equals(tag)) {
+ element.setPobox(XmlUtils.extractChildText(parser));
+ } else if (XmlNametable.GD_SPA_NEIGHBORHOOD.equals(tag)) {
+ element.setNeighborhood(XmlUtils.extractChildText(parser));
+ } else if (XmlNametable.GD_SPA_CITY.equals(tag)) {
+ element.setCity(XmlUtils.extractChildText(parser));
+ } else if (XmlNametable.GD_SPA_REGION.equals(tag)) {
+ element.setRegion(XmlUtils.extractChildText(parser));
+ } else if (XmlNametable.GD_SPA_POSTCODE.equals(tag)) {
+ element.setPostcode(XmlUtils.extractChildText(parser));
+ } else if (XmlNametable.GD_SPA_COUNTRY.equals(tag)) {
+ element.setCountry(XmlUtils.extractChildText(parser));
+ } else if (XmlNametable.GD_SPA_FORMATTEDADDRESS.equals(tag)) {
+ element.setFormattedAddress(XmlUtils.extractChildText(parser));
+ }
+ }
+ }
+
+
+ /**
+ * Parse the ExtendedProperty. The parser is assumed to be at the beginning of the tag
+ * for the ExtendedProperty.
+ * @param extendedProperty the ExtendedProperty object to populate
+ */
+ private void parseExtendedProperty(ExtendedProperty extendedProperty)
+ throws IOException, XmlPullParserException {
+ XmlPullParser parser = getParser();
+ extendedProperty.setName(parser.getAttributeValue(null /* ns */, XmlNametable.GD_NAME));
+ extendedProperty.setValue(parser.getAttributeValue(null /* ns */, XmlNametable.VALUE));
+ extendedProperty.setXmlBlob(XmlUtils.extractFirstChildTextIgnoreRest(parser));
+ }
+}
diff --git a/src/com/google/wireless/gdata2/contacts/parser/xml/XmlContactsGDataParserFactory.java b/src/com/google/wireless/gdata2/contacts/parser/xml/XmlContactsGDataParserFactory.java
new file mode 100644
index 0000000..7439966
--- /dev/null
+++ b/src/com/google/wireless/gdata2/contacts/parser/xml/XmlContactsGDataParserFactory.java
@@ -0,0 +1,137 @@
+// Copyright 2007 The Android Open Source Project
+
+package com.google.wireless.gdata2.contacts.parser.xml;
+
+import com.google.wireless.gdata2.client.GDataParserFactory;
+import com.google.wireless.gdata2.contacts.data.ContactEntry;
+import com.google.wireless.gdata2.contacts.data.GroupEntry;
+import com.google.wireless.gdata2.data.MediaEntry;
+import com.google.wireless.gdata2.contacts.serializer.xml.XmlContactEntryGDataSerializer;
+import com.google.wireless.gdata2.contacts.serializer.xml.XmlGroupEntryGDataSerializer;
+import com.google.wireless.gdata2.data.Entry;
+import com.google.wireless.gdata2.parser.GDataParser;
+import com.google.wireless.gdata2.parser.ParseException;
+import com.google.wireless.gdata2.parser.xml.XmlParserFactory;
+import com.google.wireless.gdata2.parser.xml.XmlMediaEntryGDataParser;
+import com.google.wireless.gdata2.serializer.GDataSerializer;
+import com.google.wireless.gdata2.serializer.xml.XmlBatchGDataSerializer;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.InputStream;
+import java.util.Enumeration;
+
+/**
+ * GDataParserFactory that creates XML GDataParsers and GDataSerializers for
+ * Google Contacts.
+ */
+public class XmlContactsGDataParserFactory implements GDataParserFactory {
+
+ private final XmlParserFactory xmlFactory;
+
+ public XmlContactsGDataParserFactory(XmlParserFactory xmlFactory) {
+ this.xmlFactory = xmlFactory;
+ }
+
+ /**
+ * Returns a parser for a contacts group feed.
+ *
+ * @param is The input stream to be parsed.
+ * @return A parser for the stream.
+ * @throws com.google.wireless.gdata2.parser.ParseException
+ */
+ public GDataParser createGroupEntryFeedParser(InputStream is) throws ParseException {
+ XmlPullParser xmlParser;
+ try {
+ xmlParser = xmlFactory.createParser();
+ } catch (XmlPullParserException xppe) {
+ throw new ParseException("Could not create XmlPullParser", xppe);
+ }
+ return new XmlGroupEntryGDataParser(is, xmlParser);
+ }
+
+ /**
+ * Returns a parser for a media entry feed.
+ *
+ * @param is The input stream to be parsed.
+ * @return A parser for the stream.
+ * @throws ParseException
+ */
+ public GDataParser createMediaEntryFeedParser(InputStream is) throws ParseException {
+ XmlPullParser xmlParser;
+ try {
+ xmlParser = xmlFactory.createParser();
+ } catch (XmlPullParserException xppe) {
+ throw new ParseException("Could not create XmlPullParser", xppe);
+ }
+ return new XmlMediaEntryGDataParser(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 XmlContactsGDataParser(is, xmlParser);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.google.wireless.gdata2.client.GDataParserFactory#createParser(
+ * int, java.io.InputStream)
+ */
+ public GDataParser createParser(Class entryClass, InputStream is)
+ throws ParseException {
+ if (entryClass == ContactEntry.class) {
+ return createParser(is);
+ }
+ if (entryClass == GroupEntry.class) {
+ return createGroupEntryFeedParser(is);
+ }
+ if (entryClass == MediaEntry.class) {
+ return createMediaEntryFeedParser(is);
+ }
+ throw new IllegalArgumentException("unexpected feed type, " + entryClass.getName());
+ }
+
+ /**
+ * Creates a new {@link GDataSerializer} for the provided entry. The entry
+ * <strong>must</strong> be an instance of {@link ContactEntry} or {@link GroupEntry}.
+ *
+ * @param entry The {@link ContactEntry} that should be serialized.
+ * @return The {@link GDataSerializer} that will serialize this entry.
+ * @throws IllegalArgumentException Thrown if entry is not a
+ * {@link ContactEntry} or {@link GroupEntry}.
+ * @see com.google.wireless.gdata2.client.GDataParserFactory#createSerializer
+ */
+ public GDataSerializer createSerializer(Entry entry) {
+ if (entry instanceof ContactEntry) {
+ ContactEntry contactEntry = (ContactEntry) entry;
+ return new XmlContactEntryGDataSerializer(xmlFactory, contactEntry);
+ }
+ if (entry instanceof GroupEntry) {
+ GroupEntry groupEntry = (GroupEntry) entry;
+ return new XmlGroupEntryGDataSerializer(xmlFactory, groupEntry);
+ }
+ throw new IllegalArgumentException("unexpected entry type, " + entry.getClass().toString());
+ }
+
+ /**
+ * Creates a new {@link GDataSerializer} for the given batch.
+ *
+ * @param batch the {@link Enumeration} of entries in the batch.
+ * @return The {@link GDataSerializer} that will serialize this batch.
+ */
+ public GDataSerializer createSerializer(Enumeration batch) {
+ return new XmlBatchGDataSerializer(this, xmlFactory, batch);
+ }
+}
diff --git a/src/com/google/wireless/gdata2/contacts/parser/xml/XmlGroupEntryGDataParser.java b/src/com/google/wireless/gdata2/contacts/parser/xml/XmlGroupEntryGDataParser.java
new file mode 100644
index 0000000..943eb41
--- /dev/null
+++ b/src/com/google/wireless/gdata2/contacts/parser/xml/XmlGroupEntryGDataParser.java
@@ -0,0 +1,62 @@
+// Copyright 2009 The Android Open Source Project
+
+package com.google.wireless.gdata2.contacts.parser.xml;
+
+import com.google.wireless.gdata2.contacts.data.GroupEntry;
+import com.google.wireless.gdata2.contacts.data.GroupsFeed;
+import com.google.wireless.gdata2.data.Entry;
+import com.google.wireless.gdata2.data.Feed;
+import com.google.wireless.gdata2.data.StringUtils;
+import com.google.wireless.gdata2.parser.ParseException;
+import com.google.wireless.gdata2.parser.xml.XmlGDataParser;
+
+import org.xmlpull.v1.XmlPullParser;
+
+import java.io.InputStream;
+
+/**
+ * GDataParser for a contact groups feed.
+ */
+public class XmlGroupEntryGDataParser extends XmlGDataParser {
+ /**
+ * Creates a new XmlGroupEntryGDataParser.
+ * @param is The InputStream that should be parsed.
+ * @param parser the XmlPullParser to use for the xml parsing
+ * @throws ParseException Thrown if a parser cannot be created.
+ */
+ public XmlGroupEntryGDataParser(InputStream is, XmlPullParser parser) throws ParseException {
+ super(is, parser);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.google.wireless.gdata2.parser.xml.XmlGDataParser#createFeed()
+ */
+ protected Feed createFeed() {
+ return new GroupsFeed();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.google.wireless.gdata2.parser.xml.XmlGDataParser#createEntry()
+ */
+ protected Entry createEntry() {
+ return new GroupEntry();
+ }
+
+ protected void handleExtraElementInEntry(Entry entry) {
+ XmlPullParser parser = getParser();
+
+ if (!(entry instanceof GroupEntry)) {
+ throw new IllegalArgumentException("Expected GroupEntry!");
+ }
+ GroupEntry groupEntry = (GroupEntry) entry;
+ String name = parser.getName();
+ if ("systemGroup".equals(name)) {
+ String systemGroup = parser.getAttributeValue(null /* ns */, "id");
+ // if the systemGroup is the empty string, convert it to a null
+ if (StringUtils.isEmpty(systemGroup)) systemGroup = null;
+ groupEntry.setSystemGroup(systemGroup);
+ }
+ }
+}
diff --git a/src/com/google/wireless/gdata2/contacts/parser/xml/XmlNametable.java b/src/com/google/wireless/gdata2/contacts/parser/xml/XmlNametable.java
new file mode 100644
index 0000000..44c3219
--- /dev/null
+++ b/src/com/google/wireless/gdata2/contacts/parser/xml/XmlNametable.java
@@ -0,0 +1,83 @@
+// Copyright 2009 The Android Open Source Project
+
+package com.google.wireless.gdata2.contacts.parser.xml;
+
+/**
+ * Class to hold static strings that are used to parse and
+ * serialize the xml elements in the contacts feed
+ */
+public final class XmlNametable {
+ private XmlNametable() {}
+
+ // generic strings for attributes
+ public static String HREF = "href";
+ public static String LABEL = "label";
+ public static String VALUE = "value";
+ public static String CODE = "code";
+ public static String REL = "rel";
+ public static String KEY = "key";
+ public static String ETAG = "etag";
+ public static String VALUESTRING = "valueString";
+ public static String STARTTIME = "startTime";
+ public static String PRIMARY = "primary";
+
+ // GD namespace
+ public static String GD_EMAIL = "email";
+ public static String GD_EMAIL_DISPLAYNAME = "displayName";
+ public static String GD_ADDRESS = "address";
+ public static String GD_PROTOCOL = "protocol";
+ public static String GD_IM = "im";
+ public static String GD_DELETED = "deleted";
+ public static String GD_NAME = "name";
+ public static String GD_NAME_GIVENNAME = "givenName";
+ public static String GD_NAME_ADDITIONALNAME = "additionalName";
+ public static String GD_NAME_YOMI = "yomi";
+ public static String GD_NAME_FAMILYNAME = "familyName";
+ public static String GD_NAME_PREFIX = "namePrefix";
+ public static String GD_NAME_SUFFIX = "nameSuffix";
+ public static String GD_NAME_FULLNAME = "fullName";
+ public static String GD_SPA = "structuredPostalAddress";
+ public static String GD_SPA_STREET = "street";
+ public static String GD_SPA_POBOX = "pobox";
+ public static String GD_SPA_NEIGHBORHOOD = "neighborhood";
+ public static String GD_SPA_CITY = "city";
+ public static String GD_SPA_REGION = "region";
+ public static String GD_SPA_POSTCODE = "postcode";
+ public static String GD_SPA_COUNTRY = "country";
+ public static String GD_SPA_FORMATTEDADDRESS = "formattedAddress";
+ public static String GD_PHONENUMBER = "phoneNumber";
+ public static String GD_ORGANIZATION = "organization";
+ public static String GD_EXTENDEDPROPERTY = "extendedProperty";
+ public static String GD_WHEN = "when";
+ public static String GD_WHERE = "where";
+ public static String GD_ORG_NAME = "orgName";
+ public static String GD_ORG_TITLE = "orgTitle";
+ public static String GD_ORG_DEPARTMENT = "orgDepartment";
+ public static String GD_ORG_JOBDESC = "orgJobDescription";
+ public static String GD_ORG_SYMBOL = "orgSymbol";
+
+ // Contacts namespace
+ public static String GC_GMI = "groupMembershipInfo";
+ public static String GC_BIRTHDAY = "birthday";
+ public static String GC_BILLINGINFO = "billingInformation";
+ public static String GC_CALENDARLINK = "calendarLink";
+ public static String GC_DIRECTORYSERVER = "directoryServer";
+ public static String GC_EVENT = "event";
+ public static String GC_EXTERNALID = "externalId";
+ public static String GC_GENDER = "gender";
+ public static String GC_HOBBY = "hobby";
+ public static String GC_INITIALS = "initials";
+ public static String GC_JOT = "jot";
+ public static String GC_LANGUAGE = "language";
+ public static String GC_MAIDENNAME = "maidenName";
+ public static String GC_MILEAGE = "mileage";
+ public static String GC_NICKNAME = "nickname";
+ public static String GC_OCCUPATION = "occupation";
+ public static String GC_PRIORITY = "priority";
+ public static String GC_RELATION = "relation";
+ public static String GC_SENSITIVITY = "sensitivity";
+ public static String GC_SHORTNAME = "shortName";
+ public static String GC_SUBJECT = "subject";
+ public static String GC_UDF = "userDefinedField";
+ public static String GC_WEBSITE ="website";
+} \ No newline at end of file
diff --git a/src/com/google/wireless/gdata/spreadsheets/serializer/package.html b/src/com/google/wireless/gdata2/contacts/parser/xml/package.html
index 1c9bf9d..1c9bf9d 100644
--- a/src/com/google/wireless/gdata/spreadsheets/serializer/package.html
+++ b/src/com/google/wireless/gdata2/contacts/parser/xml/package.html
diff --git a/src/com/google/wireless/gdata/spreadsheets/serializer/xml/package.html b/src/com/google/wireless/gdata2/contacts/serializer/package.html
index 1c9bf9d..1c9bf9d 100644
--- a/src/com/google/wireless/gdata/spreadsheets/serializer/xml/package.html
+++ b/src/com/google/wireless/gdata2/contacts/serializer/package.html
diff --git a/src/com/google/wireless/gdata2/contacts/serializer/xml/XmlContactEntryGDataSerializer.java b/src/com/google/wireless/gdata2/contacts/serializer/xml/XmlContactEntryGDataSerializer.java
new file mode 100644
index 0000000..78e898a
--- /dev/null
+++ b/src/com/google/wireless/gdata2/contacts/serializer/xml/XmlContactEntryGDataSerializer.java
@@ -0,0 +1,557 @@
+// Copyright 2009 The Android Open Source Project
+
+package com.google.wireless.gdata2.contacts.serializer.xml;
+
+import com.google.wireless.gdata2.contacts.data.CalendarLink;
+import com.google.wireless.gdata2.contacts.data.ContactEntry;
+import com.google.wireless.gdata2.contacts.data.ContactsElement;
+import com.google.wireless.gdata2.contacts.data.EmailAddress;
+import com.google.wireless.gdata2.contacts.data.Event;
+import com.google.wireless.gdata2.contacts.data.ExternalId;
+import com.google.wireless.gdata2.contacts.data.GroupMembershipInfo;
+import com.google.wireless.gdata2.contacts.data.ImAddress;
+import com.google.wireless.gdata2.contacts.data.Jot;
+import com.google.wireless.gdata2.contacts.data.Language;
+import com.google.wireless.gdata2.contacts.data.Name;
+import com.google.wireless.gdata2.contacts.data.Organization;
+import com.google.wireless.gdata2.contacts.data.PhoneNumber;
+import com.google.wireless.gdata2.contacts.data.Relation;
+import com.google.wireless.gdata2.contacts.data.StructuredPostalAddress;
+import com.google.wireless.gdata2.contacts.data.TypedElement;
+import com.google.wireless.gdata2.contacts.data.UserDefinedField;
+import com.google.wireless.gdata2.contacts.data.WebSite;
+import com.google.wireless.gdata2.contacts.parser.xml.XmlContactsGDataParser;
+import com.google.wireless.gdata2.contacts.parser.xml.XmlNametable;
+import com.google.wireless.gdata2.data.ExtendedProperty;
+import com.google.wireless.gdata2.data.StringUtils;
+import com.google.wireless.gdata2.parser.ParseException;
+import com.google.wireless.gdata2.parser.xml.XmlGDataParser;
+import com.google.wireless.gdata2.parser.xml.XmlParserFactory;
+import com.google.wireless.gdata2.serializer.xml.XmlEntryGDataSerializer;
+
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import org.xmlpull.v1.XmlSerializer;
+
+
+/**
+ * Serializes Google Contact entries into the Atom XML format.
+ */
+public class XmlContactEntryGDataSerializer extends XmlEntryGDataSerializer {
+
+ public XmlContactEntryGDataSerializer(XmlParserFactory factory, ContactEntry entry) {
+ super(factory, entry);
+ }
+
+ protected void declareExtraEntryNamespaces(XmlSerializer serializer) throws IOException {
+ super.declareExtraEntryNamespaces(serializer);
+ serializer.setPrefix(XmlContactsGDataParser.NAMESPACE_CONTACTS,
+ XmlContactsGDataParser.NAMESPACE_CONTACTS_URI);
+ }
+
+ protected ContactEntry getContactEntry() {
+ return (ContactEntry) getEntry();
+ }
+
+ /* (non-Javadoc)
+ * @see XmlEntryGDataSerializer#serializeExtraEntryContents
+ */
+ protected void serializeExtraEntryContents(XmlSerializer serializer, int format)
+ throws ParseException, IOException {
+ ContactEntry entry = getContactEntry();
+ entry.validate();
+
+ serializeLink(serializer, XmlContactsGDataParser.LINK_REL_PHOTO,
+ entry.getLinkPhotoHref(), entry.getLinkPhotoType(), entry.getLinkPhotoETag());
+
+ Enumeration eachEmail = entry.getEmailAddresses().elements();
+ while (eachEmail.hasMoreElements()) {
+ serialize(serializer, (EmailAddress) eachEmail.nextElement());
+ }
+
+ Enumeration eachIm = entry.getImAddresses().elements();
+ while (eachIm.hasMoreElements()) {
+ serialize(serializer, (ImAddress) eachIm.nextElement());
+ }
+
+ Enumeration eachPhone = entry.getPhoneNumbers().elements();
+ while (eachPhone.hasMoreElements()) {
+ serialize(serializer, (PhoneNumber) eachPhone.nextElement());
+ }
+
+ Enumeration eachAddress = entry.getPostalAddresses().elements();
+ while (eachAddress.hasMoreElements()) {
+ serialize(serializer, (StructuredPostalAddress) eachAddress.nextElement());
+ }
+
+ Enumeration eachOrganization = entry.getOrganizations().elements();
+ while (eachOrganization.hasMoreElements()) {
+ serialize(serializer, (Organization) eachOrganization.nextElement());
+ }
+
+ Enumeration eachExtendedProperty = entry.getExtendedProperties().elements();
+ while (eachExtendedProperty.hasMoreElements()) {
+ serialize(serializer, (ExtendedProperty) eachExtendedProperty.nextElement());
+ }
+
+ Enumeration eachGroup = entry.getGroups().elements();
+ while (eachGroup.hasMoreElements()) {
+ serialize(serializer, (GroupMembershipInfo) eachGroup.nextElement());
+ }
+
+ Enumeration eachCalendar = entry.getCalendarLinks().elements();
+ while (eachCalendar.hasMoreElements()) {
+ serialize(serializer, (CalendarLink) eachCalendar.nextElement());
+ }
+
+ Enumeration eachEvent = entry.getEvents().elements();
+ while (eachEvent.hasMoreElements()) {
+ serialize(serializer, (Event) eachEvent.nextElement());
+ }
+
+ Enumeration eachWebsite = entry.getWebSites().elements();
+ while (eachWebsite.hasMoreElements()) {
+ serialize(serializer, (WebSite) eachWebsite.nextElement());
+ }
+
+ Enumeration eachExternalId = entry.getExternalIds().elements();
+ while (eachExternalId.hasMoreElements()) {
+ serialize(serializer, (ExternalId) eachExternalId.nextElement());
+ }
+
+ Enumeration eachHobby = entry.getHobbies().elements();
+ while (eachHobby.hasMoreElements()) {
+ serializeHobby(serializer, (String) eachHobby.nextElement());
+ }
+
+ Enumeration eachJot = entry.getJots().elements();
+ while (eachJot.hasMoreElements()) {
+ serialize(serializer, (Jot) eachJot.nextElement());
+ }
+
+ Enumeration eachLanguage = entry.getLanguages().elements();
+ while (eachLanguage.hasMoreElements()) {
+ serialize(serializer, (Language) eachLanguage.nextElement());
+ }
+
+ Enumeration eachRelation = entry.getRelations().elements();
+ while (eachRelation.hasMoreElements()) {
+ serialize(serializer, (Relation) eachRelation.nextElement());
+ }
+
+ Enumeration eachUDF = entry.getUserDefinedFields().elements();
+ while (eachUDF.hasMoreElements()) {
+ serialize(serializer, (UserDefinedField) eachUDF.nextElement());
+ }
+
+ serializeBirthday(serializer, entry.getBirthday());
+
+ // now serialize simple properties
+
+ serializeElement(serializer, entry.getDirectoryServer(), XmlNametable.GC_DIRECTORYSERVER);
+ serializeGenderElement(serializer, entry.getGender());
+ serializeElement(serializer, entry.getInitials(), XmlNametable.GC_INITIALS);
+ serializeElement(serializer, entry.getMaidenName(), XmlNametable.GC_MAIDENNAME);
+ serializeElement(serializer, entry.getMileage(), XmlNametable.GC_MILEAGE);
+ serializeElement(serializer, entry.getNickname(), XmlNametable.GC_NICKNAME);
+ serializeElement(serializer, entry.getOccupation(), XmlNametable.GC_OCCUPATION);
+ serializeElement(serializer, entry.getShortName(), XmlNametable.GC_SHORTNAME);
+ serializeElement(serializer, entry.getSubject(), XmlNametable.GC_SUBJECT);
+ serializeElement(serializer, entry.getBillingInformation(), XmlNametable.GC_BILLINGINFO);
+ serializeElement(serializer, entry.getPriority(),
+ XmlNametable.GC_PRIORITY, XmlContactsGDataParser.TYPE_TO_REL_PRIORITY);
+ serializeElement(serializer, entry.getSensitivity(),
+ XmlNametable.GC_SENSITIVITY, XmlContactsGDataParser.TYPE_TO_REL_SENSITIVITY);
+
+ serializeName(serializer, entry.getName());
+ }
+
+ private static void serialize(XmlSerializer serializer, EmailAddress email)
+ throws IOException, ParseException {
+ if (StringUtils.isEmptyOrWhitespace(email.getAddress())) return;
+ serializer.startTag(XmlGDataParser.NAMESPACE_GD_URI, XmlNametable.GD_EMAIL);
+ serializeContactsElement(serializer, email, XmlContactsGDataParser.TYPE_TO_REL_EMAIL);
+ serializer.attribute(null /* ns */, XmlNametable.GD_ADDRESS, email.getAddress());
+ serializer.endTag(XmlGDataParser.NAMESPACE_GD_URI, XmlNametable.GD_EMAIL);
+ }
+
+ private static void serialize(XmlSerializer serializer, ImAddress im)
+ throws IOException, ParseException {
+ if (StringUtils.isEmptyOrWhitespace(im.getAddress())) return;
+
+ serializer.startTag(XmlGDataParser.NAMESPACE_GD_URI, XmlNametable.GD_IM);
+ serializeContactsElement(serializer, im, XmlContactsGDataParser.TYPE_TO_REL_IM);
+ serializer.attribute(null /* ns */, XmlNametable.GD_ADDRESS, im.getAddress());
+
+ String protocolString;
+ switch (im.getProtocolPredefined()) {
+ case ImAddress.PROTOCOL_NONE:
+ // don't include the attribute if no protocol was specified
+ break;
+
+ case ImAddress.PROTOCOL_CUSTOM:
+ protocolString = im.getProtocolCustom();
+ if (protocolString == null) {
+ throw new IllegalArgumentException(
+ "the protocol is custom, but the custom string is null");
+ }
+ serializer.attribute(null /* ns */, XmlNametable.GD_PROTOCOL, protocolString);
+ break;
+
+ default:
+ protocolString = (String)XmlContactsGDataParser.IM_PROTOCOL_TYPE_TO_STRING_MAP.get(
+ new Byte(im.getProtocolPredefined()));
+ serializer.attribute(null /* ns */, XmlNametable.GD_PROTOCOL, protocolString);
+ break;
+ }
+
+ serializer.endTag(XmlGDataParser.NAMESPACE_GD_URI, XmlNametable.GD_IM);
+ }
+
+ private static void serialize(XmlSerializer serializer, PhoneNumber phone)
+ throws IOException, ParseException {
+ if (StringUtils.isEmptyOrWhitespace(phone.getPhoneNumber())) return;
+ serializer.startTag(XmlGDataParser.NAMESPACE_GD_URI, XmlNametable.GD_PHONENUMBER);
+ serializeContactsElement(serializer, phone, XmlContactsGDataParser.TYPE_TO_REL_PHONE);
+ serializer.text(phone.getPhoneNumber());
+ serializer.endTag(XmlGDataParser.NAMESPACE_GD_URI, XmlNametable.GD_PHONENUMBER);
+ }
+
+ private static void serialize(XmlSerializer serializer, Organization organization)
+ throws IOException, ParseException {
+
+ serializer.startTag(XmlGDataParser.NAMESPACE_GD_URI, XmlNametable.GD_ORGANIZATION);
+ serializeContactsElement(serializer,
+ organization, XmlContactsGDataParser.TYPE_TO_REL_ORGANIZATION);
+ serializeGDSubelement(serializer, organization.getName(),
+ XmlNametable.GD_ORG_NAME);
+ serializeGDSubelement(serializer, organization.getTitle(),
+ XmlNametable.GD_ORG_TITLE);
+ serializeGDSubelement(serializer, organization.getOrgDepartment(),
+ XmlNametable.GD_ORG_DEPARTMENT);
+ serializeGDSubelement(serializer, organization.getOrgJobDescription(),
+ XmlNametable.GD_ORG_JOBDESC);
+ serializeGDSubelement(serializer, organization.getOrgSymbol(),
+ XmlNametable.GD_ORG_SYMBOL);
+
+ final String where = organization.getWhere();
+ if (!StringUtils.isEmpty(where)) {
+ serializer.startTag(XmlGDataParser.NAMESPACE_GD_URI, XmlNametable.GD_WHERE);
+ serializer.attribute(null /* ns */, XmlNametable.VALUESTRING, where);
+ serializer.endTag(XmlGDataParser.NAMESPACE_GD_URI, XmlNametable.GD_WHERE);
+ }
+
+ serializer.endTag(XmlGDataParser.NAMESPACE_GD_URI, XmlNametable.GD_ORGANIZATION);
+ }
+
+
+ /**
+ * Gets called out of the main serializer loop. Parameters are
+ * not null.
+ *
+ * @param serializer
+ * @param addr
+ */
+ private static void serialize(XmlSerializer serializer, StructuredPostalAddress addr)
+ throws IOException, ParseException {
+ serializer.startTag(XmlGDataParser.NAMESPACE_GD_URI, XmlNametable.GD_SPA);
+ serializeContactsElement(serializer, addr, XmlContactsGDataParser.TYPE_TO_REL_POSTAL);
+ serializeGDSubelement(serializer, addr.getStreet(), XmlNametable.GD_SPA_STREET);
+ serializeGDSubelement(serializer, addr.getPobox(), XmlNametable.GD_SPA_POBOX);
+ serializeGDSubelement(serializer, addr.getNeighborhood(), XmlNametable.GD_SPA_NEIGHBORHOOD);
+ serializeGDSubelement(serializer, addr.getCity(), XmlNametable.GD_SPA_CITY);
+ serializeGDSubelement(serializer, addr.getRegion(), XmlNametable.GD_SPA_REGION);
+ serializeGDSubelement(serializer, addr.getPostcode(), XmlNametable.GD_SPA_POSTCODE);
+ serializeGDSubelement(serializer, addr.getCountry(), XmlNametable.GD_SPA_COUNTRY);
+ serializeGDSubelement(serializer, addr.getFormattedAddress(),
+ XmlNametable.GD_SPA_FORMATTEDADDRESS);
+ serializer.endTag(XmlGDataParser.NAMESPACE_GD_URI, XmlNametable.GD_SPA);
+ }
+
+ private static void serializeGDSubelement(XmlSerializer serializer, String value,
+ String elementName)
+ throws IOException {
+ if (StringUtils.isEmpty(value)) return;
+ serializer.startTag(XmlContactsGDataParser.NAMESPACE_GD_URI, elementName);
+ serializer.text(value);
+ serializer.endTag(XmlContactsGDataParser.NAMESPACE_GD_URI, elementName);
+ }
+
+
+ private static void serializeTypedElement(XmlSerializer serializer, TypedElement element,
+ Hashtable typeToRelMap) throws IOException, ParseException {
+
+ final String label = element.getLabel();
+ byte type = element.getType();
+ boolean hasType = type != TypedElement.TYPE_NONE;
+
+ // validate the element
+ element.validate();
+
+ if (label != null) {
+ serializer.attribute(null /* ns */, XmlNametable.LABEL, label);
+ }
+ if (hasType) {
+ serializeRelation(serializer, type, typeToRelMap);
+ }
+ }
+
+ private static void serializeRelation(XmlSerializer serializer, byte type,
+ Hashtable typeToRelMap) throws IOException {
+
+ serializer.attribute(null /* ns */, XmlNametable.REL,
+ (String)typeToRelMap.get(new Byte(type)));
+ }
+
+ private static void serializeContactsElement(XmlSerializer serializer, ContactsElement element,
+ Hashtable typeToRelMap) throws IOException, ParseException {
+ serializeTypedElement(serializer, element, typeToRelMap);
+ if (element.isPrimary()) {
+ serializer.attribute(null /* ns */, XmlNametable.PRIMARY, "true");
+ }
+ }
+
+ private static void serialize(XmlSerializer serializer, GroupMembershipInfo groupMembershipInfo)
+ throws IOException, ParseException {
+ final String group = groupMembershipInfo.getGroup();
+ final boolean isDeleted = groupMembershipInfo.isDeleted();
+
+ if (StringUtils.isEmptyOrWhitespace(group)) {
+ throw new ParseException("the group must not be empty");
+ }
+
+ serializer.startTag(XmlContactsGDataParser.NAMESPACE_CONTACTS_URI, XmlNametable.GC_GMI);
+ serializer.attribute(null /* ns */, XmlNametable.HREF, group);
+ serializer.attribute(null /* ns */, XmlNametable.GD_DELETED, isDeleted ? "true" : "false");
+ serializer.endTag(XmlContactsGDataParser.NAMESPACE_CONTACTS_URI, XmlNametable.GC_GMI);
+ }
+
+ private static void serialize(XmlSerializer serializer, ExtendedProperty extendedProperty)
+ throws IOException {
+ final String name = extendedProperty.getName();
+ final String value = extendedProperty.getValue();
+ final String xmlBlob = extendedProperty.getXmlBlob();
+
+ serializer.startTag(XmlGDataParser.NAMESPACE_GD_URI, XmlNametable.GD_EXTENDEDPROPERTY);
+ if (!StringUtils.isEmpty(name)) {
+ serializer.attribute(null /* ns */, XmlNametable.GD_NAME, name);
+ }
+ if (!StringUtils.isEmpty(value)) {
+ serializer.attribute(null /* ns */, XmlNametable.VALUE, value);
+ }
+ if (!StringUtils.isEmpty(xmlBlob)) {
+ serializeBlob(serializer, xmlBlob);
+ }
+ serializer.endTag(XmlGDataParser.NAMESPACE_GD_URI, XmlNametable.GD_EXTENDEDPROPERTY);
+ }
+
+ private static void serializeBlob(XmlSerializer serializer, String blob)
+ throws IOException {
+ serializer.text(blob);
+ }
+
+ /**
+ * takes a typed element and a string value and determines if
+ * the element and the string together should be serialized.
+ * if the string is non empty, or the typedelement is worthy of
+ * serialization, this will return true.
+ *
+ * @param element
+ * @param value
+ *
+ * @return boolean
+ */
+ private static boolean shouldSerialize(TypedElement element, String value)
+ {
+ if (element.getType() != TypedElement.TYPE_NONE) {
+ return true;
+ }
+ if (!StringUtils.isEmptyOrWhitespace(element.getLabel())) {
+ return true;
+ }
+ if (!StringUtils.isEmptyOrWhitespace(value)) {
+ return true;
+ }
+ return false;
+ }
+
+ private static void serialize(XmlSerializer serializer, CalendarLink calendarLink)
+ throws IOException, ParseException {
+ final String href = calendarLink.getHRef();
+
+ if (shouldSerialize(calendarLink, href)) {
+ serializer.startTag(XmlContactsGDataParser.NAMESPACE_CONTACTS_URI,
+ XmlNametable.GC_CALENDARLINK);
+ serializeContactsElement(serializer, calendarLink,
+ XmlContactsGDataParser.TYPE_TO_REL_CALENDARLINK);
+ if (!StringUtils.isEmpty(href)) {
+ serializer.attribute(null /* ns */, XmlNametable.HREF, href);
+ }
+ serializer.endTag(XmlContactsGDataParser.NAMESPACE_CONTACTS_URI,
+ XmlNametable.GC_CALENDARLINK);
+ }
+ }
+
+ private static void serialize(XmlSerializer serializer, Event event)
+ throws IOException, ParseException {
+ final String startDate = event.getStartDate();
+ if (shouldSerialize(event, startDate)) {
+ serializer.startTag(XmlContactsGDataParser.NAMESPACE_CONTACTS_URI, XmlNametable.GC_EVENT);
+ serializeTypedElement(serializer, event, XmlContactsGDataParser.TYPE_TO_REL_EVENT);
+ if (!StringUtils.isEmpty(startDate)) {
+ serializer.startTag(XmlContactsGDataParser.NAMESPACE_GD_URI, XmlNametable.GD_WHEN);
+ serializer.attribute(null /* ns */, XmlNametable.STARTTIME, startDate);
+ serializer.endTag(XmlContactsGDataParser.NAMESPACE_GD_URI, XmlNametable.GD_WHEN);
+ }
+ serializer.endTag(XmlContactsGDataParser.NAMESPACE_CONTACTS_URI, XmlNametable.GC_EVENT);
+ }
+ }
+
+ private static void serialize(XmlSerializer serializer, ExternalId externalId)
+ throws IOException, ParseException {
+ final String value = externalId.getValue();
+ if (shouldSerialize(externalId, value)) {
+ serializer.startTag(XmlContactsGDataParser.NAMESPACE_CONTACTS_URI,
+ XmlNametable.GC_EXTERNALID);
+ serializeTypedElement(serializer, externalId,
+ XmlContactsGDataParser.TYPE_TO_REL_EXTERNALID);
+ if (!StringUtils.isEmpty(value)) {
+ serializer.attribute(null /* ns */, XmlNametable.VALUE, value);
+ }
+ serializer.endTag(XmlContactsGDataParser.NAMESPACE_CONTACTS_URI,
+ XmlNametable.GC_EXTERNALID);
+ }
+ }
+
+ private static void serializeHobby(XmlSerializer serializer, String hobby)
+ throws IOException {
+ if (StringUtils.isEmptyOrWhitespace(hobby)) return;
+ serializer.startTag(XmlContactsGDataParser.NAMESPACE_CONTACTS_URI, XmlNametable.GC_HOBBY);
+ serializer.text(hobby);
+ serializer.endTag(XmlContactsGDataParser.NAMESPACE_CONTACTS_URI, XmlNametable.GC_HOBBY);
+ }
+
+ private static void serializeBirthday(XmlSerializer serializer, String birthday)
+ throws IOException {
+ if (StringUtils.isEmptyOrWhitespace(birthday)) return;
+ serializer.startTag(XmlContactsGDataParser.NAMESPACE_CONTACTS_URI, XmlNametable.GC_BIRTHDAY);
+ serializer.attribute(null /* ns */, XmlNametable.GD_WHEN, birthday);
+ serializer.endTag(XmlContactsGDataParser.NAMESPACE_CONTACTS_URI, XmlNametable.GC_BIRTHDAY);
+ }
+
+ private static void serialize(XmlSerializer serializer, Jot jot)
+ throws IOException {
+ final String value = jot.getLabel();
+
+ if (!StringUtils.isEmptyOrWhitespace(value)) {
+ serializer.startTag(XmlContactsGDataParser.NAMESPACE_CONTACTS_URI, XmlNametable.GC_JOT);
+ serializeRelation(serializer, jot.getType(), XmlContactsGDataParser.TYPE_TO_REL_JOT);
+ serializer.text(value);
+ serializer.endTag(XmlContactsGDataParser.NAMESPACE_CONTACTS_URI, XmlNametable.GC_JOT);
+ }
+ }
+
+ private static void serialize(XmlSerializer serializer, Language language)
+ throws IOException, ParseException {
+ language.validate();
+ serializer.startTag(XmlContactsGDataParser.NAMESPACE_CONTACTS_URI, XmlNametable.GC_LANGUAGE);
+ final String value = language.getCode();
+ if (!StringUtils.isEmptyOrWhitespace(value)) {
+ serializer.attribute(null /* ns */, XmlNametable.CODE, value);
+ } else {
+ serializer.attribute(null /* ns */, XmlNametable.LABEL, language.getLabel());
+ }
+ serializer.endTag(XmlContactsGDataParser.NAMESPACE_CONTACTS_URI, XmlNametable.GC_LANGUAGE);
+ }
+
+ private static void serialize(XmlSerializer serializer, Relation relation)
+ throws IOException, ParseException {
+ final String value = relation.getText();
+ if (shouldSerialize(relation, value)) {
+ serializer.startTag(XmlContactsGDataParser.NAMESPACE_CONTACTS_URI, XmlNametable.GC_RELATION);
+ serializeTypedElement(serializer, relation, XmlContactsGDataParser.TYPE_TO_REL_RELATION);
+ serializer.text(value);
+ serializer.endTag(XmlContactsGDataParser.NAMESPACE_CONTACTS_URI, XmlNametable.GC_RELATION);
+ }
+ }
+
+ private static void serialize(XmlSerializer serializer, UserDefinedField udf)
+ throws IOException, ParseException {
+ udf.validate();
+ serializer.startTag(XmlContactsGDataParser.NAMESPACE_CONTACTS_URI, XmlNametable.GC_UDF);
+ serializer.attribute(null /* ns */, XmlNametable.KEY, udf.getKey());
+ serializer.attribute(null /* ns */, XmlNametable.VALUE, udf.getValue());
+ serializer.endTag(XmlContactsGDataParser.NAMESPACE_CONTACTS_URI, XmlNametable.GC_UDF);
+ }
+
+ private static void serialize(XmlSerializer serializer, WebSite webSite)
+ throws IOException, ParseException {
+ final String href = webSite.getHRef();
+
+ if (shouldSerialize(webSite, href)) {
+ serializer.startTag(XmlContactsGDataParser.NAMESPACE_CONTACTS_URI, XmlNametable.GC_WEBSITE);
+ serializeContactsElement(serializer, webSite, XmlContactsGDataParser.TYPE_TO_REL_WEBSITE);
+ if (!StringUtils.isEmpty(href)) {
+ serializer.attribute(null /* ns */, XmlNametable.HREF, href);
+ }
+ serializer.endTag(XmlContactsGDataParser.NAMESPACE_CONTACTS_URI, XmlNametable.GC_WEBSITE);
+ }
+ }
+
+ private static void serializeElement(XmlSerializer serializer, String value, String elementName)
+ throws IOException {
+ if (StringUtils.isEmpty(value)) return;
+ serializer.startTag(XmlContactsGDataParser.NAMESPACE_CONTACTS_URI, elementName);
+ serializer.text(value);
+ serializer.endTag(XmlContactsGDataParser.NAMESPACE_CONTACTS_URI, elementName);
+ }
+
+ private static void serializeGenderElement(XmlSerializer serializer, String value)
+ throws IOException {
+ if (StringUtils.isEmpty(value)) return;
+ serializer.startTag(XmlContactsGDataParser.NAMESPACE_CONTACTS_URI, XmlNametable.GC_GENDER);
+ serializer.attribute(null /* ns */, XmlNametable.VALUE, value);
+ serializer.endTag(XmlContactsGDataParser.NAMESPACE_CONTACTS_URI, XmlNametable.GC_GENDER);
+ }
+
+ private static void serializeElement(XmlSerializer serializer, byte value, String elementName,
+ Hashtable typeToRelMap) throws IOException {
+ if (value == TypedElement.TYPE_NONE) return;
+ serializer.startTag(XmlContactsGDataParser.NAMESPACE_CONTACTS_URI, elementName);
+ serializeRelation(serializer, value, typeToRelMap);
+ serializer.endTag(XmlContactsGDataParser.NAMESPACE_CONTACTS_URI, elementName);
+ }
+
+ private static void serializeName(XmlSerializer serializer, Name name)
+ throws IOException {
+ if (name == null) return;
+ serializer.startTag(XmlContactsGDataParser.NAMESPACE_GD_URI, XmlNametable.GD_NAME);
+ serializeNameSubelement(serializer, name.getGivenName(),
+ name.getGivenNameYomi(), XmlNametable.GD_NAME_GIVENNAME);
+ serializeNameSubelement(serializer, name.getAdditionalName(),
+ name.getAdditionalNameYomi(), XmlNametable.GD_NAME_ADDITIONALNAME);
+ serializeNameSubelement(serializer, name.getFamilyName(),
+ name.getFamilyNameYomi(), XmlNametable.GD_NAME_FAMILYNAME);
+ serializeNameSubelement(serializer, name.getNamePrefix(),
+ null /* yomi */, XmlNametable.GD_NAME_PREFIX);
+ serializeNameSubelement(serializer, name.getNameSuffix(),
+ null /* yomi */, XmlNametable.GD_NAME_SUFFIX);
+ serializeNameSubelement(serializer, name.getFullName(),
+ null /* yomi */, XmlNametable.GD_NAME_FULLNAME);
+ serializer.endTag(XmlContactsGDataParser.NAMESPACE_GD_URI, XmlNametable.GD_NAME);
+ }
+
+ private static void serializeNameSubelement(XmlSerializer serializer, String value,
+ String yomi, String elementName)
+ throws IOException {
+ if (StringUtils.isEmpty(value)) return;
+ serializer.startTag(XmlContactsGDataParser.NAMESPACE_GD_URI, elementName);
+ if (!StringUtils.isEmpty(yomi)) {
+ serializer.attribute(null /* ns */, XmlNametable.GD_NAME_YOMI, yomi);
+ }
+ serializer.text(value);
+ serializer.endTag(XmlContactsGDataParser.NAMESPACE_GD_URI, elementName);
+ }
+}
diff --git a/src/com/google/wireless/gdata2/contacts/serializer/xml/XmlGroupEntryGDataSerializer.java b/src/com/google/wireless/gdata2/contacts/serializer/xml/XmlGroupEntryGDataSerializer.java
new file mode 100644
index 0000000..52d955b
--- /dev/null
+++ b/src/com/google/wireless/gdata2/contacts/serializer/xml/XmlGroupEntryGDataSerializer.java
@@ -0,0 +1,54 @@
+// Copyright 2009 The Android Open Source Project
+
+package com.google.wireless.gdata2.contacts.serializer.xml;
+
+import com.google.wireless.gdata2.contacts.data.GroupEntry;
+import com.google.wireless.gdata2.contacts.parser.xml.XmlContactsGDataParser;
+import com.google.wireless.gdata2.parser.xml.XmlParserFactory;
+import com.google.wireless.gdata2.parser.ParseException;
+import com.google.wireless.gdata2.serializer.xml.XmlEntryGDataSerializer;
+import com.google.wireless.gdata2.data.StringUtils;
+
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+
+/**
+ * Serializes Google Contact Group entries into the Atom XML format.
+ */
+public class XmlGroupEntryGDataSerializer extends XmlEntryGDataSerializer {
+
+ public XmlGroupEntryGDataSerializer(XmlParserFactory factory, GroupEntry entry) {
+ super(factory, entry);
+ }
+
+ protected GroupEntry getGroupEntry() {
+ return (GroupEntry) getEntry();
+ }
+
+ protected void declareExtraEntryNamespaces(XmlSerializer serializer) throws IOException {
+ super.declareExtraEntryNamespaces(serializer);
+ serializer.setPrefix(XmlContactsGDataParser.NAMESPACE_CONTACTS,
+ XmlContactsGDataParser.NAMESPACE_CONTACTS_URI);
+ }
+
+ /* (non-Javadoc)
+ * @see XmlEntryGDataSerializer#serializeExtraEntryContents
+ */
+ protected void serializeExtraEntryContents(XmlSerializer serializer, int format)
+ throws ParseException, IOException {
+ GroupEntry entry = getGroupEntry();
+ entry.validate();
+
+ serializeSystemGroup(entry, serializer);
+ }
+
+ private void serializeSystemGroup(GroupEntry entry, XmlSerializer serializer) throws IOException {
+ final String systemGroup = entry.getSystemGroup();
+ if (!StringUtils.isEmpty(systemGroup)) {
+ serializer.startTag(null /* ns */, "systemGroup");
+ serializer.attribute(null /* ns */, "id", systemGroup);
+ serializer.endTag(null /* ns */, "systemGroup");
+ }
+ }
+}
diff --git a/src/com/google/wireless/gdata2/contacts/serializer/xml/package.html b/src/com/google/wireless/gdata2/contacts/serializer/xml/package.html
new file mode 100644
index 0000000..1c9bf9d
--- /dev/null
+++ b/src/com/google/wireless/gdata2/contacts/serializer/xml/package.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+ {@hide}
+</body>
+</html>
diff --git a/src/com/google/wireless/gdata2/data/Entry.java b/src/com/google/wireless/gdata2/data/Entry.java
new file mode 100644
index 0000000..f0ac78a
--- /dev/null
+++ b/src/com/google/wireless/gdata2/data/Entry.java
@@ -0,0 +1,401 @@
+// Copyright 2007 The Android Open Source Project
+
+package com.google.wireless.gdata2.data;
+
+import com.google.wireless.gdata2.data.batch.BatchInfo;
+import com.google.wireless.gdata2.parser.ParseException;
+
+/**
+ * Entry in a GData feed. This is the rough equivalent of the atom:Entry
+ * element. The "atom:entry" element represents an individual entry,
+ * acting as a container for metadata and data associated with the
+ * entry. This element can appear as a child of the atom:feed element,
+ * or it can appear as the document (i.e., top-level) element of a
+ * standalone Atom Entry Document.
+ * The Entry class serves as a base class for Google service specific subclasses,
+ * like a contact or a calendar event. As a base class it takes care of the default
+ * attributes and elements that are common to all entries.
+ */
+public class Entry {
+ private String id = null;
+ private String title = null;
+ private String editUri = null;
+ private String htmlUri = null;
+ private String summary = null;
+ private String content = null;
+ private String author = null;
+ private String email = null;
+ private String category = null;
+ private String categoryScheme = null;
+ private String publicationDate = null;
+ private String updateDate = null;
+ private String eTagValue = null;
+ private boolean deleted = false;
+ private BatchInfo batchInfo = null;
+ private String fields = null;
+ private String contentSource = null;
+ private String contentType = null;
+
+ /**
+ * Creates a new empty entry.
+ */
+ public Entry() {
+ }
+
+ /**
+ * Clears all the values in this entry.
+ */
+ public void clear() {
+ id = null;
+ title = null;
+ editUri = null;
+ htmlUri = null;
+ summary = null;
+ content = null;
+ contentType = null;
+ contentSource = null;
+ author = null;
+ email = null;
+ category = null;
+ categoryScheme = null;
+ publicationDate = null;
+ updateDate = null;
+ deleted = false;
+ batchInfo = null;
+ }
+
+ /**
+ * @return the author
+ */
+ public String getAuthor() {
+ return author;
+ }
+
+ /**
+ * @param author the author to set
+ */
+ public void setAuthor(String author) {
+ this.author = author;
+ }
+
+ /**
+ * @return the category
+ */
+ public String getCategory() {
+ return category;
+ }
+
+ /**
+ * @param category the category to set
+ */
+ public void setCategory(String category) {
+ this.category = category;
+ }
+
+ /**
+ * @return the categoryScheme
+ */
+ public String getCategoryScheme() {
+ return categoryScheme;
+ }
+
+ /**
+ * @param categoryScheme the categoryScheme to set
+ */
+ public void setCategoryScheme(String categoryScheme) {
+ this.categoryScheme = categoryScheme;
+ }
+
+ /**
+ * @return the content
+ */
+ public String getContent() {
+ return this.content;
+ }
+
+ /**
+ * @param content the content to set
+ */
+ public void setContent(String content) {
+ this.content = content;
+ }
+
+ /**
+ * @return the contents type, either one of the default
+ * types (text, html, xhtml) or an atomMediaType
+ */
+ public String getContentType() {
+ return contentType;
+ }
+
+ /**
+ * @param type the contentType to set
+ */
+ public void setContentType(String type) {
+ this.contentType = type;
+ }
+
+ /**
+ * If the content itself is empty, the src attribute
+ * points to the internet resource where the content
+ * can be loaded from
+ * @return the src attribute of the content element
+ */
+ public String getContentSource() {
+ return contentSource;
+ }
+
+ /**
+ * @param contentSource the url value to set
+ */
+ public void setContentSource(String contentSource) {
+ this.contentSource = contentSource;
+ }
+
+
+
+ /**
+ * @return the editUri
+ */
+ public String getEditUri() {
+ return editUri;
+ }
+
+ /**
+ * Note that setting the editUri is only valid during parsing
+ * time, this is a server generated value and can not be changed
+ * by the client normally
+ * @param editUri the editUri to set
+ */
+ public void setEditUri(String editUri) {
+ this.editUri = editUri;
+ }
+
+ /**
+ * @return The uri for the HTML version of this entry.
+ */
+ public String getHtmlUri() {
+ return htmlUri;
+ }
+
+ /**
+ * Set the uri for the HTML version of this entry.
+ * @param htmlUri The uri for the HTML version of this entry.
+ */
+ public void setHtmlUri(String htmlUri) {
+ this.htmlUri = htmlUri;
+ }
+
+ /**
+ * @return the id
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * Note that setting the ID is only valid during parsing time,
+ * an ID is a server generated value and can not be changed by
+ * the client normally
+ * @param id the id to set
+ */
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ /**
+ * @return the publicationDate
+ */
+ public String getPublicationDate() {
+ return publicationDate;
+ }
+
+ /**
+ * Note that setting the publicationDate is only valid during
+ * parsing time, this is a server generated value and can not be
+ * changed by the client normally
+ * @param publicationDate the publicationDate to set
+ */
+ public void setPublicationDate(String publicationDate) {
+ this.publicationDate = publicationDate;
+ }
+
+ /**
+ * @return the summary
+ */
+ public String getSummary() {
+ return summary;
+ }
+
+ /**
+ * @param summary the summary to set
+ */
+ public void setSummary(String summary) {
+ this.summary = summary;
+ }
+
+ /**
+ * @return the title
+ */
+ public String getTitle() {
+ return title;
+ }
+
+ /**
+ * @param title the title to set
+ */
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ /**
+ * @return the updateDate
+ */
+ public String getUpdateDate() {
+ return updateDate;
+ }
+
+ /**
+ * Note that setting the updateDate is only valid during parsing
+ * time, this is a server generated value and can not be changed
+ * by the client normally
+ * @param updateDate the updateDate to set
+ */
+ public void setUpdateDate(String updateDate) {
+ this.updateDate = updateDate;
+ }
+
+ /**
+ * @return true if this entry represents a tombstone
+ */
+ public boolean isDeleted() {
+ return deleted;
+ }
+
+ /**
+ * @param isDeleted true if the entry is deleted
+ */
+ public void setDeleted(boolean isDeleted) {
+ deleted = isDeleted;
+ }
+
+ /**
+ * @return the value of the parsed eTag attribute
+ */
+ public String getETag() {
+ return eTagValue;
+ }
+
+ /**
+ * Note that setting the etag is only valid during parsing
+ * time, this is a server generated value and can not be changed
+ * by the client normally
+ * @param eTag the eTag on the entry
+ */
+ public void setETag(String eTag) {
+ eTagValue = eTag;
+ }
+
+ /**
+ * @return the value of the parsed fields attribute
+ */
+ public String getFields() {
+ return fields;
+ }
+
+ /**
+ * @param fields the fields expression on the entry, used during serialization
+ */
+ public void setFields(String fields) {
+ this.fields = fields;
+ }
+
+ /**
+ * Used internally to access batch related properties.
+ * Clients should use {@link com.google.wireless.gdata2.data.batch.BatchUtils} instead.
+ */
+ public BatchInfo getBatchInfo() {
+ return batchInfo;
+ }
+
+ /**
+ * Used internally to update batch related properties.
+ * Clients should use {@link com.google.wireless.gdata2.data.batch.BatchUtils} instead.
+ */
+ public void setBatchInfo(BatchInfo batchInfo) {
+ this.batchInfo = batchInfo;
+ }
+
+ /**
+ * Appends the name and value to this StringBuffer, if value is not null.
+ * Uses the format: "<NAME>: <VALUE>\n"
+ * @param sb The StringBuffer in which the name and value should be
+ * appended.
+ * @param name The name that should be appended.
+ * @param value The value that should be appended.
+ */
+ protected void appendIfNotNull(StringBuffer sb,
+ String name, String value) {
+ if (!StringUtils.isEmpty(value)) {
+ sb.append(name);
+ sb.append(": ");
+ sb.append(value);
+ sb.append("\n");
+ }
+ }
+
+ /**
+ * Helper method that creates the String representation of this Entry.
+ * Called by {@link #toString()}.
+ * Subclasses can add additional data to the StringBuffer.
+ * @param sb The StringBuffer that should be modified to add to the String
+ * representation of this Entry.
+ */
+ protected void toString(StringBuffer sb) {
+ appendIfNotNull(sb, "ID", id);
+ appendIfNotNull(sb, "TITLE", title);
+ appendIfNotNull(sb, "EDIT URI", editUri);
+ appendIfNotNull(sb, "HTML URI", htmlUri);
+ appendIfNotNull(sb, "SUMMARY", summary);
+ appendIfNotNull(sb, "CONTENT", content);
+ appendIfNotNull(sb, "AUTHOR", author);
+ appendIfNotNull(sb, "CATEGORY", category);
+ appendIfNotNull(sb, "CATEGORY SCHEME", categoryScheme);
+ appendIfNotNull(sb, "PUBLICATION DATE", publicationDate);
+ appendIfNotNull(sb, "UPDATE DATE", updateDate);
+ appendIfNotNull(sb, "DELETED", String.valueOf(deleted));
+ appendIfNotNull(sb, "ETAG", String.valueOf(eTagValue));
+ if (batchInfo != null) {
+ appendIfNotNull(sb, "BATCH", batchInfo.toString());
+ }
+ }
+
+ /**
+ * Creates a StringBuffer and calls {@link #toString(StringBuffer)}. The
+ * return value for this method is simply the result of calling
+ * {@link StringBuffer#toString()} on this StringBuffer. Mainly used for
+ * debugging.
+ */
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ toString(sb);
+ return sb.toString();
+ }
+
+ /**
+ * @return the email
+ */
+ public String getEmail() {
+ return email;
+ }
+
+ /**
+ * @param email the email to set
+ */
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public void validate() throws ParseException {
+ }
+}
diff --git a/src/com/google/wireless/gdata2/data/ExtendedProperty.java b/src/com/google/wireless/gdata2/data/ExtendedProperty.java
new file mode 100644
index 0000000..1e8d060
--- /dev/null
+++ b/src/com/google/wireless/gdata2/data/ExtendedProperty.java
@@ -0,0 +1,95 @@
+// Copyright 2009 The Android Open Source Project
+
+package com.google.wireless.gdata2.data;
+
+import com.google.wireless.gdata2.parser.ParseException;
+
+/**
+ * The extendedProperty gdata type
+ * Allows you to store a limited amount of custom data as an auxiliary
+ * property of the enclosing entity.
+ * Note that the presence of anyForeignElement allows feed to optionally
+ * embed any valid XML within gd:extendedProperty element
+ * (mutually exclusive with value attribute).
+ */
+public class ExtendedProperty {
+ private String name;
+ private String value;
+ private String xmlBlob;
+
+ public ExtendedProperty() {}
+ public ExtendedProperty(String name, String value, String xmlBlob) {
+ this.name = name;
+ this.value = value;
+ this.xmlBlob = xmlBlob;
+ }
+
+ /**
+ * Returns the xml embedded inside the extended property
+ * element
+ * Mutually exclusive with the value property
+ * @return the xml as a string
+ */
+ public String getXmlBlob() {
+ return xmlBlob;
+ }
+
+ /**
+ * Sets the embedded xml for the extended property element.
+ * Mutually exclusive with the value property
+ * @param xmlBlob xml as a string
+ */
+ public void setXmlBlob(String xmlBlob) {
+ this.xmlBlob = xmlBlob;
+ }
+
+ /**
+ * @return the name of the extended property expressed as a URI. Extended
+ * property URIs usually follow the {scheme}#{local-name} convention.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @param name set's the name of the extended property
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Returns the value attribute of the extended property
+ * element.Mutually exclusive with the xmlBlob property
+ * @return the value
+ */
+ public String getValue() {
+ return value;
+ }
+
+ /**
+ * Sets the value attribute of the extended property. Mutually
+ * exclusive with the xmlBlog property
+ * @param value
+ */
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public void toString(StringBuffer sb) {
+ sb.append("ExtendedProperty");
+ if (name != null) sb.append(" name:").append(name);
+ if (value != null) sb.append(" value:").append(value);
+ if (xmlBlob != null) sb.append(" xmlBlob:").append(xmlBlob);
+ }
+
+ public void validate() throws ParseException {
+ if (name == null) {
+ throw new ParseException("name must not be null");
+ }
+
+ if ((value == null && xmlBlob == null) || (value != null && xmlBlob != null)) {
+ throw new ParseException("exactly one of value and xmlBlob must be present");
+ }
+ }
+}
diff --git a/src/com/google/wireless/gdata2/data/Feed.java b/src/com/google/wireless/gdata2/data/Feed.java
new file mode 100644
index 0000000..76c9e8a
--- /dev/null
+++ b/src/com/google/wireless/gdata2/data/Feed.java
@@ -0,0 +1,173 @@
+// Copyright 2007 The Android Open Source Project
+
+package com.google.wireless.gdata2.data;
+
+/**
+ * Class containing information about a GData feed. Note that this feed does
+ * not contain any of the entries in that feed -- the entries are yielded
+ * separately from this Feed.
+ */
+public class Feed {
+ private int totalResults;
+ private int startIndex;
+ private int itemsPerPage;
+ private String title;
+ private String id;
+ private String lastUpdated;
+ private String category;
+ private String categoryScheme;
+ private String eTagValue;
+
+ /**
+ * Creates a new, empty feed.
+ */
+ public Feed() {
+ }
+
+ /**
+ * The approximate number of total results in the feed
+ * @return totalResults
+ */
+ public int getTotalResults() {
+ return totalResults;
+ }
+
+ /**
+ * The number of toal results in the feed Only used during
+ * parsing of the feed
+ * @param totalResults
+ */
+ public void setTotalResults(int totalResults) {
+ this.totalResults = totalResults;
+ }
+
+ /**
+ * The starting Index of the current result set
+ * @return startIndex
+ */
+ public int getStartIndex() {
+ return startIndex;
+ }
+
+ /**
+ * The starting index of the current result set Only used during
+ * parsing of the feed
+ * @param startIndex
+ */
+ public void setStartIndex(int startIndex) {
+ this.startIndex = startIndex;
+ }
+
+ /**
+ * The number of items per Page in this result set
+ * @return itemsPerPage
+ */
+ public int getItemsPerPage() {
+ return itemsPerPage;
+ }
+
+ /**
+ * The number of items per page in this result set
+ * Only used during parsing of the feed
+ * @param itemsPerPage
+ */
+ public void setItemsPerPage(int itemsPerPage) {
+ this.itemsPerPage = itemsPerPage;
+ }
+
+ /**
+ * @return the category
+ */
+ public String getCategory() {
+ return category;
+ }
+
+ /**
+ * The category to set
+ * Only used during parsing of the feed
+ * @param category
+ */
+ public void setCategory(String category) {
+ this.category = category;
+ }
+
+ /**
+ * @return the categoryScheme
+ */
+ public String getCategoryScheme() {
+ return categoryScheme;
+ }
+
+ /**
+ * The categoryScheme to set
+ * Only used during parsing of the feed
+ * @param categoryScheme
+ */
+ public void setCategoryScheme(String categoryScheme) {
+ this.categoryScheme = categoryScheme;
+ }
+
+ /**
+ * @return the id
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * the id to set
+ * Only used during parsing of the feed
+ * @param id
+ */
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ /**
+ * @return the lastUpdated
+ */
+ public String getLastUpdated() {
+ return lastUpdated;
+ }
+
+ /**
+ * The lastUpdated to set
+ * Only used during parsing of the feed
+ * @param lastUpdated
+ */
+ public void setLastUpdated(String lastUpdated) {
+ this.lastUpdated = lastUpdated;
+ }
+
+ /**
+ * @return the title
+ */
+ public String getTitle() {
+ return title;
+ }
+
+ /**
+ * the title to set
+ * Only used during parsing of the feed
+ * @param title
+ */
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ /**
+ * @return the value of the parsed eTag attribute
+ */
+ public String getETag() {
+ return eTagValue;
+ }
+
+ /**
+ * @param sets the eTag on the entry, used during
+ * parsing
+ */
+ public void setETag(String eTag) {
+ eTagValue = eTag;
+ }
+
+}
diff --git a/src/com/google/wireless/gdata2/data/MediaEntry.java b/src/com/google/wireless/gdata2/data/MediaEntry.java
new file mode 100644
index 0000000..851fffb
--- /dev/null
+++ b/src/com/google/wireless/gdata2/data/MediaEntry.java
@@ -0,0 +1,12 @@
+// Copyright 2009 The Android Open Source Project
+
+package com.google.wireless.gdata2.data;
+
+/**
+ * Entry containing information about media entries
+ */
+public class MediaEntry extends Entry {
+ public MediaEntry() {
+ super();
+ }
+}
diff --git a/src/com/google/wireless/gdata2/data/StringUtils.java b/src/com/google/wireless/gdata2/data/StringUtils.java
new file mode 100644
index 0000000..5cefb83
--- /dev/null
+++ b/src/com/google/wireless/gdata2/data/StringUtils.java
@@ -0,0 +1,78 @@
+// Copyright 2007 The Android Open Source Project
+
+package com.google.wireless.gdata2.data;
+
+/**
+ * Utility class for working with and manipulating Strings.
+ */
+public final class StringUtils {
+ // utility class
+ private StringUtils() {
+ }
+
+ /**
+ * Returns whether or not the String is empty. A String is considered to
+ * be empty if it is null or if it has a length of 0.
+ * @param string The String that should be examined.
+ * @return Whether or not the String is empty.
+ */
+ public static boolean isEmpty(String string) {
+ return ((string == null) || (string.length() == 0));
+ }
+
+ /**
+ * Returns {@code true} if the given string is null, empty, or comprises only
+ * whitespace characters, as defined by {@link isWhitespace(char)}.
+ *
+ * @param string The String that should be examined.
+ * @return {@code true} if {@code string} is null, empty, or consists of
+ * whitespace characters only
+ */
+ public static boolean isEmptyOrWhitespace(String string) {
+ if (string == null) {
+ return true;
+ }
+ int length = string.length();
+ for (int i = 0; i < length; i++) {
+ if (!isWhitespace(string.charAt(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Converts a string into an integer. Uses the default value
+ * passed in if the string can not be converted
+ * @param string The String that should be converted to an integer
+ * @param defaultValue The default value that should be used if
+ * the string is not convertable
+ * @return the integer value of the string or the default value
+ */
+ public static int parseInt(String string, int defaultValue) {
+ if (string != null) {
+ try {
+ return Integer.parseInt(string);
+ } catch (NumberFormatException nfe) {
+ // ignore
+ }
+ }
+ return defaultValue;
+ }
+
+ /**
+ * Tests if a character is a whitespace character
+ * Uses
+ * http://en.wikipedia.org/wiki/Whitespace_%28computer_science%29
+ * as the algorithm.
+ * @param c A character to be tested.
+ * @return true if the character is a whitespace
+ */
+ public static boolean isWhitespace(char c) {
+ return ('\u0009' <= c && c <= '\r') || c == '\u0020' || c == '\u0085'
+ || c == '\u00A0' || c == '\u1680' || c == '\u180E'
+ || ('\u2000' <= c && c <= '\u200A') || c == '\u2028' || c == '\u2029'
+ || c == '\u202F' || c == '\u205F' || c == '\u3000';
+ }
+
+}
diff --git a/src/com/google/wireless/gdata2/data/XmlUtils.java b/src/com/google/wireless/gdata2/data/XmlUtils.java
new file mode 100644
index 0000000..a69d817
--- /dev/null
+++ b/src/com/google/wireless/gdata2/data/XmlUtils.java
@@ -0,0 +1,100 @@
+// Copyright 2007 The Android Open Source Project
+
+package com.google.wireless.gdata2.data;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+
+/**
+ * Utility class for working with an XmlPullParser.
+ */
+public final class XmlUtils {
+ // utility class
+ private XmlUtils() {
+ }
+
+ /**
+ * Extracts the child text for the current element in the pull parser.
+ * @param parser The XmlPullParser parsing an XML document.
+ * @return The child text for the current element. May be null, if there
+ * is no child text.
+ * @throws XmlPullParserException Thrown if the child text could not be
+ * parsed.
+ * @throws IOException Thrown if the InputStream behind the parser cannot
+ * be read.
+ */
+ public static String extractChildText(XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ // TODO: check that the current node is an element?
+ int eventType = parser.next();
+ if (eventType != XmlPullParser.TEXT) {
+ return null;
+ }
+ return parser.getText();
+ }
+
+ /**
+ * Extracts the child text for the first child element in the pull parser.
+ * Other child elements will be discarded
+ * @param parser The XmlPullParser parsing an XML document.
+ * @return The child text for the first child element. May be null, if there
+ * is no child text.
+ * @throws XmlPullParserException Thrown if the child text could not be
+ * parsed.
+ * @throws IOException Thrown if the InputStream behind the parser cannot
+ * be read.
+ */
+ public static String extractFirstChildTextIgnoreRest(XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ int parentDepth = parser.getDepth();
+ int eventType = parser.next();
+ String child = null;
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+ int depth = parser.getDepth();
+
+ if (eventType == XmlPullParser.TEXT) {
+ if (child == null) {
+ child = parser.getText();
+ }
+ } else if (eventType == XmlPullParser.END_TAG && depth == parentDepth) {
+ return child;
+ }
+ eventType = parser.next();
+ }
+ throw new XmlPullParserException("End of document reached; never saw expected end tag at "
+ + "depth " + parentDepth);
+ }
+
+ /**
+ * Returns the nextDirectChildTag
+ * @param parser The XmlPullParser parsing an XML document.
+ * @param parentDepth the depth in the hierachy of the parent node
+ * @return The child element
+ * @throws XmlPullParserException Thrown if the child text could not be
+ * parsed.
+ * @throws IOException Thrown if the InputStream behind the parser cannot
+ * be read.
+ */
+ public static String nextDirectChildTag(XmlPullParser parser, int parentDepth)
+ throws XmlPullParserException, IOException {
+ int targetDepth = parentDepth + 1;
+ int eventType = parser.next();
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+ int depth = parser.getDepth();
+
+ if (eventType == XmlPullParser.START_TAG && depth == targetDepth) {
+ return parser.getName();
+ }
+
+ if (eventType == XmlPullParser.END_TAG && depth == parentDepth) {
+ return null;
+ }
+ eventType = parser.next();
+ }
+ throw new XmlPullParserException("End of document reached; never saw expected end tag at "
+ + "depth " + parentDepth);
+ }
+}
diff --git a/src/com/google/wireless/gdata2/data/batch/BatchInfo.java b/src/com/google/wireless/gdata2/data/batch/BatchInfo.java
new file mode 100644
index 0000000..ef461d2
--- /dev/null
+++ b/src/com/google/wireless/gdata2/data/batch/BatchInfo.java
@@ -0,0 +1,30 @@
+// Copyright 2009 Google Inc. All Rights Reserved.
+
+package com.google.wireless.gdata2.data.batch;
+
+/**
+ * Opaque container for batch related info associated with an entry.
+ * Clients should use {@link BatchUtils} to access this data instead.
+ */
+public class BatchInfo {
+ String id;
+ String operation;
+ BatchStatus status;
+ BatchInterrupted interrupted;
+
+ /* package */ BatchInfo() {
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append("id: ").append(id);
+ sb.append(" op: ").append(operation);
+ if (status != null) {
+ sb.append(" sc: ").append(status.getStatusCode());
+ }
+ if (interrupted != null) {
+ sb.append(" interrupted: ").append(interrupted.getReason());
+ }
+ return sb.toString();
+ }
+}
diff --git a/src/com/google/wireless/gdata2/data/batch/BatchInterrupted.java b/src/com/google/wireless/gdata2/data/batch/BatchInterrupted.java
new file mode 100644
index 0000000..ff8a69f
--- /dev/null
+++ b/src/com/google/wireless/gdata2/data/batch/BatchInterrupted.java
@@ -0,0 +1,75 @@
+// Copyright 2009 Google Inc. All Rights Reserved.
+
+package com.google.wireless.gdata2.data.batch;
+
+/**
+ * Holds status information about a batch that was interrupted.
+ */
+public class BatchInterrupted {
+ private String reason;
+ private int total;
+ private int success;
+ private int error;
+
+ /**
+ * Creates a new empty BatchInterrupted.
+ */
+ public BatchInterrupted() {
+ }
+
+ /**
+ * Returns the reason for this failure.
+ */
+ public String getReason() {
+ return reason;
+ }
+
+ /**
+ * Sets the reason for this failure.
+ */
+ public void setReason(String reason) {
+ this.reason = reason;
+ }
+
+ /**
+ * Gets the total number of entries read.
+ */
+ public int getTotalCount() {
+ return total;
+ }
+
+ /**
+ * Sets the number of entries read.
+ */
+ public void setTotalCount(int total) {
+ this.total = total;
+ }
+
+ /**
+ * Gets the number of entries that were processed successfully.
+ */
+ public int getSuccessCount() {
+ return success;
+ }
+
+ /**
+ * Sets the number of entries successfuly processed.
+ */
+ public void setSuccessCount(int success) {
+ this.success = success;
+ }
+
+ /**
+ * Gets the number of entries that were rejected.
+ */
+ public int getErrorCount() {
+ return error;
+ }
+
+ /**
+ * Sets the number of entries that failed.
+ */
+ public void setErrorCount(int error) {
+ this.error = error;
+ }
+}
diff --git a/src/com/google/wireless/gdata2/data/batch/BatchStatus.java b/src/com/google/wireless/gdata2/data/batch/BatchStatus.java
new file mode 100644
index 0000000..7a19afd
--- /dev/null
+++ b/src/com/google/wireless/gdata2/data/batch/BatchStatus.java
@@ -0,0 +1,75 @@
+// Copyright 2009 Google Inc. All Rights Reserved.
+
+package com.google.wireless.gdata2.data.batch;
+
+/**
+ * Holds result status for an individual batch operation.
+ */
+public class BatchStatus {
+ private int statusCode;
+ private String reason;
+ private String contentType;
+ private String content;
+
+ /**
+ * Creates a new empty BatchStatus.
+ */
+ public BatchStatus() {
+ }
+
+ /**
+ * Returns the status of this operation.
+ */
+ public int getStatusCode() {
+ return statusCode;
+ }
+
+ /**
+ * Sets the status of this operation.
+ */
+ public void setStatusCode(int statusCode) {
+ this.statusCode = statusCode;
+ }
+
+ /**
+ * Returns the reason for this status.
+ */
+ public String getReason() {
+ return reason;
+ }
+
+ /**
+ * Sets the reason for this status.
+ */
+ public void setReason(String reason) {
+ this.reason = reason;
+ }
+
+ /**
+ * Returns the content type of the response.
+ */
+ public String getContentType() {
+ return contentType;
+ }
+
+ /**
+ * Sets the content type of the response.
+ */
+ public void setContentType(String contentType) {
+ this.contentType = contentType;
+ }
+
+ /**
+ * Returns the response content, if any.
+ */
+ public String getContent() {
+ return content;
+ }
+
+ /**
+ * Sets the response content.
+ */
+ public void setContent(String content) {
+ this.content = content;
+ }
+}
diff --git a/src/com/google/wireless/gdata2/data/batch/BatchUtils.java b/src/com/google/wireless/gdata2/data/batch/BatchUtils.java
new file mode 100644
index 0000000..c237601
--- /dev/null
+++ b/src/com/google/wireless/gdata2/data/batch/BatchUtils.java
@@ -0,0 +1,93 @@
+// Copyright 2009 Google Inc. All Rights Reserved.
+
+package com.google.wireless.gdata2.data.batch;
+
+import com.google.wireless.gdata2.data.Entry;
+
+/**
+ * Utility methods for dealing with batch operations.
+ */
+public class BatchUtils {
+
+ public static final String OPERATION_INSERT = "insert";
+
+ public static final String OPERATION_UPDATE = "update";
+
+ public static final String OPERATION_QUERY = "query";
+
+ public static final String OPERATION_DELETE = "delete";
+
+ private BatchUtils() {
+ }
+
+ /**
+ * Returns the batch id of the given entry, or null if none.
+ */
+ public static String getBatchId(Entry entry) {
+ BatchInfo info = entry.getBatchInfo();
+ return info == null ? null : info.id;
+ }
+
+ /**
+ * Sets the batch id of the given entry.
+ */
+ public static void setBatchId(Entry entry, String id) {
+ getOrCreateBatchInfo(entry).id = id;
+ }
+
+ /**
+ * Returns the batch operation of the given entry.
+ */
+ public static String getBatchOperation(Entry entry) {
+ BatchInfo info = entry.getBatchInfo();
+ return info == null ? null : info.operation;
+ }
+
+ /**
+ * Sets the operation for the given batch entry.
+ */
+ public static void setBatchOperation(Entry entry, String operation) {
+ getOrCreateBatchInfo(entry).operation = operation;
+ }
+
+ /**
+ * Returns the status of the given batch entry.
+ */
+ public static BatchStatus getBatchStatus(Entry entry) {
+ BatchInfo info = entry.getBatchInfo();
+ return info == null ? null : info.status;
+ }
+
+ /**
+ * Sets the status of the given batch entry.
+ */
+ public static void setBatchStatus(Entry entry, BatchStatus status) {
+ getOrCreateBatchInfo(entry).status = status;
+ }
+
+ /**
+ * Returns the interrupted status of the given entry, or null if none.
+ */
+ public static BatchInterrupted getBatchInterrupted(Entry entry) {
+ BatchInfo info = entry.getBatchInfo();
+ return info == null ? null : info.interrupted;
+ }
+
+ /**
+ * Sets the interrupted status of the given entry.
+ */
+ public static void setBatchInterrupted(Entry entry,
+ BatchInterrupted interrupted) {
+ getOrCreateBatchInfo(entry).interrupted = interrupted;
+ }
+
+ private static BatchInfo getOrCreateBatchInfo(Entry entry) {
+ BatchInfo info = entry.getBatchInfo();
+ if (info == null) {
+ info = new BatchInfo();
+ entry.setBatchInfo(info);
+ }
+ return info;
+ }
+
+}
diff --git a/src/com/google/wireless/gdata2/data/package.html b/src/com/google/wireless/gdata2/data/package.html
new file mode 100644
index 0000000..1c9bf9d
--- /dev/null
+++ b/src/com/google/wireless/gdata2/data/package.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+ {@hide}
+</body>
+</html>
diff --git a/src/com/google/wireless/gdata2/package.html b/src/com/google/wireless/gdata2/package.html
new file mode 100644
index 0000000..1c9bf9d
--- /dev/null
+++ b/src/com/google/wireless/gdata2/package.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+ {@hide}
+</body>
+</html>
diff --git a/src/com/google/wireless/gdata2/parser/GDataParser.java b/src/com/google/wireless/gdata2/parser/GDataParser.java
new file mode 100644
index 0000000..ae3df73
--- /dev/null
+++ b/src/com/google/wireless/gdata2/parser/GDataParser.java
@@ -0,0 +1,64 @@
+// Copyright 2008 The Android Open Source Project
+
+package com.google.wireless.gdata2.parser;
+
+import com.google.wireless.gdata2.data.Entry;
+import com.google.wireless.gdata2.data.Feed;
+
+import java.io.IOException;
+
+/**
+ * Interface for parsing GData feeds. Uses a &quot;pull&quot; model, where
+ * entries are not read or parsed until {@link #readNextEntry}
+ * is called.
+ */
+public interface GDataParser {
+
+ /**
+ * Starts parsing the feed, returning a {@link Feed} containing information
+ * about the feed. Note that the {@link Feed} does not contain any
+ * information about any entries, as the entries have not yet been parsed.
+ *
+ * @return The {@link Feed} containing information about the parsed feed.
+ * @throws ParseException Thrown if the feed cannot be parsed.
+ */
+ Feed parseFeedEnvelope() throws ParseException;
+
+ /**
+ * Parses a GData entry. You can either call {@link #init()} or
+ * {@link #parseStandaloneEntry()} for a given feed.
+ *
+ * @return The parsed entry.
+ * @throws ParseException Thrown if the entry could not be parsed.
+ */
+ Entry parseStandaloneEntry() throws ParseException, IOException;
+
+ /**
+ * Returns whether or not there is more data in the feed.
+ */
+ boolean hasMoreData();
+
+ /**
+ * Reads and parses the next entry in the feed. The {@link Entry} that
+ * should be filled is passed in -- if null, the entry will be created
+ * by the parser; if not null, the entry will be cleared and reused.
+ *
+ * @param entry The entry that should be filled. Should be null if this is
+ * the first call to this method. This entry is also returned as the return
+ * value.
+ *
+ * @return The {@link Entry} containing information about the parsed entry.
+ * If entry was not null, returns the same (reused) object as entry, filled
+ * with information about the entry that was just parsed. If the entry was
+ * null, returns a newly created entry (as appropriate for the type of
+ * feed being parsed).
+ * @throws ParseException Thrown if the entry cannot be parsed.
+ */
+ Entry readNextEntry(Entry entry) throws ParseException, IOException;
+
+ /**
+ * Cleans up any state in the parser. Should be called when caller is
+ * finished parsing a GData feed.
+ */
+ void close();
+}
diff --git a/src/com/google/wireless/gdata2/parser/ParseException.java b/src/com/google/wireless/gdata2/parser/ParseException.java
new file mode 100644
index 0000000..48786b4
--- /dev/null
+++ b/src/com/google/wireless/gdata2/parser/ParseException.java
@@ -0,0 +1,38 @@
+// Copyright 2007 The Android Open Source Project
+
+package com.google.wireless.gdata2.parser;
+
+import com.google.wireless.gdata2.GDataException;
+
+/**
+ * Exception thrown if a GData feed cannot be parsed.
+ */
+public class ParseException extends GDataException {
+
+ /**
+ * Creates a new empty ParseException.
+ */
+ public ParseException() {
+ super();
+ }
+
+ /**
+ * Creates a new ParseException with the supplied message.
+ * @param message The message for this ParseException.
+ */
+ public ParseException(String message) {
+ super(message);
+ }
+
+ /**
+ * Creates a new ParseException with the supplied message and underlying
+ * cause.
+ *
+ * @param message The message for this ParseException.
+ * @param cause The underlying cause that was caught and wrapped by this
+ * ParseException.
+ */
+ public ParseException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/com/google/wireless/gdata2/parser/package.html b/src/com/google/wireless/gdata2/parser/package.html
new file mode 100644
index 0000000..1c9bf9d
--- /dev/null
+++ b/src/com/google/wireless/gdata2/parser/package.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+ {@hide}
+</body>
+</html>
diff --git a/src/com/google/wireless/gdata2/parser/xml/SimplePullParser.java b/src/com/google/wireless/gdata2/parser/xml/SimplePullParser.java
new file mode 100644
index 0000000..7c573c9
--- /dev/null
+++ b/src/com/google/wireless/gdata2/parser/xml/SimplePullParser.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.wireless.gdata2.parser.xml;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/**
+ * This is an abstraction of a pull parser that provides several benefits:<ul>
+ * <li>it is easier to use robustly because it makes it trivial to handle unexpected tags (which
+ * might have children)</li>
+ * <li>it makes the handling of text (cdata) blocks more convenient</li>
+ * <li>it provides convenient methods for getting a mandatory attribute (and throwing an exception
+ * if it is missing) or an optional attribute (and using a default value if it is missing)
+ * </ul>
+ */
+public class SimplePullParser {
+ public static final String TEXT_TAG = "![CDATA[";
+
+ private final XmlPullParser mParser;
+ private String mCurrentStartTag;
+
+ /**
+ * Constructs a new SimplePullParser to parse the xml
+ * @param parser the underlying parser to use
+ */
+ public SimplePullParser(XmlPullParser parser) {
+ mParser = parser;
+ mCurrentStartTag = null;
+ }
+
+ /**
+ * Returns the tag of the next element whose depth is parentDepth plus one
+ * or null if there are no more such elements before the next start tag. When this returns,
+ * getDepth() and all methods relating to attributes will refer to the element whose tag is
+ * returned.
+ *
+ * @param parentDepth the depth of the parrent of the item to be returned
+ * @param textBuffer if null then text blocks will be ignored. If
+ * non-null then text blocks will be added to the builder and TEXT_TAG
+ * will be returned when one is found
+ * @return the next of the next child element's tag, TEXT_TAG if a text block is found, or null
+ * if there are no more child elements or DATA blocks
+ * @throws IOException propogated from the underlying parser
+ * @throws ParseException if there was an error parsing the xml.
+ */
+ public String nextTagOrText(int parentDepth, StringBuffer textBuffer)
+ throws IOException, ParseException {
+ while (true) {
+ int eventType = 0;
+ try {
+ eventType = mParser.next();
+ } catch (XmlPullParserException e) {
+ throw new ParseException(e);
+ }
+ int depth = mParser.getDepth();
+ mCurrentStartTag = null;
+
+ if (eventType == XmlPullParser.START_TAG && depth == parentDepth + 1) {
+ mCurrentStartTag = mParser.getName();
+ // TODO: this is an example of how to do logging of the XML
+// if (mLogTag != null && Log.isLoggable(mLogTag, Log.DEBUG)) {
+// StringBuilder sb = new StringBuilder();
+// for (int i = 0; i < depth; i++) sb.append(" ");
+// sb.append("<").append(mParser.getName());
+// int count = mParser.getAttributeCount();
+// for (int i = 0; i < count; i++) {
+// sb.append(" ");
+// sb.append(mParser.getAttributeName(i));
+// sb.append("=\"");
+// sb.append(mParser.getAttributeValue(i));
+// sb.append("\"");
+// }
+// sb.append(">");
+// Log.d(mLogTag, sb.toString());
+// }
+ return mParser.getName();
+ }
+
+ if (eventType == XmlPullParser.END_TAG && depth == parentDepth) {
+ // TODO: this is an example of how to do logging of the XML
+// if (mLogTag != null && Log.isLoggable(mLogTag, Log.DEBUG)) {
+// StringBuilder sb = new StringBuilder();
+// for (int i = 0; i < depth; i++) sb.append(" ");
+// sb.append("</>"); // Not quite valid xml but it gets the job done.
+// Log.d(mLogTag, sb.toString());
+// }
+ return null;
+ }
+
+ if (eventType == XmlPullParser.END_DOCUMENT && parentDepth == 0) {
+ return null;
+ }
+
+ if (eventType == XmlPullParser.TEXT && depth == parentDepth) {
+ if (textBuffer == null) {
+ continue;
+ }
+ String text = mParser.getText();
+ textBuffer.append(text);
+ return TEXT_TAG;
+ }
+ }
+ }
+
+ /**
+ * The same as nextTagOrText(int, StringBuilder) but ignores text blocks.
+ */
+ public String nextTag(int parentDepth) throws IOException, ParseException {
+ return nextTagOrText(parentDepth, null /* ignore text */);
+ }
+
+ /**
+ * Returns the depth of the current element. The depth is 0 before the first
+ * element has been returned, 1 after that, etc.
+ *
+ * @return the depth of the current element
+ */
+ public int getDepth() {
+ return mParser.getDepth();
+ }
+
+ /**
+ * Consumes the rest of the children, accumulating any text at this level into the builder.
+ *
+ * @param textBuffer
+ * @throws IOException propogated from the XmlPullParser
+ * @throws ParseException if there was an error parsing the xml.
+ */
+ public void readRemainingText(int parentDepth, StringBuffer textBuffer)
+ throws IOException, ParseException {
+ while (nextTagOrText(parentDepth, textBuffer) != null) {
+ }
+ }
+
+ /**
+ * Returns the number of attributes on the current element.
+ *
+ * @return the number of attributes on the current element
+ */
+ public int numAttributes() {
+ return mParser.getAttributeCount();
+ }
+
+ /**
+ * Returns the name of the nth attribute on the current element.
+ *
+ * @return the name of the nth attribute on the current element
+ */
+ public String getAttributeName(int i) {
+ return mParser.getAttributeName(i);
+ }
+
+ /**
+ * Returns the namespace of the nth attribute on the current element.
+ *
+ * @return the namespace of the nth attribute on the current element
+ */
+ public String getAttributeNamespace(int i) {
+ return mParser.getAttributeNamespace(i);
+ }
+
+ /**
+ * Returns the string value of the named attribute.
+ *
+ * @param namespace the namespace of the attribute
+ * @param name the name of the attribute
+ * @param defaultValue the value to return if the attribute is not specified
+ * @return the value of the attribute
+ */
+ public String getStringAttribute(
+ String namespace, String name, String defaultValue) {
+ String value = mParser.getAttributeValue(namespace, name);
+ if (null == value) return defaultValue;
+ return value;
+ }
+
+ /**
+ * Returns the string value of the named attribute. An exception will
+ * be thrown if the attribute is not present.
+ *
+ * @param namespace the namespace of the attribute
+ * @param name the name of the attribute @return the value of the attribute
+ * @throws ParseException thrown if the attribute is missing
+ */
+ public String getStringAttribute(String namespace, String name) throws ParseException {
+ String value = mParser.getAttributeValue(namespace, name);
+ if (null == value) {
+ throw new ParseException(
+ "missing '" + name + "' attribute on '" + mCurrentStartTag + "' element");
+ }
+ return value;
+ }
+
+ /**
+ * Returns the string value of the named attribute. An exception will
+ * be thrown if the attribute is not a valid integer.
+ *
+ * @param namespace the namespace of the attribute
+ * @param name the name of the attribute
+ * @param defaultValue the value to return if the attribute is not specified
+ * @return the value of the attribute
+ * @throws ParseException thrown if the attribute not a valid integer.
+ */
+ public int getIntAttribute(String namespace, String name, int defaultValue)
+ throws ParseException {
+ String value = mParser.getAttributeValue(namespace, name);
+ if (null == value) return defaultValue;
+ try {
+ return Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ throw new ParseException("Cannot parse '" + value + "' as an integer");
+ }
+ }
+
+ /**
+ * Returns the string value of the named attribute. An exception will
+ * be thrown if the attribute is not present or is not a valid integer.
+ *
+ * @param namespace the namespace of the attribute
+ * @param name the name of the attribute @return the value of the attribute
+ * @throws ParseException thrown if the attribute is missing or not a valid integer.
+ */
+ public int getIntAttribute(String namespace, String name)
+ throws ParseException {
+ String value = getStringAttribute(namespace, name);
+ try {
+ return Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ throw new ParseException("Cannot parse '" + value + "' as an integer");
+ }
+ }
+
+ /**
+ * Returns the string value of the named attribute. An exception will
+ * be thrown if the attribute is not a valid long.
+ *
+ * @param namespace the namespace of the attribute
+ * @param name the name of the attribute @return the value of the attribute
+ * @throws ParseException thrown if the attribute is not a valid long.
+ */
+ public long getLongAttribute(String namespace, String name, long defaultValue)
+ throws ParseException {
+ String value = mParser.getAttributeValue(namespace, name);
+ if (null == value) return defaultValue;
+ try {
+ return Long.parseLong(value);
+ } catch (NumberFormatException e) {
+ throw new ParseException("Cannot parse '" + value + "' as a long");
+ }
+ }
+
+ /**
+ * Returns the string value of the named attribute. An exception will
+ * be thrown if the attribute is not present or is not a valid long.
+ *
+ * @param namespace the namespace of the attribute
+ * @param name the name of the attribute @return the value of the attribute
+ * @throws ParseException thrown if the attribute is missing or not a valid long.
+ */
+ public long getLongAttribute(String namespace, String name)
+ throws ParseException {
+ String value = getStringAttribute(namespace, name);
+ try {
+ return Long.parseLong(value);
+ } catch (NumberFormatException e) {
+ throw new ParseException("Cannot parse '" + value + "' as a long");
+ }
+ }
+
+ public static final class ParseException extends Exception {
+ public ParseException(String message) {
+ super(message);
+ }
+
+ public ParseException(String message, Throwable cause) {
+ // constructor Exception(String, Throwable) is not supported in J2ME
+ super(message + ", Cause: " + String.valueOf(cause));
+ }
+
+ public ParseException(Throwable cause) {
+ // constructor Exception(Throwable) is not supported in J2ME
+ super("Cause: " + String.valueOf(cause));
+ }
+ }
+}
diff --git a/src/com/google/wireless/gdata2/parser/xml/XmlGDataParser.java b/src/com/google/wireless/gdata2/parser/xml/XmlGDataParser.java
new file mode 100644
index 0000000..0ea6d81
--- /dev/null
+++ b/src/com/google/wireless/gdata2/parser/xml/XmlGDataParser.java
@@ -0,0 +1,770 @@
+// Copyright 2008 The Android Open Source Project
+
+package com.google.wireless.gdata2.parser.xml;
+
+import com.google.wireless.gdata2.data.Entry;
+import com.google.wireless.gdata2.data.Feed;
+import com.google.wireless.gdata2.data.StringUtils;
+import com.google.wireless.gdata2.data.XmlUtils;
+import com.google.wireless.gdata2.parser.GDataParser;
+import com.google.wireless.gdata2.parser.ParseException;
+import com.google.wireless.gdata2.data.batch.BatchInterrupted;
+import com.google.wireless.gdata2.data.batch.BatchStatus;
+import com.google.wireless.gdata2.data.batch.BatchUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * {@link GDataParser} that uses an {@link XmlPullParser} to parse a GData feed.
+ */
+// NOTE: we do not perform any validity checks on the XML.
+public class XmlGDataParser implements GDataParser {
+
+ /** Namespace URI for Atom */
+ public static final String NAMESPACE_ATOM_URI =
+ "http://www.w3.org/2005/Atom";
+
+ /** The openSearch namespace Uri */
+ public static final String NAMESPACE_OPENSEARCH_URI =
+ "http://a9.com/-/spec/opensearch/1.1/";
+
+ /** Namespace prefix for GData */
+ public static final String NAMESPACE_GD = "gd";
+
+ /** Namespace URI for GData */
+ public static final String NAMESPACE_GD_URI =
+ "http://schemas.google.com/g/2005";
+
+ /** Namespace prefix for GData batch operations */
+ public static final String NAMESPACE_BATCH = "batch";
+
+ /** Namespace uri for GData batch operations */
+ public static final String NAMESPACE_BATCH_URI =
+ "http://schemas.google.com/gdata/batch";
+
+ private final InputStream is;
+ private final XmlPullParser parser;
+ private boolean isInBadState;
+ private String fields;
+
+ /**
+ * Creates a new XmlGDataParser for a feed in the provided InputStream.
+ * @param is The InputStream that should be parsed.
+ * @param parser The xmlpullparser to be used
+ * @throws ParseException Thrown if an XmlPullParser could not be created
+ * or set around this InputStream.
+ */
+ public XmlGDataParser(InputStream is, XmlPullParser parser)
+ throws ParseException {
+ this.is = is;
+ this.parser = parser;
+
+ if (!parser.getFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES))
+ {
+ throw new IllegalStateException("A XmlGDataParser needs to be "
+ + "constructed with a namespace aware XmlPullParser");
+ }
+
+ this.isInBadState = false;
+ if (this.is != null) {
+ try {
+ this.parser.setInput(is, null /* encoding */);
+ } catch (XmlPullParserException e) {
+ throw new ParseException("Could not create XmlGDataParser", e);
+ }
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.google.wireless.gdata2.parser.GDataParser#parseFeedEnvelope()
+ */
+ public final Feed parseFeedEnvelope() throws ParseException {
+ int eventType;
+ fields = null;
+ try {
+ eventType = parser.getEventType();
+ } catch (XmlPullParserException e) {
+ throw new ParseException("Could not parse GData feed.", e);
+ }
+ if (eventType != XmlPullParser.START_DOCUMENT) {
+ throw new ParseException("Attempting to initialize parsing beyond "
+ + "the start of the document.");
+ }
+
+ try {
+ eventType = parser.next();
+ } catch (XmlPullParserException xppe) {
+ throw new ParseException("Could not read next event.", xppe);
+ } catch (IOException ioe) {
+ throw new ParseException("Could not read next event.", ioe);
+ }
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+ switch (eventType) {
+ case XmlPullParser.START_TAG:
+ String name = parser.getName();
+ if (XmlNametable.PARTIAL.equals(name)) {
+ try {
+ return parsePartialFeed();
+ } catch (XmlPullParserException xppe) {
+ throw new ParseException("Unable to parse <partial> feed start", xppe);
+ } catch (IOException ioe) {
+ throw new ParseException("Unable to parse <partial> feed start", ioe);
+ }
+ } else if (XmlNametable.FEED.equals(name)) {
+ try {
+ return parseFeed();
+ } catch (XmlPullParserException xppe) {
+ throw new ParseException("Unable to parse <feed>.",
+ xppe);
+ } catch (IOException ioe) {
+ throw new ParseException("Unable to parse <feed>.",
+ ioe);
+ }
+ }
+ break;
+ default:
+ // ignore
+ break;
+ }
+
+ try {
+ eventType = parser.next();
+ } catch (XmlPullParserException xppe) {
+ throw new ParseException("Could not read next event.", xppe);
+ } catch (IOException ioe) {
+ throw new ParseException("Could not read next event." , ioe);
+ }
+ }
+ throw new ParseException("No <feed> found in document.");
+ }
+
+ /**
+ * @return the {@link XmlPullParser} being used to parse this feed.
+ */
+ protected final XmlPullParser getParser() {
+ return parser;
+ }
+
+ /**
+ * Creates a new {@link Feed} that should be filled with information about
+ * the feed that will be parsed.
+ * @return The {@link Feed} that should be filled.
+ */
+ protected Feed createFeed() {
+ return new Feed();
+ }
+
+ /**
+ * Creates a new {@link Entry} that should be filled with information about
+ * the entry that will be parsed.
+ * @return The {@link Entry} that should be filled.
+ */
+ protected Entry createEntry() {
+ return new Entry();
+ }
+
+ /**
+ * Parses the partial feed (but not any entries). This requires a
+ * namespace enabled parser
+ *
+ * @return A new {@link Feed} containing information about the feed.
+ * @throws XmlPullParserException Thrown if the XML document cannot be
+ * parsed.
+ * @throws IOException Thrown if the {@link InputStream} behind the feed
+ * cannot be read.
+ */
+ private final Feed parsePartialFeed() throws XmlPullParserException, IOException {
+ // first thing to do is get the attribute we care about from the partial element
+ fields = parser.getAttributeValue(null /* ns */, XmlNametable.FIELDS);
+
+ int eventType = parser.next();
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+ switch (eventType) {
+ case XmlPullParser.START_TAG:
+ String name = parser.getName();
+ String namespace = parser.getNamespace();
+
+ if (XmlGDataParser.NAMESPACE_ATOM_URI.equals(namespace)) {
+ if (XmlNametable.FEED.equals(name)) {
+ return parseFeed();
+ }
+ }
+ default:
+ break;
+ }
+ eventType = parser.next();
+ }
+ // if we get here, we have no feed
+ return null;
+ }
+
+
+ /**
+ * Parses the feed (but not any entries). This requires a
+ * namespace enabled parser
+ *
+ * @return A new {@link Feed} containing information about the feed.
+ * @throws XmlPullParserException Thrown if the XML document cannot be
+ * parsed.
+ * @throws IOException Thrown if the {@link InputStream} behind the feed
+ * cannot be read.
+ */
+ private final Feed parseFeed() throws XmlPullParserException, IOException {
+ Feed feed = createFeed();
+ // parsing <feed>
+ feed.setETag(parser.getAttributeValue(NAMESPACE_GD_URI, XmlNametable.ETAG));
+
+ int eventType = parser.next();
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+ switch (eventType) {
+ case XmlPullParser.START_TAG:
+ String name = parser.getName();
+ String namespace = parser.getNamespace();
+
+ if (XmlGDataParser.NAMESPACE_OPENSEARCH_URI.equals(namespace)) {
+ if (XmlNametable.TOTAL_RESULTS.equals(name)) {
+ feed.setTotalResults(StringUtils.parseInt(
+ XmlUtils.extractChildText(parser), 0));
+ } else if (XmlNametable.START_INDEX.equals(name)) {
+ feed.setStartIndex(StringUtils.parseInt(
+ XmlUtils.extractChildText(parser), 0));
+ } else if (XmlNametable.ITEMS_PER_PAGE.equals(name)) {
+ feed.setItemsPerPage(StringUtils.parseInt(
+ XmlUtils.extractChildText(parser), 0));
+ }
+ } else if (XmlGDataParser.NAMESPACE_ATOM_URI.equals(namespace)) {
+ if (XmlNametable.TITLE.equals(name)) {
+ feed.setTitle(XmlUtils.extractChildText(parser));
+ } else if (XmlNametable.ID.equals(name)) {
+ feed.setId(XmlUtils.extractChildText(parser));
+ } else if (XmlNametable.UPDATED.equals(name)) {
+ feed.setLastUpdated(XmlUtils.extractChildText(parser));
+ } else if (XmlNametable.CATEGORY.equals(name)) {
+ String category =
+ parser.getAttributeValue(null /* ns */, XmlNametable.TERM);
+ if (!StringUtils.isEmpty(category)) {
+ feed.setCategory(category);
+ }
+ String categoryScheme =
+ parser.getAttributeValue(null /* ns */, XmlNametable.SCHEME);
+ if (!StringUtils.isEmpty(categoryScheme)) {
+ feed.setCategoryScheme(categoryScheme);
+ }
+ } else if (XmlNametable.ENTRY.equals(name)) {
+ // stop parsing here.
+ return feed;
+ }
+ } else {
+ handleExtraElementInFeed(feed);
+ }
+ default:
+ break;
+ }
+ eventType = parser.next();
+ }
+ // if we get here, we have a feed with no entries.
+ return feed;
+ }
+
+ /**
+ * Hook that allows extra (service-specific) elements in a &lt;feed&gt; to
+ * be parsed.
+ * @param feed The {@link Feed} being filled.
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ protected void handleExtraElementInFeed(Feed feed)
+ throws XmlPullParserException, IOException {
+ // no-op in this class.
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.google.wireless.gdata2.parser.GDataParser#hasMoreData()
+ */
+ public boolean hasMoreData() {
+ if (isInBadState) {
+ return false;
+ }
+ try {
+ int eventType = parser.getEventType();
+ return (eventType != XmlPullParser.END_DOCUMENT);
+ } catch (XmlPullParserException xppe) {
+ return false;
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.google.wireless.gdata2.parser.GDataParser#readNextEntry
+ */
+ public Entry readNextEntry(Entry entry) throws ParseException, IOException {
+ if (!hasMoreData()) {
+ throw new IllegalStateException("you shouldn't call this if hasMoreData() is false");
+ }
+
+ int eventType;
+ try {
+ eventType = parser.getEventType();
+ } catch (XmlPullParserException e) {
+ throw new ParseException("Could not parse entry.", e);
+ }
+
+ if (eventType != XmlPullParser.START_TAG) {
+ throw new ParseException("Expected event START_TAG: Actual event: "
+ + XmlPullParser.TYPES[eventType]);
+ }
+
+ String name = parser.getName();
+ // if, in the future, we have a batch feed with partial results, the next element
+ // can be either an entry or a partial element
+
+ if ((!XmlNametable.ENTRY.equals(name) &&
+ !XmlNametable.PARTIAL.equals(name))) {
+ throw new ParseException("Expected <entry> or <partial>: Actual element: "
+ + "<" + name + ">");
+ }
+
+ if (entry == null) {
+ entry = createEntry();
+ } else {
+ entry.clear();
+ }
+
+ try {
+ if (XmlNametable.ENTRY.equals(name)) {
+ handleEntry(entry);
+ } else {
+ handlePartialEntry(entry);
+ }
+ } catch (ParseException xppe1) {
+ try {
+ if (hasMoreData()) skipToNextEntry();
+ } catch (XmlPullParserException xppe2) {
+ // squelch the error -- let the original one stand.
+ // set isInBadState to ensure that the next call to hasMoreData() will return false.
+ isInBadState = true;
+ }
+ throw new ParseException("Could not parse <entry>, " + entry, xppe1);
+ } catch (XmlPullParserException xppe1) {
+ try {
+ if (hasMoreData()) skipToNextEntry();
+ } catch (XmlPullParserException xppe2) {
+ // squelch the error -- let the original one stand.
+ // set isInBadState to ensure that the next call to hasMoreData() will return false.
+ isInBadState = true;
+ }
+ throw new ParseException("Could not parse <entry>, " + entry, xppe1);
+ }
+ return entry;
+ }
+
+ /**
+ * Parses a GData entry. You can either call {@link #parseFeedEnvelope()} or
+ * {@link #parseStandaloneEntry()} for a given feed.
+ *
+ * @return The parsed entry.
+ * @throws ParseException Thrown if the entry could not be parsed.
+ */
+ public Entry parseStandaloneEntry() throws ParseException, IOException {
+ fields = null;
+ Entry entry = createEntry();
+
+ int eventType;
+ try {
+ eventType = parser.getEventType();
+ } catch (XmlPullParserException e) {
+ throw new ParseException("Could not parse GData entry.", e);
+ }
+ if (eventType != XmlPullParser.START_DOCUMENT) {
+ throw new ParseException("Attempting to initialize parsing beyond "
+ + "the start of the document.");
+ }
+
+ try {
+ eventType = parser.next();
+ } catch (XmlPullParserException xppe) {
+ throw new ParseException("Could not read next event.", xppe);
+ } catch (IOException ioe) {
+ throw new ParseException("Could not read next event.", ioe);
+ }
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+ switch (eventType) {
+ case XmlPullParser.START_TAG:
+ String name = parser.getName();
+ if (XmlNametable.PARTIAL.equals(name)) {
+ try {
+ handlePartialEntry(entry);
+ return entry;
+ } catch (XmlPullParserException xppe) {
+ throw new ParseException("Unable to parse <partial> entry.",
+ xppe);
+ } catch (IOException ioe) {
+ throw new ParseException("Unable to parse <partial> entry.",
+ ioe);
+ }
+ } else if (XmlNametable.ENTRY.equals(name)) {
+ try {
+ handleEntry(entry);
+ return entry;
+ } catch (XmlPullParserException xppe) {
+ throw new ParseException("Unable to parse <entry>.",
+ xppe);
+ } catch (IOException ioe) {
+ throw new ParseException("Unable to parse <entry>.",
+ ioe);
+ }
+ }
+ break;
+ default:
+ // ignore
+ break;
+ }
+
+ try {
+ eventType = parser.next();
+ } catch (XmlPullParserException xppe) {
+ throw new ParseException("Could not read next event.", xppe);
+ }
+ }
+ throw new ParseException("No <entry> found in document.");
+ }
+
+ /**
+ * Skips the rest of the current entry until the parser reaches the next entry, if any.
+ * Does nothing if the parser is already at the beginning of an entry.
+ * @throws IOException
+ * @throws XmlPullParserException
+ */
+ protected void skipToNextEntry() throws IOException, XmlPullParserException {
+ if (!hasMoreData()) {
+ throw new IllegalStateException("you shouldn't call this if hasMoreData() is false");
+ }
+
+ int eventType = parser.getEventType();
+
+ // skip ahead until we reach an <entry> tag.
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+ switch (eventType) {
+ case XmlPullParser.START_TAG:
+ if (XmlNametable.ENTRY.equals(parser.getName())) {
+ return;
+ }
+ break;
+ }
+ eventType = parser.next();
+ }
+ }
+
+ /**
+ * Supply a 'skipSubTree' API which, for some reason, the kxml2 pull parser
+ * hasn't implemented.
+ * @throws IOException
+ * @throws XmlPullParserException
+ */
+ protected void skipSubTree()
+ throws XmlPullParserException, IOException {
+ // Iterate the remaining structure for this element, discarding events
+ // until we hit the element's corresponding end tag.
+ int level = 1;
+ while (level > 0) {
+ int eventType = parser.next();
+ switch (eventType) {
+ case XmlPullParser.START_TAG:
+ ++level;
+ break;
+ case XmlPullParser.END_TAG:
+ --level;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ /**
+ * Parses the current partial start in the XML document. Assumes
+ * that the parser is currently pointing just at the beginning
+ * of an &lt;partial&gt;.
+ *
+ * @param entry The entry that will be filled.
+ * @throws XmlPullParserException Thrown if the XML cannot be parsed.
+ * @throws IOException Thrown if the underlying inputstream cannot be read.
+ * @throws ParseException Thrown in the stream can not be parsed into gdata
+ */
+ protected void handlePartialEntry(Entry entry)
+ throws XmlPullParserException, IOException, ParseException {
+ // first thing we do is to get the attributes out of the parser for this entry
+ // so we verify that we are at the start of an entry
+ if (!XmlNametable.PARTIAL.equals(parser.getName())) {
+ throw new
+ IllegalStateException("Expected <partial>: Actual element: <"
+ + parser.getName() + ">");
+ }
+
+ fields = parser.getAttributeValue(null /* ns */, XmlNametable.FIELDS);
+ // now skip to the next parser event
+ parser.next();
+
+ int eventType = parser.getEventType();
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+ switch (eventType) {
+ case XmlPullParser.START_TAG:
+ String name = parser.getName();
+ if (XmlNametable.ENTRY.equals(name)) {
+ handleEntry(entry);
+ return;
+ }
+ default:
+ break;
+ }
+ eventType = parser.next();
+ }
+ }
+
+ /**
+ * Parses the current entry in the XML document. Assumes that the parser
+ * is currently pointing just at the beginning of an
+ * &lt;entry&gt;.
+ *
+ * @param entry The entry that will be filled.
+ * @throws XmlPullParserException Thrown if the XML cannot be parsed.
+ * @throws IOException Thrown if the underlying inputstream cannot be read.
+ * @throws ParseException Thrown in the stream can not be parsed into gdata
+ */
+ protected void handleEntry(Entry entry)
+ throws XmlPullParserException, IOException, ParseException {
+ // first thing we do is to get the attributes out of the parser for this entry
+ // so we verify that we are at the start of an entry
+ if (!XmlNametable.ENTRY.equals(parser.getName())) {
+ throw new
+ IllegalStateException("Expected <entry>: Actual element: <"
+ + parser.getName() + ">");
+ }
+
+ entry.setETag(parser.getAttributeValue(NAMESPACE_GD_URI, XmlNametable.ETAG));
+ entry.setFields(fields);
+ // now skip to the next parser event
+ parser.next();
+
+ int eventType = parser.getEventType();
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+ switch (eventType) {
+ case XmlPullParser.START_TAG:
+ String name = parser.getName();
+ if (XmlNametable.ENTRY.equals(name)) {
+ // stop parsing here.
+ return;
+ }
+ // for each start tag, we call our subclasses first. Only do the default
+ // processing, if they have not done it.
+ if (handleDefaultEntryElements(entry)){
+ break;
+ }
+
+ if (NAMESPACE_BATCH_URI.equals(parser.getNamespace())) {
+ // We must check for the BATCH namespace first in case the tag name
+ // is reused in another namespace. e.g. "id".
+ handleBatchInfo(entry);
+ } else if (XmlNametable.ID.equals(name)) {
+ entry.setId(XmlUtils.extractChildText(parser));
+ } else if (XmlNametable.TITLE.equals(name)) {
+ entry.setTitle(XmlUtils.extractChildText(parser));
+ } else if (XmlNametable.LINK.equals(name)) {
+ String rel =
+ parser.getAttributeValue(null /* ns */, XmlNametable.REL);
+ String type =
+ parser.getAttributeValue(null /* ns */, XmlNametable.TYPE);
+ String href =
+ parser.getAttributeValue(null /* ns */, XmlNametable.HREF);
+ if (XmlNametable.EDIT_REL.equals(rel)) {
+ entry.setEditUri(href);
+ } else if (XmlNametable.ALTERNATE_REL.equals(rel)
+ && XmlNametable.TEXTHTML.equals(type)) {
+ entry.setHtmlUri(href);
+ } else {
+ handleExtraLinkInEntry(rel,
+ type,
+ href,
+ entry);
+ }
+ } else if (XmlNametable.SUMMARY.equals(name)) {
+ entry.setSummary(XmlUtils.extractChildText(parser));
+ } else if (XmlNametable.CONTENT.equals(name)) {
+ entry.setContentType(parser.getAttributeValue(null /* ns */, XmlNametable.TYPE));
+ entry.setContentSource(parser.getAttributeValue(null /* ns */, XmlNametable.SRC));
+ entry.setContent(XmlUtils.extractChildText(parser));
+ } else if (XmlNametable.AUTHOR.equals(name)) {
+ handleAuthor(entry);
+ } else if (XmlNametable.CATEGORY.equals(name)) {
+ String category =
+ parser.getAttributeValue(null /* ns */, XmlNametable.TERM);
+ if (category != null && category.length() > 0) {
+ entry.setCategory(category);
+ }
+ String categoryScheme =
+ parser.getAttributeValue(null /* ns */, XmlNametable.SCHEME);
+ if (categoryScheme != null && category.length() > 0) {
+ entry.setCategoryScheme(categoryScheme);
+ }
+ } else if (XmlNametable.PUBLISHED.equals(name)) {
+ entry.setPublicationDate(
+ XmlUtils.extractChildText(parser));
+ } else if (XmlNametable.UPDATED.equals(name)) {
+ entry.setUpdateDate(XmlUtils.extractChildText(parser));
+ } else if (XmlNametable.DELETED.equals(name)) {
+ entry.setDeleted(true);
+ } else {
+ handleExtraElementInEntry(entry);
+ }
+ break;
+ default:
+ break;
+ }
+
+ eventType = parser.next();
+ }
+ entry.validate();
+ }
+
+ private void handleAuthor(Entry entry)
+ throws XmlPullParserException, IOException {
+
+ int eventType = parser.getEventType();
+ String name = parser.getName();
+
+ if (eventType != XmlPullParser.START_TAG ||
+ (!XmlNametable.AUTHOR.equals(parser.getName()))) {
+ // should not happen.
+ throw new
+ IllegalStateException("Expected <author>: Actual element: <"
+ + parser.getName() + ">");
+ }
+
+ eventType = parser.next();
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+ switch (eventType) {
+ case XmlPullParser.START_TAG:
+ name = parser.getName();
+ if (XmlNametable.NAME.equals(name)) {
+ String authorName = XmlUtils.extractChildText(parser);
+ entry.setAuthor(authorName);
+ } else if (XmlNametable.EMAIL.equals(name)) {
+ String email = XmlUtils.extractChildText(parser);
+ entry.setEmail(email);
+ }
+ break;
+ case XmlPullParser.END_TAG:
+ name = parser.getName();
+ if (XmlNametable.AUTHOR.equals(name)) {
+ return;
+ }
+ default:
+ // ignore
+ }
+
+ eventType = parser.next();
+ }
+ }
+
+ private void handleBatchInfo(Entry entry)
+ throws IOException, XmlPullParserException {
+ String name = parser.getName();
+ if (XmlNametable.STATUS.equals(name)) {
+ BatchStatus status = new BatchStatus();
+ BatchUtils.setBatchStatus(entry, status);
+ status.setStatusCode(getIntAttribute(parser, XmlNametable.CODE));
+ status.setReason(getAttribute(parser, XmlNametable.REASON));
+ status.setContentType(getAttribute(parser, XmlNametable.CONTENT_TYPE));
+ // TODO: Read sub-tree into content.
+ skipSubTree();
+ } else if (XmlNametable.ID.equals(name)) {
+ BatchUtils.setBatchId(entry, XmlUtils.extractChildText(parser));
+ } else if (XmlNametable.OPERATION.equals(name)) {
+ BatchUtils.setBatchOperation(entry, getAttribute(parser, XmlNametable.TYPE));
+ } else if ("interrupted".equals(name)) {
+ BatchInterrupted interrupted = new BatchInterrupted();
+ BatchUtils.setBatchInterrupted(entry, interrupted);
+ interrupted.setReason(getAttribute(parser, XmlNametable.REASON));
+ interrupted.setErrorCount(getIntAttribute(parser, XmlNametable.ERROR));
+ interrupted.setSuccessCount(getIntAttribute(parser, XmlNametable.SUCCESS));
+ interrupted.setTotalCount(getIntAttribute(parser, XmlNametable.PARSED));
+ // TODO: Read sub-tree into content.
+ skipSubTree();
+ } else {
+ throw new XmlPullParserException("Unexpected batch element " + name);
+ }
+ }
+
+ private static String getAttribute(XmlPullParser parser, String name) {
+ return parser.getAttributeValue(null /* ns */, name);
+ }
+
+ private static int getIntAttribute(XmlPullParser parser, String name) {
+ return Integer.parseInt(getAttribute(parser, name));
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.google.wireless.gdata2.parser.GDataParser#close()
+ */
+ public void close() {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException ioe) {
+ // ignore
+ }
+ }
+ }
+
+ /**
+ * Hook that allows extra (service-specific) elements in an &lt;entry&gt;
+ * to be parsed.
+ * @param entry The {@link Entry} being filled.
+ * @throws IOException
+ * @throws XmlPullParserException
+ * @throws ParseException Thrown in the stream can not be parsed into gdata
+ */
+ protected void handleExtraElementInEntry(Entry entry)
+ throws XmlPullParserException, IOException, ParseException {
+ // no-op in this class.
+ }
+
+ /**
+ * Hook that allows a subclass to override default parsing
+ * behaviour. If the subclass returns true from this call,
+ * no default parsing will happen for the currently parsed tag
+ * @param entry The {@link Entry} being filled.
+ * @return true if the subclass handled the parsing.
+ * @throws IOException
+ * @throws XmlPullParserException
+ */
+ protected boolean handleDefaultEntryElements(Entry entry)
+ throws XmlPullParserException, IOException {
+ // no-op in this class.
+ return false;
+ }
+ /**
+ * Hook that allows extra (service-specific) &lt;link&gt;s in an entry to be
+ * parsed.
+ * @param rel The rel attribute value.
+ * @param type The type attribute value.
+ * @param href The href attribute value.
+ * @param entry The {@link Entry} being filled.
+ * @throws IOException
+ * @throws XmlPullParserException
+ */
+ protected void handleExtraLinkInEntry(String rel,
+ String type,
+ String href,
+ Entry entry)
+ throws XmlPullParserException, IOException {
+ // no-op in this class.
+ }
+}
diff --git a/src/com/google/wireless/gdata2/parser/xml/XmlMediaEntryGDataParser.java b/src/com/google/wireless/gdata2/parser/xml/XmlMediaEntryGDataParser.java
new file mode 100644
index 0000000..d15f906
--- /dev/null
+++ b/src/com/google/wireless/gdata2/parser/xml/XmlMediaEntryGDataParser.java
@@ -0,0 +1,44 @@
+// Copyright 2009 The Android Open Source Project
+
+package com.google.wireless.gdata2.parser.xml;
+
+import com.google.wireless.gdata2.data.MediaEntry;
+import com.google.wireless.gdata2.data.Entry;
+import com.google.wireless.gdata2.data.Feed;
+import com.google.wireless.gdata2.parser.ParseException;
+
+import org.xmlpull.v1.XmlPullParser;
+
+import java.io.InputStream;
+
+/**
+ * GDataParser for a MediaEntry. This must only be used to parse an entry, not a feed, since
+ * there is no such thing as a feed of media entries.
+ */
+public class XmlMediaEntryGDataParser extends XmlGDataParser {
+ /**
+ * Creates a new XmlMediaEntryGDataParser.
+ * @param is The InputStream that should be parsed.
+ * @param parser the XmlPullParser to use for the xml parsing
+ * @throws com.google.wireless.gdata2.parser.ParseException Thrown if a parser cannot be created.
+ */
+ public XmlMediaEntryGDataParser(InputStream is, XmlPullParser parser) throws ParseException {
+ super(is, parser);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.google.wireless.gdata2.parser.xml.XmlGDataParser#createFeed()
+ */
+ protected Feed createFeed() {
+ throw new IllegalStateException("there is no such thing as a feed of media entries");
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.google.wireless.gdata2.parser.xml.XmlGDataParser#createEntry()
+ */
+ protected Entry createEntry() {
+ return new MediaEntry();
+ }
+}
diff --git a/src/com/google/wireless/gdata2/parser/xml/XmlNametable.java b/src/com/google/wireless/gdata2/parser/xml/XmlNametable.java
new file mode 100644
index 0000000..381a48c
--- /dev/null
+++ b/src/com/google/wireless/gdata2/parser/xml/XmlNametable.java
@@ -0,0 +1,53 @@
+// Copyright 2009 The Android Open Source Project
+
+package com.google.wireless.gdata2.parser.xml;
+
+/**
+ * Class to hold static strings that are used to parse and
+ * serialize the xml elements
+ */
+public final class XmlNametable {
+ private XmlNametable() {}
+
+ // generic strings for attributes
+ public static String PARTIAL = "partial";
+ public static String FIELDS = "fields";
+ public static String ENTRY = "entry";
+ public static String FEED = "feed";
+ public static String UTF8 = "UTF-8";
+ public static String EDIT_REL = "edit";
+ public static String ALTERNATE_REL = "alternate";
+ public static String REL = "rel";
+ public static String LINK = "link";
+ public static String HREF = "href";
+ public static String ETAG = "etag";
+ public static String TYPE = "type";
+ public static String SRC = "src";
+ public static String TEXT = "text";
+ public static String TEXTHTML = "text/html";
+ public static String ID = "id";
+ public static String TITLE = "title";
+ public static String SUMMARY = "summary";
+ public static String CONTENT = "content";
+ public static String AUTHOR = "author";
+ public static String EMAIL = "email";
+ public static String NAME = "name";
+ public static String CATEGORY = "category";
+ public static String TERM = "term";
+ public static String SCHEME = "scheme";
+ public static String PUBLISHED = "published";
+ public static String UPDATED = "updated";
+ public static String OPERATION = "operation";
+ public static String TOTAL_RESULTS = "totalResults";
+ public static String START_INDEX = "startIndex";
+ public static String ITEMS_PER_PAGE = "itemsPerPage";
+ public static String DELETED = "deleted";
+ public static String STATUS = "status";
+ public static String CODE = "code";
+ public static String REASON = "reason";
+ public static String CONTENT_TYPE = "content-type";
+ public static String ERROR = "error";
+ public static String SUCCESS = "success";
+ public static String PARSED = "parsed";
+
+}
diff --git a/src/com/google/wireless/gdata2/parser/xml/XmlParserFactory.java b/src/com/google/wireless/gdata2/parser/xml/XmlParserFactory.java
new file mode 100644
index 0000000..650db50
--- /dev/null
+++ b/src/com/google/wireless/gdata2/parser/xml/XmlParserFactory.java
@@ -0,0 +1,31 @@
+// Copyright 2007 The Android Open Source Project
+
+package com.google.wireless.gdata2.parser.xml;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+/**
+ * Factory for creating new {@link org.xmlpull.v1.XmlPullParser}s and
+ * {@link org.xmlpull.v1.XmlSerializer}s
+ */
+public interface XmlParserFactory {
+
+ /**
+ * Creates a new {@link XmlPullParser}.
+ *
+ * @return A new {@link XmlPullParser}.
+ * @throws XmlPullParserException Thrown if the parser could not be created.
+ */
+ XmlPullParser createParser() throws XmlPullParserException;
+
+ /**
+ * Creates a new {@link XmlSerializer}.
+ *
+ * @return A new {@link XmlSerializer}.
+ * @throws XmlPullParserException Thrown if the serializer could not be
+ * created.
+ */
+ XmlSerializer createSerializer() throws XmlPullParserException;
+}
diff --git a/src/com/google/wireless/gdata2/parser/xml/package.html b/src/com/google/wireless/gdata2/parser/xml/package.html
new file mode 100644
index 0000000..1c9bf9d
--- /dev/null
+++ b/src/com/google/wireless/gdata2/parser/xml/package.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+ {@hide}
+</body>
+</html>
diff --git a/src/com/google/wireless/gdata2/serializer/GDataSerializer.java b/src/com/google/wireless/gdata2/serializer/GDataSerializer.java
new file mode 100644
index 0000000..587790d
--- /dev/null
+++ b/src/com/google/wireless/gdata2/serializer/GDataSerializer.java
@@ -0,0 +1,69 @@
+// Copyright 2007 The Android Open Source Project
+
+package com.google.wireless.gdata2.serializer;
+
+import com.google.wireless.gdata2.parser.ParseException;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Interface for serializing GData entries. A serializer has to be aware
+ * what mode it is in while it is serializing.
+ */
+public interface GDataSerializer {
+
+ /**
+ * Serialize all data in the entry. Used for debugging.
+ */
+ public static final int FORMAT_FULL = 0;
+
+ /**
+ * Serialize only the data necessary for creating a new entry.
+ */
+ public static final int FORMAT_CREATE = 1;
+
+ /**
+ * Serialize only the data necessary for updating an existing entry.
+ */
+ public static final int FORMAT_UPDATE = 2;
+
+ /**
+ * Serialize the entry as part of a batch of operations.
+ */
+ public static final int FORMAT_BATCH = 3;
+
+ /**
+ * Returns the Content-Type for this serialization format.
+ * @return The Content-Type for this serialization format.
+ */
+ String getContentType();
+
+ /**
+ * Returns if this serializer supports a partial representation
+ * of the underlying content
+ *
+ * @return boolean True if a partial representation will be
+ * created
+ */
+ boolean getSupportsPartial();
+
+ /**
+ * Serializes a GData entry to the provided {@link OutputStream}, using the
+ * specified serialization format.
+ *
+ * @see #FORMAT_FULL
+ * @see #FORMAT_CREATE
+ * @see #FORMAT_UPDATE
+ * @see #FORMAT_BATCH
+ *
+ * @param out The {@link OutputStream} to which the entry should be
+ * serialized.
+ * @param format The format of the serialized output.
+ * @throws IOException Thrown if there is an issue writing the serialized
+ * entry to the provided {@link OutputStream}.
+ * @throws ParseException Thrown if the entry cannot be serialized.
+ */
+ void serialize(OutputStream out, int format)
+ throws IOException, ParseException;
+}
diff --git a/src/com/google/wireless/gdata2/serializer/package.html b/src/com/google/wireless/gdata2/serializer/package.html
new file mode 100644
index 0000000..1c9bf9d
--- /dev/null
+++ b/src/com/google/wireless/gdata2/serializer/package.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+ {@hide}
+</body>
+</html>
diff --git a/src/com/google/wireless/gdata2/serializer/xml/XmlBatchGDataSerializer.java b/src/com/google/wireless/gdata2/serializer/xml/XmlBatchGDataSerializer.java
new file mode 100644
index 0000000..22a6824
--- /dev/null
+++ b/src/com/google/wireless/gdata2/serializer/xml/XmlBatchGDataSerializer.java
@@ -0,0 +1,108 @@
+// Copyright 2008 Google Inc. All Rights Reserved.
+
+package com.google.wireless.gdata2.serializer.xml;
+
+import com.google.wireless.gdata2.serializer.GDataSerializer;
+import com.google.wireless.gdata2.parser.ParseException;
+import com.google.wireless.gdata2.parser.xml.XmlParserFactory;
+import com.google.wireless.gdata2.parser.xml.XmlGDataParser;
+import com.google.wireless.gdata2.parser.xml.XmlNametable;
+import com.google.wireless.gdata2.client.GDataParserFactory;
+import com.google.wireless.gdata2.data.Entry;
+
+import org.xmlpull.v1.XmlSerializer;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.OutputStream;
+import java.io.IOException;
+import java.util.Enumeration;
+
+/**
+ * Serializes a batch of GData requests as an XML stream.
+ */
+public class XmlBatchGDataSerializer implements GDataSerializer {
+
+ private final GDataParserFactory gdataFactory;
+ private final XmlParserFactory xmlFactory;
+ private final Enumeration batch;
+
+ /*
+ * Constructs an XmlBatchGDataSerializer for serializing the given batch
+ * of GData requests
+ *
+ * @param gdataFactory used to create serializers for individual requests
+ * @param xmlFactory used to create the XML stream
+ * @param Enumeration the batch of requests to serialize
+ */
+ public XmlBatchGDataSerializer(GDataParserFactory gdataFactory,
+ XmlParserFactory xmlFactory, Enumeration batch) {
+ this.gdataFactory = gdataFactory;
+ this.xmlFactory = xmlFactory;
+ this.batch = batch;
+ }
+
+ /* (non-Javadoc)
+ * @see GDataSerializer#getContentType()
+ */
+ public String getContentType() {
+ return "application/atom+xml";
+ }
+
+ /* (non-Javadoc)
+ * @see GDataSerializer#doesSupportPartial()
+ */
+ public boolean getSupportsPartial() {
+ return false;
+ }
+
+
+ /* (non-Javadoc)
+ * @see GDataSerializer#serialize()
+ */
+ public void serialize(OutputStream out, int format)
+ throws IOException, ParseException {
+ XmlSerializer serializer;
+ try {
+ serializer = xmlFactory.createSerializer();
+ } catch (XmlPullParserException e) {
+ throw new ParseException("Unable to create XmlSerializer.", e);
+ }
+
+ serializer.setOutput(out, XmlNametable.UTF8);
+ serializer.startDocument(XmlNametable.UTF8, Boolean.FALSE);
+
+ declareNamespaces(serializer);
+
+ boolean first = true;
+ while (batch.hasMoreElements()) {
+ Entry entry = (Entry) batch.nextElement();
+ XmlEntryGDataSerializer entrySerializer = (XmlEntryGDataSerializer)
+ gdataFactory.createSerializer(entry);
+
+ if (first) {
+ // Let the first entry serializer declare extra namespaces.
+ first = false;
+ serializer.startTag(XmlGDataParser.NAMESPACE_ATOM_URI, XmlNametable.FEED);
+ entrySerializer.declareExtraEntryNamespaces(serializer);
+ }
+ entrySerializer.serialize(out, GDataSerializer.FORMAT_BATCH);
+ }
+
+ if (first) {
+ serializer.startTag(XmlGDataParser.NAMESPACE_ATOM_URI, XmlNametable.FEED);
+ }
+
+ serializer.endTag(XmlGDataParser.NAMESPACE_ATOM_URI, XmlNametable.FEED);
+ serializer.endDocument();
+ serializer.flush();
+ }
+
+ private static void declareNamespaces(XmlSerializer serializer) throws IOException {
+ serializer.setPrefix("" /* default ns */,
+ XmlGDataParser.NAMESPACE_ATOM_URI);
+ serializer.setPrefix(XmlGDataParser.NAMESPACE_GD,
+ XmlGDataParser.NAMESPACE_GD_URI);
+ serializer.setPrefix(XmlGDataParser.NAMESPACE_BATCH,
+ XmlGDataParser.NAMESPACE_BATCH_URI);
+ }
+}
diff --git a/src/com/google/wireless/gdata2/serializer/xml/XmlEntryGDataSerializer.java b/src/com/google/wireless/gdata2/serializer/xml/XmlEntryGDataSerializer.java
new file mode 100644
index 0000000..f9fad40
--- /dev/null
+++ b/src/com/google/wireless/gdata2/serializer/xml/XmlEntryGDataSerializer.java
@@ -0,0 +1,326 @@
+// Copyright 2007 The Android Open Source Project
+
+package com.google.wireless.gdata2.serializer.xml;
+
+import com.google.wireless.gdata2.data.batch.BatchUtils;
+import com.google.wireless.gdata2.data.Entry;
+import com.google.wireless.gdata2.data.StringUtils;
+import com.google.wireless.gdata2.parser.ParseException;
+import com.google.wireless.gdata2.parser.xml.XmlGDataParser;
+import com.google.wireless.gdata2.parser.xml.XmlNametable;
+import com.google.wireless.gdata2.parser.xml.XmlParserFactory;
+import com.google.wireless.gdata2.serializer.GDataSerializer;
+
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Serializes GData entries to the Atom XML format.
+ */
+public class XmlEntryGDataSerializer implements GDataSerializer {
+
+ /** The XmlParserFactory that is used to create the XmlSerializer */
+ private final XmlParserFactory factory;
+
+ /** The entry being serialized. */
+ private final Entry entry;
+
+ private final boolean supportsPartial;
+
+ /**
+ * Creates a new XmlEntryGDataSerializer that will serialize the provided
+ * entry.
+ *
+ * @param entry The entry that should be serialized.
+ */
+ public XmlEntryGDataSerializer(XmlParserFactory factory, Entry entry) {
+ this.factory = factory;
+ this.entry = entry;
+ supportsPartial = !StringUtils.isEmptyOrWhitespace(this.entry.getFields());
+}
+
+ /**
+ * Returns the entry being serialized.
+ * @return The entry being serialized.
+ */
+ protected Entry getEntry() {
+ return entry;
+ }
+
+ /* (non-Javadoc)
+ * @see GDataSerializer#getContentType()
+ */
+ public String getContentType() {
+ return "application/atom+xml";
+ }
+
+
+ /* (non-Javadoc)
+ * @see GDataSerializer#getSupportsPartial()
+ */
+ public boolean getSupportsPartial() {
+ return supportsPartial;
+ }
+
+
+ /* (non-Javadoc)
+ * @see GDataSerializer#serialize(java.io.OutputStream)
+ */
+ public void serialize(OutputStream out, int format)
+ throws IOException, ParseException {
+ XmlSerializer serializer = null;
+ try {
+ serializer = factory.createSerializer();
+ } catch (XmlPullParserException e) {
+ throw new ParseException("Unable to create XmlSerializer.", e);
+ }
+ // TODO: make the output compact
+
+ serializer.setOutput(out, XmlNametable.UTF8);
+ if (format != FORMAT_BATCH) {
+ serializer.startDocument(XmlNametable.UTF8, Boolean.FALSE);
+ declareEntryNamespaces(serializer);
+ }
+ final String fields = entry.getFields();
+ if (getSupportsPartial()) {
+ serializer.startTag(XmlGDataParser.NAMESPACE_GD_URI, XmlNametable.PARTIAL);
+ serializer.attribute(null /* ns */, XmlNametable.FIELDS, fields);
+ }
+
+ serializer.startTag(XmlGDataParser.NAMESPACE_ATOM_URI, XmlNametable.ENTRY);
+
+ serializeEntryContents(serializer, format);
+
+ serializer.endTag(XmlGDataParser.NAMESPACE_ATOM_URI, XmlNametable.ENTRY);
+
+ if (getSupportsPartial()) {
+ serializer.endTag(XmlGDataParser.NAMESPACE_GD_URI, XmlNametable.PARTIAL);
+ }
+
+ if (format != FORMAT_BATCH) {
+ serializer.endDocument();
+ }
+ serializer.flush();
+ }
+
+ private void declareEntryNamespaces(XmlSerializer serializer)
+ throws IOException {
+ serializer.setPrefix("" /* default ns */, XmlGDataParser.NAMESPACE_ATOM_URI);
+ serializer.setPrefix(XmlGDataParser.NAMESPACE_GD, XmlGDataParser.NAMESPACE_GD_URI);
+ declareExtraEntryNamespaces(serializer);
+ }
+
+ protected void declareExtraEntryNamespaces(XmlSerializer serializer)
+ throws IOException {
+ // no-op in this class
+ }
+
+ /**
+ * @param serializer
+ * @throws IOException
+ */
+ private void serializeEntryContents(XmlSerializer serializer, int format)
+ throws ParseException, IOException {
+
+ if (format == FORMAT_BATCH) {
+ serializeBatchInfo(serializer);
+ }
+
+ if (format != FORMAT_CREATE) {
+ serializeId(serializer, entry.getId());
+ }
+
+ serializeTitle(serializer, entry.getTitle());
+
+ if (format != FORMAT_CREATE) {
+ serializeLink(serializer, XmlNametable.EDIT_REL /* rel */,
+ entry.getEditUri(), null /* type */, null /* etag */ );
+ serializeLink(serializer, XmlNametable.ALTERNATE_REL /* rel */,
+ entry.getHtmlUri(), XmlNametable.TEXTHTML /* type */, null /* etag */ );
+ }
+
+ serializeSummary(serializer, entry.getSummary());
+
+ serializeContent(serializer, entry.getContent());
+
+ serializeAuthor(serializer, entry.getAuthor(), entry.getEmail());
+
+ serializeCategory(serializer, entry.getCategory(), entry.getCategoryScheme());
+
+ if (format == FORMAT_FULL) {
+ serializePublicationDate(serializer,
+ entry.getPublicationDate());
+ }
+
+ if (format != FORMAT_CREATE) {
+ serializeUpdateDate(serializer,
+ entry.getUpdateDate());
+ }
+
+ serializeExtraEntryContents(serializer, format);
+ }
+
+ /**
+ * Hook for subclasses to serialize extra fields within the entry.
+ * @param serializer The XmlSerializer being used to serialize the entry.
+ * @param format The serialization format for the entry.
+ * @throws ParseException Thrown if the entry cannot be serialized.
+ * @throws IOException Thrown if the entry cannot be written to the
+ * underlying {@link OutputStream}.
+ */
+ protected void serializeExtraEntryContents(XmlSerializer serializer, int format)
+ throws ParseException, IOException {
+ // no-op in this class.
+ }
+
+ // TODO: make these helper methods protected so sublcasses can use them?
+
+ private static void serializeId(XmlSerializer serializer, String id) throws IOException {
+ if (StringUtils.isEmpty(id)) {
+ return;
+ }
+ serializer.startTag(null /* ns */, XmlNametable.ID);
+ serializer.text(id);
+ serializer.endTag(null /* ns */, XmlNametable.ID);
+ }
+
+ private static void serializeTitle(XmlSerializer serializer,
+ String title)
+ throws IOException {
+ if (StringUtils.isEmpty(title)) {
+ return;
+ }
+ serializer.startTag(null /* ns */, XmlNametable.TITLE);
+ serializer.text(title);
+ serializer.endTag(null /* ns */, XmlNametable.TITLE);
+ }
+
+ /**
+ * Serializes a link relationship into the xml serializer passed in.
+ *
+ * @param serializer The serializer to be used
+ * @param rel The relationship value (like alternate, edit etc)
+ * @param href the URI this link points to
+ * @param type the content type
+ * @param etag the optional etag if this link specifies a resource
+ *
+ * @exception IOException
+ */
+ protected static void serializeLink(XmlSerializer serializer, String rel, String href,
+ String type, String etag) throws IOException {
+ if (StringUtils.isEmpty(href)) {
+ return;
+ }
+ serializer.startTag(null /* ns */, XmlNametable.LINK);
+ serializer.attribute(null /* ns */, XmlNametable.REL, rel);
+ serializer.attribute(null /* ns */, XmlNametable.HREF, href);
+ if (!StringUtils.isEmpty(type)) serializer.attribute(null /* ns */, XmlNametable.TYPE, type);
+ if (!StringUtils.isEmpty(etag)) serializer.attribute(null /* ns */, XmlNametable.ETAG, etag);
+ serializer.endTag(null /* ns */, XmlNametable.LINK);
+ }
+
+ private static void serializeSummary(XmlSerializer serializer,
+ String summary)
+ throws IOException {
+ if (StringUtils.isEmpty(summary)) {
+ return;
+ }
+ serializer.startTag(null /* ns */, XmlNametable.SUMMARY);
+ serializer.text(summary);
+ serializer.endTag(null /* ns */, XmlNametable.SUMMARY);
+ }
+
+ private static void serializeContent(XmlSerializer serializer,
+ String content)
+ throws IOException {
+ if (content == null) {
+ return;
+ }
+ serializer.startTag(null /* ns */, XmlNametable.CONTENT);
+ serializer.attribute(null /* ns */, XmlNametable.TYPE, XmlNametable.TEXT);
+ serializer.text(content);
+ serializer.endTag(null /* ns */, XmlNametable.CONTENT);
+ }
+
+ private static void serializeAuthor(XmlSerializer serializer,
+ String author,
+ String email)
+ throws IOException {
+ if (StringUtils.isEmpty(author) || StringUtils.isEmpty(email)) {
+ return;
+ }
+ serializer.startTag(null /* ns */, XmlNametable.AUTHOR);
+ serializer.startTag(null /* ns */, XmlNametable.NAME);
+ serializer.text(author);
+ serializer.endTag(null /* ns */, XmlNametable.NAME);
+ serializer.startTag(null /* ns */, XmlNametable.EMAIL);
+ serializer.text(email);
+ serializer.endTag(null /* ns */, XmlNametable.EMAIL);
+ serializer.endTag(null /* ns */, XmlNametable.AUTHOR);
+ }
+
+ private static void serializeCategory(XmlSerializer serializer,
+ String category,
+ String categoryScheme)
+ throws IOException {
+ if (StringUtils.isEmpty(category) &&
+ StringUtils.isEmpty(categoryScheme)) {
+ return;
+ }
+ serializer.startTag(null /* ns */, XmlNametable.CATEGORY);
+ if (!StringUtils.isEmpty(category)) {
+ serializer.attribute(null /* ns */, XmlNametable.TERM, category);
+ }
+ if (!StringUtils.isEmpty(categoryScheme)) {
+ serializer.attribute(null /* ns */, XmlNametable.SCHEME, categoryScheme);
+ }
+ serializer.endTag(null /* ns */, XmlNametable.CATEGORY);
+ }
+
+ private static void
+ serializePublicationDate(XmlSerializer serializer,
+ String publicationDate)
+ throws IOException {
+ if (StringUtils.isEmpty(publicationDate)) {
+ return;
+ }
+ serializer.startTag(null /* ns */, XmlNametable.PUBLISHED);
+ serializer.text(publicationDate);
+ serializer.endTag(null /* ns */, XmlNametable.PUBLISHED);
+ }
+
+ private static void
+ serializeUpdateDate(XmlSerializer serializer,
+ String updateDate)
+ throws IOException {
+ if (StringUtils.isEmpty(updateDate)) {
+ return;
+ }
+ serializer.startTag(null /* ns */, XmlNametable.UPDATED);
+ serializer.text(updateDate);
+ serializer.endTag(null /* ns */, XmlNametable.UPDATED);
+ }
+
+ private void serializeBatchInfo(XmlSerializer serializer)
+ throws IOException {
+ if (!StringUtils.isEmpty(entry.getETag())) {
+ serializer.attribute(XmlGDataParser.NAMESPACE_GD_URI, XmlNametable.ETAG,
+ entry.getETag());
+ }
+ if (!StringUtils.isEmpty(BatchUtils.getBatchOperation(entry))) {
+ serializer.startTag(XmlGDataParser.NAMESPACE_BATCH_URI, XmlNametable.OPERATION);
+ serializer.attribute(null /* ns */, XmlNametable.TYPE,
+ BatchUtils.getBatchOperation(entry));
+ serializer.endTag(XmlGDataParser.NAMESPACE_BATCH_URI, XmlNametable.OPERATION);
+ }
+ if (!StringUtils.isEmpty(BatchUtils.getBatchId(entry))) {
+ serializer.startTag(XmlGDataParser.NAMESPACE_BATCH_URI, XmlNametable.ID);
+ serializer.text(BatchUtils.getBatchId(entry));
+ serializer.endTag(XmlGDataParser.NAMESPACE_BATCH_URI, XmlNametable.ID);
+ }
+ }
+
+}
diff --git a/src/com/google/wireless/gdata2/serializer/xml/package.html b/src/com/google/wireless/gdata2/serializer/xml/package.html
new file mode 100644
index 0000000..1c9bf9d
--- /dev/null
+++ b/src/com/google/wireless/gdata2/serializer/xml/package.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+ {@hide}
+</body>
+</html>