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}