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