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.DateTime; michael@0: import net.fortuna.ical4j.model.Dur; michael@0: import net.fortuna.ical4j.model.Parameter; michael@0: import net.fortuna.ical4j.model.Period; michael@0: import net.fortuna.ical4j.model.PeriodList; 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.parameter.Value; michael@0: import net.fortuna.ical4j.model.property.Clazz; 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.DtEnd; 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.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.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.Transp; 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.Dates; michael@0: import net.fortuna.ical4j.util.PropertyValidator; michael@0: import net.fortuna.ical4j.util.Strings; michael@0: michael@4: import org.apache.commons.lang3.ObjectUtils; michael@4: import org.apache.commons.lang3.builder.HashCodeBuilder; michael@0: michael@0: /** michael@0: * $Id$ [Apr 5, 2004] michael@0: * michael@0: * Defines an iCalendar VEVENT component. michael@0: * michael@0: *
michael@0:  *       4.6.1 Event Component
michael@0:  *   
michael@0:  *          Component Name: "VEVENT"
michael@0:  *   
michael@0:  *          Purpose: Provide a grouping of component properties that describe an
michael@0:  *          event.
michael@0:  *   
michael@0:  *          Format Definition: A "VEVENT" calendar component is defined by the
michael@0:  *          following notation:
michael@0:  *   
michael@0:  *            eventc     = "BEGIN" ":" "VEVENT" CRLF
michael@0:  *                         eventprop *alarmc
michael@0:  *                         "END" ":" "VEVENT" CRLF
michael@0:  *   
michael@0:  *            eventprop  = *(
michael@0:  *   
michael@0:  *                       ; the following are optional,
michael@0:  *                       ; but MUST NOT occur more than once
michael@0:  *   
michael@0:  *                       class / created / description / dtstart / geo /
michael@0:  *                       last-mod / location / organizer / priority /
michael@0:  *                       dtstamp / seq / status / summary / transp /
michael@0:  *                       uid / url / recurid /
michael@0:  *   
michael@0:  *                       ; either 'dtend' or 'duration' may appear in
michael@0:  *                       ; a 'eventprop', but 'dtend' and 'duration'
michael@0:  *                       ; MUST NOT occur in the same 'eventprop'
michael@0:  *   
michael@0:  *                       dtend / duration /
michael@0:  *   
michael@0:  *                       ; the following are optional,
michael@0:  *                       ; and MAY occur more than once
michael@0:  *   
michael@0:  *                       attach / attendee / categories / comment /
michael@0:  *                       contact / exdate / exrule / rstatus / related /
michael@0:  *                       resources / rdate / rrule / x-prop
michael@0:  *   
michael@0:  *                       )
michael@0:  * 
michael@0: * michael@0: * Example 1 - Creating a new all-day event: michael@0: * michael@0: *

michael@0:  * java.util.Calendar cal = java.util.Calendar.getInstance();
michael@0:  * cal.set(java.util.Calendar.MONTH, java.util.Calendar.DECEMBER);
michael@0:  * cal.set(java.util.Calendar.DAY_OF_MONTH, 25);
michael@0:  * 
michael@0:  * VEvent christmas = new VEvent(cal.getTime(), "Christmas Day");
michael@0:  * 
michael@0:  * // initialise as an all-day event..
michael@0:  * christmas.getProperties().getProperty(Property.DTSTART).getParameters().add(
michael@0:  *         Value.DATE);
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:  * christmas.getProperties().getProperty(Property.DTSTART).getParameters().add(
michael@0:  *         tzParam);
michael@0:  * 
michael@0: * michael@0: * Example 2 - Creating an event of one (1) hour duration: 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, 9);
michael@0:  * cal.set(java.util.Calendar.MINUTE, 30);
michael@0:  * 
michael@0:  * VEvent meeting = new VEvent(cal.getTime(), 1000 * 60 * 60, "Progress Meeting");
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:  * meeting.getProperties().getProperty(Property.DTSTART).getParameters().add(
michael@0:  *         tzParam);
michael@0:  * 
michael@0: * michael@0: * Example 3 - Retrieve a list of periods representing a recurring event in a specified range: michael@0: * michael@0: *

michael@0:  * Calendar weekday9AM = Calendar.getInstance();
michael@0:  * weekday9AM.set(2005, Calendar.MARCH, 7, 9, 0, 0);
michael@0:  * weekday9AM.set(Calendar.MILLISECOND, 0);
michael@0:  * 
michael@0:  * Calendar weekday5PM = Calendar.getInstance();
michael@0:  * weekday5PM.set(2005, Calendar.MARCH, 7, 17, 0, 0);
michael@0:  * weekday5PM.set(Calendar.MILLISECOND, 0);
michael@0:  * 
michael@0:  * // Do the recurrence until December 31st.
michael@0:  * Calendar untilCal = Calendar.getInstance();
michael@0:  * untilCal.set(2005, Calendar.DECEMBER, 31);
michael@0:  * untilCal.set(Calendar.MILLISECOND, 0);
michael@0:  * 
michael@0:  * // 9:00AM to 5:00PM Rule
michael@0:  * Recur recur = new Recur(Recur.WEEKLY, untilCal.getTime());
michael@0:  * recur.getDayList().add(WeekDay.MO);
michael@0:  * recur.getDayList().add(WeekDay.TU);
michael@0:  * recur.getDayList().add(WeekDay.WE);
michael@0:  * recur.getDayList().add(WeekDay.TH);
michael@0:  * recur.getDayList().add(WeekDay.FR);
michael@0:  * recur.setInterval(3);
michael@0:  * recur.setWeekStartDay(WeekDay.MO.getDay());
michael@0:  * RRule rrule = new RRule(recur);
michael@0:  * 
michael@0:  * Summary summary = new Summary("TEST EVENTS THAT HAPPEN 9-5 MON-FRI");
michael@0:  * 
michael@0:  * weekdayNineToFiveEvents = new VEvent();
michael@0:  * weekdayNineToFiveEvents.getProperties().add(rrule);
michael@0:  * weekdayNineToFiveEvents.getProperties().add(summary);
michael@0:  * weekdayNineToFiveEvents.getProperties().add(new DtStart(weekday9AM.getTime()));
michael@0:  * weekdayNineToFiveEvents.getProperties().add(new DtEnd(weekday5PM.getTime()));
michael@0:  * 
michael@0:  * // Test Start 04/01/2005, End One month later.
michael@0:  * // Query Calendar Start and End Dates.
michael@0:  * Calendar queryStartDate = Calendar.getInstance();
michael@0:  * queryStartDate.set(2005, Calendar.APRIL, 1, 14, 47, 0);
michael@0:  * queryStartDate.set(Calendar.MILLISECOND, 0);
michael@0:  * Calendar queryEndDate = Calendar.getInstance();
michael@0:  * queryEndDate.set(2005, Calendar.MAY, 1, 11, 15, 0);
michael@0:  * queryEndDate.set(Calendar.MILLISECOND, 0);
michael@0:  * 
michael@0:  * // This range is monday to friday every three weeks, starting from
michael@0:  * // March 7th 2005, which means for our query dates we need
michael@0:  * // April 18th through to the 22nd.
michael@0:  * PeriodList periods = weekdayNineToFiveEvents.getPeriods(queryStartDate
michael@0:  *         .getTime(), queryEndDate.getTime());
michael@0:  * 
michael@0: * michael@0: * @author Ben Fortuna michael@0: */ michael@0: public class VEvent extends CalendarComponent { michael@0: michael@0: private static final long serialVersionUID = 2547948989200697335L; 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; michael@0: michael@0: /** michael@0: * Default constructor. michael@0: */ michael@0: public VEvent() { michael@0: super(VEVENT); michael@0: this.alarms = new ComponentList(); 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 VEvent(final PropertyList properties) { michael@0: super(VEVENT, properties); michael@0: this.alarms = new ComponentList(); michael@0: } michael@0: michael@0: /** michael@0: * Constructor. michael@0: * @param properties a list of properties michael@0: * @param alarms a list of alarms michael@0: */ michael@0: public VEvent(final PropertyList properties, final ComponentList alarms) { michael@0: super(VEVENT, properties); michael@0: this.alarms = alarms; michael@0: } michael@0: michael@0: /** michael@0: * Constructs a new VEVENT instance starting at the specified time with the specified summary. michael@0: * @param start the start date of the new event michael@0: * @param summary the event summary michael@0: */ michael@0: public VEvent(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 VEVENT instance starting and ending at the specified times with the specified summary. michael@0: * @param start the start date of the new event michael@0: * @param end the end date of the new event michael@0: * @param summary the event summary michael@0: */ michael@0: public VEvent(final Date start, final Date end, final String summary) { michael@0: this(); michael@0: getProperties().add(new DtStart(start)); michael@0: getProperties().add(new DtEnd(end)); michael@0: getProperties().add(new Summary(summary)); michael@0: } michael@0: michael@0: /** michael@0: * Constructs a new VEVENT 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 event michael@0: * @param duration the duration of the new event michael@0: * @param summary the event summary michael@0: */ michael@0: public VEvent(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 event. 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 b = new StringBuffer(); michael@0: b.append(BEGIN); michael@0: b.append(':'); michael@0: b.append(getName()); michael@0: b.append(Strings.LINE_SEPARATOR); michael@0: b.append(getProperties()); michael@0: b.append(getAlarms()); michael@0: b.append(END); michael@0: b.append(':'); michael@0: b.append(getName()); michael@0: b.append(Strings.LINE_SEPARATOR); michael@0: return b.toString(); michael@0: } michael@0: michael@0: /** michael@0: * {@inheritDoc} michael@0: */ michael@0: public final void validate(final boolean recurse) 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: michael@0: if (!(component instanceof VAlarm)) { michael@0: throw new ValidationException("Component [" michael@0: + component.getName() + "] may not occur in VEVENT"); michael@0: } 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 / created / description / dtstart / michael@0: * geo / last-mod / location / organizer / priority / dtstamp / seq / status / summary / transp / uid / url / michael@0: * recurid / michael@0: */ michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.CLASS, 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.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(Property.PRIORITY, michael@0: getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.DTSTAMP, 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.TRANSP, 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: PropertyValidator.getInstance().assertOneOrLess(Property.RECURRENCE_ID, michael@0: getProperties()); michael@0: michael@0: final Status status = (Status) getProperty(Property.STATUS); michael@0: if (status != null && !Status.VEVENT_TENTATIVE.getValue().equals(status.getValue()) michael@0: && !Status.VEVENT_CONFIRMED.getValue().equals(status.getValue()) michael@0: && !Status.VEVENT_CANCELLED.getValue().equals(status.getValue())) { michael@0: throw new ValidationException("Status property [" michael@0: + status.toString() + "] is not applicable for VEVENT"); michael@0: } michael@0: michael@0: /* michael@0: * ; either 'dtend' or 'duration' may appear in ; a 'eventprop', but 'dtend' and 'duration' ; MUST NOT occur in michael@0: * the same 'eventprop' dtend / duration / michael@0: */ michael@0: try { michael@0: PropertyValidator.getInstance().assertNone(Property.DTEND, 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: if (getProperty(Property.DTEND) != null) { michael@0: michael@0: /* michael@0: * The "VEVENT" is also the calendar component used to specify an anniversary or daily reminder within a michael@0: * calendar. These events have a DATE value type for the "DTSTART" property instead of the default data type michael@0: * of DATE-TIME. If such a "VEVENT" has a "DTEND" property, it MUST be specified as a DATE value also. The michael@0: * anniversary type of "VEVENT" can span more than one date (i.e, "DTEND" property value is set to a michael@0: * calendar date after the "DTSTART" property value). michael@0: */ michael@0: final DtStart start = (DtStart) getProperty(Property.DTSTART); michael@0: final DtEnd end = (DtEnd) getProperty(Property.DTEND); michael@0: michael@0: if (start != null) { michael@0: final Parameter startValue = start.getParameter(Parameter.VALUE); michael@0: final Parameter endValue = end.getParameter(Parameter.VALUE); michael@0: michael@0: boolean startEndValueMismatch = false; michael@0: if (endValue != null) { michael@0: if (startValue != null && !endValue.equals(startValue)) { michael@0: // invalid.. michael@0: startEndValueMismatch = true; michael@0: } michael@0: else if (startValue == null && !Value.DATE_TIME.equals(endValue)) { michael@0: // invalid.. michael@0: startEndValueMismatch = true; michael@0: } michael@0: } michael@0: else if (startValue != null && !Value.DATE_TIME.equals(startValue)) { michael@0: //invalid.. michael@0: startEndValueMismatch = true; michael@0: } michael@0: if (startEndValueMismatch) { michael@0: throw new ValidationException("Property [" + Property.DTEND michael@0: + "] must have the same [" + Parameter.VALUE michael@0: + "] as [" + Property.DTSTART + "]"); michael@0: } michael@0: } 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: * METHOD:ADD Validator. michael@0: * michael@0: *
michael@0:      * Component/Property  Presence
michael@0:      * ------------------- ----------------------------------------------
michael@0:      * METHOD              1      MUST be "ADD"
michael@0:      * VEVENT              1
michael@0:      *     DTSTAMP         1
michael@0:      *     DTSTART         1
michael@0:      *     ORGANIZER       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 event
michael@0:      * 
michael@0:      *     ATTACH          0+
michael@0:      *     ATTENDEE        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:      *     DTEND           0 or 1  if present DURATION MUST NOT be present
michael@0:      *     DURATION        0 or 1  if present DTEND 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:      *     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:      *     STATUS          0 or 1  MAY be one of TENTATIVE/CONFIRMED
michael@0:      *     TRANSP          0 or 1
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 to
michael@0:      *                            a timezone
michael@0:      * X-COMPONENT         0+
michael@0:      * 
michael@0:      * VFREEBUSY           0
michael@0:      * VTODO               0
michael@0:      * VJOURNAL            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.DTSTART, getProperties()); michael@0: PropertyValidator.getInstance().assertOne(Property.ORGANIZER, 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.DTEND, 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.PRIORITY, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.RESOURCES, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.STATUS, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.TRANSP, 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: * METHOD:CANCEL Validator. michael@0: * michael@0: *
michael@0:      * Component/Property  Presence
michael@0:      * ------------------- ----------------------------------------------
michael@0:      * METHOD              1      MUST be "CANCEL"
michael@0:      * 
michael@0:      * VEVENT              1+     All must have the same UID
michael@0:      *     ATTENDEE        0+     MUST include all "Attendees" being removed
michael@0:      *                            the event. MUST include all "Attendees" if
michael@0:      *                            the entire event is cancelled.
michael@0:      *     DTSTAMP         1
michael@0:      *     ORGANIZER       1
michael@0:      *     SEQUENCE        1
michael@0:      *     UID             1       MUST be the UID of the original REQUEST
michael@0:      * 
michael@0:      *     COMMENT         0 or 1
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:      *     CONTACT         0+
michael@0:      *     CREATED         0 or 1
michael@0:      *     DESCRIPTION     0 or 1
michael@0:      *     DTEND           0 or 1 if present DURATION MUST NOT be present
michael@0:      *     DTSTART         0 or 1
michael@0:      *     DURATION        0 or 1 if present DTEND 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:      *     PRIORITY        0 or 1
michael@0:      *     RDATE           0+
michael@0:      *     RECURRENCE-ID   0 or 1  MUST be present if referring to one or
michael@0:      *                             more or more recurring instances.
michael@0:      *                             Otherwise it MUST NOT be present
michael@0:      *     RELATED-TO      0+
michael@0:      *     RESOURCES       0 or 1
michael@0:      *     RRULE           0+
michael@0:      *     STATUS          0 or 1  MUST be set to CANCELLED. If uninviting
michael@0:      *                             specific "Attendees" then MUST NOT be
michael@0:      *                             included.
michael@0:      *     SUMMARY         0 or 1
michael@0:      *     TRANSP          0 or 1
michael@0:      *     URL             0 or 1
michael@0:      *     X-PROPERTY      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:      * X-COMPONENT         0+
michael@0:      * 
michael@0:      * VTODO               0
michael@0:      * VJOURNAL            0
michael@0:      * VFREEBUSY           0
michael@0:      * VALARM              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 final void validate() throws ValidationException { 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.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.DTEND, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.DTSTART, 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.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.SUMMARY, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.TRANSP, 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: * METHOD:COUNTER Validator. michael@0: * michael@0: *
michael@0:      * Component/Property  Presence
michael@0:      * ------------------- ----------------------------------------------
michael@0:      * METHOD              1      MUST be "COUNTER"
michael@0:      * 
michael@0:      * VEVENT              1
michael@0:      *     DTSTAMP         1
michael@0:      *     DTSTART         1
michael@0:      *     ORGANIZER       1       MUST be the "Organizer" of the original
michael@0:      *                             event
michael@0:      *     SEQUENCE        1       MUST be present if value is greater than 0,
michael@0:      *                             MAY be present if 0
michael@0:      *     SUMMARY         1       Can be null
michael@0:      *     UID             1       MUST be the UID associated with the REQUEST
michael@0:      *                             being countered
michael@0:      * 
michael@0:      *     ATTACH          0+
michael@0:      *     ATTENDEE        0+      Can also  be used to propose other
michael@0:      *                             "Attendees"
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:      *     DTEND           0 or 1  if present DURATION MUST NOT be present
michael@0:      *     DURATION        0 or 1  if present DTEND 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:      *     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 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+
michael@0:      *     STATUS          0 or 1  Value must be one of CONFIRMED/TENATIVE/
michael@0:      *                             CANCELLED
michael@0:      *     TRANSP          0 or 1
michael@0:      *     URL             0 or 1
michael@0:      *     X-PROPERTY      0+
michael@0:      * 
michael@0:      * VALARM              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:      * VTODO               0
michael@0:      * VJOURNAL            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().assertOne(Property.DTSTAMP, getProperties()); michael@0: PropertyValidator.getInstance().assertOne(Property.DTSTART, getProperties()); michael@0: michael@0: if (!CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_RELAXED_VALIDATION)) { michael@0: PropertyValidator.getInstance().assertOne(Property.ORGANIZER, getProperties()); michael@0: } michael@0: 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.DTEND, 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.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.TRANSP, 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: * METHOD:DECLINECOUNTER Validator. michael@0: * michael@0: *
michael@0:      * Component/Property  Presence
michael@0:      * ------------------- ----------------------------------------------
michael@0:      * METHOD              1      MUST be "DECLINECOUNTER"
michael@0:      * 
michael@0:      * VEVENT              1
michael@0:      *     DTSTAMP         1
michael@0:      *     ORGANIZER       1
michael@0:      *     UID             1       MUST, same UID specified in original
michael@0:      *                             REQUEST and subsequent COUNTER
michael@0:      *     COMMENT         0 or 1
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:      *     REQUEST-STATUS  0+
michael@0:      *     SEQUENCE        0 OR 1  MUST be present if value is greater than 0,
michael@0:      *                             MAY be present if 0
michael@0:      *     X-PROPERTY      0+
michael@0:      *     ATTACH          0
michael@0:      *     ATTENDEE        0
michael@0:      *     CATEGORIES      0
michael@0:      *     CLASS           0
michael@0:      *     CONTACT         0
michael@0:      *     CREATED         0
michael@0:      *     DESCRIPTION     0
michael@0:      *     DTEND           0
michael@0:      *     DTSTART         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:      *     PRIORITY        0
michael@0:      *     RDATE           0
michael@0:      *     RELATED-TO      0
michael@0:      *     RESOURCES       0
michael@0:      *     RRULE           0
michael@0:      *     STATUS          0
michael@0:      *     SUMMARY         0
michael@0:      *     TRANSP          0
michael@0:      *     URL             0
michael@0:      * 
michael@0:      * X-COMPONENT         0+
michael@0:      * VTODO               0
michael@0:      * VJOURNAL            0
michael@0:      * VFREEBUSY           0
michael@0:      * VTIMEZONE           0
michael@0:      * VALARM              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().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.RECURRENCE_ID, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.SEQUENCE, getProperties()); michael@0: michael@0: PropertyValidator.getInstance().assertNone(Property.ATTACH, getProperties()); michael@0: PropertyValidator.getInstance().assertNone(Property.ATTENDEE, 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.DTEND, getProperties()); michael@0: PropertyValidator.getInstance().assertNone(Property.DTSTART, 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.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.RESOURCES, getProperties()); michael@0: PropertyValidator.getInstance().assertNone(Property.RRULE, getProperties()); michael@0: PropertyValidator.getInstance().assertNone(Property.STATUS, getProperties()); michael@0: PropertyValidator.getInstance().assertNone(Property.SUMMARY, getProperties()); michael@0: PropertyValidator.getInstance().assertNone(Property.TRANSP, 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: * METHOD:PUBLISH Validator. michael@0: * michael@0: *
michael@0:      * Component/Property  Presence
michael@0:      * ------------------- ----------------------------------------------
michael@0:      * METHOD              1       MUST equal "PUBLISH"
michael@0:      * VEVENT              1+
michael@0:      *      DTSTAMP        1
michael@0:      *      DTSTART        1
michael@0:      *      ORGANIZER      1
michael@0:      *      SUMMARY        1       Can be null.
michael@0:      *      UID            1
michael@0:      *      RECURRENCE-ID  0 or 1  only if referring to an instance of a
michael@0:      *                             recurring calendar component.  Otherwise
michael@0:      *                             it MUST NOT be present.
michael@0:      *      SEQUENCE       0 or 1  MUST be present if value is greater than
michael@0:      *                             0, MAY be present if 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:      *      DTEND          0 or 1  if present DURATION MUST NOT be present
michael@0:      *      DURATION       0 or 1  if present DTEND 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:      *      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:      *      STATUS         0 or 1 MAY be one of TENTATIVE/CONFIRMED/CANCELLED
michael@0:      *      TRANSP         0 or 1
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:      * VALARM              0+
michael@0:      * VFREEBUSY           0
michael@0:      * VJOURNAL            0
michael@0:      * VTODO               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: * 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: PropertyValidator.getInstance().assertOne(Property.DTSTART, 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.SUMMARY, getProperties()); michael@0: } michael@0: michael@0: PropertyValidator.getInstance().assertOne(Property.UID, getProperties()); michael@0: michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.RECURRENCE_ID, 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.DTEND, 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.PRIORITY, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.RESOURCES, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.STATUS, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.TRANSP, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.URL, getProperties()); michael@0: michael@0: if (!CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_RELAXED_VALIDATION)) { michael@0: PropertyValidator.getInstance().assertNone(Property.ATTENDEE, getProperties()); michael@0: } 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.PUBLISH); michael@0: } michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * METHOD:REFRESH Validator. michael@0: * michael@0: *
michael@0:      * Component/Property  Presence
michael@0:      * ------------------- ----------------------------------------------
michael@0:      * METHOD              1      MUST be "REFRESH"
michael@0:      * 
michael@0:      * VEVENT              1
michael@0:      *     ATTENDEE        1      MUST be the address of requestor
michael@0:      *     DTSTAMP         1
michael@0:      *     ORGANIZER       1
michael@0:      *     UID             1      MUST be the UID associated with original
michael@0:      *                            REQUEST
michael@0:      *     COMMENT         0 or 1
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:      *     X-PROPERTY      0+
michael@0:      * 
michael@0:      *     ATTACH          0
michael@0:      *     CATEGORIES      0
michael@0:      *     CLASS           0
michael@0:      *     CONTACT         0
michael@0:      *     CREATED         0
michael@0:      *     DESCRIPTION     0
michael@0:      *     DTEND           0
michael@0:      *     DTSTART         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:      *     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:      *     SUMMARY         0
michael@0:      *     TRANSP          0
michael@0:      *     URL             0
michael@0:      * 
michael@0:      * X-COMPONENT         0+
michael@0:      * 
michael@0:      * VTODO               0
michael@0:      * VJOURNAL            0
michael@0:      * VFREEBUSY           0
michael@0:      * VTIMEZONE           0
michael@0:      * VALARM              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.ORGANIZER, 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.DTEND, getProperties()); michael@0: PropertyValidator.getInstance().assertNone(Property.DTSTART, 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.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.SUMMARY, getProperties()); michael@0: PropertyValidator.getInstance().assertNone(Property.TRANSP, 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: * METHOD:REPLY Validator. michael@0: * michael@0: *
michael@0:      * Component/Property  Presence
michael@0:      * ------------------- ----------------------------------------------
michael@0:      * METHOD              1       MUST be "REPLY"
michael@0:      * VEVENT              1+      All components MUST have the same UID
michael@0:      *     ATTENDEE        1       MUST be the address of the Attendee
michael@0:      *                             replying.
michael@0:      *     DTSTAMP         1
michael@0:      *     ORGANIZER       1
michael@0:      *     RECURRENCE-ID   0 or 1  only if referring to an instance of a
michael@0:      *                             recurring calendar component.  Otherwise
michael@0:      *                             it must NOT be present.
michael@0:      *     UID             1       MUST be the UID of the original REQUEST
michael@0:      *     
michael@0:      *     SEQUENCE        0 or 1  MUST if non-zero, MUST be the sequence
michael@0:      *                             number of the original REQUEST. MAY be
michael@0:      *                             present if 0.
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:      *     DTEND           0 or 1  if present DURATION MUST NOT be present
michael@0:      *     DTSTART         0 or 1
michael@0:      *     DURATION        0 or 1  if present DTEND 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:      *     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:      *     REQUEST-STATUS  0+
michael@0:      *     RRULE           0+
michael@0:      *     STATUS          0 or 1
michael@0:      *     SUMMARY         0 or 1
michael@0:      *     TRANSP          0 or 1
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
michael@0:      *                            to a timezone
michael@0:      * X-COMPONENT         0+
michael@0:      * 
michael@0:      * VALARM              0
michael@0:      * VFREEBUSY           0
michael@0:      * VJOURNAL            0
michael@0:      * VTODO               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().assertOne(Property.ATTENDEE, getProperties()); 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.RECURRENCE_ID, 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.DTEND, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.DTSTART, 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.PRIORITY, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.RESOURCES, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.STATUS, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.SUMMARY, getProperties()); michael@0: PropertyValidator.getInstance().assertOneOrLess(Property.TRANSP, 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: * METHOD:REQUEST Validator. michael@0: * michael@0: *
michael@0:      * Component/Property  Presence
michael@0:      * -----------------------------------------------------------------
michael@0:      * METHOD              1       MUST be "REQUEST"
michael@0:      * VEVENT              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:      *     SEQUENCE        0 or 1  MUST be present if value is greater than 0,
michael@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:      *     DTEND           0 or 1  if present DURATION MUST NOT be present
michael@0:      *     DURATION        0 or 1  if present DTEND 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:      *     PRIORITY        0 or 1
michael@0:      *     RDATE           0+
michael@0:      *     RECURRENCE-ID   0 or 1  only if 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+
michael@0:      *     STATUS          0 or 1  MAY be one of TENTATIVE/CONFIRMED
michael@0:      *     TRANSP          0 or 1
michael@0:      *     URL             0 or 1
michael@0:      *     X-PROPERTY      0+
michael@0:      *     
michael@0:      * VALARM              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:      * VFREEBUSY           0
michael@0:      * VJOURNAL            0
michael@0:      * VTODO               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: if (!CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_RELAXED_VALIDATION)) { michael@0: PropertyValidator.getInstance().assertOneOrMore(Property.ATTENDEE, getProperties()); michael@0: } 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.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.DTEND, 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.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.TRANSP, 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.REQUEST); michael@0: } michael@0: } michael@0: } michael@0: /** michael@0: * Returns a normalised list of periods representing the consumed time for this event. michael@0: * @param rangeStart the start of a range michael@0: * @param rangeEnd the end of a range michael@0: * @return a normalised list of periods representing consumed time for this event michael@0: * @see VEvent#getConsumedTime(Date, Date, boolean) michael@0: */ michael@0: public final PeriodList getConsumedTime(final Date rangeStart, michael@0: final Date rangeEnd) { michael@0: return getConsumedTime(rangeStart, rangeEnd, true); michael@0: } michael@0: michael@0: /** michael@0: * Returns a list of periods representing the consumed time for this event in the specified range. Note that the michael@0: * returned list may contain a single period for non-recurring components or multiple periods for recurring michael@0: * components. If no time is consumed by this event an empty list is returned. michael@0: * @param rangeStart the start of the range to check for consumed time michael@0: * @param rangeEnd the end of the range to check for consumed time michael@0: * @param normalise indicate whether the returned list of periods should be normalised michael@0: * @return a list of periods representing consumed time for this event michael@0: */ michael@0: public final PeriodList getConsumedTime(final Date rangeStart, michael@0: final Date rangeEnd, final boolean normalise) { michael@0: PeriodList periods = new PeriodList(); michael@0: // if component is transparent return empty list.. michael@0: if (!Transp.TRANSPARENT.equals(getProperty(Property.TRANSP))) { michael@0: michael@0: // try { michael@0: periods = calculateRecurrenceSet(new Period(new DateTime(rangeStart), michael@0: new DateTime(rangeEnd))); michael@0: // } michael@0: // catch (ValidationException ve) { michael@0: // log.error("Invalid event data", ve); michael@0: // return periods; michael@0: // } michael@0: michael@0: // if periods already specified through recurrence, return.. michael@0: // ..also normalise before returning. michael@0: if (!periods.isEmpty() && normalise) { michael@0: periods = periods.normalise(); michael@0: } michael@0: } michael@0: michael@0: return periods; michael@0: } michael@0: michael@0: /** michael@0: * Returns a single occurrence of a recurring event. michael@0: * @param date a date on which the occurence should occur michael@0: * @return a single non-recurring event instance for the specified date, or null if the event doesn't michael@0: * occur on the specified date michael@0: * @throws IOException where an error occurs reading data michael@0: * @throws URISyntaxException where an invalid URI is encountered michael@0: * @throws ParseException where an error occurs parsing data michael@0: */ michael@0: public final VEvent getOccurrence(final Date date) throws IOException, michael@0: URISyntaxException, ParseException { michael@0: michael@0: final PeriodList consumedTime = getConsumedTime(date, date); michael@0: for (final Iterator i = consumedTime.iterator(); i.hasNext();) { michael@0: final Period p = (Period) i.next(); michael@0: if (p.getStart().equals(date)) { michael@0: final VEvent occurrence = (VEvent) this.copy(); michael@0: occurrence.getProperties().add(new RecurrenceId(date)); michael@0: return occurrence; michael@0: } michael@0: } michael@0: return null; michael@0: } michael@0: michael@0: /** michael@0: * @return the optional access classification property for an event 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 creation-time property for an event 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 for an event 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 for an event 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 for an event 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 for an event 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 for an event 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 priority property for an event 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 for an event 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 for an event 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 for an event 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 time transparency property for an event michael@0: */ michael@0: public final Transp getTransparency() { michael@0: return (Transp) getProperty(Property.TRANSP); michael@0: } michael@0: michael@0: /** michael@0: * @return the optional URL property for an event 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 for an event michael@0: */ michael@0: public final RecurrenceId getRecurrenceId() { michael@0: return (RecurrenceId) getProperty(Property.RECURRENCE_ID); michael@0: } michael@0: michael@0: /** michael@0: * Returns the end date of this event. Where an end date is not available it will be derived from the event michael@0: * duration. michael@0: * @return a DtEnd instance, or null if one cannot be derived michael@0: */ michael@0: public final DtEnd getEndDate() { michael@0: return getEndDate(true); michael@0: } michael@0: michael@0: /** michael@0: * Convenience method to pull the DTEND out of the property list. If DTEND was not specified, use the DTSTART + michael@0: * DURATION to calculate it. michael@0: * @param deriveFromDuration specifies whether to derive an end date from the event duration where an end date is michael@0: * not found michael@0: * @return The end for this VEVENT. michael@0: */ michael@0: public final DtEnd getEndDate(final boolean deriveFromDuration) { michael@0: DtEnd dtEnd = (DtEnd) getProperty(Property.DTEND); michael@0: // No DTEND? No problem, we'll use the DURATION. michael@3: if (dtEnd == null && deriveFromDuration && getStartDate() != null) { michael@0: final DtStart dtStart = getStartDate(); michael@3: final Duration vEventDuration; michael@3: if (getDuration() != null) { michael@3: vEventDuration = getDuration(); michael@3: } else if (dtStart.getDate() instanceof DateTime) { michael@3: // If "DTSTART" is a DATE-TIME, then the event's duration is zero (see: RFC 5545, 3.6.1 Event Component) michael@3: vEventDuration = new Duration(new Dur(0, 0, 0, 0)); michael@3: } else { michael@3: // If "DTSTART" is a DATE, then the event's duration is one day (see: RFC 5545, 3.6.1 Event Component) michael@3: vEventDuration = new Duration(new Dur(1, 0, 0, 0)); michael@3: } michael@3: michael@0: dtEnd = new DtEnd(Dates.getInstance(vEventDuration.getDuration() michael@0: .getTime(dtStart.getDate()), (Value) dtStart michael@0: .getParameter(Parameter.VALUE))); michael@0: if (dtStart.isUtc()) { michael@0: dtEnd.setUtc(true); michael@0: } michael@0: } michael@0: return dtEnd; 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: * 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 VEvent) { michael@0: return super.equals(arg0) michael@0: && ObjectUtils.equals(alarms, ((VEvent) 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 values in the instance cannot be parsed michael@0: * @throws IOException where values in the instance cannot be read michael@0: * @throws URISyntaxException where an invalid URI value is encountered in the instance michael@0: * @see net.fortuna.ical4j.model.Component#copy() michael@0: */ michael@0: public Component copy() throws ParseException, IOException, michael@0: URISyntaxException { michael@0: final VEvent copy = (VEvent) super.copy(); michael@0: copy.alarms = new ComponentList(alarms); michael@0: return copy; michael@0: } michael@0: }