001 package biweekly.component;
002
003 import java.util.Date;
004 import java.util.List;
005
006 import biweekly.Warning;
007 import biweekly.parameter.FreeBusyType;
008 import biweekly.property.Attendee;
009 import biweekly.property.Comment;
010 import biweekly.property.Contact;
011 import biweekly.property.DateEnd;
012 import biweekly.property.DateStart;
013 import biweekly.property.DateTimeStamp;
014 import biweekly.property.FreeBusy;
015 import biweekly.property.LastModified;
016 import biweekly.property.Method;
017 import biweekly.property.Organizer;
018 import biweekly.property.RequestStatus;
019 import biweekly.property.Uid;
020 import biweekly.property.Url;
021 import 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 the person is
051 * available or 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 * @rfc 5545 p.59-62
071 */
072 public 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 * @rfc 5545 p.117-8
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 * @rfc 5545 p.117-8
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 * @rfc 5545 p.117-8
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 * @rfc 5545 p.137-8
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 * @rfc 5545 p.137-8
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 * @rfc 5545 p.137-8
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 * @rfc 5545 p.109-11
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 * @rfc 5545 p.109-11
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 * @rfc 5545 p.109-11
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 * @rfc 5545 p.97-8
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 * @rfc 5545 p.97-8
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 * @rfc 5545 p.97-8
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 * @rfc 5545 p.95-6
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 * @rfc 5545 p.95-6
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 * @rfc 5545 p.95-6
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 * @rfc 5545 p.111-2
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 * @rfc 5545 p.111-2
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 * @rfc 5545 p.111-2
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 * @rfc 5545 p.116-7
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 * @rfc 5545 p.116-7
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 * @rfc 5545 p.116-7
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 * @rfc 5545 p.107-9
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 * @rfc 5545 p.107-9
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 * @rfc 5545 p.83-4
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 * @rfc 5545 p.83-4
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 * @rfc 5545 p.83-4
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 * @rfc 5545 p.100-1
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 * @rfc 5545 p.100-1
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 * @rfc 5545 p.100-1
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 * @rfc 5545 p.100-1
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 * @rfc 5545 p.141-3
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 * @rfc 5545 p.141-3
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 if (dateEnd != null && dateStart == null) {
486 warnings.add(Warning.validate(15));
487 }
488
489 if (dateStart != null && dateStart.getValue() != null && !dateStart.hasTime()) {
490 warnings.add(Warning.validate(20, DateStart.class.getSimpleName()));
491 }
492
493 if (dateEnd != null && dateEnd.getValue() != null && !dateEnd.hasTime()) {
494 warnings.add(Warning.validate(20, DateEnd.class.getSimpleName()));
495 }
496
497 if (dateStart != null && dateEnd != null) {
498 Date start = dateStart.getValue();
499 Date end = dateEnd.getValue();
500 if (start != null && end != null && start.compareTo(end) >= 0) {
501 warnings.add(Warning.validate(16));
502 }
503 }
504 }
505 }