1 /* 2 * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package oracle.code.json; 27 28 import java.util.Objects; 29 30 /** 31 * JsonString implementation class 32 */ 33 final class JsonStringImpl implements JsonString, JsonValueImpl { 34 35 private final JsonDocumentInfo docInfo; 36 private final int startOffset; 37 private final int endOffset; 38 private final int endIndex; 39 private String theString; 40 private String source; 41 42 JsonStringImpl(String str) { 43 docInfo = new JsonDocumentInfo(("\"" + str + "\"").toCharArray()); 44 startOffset = 0; 45 endOffset = docInfo.getEndOffset(); 46 theString = unescape(startOffset + 1, endOffset - 1); 47 endIndex = 0; 48 } 49 50 JsonStringImpl(JsonDocumentInfo doc, int offset, int index) { 51 docInfo = doc; 52 startOffset = offset; 53 endIndex = index + 1; 54 endOffset = docInfo.getOffset(endIndex) + 1; 55 } 56 57 @Override 58 public String value() { 59 if (theString == null) { 60 theString = unescape(startOffset + 1, endOffset - 1); 61 } 62 return theString; 63 } 64 65 @Override 66 public int getEndIndex() { 67 return endIndex + 1; // We are interested in the index after '"' 68 } 69 70 @Override 71 public boolean equals(Object o) { 72 return this == o || 73 o instanceof JsonStringImpl ojsi && 74 Objects.equals(toString(), ojsi.toString()); 75 } 76 77 @Override 78 public int hashCode() { 79 return Objects.hash(toString()); 80 } 81 82 @Override 83 public String toUntyped() { 84 return value(); 85 } 86 87 @Override 88 public String toString() { 89 if (source == null) { 90 source = docInfo.substring(startOffset, endOffset); 91 } 92 return source; 93 } 94 95 String unescape(int startOffset, int endOffset) { 96 var sb = new StringBuilder(); 97 var escape = false; 98 int offset = startOffset; 99 for (; offset < endOffset; offset++) { 100 var c = docInfo.charAt(offset); 101 if (escape) { 102 switch (c) { 103 case '"', '\\', '/' -> {} 104 case 'b' -> c = '\b'; 105 case 'f' -> c = '\f'; 106 case 'n' -> c = '\n'; 107 case 'r' -> c = '\r'; 108 case 't' -> c = '\t'; 109 case 'u' -> { 110 c = JsonParser.codeUnit(docInfo, offset + 1); 111 offset += 4; 112 } 113 // TBD: should be replaced with appropriate runtime exception 114 default -> throw new RuntimeException("Illegal escape sequence"); 115 } 116 escape = false; 117 } else if (c == '\\') { 118 escape = true; 119 continue; 120 } 121 sb.append(c); 122 } 123 return sb.toString(); 124 } 125 }