michael@0: /** michael@0: * Copyright (c) 2012, Ben Fortuna michael@0: * All rights reserved. michael@0: * michael@0: * Redistribution and use in source and binary forms, with or without michael@0: * modification, are permitted provided that the following conditions michael@0: * are met: michael@0: * michael@0: * o Redistributions of source code must retain the above copyright michael@0: * notice, this list of conditions and the following disclaimer. michael@0: * michael@0: * o Redistributions in binary form must reproduce the above copyright michael@0: * notice, this list of conditions and the following disclaimer in the michael@0: * documentation and/or other materials provided with the distribution. michael@0: * michael@0: * o Neither the name of Ben Fortuna nor the names of any other contributors michael@0: * may be used to endorse or promote products derived from this software michael@0: * without specific prior written permission. michael@0: * michael@0: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS michael@0: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT michael@0: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR michael@0: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR michael@0: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, michael@0: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, michael@0: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR michael@0: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF michael@0: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING michael@0: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS michael@0: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. michael@0: */ michael@0: package net.fortuna.ical4j.model.component; michael@0: michael@0: import java.io.IOException; michael@0: import java.net.URISyntaxException; michael@0: import java.text.ParseException; michael@0: import java.util.HashMap; michael@0: import java.util.Iterator; michael@0: import java.util.Map; michael@0: michael@0: import net.fortuna.ical4j.model.Component; michael@0: import net.fortuna.ical4j.model.ComponentList; michael@0: import net.fortuna.ical4j.model.Date; michael@0: import net.fortuna.ical4j.model.Dur; michael@0: import net.fortuna.ical4j.model.Property; michael@0: import net.fortuna.ical4j.model.PropertyList; michael@0: import net.fortuna.ical4j.model.ValidationException; michael@0: import net.fortuna.ical4j.model.Validator; michael@0: import net.fortuna.ical4j.model.property.Clazz; michael@0: import net.fortuna.ical4j.model.property.Completed; michael@0: import net.fortuna.ical4j.model.property.Created; michael@0: import net.fortuna.ical4j.model.property.Description; michael@0: import net.fortuna.ical4j.model.property.DtStamp; michael@0: import net.fortuna.ical4j.model.property.DtStart; michael@0: import net.fortuna.ical4j.model.property.Due; michael@0: import net.fortuna.ical4j.model.property.Duration; michael@0: import net.fortuna.ical4j.model.property.Geo; michael@0: import net.fortuna.ical4j.model.property.LastModified; michael@0: import net.fortuna.ical4j.model.property.Location; michael@0: import net.fortuna.ical4j.model.property.Method; michael@0: import net.fortuna.ical4j.model.property.Organizer; michael@0: import net.fortuna.ical4j.model.property.PercentComplete; michael@0: import net.fortuna.ical4j.model.property.Priority; michael@0: import net.fortuna.ical4j.model.property.RecurrenceId; michael@0: import net.fortuna.ical4j.model.property.Sequence; michael@0: import net.fortuna.ical4j.model.property.Status; michael@0: import net.fortuna.ical4j.model.property.Summary; michael@0: import net.fortuna.ical4j.model.property.Uid; michael@0: import net.fortuna.ical4j.model.property.Url; michael@0: import net.fortuna.ical4j.util.CompatibilityHints; michael@0: import net.fortuna.ical4j.util.ComponentValidator; michael@0: import net.fortuna.ical4j.util.PropertyValidator; michael@0: import net.fortuna.ical4j.util.Strings; michael@0: michael@0: import org.apache.commons.lang.ObjectUtils; michael@0: import org.apache.commons.lang.builder.HashCodeBuilder; michael@0: michael@0: /** michael@0: * $Id$ [Apr 5, 2004] michael@0: * michael@0: * Defines an iCalendar VTODO component. michael@0: * michael@0: *
michael@0:  *       4.6.2 To-do Component
michael@0:  *  
michael@0:  *          Component Name: VTODO
michael@0:  *  
michael@0:  *          Purpose: Provide a grouping of calendar properties that describe a
michael@0:  *          to-do.
michael@0:  *  
michael@0:  *          Formal Definition: A "VTODO" calendar component is defined by the
michael@0:  *          following notation:
michael@0:  *  
michael@0:  *            todoc      = "BEGIN" ":" "VTODO" CRLF
michael@0:  *                         todoprop *alarmc
michael@0:  *                         "END" ":" "VTODO" CRLF
michael@0:  *  
michael@0:  *            todoprop   = *(
michael@0:  *  
michael@0:  *                       ; the following are optional,
michael@0:  *                       ; but MUST NOT occur more than once
michael@0:  *  
michael@0:  *                       class / completed / created / description / dtstamp /
michael@0:  *                       dtstart / geo / last-mod / location / organizer /
michael@0:  *                       percent / priority / recurid / seq / status /
michael@0:  *                       summary / uid / url /
michael@0:  *  
michael@0:  *                       ; either 'due' or 'duration' may appear in
michael@0:  *                       ; a 'todoprop', but 'due' and 'duration'
michael@0:  *                       ; MUST NOT occur in the same 'todoprop'
michael@0:  *  
michael@0:  *                       due / duration /
michael@0:  *  
michael@0:  *                       ; the following are optional,
michael@0:  *                       ; and MAY occur more than once
michael@0:  *                       attach / attendee / categories / comment / contact /
michael@0:  *                       exdate / exrule / rstatus / related / resources /
michael@0:  *                       rdate / rrule / x-prop
michael@0:  *  
michael@0:  *                       )
michael@0:  * 
michael@0: * michael@0: * Example 1 - Creating a todo of two (2) hour duration starting tomorrow: michael@0: * michael@0: *

michael@0:  * java.util.Calendar cal = java.util.Calendar.getInstance();
michael@0:  * // tomorrow..
michael@0:  * cal.add(java.util.Calendar.DAY_OF_MONTH, 1);
michael@0:  * cal.set(java.util.Calendar.HOUR_OF_DAY, 11);
michael@0:  * cal.set(java.util.Calendar.MINUTE, 00);
michael@0:  * 
michael@0:  * VToDo documentation = new VEvent(cal.getTime(), 1000 * 60 * 60 * 2,
michael@0:  *         "Document calendar component usage");
michael@0:  * 
michael@0:  * // add timezone information..
michael@0:  * VTimeZone tz = VTimeZone.getDefault();
michael@0:  * TzId tzParam = new TzId(tz.getProperties().getProperty(Property.TZID)
michael@0:  *         .getValue());
michael@0:  * documentation.getProperties().getProperty(Property.DTSTART).getParameters()
michael@0:  *         .add(tzParam);
michael@0:  * 
michael@0: * michael@0: * @author Ben Fortuna michael@0: */ michael@0: public class VToDo extends CalendarComponent { michael@0: michael@0: private static final long serialVersionUID = -269658210065896668L; michael@0: michael@0: private final Map methodValidators = new HashMap(); michael@0: { michael@0: methodValidators.put(Method.ADD, new AddValidator()); michael@0: methodValidators.put(Method.CANCEL, new CancelValidator()); michael@0: methodValidators.put(Method.COUNTER, new CounterValidator()); michael@0: methodValidators.put(Method.DECLINE_COUNTER, new DeclineCounterValidator()); michael@0: methodValidators.put(Method.PUBLISH, new PublishValidator()); michael@0: methodValidators.put(Method.REFRESH, new RefreshValidator()); michael@0: methodValidators.put(Method.REPLY, new ReplyValidator()); michael@0: methodValidators.put(Method.REQUEST, new RequestValidator()); michael@0: } michael@0: michael@0: private ComponentList alarms = new ComponentList(); michael@0: michael@0: /** michael@0: * Default constructor. michael@0: */ michael@0: public VToDo() { michael@0: super(VTODO); michael@0: getProperties().add(new DtStamp()); michael@0: } michael@0: michael@0: /** michael@0: * Constructor. michael@0: * @param properties a list of properties michael@0: */ michael@0: public VToDo(final PropertyList properties) { michael@0: super(VTODO, properties); michael@0: } michael@0: michael@0: /** michael@0: * Constructs a new VTODO instance starting at the specified time with the specified summary. michael@0: * @param start the start date of the new todo michael@0: * @param summary the todo summary michael@0: */ michael@0: public VToDo(final Date start, final String summary) { michael@0: this(); michael@0: getProperties().add(new DtStart(start)); michael@0: getProperties().add(new Summary(summary)); michael@0: } michael@0: michael@0: /** michael@0: * Constructs a new VTODO instance starting and ending at the specified times with the specified summary. michael@0: * @param start the start date of the new todo michael@0: * @param due the due date of the new todo michael@0: * @param summary the todo summary michael@0: */ michael@0: public VToDo(final Date start, final Date due, final String summary) { michael@0: this(); michael@0: getProperties().add(new DtStart(start)); michael@0: getProperties().add(new Due(due)); michael@0: getProperties().add(new Summary(summary)); michael@0: } michael@0: michael@0: /** michael@0: * Constructs a new VTODO instance starting at the specified times, for the specified duration, with the specified michael@0: * summary. michael@0: * @param start the start date of the new todo michael@0: * @param duration the duration of the new todo michael@0: * @param summary the todo summary michael@0: */ michael@0: public VToDo(final Date start, final Dur duration, final String summary) { michael@0: this(); michael@0: getProperties().add(new DtStart(start)); michael@0: getProperties().add(new Duration(duration)); michael@0: getProperties().add(new Summary(summary)); michael@0: } michael@0: michael@0: /** michael@0: * Returns the list of alarms for this todo. michael@0: * @return a component list michael@0: */ michael@0: public final ComponentList getAlarms() { michael@0: return alarms; michael@0: } michael@0: michael@0: /** michael@0: * {@inheritDoc} michael@0: */ michael@0: public final String toString() { michael@0: final StringBuffer buffer = new StringBuffer(); michael@0: buffer.append(BEGIN); michael@0: buffer.append(':'); michael@0: buffer.append(getName()); michael@0: buffer.append(Strings.LINE_SEPARATOR); michael@0: buffer.append(getProperties()); michael@0: buffer.append(getAlarms()); michael@0: buffer.append(END); michael@0: buffer.append(':'); michael@0: buffer.append(getName()); michael@0: buffer.append(Strings.LINE_SEPARATOR); michael@0: return buffer.toString(); michael@0: } michael@0: michael@0: /** michael@0: * {@inheritDoc} michael@0: */ michael@0: public final void validate(final boolean recurse) michael@0: throws ValidationException { michael@0: michael@0: // validate that getAlarms() only contains VAlarm components michael@0: final Iterator iterator = getAlarms().iterator(); michael@0: while (iterator.hasNext()) { michael@0: final Component component = (Component) iterator.next(); michael@0: if (!(component instanceof VAlarm)) { michael@0: throw new ValidationException("Component [" michael@0: + component.getName() + "] may not occur in VTODO"); michael@0: } michael@0: ((VAlarm) component).validate(recurse); michael@0: } michael@0: michael@0: if (!CompatibilityHints michael@0: .isHintEnabled(CompatibilityHints.KEY_RELAXED_VALIDATION)) { michael@0: michael@0: // From "4.8.4.7 Unique Identifier": michael@0: // Conformance: The property MUST be specified in the "VEVENT", "VTODO", michael@0: // "VJOURNAL" or "VFREEBUSY" calendar components. michael@0: PropertyValidator.getInstance().assertOne(Property.UID, michael@0: getProperties()); michael@0: michael@0: // From "4.8.7.2 Date/Time Stamp": michael@0: // Conformance: This property MUST be included in the "VEVENT", "VTODO", michael@0: // "VJOURNAL" or "VFREEBUSY" calendar components. michael@0: PropertyValidator.getInstance().assertOne(Property.DTSTAMP, michael@0: getProperties()); michael@0: } michael@0: michael@0: /* michael@0: * ; the following are optional, ; but MUST NOT occur more than once class / completed / created / description / michael@0: * dtstamp / dtstart / geo / last-mod / location / organizer / percent / priority / recurid / seq / status / michael@0: * summary / uid / url / michael@0: */ michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.CLASS, michael@0: getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.COMPLETED, michael@0: getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.CREATED, michael@0: getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.DESCRIPTION, michael@0: getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.DTSTAMP, michael@0: getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.DTSTART, michael@0: getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.GEO, michael@0: getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.LAST_MODIFIED, michael@0: getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.LOCATION, michael@0: getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.ORGANIZER, michael@0: getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess( michael@0: Property.PERCENT_COMPLETE, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.PRIORITY, michael@0: getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.RECURRENCE_ID, michael@0: getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.SEQUENCE, michael@0: getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.STATUS, michael@0: getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.SUMMARY, michael@0: getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.UID, michael@0: getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.URL, michael@0: getProperties()); michael@0: michael@0: final Status status = (Status) getProperty(Property.STATUS); michael@0: if (status != null && !Status.VTODO_NEEDS_ACTION.getValue().equals(status.getValue()) michael@0: && !Status.VTODO_COMPLETED.getValue().equals(status.getValue()) michael@0: && !Status.VTODO_IN_PROCESS.getValue().equals(status.getValue()) michael@0: && !Status.VTODO_CANCELLED.getValue().equals(status.getValue())) { michael@0: throw new ValidationException("Status property [" michael@0: + status.toString() + "] may not occur in VTODO"); michael@0: } michael@0: michael@0: /* michael@0: * ; either 'due' or 'duration' may appear in ; a 'todoprop', but 'due' and 'duration' ; MUST NOT occur in the michael@0: * same 'todoprop' due / duration / michael@0: */ michael@0: try { michael@0: PropertyValidator.getInstance().assertNone(Property.DUE, michael@0: getProperties()); michael@0: } michael@0: catch (ValidationException ve) { michael@0: PropertyValidator.getInstance().assertNone(Property.DURATION, michael@0: getProperties()); michael@0: } michael@0: michael@0: /* michael@0: * ; the following are optional, ; and MAY occur more than once attach / attendee / categories / comment / michael@0: * contact / exdate / exrule / rstatus / related / resources / rdate / rrule / x-prop michael@0: */ michael@0: michael@0: if (recurse) { michael@0: validateProperties(); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * {@inheritDoc} michael@0: */ michael@0: protected Validator getValidator(Method method) { michael@0: return (Validator) methodValidators.get(method); michael@0: } michael@0: michael@0: /** michael@0: *
michael@0:      * Component/Property  Presence
michael@0:      * ------------------- ----------------------------------------------
michael@0:      * METHOD                1       MUST be "ADD"
michael@0:      * VTODO                 1
michael@0:      *     DTSTAMP           1
michael@0:      *     ORGANIZER         1
michael@0:      *     PRIORITY          1
michael@0:      *     SEQUENCE          1       MUST be greater than 0
michael@0:      *     SUMMARY           1       Can be null.
michael@0:      *     UID               1       MUST match that of the original to-do
michael@0:      * 
michael@0:      *     ATTACH            0+
michael@0:      *     ATTENDEE          0+
michael@0:      *     CATEGORIES        0 or 1  This property may contain a list of
michael@0:      *                               values
michael@0:      *     CLASS             0 or 1
michael@0:      *     COMMENT           0 or 1
michael@0:      *     CONTACT           0+
michael@0:      *     CREATED           0 or 1
michael@0:      *     DESCRIPTION       0 or 1  Can be null
michael@0:      *     DTSTART           0 or 1
michael@0:      *     DUE               0 or 1  If present DURATION MUST NOT be present
michael@0:      *     DURATION          0 or 1  If present DUE MUST NOT be present
michael@0:      *     EXDATE            0+
michael@0:      *     EXRULE            0+
michael@0:      *     GEO               0 or 1
michael@0:      *     LAST-MODIFIED     0 or 1
michael@0:      *     LOCATION          0 or 1
michael@0:      *     PERCENT-COMPLETE  0 or 1
michael@0:      *     RDATE             0+
michael@0:      *     RELATED-TO        0+
michael@0:      *     RESOURCES         0 or 1  This property may contain a list of
michael@0:      *                               values
michael@0:      *     RRULE             0+
michael@0:      *     STATUS            0 or 1  MAY be one of COMPLETED/NEEDS ACTION/IN-
michael@0:      *                               PROCESS
michael@0:      *     URL               0 or 1
michael@0:      *     X-PROPERTY        0+
michael@0:      * 
michael@0:      *     RECURRENCE-ID     0
michael@0:      *     REQUEST-STATUS    0
michael@0:      * 
michael@0:      * VALARM                0+
michael@0:      * VTIMEZONE             0+      MUST be present if any date/time refers
michael@0:      *                               to a timezone
michael@0:      * X-COMPONENT           0+
michael@0:      * 
michael@0:      * VEVENT                0
michael@0:      * VJOURNAL              0
michael@0:      * VFREEBUSY             0
michael@0:      * 
michael@0: * michael@0: */ michael@0: private class AddValidator implements Validator { michael@0: michael@0: private static final long serialVersionUID = 1L; michael@0: michael@0: public void validate() throws ValidationException { michael@0: PropertyValidator.getInstance().assertOne(Property.DTSTAMP, getProperties()); michael@0: PropertyValidator.getInstance().assertOne(Property.ORGANIZER, getProperties()); michael@0: PropertyValidator.getInstance().assertOne(Property.PRIORITY, getProperties()); michael@0: PropertyValidator.getInstance().assertOne(Property.SEQUENCE, getProperties()); michael@0: PropertyValidator.getInstance().assertOne(Property.SUMMARY, getProperties()); michael@0: PropertyValidator.getInstance().assertOne(Property.UID, getProperties()); michael@0: michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.CATEGORIES, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.CLASS, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.CREATED, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.DESCRIPTION, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.DTSTART, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.DUE, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.DURATION, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.GEO, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.LAST_MODIFIED, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.LOCATION, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.PERCENT_COMPLETE, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.RESOURCES, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.STATUS, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.URL, getProperties()); michael@0: michael@0: PropertyValidator.getInstance().assertNone(Property.RECURRENCE_ID, getProperties()); michael@0: PropertyValidator.getInstance().assertNone(Property.REQUEST_STATUS, getProperties()); michael@0: michael@0: for (final Iterator i = getAlarms().iterator(); i.hasNext();) { michael@0: final VAlarm alarm = (VAlarm) i.next(); michael@0: alarm.validate(Method.ADD); michael@0: } michael@0: } michael@0: } michael@0: michael@0: /** michael@0: *
michael@0:      * Component/Property   Presence
michael@0:      * -------------------  ---------------------------------------------
michael@0:      * METHOD               1     MUST be "CANCEL"
michael@0:      * VTODO                1
michael@0:      *     ATTENDEE         0+    include all "Attendees" being removed from
michael@0:      *                            the todo. MUST include all "Attendees" if
michael@0:      *                            the entire todo is cancelled.
michael@0:      *     UID              1     MUST echo original UID
michael@0:      *     DTSTAMP          1
michael@0:      *     ORGANIZER        1
michael@0:      *     SEQUENCE         1
michael@0:      * 
michael@0:      *     ATTACH           0+
michael@0:      *     CATEGORIES       0 or 1 This property MAY contain a list of values
michael@0:      *     CLASS            0 or 1
michael@0:      *     COMMENT          0 or 1
michael@0:      *     CONTACT          0+
michael@0:      *     CREATED          0 or 1
michael@0:      *     DESCRIPTION      0 or 1
michael@0:      *     DTSTART          0 or 1
michael@0:      *     DUE              0 or 1  If present DURATION MUST NOT be present
michael@0:      *     DURATION         0 or 1  If present DUE MUST NOT be present
michael@0:      *     EXDATE           0+
michael@0:      *     EXRULE           0+
michael@0:      *     GEO              0 or 1
michael@0:      *     LAST-MODIFIED    0 or 1
michael@0:      *     LOCATION         0 or 1
michael@0:      *     PERCENT-COMPLETE 0 or 1
michael@0:      *     RDATE            0+
michael@0:      *     RECURRENCE-ID    0 or 1 MUST only if referring to one or more
michael@0:      *                             instances of a recurring calendar
michael@0:      *                             component. Otherwise it MUST NOT be
michael@0:      *                             present.
michael@0:      *     RELATED-TO       0+
michael@0:      *     RESOURCES        0 or 1 This property MAY contain a list of values
michael@0:      *     RRULE            0+
michael@0:      *     PRIORITY         0 or 1
michael@0:      *     STATUS           0 or 1  If present it MUST be set to "CANCELLED".
michael@0:      *                              MUST NOT be used if purpose is to remove
michael@0:      *                              "ATTENDEES" rather than cancel the entire
michael@0:      *                              VTODO.
michael@0:      *     URL              0 or 1
michael@0:      *     X-PROPERTY       0+
michael@0:      * 
michael@0:      *     REQUEST-STATUS   0
michael@0:      * 
michael@0:      * VTIMEZONE            0 or 1  MUST be present if any date/time refers to
michael@0:      *                              a timezone
michael@0:      * X-COMPONENT          0+
michael@0:      * 
michael@0:      * VALARM               0
michael@0:      * VEVENT               0
michael@0:      * VFREEBUSY            0
michael@0:      * 
michael@0: * michael@0: */ michael@0: private class CancelValidator implements Validator { michael@0: michael@0: private static final long serialVersionUID = 1L; michael@0: michael@0: public void validate() throws ValidationException { michael@0: PropertyValidator.getInstance().assertOne(Property.UID, getProperties()); michael@0: PropertyValidator.getInstance().assertOne(Property.DTSTAMP, getProperties()); michael@0: PropertyValidator.getInstance().assertOne(Property.ORGANIZER, getProperties()); michael@0: PropertyValidator.getInstance().assertOne(Property.SEQUENCE, getProperties()); michael@0: michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.CATEGORIES, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.CLASS, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.CREATED, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.DESCRIPTION, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.DTSTART, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.DUE, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.DURATION, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.GEO, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.LAST_MODIFIED, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.LOCATION, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.PERCENT_COMPLETE, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.RECURRENCE_ID, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.RESOURCES, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.PRIORITY, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.STATUS, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.URL, getProperties()); michael@0: michael@0: PropertyValidator.getInstance().assertNone(Property.REQUEST_STATUS, getProperties()); michael@0: michael@0: ComponentValidator.assertNone(Component.VALARM, getAlarms()); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: *
michael@0:      * Component/Property  Presence
michael@0:      * ------------------- ----------------------------------------------
michael@0:      * METHOD               1      MUST be "COUNTER"
michael@0:      * VTODO                1
michael@0:      *     ATTENDEE         1+
michael@0:      *     DTSTAMP          1
michael@0:      *     ORGANIZER        1
michael@0:      *     PRIORITY         1
michael@0:      *     SUMMARY          1      Can be null
michael@0:      *     UID              1
michael@0:      * 
michael@0:      *     ATTACH           0+
michael@0:      *     CATEGORIES       0 or 1 This property MAY contain a list of values
michael@0:      *     CLASS            0 or 1
michael@0:      *     COMMENT          0 or 1
michael@0:      *     CONTACT          0+
michael@0:      *     CREATED          0 or 1
michael@0:      *     DESCRIPTION      0 or 1 Can be null
michael@0:      *     DTSTART          0 or 1
michael@0:      *     DUE              0 or 1  If present DURATION MUST NOT be present
michael@0:      *     DURATION         0 or 1  If present DUE MUST NOT be present
michael@0:      *     EXDATE           0+
michael@0:      *     EXRULE           0+
michael@0:      *     GEO              0 or 1
michael@0:      *     LAST-MODIFIED    0 or 1
michael@0:      *     LOCATION         0 or 1
michael@0:      *     PERCENT-COMPLETE 0 or 1
michael@0:      *     RDATE            0+
michael@0:      *     RECURRENCE-ID    0 or 1 MUST only 3.5if referring to an instance of a
michael@0:      *                             recurring calendar component.  Otherwise it
michael@0:      *                             MUST NOT be present.
michael@0:      *     RELATED-TO       0+
michael@0:      *     REQUEST-STATUS   0+
michael@0:      *     RESOURCES        0 or 1 This property MAY contain a list of values
michael@0:      *     RRULE            0 or 1
michael@0:      *     SEQUENCE         0 or 1 MUST echo the original SEQUENCE number.
michael@0:      *                             MUST be present if non-zero. MAY be present
michael@0:      *                             if zero.
michael@0:      *     STATUS           0 or 1 MAY be one of COMPLETED/NEEDS ACTION/IN-
michael@0:      *                             PROCESS/CANCELLED
michael@0:      *     URL              0 or 1
michael@0:      *     X-PROPERTY       0+
michael@0:      * 
michael@0:      * 
michael@0:      * VALARM               0+
michael@0:      * VTIMEZONE            0 or 1  MUST be present if any date/time refers to
michael@0:      *                              a timezone
michael@0:      * X-COMPONENT          0+
michael@0:      * 
michael@0:      * VEVENT               0
michael@0:      * VFREEBUSY            0
michael@0:      * 
michael@0: * michael@0: */ michael@0: private class CounterValidator implements Validator { michael@0: michael@0: private static final long serialVersionUID = 1L; michael@0: michael@0: public void validate() throws ValidationException { michael@0: PropertyValidator.getInstance().assertOneOrMore(Property.ATTENDEE, getProperties()); michael@0: michael@0: PropertyValidator.getInstance().assertOne(Property.DTSTAMP, getProperties()); michael@0: PropertyValidator.getInstance().assertOne(Property.ORGANIZER, getProperties()); michael@0: PropertyValidator.getInstance().assertOne(Property.PRIORITY, getProperties()); michael@0: PropertyValidator.getInstance().assertOne(Property.SUMMARY, getProperties()); michael@0: PropertyValidator.getInstance().assertOne(Property.UID, getProperties()); michael@0: michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.CATEGORIES, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.CLASS, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.CREATED, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.DESCRIPTION, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.DTSTART, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.DUE, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.DURATION, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.GEO, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.LAST_MODIFIED, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.LOCATION, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.PERCENT_COMPLETE, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.RECURRENCE_ID, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.RESOURCES, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.RRULE, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.SEQUENCE, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.STATUS, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.URL, getProperties()); michael@0: michael@0: for (final Iterator i = getAlarms().iterator(); i.hasNext();) { michael@0: final VAlarm alarm = (VAlarm) i.next(); michael@0: alarm.validate(Method.COUNTER); michael@0: } michael@0: } michael@0: } michael@0: michael@0: /** michael@0: *
michael@0:      * Component/Property   Presence
michael@0:      * -------------------  ---------------------------------------------
michael@0:      * METHOD               1       MUST be "DECLINECOUNTER"
michael@0:      * 
michael@0:      * VTODO                1
michael@0:      *     ATTENDEE         1+      MUST for all attendees
michael@0:      *     DTSTAMP          1
michael@0:      *     ORGANIZER        1
michael@0:      *     SEQUENCE         1       MUST echo the original SEQUENCE number
michael@0:      *     UID              1       MUST echo original UID
michael@0:      *     ATTACH           0+
michael@0:      *     CATEGORIES       0 or 1  This property may contain a list of values
michael@0:      *     CLASS            0 or 1
michael@0:      *     COMMENT          0 or 1
michael@0:      *     CONTACT          0+
michael@0:      *     CREATED          0 or 1
michael@0:      *     DESCRIPTION      0 or 1
michael@0:      *     DTSTART          0 or 1
michael@0:      *     DUE              0 or 1  If present DURATION MUST NOT be present
michael@0:      *     DURATION         0 or 1  If present DUE MUST NOT be present
michael@0:      *     EXDATE           0+
michael@0:      *     EXRULE           0+
michael@0:      *     GEO              0 or 1
michael@0:      *     LAST-MODIFIED    0 or 1
michael@0:      *     LOCATION         0 or 1
michael@0:      *     PERCENT-COMPLETE 0 or 1
michael@0:      *     PRIORITY         0 or 1
michael@0:      *     RDATE            0+
michael@0:      *     RECURRENCE-ID    0 or 1  MUST only if referring to an instance of a
michael@0:      *                              recurring calendar component.  Otherwise
michael@0:      *                              it MUST NOT be present.
michael@0:      *     RELATED-TO       0+
michael@0:      *     REQUEST-STATUS   0+
michael@0:      *     RESOURCES        0 or 1  This property MAY contain a list of values
michael@0:      *     RRULE            0+
michael@0:      *     STATUS           0 or 1  MAY be one of COMPLETED/NEEDS ACTION/IN-
michael@0:      *                              PROCESS
michael@0:      *     URL              0 or 1
michael@0:      *     X-PROPERTY       0+
michael@0:      * 
michael@0:      * VTIMEZONE            0+  MUST be present if any date/time refers to
michael@0:      *                          a timezone
michael@0:      * X-COMPONENT          0+
michael@0:      * 
michael@0:      * VALARM               0
michael@0:      * VEVENT               0
michael@0:      * VFREEBUSY            0
michael@0:      * 
michael@0: * michael@0: */ michael@0: private class DeclineCounterValidator implements Validator { michael@0: michael@0: private static final long serialVersionUID = 1L; michael@0: michael@0: public void validate() throws ValidationException { michael@0: PropertyValidator.getInstance().assertOneOrMore(Property.ATTENDEE, getProperties()); michael@0: michael@0: PropertyValidator.getInstance().assertOne(Property.DTSTAMP, getProperties()); michael@0: PropertyValidator.getInstance().assertOne(Property.ORGANIZER, getProperties()); michael@0: PropertyValidator.getInstance().assertOne(Property.SEQUENCE, getProperties()); michael@0: PropertyValidator.getInstance().assertOne(Property.UID, getProperties()); michael@0: michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.CATEGORIES, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.CLASS, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.CREATED, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.DESCRIPTION, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.DTSTART, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.DUE, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.DURATION, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.GEO, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.LAST_MODIFIED, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.LOCATION, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.PERCENT_COMPLETE, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.PRIORITY, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.RECURRENCE_ID, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.RESOURCES, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.STATUS, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.URL, getProperties()); michael@0: michael@0: ComponentValidator.assertNone(Component.VALARM, getAlarms()); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: *
michael@0:      * Component/Property  Presence
michael@0:      * ------------------- ----------------------------------------------
michael@0:      * METHOD               1       MUST be "PUBLISH"
michael@0:      * VTODO                1+
michael@0:      *     DTSTAMP          1
michael@0:      *     DTSTART          1
michael@0:      *     ORGANIZER        1
michael@0:      *     PRIORITY         1
michael@0:      *     SEQUENCE         0 or 1  MUST be present if value is greater than
michael@0:      *                              0, MAY be present if 0
michael@0:      *     SUMMARY          1       Can be null.
michael@0:      *     UID              1
michael@0:      * 
michael@0:      *     ATTACH           0+
michael@0:      *     CATEGORIES       0 or 1  This property may contain a list of values
michael@0:      *     CLASS            0 or 1
michael@0:      *     COMMENT          0 or 1
michael@0:      *     CONTACT          0+
michael@0:      *     CREATED          0 or 1
michael@0:      *     DESCRIPTION      0 or 1  Can be null
michael@0:      *     DUE              0 or 1  If present DURATION MUST NOT be present
michael@0:      *     DURATION         0 or 1  If present DUE MUST NOT be present
michael@0:      *     EXDATE           0+
michael@0:      *     EXRULE           0+
michael@0:      *     GEO              0 or 1
michael@0:      *     LAST-MODIFIED    0 or 1
michael@0:      *     LOCATION         0 or 1
michael@0:      *     PERCENT-COMPLETE 0 or 1
michael@0:      *     RDATE            0+
michael@0:      *     RECURRENCE-ID    0 or 1  MUST only if referring to an instance of a
michael@0:      *                              recurring calendar component.  Otherwise
michael@0:      *                              it MUST NOT be present.
michael@0:      * 
michael@0:      *     RELATED-TO       0+
michael@0:      *     RESOURCES        0 or 1  This property may contain a list of values
michael@0:      *     RRULE            0+
michael@0:      *     STATUS           0 or 1  MAY be one of COMPLETED/NEEDS ACTION/IN-
michael@0:      *                              PROCESS/CANCELLED
michael@0:      *     URL              0 or 1
michael@0:      *     X-PROPERTY       0+
michael@0:      * 
michael@0:      *     ATTENDEE         0
michael@0:      *     REQUEST-STATUS   0
michael@0:      * 
michael@0:      * VTIMEZONE            0+    MUST be present if any date/time refers to
michael@0:      *                            a timezone
michael@0:      * VALARM               0+
michael@0:      * X-COMPONENT          0+
michael@0:      * 
michael@0:      * VFREEBUSY            0
michael@0:      * VEVENT               0
michael@0:      * VJOURNAL             0
michael@0:      * 
michael@0: * michael@0: */ michael@0: private class PublishValidator implements Validator { michael@0: michael@0: private static final long serialVersionUID = 1L; michael@0: michael@0: public void validate() throws ValidationException { michael@0: PropertyValidator.getInstance().assertOne(Property.DTSTAMP, getProperties()); michael@0: michael@0: if (!CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_RELAXED_VALIDATION)) { michael@0: PropertyValidator.getInstance().assertOne(Property.ORGANIZER, getProperties()); michael@0: PropertyValidator.getInstance().assertOne(Property.PRIORITY, getProperties()); michael@0: } michael@0: michael@0: PropertyValidator.getInstance().assertOne(Property.SUMMARY, getProperties()); michael@0: PropertyValidator.getInstance().assertOne(Property.UID, getProperties()); michael@0: michael@0: // DTSTART: RFC2446 conflicts with RCF2445.. michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.DTSTART, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.SEQUENCE, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.CATEGORIES, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.CLASS, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.CREATED, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.DESCRIPTION, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.DUE, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.DURATION, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.GEO, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.LAST_MODIFIED, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.LOCATION, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.PERCENT_COMPLETE, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.RECURRENCE_ID, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.RESOURCES, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.STATUS, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.URL, getProperties()); michael@0: michael@0: PropertyValidator.getInstance().assertNone(Property.ATTENDEE, getProperties()); michael@0: PropertyValidator.getInstance().assertNone(Property.REQUEST_STATUS, getProperties()); michael@0: michael@0: for (final Iterator i = getAlarms().iterator(); i.hasNext();) { michael@0: final VAlarm alarm = (VAlarm) i.next(); michael@0: alarm.validate(Method.PUBLISH); michael@0: } michael@0: } michael@0: } michael@0: michael@0: /** michael@0: *
michael@0:      * Component/Property   Presence
michael@0:      * -------------------  ---------------------------------------------
michael@0:      * METHOD               1      MUST be "REFRESH"
michael@0:      * VTODO                1
michael@0:      *     ATTENDEE         1
michael@0:      *     DTSTAMP          1
michael@0:      *     UID              1       MUST echo original UID
michael@0:      * 
michael@0:      *     RECURRENCE-ID    0 or 1  MUST only if referring to an instance of a
michael@0:      *                              Recurring calendar component. Otherwise it
michael@0:      *                              MUST NOT be present
michael@0:      *     X-PROPERTY       0+
michael@0:      * 
michael@0:      *     ATTACH           0
michael@0:      *     CATEGORIES       0
michael@0:      *     CLASS            0
michael@0:      *     COMMENT          0
michael@0:      *     CONTACT          0
michael@0:      *     CREATED          0
michael@0:      *     DESCRIPTION      0
michael@0:      *     DTSTART          0
michael@0:      *     DUE              0
michael@0:      *     DURATION         0
michael@0:      *     EXDATE           0
michael@0:      *     EXRULE           0
michael@0:      *     GEO              0
michael@0:      *     LAST-MODIFIED    0
michael@0:      *     LOCATION         0
michael@0:      *     ORGANIZER        0
michael@0:      *     PERCENT-COMPLETE 0
michael@0:      *     PRIORITY         0
michael@0:      *     RDATE            0
michael@0:      *     RELATED-TO       0
michael@0:      *     REQUEST-STATUS   0
michael@0:      *     RESOURCES        0
michael@0:      *     RRULE            0
michael@0:      *     SEQUENCE         0
michael@0:      *     STATUS           0
michael@0:      *     URL              0
michael@0:      * 
michael@0:      * X-COMPONENT          0+
michael@0:      * 
michael@0:      * VALARM               0
michael@0:      * VEVENT               0
michael@0:      * VFREEBUSY            0
michael@0:      * VTIMEZONE            0
michael@0:      * 
michael@0: * michael@0: */ michael@0: private class RefreshValidator implements Validator { michael@0: michael@0: private static final long serialVersionUID = 1L; michael@0: michael@0: public void validate() throws ValidationException { michael@0: PropertyValidator.getInstance().assertOne(Property.ATTENDEE, getProperties()); michael@0: PropertyValidator.getInstance().assertOne(Property.DTSTAMP, getProperties()); michael@0: PropertyValidator.getInstance().assertOne(Property.UID, getProperties()); michael@0: michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.RECURRENCE_ID, getProperties()); michael@0: michael@0: PropertyValidator.getInstance().assertNone(Property.ATTACH, getProperties()); michael@0: PropertyValidator.getInstance().assertNone(Property.CATEGORIES, getProperties()); michael@0: PropertyValidator.getInstance().assertNone(Property.CLASS, getProperties()); michael@0: PropertyValidator.getInstance().assertNone(Property.CONTACT, getProperties()); michael@0: PropertyValidator.getInstance().assertNone(Property.CREATED, getProperties()); michael@0: PropertyValidator.getInstance().assertNone(Property.DESCRIPTION, getProperties()); michael@0: PropertyValidator.getInstance().assertNone(Property.DTSTART, getProperties()); michael@0: PropertyValidator.getInstance().assertNone(Property.DUE, getProperties()); michael@0: PropertyValidator.getInstance().assertNone(Property.DURATION, getProperties()); michael@0: PropertyValidator.getInstance().assertNone(Property.EXDATE, getProperties()); michael@0: PropertyValidator.getInstance().assertNone(Property.EXRULE, getProperties()); michael@0: PropertyValidator.getInstance().assertNone(Property.GEO, getProperties()); michael@0: PropertyValidator.getInstance().assertNone(Property.LAST_MODIFIED, getProperties()); michael@0: PropertyValidator.getInstance().assertNone(Property.LOCATION, getProperties()); michael@0: PropertyValidator.getInstance().assertNone(Property.ORGANIZER, getProperties()); michael@0: PropertyValidator.getInstance().assertNone(Property.PERCENT_COMPLETE, getProperties()); michael@0: PropertyValidator.getInstance().assertNone(Property.PRIORITY, getProperties()); michael@0: PropertyValidator.getInstance().assertNone(Property.RDATE, getProperties()); michael@0: PropertyValidator.getInstance().assertNone(Property.RELATED_TO, getProperties()); michael@0: PropertyValidator.getInstance().assertNone(Property.REQUEST_STATUS, getProperties()); michael@0: PropertyValidator.getInstance().assertNone(Property.RESOURCES, getProperties()); michael@0: PropertyValidator.getInstance().assertNone(Property.RRULE, getProperties()); michael@0: PropertyValidator.getInstance().assertNone(Property.SEQUENCE, getProperties()); michael@0: PropertyValidator.getInstance().assertNone(Property.STATUS, getProperties()); michael@0: PropertyValidator.getInstance().assertNone(Property.URL, getProperties()); michael@0: michael@0: ComponentValidator.assertNone(Component.VALARM, getAlarms()); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: *
michael@0:      * Component/Property   Presence
michael@0:      * -------------------  ---------------------------------------------
michael@0:      * METHOD               1      MUST be "REPLY"
michael@0:      * VTODO                1+     All component MUST have the same UID
michael@0:      *     ATTENDEE         1+
michael@0:      *     DTSTAMP          1
michael@0:      *     ORGANIZER        1
michael@0:      *     UID              1      MUST must be the address of the replying
michael@0:      *                             attendee
michael@0:      *     REQUEST-STATUS   0+
michael@0:      *     ATTACH           0+
michael@0:      *     CATEGORIES       0 or 1 This property may contain a list of values
michael@0:      *     CLASS            0 or 1
michael@0:      *     COMMENT          0 or 1
michael@0:      *     CONTACT          0+
michael@0:      *     CREATED          0 or 1
michael@0:      *     DESCRIPTION      0 or 1
michael@0:      *     DTSTART          0 or 1
michael@0:      *     DUE              0 or 1  If present DURATION MUST NOT be present
michael@0:      *     DURATION         0 or 1  If present DUE MUST NOT be present
michael@0:      *     EXDATE           0+
michael@0:      *     EXRULE           0+
michael@0:      *     GEO              0 or 1
michael@0:      *     LAST-MODIFIED    0 or 1
michael@0:      *     LOCATION         0 or 1
michael@0:      *     PERCENT-COMPLETE 0 or 1
michael@0:      *     PRIORITY         0 or 1
michael@0:      *     RDATE            0+
michael@0:      *     RELATED-TO       0+
michael@0:      *     RESOURCES        0 or 1  This property may contain a list of values
michael@0:      *     RRULE            0+
michael@0:      *     RECURRENCE-ID    0 or 1  MUST only if referring to an instance of a
michael@0:      *                              Recurring calendar component. Otherwise it
michael@0:      *                              MUST NOT be present
michael@0:      *     SEQUENCE         0 or 1  MUST be the sequence number of
michael@0:      *                              the original REQUEST if greater than 0.
michael@0:      *                              MAY be present if 0.
michael@0:      *     STATUS           0 or 1
michael@0:      *     SUMMARY          0 or 1  Can be null
michael@0:      *     URL              0 or 1
michael@0:      *     X-PROPERTY       0+
michael@0:      * 
michael@0:      * VTIMEZONE            0 or 1  MUST be present if any date/time refers to
michael@0:      *                              a timezone
michael@0:      * X-COMPONENT          0+
michael@0:      * 
michael@0:      * VALARM               0
michael@0:      * VEVENT               0
michael@0:      * VFREEBUSY            0
michael@0:      * 
michael@0: * michael@0: */ michael@0: private class ReplyValidator implements Validator { michael@0: michael@0: private static final long serialVersionUID = 1L; michael@0: michael@0: public void validate() throws ValidationException { michael@0: PropertyValidator.getInstance().assertOneOrMore(Property.ATTENDEE, getProperties()); michael@0: michael@0: PropertyValidator.getInstance().assertOne(Property.DTSTAMP, getProperties()); michael@0: PropertyValidator.getInstance().assertOne(Property.ORGANIZER, getProperties()); michael@0: PropertyValidator.getInstance().assertOne(Property.UID, getProperties()); michael@0: michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.CATEGORIES, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.CLASS, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.CREATED, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.DESCRIPTION, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.DTSTART, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.DUE, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.DURATION, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.GEO, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.LAST_MODIFIED, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.LOCATION, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.PERCENT_COMPLETE, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.PRIORITY, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.RESOURCES, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.RECURRENCE_ID, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.SEQUENCE, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.STATUS, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.SUMMARY, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.URL, getProperties()); michael@0: michael@0: ComponentValidator.assertNone(Component.VALARM, getAlarms()); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: *
michael@0:      * Component/Property  Presence
michael@0:      * ------------------- ----------------------------------------------
michael@0:      * METHOD                1       MUST be "REQUEST"
michael@0:      * VTODO                 1+      All components must have the same UID
michael@0:      *     ATTENDEE          1+
michael@0:      *     DTSTAMP           1
michael@0:      *     DTSTART           1
michael@0:      *     ORGANIZER         1
michael@0:      *     PRIORITY          1
michael@0:      *     SEQUENCE          0 or 1  MUST be present if value is greater than
michael@0:      *                               0, MAY be present if 0
michael@0:      *     SUMMARY           1       Can be null.
michael@0:      *     UID               1
michael@0:      * 
michael@0:      *     ATTACH            0+
michael@0:      *     CATEGORIES        0 or 1   This property may contain a list of
michael@0:      *                                values
michael@0:      *     CLASS             0 or 1
michael@0:      *     COMMENT           0 or 1
michael@0:      *     CONTACT           0+
michael@0:      *     CREATED           0 or 1
michael@0:      *     DESCRIPTION       0 or 1  Can be null
michael@0:      *     DUE               0 or 1  If present DURATION MUST NOT be present
michael@0:      *     DURATION          0 or 1  If present DUE MUST NOT be present
michael@0:      *     EXDATE            0+
michael@0:      *     EXRULE            0+
michael@0:      *     GEO               0 or 1
michael@0:      *     LAST-MODIFIED     0 or 1
michael@0:      *     LOCATION          0 or 1
michael@0:      *     PERCENT-COMPLETE  0 or 1
michael@0:      *     RDATE             0+
michael@0:      *     RECURRENCE-ID     0 or 1  present if referring to an instance of a
michael@0:      *                               recurring calendar component.  Otherwise
michael@0:      *                               it MUST NOT be present.
michael@0:      *     RELATED-TO        0+
michael@0:      *     RESOURCES         0 or 1  This property may contain a list of
michael@0:      *                               values
michael@0:      *     RRULE             0+
michael@0:      *     STATUS            0 or 1  MAY be one of COMPLETED/NEEDS ACTION/IN-
michael@0:      *                               PROCESS
michael@0:      *     URL               0 or 1
michael@0:      *     X-PROPERTY        0+
michael@0:      * 
michael@0:      *     REQUEST-STATUS    0
michael@0:      * 
michael@0:      * VALARM                0+
michael@0:      * 
michael@0:      * VTIMEZONE             0+  MUST be present if any date/time refers
michael@0:      *                           to a timezone
michael@0:      * X-COMPONENT           0+
michael@0:      * 
michael@0:      * VEVENT                0
michael@0:      * VFREEBUSY             0
michael@0:      * VJOURNAL              0
michael@0:      * 
michael@0: * michael@0: */ michael@0: private class RequestValidator implements Validator { michael@0: michael@0: private static final long serialVersionUID = 1L; michael@0: michael@0: public void validate() throws ValidationException { michael@0: PropertyValidator.getInstance().assertOneOrMore(Property.ATTENDEE, getProperties()); michael@0: michael@0: PropertyValidator.getInstance().assertOne(Property.DTSTAMP, getProperties()); michael@0: PropertyValidator.getInstance().assertOne(Property.DTSTART, getProperties()); michael@0: PropertyValidator.getInstance().assertOne(Property.ORGANIZER, getProperties()); michael@0: PropertyValidator.getInstance().assertOne(Property.PRIORITY, getProperties()); michael@0: PropertyValidator.getInstance().assertOne(Property.SUMMARY, getProperties()); michael@0: PropertyValidator.getInstance().assertOne(Property.UID, getProperties()); michael@0: michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.SEQUENCE, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.CATEGORIES, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.CLASS, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.CREATED, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.DESCRIPTION, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.DUE, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.DURATION, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.GEO, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.LAST_MODIFIED, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.LOCATION, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.PERCENT_COMPLETE, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.RECURRENCE_ID, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.RESOURCES, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.STATUS, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.URL, getProperties()); michael@0: michael@0: PropertyValidator.getInstance().assertNone(Property.REQUEST_STATUS, getProperties()); michael@0: michael@0: for (final Iterator i = getAlarms().iterator(); i.hasNext();) { michael@0: final VAlarm alarm = (VAlarm) i.next(); michael@0: alarm.validate(Method.REQUEST); michael@0: } michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * @return the optional access classification property michael@0: */ michael@0: public final Clazz getClassification() { michael@0: return (Clazz) getProperty(Property.CLASS); michael@0: } michael@0: michael@0: /** michael@0: * @return the optional date completed property michael@0: */ michael@0: public final Completed getDateCompleted() { michael@0: return (Completed) getProperty(Property.COMPLETED); michael@0: } michael@0: michael@0: /** michael@0: * @return the optional creation-time property michael@0: */ michael@0: public final Created getCreated() { michael@0: return (Created) getProperty(Property.CREATED); michael@0: } michael@0: michael@0: /** michael@0: * @return the optional description property michael@0: */ michael@0: public final Description getDescription() { michael@0: return (Description) getProperty(Property.DESCRIPTION); michael@0: } michael@0: michael@0: /** michael@0: * Convenience method to pull the DTSTART out of the property list. michael@0: * @return The DtStart object representation of the start Date michael@0: */ michael@0: public final DtStart getStartDate() { michael@0: return (DtStart) getProperty(Property.DTSTART); michael@0: } michael@0: michael@0: /** michael@0: * @return the optional geographic position property michael@0: */ michael@0: public final Geo getGeographicPos() { michael@0: return (Geo) getProperty(Property.GEO); michael@0: } michael@0: michael@0: /** michael@0: * @return the optional last-modified property michael@0: */ michael@0: public final LastModified getLastModified() { michael@0: return (LastModified) getProperty(Property.LAST_MODIFIED); michael@0: } michael@0: michael@0: /** michael@0: * @return the optional location property michael@0: */ michael@0: public final Location getLocation() { michael@0: return (Location) getProperty(Property.LOCATION); michael@0: } michael@0: michael@0: /** michael@0: * @return the optional organizer property michael@0: */ michael@0: public final Organizer getOrganizer() { michael@0: return (Organizer) getProperty(Property.ORGANIZER); michael@0: } michael@0: michael@0: /** michael@0: * @return the optional percentage complete property michael@0: */ michael@0: public final PercentComplete getPercentComplete() { michael@0: return (PercentComplete) getProperty(Property.PERCENT_COMPLETE); michael@0: } michael@0: michael@0: /** michael@0: * @return the optional priority property michael@0: */ michael@0: public final Priority getPriority() { michael@0: return (Priority) getProperty(Property.PRIORITY); michael@0: } michael@0: michael@0: /** michael@0: * @return the optional date-stamp property michael@0: */ michael@0: public final DtStamp getDateStamp() { michael@0: return (DtStamp) getProperty(Property.DTSTAMP); michael@0: } michael@0: michael@0: /** michael@0: * @return the optional sequence number property michael@0: */ michael@0: public final Sequence getSequence() { michael@0: return (Sequence) getProperty(Property.SEQUENCE); michael@0: } michael@0: michael@0: /** michael@0: * @return the optional status property michael@0: */ michael@0: public final Status getStatus() { michael@0: return (Status) getProperty(Property.STATUS); michael@0: } michael@0: michael@0: /** michael@0: * @return the optional summary property michael@0: */ michael@0: public final Summary getSummary() { michael@0: return (Summary) getProperty(Property.SUMMARY); michael@0: } michael@0: michael@0: /** michael@0: * @return the optional URL property michael@0: */ michael@0: public final Url getUrl() { michael@0: return (Url) getProperty(Property.URL); michael@0: } michael@0: michael@0: /** michael@0: * @return the optional recurrence identifier property michael@0: */ michael@0: public final RecurrenceId getRecurrenceId() { michael@0: return (RecurrenceId) getProperty(Property.RECURRENCE_ID); michael@0: } michael@0: michael@0: /** michael@0: * @return the optional Duration property michael@0: */ michael@0: public final Duration getDuration() { michael@0: return (Duration) getProperty(Property.DURATION); michael@0: } michael@0: michael@0: /** michael@0: * @return the optional due property michael@0: */ michael@0: public final Due getDue() { michael@0: return (Due) getProperty(Property.DUE); michael@0: } michael@0: michael@0: /** michael@0: * Returns the UID property of this component if available. michael@0: * @return a Uid instance, or null if no UID property exists michael@0: */ michael@0: public final Uid getUid() { michael@0: return (Uid) getProperty(Property.UID); michael@0: } michael@0: michael@0: /** michael@0: * {@inheritDoc} michael@0: */ michael@0: public boolean equals(final Object arg0) { michael@0: if (arg0 instanceof VToDo) { michael@0: return super.equals(arg0) michael@0: && ObjectUtils.equals(alarms, ((VToDo) arg0).getAlarms()); michael@0: } michael@0: return super.equals(arg0); michael@0: } michael@0: michael@0: /** michael@0: * {@inheritDoc} michael@0: */ michael@0: public int hashCode() { michael@0: return new HashCodeBuilder().append(getName()).append(getProperties()) michael@0: .append(getAlarms()).toHashCode(); michael@0: } michael@0: michael@0: /** michael@0: * Overrides default copy method to add support for copying alarm sub-components. michael@0: * @return a copy of the instance michael@0: * @throws ParseException where an error occurs parsing data michael@0: * @throws IOException where an error occurs reading data michael@0: * @throws URISyntaxException where an invalid URI is encountered michael@0: * @see net.fortuna.ical4j.model.Component#copy() michael@0: */ michael@0: public Component copy() throws ParseException, IOException, URISyntaxException { michael@0: final VToDo copy = (VToDo) super.copy(); michael@0: copy.alarms = new ComponentList(alarms); michael@0: return copy; michael@0: } michael@0: }