src/net/fortuna/ical4j/model/component/VFreeBusy.java

Tue, 10 Feb 2015 18:12:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 10 Feb 2015 18:12:00 +0100
changeset 0
fb9019fb1bf7
child 3
73bdfa70b04e
permissions
-rw-r--r--

Import initial revisions of existing project AndroidCaldavSyncAdapater,
forked from upstream repository at 27e8a0f8495c92e0780d450bdf0c7cec77a03a55.

     1 /**
     2  * Copyright (c) 2012, Ben Fortuna
     3  * All rights reserved.
     4  *
     5  * Redistribution and use in source and binary forms, with or without
     6  * modification, are permitted provided that the following conditions
     7  * are met:
     8  *
     9  *  o Redistributions of source code must retain the above copyright
    10  * notice, this list of conditions and the following disclaimer.
    11  *
    12  *  o Redistributions in binary form must reproduce the above copyright
    13  * notice, this list of conditions and the following disclaimer in the
    14  * documentation and/or other materials provided with the distribution.
    15  *
    16  *  o Neither the name of Ben Fortuna nor the names of any other contributors
    17  * may be used to endorse or promote products derived from this software
    18  * without specific prior written permission.
    19  *
    20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
    24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
    25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
    26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
    27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
    28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
    29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    31  */
    32 package net.fortuna.ical4j.model.component;
    34 import java.util.HashMap;
    35 import java.util.Iterator;
    36 import java.util.Map;
    38 import net.fortuna.ical4j.model.Component;
    39 import net.fortuna.ical4j.model.ComponentList;
    40 import net.fortuna.ical4j.model.DateRange;
    41 import net.fortuna.ical4j.model.DateTime;
    42 import net.fortuna.ical4j.model.Dur;
    43 import net.fortuna.ical4j.model.Period;
    44 import net.fortuna.ical4j.model.PeriodList;
    45 import net.fortuna.ical4j.model.Property;
    46 import net.fortuna.ical4j.model.PropertyList;
    47 import net.fortuna.ical4j.model.ValidationException;
    48 import net.fortuna.ical4j.model.Validator;
    49 import net.fortuna.ical4j.model.parameter.FbType;
    50 import net.fortuna.ical4j.model.property.Contact;
    51 import net.fortuna.ical4j.model.property.DtEnd;
    52 import net.fortuna.ical4j.model.property.DtStamp;
    53 import net.fortuna.ical4j.model.property.DtStart;
    54 import net.fortuna.ical4j.model.property.Duration;
    55 import net.fortuna.ical4j.model.property.FreeBusy;
    56 import net.fortuna.ical4j.model.property.Method;
    57 import net.fortuna.ical4j.model.property.Organizer;
    58 import net.fortuna.ical4j.model.property.Uid;
    59 import net.fortuna.ical4j.model.property.Url;
    60 import net.fortuna.ical4j.util.CompatibilityHints;
    61 import net.fortuna.ical4j.util.PropertyValidator;
    63 /**
    64  * $Id$ [Apr 5, 2004]
    65  *
    66  * Defines an iCalendar VFREEBUSY component.
    67  *
    68  * <pre>
    69  *  4.6.4 Free/Busy Component
    70  *
    71  *     Component Name: VFREEBUSY
    72  *
    73  *     Purpose: Provide a grouping of component properties that describe
    74  *     either a request for free/busy time, describe a response to a request
    75  *     for free/busy time or describe a published set of busy time.
    76  *
    77  *     Formal Definition: A &quot;VFREEBUSY&quot; calendar component is defined by the
    78  *     following notation:
    79  *
    80  *       freebusyc  = &quot;BEGIN&quot; &quot;:&quot; &quot;VFREEBUSY&quot; CRLF
    81  *                    fbprop
    82  *                    &quot;END&quot; &quot;:&quot; &quot;VFREEBUSY&quot; CRLF
    83  *
    84  *       fbprop     = *(
    85  *
    86  *                  ; the following are optional,
    87  *                  ; but MUST NOT occur more than once
    88  *
    89  *                  contact / dtstart / dtend / duration / dtstamp /
    90  *                  organizer / uid / url /
    91  *
    92  *                  ; the following are optional,
    93  *                  ; and MAY occur more than once
    94  *
    95  *                  attendee / comment / freebusy / rstatus / x-prop
    96  *
    97  *                  )
    98  *
    99  *     Description: A &quot;VFREEBUSY&quot; calendar component is a grouping of
   100  *     component properties that represents either a request for, a reply to
   101  *     a request for free or busy time information or a published set of
   102  *     busy time information.
   103  *
   104  *     When used to request free/busy time information, the &quot;ATTENDEE&quot;
   105  *     property specifies the calendar users whose free/busy time is being
   106  *     requested; the &quot;ORGANIZER&quot; property specifies the calendar user who
   107  *     is requesting the free/busy time; the &quot;DTSTART&quot; and &quot;DTEND&quot;
   108  *     properties specify the window of time for which the free/busy time is
   109  *     being requested; the &quot;UID&quot; and &quot;DTSTAMP&quot; properties are specified to
   110  *     assist in proper sequencing of multiple free/busy time requests.
   111  *
   112  *     When used to reply to a request for free/busy time, the &quot;ATTENDEE&quot;
   113  *     property specifies the calendar user responding to the free/busy time
   114  *     request; the &quot;ORGANIZER&quot; property specifies the calendar user that
   115  *     originally requested the free/busy time; the &quot;FREEBUSY&quot; property
   116  *     specifies the free/busy time information (if it exists); and the
   117  *     &quot;UID&quot; and &quot;DTSTAMP&quot; properties are specified to assist in proper
   118  *     sequencing of multiple free/busy time replies.
   119  *
   120  *     When used to publish busy time, the &quot;ORGANIZER&quot; property specifies
   121  *     the calendar user associated with the published busy time; the
   122  *     &quot;DTSTART&quot; and &quot;DTEND&quot; properties specify an inclusive time window
   123  *     that surrounds the busy time information; the &quot;FREEBUSY&quot; property
   124  *     specifies the published busy time information; and the &quot;DTSTAMP&quot;
   125  *     property specifies the date/time that iCalendar object was created.
   126  *
   127  *     The &quot;VFREEBUSY&quot; calendar component cannot be nested within another
   128  *     calendar component. Multiple &quot;VFREEBUSY&quot; calendar components can be
   129  *     specified within an iCalendar object. This permits the grouping of
   130  *     Free/Busy information into logical collections, such as monthly
   131  *     groups of busy time information.
   132  *
   133  *     The &quot;VFREEBUSY&quot; calendar component is intended for use in iCalendar
   134  *     object methods involving requests for free time, requests for busy
   135  *     time, requests for both free and busy, and the associated replies.
   136  *
   137  *     Free/Busy information is represented with the &quot;FREEBUSY&quot; property.
   138  *     This property provides a terse representation of time periods. One or
   139  *     more &quot;FREEBUSY&quot; properties can be specified in the &quot;VFREEBUSY&quot;
   140  *     calendar component.
   141  *
   142  *     When present in a &quot;VFREEBUSY&quot; calendar component, the &quot;DTSTART&quot; and
   143  *     &quot;DTEND&quot; properties SHOULD be specified prior to any &quot;FREEBUSY&quot;
   144  *     properties. In a free time request, these properties can be used in
   145  *     combination with the &quot;DURATION&quot; property to represent a request for a
   146  *     duration of free time within a specified window of time.
   147  *
   148  *     The recurrence properties (&quot;RRULE&quot;, &quot;EXRULE&quot;, &quot;RDATE&quot;, &quot;EXDATE&quot;) are
   149  *     not permitted within a &quot;VFREEBUSY&quot; calendar component. Any recurring
   150  *     events are resolved into their individual busy time periods using the
   151  *     &quot;FREEBUSY&quot; property.
   152  *
   153  *     Example: The following is an example of a &quot;VFREEBUSY&quot; calendar
   154  *     component used to request free or busy time information:
   155  *
   156  *       BEGIN:VFREEBUSY
   157  *       ORGANIZER:MAILTO:jane_doe@host1.com
   158  *       ATTENDEE:MAILTO:john_public@host2.com
   159  *       DTSTART:19971015T050000Z
   160  *       DTEND:19971016T050000Z
   161  *       DTSTAMP:19970901T083000Z
   162  *       END:VFREEBUSY
   163  *
   164  *     The following is an example of a &quot;VFREEBUSY&quot; calendar component used
   165  *     to reply to the request with busy time information:
   166  *
   167  *       BEGIN:VFREEBUSY
   168  *       ORGANIZER:MAILTO:jane_doe@host1.com
   169  *       ATTENDEE:MAILTO:john_public@host2.com
   170  *       DTSTAMP:19970901T100000Z
   171  *       FREEBUSY;VALUE=PERIOD:19971015T050000Z/PT8H30M,
   172  *        19971015T160000Z/PT5H30M,19971015T223000Z/PT6H30M
   173  *       URL:http://host2.com/pub/busy/jpublic-01.ifb
   174  *       COMMENT:This iCalendar file contains busy time information for
   175  *         the next three months.
   176  *       END:VFREEBUSY
   177  *
   178  *     The following is an example of a &quot;VFREEBUSY&quot; calendar component used
   179  *     to publish busy time information.
   180  *
   181  *       BEGIN:VFREEBUSY
   182  *       ORGANIZER:jsmith@host.com
   183  *       DTSTART:19980313T141711Z
   184  *       DTEND:19980410T141711Z
   185  *       FREEBUSY:19980314T233000Z/19980315T003000Z
   186  *       FREEBUSY:19980316T153000Z/19980316T163000Z
   187  *       FREEBUSY:19980318T030000Z/19980318T040000Z
   188  *       URL:http://www.host.com/calendar/busytime/jsmith.ifb
   189  *       END:VFREEBUSY
   190  * </pre>
   191  *
   192  * Example 1 - Requesting all busy time slots for a given period:
   193  *
   194  * <pre><code>
   195  * // request all busy times between today and 1 week from now..
   196  * DateTime start = new DateTime();
   197  * DateTime end = new DateTime(start.getTime() + 1000 * 60 * 60 * 24 * 7);
   198  *
   199  * VFreeBusy request = new VFreeBusy(start, end);
   200  *
   201  * VFreeBusy reply = new VFreeBusy(request, calendar.getComponents());
   202  * </code></pre>
   203  *
   204  * Example 2 - Requesting all free time slots for a given period of at least the specified duration:
   205  *
   206  * <pre><code>
   207  * // request all free time between today and 1 week from now of
   208  * // duration 2 hours or more..
   209  * DateTime start = new DateTime();
   210  * DateTime end = new DateTime(start.getTime() + 1000 * 60 * 60 * 24 * 7);
   211  *
   212  * VFreeBusy request = new VFreeBusy(start, end, new Dur(0, 2, 0, 0));
   213  *
   214  * VFreeBusy response = new VFreeBusy(request, myCalendar.getComponents());
   215  * </code></pre>
   216  *
   217  * @author Ben Fortuna
   218  */
   219 public class VFreeBusy extends CalendarComponent {
   221     private static final long serialVersionUID = 1046534053331139832L;
   223     private final Map methodValidators = new HashMap();
   224     {
   225         methodValidators.put(Method.PUBLISH, new PublishValidator());
   226         methodValidators.put(Method.REPLY, new ReplyValidator());
   227         methodValidators.put(Method.REQUEST, new RequestValidator());
   228     }
   230     /**
   231      * Default constructor.
   232      */
   233     public VFreeBusy() {
   234         super(VFREEBUSY);
   235         getProperties().add(new DtStamp());
   236     }
   238     /**
   239      * Constructor.
   240      * @param properties a list of properties
   241      */
   242     public VFreeBusy(final PropertyList properties) {
   243         super(VFREEBUSY, properties);
   244     }
   246     /**
   247      * Constructs a new VFreeBusy instance with the specified start and end boundaries. This constructor should be used
   248      * for requesting busy time for a specified period.
   249      * @param start the starting boundary for the VFreeBusy
   250      * @param end the ending boundary for the VFreeBusy
   251      */
   252     public VFreeBusy(final DateTime start, final DateTime end) {
   253         this();
   255         // 4.8.2.4 Date/Time Start:
   256         //
   257         //    Within the "VFREEBUSY" calendar component, this property defines the
   258         //    start date and time for the free or busy time information. The time
   259         //    MUST be specified in UTC time.
   260         getProperties().add(new DtStart(start, true));
   262         // 4.8.2.2 Date/Time End
   263         //
   264         //    Within the "VFREEBUSY" calendar component, this property defines the
   265         //    end date and time for the free or busy time information. The time
   266         //    MUST be specified in the UTC time format. The value MUST be later in
   267         //    time than the value of the "DTSTART" property.
   268         getProperties().add(new DtEnd(end, true));
   269     }
   271     /**
   272      * Constructs a new VFreeBusy instance with the specified start and end boundaries. This constructor should be used
   273      * for requesting free time for a specified duration in given period defined by the start date and end date.
   274      * @param start the starting boundary for the VFreeBusy
   275      * @param end the ending boundary for the VFreeBusy
   276      * @param duration the length of the period being requested
   277      */
   278     public VFreeBusy(final DateTime start, final DateTime end, final Dur duration) {
   279         this();
   281         // 4.8.2.4 Date/Time Start:
   282         //
   283         //    Within the "VFREEBUSY" calendar component, this property defines the
   284         //    start date and time for the free or busy time information. The time
   285         //    MUST be specified in UTC time.
   286         getProperties().add(new DtStart(start, true));
   288         // 4.8.2.2 Date/Time End
   289         //
   290         //    Within the "VFREEBUSY" calendar component, this property defines the
   291         //    end date and time for the free or busy time information. The time
   292         //    MUST be specified in the UTC time format. The value MUST be later in
   293         //    time than the value of the "DTSTART" property.
   294         getProperties().add(new DtEnd(end, true));
   296         getProperties().add(new Duration(duration));
   297     }
   299     /**
   300      * Constructs a new VFreeBusy instance representing a reply to the specified VFREEBUSY request according to the
   301      * specified list of components.
   302      * If the request argument has its duration set, then the result
   303      * represents a list of <em>free</em> times (that is, parameter FBTYPE
   304      * is set to FbType.FREE).
   305      * If the request argument does not have its duration set, then the result
   306      * represents a list of <em>busy</em> times.
   307      * @param request a VFREEBUSY request
   308      * @param components a component list used to initialise busy time
   309      * @throws ValidationException 
   310      */
   311     public VFreeBusy(final VFreeBusy request, final ComponentList components) {
   312         this();
   314         final DtStart start = (DtStart) request.getProperty(Property.DTSTART);
   316         final DtEnd end = (DtEnd) request.getProperty(Property.DTEND);
   318         final Duration duration = (Duration) request.getProperty(Property.DURATION);
   320         // 4.8.2.4 Date/Time Start:
   321         //
   322         //    Within the "VFREEBUSY" calendar component, this property defines the
   323         //    start date and time for the free or busy time information. The time
   324         //    MUST be specified in UTC time.
   325         getProperties().add(new DtStart(start.getDate(), true));
   327         // 4.8.2.2 Date/Time End
   328         //
   329         //    Within the "VFREEBUSY" calendar component, this property defines the
   330         //    end date and time for the free or busy time information. The time
   331         //    MUST be specified in the UTC time format. The value MUST be later in
   332         //    time than the value of the "DTSTART" property.
   333         getProperties().add(new DtEnd(end.getDate(), true));
   335         if (duration != null) {
   336             getProperties().add(new Duration(duration.getDuration()));
   337             // Initialise with all free time of at least the specified duration..
   338             final DateTime freeStart = new DateTime(start.getDate());
   339             final DateTime freeEnd = new DateTime(end.getDate());
   340             final FreeBusy fb = new FreeTimeBuilder().start(freeStart)
   341                 .end(freeEnd)
   342                 .duration(duration.getDuration())
   343                 .components(components)
   344                 .build();
   345             if (fb != null && !fb.getPeriods().isEmpty()) {
   346                 getProperties().add(fb);
   347             }
   348         }
   349         else {
   350             // initialise with all busy time for the specified period..
   351             final DateTime busyStart = new DateTime(start.getDate());
   352             final DateTime busyEnd = new DateTime(end.getDate());
   353             final FreeBusy fb = new BusyTimeBuilder().start(busyStart)
   354                 .end(busyEnd)
   355                 .components(components)
   356                 .build();
   357             if (fb != null && !fb.getPeriods().isEmpty()) {
   358                 getProperties().add(fb);
   359             }
   360         }
   361     }
   363     /**
   364      * Create a FREEBUSY property representing the busy time for the specified component list. If the component is not
   365      * applicable to FREEBUSY time, or if the component is outside the bounds of the start and end dates, null is
   366      * returned. If no valid busy periods are identified in the component an empty FREEBUSY property is returned (i.e.
   367      * empty period list).
   368      */
   369     private class BusyTimeBuilder {
   371         private DateTime start;
   373         private DateTime end;
   375         private ComponentList components;
   377         public BusyTimeBuilder start(DateTime start) {
   378             this.start = start;
   379             return this;
   380         }
   382         public BusyTimeBuilder end(DateTime end) {
   383             this.end = end;
   384             return this;
   385         }
   387         public BusyTimeBuilder components(ComponentList components) {
   388             this.components = components;
   389             return this;
   390         }
   392         public FreeBusy build() {
   393             final PeriodList periods = getConsumedTime(components, start, end);
   394             final DateRange range = new DateRange(start, end);
   395             // periods must be in UTC time for freebusy..
   396             periods.setUtc(true);
   397             for (final Iterator i = periods.iterator(); i.hasNext();) {
   398                 final Period period = (Period) i.next();
   399                 // check if period outside bounds..
   400                 if (!range.intersects(period)) {
   401                     periods.remove(period);
   402                 }
   403             }
   404             return new FreeBusy(periods);
   405         }
   406     }
   408     /**
   409      * Create a FREEBUSY property representing the free time available of the specified duration for the given list of
   410      * components. component. If the component is not applicable to FREEBUSY time, or if the component is outside the
   411      * bounds of the start and end dates, null is returned. If no valid busy periods are identified in the component an
   412      * empty FREEBUSY property is returned (i.e. empty period list).
   413      */
   414     private class FreeTimeBuilder {
   416         private DateTime start;
   418         private DateTime end;
   420         private Dur duration;
   422         private ComponentList components;
   424         public FreeTimeBuilder start(DateTime start) {
   425             this.start = start;
   426             return this;
   427         }
   429         public FreeTimeBuilder end(DateTime end) {
   430             this.end = end;
   431             return this;
   432         }
   434         private FreeTimeBuilder duration(Dur duration) {
   435             this.duration = duration;
   436             return this;
   437         }
   439         public FreeTimeBuilder components(ComponentList components) {
   440             this.components = components;
   441             return this;
   442         }
   444         public FreeBusy build() {
   445             final FreeBusy fb = new FreeBusy();
   446             fb.getParameters().add(FbType.FREE);
   447             final PeriodList periods = getConsumedTime(components, start, end);
   448             final DateRange range = new DateRange(start, end);
   449             // Add final consumed time to avoid special-case end-of-list processing
   450             periods.add(new Period(end, end));
   451             DateTime lastPeriodEnd = new DateTime(start);
   452             // where no time is consumed set the last period end as the range start..
   453             for (final Iterator i = periods.iterator(); i.hasNext();) {
   454                 final Period period = (Period) i.next();
   456                 // check if period outside bounds.. or period intersects with the end of the range..
   457                 if (range.contains(period) || 
   458                 		(range.intersects(period) && period.getStart().after(range.getRangeStart()))) {
   460                     // calculate duration between this period start and last period end..
   461                     final Duration freeDuration = new Duration(lastPeriodEnd, period.getStart());
   462                     if (freeDuration.getDuration().compareTo(duration) >= 0) {
   463                         fb.getPeriods().add(new Period(lastPeriodEnd, freeDuration.getDuration()));
   464                     }
   465                 }
   467                 if (period.getEnd().after(lastPeriodEnd)) {
   468                     lastPeriodEnd = period.getEnd();
   469                 }
   470             }
   471             return fb;
   472         }
   473     }
   475     /**
   476      * Creates a list of periods representing the time consumed by the specified list of components.
   477      * @param components
   478      * @return
   479      */
   480     private PeriodList getConsumedTime(final ComponentList components, final DateTime rangeStart,
   481             final DateTime rangeEnd) {
   483         final PeriodList periods = new PeriodList();
   484         // only events consume time..
   485         for (final Iterator i = components.getComponents(Component.VEVENT).iterator(); i.hasNext();) {
   486             final Component component = (Component) i.next();
   487             periods.addAll(((VEvent) component).getConsumedTime(rangeStart, rangeEnd, false));
   488         }
   489         return periods.normalise();
   490     }
   492     /**
   493      * {@inheritDoc}
   494      */
   495     public final void validate(final boolean recurse) throws ValidationException {
   497         if (!CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_RELAXED_VALIDATION)) {
   499             // From "4.8.4.7 Unique Identifier":
   500             // Conformance: The property MUST be specified in the "VEVENT", "VTODO",
   501             // "VJOURNAL" or "VFREEBUSY" calendar components.
   502             PropertyValidator.getInstance().assertOne(Property.UID,
   503                     getProperties());
   505             // From "4.8.7.2 Date/Time Stamp":
   506             // Conformance: This property MUST be included in the "VEVENT", "VTODO",
   507             // "VJOURNAL" or "VFREEBUSY" calendar components.
   508             PropertyValidator.getInstance().assertOne(Property.DTSTAMP,
   509                     getProperties());
   510         }
   512         final PropertyValidator validator = PropertyValidator.getInstance();
   514         /*
   515          * ; the following are optional, ; but MUST NOT occur more than once contact / dtstart / dtend / duration /
   516          * dtstamp / organizer / uid / url /
   517          */
   518         validator.assertOneOrLess(Property.CONTACT, getProperties());
   519         validator.assertOneOrLess(Property.DTSTART, getProperties());
   520         validator.assertOneOrLess(Property.DTEND, getProperties());
   521         validator.assertOneOrLess(Property.DURATION, getProperties());
   522         validator.assertOneOrLess(Property.DTSTAMP, getProperties());
   523         validator.assertOneOrLess(Property.ORGANIZER, getProperties());
   524         validator.assertOneOrLess(Property.UID, getProperties());
   525         validator.assertOneOrLess(Property.URL, getProperties());
   527         /*
   528          * ; the following are optional, ; and MAY occur more than once attendee / comment / freebusy / rstatus / x-prop
   529          */
   531         /*
   532          * The recurrence properties ("RRULE", "EXRULE", "RDATE", "EXDATE") are not permitted within a "VFREEBUSY"
   533          * calendar component. Any recurring events are resolved into their individual busy time periods using the
   534          * "FREEBUSY" property.
   535          */
   536         validator.assertNone(Property.RRULE, getProperties());
   537         validator.assertNone(Property.EXRULE, getProperties());
   538         validator.assertNone(Property.RDATE, getProperties());
   539         validator.assertNone(Property.EXDATE, getProperties());
   541         // DtEnd value must be later in time that DtStart..
   542         final DtStart dtStart = (DtStart) getProperty(Property.DTSTART);
   544         // 4.8.2.4 Date/Time Start:
   545         //
   546         //    Within the "VFREEBUSY" calendar component, this property defines the
   547         //    start date and time for the free or busy time information. The time
   548         //    MUST be specified in UTC time.
   549         if (dtStart != null && !dtStart.isUtc()) {
   550             throw new ValidationException("DTSTART must be specified in UTC time");
   551         }
   553         final DtEnd dtEnd = (DtEnd) getProperty(Property.DTEND);
   555         // 4.8.2.2 Date/Time End
   556         //
   557         //    Within the "VFREEBUSY" calendar component, this property defines the
   558         //    end date and time for the free or busy time information. The time
   559         //    MUST be specified in the UTC time format. The value MUST be later in
   560         //    time than the value of the "DTSTART" property.
   561         if (dtEnd != null && !dtEnd.isUtc()) {
   562             throw new ValidationException("DTEND must be specified in UTC time");
   563         }
   565         if (dtStart != null && dtEnd != null
   566                 && !dtStart.getDate().before(dtEnd.getDate())) {
   567             throw new ValidationException("Property [" + Property.DTEND
   568                     + "] must be later in time than [" + Property.DTSTART + "]");
   569         }
   571         if (recurse) {
   572             validateProperties();
   573         }
   574     }
   576     /**
   577      * {@inheritDoc}
   578      */
   579     protected Validator getValidator(Method method) {
   580         return (Validator) methodValidators.get(method);
   581     }
   583     /**
   584      * <pre>
   585      * Component/Property  Presence
   586      * ------------------- ----------------------------------------------
   587      * METHOD              1       MUST be "PUBLISH"
   588      * 
   589      * VFREEBUSY           1+
   590      *     DTSTAMP         1
   591      *     DTSTART         1       DateTime values must be in UTC
   592      *     DTEND           1       DateTime values must be in UTC
   593      *     FREEBUSY        1+      MUST be BUSYTIME. Multiple instances are
   594      *                             allowed. Multiple instances must be sorted
   595      *                             in ascending order
   596      *     ORGANIZER       1       MUST contain the address of originator of
   597      *                             busy time data.
   598      *     UID             1
   599      *     COMMENT         0 or 1
   600      *     CONTACT         0+
   601      *     X-PROPERTY      0+
   602      *     URL             0 or 1  Specifies busy time URL
   603      * 
   604      *     ATTENDEE        0
   605      *     DURATION        0
   606      *     REQUEST-STATUS  0
   607      * 
   608      * X-COMPONENT         0+
   609      * 
   610      * VEVENT              0
   611      * VTODO               0
   612      * VJOURNAL            0
   613      * VTIMEZONE           0
   614      * VALARM              0
   615      * </pre>
   616      * 
   617      */
   618     private class PublishValidator implements Validator {
   620 		private static final long serialVersionUID = 1L;
   622         public void validate() throws ValidationException {
   623             PropertyValidator.getInstance().assertOneOrMore(Property.FREEBUSY, getProperties());
   625             PropertyValidator.getInstance().assertOne(Property.DTSTAMP, getProperties());
   626             PropertyValidator.getInstance().assertOne(Property.DTSTART, getProperties());
   627             PropertyValidator.getInstance().assertOne(Property.DTEND, getProperties());
   628             PropertyValidator.getInstance().assertOne(Property.ORGANIZER, getProperties());
   629             PropertyValidator.getInstance().assertOne(Property.UID, getProperties());
   631             PropertyValidator.getInstance().assertOneOrLess(Property.COMMENT, getProperties());
   632             PropertyValidator.getInstance().assertOneOrLess(Property.URL, getProperties());
   634             PropertyValidator.getInstance().assertNone(Property.ATTENDEE, getProperties());
   635             PropertyValidator.getInstance().assertNone(Property.DURATION, getProperties());
   636             PropertyValidator.getInstance().assertNone(Property.REQUEST_STATUS, getProperties());
   637         }
   638     }
   640     /**
   641      * <pre>
   642      * Component/Property  Presence
   643      * ------------------- ----------------------------------------------
   644      * METHOD              1      MUST be "REPLY"
   645      * 
   646      * VFREEBUSY           1
   647      *     ATTENDEE        1      (address of recipient replying)
   648      *     DTSTAMP         1
   649      *     DTEND           1      DateTime values must be in UTC
   650      *     DTSTART         1      DateTime values must be in UTC
   651      *     FREEBUSY        0+      (values MUST all be of the same data
   652      *                             type. Multiple instances are allowed.
   653      *                             Multiple instances MUST be sorted in
   654      *                             ascending order. Values MAY NOT overlap)
   655      *     ORGANIZER       1       MUST be the request originator's address
   656      *     UID             1
   657      * 
   658      *     COMMENT         0 or 1
   659      *     CONTACT         0+
   660      *     REQUEST-STATUS  0+
   661      *     URL             0 or 1  (specifies busy time URL)
   662      *     X-PROPERTY      0+
   663      *     DURATION        0
   664      *     SEQUENCE        0
   665      * 
   666      * X-COMPONENT         0+
   667      * VALARM              0
   668      * VEVENT              0
   669      * VTODO               0
   670      * VJOURNAL            0
   671      * VTIMEZONE           0
   672      * </pre>
   673      * 
   674      */
   675     private class ReplyValidator implements Validator {
   677 		private static final long serialVersionUID = 1L;
   679         public void validate() throws ValidationException {
   681             // FREEBUSY is 1+ in RFC2446 but 0+ in Calsify
   683             PropertyValidator.getInstance().assertOne(Property.ATTENDEE, getProperties());
   684             PropertyValidator.getInstance().assertOne(Property.DTSTAMP, getProperties());
   685             PropertyValidator.getInstance().assertOne(Property.DTEND, getProperties());
   686             PropertyValidator.getInstance().assertOne(Property.DTSTART, getProperties());
   687             PropertyValidator.getInstance().assertOne(Property.ORGANIZER, getProperties());
   688             PropertyValidator.getInstance().assertOne(Property.UID, getProperties());
   690             PropertyValidator.getInstance().assertOneOrLess(Property.COMMENT, getProperties());
   691             PropertyValidator.getInstance().assertOneOrLess(Property.URL, getProperties());
   693             PropertyValidator.getInstance().assertNone(Property.DURATION, getProperties());
   694             PropertyValidator.getInstance().assertNone(Property.SEQUENCE, getProperties());
   695         }
   696     }
   698     /**
   699      * METHOD:REQUEST Validator.
   700      * 
   701      * <pre>
   702      * Component/Property  Presence
   703      * ------------------- ----------------------------------------------
   704      * METHOD              1      MUST be "REQUEST"
   705      * 
   706      * VFREEBUSY           1
   707      *     ATTENDEE        1+     contain the address of the calendar store
   708      *     DTEND           1      DateTime values must be in UTC
   709      *     DTSTAMP         1
   710      *     DTSTART         1      DateTime values must be in UTC
   711      *     ORGANIZER       1      MUST be the request originator's address
   712      *     UID             1
   713      *     COMMENT         0 or 1
   714      *     CONTACT         0+
   715      *     X-PROPERTY      0+
   716      * 
   717      *     FREEBUSY        0
   718      *     DURATION        0
   719      *     REQUEST-STATUS  0
   720      *     URL             0
   721      * 
   722      * X-COMPONENT         0+
   723      * VALARM              0
   724      * VEVENT              0
   725      * VTODO               0
   726      * VJOURNAL            0
   727      * VTIMEZONE           0
   728      * </pre>
   729      * 
   730      */
   731     private class RequestValidator implements Validator {
   733 		private static final long serialVersionUID = 1L;
   735         public void validate() throws ValidationException {
   736             PropertyValidator.getInstance().assertOneOrMore(Property.ATTENDEE, getProperties());
   738             PropertyValidator.getInstance().assertOne(Property.DTEND, getProperties());
   739             PropertyValidator.getInstance().assertOne(Property.DTSTAMP, getProperties());
   740             PropertyValidator.getInstance().assertOne(Property.DTSTART, getProperties());
   741             PropertyValidator.getInstance().assertOne(Property.ORGANIZER, getProperties());
   742             PropertyValidator.getInstance().assertOne(Property.UID, getProperties());
   744             PropertyValidator.getInstance().assertOneOrLess(Property.COMMENT, getProperties());
   746             PropertyValidator.getInstance().assertNone(Property.FREEBUSY, getProperties());
   747             PropertyValidator.getInstance().assertNone(Property.DURATION, getProperties());
   748             PropertyValidator.getInstance().assertNone(Property.REQUEST_STATUS, getProperties());
   749             PropertyValidator.getInstance().assertNone(Property.URL, getProperties());
   750         }
   751     }
   753     /**
   754      * @return the CONTACT property or null if not specified
   755      */
   756     public final Contact getContact() {
   757         return (Contact) getProperty(Property.CONTACT);
   758     }
   760     /**
   761      * @return the DTSTART propery or null if not specified
   762      */
   763     public final DtStart getStartDate() {
   764         return (DtStart) getProperty(Property.DTSTART);
   765     }
   767     /**
   768      * @return the DTEND property or null if not specified
   769      */
   770     public final DtEnd getEndDate() {
   771         return (DtEnd) getProperty(Property.DTEND);
   772     }
   774     /**
   775      * @return the DURATION property or null if not specified
   776      */
   777     public final Duration getDuration() {
   778         return (Duration) getProperty(Property.DURATION);
   779     }
   781     /**
   782      * @return the DTSTAMP property or null if not specified
   783      */
   784     public final DtStamp getDateStamp() {
   785         return (DtStamp) getProperty(Property.DTSTAMP);
   786     }
   788     /**
   789      * @return the ORGANIZER property or null if not specified
   790      */
   791     public final Organizer getOrganizer() {
   792         return (Organizer) getProperty(Property.ORGANIZER);
   793     }
   795     /**
   796      * @return the URL property or null if not specified
   797      */
   798     public final Url getUrl() {
   799         return (Url) getProperty(Property.URL);
   800     }
   802     /**
   803      * Returns the UID property of this component if available.
   804      * @return a Uid instance, or null if no UID property exists
   805      */
   806     public final Uid getUid() {
   807         return (Uid) getProperty(Property.UID);
   808     }
   809 }

mercurial