Skip to content

Commit b9fe3ef

Browse files
committed
Issue #145 uplift upstream jdk.incubator.json sources at 43325738c
Take upstream public API (11 files incl. new JsonValueException, package-info) and impl (10 files incl. new JsonGenerator, JsonValueSupport) with mechanical transforms: package mapping jdk.incubator.json.impl -> jdk.incubator.internal.util.json, jdk.incubator.json -> jdk.incubator.java.util.json; unnamed _ variables named (ignored/v); Utils.powExact polyfill re-applied (upstream uses Math.powExact); LazyConstant polyfill preserved (API identical). Deleted JsonAssertionException (replaced by upstream JsonValueException), JsonValueImpl (folded into JsonValueSupport) and dead StableValue polyfill. Docs updated accordingly.
1 parent 1b868f5 commit b9fe3ef

29 files changed

Lines changed: 1230 additions & 1029 deletions

‎json-java21/AGENTS.md‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,14 +106,14 @@ upstream call sites, **no import or call-site rewrite is needed**; upstream `Laz
106106
usages compile unchanged against the polyfill.
107107

108108
This file is NOT from upstream and must be preserved during sync. The legacy
109-
`StableValue.java` polyfill (for the pre-`c1a4f80` upstream API) is unused dead code and is
109+
`StableValue.java` polyfill (for the pre-`c1a4f80` upstream API) was unused dead code and was
110110
removed during the incubator uplift.
111111

112112
#### 4.4 DO NOT Convert JavaDoc to JEP 467 Markdown
113113
If upstream uses `/** ... */` style, DO NOT convert them to our `/// ...` format; we will not edit the upstream files more than the absolute minimum to get them to run on Java 21.
114114

115-
#### 4.5 JsonAssertionException (Shipped Upstream)
116-
Upstream at `c1a4f80` DOES ship `java/util/json/JsonAssertionException.java`; it is NOT a local
115+
#### 4.5 JsonValueException (Shipped Upstream)
116+
Upstream at `c1a4f80` DOES ship `java/util/json/JsonValueException.java`; it is NOT a local
117117
addition. Our copy is a minimized mechanical backport of the upstream file (copyright header,
118118
javadoc and `@Serial serialVersionUID` stripped; behaviour identical). Take the upstream file
119119
with the standard transforms of 4.1/4.2; do not treat it as local-only.
@@ -169,8 +169,9 @@ $(command -v mvnd || command -v mvn || command -v ./mvnw) clean test -pl json-ja
169169
| `jdk/incubator/internal/util/json/LazyConstant.java` | Java 21 polyfill for the JDK `java.lang.LazyConstant` API used by upstream since `c1a4f80` |
170170
| `jdk/incubator/demo/JsonDemo.java` | Demonstration/example code |
171171

172-
Note: `JsonAssertionException.java` is shipped upstream (see step 4.5) and
173-
`StableValue.java` is unused legacy pending removal; neither is a local addition anymore.
172+
Note: since the `43325738c` uplift, upstream ships `JsonValueException.java` instead of
173+
`JsonValueException.java`, and `StableValue.java` has been removed; neither is a
174+
local addition.
174175

175176
## Transformation Example
176177

‎json-java21/src/main/java/jdk/incubator/internal/util/json/JsonArrayImpl.java‎

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,15 @@
2727

2828
import java.util.Collections;
2929
import java.util.List;
30+
import java.util.Locale;
3031

3132
import jdk.incubator.java.util.json.JsonArray;
3233
import jdk.incubator.java.util.json.JsonValue;
34+
3335
/**
3436
* JsonArray implementation class
3537
*/
36-
public final class JsonArrayImpl implements JsonArray, JsonValueImpl {
38+
public final class JsonArrayImpl implements JsonArray, JsonValueSupport {
3739

3840
private final List<JsonValue> theValues;
3941
private final int offset;
@@ -49,11 +51,24 @@ public JsonArrayImpl(List<JsonValue> from, int o, char[] d) {
4951
doc = d;
5052
}
5153

54+
// Conversion override
5255
@Override
53-
public List<JsonValue> elements() {
56+
public List<JsonValue> asList() {
5457
return Collections.unmodifiableList(theValues);
5558
}
5659

60+
// Navigation overrides (on default) -> bypass the unmodifiable wrap
61+
@Override
62+
public JsonValue get(int index) {
63+
try {
64+
return theValues.get(index);
65+
} catch (IndexOutOfBoundsException ignored) {
66+
throw Utils.composeError(this, String.format(Locale.ROOT,
67+
"JsonArray index %d out of bounds for length %d.",
68+
index, theValues.size()));
69+
}
70+
}
71+
5772
@Override
5873
public char[] doc() {
5974
return doc;
@@ -66,24 +81,6 @@ public int offset() {
6681

6782
@Override
6883
public String toString() {
69-
var s = new StringBuilder("[");
70-
for (JsonValue v: elements()) {
71-
s.append(v.toString()).append(",");
72-
}
73-
if (!elements().isEmpty()) {
74-
s.setLength(s.length() - 1); // trim final comma
75-
}
76-
return s.append("]").toString();
77-
}
78-
79-
@Override
80-
public boolean equals(Object o) {
81-
return o instanceof JsonArray oja &&
82-
elements().equals(oja.elements());
83-
}
84-
85-
@Override
86-
public int hashCode() {
87-
return elements().hashCode();
84+
return JsonGenerator.toCompactString(this);
8885
}
8986
}

‎json-java21/src/main/java/jdk/incubator/internal/util/json/JsonBooleanImpl.java‎

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,26 +26,27 @@
2626
package jdk.incubator.internal.util.json;
2727

2828
import jdk.incubator.java.util.json.JsonBoolean;
29+
2930
/**
3031
* JsonBoolean implementation class
3132
*/
32-
public final class JsonBooleanImpl implements JsonBoolean, JsonValueImpl {
33+
public final class JsonBooleanImpl implements JsonBoolean, JsonValueSupport {
3334

34-
private final Boolean theBoolean;
35+
private final boolean theBoolean;
3536
private final int offset;
3637
private final char[] doc;
3738

3839
public static final JsonBooleanImpl TRUE = new JsonBooleanImpl(true, null, -1);
3940
public static final JsonBooleanImpl FALSE = new JsonBooleanImpl(false, null, -1);
4041

41-
public JsonBooleanImpl(Boolean bool, char[] doc, int offset) {
42+
public JsonBooleanImpl(boolean bool, char[] doc, int offset) {
4243
theBoolean = bool;
4344
this.doc = doc;
4445
this.offset = offset;
4546
}
4647

4748
@Override
48-
public boolean bool() {
49+
public boolean asBoolean() {
4950
return theBoolean;
5051
}
5152

@@ -61,16 +62,6 @@ public int offset() {
6162

6263
@Override
6364
public String toString() {
64-
return String.valueOf(bool());
65-
}
66-
67-
@Override
68-
public boolean equals(Object o) {
69-
return o instanceof JsonBoolean ojb && bool() == ojb.bool();
70-
}
71-
72-
@Override
73-
public int hashCode() {
74-
return Boolean.hashCode(bool());
65+
return String.valueOf(asBoolean());
7566
}
7667
}
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
/*
2+
* Copyright (c) 2026, 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 jdk.incubator.internal.util.json;
27+
28+
import java.util.ArrayDeque;
29+
import java.util.Deque;
30+
import java.util.Iterator;
31+
import java.util.Map;
32+
33+
import jdk.incubator.java.util.json.JsonArray;
34+
import jdk.incubator.java.util.json.JsonObject;
35+
import jdk.incubator.java.util.json.JsonValue;
36+
37+
/**
38+
* Generates JSON text for JsonValue, either for toString() or toDisplayString().
39+
*/
40+
public final class JsonGenerator {
41+
42+
private sealed interface StructureFrame permits ArrayFrame, ObjectFrame {}
43+
44+
private static final class ArrayFrame implements StructureFrame {
45+
private final Iterator<JsonValue> elements;
46+
private final int depth; // For indentation
47+
private boolean first; // Whether iterator points to first value
48+
49+
private ArrayFrame(Iterator<JsonValue> elements, int depth) {
50+
this.elements = elements;
51+
this.depth = depth;
52+
first = true;
53+
}
54+
}
55+
56+
private static final class ObjectFrame implements StructureFrame {
57+
private final Iterator<Map.Entry<String, JsonValue>> members;
58+
private final int depth; // For indentation
59+
private boolean first; // Whether iterator points to first entry
60+
61+
private ObjectFrame(Iterator<Map.Entry<String, JsonValue>> members, int depth) {
62+
this.members = members;
63+
this.depth = depth;
64+
first = true;
65+
}
66+
}
67+
68+
// Generates JSON text for Json[Object|Array].toString()
69+
public static String toCompactString(JsonValue jv) {
70+
return generate(jv, "", false);
71+
}
72+
73+
// Generates JSON text for Json.toDisplayString()
74+
public static String toDisplayString(JsonValue jv, String indent) {
75+
return generate(jv, indent, true);
76+
}
77+
78+
private static String generate(JsonValue root, String indent, boolean isDisplay) {
79+
var sb = new StringBuilder();
80+
Deque<StructureFrame> stack = new ArrayDeque<>();
81+
enterValue(root, sb, stack, 0, isDisplay);
82+
83+
while (!stack.isEmpty()) {
84+
switch (stack.peek()) {
85+
case ArrayFrame af -> {
86+
var elements = af.elements;
87+
if (elements.hasNext()) {
88+
if (af.first) {
89+
af.first = false;
90+
} else {
91+
sb.append(isDisplay ? ",\n" : ",");
92+
}
93+
if (isDisplay) {
94+
sb.repeat(indent, af.depth + 1);
95+
}
96+
enterValue(elements.next(), sb, stack, af.depth + 1, isDisplay);
97+
} else {
98+
if (isDisplay) {
99+
sb.append("\n");
100+
sb.repeat(indent, af.depth);
101+
}
102+
sb.append("]");
103+
stack.pop();
104+
}
105+
}
106+
case ObjectFrame of -> {
107+
var members = of.members;
108+
if (members.hasNext()) {
109+
if (of.first) {
110+
of.first = false;
111+
} else {
112+
sb.append(isDisplay ? ",\n" : ",");
113+
}
114+
var entry = members.next();
115+
if (isDisplay) {
116+
sb.repeat(indent, of.depth + 1);
117+
}
118+
sb.append('"')
119+
.append(Utils.escape(entry.getKey()))
120+
.append("\":")
121+
.append(isDisplay ? " " : "");
122+
enterValue(entry.getValue(), sb, stack, of.depth + 1, isDisplay);
123+
} else {
124+
if (isDisplay) {
125+
sb.append("\n");
126+
sb.repeat(indent, of.depth);
127+
}
128+
sb.append("}");
129+
stack.pop();
130+
}
131+
}
132+
}
133+
}
134+
return sb.toString();
135+
}
136+
137+
private static void enterValue(JsonValue jv, StringBuilder sb, Deque<StructureFrame> stack,
138+
int depth, boolean isDisplay) {
139+
switch (jv) {
140+
case JsonArray ja -> {
141+
var elements = ja.asList().iterator();
142+
if (!elements.hasNext()) {
143+
sb.append("[]");
144+
} else {
145+
sb.append(isDisplay ? "[\n" : "[");
146+
stack.push(new ArrayFrame(elements, depth));
147+
}
148+
}
149+
case JsonObject jo -> {
150+
var members = jo.asMap().entrySet().iterator();
151+
if (!members.hasNext()) {
152+
sb.append("{}");
153+
} else {
154+
sb.append(isDisplay ? "{\n" : "{");
155+
stack.push(new ObjectFrame(members, depth));
156+
}
157+
}
158+
default -> sb.append(jv);
159+
}
160+
}
161+
162+
// Instantiation is not allowed
163+
private JsonGenerator() {}
164+
}

‎json-java21/src/main/java/jdk/incubator/internal/util/json/JsonNullImpl.java‎

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,17 @@
2626
package jdk.incubator.internal.util.json;
2727

2828
import jdk.incubator.java.util.json.JsonNull;
29+
2930
/**
3031
* JsonNull implementation class
3132
*/
32-
public final class JsonNullImpl implements JsonNull, JsonValueImpl {
33+
public final class JsonNullImpl implements JsonNull, JsonValueSupport {
3334

3435
private final int offset;
3536
private final char[] doc;
3637

3738
public static final JsonNullImpl NULL = new JsonNullImpl(null, -1);
3839
private static final String VALUE = "null";
39-
private static final int HASH = VALUE.hashCode();
4040

4141
public JsonNullImpl(char[] doc, int offset) {
4242
this.doc = doc;
@@ -57,14 +57,4 @@ public int offset() {
5757
public String toString() {
5858
return VALUE;
5959
}
60-
61-
@Override
62-
public boolean equals(Object obj) {
63-
return obj instanceof JsonNull;
64-
}
65-
66-
@Override
67-
public int hashCode() {
68-
return HASH;
69-
}
7060
}

0 commit comments

Comments
 (0)