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 }