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.impl;
27
28 import oracle.code.json.JsonArray;
29 import oracle.code.json.JsonObject;
30 import oracle.code.json.JsonValue;
31
32 import java.util.List;
33 import java.util.Map;
34
35 /**
36 * Shared utilities for Json classes.
37 */
38 public class Utils {
39
40 // Non instantiable
41 private Utils() {}
42
43 // Equivalent to JsonObject/Array.of() factories without the need for defensive copy
44 // and other input validation
45 public static JsonArray arrayOf(List<JsonValue> list) {
46 return new JsonArrayImpl(list);
47 }
48
49 public static JsonObject objectOf(Map<String, JsonValue> map) {
50 return new JsonObjectImpl(map);
51 }
52
53 // Used for escaping String values, applicable to JSON Strings and member names
54 public static String unescape(char[] doc, int startOffset, int endOffset) {
55 StringBuilder sb = null; // Only use if required
56 var escape = false;
57 int offset = startOffset;
58 boolean useBldr = false;
59 for (; offset < endOffset; offset++) {
60 var c = doc[offset];
61 if (escape) {
62 var length = 0;
63 switch (c) {
64 case '"', '\\', '/' -> {}
65 case 'b' -> c = '\b';
66 case 'f' -> c = '\f';
67 case 'n' -> c = '\n';
68 case 'r' -> c = '\r';
69 case 't' -> c = '\t';
70 case 'u' -> {
71 if (offset + 4 < endOffset) {
72 c = codeUnit(doc, offset + 1);
73 length = 4;
74 } else {
75 throw new IllegalArgumentException("Illegal Unicode escape sequence");
76 }
77 }
78 default -> throw new IllegalArgumentException("Illegal escape sequence");
79 }
80 if (!useBldr) {
81 useBldr = true;
82 // At best, we know the size of the first escaped value
83 sb = new StringBuilder(endOffset - startOffset - length - 1)
84 .append(doc, startOffset, offset - 1 - startOffset);
85 }
86 offset+=length;
87 escape = false;
88 } else if (c == '\\') {
89 escape = true;
90 continue;
91 }
92 if (useBldr) {
93 sb.append(c);
94 }
95 }
96 if (useBldr) {
97 return sb.toString();
98 } else {
99 return new String(doc, startOffset, endOffset - startOffset);
100 }
101 }
102
103 // Validate and construct corresponding value of Unicode escape sequence
104 // This method does not increment offset
105 static char codeUnit(char[] doc, int o) {
106 char val = 0;
107 for (int index = 0; index < 4; index ++) {
108 char c = doc[o + index];
109 val <<= 4;
110 val += (char) (
111 switch (c) {
112 case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' -> c - '0';
113 case 'a', 'b', 'c', 'd', 'e', 'f' -> c - 'a' + 10;
114 case 'A', 'B', 'C', 'D', 'E', 'F' -> c - 'A' + 10;
115 default -> throw new IllegalArgumentException("Illegal Unicode escape sequence");
116 });
117 }
118 return val;
119 }
120 }