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