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