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