001    package biweekly.component;
002    
003    import java.util.Date;
004    import java.util.List;
005    
006    import biweekly.parameter.FreeBusyType;
007    import biweekly.property.Attendee;
008    import biweekly.property.Comment;
009    import biweekly.property.Contact;
010    import biweekly.property.DateEnd;
011    import biweekly.property.DateStart;
012    import biweekly.property.DateTimeStamp;
013    import biweekly.property.FreeBusy;
014    import biweekly.property.LastModified;
015    import biweekly.property.Method;
016    import biweekly.property.Organizer;
017    import biweekly.property.RequestStatus;
018    import biweekly.property.Uid;
019    import biweekly.property.Url;
020    import biweekly.util.Duration;
021    
022    /*
023     Copyright (c) 2013, Michael Angstadt
024     All rights reserved.
025    
026     Redistribution and use in source and binary forms, with or without
027     modification, are permitted provided that the following conditions are met: 
028    
029     1. Redistributions of source code must retain the above copyright notice, this
030     list of conditions and the following disclaimer. 
031     2. Redistributions in binary form must reproduce the above copyright notice,
032     this list of conditions and the following disclaimer in the documentation
033     and/or other materials provided with the distribution. 
034    
035     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
036     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
037     WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
038     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
039     ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
040     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
041     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
042     ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
043     (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
044     SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
045     */
046    
047    /**
048     * Defines a set of free/busy data.
049     * @author Michael Angstadt
050     * @see <a href="http://tools.ietf.org/html/rfc5545#page-59">RFC 5545
051     * p.59-62</a>
052     */
053    public class VFreeBusy extends ICalComponent {
054            /**
055             * <p>
056             * Creates a new free/busy component.
057             * </p>
058             * <p>
059             * The following properties are auto-generated on object creation. These
060             * properties <b>must</b> be present in order for the free/busy component to
061             * be valid:
062             * <ul>
063             * <li>{@link Uid} - Set to a UUID.</li>
064             * <li>{@link DateTimeStamp} - Set to the current date-time.</li>
065             * </ul>
066             * </p>
067             */
068            public VFreeBusy() {
069                    setUid(Uid.random());
070                    setDateTimeStamp(new Date());
071            }
072    
073            /**
074             * Gets the unique identifier for this free/busy entry. This component
075             * object comes populated with a UID on creation. This is a <b>required</b>
076             * property.
077             * @return the UID or null if not set
078             * @see <a href="http://tools.ietf.org/html/rfc5545#page-117">RFC 5545
079             * p.117-8</a>
080             */
081            public Uid getUid() {
082                    return getProperty(Uid.class);
083            }
084    
085            /**
086             * Sets the unique identifier for this free/busy entry. This component
087             * object comes populated with a UID on creation. This is a <b>required</b>
088             * property.
089             * @param uid the UID or null to remove
090             * @see <a href="http://tools.ietf.org/html/rfc5545#page-117">RFC 5545
091             * p.117-8</a>
092             */
093            public void setUid(Uid uid) {
094                    setProperty(Uid.class, uid);
095            }
096    
097            /**
098             * Sets the unique identifier for this free/busy entry. This component
099             * object comes populated with a UID on creation. This is a <b>required</b>
100             * property.
101             * @param uid the UID or null to remove
102             * @return the property that was created
103             * @see <a href="http://tools.ietf.org/html/rfc5545#page-117">RFC 5545
104             * p.117-8</a>
105             */
106            public Uid setUid(String uid) {
107                    Uid prop = (uid == null) ? null : new Uid(uid);
108                    setUid(prop);
109                    return prop;
110            }
111    
112            /**
113             * Gets either (a) the creation date of the iCalendar object (if the
114             * {@link Method} property is defined) or (b) the date that the free/busy
115             * entry was last modified (the {@link LastModified} property also holds
116             * this information). This free/busy object comes populated with a
117             * {@link DateTimeStamp} property that is set to the current time. This is a
118             * <b>required</b> property.
119             * @return the date time stamp or null if not set
120             * @see <a href="http://tools.ietf.org/html/rfc5545#page-137">RFC 5545
121             * p.137-8</a>
122             */
123            public DateTimeStamp getDateTimeStamp() {
124                    return getProperty(DateTimeStamp.class);
125            }
126    
127            /**
128             * Sets either (a) the creation date of the iCalendar object (if the
129             * {@link Method} property is defined) or (b) the date that the free/busy
130             * entry was last modified (the {@link LastModified} property also holds
131             * this information). This free/busy object comes populated with a
132             * {@link DateTimeStamp} property that is set to the current time. This is a
133             * <b>required</b> property.
134             * @param dateTimeStamp the date time stamp or null to remove
135             * @see <a href="http://tools.ietf.org/html/rfc5545#page-137">RFC 5545
136             * p.137-8</a>
137             */
138            public void setDateTimeStamp(DateTimeStamp dateTimeStamp) {
139                    setProperty(DateTimeStamp.class, dateTimeStamp);
140            }
141    
142            /**
143             * Sets either (a) the creation date of the iCalendar object (if the
144             * {@link Method} property is defined) or (b) the date that the free/busy
145             * entry was last modified (the {@link LastModified} property also holds
146             * this information). This free/busy object comes populated with a
147             * {@link DateTimeStamp} property that is set to the current time. This is a
148             * <b>required</b> property.
149             * @param dateTimeStamp the date time stamp or null to remove
150             * @return the property that was created
151             * @see <a href="http://tools.ietf.org/html/rfc5545#page-137">RFC 5545
152             * p.137-8</a>
153             */
154            public DateTimeStamp setDateTimeStamp(Date dateTimeStamp) {
155                    DateTimeStamp prop = (dateTimeStamp == null) ? null : new DateTimeStamp(dateTimeStamp);
156                    setDateTimeStamp(prop);
157                    return prop;
158            }
159    
160            /**
161             * Gets the contact associated with the free/busy entry.
162             * @return the contact or null if not set
163             * @see <a href="http://tools.ietf.org/html/rfc5545#page-109">RFC 5545
164             * p.109-11</a>
165             */
166            public Contact getContact() {
167                    return getProperty(Contact.class);
168            }
169    
170            /**
171             * Sets the contact for the free/busy entry.
172             * @param contact the contact or null to remove
173             * @see <a href="http://tools.ietf.org/html/rfc5545#page-109">RFC 5545
174             * p.109-11</a>
175             */
176            public void setContact(Contact contact) {
177                    setProperty(Contact.class, contact);
178            }
179    
180            /**
181             * Sets the contact for the free/busy entry.
182             * @param contact the contact (e.g. "ACME Co - (123) 555-1234")
183             * @return the property that was created
184             * @see <a href="http://tools.ietf.org/html/rfc5545#page-109">RFC 5545
185             * p.109-11</a>
186             */
187            public Contact addContact(String contact) {
188                    Contact prop = new Contact(contact);
189                    setContact(prop);
190                    return prop;
191            }
192    
193            /**
194             * Gets the date that the free/busy entry starts.
195             * @return the start date or null if not set
196             * @see <a href="http://tools.ietf.org/html/rfc5545#page-97">RFC 5545
197             * p.97-8</a>
198             */
199            public DateStart getDateStart() {
200                    return getProperty(DateStart.class);
201            }
202    
203            /**
204             * Sets the date that the free/busy entry starts.
205             * @param dateStart the start date or null to remove
206             * @see <a href="http://tools.ietf.org/html/rfc5545#page-97">RFC 5545
207             * p.97-8</a>
208             */
209            public void setDateStart(DateStart dateStart) {
210                    setProperty(DateStart.class, dateStart);
211            }
212    
213            /**
214             * Sets the date that the free/busy entry starts.
215             * @param dateStart the start date or null to remove
216             * @return the property that was created
217             * @see <a href="http://tools.ietf.org/html/rfc5545#page-97">RFC 5545
218             * p.97-8</a>
219             */
220            public DateStart setDateStart(Date dateStart) {
221                    DateStart prop = (dateStart == null) ? null : new DateStart(dateStart);
222                    setDateStart(prop);
223                    return prop;
224            }
225    
226            /**
227             * Gets the date that the free/busy entry ends.
228             * @return the end date or null if not set
229             * @see <a href="http://tools.ietf.org/html/rfc5545#page-95">RFC 5545
230             * p.95-6</a>
231             */
232            public DateEnd getDateEnd() {
233                    return getProperty(DateEnd.class);
234            }
235    
236            /**
237             * Sets the date that the free/busy entry ends.
238             * @param dateEnd the end date or null to remove
239             * @see <a href="http://tools.ietf.org/html/rfc5545#page-95">RFC 5545
240             * p.95-6</a>
241             */
242            public void setDateEnd(DateEnd dateEnd) {
243                    setProperty(DateEnd.class, dateEnd);
244            }
245    
246            /**
247             * Sets the date that the free/busy entry ends.
248             * @param dateEnd the end date or null to remove
249             * @return the property that was created
250             * @see <a href="http://tools.ietf.org/html/rfc5545#page-95">RFC 5545
251             * p.95-6</a>
252             */
253            public DateEnd setDateEnd(Date dateEnd) {
254                    DateEnd prop = (dateEnd == null) ? null : new DateEnd(dateEnd);
255                    setDateEnd(prop);
256                    return prop;
257            }
258    
259            /**
260             * Gets the person requesting the free/busy time.
261             * @return the person requesting the free/busy time or null if not set
262             * @see <a href="http://tools.ietf.org/html/rfc5545#page-111">RFC 5545
263             * p.111-2</a>
264             */
265            public Organizer getOrganizer() {
266                    return getProperty(Organizer.class);
267            }
268    
269            /**
270             * Sets the person requesting the free/busy time.
271             * @param organizer the person requesting the free/busy time or null to
272             * remove
273             * @see <a href="http://tools.ietf.org/html/rfc5545#page-111">RFC 5545
274             * p.111-2</a>
275             */
276            public void setOrganizer(Organizer organizer) {
277                    setProperty(Organizer.class, organizer);
278            }
279    
280            /**
281             * Sets the person requesting the free/busy time.
282             * @param email the email address of the person requesting the free/busy
283             * time (e.g. "johndoe@example.com") or null to remove
284             * @return the property that was created
285             * @see <a href="http://tools.ietf.org/html/rfc5545#page-111">RFC 5545
286             * p.111-2</a>
287             */
288            public Organizer setOrganizer(String email) {
289                    Organizer prop = (email == null) ? null : Organizer.email(email);
290                    setOrganizer(prop);
291                    return prop;
292            }
293    
294            /**
295             * Gets a URL to a resource that contains additional information about the
296             * free/busy entry.
297             * @return the URL or null if not set
298             * @see <a href="http://tools.ietf.org/html/rfc5545#page-116">RFC 5545
299             * p.116-7</a>
300             */
301            public Url getUrl() {
302                    return getProperty(Url.class);
303            }
304    
305            /**
306             * Sets a URL to a resource that contains additional information about the
307             * free/busy entry.
308             * @param url the URL or null to remove
309             * @see <a href="http://tools.ietf.org/html/rfc5545#page-116">RFC 5545
310             * p.116-7</a>
311             */
312            public void setUrl(Url url) {
313                    setProperty(Url.class, url);
314            }
315    
316            /**
317             * Sets a URL to a resource that contains additional information about the
318             * free/busy entry.
319             * @param url the URL (e.g. "http://example.com/resource.ics") or null to
320             * remove
321             * @return the property that was created
322             * @see <a href="http://tools.ietf.org/html/rfc5545#page-116">RFC 5545
323             * p.116-7</a>
324             */
325            public Url setUrl(String url) {
326                    Url prop = (url == null) ? null : new Url(url);
327                    setUrl(prop);
328                    return prop;
329            }
330    
331            //
332            //zero or more
333            //      private List<Attendee> attendees;
334            //      private List<Comment> comments;
335            //      private List<FreeBusy> freeBusy;
336            //      private List<Rstatus> rstatus;
337    
338            /**
339             * Gets the people who are involved in the free/busy entry.
340             * @return the attendees
341             * @see <a href="http://tools.ietf.org/html/rfc5545#page-107">RFC 5545
342             * p.107-9</a>
343             */
344            public List<Attendee> getAttendees() {
345                    return getProperties(Attendee.class);
346            }
347    
348            /**
349             * Adds a person who is involved in the free/busy entry.
350             * @param attendee the attendee
351             * @see <a href="http://tools.ietf.org/html/rfc5545#page-107">RFC 5545
352             * p.107-9</a>
353             */
354            public void addAttendee(Attendee attendee) {
355                    addProperty(attendee);
356            }
357    
358            /**
359             * Gets the comments attached to the free/busy entry.
360             * @return the comments
361             * @see <a href="http://tools.ietf.org/html/rfc5545#page-83">RFC 5545
362             * p.83-4</a>
363             */
364            public List<Comment> getComments() {
365                    return getProperties(Comment.class);
366            }
367    
368            /**
369             * Adds a comment to the free/busy entry.
370             * @param comment the comment to add
371             * @see <a href="http://tools.ietf.org/html/rfc5545#page-83">RFC 5545
372             * p.83-4</a>
373             */
374            public void addComment(Comment comment) {
375                    addProperty(comment);
376            }
377    
378            /**
379             * Adds a comment to the free/busy entry.
380             * @param comment the comment to add
381             * @return the property that was created
382             * @see <a href="http://tools.ietf.org/html/rfc5545#page-83">RFC 5545
383             * p.83-4</a>
384             */
385            public Comment addComment(String comment) {
386                    Comment prop = new Comment(comment);
387                    addComment(prop);
388                    return prop;
389            }
390    
391            /**
392             * Gets the person's availabilities over certain time periods (for example,
393             * "free" between 1pm-3pm, but "busy" between 3pm-4pm).
394             * @return the availabilities
395             * @see <a href="http://tools.ietf.org/html/rfc5545#page-100">RFC 5545
396             * p.100-1</a>
397             */
398            public List<FreeBusy> getFreeBusy() {
399                    return getProperties(FreeBusy.class);
400            }
401    
402            /**
403             * Adds a list of time periods for which the person is free or busy (for
404             * example, "free" between 1pm-3pm and 4pm-5pm). Note that a
405             * {@link FreeBusy} property can contain multiple time periods, but only one
406             * availability type (e.g. "busy").
407             * @param freeBusy the availabilities
408             * @see <a href="http://tools.ietf.org/html/rfc5545#page-100">RFC 5545
409             * p.100-1</a>
410             */
411            public void addFreeBusy(FreeBusy freeBusy) {
412                    addProperty(freeBusy);
413            }
414    
415            /**
416             * Adds a single time period for which the person is free or busy (for
417             * example, "free" between 1pm-3pm). This method will look for an existing
418             * property that has the given {@link FreeBusyType} and add the time period
419             * to it, or create a new property is one cannot be found.
420             * @param type the availability type (e.g. "free" or "busy")
421             * @param start the start date-time
422             * @param end the end date-time
423             * @return the property that was created/modified
424             * @see <a href="http://tools.ietf.org/html/rfc5545#page-100">RFC 5545
425             * p.100-1</a>
426             */
427            public FreeBusy addFreeBusy(FreeBusyType type, Date start, Date end) {
428                    FreeBusy found = findByFbType(type);
429                    found.addValue(start, end);
430                    return found;
431            }
432    
433            /**
434             * Adds a single time period for which the person is free or busy (for
435             * example, "free" for 2 hours after 1pm). This method will look for an
436             * existing property that has the given {@link FreeBusyType} and add the
437             * time period to it, or create a new property is one cannot be found.
438             * @param type the availability type (e.g. "free" or "busy")
439             * @param start the start date-time
440             * @param duration the length of time
441             * @return the property that was created/modified
442             * @see <a href="http://tools.ietf.org/html/rfc5545#page-100">RFC 5545
443             * p.100-1</a>
444             */
445            public FreeBusy addFreeBusy(FreeBusyType type, Date start, Duration duration) {
446                    FreeBusy found = findByFbType(type);
447                    found.addValue(start, duration);
448                    return found;
449            }
450    
451            private FreeBusy findByFbType(FreeBusyType type) {
452                    FreeBusy found = null;
453    
454                    for (FreeBusy fb : getFreeBusy()) {
455                            if (fb.getType() == type) {
456                                    found = fb;
457                                    break;
458                            }
459                    }
460    
461                    if (found == null) {
462                            found = new FreeBusy();
463                            found.setType(type);
464                            addFreeBusy(found);
465                    }
466                    return found;
467            }
468    
469            /**
470             * Gets the response to a scheduling request.
471             * @return the response
472             * @see <a href="http://tools.ietf.org/html/rfc5545#page-141">RFC 5545
473             * p.141-3</a>
474             */
475            public RequestStatus getRequestStatus() {
476                    return getProperty(RequestStatus.class);
477            }
478    
479            /**
480             * Sets the response to a scheduling request.
481             * @param requestStatus the response
482             * @see <a href="http://tools.ietf.org/html/rfc5545#page-141">RFC 5545
483             * p.141-3</a>
484             */
485            public void setRequestStatus(RequestStatus requestStatus) {
486                    setProperty(RequestStatus.class, requestStatus);
487            }
488    
489            @SuppressWarnings("unchecked")
490            @Override
491            protected void validate(List<ICalComponent> components, List<String> warnings) {
492                    checkRequiredCardinality(warnings, Uid.class, DateTimeStamp.class);
493                    checkOptionalCardinality(warnings, Contact.class, DateStart.class, DateEnd.class, Organizer.class, Url.class);
494    
495                    DateStart dateStart = getDateStart();
496                    DateEnd dateEnd = getDateEnd();
497    
498                    if (dateEnd != null && dateStart == null) {
499                            warnings.add("A " + DateStart.class.getSimpleName() + " property must be defined if a " + DateEnd.class.getSimpleName() + " property is defined.");
500                    }
501    
502                    if (dateStart != null && dateStart.getValue() != null && !dateStart.hasTime()) {
503                            warnings.add(DateStart.class.getSimpleName() + " properties in free/busy components must always have a time component.");
504                    }
505    
506                    if (dateEnd != null && dateEnd.getValue() != null && !dateEnd.hasTime()) {
507                            warnings.add(DateEnd.class.getSimpleName() + " properties in free/busy components must always have a time component.");
508                    }
509    
510                    if (dateStart != null && dateEnd != null) {
511                            Date start = dateStart.getValue();
512                            Date end = dateEnd.getValue();
513                            if (start != null && end != null && start.compareTo(end) >= 0) {
514                                    warnings.add(DateStart.class.getSimpleName() + " must come before " + DateEnd.class.getSimpleName() + ".");
515                            }
516                    }
517            }
518    }