001package ezvcard.parameter; 002 003import java.nio.charset.Charset; 004import java.nio.charset.IllegalCharsetNameException; 005import java.nio.charset.UnsupportedCharsetException; 006import java.util.AbstractList; 007import java.util.ArrayList; 008import java.util.Arrays; 009import java.util.Collections; 010import java.util.EnumSet; 011import java.util.HashMap; 012import java.util.List; 013import java.util.Map; 014import java.util.Set; 015 016import com.github.mangstadt.vinnie.SyntaxStyle; 017import com.github.mangstadt.vinnie.validate.AllowedCharacters; 018import com.github.mangstadt.vinnie.validate.VObjectValidator; 019 020import ezvcard.Messages; 021import ezvcard.VCardDataType; 022import ezvcard.VCardVersion; 023import ezvcard.ValidationWarning; 024import ezvcard.property.Address; 025import ezvcard.property.ClientPidMap; 026import ezvcard.property.Email; 027import ezvcard.property.Note; 028import ezvcard.property.Organization; 029import ezvcard.property.Photo; 030import ezvcard.property.SortString; 031import ezvcard.property.Sound; 032import ezvcard.property.StructuredName; 033import ezvcard.util.GeoUri; 034import ezvcard.util.ListMultimap; 035 036/* 037 Copyright (c) 2012-2023, Michael Angstadt 038 All rights reserved. 039 040 Redistribution and use in source and binary forms, with or without 041 modification, are permitted provided that the following conditions are met: 042 043 1. Redistributions of source code must retain the above copyright notice, this 044 list of conditions and the following disclaimer. 045 2. Redistributions in binary form must reproduce the above copyright notice, 046 this list of conditions and the following disclaimer in the documentation 047 and/or other materials provided with the distribution. 048 049 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 050 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 051 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 052 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 053 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 054 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 055 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 056 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 057 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 058 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 059 060 The views and conclusions contained in the software and documentation are those 061 of the authors and should not be interpreted as representing official policies, 062 either expressed or implied, of the FreeBSD Project. 063 */ 064 065/** 066 * Stores the parameters (also known as "sub types") that belong to a property. 067 * @author Michael Angstadt 068 */ 069public class VCardParameters extends ListMultimap<String, String> { 070 /** 071 * <p> 072 * Used to specify that the property value is an alternative representation 073 * of another property value. 074 * </p> 075 * <p> 076 * In the example below, the first three {@link Note} properties have the 077 * same ALTID. This means that they each contain the same value, but in 078 * different forms. In this case, each property value is written in a 079 * different language. The other {@link Note} properties in the example have 080 * different (or absent) ALTID values, which means they are not associated 081 * with the top three. 082 * </p> 083 * 084 * <pre> 085 * NOTE;ALTID=1;LANGUAGE=en:Hello world! 086 * NOTE;ALTID=1;LANGUAGE=fr:Bonjour tout le monde! 087 * NOTE;ALTID=1;LANGUAGE=es:¡Hola, mundo! 088 * NOTE;ALTID=2;LANGUAGE=de:Meine Lieblingsfarbe ist blau. 089 * NOTE;ALTID=2;LANGUAGE=en:My favorite color is blue. 090 * NOTE:This vCard will self-destruct in 5 seconds. 091 * </pre> 092 * 093 * <p> 094 * <b>Supported versions:</b> {@code 4.0} 095 * </p> 096 * @see <a href="http://tools.ietf.org/html/rfc6350#page-18">RFC 6350 097 * p.18</a> 098 */ 099 public static final String ALTID = "ALTID"; 100 101 /** 102 * <p> 103 * Defines the type of calendar that is used in a date or date-time property 104 * value (for example, "gregorian"). 105 * </p> 106 * <p> 107 * <b>Supported versions:</b> {@code 4.0} 108 * </p> 109 * @see <a href="http://tools.ietf.org/html/rfc6350#page-20">RFC 6350 110 * p.20</a> 111 */ 112 public static final String CALSCALE = "CALSCALE"; 113 114 /** 115 * <p> 116 * Defines the character set that the property value is encoded in (for 117 * example, "UTF-8"). Typically, this is only used in 2.1 vCards when the 118 * property value is encoded in quoted-printable encoding. 119 * </p> 120 * <p> 121 * <b>Supported versions:</b> {@code 2.1} 122 * </p> 123 * @see <a href="http://www.imc.org/pdi/vcard-21.doc">vCard 2.1 p.20</a> 124 */ 125 public static final String CHARSET = "CHARSET"; 126 127 /** 128 * <p> 129 * This parameter is used when the property value is encoded in a form other 130 * than plain text (for example, "base64"). 131 * </p> 132 * <p> 133 * <b>Supported versions:</b> {@code 2.1, 3.0} 134 * </p> 135 * @see <a href="http://tools.ietf.org/html/rfc2426">RFC 2426</a> 136 * @see <a href="http://www.imc.org/pdi/vcard-21.doc">vCard 2.1 p.19</a> 137 */ 138 public static final String ENCODING = "ENCODING"; 139 140 /** 141 * <p> 142 * Used to associate global positioning information with the property. It 143 * can be used with the {@link Address} property. 144 * </p> 145 * <p> 146 * <b>Supported versions:</b> {@code 4.0} 147 * </p> 148 * @see <a href="http://tools.ietf.org/html/rfc6350#page-22">RFC 6350 149 * p.22</a> 150 */ 151 public static final String GEO = "GEO"; 152 153 /** 154 * <p> 155 * Defines the sorted position of this property when it is grouped together 156 * with other properties of the same type. Properties with low INDEX values 157 * are put at the beginning of the sorted list. Properties with high INDEX 158 * values are put at the end of the list. 159 * </p> 160 * <p> 161 * <b>Supported versions:</b> {@code 4.0} 162 * </p> 163 * @see <a href="https://tools.ietf.org/html/rfc6715#page-7">RFC 6715 164 * p.7</a> 165 */ 166 public static final String INDEX = "INDEX"; 167 168 /** 169 * <p> 170 * Used by the {@link Address} property to define a mailing label for the 171 * address. 172 * </p> 173 * <p> 174 * <b>Supported versions:</b> {@code 4.0} 175 * </p> 176 * @see <a href="http://tools.ietf.org/html/rfc6350#page-33">RFC 6350 177 * p.33</a> 178 */ 179 public static final String LABEL = "LABEL"; 180 181 /** 182 * <p> 183 * Defines the language that the property value is written in (for example, 184 * "en" for English"). 185 * </p> 186 * <p> 187 * <b>Supported versions:</b> {@code 2.1, 3.0, 4.0} 188 * </p> 189 * @see <a href="http://tools.ietf.org/html/rfc6350#page-16">RFC 6350 190 * p.16</a> 191 * @see <a href="http://tools.ietf.org/html/rfc2426#page-6">RFC 2426 p.6</a> 192 * @see <a href="http://www.imc.org/pdi/vcard-21.doc">vCard 2.1 p.20</a> 193 */ 194 public static final String LANGUAGE = "LANGUAGE"; 195 196 /** 197 * <p> 198 * Used to define the skill or interest level the person has towards the 199 * topic defined by the property (for example, "beginner"). Its value varies 200 * depending on the property. 201 * </p> 202 * <p> 203 * <b>Supported versions:</b> {@code 4.0} 204 * </p> 205 * @see <a href="https://tools.ietf.org/html/rfc6715#page-8">RFC 6715 206 * p.8</a> 207 */ 208 public static final String LEVEL = "LEVEL"; 209 210 /** 211 * <p> 212 * Used in properties that have a URL as a value, such as {@link Photo} and 213 * {@link Sound}. It defines the content type of the referenced resource 214 * (for example, "image/png" for a PNG image). 215 * </p> 216 * <p> 217 * <b>Supported versions:</b> {@code 4.0} 218 * </p> 219 * @see <a href="http://tools.ietf.org/html/rfc6350#page-20">RFC 6350 220 * p.20</a> 221 */ 222 public static final String MEDIATYPE = "MEDIATYPE"; 223 224 /** 225 * <p> 226 * Defines a property ID. PIDs can exist on any property where multiple 227 * instances are allowed (such as {@link Email} or {@link Address}, but not 228 * {@link StructuredName} because only 1 instance of this property is 229 * allowed per vCard). 230 * </p> 231 * <p> 232 * When used in conjunction with the {@link ClientPidMap} property, it 233 * allows an individual property instance to be uniquely identifiable. This 234 * feature is made use of when two different versions of the same vCard have 235 * to be merged together (called "synchronizing"). 236 * </p> 237 * <p> 238 * <b>Supported versions:</b> {@code 4.0} 239 * </p> 240 * @see <a href="http://tools.ietf.org/html/rfc6350#page-19">RFC 6350 241 * p.19</a> 242 */ 243 public static final String PID = "PID"; 244 245 /** 246 * <p> 247 * Defines the preference value. The lower this number is, the more 248 * "preferred" the property instance is compared with other properties of 249 * the same type. If a property doesn't have a preference value, then it is 250 * considered the least preferred. 251 * </p> 252 * <p> 253 * In the vCard below, the {@link Address} on the second row is the most 254 * preferred because it has the lowest PREF value. 255 * </p> 256 * 257 * <pre> 258 * ADR;TYPE=work;PREF=2:;;1600 Amphitheatre Parkway;Mountain View;CA;94043 259 * ADR;TYPE=work;PREF=1:;;One Microsoft Way;Redmond;WA;98052 260 * ADR;TYPE=home:;;123 Maple St;Hometown;KS;12345 261 * </pre> 262 * 263 * <p> 264 * <b>Supported versions:</b> {@code 4.0} 265 * </p> 266 * @see <a href="http://tools.ietf.org/html/rfc6350#page-17">RFC 6350 267 * p.17</a> 268 */ 269 public static final String PREF = "PREF"; 270 271 /** 272 * <p> 273 * This parameter defines how the vCard should be sorted amongst other 274 * vCards. For example, this can be used if the person's last name (defined 275 * in the {@link StructuredName} property) starts with characters that 276 * should be ignored during sorting (such as "d'Aboville"). 277 * </p> 278 * <p> 279 * This parameter can be used with the {@link StructuredName} and 280 * {@link Organization} properties. 2.1 and 3.0 vCards should use the 281 * {@link SortString} property instead. 282 * </p> 283 * <p> 284 * This parameter can be multi-valued. The first value is the primary sort 285 * keyword (such as the person's last name), the second value is the 286 * secondary sort keyword (such as the person's first name), etc. 287 * </p> 288 * <p> 289 * <b>Supported versions:</b> {@code 4.0} 290 * </p> 291 * @see <a href="http://tools.ietf.org/html/rfc6350#page-21">RFC 6350 292 * p.21</a> 293 */ 294 public static final String SORT_AS = "SORT-AS"; 295 296 /** 297 * <p> 298 * The meaning of this parameter varies depending on the property. 299 * </p> 300 * <p> 301 * <b>Supported versions:</b> {@code 2.1, 3.0, 4.0} 302 * </p> 303 * @see <a href="http://tools.ietf.org/html/rfc6350#page-19">RFC 6350 304 * p.19</a> 305 * @see <a href="http://tools.ietf.org/html/rfc2426#page-6">RFC 2426 p.6</a> 306 * @see <a href="http://www.imc.org/pdi/vcard-21.doc">vCard 2.1</a> 307 */ 308 public static final String TYPE = "TYPE"; 309 310 /** 311 * <p> 312 * Used to associate timezone information with an {@link Address} property 313 * (for example, "America/New_York" to indicate that an address adheres to 314 * that timezone). 315 * </p> 316 * <p> 317 * <b>Supported versions:</b> {@code 4.0} 318 * </p> 319 * @see <a href="http://tools.ietf.org/html/rfc6350#page-22">RFC 6350 320 * p.22</a> 321 */ 322 public static final String TZ = "TZ"; 323 324 /** 325 * <p> 326 * Defines the data type of the property value (for example, "date" if the 327 * property value is a date without a time component). It is used if the 328 * property accepts multiple values that have different data types. 329 * </p> 330 * <p> 331 * <b>Supported versions:</b> {@code 2.1, 3.0, 4.0} 332 * </p> 333 * @see <a href="http://tools.ietf.org/html/rfc6350#page-16">RFC 6350 334 * p.16</a> 335 * @see <a href="http://tools.ietf.org/html/rfc2426#page-6">RFC 2426 p.6</a> 336 * @see <a href="http://www.imc.org/pdi/vcard-21.doc">vCard 2.1 p.20</a> 337 */ 338 public static final String VALUE = "VALUE"; 339 340 private static final Map<String, Set<VCardVersion>> supportedVersions; 341 static { 342 Map<String, Set<VCardVersion>> m = new HashMap<>(); 343 m.put(ALTID, EnumSet.of(VCardVersion.V4_0)); 344 m.put(CALSCALE, EnumSet.of(VCardVersion.V4_0)); 345 m.put(CHARSET, EnumSet.of(VCardVersion.V2_1)); 346 m.put(GEO, EnumSet.of(VCardVersion.V4_0)); 347 m.put(INDEX, EnumSet.of(VCardVersion.V4_0)); 348 349 /* 350 * Don't check LABEL because this is removed and converted to LABEL 351 * properties for 2.1 and 3.0 vCards. 352 */ 353 //m.put(LABEL, EnumSet.of(VCardVersion.V4_0)); 354 355 m.put(LEVEL, EnumSet.of(VCardVersion.V4_0)); 356 m.put(MEDIATYPE, EnumSet.of(VCardVersion.V4_0)); 357 m.put(PID, EnumSet.of(VCardVersion.V4_0)); 358 359 /* 360 * Don't check PREF because this is removed and converted to "TYPE=PREF" 361 * for 2.1 and 3.0 vCards. 362 */ 363 //m.put(PREF, EnumSet.of(VCardVersion.V4_0)); 364 365 m.put(SORT_AS, EnumSet.of(VCardVersion.V4_0)); 366 m.put(TZ, EnumSet.of(VCardVersion.V4_0)); 367 368 supportedVersions = Collections.unmodifiableMap(m); 369 } 370 371 /** 372 * Creates a list of parameters. 373 */ 374 public VCardParameters() { 375 //empty 376 } 377 378 /** 379 * Creates a copy of an existing parameter list. 380 * @param orig the object to copy 381 */ 382 public VCardParameters(VCardParameters orig) { 383 super(orig); 384 } 385 386 /** 387 * <p> 388 * Creates a parameter list that is backed by the given map. Any changes 389 * made to the given map will effect the parameter list and vice versa. 390 * </p> 391 * <p> 392 * Care must be taken to ensure that the given map's keys are all in 393 * uppercase. 394 * </p> 395 * <p> 396 * To avoid problems, it is highly recommended that the given map NOT be 397 * modified by anything other than this {@link VCardParameters} class after 398 * being passed into this constructor. 399 * </p> 400 * @param map the map 401 */ 402 public VCardParameters(Map<String, List<String>> map) { 403 super(map); 404 } 405 406 /** 407 * <p> 408 * Gets the ALTID parameter value. 409 * </p> 410 * <p> 411 * This parameter is used to specify that the property value is an 412 * alternative representation of another property value. 413 * </p> 414 * <p> 415 * In the example below, the first three {@link Note} properties have the 416 * same ALTID. This means that they each contain the same value, but in 417 * different forms. In this case, each property value is written in a 418 * different language. The other {@link Note} properties in the example have 419 * different (or absent) ALTID values, which means they are not associated 420 * with the top three. 421 * </p> 422 * 423 * <pre> 424 * NOTE;ALTID=1;LANGUAGE=en:Hello world! 425 * NOTE;ALTID=1;LANGUAGE=fr:Bonjour tout le monde! 426 * NOTE;ALTID=1;LANGUAGE=es:¡Hola, mundo! 427 * NOTE;ALTID=2;LANGUAGE=de:Meine Lieblingsfarbe ist blau. 428 * NOTE;ALTID=2;LANGUAGE=en:My favorite color is blue. 429 * NOTE:This vCard will self-destruct in 5 seconds. 430 * </pre> 431 * 432 * <p> 433 * <b>Supported versions:</b> {@code 4.0} 434 * </p> 435 * @return the ALTID or null if not set 436 * @see <a href="http://tools.ietf.org/html/rfc6350#page-18">RFC 6350 437 * p.18</a> 438 */ 439 public String getAltId() { 440 return first(ALTID); 441 } 442 443 /** 444 * <p> 445 * Sets the ALTID parameter value. 446 * </p> 447 * <p> 448 * This parameter is used to specify that the property value is an 449 * alternative representation of another property value. 450 * </p> 451 * <p> 452 * In the example below, the first three {@link Note} properties have the 453 * same ALTID. This means that they each contain the same value, but in 454 * different forms. In this case, each property value is written in a 455 * different language. The other {@link Note} properties in the example have 456 * different (or absent) ALTID values, which means they are not associated 457 * with the top three. 458 * </p> 459 * 460 * <pre> 461 * NOTE;ALTID=1;LANGUAGE=en:Hello world! 462 * NOTE;ALTID=1;LANGUAGE=fr:Bonjour tout le monde! 463 * NOTE;ALTID=1;LANGUAGE=es:¡Hola, mundo! 464 * NOTE;ALTID=2;LANGUAGE=de:Meine Lieblingsfarbe ist blau. 465 * NOTE;ALTID=2;LANGUAGE=en:My favorite color is blue. 466 * NOTE:This vCard will self-destruct in 5 seconds. 467 * </pre> 468 * 469 * <p> 470 * <b>Supported versions:</b> {@code 4.0} 471 * </p> 472 * @param altId the ALTID or null to remove 473 * @see <a href="http://tools.ietf.org/html/rfc6350#page-18">RFC 6350 474 * p.18</a> 475 */ 476 public void setAltId(String altId) { 477 replace(ALTID, altId); 478 } 479 480 /** 481 * <p> 482 * Gets the CALSCALE (calendar scale) parameter value. 483 * </p> 484 * <p> 485 * This parameter defines the type of calendar that is used in a date or 486 * date-time property value (for example, "gregorian"). 487 * </p> 488 * <p> 489 * <b>Supported versions:</b> {@code 4.0} 490 * </p> 491 * @return the type of calendar or null if not set 492 * @see <a href="http://tools.ietf.org/html/rfc6350#page-20">RFC 6350 493 * p.20</a> 494 */ 495 public Calscale getCalscale() { 496 String value = first(CALSCALE); 497 return (value == null) ? null : Calscale.get(value); 498 } 499 500 /** 501 * <p> 502 * Sets the CALSCALE (calendar scale) parameter value. 503 * </p> 504 * <p> 505 * This parameter defines the type of calendar that is used in a date or 506 * date-time property value (for example, "gregorian"). 507 * </p> 508 * <p> 509 * <b>Supported versions:</b> {@code 4.0} 510 * </p> 511 * @param calscale the type of calendar or null to remove 512 * @see <a href="http://tools.ietf.org/html/rfc6350#page-20">RFC 6350 513 * p.20</a> 514 */ 515 public void setCalscale(Calscale calscale) { 516 replace(CALSCALE, (calscale == null) ? null : calscale.getValue()); 517 } 518 519 /** 520 * <p> 521 * Gets the CHARSET (character set) parameter value. 522 * </p> 523 * <p> 524 * This parameter defines the character set that the property value is 525 * encoded in (for example, "UTF-8"). Typically, this is only used in 2.1 526 * vCards when the property value is encoded in quoted-printable encoding. 527 * </p> 528 * <p> 529 * <b>Supported versions:</b> {@code 2.1} 530 * </p> 531 * @return the character set or null if not set 532 * @see <a href="http://www.imc.org/pdi/vcard-21.doc">vCard 2.1 p.20</a> 533 */ 534 public String getCharset() { 535 return first(CHARSET); 536 } 537 538 /** 539 * <p> 540 * Sets the CHARSET (character set) parameter value. 541 * </p> 542 * <p> 543 * This parameter defines the character set that the property value is 544 * encoded in (for example, "UTF-8"). Typically, this is only used in 2.1 545 * vCards when the property value is encoded in quoted-printable encoding. 546 * </p> 547 * <p> 548 * <b>Supported versions:</b> {@code 2.1} 549 * </p> 550 * @param charset the character set or null to remove 551 * @see <a href="http://www.imc.org/pdi/vcard-21.doc">vCard 2.1 p.20</a> 552 */ 553 public void setCharset(String charset) { 554 replace(CHARSET, charset); 555 } 556 557 /** 558 * <p> 559 * Gets the ENCODING parameter value. 560 * </p> 561 * <p> 562 * This parameter is used when the property value is encoded in a form other 563 * than plain text (for example, "base64"). 564 * </p> 565 * <p> 566 * <b>Supported versions:</b> {@code 2.1, 3.0} 567 * </p> 568 * @return the encoding or null if not set 569 * @see <a href="http://tools.ietf.org/html/rfc2426">RFC 2426</a> 570 * @see <a href="http://www.imc.org/pdi/vcard-21.doc">vCard 2.1 p.19</a> 571 */ 572 public Encoding getEncoding() { 573 String value = first(ENCODING); 574 return (value == null) ? null : Encoding.get(value); 575 } 576 577 /** 578 * <p> 579 * Sets the ENCODING parameter value. 580 * </p> 581 * <p> 582 * This parameter is used when the property value is encoded in a form other 583 * than plain text (for example, "base64"). 584 * </p> 585 * <p> 586 * <b>Supported versions:</b> {@code 2.1, 3.0} 587 * </p> 588 * @param encoding the encoding or null to remove 589 * @see <a href="http://tools.ietf.org/html/rfc2426">RFC 2426</a> 590 * @see <a href="http://www.imc.org/pdi/vcard-21.doc">vCard 2.1 p.19</a> 591 */ 592 public void setEncoding(Encoding encoding) { 593 replace(ENCODING, (encoding == null) ? null : encoding.getValue()); 594 } 595 596 /** 597 * <p> 598 * Gets the GEO parameter value. 599 * </p> 600 * <p> 601 * This parameter is used to associate global positioning information with 602 * the property. It can be used with the {@link Address} property. 603 * </p> 604 * <p> 605 * <b>Supported versions:</b> {@code 4.0} 606 * </p> 607 * @return the geo URI or null if not set 608 * @throws IllegalStateException if the parameter value is malformed and 609 * cannot be parsed into a geo URI. If this happens, you may use the 610 * {@link ListMultimap#get(Object) get()} method to retrieve its raw value. 611 * @see <a href="http://tools.ietf.org/html/rfc6350#page-22">RFC 6350 612 * p.22</a> 613 */ 614 public GeoUri getGeo() { 615 String value = first(GEO); 616 if (value == null) { 617 return null; 618 } 619 620 try { 621 return GeoUri.parse(value); 622 } catch (IllegalArgumentException e) { 623 throw new IllegalStateException(Messages.INSTANCE.getExceptionMessage(15, GEO), e); 624 } 625 } 626 627 /** 628 * <p> 629 * Sets the GEO parameter value. 630 * </p> 631 * <p> 632 * This parameter is used to associate global positioning information with 633 * the property. It can be used with the {@link Address} property. 634 * </p> 635 * <p> 636 * <b>Supported versions:</b> {@code 4.0} 637 * </p> 638 * @param uri the geo URI or null to remove 639 * @see <a href="http://tools.ietf.org/html/rfc6350#page-22">RFC 6350 640 * p.22</a> 641 */ 642 public void setGeo(GeoUri uri) { 643 replace(GEO, (uri == null) ? null : uri.toString()); 644 } 645 646 /** 647 * <p> 648 * Gets the INDEX parameter value. 649 * </p> 650 * <p> 651 * This parameter defines the sorted position of this property when it is 652 * grouped together with other properties of the same type. Properties with 653 * low INDEX values are put at the beginning of the sorted list. Properties 654 * with high INDEX values are put at the end of the list. 655 * </p> 656 * <p> 657 * <b>Supported versions:</b> {@code 4.0} 658 * </p> 659 * @return the index or null if not set 660 * @throws IllegalStateException if the parameter value cannot be parsed as 661 * an integer. If this happens, you may use the 662 * {@link ListMultimap#get(Object) get()} method to retrieve its raw value. 663 * @see <a href="https://tools.ietf.org/html/rfc6715#page-7">RFC 6715 664 * p.7</a> 665 */ 666 public Integer getIndex() { 667 String index = first(INDEX); 668 if (index == null) { 669 return null; 670 } 671 672 try { 673 return Integer.valueOf(index); 674 } catch (NumberFormatException e) { 675 throw new IllegalStateException(Messages.INSTANCE.getExceptionMessage(15, INDEX), e); 676 } 677 } 678 679 /** 680 * <p> 681 * Sets the INDEX parameter value. 682 * </p> 683 * <p> 684 * This parameter defines the sorted position of this property when it is 685 * grouped together with other properties of the same type. Properties with 686 * low INDEX values are put at the beginning of the sorted list. Properties 687 * with high INDEX values are put at the end of the list. 688 * </p> 689 * <p> 690 * <b>Supported versions:</b> {@code 4.0} 691 * </p> 692 * @param index the index or null to remove 693 * @see <a href="https://tools.ietf.org/html/rfc6715#page-7">RFC 6715 694 * p.7</a> 695 */ 696 public void setIndex(Integer index) { 697 replace(INDEX, (index == null) ? null : index.toString()); 698 } 699 700 /** 701 * <p> 702 * Gets the LABEL parameter value. 703 * </p> 704 * <p> 705 * This parameter is used by the {@link Address} property to define a 706 * mailing label for the address. 707 * </p> 708 * <p> 709 * <b>Supported versions:</b> {@code 4.0} 710 * </p> 711 * @return the label or null if not set 712 * @see <a href="http://tools.ietf.org/html/rfc6350#page-33">RFC 6350 713 * p.33</a> 714 */ 715 public String getLabel() { 716 return first(LABEL); 717 } 718 719 /** 720 * <p> 721 * Sets the LABEL parameter value. 722 * </p> 723 * <p> 724 * This parameter is used by the {@link Address} property to define a 725 * mailing label for the address. 726 * </p> 727 * <p> 728 * <b>Supported versions:</b> {@code 4.0} 729 * </p> 730 * @param label the label or null to remove 731 * @see <a href="http://tools.ietf.org/html/rfc6350#page-33">RFC 6350 732 * p.33</a> 733 */ 734 public void setLabel(String label) { 735 replace(LABEL, label); 736 } 737 738 /** 739 * <p> 740 * Gets the LANGUAGE parameter value. 741 * </p> 742 * <p> 743 * This parameter defines the language that the property value is written in 744 * (for example, "en" for English"). 745 * </p> 746 * <p> 747 * <b>Supported versions:</b> {@code 2.1, 3.0, 4.0} 748 * </p> 749 * @return the language or null if not set 750 * @see <a href="http://tools.ietf.org/html/rfc6350#page-16">RFC 6350 751 * p.16</a> 752 * @see <a href="http://tools.ietf.org/html/rfc2426#page-6">RFC 2426 p.6</a> 753 * @see <a href="http://www.imc.org/pdi/vcard-21.doc">vCard 2.1 p.20</a> 754 */ 755 public String getLanguage() { 756 return first(LANGUAGE); 757 } 758 759 /** 760 * <p> 761 * Sets the LANGUAGE parameter value. 762 * </p> 763 * <p> 764 * This parameter defines the language that the property value is written in 765 * (for example, "en" for English"). 766 * </p> 767 * <p> 768 * <b>Supported versions:</b> {@code 2.1, 3.0, 4.0} 769 * </p> 770 * @param language the language or null to remove 771 * @see <a href="http://tools.ietf.org/html/rfc6350#page-16">RFC 6350 772 * p.16</a> 773 * @see <a href="http://tools.ietf.org/html/rfc2426#page-6">RFC 2426 p.6</a> 774 * @see <a href="http://www.imc.org/pdi/vcard-21.doc">vCard 2.1 p.20</a> 775 */ 776 public void setLanguage(String language) { 777 replace(LANGUAGE, language); 778 } 779 780 /** 781 * <p> 782 * Gets the LEVEL parameter value. 783 * </p> 784 * <p> 785 * This parameter is used to define the skill or interest level the person 786 * has towards the topic defined by the property (for example, "beginner"). 787 * Its value varies depending on the property. 788 * </p> 789 * <p> 790 * <b>Supported versions:</b> {@code 4.0} 791 * </p> 792 * @return the level or null if not set 793 * @see <a href="https://tools.ietf.org/html/rfc6715#page-8">RFC 6715 794 * p.8</a> 795 */ 796 public String getLevel() { 797 return first(LEVEL); 798 } 799 800 /** 801 * <p> 802 * Sets the LEVEL parameter value. 803 * </p> 804 * <p> 805 * This parameter is used to define the skill or interest level the person 806 * has towards the topic defined by the property (for example, "beginner"). 807 * Its value varies depending on the property. 808 * </p> 809 * <p> 810 * <b>Supported versions:</b> {@code 4.0} 811 * </p> 812 * @param level the level or null to remove 813 * @see <a href="https://tools.ietf.org/html/rfc6715#page-8">RFC 6715 814 * p.8</a> 815 */ 816 public void setLevel(String level) { 817 replace(LEVEL, level); 818 } 819 820 /** 821 * <p> 822 * Gets the MEDIATYPE parameter value. 823 * </p> 824 * <p> 825 * This parameter is used in properties that have a URL as a value, such as 826 * {@link Photo} and {@link Sound}. It defines the content type of the 827 * referenced resource (for example, "image/png" for a PNG image). 828 * </p> 829 * <p> 830 * <b>Supported versions:</b> {@code 4.0} 831 * </p> 832 * @return the media type or null if not set 833 * @see <a href="http://tools.ietf.org/html/rfc6350#page-20">RFC 6350 834 * p.20</a> 835 */ 836 public String getMediaType() { 837 return first(MEDIATYPE); 838 } 839 840 /** 841 * <p> 842 * Sets the MEDIATYPE parameter value. 843 * </p> 844 * <p> 845 * This parameter is used in properties that have a URL as a value, such as 846 * {@link Photo} and {@link Sound}. It defines the content type of the 847 * referenced resource (for example, "image/png" for a PNG image). 848 * </p> 849 * <p> 850 * <b>Supported versions:</b> {@code 4.0} 851 * </p> 852 * @param mediaType the media type or null to remove 853 * @see <a href="http://tools.ietf.org/html/rfc6350#page-20">RFC 6350 854 * p.20</a> 855 */ 856 public void setMediaType(String mediaType) { 857 replace(MEDIATYPE, mediaType); 858 } 859 860 /** 861 * <p> 862 * Gets the PID (property ID) parameter values. 863 * </p> 864 * <p> 865 * PIDs can exist on any property where multiple instances are allowed (such 866 * as {@link Email} or {@link Address}, but not {@link StructuredName} 867 * because only 1 instance of this property is allowed per vCard). 868 * </p> 869 * <p> 870 * When used in conjunction with the {@link ClientPidMap} property, it 871 * allows an individual property instance to be uniquely identifiable. This 872 * feature is made use of when two different versions of the same vCard have 873 * to be merged together (called "synchronizing"). 874 * </p> 875 * <p> 876 * Changes to the returned list will update the {@link VCardParameters} 877 * object, and vice versa. 878 * </p> 879 * <p> 880 * <b>Supported versions:</b> {@code 4.0} 881 * </p> 882 * @return the PIDs 883 * @throws IllegalStateException if one or more parameter values cannot be 884 * parsed as PIDs. If this happens, you may use the 885 * {@link ListMultimap#get(Object) get()} method to retrieve the raw values. 886 * @see <a href="http://tools.ietf.org/html/rfc6350#page-19">RFC 6350 887 * p.19</a> 888 */ 889 public List<Pid> getPids() { 890 return new VCardParameterList<Pid>(PID) { 891 @Override 892 protected String _asString(Pid value) { 893 return value.toString(); 894 } 895 896 @Override 897 protected Pid _asObject(String value) { 898 return Pid.valueOf(value); 899 } 900 901 @Override 902 protected IllegalStateException _exception(String value, Exception thrown) { 903 return new IllegalStateException(Messages.INSTANCE.getExceptionMessage(15, PID), thrown); 904 } 905 }; 906 } 907 908 /** 909 * <p> 910 * Adds a PID (property ID) parameter value. 911 * </p> 912 * <p> 913 * PIDs can exist on any property where multiple instances are allowed (such 914 * as {@link Email} or {@link Address}, but not {@link StructuredName} 915 * because only 1 instance of this property is allowed per vCard). 916 * </p> 917 * <p> 918 * When used in conjunction with the {@link ClientPidMap} property, it 919 * allows an individual property instance to be uniquely identifiable. This 920 * feature is made use of when two different versions of the same vCard have 921 * to be merged together (called "synchronizing"). 922 * </p> 923 * <p> 924 * <b>Supported versions:</b> {@code 4.0} 925 * </p> 926 * @param pid the PID to add 927 * @see <a href="http://tools.ietf.org/html/rfc6350#page-19">RFC 6350 928 * p.19</a> 929 */ 930 public void addPid(Pid pid) { 931 put(PID, pid.toString()); 932 } 933 934 /** 935 * <p> 936 * Removes a PID (property ID) parameter value. 937 * </p> 938 * <p> 939 * PIDs can exist on any property where multiple instances are allowed (such 940 * as {@link Email} or {@link Address}, but not {@link StructuredName} 941 * because only 1 instance of this property is allowed per vCard). 942 * </p> 943 * <p> 944 * When used in conjunction with the {@link ClientPidMap} property, it 945 * allows an individual property instance to be uniquely identifiable. This 946 * feature is made use of when two different versions of the same vCard have 947 * to be merged together (called "synchronizing"). 948 * </p> 949 * <p> 950 * <b>Supported versions:</b> {@code 4.0} 951 * </p> 952 * @param pid the PID to remove 953 * @see <a href="http://tools.ietf.org/html/rfc6350#page-19">RFC 6350 954 * p.19</a> 955 */ 956 public void removePid(Pid pid) { 957 String value = pid.toString(); 958 remove(PID, value); 959 } 960 961 /** 962 * <p> 963 * Removes all PID (property ID) parameter values. 964 * </p> 965 * <p> 966 * PIDs can exist on any property where multiple instances are allowed (such 967 * as {@link Email} or {@link Address}, but not {@link StructuredName} 968 * because only 1 instance of this property is allowed per vCard). 969 * </p> 970 * <p> 971 * When used in conjunction with the {@link ClientPidMap} property, it 972 * allows an individual property instance to be uniquely identifiable. This 973 * feature is made use of when two different versions of the same vCard have 974 * to be merged together (called "synchronizing"). 975 * </p> 976 * <p> 977 * <b>Supported versions:</b> {@code 4.0} 978 * </p> 979 * @see <a href="http://tools.ietf.org/html/rfc6350#page-19">RFC 6350 980 * p.19</a> 981 */ 982 public void removePids() { 983 removeAll(PID); 984 } 985 986 /** 987 * <p> 988 * Gets the PREF (preference) parameter value. 989 * </p> 990 * <p> 991 * The lower this number is, the more "preferred" the property instance is 992 * compared with other properties of the same type. If a property doesn't 993 * have a preference value, then it is considered the least preferred. 994 * </p> 995 * <p> 996 * In the vCard below, the {@link Address} on the second row is the most 997 * preferred because it has the lowest PREF value. 998 * </p> 999 * 1000 * <pre> 1001 * ADR;TYPE=work;PREF=2:;;1600 Amphitheatre Parkway;Mountain View;CA;94043 1002 * ADR;TYPE=work;PREF=1:;;One Microsoft Way;Redmond;WA;98052 1003 * ADR;TYPE=home:;;123 Maple St;Hometown;KS;12345 1004 * </pre> 1005 * 1006 * <p> 1007 * <b>Supported versions:</b> {@code 4.0} 1008 * </p> 1009 * @return the preference value or null if not set 1010 * @throws IllegalStateException if the parameter value cannot be parsed as 1011 * an integer. If this happens, you may use the 1012 * {@link ListMultimap#get(Object) get()} method to retrieve its raw value. 1013 * @see <a href="http://tools.ietf.org/html/rfc6350#page-17">RFC 6350 1014 * p.17</a> 1015 */ 1016 public Integer getPref() { 1017 String pref = first(PREF); 1018 if (pref == null) { 1019 return null; 1020 } 1021 1022 try { 1023 return Integer.valueOf(pref); 1024 } catch (NumberFormatException e) { 1025 throw new IllegalStateException(Messages.INSTANCE.getExceptionMessage(15, PREF), e); 1026 } 1027 } 1028 1029 /** 1030 * <p> 1031 * Sets the PREF (preference) parameter value. 1032 * </p> 1033 * <p> 1034 * The lower this number is, the more "preferred" the property instance is 1035 * compared with other properties of the same type. If a property doesn't 1036 * have a preference value, then it is considered the least preferred. 1037 * </p> 1038 * <p> 1039 * In the vCard below, the {@link Address} on the second row is the most 1040 * preferred because it has the lowest PREF value. 1041 * </p> 1042 * 1043 * <pre> 1044 * ADR;TYPE=work;PREF=2:;;1600 Amphitheatre Parkway;Mountain View;CA;94043 1045 * ADR;TYPE=work;PREF=1:;;One Microsoft Way;Redmond;WA;98052 1046 * ADR;TYPE=home:;;123 Maple St;Hometown;KS;12345 1047 * </pre> 1048 * 1049 * <p> 1050 * <b>Supported versions:</b> {@code 4.0} 1051 * </p> 1052 * @param pref the preference value or null to remove 1053 * @see <a href="http://tools.ietf.org/html/rfc6350#page-17">RFC 6350 1054 * p.17</a> 1055 */ 1056 public void setPref(Integer pref) { 1057 replace(PREF, (pref == null) ? null : pref.toString()); 1058 } 1059 1060 /** 1061 * <p> 1062 * Gets the SORT-AS parameter values. 1063 * </p> 1064 * <p> 1065 * This parameter defines how the vCard should be sorted amongst other 1066 * vCards. For example, this can be used if the person's last name (defined 1067 * in the {@link StructuredName} property) starts with characters that 1068 * should be ignored during sorting (such as "d'Aboville"). 1069 * </p> 1070 * <p> 1071 * This parameter can be used with the {@link StructuredName} and 1072 * {@link Organization} properties. 2.1 and 3.0 vCards should use the 1073 * {@link SortString} property instead. 1074 * </p> 1075 * <p> 1076 * This parameter can be multi-valued. The first value is the primary sort 1077 * keyword (such as the person's last name), the second value is the 1078 * secondary sort keyword (such as the person's first name), etc. 1079 * </p> 1080 * <p> 1081 * Changes to the returned list will update the {@link VCardParameters} 1082 * object, and vice versa. 1083 * </p> 1084 * <p> 1085 * <b>Supported versions:</b> {@code 4.0} 1086 * </p> 1087 * @return the sort strings 1088 * @see <a href="http://tools.ietf.org/html/rfc6350#page-21">RFC 6350 1089 * p.21</a> 1090 */ 1091 public List<String> getSortAs() { 1092 return get(SORT_AS); 1093 } 1094 1095 /** 1096 * <p> 1097 * Sets the SORT-AS parameter values. 1098 * </p> 1099 * <p> 1100 * This parameter defines how the vCard should be sorted amongst other 1101 * vCards. For example, this can be used if the person's last name (defined 1102 * in the {@link StructuredName} property) starts with characters that 1103 * should be ignored during sorting (such as "d'Aboville"). 1104 * </p> 1105 * <p> 1106 * This parameter can be used with the {@link StructuredName} and 1107 * {@link Organization} properties. 2.1 and 3.0 vCards should use the 1108 * {@link SortString} property instead. 1109 * </p> 1110 * <p> 1111 * This parameter can be multi-valued. The first value is the primary sort 1112 * keyword (such as the person's last name), the second value is the 1113 * secondary sort keyword (such as the person's first name), etc. 1114 * </p> 1115 * <p> 1116 * <b>Supported versions:</b> {@code 4.0} 1117 * </p> 1118 * @param sortStrings the sort strings or an empty parameter list to remove 1119 * @see <a href="http://tools.ietf.org/html/rfc6350#page-21">RFC 6350 1120 * p.21</a> 1121 */ 1122 public void setSortAs(String... sortStrings) { 1123 removeAll(SORT_AS); 1124 putAll(SORT_AS, Arrays.asList(sortStrings)); 1125 } 1126 1127 /** 1128 * <p> 1129 * Gets the TYPE parameter values. 1130 * </p> 1131 * <p> 1132 * The meaning of this parameter varies depending on the property. 1133 * </p> 1134 * <p> 1135 * Changes to the returned list will update the {@link VCardParameters} 1136 * object, and vice versa. 1137 * </p> 1138 * <p> 1139 * <b>Supported versions:</b> {@code 2.1, 3.0, 4.0} 1140 * </p> 1141 * @return the types 1142 * @see <a href="http://tools.ietf.org/html/rfc6350#page-19">RFC 6350 1143 * p.19</a> 1144 * @see <a href="http://tools.ietf.org/html/rfc2426#page-6">RFC 2426 p.6</a> 1145 * @see <a href="http://www.imc.org/pdi/vcard-21.doc">vCard 2.1</a> 1146 */ 1147 public List<String> getTypes() { 1148 return get(TYPE); 1149 } 1150 1151 /** 1152 * <p> 1153 * Gets the first TYPE parameter value. 1154 * </p> 1155 * <p> 1156 * The meaning of this parameter varies depending on the property. 1157 * </p> 1158 * <p> 1159 * <b>Supported versions:</b> {@code 2.1, 3.0, 4.0} 1160 * </p> 1161 * @return the type or null if not set 1162 * @see <a href="http://tools.ietf.org/html/rfc6350#page-19">RFC 6350 1163 * p.19</a> 1164 * @see <a href="http://tools.ietf.org/html/rfc2426#page-6">RFC 2426 p.6</a> 1165 * @see <a href="http://www.imc.org/pdi/vcard-21.doc">vCard 2.1</a> 1166 */ 1167 public String getType() { 1168 return first(TYPE); 1169 } 1170 1171 /** 1172 * <p> 1173 * Adds a TYPE parameter value. 1174 * </p> 1175 * <p> 1176 * The meaning of this parameter varies depending on the property. 1177 * </p> 1178 * <p> 1179 * <b>Supported versions:</b> {@code 2.1, 3.0, 4.0} 1180 * </p> 1181 * @param type the type to add 1182 * @see <a href="http://tools.ietf.org/html/rfc6350#page-19">RFC 6350 1183 * p.19</a> 1184 * @see <a href="http://tools.ietf.org/html/rfc2426#page-6">RFC 2426 p.6</a> 1185 * @see <a href="http://www.imc.org/pdi/vcard-21.doc">vCard 2.1</a> 1186 */ 1187 public void addType(String type) { 1188 put(TYPE, type); 1189 } 1190 1191 /** 1192 * <p> 1193 * Removes a TYPE parameter value. 1194 * </p> 1195 * <p> 1196 * The meaning of this parameter varies depending on the property. 1197 * </p> 1198 * <p> 1199 * <b>Supported versions:</b> {@code 2.1, 3.0, 4.0} 1200 * </p> 1201 * @param type the type to remove 1202 * @see <a href="http://tools.ietf.org/html/rfc6350#page-19">RFC 6350 1203 * p.19</a> 1204 * @see <a href="http://tools.ietf.org/html/rfc2426#page-6">RFC 2426 p.6</a> 1205 * @see <a href="http://www.imc.org/pdi/vcard-21.doc">vCard 2.1</a> 1206 */ 1207 public void removeType(String type) { 1208 remove(TYPE, type); 1209 } 1210 1211 /** 1212 * <p> 1213 * Sets the TYPE parameter value. 1214 * </p> 1215 * <p> 1216 * The meaning of this parameter varies depending on the property. 1217 * </p> 1218 * <p> 1219 * <b>Supported versions:</b> {@code 2.1, 3.0, 4.0} 1220 * </p> 1221 * @param type the type or null to remove 1222 * @see <a href="http://tools.ietf.org/html/rfc6350#page-19">RFC 6350 1223 * p.19</a> 1224 * @see <a href="http://tools.ietf.org/html/rfc2426#page-6">RFC 2426 p.6</a> 1225 * @see <a href="http://www.imc.org/pdi/vcard-21.doc">vCard 2.1</a> 1226 */ 1227 public void setType(String type) { 1228 replace(TYPE, type); 1229 } 1230 1231 /** 1232 * <p> 1233 * Gets the TZ (timezone) parameter value. 1234 * </p> 1235 * <p> 1236 * This parameter is used to associate timezone information with an 1237 * {@link Address} property (for example, "America/New_York" to indicate 1238 * that an address adheres to that timezone). 1239 * </p> 1240 * <p> 1241 * <b>Supported versions:</b> {@code 4.0} 1242 * </p> 1243 * @return the timezone or null if not set 1244 * @see <a href="http://tools.ietf.org/html/rfc6350#page-22">RFC 6350 1245 * p.22</a> 1246 */ 1247 public String getTimezone() { 1248 return first(TZ); 1249 } 1250 1251 /** 1252 * <p> 1253 * Sets the TZ (timezone) parameter value. 1254 * </p> 1255 * <p> 1256 * This parameter is used to associate timezone information with an 1257 * {@link Address} property (for example, "America/New_York" to indicate 1258 * that an address adheres to that timezone). 1259 * </p> 1260 * <p> 1261 * <b>Supported versions:</b> {@code 4.0} 1262 * </p> 1263 * @param timezone the timezone or null to remove 1264 * @see <a href="http://tools.ietf.org/html/rfc6350#page-22">RFC 6350 1265 * p.22</a> 1266 */ 1267 public void setTimezone(String timezone) { 1268 replace(TZ, timezone); 1269 } 1270 1271 /** 1272 * <p> 1273 * Gets the VALUE parameter value. 1274 * </p> 1275 * <p> 1276 * This parameter defines the data type of the property value (for example, 1277 * "date" if the property value is a date without a time component). It is 1278 * used if the property accepts multiple values that have different data 1279 * types. 1280 * </p> 1281 * <p> 1282 * <b>Supported versions:</b> {@code 2.1, 3.0, 4.0} 1283 * </p> 1284 * @return the data type or null if not set 1285 * @see <a href="http://tools.ietf.org/html/rfc6350#page-16">RFC 6350 1286 * p.16</a> 1287 * @see <a href="http://tools.ietf.org/html/rfc2426#page-6">RFC 2426 p.6</a> 1288 * @see <a href="http://www.imc.org/pdi/vcard-21.doc">vCard 2.1 p.20</a> 1289 */ 1290 public VCardDataType getValue() { 1291 String value = first(VALUE); 1292 return (value == null) ? null : VCardDataType.get(value); 1293 } 1294 1295 /** 1296 * <p> 1297 * Sets the VALUE parameter value. 1298 * </p> 1299 * <p> 1300 * This parameter defines the data type of the property value (for example, 1301 * "date" if the property value is a date without a time component). It is 1302 * used if the property accepts multiple values that have different data 1303 * types. 1304 * </p> 1305 * <p> 1306 * <b>Supported versions:</b> {@code 2.1, 3.0, 4.0} 1307 * </p> 1308 * @param dataType the data type or null to remove 1309 * @see <a href="http://tools.ietf.org/html/rfc6350#page-16">RFC 6350 1310 * p.16</a> 1311 * @see <a href="http://tools.ietf.org/html/rfc2426#page-6">RFC 2426 p.6</a> 1312 * @see <a href="http://www.imc.org/pdi/vcard-21.doc">vCard 2.1 p.20</a> 1313 */ 1314 public void setValue(VCardDataType dataType) { 1315 replace(VALUE, (dataType == null) ? null : dataType.getName()); 1316 } 1317 1318 /** 1319 * <p> 1320 * Checks the parameters for data consistency problems or deviations from 1321 * the specification. 1322 * </p> 1323 * <p> 1324 * These problems will not prevent the vCard from being written to a data 1325 * stream*, but may prevent it from being parsed correctly by the consuming 1326 * application. 1327 * </p> 1328 * <p> 1329 * *With a few exceptions: One thing this method does is check for illegal 1330 * characters. There are certain characters that will break the vCard syntax 1331 * if written (such as a newline character in a parameter name). If one of 1332 * these characters is present, it WILL prevent the vCard from being 1333 * written. 1334 * </p> 1335 * @param version the vCard version to validate against 1336 * @return a list of warnings or an empty list if no problems were found 1337 */ 1338 public List<ValidationWarning> validate(VCardVersion version) { 1339 List<ValidationWarning> warnings = new ArrayList<>(0); 1340 1341 /* 1342 * Check for invalid characters in names and values. 1343 */ 1344 SyntaxStyle syntax = version.getSyntaxStyle(); 1345 for (Map.Entry<String, List<String>> entry : this) { 1346 String name = entry.getKey(); 1347 1348 /* 1349 * Don't check LABEL parameter for 2.1 and 3.0 because this 1350 * parameter is converted to a property in those versions. 1351 */ 1352 if (version != VCardVersion.V4_0 && LABEL.equalsIgnoreCase(name)) { 1353 continue; 1354 } 1355 1356 //check the parameter name 1357 if (!VObjectValidator.validateParameterName(name, syntax, true)) { 1358 if (syntax == SyntaxStyle.OLD) { 1359 AllowedCharacters notAllowed = VObjectValidator.allowedCharactersParameterName(syntax, true).flip(); 1360 warnings.add(new ValidationWarning(30, name, notAllowed.toString(true))); 1361 } else { 1362 warnings.add(new ValidationWarning(26, name)); 1363 } 1364 } 1365 1366 //check the parameter value(s) 1367 List<String> values = entry.getValue(); 1368 for (String value : values) { 1369 /* 1370 * Newlines are allowed in LABEL parameters, but are not allowed 1371 * by vobject, so remove them from the value before validating. 1372 */ 1373 if (LABEL.equalsIgnoreCase(name)) { 1374 value = value.replaceAll("\r\n|\r|\n", ""); 1375 } 1376 1377 if (!VObjectValidator.validateParameterValue(value, syntax, false, true)) { 1378 AllowedCharacters notAllowed = VObjectValidator.allowedCharactersParameterValue(syntax, false, true).flip(); 1379 int code = (syntax == SyntaxStyle.OLD) ? 31 : 25; 1380 warnings.add(new ValidationWarning(code, name, value, notAllowed.toString(true))); 1381 } 1382 } 1383 } 1384 1385 /* 1386 * Check for invalid or unsupported values (e.g. "ENCODING=foo"). 1387 */ 1388 { 1389 final int nonStandardValueCode = 3; 1390 final int unsupportedValueCode = 4; 1391 1392 String value = first(CALSCALE); 1393 if (value != null && Calscale.find(value) == null) { 1394 warnings.add(new ValidationWarning(nonStandardValueCode, CALSCALE, value, Calscale.all())); 1395 } 1396 1397 value = first(ENCODING); 1398 if (value != null) { 1399 Encoding encoding = Encoding.find(value); 1400 if (encoding == null) { 1401 warnings.add(new ValidationWarning(nonStandardValueCode, ENCODING, value, Encoding.all())); 1402 } else if (!encoding.isSupportedBy(version)) { 1403 warnings.add(new ValidationWarning(unsupportedValueCode, ENCODING, value)); 1404 } 1405 } 1406 1407 value = first(VALUE); 1408 if (value != null) { 1409 VCardDataType dataType = VCardDataType.find(value); 1410 if (dataType == null) { 1411 warnings.add(new ValidationWarning(nonStandardValueCode, VALUE, value, VCardDataType.all())); 1412 } else if (!dataType.isSupportedBy(version)) { 1413 warnings.add(new ValidationWarning(unsupportedValueCode, VALUE, value)); 1414 } 1415 } 1416 } 1417 1418 /* 1419 * Check for parameters with malformed values. 1420 */ 1421 { 1422 final int malformedValueCode = 5; 1423 1424 try { 1425 getGeo(); 1426 } catch (IllegalStateException e) { 1427 warnings.add(new ValidationWarning(malformedValueCode, GEO, first(GEO))); 1428 } 1429 1430 try { 1431 Integer index = getIndex(); 1432 if (index != null && index <= 0) { 1433 warnings.add(new ValidationWarning(28, index)); 1434 } 1435 } catch (IllegalStateException e) { 1436 warnings.add(new ValidationWarning(malformedValueCode, INDEX, first(INDEX))); 1437 } 1438 1439 List<String> pids = get(PID); 1440 for (String pid : pids) { 1441 if (!isPidValid(pid)) { 1442 warnings.add(new ValidationWarning(27, pid)); 1443 } 1444 } 1445 1446 try { 1447 Integer pref = getPref(); 1448 if (pref != null && (pref < 1 || pref > 100)) { 1449 warnings.add(new ValidationWarning(29, pref)); 1450 } 1451 } catch (IllegalStateException e) { 1452 warnings.add(new ValidationWarning(malformedValueCode, PREF, first(PREF))); 1453 } 1454 } 1455 1456 /* 1457 * Check that each parameter is supported by the given vCard version. 1458 */ 1459 { 1460 final int paramNotSupportedCode = 6; 1461 1462 for (Map.Entry<String, Set<VCardVersion>> entry : supportedVersions.entrySet()) { 1463 String name = entry.getKey(); 1464 String value = first(name); 1465 if (value == null) { 1466 continue; 1467 } 1468 1469 Set<VCardVersion> versions = entry.getValue(); 1470 if (!versions.contains(version)) { 1471 warnings.add(new ValidationWarning(paramNotSupportedCode, name)); 1472 } 1473 } 1474 } 1475 1476 /* 1477 * Check that the CHARSET parameter has a character set that is 1478 * supported by this JVM. 1479 */ 1480 { 1481 final int invalidCharsetCode = 22; 1482 1483 String charsetStr = getCharset(); 1484 if (charsetStr != null) { 1485 try { 1486 Charset.forName(charsetStr); 1487 } catch (IllegalCharsetNameException e) { 1488 warnings.add(new ValidationWarning(invalidCharsetCode, charsetStr)); 1489 } catch (UnsupportedCharsetException e) { 1490 warnings.add(new ValidationWarning(invalidCharsetCode, charsetStr)); 1491 } 1492 } 1493 } 1494 1495 return warnings; 1496 } 1497 1498 private static boolean isPidValid(String pid) { 1499 boolean dotFound = false; 1500 for (int i = 0; i < pid.length(); i++) { 1501 char c = pid.charAt(i); 1502 1503 if (c == '.') { 1504 if (i == 0 || i == pid.length() - 1) { 1505 return false; 1506 } 1507 if (dotFound) { 1508 return false; 1509 } 1510 dotFound = true; 1511 continue; 1512 } 1513 1514 if (c >= '0' && c <= '9') { 1515 continue; 1516 } 1517 1518 return false; 1519 } 1520 1521 return true; 1522 } 1523 1524 @Override 1525 protected String sanitizeKey(String key) { 1526 return (key == null) ? null : key.toUpperCase(); 1527 } 1528 1529 @Override 1530 public int hashCode() { 1531 /* 1532 * Remember: Keys are case-insensitive, key order does not matter, and 1533 * value order does not matter 1534 */ 1535 final int prime = 31; 1536 int result = 1; 1537 1538 for (Map.Entry<String, List<String>> entry : this) { 1539 String key = entry.getKey(); 1540 List<String> value = entry.getValue(); 1541 1542 int valueHash = 1; 1543 for (String v : value) { 1544 valueHash += v.toLowerCase().hashCode(); 1545 } 1546 1547 int entryHash = 1; 1548 entryHash += prime * entryHash + ((key == null) ? 0 : key.toLowerCase().hashCode()); 1549 entryHash += prime * entryHash + valueHash; 1550 1551 result += entryHash; 1552 } 1553 1554 return result; 1555 } 1556 1557 /** 1558 * <p> 1559 * Determines whether the given object is logically equivalent to this list 1560 * of vCard parameters. 1561 * </p> 1562 * <p> 1563 * vCard parameters are case-insensitive. Also, the order in which they are 1564 * defined does not matter. 1565 * </p> 1566 * @param obj the object to compare to 1567 * @return true if the objects are equal, false if not 1568 */ 1569 @Override 1570 public boolean equals(Object obj) { 1571 /* 1572 * Remember: Keys are case-insensitive, key order does not matter, and 1573 * value order does not matter 1574 */ 1575 if (this == obj) return true; 1576 if (obj == null) return false; 1577 if (getClass() != obj.getClass()) return false; 1578 1579 VCardParameters other = (VCardParameters) obj; 1580 if (size() != other.size()) return false; 1581 1582 for (Map.Entry<String, List<String>> entry : this) { 1583 String key = entry.getKey(); 1584 List<String> value = entry.getValue(); 1585 List<String> otherValue = other.get(key); 1586 1587 if (value.size() != otherValue.size()) { 1588 return false; 1589 } 1590 1591 List<String> valueLower = new ArrayList<>(value.size()); 1592 for (String v : value) { 1593 valueLower.add(v.toLowerCase()); 1594 } 1595 Collections.sort(valueLower); 1596 1597 List<String> otherValueLower = new ArrayList<>(otherValue.size()); 1598 for (String v : otherValue) { 1599 otherValueLower.add(v.toLowerCase()); 1600 } 1601 Collections.sort(otherValueLower); 1602 1603 if (!valueLower.equals(otherValueLower)) { 1604 return false; 1605 } 1606 } 1607 1608 return true; 1609 } 1610 1611 /** 1612 * <p> 1613 * A list that converts the raw string values of a TYPE parameter to the 1614 * appropriate {@link VCardParameter} object that some parameters use. 1615 * </p> 1616 * <p> 1617 * This list is backed by the {@link VCardParameters} object. Any changes 1618 * made to the list will affect the {@link VCardParameters} object and vice 1619 * versa. 1620 * </p> 1621 * @param <T> the parameter class 1622 */ 1623 public abstract class TypeParameterList<T extends VCardParameter> extends EnumParameterList<T> { 1624 public TypeParameterList() { 1625 super(TYPE); 1626 } 1627 } 1628 1629 /** 1630 * <p> 1631 * A list that converts the raw string values of a parameter to the 1632 * appropriate {@link VCardParameter} object that some parameters use. 1633 * </p> 1634 * <p> 1635 * This list is backed by the {@link VCardParameters} object. Any changes 1636 * made to the list will affect the {@link VCardParameters} object and vice 1637 * versa. 1638 * </p> 1639 * @param <T> the parameter class 1640 */ 1641 public abstract class EnumParameterList<T extends VCardParameter> extends VCardParameterList<T> { 1642 public EnumParameterList(String parameterName) { 1643 super(parameterName); 1644 } 1645 1646 @Override 1647 protected String _asString(T value) { 1648 return value.getValue(); 1649 } 1650 } 1651 1652 /** 1653 * <p> 1654 * A list that converts the raw string values of a parameter to another kind 1655 * of value (for example, Integers). 1656 * </p> 1657 * <p> 1658 * This list is backed by the {@link VCardParameters} object. Any changes 1659 * made to the list will affect the {@link VCardParameters} object and vice 1660 * versa. 1661 * </p> 1662 * <p> 1663 * If a String value cannot be converted to the appropriate data type, an 1664 * {@link IllegalStateException} is thrown. 1665 * </p> 1666 */ 1667 public abstract class VCardParameterList<T> extends AbstractList<T> { 1668 protected final String parameterName; 1669 protected final List<String> parameterValues; 1670 1671 /** 1672 * @param parameterName the name of the parameter (case insensitive) 1673 */ 1674 public VCardParameterList(String parameterName) { 1675 this.parameterName = parameterName; 1676 parameterValues = VCardParameters.this.get(parameterName); 1677 } 1678 1679 @Override 1680 public void add(int index, T value) { 1681 String valueStr = _asString(value); 1682 parameterValues.add(index, valueStr); 1683 } 1684 1685 @Override 1686 public T remove(int index) { 1687 String removed = parameterValues.remove(index); 1688 return asObject(removed); 1689 } 1690 1691 @Override 1692 public T get(int index) { 1693 String value = parameterValues.get(index); 1694 return asObject(value); 1695 } 1696 1697 @Override 1698 public T set(int index, T value) { 1699 String valueStr = _asString(value); 1700 String replaced = parameterValues.set(index, valueStr); 1701 return asObject(replaced); 1702 } 1703 1704 @Override 1705 public int size() { 1706 return parameterValues.size(); 1707 } 1708 1709 private T asObject(String value) { 1710 try { 1711 return _asObject(value); 1712 } catch (Exception e) { 1713 throw _exception(value, e); 1714 } 1715 } 1716 1717 /** 1718 * Converts the object to a String value for storing in the 1719 * {@link VCardParameters} object. 1720 * @param value the value 1721 * @return the string value 1722 */ 1723 protected abstract String _asString(T value); 1724 1725 /** 1726 * Converts a String value to its object form. 1727 * @param value the string value 1728 * @return the object 1729 * @throws Exception if there is a problem parsing the string 1730 */ 1731 protected abstract T _asObject(String value) throws Exception; 1732 1733 /** 1734 * Creates the exception that is thrown when the raw string value cannot 1735 * be parsed into its object form. 1736 * @param value the raw string value 1737 * @param thrown the thrown exception 1738 * @return the exception to throw 1739 */ 1740 protected IllegalStateException _exception(String value, Exception thrown) { 1741 return new IllegalStateException(Messages.INSTANCE.getExceptionMessage(26, parameterName), thrown); 1742 } 1743 } 1744}