001    package biweekly.component;
002    
003    import java.util.Date;
004    import java.util.List;
005    
006    import biweekly.property.Attachment;
007    import biweekly.property.Attendee;
008    import biweekly.property.Categories;
009    import biweekly.property.Classification;
010    import biweekly.property.Comment;
011    import biweekly.property.Contact;
012    import biweekly.property.Created;
013    import biweekly.property.DateStart;
014    import biweekly.property.DateTimeStamp;
015    import biweekly.property.Description;
016    import biweekly.property.ExceptionDates;
017    import biweekly.property.ExceptionRule;
018    import biweekly.property.LastModified;
019    import biweekly.property.Method;
020    import biweekly.property.Organizer;
021    import biweekly.property.RecurrenceDates;
022    import biweekly.property.RecurrenceId;
023    import biweekly.property.RecurrenceRule;
024    import biweekly.property.RelatedTo;
025    import biweekly.property.RequestStatus;
026    import biweekly.property.Sequence;
027    import biweekly.property.Status;
028    import biweekly.property.Summary;
029    import biweekly.property.Uid;
030    import biweekly.property.Url;
031    import biweekly.util.Recurrence;
032    
033    /*
034     Copyright (c) 2013, Michael Angstadt
035     All rights reserved.
036    
037     Redistribution and use in source and binary forms, with or without
038     modification, are permitted provided that the following conditions are met: 
039    
040     1. Redistributions of source code must retain the above copyright notice, this
041     list of conditions and the following disclaimer. 
042     2. Redistributions in binary form must reproduce the above copyright notice,
043     this list of conditions and the following disclaimer in the documentation
044     and/or other materials provided with the distribution. 
045    
046     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
047     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
048     WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
049     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
050     ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
051     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
052     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
053     ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
054     (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
055     SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
056     */
057    
058    /**
059     * <p>
060     * Defines descriptive text associated with the calendar data.
061     * </p>
062     * <p>
063     * <b>Examples:</b>
064     * 
065     * <pre class="brush:java">
066     * VJournal journal = new VJournal();
067     * journal.setSummary("Team Meeting");
068     * journal.setDescription("The following items were discussed: ...");
069     * byte[] slides = ...
070     * journal.addAttachment(new Attachment("application/vnd.ms-powerpoint", slides));
071     * </pre>
072     * 
073     * </p>
074     * @author Michael Angstadt
075     * @rfc 5545 p.57-9
076     */
077    public class VJournal extends ICalComponent {
078            /**
079             * <p>
080             * Creates a new journal entry.
081             * </p>
082             * <p>
083             * The following properties are auto-generated on object creation. These
084             * properties <b>must</b> be present in order for the journal entry to be
085             * valid:
086             * <ul>
087             * <li>{@link Uid} - Set to a UUID.</li>
088             * <li>{@link DateTimeStamp} - Set to the current date-time.</li>
089             * </ul>
090             * </p>
091             */
092            public VJournal() {
093                    setUid(Uid.random());
094                    setDateTimeStamp(new Date());
095            }
096    
097            /**
098             * Gets the unique identifier for this journal entry. This component object
099             * comes populated with a UID on creation. This is a <b>required</b>
100             * property.
101             * @return the UID or null if not set
102             * @rfc 5545 p.117-8
103             */
104            public Uid getUid() {
105                    return getProperty(Uid.class);
106            }
107    
108            /**
109             * Sets the unique identifier for this journal entry. This component object
110             * comes populated with a UID on creation. This is a <b>required</b>
111             * property.
112             * @param uid the UID or null to remove
113             * @rfc 5545 p.117-8
114             */
115            public void setUid(Uid uid) {
116                    setProperty(Uid.class, uid);
117            }
118    
119            /**
120             * Sets the unique identifier for this journal entry. This component object
121             * comes populated with a UID on creation. This is a <b>required</b>
122             * property.
123             * @param uid the UID or null to remove
124             * @return the property that was created
125             * @rfc 5545 p.117-8
126             */
127            public Uid setUid(String uid) {
128                    Uid prop = (uid == null) ? null : new Uid(uid);
129                    setUid(prop);
130                    return prop;
131            }
132    
133            /**
134             * Gets either (a) the creation date of the iCalendar object (if the
135             * {@link Method} property is defined) or (b) the date that the journal
136             * entry was last modified (the {@link LastModified} property also holds
137             * this information). This journal entry object comes populated with a
138             * {@link DateTimeStamp} property that is set to the current time. This is a
139             * <b>required</b> property.
140             * @return the date time stamp or null if not set
141             * @rfc 5545 p.137-8
142             */
143            public DateTimeStamp getDateTimeStamp() {
144                    return getProperty(DateTimeStamp.class);
145            }
146    
147            /**
148             * Sets either (a) the creation date of the iCalendar object (if the
149             * {@link Method} property is defined) or (b) the date that the journal
150             * entry was last modified (the {@link LastModified} property also holds
151             * this information). This journal entry object comes populated with a
152             * {@link DateTimeStamp} property that is set to the current time. This is a
153             * <b>required</b> property.
154             * @param dateTimeStamp the date time stamp or null to remove
155             * @rfc 5545 p.137-8
156             */
157            public void setDateTimeStamp(DateTimeStamp dateTimeStamp) {
158                    setProperty(DateTimeStamp.class, dateTimeStamp);
159            }
160    
161            /**
162             * Sets either (a) the creation date of the iCalendar object (if the
163             * {@link Method} property is defined) or (b) the date that the journal
164             * entry was last modified (the {@link LastModified} property also holds
165             * this information). This journal entry object comes populated with a
166             * {@link DateTimeStamp} property that is set to the current time. This is a
167             * <b>required</b> property.
168             * @param dateTimeStamp the date time stamp or null to remove
169             * @return the property that was created
170             * @rfc 5545 p.137-8
171             */
172            public DateTimeStamp setDateTimeStamp(Date dateTimeStamp) {
173                    DateTimeStamp prop = (dateTimeStamp == null) ? null : new DateTimeStamp(dateTimeStamp);
174                    setDateTimeStamp(prop);
175                    return prop;
176            }
177    
178            /**
179             * Gets the level of sensitivity of the journal entry. If not specified, the
180             * data within the journal entry should be considered "public".
181             * @return the classification level or null if not set
182             * @rfc 5545 p.82-3
183             */
184            public Classification getClassification() {
185                    return getProperty(Classification.class);
186            }
187    
188            /**
189             * Sets the level of sensitivity of the journal entry. If not specified, the
190             * data within the journal entry should be considered "public".
191             * @param classification the classification level or null to remove
192             * @rfc 5545 p.82-3
193             */
194            public void setClassification(Classification classification) {
195                    setProperty(Classification.class, classification);
196            }
197    
198            /**
199             * Sets the level of sensitivity of the journal entry. If not specified, the
200             * data within the journal entry should be considered "public".
201             * @param classification the classification level (e.g. "CONFIDENTIAL") or
202             * null to remove
203             * @return the property that was created
204             * @rfc 5545 p.82-3
205             */
206            public Classification setClassification(String classification) {
207                    Classification prop = (classification == null) ? null : new Classification(classification);
208                    setClassification(prop);
209                    return prop;
210            }
211    
212            /**
213             * Gets the date-time that the journal entry was initially created.
214             * @return the creation date-time or null if not set
215             * @rfc 5545 p.136
216             */
217            public Created getCreated() {
218                    return getProperty(Created.class);
219            }
220    
221            /**
222             * Sets the date-time that the journal entry was initially created.
223             * @param created the creation date-time or null to remove
224             * @rfc 5545 p.136
225             */
226            public void setCreated(Created created) {
227                    setProperty(Created.class, created);
228            }
229    
230            /**
231             * Sets the date-time that the journal entry was initially created.
232             * @param created the creation date-time or null to remove
233             * @return the property that was created
234             * @rfc 5545 p.136
235             */
236            public Created setCreated(Date created) {
237                    Created prop = (created == null) ? null : new Created(created);
238                    setCreated(prop);
239                    return prop;
240            }
241    
242            /**
243             * Gets the date that the journal entry starts.
244             * @return the start date or null if not set
245             * @rfc 5545 p.97-8
246             */
247            public DateStart getDateStart() {
248                    return getProperty(DateStart.class);
249            }
250    
251            /**
252             * Sets the date that the journal entry starts.
253             * @param dateStart the start date or null to remove
254             * @rfc 5545 p.97-8
255             */
256            public void setDateStart(DateStart dateStart) {
257                    setProperty(DateStart.class, dateStart);
258            }
259    
260            /**
261             * Sets the date that the journal entry starts.
262             * @param dateStart the start date or null to remove
263             * @return the property that was created
264             * @rfc 5545 p.97-8
265             */
266            public DateStart setDateStart(Date dateStart) {
267                    DateStart prop = (dateStart == null) ? null : new DateStart(dateStart);
268                    setDateStart(prop);
269                    return prop;
270            }
271    
272            /**
273             * Gets the date-time that the journal entry was last changed.
274             * @return the last modified date or null if not set
275             * @rfc 5545 p.138
276             */
277            public LastModified getLastModified() {
278                    return getProperty(LastModified.class);
279            }
280    
281            /**
282             * Sets the date-time that the journal entry was last changed.
283             * @param lastModified the last modified date or null to remove
284             * @rfc 5545 p.138
285             */
286            public void setLastModified(LastModified lastModified) {
287                    setProperty(LastModified.class, lastModified);
288            }
289    
290            /**
291             * Sets the date-time that the journal entry was last changed.
292             * @param lastModified the last modified date or null to remove
293             * @return the property that was created
294             * @rfc 5545 p.138
295             */
296            public LastModified setLastModified(Date lastModified) {
297                    LastModified prop = (lastModified == null) ? null : new LastModified(lastModified);
298                    setLastModified(prop);
299                    return prop;
300            }
301    
302            /**
303             * Gets the organizer of the journal entry.
304             * @return the organizer or null if not set
305             * @rfc 5545 p.111-2
306             */
307            public Organizer getOrganizer() {
308                    return getProperty(Organizer.class);
309            }
310    
311            /**
312             * Sets the organizer of the journal entry.
313             * @param organizer the organizer or null to remove
314             * @rfc 5545 p.111-2
315             */
316            public void setOrganizer(Organizer organizer) {
317                    setProperty(Organizer.class, organizer);
318            }
319    
320            /**
321             * Sets the organizer of the journal entry.
322             * @param email the organizer's email address (e.g. "johndoe@example.com")
323             * or null to remove
324             * @return the property that was created
325             * @rfc 5545 p.111-2
326             */
327            public Organizer setOrganizer(String email) {
328                    Organizer prop = (email == null) ? null : Organizer.email(email);
329                    setOrganizer(prop);
330                    return prop;
331            }
332    
333            /**
334             * Gets the original value of the {@link DateStart} property if the event is
335             * recurring and has been modified. Used in conjunction with the {@link Uid}
336             * and {@link Sequence} properties to uniquely identify a recurrence
337             * instance.
338             * @return the recurrence ID or null if not set
339             * @rfc 5545 p.112-4
340             */
341            public RecurrenceId getRecurrenceId() {
342                    return getProperty(RecurrenceId.class);
343            }
344    
345            /**
346             * Sets the original value of the {@link DateStart} property if the event is
347             * recurring and has been modified. Used in conjunction with the {@link Uid}
348             * and {@link Sequence} properties to uniquely identify a recurrence
349             * instance.
350             * @param recurrenceId the recurrence ID or null to remove
351             * @rfc 5545 p.112-4
352             */
353            public void setRecurrenceId(RecurrenceId recurrenceId) {
354                    setProperty(RecurrenceId.class, recurrenceId);
355            }
356    
357            /**
358             * Sets the original value of the {@link DateStart} property if the journal
359             * entry is recurring and has been modified. Used in conjunction with the
360             * {@link Uid} and {@link Sequence} properties to uniquely identify a
361             * recurrence instance.
362             * @param originalStartDate the original start date or null to remove
363             * @return the property that was created
364             * @rfc 5545 p.112-4
365             */
366            public RecurrenceId setRecurrenceId(Date originalStartDate) {
367                    RecurrenceId prop = (originalStartDate == null) ? null : new RecurrenceId(originalStartDate);
368                    setRecurrenceId(prop);
369                    return prop;
370            }
371    
372            /**
373             * Gets the revision number of the journal entry. The organizer can
374             * increment this number every time he or she makes a significant change.
375             * @return the sequence number
376             * @rfc 5545 p.138-9
377             */
378            public Sequence getSequence() {
379                    return getProperty(Sequence.class);
380            }
381    
382            /**
383             * Sets the revision number of the journal entry. The organizer can
384             * increment this number every time he or she makes a significant change.
385             * @param sequence the sequence number
386             * @rfc 5545 p.138-9
387             */
388            public void setSequence(Sequence sequence) {
389                    setProperty(Sequence.class, sequence);
390            }
391    
392            /**
393             * Sets the revision number of the journal entry. The organizer can
394             * increment this number every time he or she makes a significant change.
395             * @param sequence the sequence number
396             * @return the property that was created
397             * @rfc 5545 p.138-9
398             */
399            public Sequence setSequence(Integer sequence) {
400                    Sequence prop = (sequence == null) ? null : new Sequence(sequence);
401                    setSequence(prop);
402                    return prop;
403            }
404    
405            /**
406             * Increments the revision number of the journal entry. The organizer can
407             * increment this number every time he or she makes a significant change.
408             * @rfc 5545 p.138-9
409             */
410            public void incrementSequence() {
411                    Sequence sequence = getSequence();
412                    if (sequence == null) {
413                            setSequence(1);
414                    } else {
415                            sequence.increment();
416                    }
417            }
418    
419            /**
420             * Gets the status of the journal entry.
421             * @return the status or null if not set
422             * @rfc 5545 p.92-3
423             */
424            public Status getStatus() {
425                    return getProperty(Status.class);
426            }
427    
428            /**
429             * Sets the status of the journal entry.
430             * <p>
431             * Valid journal status codes are:
432             * <ul>
433             * <li>DRAFT</li>
434             * <li>FINAL</li>
435             * <li>CANCELLED</li>
436             * </ul>
437             * </p>
438             * @param status the status or null to remove
439             * @rfc 5545 p.92-3
440             */
441            public void setStatus(Status status) {
442                    setProperty(Status.class, status);
443            }
444    
445            /**
446             * Gets the summary of the journal entry.
447             * @return the summary or null if not set
448             * @rfc 5545 p.93-4
449             */
450            public Summary getSummary() {
451                    return getProperty(Summary.class);
452            }
453    
454            /**
455             * Sets the summary of the journal entry.
456             * @param summary the summary or null to remove
457             * @rfc 5545 p.93-4
458             */
459            public void setSummary(Summary summary) {
460                    setProperty(Summary.class, summary);
461            }
462    
463            /**
464             * Sets the summary of the journal entry.
465             * @param summary the summary or null to remove
466             * @return the property that was created
467             * @rfc 5545 p.93-4
468             */
469            public Summary setSummary(String summary) {
470                    Summary prop = (summary == null) ? null : new Summary(summary);
471                    setSummary(prop);
472                    return prop;
473            }
474    
475            /**
476             * Gets a URL to a resource that contains additional information about the
477             * journal entry.
478             * @return the URL or null if not set
479             * @rfc 5545 p.116-7
480             */
481            public Url getUrl() {
482                    return getProperty(Url.class);
483            }
484    
485            /**
486             * Sets a URL to a resource that contains additional information about the
487             * journal entry.
488             * @param url the URL or null to remove
489             * @rfc 5545 p.116-7
490             */
491            public void setUrl(Url url) {
492                    setProperty(Url.class, url);
493            }
494    
495            /**
496             * Sets a URL to a resource that contains additional information about the
497             * journal entry.
498             * @param url the URL (e.g. "http://example.com/resource.ics") or null to
499             * remove
500             * @return the property that was created
501             * @rfc 5545 p.116-7
502             */
503            public Url setUrl(String url) {
504                    Url prop = (url == null) ? null : new Url(url);
505                    setUrl(prop);
506                    return prop;
507            }
508    
509            /**
510             * Gets how often the journal entry repeats.
511             * @return the recurrence rule or null if not set
512             * @rfc 5545 p.122-32
513             */
514            public RecurrenceRule getRecurrenceRule() {
515                    return getProperty(RecurrenceRule.class);
516            }
517    
518            /**
519             * Sets how often the journal entry repeats.
520             * @param recur the recurrence rule or null to remove
521             * @return the property that was created
522             * @rfc 5545 p.122-32
523             */
524            public RecurrenceRule setRecurrenceRule(Recurrence recur) {
525                    RecurrenceRule prop = (recur == null) ? null : new RecurrenceRule(recur);
526                    setRecurrenceRule(prop);
527                    return prop;
528            }
529    
530            /**
531             * Sets how often the journal entry repeats.
532             * @param recurrenceRule the recurrence rule or null to remove
533             * @rfc 5545 p.122-32
534             */
535            public void setRecurrenceRule(RecurrenceRule recurrenceRule) {
536                    setProperty(RecurrenceRule.class, recurrenceRule);
537            }
538    
539            /**
540             * Gets any attachments that are associated with the journal entry.
541             * @return the attachments
542             * @rfc 5545 p.80-1
543             */
544            public List<Attachment> getAttachments() {
545                    return getProperties(Attachment.class);
546            }
547    
548            /**
549             * Adds an attachment to the journal entry.
550             * @param attachment the attachment to add
551             * @rfc 5545 p.80-1
552             */
553            public void addAttachment(Attachment attachment) {
554                    addProperty(attachment);
555            }
556    
557            /**
558             * Gets the people who are involved in the journal entry.
559             * @return the attendees
560             * @rfc 5545 p.107-9
561             */
562            public List<Attendee> getAttendees() {
563                    return getProperties(Attendee.class);
564            }
565    
566            /**
567             * Adds a person who is involved in the journal entry.
568             * @param attendee the attendee
569             * @rfc 5545 p.107-9
570             */
571            public void addAttendee(Attendee attendee) {
572                    addProperty(attendee);
573            }
574    
575            /**
576             * Adds a person who is involved in the journal entry.
577             * @param email the attendee's email address
578             * @return the property that was created
579             * @rfc 5545 p.107-9
580             */
581            public Attendee addAttendee(String email) {
582                    Attendee prop = Attendee.email(email);
583                    addAttendee(prop);
584                    return prop;
585            }
586    
587            /**
588             * Gets a list of "tags" or "keywords" that describe the journal entry.
589             * @return the categories
590             * @rfc 5545 p.81-2
591             */
592            public List<Categories> getCategories() {
593                    return getProperties(Categories.class);
594            }
595    
596            /**
597             * Adds a list of "tags" or "keywords" that describe the journal entry. Note
598             * that a single property can hold multiple keywords.
599             * @param categories the categories to add
600             * @rfc 5545 p.81-2
601             */
602            public void addCategories(Categories categories) {
603                    addProperty(categories);
604            }
605    
606            /**
607             * Adds a list of "tags" or "keywords" that describe the journal entry.
608             * @param categories the categories to add
609             * @return the property that was created
610             * @rfc 5545 p.81-2
611             */
612            public Categories addCategories(String... categories) {
613                    Categories prop = new Categories(categories);
614                    addCategories(prop);
615                    return prop;
616            }
617    
618            /**
619             * Adds a list of "tags" or "keywords" that describe the journal entry.
620             * @param categories the categories to add
621             * @return the property that was created
622             * @rfc 5545 p.81-2
623             */
624            public Categories addCategories(List<String> categories) {
625                    Categories prop = new Categories(categories);
626                    addCategories(prop);
627                    return prop;
628            }
629    
630            /**
631             * Gets the comments attached to the journal entry.
632             * @return the comments
633             * @rfc 5545 p.83-4
634             */
635            public List<Comment> getComments() {
636                    return getProperties(Comment.class);
637            }
638    
639            /**
640             * Adds a comment to the journal entry.
641             * @param comment the comment to add
642             * @rfc 5545 p.83-4
643             */
644            public void addComment(Comment comment) {
645                    addProperty(comment);
646            }
647    
648            /**
649             * Adds a comment to the journal entry.
650             * @param comment the comment to add
651             * @return the property that was created
652             * @rfc 5545 p.83-4
653             */
654            public Comment addComment(String comment) {
655                    Comment prop = new Comment(comment);
656                    addComment(prop);
657                    return prop;
658            }
659    
660            /**
661             * Gets the contacts associated with the journal entry.
662             * @return the contacts
663             * @rfc 5545 p.109-11
664             */
665            public List<Contact> getContacts() {
666                    return getProperties(Contact.class);
667            }
668    
669            /**
670             * Adds a contact to the journal entry.
671             * @param contact the contact
672             * @rfc 5545 p.109-11
673             */
674            public void addContact(Contact contact) {
675                    addProperty(contact);
676            }
677    
678            /**
679             * Adds a contact to the journal entry.
680             * @param contact the contact (e.g. "ACME Co - (123) 555-1234")
681             * @return the property that was created
682             * @rfc 5545 p.109-11
683             */
684            public Contact addContact(String contact) {
685                    Contact prop = new Contact(contact);
686                    addContact(prop);
687                    return prop;
688            }
689    
690            /**
691             * Gets the detailed descriptions to the journal entry. The descriptions
692             * should be a more detailed version of the one provided by the
693             * {@link Summary} property.
694             * @return the descriptions
695             * @rfc 5545 p.84-5
696             */
697            public List<Description> getDescriptions() {
698                    return getProperties(Description.class);
699            }
700    
701            /**
702             * Adds a detailed description to the journal entry. The description should
703             * be a more detailed version of the one provided by the {@link Summary}
704             * property.
705             * @param description the description
706             * @rfc 5545 p.84-5
707             */
708            public void addDescription(Description description) {
709                    addProperty(description);
710            }
711    
712            /**
713             * Adds a detailed description to the journal entry. The description should
714             * be a more detailed version of the one provided by the {@link Summary}
715             * property.
716             * @param description the description
717             * @return the property that was created
718             * @rfc 5545 p.84-5
719             */
720            public Description addDescription(String description) {
721                    Description prop = new Description(description);
722                    addDescription(prop);
723                    return prop;
724            }
725    
726            /**
727             * Gets the list of exceptions to the recurrence rule defined in the journal
728             * entry (if one is defined).
729             * @return the list of exceptions
730             * @rfc 5545 p.118-20
731             */
732            public List<ExceptionDates> getExceptionDates() {
733                    return getProperties(ExceptionDates.class);
734            }
735    
736            /**
737             * Adds a list of exceptions to the recurrence rule defined in the journal
738             * entry (if one is defined). Note that this property can contain multiple
739             * dates.
740             * @param exceptionDates the list of exceptions
741             * @rfc 5545 p.118-20
742             */
743            public void addExceptionDates(ExceptionDates exceptionDates) {
744                    addProperty(exceptionDates);
745            }
746    
747            /**
748             * Gets the components that the journal entry is related to.
749             * @return the relationships
750             * @rfc 5545 p.115-6
751             */
752            public List<RelatedTo> getRelatedTo() {
753                    return getProperties(RelatedTo.class);
754            }
755    
756            /**
757             * Adds a component that the journal entry is related to.
758             * @param relatedTo the relationship
759             * @rfc 5545 p.115-6
760             */
761            public void addRelatedTo(RelatedTo relatedTo) {
762                    //TODO create a method that accepts a component and make the RelatedTo property invisible to the user
763                    //@formatter:off
764                    /*
765                     * addRelation(RelationshipType relType, ICalComponent component){
766                     *   RelatedTo prop = new RelatedTo(component.getUid().getValue());
767                     *   prop.setRelationshipType(relType);
768                     *   addProperty(prop);
769                     * }
770                     */
771                    //@formatter:on
772                    addProperty(relatedTo);
773            }
774    
775            /**
776             * Adds a component that the journal entry is related to.
777             * @param uid the UID of the other component
778             * @return the property that was created
779             * @rfc 5545 p.115-6
780             */
781            public RelatedTo addRelatedTo(String uid) {
782                    RelatedTo prop = new RelatedTo(uid);
783                    addRelatedTo(prop);
784                    return prop;
785            }
786    
787            /**
788             * Gets the list of dates/periods that help define the recurrence rule of
789             * this journal entry (if one is defined).
790             * @return the recurrence dates
791             * @rfc 5545 p.120-2
792             */
793            public List<RecurrenceDates> getRecurrenceDates() {
794                    return getProperties(RecurrenceDates.class);
795            }
796    
797            /**
798             * Adds a list of dates/periods that help define the recurrence rule of this
799             * journal entry (if one is defined).
800             * @param recurrenceDates the recurrence dates
801             * @rfc 5545 p.120-2
802             */
803            public void addRecurrenceDates(RecurrenceDates recurrenceDates) {
804                    addProperty(recurrenceDates);
805            }
806    
807            /**
808             * Gets the response to a scheduling request.
809             * @return the response
810             * @rfc 5545 p.141-3
811             */
812            public RequestStatus getRequestStatus() {
813                    return getProperty(RequestStatus.class);
814            }
815    
816            /**
817             * Sets the response to a scheduling request.
818             * @param requestStatus the response
819             * @rfc 5545 p.141-3
820             */
821            public void setRequestStatus(RequestStatus requestStatus) {
822                    setProperty(RequestStatus.class, requestStatus);
823            }
824    
825            /**
826             * <p>
827             * Gets the exceptions for the {@link RecurrenceRule} property.
828             * </p>
829             * <p>
830             * Note that this property has been removed from the latest version of the
831             * iCal specification. Its use should be avoided.
832             * </p>
833             * @return the exception rules
834             * @rfc 2445 p.114-15
835             */
836            public List<ExceptionRule> getExceptionRules() {
837                    return getProperties(ExceptionRule.class);
838            }
839    
840            /**
841             * <p>
842             * Adds an exception for the {@link RecurrenceRule} property.
843             * </p>
844             * <p>
845             * Note that this property has been removed from the latest version of the
846             * iCal specification. Its use should be avoided.
847             * </p>
848             * @param recur the exception rule to add
849             * @return the property that was created
850             * @rfc 2445 p.114-15
851             */
852            public ExceptionRule addExceptionRule(Recurrence recur) {
853                    ExceptionRule prop = (recur == null) ? null : new ExceptionRule(recur);
854                    addExceptionRule(prop);
855                    return prop;
856            }
857    
858            /**
859             * <p>
860             * Adds an exception for the {@link RecurrenceRule} property.
861             * </p>
862             * <p>
863             * Note that this property has been removed from the latest version of the
864             * iCal specification. Its use should be avoided.
865             * </p>
866             * @param exceptionRule the exception rule to add
867             * @rfc 2445 p.114-15
868             */
869            public void addExceptionRule(ExceptionRule exceptionRule) {
870                    addProperty(exceptionRule);
871            }
872    
873            @SuppressWarnings("unchecked")
874            @Override
875            protected void validate(List<ICalComponent> components, List<String> warnings) {
876                    checkRequiredCardinality(warnings, Uid.class, DateTimeStamp.class);
877                    checkOptionalCardinality(warnings, Classification.class, Created.class, DateStart.class, LastModified.class, Organizer.class, RecurrenceId.class, Sequence.class, Status.class, Summary.class, Url.class);
878    
879                    Status status = getStatus();
880                    if (status != null && (status.isTentative() || status.isConfirmed() || status.isNeedsAction() || status.isCompleted() || status.isInProgress())) {
881                            warnings.add("Invalid status value (\"" + status.getValue() + "\").  Valid status values are \"draft\", \"final\", and \"cancelled\".");
882                    }
883    
884                    RecurrenceId recurrenceId = getRecurrenceId();
885                    DateStart dateStart = getDateStart();
886                    if (recurrenceId != null && dateStart != null && dateStart.hasTime() != recurrenceId.hasTime()) {
887                            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 datetimes).");
888                    }
889    
890                    //RFC 5545 p. 167
891                    RecurrenceRule rrule = getRecurrenceRule();
892                    if (dateStart != null && rrule != null) {
893                            Date start = dateStart.getValue();
894                            Recurrence recur = rrule.getValue();
895                            if (start != null && recur != null) {
896                                    if (!dateStart.hasTime() && (!recur.getByHour().isEmpty() || !recur.getByMinute().isEmpty() || !recur.getBySecond().isEmpty())) {
897                                            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).");
898                                    }
899                            }
900                    }
901    
902                    //RFC 5545 p. 167
903                    if (getProperties(RecurrenceRule.class).size() > 1) {
904                            warnings.add("There should be only one instance of the " + RecurrenceRule.class.getSimpleName() + " property.");
905                    }
906            }
907    }