Tue, 10 Feb 2015 19:58:00 +0100
Upgrade the upgraded ical4j component to use org.apache.commons.lang3.
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.lang3.ObjectUtils;
81 import org.apache.commons.lang3.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.CREATED, getProperties());
552 PropertyValidator.getInstance().assertOneOrLess(Property.DESCRIPTION, getProperties());
553 PropertyValidator.getInstance().assertOneOrLess(Property.DTEND, getProperties());
554 PropertyValidator.getInstance().assertOneOrLess(Property.DURATION, getProperties());
555 PropertyValidator.getInstance().assertOneOrLess(Property.GEO, getProperties());
556 PropertyValidator.getInstance().assertOneOrLess(Property.LAST_MODIFIED, getProperties());
557 PropertyValidator.getInstance().assertOneOrLess(Property.LOCATION, getProperties());
558 PropertyValidator.getInstance().assertOneOrLess(Property.PRIORITY, getProperties());
559 PropertyValidator.getInstance().assertOneOrLess(Property.RESOURCES, getProperties());
560 PropertyValidator.getInstance().assertOneOrLess(Property.STATUS, getProperties());
561 PropertyValidator.getInstance().assertOneOrLess(Property.TRANSP, getProperties());
562 PropertyValidator.getInstance().assertOneOrLess(Property.URL, getProperties());
564 PropertyValidator.getInstance().assertNone(Property.RECURRENCE_ID, getProperties());
565 PropertyValidator.getInstance().assertNone(Property.REQUEST_STATUS, getProperties());
567 for (final Iterator i = getAlarms().iterator(); i.hasNext();) {
568 final VAlarm alarm = (VAlarm) i.next();
569 alarm.validate(Method.ADD);
570 }
571 }
572 }
574 /**
575 * METHOD:CANCEL Validator.
576 *
577 * <pre>
578 * Component/Property Presence
579 * ------------------- ----------------------------------------------
580 * METHOD 1 MUST be "CANCEL"
581 *
582 * VEVENT 1+ All must have the same UID
583 * ATTENDEE 0+ MUST include all "Attendees" being removed
584 * the event. MUST include all "Attendees" if
585 * the entire event is cancelled.
586 * DTSTAMP 1
587 * ORGANIZER 1
588 * SEQUENCE 1
589 * UID 1 MUST be the UID of the original REQUEST
590 *
591 * COMMENT 0 or 1
592 * ATTACH 0+
593 * CATEGORIES 0 or 1 This property may contain a list of values
594 * CLASS 0 or 1
595 * CONTACT 0+
596 * CREATED 0 or 1
597 * DESCRIPTION 0 or 1
598 * DTEND 0 or 1 if present DURATION MUST NOT be present
599 * DTSTART 0 or 1
600 * DURATION 0 or 1 if present DTEND MUST NOT be present
601 * EXDATE 0+
602 * EXRULE 0+
603 * GEO 0 or 1
604 * LAST-MODIFIED 0 or 1
605 * LOCATION 0 or 1
606 * PRIORITY 0 or 1
607 * RDATE 0+
608 * RECURRENCE-ID 0 or 1 MUST be present if referring to one or
609 * more or more recurring instances.
610 * Otherwise it MUST NOT be present
611 * RELATED-TO 0+
612 * RESOURCES 0 or 1
613 * RRULE 0+
614 * STATUS 0 or 1 MUST be set to CANCELLED. If uninviting
615 * specific "Attendees" then MUST NOT be
616 * included.
617 * SUMMARY 0 or 1
618 * TRANSP 0 or 1
619 * URL 0 or 1
620 * X-PROPERTY 0+
621 * REQUEST-STATUS 0
622 *
623 * VTIMEZONE 0+ MUST be present if any date/time refers to
624 * a timezone
625 * X-COMPONENT 0+
626 *
627 * VTODO 0
628 * VJOURNAL 0
629 * VFREEBUSY 0
630 * VALARM 0
631 * </pre>
632 *
633 */
634 private class CancelValidator implements Validator {
636 private static final long serialVersionUID = 1L;
638 public final void validate() throws ValidationException {
639 PropertyValidator.getInstance().assertOne(Property.DTSTAMP, getProperties());
640 PropertyValidator.getInstance().assertOne(Property.DTSTART, getProperties());
641 PropertyValidator.getInstance().assertOne(Property.ORGANIZER, getProperties());
642 PropertyValidator.getInstance().assertOne(Property.SEQUENCE, getProperties());
643 PropertyValidator.getInstance().assertOne(Property.UID, getProperties());
645 PropertyValidator.getInstance().assertOneOrLess(Property.CATEGORIES, getProperties());
646 PropertyValidator.getInstance().assertOneOrLess(Property.CLASS, getProperties());
647 PropertyValidator.getInstance().assertOneOrLess(Property.CREATED, getProperties());
648 PropertyValidator.getInstance().assertOneOrLess(Property.DESCRIPTION, getProperties());
649 PropertyValidator.getInstance().assertOneOrLess(Property.DTEND, getProperties());
650 PropertyValidator.getInstance().assertOneOrLess(Property.DTSTART, getProperties());
651 PropertyValidator.getInstance().assertOneOrLess(Property.DURATION, getProperties());
652 PropertyValidator.getInstance().assertOneOrLess(Property.GEO, getProperties());
653 PropertyValidator.getInstance().assertOneOrLess(Property.LAST_MODIFIED, getProperties());
654 PropertyValidator.getInstance().assertOneOrLess(Property.LOCATION, getProperties());
655 PropertyValidator.getInstance().assertOneOrLess(Property.PRIORITY, getProperties());
656 PropertyValidator.getInstance().assertOneOrLess(Property.RECURRENCE_ID, getProperties());
657 PropertyValidator.getInstance().assertOneOrLess(Property.RESOURCES, getProperties());
658 PropertyValidator.getInstance().assertOneOrLess(Property.STATUS, getProperties());
659 PropertyValidator.getInstance().assertOneOrLess(Property.SUMMARY, getProperties());
660 PropertyValidator.getInstance().assertOneOrLess(Property.TRANSP, getProperties());
661 PropertyValidator.getInstance().assertOneOrLess(Property.URL, getProperties());
663 PropertyValidator.getInstance().assertNone(Property.REQUEST_STATUS, getProperties());
665 ComponentValidator.assertNone(Component.VALARM, getAlarms());
666 }
667 }
669 /**
670 * METHOD:COUNTER Validator.
671 *
672 * <pre>
673 * Component/Property Presence
674 * ------------------- ----------------------------------------------
675 * METHOD 1 MUST be "COUNTER"
676 *
677 * VEVENT 1
678 * DTSTAMP 1
679 * DTSTART 1
680 * ORGANIZER 1 MUST be the "Organizer" of the original
681 * event
682 * SEQUENCE 1 MUST be present if value is greater than 0,
683 * MAY be present if 0
684 * SUMMARY 1 Can be null
685 * UID 1 MUST be the UID associated with the REQUEST
686 * being countered
687 *
688 * ATTACH 0+
689 * ATTENDEE 0+ Can also be used to propose other
690 * "Attendees"
691 * CATEGORIES 0 or 1 This property may contain a list of values
692 * CLASS 0 or 1
693 * COMMENT 0 or 1
694 * CONTACT 0+
695 * CREATED 0 or 1
696 * DESCRIPTION 0 or 1
697 * DTEND 0 or 1 if present DURATION MUST NOT be present
698 * DURATION 0 or 1 if present DTEND MUST NOT be present
699 * EXDATE 0+
700 * EXRULE 0+
701 * GEO 0 or 1
702 * LAST-MODIFIED 0 or 1
703 * LOCATION 0 or 1
704 * PRIORITY 0 or 1
705 * RDATE 0+
706 * RECURRENCE-ID 0 or 1 MUST only if referring to an instance of a
707 * recurring calendar component. Otherwise it
708 * MUST NOT be present.
709 * RELATED-TO 0+
710 * REQUEST-STATUS 0+
711 * RESOURCES 0 or 1 This property may contain a list of values
712 * RRULE 0+
713 * STATUS 0 or 1 Value must be one of CONFIRMED/TENATIVE/
714 * CANCELLED
715 * TRANSP 0 or 1
716 * URL 0 or 1
717 * X-PROPERTY 0+
718 *
719 * VALARM 0+
720 * VTIMEZONE 0+ MUST be present if any date/time refers to
721 * a timezone
722 * X-COMPONENT 0+
723 *
724 * VTODO 0
725 * VJOURNAL 0
726 * VFREEBUSY 0
727 * </pre>
728 *
729 */
730 private class CounterValidator implements Validator {
732 private static final long serialVersionUID = 1L;
734 public void validate() throws ValidationException {
735 PropertyValidator.getInstance().assertOne(Property.DTSTAMP, getProperties());
736 PropertyValidator.getInstance().assertOne(Property.DTSTART, getProperties());
738 if (!CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_RELAXED_VALIDATION)) {
739 PropertyValidator.getInstance().assertOne(Property.ORGANIZER, getProperties());
740 }
742 PropertyValidator.getInstance().assertOne(Property.SEQUENCE, getProperties());
743 PropertyValidator.getInstance().assertOne(Property.SUMMARY, getProperties());
744 PropertyValidator.getInstance().assertOne(Property.UID, getProperties());
746 PropertyValidator.getInstance().assertOneOrLess(Property.CATEGORIES, getProperties());
747 PropertyValidator.getInstance().assertOneOrLess(Property.CLASS, getProperties());
748 PropertyValidator.getInstance().assertOneOrLess(Property.CREATED, getProperties());
749 PropertyValidator.getInstance().assertOneOrLess(Property.DESCRIPTION, getProperties());
750 PropertyValidator.getInstance().assertOneOrLess(Property.DTEND, getProperties());
751 PropertyValidator.getInstance().assertOneOrLess(Property.DURATION, getProperties());
752 PropertyValidator.getInstance().assertOneOrLess(Property.GEO, getProperties());
753 PropertyValidator.getInstance().assertOneOrLess(Property.LAST_MODIFIED, getProperties());
754 PropertyValidator.getInstance().assertOneOrLess(Property.LOCATION, getProperties());
755 PropertyValidator.getInstance().assertOneOrLess(Property.PRIORITY, getProperties());
756 PropertyValidator.getInstance().assertOneOrLess(Property.RECURRENCE_ID, getProperties());
757 PropertyValidator.getInstance().assertOneOrLess(Property.RESOURCES, getProperties());
758 PropertyValidator.getInstance().assertOneOrLess(Property.STATUS, getProperties());
759 PropertyValidator.getInstance().assertOneOrLess(Property.TRANSP, getProperties());
760 PropertyValidator.getInstance().assertOneOrLess(Property.URL, getProperties());
762 for (final Iterator i = getAlarms().iterator(); i.hasNext();) {
763 final VAlarm alarm = (VAlarm) i.next();
764 alarm.validate(Method.COUNTER);
765 }
766 }
767 }
769 /**
770 * METHOD:DECLINECOUNTER Validator.
771 *
772 * <pre>
773 * Component/Property Presence
774 * ------------------- ----------------------------------------------
775 * METHOD 1 MUST be "DECLINECOUNTER"
776 *
777 * VEVENT 1
778 * DTSTAMP 1
779 * ORGANIZER 1
780 * UID 1 MUST, same UID specified in original
781 * REQUEST and subsequent COUNTER
782 * COMMENT 0 or 1
783 * RECURRENCE-ID 0 or 1 MUST only if referring to an instance of a
784 * recurring calendar component. Otherwise it
785 * MUST NOT be present.
786 * REQUEST-STATUS 0+
787 * SEQUENCE 0 OR 1 MUST be present if value is greater than 0,
788 * MAY be present if 0
789 * X-PROPERTY 0+
790 * ATTACH 0
791 * ATTENDEE 0
792 * CATEGORIES 0
793 * CLASS 0
794 * CONTACT 0
795 * CREATED 0
796 * DESCRIPTION 0
797 * DTEND 0
798 * DTSTART 0
799 * DURATION 0
800 * EXDATE 0
801 * EXRULE 0
802 * GEO 0
803 * LAST-MODIFIED 0
804 * LOCATION 0
805 * PRIORITY 0
806 * RDATE 0
807 * RELATED-TO 0
808 * RESOURCES 0
809 * RRULE 0
810 * STATUS 0
811 * SUMMARY 0
812 * TRANSP 0
813 * URL 0
814 *
815 * X-COMPONENT 0+
816 * VTODO 0
817 * VJOURNAL 0
818 * VFREEBUSY 0
819 * VTIMEZONE 0
820 * VALARM 0
821 * </pre>
822 *
823 */
824 private class DeclineCounterValidator implements Validator {
826 private static final long serialVersionUID = 1L;
828 public void validate() throws ValidationException {
829 PropertyValidator.getInstance().assertOne(Property.DTSTAMP, getProperties());
830 PropertyValidator.getInstance().assertOne(Property.ORGANIZER, getProperties());
831 PropertyValidator.getInstance().assertOne(Property.UID, getProperties());
833 PropertyValidator.getInstance().assertOneOrLess(Property.RECURRENCE_ID, getProperties());
834 PropertyValidator.getInstance().assertOneOrLess(Property.SEQUENCE, getProperties());
836 PropertyValidator.getInstance().assertNone(Property.ATTACH, getProperties());
837 PropertyValidator.getInstance().assertNone(Property.ATTENDEE, getProperties());
838 PropertyValidator.getInstance().assertNone(Property.CATEGORIES, getProperties());
839 PropertyValidator.getInstance().assertNone(Property.CLASS, getProperties());
840 PropertyValidator.getInstance().assertNone(Property.CONTACT, getProperties());
841 PropertyValidator.getInstance().assertNone(Property.CREATED, getProperties());
842 PropertyValidator.getInstance().assertNone(Property.DESCRIPTION, getProperties());
843 PropertyValidator.getInstance().assertNone(Property.DTEND, getProperties());
844 PropertyValidator.getInstance().assertNone(Property.DTSTART, getProperties());
845 PropertyValidator.getInstance().assertNone(Property.DURATION, getProperties());
846 PropertyValidator.getInstance().assertNone(Property.EXDATE, getProperties());
847 PropertyValidator.getInstance().assertNone(Property.EXRULE, getProperties());
848 PropertyValidator.getInstance().assertNone(Property.GEO, getProperties());
849 PropertyValidator.getInstance().assertNone(Property.LAST_MODIFIED, getProperties());
850 PropertyValidator.getInstance().assertNone(Property.LOCATION, getProperties());
851 PropertyValidator.getInstance().assertNone(Property.PRIORITY, getProperties());
852 PropertyValidator.getInstance().assertNone(Property.RDATE, getProperties());
853 PropertyValidator.getInstance().assertNone(Property.RELATED_TO, getProperties());
854 PropertyValidator.getInstance().assertNone(Property.RESOURCES, getProperties());
855 PropertyValidator.getInstance().assertNone(Property.RRULE, getProperties());
856 PropertyValidator.getInstance().assertNone(Property.STATUS, getProperties());
857 PropertyValidator.getInstance().assertNone(Property.SUMMARY, getProperties());
858 PropertyValidator.getInstance().assertNone(Property.TRANSP, getProperties());
859 PropertyValidator.getInstance().assertNone(Property.URL, getProperties());
861 ComponentValidator.assertNone(Component.VALARM, getAlarms());
862 }
863 }
865 /**
866 * METHOD:PUBLISH Validator.
867 *
868 * <pre>
869 * Component/Property Presence
870 * ------------------- ----------------------------------------------
871 * METHOD 1 MUST equal "PUBLISH"
872 * VEVENT 1+
873 * DTSTAMP 1
874 * DTSTART 1
875 * ORGANIZER 1
876 * SUMMARY 1 Can be null.
877 * UID 1
878 * RECURRENCE-ID 0 or 1 only if referring to an instance of a
879 * recurring calendar component. Otherwise
880 * it MUST NOT be present.
881 * SEQUENCE 0 or 1 MUST be present if value is greater than
882 * 0, MAY be present if 0
883 * ATTACH 0+
884 * CATEGORIES 0 or 1 This property may contain a list of
885 * values
886 * CLASS 0 or 1
887 * COMMENT 0 or 1
888 * CONTACT 0+
889 * CREATED 0 or 1
890 * DESCRIPTION 0 or 1 Can be null
891 * DTEND 0 or 1 if present DURATION MUST NOT be present
892 * DURATION 0 or 1 if present DTEND MUST NOT be present
893 * EXDATE 0+
894 * EXRULE 0+
895 * GEO 0 or 1
896 * LAST-MODIFIED 0 or 1
897 * LOCATION 0 or 1
898 * PRIORITY 0 or 1
899 * RDATE 0+
900 * RELATED-TO 0+
901 * RESOURCES 0 or 1 This property MAY contain a list of values
902 * RRULE 0+
903 * STATUS 0 or 1 MAY be one of TENTATIVE/CONFIRMED/CANCELLED
904 * TRANSP 0 or 1
905 * URL 0 or 1
906 * X-PROPERTY 0+
907 *
908 * ATTENDEE 0
909 * REQUEST-STATUS 0
910 *
911 * VALARM 0+
912 * VFREEBUSY 0
913 * VJOURNAL 0
914 * VTODO 0
915 * VTIMEZONE 0+ MUST be present if any date/time refers to
916 * a timezone
917 * X-COMPONENT 0+
918 * </pre>
919 *
920 */
921 private class PublishValidator implements Validator {
923 private static final long serialVersionUID = 1L;
925 public void validate() throws ValidationException {
926 PropertyValidator.getInstance().assertOne(Property.DTSTAMP, getProperties());
927 PropertyValidator.getInstance().assertOne(Property.DTSTART, getProperties());
929 if (!CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_RELAXED_VALIDATION)) {
930 PropertyValidator.getInstance().assertOne(Property.ORGANIZER, getProperties());
931 PropertyValidator.getInstance().assertOne(Property.SUMMARY, getProperties());
932 }
934 PropertyValidator.getInstance().assertOne(Property.UID, getProperties());
936 PropertyValidator.getInstance().assertOneOrLess(Property.RECURRENCE_ID, getProperties());
937 PropertyValidator.getInstance().assertOneOrLess(Property.SEQUENCE, getProperties());
938 PropertyValidator.getInstance().assertOneOrLess(Property.CATEGORIES, getProperties());
939 PropertyValidator.getInstance().assertOneOrLess(Property.CLASS, getProperties());
940 PropertyValidator.getInstance().assertOneOrLess(Property.CREATED, getProperties());
941 PropertyValidator.getInstance().assertOneOrLess(Property.DESCRIPTION, getProperties());
942 PropertyValidator.getInstance().assertOneOrLess(Property.DTEND, getProperties());
943 PropertyValidator.getInstance().assertOneOrLess(Property.DURATION, getProperties());
944 PropertyValidator.getInstance().assertOneOrLess(Property.GEO, getProperties());
945 PropertyValidator.getInstance().assertOneOrLess(Property.LAST_MODIFIED, getProperties());
946 PropertyValidator.getInstance().assertOneOrLess(Property.LOCATION, getProperties());
947 PropertyValidator.getInstance().assertOneOrLess(Property.PRIORITY, getProperties());
948 PropertyValidator.getInstance().assertOneOrLess(Property.RESOURCES, getProperties());
949 PropertyValidator.getInstance().assertOneOrLess(Property.STATUS, getProperties());
950 PropertyValidator.getInstance().assertOneOrLess(Property.TRANSP, getProperties());
951 PropertyValidator.getInstance().assertOneOrLess(Property.URL, getProperties());
953 if (!CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_RELAXED_VALIDATION)) {
954 PropertyValidator.getInstance().assertNone(Property.ATTENDEE, getProperties());
955 }
957 PropertyValidator.getInstance().assertNone(Property.REQUEST_STATUS, getProperties());
959 for (final Iterator i = getAlarms().iterator(); i.hasNext();) {
960 final VAlarm alarm = (VAlarm) i.next();
961 alarm.validate(Method.PUBLISH);
962 }
963 }
964 }
966 /**
967 * METHOD:REFRESH Validator.
968 *
969 * <pre>
970 * Component/Property Presence
971 * ------------------- ----------------------------------------------
972 * METHOD 1 MUST be "REFRESH"
973 *
974 * VEVENT 1
975 * ATTENDEE 1 MUST be the address of requestor
976 * DTSTAMP 1
977 * ORGANIZER 1
978 * UID 1 MUST be the UID associated with original
979 * REQUEST
980 * COMMENT 0 or 1
981 * RECURRENCE-ID 0 or 1 MUST only if referring to an instance of a
982 * recurring calendar component. Otherwise
983 * it must NOT be present.
984 * X-PROPERTY 0+
985 *
986 * ATTACH 0
987 * CATEGORIES 0
988 * CLASS 0
989 * CONTACT 0
990 * CREATED 0
991 * DESCRIPTION 0
992 * DTEND 0
993 * DTSTART 0
994 * DURATION 0
995 * EXDATE 0
996 * EXRULE 0
997 * GEO 0
998 * LAST-MODIFIED 0
999 * LOCATION 0
1000 * PRIORITY 0
1001 * RDATE 0
1002 * RELATED-TO 0
1003 * REQUEST-STATUS 0
1004 * RESOURCES 0
1005 * RRULE 0
1006 * SEQUENCE 0
1007 * STATUS 0
1008 * SUMMARY 0
1009 * TRANSP 0
1010 * URL 0
1011 *
1012 * X-COMPONENT 0+
1013 *
1014 * VTODO 0
1015 * VJOURNAL 0
1016 * VFREEBUSY 0
1017 * VTIMEZONE 0
1018 * VALARM 0
1019 * </pre>
1020 *
1021 */
1022 private class RefreshValidator implements Validator {
1024 private static final long serialVersionUID = 1L;
1026 public void validate() throws ValidationException {
1027 PropertyValidator.getInstance().assertOne(Property.ATTENDEE, getProperties());
1028 PropertyValidator.getInstance().assertOne(Property.DTSTAMP, getProperties());
1029 PropertyValidator.getInstance().assertOne(Property.ORGANIZER, getProperties());
1030 PropertyValidator.getInstance().assertOne(Property.UID, getProperties());
1032 PropertyValidator.getInstance().assertOneOrLess(Property.RECURRENCE_ID, getProperties());
1034 PropertyValidator.getInstance().assertNone(Property.ATTACH, getProperties());
1035 PropertyValidator.getInstance().assertNone(Property.CATEGORIES, getProperties());
1036 PropertyValidator.getInstance().assertNone(Property.CLASS, getProperties());
1037 PropertyValidator.getInstance().assertNone(Property.CONTACT, getProperties());
1038 PropertyValidator.getInstance().assertNone(Property.CREATED, getProperties());
1039 PropertyValidator.getInstance().assertNone(Property.DESCRIPTION, getProperties());
1040 PropertyValidator.getInstance().assertNone(Property.DTEND, getProperties());
1041 PropertyValidator.getInstance().assertNone(Property.DTSTART, getProperties());
1042 PropertyValidator.getInstance().assertNone(Property.DURATION, getProperties());
1043 PropertyValidator.getInstance().assertNone(Property.EXDATE, getProperties());
1044 PropertyValidator.getInstance().assertNone(Property.EXRULE, getProperties());
1045 PropertyValidator.getInstance().assertNone(Property.GEO, getProperties());
1046 PropertyValidator.getInstance().assertNone(Property.LAST_MODIFIED, getProperties());
1047 PropertyValidator.getInstance().assertNone(Property.LOCATION, getProperties());
1048 PropertyValidator.getInstance().assertNone(Property.PRIORITY, getProperties());
1049 PropertyValidator.getInstance().assertNone(Property.RDATE, getProperties());
1050 PropertyValidator.getInstance().assertNone(Property.RELATED_TO, getProperties());
1051 PropertyValidator.getInstance().assertNone(Property.REQUEST_STATUS, getProperties());
1052 PropertyValidator.getInstance().assertNone(Property.RESOURCES, getProperties());
1053 PropertyValidator.getInstance().assertNone(Property.RRULE, getProperties());
1054 PropertyValidator.getInstance().assertNone(Property.SEQUENCE, getProperties());
1055 PropertyValidator.getInstance().assertNone(Property.STATUS, getProperties());
1056 PropertyValidator.getInstance().assertNone(Property.SUMMARY, getProperties());
1057 PropertyValidator.getInstance().assertNone(Property.TRANSP, getProperties());
1058 PropertyValidator.getInstance().assertNone(Property.URL, getProperties());
1060 ComponentValidator.assertNone(Component.VALARM, getAlarms());
1061 }
1062 }
1064 /**
1065 * METHOD:REPLY Validator.
1066 *
1067 * <pre>
1068 * Component/Property Presence
1069 * ------------------- ----------------------------------------------
1070 * METHOD 1 MUST be "REPLY"
1071 * VEVENT 1+ All components MUST have the same UID
1072 * ATTENDEE 1 MUST be the address of the Attendee
1073 * replying.
1074 * DTSTAMP 1
1075 * ORGANIZER 1
1076 * RECURRENCE-ID 0 or 1 only if referring to an instance of a
1077 * recurring calendar component. Otherwise
1078 * it must NOT be present.
1079 * UID 1 MUST be the UID of the original REQUEST
1080 *
1081 * SEQUENCE 0 or 1 MUST if non-zero, MUST be the sequence
1082 * number of the original REQUEST. MAY be
1083 * present if 0.
1084 *
1085 * ATTACH 0+
1086 * CATEGORIES 0 or 1 This property may contain a list of values
1087 * CLASS 0 or 1
1088 * COMMENT 0 or 1
1089 * CONTACT 0+
1090 * CREATED 0 or 1
1091 * DESCRIPTION 0 or 1
1092 * DTEND 0 or 1 if present DURATION MUST NOT be present
1093 * DTSTART 0 or 1
1094 * DURATION 0 or 1 if present DTEND MUST NOT be present
1095 * EXDATE 0+
1096 * EXRULE 0+
1097 * GEO 0 or 1
1098 * LAST-MODIFIED 0 or 1
1099 * LOCATION 0 or 1
1100 * PRIORITY 0 or 1
1101 * RDATE 0+
1102 * RELATED-TO 0+
1103 * RESOURCES 0 or 1 This property MAY contain a list of values
1104 * REQUEST-STATUS 0+
1105 * RRULE 0+
1106 * STATUS 0 or 1
1107 * SUMMARY 0 or 1
1108 * TRANSP 0 or 1
1109 * URL 0 or 1
1110 * X-PROPERTY 0+
1111 *
1112 * VTIMEZONE 0 or 1 MUST be present if any date/time refers
1113 * to a timezone
1114 * X-COMPONENT 0+
1115 *
1116 * VALARM 0
1117 * VFREEBUSY 0
1118 * VJOURNAL 0
1119 * VTODO 0
1120 * </pre>
1121 *
1122 */
1123 private class ReplyValidator implements Validator {
1125 private static final long serialVersionUID = 1L;
1127 public void validate() throws ValidationException {
1128 PropertyValidator.getInstance().assertOne(Property.ATTENDEE, getProperties());
1129 PropertyValidator.getInstance().assertOne(Property.DTSTAMP, getProperties());
1130 PropertyValidator.getInstance().assertOne(Property.ORGANIZER, getProperties());
1131 PropertyValidator.getInstance().assertOne(Property.UID, getProperties());
1133 PropertyValidator.getInstance().assertOneOrLess(Property.RECURRENCE_ID, getProperties());
1134 PropertyValidator.getInstance().assertOneOrLess(Property.SEQUENCE, getProperties());
1135 PropertyValidator.getInstance().assertOneOrLess(Property.CATEGORIES, getProperties());
1136 PropertyValidator.getInstance().assertOneOrLess(Property.CLASS, getProperties());
1137 PropertyValidator.getInstance().assertOneOrLess(Property.CREATED, getProperties());
1138 PropertyValidator.getInstance().assertOneOrLess(Property.DESCRIPTION, getProperties());
1139 PropertyValidator.getInstance().assertOneOrLess(Property.DTEND, getProperties());
1140 PropertyValidator.getInstance().assertOneOrLess(Property.DTSTART, getProperties());
1141 PropertyValidator.getInstance().assertOneOrLess(Property.DURATION, getProperties());
1142 PropertyValidator.getInstance().assertOneOrLess(Property.GEO, getProperties());
1143 PropertyValidator.getInstance().assertOneOrLess(Property.LAST_MODIFIED, getProperties());
1144 PropertyValidator.getInstance().assertOneOrLess(Property.LOCATION, getProperties());
1145 PropertyValidator.getInstance().assertOneOrLess(Property.PRIORITY, getProperties());
1146 PropertyValidator.getInstance().assertOneOrLess(Property.RESOURCES, getProperties());
1147 PropertyValidator.getInstance().assertOneOrLess(Property.STATUS, getProperties());
1148 PropertyValidator.getInstance().assertOneOrLess(Property.SUMMARY, getProperties());
1149 PropertyValidator.getInstance().assertOneOrLess(Property.TRANSP, getProperties());
1150 PropertyValidator.getInstance().assertOneOrLess(Property.URL, getProperties());
1152 ComponentValidator.assertNone(Component.VALARM, getAlarms());
1153 }
1154 }
1156 /**
1157 * METHOD:REQUEST Validator.
1158 *
1159 * <pre>
1160 * Component/Property Presence
1161 * -----------------------------------------------------------------
1162 * METHOD 1 MUST be "REQUEST"
1163 * VEVENT 1+ All components MUST have the same UID
1164 * ATTENDEE 1+
1165 * DTSTAMP 1
1166 * DTSTART 1
1167 * ORGANIZER 1
1168 * SEQUENCE 0 or 1 MUST be present if value is greater than 0,
1169 * MAY be present if 0
1170 * SUMMARY 1 Can be null
1171 * UID 1
1172 *
1173 * ATTACH 0+
1174 * CATEGORIES 0 or 1 This property may contain a list of values
1175 * CLASS 0 or 1
1176 * COMMENT 0 or 1
1177 * CONTACT 0+
1178 * CREATED 0 or 1
1179 * DESCRIPTION 0 or 1 Can be null
1180 * DTEND 0 or 1 if present DURATION MUST NOT be present
1181 * DURATION 0 or 1 if present DTEND MUST NOT be present
1182 * EXDATE 0+
1183 * EXRULE 0+
1184 * GEO 0 or 1
1185 * LAST-MODIFIED 0 or 1
1186 * LOCATION 0 or 1
1187 * PRIORITY 0 or 1
1188 * RDATE 0+
1189 * RECURRENCE-ID 0 or 1 only if referring to an instance of a
1190 * recurring calendar component. Otherwise it
1191 * MUST NOT be present.
1192 * RELATED-TO 0+
1193 * REQUEST-STATUS 0+
1194 * RESOURCES 0 or 1 This property MAY contain a list of values
1195 * RRULE 0+
1196 * STATUS 0 or 1 MAY be one of TENTATIVE/CONFIRMED
1197 * TRANSP 0 or 1
1198 * URL 0 or 1
1199 * X-PROPERTY 0+
1200 *
1201 * VALARM 0+
1202 * VTIMEZONE 0+ MUST be present if any date/time refers to
1203 * a timezone
1204 * X-COMPONENT 0+
1205 * VFREEBUSY 0
1206 * VJOURNAL 0
1207 * VTODO 0
1208 * </pre>
1209 *
1210 */
1211 private class RequestValidator implements Validator {
1213 private static final long serialVersionUID = 1L;
1215 public void validate() throws ValidationException {
1216 if (!CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_RELAXED_VALIDATION)) {
1217 PropertyValidator.getInstance().assertOneOrMore(Property.ATTENDEE, getProperties());
1218 }
1220 PropertyValidator.getInstance().assertOne(Property.DTSTAMP, getProperties());
1221 PropertyValidator.getInstance().assertOne(Property.DTSTART, getProperties());
1222 PropertyValidator.getInstance().assertOne(Property.ORGANIZER, getProperties());
1223 PropertyValidator.getInstance().assertOne(Property.SUMMARY, getProperties());
1224 PropertyValidator.getInstance().assertOne(Property.UID, getProperties());
1226 PropertyValidator.getInstance().assertOneOrLess(Property.SEQUENCE, getProperties());
1227 PropertyValidator.getInstance().assertOneOrLess(Property.CATEGORIES, getProperties());
1228 PropertyValidator.getInstance().assertOneOrLess(Property.CLASS, getProperties());
1229 PropertyValidator.getInstance().assertOneOrLess(Property.CREATED, getProperties());
1230 PropertyValidator.getInstance().assertOneOrLess(Property.DESCRIPTION, getProperties());
1231 PropertyValidator.getInstance().assertOneOrLess(Property.DTEND, getProperties());
1232 PropertyValidator.getInstance().assertOneOrLess(Property.DURATION, getProperties());
1233 PropertyValidator.getInstance().assertOneOrLess(Property.GEO, getProperties());
1234 PropertyValidator.getInstance().assertOneOrLess(Property.LAST_MODIFIED, getProperties());
1235 PropertyValidator.getInstance().assertOneOrLess(Property.LOCATION, getProperties());
1236 PropertyValidator.getInstance().assertOneOrLess(Property.PRIORITY, getProperties());
1237 PropertyValidator.getInstance().assertOneOrLess(Property.RECURRENCE_ID, getProperties());
1238 PropertyValidator.getInstance().assertOneOrLess(Property.RESOURCES, getProperties());
1239 PropertyValidator.getInstance().assertOneOrLess(Property.STATUS, getProperties());
1240 PropertyValidator.getInstance().assertOneOrLess(Property.TRANSP, getProperties());
1241 PropertyValidator.getInstance().assertOneOrLess(Property.URL, getProperties());
1243 for (final Iterator i = getAlarms().iterator(); i.hasNext();) {
1244 final VAlarm alarm = (VAlarm) i.next();
1245 alarm.validate(Method.REQUEST);
1246 }
1247 }
1248 }
1249 /**
1250 * Returns a normalised list of periods representing the consumed time for this event.
1251 * @param rangeStart the start of a range
1252 * @param rangeEnd the end of a range
1253 * @return a normalised list of periods representing consumed time for this event
1254 * @see VEvent#getConsumedTime(Date, Date, boolean)
1255 */
1256 public final PeriodList getConsumedTime(final Date rangeStart,
1257 final Date rangeEnd) {
1258 return getConsumedTime(rangeStart, rangeEnd, true);
1259 }
1261 /**
1262 * Returns a list of periods representing the consumed time for this event in the specified range. Note that the
1263 * returned list may contain a single period for non-recurring components or multiple periods for recurring
1264 * components. If no time is consumed by this event an empty list is returned.
1265 * @param rangeStart the start of the range to check for consumed time
1266 * @param rangeEnd the end of the range to check for consumed time
1267 * @param normalise indicate whether the returned list of periods should be normalised
1268 * @return a list of periods representing consumed time for this event
1269 */
1270 public final PeriodList getConsumedTime(final Date rangeStart,
1271 final Date rangeEnd, final boolean normalise) {
1272 PeriodList periods = new PeriodList();
1273 // if component is transparent return empty list..
1274 if (!Transp.TRANSPARENT.equals(getProperty(Property.TRANSP))) {
1276 // try {
1277 periods = calculateRecurrenceSet(new Period(new DateTime(rangeStart),
1278 new DateTime(rangeEnd)));
1279 // }
1280 // catch (ValidationException ve) {
1281 // log.error("Invalid event data", ve);
1282 // return periods;
1283 // }
1285 // if periods already specified through recurrence, return..
1286 // ..also normalise before returning.
1287 if (!periods.isEmpty() && normalise) {
1288 periods = periods.normalise();
1289 }
1290 }
1292 return periods;
1293 }
1295 /**
1296 * Returns a single occurrence of a recurring event.
1297 * @param date a date on which the occurence should occur
1298 * @return a single non-recurring event instance for the specified date, or null if the event doesn't
1299 * occur on the specified date
1300 * @throws IOException where an error occurs reading data
1301 * @throws URISyntaxException where an invalid URI is encountered
1302 * @throws ParseException where an error occurs parsing data
1303 */
1304 public final VEvent getOccurrence(final Date date) throws IOException,
1305 URISyntaxException, ParseException {
1307 final PeriodList consumedTime = getConsumedTime(date, date);
1308 for (final Iterator i = consumedTime.iterator(); i.hasNext();) {
1309 final Period p = (Period) i.next();
1310 if (p.getStart().equals(date)) {
1311 final VEvent occurrence = (VEvent) this.copy();
1312 occurrence.getProperties().add(new RecurrenceId(date));
1313 return occurrence;
1314 }
1315 }
1316 return null;
1317 }
1319 /**
1320 * @return the optional access classification property for an event
1321 */
1322 public final Clazz getClassification() {
1323 return (Clazz) getProperty(Property.CLASS);
1324 }
1326 /**
1327 * @return the optional creation-time property for an event
1328 */
1329 public final Created getCreated() {
1330 return (Created) getProperty(Property.CREATED);
1331 }
1333 /**
1334 * @return the optional description property for an event
1335 */
1336 public final Description getDescription() {
1337 return (Description) getProperty(Property.DESCRIPTION);
1338 }
1340 /**
1341 * Convenience method to pull the DTSTART out of the property list.
1342 * @return The DtStart object representation of the start Date
1343 */
1344 public final DtStart getStartDate() {
1345 return (DtStart) getProperty(Property.DTSTART);
1346 }
1348 /**
1349 * @return the optional geographic position property for an event
1350 */
1351 public final Geo getGeographicPos() {
1352 return (Geo) getProperty(Property.GEO);
1353 }
1355 /**
1356 * @return the optional last-modified property for an event
1357 */
1358 public final LastModified getLastModified() {
1359 return (LastModified) getProperty(Property.LAST_MODIFIED);
1360 }
1362 /**
1363 * @return the optional location property for an event
1364 */
1365 public final Location getLocation() {
1366 return (Location) getProperty(Property.LOCATION);
1367 }
1369 /**
1370 * @return the optional organizer property for an event
1371 */
1372 public final Organizer getOrganizer() {
1373 return (Organizer) getProperty(Property.ORGANIZER);
1374 }
1376 /**
1377 * @return the optional priority property for an event
1378 */
1379 public final Priority getPriority() {
1380 return (Priority) getProperty(Property.PRIORITY);
1381 }
1383 /**
1384 * @return the optional date-stamp property
1385 */
1386 public final DtStamp getDateStamp() {
1387 return (DtStamp) getProperty(Property.DTSTAMP);
1388 }
1390 /**
1391 * @return the optional sequence number property for an event
1392 */
1393 public final Sequence getSequence() {
1394 return (Sequence) getProperty(Property.SEQUENCE);
1395 }
1397 /**
1398 * @return the optional status property for an event
1399 */
1400 public final Status getStatus() {
1401 return (Status) getProperty(Property.STATUS);
1402 }
1404 /**
1405 * @return the optional summary property for an event
1406 */
1407 public final Summary getSummary() {
1408 return (Summary) getProperty(Property.SUMMARY);
1409 }
1411 /**
1412 * @return the optional time transparency property for an event
1413 */
1414 public final Transp getTransparency() {
1415 return (Transp) getProperty(Property.TRANSP);
1416 }
1418 /**
1419 * @return the optional URL property for an event
1420 */
1421 public final Url getUrl() {
1422 return (Url) getProperty(Property.URL);
1423 }
1425 /**
1426 * @return the optional recurrence identifier property for an event
1427 */
1428 public final RecurrenceId getRecurrenceId() {
1429 return (RecurrenceId) getProperty(Property.RECURRENCE_ID);
1430 }
1432 /**
1433 * Returns the end date of this event. Where an end date is not available it will be derived from the event
1434 * duration.
1435 * @return a DtEnd instance, or null if one cannot be derived
1436 */
1437 public final DtEnd getEndDate() {
1438 return getEndDate(true);
1439 }
1441 /**
1442 * Convenience method to pull the DTEND out of the property list. If DTEND was not specified, use the DTSTART +
1443 * DURATION to calculate it.
1444 * @param deriveFromDuration specifies whether to derive an end date from the event duration where an end date is
1445 * not found
1446 * @return The end for this VEVENT.
1447 */
1448 public final DtEnd getEndDate(final boolean deriveFromDuration) {
1449 DtEnd dtEnd = (DtEnd) getProperty(Property.DTEND);
1450 // No DTEND? No problem, we'll use the DURATION.
1451 if (dtEnd == null && deriveFromDuration && getStartDate() != null) {
1452 final DtStart dtStart = getStartDate();
1453 final Duration vEventDuration;
1454 if (getDuration() != null) {
1455 vEventDuration = getDuration();
1456 } else if (dtStart.getDate() instanceof DateTime) {
1457 // If "DTSTART" is a DATE-TIME, then the event's duration is zero (see: RFC 5545, 3.6.1 Event Component)
1458 vEventDuration = new Duration(new Dur(0, 0, 0, 0));
1459 } else {
1460 // If "DTSTART" is a DATE, then the event's duration is one day (see: RFC 5545, 3.6.1 Event Component)
1461 vEventDuration = new Duration(new Dur(1, 0, 0, 0));
1462 }
1464 dtEnd = new DtEnd(Dates.getInstance(vEventDuration.getDuration()
1465 .getTime(dtStart.getDate()), (Value) dtStart
1466 .getParameter(Parameter.VALUE)));
1467 if (dtStart.isUtc()) {
1468 dtEnd.setUtc(true);
1469 }
1470 }
1471 return dtEnd;
1472 }
1474 /**
1475 * @return the optional Duration property
1476 */
1477 public final Duration getDuration() {
1478 return (Duration) getProperty(Property.DURATION);
1479 }
1481 /**
1482 * Returns the UID property of this component if available.
1483 * @return a Uid instance, or null if no UID property exists
1484 */
1485 public final Uid getUid() {
1486 return (Uid) getProperty(Property.UID);
1487 }
1489 /**
1490 * {@inheritDoc}
1491 */
1492 public boolean equals(final Object arg0) {
1493 if (arg0 instanceof VEvent) {
1494 return super.equals(arg0)
1495 && ObjectUtils.equals(alarms, ((VEvent) arg0).getAlarms());
1496 }
1497 return super.equals(arg0);
1498 }
1500 /**
1501 * {@inheritDoc}
1502 */
1503 public int hashCode() {
1504 return new HashCodeBuilder().append(getName()).append(getProperties())
1505 .append(getAlarms()).toHashCode();
1506 }
1508 /**
1509 * Overrides default copy method to add support for copying alarm sub-components.
1510 * @return a copy of the instance
1511 * @throws ParseException where values in the instance cannot be parsed
1512 * @throws IOException where values in the instance cannot be read
1513 * @throws URISyntaxException where an invalid URI value is encountered in the instance
1514 * @see net.fortuna.ical4j.model.Component#copy()
1515 */
1516 public Component copy() throws ParseException, IOException,
1517 URISyntaxException {
1518 final VEvent copy = (VEvent) super.copy();
1519 copy.alarms = new ComponentList(alarms);
1520 return copy;
1521 }
1522 }