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 }