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