|
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; |
|
33 |
|
34 import java.util.HashMap; |
|
35 import java.util.Iterator; |
|
36 import java.util.Map; |
|
37 |
|
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; |
|
62 |
|
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 "VFREEBUSY" calendar component is defined by the |
|
78 * following notation: |
|
79 * |
|
80 * freebusyc = "BEGIN" ":" "VFREEBUSY" CRLF |
|
81 * fbprop |
|
82 * "END" ":" "VFREEBUSY" 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 "VFREEBUSY" 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 "ATTENDEE" |
|
105 * property specifies the calendar users whose free/busy time is being |
|
106 * requested; the "ORGANIZER" property specifies the calendar user who |
|
107 * is requesting the free/busy time; the "DTSTART" and "DTEND" |
|
108 * properties specify the window of time for which the free/busy time is |
|
109 * being requested; the "UID" and "DTSTAMP" 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 "ATTENDEE" |
|
113 * property specifies the calendar user responding to the free/busy time |
|
114 * request; the "ORGANIZER" property specifies the calendar user that |
|
115 * originally requested the free/busy time; the "FREEBUSY" property |
|
116 * specifies the free/busy time information (if it exists); and the |
|
117 * "UID" and "DTSTAMP" properties are specified to assist in proper |
|
118 * sequencing of multiple free/busy time replies. |
|
119 * |
|
120 * When used to publish busy time, the "ORGANIZER" property specifies |
|
121 * the calendar user associated with the published busy time; the |
|
122 * "DTSTART" and "DTEND" properties specify an inclusive time window |
|
123 * that surrounds the busy time information; the "FREEBUSY" property |
|
124 * specifies the published busy time information; and the "DTSTAMP" |
|
125 * property specifies the date/time that iCalendar object was created. |
|
126 * |
|
127 * The "VFREEBUSY" calendar component cannot be nested within another |
|
128 * calendar component. Multiple "VFREEBUSY" 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 "VFREEBUSY" 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 "FREEBUSY" property. |
|
138 * This property provides a terse representation of time periods. One or |
|
139 * more "FREEBUSY" properties can be specified in the "VFREEBUSY" |
|
140 * calendar component. |
|
141 * |
|
142 * When present in a "VFREEBUSY" calendar component, the "DTSTART" and |
|
143 * "DTEND" properties SHOULD be specified prior to any "FREEBUSY" |
|
144 * properties. In a free time request, these properties can be used in |
|
145 * combination with the "DURATION" property to represent a request for a |
|
146 * duration of free time within a specified window of time. |
|
147 * |
|
148 * The recurrence properties ("RRULE", "EXRULE", "RDATE", "EXDATE") are |
|
149 * not permitted within a "VFREEBUSY" calendar component. Any recurring |
|
150 * events are resolved into their individual busy time periods using the |
|
151 * "FREEBUSY" property. |
|
152 * |
|
153 * Example: The following is an example of a "VFREEBUSY" 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 "VFREEBUSY" 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 "VFREEBUSY" 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 { |
|
220 |
|
221 private static final long serialVersionUID = 1046534053331139832L; |
|
222 |
|
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 } |
|
229 |
|
230 /** |
|
231 * Default constructor. |
|
232 */ |
|
233 public VFreeBusy() { |
|
234 super(VFREEBUSY); |
|
235 getProperties().add(new DtStamp()); |
|
236 } |
|
237 |
|
238 /** |
|
239 * Constructor. |
|
240 * @param properties a list of properties |
|
241 */ |
|
242 public VFreeBusy(final PropertyList properties) { |
|
243 super(VFREEBUSY, properties); |
|
244 } |
|
245 |
|
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(); |
|
254 |
|
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)); |
|
261 |
|
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 } |
|
270 |
|
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(); |
|
280 |
|
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)); |
|
287 |
|
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)); |
|
295 |
|
296 getProperties().add(new Duration(duration)); |
|
297 } |
|
298 |
|
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(); |
|
313 |
|
314 final DtStart start = (DtStart) request.getProperty(Property.DTSTART); |
|
315 |
|
316 final DtEnd end = (DtEnd) request.getProperty(Property.DTEND); |
|
317 |
|
318 final Duration duration = (Duration) request.getProperty(Property.DURATION); |
|
319 |
|
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)); |
|
326 |
|
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)); |
|
334 |
|
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 } |
|
362 |
|
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 { |
|
370 |
|
371 private DateTime start; |
|
372 |
|
373 private DateTime end; |
|
374 |
|
375 private ComponentList components; |
|
376 |
|
377 public BusyTimeBuilder start(DateTime start) { |
|
378 this.start = start; |
|
379 return this; |
|
380 } |
|
381 |
|
382 public BusyTimeBuilder end(DateTime end) { |
|
383 this.end = end; |
|
384 return this; |
|
385 } |
|
386 |
|
387 public BusyTimeBuilder components(ComponentList components) { |
|
388 this.components = components; |
|
389 return this; |
|
390 } |
|
391 |
|
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 } |
|
407 |
|
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 { |
|
415 |
|
416 private DateTime start; |
|
417 |
|
418 private DateTime end; |
|
419 |
|
420 private Dur duration; |
|
421 |
|
422 private ComponentList components; |
|
423 |
|
424 public FreeTimeBuilder start(DateTime start) { |
|
425 this.start = start; |
|
426 return this; |
|
427 } |
|
428 |
|
429 public FreeTimeBuilder end(DateTime end) { |
|
430 this.end = end; |
|
431 return this; |
|
432 } |
|
433 |
|
434 private FreeTimeBuilder duration(Dur duration) { |
|
435 this.duration = duration; |
|
436 return this; |
|
437 } |
|
438 |
|
439 public FreeTimeBuilder components(ComponentList components) { |
|
440 this.components = components; |
|
441 return this; |
|
442 } |
|
443 |
|
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(); |
|
455 |
|
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()))) { |
|
459 |
|
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 } |
|
466 |
|
467 if (period.getEnd().after(lastPeriodEnd)) { |
|
468 lastPeriodEnd = period.getEnd(); |
|
469 } |
|
470 } |
|
471 return fb; |
|
472 } |
|
473 } |
|
474 |
|
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) { |
|
482 |
|
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 } |
|
491 |
|
492 /** |
|
493 * {@inheritDoc} |
|
494 */ |
|
495 public final void validate(final boolean recurse) throws ValidationException { |
|
496 |
|
497 if (!CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_RELAXED_VALIDATION)) { |
|
498 |
|
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()); |
|
504 |
|
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 } |
|
511 |
|
512 final PropertyValidator validator = PropertyValidator.getInstance(); |
|
513 |
|
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()); |
|
526 |
|
527 /* |
|
528 * ; the following are optional, ; and MAY occur more than once attendee / comment / freebusy / rstatus / x-prop |
|
529 */ |
|
530 |
|
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()); |
|
540 |
|
541 // DtEnd value must be later in time that DtStart.. |
|
542 final DtStart dtStart = (DtStart) getProperty(Property.DTSTART); |
|
543 |
|
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 } |
|
552 |
|
553 final DtEnd dtEnd = (DtEnd) getProperty(Property.DTEND); |
|
554 |
|
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 } |
|
564 |
|
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 } |
|
570 |
|
571 if (recurse) { |
|
572 validateProperties(); |
|
573 } |
|
574 } |
|
575 |
|
576 /** |
|
577 * {@inheritDoc} |
|
578 */ |
|
579 protected Validator getValidator(Method method) { |
|
580 return (Validator) methodValidators.get(method); |
|
581 } |
|
582 |
|
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 { |
|
619 |
|
620 private static final long serialVersionUID = 1L; |
|
621 |
|
622 public void validate() throws ValidationException { |
|
623 PropertyValidator.getInstance().assertOneOrMore(Property.FREEBUSY, getProperties()); |
|
624 |
|
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()); |
|
630 |
|
631 PropertyValidator.getInstance().assertOneOrLess(Property.COMMENT, getProperties()); |
|
632 PropertyValidator.getInstance().assertOneOrLess(Property.URL, getProperties()); |
|
633 |
|
634 PropertyValidator.getInstance().assertNone(Property.ATTENDEE, getProperties()); |
|
635 PropertyValidator.getInstance().assertNone(Property.DURATION, getProperties()); |
|
636 PropertyValidator.getInstance().assertNone(Property.REQUEST_STATUS, getProperties()); |
|
637 } |
|
638 } |
|
639 |
|
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 { |
|
676 |
|
677 private static final long serialVersionUID = 1L; |
|
678 |
|
679 public void validate() throws ValidationException { |
|
680 |
|
681 // FREEBUSY is 1+ in RFC2446 but 0+ in Calsify |
|
682 |
|
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()); |
|
689 |
|
690 PropertyValidator.getInstance().assertOneOrLess(Property.COMMENT, getProperties()); |
|
691 PropertyValidator.getInstance().assertOneOrLess(Property.URL, getProperties()); |
|
692 |
|
693 PropertyValidator.getInstance().assertNone(Property.DURATION, getProperties()); |
|
694 PropertyValidator.getInstance().assertNone(Property.SEQUENCE, getProperties()); |
|
695 } |
|
696 } |
|
697 |
|
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 { |
|
732 |
|
733 private static final long serialVersionUID = 1L; |
|
734 |
|
735 public void validate() throws ValidationException { |
|
736 PropertyValidator.getInstance().assertOneOrMore(Property.ATTENDEE, getProperties()); |
|
737 |
|
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()); |
|
743 |
|
744 PropertyValidator.getInstance().assertOneOrLess(Property.COMMENT, getProperties()); |
|
745 |
|
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 } |
|
752 |
|
753 /** |
|
754 * @return the CONTACT property or null if not specified |
|
755 */ |
|
756 public final Contact getContact() { |
|
757 return (Contact) getProperty(Property.CONTACT); |
|
758 } |
|
759 |
|
760 /** |
|
761 * @return the DTSTART propery or null if not specified |
|
762 */ |
|
763 public final DtStart getStartDate() { |
|
764 return (DtStart) getProperty(Property.DTSTART); |
|
765 } |
|
766 |
|
767 /** |
|
768 * @return the DTEND property or null if not specified |
|
769 */ |
|
770 public final DtEnd getEndDate() { |
|
771 return (DtEnd) getProperty(Property.DTEND); |
|
772 } |
|
773 |
|
774 /** |
|
775 * @return the DURATION property or null if not specified |
|
776 */ |
|
777 public final Duration getDuration() { |
|
778 return (Duration) getProperty(Property.DURATION); |
|
779 } |
|
780 |
|
781 /** |
|
782 * @return the DTSTAMP property or null if not specified |
|
783 */ |
|
784 public final DtStamp getDateStamp() { |
|
785 return (DtStamp) getProperty(Property.DTSTAMP); |
|
786 } |
|
787 |
|
788 /** |
|
789 * @return the ORGANIZER property or null if not specified |
|
790 */ |
|
791 public final Organizer getOrganizer() { |
|
792 return (Organizer) getProperty(Property.ORGANIZER); |
|
793 } |
|
794 |
|
795 /** |
|
796 * @return the URL property or null if not specified |
|
797 */ |
|
798 public final Url getUrl() { |
|
799 return (Url) getProperty(Property.URL); |
|
800 } |
|
801 |
|
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 } |