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