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