diff -r 000000000000 -r fb9019fb1bf7 src/net/fortuna/ical4j/model/component/VFreeBusy.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/net/fortuna/ical4j/model/component/VFreeBusy.java Tue Feb 10 18:12:00 2015 +0100 @@ -0,0 +1,809 @@ +/** + * Copyright (c) 2012, Ben Fortuna + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * o Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * o Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * o Neither the name of Ben Fortuna nor the names of any other contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.fortuna.ical4j.model.component; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import net.fortuna.ical4j.model.Component; +import net.fortuna.ical4j.model.ComponentList; +import net.fortuna.ical4j.model.DateRange; +import net.fortuna.ical4j.model.DateTime; +import net.fortuna.ical4j.model.Dur; +import net.fortuna.ical4j.model.Period; +import net.fortuna.ical4j.model.PeriodList; +import net.fortuna.ical4j.model.Property; +import net.fortuna.ical4j.model.PropertyList; +import net.fortuna.ical4j.model.ValidationException; +import net.fortuna.ical4j.model.Validator; +import net.fortuna.ical4j.model.parameter.FbType; +import net.fortuna.ical4j.model.property.Contact; +import net.fortuna.ical4j.model.property.DtEnd; +import net.fortuna.ical4j.model.property.DtStamp; +import net.fortuna.ical4j.model.property.DtStart; +import net.fortuna.ical4j.model.property.Duration; +import net.fortuna.ical4j.model.property.FreeBusy; +import net.fortuna.ical4j.model.property.Method; +import net.fortuna.ical4j.model.property.Organizer; +import net.fortuna.ical4j.model.property.Uid; +import net.fortuna.ical4j.model.property.Url; +import net.fortuna.ical4j.util.CompatibilityHints; +import net.fortuna.ical4j.util.PropertyValidator; + +/** + * $Id$ [Apr 5, 2004] + * + * Defines an iCalendar VFREEBUSY component. + * + *
+ * 4.6.4 Free/Busy Component + * + * Component Name: VFREEBUSY + * + * Purpose: Provide a grouping of component properties that describe + * either a request for free/busy time, describe a response to a request + * for free/busy time or describe a published set of busy time. + * + * Formal Definition: A "VFREEBUSY" calendar component is defined by the + * following notation: + * + * freebusyc = "BEGIN" ":" "VFREEBUSY" CRLF + * fbprop + * "END" ":" "VFREEBUSY" CRLF + * + * fbprop = *( + * + * ; the following are optional, + * ; but MUST NOT occur more than once + * + * contact / dtstart / dtend / duration / dtstamp / + * organizer / uid / url / + * + * ; the following are optional, + * ; and MAY occur more than once + * + * attendee / comment / freebusy / rstatus / x-prop + * + * ) + * + * Description: A "VFREEBUSY" calendar component is a grouping of + * component properties that represents either a request for, a reply to + * a request for free or busy time information or a published set of + * busy time information. + * + * When used to request free/busy time information, the "ATTENDEE" + * property specifies the calendar users whose free/busy time is being + * requested; the "ORGANIZER" property specifies the calendar user who + * is requesting the free/busy time; the "DTSTART" and "DTEND" + * properties specify the window of time for which the free/busy time is + * being requested; the "UID" and "DTSTAMP" properties are specified to + * assist in proper sequencing of multiple free/busy time requests. + * + * When used to reply to a request for free/busy time, the "ATTENDEE" + * property specifies the calendar user responding to the free/busy time + * request; the "ORGANIZER" property specifies the calendar user that + * originally requested the free/busy time; the "FREEBUSY" property + * specifies the free/busy time information (if it exists); and the + * "UID" and "DTSTAMP" properties are specified to assist in proper + * sequencing of multiple free/busy time replies. + * + * When used to publish busy time, the "ORGANIZER" property specifies + * the calendar user associated with the published busy time; the + * "DTSTART" and "DTEND" properties specify an inclusive time window + * that surrounds the busy time information; the "FREEBUSY" property + * specifies the published busy time information; and the "DTSTAMP" + * property specifies the date/time that iCalendar object was created. + * + * The "VFREEBUSY" calendar component cannot be nested within another + * calendar component. Multiple "VFREEBUSY" calendar components can be + * specified within an iCalendar object. This permits the grouping of + * Free/Busy information into logical collections, such as monthly + * groups of busy time information. + * + * The "VFREEBUSY" calendar component is intended for use in iCalendar + * object methods involving requests for free time, requests for busy + * time, requests for both free and busy, and the associated replies. + * + * Free/Busy information is represented with the "FREEBUSY" property. + * This property provides a terse representation of time periods. One or + * more "FREEBUSY" properties can be specified in the "VFREEBUSY" + * calendar component. + * + * When present in a "VFREEBUSY" calendar component, the "DTSTART" and + * "DTEND" properties SHOULD be specified prior to any "FREEBUSY" + * properties. In a free time request, these properties can be used in + * combination with the "DURATION" property to represent a request for a + * duration of free time within a specified window of time. + * + * The recurrence properties ("RRULE", "EXRULE", "RDATE", "EXDATE") are + * not permitted within a "VFREEBUSY" calendar component. Any recurring + * events are resolved into their individual busy time periods using the + * "FREEBUSY" property. + * + * Example: The following is an example of a "VFREEBUSY" calendar + * component used to request free or busy time information: + * + * BEGIN:VFREEBUSY + * ORGANIZER:MAILTO:jane_doe@host1.com + * ATTENDEE:MAILTO:john_public@host2.com + * DTSTART:19971015T050000Z + * DTEND:19971016T050000Z + * DTSTAMP:19970901T083000Z + * END:VFREEBUSY + * + * The following is an example of a "VFREEBUSY" calendar component used + * to reply to the request with busy time information: + * + * BEGIN:VFREEBUSY + * ORGANIZER:MAILTO:jane_doe@host1.com + * ATTENDEE:MAILTO:john_public@host2.com + * DTSTAMP:19970901T100000Z + * FREEBUSY;VALUE=PERIOD:19971015T050000Z/PT8H30M, + * 19971015T160000Z/PT5H30M,19971015T223000Z/PT6H30M + * URL:http://host2.com/pub/busy/jpublic-01.ifb + * COMMENT:This iCalendar file contains busy time information for + * the next three months. + * END:VFREEBUSY + * + * The following is an example of a "VFREEBUSY" calendar component used + * to publish busy time information. + * + * BEGIN:VFREEBUSY + * ORGANIZER:jsmith@host.com + * DTSTART:19980313T141711Z + * DTEND:19980410T141711Z + * FREEBUSY:19980314T233000Z/19980315T003000Z + * FREEBUSY:19980316T153000Z/19980316T163000Z + * FREEBUSY:19980318T030000Z/19980318T040000Z + * URL:http://www.host.com/calendar/busytime/jsmith.ifb + * END:VFREEBUSY + *+ * + * Example 1 - Requesting all busy time slots for a given period: + * + *
+ * // request all busy times between today and 1 week from now..
+ * DateTime start = new DateTime();
+ * DateTime end = new DateTime(start.getTime() + 1000 * 60 * 60 * 24 * 7);
+ *
+ * VFreeBusy request = new VFreeBusy(start, end);
+ *
+ * VFreeBusy reply = new VFreeBusy(request, calendar.getComponents());
+ *
+ *
+ * Example 2 - Requesting all free time slots for a given period of at least the specified duration:
+ *
+ *
+ * // request all free time between today and 1 week from now of
+ * // duration 2 hours or more..
+ * DateTime start = new DateTime();
+ * DateTime end = new DateTime(start.getTime() + 1000 * 60 * 60 * 24 * 7);
+ *
+ * VFreeBusy request = new VFreeBusy(start, end, new Dur(0, 2, 0, 0));
+ *
+ * VFreeBusy response = new VFreeBusy(request, myCalendar.getComponents());
+ *
+ *
+ * @author Ben Fortuna
+ */
+public class VFreeBusy extends CalendarComponent {
+
+ private static final long serialVersionUID = 1046534053331139832L;
+
+ private final Map methodValidators = new HashMap();
+ {
+ methodValidators.put(Method.PUBLISH, new PublishValidator());
+ methodValidators.put(Method.REPLY, new ReplyValidator());
+ methodValidators.put(Method.REQUEST, new RequestValidator());
+ }
+
+ /**
+ * Default constructor.
+ */
+ public VFreeBusy() {
+ super(VFREEBUSY);
+ getProperties().add(new DtStamp());
+ }
+
+ /**
+ * Constructor.
+ * @param properties a list of properties
+ */
+ public VFreeBusy(final PropertyList properties) {
+ super(VFREEBUSY, properties);
+ }
+
+ /**
+ * Constructs a new VFreeBusy instance with the specified start and end boundaries. This constructor should be used
+ * for requesting busy time for a specified period.
+ * @param start the starting boundary for the VFreeBusy
+ * @param end the ending boundary for the VFreeBusy
+ */
+ public VFreeBusy(final DateTime start, final DateTime end) {
+ this();
+
+ // 4.8.2.4 Date/Time Start:
+ //
+ // Within the "VFREEBUSY" calendar component, this property defines the
+ // start date and time for the free or busy time information. The time
+ // MUST be specified in UTC time.
+ getProperties().add(new DtStart(start, true));
+
+ // 4.8.2.2 Date/Time End
+ //
+ // Within the "VFREEBUSY" calendar component, this property defines the
+ // end date and time for the free or busy time information. The time
+ // MUST be specified in the UTC time format. The value MUST be later in
+ // time than the value of the "DTSTART" property.
+ getProperties().add(new DtEnd(end, true));
+ }
+
+ /**
+ * Constructs a new VFreeBusy instance with the specified start and end boundaries. This constructor should be used
+ * for requesting free time for a specified duration in given period defined by the start date and end date.
+ * @param start the starting boundary for the VFreeBusy
+ * @param end the ending boundary for the VFreeBusy
+ * @param duration the length of the period being requested
+ */
+ public VFreeBusy(final DateTime start, final DateTime end, final Dur duration) {
+ this();
+
+ // 4.8.2.4 Date/Time Start:
+ //
+ // Within the "VFREEBUSY" calendar component, this property defines the
+ // start date and time for the free or busy time information. The time
+ // MUST be specified in UTC time.
+ getProperties().add(new DtStart(start, true));
+
+ // 4.8.2.2 Date/Time End
+ //
+ // Within the "VFREEBUSY" calendar component, this property defines the
+ // end date and time for the free or busy time information. The time
+ // MUST be specified in the UTC time format. The value MUST be later in
+ // time than the value of the "DTSTART" property.
+ getProperties().add(new DtEnd(end, true));
+
+ getProperties().add(new Duration(duration));
+ }
+
+ /**
+ * Constructs a new VFreeBusy instance representing a reply to the specified VFREEBUSY request according to the
+ * specified list of components.
+ * If the request argument has its duration set, then the result
+ * represents a list of free times (that is, parameter FBTYPE
+ * is set to FbType.FREE).
+ * If the request argument does not have its duration set, then the result
+ * represents a list of busy times.
+ * @param request a VFREEBUSY request
+ * @param components a component list used to initialise busy time
+ * @throws ValidationException
+ */
+ public VFreeBusy(final VFreeBusy request, final ComponentList components) {
+ this();
+
+ final DtStart start = (DtStart) request.getProperty(Property.DTSTART);
+
+ final DtEnd end = (DtEnd) request.getProperty(Property.DTEND);
+
+ final Duration duration = (Duration) request.getProperty(Property.DURATION);
+
+ // 4.8.2.4 Date/Time Start:
+ //
+ // Within the "VFREEBUSY" calendar component, this property defines the
+ // start date and time for the free or busy time information. The time
+ // MUST be specified in UTC time.
+ getProperties().add(new DtStart(start.getDate(), true));
+
+ // 4.8.2.2 Date/Time End
+ //
+ // Within the "VFREEBUSY" calendar component, this property defines the
+ // end date and time for the free or busy time information. The time
+ // MUST be specified in the UTC time format. The value MUST be later in
+ // time than the value of the "DTSTART" property.
+ getProperties().add(new DtEnd(end.getDate(), true));
+
+ if (duration != null) {
+ getProperties().add(new Duration(duration.getDuration()));
+ // Initialise with all free time of at least the specified duration..
+ final DateTime freeStart = new DateTime(start.getDate());
+ final DateTime freeEnd = new DateTime(end.getDate());
+ final FreeBusy fb = new FreeTimeBuilder().start(freeStart)
+ .end(freeEnd)
+ .duration(duration.getDuration())
+ .components(components)
+ .build();
+ if (fb != null && !fb.getPeriods().isEmpty()) {
+ getProperties().add(fb);
+ }
+ }
+ else {
+ // initialise with all busy time for the specified period..
+ final DateTime busyStart = new DateTime(start.getDate());
+ final DateTime busyEnd = new DateTime(end.getDate());
+ final FreeBusy fb = new BusyTimeBuilder().start(busyStart)
+ .end(busyEnd)
+ .components(components)
+ .build();
+ if (fb != null && !fb.getPeriods().isEmpty()) {
+ getProperties().add(fb);
+ }
+ }
+ }
+
+ /**
+ * Create a FREEBUSY property representing the busy time for the specified component list. If the component is not
+ * applicable to FREEBUSY time, or if the component is outside the bounds of the start and end dates, null is
+ * returned. If no valid busy periods are identified in the component an empty FREEBUSY property is returned (i.e.
+ * empty period list).
+ */
+ private class BusyTimeBuilder {
+
+ private DateTime start;
+
+ private DateTime end;
+
+ private ComponentList components;
+
+ public BusyTimeBuilder start(DateTime start) {
+ this.start = start;
+ return this;
+ }
+
+ public BusyTimeBuilder end(DateTime end) {
+ this.end = end;
+ return this;
+ }
+
+ public BusyTimeBuilder components(ComponentList components) {
+ this.components = components;
+ return this;
+ }
+
+ public FreeBusy build() {
+ final PeriodList periods = getConsumedTime(components, start, end);
+ final DateRange range = new DateRange(start, end);
+ // periods must be in UTC time for freebusy..
+ periods.setUtc(true);
+ for (final Iterator i = periods.iterator(); i.hasNext();) {
+ final Period period = (Period) i.next();
+ // check if period outside bounds..
+ if (!range.intersects(period)) {
+ periods.remove(period);
+ }
+ }
+ return new FreeBusy(periods);
+ }
+ }
+
+ /**
+ * Create a FREEBUSY property representing the free time available of the specified duration for the given list of
+ * components. component. If the component is not applicable to FREEBUSY time, or if the component is outside the
+ * bounds of the start and end dates, null is returned. If no valid busy periods are identified in the component an
+ * empty FREEBUSY property is returned (i.e. empty period list).
+ */
+ private class FreeTimeBuilder {
+
+ private DateTime start;
+
+ private DateTime end;
+
+ private Dur duration;
+
+ private ComponentList components;
+
+ public FreeTimeBuilder start(DateTime start) {
+ this.start = start;
+ return this;
+ }
+
+ public FreeTimeBuilder end(DateTime end) {
+ this.end = end;
+ return this;
+ }
+
+ private FreeTimeBuilder duration(Dur duration) {
+ this.duration = duration;
+ return this;
+ }
+
+ public FreeTimeBuilder components(ComponentList components) {
+ this.components = components;
+ return this;
+ }
+
+ public FreeBusy build() {
+ final FreeBusy fb = new FreeBusy();
+ fb.getParameters().add(FbType.FREE);
+ final PeriodList periods = getConsumedTime(components, start, end);
+ final DateRange range = new DateRange(start, end);
+ // Add final consumed time to avoid special-case end-of-list processing
+ periods.add(new Period(end, end));
+ DateTime lastPeriodEnd = new DateTime(start);
+ // where no time is consumed set the last period end as the range start..
+ for (final Iterator i = periods.iterator(); i.hasNext();) {
+ final Period period = (Period) i.next();
+
+ // check if period outside bounds.. or period intersects with the end of the range..
+ if (range.contains(period) ||
+ (range.intersects(period) && period.getStart().after(range.getRangeStart()))) {
+
+ // calculate duration between this period start and last period end..
+ final Duration freeDuration = new Duration(lastPeriodEnd, period.getStart());
+ if (freeDuration.getDuration().compareTo(duration) >= 0) {
+ fb.getPeriods().add(new Period(lastPeriodEnd, freeDuration.getDuration()));
+ }
+ }
+
+ if (period.getEnd().after(lastPeriodEnd)) {
+ lastPeriodEnd = period.getEnd();
+ }
+ }
+ return fb;
+ }
+ }
+
+ /**
+ * Creates a list of periods representing the time consumed by the specified list of components.
+ * @param components
+ * @return
+ */
+ private PeriodList getConsumedTime(final ComponentList components, final DateTime rangeStart,
+ final DateTime rangeEnd) {
+
+ final PeriodList periods = new PeriodList();
+ // only events consume time..
+ for (final Iterator i = components.getComponents(Component.VEVENT).iterator(); i.hasNext();) {
+ final Component component = (Component) i.next();
+ periods.addAll(((VEvent) component).getConsumedTime(rangeStart, rangeEnd, false));
+ }
+ return periods.normalise();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public final void validate(final boolean recurse) throws ValidationException {
+
+ if (!CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_RELAXED_VALIDATION)) {
+
+ // From "4.8.4.7 Unique Identifier":
+ // Conformance: The property MUST be specified in the "VEVENT", "VTODO",
+ // "VJOURNAL" or "VFREEBUSY" calendar components.
+ PropertyValidator.getInstance().assertOne(Property.UID,
+ getProperties());
+
+ // From "4.8.7.2 Date/Time Stamp":
+ // Conformance: This property MUST be included in the "VEVENT", "VTODO",
+ // "VJOURNAL" or "VFREEBUSY" calendar components.
+ PropertyValidator.getInstance().assertOne(Property.DTSTAMP,
+ getProperties());
+ }
+
+ final PropertyValidator validator = PropertyValidator.getInstance();
+
+ /*
+ * ; the following are optional, ; but MUST NOT occur more than once contact / dtstart / dtend / duration /
+ * dtstamp / organizer / uid / url /
+ */
+ validator.assertOneOrLess(Property.CONTACT, getProperties());
+ validator.assertOneOrLess(Property.DTSTART, getProperties());
+ validator.assertOneOrLess(Property.DTEND, getProperties());
+ validator.assertOneOrLess(Property.DURATION, getProperties());
+ validator.assertOneOrLess(Property.DTSTAMP, getProperties());
+ validator.assertOneOrLess(Property.ORGANIZER, getProperties());
+ validator.assertOneOrLess(Property.UID, getProperties());
+ validator.assertOneOrLess(Property.URL, getProperties());
+
+ /*
+ * ; the following are optional, ; and MAY occur more than once attendee / comment / freebusy / rstatus / x-prop
+ */
+
+ /*
+ * The recurrence properties ("RRULE", "EXRULE", "RDATE", "EXDATE") are not permitted within a "VFREEBUSY"
+ * calendar component. Any recurring events are resolved into their individual busy time periods using the
+ * "FREEBUSY" property.
+ */
+ validator.assertNone(Property.RRULE, getProperties());
+ validator.assertNone(Property.EXRULE, getProperties());
+ validator.assertNone(Property.RDATE, getProperties());
+ validator.assertNone(Property.EXDATE, getProperties());
+
+ // DtEnd value must be later in time that DtStart..
+ final DtStart dtStart = (DtStart) getProperty(Property.DTSTART);
+
+ // 4.8.2.4 Date/Time Start:
+ //
+ // Within the "VFREEBUSY" calendar component, this property defines the
+ // start date and time for the free or busy time information. The time
+ // MUST be specified in UTC time.
+ if (dtStart != null && !dtStart.isUtc()) {
+ throw new ValidationException("DTSTART must be specified in UTC time");
+ }
+
+ final DtEnd dtEnd = (DtEnd) getProperty(Property.DTEND);
+
+ // 4.8.2.2 Date/Time End
+ //
+ // Within the "VFREEBUSY" calendar component, this property defines the
+ // end date and time for the free or busy time information. The time
+ // MUST be specified in the UTC time format. The value MUST be later in
+ // time than the value of the "DTSTART" property.
+ if (dtEnd != null && !dtEnd.isUtc()) {
+ throw new ValidationException("DTEND must be specified in UTC time");
+ }
+
+ if (dtStart != null && dtEnd != null
+ && !dtStart.getDate().before(dtEnd.getDate())) {
+ throw new ValidationException("Property [" + Property.DTEND
+ + "] must be later in time than [" + Property.DTSTART + "]");
+ }
+
+ if (recurse) {
+ validateProperties();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ protected Validator getValidator(Method method) {
+ return (Validator) methodValidators.get(method);
+ }
+
+ /**
+ * + * Component/Property Presence + * ------------------- ---------------------------------------------- + * METHOD 1 MUST be "PUBLISH" + * + * VFREEBUSY 1+ + * DTSTAMP 1 + * DTSTART 1 DateTime values must be in UTC + * DTEND 1 DateTime values must be in UTC + * FREEBUSY 1+ MUST be BUSYTIME. Multiple instances are + * allowed. Multiple instances must be sorted + * in ascending order + * ORGANIZER 1 MUST contain the address of originator of + * busy time data. + * UID 1 + * COMMENT 0 or 1 + * CONTACT 0+ + * X-PROPERTY 0+ + * URL 0 or 1 Specifies busy time URL + * + * ATTENDEE 0 + * DURATION 0 + * REQUEST-STATUS 0 + * + * X-COMPONENT 0+ + * + * VEVENT 0 + * VTODO 0 + * VJOURNAL 0 + * VTIMEZONE 0 + * VALARM 0 + *+ * + */ + private class PublishValidator implements Validator { + + private static final long serialVersionUID = 1L; + + public void validate() throws ValidationException { + PropertyValidator.getInstance().assertOneOrMore(Property.FREEBUSY, getProperties()); + + PropertyValidator.getInstance().assertOne(Property.DTSTAMP, getProperties()); + PropertyValidator.getInstance().assertOne(Property.DTSTART, getProperties()); + PropertyValidator.getInstance().assertOne(Property.DTEND, getProperties()); + PropertyValidator.getInstance().assertOne(Property.ORGANIZER, getProperties()); + PropertyValidator.getInstance().assertOne(Property.UID, getProperties()); + + PropertyValidator.getInstance().assertOneOrLess(Property.COMMENT, getProperties()); + PropertyValidator.getInstance().assertOneOrLess(Property.URL, getProperties()); + + PropertyValidator.getInstance().assertNone(Property.ATTENDEE, getProperties()); + PropertyValidator.getInstance().assertNone(Property.DURATION, getProperties()); + PropertyValidator.getInstance().assertNone(Property.REQUEST_STATUS, getProperties()); + } + } + + /** + *
+ * Component/Property Presence + * ------------------- ---------------------------------------------- + * METHOD 1 MUST be "REPLY" + * + * VFREEBUSY 1 + * ATTENDEE 1 (address of recipient replying) + * DTSTAMP 1 + * DTEND 1 DateTime values must be in UTC + * DTSTART 1 DateTime values must be in UTC + * FREEBUSY 0+ (values MUST all be of the same data + * type. Multiple instances are allowed. + * Multiple instances MUST be sorted in + * ascending order. Values MAY NOT overlap) + * ORGANIZER 1 MUST be the request originator's address + * UID 1 + * + * COMMENT 0 or 1 + * CONTACT 0+ + * REQUEST-STATUS 0+ + * URL 0 or 1 (specifies busy time URL) + * X-PROPERTY 0+ + * DURATION 0 + * SEQUENCE 0 + * + * X-COMPONENT 0+ + * VALARM 0 + * VEVENT 0 + * VTODO 0 + * VJOURNAL 0 + * VTIMEZONE 0 + *+ * + */ + private class ReplyValidator implements Validator { + + private static final long serialVersionUID = 1L; + + public void validate() throws ValidationException { + + // FREEBUSY is 1+ in RFC2446 but 0+ in Calsify + + PropertyValidator.getInstance().assertOne(Property.ATTENDEE, getProperties()); + PropertyValidator.getInstance().assertOne(Property.DTSTAMP, getProperties()); + PropertyValidator.getInstance().assertOne(Property.DTEND, getProperties()); + PropertyValidator.getInstance().assertOne(Property.DTSTART, getProperties()); + PropertyValidator.getInstance().assertOne(Property.ORGANIZER, getProperties()); + PropertyValidator.getInstance().assertOne(Property.UID, getProperties()); + + PropertyValidator.getInstance().assertOneOrLess(Property.COMMENT, getProperties()); + PropertyValidator.getInstance().assertOneOrLess(Property.URL, getProperties()); + + PropertyValidator.getInstance().assertNone(Property.DURATION, getProperties()); + PropertyValidator.getInstance().assertNone(Property.SEQUENCE, getProperties()); + } + } + + /** + * METHOD:REQUEST Validator. + * + *
+ * Component/Property Presence + * ------------------- ---------------------------------------------- + * METHOD 1 MUST be "REQUEST" + * + * VFREEBUSY 1 + * ATTENDEE 1+ contain the address of the calendar store + * DTEND 1 DateTime values must be in UTC + * DTSTAMP 1 + * DTSTART 1 DateTime values must be in UTC + * ORGANIZER 1 MUST be the request originator's address + * UID 1 + * COMMENT 0 or 1 + * CONTACT 0+ + * X-PROPERTY 0+ + * + * FREEBUSY 0 + * DURATION 0 + * REQUEST-STATUS 0 + * URL 0 + * + * X-COMPONENT 0+ + * VALARM 0 + * VEVENT 0 + * VTODO 0 + * VJOURNAL 0 + * VTIMEZONE 0 + *+ * + */ + private class RequestValidator implements Validator { + + private static final long serialVersionUID = 1L; + + public void validate() throws ValidationException { + PropertyValidator.getInstance().assertOneOrMore(Property.ATTENDEE, getProperties()); + + PropertyValidator.getInstance().assertOne(Property.DTEND, getProperties()); + PropertyValidator.getInstance().assertOne(Property.DTSTAMP, getProperties()); + PropertyValidator.getInstance().assertOne(Property.DTSTART, getProperties()); + PropertyValidator.getInstance().assertOne(Property.ORGANIZER, getProperties()); + PropertyValidator.getInstance().assertOne(Property.UID, getProperties()); + + PropertyValidator.getInstance().assertOneOrLess(Property.COMMENT, getProperties()); + + PropertyValidator.getInstance().assertNone(Property.FREEBUSY, getProperties()); + PropertyValidator.getInstance().assertNone(Property.DURATION, getProperties()); + PropertyValidator.getInstance().assertNone(Property.REQUEST_STATUS, getProperties()); + PropertyValidator.getInstance().assertNone(Property.URL, getProperties()); + } + } + + /** + * @return the CONTACT property or null if not specified + */ + public final Contact getContact() { + return (Contact) getProperty(Property.CONTACT); + } + + /** + * @return the DTSTART propery or null if not specified + */ + public final DtStart getStartDate() { + return (DtStart) getProperty(Property.DTSTART); + } + + /** + * @return the DTEND property or null if not specified + */ + public final DtEnd getEndDate() { + return (DtEnd) getProperty(Property.DTEND); + } + + /** + * @return the DURATION property or null if not specified + */ + public final Duration getDuration() { + return (Duration) getProperty(Property.DURATION); + } + + /** + * @return the DTSTAMP property or null if not specified + */ + public final DtStamp getDateStamp() { + return (DtStamp) getProperty(Property.DTSTAMP); + } + + /** + * @return the ORGANIZER property or null if not specified + */ + public final Organizer getOrganizer() { + return (Organizer) getProperty(Property.ORGANIZER); + } + + /** + * @return the URL property or null if not specified + */ + public final Url getUrl() { + return (Url) getProperty(Property.URL); + } + + /** + * Returns the UID property of this component if available. + * @return a Uid instance, or null if no UID property exists + */ + public final Uid getUid() { + return (Uid) getProperty(Property.UID); + } +}