001package biweekly.parameter; 002 003import java.util.ArrayList; 004import java.util.Arrays; 005import java.util.List; 006 007import biweekly.ICalDataType; 008import biweekly.ICalVersion; 009import biweekly.Warning; 010import biweekly.property.FreeBusy; 011import biweekly.property.RecurrenceId; 012import biweekly.property.RelatedTo; 013import biweekly.property.Trigger; 014import biweekly.util.ListMultimap; 015 016/* 017 Copyright (c) 2013-2015, Michael Angstadt 018 All rights reserved. 019 020 Redistribution and use in source and binary forms, with or without 021 modification, are permitted provided that the following conditions are met: 022 023 1. Redistributions of source code must retain the above copyright notice, this 024 list of conditions and the following disclaimer. 025 2. Redistributions in binary form must reproduce the above copyright notice, 026 this list of conditions and the following disclaimer in the documentation 027 and/or other materials provided with the distribution. 028 029 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 030 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 031 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 032 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 033 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 034 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 035 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 036 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 037 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 038 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 039 */ 040 041/** 042 * Contains the list of parameters that belong to a property. 043 * @author Michael Angstadt 044 */ 045public class ICalParameters extends ListMultimap<String, String> { 046 public static final String CN = "CN"; 047 public static final String ALTREP = "ALTREP"; 048 public static final String CHARSET = "CHARSET"; //1.0 only 049 public static final String CUTYPE = "CUTYPE"; 050 public static final String DELEGATED_FROM = "DELEGATED-FROM"; 051 public static final String DELEGATED_TO = "DELEGATED-TO"; 052 public static final String DIR = "DIR"; 053 public static final String ENCODING = "ENCODING"; 054 public static final String FMTTYPE = "FMTTYPE"; 055 public static final String FBTYPE = "FBTYPE"; 056 public static final String LANGUAGE = "LANGUAGE"; 057 public static final String MEMBER = "MEMBER"; 058 public static final String PARTSTAT = "PARTSTAT"; 059 public static final String RANGE = "RANGE"; 060 public static final String RELATED = "RELATED"; 061 public static final String RELTYPE = "RELTYPE"; 062 public static final String ROLE = "ROLE"; 063 public static final String RSVP = "RSVP"; 064 public static final String SENT_BY = "SENT-BY"; 065 public static final String TYPE = "TYPE"; //1.0 only 066 public static final String TZID = "TZID"; 067 public static final String VALUE = "VALUE"; 068 069 /** 070 * Creates a parameters list. 071 */ 072 public ICalParameters() { 073 super(0); //initialize map size to 0 because most properties don't use any parameters 074 } 075 076 /** 077 * Copies an existing parameters list. 078 * @param parameters the list to copy 079 */ 080 public ICalParameters(ICalParameters parameters) { 081 super(parameters); 082 } 083 084 /** 085 * Gets a URI pointing to additional information about the entity 086 * represented by the property. 087 * @return the URI or null if not set 088 * @see <a href="http://tools.ietf.org/html/rfc5545#page-14">RFC 5545 089 * p.14-5</a> 090 */ 091 public String getAltRepresentation() { 092 return first(ALTREP); 093 } 094 095 /** 096 * Sets a URI pointing to additional information about the entity 097 * represented by the property. 098 * @param uri the URI or null to remove 099 * @see <a href="http://tools.ietf.org/html/rfc5545#page-14">RFC 5545 100 * p.14-5</a> 101 */ 102 public void setAltRepresentation(String uri) { 103 replace(ALTREP, uri); 104 } 105 106 /** 107 * Gets the character set that the property value is encoded in. 108 * @return the character set or null if not set 109 * @see <a href="http://www.imc.org/pdi/vcal-10.doc">vCal 1.0 p.16</a> 110 */ 111 public String getCharset() { 112 return first(CHARSET); 113 } 114 115 /** 116 * Sets the character set that the property value is encoded in. 117 * @param charset the character set or null to remove 118 * @see <a href="http://www.imc.org/pdi/vcal-10.doc">vCal 1.0 p.16</a> 119 */ 120 public void setCharset(String charset) { 121 replace(CHARSET, charset); 122 } 123 124 /** 125 * Gets the display name of a person. 126 * @return the display name (e.g. "John Doe") or null if not set 127 * @see <a href="http://tools.ietf.org/html/rfc5545#page-15">RFC 5545 128 * p.15-6</a> 129 */ 130 public String getCommonName() { 131 return first(CN); 132 } 133 134 /** 135 * Sets the display name of a person. 136 * @param cn the display name (e.g. "John Doe") or null to remove 137 * @see <a href="http://tools.ietf.org/html/rfc5545#page-15">RFC 5545 138 * p.15-6</a> 139 */ 140 public void setCommonName(String cn) { 141 replace(CN, cn); 142 } 143 144 /** 145 * Gets the type of user an attendee is (for example, an "individual" or a 146 * "room"). 147 * @return the calendar user type or null if not set 148 * @see <a href="http://tools.ietf.org/html/rfc5545#page-16">RFC 5545 149 * p.16</a> 150 */ 151 public CalendarUserType getCalendarUserType() { 152 String value = first(CUTYPE); 153 return (value == null) ? null : CalendarUserType.get(value); 154 } 155 156 /** 157 * Sets the type of user an attendee is (for example, an "individual" or a 158 * "room"). 159 * @param cutype the calendar user type or null to remove 160 * @see <a href="http://tools.ietf.org/html/rfc5545#page-16">RFC 5545 161 * p.16</a> 162 */ 163 public void setCalendarUserType(CalendarUserType cutype) { 164 replace(CUTYPE, (cutype == null) ? null : cutype.getValue()); 165 } 166 167 /** 168 * Gets the people who have delegated their responsibility to an attendee. 169 * @return the delegators (typically email URIs, e.g. 170 * "mailto:janedoe@example.com") 171 * @see <a href="http://tools.ietf.org/html/rfc5545#page-17">RFC 5545 172 * p.17</a> 173 */ 174 public List<String> getDelegatedFrom() { 175 return get(DELEGATED_FROM); 176 } 177 178 /** 179 * Adds a person who has delegated his or her responsibility to an attendee. 180 * @param uri the delegator (typically an email URI, e.g. 181 * "mailto:janedoe@example.com") 182 * @see <a href="http://tools.ietf.org/html/rfc5545#page-17">RFC 5545 183 * p.17</a> 184 */ 185 public void addDelegatedFrom(String uri) { 186 put(DELEGATED_FROM, uri); 187 } 188 189 /** 190 * Removes a person who has delegated his or her responsibility to an 191 * attendee. 192 * @param uri the delegator to remove (typically an email URI, e.g. 193 * "mailto:janedoe@example.com") 194 * @see <a href="http://tools.ietf.org/html/rfc5545#page-17">RFC 5545 195 * p.17</a> 196 */ 197 public void removeDelegatedFrom(String uri) { 198 remove(DELEGATED_FROM, uri); 199 } 200 201 /** 202 * Removes everyone who has delegated his or her responsibility to an 203 * attendee. 204 * @see <a href="http://tools.ietf.org/html/rfc5545#page-17">RFC 5545 205 * p.17</a> 206 */ 207 public void removeDelegatedFrom() { 208 removeAll(DELEGATED_FROM); 209 } 210 211 /** 212 * Gets the people to which an attendee has delegated his or her 213 * responsibility. 214 * @return the delegatees (typically email URIs, e.g. 215 * "mailto:janedoe@example.com") 216 * @see <a href="http://tools.ietf.org/html/rfc5545#page-17">RFC 5545 217 * p.17-8</a> 218 */ 219 public List<String> getDelegatedTo() { 220 return get(DELEGATED_TO); 221 } 222 223 /** 224 * Adds a person to which an attendee has delegated his or her 225 * responsibility. 226 * @param uri the delegatee (typically an email URI, e.g. 227 * "mailto:janedoe@example.com") 228 * @see <a href="http://tools.ietf.org/html/rfc5545#page-17">RFC 5545 229 * p.17-8</a> 230 */ 231 public void addDelegatedTo(String uri) { 232 put(DELEGATED_TO, uri); 233 } 234 235 /** 236 * Removes a person to which an attendee has delegated his or her 237 * responsibility. 238 * @param uri the delegatee to remove (typically an email URI, e.g. 239 * "mailto:janedoe@example.com") 240 * @see <a href="http://tools.ietf.org/html/rfc5545#page-17">RFC 5545 241 * p.17-8</a> 242 */ 243 public void removeDelegatedTo(String uri) { 244 remove(DELEGATED_TO, uri); 245 } 246 247 /** 248 * Removes everyone to which an attendee has delegated his or her 249 * responsibility. 250 * @see <a href="http://tools.ietf.org/html/rfc5545#page-17">RFC 5545 251 * p.17-8</a> 252 */ 253 public void removeDelegatedTo() { 254 removeAll(DELEGATED_TO); 255 } 256 257 /** 258 * Gets a URI that contains additional information about the person. 259 * @return the URI (e.g. an LDAP URI) or null if not set 260 * @see <a href="http://tools.ietf.org/html/rfc5545#page-18">RFC 5545 261 * p.18</a> 262 */ 263 public String getDirectoryEntry() { 264 return first(DIR); 265 } 266 267 /** 268 * Sets a URI that contains additional information about the person. 269 * @param uri the URI (e.g. an LDAP URI) or null to remove 270 * @see <a href="http://tools.ietf.org/html/rfc5545#page-18">RFC 5545 271 * p.18</a> 272 */ 273 public void setDirectoryEntry(String uri) { 274 replace(DIR, uri); 275 } 276 277 /** 278 * Gets the encoding of the property value (for example, "base64"). 279 * @return the encoding or null if not set 280 * @see <a href="http://tools.ietf.org/html/rfc5545#page-18">RFC 5545 281 * p.18-9</a> 282 */ 283 public Encoding getEncoding() { 284 String value = first(ENCODING); 285 return (value == null) ? null : Encoding.get(value); 286 } 287 288 /** 289 * Sets the encoding of the property value (for example, "base64"). 290 * @param encoding the encoding or null to remove 291 * @see <a href="http://tools.ietf.org/html/rfc5545#page-18">RFC 5545 292 * p.18-9</a> 293 */ 294 public void setEncoding(Encoding encoding) { 295 replace(ENCODING, (encoding == null) ? null : encoding.getValue()); 296 } 297 298 /** 299 * Gets the content-type of the property's value. 300 * @return the content type (e.g. "image/png") or null if not set 301 * @see <a href="http://tools.ietf.org/html/rfc5545#page-19">RFC 5545 302 * p.19-20</a> 303 */ 304 public String getFormatType() { 305 return first(FMTTYPE); 306 } 307 308 /** 309 * Sets the content-type of the property's value. 310 * @param formatType the content type (e.g. "image/png") or null to remove 311 * @see <a href="http://tools.ietf.org/html/rfc5545#page-19">RFC 5545 312 * p.19-20</a> 313 */ 314 public void setFormatType(String formatType) { 315 replace(FMTTYPE, formatType); 316 } 317 318 /** 319 * Gets the person's status over the time periods that are specified in a 320 * {@link FreeBusy} property (for example, "free" or "busy"). If not set, 321 * the user should be considered "busy". 322 * @return the type or null if not set 323 * @see <a href="http://tools.ietf.org/html/rfc5545#page-20">RFC 5545 324 * p.20</a> 325 */ 326 public FreeBusyType getFreeBusyType() { 327 String value = first(FBTYPE); 328 return (value == null) ? null : FreeBusyType.get(value); 329 } 330 331 /** 332 * Sets the person's status over the time periods that are specified in a 333 * {@link FreeBusy} property (for example, "free" or "busy"). If not set, 334 * the user should be considered "busy". 335 * @param fbType the type or null to remove 336 * @see <a href="http://tools.ietf.org/html/rfc5545#page-20">RFC 5545 337 * p.20</a> 338 */ 339 public void setFreeBusyType(FreeBusyType fbType) { 340 replace(FBTYPE, (fbType == null) ? null : fbType.getValue()); 341 } 342 343 /** 344 * Gets the language that the property value is written in. 345 * @return the language (e.g. "en" for English) or null if not set 346 * @see <a href="http://tools.ietf.org/html/rfc5545#page-21">RFC 5545 347 * p.21</a> 348 */ 349 public String getLanguage() { 350 return first(LANGUAGE); 351 } 352 353 /** 354 * Sets the language that the property value is written in. 355 * @param language the language (e.g. "en" for English) or null to remove 356 * @see <a href="http://tools.ietf.org/html/rfc5545#page-21">RFC 5545 357 * p.21</a> 358 */ 359 public void setLanguage(String language) { 360 replace(LANGUAGE, language); 361 } 362 363 /** 364 * Adds a group that an attendee is a member of. 365 * @param uri the group URI (typically, an email address URI, e.g. 366 * "mailto:mailinglist@example.com") 367 * @see <a href="http://tools.ietf.org/html/rfc5545#page-21">RFC 5545 368 * p.21-2</a> 369 */ 370 public void addMember(String uri) { 371 put(MEMBER, uri); 372 } 373 374 /** 375 * Gets the groups that an attendee is a member of. 376 * @return the group URIs (typically, these are email address URIs, e.g. 377 * "mailto:mailinglist@example.com") 378 * @see <a href="http://tools.ietf.org/html/rfc5545#page-21">RFC 5545 379 * p.21-2</a> 380 */ 381 public List<String> getMembers() { 382 return get(MEMBER); 383 } 384 385 /** 386 * Removes a group that an attendee is a member of. 387 * @param uri the group URI to remove (typically, an email address URI, e.g. 388 * "mailto:mailinglist@example.com") 389 * @see <a href="http://tools.ietf.org/html/rfc5545#page-21">RFC 5545 390 * p.21-2</a> 391 */ 392 public void removeMember(String uri) { 393 remove(MEMBER, uri); 394 } 395 396 /** 397 * Removes all groups that an attendee is a member of. 398 * @see <a href="http://tools.ietf.org/html/rfc5545#page-21">RFC 5545 399 * p.21-2</a> 400 */ 401 public void removeMembers() { 402 removeAll(MEMBER); 403 } 404 405 /** 406 * Gets the effective range of recurrence instances from the instance 407 * specified by a {@link RecurrenceId} property. 408 * @return the range or null if not set 409 * @see <a href="http://tools.ietf.org/html/rfc5545#page-23">RFC 5545 410 * p.23-4</a> 411 */ 412 public Range getRange() { 413 String value = first(RANGE); 414 return (value == null) ? null : Range.get(value); 415 } 416 417 /** 418 * Sets the effective range of recurrence instances from the instance 419 * specified by a {@link RecurrenceId} property. 420 * @param range the range or null to remove 421 * @see <a href="http://tools.ietf.org/html/rfc5545#page-23">RFC 5545 422 * p.23-4</a> 423 */ 424 public void setRange(Range range) { 425 replace(RANGE, (range == null) ? null : range.getValue()); 426 } 427 428 /** 429 * Gets the date-time field that the duration in a {@link Trigger} property 430 * is relative to. 431 * @return the field or null if not set 432 * @see <a href="http://tools.ietf.org/html/rfc5545#page-24">RFC 5545 433 * p.24</a> 434 */ 435 public Related getRelated() { 436 String value = first(RELATED); 437 return (value == null) ? null : Related.get(value); 438 } 439 440 /** 441 * Sets the date-time field that the duration in a {@link Trigger} property 442 * is relative to. 443 * @param related the field or null to remove 444 * @see <a href="http://tools.ietf.org/html/rfc5545#page-24">RFC 5545 445 * p.24</a> 446 */ 447 public void setRelated(Related related) { 448 replace(RELATED, (related == null) ? null : related.getValue()); 449 } 450 451 /** 452 * Gets the relationship type of a {@link RelatedTo} property. 453 * @return the relationship type (e.g. "child") or null if not set 454 * @see <a href="http://tools.ietf.org/html/rfc5545#page-25">RFC 5545 455 * p.25</a> 456 */ 457 public RelationshipType getRelationshipType() { 458 String value = first(RELTYPE); 459 return (value == null) ? null : RelationshipType.get(value); 460 } 461 462 /** 463 * Sets the relationship type of a {@link RelatedTo} property. 464 * @param relationshipType the relationship type (e.g. "child") or null to 465 * remove 466 * @see <a href="http://tools.ietf.org/html/rfc5545#page-25">RFC 5545 467 * p.25</a> 468 */ 469 public void setRelationshipType(RelationshipType relationshipType) { 470 replace(RELTYPE, (relationshipType == null) ? null : relationshipType.getValue()); 471 } 472 473 /** 474 * Gets a person that is acting on behalf of the person defined in the 475 * property. 476 * @return a URI representing the person (typically, an email URI, e.g. 477 * "mailto:janedoe@example.com") or null if not set 478 * @see <a href="http://tools.ietf.org/html/rfc5545#page-27">RFC 5545 479 * p.27</a> 480 */ 481 public String getSentBy() { 482 return first(SENT_BY); 483 } 484 485 /** 486 * Sets a person that is acting on behalf of the person defined in the 487 * property. 488 * @param uri a URI representing the person (typically, an email URI, e.g. 489 * "mailto:janedoe@example.com") or null to remove 490 * @see <a href="http://tools.ietf.org/html/rfc5545#page-27">RFC 5545 491 * p.27</a> 492 */ 493 public void setSentBy(String uri) { 494 replace(SENT_BY, uri); 495 } 496 497 /** 498 * Gets the TZID property, which defines the timezone that this property is 499 * formatted in. It is either the ID of the VTIMEZONE component which 500 * contains the timezone definition, or globally unique ID (if it starts 501 * with a "/" character). 502 * @return the timezone ID or null if not set 503 * @see <a href="http://tools.ietf.org/html/rfc5545#page-27">RFC 5545 504 * p.27-8</a> 505 */ 506 public String getTimezoneId() { 507 return first(TZID); 508 } 509 510 /** 511 * Sets the TZID property, which defines the timezone that this property is 512 * formatted in. It is either the ID of the VTIMEZONE component which 513 * contains the timezone definition, or globally unique ID (if it starts 514 * with a "/" character). 515 * @param timezoneId the timezone identifier or null to remove 516 * @see <a href="http://tools.ietf.org/html/rfc5545#page-27">RFC 5545 517 * p.27-8</a> 518 */ 519 public void setTimezoneId(String timezoneId) { 520 replace(TZID, timezoneId); 521 } 522 523 /** 524 * Gets the data type of the property's value (for example, "text" or 525 * "datetime"). 526 * @return the data type or null if not set 527 * @see <a href="http://tools.ietf.org/html/rfc5545#page-29">RFC 5545 528 * p.29-50</a> 529 */ 530 public ICalDataType getValue() { 531 String value = first(VALUE); 532 return (value == null) ? null : ICalDataType.get(value); 533 } 534 535 /** 536 * Sets the data type of the property's value (for example, "text" or 537 * "datetime"). 538 * @param value the data type or null to remove 539 * @see <a href="http://tools.ietf.org/html/rfc5545#page-29">RFC 5545 540 * p.29-50</a> 541 */ 542 public void setValue(ICalDataType value) { 543 replace(VALUE, (value == null) ? null : value.getName()); 544 } 545 546 /** 547 * Checks this parameters list for data consistency problems or deviations 548 * from the spec. These problems will not prevent the iCalendar object from 549 * being written to a data stream, but may prevent it from being parsed 550 * correctly by the consuming application. 551 * @param version the version to validate against 552 * @return a list of warnings or an empty list if no problems were found 553 */ 554 public List<Warning> validate(ICalVersion version) { 555 List<Warning> warnings = new ArrayList<Warning>(0); 556 557 final int nonStandardCode = 1, deprecated = 47; 558 559 String value = first(RSVP); 560 if (value != null) { 561 value = value.toLowerCase(); 562 List<String> validValues = Arrays.asList("true", "false", "yes", "no"); 563 if (!validValues.contains(value)) { 564 warnings.add(Warning.validate(nonStandardCode, RSVP, value, validValues)); 565 } 566 } 567 568 value = first(CUTYPE); 569 if (value != null && CalendarUserType.find(value) == null) { 570 warnings.add(Warning.validate(nonStandardCode, CUTYPE, value, CalendarUserType.all())); 571 } 572 573 value = first(ENCODING); 574 if (value != null && Encoding.find(value) == null) { 575 warnings.add(Warning.validate(nonStandardCode, ENCODING, value, Encoding.all())); 576 } 577 578 value = first(FBTYPE); 579 if (value != null && FreeBusyType.find(value) == null) { 580 warnings.add(Warning.validate(nonStandardCode, FBTYPE, value, FreeBusyType.all())); 581 } 582 583 value = first(PARTSTAT); 584 if (value != null && ParticipationStatus.find(value) == null) { 585 warnings.add(Warning.validate(nonStandardCode, PARTSTAT, value, ParticipationStatus.all())); 586 } 587 588 value = first(RANGE); 589 if (value != null) { 590 Range range = Range.find(value); 591 592 if (range == null) { 593 warnings.add(Warning.validate(nonStandardCode, RANGE, value, Range.all())); 594 } 595 596 if (range == Range.THIS_AND_PRIOR && version == ICalVersion.V2_0) { 597 warnings.add(Warning.validate(deprecated, RANGE, value)); 598 } 599 } 600 601 value = first(RELATED); 602 if (value != null && Related.find(value) == null) { 603 warnings.add(Warning.validate(nonStandardCode, RELATED, value, Related.all())); 604 } 605 606 value = first(RELTYPE); 607 if (value != null && RelationshipType.find(value) == null) { 608 warnings.add(Warning.validate(nonStandardCode, RELTYPE, value, RelationshipType.all())); 609 } 610 611 value = first(ROLE); 612 if (value != null && Role.find(value) == null) { 613 warnings.add(Warning.validate(nonStandardCode, ROLE, value, Role.all())); 614 } 615 616 value = first(VALUE); 617 if (value != null && ICalDataType.find(value) == null) { 618 warnings.add(Warning.validate(nonStandardCode, VALUE, value, ICalDataType.all())); 619 } 620 621 return warnings; 622 } 623 624 @Override 625 protected String sanitizeKey(String key) { 626 return (key == null) ? null : key.toUpperCase(); 627 } 628}