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.

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

mercurial