001    package biweekly.component;
002    
003    import java.util.Arrays;
004    import java.util.Date;
005    import java.util.List;
006    
007    import biweekly.Warning;
008    import biweekly.property.Attachment;
009    import biweekly.property.Attendee;
010    import biweekly.property.Categories;
011    import biweekly.property.Classification;
012    import biweekly.property.Comment;
013    import biweekly.property.Contact;
014    import biweekly.property.Created;
015    import biweekly.property.DateEnd;
016    import biweekly.property.DateStart;
017    import biweekly.property.DateTimeStamp;
018    import biweekly.property.Description;
019    import biweekly.property.DurationProperty;
020    import biweekly.property.ExceptionDates;
021    import biweekly.property.ExceptionRule;
022    import biweekly.property.Geo;
023    import biweekly.property.LastModified;
024    import biweekly.property.Location;
025    import biweekly.property.Method;
026    import biweekly.property.Organizer;
027    import biweekly.property.Priority;
028    import biweekly.property.RecurrenceDates;
029    import biweekly.property.RecurrenceId;
030    import biweekly.property.RecurrenceRule;
031    import biweekly.property.RelatedTo;
032    import biweekly.property.RequestStatus;
033    import biweekly.property.Resources;
034    import biweekly.property.Sequence;
035    import biweekly.property.Status;
036    import biweekly.property.Summary;
037    import biweekly.property.Transparency;
038    import biweekly.property.Uid;
039    import biweekly.property.Url;
040    import biweekly.util.Duration;
041    import biweekly.util.Recurrence;
042    
043    /*
044     Copyright (c) 2013, Michael Angstadt
045     All rights reserved.
046    
047     Redistribution and use in source and binary forms, with or without
048     modification, are permitted provided that the following conditions are met: 
049    
050     1. Redistributions of source code must retain the above copyright notice, this
051     list of conditions and the following disclaimer. 
052     2. Redistributions in binary form must reproduce the above copyright notice,
053     this list of conditions and the following disclaimer in the documentation
054     and/or other materials provided with the distribution. 
055    
056     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
057     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
058     WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
059     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
060     ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
061     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
062     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
063     ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
064     (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
065     SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
066     */
067    
068    /**
069     * <p>
070     * Defines a scheduled activity, such as a meeting that's two hours long.
071     * </p>
072     * <p>
073     * <b>Examples:</b>
074     * 
075     * <pre class="brush:java">
076     * VEvent event = new VEvent();
077     * Date start = ...
078     * event.setDateStart(start);
079     * Date end = ...
080     * event.setDateEnd(end);
081     * event.setSummary("Team Meeting");
082     * event.setLocation("Room 21C");
083     * event.setCreated(new Date());
084     * event.setRecurrenceRule(new Recurrence.Builder(Frequency.WEEKLY).build());
085     * </pre>
086     * 
087     * </p>
088     * @author Michael Angstadt
089     * @rfc 5545 p.52-5
090     */
091    public class VEvent extends ICalComponent {
092            /**
093             * <p>
094             * Creates a new event.
095             * </p>
096             * <p>
097             * The following properties are auto-generated on object creation. These
098             * properties <b>must</b> be present in order for the event to be valid:
099             * <ul>
100             * <li>{@link Uid} - Set to a UUID.</li>
101             * <li>{@link DateTimeStamp} - Set to the current date-time.</li>
102             * </ul>
103             * </p>
104             */
105            public VEvent() {
106                    setUid(Uid.random());
107                    setDateTimeStamp(new Date());
108            }
109    
110            /**
111             * Gets the unique identifier for this event. This component object comes
112             * populated with a UID on creation. This is a <b>required</b> property.
113             * @return the UID or null if not set
114             * @rfc 5545 p.117-8
115             */
116            public Uid getUid() {
117                    return getProperty(Uid.class);
118            }
119    
120            /**
121             * Sets the unique identifier for this event. This component object comes
122             * populated with a UID on creation. This is a <b>required</b> property.
123             * @param uid the UID or null to remove
124             * @rfc 5545 p.117-8
125             */
126            public void setUid(Uid uid) {
127                    setProperty(Uid.class, uid);
128            }
129    
130            /**
131             * Sets the unique identifier for this event. This component object comes
132             * populated with a UID on creation. This is a <b>required</b> property.
133             * @param uid the UID or null to remove
134             * @return the property that was created
135             * @rfc 5545 p.117-8
136             */
137            public Uid setUid(String uid) {
138                    Uid prop = (uid == null) ? null : new Uid(uid);
139                    setUid(prop);
140                    return prop;
141            }
142    
143            /**
144             * Gets either (a) the creation date of the iCalendar object (if the
145             * {@link Method} property is defined) or (b) the date that the event was
146             * last modified (the {@link LastModified} property also holds this
147             * information). This event object comes populated with a
148             * {@link DateTimeStamp} property that is set to the current time. This is a
149             * <b>required</b> property.
150             * @return the date time stamp or null if not set
151             * @rfc 5545 p.137-8
152             */
153            public DateTimeStamp getDateTimeStamp() {
154                    return getProperty(DateTimeStamp.class);
155            }
156    
157            /**
158             * Sets either (a) the creation date of the iCalendar object (if the
159             * {@link Method} property is defined) or (b) the date that the event was
160             * last modified (the {@link LastModified} property also holds this
161             * information). This event object comes populated with a
162             * {@link DateTimeStamp} property that is set to the current time. This is a
163             * <b>required</b> property.
164             * @param dateTimeStamp the date time stamp or null to remove
165             * @rfc 5545 p.137-8
166             */
167            public void setDateTimeStamp(DateTimeStamp dateTimeStamp) {
168                    setProperty(DateTimeStamp.class, dateTimeStamp);
169            }
170    
171            /**
172             * Sets either (a) the creation date of the iCalendar object (if the
173             * {@link Method} property is defined) or (b) the date that the event was
174             * last modified (the {@link LastModified} property also holds this
175             * information). This event object comes populated with a
176             * {@link DateTimeStamp} property that is set to the current time. This is a
177             * <b>required</b> property.
178             * @param dateTimeStamp the date time stamp or null to remove
179             * @return the property that was created
180             * @rfc 5545 p.137-8
181             */
182            public DateTimeStamp setDateTimeStamp(Date dateTimeStamp) {
183                    DateTimeStamp prop = (dateTimeStamp == null) ? null : new DateTimeStamp(dateTimeStamp);
184                    setDateTimeStamp(prop);
185                    return prop;
186            }
187    
188            /**
189             * Gets the date that the event starts.
190             * @return the start date or null if not set
191             * @rfc 5545 p.97-8
192             */
193            public DateStart getDateStart() {
194                    return getProperty(DateStart.class);
195            }
196    
197            /**
198             * Sets the date that the event starts (required if no {@link Method}
199             * property is defined).
200             * @param dateStart the start date or null to remove
201             * @rfc 5545 p.97-8
202             */
203            public void setDateStart(DateStart dateStart) {
204                    setProperty(DateStart.class, dateStart);
205            }
206    
207            /**
208             * Sets the date that the event starts (required if no {@link Method}
209             * property is defined).
210             * @param dateStart the start date or null to remove
211             * @return the property that was created
212             * @rfc 5545 p.97-8
213             */
214            public DateStart setDateStart(Date dateStart) {
215                    DateStart prop = (dateStart == null) ? null : new DateStart(dateStart);
216                    setDateStart(prop);
217                    return prop;
218            }
219    
220            /**
221             * Gets the level of sensitivity of the event data. If not specified, the
222             * data within the event should be considered "public".
223             * @return the classification level or null if not set
224             * @rfc 5545 p.82-3
225             */
226            public Classification getClassification() {
227                    return getProperty(Classification.class);
228            }
229    
230            /**
231             * Sets the level of sensitivity of the event data. If not specified, the
232             * data within the event should be considered "public".
233             * @param classification the classification level or null to remove
234             * @rfc 5545 p.82-3
235             */
236            public void setClassification(Classification classification) {
237                    setProperty(Classification.class, classification);
238            }
239    
240            /**
241             * Sets the level of sensitivity of the event data. If not specified, the
242             * data within the event should be considered "public".
243             * @param classification the classification level (e.g. "CONFIDENTIAL") or
244             * null to remove
245             * @return the property that was created
246             * @rfc 5545 p.82-3
247             */
248            public Classification setClassification(String classification) {
249                    Classification prop = (classification == null) ? null : new Classification(classification);
250                    setClassification(prop);
251                    return prop;
252            }
253    
254            /**
255             * Gets a detailed description of the event. The description should be more
256             * detailed than the one provided by the {@link Summary} property.
257             * @return the description or null if not set
258             * @rfc 5545 p.84-5
259             */
260            public Description getDescription() {
261                    return getProperty(Description.class);
262            }
263    
264            /**
265             * Sets a detailed description of the event. The description should be more
266             * detailed than the one provided by the {@link Summary} property.
267             * @param description the description or null to remove
268             * @rfc 5545 p.84-5
269             */
270            public void setDescription(Description description) {
271                    setProperty(Description.class, description);
272            }
273    
274            /**
275             * Sets a detailed description of the event. The description should be more
276             * detailed than the one provided by the {@link Summary} property.
277             * @param description the description or null to remove
278             * @return the property that was created
279             * @rfc 5545 p.84-5
280             */
281            public Description setDescription(String description) {
282                    Description prop = (description == null) ? null : new Description(description);
283                    setDescription(prop);
284                    return prop;
285            }
286    
287            /**
288             * Gets a set of geographical coordinates.
289             * @return the geographical coordinates or null if not set
290             * @rfc 5545 p.85-7
291             */
292            public Geo getGeo() {
293                    return getProperty(Geo.class);
294            }
295    
296            /**
297             * Sets a set of geographical coordinates.
298             * @param geo the geographical coordinates or null to remove
299             * @rfc 5545 p.85-7
300             */
301            public void setGeo(Geo geo) {
302                    setProperty(Geo.class, geo);
303            }
304    
305            /**
306             * Gets the physical location of the event.
307             * @return the location or null if not set
308             * @rfc 5545 p.87-8
309             */
310            public Location getLocation() {
311                    return getProperty(Location.class);
312            }
313    
314            /**
315             * Sets the physical location of the event.
316             * @param location the location or null to remove
317             * @rfc 5545 p.87-8
318             */
319            public void setLocation(Location location) {
320                    setProperty(Location.class, location);
321            }
322    
323            /**
324             * Sets the physical location of the event.
325             * @param location the location (e.g. "Room 101") or null to remove
326             * @return the property that was created
327             * @rfc 5545 p.87-8
328             */
329            public Location setLocation(String location) {
330                    Location prop = (location == null) ? null : new Location(location);
331                    setLocation(prop);
332                    return prop;
333            }
334    
335            /**
336             * Gets the priority of the event.
337             * @return the priority or null if not set
338             * @rfc 5545 p.89-90
339             */
340            public Priority getPriority() {
341                    return getProperty(Priority.class);
342            }
343    
344            /**
345             * Sets the priority of the event.
346             * @param priority the priority or null to remove
347             * @rfc 5545 p.89-90
348             */
349            public void setPriority(Priority priority) {
350                    setProperty(Priority.class, priority);
351            }
352    
353            /**
354             * Sets the priority of the event.
355             * @param priority the priority ("0" is undefined, "1" is the highest, "9"
356             * is the lowest) or null to remove
357             * @return the property that was created
358             * @rfc 5545 p.89-90
359             */
360            public Priority setPriority(Integer priority) {
361                    Priority prop = (priority == null) ? null : new Priority(priority);
362                    setPriority(prop);
363                    return prop;
364            }
365    
366            /**
367             * Gets the status of the event.
368             * @return the status or null if not set
369             * @rfc 5545 p.92-3
370             */
371            public Status getStatus() {
372                    return getProperty(Status.class);
373            }
374    
375            /**
376             * Sets the status of the event.
377             * <p>
378             * Valid event status codes are:
379             * <ul>
380             * <li>TENTATIVE</li>
381             * <li>CONFIRMED</li>
382             * <li>CANCELLED</li>
383             * </ul>
384             * </p>
385             * @param status the status or null to remove
386             * @rfc 5545 p.92-3
387             */
388            public void setStatus(Status status) {
389                    setProperty(Status.class, status);
390            }
391    
392            /**
393             * Gets the summary of the event.
394             * @return the summary or null if not set
395             * @rfc 5545 p.93-4
396             */
397            public Summary getSummary() {
398                    return getProperty(Summary.class);
399            }
400    
401            /**
402             * Sets the summary of the event.
403             * @param summary the summary or null to remove
404             * @rfc 5545 p.93-4
405             */
406            public void setSummary(Summary summary) {
407                    setProperty(Summary.class, summary);
408            }
409    
410            /**
411             * Sets the summary of the event.
412             * @param summary the summary or null to remove
413             * @return the property that was created
414             * @rfc 5545 p.93-4
415             */
416            public Summary setSummary(String summary) {
417                    Summary prop = (summary == null) ? null : new Summary(summary);
418                    setSummary(prop);
419                    return prop;
420            }
421    
422            /**
423             * Gets whether an event is visible to free/busy time searches. If the event
424             * does not have this property, it should be considered visible ("opaque").
425             * @return the transparency or null if not set
426             * @rfc 5545 p.101-2
427             */
428            public Transparency getTransparency() {
429                    return getProperty(Transparency.class);
430            }
431    
432            /**
433             * Sets whether an event is visible to free/busy time searches.
434             * @param transparency the transparency or null to remove
435             * @rfc 5545 p.101-2
436             */
437            public void setTransparency(Transparency transparency) {
438                    setProperty(Transparency.class, transparency);
439            }
440    
441            /**
442             * Sets whether an event is visible to free/busy time searches.
443             * @param transparent true to hide the event, false to make it visible it,
444             * or null to remove the property
445             * @return the property that was created
446             * @rfc 5545 p.101-2
447             */
448            public Transparency setTransparency(Boolean transparent) {
449                    Transparency prop = null;
450                    if (transparent != null) {
451                            prop = transparent ? Transparency.transparent() : Transparency.opaque();
452                    }
453                    setTransparency(prop);
454                    return prop;
455            }
456    
457            /**
458             * Gets the organizer of the event.
459             * @return the organizer or null if not set
460             * @rfc 5545 p.111-2
461             */
462            public Organizer getOrganizer() {
463                    return getProperty(Organizer.class);
464            }
465    
466            /**
467             * Sets the organizer of the event.
468             * @param organizer the organizer or null to remove
469             * @rfc 5545 p.111-2
470             */
471            public void setOrganizer(Organizer organizer) {
472                    setProperty(Organizer.class, organizer);
473            }
474    
475            /**
476             * Sets the organizer of the event.
477             * @param email the organizer's email address (e.g. "johndoe@example.com")
478             * or null to remove
479             * @return the property that was created
480             * @rfc 5545 p.111-2
481             */
482            public Organizer setOrganizer(String email) {
483                    Organizer prop = (email == null) ? null : Organizer.email(email);
484                    setOrganizer(prop);
485                    return prop;
486            }
487    
488            /**
489             * Gets the original value of the {@link DateStart} property if the event is
490             * recurring and has been modified. Used in conjunction with the {@link Uid}
491             * and {@link Sequence} properties to uniquely identify a recurrence
492             * instance.
493             * @return the recurrence ID or null if not set
494             * @rfc 5545 p.112-4
495             */
496            public RecurrenceId getRecurrenceId() {
497                    return getProperty(RecurrenceId.class);
498            }
499    
500            /**
501             * Sets the original value of the {@link DateStart} property if the event is
502             * recurring and has been modified. Used in conjunction with the {@link Uid}
503             * and {@link Sequence} properties to uniquely identify a recurrence
504             * instance.
505             * @param recurrenceId the recurrence ID or null to remove
506             * @rfc 5545 p.112-4
507             */
508            public void setRecurrenceId(RecurrenceId recurrenceId) {
509                    setProperty(RecurrenceId.class, recurrenceId);
510            }
511    
512            /**
513             * Sets the original value of the {@link DateStart} property if the event is
514             * recurring and has been modified. Used in conjunction with the {@link Uid}
515             * and {@link Sequence} properties to uniquely identify a recurrence
516             * instance.
517             * @param originalStartDate the original start date or null to remove
518             * @return the property that was created
519             * @rfc 5545 p.112-4
520             */
521            public RecurrenceId setRecurrenceId(Date originalStartDate) {
522                    RecurrenceId prop = (originalStartDate == null) ? null : new RecurrenceId(originalStartDate);
523                    setRecurrenceId(prop);
524                    return prop;
525            }
526    
527            /**
528             * Gets a URL to a resource that contains additional information about the
529             * event.
530             * @return the URL or null if not set
531             * @rfc 5545 p.116-7
532             */
533            public Url getUrl() {
534                    return getProperty(Url.class);
535            }
536    
537            /**
538             * Sets a URL to a resource that contains additional information about the
539             * event.
540             * @param url the URL or null to remove
541             * @rfc 5545 p.116-7
542             */
543            public void setUrl(Url url) {
544                    setProperty(Url.class, url);
545            }
546    
547            /**
548             * Sets a URL to a resource that contains additional information about the
549             * event.
550             * @param url the URL (e.g. "http://example.com/resource.ics") or null to
551             * remove
552             * @return the property that was created
553             * @rfc 5545 p.116-7
554             */
555            public Url setUrl(String url) {
556                    Url prop = (url == null) ? null : new Url(url);
557                    setUrl(prop);
558                    return prop;
559            }
560    
561            /**
562             * Gets how often the event repeats.
563             * @return the recurrence rule or null if not set
564             * @rfc 5545 p.122-32
565             */
566            public RecurrenceRule getRecurrenceRule() {
567                    return getProperty(RecurrenceRule.class);
568            }
569    
570            /**
571             * Sets how often the event repeats.
572             * @param recur the recurrence rule or null to remove
573             * @return the property that was created
574             * @rfc 5545 p.122-32
575             */
576            public RecurrenceRule setRecurrenceRule(Recurrence recur) {
577                    RecurrenceRule prop = (recur == null) ? null : new RecurrenceRule(recur);
578                    setRecurrenceRule(prop);
579                    return prop;
580            }
581    
582            /**
583             * Sets how often the event repeats.
584             * @param recurrenceRule the recurrence rule or null to remove
585             * @rfc 5545 p.122-32
586             */
587            public void setRecurrenceRule(RecurrenceRule recurrenceRule) {
588                    setProperty(RecurrenceRule.class, recurrenceRule);
589            }
590    
591            /**
592             * Gets the date that the event ends.
593             * @return the end date or null if not set
594             * @rfc 5545 p.95-6
595             */
596            public DateEnd getDateEnd() {
597                    return getProperty(DateEnd.class);
598            }
599    
600            /**
601             * Sets the date that the event ends. This must NOT be set if a
602             * {@link DurationProperty} is defined.
603             * @param dateEnd the end date or null to remove
604             * @rfc 5545 p.95-6
605             */
606            public void setDateEnd(DateEnd dateEnd) {
607                    setProperty(DateEnd.class, dateEnd);
608            }
609    
610            /**
611             * Sets the date that the event ends. This must NOT be set if a
612             * {@link DurationProperty} is defined.
613             * @param dateEnd the end date or null to remove
614             * @return the property that was created
615             * @rfc 5545 p.95-6
616             */
617            public DateEnd setDateEnd(Date dateEnd) {
618                    DateEnd prop = (dateEnd == null) ? null : new DateEnd(dateEnd);
619                    setDateEnd(prop);
620                    return prop;
621            }
622    
623            /**
624             * Gets the duration of the event.
625             * @return the duration or null if not set
626             * @rfc 5545 p.99
627             */
628            public DurationProperty getDuration() {
629                    return getProperty(DurationProperty.class);
630            }
631    
632            /**
633             * Sets the duration of the event. This must NOT be set if a {@link DateEnd}
634             * is defined.
635             * @param duration the duration or null to remove
636             * @rfc 5545 p.99
637             */
638            public void setDuration(DurationProperty duration) {
639                    setProperty(DurationProperty.class, duration);
640            }
641    
642            /**
643             * Sets the duration of the event. This must NOT be set if a {@link DateEnd}
644             * is defined.
645             * @param duration the duration or null to remove
646             * @return the property that was created
647             * @rfc 5545 p.99
648             */
649            public DurationProperty setDuration(Duration duration) {
650                    DurationProperty prop = (duration == null) ? null : new DurationProperty(duration);
651                    setDuration(prop);
652                    return prop;
653            }
654    
655            /**
656             * Gets the date-time that the event was initially created.
657             * @return the creation date-time or null if not set
658             * @rfc 5545 p.136
659             */
660            public Created getCreated() {
661                    return getProperty(Created.class);
662            }
663    
664            /**
665             * Sets the date-time that the event was initially created.
666             * @param created the creation date-time or null to remove
667             * @rfc 5545 p.136
668             */
669            public void setCreated(Created created) {
670                    setProperty(Created.class, created);
671            }
672    
673            /**
674             * Sets the date-time that the event was initially created.
675             * @param created the creation date-time or null to remove
676             * @return the property that was created
677             * @rfc 5545 p.136
678             */
679            public Created setCreated(Date created) {
680                    Created prop = (created == null) ? null : new Created(created);
681                    setCreated(prop);
682                    return prop;
683            }
684    
685            /**
686             * Gets the date-time that the event was last changed.
687             * @return the last modified date or null if not set
688             * @rfc 5545 p.138
689             */
690            public LastModified getLastModified() {
691                    return getProperty(LastModified.class);
692            }
693    
694            /**
695             * Sets the date-time that event was last changed.
696             * @param lastModified the last modified date or null to remove
697             * @rfc 5545 p.138
698             */
699            public void setLastModified(LastModified lastModified) {
700                    setProperty(LastModified.class, lastModified);
701            }
702    
703            /**
704             * Sets the date-time that the event was last changed.
705             * @param lastModified the last modified date or null to remove
706             * @return the property that was created
707             * @rfc 5545 p.138
708             */
709            public LastModified setLastModified(Date lastModified) {
710                    LastModified prop = (lastModified == null) ? null : new LastModified(lastModified);
711                    setLastModified(prop);
712                    return prop;
713            }
714    
715            /**
716             * Gets the revision number of the event. The organizer can increment this
717             * number every time he or she makes a significant change.
718             * @return the sequence number
719             * @rfc 5545 p.138-9
720             */
721            public Sequence getSequence() {
722                    return getProperty(Sequence.class);
723            }
724    
725            /**
726             * Sets the revision number of the event. The organizer can increment this
727             * number every time he or she makes a significant change.
728             * @param sequence the sequence number
729             * @rfc 5545 p.138-9
730             */
731            public void setSequence(Sequence sequence) {
732                    setProperty(Sequence.class, sequence);
733            }
734    
735            /**
736             * Sets the revision number of the event. The organizer can increment this
737             * number every time he or she makes a significant change.
738             * @param sequence the sequence number
739             * @return the property that was created
740             * @rfc 5545 p.138-9
741             */
742            public Sequence setSequence(Integer sequence) {
743                    Sequence prop = (sequence == null) ? null : new Sequence(sequence);
744                    setSequence(prop);
745                    return prop;
746            }
747    
748            /**
749             * Increments the revision number of the event. The organizer can increment
750             * this number every time he or she makes a significant change.
751             * @rfc 5545 p.138-9
752             */
753            public void incrementSequence() {
754                    Sequence sequence = getSequence();
755                    if (sequence == null) {
756                            setSequence(1);
757                    } else {
758                            sequence.increment();
759                    }
760            }
761    
762            /**
763             * Gets any attachments that are associated with the event.
764             * @return the attachments
765             * @rfc 5545 p.80-1
766             */
767            public List<Attachment> getAttachments() {
768                    return getProperties(Attachment.class);
769            }
770    
771            /**
772             * Adds an attachment to the event.
773             * @param attachment the attachment to add
774             * @rfc 5545 p.80-1
775             */
776            public void addAttachment(Attachment attachment) {
777                    addProperty(attachment);
778            }
779    
780            /**
781             * Gets the people who are attending the event.
782             * @return the attendees
783             * @rfc 5545 p.107-9
784             */
785            public List<Attendee> getAttendees() {
786                    return getProperties(Attendee.class);
787            }
788    
789            /**
790             * Adds a person who is attending the event.
791             * @param attendee the attendee
792             * @rfc 5545 p.107-9
793             */
794            public void addAttendee(Attendee attendee) {
795                    addProperty(attendee);
796            }
797    
798            /**
799             * Adds a person who is attending the event.
800             * @param email the attendee's email address
801             * @return the property that was created
802             * @rfc 5545 p.107-9
803             */
804            public Attendee addAttendee(String email) {
805                    Attendee prop = Attendee.email(email);
806                    addAttendee(prop);
807                    return prop;
808            }
809    
810            /**
811             * Gets a list of "tags" or "keywords" that describe the event.
812             * @return the categories
813             * @rfc 5545 p.81-2
814             */
815            public List<Categories> getCategories() {
816                    return getProperties(Categories.class);
817            }
818    
819            /**
820             * Adds a list of "tags" or "keywords" that describe the event. Note that a
821             * single property can hold multiple keywords.
822             * @param categories the categories to add
823             * @rfc 5545 p.81-2
824             */
825            public void addCategories(Categories categories) {
826                    addProperty(categories);
827            }
828    
829            /**
830             * Adds a list of "tags" or "keywords" that describe the event.
831             * @param categories the categories to add
832             * @return the property that was created
833             * @rfc 5545 p.81-2
834             */
835            public Categories addCategories(String... categories) {
836                    Categories prop = new Categories(categories);
837                    addCategories(prop);
838                    return prop;
839            }
840    
841            /**
842             * Adds a list of "tags" or "keywords" that describe the event.
843             * @param categories the categories to add
844             * @return the property that was created
845             * @rfc 5545 p.81-2
846             */
847            public Categories addCategories(List<String> categories) {
848                    Categories prop = new Categories(categories);
849                    addCategories(prop);
850                    return prop;
851            }
852    
853            /**
854             * Gets the comments attached to the event.
855             * @return the comments
856             * @rfc 5545 p.83-4
857             */
858            public List<Comment> getComments() {
859                    return getProperties(Comment.class);
860            }
861    
862            /**
863             * Adds a comment to the event.
864             * @param comment the comment to add
865             * @rfc 5545 p.83-4
866             */
867            public void addComment(Comment comment) {
868                    addProperty(comment);
869            }
870    
871            /**
872             * Adds a comment to the event.
873             * @param comment the comment to add
874             * @return the property that was created
875             * @rfc 5545 p.83-4
876             */
877            public Comment addComment(String comment) {
878                    Comment prop = new Comment(comment);
879                    addComment(prop);
880                    return prop;
881            }
882    
883            /**
884             * Gets the contacts associated with the event.
885             * @return the contacts
886             * @rfc 5545 p.109-11
887             */
888            public List<Contact> getContacts() {
889                    return getProperties(Contact.class);
890            }
891    
892            /**
893             * Adds a contact to the event.
894             * @param contact the contact
895             * @rfc 5545 p.109-11
896             */
897            public void addContact(Contact contact) {
898                    addProperty(contact);
899            }
900    
901            /**
902             * Adds a contact to the event.
903             * @param contact the contact (e.g. "ACME Co - (123) 555-1234")
904             * @return the property that was created
905             * @rfc 5545 p.109-11
906             */
907            public Contact addContact(String contact) {
908                    Contact prop = new Contact(contact);
909                    addContact(prop);
910                    return prop;
911            }
912    
913            /**
914             * Gets the list of exceptions to the recurrence rule defined in the event
915             * (if one is defined).
916             * @return the list of exceptions
917             * @rfc 5545 p.118-20
918             */
919            public List<ExceptionDates> getExceptionDates() {
920                    return getProperties(ExceptionDates.class);
921            }
922    
923            /**
924             * Adds a list of exceptions to the recurrence rule defined in the event (if
925             * one is defined). Note that this property can contain multiple dates.
926             * @param exceptionDates the list of exceptions
927             * @rfc 5545 p.118-20
928             */
929            public void addExceptionDates(ExceptionDates exceptionDates) {
930                    addProperty(exceptionDates);
931            }
932    
933            /**
934             * Gets the response to a scheduling request.
935             * @return the response
936             * @rfc 5545 p.141-3
937             */
938            public RequestStatus getRequestStatus() {
939                    return getProperty(RequestStatus.class);
940            }
941    
942            /**
943             * Sets the response to a scheduling request.
944             * @param requestStatus the response
945             * @rfc 5545 p.141-3
946             */
947            public void setRequestStatus(RequestStatus requestStatus) {
948                    setProperty(RequestStatus.class, requestStatus);
949            }
950    
951            /**
952             * Gets the components that the event is related to.
953             * @return the relationships
954             * @rfc 5545 p.115-6
955             */
956            public List<RelatedTo> getRelatedTo() {
957                    return getProperties(RelatedTo.class);
958            }
959    
960            /**
961             * Adds a component that the event is related to.
962             * @param relatedTo the relationship
963             * @rfc 5545 p.115-6
964             */
965            public void addRelatedTo(RelatedTo relatedTo) {
966                    //TODO create a method that accepts a component and make the RelatedTo property invisible to the user
967                    //@formatter:off
968                    /*
969                     * addRelation(RelationshipType relType, ICalComponent component){
970                     *   RelatedTo prop = new RelatedTo(component.getUid().getValue());
971                     *   prop.setRelationshipType(relType);
972                     *   addProperty(prop);
973                     * }
974                     */
975                    //@formatter:on
976                    addProperty(relatedTo);
977            }
978    
979            /**
980             * Adds a component that the event is related to.
981             * @param uid the UID of the other component
982             * @return the property that was created
983             * @rfc 5545 p.115-6
984             */
985            public RelatedTo addRelatedTo(String uid) {
986                    RelatedTo prop = new RelatedTo(uid);
987                    addRelatedTo(prop);
988                    return prop;
989            }
990    
991            /**
992             * Gets the resources that are needed for the event.
993             * @return the resources
994             * @rfc 5545 p.91
995             */
996            public List<Resources> getResources() {
997                    return getProperties(Resources.class);
998            }
999    
1000            /**
1001             * Adds a list of resources that are needed for the event. Note that a
1002             * single property can hold multiple resources.
1003             * @param resources the resources to add
1004             * @rfc 5545 p.91
1005             */
1006            public void addResources(Resources resources) {
1007                    addProperty(resources);
1008            }
1009    
1010            /**
1011             * Adds a list of resources that are needed for the event.
1012             * @param resources the resources to add (e.g. "easel", "projector")
1013             * @return the property that was created
1014             * @rfc 5545 p.91
1015             */
1016            public Resources addResources(String... resources) {
1017                    Resources prop = new Resources(resources);
1018                    addResources(prop);
1019                    return prop;
1020            }
1021    
1022            /**
1023             * Adds a list of resources that are needed for the event.
1024             * @param resources the resources to add (e.g. "easel", "projector")
1025             * @return the property that was created
1026             * @rfc 5545 p.91
1027             */
1028            public Resources addResources(List<String> resources) {
1029                    Resources prop = new Resources(resources);
1030                    addResources(prop);
1031                    return prop;
1032            }
1033    
1034            /**
1035             * Gets the list of dates/periods that help define the recurrence rule of
1036             * this event (if one is defined).
1037             * @return the recurrence dates
1038             * @rfc 5545 p.120-2
1039             */
1040            public List<RecurrenceDates> getRecurrenceDates() {
1041                    return getProperties(RecurrenceDates.class);
1042            }
1043    
1044            /**
1045             * Adds a list of dates/periods that help define the recurrence rule of this
1046             * event (if one is defined).
1047             * @param recurrenceDates the recurrence dates
1048             * @rfc 5545 p.120-2
1049             */
1050            public void addRecurrenceDates(RecurrenceDates recurrenceDates) {
1051                    addProperty(recurrenceDates);
1052            }
1053    
1054            /**
1055             * Gets the alarms that are assigned to this event.
1056             * @return the alarms
1057             * @rfc 5545 p.71-6
1058             */
1059            public List<VAlarm> getAlarms() {
1060                    return getComponents(VAlarm.class);
1061            }
1062    
1063            /**
1064             * Adds an alarm to this event.
1065             * @param alarm the alarm
1066             * @rfc 5545 p.71-6
1067             */
1068            public void addAlarm(VAlarm alarm) {
1069                    addComponent(alarm);
1070            }
1071    
1072            /**
1073             * <p>
1074             * Gets the exceptions for the {@link RecurrenceRule} property.
1075             * </p>
1076             * <p>
1077             * Note that this property has been removed from the latest version of the
1078             * iCal specification. Its use should be avoided.
1079             * </p>
1080             * @return the exception rules
1081             * @rfc 2445 p.114-15
1082             */
1083            public List<ExceptionRule> getExceptionRules() {
1084                    return getProperties(ExceptionRule.class);
1085            }
1086    
1087            /**
1088             * <p>
1089             * Adds an exception for the {@link RecurrenceRule} property.
1090             * </p>
1091             * <p>
1092             * Note that this property has been removed from the latest version of the
1093             * iCal specification. Its use should be avoided.
1094             * </p>
1095             * @param recur the exception rule to add
1096             * @return the property that was created
1097             * @rfc 2445 p.114-15
1098             */
1099            public ExceptionRule addExceptionRule(Recurrence recur) {
1100                    ExceptionRule prop = (recur == null) ? null : new ExceptionRule(recur);
1101                    addExceptionRule(prop);
1102                    return prop;
1103            }
1104    
1105            /**
1106             * <p>
1107             * Adds an exception for the {@link RecurrenceRule} property.
1108             * </p>
1109             * <p>
1110             * Note that this property has been removed from the latest version of the
1111             * iCal specification. Its use should be avoided.
1112             * </p>
1113             * @param exceptionRule the exception rule to add
1114             * @rfc 2445 p.114-15
1115             */
1116            public void addExceptionRule(ExceptionRule exceptionRule) {
1117                    addProperty(exceptionRule);
1118            }
1119    
1120            @SuppressWarnings("unchecked")
1121            @Override
1122            protected void validate(List<ICalComponent> components, List<Warning> warnings) {
1123                    checkRequiredCardinality(warnings, Uid.class, DateTimeStamp.class);
1124                    checkOptionalCardinality(warnings, Classification.class, Created.class, Description.class, Geo.class, LastModified.class, Location.class, Organizer.class, Priority.class, Priority.class, Status.class, Summary.class, Transparency.class, Url.class, RecurrenceId.class);
1125    
1126                    Status status = getStatus();
1127                    if (status != null && (status.isNeedsAction() || status.isCompleted() || status.isInProgress() || status.isDraft() || status.isFinal())) {
1128                            warnings.add(Warning.validate(13, status.getValue(), Arrays.asList(Status.tentative().getValue(), Status.confirmed().getValue(), Status.cancelled().getValue())));
1129                    }
1130    
1131                    DateStart dateStart = getDateStart();
1132                    DateEnd dateEnd = getDateEnd();
1133    
1134                    ICalComponent ical = components.get(0);
1135                    if (dateStart == null && ical.getProperty(Method.class) == null) {
1136                            warnings.add(Warning.validate(14));
1137                    }
1138    
1139                    if (dateEnd != null && dateStart == null) {
1140                            warnings.add(Warning.validate(15));
1141                    }
1142    
1143                    if (dateStart != null && dateEnd != null) {
1144                            Date start = dateStart.getValue();
1145                            Date end = dateEnd.getValue();
1146                            if (start != null && end != null && start.compareTo(end) > 0) {
1147                                    warnings.add(Warning.validate(16));
1148                            }
1149    
1150                            if (dateStart.hasTime() != dateEnd.hasTime()) {
1151                                    warnings.add(Warning.validate(17));
1152                            }
1153                    }
1154    
1155                    if (dateEnd != null && getDuration() != null) {
1156                            warnings.add(Warning.validate(18));
1157                    }
1158    
1159                    RecurrenceId recurrenceId = getRecurrenceId();
1160                    if (recurrenceId != null && dateStart != null && dateStart.hasTime() != recurrenceId.hasTime()) {
1161                            warnings.add(Warning.validate(19));
1162                    }
1163    
1164                    //RFC 5545 p. 167
1165                    RecurrenceRule rrule = getRecurrenceRule();
1166                    if (dateStart != null && rrule != null) {
1167                            Date start = dateStart.getValue();
1168                            Recurrence recur = rrule.getValue();
1169                            if (start != null && recur != null) {
1170                                    if (!dateStart.hasTime() && (!recur.getByHour().isEmpty() || !recur.getByMinute().isEmpty() || !recur.getBySecond().isEmpty())) {
1171                                            warnings.add(Warning.validate(5));
1172                                    }
1173                            }
1174                    }
1175    
1176                    //RFC 5545 p. 167
1177                    if (getProperties(RecurrenceRule.class).size() > 1) {
1178                            warnings.add(Warning.validate(6));
1179                    }
1180    
1181                    //TODO check for properties which shouldn't be added to VEVENTs
1182            }
1183    }