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 oracle.code.json.impl.JsonObjectImpl;
29 import oracle.code.json.impl.Utils;
30
31 import java.util.LinkedHashMap;
32 import java.util.Map;
33 import java.util.Objects;
34
35 /**
36 * The interface that represents JSON object.
37 * <p>
38 * A {@code JsonObject} can be produced by a {@link Json#parse(String)}.
39 * <p> Alternatively, {@link #of(Map)} can be used to obtain a {@code JsonObject}.
40 * Implementations of {@code JsonObject} cannot be created from sources that
41 * contain duplicate member names. If duplicate names appear during
42 * a {@link Json#parse(String)}, a {@code JsonParseException} is thrown.
43 *
44 * @since 99
45 */
46 public non-sealed interface JsonObject extends JsonValue {
47
48 /**
49 * {@return an unmodifiable map of the {@code String} to {@code JsonValue}
50 * members in this {@code JsonObject}}
51 */
52 Map<String, JsonValue> members();
53
54 /**
55 * {@return the {@code JsonObject} created from the given
56 * map of {@code String} to {@code JsonValue}s}
57 *
58 * The {@code JsonObject}'s members occur in the same order as the given
59 * map's entries.
60 * <p>
61 * If a key in the provided {@code map} contains escape characters, they are
62 * unescaped before being added to the resulting {@code JsonObject}. If multiple
63 * keys unescape to the same value, an {@code IllegalArgumentException} is thrown.
64 *
65 * @param map the map of {@code JsonValue}s. Non-null.
66 * @throws IllegalArgumentException if {@code map} contains multiple keys
67 * that unescape to the same value
68 * @throws NullPointerException if {@code map} is {@code null}, contains
69 * any keys that are {@code null}, or contains any values that are {@code null}
70 */
71 static JsonObject of(Map<String, ? extends JsonValue> map) {
72 Map<String, JsonValue> ret = new LinkedHashMap<>(map.size()); // implicit NPE on map
73 for (var e : map.entrySet()) {
74 var key = e.getKey();
75 // Implicit NPE on key
76 var unescapedKey = Utils.unescape(key.toCharArray(), 0, key.length());
77 var val = e.getValue();
78 if (ret.containsKey(unescapedKey)) {
79 throw new IllegalArgumentException(
80 "Multiple keys unescape to the same value: '%s'".formatted(unescapedKey));
81 } else {
82 ret.put(unescapedKey, Objects.requireNonNull(val));
83 }
84 }
85 return new JsonObjectImpl(ret);
86 }
87
88 /**
89 * {@return {@code true} if the given object is also a {@code JsonObject}
90 * and the two {@code JsonObject}s represent the same mappings} Two
91 * {@code JsonObject}s {@code jo1} and {@code jo2} represent the same
92 * mappings if {@code jo1.members().equals(jo2.members())}.
93 *
94 * @see #members()
95 */
96 @Override
97 boolean equals(Object obj);
98
99 /**
100 * {@return the hash code value for this {@code JsonObject}} The hash code value
101 * of a {@code JsonObject} is defined to be the hash code of {@code JsonObject}'s
102 * {@link #members()} value. Thus, for two {@code JsonObject}s {@code jo1} and {@code jo2},
103 * {@code jo1.equals(jo2)} implies that {@code jo1.hashCode() == jo2.hashCode()}
104 * as required by the general contract of {@link Object#hashCode}.
105 *
106 * @see #members()
107 */
108 @Override
109 int hashCode();
110 }