1 package org.simantics.document.server;
3 import java.io.IOException;
4 import java.util.Arrays;
5 import java.util.Iterator;
8 import java.util.Map.Entry;
10 import java.util.TreeMap;
12 import org.simantics.databoard.Bindings;
13 import org.simantics.databoard.adapter.AdaptException;
14 import org.simantics.databoard.adapter.Adapter;
15 import org.simantics.databoard.adapter.AdapterConstructionException;
16 import org.simantics.databoard.binding.ArrayBinding;
17 import org.simantics.databoard.binding.Binding;
18 import org.simantics.databoard.binding.MapBinding;
19 import org.simantics.databoard.binding.OptionalBinding;
20 import org.simantics.databoard.binding.RecordBinding;
21 import org.simantics.databoard.binding.error.BindingException;
22 import org.simantics.databoard.parser.repository.DataValueRepository;
23 import org.simantics.databoard.type.Component;
24 import org.simantics.databoard.type.RecordType;
25 import org.simantics.databoard.util.Bean;
26 import org.simantics.document.server.io.IJSONObject;
27 import org.simantics.scl.runtime.tuple.Tuple;
29 final public class JSONObject extends Bean implements IJSONObject {
31 final public String id;
32 final public TreeMap<String, Object> fields = new TreeMap<String, Object>();
33 private int hashCode = 0;
35 public JSONObject(Binding binding, String id) {
37 assert (binding != null);
39 this.id = id.intern();
42 public JSONObject(String id) {
44 this.id = id.intern();
47 public JSONObject clone() {
48 JSONObject result = new JSONObject(binding, id);
49 for (Map.Entry<String, Object> e : fields.entrySet())
50 result.addJSONField(e.getKey(), e.getValue());
54 public void add(Map<String, Object> fields) {
55 for (Map.Entry<String, Object> e : fields.entrySet())
56 addJSONField(e.getKey(), e.getValue());
60 public int hashCode() {
63 int result = id.hashCode();
64 Iterator<Entry<String,Object>> i = fields.entrySet().iterator();
66 Entry<String,Object> entry = i.next();
67 String key = entry.getKey();
68 Object value = entry.getValue();
70 if(value.getClass().isArray())
71 result += objectHashCode(key) ^ arrayHashCode(value);
73 result += objectHashCode(key) ^ objectHashCode(value);
75 result += objectHashCode(key);
85 * Returns the hash code of a non-{@code null} argument and 0 for
86 * a {@code null} argument.
89 * @return the hash code of a non-{@code null} argument and 0 for
90 * a {@code null} argument
91 * @see Object#hashCode
93 private static int objectHashCode(Object o) {
94 return o != null ? o.hashCode() : 0;
97 private final boolean arrayEquals(Object av1, Object av2) {
100 Class<?> c1 = av1.getClass().getComponentType();
101 Class<?> c2 = av2.getClass().getComponentType();
102 if (c2 == null || !c1.equals(c2))
104 boolean p1 = c1.isPrimitive();
105 boolean p2 = c2.isPrimitive();
109 return Arrays.equals((Object[]) av1, (Object[]) av2);
110 if (boolean.class.equals(c1))
111 return Arrays.equals((boolean[]) av1, (boolean[]) av2);
112 else if (byte.class.equals(c1))
113 return Arrays.equals((byte[]) av1, (byte[]) av2);
114 else if (int.class.equals(c1))
115 return Arrays.equals((int[]) av1, (int[]) av2);
116 else if (long.class.equals(c1))
117 return Arrays.equals((long[]) av1, (long[]) av2);
118 else if (float.class.equals(c1))
119 return Arrays.equals((float[]) av1, (float[]) av2);
120 else if (double.class.equals(c1))
121 return Arrays.equals((double[]) av1, (double[]) av2);
122 throw new RuntimeException("??? Contact application querySupport.");
125 private final int arrayHashCode(Object av) {
128 Class<?> c1 = av.getClass().getComponentType();
129 boolean p1 = c1.isPrimitive();
131 return Arrays.hashCode((Object[]) av);
132 if (boolean.class.equals(c1))
133 return Arrays.hashCode((boolean[]) av);
134 else if (byte.class.equals(c1))
135 return Arrays.hashCode((byte[]) av);
136 else if (int.class.equals(c1))
137 return Arrays.hashCode((int[]) av);
138 else if (long.class.equals(c1))
139 return Arrays.hashCode((long[]) av);
140 else if (float.class.equals(c1))
141 return Arrays.hashCode((float[]) av);
142 else if (double.class.equals(c1))
143 return Arrays.hashCode((double[]) av);
144 throw new RuntimeException("??? Contact application querySupport.");
148 public boolean equals(Object object) {
152 else if (object == null)
154 else if (!(object instanceof JSONObject))
156 JSONObject o = (JSONObject) object;
158 if (!id.equals(o.id))
161 Set<String> keys = fields.keySet();
162 Set<String> otherKeys = o.fields.keySet();
164 if (!keys.equals(otherKeys))
167 for (String key : keys) {
169 Object value = fields.get(key);
170 Object otherValue = o.fields.get(key);
172 if (otherValue != null) {
173 if (otherValue.getClass().isArray()) {
174 if (!arrayEquals(otherValue, value)) {
178 if (!otherValue.equals(value)) {
182 } else if (value != null)
191 public void addJSONField(String key, Object content) {
192 fields.put(key, content);
195 @SuppressWarnings("unchecked")
196 public <T> T getJSONField(String key) {
197 return (T) fields.get(key);
200 @SuppressWarnings("unchecked")
201 public <T> T getJSONFieldDefault(String key, T defaultValue) {
202 T value = (T) fields.get(key);
209 @SuppressWarnings("unchecked")
210 public <T> T getBeanJSONFieldDefault(String key, Binding target,
212 T value = (T) fields.get(key);
215 // if (value instanceof Bean) {
216 Binding source = Bindings.getBinding(target.type());
217 Adapter adapter = Bindings.getAdapter(source, target);
218 return (T) adapter.adapt(value);
222 } catch (AdapterConstructionException e) {
223 } catch (AdaptException e) {
228 public String getParent() {
229 return (String) fields.get("parent");
232 public String getParentOrd() {
233 return (String) fields.get("parentOrd");
236 public String getType() {
237 return (String) fields.get("type");
240 public String toString() {
241 StringBuilder b = new StringBuilder();
243 boolean first = true;
244 for (Map.Entry<String, Object> entry : fields.entrySet()) {
249 String key = entry.getKey();
250 String value = fieldJSON(entry.getValue());
252 first = true; // prevents ", ," when no key and value are given
266 private void printValue(Object value, Binding binding_, StringBuilder sb)
269 if (binding_ instanceof RecordBinding) {
270 RecordBinding binding = (RecordBinding) binding_;
272 RecordType type = binding.type();
273 for (int i = 0, j = 0; i < type.getComponentCount(); i++) {
275 Component c = type.getComponent(i);
277 Object field = binding.getComponent(value, i);
279 Binding b = binding.getComponentBinding(i);
280 if (b instanceof OptionalBinding) {
281 OptionalBinding ob = (OptionalBinding) b;
282 if (!ob.hasValueUnchecked(field))
284 b = ob.getComponentBinding();
295 printValue(field, b, sb);
298 } else if (binding_ instanceof ArrayBinding) {
299 ArrayBinding binding = (ArrayBinding) binding_;
300 Binding b = binding.getComponentBinding();
302 for (int i = 0; i < binding.size(value); i++) {
305 printValue(binding.get(value, i), b, sb);
308 } else if (binding_ instanceof MapBinding) {
310 MapBinding binding = (MapBinding) binding_;
312 for (Object key : binding.getKeys(value)) {
313 Object val = binding.get(value, key);
314 if (key instanceof String && val instanceof String) {
322 sb.append((String) key);
323 sb.append("\" : \"");
324 sb.append((String) val);
331 DataValueRepository rep = new DataValueRepository();
332 binding_.printValue(value, sb, rep, false);
334 } catch (BindingException e) {
339 private String printList(List<?> list) {
340 StringBuilder b = new StringBuilder();
342 boolean first = true;
343 for (Object o : list) {
349 b.append(fieldJSON(o));
355 private String fieldJSON(Object field) {
360 String valueString = null;
361 if (field instanceof Bean) {
364 Bean bean = (Bean) field;
365 StringBuilder sb = new StringBuilder();
366 printValue(bean, bean.getBinding(), sb);
367 valueString = sb.toString();
368 } catch (IOException e) {
371 } else if (field instanceof List) {
372 return printList((List<?>) field);
373 } else if (field instanceof Tuple) {
374 Tuple t = (Tuple) field;
375 if (t.length() == 2) {
376 Object o1 = t.get(0);
377 Object o2 = t.get(1);
378 if (o1 instanceof String) {
379 return fieldJSON(o1) + " : " + fieldJSON(o2);
381 return "{" + fieldJSON(o1) + " , " + fieldJSON(o2) + "}";
384 StringBuilder b = new StringBuilder();
386 for (int i = 0; i < t.length(); i++) {
389 b.append(fieldJSON(t.get(i)));
395 if (field.getClass().isArray()) {
398 if (field instanceof float[]) {
399 array = new Float[((float[]) field).length];
400 for (int i = 0; i < array.length; i++) {
401 array[i] = ((float[]) field)[i];
403 } else if (field instanceof int[]) {
404 array = new Integer[((int[]) field).length];
405 for (int i = 0; i < array.length; i++) {
406 array[i] = ((int[]) field)[i];
409 array = (Object[]) field;
411 // Build a string of the value array. Format is: [ value, value,
413 StringBuilder arrayBuilder = new StringBuilder();
414 arrayBuilder.append("[");
415 for (int i = 0; i < array.length; i++) {
418 arrayBuilder.append(",");
420 if (o instanceof String)
421 arrayBuilder.append("\"");
423 arrayBuilder.append(o.toString());
425 if (o instanceof String)
426 arrayBuilder.append("\"");
428 arrayBuilder.append("]");
429 valueString = arrayBuilder.toString();
431 if (field instanceof String) {
432 // Use a string representation of the value
433 valueString = quote((String) field);
435 // Use a string representation of the value
436 valueString = "\"" + field.toString() + "\"";
447 * Copied from org.json
449 Copyright (c) 2002 JSON.org
451 Permission is hereby granted, free of charge, to any person obtaining a copy
452 of this software and associated documentation files (the "Software"), to deal
453 in the Software without restriction, including without limitation the rights
454 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
455 copies of the Software, and to permit persons to whom the Software is
456 furnished to do so, subject to the following conditions:
458 The above copyright notice and this permission notice shall be included in all
459 copies or substantial portions of the Software.
461 The Software shall be used for Good, not Evil.
463 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
464 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
465 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
466 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
467 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
468 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
472 * Produce a string in double quotes with backslash sequences in all the
473 * right places. A backslash will be inserted within </, allowing JSON
474 * text to be delivered in HTML. In JSON text, a string cannot contain a
475 * control character or an unescaped quote or backslash.
476 * @param string A String
477 * @return A String correctly formatted for insertion in a JSON text.
479 public static String quote(String string) {
480 if (string == null || string.length() == 0) {
487 int len = string.length();
488 StringBuffer sb = new StringBuffer(len + 4);
492 for (i = 0; i < len; i += 1) {
494 c = string.charAt(i);
523 if (c < ' ' || (c >= '\u0080' && c < '\u00a0') ||
524 (c >= '\u2000' && c < '\u2100')) {
525 t = "000" + Integer.toHexString(c);
526 sb.append("\\u" + t.substring(t.length() - 4));
533 return sb.toString();
536 public String getId() {
540 @SuppressWarnings("unchecked")
542 public <T> T getValue(String key) {
543 return (T)fields.get(key);
547 public Iterator<String> keys() {
548 return fields.keySet().iterator();
552 public IJSONObject clone(Map<String, Object> newObjects) {
553 JSONObject result = new JSONObject(binding, id);
554 for (Map.Entry<String, Object> e : fields.entrySet())
555 result.addJSONField(e.getKey(), e.getValue());
557 for (Map.Entry<String, Object> e : newObjects.entrySet())
558 result.addJSONField(e.getKey(), e.getValue());