001/* 002 * Copyright 2015-2020 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2015-2020 Ping Identity Corporation 007 * 008 * Licensed under the Apache License, Version 2.0 (the "License"); 009 * you may not use this file except in compliance with the License. 010 * You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, software 015 * distributed under the License is distributed on an "AS IS" BASIS, 016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 017 * See the License for the specific language governing permissions and 018 * limitations under the License. 019 */ 020/* 021 * Copyright (C) 2015-2020 Ping Identity Corporation 022 * 023 * This program is free software; you can redistribute it and/or modify 024 * it under the terms of the GNU General Public License (GPLv2 only) 025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 026 * as published by the Free Software Foundation. 027 * 028 * This program is distributed in the hope that it will be useful, 029 * but WITHOUT ANY WARRANTY; without even the implied warranty of 030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 031 * GNU General Public License for more details. 032 * 033 * You should have received a copy of the GNU General Public License 034 * along with this program; if not, see <http://www.gnu.org/licenses>. 035 */ 036package com.unboundid.util.json; 037 038 039 040import com.unboundid.util.ByteStringBuffer; 041import com.unboundid.util.NotMutable; 042import com.unboundid.util.StaticUtils; 043import com.unboundid.util.ThreadSafety; 044import com.unboundid.util.ThreadSafetyLevel; 045 046 047 048/** 049 * This class provides an implementation of a JSON value that represents a 050 * string of Unicode characters. The string representation of a JSON string 051 * must start and end with the double quotation mark character, and a Unicode 052 * (preferably UTF-8) representation of the string between the quotes. The 053 * following special characters must be escaped: 054 * <UL> 055 * <LI> 056 * The double quotation mark (Unicode character U+0022) must be escaped as 057 * either {@code \"} or {@code \}{@code u0022}. 058 * </LI> 059 * <LI> 060 * The backslash (Unicode character U+005C) must be escaped as either 061 * {@code \\} or {@code \}{@code u005C}. 062 * </LI> 063 * <LI> 064 * All ASCII control characters (Unicode characters U+0000 through U+001F) 065 * must be escaped. They can all be escaped by prefixing the 066 * four-hexadecimal-digit Unicode character code with {@code \}{@code u}, 067 * like {@code \}{@code u0000} to represent the ASCII null character U+0000. 068 * For certain characters, a more user-friendly escape sequence is also 069 * defined: 070 * <UL> 071 * <LI> 072 * The horizontal tab character can be escaped as either {@code \t} or 073 * {@code \}{@code u0009}. 074 * </LI> 075 * <LI> 076 * The newline character can be escaped as either {@code \n} or 077 * {@code \}{@code u000A}. 078 * </LI> 079 * <LI> 080 * The formfeed character can be escaped as either {@code \f} or 081 * {@code \}{@code u000C}. 082 * </LI> 083 * <LI> 084 * The carriage return character can be escaped as either {@code \r} or 085 * {@code \}{@code u000D}. 086 * </LI> 087 * </UL> 088 * </LI> 089 * </UL> 090 * In addition, any other character may optionally be escaped by placing the 091 * {@code \}{@code u} prefix in front of each four-hexadecimal digit sequence in 092 * the UTF-16 representation of that character. For example, the "LATIN SMALL 093 * LETTER N WITH TILDE" character U+00F1 may be escaped as 094 * {@code \}{@code u00F1}, while the "MUSICAL SYMBOL G CLEF" character U+1D11E 095 * may be escaped as {@code \}{@code uD834}{@code \}{@code uDD1E}. And while 096 * the forward slash character is not required to be escaped in JSON strings, it 097 * can be escaped using {@code \/} as a more human-readable alternative to 098 * {@code \}{@code u002F}. 099 * <BR><BR> 100 * The string provided to the {@link #JSONString(String)} constructor should not 101 * have any escaping performed, and the string returned by the 102 * {@link #stringValue()} method will not have any escaping performed. These 103 * methods work with the Java string that is represented by the JSON string. 104 * <BR><BR> 105 * If this JSON string was parsed from the string representation of a JSON 106 * object, then the value returned by the {@link #toString()} method (or 107 * appended to the buffer provided to the {@link #toString(StringBuilder)} 108 * method) will be the string representation used in the JSON object that was 109 * parsed. Otherwise, this class will generate an appropriate string 110 * representation, which will be surrounded by quotation marks and will have the 111 * minimal required encoding applied. 112 * <BR><BR> 113 * The string returned by the {@link #toNormalizedString()} method (or appended 114 * to the buffer provided to the {@link #toNormalizedString(StringBuilder)} 115 * method) will be generated by converting it to lowercase, surrounding it with 116 * quotation marks, and using the {@code \}{@code u}-style escaping for all 117 * characters other than the following (as contained in the LDAP printable 118 * character set defined in <A HREF="http://www.ietf.org/rfc/rfc4517.txt">RFC 119 * 4517</A> section 3.2, and indicated by the 120 * {@link StaticUtils#isPrintable(char)} method): 121 * <UL> 122 * <LI>All uppercase ASCII alphabetic letters (U+0041 through U+005A).</LI> 123 * <LI>All lowercase ASCII alphabetic letters (U+0061 through U+007A).</LI> 124 * <LI>All ASCII numeric digits (U+0030 through U+0039).</LI> 125 * <LI>The ASCII space character U+0020.</LI> 126 * <LI>The ASCII single quote (aka apostrophe) character U+0027.</LI> 127 * <LI>The ASCII left parenthesis character U+0028.</LI> 128 * <LI>The ASCII right parenthesis character U+0029.</LI> 129 * <LI>The ASCII plus sign character U+002B.</LI> 130 * <LI>The ASCII comma character U+002C.</LI> 131 * <LI>The ASCII minus sign (aka hyphen) character U+002D.</LI> 132 * <LI>The ASCII period character U+002E.</LI> 133 * <LI>The ASCII forward slash character U+002F.</LI> 134 * <LI>The ASCII colon character U+003A.</LI> 135 * <LI>The ASCII equals sign character U+003D.</LI> 136 * <LI>The ASCII question mark character U+003F.</LI> 137 * </UL> 138 */ 139@NotMutable() 140@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 141public final class JSONString 142 extends JSONValue 143{ 144 /** 145 * The serial version UID for this serializable class. 146 */ 147 private static final long serialVersionUID = -4677194657299153890L; 148 149 150 151 // The JSON-formatted string representation for this JSON string. It will be 152 // surrounded by quotation marks and any necessary escaping will have been 153 // performed. 154 private String jsonStringRepresentation; 155 156 // The string value for this object. 157 private final String value; 158 159 160 161 /** 162 * Creates a new JSON string. 163 * 164 * @param value The string to represent in this JSON value. It must not be 165 * {@code null}. 166 */ 167 public JSONString(final String value) 168 { 169 this.value = value; 170 jsonStringRepresentation = null; 171 } 172 173 174 175 /** 176 * Creates a new JSON string. This method should be used for strings parsed 177 * from the string representation of a JSON object. 178 * 179 * @param javaString The Java string to represent. 180 * @param jsonString The JSON string representation to use for the Java 181 * string. 182 */ 183 JSONString(final String javaString, final String jsonString) 184 { 185 value = javaString; 186 jsonStringRepresentation = jsonString; 187 } 188 189 190 191 /** 192 * Retrieves the string value for this object. This will be the interpreted 193 * value, without the surrounding quotation marks or escaping. 194 * 195 * @return The string value for this object. 196 */ 197 public String stringValue() 198 { 199 return value; 200 } 201 202 203 204 /** 205 * {@inheritDoc} 206 */ 207 @Override() 208 public int hashCode() 209 { 210 return stringValue().hashCode(); 211 } 212 213 214 215 /** 216 * {@inheritDoc} 217 */ 218 @Override() 219 public boolean equals(final Object o) 220 { 221 if (o == this) 222 { 223 return true; 224 } 225 226 if (o instanceof JSONString) 227 { 228 final JSONString s = (JSONString) o; 229 return value.equals(s.value); 230 } 231 232 return false; 233 } 234 235 236 237 /** 238 * Indicates whether the value of this JSON string matches that of the 239 * provided string, optionally ignoring differences in capitalization. 240 * 241 * @param s The JSON string to compare against this JSON string. 242 * It must not be {@code null}. 243 * @param ignoreCase Indicates whether to ignore differences in 244 * capitalization. 245 * 246 * @return {@code true} if the value of this JSON string matches the value of 247 * the provided string (optionally ignoring differences in 248 * capitalization), or {@code false} if not. 249 */ 250 public boolean equals(final JSONString s, final boolean ignoreCase) 251 { 252 if (ignoreCase) 253 { 254 return value.equalsIgnoreCase(s.value); 255 } 256 else 257 { 258 return value.equals(s.value); 259 } 260 } 261 262 263 264 /** 265 * {@inheritDoc} 266 */ 267 @Override() 268 public boolean equals(final JSONValue v, final boolean ignoreFieldNameCase, 269 final boolean ignoreValueCase, 270 final boolean ignoreArrayOrder) 271 { 272 return ((v instanceof JSONString) && 273 equals((JSONString) v, ignoreValueCase)); 274 } 275 276 277 278 /** 279 * Retrieves a string representation of this JSON string as it should appear 280 * in a JSON object, including the surrounding quotation marks and any 281 * appropriate escaping To obtain the string to which this value refers 282 * without the surrounding quotation marks or escaping, use the 283 * {@link #stringValue()} method. 284 * <BR><BR> 285 * If the object containing this string was decoded from a string, then this 286 * method will use the same string representation as in that original object. 287 * Otherwise, the string representation will be constructed. 288 * 289 * @return A string representation of this value as it should appear in a 290 * JSON object. 291 */ 292 @Override() 293 public String toString() 294 { 295 if (jsonStringRepresentation == null) 296 { 297 final StringBuilder buffer = new StringBuilder(); 298 toString(buffer); 299 jsonStringRepresentation = buffer.toString(); 300 } 301 302 return jsonStringRepresentation; 303 } 304 305 306 307 /** 308 * Appends a string representation of this JSON string as it should appear 309 * in a JSON object, including the surrounding quotation marks and any 310 * appropriate escaping, to the provided buffer. To obtain the string to 311 * which this value refers without the surrounding quotation marks or 312 * escaping, use the {@link #stringValue()} method. 313 * <BR><BR> 314 * If the object containing this string was decoded from a string, then this 315 * method will use the same string representation as in that original object. 316 * Otherwise, the string representation will be constructed. 317 * 318 * @param buffer The buffer to which the information should be appended. 319 */ 320 @Override() 321 public void toString(final StringBuilder buffer) 322 { 323 if (jsonStringRepresentation != null) 324 { 325 buffer.append(jsonStringRepresentation); 326 } 327 else 328 { 329 final boolean emptyBufferProvided = (buffer.length() == 0); 330 encodeString(value, buffer); 331 332 if (emptyBufferProvided) 333 { 334 jsonStringRepresentation = buffer.toString(); 335 } 336 } 337 } 338 339 340 341 /** 342 * Retrieves a single-line representation of this JSON string as it should 343 * appear in a JSON object, including the surrounding quotation marks and any 344 * appropriate escaping. To obtain the string to which this value refers 345 * without the surrounding quotation marks or escaping, use the 346 * {@link #stringValue()} method. 347 * 348 * @return A single-line representation of this value as it should appear in 349 * a JSON object. 350 */ 351 @Override() 352 public String toSingleLineString() 353 { 354 return toString(); 355 } 356 357 358 359 /** 360 * Appends a single-line string representation of this JSON string as it 361 * should appear in a JSON object, including the surrounding quotation marks 362 * and any appropriate escaping, to the provided buffer. To obtain the string 363 * to which this value refers without the surrounding quotation marks or 364 * escaping, use the {@link #stringValue()} method. 365 * 366 * @param buffer The buffer to which the information should be appended. 367 */ 368 @Override() 369 public void toSingleLineString(final StringBuilder buffer) 370 { 371 toString(buffer); 372 } 373 374 375 376 /** 377 * Appends a minimally-escaped JSON representation of the provided string to 378 * the given buffer. When escaping is required, the most user-friendly form 379 * of escaping will be used. 380 * 381 * @param s The string to be encoded. 382 * @param buffer The buffer to which the encoded representation should be 383 * appended. 384 */ 385 static void encodeString(final String s, final StringBuilder buffer) 386 { 387 buffer.append('"'); 388 389 for (final char c : s.toCharArray()) 390 { 391 switch (c) 392 { 393 case '"': 394 buffer.append("\\\""); 395 break; 396 case '\\': 397 buffer.append("\\\\"); 398 break; 399 case '\b': // backspace 400 buffer.append("\\b"); 401 break; 402 case '\f': // formfeed 403 buffer.append("\\f"); 404 break; 405 case '\n': // newline 406 buffer.append("\\n"); 407 break; 408 case '\r': // carriage return 409 buffer.append("\\r"); 410 break; 411 case '\t': // horizontal tab 412 buffer.append("\\t"); 413 break; 414 default: 415 if (c <= '\u001F') 416 { 417 buffer.append("\\u"); 418 buffer.append(String.format("%04X", (int) c)); 419 } 420 else 421 { 422 buffer.append(c); 423 } 424 break; 425 } 426 } 427 428 buffer.append('"'); 429 } 430 431 432 433 /** 434 * Appends a minimally-escaped JSON representation of the provided string to 435 * the given buffer. When escaping is required, the most user-friendly form 436 * of escaping will be used. 437 * 438 * @param s The string to be encoded. 439 * @param buffer The buffer to which the encoded representation should be 440 * appended. 441 */ 442 static void encodeString(final String s, final ByteStringBuffer buffer) 443 { 444 buffer.append('"'); 445 446 for (final char c : s.toCharArray()) 447 { 448 switch (c) 449 { 450 case '"': 451 buffer.append("\\\""); 452 break; 453 case '\\': 454 buffer.append("\\\\"); 455 break; 456 case '\b': // backspace 457 buffer.append("\\b"); 458 break; 459 case '\f': // formfeed 460 buffer.append("\\f"); 461 break; 462 case '\n': // newline 463 buffer.append("\\n"); 464 break; 465 case '\r': // carriage return 466 buffer.append("\\r"); 467 break; 468 case '\t': // horizontal tab 469 buffer.append("\\t"); 470 break; 471 default: 472 if (c <= '\u001F') 473 { 474 buffer.append("\\u"); 475 buffer.append(String.format("%04X", (int) c)); 476 } 477 else 478 { 479 buffer.append(c); 480 } 481 break; 482 } 483 } 484 485 buffer.append('"'); 486 } 487 488 489 490 /** 491 * Retrieves a normalized representation of this JSON string as it should 492 * appear in a JSON object, including the surrounding quotes and any 493 * appropriate escaping. The normalized representation will use the unescaped 494 * ASCII representation of all of the following characters: 495 * <UL> 496 * <LI>The letters a through z (ASCII character codes 0x61 through 497 * 0x7A).</LI> 498 * <LI>The digits 0 through 9 (ASCII character codes 0x30 through 499 * 0x39).</LI> 500 * <LI>The space (ASCII character code 0x20).</LI> 501 * <LI>The single quote (ASCII character code 0x27).</LI> 502 * <LI>The left parenthesis (ASCII character code 0x28).</LI> 503 * <LI>The right parenthesis (ASCII character code 0x29).</LI> 504 * <LI>The plus sign (ASCII character code 0x2B).</LI> 505 * <LI>The comma (ASCII character code 0x2C).</LI> 506 * <LI>The hyphen (ASCII character code 0x2D).</LI> 507 * <LI>The period (ASCII character code 0x2E).</LI> 508 * <LI>The forward slash (ASCII character code 0x2F).</LI> 509 * <LI>The colon (ASCII character code 0x3A).</LI> 510 * <LI>The equal sign (ASCII character code 0x3D).</LI> 511 * <LI>The question mark (ASCII character code 0x3F).</LI> 512 * </UL> 513 * All characters except those listed above will be escaped using their 514 * Unicode representation. 515 * 516 * @return A normalized representation of this JSON string as it should 517 * appear in a JSON object, including 518 */ 519 @Override() 520 public String toNormalizedString() 521 { 522 final StringBuilder buffer = new StringBuilder(); 523 toNormalizedString(buffer); 524 return buffer.toString(); 525 } 526 527 528 529 /** 530 * Appends a normalized representation of this JSON string as it should 531 * appear in a JSON object, including the surrounding quotes and any 532 * appropriate escaping, to the provided buffer. The normalized 533 * representation will use the unescaped ASCII representation of all of the 534 * following characters: 535 * <UL> 536 * <LI>The letters a through z (ASCII character codes 0x61 through 537 * 0x7A).</LI> 538 * <LI>The digits 0 through 9 (ASCII character codes 0x30 through 539 * 0x39).</LI> 540 * <LI>The space (ASCII character code 0x20).</LI> 541 * <LI>The single quote (ASCII character code 0x27).</LI> 542 * <LI>The left parenthesis (ASCII character code 0x28).</LI> 543 * <LI>The right parenthesis (ASCII character code 0x29).</LI> 544 * <LI>The plus sign (ASCII character code 0x2B).</LI> 545 * <LI>The comma (ASCII character code 0x2C).</LI> 546 * <LI>The hyphen (ASCII character code 0x2D).</LI> 547 * <LI>The period (ASCII character code 0x2E).</LI> 548 * <LI>The forward slash (ASCII character code 0x2F).</LI> 549 * <LI>The colon (ASCII character code 0x3A).</LI> 550 * <LI>The equal sign (ASCII character code 0x3D).</LI> 551 * <LI>The question mark (ASCII character code 0x3F).</LI> 552 * </UL> 553 * All characters except those listed above will be escaped using their 554 * Unicode representation. 555 * 556 * @param buffer The buffer to which the information should be appended. 557 */ 558 @Override() 559 public void toNormalizedString(final StringBuilder buffer) 560 { 561 toNormalizedString(buffer, false, true, false); 562 } 563 564 565 566 /** 567 * Retrieves a normalized representation of this JSON string as it should 568 * appear in a JSON object, including the surrounding quotes and any 569 * appropriate escaping. The normalized representation will use the unescaped 570 * ASCII representation of all of the following characters: 571 * <UL> 572 * <LI>The letters a through z (ASCII character codes 0x61 through 573 * 0x7A).</LI> 574 * <LI>The letters A through Z (ASCII character codes 0x41 through 0x5A). 575 * These characters will only be used if {@code ignoreValueCase} is 576 * {@code false}.</LI> 577 * <LI>The digits 0 through 9 (ASCII character codes 0x30 through 578 * 0x39).</LI> 579 * <LI>The space (ASCII character code 0x20).</LI> 580 * <LI>The single quote (ASCII character code 0x27).</LI> 581 * <LI>The left parenthesis (ASCII character code 0x28).</LI> 582 * <LI>The right parenthesis (ASCII character code 0x29).</LI> 583 * <LI>The plus sign (ASCII character code 0x2B).</LI> 584 * <LI>The comma (ASCII character code 0x2C).</LI> 585 * <LI>The hyphen (ASCII character code 0x2D).</LI> 586 * <LI>The period (ASCII character code 0x2E).</LI> 587 * <LI>The forward slash (ASCII character code 0x2F).</LI> 588 * <LI>The colon (ASCII character code 0x3A).</LI> 589 * <LI>The equal sign (ASCII character code 0x3D).</LI> 590 * <LI>The question mark (ASCII character code 0x3F).</LI> 591 * </UL> 592 * All characters except those listed above will be escaped using their 593 * Unicode representation. 594 * 595 * @param ignoreFieldNameCase Indicates whether field names should be 596 * treated in a case-sensitive (if {@code false}) 597 * or case-insensitive (if {@code true}) manner. 598 * @param ignoreValueCase Indicates whether string field values should 599 * be treated in a case-sensitive (if 600 * {@code false}) or case-insensitive (if 601 * {@code true}) manner. 602 * @param ignoreArrayOrder Indicates whether the order of elements in an 603 * array should be considered significant (if 604 * {@code false}) or insignificant (if 605 * {@code true}). 606 * 607 * @return A normalized representation of this JSON string as it should 608 * appear in a JSON object, including 609 */ 610 @Override() 611 public String toNormalizedString(final boolean ignoreFieldNameCase, 612 final boolean ignoreValueCase, 613 final boolean ignoreArrayOrder) 614 { 615 final StringBuilder buffer = new StringBuilder(); 616 toNormalizedString(buffer, ignoreFieldNameCase, ignoreValueCase, 617 ignoreArrayOrder); 618 return buffer.toString(); 619 } 620 621 622 623 /** 624 * Appends a normalized representation of this JSON string as it should 625 * appear in a JSON object, including the surrounding quotes and any 626 * appropriate escaping, to the provided buffer. The normalized 627 * representation will use the unescaped ASCII representation of all of the 628 * following characters: 629 * <UL> 630 * <LI>The letters a through z (ASCII character codes 0x61 through 631 * 0x7A).</LI> 632 * <LI>The letters A through Z (ASCII character codes 0x41 through 0x5A). 633 * These characters will only be used if {@code ignoreValueCase} is 634 * {@code false}.</LI> 635 * <LI>The digits 0 through 9 (ASCII character codes 0x30 through 636 * 0x39).</LI> 637 * <LI>The space (ASCII character code 0x20).</LI> 638 * <LI>The single quote (ASCII character code 0x27).</LI> 639 * <LI>The left parenthesis (ASCII character code 0x28).</LI> 640 * <LI>The right parenthesis (ASCII character code 0x29).</LI> 641 * <LI>The plus sign (ASCII character code 0x2B).</LI> 642 * <LI>The comma (ASCII character code 0x2C).</LI> 643 * <LI>The hyphen (ASCII character code 0x2D).</LI> 644 * <LI>The period (ASCII character code 0x2E).</LI> 645 * <LI>The forward slash (ASCII character code 0x2F).</LI> 646 * <LI>The colon (ASCII character code 0x3A).</LI> 647 * <LI>The equal sign (ASCII character code 0x3D).</LI> 648 * <LI>The question mark (ASCII character code 0x3F).</LI> 649 * </UL> 650 * All characters except those listed above will be escaped using their 651 * Unicode representation. 652 * 653 * @param buffer The buffer to which the information should be 654 * appended. 655 * @param ignoreFieldNameCase Indicates whether field names should be 656 * treated in a case-sensitive (if {@code false}) 657 * or case-insensitive (if {@code true}) manner. 658 * @param ignoreValueCase Indicates whether string field values should 659 * be treated in a case-sensitive (if 660 * {@code false}) or case-insensitive (if 661 * {@code true}) manner. 662 * @param ignoreArrayOrder Indicates whether the order of elements in an 663 * array should be considered significant (if 664 * {@code false}) or insignificant (if 665 * {@code true}). 666 */ 667 @Override() 668 public void toNormalizedString(final StringBuilder buffer, 669 final boolean ignoreFieldNameCase, 670 final boolean ignoreValueCase, 671 final boolean ignoreArrayOrder) 672 { 673 buffer.append('"'); 674 675 final char[] charArray; 676 if (ignoreValueCase) 677 { 678 charArray = StaticUtils.toLowerCase(value).toCharArray(); 679 } 680 else 681 { 682 charArray = value.toCharArray(); 683 } 684 685 for (final char c : charArray) 686 { 687 if (StaticUtils.isPrintable(c)) 688 { 689 buffer.append(c); 690 } 691 else 692 { 693 buffer.append("\\u"); 694 buffer.append(String.format("%04X", (int) c)); 695 } 696 } 697 698 buffer.append('"'); 699 } 700 701 702 703 /** 704 * {@inheritDoc} 705 */ 706 @Override() 707 public void appendToJSONBuffer(final JSONBuffer buffer) 708 { 709 buffer.appendString(value); 710 } 711 712 713 714 /** 715 * {@inheritDoc} 716 */ 717 @Override() 718 public void appendToJSONBuffer(final String fieldName, 719 final JSONBuffer buffer) 720 { 721 buffer.appendString(fieldName, value); 722 } 723}