]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.document.server/src/org/simantics/document/server/JSONObject.java
Fixed multiple issues causing dangling references to discarded queries
[simantics/platform.git] / bundles / org.simantics.document.server / src / org / simantics / document / server / JSONObject.java
1 package org.simantics.document.server;
2
3 import java.io.IOException;
4 import java.util.Arrays;
5 import java.util.Iterator;
6 import java.util.List;
7 import java.util.Map;
8 import java.util.Map.Entry;
9 import java.util.Set;
10 import java.util.TreeMap;
11
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;
28
29 final public class JSONObject extends Bean implements IJSONObject {
30
31         final public String id;
32         final public TreeMap<String, Object> fields = new TreeMap<String, Object>();
33         private int hashCode = 0;
34
35         public JSONObject(Binding binding, String id) {
36                 super(binding);
37                 assert (binding != null);
38                 assert (id != null);
39                 this.id = id.intern();
40         }
41
42         public JSONObject(String id) {
43                 assert (id != null);
44                 this.id = id.intern();
45         }
46
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());
51                 return result;
52         }
53
54         public void add(Map<String, Object> fields) {
55                 for (Map.Entry<String, Object> e : fields.entrySet())
56                         addJSONField(e.getKey(), e.getValue());
57         }
58
59         @Override
60         public int hashCode() {
61                 
62                 if(hashCode == 0) {
63                         int result = id.hashCode();
64                         Iterator<Entry<String,Object>> i = fields.entrySet().iterator();
65                         while (i.hasNext()) {
66                                 Entry<String,Object> entry = i.next();
67                                 String key = entry.getKey();
68                                 Object value = entry.getValue();
69                                 if(value != null) {
70                                         if(value.getClass().isArray())
71                                                 result += objectHashCode(key) ^ arrayHashCode(value);
72                                         else 
73                                                 result += objectHashCode(key) ^ objectHashCode(value);
74                                 } else {
75                                         result += objectHashCode(key);
76                                 }
77                         }
78                         hashCode = result;
79                 }
80                 return hashCode;
81
82         }
83
84     /**
85      * Returns the hash code of a non-{@code null} argument and 0 for
86      * a {@code null} argument.
87      *
88      * @param o an object
89      * @return the hash code of a non-{@code null} argument and 0 for
90      * a {@code null} argument
91      * @see Object#hashCode
92      */
93     private static int objectHashCode(Object o) {
94         return o != null ? o.hashCode() : 0;
95     }
96
97         private final boolean arrayEquals(Object av1, Object av2) {
98                 if (av2 == null)
99                         return false;
100                 Class<?> c1 = av1.getClass().getComponentType();
101                 Class<?> c2 = av2.getClass().getComponentType();
102                 if (c2 == null || !c1.equals(c2))
103                         return false;
104                 boolean p1 = c1.isPrimitive();
105                 boolean p2 = c2.isPrimitive();
106                 if (p1 != p2)
107                         return false;
108                 if (!p1)
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.");
123         }
124
125         private final int arrayHashCode(Object av) {
126                 if (av == null)
127                         return 0;
128                 Class<?> c1 = av.getClass().getComponentType();
129                 boolean p1 = c1.isPrimitive();
130                 if (!p1)
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.");
145         }
146         
147         @Override
148         public boolean equals(Object object) {
149
150                 if (this == object)
151                         return true;
152                 else if (object == null)
153                         return false;
154                 else if (!(object instanceof JSONObject))
155                         return false;
156                 JSONObject o = (JSONObject) object;
157                 
158                 if (!id.equals(o.id))
159                         return false;
160
161                 Set<String> keys = fields.keySet();
162                 Set<String> otherKeys = o.fields.keySet();
163                 
164                 if (!keys.equals(otherKeys))
165                         return false;
166
167                 for (String key : keys) {
168
169                         Object value = fields.get(key);
170                         Object otherValue = o.fields.get(key);
171
172                         if (otherValue != null) {
173                                 if (otherValue.getClass().isArray()) {
174                                         if (!arrayEquals(otherValue, value)) {
175                                                 return false;
176                                         }
177                                 } else {
178                                         if (!otherValue.equals(value)) {
179                                                 return false;
180                                         }
181                                 }
182                         } else if (value != null)
183                                 return false;
184
185                 }
186
187                 return true;
188
189         }
190
191         public void addJSONField(String key, Object content) {
192                 fields.put(key, content);
193         }
194
195         @SuppressWarnings("unchecked")
196         public <T> T getJSONField(String key) {
197                 return (T) fields.get(key);
198         }
199
200         @SuppressWarnings("unchecked")
201         public <T> T getJSONFieldDefault(String key, T defaultValue) {
202                 T value = (T) fields.get(key);
203                 if (value != null)
204                         return value;
205                 else
206                         return defaultValue;
207         }
208
209         @SuppressWarnings("unchecked")
210         public <T> T getBeanJSONFieldDefault(String key, Binding target,
211                         T defaultValue) {
212                 T value = (T) fields.get(key);
213                 try {
214                         if (value != null) {
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);
219 //                              }
220 //                              return value;
221                         }
222                 } catch (AdapterConstructionException e) {
223                 } catch (AdaptException e) {
224                 }
225                 return defaultValue;
226         }
227
228         public String getParent() {
229                 return (String) fields.get("parent");
230         }
231
232         public String getParentOrd() {
233                 return (String) fields.get("parentOrd");
234         }
235
236         public String getType() {
237                 return (String) fields.get("type");
238         }
239
240         public String toString() {
241                 StringBuilder b = new StringBuilder();
242                 b.append("{");
243                 boolean first = true;
244                 for (Map.Entry<String, Object> entry : fields.entrySet()) {
245                         if (first)
246                                 first = false;
247                         else
248                                 b.append(",");
249                         String key = entry.getKey();
250                         String value = fieldJSON(entry.getValue());
251                         if (value == null) {
252                                 first = true; // prevents ", ," when no key and value are given
253                                 continue;
254                         }
255                         b.append('"');
256                         b.append(key);
257                         b.append('"');
258                         b.append(':');
259                         b.append(value);
260                         b.append("\n");
261                 }
262                 b.append("}");
263                 return b.toString();
264         }
265
266         private void printValue(Object value, Binding binding_, StringBuilder sb)
267                         throws IOException {
268                 try {
269                         if (binding_ instanceof RecordBinding) {
270                                 RecordBinding binding = (RecordBinding) binding_;
271                                 sb.append("{");
272                                 RecordType type = binding.type();
273                                 for (int i = 0, j = 0; i < type.getComponentCount(); i++) {
274
275                                         Component c = type.getComponent(i);
276
277                                         Object field = binding.getComponent(value, i);
278
279                                         Binding b = binding.getComponentBinding(i);
280                                         if (b instanceof OptionalBinding) {
281                                                 OptionalBinding ob = (OptionalBinding) b;
282                                                 if (!ob.hasValueUnchecked(field))
283                                                         continue;
284                                                 b = ob.getComponentBinding();
285                                         }
286
287                                         if (j > 0)
288                                                 sb.append(",");
289                                         sb.append("\n");
290                                         j++;
291
292                                         sb.append("\"");
293                                         sb.append(c.name);
294                                         sb.append("\" : ");
295                                         printValue(field, b, sb);
296                                 }
297                                 sb.append("}");
298                         } else if (binding_ instanceof ArrayBinding) {
299                                 ArrayBinding binding = (ArrayBinding) binding_;
300                                 Binding b = binding.getComponentBinding();
301                                 sb.append("[");
302                                 for (int i = 0; i < binding.size(value); i++) {
303                                         if (i > 0)
304                                                 sb.append(",");
305                                         printValue(binding.get(value, i), b, sb);
306                                 }
307                                 sb.append("]");
308                         } else if (binding_ instanceof MapBinding) {
309                                 sb.append("{");
310                                 MapBinding binding = (MapBinding) binding_;
311                                 int j = 0;
312                                 for (Object key : binding.getKeys(value)) {
313                                         Object val = binding.get(value, key);
314                                         if (key instanceof String && val instanceof String) {
315
316                                                 if (j > 0)
317                                                         sb.append(",");
318                                                 sb.append("\n");
319                                                 j++;
320
321                                                 sb.append("\"");
322                                                 sb.append((String) key);
323                                                 sb.append("\" : \"");
324                                                 sb.append((String) val);
325                                                 sb.append("\"");
326
327                                         }
328                                 }
329                                 sb.append("}");
330                         } else {
331                                 DataValueRepository rep = new DataValueRepository();
332                                 binding_.printValue(value, sb, rep, false);
333                         }
334                 } catch (BindingException e) {
335                         e.printStackTrace();
336                 }
337         }
338
339         private String printList(List<?> list) {
340                 StringBuilder b = new StringBuilder();
341                 b.append("[");
342                 boolean first = true;
343                 for (Object o : list) {
344                         if (first) {
345                                 first = false;
346                         } else {
347                                 b.append(",");
348                         }
349                         b.append(fieldJSON(o));
350                 }
351                 b.append("]");
352                 return b.toString();
353         }
354
355         private String fieldJSON(Object field) {
356
357                 if (field == null)
358                         return null;
359
360                 String valueString = null;
361                 if (field instanceof Bean) {
362                         // Try bean to JSON
363                         try {
364                                 Bean bean = (Bean) field;
365                                 StringBuilder sb = new StringBuilder();
366                                 printValue(bean, bean.getBinding(), sb);
367                                 valueString = sb.toString();
368                         } catch (IOException e) {
369                                 e.printStackTrace();
370                         }
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);
380                                 } else {
381                                         return "{" + fieldJSON(o1) + " , " + fieldJSON(o2) + "}";
382                                 }
383                         } else {
384                                 StringBuilder b = new StringBuilder();
385                                 b.append("{");
386                                 for (int i = 0; i < t.length(); i++) {
387                                         if (i > 0)
388                                                 b.append(",");
389                                         b.append(fieldJSON(t.get(i)));
390                                 }
391                                 b.append("}");
392                                 return b.toString();
393                         }
394                 } else {
395                         if (field.getClass().isArray()) {
396
397                                 Object[] array;
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];
402                                         }
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];
407                     }
408                                 } else
409                                         array = (Object[]) field;
410
411                                 // Build a string of the value array. Format is: [ value, value,
412                                 // value, ... ]
413                                 StringBuilder arrayBuilder = new StringBuilder();
414                                 arrayBuilder.append("[");
415                                 for (int i = 0; i < array.length; i++) {
416                                         Object o = array[i];
417                                         if (i != 0)
418                                                 arrayBuilder.append(",");
419
420                                         if (o instanceof String)
421                                                 arrayBuilder.append("\"");
422
423                                         arrayBuilder.append(o.toString());
424
425                                         if (o instanceof String)
426                                                 arrayBuilder.append("\"");
427                                 }
428                                 arrayBuilder.append("]");
429                                 valueString = arrayBuilder.toString();
430                         } else {
431                                 if (field instanceof String) {
432                                         // Use a string representation of the value
433                                         valueString = quote((String) field);
434                                 } else {
435                                         // Use a string representation of the value
436                                         valueString = "\"" + field.toString() + "\"";
437                                 }
438
439                         }
440                 }
441
442                 return valueString;
443
444         }
445         
446         /*
447          * Copied from org.json
448          * 
449             Copyright (c) 2002 JSON.org
450
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:
457
458             The above copyright notice and this permission notice shall be included in all
459             copies or substantial portions of the Software.
460
461             The Software shall be used for Good, not Evil.
462
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
469             SOFTWARE.
470          */
471         /**
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.
478      */
479     public static String quote(String string) {
480         if (string == null || string.length() == 0) {
481             return "\"\"";
482         }
483
484         char         b;
485         char         c = 0;
486         int          i;
487         int          len = string.length();
488         StringBuffer sb = new StringBuffer(len + 4);
489         String       t;
490
491         sb.append('"');
492         for (i = 0; i < len; i += 1) {
493             b = c;
494             c = string.charAt(i);
495             switch (c) {
496                 case '\\':
497                 case '"':
498                     sb.append('\\');
499                     sb.append(c);
500                     break;
501                 case '/':
502                     if (b == '<') {
503                         sb.append('\\');
504                     }
505                     sb.append(c);
506                     break;
507                 case '\b':
508                     sb.append("\\b");
509                     break;
510                 case '\t':
511                     sb.append("\\t");
512                     break;
513                 case '\n':
514                     sb.append("\\n");
515                     break;
516                 case '\f':
517                     sb.append("\\f");
518                     break;
519                 case '\r':
520                     sb.append("\\r");
521                     break;
522                 default:
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));
527                     } else {
528                         sb.append(c);
529                     }
530             }
531         }
532         sb.append('"');
533         return sb.toString();
534     }   
535         
536         public String getId() {
537                 return id;
538         }
539
540     @SuppressWarnings("unchecked")
541         @Override
542     public <T> T getValue(String key) {
543         return (T)fields.get(key);
544     }
545
546     @Override
547     public Iterator<String> keys() {
548         return fields.keySet().iterator();
549     }
550
551     @Override
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());
556         
557         for (Map.Entry<String, Object> e : newObjects.entrySet())
558             result.addJSONField(e.getKey(), e.getValue());
559         return result;
560     }
561 }