Tue, 10 Feb 2015 18:12:00 +0100
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.io.IOException;
35 import java.net.URISyntaxException;
36 import java.text.ParseException;
37 import java.util.HashMap;
38 import java.util.Iterator;
39 import java.util.Map;
41 import net.fortuna.ical4j.model.Component;
42 import net.fortuna.ical4j.model.ComponentList;
43 import net.fortuna.ical4j.model.Date;
44 import net.fortuna.ical4j.model.DateTime;
45 import net.fortuna.ical4j.model.Dur;
46 import net.fortuna.ical4j.model.Parameter;
47 import net.fortuna.ical4j.model.Period;
48 import net.fortuna.ical4j.model.PeriodList;
49 import net.fortuna.ical4j.model.Property;
50 import net.fortuna.ical4j.model.PropertyList;
51 import net.fortuna.ical4j.model.ValidationException;
52 import net.fortuna.ical4j.model.Validator;
53 import net.fortuna.ical4j.model.parameter.Value;
54 import net.fortuna.ical4j.model.property.Clazz;
55 import net.fortuna.ical4j.model.property.Created;
56 import net.fortuna.ical4j.model.property.Description;
57 import net.fortuna.ical4j.model.property.DtEnd;
58 import net.fortuna.ical4j.model.property.DtStamp;
59 import net.fortuna.ical4j.model.property.DtStart;
60 import net.fortuna.ical4j.model.property.Duration;
61 import net.fortuna.ical4j.model.property.Geo;
62 import net.fortuna.ical4j.model.property.LastModified;
63 import net.fortuna.ical4j.model.property.Location;
64 import net.fortuna.ical4j.model.property.Method;
65 import net.fortuna.ical4j.model.property.Organizer;
66 import net.fortuna.ical4j.model.property.Priority;
67 import net.fortuna.ical4j.model.property.RecurrenceId;
68 import net.fortuna.ical4j.model.property.Sequence;
69 import net.fortuna.ical4j.model.property.Status;
70 import net.fortuna.ical4j.model.property.Summary;
71 import net.fortuna.ical4j.model.property.Transp;
72 import net.fortuna.ical4j.model.property.Uid;
73 import net.fortuna.ical4j.model.property.Url;
74 import net.fortuna.ical4j.util.CompatibilityHints;
75 import net.fortuna.ical4j.util.ComponentValidator;
76 import net.fortuna.ical4j.util.Dates;
77 import net.fortuna.ical4j.util.PropertyValidator;
78 import net.fortuna.ical4j.util.Strings;
80 import org.apache.commons.lang.ObjectUtils;
81 import org.apache.commons.lang.builder.HashCodeBuilder;
83 /**
84 * $Id$ [Apr 5, 2004]
85 *
86 * Defines an iCalendar VEVENT component.
87 *
88 * <pre>
89 * 4.6.1 Event Component
90 *
91 * Component Name: "VEVENT"
92 *
93 * Purpose: Provide a grouping of component properties that describe an
94 * event.
95 *
96 * Format Definition: A "VEVENT" calendar component is defined by the
97 * following notation:
98 *
99 * eventc = "BEGIN" ":" "VEVENT" CRLF
100 * eventprop *alarmc
101 * "END" ":" "VEVENT" CRLF
102 *
103 * eventprop = *(
104 *
105 * ; the following are optional,
106 * ; but MUST NOT occur more than once
107 *
108 * class / created / description / dtstart / geo /
109 * last-mod / location / organizer / priority /
110 * dtstamp / seq / status / summary / transp /
111 * uid / url / recurid /
112 *
113 * ; either 'dtend' or 'duration' may appear in
114 * ; a 'eventprop', but 'dtend' and 'duration'
115 * ; MUST NOT occur in the same 'eventprop'
116 *
117 * dtend / duration /
118 *
119 * ; the following are optional,
120 * ; and MAY occur more than once
121 *
122 * attach / attendee / categories / comment /
123 * contact / exdate / exrule / rstatus / related /
124 * resources / rdate / rrule / x-prop
125 *
126 * )
127 * </pre>
128 *
129 * Example 1 - Creating a new all-day event:
130 *
131 * <pre><code>
132 * java.util.Calendar cal = java.util.Calendar.getInstance();
133 * cal.set(java.util.Calendar.MONTH, java.util.Calendar.DECEMBER);
134 * cal.set(java.util.Calendar.DAY_OF_MONTH, 25);
135 *
136 * VEvent christmas = new VEvent(cal.getTime(), "Christmas Day");
137 *
138 * // initialise as an all-day event..
139 * christmas.getProperties().getProperty(Property.DTSTART).getParameters().add(
140 * Value.DATE);
141 *
142 * // add timezone information..
143 * VTimeZone tz = VTimeZone.getDefault();
144 * TzId tzParam = new TzId(tz.getProperties().getProperty(Property.TZID)
145 * .getValue());
146 * christmas.getProperties().getProperty(Property.DTSTART).getParameters().add(
147 * tzParam);
148 * </code></pre>
149 *
150 * Example 2 - Creating an event of one (1) hour duration:
151 *
152 * <pre><code>
153 * java.util.Calendar cal = java.util.Calendar.getInstance();
154 * // tomorrow..
155 * cal.add(java.util.Calendar.DAY_OF_MONTH, 1);
156 * cal.set(java.util.Calendar.HOUR_OF_DAY, 9);
157 * cal.set(java.util.Calendar.MINUTE, 30);
158 *
159 * VEvent meeting = new VEvent(cal.getTime(), 1000 * 60 * 60, "Progress Meeting");
160 *
161 * // add timezone information..
162 * VTimeZone tz = VTimeZone.getDefault();
163 * TzId tzParam = new TzId(tz.getProperties().getProperty(Property.TZID)
164 * .getValue());
165 * meeting.getProperties().getProperty(Property.DTSTART).getParameters().add(
166 * tzParam);
167 * </code></pre>
168 *
169 * Example 3 - Retrieve a list of periods representing a recurring event in a specified range:
170 *
171 * <pre><code>
172 * Calendar weekday9AM = Calendar.getInstance();
173 * weekday9AM.set(2005, Calendar.MARCH, 7, 9, 0, 0);
174 * weekday9AM.set(Calendar.MILLISECOND, 0);
175 *
176 * Calendar weekday5PM = Calendar.getInstance();
177 * weekday5PM.set(2005, Calendar.MARCH, 7, 17, 0, 0);
178 * weekday5PM.set(Calendar.MILLISECOND, 0);
179 *
180 * // Do the recurrence until December 31st.
181 * Calendar untilCal = Calendar.getInstance();
182 * untilCal.set(2005, Calendar.DECEMBER, 31);
183 * untilCal.set(Calendar.MILLISECOND, 0);
184 *
185 * // 9:00AM to 5:00PM Rule
186 * Recur recur = new Recur(Recur.WEEKLY, untilCal.getTime());
187 * recur.getDayList().add(WeekDay.MO);
188 * recur.getDayList().add(WeekDay.TU);
189 * recur.getDayList().add(WeekDay.WE);
190 * recur.getDayList().add(WeekDay.TH);
191 * recur.getDayList().add(WeekDay.FR);
192 * recur.setInterval(3);
193 * recur.setWeekStartDay(WeekDay.MO.getDay());
194 * RRule rrule = new RRule(recur);
195 *
196 * Summary summary = new Summary("TEST EVENTS THAT HAPPEN 9-5 MON-FRI");
197 *
198 * weekdayNineToFiveEvents = new VEvent();
199 * weekdayNineToFiveEvents.getProperties().add(rrule);
200 * weekdayNineToFiveEvents.getProperties().add(summary);
201 * weekdayNineToFiveEvents.getProperties().add(new DtStart(weekday9AM.getTime()));
202 * weekdayNineToFiveEvents.getProperties().add(new DtEnd(weekday5PM.getTime()));
203 *
204 * // Test Start 04/01/2005, End One month later.
205 * // Query Calendar Start and End Dates.
206 * Calendar queryStartDate = Calendar.getInstance();
207 * queryStartDate.set(2005, Calendar.APRIL, 1, 14, 47, 0);
208 * queryStartDate.set(Calendar.MILLISECOND, 0);
209 * Calendar queryEndDate = Calendar.getInstance();
210 * queryEndDate.set(2005, Calendar.MAY, 1, 11, 15, 0);
211 * queryEndDate.set(Calendar.MILLISECOND, 0);
212 *
213 * // This range is monday to friday every three weeks, starting from
214 * // March 7th 2005, which means for our query dates we need
215 * // April 18th through to the 22nd.
216 * PeriodList periods = weekdayNineToFiveEvents.getPeriods(queryStartDate
217 * .getTime(), queryEndDate.getTime());
218 * </code></pre>
219 *
220 * @author Ben Fortuna
221 */
222 public class VEvent extends CalendarComponent {
224 private static final long serialVersionUID = 2547948989200697335L;
226 private final Map methodValidators = new HashMap();
227 {
228 methodValidators.put(Method.ADD, new AddValidator());
229 methodValidators.put(Method.CANCEL, new CancelValidator());
230 methodValidators.put(Method.COUNTER, new CounterValidator());
231 methodValidators.put(Method.DECLINE_COUNTER, new DeclineCounterValidator());
232 methodValidators.put(Method.PUBLISH, new PublishValidator());
233 methodValidators.put(Method.REFRESH, new RefreshValidator());
234 methodValidators.put(Method.REPLY, new ReplyValidator());
235 methodValidators.put(Method.REQUEST, new RequestValidator());
236 }
238 private ComponentList alarms;
240 /**
241 * Default constructor.
242 */
243 public VEvent() {
244 super(VEVENT);
245 this.alarms = new ComponentList();
246 getProperties().add(new DtStamp());
247 }
249 /**
250 * Constructor.
251 * @param properties a list of properties
252 */
253 public VEvent(final PropertyList properties) {
254 super(VEVENT, properties);
255 this.alarms = new ComponentList();
256 }
258 /**
259 * Constructor.
260 * @param properties a list of properties
261 * @param alarms a list of alarms
262 */
263 public VEvent(final PropertyList properties, final ComponentList alarms) {
264 super(VEVENT, properties);
265 this.alarms = alarms;
266 }
268 /**
269 * Constructs a new VEVENT instance starting at the specified time with the specified summary.
270 * @param start the start date of the new event
271 * @param summary the event summary
272 */
273 public VEvent(final Date start, final String summary) {
274 this();
275 getProperties().add(new DtStart(start));
276 getProperties().add(new Summary(summary));
277 }
279 /**
280 * Constructs a new VEVENT instance starting and ending at the specified times with the specified summary.
281 * @param start the start date of the new event
282 * @param end the end date of the new event
283 * @param summary the event summary
284 */
285 public VEvent(final Date start, final Date end, final String summary) {
286 this();
287 getProperties().add(new DtStart(start));
288 getProperties().add(new DtEnd(end));
289 getProperties().add(new Summary(summary));
290 }
292 /**
293 * Constructs a new VEVENT instance starting at the specified times, for the specified duration, with the specified
294 * summary.
295 * @param start the start date of the new event
296 * @param duration the duration of the new event
297 * @param summary the event summary
298 */
299 public VEvent(final Date start, final Dur duration, final String summary) {
300 this();
301 getProperties().add(new DtStart(start));
302 getProperties().add(new Duration(duration));
303 getProperties().add(new Summary(summary));
304 }
306 /**
307 * Returns the list of alarms for this event.
308 * @return a component list
309 */
310 public final ComponentList getAlarms() {
311 return alarms;
312 }
314 /**
315 * {@inheritDoc}
316 */
317 public final String toString() {
318 final StringBuffer b = new StringBuffer();
319 b.append(BEGIN);
320 b.append(':');
321 b.append(getName());
322 b.append(Strings.LINE_SEPARATOR);
323 b.append(getProperties());
324 b.append(getAlarms());
325 b.append(END);
326 b.append(':');
327 b.append(getName());
328 b.append(Strings.LINE_SEPARATOR);
329 return b.toString();
330 }
332 /**
333 * {@inheritDoc}
334 */
335 public final void validate(final boolean recurse) throws ValidationException {
337 // validate that getAlarms() only contains VAlarm components
338 final Iterator iterator = getAlarms().iterator();
339 while (iterator.hasNext()) {
340 final Component component = (Component) iterator.next();
342 if (!(component instanceof VAlarm)) {
343 throw new ValidationException("Component ["
344 + component.getName() + "] may not occur in VEVENT");
345 }
347 ((VAlarm) component).validate(recurse);
348 }
350 if (!CompatibilityHints
351 .isHintEnabled(CompatibilityHints.KEY_RELAXED_VALIDATION)) {
353 // From "4.8.4.7 Unique Identifier":
354 // Conformance: The property MUST be specified in the "VEVENT", "VTODO",
355 // "VJOURNAL" or "VFREEBUSY" calendar components.
356 PropertyValidator.getInstance().assertOne(Property.UID,
357 getProperties());
359 // From "4.8.7.2 Date/Time Stamp":
360 // Conformance: This property MUST be included in the "VEVENT", "VTODO",
361 // "VJOURNAL" or "VFREEBUSY" calendar components.
362 PropertyValidator.getInstance().assertOne(Property.DTSTAMP,
363 getProperties());
364 }
366 /*
367 * ; the following are optional, ; but MUST NOT occur more than once class / created / description / dtstart /
368 * geo / last-mod / location / organizer / priority / dtstamp / seq / status / summary / transp / uid / url /
369 * recurid /
370 */
371 PropertyValidator.getInstance().assertOneOrLess(Property.CLASS,
372 getProperties());
373 PropertyValidator.getInstance().assertOneOrLess(Property.CREATED,
374 getProperties());
375 PropertyValidator.getInstance().assertOneOrLess(Property.DESCRIPTION,
376 getProperties());
377 PropertyValidator.getInstance().assertOneOrLess(Property.DTSTART,
378 getProperties());
379 PropertyValidator.getInstance().assertOneOrLess(Property.GEO,
380 getProperties());
381 PropertyValidator.getInstance().assertOneOrLess(Property.LAST_MODIFIED,
382 getProperties());
383 PropertyValidator.getInstance().assertOneOrLess(Property.LOCATION,
384 getProperties());
385 PropertyValidator.getInstance().assertOneOrLess(Property.ORGANIZER,
386 getProperties());
387 PropertyValidator.getInstance().assertOneOrLess(Property.PRIORITY,
388 getProperties());
389 PropertyValidator.getInstance().assertOneOrLess(Property.DTSTAMP,
390 getProperties());
391 PropertyValidator.getInstance().assertOneOrLess(Property.SEQUENCE,
392 getProperties());
393 PropertyValidator.getInstance().assertOneOrLess(Property.STATUS,
394 getProperties());
395 PropertyValidator.getInstance().assertOneOrLess(Property.SUMMARY,
396 getProperties());
397 PropertyValidator.getInstance().assertOneOrLess(Property.TRANSP,
398 getProperties());
399 PropertyValidator.getInstance().assertOneOrLess(Property.UID,
400 getProperties());
401 PropertyValidator.getInstance().assertOneOrLess(Property.URL,
402 getProperties());
403 PropertyValidator.getInstance().assertOneOrLess(Property.RECURRENCE_ID,
404 getProperties());
406 final Status status = (Status) getProperty(Property.STATUS);
407 if (status != null && !Status.VEVENT_TENTATIVE.getValue().equals(status.getValue())
408 && !Status.VEVENT_CONFIRMED.getValue().equals(status.getValue())
409 && !Status.VEVENT_CANCELLED.getValue().equals(status.getValue())) {
410 throw new ValidationException("Status property ["
411 + status.toString() + "] is not applicable for VEVENT");
412 }
414 /*
415 * ; either 'dtend' or 'duration' may appear in ; a 'eventprop', but 'dtend' and 'duration' ; MUST NOT occur in
416 * the same 'eventprop' dtend / duration /
417 */
418 try {
419 PropertyValidator.getInstance().assertNone(Property.DTEND,
420 getProperties());
421 }
422 catch (ValidationException ve) {
423 PropertyValidator.getInstance().assertNone(Property.DURATION,
424 getProperties());
425 }
427 if (getProperty(Property.DTEND) != null) {
429 /*
430 * The "VEVENT" is also the calendar component used to specify an anniversary or daily reminder within a
431 * calendar. These events have a DATE value type for the "DTSTART" property instead of the default data type
432 * of DATE-TIME. If such a "VEVENT" has a "DTEND" property, it MUST be specified as a DATE value also. The
433 * anniversary type of "VEVENT" can span more than one date (i.e, "DTEND" property value is set to a
434 * calendar date after the "DTSTART" property value).
435 */
436 final DtStart start = (DtStart) getProperty(Property.DTSTART);
437 final DtEnd end = (DtEnd) getProperty(Property.DTEND);
439 if (start != null) {
440 final Parameter startValue = start.getParameter(Parameter.VALUE);
441 final Parameter endValue = end.getParameter(Parameter.VALUE);
443 boolean startEndValueMismatch = false;
444 if (endValue != null) {
445 if (startValue != null && !endValue.equals(startValue)) {
446 // invalid..
447 startEndValueMismatch = true;
448 }
449 else if (startValue == null && !Value.DATE_TIME.equals(endValue)) {
450 // invalid..
451 startEndValueMismatch = true;
452 }
453 }
454 else if (startValue != null && !Value.DATE_TIME.equals(startValue)) {
455 //invalid..
456 startEndValueMismatch = true;
457 }
458 if (startEndValueMismatch) {
459 throw new ValidationException("Property [" + Property.DTEND
460 + "] must have the same [" + Parameter.VALUE
461 + "] as [" + Property.DTSTART + "]");
462 }
463 }
464 }
466 /*
467 * ; the following are optional, ; and MAY occur more than once attach / attendee / categories / comment /
468 * contact / exdate / exrule / rstatus / related / resources / rdate / rrule / x-prop
469 */
471 if (recurse) {
472 validateProperties();
473 }
474 }
476 /**
477 * {@inheritDoc}
478 */
479 protected Validator getValidator(Method method) {
480 return (Validator) methodValidators.get(method);
481 }
483 /**
484 * METHOD:ADD Validator.
485 *
486 * <pre>
487 * Component/Property Presence
488 * ------------------- ----------------------------------------------
489 * METHOD 1 MUST be "ADD"
490 * VEVENT 1
491 * DTSTAMP 1
492 * DTSTART 1
493 * ORGANIZER 1
494 * SEQUENCE 1 MUST be greater than 0
495 * SUMMARY 1 Can be null
496 * UID 1 MUST match that of the original event
497 *
498 * ATTACH 0+
499 * ATTENDEE 0+
500 * CATEGORIES 0 or 1 This property MAY contain a list of values
501 * CLASS 0 or 1
502 * COMMENT 0 or 1
503 * CONTACT 0+
504 * CREATED 0 or 1
505 * DESCRIPTION 0 or 1 Can be null
506 * DTEND 0 or 1 if present DURATION MUST NOT be present
507 * DURATION 0 or 1 if present DTEND MUST NOT be present
508 * EXDATE 0+
509 * EXRULE 0+
510 * GEO 0 or 1
511 * LAST-MODIFIED 0 or 1
512 * LOCATION 0 or 1
513 * PRIORITY 0 or 1
514 * RDATE 0+
515 * RELATED-TO 0+
516 * RESOURCES 0 or 1 This property MAY contain a list of values
517 * RRULE 0+
518 * STATUS 0 or 1 MAY be one of TENTATIVE/CONFIRMED
519 * TRANSP 0 or 1
520 * URL 0 or 1
521 * X-PROPERTY 0+
522 *
523 * RECURRENCE-ID 0
524 * REQUEST-STATUS 0
525 *
526 * VALARM 0+
527 * VTIMEZONE 0+ MUST be present if any date/time refers to
528 * a timezone
529 * X-COMPONENT 0+
530 *
531 * VFREEBUSY 0
532 * VTODO 0
533 * VJOURNAL 0
534 * </pre>
535 *
536 */
537 private class AddValidator implements Validator {
539 private static final long serialVersionUID = 1L;
541 public void validate() throws ValidationException {
542 PropertyValidator.getInstance().assertOne(Property.DTSTAMP, getProperties());
543 PropertyValidator.getInstance().assertOne(Property.DTSTART, getProperties());
544 PropertyValidator.getInstance().assertOne(Property.ORGANIZER, getProperties());
545 PropertyValidator.getInstance().assertOne(Property.SEQUENCE, getProperties());
546 PropertyValidator.getInstance().assertOne(Property.SUMMARY, getProperties());
547 PropertyValidator.getInstance().assertOne(Property.UID, getProperties());
549 PropertyValidator.getInstance().assertOneOrLess(Property.CATEGORIES, getProperties());
550 PropertyValidator.getInstance().assertOneOrLess(Property.CLASS, getProperties());
551 PropertyValidator.getInstance().assertOneOrLess(Property.COMMENT, getProperties());
552 PropertyValidator.getInstance().assertOneOrLess(Property.CREATED, getProperties());
553 PropertyValidator.getInstance().assertOneOrLess(Property.DESCRIPTION, getProperties());
554 PropertyValidator.getInstance().assertOneOrLess(Property.DTEND, getProperties());
555 PropertyValidator.getInstance().assertOneOrLess(Property.DURATION, getProperties());
556 PropertyValidator.getInstance().assertOneOrLess(Property.GEO, getProperties());
557 PropertyValidator.getInstance().assertOneOrLess(Property.LAST_MODIFIED, getProperties());
558 PropertyValidator.getInstance().assertOneOrLess(Property.LOCATION, getProperties());
559 PropertyValidator.getInstance().assertOneOrLess(Property.PRIORITY, getProperties());
560 PropertyValidator.getInstance().assertOneOrLess(Property.RESOURCES, getProperties());
561 PropertyValidator.getInstance().assertOneOrLess(Property.STATUS, getProperties());
562 PropertyValidator.getInstance().assertOneOrLess(Property.TRANSP, getProperties());
563 PropertyValidator.getInstance().assertOneOrLess(Property.URL, getProperties());
565 PropertyValidator.getInstance().assertNone(Property.RECURRENCE_ID, getProperties());
566 PropertyValidator.getInstance().assertNone(Property.REQUEST_STATUS, getProperties());
568 for (final Iterator i = getAlarms().iterator(); i.hasNext();) {
569 final VAlarm alarm = (VAlarm) i.next();
570 alarm.validate(Method.ADD);
571 }
572 }
573 }
575 /**
576 * METHOD:CANCEL Validator.
577 *
578 * <pre>
579 * Component/Property Presence
580 * ------------------- ----------------------------------------------
581 * METHOD 1 MUST be "CANCEL"
582 *
583 * VEVENT 1+ All must have the same UID
584 * ATTENDEE 0+ MUST include all "Attendees" being removed
585 * the event. MUST include all "Attendees" if
586 * the entire event is cancelled.
587 * DTSTAMP 1
588 * ORGANIZER 1
589 * SEQUENCE 1
590 * UID 1 MUST be the UID of the original REQUEST
591 *
592 * COMMENT 0 or 1
593 * ATTACH 0+
594 * CATEGORIES 0 or 1 This property may contain a list of values
595 * CLASS 0 or 1
596 * CONTACT 0+
597 * CREATED 0 or 1
598 * DESCRIPTION 0 or 1
599 * DTEND 0 or 1 if present DURATION MUST NOT be present
600 * DTSTART 0 or 1
601 * DURATION 0 or 1 if present DTEND MUST NOT be present
602 * EXDATE 0+
603 * EXRULE 0+
604 * GEO 0 or 1
605 * LAST-MODIFIED 0 or 1
606 * LOCATION 0 or 1
607 * PRIORITY 0 or 1
608 * RDATE 0+
609 * RECURRENCE-ID 0 or 1 MUST be present if referring to one or
610 * more or more recurring instances.
611 * Otherwise it MUST NOT be present
612 * RELATED-TO 0+
613 * RESOURCES 0 or 1
614 * RRULE 0+
615 * STATUS 0 or 1 MUST be set to CANCELLED. If uninviting
616 * specific "Attendees" then MUST NOT be
617 * included.
618 * SUMMARY 0 or 1
619 * TRANSP 0 or 1
620 * URL 0 or 1
621 * X-PROPERTY 0+
622 * REQUEST-STATUS 0
623 *
624 * VTIMEZONE 0+ MUST be present if any date/time refers to
625 * a timezone
626 * X-COMPONENT 0+
627 *
628 * VTODO 0
629 * VJOURNAL 0
630 * VFREEBUSY 0
631 * VALARM 0
632 * </pre>
633 *
634 */
635 private class CancelValidator implements Validator {
637 private static final long serialVersionUID = 1L;
639 public final void validate() throws ValidationException {
640 PropertyValidator.getInstance().assertOne(Property.DTSTAMP, getProperties());
641 PropertyValidator.getInstance().assertOne(Property.DTSTART, getProperties());
642 PropertyValidator.getInstance().assertOne(Property.ORGANIZER, getProperties());
643 PropertyValidator.getInstance().assertOne(Property.SEQUENCE, getProperties());
644 PropertyValidator.getInstance().assertOne(Property.UID, getProperties());
646 PropertyValidator.getInstance().assertOneOrLess(Property.COMMENT, getProperties());
647 PropertyValidator.getInstance().assertOneOrLess(Property.CATEGORIES, getProperties());
648 PropertyValidator.getInstance().assertOneOrLess(Property.CLASS, getProperties());
649 PropertyValidator.getInstance().assertOneOrLess(Property.CREATED, getProperties());
650 PropertyValidator.getInstance().assertOneOrLess(Property.DESCRIPTION, getProperties());
651 PropertyValidator.getInstance().assertOneOrLess(Property.DTEND, getProperties());
652 PropertyValidator.getInstance().assertOneOrLess(Property.DTSTART, getProperties());
653 PropertyValidator.getInstance().assertOneOrLess(Property.DURATION, getProperties());
654 PropertyValidator.getInstance().assertOneOrLess(Property.GEO, getProperties());
655 PropertyValidator.getInstance().assertOneOrLess(Property.LAST_MODIFIED, getProperties());
656 PropertyValidator.getInstance().assertOneOrLess(Property.LOCATION, getProperties());
657 PropertyValidator.getInstance().assertOneOrLess(Property.PRIORITY, getProperties());
658 PropertyValidator.getInstance().assertOneOrLess(Property.RECURRENCE_ID, getProperties());
659 PropertyValidator.getInstance().assertOneOrLess(Property.RESOURCES, getProperties());
660 PropertyValidator.getInstance().assertOneOrLess(Property.STATUS, getProperties());
661 PropertyValidator.getInstance().assertOneOrLess(Property.SUMMARY, getProperties());
662 PropertyValidator.getInstance().assertOneOrLess(Property.TRANSP, getProperties());
663 PropertyValidator.getInstance().assertOneOrLess(Property.URL, getProperties());
665 PropertyValidator.getInstance().assertNone(Property.REQUEST_STATUS, getProperties());
667 ComponentValidator.assertNone(Component.VALARM, getAlarms());
668 }
669 }
671 /**
672 * METHOD:COUNTER Validator.
673 *
674 * <pre>
675 * Component/Property Presence
676 * ------------------- ----------------------------------------------
677 * METHOD 1 MUST be "COUNTER"
678 *
679 * VEVENT 1
680 * DTSTAMP 1
681 * DTSTART 1
682 * ORGANIZER 1 MUST be the "Organizer" of the original
683 * event
684 * SEQUENCE 1 MUST be present if value is greater than 0,
685 * MAY be present if 0
686 * SUMMARY 1 Can be null
687 * UID 1 MUST be the UID associated with the REQUEST
688 * being countered
689 *
690 * ATTACH 0+
691 * ATTENDEE 0+ Can also be used to propose other
692 * "Attendees"
693 * CATEGORIES 0 or 1 This property may contain a list of values
694 * CLASS 0 or 1
695 * COMMENT 0 or 1
696 * CONTACT 0+
697 * CREATED 0 or 1
698 * DESCRIPTION 0 or 1
699 * DTEND 0 or 1 if present DURATION MUST NOT be present
700 * DURATION 0 or 1 if present DTEND MUST NOT be present
701 * EXDATE 0+
702 * EXRULE 0+
703 * GEO 0 or 1
704 * LAST-MODIFIED 0 or 1
705 * LOCATION 0 or 1
706 * PRIORITY 0 or 1
707 * RDATE 0+
708 * RECURRENCE-ID 0 or 1 MUST only if referring to an instance of a
709 * recurring calendar component. Otherwise it
710 * MUST NOT be present.
711 * RELATED-TO 0+
712 * REQUEST-STATUS 0+
713 * RESOURCES 0 or 1 This property may contain a list of values
714 * RRULE 0+
715 * STATUS 0 or 1 Value must be one of CONFIRMED/TENATIVE/
716 * CANCELLED
717 * TRANSP 0 or 1
718 * URL 0 or 1
719 * X-PROPERTY 0+
720 *
721 * VALARM 0+
722 * VTIMEZONE 0+ MUST be present if any date/time refers to
723 * a timezone
724 * X-COMPONENT 0+
725 *
726 * VTODO 0
727 * VJOURNAL 0
728 * VFREEBUSY 0
729 * </pre>
730 *
731 */
732 private class CounterValidator implements Validator {
734 private static final long serialVersionUID = 1L;
736 public void validate() throws ValidationException {
737 PropertyValidator.getInstance().assertOne(Property.DTSTAMP, getProperties());
738 PropertyValidator.getInstance().assertOne(Property.DTSTART, getProperties());
740 if (!CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_RELAXED_VALIDATION)) {
741 PropertyValidator.getInstance().assertOne(Property.ORGANIZER, getProperties());
742 }
744 PropertyValidator.getInstance().assertOne(Property.SEQUENCE, getProperties());
745 PropertyValidator.getInstance().assertOne(Property.SUMMARY, getProperties());
746 PropertyValidator.getInstance().assertOne(Property.UID, getProperties());
748 PropertyValidator.getInstance().assertOneOrLess(Property.CATEGORIES, getProperties());
749 PropertyValidator.getInstance().assertOneOrLess(Property.CLASS, getProperties());
750 PropertyValidator.getInstance().assertOneOrLess(Property.COMMENT, getProperties());
751 PropertyValidator.getInstance().assertOneOrLess(Property.CREATED, getProperties());
752 PropertyValidator.getInstance().assertOneOrLess(Property.DESCRIPTION, getProperties());
753 PropertyValidator.getInstance().assertOneOrLess(Property.DTEND, getProperties());
754 PropertyValidator.getInstance().assertOneOrLess(Property.DURATION, getProperties());
755 PropertyValidator.getInstance().assertOneOrLess(Property.GEO, getProperties());
756 PropertyValidator.getInstance().assertOneOrLess(Property.LAST_MODIFIED, getProperties());
757 PropertyValidator.getInstance().assertOneOrLess(Property.LOCATION, getProperties());
758 PropertyValidator.getInstance().assertOneOrLess(Property.PRIORITY, getProperties());
759 PropertyValidator.getInstance().assertOneOrLess(Property.RECURRENCE_ID, getProperties());
760 PropertyValidator.getInstance().assertOneOrLess(Property.RESOURCES, getProperties());
761 PropertyValidator.getInstance().assertOneOrLess(Property.STATUS, getProperties());
762 PropertyValidator.getInstance().assertOneOrLess(Property.TRANSP, getProperties());
763 PropertyValidator.getInstance().assertOneOrLess(Property.URL, getProperties());
765 for (final Iterator i = getAlarms().iterator(); i.hasNext();) {
766 final VAlarm alarm = (VAlarm) i.next();
767 alarm.validate(Method.COUNTER);
768 }
769 }
770 }
772 /**
773 * METHOD:DECLINECOUNTER Validator.
774 *
775 * <pre>
776 * Component/Property Presence
777 * ------------------- ----------------------------------------------
778 * METHOD 1 MUST be "DECLINECOUNTER"
779 *
780 * VEVENT 1
781 * DTSTAMP 1
782 * ORGANIZER 1
783 * UID 1 MUST, same UID specified in original
784 * REQUEST and subsequent COUNTER
785 * COMMENT 0 or 1
786 * RECURRENCE-ID 0 or 1 MUST only if referring to an instance of a
787 * recurring calendar component. Otherwise it
788 * MUST NOT be present.
789 * REQUEST-STATUS 0+
790 * SEQUENCE 0 OR 1 MUST be present if value is greater than 0,
791 * MAY be present if 0
792 * X-PROPERTY 0+
793 * ATTACH 0
794 * ATTENDEE 0
795 * CATEGORIES 0
796 * CLASS 0
797 * CONTACT 0
798 * CREATED 0
799 * DESCRIPTION 0
800 * DTEND 0
801 * DTSTART 0
802 * DURATION 0
803 * EXDATE 0
804 * EXRULE 0
805 * GEO 0
806 * LAST-MODIFIED 0
807 * LOCATION 0
808 * PRIORITY 0
809 * RDATE 0
810 * RELATED-TO 0
811 * RESOURCES 0
812 * RRULE 0
813 * STATUS 0
814 * SUMMARY 0
815 * TRANSP 0
816 * URL 0
817 *
818 * X-COMPONENT 0+
819 * VTODO 0
820 * VJOURNAL 0
821 * VFREEBUSY 0
822 * VTIMEZONE 0
823 * VALARM 0
824 * </pre>
825 *
826 */
827 private class DeclineCounterValidator implements Validator {
829 private static final long serialVersionUID = 1L;
831 public void validate() throws ValidationException {
832 PropertyValidator.getInstance().assertOne(Property.DTSTAMP, getProperties());
833 PropertyValidator.getInstance().assertOne(Property.ORGANIZER, getProperties());
834 PropertyValidator.getInstance().assertOne(Property.UID, getProperties());
836 PropertyValidator.getInstance().assertOneOrLess(Property.COMMENT, getProperties());
837 PropertyValidator.getInstance().assertOneOrLess(Property.RECURRENCE_ID, getProperties());
838 PropertyValidator.getInstance().assertOneOrLess(Property.SEQUENCE, getProperties());
840 PropertyValidator.getInstance().assertNone(Property.ATTACH, getProperties());
841 PropertyValidator.getInstance().assertNone(Property.ATTENDEE, getProperties());
842 PropertyValidator.getInstance().assertNone(Property.CATEGORIES, getProperties());
843 PropertyValidator.getInstance().assertNone(Property.CLASS, getProperties());
844 PropertyValidator.getInstance().assertNone(Property.CONTACT, getProperties());
845 PropertyValidator.getInstance().assertNone(Property.CREATED, getProperties());
846 PropertyValidator.getInstance().assertNone(Property.DESCRIPTION, getProperties());
847 PropertyValidator.getInstance().assertNone(Property.DTEND, getProperties());
848 PropertyValidator.getInstance().assertNone(Property.DTSTART, getProperties());
849 PropertyValidator.getInstance().assertNone(Property.DURATION, getProperties());
850 PropertyValidator.getInstance().assertNone(Property.EXDATE, getProperties());
851 PropertyValidator.getInstance().assertNone(Property.EXRULE, getProperties());
852 PropertyValidator.getInstance().assertNone(Property.GEO, getProperties());
853 PropertyValidator.getInstance().assertNone(Property.LAST_MODIFIED, getProperties());
854 PropertyValidator.getInstance().assertNone(Property.LOCATION, getProperties());
855 PropertyValidator.getInstance().assertNone(Property.PRIORITY, getProperties());
856 PropertyValidator.getInstance().assertNone(Property.RDATE, getProperties());
857 PropertyValidator.getInstance().assertNone(Property.RELATED_TO, getProperties());
858 PropertyValidator.getInstance().assertNone(Property.RESOURCES, getProperties());
859 PropertyValidator.getInstance().assertNone(Property.RRULE, getProperties());
860 PropertyValidator.getInstance().assertNone(Property.STATUS, getProperties());
861 PropertyValidator.getInstance().assertNone(Property.SUMMARY, getProperties());
862 PropertyValidator.getInstance().assertNone(Property.TRANSP, getProperties());
863 PropertyValidator.getInstance().assertNone(Property.URL, getProperties());
865 ComponentValidator.assertNone(Component.VALARM, getAlarms());
866 }
867 }
869 /**
870 * METHOD:PUBLISH Validator.
871 *
872 * <pre>
873 * Component/Property Presence
874 * ------------------- ----------------------------------------------
875 * METHOD 1 MUST equal "PUBLISH"
876 * VEVENT 1+
877 * DTSTAMP 1
878 * DTSTART 1
879 * ORGANIZER 1
880 * SUMMARY 1 Can be null.
881 * UID 1
882 * RECURRENCE-ID 0 or 1 only if referring to an instance of a
883 * recurring calendar component. Otherwise
884 * it MUST NOT be present.
885 * SEQUENCE 0 or 1 MUST be present if value is greater than
886 * 0, MAY be present if 0
887 * ATTACH 0+
888 * CATEGORIES 0 or 1 This property may contain a list of
889 * values
890 * CLASS 0 or 1
891 * COMMENT 0 or 1
892 * CONTACT 0+
893 * CREATED 0 or 1
894 * DESCRIPTION 0 or 1 Can be null
895 * DTEND 0 or 1 if present DURATION MUST NOT be present
896 * DURATION 0 or 1 if present DTEND MUST NOT be present
897 * EXDATE 0+
898 * EXRULE 0+
899 * GEO 0 or 1
900 * LAST-MODIFIED 0 or 1
901 * LOCATION 0 or 1
902 * PRIORITY 0 or 1
903 * RDATE 0+
904 * RELATED-TO 0+
905 * RESOURCES 0 or 1 This property MAY contain a list of values
906 * RRULE 0+
907 * STATUS 0 or 1 MAY be one of TENTATIVE/CONFIRMED/CANCELLED
908 * TRANSP 0 or 1
909 * URL 0 or 1
910 * X-PROPERTY 0+
911 *
912 * ATTENDEE 0
913 * REQUEST-STATUS 0
914 *
915 * VALARM 0+
916 * VFREEBUSY 0
917 * VJOURNAL 0
918 * VTODO 0
919 * VTIMEZONE 0+ MUST be present if any date/time refers to
920 * a timezone
921 * X-COMPONENT 0+
922 * </pre>
923 *
924 */
925 private class PublishValidator implements Validator {
927 private static final long serialVersionUID = 1L;
929 public void validate() throws ValidationException {
930 PropertyValidator.getInstance().assertOne(Property.DTSTAMP, getProperties());
931 PropertyValidator.getInstance().assertOne(Property.DTSTART, getProperties());
933 if (!CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_RELAXED_VALIDATION)) {
934 PropertyValidator.getInstance().assertOne(Property.ORGANIZER, getProperties());
935 PropertyValidator.getInstance().assertOne(Property.SUMMARY, getProperties());
936 }
938 PropertyValidator.getInstance().assertOne(Property.UID, getProperties());
940 PropertyValidator.getInstance().assertOneOrLess(Property.RECURRENCE_ID, getProperties());
941 PropertyValidator.getInstance().assertOneOrLess(Property.SEQUENCE, getProperties());
942 PropertyValidator.getInstance().assertOneOrLess(Property.CATEGORIES, getProperties());
943 PropertyValidator.getInstance().assertOneOrLess(Property.CLASS, getProperties());
944 PropertyValidator.getInstance().assertOneOrLess(Property.COMMENT, getProperties());
945 PropertyValidator.getInstance().assertOneOrLess(Property.CREATED, getProperties());
946 PropertyValidator.getInstance().assertOneOrLess(Property.DESCRIPTION, getProperties());
947 PropertyValidator.getInstance().assertOneOrLess(Property.DTEND, getProperties());
948 PropertyValidator.getInstance().assertOneOrLess(Property.DURATION, getProperties());
949 PropertyValidator.getInstance().assertOneOrLess(Property.GEO, getProperties());
950 PropertyValidator.getInstance().assertOneOrLess(Property.LAST_MODIFIED, getProperties());
951 PropertyValidator.getInstance().assertOneOrLess(Property.LOCATION, getProperties());
952 PropertyValidator.getInstance().assertOneOrLess(Property.PRIORITY, getProperties());
953 PropertyValidator.getInstance().assertOneOrLess(Property.RESOURCES, getProperties());
954 PropertyValidator.getInstance().assertOneOrLess(Property.STATUS, getProperties());
955 PropertyValidator.getInstance().assertOneOrLess(Property.TRANSP, getProperties());
956 PropertyValidator.getInstance().assertOneOrLess(Property.URL, getProperties());
958 if (!CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_RELAXED_VALIDATION)) {
959 PropertyValidator.getInstance().assertNone(Property.ATTENDEE, getProperties());
960 }
962 PropertyValidator.getInstance().assertNone(Property.REQUEST_STATUS, getProperties());
964 for (final Iterator i = getAlarms().iterator(); i.hasNext();) {
965 final VAlarm alarm = (VAlarm) i.next();
966 alarm.validate(Method.PUBLISH);
967 }
968 }
969 }
971 /**
972 * METHOD:REFRESH Validator.
973 *
974 * <pre>
975 * Component/Property Presence
976 * ------------------- ----------------------------------------------
977 * METHOD 1 MUST be "REFRESH"
978 *
979 * VEVENT 1
980 * ATTENDEE 1 MUST be the address of requestor
981 * DTSTAMP 1
982 * ORGANIZER 1
983 * UID 1 MUST be the UID associated with original
984 * REQUEST
985 * COMMENT 0 or 1
986 * RECURRENCE-ID 0 or 1 MUST only if referring to an instance of a
987 * recurring calendar component. Otherwise
988 * it must NOT be present.
989 * X-PROPERTY 0+
990 *
991 * ATTACH 0
992 * CATEGORIES 0
993 * CLASS 0
994 * CONTACT 0
995 * CREATED 0
996 * DESCRIPTION 0
997 * DTEND 0
998 * DTSTART 0
999 * DURATION 0
1000 * EXDATE 0
1001 * EXRULE 0
1002 * GEO 0
1003 * LAST-MODIFIED 0
1004 * LOCATION 0
1005 * PRIORITY 0
1006 * RDATE 0
1007 * RELATED-TO 0
1008 * REQUEST-STATUS 0
1009 * RESOURCES 0
1010 * RRULE 0
1011 * SEQUENCE 0
1012 * STATUS 0
1013 * SUMMARY 0
1014 * TRANSP 0
1015 * URL 0
1016 *
1017 * X-COMPONENT 0+
1018 *
1019 * VTODO 0
1020 * VJOURNAL 0
1021 * VFREEBUSY 0
1022 * VTIMEZONE 0
1023 * VALARM 0
1024 * </pre>
1025 *
1026 */
1027 private class RefreshValidator implements Validator {
1029 private static final long serialVersionUID = 1L;
1031 public void validate() throws ValidationException {
1032 PropertyValidator.getInstance().assertOne(Property.ATTENDEE, getProperties());
1033 PropertyValidator.getInstance().assertOne(Property.DTSTAMP, getProperties());
1034 PropertyValidator.getInstance().assertOne(Property.ORGANIZER, getProperties());
1035 PropertyValidator.getInstance().assertOne(Property.UID, getProperties());
1037 PropertyValidator.getInstance().assertOneOrLess(Property.COMMENT, getProperties());
1038 PropertyValidator.getInstance().assertOneOrLess(Property.RECURRENCE_ID, getProperties());
1040 PropertyValidator.getInstance().assertNone(Property.ATTACH, getProperties());
1041 PropertyValidator.getInstance().assertNone(Property.CATEGORIES, getProperties());
1042 PropertyValidator.getInstance().assertNone(Property.CLASS, getProperties());
1043 PropertyValidator.getInstance().assertNone(Property.CONTACT, getProperties());
1044 PropertyValidator.getInstance().assertNone(Property.CREATED, getProperties());
1045 PropertyValidator.getInstance().assertNone(Property.DESCRIPTION, getProperties());
1046 PropertyValidator.getInstance().assertNone(Property.DTEND, getProperties());
1047 PropertyValidator.getInstance().assertNone(Property.DTSTART, getProperties());
1048 PropertyValidator.getInstance().assertNone(Property.DURATION, getProperties());
1049 PropertyValidator.getInstance().assertNone(Property.EXDATE, getProperties());
1050 PropertyValidator.getInstance().assertNone(Property.EXRULE, getProperties());
1051 PropertyValidator.getInstance().assertNone(Property.GEO, getProperties());
1052 PropertyValidator.getInstance().assertNone(Property.LAST_MODIFIED, getProperties());
1053 PropertyValidator.getInstance().assertNone(Property.LOCATION, getProperties());
1054 PropertyValidator.getInstance().assertNone(Property.PRIORITY, getProperties());
1055 PropertyValidator.getInstance().assertNone(Property.RDATE, getProperties());
1056 PropertyValidator.getInstance().assertNone(Property.RELATED_TO, getProperties());
1057 PropertyValidator.getInstance().assertNone(Property.REQUEST_STATUS, getProperties());
1058 PropertyValidator.getInstance().assertNone(Property.RESOURCES, getProperties());
1059 PropertyValidator.getInstance().assertNone(Property.RRULE, getProperties());
1060 PropertyValidator.getInstance().assertNone(Property.SEQUENCE, getProperties());
1061 PropertyValidator.getInstance().assertNone(Property.STATUS, getProperties());
1062 PropertyValidator.getInstance().assertNone(Property.SUMMARY, getProperties());
1063 PropertyValidator.getInstance().assertNone(Property.TRANSP, getProperties());
1064 PropertyValidator.getInstance().assertNone(Property.URL, getProperties());
1066 ComponentValidator.assertNone(Component.VALARM, getAlarms());
1067 }
1068 }
1070 /**
1071 * METHOD:REPLY Validator.
1072 *
1073 * <pre>
1074 * Component/Property Presence
1075 * ------------------- ----------------------------------------------
1076 * METHOD 1 MUST be "REPLY"
1077 * VEVENT 1+ All components MUST have the same UID
1078 * ATTENDEE 1 MUST be the address of the Attendee
1079 * replying.
1080 * DTSTAMP 1
1081 * ORGANIZER 1
1082 * RECURRENCE-ID 0 or 1 only if referring to an instance of a
1083 * recurring calendar component. Otherwise
1084 * it must NOT be present.
1085 * UID 1 MUST be the UID of the original REQUEST
1086 *
1087 * SEQUENCE 0 or 1 MUST if non-zero, MUST be the sequence
1088 * number of the original REQUEST. MAY be
1089 * present if 0.
1090 *
1091 * ATTACH 0+
1092 * CATEGORIES 0 or 1 This property may contain a list of values
1093 * CLASS 0 or 1
1094 * COMMENT 0 or 1
1095 * CONTACT 0+
1096 * CREATED 0 or 1
1097 * DESCRIPTION 0 or 1
1098 * DTEND 0 or 1 if present DURATION MUST NOT be present
1099 * DTSTART 0 or 1
1100 * DURATION 0 or 1 if present DTEND MUST NOT be present
1101 * EXDATE 0+
1102 * EXRULE 0+
1103 * GEO 0 or 1
1104 * LAST-MODIFIED 0 or 1
1105 * LOCATION 0 or 1
1106 * PRIORITY 0 or 1
1107 * RDATE 0+
1108 * RELATED-TO 0+
1109 * RESOURCES 0 or 1 This property MAY contain a list of values
1110 * REQUEST-STATUS 0+
1111 * RRULE 0+
1112 * STATUS 0 or 1
1113 * SUMMARY 0 or 1
1114 * TRANSP 0 or 1
1115 * URL 0 or 1
1116 * X-PROPERTY 0+
1117 *
1118 * VTIMEZONE 0 or 1 MUST be present if any date/time refers
1119 * to a timezone
1120 * X-COMPONENT 0+
1121 *
1122 * VALARM 0
1123 * VFREEBUSY 0
1124 * VJOURNAL 0
1125 * VTODO 0
1126 * </pre>
1127 *
1128 */
1129 private class ReplyValidator implements Validator {
1131 private static final long serialVersionUID = 1L;
1133 public void validate() throws ValidationException {
1134 PropertyValidator.getInstance().assertOne(Property.ATTENDEE, getProperties());
1135 PropertyValidator.getInstance().assertOne(Property.DTSTAMP, getProperties());
1136 PropertyValidator.getInstance().assertOne(Property.ORGANIZER, getProperties());
1137 PropertyValidator.getInstance().assertOne(Property.UID, getProperties());
1139 PropertyValidator.getInstance().assertOneOrLess(Property.RECURRENCE_ID, getProperties());
1140 PropertyValidator.getInstance().assertOneOrLess(Property.SEQUENCE, getProperties());
1141 PropertyValidator.getInstance().assertOneOrLess(Property.CATEGORIES, getProperties());
1142 PropertyValidator.getInstance().assertOneOrLess(Property.CLASS, getProperties());
1143 PropertyValidator.getInstance().assertOneOrLess(Property.COMMENT, getProperties());
1144 PropertyValidator.getInstance().assertOneOrLess(Property.CREATED, getProperties());
1145 PropertyValidator.getInstance().assertOneOrLess(Property.DESCRIPTION, getProperties());
1146 PropertyValidator.getInstance().assertOneOrLess(Property.DTEND, getProperties());
1147 PropertyValidator.getInstance().assertOneOrLess(Property.DTSTART, getProperties());
1148 PropertyValidator.getInstance().assertOneOrLess(Property.DURATION, getProperties());
1149 PropertyValidator.getInstance().assertOneOrLess(Property.GEO, getProperties());
1150 PropertyValidator.getInstance().assertOneOrLess(Property.LAST_MODIFIED, getProperties());
1151 PropertyValidator.getInstance().assertOneOrLess(Property.LOCATION, getProperties());
1152 PropertyValidator.getInstance().assertOneOrLess(Property.PRIORITY, getProperties());
1153 PropertyValidator.getInstance().assertOneOrLess(Property.RESOURCES, getProperties());
1154 PropertyValidator.getInstance().assertOneOrLess(Property.STATUS, getProperties());
1155 PropertyValidator.getInstance().assertOneOrLess(Property.SUMMARY, getProperties());
1156 PropertyValidator.getInstance().assertOneOrLess(Property.TRANSP, getProperties());
1157 PropertyValidator.getInstance().assertOneOrLess(Property.URL, getProperties());
1159 ComponentValidator.assertNone(Component.VALARM, getAlarms());
1160 }
1161 }
1163 /**
1164 * METHOD:REQUEST Validator.
1165 *
1166 * <pre>
1167 * Component/Property Presence
1168 * -----------------------------------------------------------------
1169 * METHOD 1 MUST be "REQUEST"
1170 * VEVENT 1+ All components MUST have the same UID
1171 * ATTENDEE 1+
1172 * DTSTAMP 1
1173 * DTSTART 1
1174 * ORGANIZER 1
1175 * SEQUENCE 0 or 1 MUST be present if value is greater than 0,
1176 * MAY be present if 0
1177 * SUMMARY 1 Can be null
1178 * UID 1
1179 *
1180 * ATTACH 0+
1181 * CATEGORIES 0 or 1 This property may contain a list of values
1182 * CLASS 0 or 1
1183 * COMMENT 0 or 1
1184 * CONTACT 0+
1185 * CREATED 0 or 1
1186 * DESCRIPTION 0 or 1 Can be null
1187 * DTEND 0 or 1 if present DURATION MUST NOT be present
1188 * DURATION 0 or 1 if present DTEND MUST NOT be present
1189 * EXDATE 0+
1190 * EXRULE 0+
1191 * GEO 0 or 1
1192 * LAST-MODIFIED 0 or 1
1193 * LOCATION 0 or 1
1194 * PRIORITY 0 or 1
1195 * RDATE 0+
1196 * RECURRENCE-ID 0 or 1 only if referring to an instance of a
1197 * recurring calendar component. Otherwise it
1198 * MUST NOT be present.
1199 * RELATED-TO 0+
1200 * REQUEST-STATUS 0+
1201 * RESOURCES 0 or 1 This property MAY contain a list of values
1202 * RRULE 0+
1203 * STATUS 0 or 1 MAY be one of TENTATIVE/CONFIRMED
1204 * TRANSP 0 or 1
1205 * URL 0 or 1
1206 * X-PROPERTY 0+
1207 *
1208 * VALARM 0+
1209 * VTIMEZONE 0+ MUST be present if any date/time refers to
1210 * a timezone
1211 * X-COMPONENT 0+
1212 * VFREEBUSY 0
1213 * VJOURNAL 0
1214 * VTODO 0
1215 * </pre>
1216 *
1217 */
1218 private class RequestValidator implements Validator {
1220 private static final long serialVersionUID = 1L;
1222 public void validate() throws ValidationException {
1223 if (!CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_RELAXED_VALIDATION)) {
1224 PropertyValidator.getInstance().assertOneOrMore(Property.ATTENDEE, getProperties());
1225 }
1227 PropertyValidator.getInstance().assertOne(Property.DTSTAMP, getProperties());
1228 PropertyValidator.getInstance().assertOne(Property.DTSTART, getProperties());
1229 PropertyValidator.getInstance().assertOne(Property.ORGANIZER, getProperties());
1230 PropertyValidator.getInstance().assertOne(Property.SUMMARY, getProperties());
1231 PropertyValidator.getInstance().assertOne(Property.UID, getProperties());
1233 PropertyValidator.getInstance().assertOneOrLess(Property.SEQUENCE, getProperties());
1234 PropertyValidator.getInstance().assertOneOrLess(Property.CATEGORIES, getProperties());
1235 PropertyValidator.getInstance().assertOneOrLess(Property.CLASS, getProperties());
1236 PropertyValidator.getInstance().assertOneOrLess(Property.COMMENT, getProperties());
1237 PropertyValidator.getInstance().assertOneOrLess(Property.CREATED, getProperties());
1238 PropertyValidator.getInstance().assertOneOrLess(Property.DESCRIPTION, getProperties());
1239 PropertyValidator.getInstance().assertOneOrLess(Property.DTEND, getProperties());
1240 PropertyValidator.getInstance().assertOneOrLess(Property.DURATION, getProperties());
1241 PropertyValidator.getInstance().assertOneOrLess(Property.GEO, getProperties());
1242 PropertyValidator.getInstance().assertOneOrLess(Property.LAST_MODIFIED, getProperties());
1243 PropertyValidator.getInstance().assertOneOrLess(Property.LOCATION, getProperties());
1244 PropertyValidator.getInstance().assertOneOrLess(Property.PRIORITY, getProperties());
1245 PropertyValidator.getInstance().assertOneOrLess(Property.RECURRENCE_ID, getProperties());
1246 PropertyValidator.getInstance().assertOneOrLess(Property.RESOURCES, getProperties());
1247 PropertyValidator.getInstance().assertOneOrLess(Property.STATUS, getProperties());
1248 PropertyValidator.getInstance().assertOneOrLess(Property.TRANSP, getProperties());
1249 PropertyValidator.getInstance().assertOneOrLess(Property.URL, getProperties());
1251 for (final Iterator i = getAlarms().iterator(); i.hasNext();) {
1252 final VAlarm alarm = (VAlarm) i.next();
1253 alarm.validate(Method.REQUEST);
1254 }
1255 }
1256 }
1257 /**
1258 * Returns a normalised list of periods representing the consumed time for this event.
1259 * @param rangeStart the start of a range
1260 * @param rangeEnd the end of a range
1261 * @return a normalised list of periods representing consumed time for this event
1262 * @see VEvent#getConsumedTime(Date, Date, boolean)
1263 */
1264 public final PeriodList getConsumedTime(final Date rangeStart,
1265 final Date rangeEnd) {
1266 return getConsumedTime(rangeStart, rangeEnd, true);
1267 }
1269 /**
1270 * Returns a list of periods representing the consumed time for this event in the specified range. Note that the
1271 * returned list may contain a single period for non-recurring components or multiple periods for recurring
1272 * components. If no time is consumed by this event an empty list is returned.
1273 * @param rangeStart the start of the range to check for consumed time
1274 * @param rangeEnd the end of the range to check for consumed time
1275 * @param normalise indicate whether the returned list of periods should be normalised
1276 * @return a list of periods representing consumed time for this event
1277 */
1278 public final PeriodList getConsumedTime(final Date rangeStart,
1279 final Date rangeEnd, final boolean normalise) {
1280 PeriodList periods = new PeriodList();
1281 // if component is transparent return empty list..
1282 if (!Transp.TRANSPARENT.equals(getProperty(Property.TRANSP))) {
1284 // try {
1285 periods = calculateRecurrenceSet(new Period(new DateTime(rangeStart),
1286 new DateTime(rangeEnd)));
1287 // }
1288 // catch (ValidationException ve) {
1289 // log.error("Invalid event data", ve);
1290 // return periods;
1291 // }
1293 // if periods already specified through recurrence, return..
1294 // ..also normalise before returning.
1295 if (!periods.isEmpty() && normalise) {
1296 periods = periods.normalise();
1297 }
1298 }
1300 return periods;
1301 }
1303 /**
1304 * Returns a single occurrence of a recurring event.
1305 * @param date a date on which the occurence should occur
1306 * @return a single non-recurring event instance for the specified date, or null if the event doesn't
1307 * occur on the specified date
1308 * @throws IOException where an error occurs reading data
1309 * @throws URISyntaxException where an invalid URI is encountered
1310 * @throws ParseException where an error occurs parsing data
1311 */
1312 public final VEvent getOccurrence(final Date date) throws IOException,
1313 URISyntaxException, ParseException {
1315 final PeriodList consumedTime = getConsumedTime(date, date);
1316 for (final Iterator i = consumedTime.iterator(); i.hasNext();) {
1317 final Period p = (Period) i.next();
1318 if (p.getStart().equals(date)) {
1319 final VEvent occurrence = (VEvent) this.copy();
1320 occurrence.getProperties().add(new RecurrenceId(date));
1321 return occurrence;
1322 }
1323 }
1324 return null;
1325 }
1327 /**
1328 * @return the optional access classification property for an event
1329 */
1330 public final Clazz getClassification() {
1331 return (Clazz) getProperty(Property.CLASS);
1332 }
1334 /**
1335 * @return the optional creation-time property for an event
1336 */
1337 public final Created getCreated() {
1338 return (Created) getProperty(Property.CREATED);
1339 }
1341 /**
1342 * @return the optional description property for an event
1343 */
1344 public final Description getDescription() {
1345 return (Description) getProperty(Property.DESCRIPTION);
1346 }
1348 /**
1349 * Convenience method to pull the DTSTART out of the property list.
1350 * @return The DtStart object representation of the start Date
1351 */
1352 public final DtStart getStartDate() {
1353 return (DtStart) getProperty(Property.DTSTART);
1354 }
1356 /**
1357 * @return the optional geographic position property for an event
1358 */
1359 public final Geo getGeographicPos() {
1360 return (Geo) getProperty(Property.GEO);
1361 }
1363 /**
1364 * @return the optional last-modified property for an event
1365 */
1366 public final LastModified getLastModified() {
1367 return (LastModified) getProperty(Property.LAST_MODIFIED);
1368 }
1370 /**
1371 * @return the optional location property for an event
1372 */
1373 public final Location getLocation() {
1374 return (Location) getProperty(Property.LOCATION);
1375 }
1377 /**
1378 * @return the optional organizer property for an event
1379 */
1380 public final Organizer getOrganizer() {
1381 return (Organizer) getProperty(Property.ORGANIZER);
1382 }
1384 /**
1385 * @return the optional priority property for an event
1386 */
1387 public final Priority getPriority() {
1388 return (Priority) getProperty(Property.PRIORITY);
1389 }
1391 /**
1392 * @return the optional date-stamp property
1393 */
1394 public final DtStamp getDateStamp() {
1395 return (DtStamp) getProperty(Property.DTSTAMP);
1396 }
1398 /**
1399 * @return the optional sequence number property for an event
1400 */
1401 public final Sequence getSequence() {
1402 return (Sequence) getProperty(Property.SEQUENCE);
1403 }
1405 /**
1406 * @return the optional status property for an event
1407 */
1408 public final Status getStatus() {
1409 return (Status) getProperty(Property.STATUS);
1410 }
1412 /**
1413 * @return the optional summary property for an event
1414 */
1415 public final Summary getSummary() {
1416 return (Summary) getProperty(Property.SUMMARY);
1417 }
1419 /**
1420 * @return the optional time transparency property for an event
1421 */
1422 public final Transp getTransparency() {
1423 return (Transp) getProperty(Property.TRANSP);
1424 }
1426 /**
1427 * @return the optional URL property for an event
1428 */
1429 public final Url getUrl() {
1430 return (Url) getProperty(Property.URL);
1431 }
1433 /**
1434 * @return the optional recurrence identifier property for an event
1435 */
1436 public final RecurrenceId getRecurrenceId() {
1437 return (RecurrenceId) getProperty(Property.RECURRENCE_ID);
1438 }
1440 /**
1441 * Returns the end date of this event. Where an end date is not available it will be derived from the event
1442 * duration.
1443 * @return a DtEnd instance, or null if one cannot be derived
1444 */
1445 public final DtEnd getEndDate() {
1446 return getEndDate(true);
1447 }
1449 /**
1450 * Convenience method to pull the DTEND out of the property list. If DTEND was not specified, use the DTSTART +
1451 * DURATION to calculate it.
1452 * @param deriveFromDuration specifies whether to derive an end date from the event duration where an end date is
1453 * not found
1454 * @return The end for this VEVENT.
1455 */
1456 public final DtEnd getEndDate(final boolean deriveFromDuration) {
1457 DtEnd dtEnd = (DtEnd) getProperty(Property.DTEND);
1458 // No DTEND? No problem, we'll use the DURATION.
1459 if (dtEnd == null && deriveFromDuration && getDuration() != null) {
1460 final DtStart dtStart = getStartDate();
1461 final Duration vEventDuration = getDuration();
1462 dtEnd = new DtEnd(Dates.getInstance(vEventDuration.getDuration()
1463 .getTime(dtStart.getDate()), (Value) dtStart
1464 .getParameter(Parameter.VALUE)));
1465 if (dtStart.isUtc()) {
1466 dtEnd.setUtc(true);
1467 }
1468 }
1469 return dtEnd;
1470 }
1472 /**
1473 * @return the optional Duration property
1474 */
1475 public final Duration getDuration() {
1476 return (Duration) getProperty(Property.DURATION);
1477 }
1479 /**
1480 * Returns the UID property of this component if available.
1481 * @return a Uid instance, or null if no UID property exists
1482 */
1483 public final Uid getUid() {
1484 return (Uid) getProperty(Property.UID);
1485 }
1487 /**
1488 * {@inheritDoc}
1489 */
1490 public boolean equals(final Object arg0) {
1491 if (arg0 instanceof VEvent) {
1492 return super.equals(arg0)
1493 && ObjectUtils.equals(alarms, ((VEvent) arg0).getAlarms());
1494 }
1495 return super.equals(arg0);
1496 }
1498 /**
1499 * {@inheritDoc}
1500 */
1501 public int hashCode() {
1502 return new HashCodeBuilder().append(getName()).append(getProperties())
1503 .append(getAlarms()).toHashCode();
1504 }
1506 /**
1507 * Overrides default copy method to add support for copying alarm sub-components.
1508 * @return a copy of the instance
1509 * @throws ParseException where values in the instance cannot be parsed
1510 * @throws IOException where values in the instance cannot be read
1511 * @throws URISyntaxException where an invalid URI value is encountered in the instance
1512 * @see net.fortuna.ical4j.model.Component#copy()
1513 */
1514 public Component copy() throws ParseException, IOException,
1515 URISyntaxException {
1516 final VEvent copy = (VEvent) super.copy();
1517 copy.alarms = new ComponentList(alarms);
1518 return copy;
1519 }
1520 }