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 * <p>
049 * Defines a collection of time ranges that describe when the person is
050 * available or unavailable.
051 * </p>
052 * <p>
053 * <b>Examples:</b>
054 *
055 * <pre class="brush:java">
056 * VFreeBusy freebusy = new VFreeBusy();
057 *
058 * Date start = ...
059 * Date end = ...
060 * freebusy.addFreeBusy(FreeBusyType.FREE, start, end);
061 *
062 * start = ...
063 * Duration duration = Duration.builder().hours(2).build();
064 * freebusy.addFreeBusy(FreeBusyType.BUSY, start, duration);
065 * </pre>
066 *
067 * </p>
068 * @author Michael Angstadt
069 * @rfc 5545 p.59-62
070 */
071 public class VFreeBusy extends ICalComponent {
072 /**
073 * <p>
074 * Creates a new free/busy component.
075 * </p>
076 * <p>
077 * The following properties are auto-generated on object creation. These
078 * properties <b>must</b> be present in order for the free/busy component to
079 * be valid:
080 * <ul>
081 * <li>{@link Uid} - Set to a UUID.</li>
082 * <li>{@link DateTimeStamp} - Set to the current date-time.</li>
083 * </ul>
084 * </p>
085 */
086 public VFreeBusy() {
087 setUid(Uid.random());
088 setDateTimeStamp(new Date());
089 }
090
091 /**
092 * Gets the unique identifier for this free/busy entry. This component
093 * object comes populated with a UID on creation. This is a <b>required</b>
094 * property.
095 * @return the UID or null if not set
096 * @rfc 5545 p.117-8
097 */
098 public Uid getUid() {
099 return getProperty(Uid.class);
100 }
101
102 /**
103 * Sets the unique identifier for this free/busy entry. This component
104 * object comes populated with a UID on creation. This is a <b>required</b>
105 * property.
106 * @param uid the UID or null to remove
107 * @rfc 5545 p.117-8
108 */
109 public void setUid(Uid uid) {
110 setProperty(Uid.class, uid);
111 }
112
113 /**
114 * Sets the unique identifier for this free/busy entry. This component
115 * object comes populated with a UID on creation. This is a <b>required</b>
116 * property.
117 * @param uid the UID or null to remove
118 * @return the property that was created
119 * @rfc 5545 p.117-8
120 */
121 public Uid setUid(String uid) {
122 Uid prop = (uid == null) ? null : new Uid(uid);
123 setUid(prop);
124 return prop;
125 }
126
127 /**
128 * Gets 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 * @return the date time stamp or null if not set
135 * @rfc 5545 p.137-8
136 */
137 public DateTimeStamp getDateTimeStamp() {
138 return getProperty(DateTimeStamp.class);
139 }
140
141 /**
142 * Sets either (a) the creation date of the iCalendar object (if the
143 * {@link Method} property is defined) or (b) the date that the free/busy
144 * entry was last modified (the {@link LastModified} property also holds
145 * this information). This free/busy object comes populated with a
146 * {@link DateTimeStamp} property that is set to the current time. This is a
147 * <b>required</b> property.
148 * @param dateTimeStamp the date time stamp or null to remove
149 * @rfc 5545 p.137-8
150 */
151 public void setDateTimeStamp(DateTimeStamp dateTimeStamp) {
152 setProperty(DateTimeStamp.class, dateTimeStamp);
153 }
154
155 /**
156 * Sets either (a) the creation date of the iCalendar object (if the
157 * {@link Method} property is defined) or (b) the date that the free/busy
158 * entry was last modified (the {@link LastModified} property also holds
159 * this information). This free/busy object comes populated with a
160 * {@link DateTimeStamp} property that is set to the current time. This is a
161 * <b>required</b> property.
162 * @param dateTimeStamp the date time stamp or null to remove
163 * @return the property that was created
164 * @rfc 5545 p.137-8
165 */
166 public DateTimeStamp setDateTimeStamp(Date dateTimeStamp) {
167 DateTimeStamp prop = (dateTimeStamp == null) ? null : new DateTimeStamp(dateTimeStamp);
168 setDateTimeStamp(prop);
169 return prop;
170 }
171
172 /**
173 * Gets the contact associated with the free/busy entry.
174 * @return the contact or null if not set
175 * @rfc 5545 p.109-11
176 */
177 public Contact getContact() {
178 return getProperty(Contact.class);
179 }
180
181 /**
182 * Sets the contact for the free/busy entry.
183 * @param contact the contact or null to remove
184 * @rfc 5545 p.109-11
185 */
186 public void setContact(Contact contact) {
187 setProperty(Contact.class, contact);
188 }
189
190 /**
191 * Sets the contact for the free/busy entry.
192 * @param contact the contact (e.g. "ACME Co - (123) 555-1234")
193 * @return the property that was created
194 * @rfc 5545 p.109-11
195 */
196 public Contact addContact(String contact) {
197 Contact prop = new Contact(contact);
198 setContact(prop);
199 return prop;
200 }
201
202 /**
203 * Gets the date that the free/busy entry starts.
204 * @return the start date or null if not set
205 * @rfc 5545 p.97-8
206 */
207 public DateStart getDateStart() {
208 return getProperty(DateStart.class);
209 }
210
211 /**
212 * Sets the date that the free/busy entry starts.
213 * @param dateStart the start date or null to remove
214 * @rfc 5545 p.97-8
215 */
216 public void setDateStart(DateStart dateStart) {
217 setProperty(DateStart.class, dateStart);
218 }
219
220 /**
221 * Sets the date that the free/busy entry starts.
222 * @param dateStart the start date or null to remove
223 * @return the property that was created
224 * @rfc 5545 p.97-8
225 */
226 public DateStart setDateStart(Date dateStart) {
227 DateStart prop = (dateStart == null) ? null : new DateStart(dateStart);
228 setDateStart(prop);
229 return prop;
230 }
231
232 /**
233 * Gets the date that the free/busy entry ends.
234 * @return the end date or null if not set
235 * @rfc 5545 p.95-6
236 */
237 public DateEnd getDateEnd() {
238 return getProperty(DateEnd.class);
239 }
240
241 /**
242 * Sets the date that the free/busy entry ends.
243 * @param dateEnd the end date or null to remove
244 * @rfc 5545 p.95-6
245 */
246 public void setDateEnd(DateEnd dateEnd) {
247 setProperty(DateEnd.class, dateEnd);
248 }
249
250 /**
251 * Sets the date that the free/busy entry ends.
252 * @param dateEnd the end date or null to remove
253 * @return the property that was created
254 * @rfc 5545 p.95-6
255 */
256 public DateEnd setDateEnd(Date dateEnd) {
257 DateEnd prop = (dateEnd == null) ? null : new DateEnd(dateEnd);
258 setDateEnd(prop);
259 return prop;
260 }
261
262 /**
263 * Gets the person requesting the free/busy time.
264 * @return the person requesting the free/busy time or null if not set
265 * @rfc 5545 p.111-2
266 */
267 public Organizer getOrganizer() {
268 return getProperty(Organizer.class);
269 }
270
271 /**
272 * Sets the person requesting the free/busy time.
273 * @param organizer the person requesting the free/busy time or null to
274 * remove
275 * @rfc 5545 p.111-2
276 */
277 public void setOrganizer(Organizer organizer) {
278 setProperty(Organizer.class, organizer);
279 }
280
281 /**
282 * Sets the person requesting the free/busy time.
283 * @param email the email address of the person requesting the free/busy
284 * time (e.g. "johndoe@example.com") or null to remove
285 * @return the property that was created
286 * @rfc 5545 p.111-2
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 * @rfc 5545 p.116-7
299 */
300 public Url getUrl() {
301 return getProperty(Url.class);
302 }
303
304 /**
305 * Sets a URL to a resource that contains additional information about the
306 * free/busy entry.
307 * @param url the URL or null to remove
308 * @rfc 5545 p.116-7
309 */
310 public void setUrl(Url url) {
311 setProperty(Url.class, url);
312 }
313
314 /**
315 * Sets a URL to a resource that contains additional information about the
316 * free/busy entry.
317 * @param url the URL (e.g. "http://example.com/resource.ics") or null to
318 * remove
319 * @return the property that was created
320 * @rfc 5545 p.116-7
321 */
322 public Url setUrl(String url) {
323 Url prop = (url == null) ? null : new Url(url);
324 setUrl(prop);
325 return prop;
326 }
327
328 //
329 //zero or more
330 // private List<Attendee> attendees;
331 // private List<Comment> comments;
332 // private List<FreeBusy> freeBusy;
333 // private List<Rstatus> rstatus;
334
335 /**
336 * Gets the people who are involved in the free/busy entry.
337 * @return the attendees
338 * @rfc 5545 p.107-9
339 */
340 public List<Attendee> getAttendees() {
341 return getProperties(Attendee.class);
342 }
343
344 /**
345 * Adds a person who is involved in the free/busy entry.
346 * @param attendee the attendee
347 * @rfc 5545 p.107-9
348 */
349 public void addAttendee(Attendee attendee) {
350 addProperty(attendee);
351 }
352
353 /**
354 * Gets the comments attached to the free/busy entry.
355 * @return the comments
356 * @rfc 5545 p.83-4
357 */
358 public List<Comment> getComments() {
359 return getProperties(Comment.class);
360 }
361
362 /**
363 * Adds a comment to the free/busy entry.
364 * @param comment the comment to add
365 * @rfc 5545 p.83-4
366 */
367 public void addComment(Comment comment) {
368 addProperty(comment);
369 }
370
371 /**
372 * Adds a comment to the free/busy entry.
373 * @param comment the comment to add
374 * @return the property that was created
375 * @rfc 5545 p.83-4
376 */
377 public Comment addComment(String comment) {
378 Comment prop = new Comment(comment);
379 addComment(prop);
380 return prop;
381 }
382
383 /**
384 * Gets the person's availabilities over certain time periods (for example,
385 * "free" between 1pm-3pm, but "busy" between 3pm-4pm).
386 * @return the availabilities
387 * @rfc 5545 p.100-1
388 */
389 public List<FreeBusy> getFreeBusy() {
390 return getProperties(FreeBusy.class);
391 }
392
393 /**
394 * Adds a list of time periods for which the person is free or busy (for
395 * example, "free" between 1pm-3pm and 4pm-5pm). Note that a
396 * {@link FreeBusy} property can contain multiple time periods, but only one
397 * availability type (e.g. "busy").
398 * @param freeBusy the availabilities
399 * @rfc 5545 p.100-1
400 */
401 public void addFreeBusy(FreeBusy freeBusy) {
402 addProperty(freeBusy);
403 }
404
405 /**
406 * Adds a single time period for which the person is free or busy (for
407 * example, "free" between 1pm-3pm). This method will look for an existing
408 * property that has the given {@link FreeBusyType} and add the time period
409 * to it, or create a new property is one cannot be found.
410 * @param type the availability type (e.g. "free" or "busy")
411 * @param start the start date-time
412 * @param end the end date-time
413 * @return the property that was created/modified
414 * @rfc 5545 p.100-1
415 */
416 public FreeBusy addFreeBusy(FreeBusyType type, Date start, Date end) {
417 FreeBusy found = findByFbType(type);
418 found.addValue(start, end);
419 return found;
420 }
421
422 /**
423 * Adds a single time period for which the person is free or busy (for
424 * example, "free" for 2 hours after 1pm). This method will look for an
425 * existing property that has the given {@link FreeBusyType} and add the
426 * time period to it, or create a new property is one cannot be found.
427 * @param type the availability type (e.g. "free" or "busy")
428 * @param start the start date-time
429 * @param duration the length of time
430 * @return the property that was created/modified
431 * @rfc 5545 p.100-1
432 */
433 public FreeBusy addFreeBusy(FreeBusyType type, Date start, Duration duration) {
434 FreeBusy found = findByFbType(type);
435 found.addValue(start, duration);
436 return found;
437 }
438
439 private FreeBusy findByFbType(FreeBusyType type) {
440 FreeBusy found = null;
441
442 for (FreeBusy fb : getFreeBusy()) {
443 if (fb.getType() == type) {
444 found = fb;
445 break;
446 }
447 }
448
449 if (found == null) {
450 found = new FreeBusy();
451 found.setType(type);
452 addFreeBusy(found);
453 }
454 return found;
455 }
456
457 /**
458 * Gets the response to a scheduling request.
459 * @return the response
460 * @rfc 5545 p.141-3
461 */
462 public RequestStatus getRequestStatus() {
463 return getProperty(RequestStatus.class);
464 }
465
466 /**
467 * Sets the response to a scheduling request.
468 * @param requestStatus the response
469 * @rfc 5545 p.141-3
470 */
471 public void setRequestStatus(RequestStatus requestStatus) {
472 setProperty(RequestStatus.class, requestStatus);
473 }
474
475 @SuppressWarnings("unchecked")
476 @Override
477 protected void validate(List<ICalComponent> components, List<String> warnings) {
478 checkRequiredCardinality(warnings, Uid.class, DateTimeStamp.class);
479 checkOptionalCardinality(warnings, Contact.class, DateStart.class, DateEnd.class, Organizer.class, Url.class);
480
481 DateStart dateStart = getDateStart();
482 DateEnd dateEnd = getDateEnd();
483
484 if (dateEnd != null && dateStart == null) {
485 warnings.add("A " + DateStart.class.getSimpleName() + " property must be defined if a " + DateEnd.class.getSimpleName() + " property is defined.");
486 }
487
488 if (dateStart != null && dateStart.getValue() != null && !dateStart.hasTime()) {
489 warnings.add(DateStart.class.getSimpleName() + " properties in free/busy components must always have a time component.");
490 }
491
492 if (dateEnd != null && dateEnd.getValue() != null && !dateEnd.hasTime()) {
493 warnings.add(DateEnd.class.getSimpleName() + " properties in free/busy components must always have a time component.");
494 }
495
496 if (dateStart != null && dateEnd != null) {
497 Date start = dateStart.getValue();
498 Date end = dateEnd.getValue();
499 if (start != null && end != null && start.compareTo(end) >= 0) {
500 warnings.add(DateStart.class.getSimpleName() + " must come before " + DateEnd.class.getSimpleName() + ".");
501 }
502 }
503 }
504 }