001package biweekly.parameter; 002 003import java.util.ArrayList; 004import java.util.List; 005 006import biweekly.ICalDataType; 007import biweekly.Warning; 008import biweekly.component.VTimezone; 009import biweekly.property.FreeBusy; 010import biweekly.property.RecurrenceId; 011import biweekly.property.RelatedTo; 012import biweekly.property.TimezoneId; 013import biweekly.property.Trigger; 014import biweekly.util.ListMultimap; 015 016/* 017 Copyright (c) 2013, 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 CUTYPE = "CUTYPE"; 049 public static final String DELEGATED_FROM = "DELEGATED-FROM"; 050 public static final String DELEGATED_TO = "DELEGATED-TO"; 051 public static final String DIR = "DIR"; 052 public static final String ENCODING = "ENCODING"; 053 public static final String FMTTYPE = "FMTTYPE"; 054 public static final String FBTYPE = "FBTYPE"; 055 public static final String LANGUAGE = "LANGUAGE"; 056 public static final String MEMBER = "MEMBER"; 057 public static final String PARTSTAT = "PARTSTAT"; 058 public static final String RANGE = "RANGE"; 059 public static final String RELATED = "RELATED"; 060 public static final String RELTYPE = "RELTYPE"; 061 public static final String ROLE = "ROLE"; 062 public static final String RSVP = "RSVP"; 063 public static final String SENT_BY = "SENT-BY"; 064 public static final String TZID = "TZID"; 065 public static final String VALUE = "VALUE"; 066 067 /** 068 * Creates a parameters list. 069 */ 070 public ICalParameters() { 071 super(0); //initialize map size to 0 because most properties don't use any parameters 072 } 073 074 /** 075 * Copies an existing parameters list. 076 * @param parameters the list to copy 077 */ 078 public ICalParameters(ICalParameters parameters) { 079 super(parameters); 080 } 081 082 /** 083 * Gets a URI pointing to additional information about the entity 084 * represented by the property. 085 * @return the URI or null if not set 086 * @see <a href="http://tools.ietf.org/html/rfc5545#page-14">RFC 5545 p.14-5</a> 087 */ 088 public String getAltRepresentation() { 089 return first(ALTREP); 090 } 091 092 /** 093 * Sets a URI pointing to additional information about the entity 094 * represented by the property. 095 * @param uri the URI or null to remove 096 * @see <a href="http://tools.ietf.org/html/rfc5545#page-14">RFC 5545 p.14-5</a> 097 */ 098 public void setAltRepresentation(String uri) { 099 replace(ALTREP, uri); 100 } 101 102 /** 103 * Gets the display name of a person. 104 * @return the display name (e.g. "John Doe") or null if not set 105 * @see <a href="http://tools.ietf.org/html/rfc5545#page-15">RFC 5545 p.15-6</a> 106 */ 107 public String getCommonName() { 108 return first(CN); 109 } 110 111 /** 112 * Sets the display name of a person. 113 * @param cn the display name (e.g. "John Doe") or null to remove 114 * @see <a href="http://tools.ietf.org/html/rfc5545#page-15">RFC 5545 p.15-6</a> 115 */ 116 public void setCommonName(String cn) { 117 replace(CN, cn); 118 } 119 120 /** 121 * Gets the type of user an attendee is (for example, an "individual" or a 122 * "room"). 123 * @return the calendar user type or null if not set 124 * @see <a href="http://tools.ietf.org/html/rfc5545#page-16">RFC 5545 p.16</a> 125 */ 126 public CalendarUserType getCalendarUserType() { 127 String value = first(CUTYPE); 128 return (value == null) ? null : CalendarUserType.get(value); 129 } 130 131 /** 132 * Sets the type of user an attendee is (for example, an "individual" or a 133 * "room"). 134 * @param cutype the calendar user type or null to remove 135 * @see <a href="http://tools.ietf.org/html/rfc5545#page-16">RFC 5545 p.16</a> 136 */ 137 public void setCalendarUserType(CalendarUserType cutype) { 138 replace(CUTYPE, (cutype == null) ? null : cutype.getValue()); 139 } 140 141 /** 142 * Gets the people who have delegated their responsibility to an attendee. 143 * @return the delegators (typically email URIs, e.g. 144 * "mailto:janedoe@example.com") 145 * @see <a href="http://tools.ietf.org/html/rfc5545#page-17">RFC 5545 p.17</a> 146 */ 147 public List<String> getDelegatedFrom() { 148 return get(DELEGATED_FROM); 149 } 150 151 /** 152 * Adds a person who has delegated his or her responsibility to an attendee. 153 * @param uri the delegator (typically an email URI, e.g. 154 * "mailto:janedoe@example.com") 155 * @see <a href="http://tools.ietf.org/html/rfc5545#page-17">RFC 5545 p.17</a> 156 */ 157 public void addDelegatedFrom(String uri) { 158 put(DELEGATED_FROM, uri); 159 } 160 161 /** 162 * Removes a person who has delegated his or her responsibility to an 163 * attendee. 164 * @param uri the delegator to remove (typically an email URI, e.g. 165 * "mailto:janedoe@example.com") 166 * @see <a href="http://tools.ietf.org/html/rfc5545#page-17">RFC 5545 p.17</a> 167 */ 168 public void removeDelegatedFrom(String uri) { 169 remove(DELEGATED_FROM, uri); 170 } 171 172 /** 173 * Removes everyone who has delegated his or her responsibility to an 174 * attendee. 175 * @see <a href="http://tools.ietf.org/html/rfc5545#page-17">RFC 5545 p.17</a> 176 */ 177 public void removeDelegatedFrom() { 178 removeAll(DELEGATED_FROM); 179 } 180 181 /** 182 * Gets the people to which an attendee has delegated his or her 183 * responsibility. 184 * @return the delegatees (typically email URIs, e.g. 185 * "mailto:janedoe@example.com") 186 * @see <a href="http://tools.ietf.org/html/rfc5545#page-17">RFC 5545 p.17-8</a> 187 */ 188 public List<String> getDelegatedTo() { 189 return get(DELEGATED_TO); 190 } 191 192 /** 193 * Adds a person to which an attendee has delegated his or her 194 * responsibility. 195 * @param uri the delegatee (typically an email URI, e.g. 196 * "mailto:janedoe@example.com") 197 * @see <a href="http://tools.ietf.org/html/rfc5545#page-17">RFC 5545 p.17-8</a> 198 */ 199 public void addDelegatedTo(String uri) { 200 put(DELEGATED_TO, uri); 201 } 202 203 /** 204 * Removes a person to which an attendee has delegated his or her 205 * responsibility. 206 * @param uri the delegatee to remove (typically an email URI, e.g. 207 * "mailto:janedoe@example.com") 208 * @see <a href="http://tools.ietf.org/html/rfc5545#page-17">RFC 5545 p.17-8</a> 209 */ 210 public void removeDelegatedTo(String uri) { 211 remove(DELEGATED_TO, uri); 212 } 213 214 /** 215 * Removes everyone to which an attendee has delegated his or her 216 * responsibility. 217 * @see <a href="http://tools.ietf.org/html/rfc5545#page-17">RFC 5545 p.17-8</a> 218 */ 219 public void removeDelegatedTo() { 220 removeAll(DELEGATED_TO); 221 } 222 223 /** 224 * Gets a URI that contains additional information about the person. 225 * @return the URI (e.g. an LDAP URI) or null if not set 226 * @see <a href="http://tools.ietf.org/html/rfc5545#page-18">RFC 5545 p.18</a> 227 */ 228 public String getDirectoryEntry() { 229 return first(DIR); 230 } 231 232 /** 233 * Sets a URI that contains additional information about the person. 234 * @param uri the URI (e.g. an LDAP URI) or null to remove 235 * @see <a href="http://tools.ietf.org/html/rfc5545#page-18">RFC 5545 p.18</a> 236 */ 237 public void setDirectoryEntry(String uri) { 238 replace(DIR, uri); 239 } 240 241 /** 242 * Gets the encoding of the property value (for example, "base64"). 243 * @return the encoding or null if not set 244 * @see <a href="http://tools.ietf.org/html/rfc5545#page-18">RFC 5545 p.18-9</a> 245 */ 246 public Encoding getEncoding() { 247 String value = first(ENCODING); 248 return (value == null) ? null : Encoding.get(value); 249 } 250 251 /** 252 * Sets the encoding of the property value (for example, "base64"). 253 * @param encoding the encoding or null to remove 254 * @see <a href="http://tools.ietf.org/html/rfc5545#page-18">RFC 5545 p.18-9</a> 255 */ 256 public void setEncoding(Encoding encoding) { 257 replace(ENCODING, (encoding == null) ? null : encoding.getValue()); 258 } 259 260 /** 261 * Gets the content-type of the property's value. 262 * @return the content type (e.g. "image/png") or null if not set 263 * @see <a href="http://tools.ietf.org/html/rfc5545#page-19">RFC 5545 p.19-20</a> 264 */ 265 public String getFormatType() { 266 return first(FMTTYPE); 267 } 268 269 /** 270 * Sets the content-type of the property's value. 271 * @param formatType the content type (e.g. "image/png") or null to remove 272 * @see <a href="http://tools.ietf.org/html/rfc5545#page-19">RFC 5545 p.19-20</a> 273 */ 274 public void setFormatType(String formatType) { 275 replace(FMTTYPE, formatType); 276 } 277 278 /** 279 * Gets the person's status over the time periods that are specified in a 280 * {@link FreeBusy} property (for example, "free" or "busy"). If not set, 281 * the user should be considered "busy". 282 * @return the type or null if not set 283 * @see <a href="http://tools.ietf.org/html/rfc5545#page-20">RFC 5545 p.20</a> 284 */ 285 public FreeBusyType getFreeBusyType() { 286 String value = first(FBTYPE); 287 return (value == null) ? null : FreeBusyType.get(value); 288 } 289 290 /** 291 * Sets the person's status over the time periods that are specified in a 292 * {@link FreeBusy} property (for example, "free" or "busy"). If not set, 293 * the user should be considered "busy". 294 * @param fbType the type or null to remove 295 * @see <a href="http://tools.ietf.org/html/rfc5545#page-20">RFC 5545 p.20</a> 296 */ 297 public void setFreeBusyType(FreeBusyType fbType) { 298 replace(FBTYPE, (fbType == null) ? null : fbType.getValue()); 299 } 300 301 /** 302 * Gets the language that the property value is written in. 303 * @return the language (e.g. "en" for English) or null if not set 304 * @see <a href="http://tools.ietf.org/html/rfc5545#page-21">RFC 5545 p.21</a> 305 */ 306 public String getLanguage() { 307 return first(LANGUAGE); 308 } 309 310 /** 311 * Sets the language that the property value is written in. 312 * @param language the language (e.g. "en" for English) or null to remove 313 * @see <a href="http://tools.ietf.org/html/rfc5545#page-21">RFC 5545 p.21</a> 314 */ 315 public void setLanguage(String language) { 316 replace(LANGUAGE, language); 317 } 318 319 /** 320 * Adds a group that an attendee is a member of. 321 * @param uri the group URI (typically, an email address URI, e.g. 322 * "mailto:mailinglist@example.com") 323 * @see <a href="http://tools.ietf.org/html/rfc5545#page-21">RFC 5545 p.21-2</a> 324 */ 325 public void addMember(String uri) { 326 put(MEMBER, uri); 327 } 328 329 /** 330 * Gets the groups that an attendee is a member of. 331 * @return the group URIs (typically, these are email address URIs, e.g. 332 * "mailto:mailinglist@example.com") 333 * @see <a href="http://tools.ietf.org/html/rfc5545#page-21">RFC 5545 p.21-2</a> 334 */ 335 public List<String> getMembers() { 336 return get(MEMBER); 337 } 338 339 /** 340 * Removes a group that an attendee is a member of. 341 * @param uri the group URI to remove (typically, an email address URI, e.g. 342 * "mailto:mailinglist@example.com") 343 * @see <a href="http://tools.ietf.org/html/rfc5545#page-21">RFC 5545 p.21-2</a> 344 */ 345 public void removeMember(String uri) { 346 remove(MEMBER, uri); 347 } 348 349 /** 350 * Removes all groups that an attendee is a member of. 351 * @see <a href="http://tools.ietf.org/html/rfc5545#page-21">RFC 5545 p.21-2</a> 352 */ 353 public void removeMembers() { 354 removeAll(MEMBER); 355 } 356 357 /** 358 * Gets an attendee's level of participation. 359 * @return the participation status or null if not set 360 * @see <a href="http://tools.ietf.org/html/rfc5545#page-22">RFC 5545 p.22-3</a> 361 */ 362 public ParticipationStatus getParticipationStatus() { 363 String value = first(PARTSTAT); 364 return (value == null) ? null : ParticipationStatus.get(value); 365 } 366 367 /** 368 * Sets an attendee's level of participation. 369 * @param status the participation status or null to remove 370 * @see <a href="http://tools.ietf.org/html/rfc5545#page-22">RFC 5545 p.22-3</a> 371 */ 372 public void setParticipationStatus(ParticipationStatus status) { 373 replace(PARTSTAT, (status == null) ? null : status.getValue()); 374 } 375 376 /** 377 * Gets the effective range of recurrence instances from the instance 378 * specified by a {@link RecurrenceId} property. 379 * @return the range or null if not set 380 * @see <a href="http://tools.ietf.org/html/rfc5545#page-23">RFC 5545 p.23-4</a> 381 */ 382 public Range getRange() { 383 String value = first(RANGE); 384 return (value == null) ? null : Range.get(value); 385 } 386 387 /** 388 * Sets the effective range of recurrence instances from the instance 389 * specified by a {@link RecurrenceId} property. 390 * @param range the range or null to remove 391 * @see <a href="http://tools.ietf.org/html/rfc5545#page-23">RFC 5545 p.23-4</a> 392 */ 393 public void setRange(Range range) { 394 replace(RANGE, (range == null) ? null : range.getValue()); 395 } 396 397 /** 398 * Gets the date-time field that the duration in a {@link Trigger} property 399 * is relative to. 400 * @return the field or null if not set 401 * @see <a href="http://tools.ietf.org/html/rfc5545#page-24">RFC 5545 p.24</a> 402 */ 403 public Related getRelated() { 404 String value = first(RELATED); 405 return (value == null) ? null : Related.get(value); 406 } 407 408 /** 409 * Sets the date-time field that the duration in a {@link Trigger} property 410 * is relative to. 411 * @param related the field or null to remove 412 * @see <a href="http://tools.ietf.org/html/rfc5545#page-24">RFC 5545 p.24</a> 413 */ 414 public void setRelated(Related related) { 415 replace(RELATED, (related == null) ? null : related.getValue()); 416 } 417 418 /** 419 * Gets the relationship type of a {@link RelatedTo} property. 420 * @return the relationship type (e.g. "child") or null if not set 421 * @see <a href="http://tools.ietf.org/html/rfc5545#page-25">RFC 5545 p.25</a> 422 */ 423 public RelationshipType getRelationshipType() { 424 String value = first(RELTYPE); 425 return (value == null) ? null : RelationshipType.get(value); 426 } 427 428 /** 429 * Sets the relationship type of a {@link RelatedTo} property. 430 * @param relationshipType the relationship type (e.g. "child") or null to 431 * remove 432 * @see <a href="http://tools.ietf.org/html/rfc5545#page-25">RFC 5545 p.25</a> 433 */ 434 public void setRelationshipType(RelationshipType relationshipType) { 435 replace(RELTYPE, (relationshipType == null) ? null : relationshipType.getValue()); 436 } 437 438 /** 439 * Gets an attendee's role (for example, "chair" or "required participant"). 440 * @return the role or null if not set 441 * @see <a href="http://tools.ietf.org/html/rfc5545#page-25">RFC 5545 p.25-6</a> 442 */ 443 public Role getRole() { 444 String value = first(ROLE); 445 return (value == null) ? null : Role.get(value); 446 } 447 448 /** 449 * Sets an attendee's role (for example, "chair" or "required participant"). 450 * @param role the role or null to remove 451 * @see <a href="http://tools.ietf.org/html/rfc5545#page-25">RFC 5545 p.25-6</a> 452 */ 453 public void setRole(Role role) { 454 replace(ROLE, (role == null) ? null : role.getValue()); 455 } 456 457 /** 458 * Gets whether the organizer requests a response from an attendee. 459 * @throws IllegalStateException if the parameter value is malformed and 460 * cannot be parsed 461 * @return true if an RSVP is requested, false if not, null if not set 462 * @see <a href="http://tools.ietf.org/html/rfc5545#page-26">RFC 5545 p.26-7</a> 463 */ 464 public Boolean getRsvp() { 465 String value = first(RSVP); 466 467 if (value == null) { 468 return null; 469 } 470 if ("true".equalsIgnoreCase(value)) { 471 return true; 472 } 473 if ("false".equalsIgnoreCase(value)) { 474 return false; 475 } 476 throw new IllegalStateException(RSVP + " parameter value is malformed and could not be parsed. Retrieve its raw text value instead."); 477 } 478 479 /** 480 * Sets whether the organizer requests a response from an attendee. 481 * @param rsvp true if an RSVP has been requested, false if not, null to 482 * remove 483 * @see <a href="http://tools.ietf.org/html/rfc5545#page-26">RFC 5545 p.26-7</a> 484 */ 485 public void setRsvp(Boolean rsvp) { 486 replace(RSVP, (rsvp == null) ? null : rsvp.toString().toUpperCase()); 487 } 488 489 /** 490 * Gets a person that is acting on behalf of the person defined in the 491 * property. 492 * @return a URI representing the person (typically, an email URI, e.g. 493 * "mailto:janedoe@example.com") or null if not set 494 * @see <a href="http://tools.ietf.org/html/rfc5545#page-27">RFC 5545 p.27</a> 495 */ 496 public String getSentBy() { 497 return first(SENT_BY); 498 } 499 500 /** 501 * Sets a person that is acting on behalf of the person defined in the 502 * property. 503 * @param uri a URI representing the person (typically, an email URI, e.g. 504 * "mailto:janedoe@example.com") or null to remove 505 * @see <a href="http://tools.ietf.org/html/rfc5545#page-27">RFC 5545 p.27</a> 506 */ 507 public void setSentBy(String uri) { 508 replace(SENT_BY, uri); 509 } 510 511 /** 512 * Gets the timezone identifier. This either (a) references the 513 * {@link TimezoneId} property of a {@link VTimezone} component, or (b) 514 * specifies a globally-defined timezone (e.g. "America/New_York"). For a 515 * list of globally-defined timezones, see the <a 516 * href="http://www.twinsun.com/tz/tz-link.htm">TZ database</a>. 517 * @return the timezone identifier or null if not set 518 * @see <a href="http://tools.ietf.org/html/rfc5545#page-27">RFC 5545 p.27-8</a> 519 */ 520 public String getTimezoneId() { 521 return first(TZID); 522 } 523 524 /** 525 * Sets the timezone identifier. This either (a) references the 526 * {@link TimezoneId} property of a {@link VTimezone} component, or (b) 527 * specifies a globally-defined timezone (e.g. "America/New_York"). For a 528 * list of globally-defined timezones, see the <a 529 * href="http://www.twinsun.com/tz/tz-link.htm">TZ database</a>. 530 * @param timezoneId the timezone identifier or null to remove 531 * @see <a href="http://tools.ietf.org/html/rfc5545#page-27">RFC 5545 p.27-8</a> 532 */ 533 public void setTimezoneId(String timezoneId) { 534 replace(TZID, timezoneId); 535 } 536 537 /** 538 * Gets the data type of the property's value (for example, "text" or 539 * "datetime"). 540 * @return the data type or null if not set 541 * @see <a href="http://tools.ietf.org/html/rfc5545#page-29">RFC 5545 p.29-50</a> 542 */ 543 public ICalDataType getValue() { 544 String value = first(VALUE); 545 return (value == null) ? null : ICalDataType.get(value); 546 } 547 548 /** 549 * Sets the data type of the property's value (for example, "text" or 550 * "datetime"). 551 * @param value the data type or null to remove 552 * @see <a href="http://tools.ietf.org/html/rfc5545#page-29">RFC 5545 p.29-50</a> 553 */ 554 public void setValue(ICalDataType value) { 555 replace(VALUE, (value == null) ? null : value.getName()); 556 } 557 558 /** 559 * Checks this parameters list for data consistency problems or deviations 560 * from the spec. These problems will not prevent the iCalendar object from 561 * being written to a data stream, but may prevent it from being parsed 562 * correctly by the consuming application. 563 * @return a list of warnings or an empty list if no problems were found 564 */ 565 public List<Warning> validate() { 566 List<Warning> warnings = new ArrayList<Warning>(0); 567 568 final int nonStandardCode = 1; 569 570 String value = first(RSVP); 571 if (value != null && !value.equalsIgnoreCase("true") && !value.equalsIgnoreCase("false")) { 572 warnings.add(Warning.validate(nonStandardCode, RSVP, value, "[TRUE, FALSE]")); 573 } 574 575 value = first(CUTYPE); 576 if (value != null && CalendarUserType.find(value) == null) { 577 warnings.add(Warning.validate(nonStandardCode, CUTYPE, value, CalendarUserType.all())); 578 } 579 580 value = first(ENCODING); 581 if (value != null && Encoding.find(value) == null) { 582 warnings.add(Warning.validate(nonStandardCode, ENCODING, value, Encoding.all())); 583 } 584 585 value = first(FBTYPE); 586 if (value != null && FreeBusyType.find(value) == null) { 587 warnings.add(Warning.validate(nonStandardCode, FBTYPE, value, FreeBusyType.all())); 588 } 589 590 value = first(PARTSTAT); 591 if (value != null && ParticipationStatus.find(value) == null) { 592 warnings.add(Warning.validate(nonStandardCode, PARTSTAT, value, ParticipationStatus.all())); 593 } 594 595 value = first(RANGE); 596 if (value != null && Range.find(value) == null) { 597 warnings.add(Warning.validate(nonStandardCode, RANGE, value, Range.all())); 598 } 599 600 value = first(RELATED); 601 if (value != null && Related.find(value) == null) { 602 warnings.add(Warning.validate(nonStandardCode, RELATED, value, Related.all())); 603 } 604 605 value = first(RELTYPE); 606 if (value != null && RelationshipType.find(value) == null) { 607 warnings.add(Warning.validate(nonStandardCode, RELTYPE, value, RelationshipType.all())); 608 } 609 610 value = first(ROLE); 611 if (value != null && Role.find(value) == null) { 612 warnings.add(Warning.validate(nonStandardCode, ROLE, value, Role.all())); 613 } 614 615 value = first(VALUE); 616 if (value != null && ICalDataType.find(value) == null) { 617 warnings.add(Warning.validate(nonStandardCode, VALUE, value, ICalDataType.all())); 618 } 619 620 return warnings; 621 } 622 623 @Override 624 protected String sanitizeKey(String key) { 625 return (key == null) ? null : key.toUpperCase(); 626 } 627}