/* Copyright (c) 2008 Google Inc.
*
* 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.gdata.data.extensions;
import com.google.gdata.util.common.xml.XmlWriter;
import com.google.gdata.client.CoreErrorDomain;
import com.google.gdata.data.DateTime;
import com.google.gdata.data.Extension;
import com.google.gdata.data.ExtensionDescription;
import com.google.gdata.data.ExtensionPoint;
import com.google.gdata.data.ExtensionProfile;
import com.google.gdata.util.Namespaces;
import com.google.gdata.util.ParseException;
import com.google.gdata.util.XmlParser;
import org.xml.sax.Attributes;
import java.io.IOException;
import java.util.ArrayList;
/**
* GData schema extension describing a reminder on an event. You can
* represent a set of reminders where each has a (1) reminder period
* and (2) notification method. The method can be either "sms",
* "email", "alert", "none", "all".
*
* <p>
* The meaning of this set of reminders differs based on whether you
* are reading or writing feeds. When reading, the set of reminders
* returned on an event takes into account both defaults on a
* parent recurring event (when applicable) as well as the user's
* defaults on calendar. If there are no gd:reminders returned that
* means the event has absolutely no reminders. "none" or "all" will
* not apply in this case.
*
* <p>
* Writing is different because we have to be backwards-compatible
* (see *) with the old way of setting reminders. For easier analysis
* we describe all the behaviors defined in the table below. (Notice
* we only include cases for minutes, as the other cases specified in
* terms of days/hours/absoluteTime can be converted to this case.)
*
* <p>
* Notice method is case-sensitive: must be in lowercase!
*
* <pre>
* no method method method=
* or method=all =none email|sms|alert
* ____________________________________________________________________________
* no gd:rem *no reminder N/A N/A
*
* 1 gd:rem *use user's no reminder InvalidEntryException
* def. settings
*
* 1 gd:rem min=0 *use user's no reminder InvalidEntryException
* def. settings
*
* 1 gd:rem min=-1 *no reminder no reminder InvalidEntryException
*
* 1 gd:rem min=+n *override with no reminder set exactly one reminder
* +n for user's on event at +n with given
* selected method
* methods
*
* multiple gd:rem InvalidEntry- InvalidEntry- copy this set exactly
* Exception Exception
* </pre>
*
* Hence, to override an event with a set of reminder <time, method>
* pairs, just specify them exactly. To clear an event of all
* overrides (and go back to inheriting the user's defaults), one can
* simply specify a single gd:reminder with no extra attributes. To
* have NO event reminders on an event, either set a single
* gd:reminder with negative reminder time, or simply update the event
* with a single <gd:reminder method=none/>.
*
*
*/
public class Reminder extends ExtensionPoint implements Extension {
public enum Method {
ALERT,
ALL,
EMAIL,
NONE,
SMS;
/**
* @throws IllegalArgumentException if it doesn't match a method
*/
public static Method fromString(String s) {
if (!s.equals(s.toLowerCase())) {
throw new IllegalArgumentException("Bad input for method: " + s);
}
return Enum.<Method>valueOf(Method.class, s.toUpperCase());
}
/**
* Creates the string value suitable for the value of "method" in
* a GData feed.
*/
public String generate() {
return name().toLowerCase();
}
}
/** Number of days before the start time. */
protected Integer days;
public Integer getDays() { return days; }
public void setDays(Integer v) { days = v; }
/** Number of hours before the start time. */
protected Integer hours;
public Integer getHours() { return hours; }
public void setHours(Integer v) { hours = v; }
/** Number of minute before the start times. */
protected Integer minutes;
public Integer getMinutes() { return minutes; }
public void setMinutes(Integer v) { minutes = v; }
/** Absolute time of the reminder. */
protected DateTime absoluteTime;
public DateTime getAbsoluteTime() { return absoluteTime; }
public void setAbsoluteTime(DateTime v) { absoluteTime = v; }
/** Optional: if not set we use the user's default methods on this calendar */
protected Method method;
public Method getMethod() { return method; }
public void setMethod(Method v) { method = v; }
/** Returns the suggested extension description. */
public static ExtensionDescription getDefaultDescription() {
ExtensionDescription desc = new ExtensionDescription();
desc.setExtensionClass(Reminder.class);
desc.setNamespace(Namespaces.gNs);
desc.setLocalName("reminder");
desc.setRepeatable(true);
return desc;
}
@Override
public void generate(XmlWriter w, ExtensionProfile extProfile)
throws IOException {
ArrayList<XmlWriter.Attribute> attrs = new ArrayList<XmlWriter.Attribute>();
if (days != null) {
attrs.add(new XmlWriter.Attribute("days", days.toString()));
}
if (hours != null) {
attrs.add(new XmlWriter.Attribute("hours", hours.toString()));
}
if (minutes != null) {
attrs.add(new XmlWriter.Attribute("minutes", minutes.toString()));
}
if (absoluteTime != null) {
attrs.add(new XmlWriter.Attribute("absoluteTime",
absoluteTime.toString()));
}
if (method != null) {
attrs.add(new XmlWriter.Attribute("method",
method.generate()));
}
generateStartElement(w, Namespaces.gNs, "reminder", attrs, null);
// Invoke ExtensionPoint.
generateExtensions(w, extProfile);
w.endElement(Namespaces.gNs, "reminder");
}
@Override
public XmlParser.ElementHandler getHandler(ExtensionProfile extProfile,
String namespace, String localName, Attributes attrs) {
return new Handler(extProfile);
}
/** <g:reminder> parser. */
private class Handler extends ExtensionPoint.ExtensionHandler {
public Handler(ExtensionProfile extProfile) {
super(extProfile, Reminder.class);
}
@Override
public void processAttribute(String namespace,
String localName,
String value)
throws ParseException {
if (namespace.equals("")) {
if (localName.equals("days")) {
try {
days = Integer.valueOf(value);
} catch (NumberFormatException e) {
throw new ParseException(
CoreErrorDomain.ERR.invalidReminderDays, e);
}
} else if (localName.equals("hours")) {
try {
hours = Integer.valueOf(value);
} catch (NumberFormatException e) {
throw new ParseException(
CoreErrorDomain.ERR.invalidReminderHours, e);
}
} else if (localName.equals("minutes")) {
try {
minutes = Integer.valueOf(value);
} catch (NumberFormatException e) {
throw new ParseException(
CoreErrorDomain.ERR.invalidReminderMinutes, e);
}
} else if (localName.equals("absoluteTime")) {
try {
absoluteTime = DateTime.parseDateTime(value);
} catch (NumberFormatException e) {
throw new ParseException(
CoreErrorDomain.ERR.invalidReminderAbsoluteTime, e);
}
} else if (localName.equals("method")) {
try {
method = Method.fromString(value);
} catch (IllegalArgumentException e) {
throw new ParseException(
CoreErrorDomain.ERR.invalidReminderMethod, e);
}
}
}
}
@Override
public void processEndElement() throws ParseException {
if ((days == null ? 0 : 1) +
(hours == null ? 0 : 1) +
(minutes == null ? 0 : 1) +
(absoluteTime == null ? 0 : 1) > 1) {
throw new ParseException(
CoreErrorDomain.ERR.tooManyAttributes);
}
super.processEndElement();
}
}
}