1 package org.onap.dmaap.datarouter.provisioning.utils;
3 /*******************************************************************************
4 * ============LICENSE_START==================================================
6 * * ===========================================================================
7 * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
8 * * ===========================================================================
9 * * Licensed under the Apache License, Version 2.0 (the "License");
10 * * you may not use this file except in compliance with the License.
11 * * You may obtain a copy of the License at
13 * * http://www.apache.org/licenses/LICENSE-2.0
15 * * Unless required by applicable law or agreed to in writing, software
16 * * distributed under the License is distributed on an "AS IS" BASIS,
17 * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * * See the License for the specific language governing permissions and
19 * * limitations under the License.
20 * * ============LICENSE_END====================================================
22 * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
24 ******************************************************************************/
26 import com.att.eelf.configuration.EELFLogger;
27 import com.att.eelf.configuration.EELFManager;
28 import java.io.IOException;
29 import java.io.StringWriter;
30 import java.io.Writer;
31 import java.lang.reflect.Field;
32 import java.lang.reflect.Method;
33 import java.lang.reflect.Modifier;
36 import org.json.JSONArray;
37 import org.json.JSONException;
38 import org.json.JSONString;
39 import org.json.JSONTokener;
42 * A JSONObject is an unordered collection of name/value pairs. Its external
43 * form is a string wrapped in curly braces with colons between the names and
44 * values, and commas between the values and names. The internal form is an
45 * object having <code>get</code> and <code>opt</code> methods for accessing the
46 * values by name, and <code>put</code> methods for adding or replacing values
47 * by name. The values can be any of these types: <code>Boolean</code>,
48 * <code>JSONArray</code>, <code>JSONObject</code>, <code>Number</code>,
49 * <code>String</code>, or the <code>JSONObject.NULL</code> object. A JSONObject
50 * constructor can be used to convert an external form JSON text into an
51 * internal form whose values can be retrieved with the <code>get</code> and
52 * <code>opt</code> methods, or to convert values into a JSON text using the
53 * <code>put</code> and <code>toString</code> methods. A <code>get</code> method
54 * returns a value if one can be found, and throws an exception if one cannot be
55 * found. An <code>opt</code> method returns a default value instead of throwing
56 * an exception, and so is useful for obtaining optional values.
58 * The generic <code>get()</code> and <code>opt()</code> methods return an
59 * object, which you can cast or query for type. There are also typed
60 * <code>get</code> and <code>opt</code> methods that do type checking and type
61 * coercion for you. The opt methods differ from the get methods in that they do
62 * not throw. Instead, they return a specified value, such as null.
64 * The <code>put</code> methods add or replace values in an object. For example,
67 * myString = new JSONObject().put("JSON", "Hello, World!").toString();
70 * produces the string <code>{"JSON": "Hello, World"}</code>.
72 * The texts produced by the <code>toString</code> methods strictly conform to
73 * the JSON syntax rules. The constructors are more forgiving in the texts they
76 * <li>An extra <code>,</code> <small>(comma)</small> may appear just
77 * before the closing brace.</li>
78 * <li>Strings may be quoted with <code>'</code> <small>(single
79 * quote)</small>.</li>
80 * <li>Strings do not need to be quoted at all if they do not begin with a quote
81 * or single quote, and if they do not contain leading or trailing spaces, and
82 * if they do not contain any of these characters:
83 * <code>{ } [ ] / \ : , = ; #</code> and if they do not look like numbers and
84 * if they are not the reserved words <code>true</code>, <code>false</code>, or
85 * <code>null</code>.</li>
86 * <li>Keys can be followed by <code>=</code> or <code>=></code> as well as by
87 * <code>:</code>.</li>
88 * <li>Values can be followed by <code>;</code> <small>(semicolon)</small> as
89 * well as by <code>,</code> <small>(comma)</small>.</li>
95 public class LOGJSONObject {
97 * The maximum number of keys in the key pool.
99 private static final int keyPoolSize = 100;
102 * Key pooling is like string interning, but without permanently tying up
103 * memory. To help conserve memory, storage of duplicated key strings in
104 * JSONObjects will be avoided by using a key pool to manage unique key
105 * string objects. This is used by JSONObject.put(string, object).
107 private static Map<String, Object> keyPool = new LinkedHashMap<String, Object>(keyPoolSize);
109 private static final EELFLogger intlogger = EELFManager.getInstance().getLogger("InternalLog");
112 * JSONObject.NULL is equivalent to the value that JavaScript calls null,
113 * whilst Java's null is equivalent to the value that JavaScript calls
116 private static final class Null {
119 * There is only intended to be a single instance of the NULL object,
120 * so the clone method returns itself.
124 protected final Object clone() {
129 * A Null object is equal to the null value and to itself.
131 * @param object An object to test for nullness.
132 * @return true if the object parameter is the JSONObject.NULL object
135 public boolean equals(Object object) {
136 return object == null || object == this;
140 * Returns a hash code value for the object. This method is
141 * supported for the benefit of hash tables such as those provided by
144 * The general contract of {@code hashCode} is:
146 * <li>Whenever it is invoked on the same object more than once during
147 * an execution of a Java application, the {@code hashCode} method
148 * must consistently return the same integer, provided no information
149 * used in {@code equals} comparisons on the object is modified.
150 * This integer need not remain consistent from one execution of an
151 * application to another execution of the same application.
152 * <li>If two objects are equal according to the {@code equals(Object)}
153 * method, then calling the {@code hashCode} method on each of
154 * the two objects must produce the same integer result.
155 * <li>It is <em>not</em> required that if two objects are unequal
156 * according to the {@link Object#equals(Object)}
157 * method, then calling the {@code hashCode} method on each of the
158 * two objects must produce distinct integer results. However, the
159 * programmer should be aware that producing distinct integer results
160 * for unequal objects may improve the performance of hash tables.
163 * As much as is reasonably practical, the hashCode method defined by
164 * class {@code Object} does return distinct integers for distinct
165 * objects. (This is typically implemented by converting the internal
166 * address of the object into an integer, but this implementation
167 * technique is not required by the
168 * Java™ programming language.)
170 * @return a hash code value for this object.
171 * @see Object#equals(Object)
172 * @see System#identityHashCode
175 public int hashCode() {
176 return super.hashCode();
180 * Get the "null" string value.
182 * @return The string "null".
184 public String toString() {
190 * The map where the JSONObject's properties are kept.
192 private final Map<String, Object> map;
195 * It is sometimes more convenient and less ambiguous to have a
196 * <code>NULL</code> object than to use Java's <code>null</code> value.
197 * <code>JSONObject.NULL.equals(null)</code> returns <code>true</code>.
198 * <code>JSONObject.NULL.toString()</code> returns <code>"null"</code>.
200 public static final Object NULL = new Null();
203 * Construct an empty JSONObject.
205 public LOGJSONObject() {
206 this.map = new LinkedHashMap<>();
210 * Construct a JSONObject from a subset of another JSONObject.
211 * An array of strings is used to identify the keys that should be copied.
212 * Missing keys are ignored.
214 * @param jo A JSONObject.
215 * @param names An array of strings.
217 public LOGJSONObject(LOGJSONObject jo, String[] names) {
219 for (int i = 0; i < names.length; i += 1) {
221 this.putOnce(names[i], jo.opt(names[i]));
222 } catch (Exception ignore) {
223 intlogger.error("PROV0001 LOGJSONObject: " + ignore.getMessage(), ignore);
229 * Construct a JSONObject from a JSONTokener.
231 * @param x A JSONTokener object containing the source string.
232 * @throws JSONException If there is a syntax error in the source string
233 * or a duplicated key.
235 public LOGJSONObject(JSONTokener x) {
240 if (x.nextClean() != '{') {
241 throw x.syntaxError("A JSONObject text must begin with '{'");
247 throw x.syntaxError("A JSONObject text must end with '}'");
252 key = x.nextValue().toString();
255 // The key is followed by ':'. We will also tolerate '=' or '=>'.
259 if (x.next() != '>') {
262 } else if (c != ':') {
263 throw x.syntaxError("Expected a ':' after a key");
265 this.putOnce(key, x.nextValue());
267 // Pairs are separated by ','. We will also tolerate ';'.
269 switch (x.nextClean()) {
272 if (x.nextClean() == '}') {
280 throw x.syntaxError("Expected a ',' or '}'");
286 * Construct a JSONObject from a Map.
288 * @param map A map object that can be used to initialize the contents of
290 * @throws JSONException
292 public LOGJSONObject(Map<String, Object> map) {
293 this.map = new LinkedHashMap<String, Object>();
295 Iterator<Map.Entry<String, Object>> i = map.entrySet().iterator();
296 while (i.hasNext()) {
297 Map.Entry<String, Object> e = i.next();
298 Object value = e.getValue();
300 this.map.put(e.getKey(), wrap(value));
307 * Construct a JSONObject from an Object using bean getters.
308 * It reflects on all of the public methods of the object.
309 * For each of the methods with no parameters and a name starting
310 * with <code>"get"</code> or <code>"is"</code> followed by an uppercase letter,
311 * the method is invoked, and a key and the value returned from the getter method
312 * are put into the new JSONObject.
314 * The key is formed by removing the <code>"get"</code> or <code>"is"</code> prefix.
315 * If the second remaining character is not upper case, then the first
316 * character is converted to lower case.
318 * For example, if an object has a method named <code>"getName"</code>, and
319 * if the result of calling <code>object.getName()</code> is <code>"Larry Fine"</code>,
320 * then the JSONObject will contain <code>"name": "Larry Fine"</code>.
322 * @param bean An object that has getter methods that should be used
323 * to make a JSONObject.
325 public LOGJSONObject(Object bean) {
327 this.populateMap(bean);
331 * Construct a JSONObject from an Object, using reflection to find the
332 * public members. The resulting JSONObject's keys will be the strings
333 * from the names array, and the values will be the field values associated
334 * with those keys in the object. If a key is not found or not visible,
335 * then it will not be copied into the new JSONObject.
337 * @param object An object that has fields that should be used to make a
339 * @param names An array of strings, the names of the fields to be obtained
342 public LOGJSONObject(Object object, String names[]) {
344 Class<? extends Object> c = object.getClass();
345 for (int i = 0; i < names.length; i += 1) {
346 String name = names[i];
348 this.putOpt(name, c.getField(name).get(object));
349 } catch (Exception ignore) {
355 * Construct a JSONObject from a source JSON text string.
356 * This is the most commonly used JSONObject constructor.
358 * @param source A string beginning
359 * with <code>{</code> <small>(left brace)</small> and ending
360 * with <code>}</code> <small>(right brace)</small>.
361 * @throws JSONException If there is a syntax error in the source
362 * string or a duplicated key.
364 public LOGJSONObject(String source) throws JSONException {
365 this(new JSONTokener(source));
369 * Construct a JSONObject from a ResourceBundle.
371 * @param baseName The ResourceBundle base name.
372 * @param locale The Locale to load the ResourceBundle for.
373 * @throws JSONException If any JSONExceptions are detected.
375 public LOGJSONObject(String baseName, Locale locale) throws JSONException {
377 ResourceBundle bundle = ResourceBundle.getBundle(baseName, locale,
378 Thread.currentThread().getContextClassLoader());
380 // Iterate through the keys in the bundle.
382 Enumeration<?> keys = bundle.getKeys();
383 while (keys.hasMoreElements()) {
384 Object key = keys.nextElement();
385 if (key instanceof String) {
387 // Go through the path, ensuring that there is a nested JSONObject for each
388 // segment except the last. Add the value using the last segment's name into
389 // the deepest nested JSONObject.
391 String[] path = ((String) key).split("\\.");
392 int last = path.length - 1;
393 LOGJSONObject target = this;
394 for (int i = 0; i < last; i += 1) {
395 String segment = path[i];
396 LOGJSONObject nextTarget = target.optJSONObject(segment);
397 if (nextTarget == null) {
398 nextTarget = new LOGJSONObject();
399 target.put(segment, nextTarget);
403 target.put(path[last], bundle.getString((String) key));
409 * Accumulate values under a key. It is similar to the put method except
410 * that if there is already an object stored under the key then a
411 * JSONArray is stored under the key to hold all of the accumulated values.
412 * If there is already a JSONArray, then the new value is appended to it.
413 * In contrast, the put method replaces the previous value.
415 * If only one value is accumulated that is not a JSONArray, then the
416 * result will be the same as using put. But if multiple values are
417 * accumulated, then the result will be like append.
419 * @param key A key string.
420 * @param value An object to be accumulated under the key.
422 * @throws JSONException If the value is an invalid number
423 * or if the key is null.
425 public LOGJSONObject accumulate(
428 ) throws JSONException {
430 Object object = this.opt(key);
431 if (object == null) {
432 this.put(key, value instanceof JSONArray
433 ? new JSONArray().put(value)
435 } else if (object instanceof JSONArray) {
436 ((JSONArray) object).put(value);
438 this.put(key, new JSONArray().put(object).put(value));
444 * Append values to the array under a key. If the key does not exist in the
445 * JSONObject, then the key is put in the JSONObject with its value being a
446 * JSONArray containing the value parameter. If the key was already
447 * associated with a JSONArray, then the value parameter is appended to it.
449 * @param key A key string.
450 * @param value An object to be accumulated under the key.
452 * @throws JSONException If the key is null or if the current value
453 * associated with the key is not a JSONArray.
455 public LOGJSONObject append(String key, Object value) throws JSONException {
457 Object object = this.opt(key);
458 if (object == null) {
459 this.put(key, new JSONArray().put(value));
460 } else if (object instanceof JSONArray) {
461 this.put(key, ((JSONArray) object).put(value));
463 throw new JSONException("JSONObject[" + key +
464 "] is not a JSONArray.");
470 * Produce a string from a double. The string "null" will be returned if
471 * the number is not finite.
476 public static String doubleToString(double d) {
477 if (Double.isInfinite(d) || Double.isNaN(d)) {
481 // Shave off trailing zeros and decimal point, if possible.
483 String string = Double.toString(d);
484 if (string.indexOf('.') > 0 && string.indexOf('e') < 0 &&
485 string.indexOf('E') < 0) {
486 while (string.endsWith("0")) {
487 string = string.substring(0, string.length() - 1);
489 if (string.endsWith(".")) {
490 string = string.substring(0, string.length() - 1);
497 * Get the value object associated with a key.
499 * @param key A key string.
500 * @return The object associated with the key.
501 * @throws JSONException if the key is not found.
503 public Object get(String key) throws JSONException {
505 throw new JSONException("Null key.");
507 Object object = this.opt(key);
508 if (object == null) {
509 throw new JSONException("JSONObject[" + quote(key) +
516 * Get the boolean value associated with a key.
518 * @param key A key string.
520 * @throws JSONException if the value is not a Boolean or the String "true" or "false".
522 public boolean getBoolean(String key) throws JSONException {
523 Object object = this.get(key);
524 if (object.equals(Boolean.FALSE) ||
525 (object instanceof String &&
526 ((String) object).equalsIgnoreCase("false"))) {
528 } else if (object.equals(Boolean.TRUE) ||
529 (object instanceof String &&
530 ((String) object).equalsIgnoreCase("true"))) {
533 throw new JSONException("JSONObject[" + quote(key) +
534 "] is not a Boolean.");
538 * Get the double value associated with a key.
540 * @param key A key string.
541 * @return The numeric value.
542 * @throws JSONException if the key is not found or
543 * if the value is not a Number object and cannot be converted to a number.
545 public double getDouble(String key) {
546 Object object = this.get(key);
548 return object instanceof Number
549 ? ((Number) object).doubleValue()
550 : Double.parseDouble((String) object);
551 } catch (Exception e) {
552 intlogger.error("JSONObject[" + quote(key) + "] is not a number.", e);
553 throw new JSONException("JSONObject[" + quote(key) + "] is not a number.");
558 * Get the int value associated with a key.
560 * @param key A key string.
561 * @return The integer value.
562 * @throws JSONException if the key is not found or if the value cannot
563 * be converted to an integer.
565 public int getInt(String key) {
566 Object object = this.get(key);
568 return object instanceof Number
569 ? ((Number) object).intValue()
570 : Integer.parseInt((String) object);
571 } catch (Exception e) {
572 intlogger.error("JSONObject[" + quote(key) + "] is not an int.", e);
573 throw new JSONException("JSONObject[" + quote(key) + "] is not an int.");
578 * Get the JSONArray value associated with a key.
580 * @param key A key string.
581 * @return A JSONArray which is the value.
582 * @throws JSONException if the key is not found or
583 * if the value is not a JSONArray.
585 public JSONArray getJSONArray(String key) throws JSONException {
586 Object object = this.get(key);
587 if (object instanceof JSONArray) {
588 return (JSONArray) object;
590 throw new JSONException("JSONObject[" + quote(key) +
591 "] is not a JSONArray.");
595 * Get the JSONObject value associated with a key.
597 * @param key A key string.
598 * @return A JSONObject which is the value.
599 * @throws JSONException if the key is not found or
600 * if the value is not a JSONObject.
602 public LOGJSONObject getJSONObject(String key) throws JSONException {
603 Object object = this.get(key);
604 if (object instanceof LOGJSONObject) {
605 return (LOGJSONObject) object;
607 throw new JSONException("JSONObject[" + quote(key) +
608 "] is not a JSONObject.");
612 * Get the long value associated with a key.
614 * @param key A key string.
615 * @return The long value.
616 * @throws JSONException if the key is not found or if the value cannot
617 * be converted to a long.
619 public long getLong(String key) throws JSONException {
620 Object object = this.get(key);
622 return object instanceof Number
623 ? ((Number) object).longValue()
624 : Long.parseLong((String) object);
625 } catch (Exception e) {
626 intlogger.error("JSONObject[" + quote(key) + "] is not a long.", e);
627 throw new JSONException("JSONObject[" + quote(key) + "] is not a long.");
632 * Get an array of field names from a JSONObject.
634 * @return An array of field names, or null if there are no names.
636 public static String[] getNames(LOGJSONObject jo) {
637 int length = jo.length();
641 Iterator<String> iterator = jo.keys();
642 String[] names = new String[length];
644 while (iterator.hasNext()) {
645 names[i] = iterator.next();
652 * Get the string associated with a key.
654 * @param key A key string.
655 * @return A string which is the value.
656 * @throws JSONException if there is no string value for the key.
658 public String getString(String key) {
659 Object object = this.get(key);
660 if (object instanceof String) {
661 return (String) object;
663 throw new JSONException("JSONObject[" + quote(key) +
668 * Determine if the JSONObject contains a specific key.
670 * @param key A key string.
671 * @return true if the key exists in the JSONObject.
673 public boolean has(String key) {
674 return this.map.containsKey(key);
678 * Increment a property of a JSONObject. If there is no such property,
679 * create one with a value of 1. If there is such a property, and if
680 * it is an Integer, Long, Double, or Float, then add one to it.
682 * @param key A key string.
684 * @throws JSONException If there is already a property with this name
685 * that is not an Integer, Long, Double, or Float.
687 public LOGJSONObject increment(String key) {
688 Object value = this.opt(key);
691 } else if (value instanceof Integer) {
692 this.put(key, ((Integer) value).intValue() + 1);
693 } else if (value instanceof Long) {
694 this.put(key, ((Long) value).longValue() + 1);
695 } else if (value instanceof Double) {
696 this.put(key, ((Double) value).doubleValue() + 1);
697 } else if (value instanceof Float) {
698 this.put(key, ((Float) value).floatValue() + 1);
700 throw new JSONException("Unable to increment [" + quote(key) + "].");
706 * Get an enumeration of the keys of the JSONObject.
708 * @return An iterator of the keys.
710 public Iterator<String> keys() {
711 return this.keySet().iterator();
715 * Get a set of keys of the JSONObject.
719 public Set<String> keySet() {
720 return this.map.keySet();
724 * Get the number of keys stored in the JSONObject.
726 * @return The number of keys in the JSONObject.
728 public int length() {
729 return this.map.size();
733 * Produce a JSONArray containing the names of the elements of this
736 * @return A JSONArray containing the key strings, or null if the JSONObject
739 public JSONArray names() {
740 JSONArray ja = new JSONArray();
741 Iterator<String> keys = this.keys();
742 while (keys.hasNext()) {
745 return ja.length() == 0 ? null : ja;
749 * Produce a string from a Number.
751 * @param number A Number
753 * @throws JSONException If n is a non-finite number.
755 public static String numberToString(Number number)
756 throws JSONException {
757 if (number == null) {
758 throw new JSONException("Null pointer");
760 testValidity(number);
762 // Shave off trailing zeros and decimal point, if possible.
764 String string = number.toString();
765 if (string.indexOf('.') > 0 && string.indexOf('e') < 0 &&
766 string.indexOf('E') < 0) {
767 while (string.endsWith("0")) {
768 string = string.substring(0, string.length() - 1);
770 if (string.endsWith(".")) {
771 string = string.substring(0, string.length() - 1);
778 * Get an optional value associated with a key.
780 * @param key A key string.
781 * @return An object which is the value, or null if there is no value.
783 public Object opt(String key) {
784 return key == null ? null : this.map.get(key);
788 * Get an optional boolean associated with a key.
789 * It returns the defaultValue if there is no such key, or if it is not
790 * a Boolean or the String "true" or "false" (case insensitive).
792 * @param key A key string.
793 * @param defaultValue The default.
796 public boolean optBoolean(String key, boolean defaultValue) {
798 return this.getBoolean(key);
799 } catch (Exception e) {
800 intlogger.trace("Using defaultValue: " + defaultValue, e);
806 * Get an optional double associated with a key, or the
807 * defaultValue if there is no such key or if its value is not a number.
808 * If the value is a string, an attempt will be made to evaluate it as
811 * @param key A key string.
812 * @param defaultValue The default.
813 * @return An object which is the value.
815 public double optDouble(String key, double defaultValue) {
817 return this.getDouble(key);
818 } catch (Exception e) {
819 intlogger.trace("Using defaultValue: " + defaultValue, e);
825 * Get an optional int value associated with a key,
826 * or the default if there is no such key or if the value is not a number.
827 * If the value is a string, an attempt will be made to evaluate it as
830 * @param key A key string.
831 * @param defaultValue The default.
832 * @return An object which is the value.
834 public int optInt(String key, int defaultValue) {
836 return this.getInt(key);
837 } catch (Exception e) {
838 intlogger.trace("Using defaultValue: " + defaultValue, e);
844 * Get an optional JSONObject associated with a key.
845 * It returns null if there is no such key, or if its value is not a
848 * @param key A key string.
849 * @return A JSONObject which is the value.
851 public LOGJSONObject optJSONObject(String key) {
852 Object object = this.opt(key);
853 return object instanceof LOGJSONObject ? (LOGJSONObject) object : null;
857 * Get an optional long value associated with a key,
858 * or the default if there is no such key or if the value is not a number.
859 * If the value is a string, an attempt will be made to evaluate it as
862 * @param key A key string.
863 * @param defaultValue The default.
864 * @return An object which is the value.
866 public long optLong(String key, long defaultValue) {
868 return this.getLong(key);
869 } catch (Exception e) {
875 * Get an optional string associated with a key.
876 * It returns the defaultValue if there is no such key.
878 * @param key A key string.
879 * @param defaultValue The default.
880 * @return A string which is the value.
882 public String optString(String key, String defaultValue) {
883 Object object = this.opt(key);
884 return NULL.equals(object) ? defaultValue : object.toString();
887 private void populateMap(Object bean) {
888 Class<? extends Object> klass = bean.getClass();
890 // If klass is a System class then set includeSuperClass to false.
892 boolean includeSuperClass = klass.getClassLoader() != null;
894 Method[] methods = includeSuperClass
896 : klass.getDeclaredMethods();
897 for (int i = 0; i < methods.length; i += 1) {
899 Method method = methods[i];
900 if (Modifier.isPublic(method.getModifiers())) {
901 String name = method.getName();
903 if (name.startsWith("get")) {
904 if ("getClass".equals(name) ||
905 "getDeclaringClass".equals(name)) {
908 key = name.substring(3);
910 } else if (name.startsWith("is")) {
911 key = name.substring(2);
913 if (key.length() > 0 &&
914 Character.isUpperCase(key.charAt(0)) &&
915 method.getParameterTypes().length == 0) {
916 if (key.length() == 1) {
917 key = key.toLowerCase();
918 } else if (!Character.isUpperCase(key.charAt(1))) {
919 key = key.substring(0, 1).toLowerCase() +
923 Object result = method.invoke(bean, (Object[]) null);
924 if (result != null) {
925 this.map.put(key, wrap(result));
929 } catch (Exception ignore) {
930 intlogger.trace("populateMap: " + ignore.getMessage(), ignore);
936 * Put a key/boolean pair in the JSONObject.
938 * @param key A key string.
939 * @param value A boolean which is the value.
941 * @throws JSONException If the key is null.
943 public LOGJSONObject put(String key, boolean value) throws JSONException {
944 this.put(key, value ? Boolean.TRUE : Boolean.FALSE);
949 * Put a key/value pair in the JSONObject, where the value will be a
950 * JSONArray which is produced from a Collection.
952 * @param key A key string.
953 * @param value A Collection value.
955 * @throws JSONException
957 public LOGJSONObject put(String key, Collection<Object> value) throws JSONException {
958 this.put(key, new JSONArray(value));
963 * Put a key/double pair in the JSONObject.
965 * @param key A key string.
966 * @param value A double which is the value.
968 * @throws JSONException If the key is null or if the number is invalid.
970 public LOGJSONObject put(String key, double value) throws JSONException {
971 this.put(key, new Double(value));
976 * Put a key/int pair in the JSONObject.
978 * @param key A key string.
979 * @param value An int which is the value.
981 * @throws JSONException If the key is null.
983 public LOGJSONObject put(String key, int value) throws JSONException {
984 this.put(key, new Integer(value));
989 * Put a key/long pair in the JSONObject.
991 * @param key A key string.
992 * @param value A long which is the value.
994 * @throws JSONException If the key is null.
996 public LOGJSONObject put(String key, long value) throws JSONException {
997 this.put(key, new Long(value));
1002 * Put a key/value pair in the JSONObject, where the value will be a
1003 * JSONObject which is produced from a Map.
1005 * @param key A key string.
1006 * @param value A Map value.
1008 * @throws JSONException
1010 public LOGJSONObject put(String key, Map<String, Object> value) throws JSONException {
1011 this.put(key, new LOGJSONObject(value));
1016 * Put a key/value pair in the JSONObject. If the value is null,
1017 * then the key will be removed from the JSONObject if it is present.
1019 * @param key A key string.
1020 * @param value An object which is the value. It should be of one of these
1021 * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, String,
1022 * or the JSONObject.NULL object.
1024 * @throws JSONException If the value is non-finite number
1025 * or if the key is null.
1027 public LOGJSONObject put(String key, Object value) throws JSONException {
1030 throw new JSONException("Null key.");
1032 if (value != null) {
1033 testValidity(value);
1034 pooled = (String) keyPool.get(key);
1035 if (pooled == null) {
1036 if (keyPool.size() >= keyPoolSize) {
1037 keyPool = new LinkedHashMap<String, Object>(keyPoolSize);
1039 keyPool.put(key, key);
1043 this.map.put(key, value);
1051 * Put a key/value pair in the JSONObject, but only if the key and the
1052 * value are both non-null, and only if there is not already a member
1058 * @throws JSONException if the key is a duplicate
1060 public LOGJSONObject putOnce(String key, Object value) throws JSONException {
1061 if (key != null && value != null) {
1062 if (this.opt(key) != null) {
1063 throw new JSONException("Duplicate key \"" + key + "\"");
1065 this.put(key, value);
1071 * Put a key/value pair in the JSONObject, but only if the
1072 * key and the value are both non-null.
1074 * @param key A key string.
1075 * @param value An object which is the value. It should be of one of these
1076 * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, String,
1077 * or the JSONObject.NULL object.
1079 * @throws JSONException If the value is a non-finite number.
1081 public LOGJSONObject putOpt(String key, Object value) throws JSONException {
1082 if (key != null && value != null) {
1083 this.put(key, value);
1089 * Produce a string in double quotes with backslash sequences in all the
1090 * right places. A backslash will be inserted within </, producing <\/,
1091 * allowing JSON text to be delivered in HTML. In JSON text, a string
1092 * cannot contain a control character or an unescaped quote or backslash.
1094 * @param string A String
1095 * @return A String correctly formatted for insertion in a JSON text.
1097 public static String quote(String string) {
1098 StringWriter sw = new StringWriter();
1099 synchronized (sw.getBuffer()) {
1101 return quote(string, sw).toString();
1102 } catch (IOException e) {
1103 intlogger.trace("Ignore Exception message: ", e);
1109 public static Writer quote(String string, Writer w) throws IOException {
1110 if (string == null || string.length() == 0) {
1119 int len = string.length();
1122 for (i = 0; i < len; i += 1) {
1124 c = string.charAt(i);
1153 if (c < ' ' || (c >= '\u0080' && c < '\u00a0')
1154 || (c >= '\u2000' && c < '\u2100')) {
1156 hhhh = Integer.toHexString(c);
1157 w.write("0000", 0, 4 - hhhh.length());
1169 * Remove a name and its value, if present.
1171 * @param key The name to be removed.
1172 * @return The value that was associated with the name,
1173 * or null if there was no value.
1175 public Object remove(String key) {
1176 return this.map.remove(key);
1180 * Try to convert a string into a number, boolean, or null. If the string
1181 * can't be converted, return the string.
1183 * @param string A String.
1184 * @return A simple JSON value.
1186 public static Object stringToValue(String string) {
1188 if (string.equals("")) {
1191 if (string.equalsIgnoreCase("true")) {
1192 return Boolean.TRUE;
1194 if (string.equalsIgnoreCase("false")) {
1195 return Boolean.FALSE;
1197 if (string.equalsIgnoreCase("null")) {
1198 return LOGJSONObject.NULL;
1202 * If it might be a number, try converting it.
1203 * If a number cannot be produced, then the value will just
1204 * be a string. Note that the plus and implied string
1205 * conventions are non-standard. A JSON parser may accept
1206 * non-JSON forms as long as it accepts all correct JSON forms.
1209 char b = string.charAt(0);
1210 if ((b >= '0' && b <= '9') || b == '.' || b == '-' || b == '+') {
1212 if (string.indexOf('.') > -1 ||
1213 string.indexOf('e') > -1 || string.indexOf('E') > -1) {
1214 d = Double.valueOf(string);
1215 if (!d.isInfinite() && !d.isNaN()) {
1219 Long myLong = new Long(string);
1220 if (myLong.longValue() == myLong.intValue()) {
1221 return new Integer(myLong.intValue());
1226 } catch (Exception e) {
1227 intlogger.trace("Ignore Exception message: ", e);
1234 * Throw an exception if the object is a NaN or infinite number.
1236 * @param o The object to test.
1237 * @throws JSONException If o is a non-finite number.
1239 public static void testValidity(Object o) {
1241 if (o instanceof Double) {
1242 if (((Double) o).isInfinite() || ((Double) o).isNaN()) {
1243 throw new JSONException(
1244 "JSON does not allow non-finite numbers.");
1246 } else if (o instanceof Float) {
1247 if (((Float) o).isInfinite() || ((Float) o).isNaN()) {
1248 throw new JSONException(
1249 "JSON does not allow non-finite numbers.");
1256 * Produce a JSONArray containing the values of the members of this
1259 * @param names A JSONArray containing a list of key strings. This
1260 * determines the sequence of the values in the result.
1261 * @return A JSONArray of values.
1262 * @throws JSONException If any of the values are non-finite numbers.
1264 public JSONArray toJSONArray(JSONArray names) throws JSONException {
1265 if (names == null || names.length() == 0) {
1268 JSONArray ja = new JSONArray();
1269 for (int i = 0; i < names.length(); i += 1) {
1270 ja.put(this.opt(names.getString(i)));
1276 * Make a JSON text of this JSONObject. For compactness, no whitespace
1277 * is added. If this would not result in a syntactically correct JSON text,
1278 * then null will be returned instead.
1280 * Warning: This method assumes that the data structure is acyclical.
1282 * @return a printable, displayable, portable, transmittable
1283 * representation of the object, beginning
1284 * with <code>{</code> <small>(left brace)</small> and ending
1285 * with <code>}</code> <small>(right brace)</small>.
1287 public String toString() {
1289 return this.toString(0);
1290 } catch (Exception e) {
1291 intlogger.trace("Exception: ", e);
1297 * Make a prettyprinted JSON text of this JSONObject.
1299 * Warning: This method assumes that the data structure is acyclical.
1301 * @param indentFactor The number of spaces to add to each level of
1303 * @return a printable, displayable, portable, transmittable
1304 * representation of the object, beginning
1305 * with <code>{</code> <small>(left brace)</small> and ending
1306 * with <code>}</code> <small>(right brace)</small>.
1307 * @throws JSONException If the object contains an invalid number.
1309 public String toString(int indentFactor) throws JSONException {
1310 StringWriter w = new StringWriter();
1311 synchronized (w.getBuffer()) {
1312 return this.write(w, indentFactor, 0).toString();
1317 * Make a JSON text of an Object value. If the object has an
1318 * value.toJSONString() method, then that method will be used to produce
1319 * the JSON text. The method is required to produce a strictly
1320 * conforming text. If the object does not contain a toJSONString
1321 * method (which is the most common case), then a text will be
1322 * produced by other means. If the value is an array or Collection,
1323 * then a JSONArray will be made from it and its toJSONString method
1324 * will be called. If the value is a MAP, then a JSONObject will be made
1325 * from it and its toJSONString method will be called. Otherwise, the
1326 * value's toString method will be called, and the result will be quoted.
1329 * Warning: This method assumes that the data structure is acyclical.
1331 * @param value The value to be serialized.
1332 * @return a printable, displayable, transmittable
1333 * representation of the object, beginning
1334 * with <code>{</code> <small>(left brace)</small> and ending
1335 * with <code>}</code> <small>(right brace)</small>.
1336 * @throws JSONException If the value is or contains an invalid number.
1338 @SuppressWarnings("unchecked")
1339 public static String valueToString(Object value) throws JSONException {
1340 if (value == null) {
1343 if (value instanceof JSONString) {
1346 object = ((JSONString) value).toJSONString();
1347 } catch (Exception e) {
1348 throw new JSONException(e);
1350 if (object instanceof String) {
1351 return (String) object;
1353 throw new JSONException("Bad value from toJSONString: " + object);
1355 if (value instanceof Number) {
1356 return numberToString((Number) value);
1358 if (value instanceof Boolean || value instanceof LOGJSONObject ||
1359 value instanceof JSONArray) {
1360 return value.toString();
1362 if (value instanceof Map) {
1363 return new LOGJSONObject((Map<String, Object>) value).toString();
1365 if (value instanceof Collection) {
1366 return new JSONArray((Collection<Object>) value).toString();
1368 if (value.getClass().isArray()) {
1369 return new JSONArray(value).toString();
1371 return quote(value.toString());
1375 * Wrap an object, if necessary. If the object is null, return the NULL
1376 * object. If it is an array or collection, wrap it in a JSONArray. If
1377 * it is a map, wrap it in a JSONObject. If it is a standard property
1378 * (Double, String, et al) then it is already wrapped. Otherwise, if it
1379 * comes from one of the java packages, turn it into a string. And if
1380 * it doesn't, try to wrap it in a JSONObject. If the wrapping fails,
1381 * then null is returned.
1383 * @param object The object to wrap
1384 * @return The wrapped value
1386 @SuppressWarnings("unchecked")
1387 public static Object wrap(Object object) {
1389 if (object == null) {
1392 if (object instanceof LOGJSONObject || object instanceof JSONArray ||
1393 NULL.equals(object) || object instanceof JSONString ||
1394 object instanceof Byte || object instanceof Character ||
1395 object instanceof Short || object instanceof Integer ||
1396 object instanceof Long || object instanceof Boolean ||
1397 object instanceof Float || object instanceof Double ||
1398 object instanceof String) {
1402 if (object instanceof Collection) {
1403 return new JSONArray((Collection<Object>) object);
1405 if (object.getClass().isArray()) {
1406 return new JSONArray(object);
1408 if (object instanceof Map) {
1409 return new LOGJSONObject((Map<String, Object>) object);
1411 Package objectPackage = object.getClass().getPackage();
1412 String objectPackageName = objectPackage != null
1413 ? objectPackage.getName()
1416 objectPackageName.startsWith("java.") ||
1417 objectPackageName.startsWith("javax.") ||
1418 object.getClass().getClassLoader() == null
1420 return object.toString();
1422 return new LOGJSONObject(object);
1423 } catch (Exception exception) {
1424 intlogger.trace("Exception: ", exception);
1429 @SuppressWarnings("unchecked")
1430 static final Writer writeValue(Writer writer, Object value,
1431 int indentFactor, int indent) throws JSONException, IOException {
1432 if (value == null) {
1433 writer.write("null");
1434 } else if (value instanceof LOGJSONObject) {
1435 ((LOGJSONObject) value).write(writer, indentFactor, indent);
1436 } else if (value instanceof JSONArray) {
1437 ((JSONArray) value).write(writer, indentFactor, indent);
1438 } else if (value instanceof Map) {
1439 new LOGJSONObject((Map<String, Object>) value).write(writer, indentFactor, indent);
1440 } else if (value instanceof Collection) {
1441 new JSONArray((Collection<Object>) value).write(writer, indentFactor,
1443 } else if (value.getClass().isArray()) {
1444 new JSONArray(value).write(writer, indentFactor, indent);
1445 } else if (value instanceof Number) {
1446 writer.write(numberToString((Number) value));
1447 } else if (value instanceof Boolean) {
1448 writer.write(value.toString());
1449 } else if (value instanceof JSONString) {
1452 o = ((JSONString) value).toJSONString();
1453 } catch (Exception e) {
1454 throw new JSONException(e);
1456 writer.write(o != null ? o.toString() : quote(value.toString()));
1458 quote(value.toString(), writer);
1463 static final void indent(Writer writer, int indent) throws IOException {
1464 for (int i = 0; i < indent; i += 1) {
1470 * Write the contents of the JSONObject as JSON text to a writer. For
1471 * compactness, no whitespace is added.
1473 * Warning: This method assumes that the data structure is acyclical.
1475 * @return The writer.
1476 * @throws JSONException
1478 Writer write(Writer writer, int indentFactor, int indent)
1479 throws JSONException {
1481 boolean commanate = false;
1482 final int length = this.length();
1483 Iterator<String> keys = this.keys();
1487 Object key = keys.next();
1488 writer.write(quote(key.toString()));
1490 if (indentFactor > 0) {
1493 writeValue(writer, this.map.get(key), indentFactor, indent);
1494 } else if (length != 0) {
1495 final int newindent = indent + indentFactor;
1496 while (keys.hasNext()) {
1497 Object key = keys.next();
1501 if (indentFactor > 0) {
1504 indent(writer, newindent);
1505 writer.write(quote(key.toString()));
1507 if (indentFactor > 0) {
1510 writeValue(writer, this.map.get(key), indentFactor,
1514 if (indentFactor > 0) {
1517 indent(writer, indent);
1521 } catch (IOException exception) {
1522 throw new JSONException(exception);