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 }