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 java.io.IOException;
27 import java.io.StringWriter;
28 import java.io.Writer;
29 import java.lang.reflect.Field;
30 import java.lang.reflect.Method;
31 import java.lang.reflect.Modifier;
34 import org.json.JSONArray;
35 import org.json.JSONException;
36 import org.json.JSONString;
37 import org.json.JSONTokener;
40 * A JSONObject is an unordered collection of name/value pairs. Its external
41 * form is a string wrapped in curly braces with colons between the names and
42 * values, and commas between the values and names. The internal form is an
43 * object having <code>get</code> and <code>opt</code> methods for accessing the
44 * values by name, and <code>put</code> methods for adding or replacing values
45 * by name. The values can be any of these types: <code>Boolean</code>,
46 * <code>JSONArray</code>, <code>JSONObject</code>, <code>Number</code>,
47 * <code>String</code>, or the <code>JSONObject.NULL</code> object. A JSONObject
48 * constructor can be used to convert an external form JSON text into an
49 * internal form whose values can be retrieved with the <code>get</code> and
50 * <code>opt</code> methods, or to convert values into a JSON text using the
51 * <code>put</code> and <code>toString</code> methods. A <code>get</code> method
52 * returns a value if one can be found, and throws an exception if one cannot be
53 * found. An <code>opt</code> method returns a default value instead of throwing
54 * an exception, and so is useful for obtaining optional values.
56 * The generic <code>get()</code> and <code>opt()</code> methods return an
57 * object, which you can cast or query for type. There are also typed
58 * <code>get</code> and <code>opt</code> methods that do type checking and type
59 * coercion for you. The opt methods differ from the get methods in that they do
60 * not throw. Instead, they return a specified value, such as null.
62 * The <code>put</code> methods add or replace values in an object. For example,
65 * myString = new JSONObject().put("JSON", "Hello, World!").toString();
68 * produces the string <code>{"JSON": "Hello, World"}</code>.
70 * The texts produced by the <code>toString</code> methods strictly conform to
71 * the JSON syntax rules. The constructors are more forgiving in the texts they
74 * <li>An extra <code>,</code> <small>(comma)</small> may appear just
75 * before the closing brace.</li>
76 * <li>Strings may be quoted with <code>'</code> <small>(single
77 * quote)</small>.</li>
78 * <li>Strings do not need to be quoted at all if they do not begin with a quote
79 * or single quote, and if they do not contain leading or trailing spaces, and
80 * if they do not contain any of these characters:
81 * <code>{ } [ ] / \ : , = ; #</code> and if they do not look like numbers and
82 * if they are not the reserved words <code>true</code>, <code>false</code>, or
83 * <code>null</code>.</li>
84 * <li>Keys can be followed by <code>=</code> or <code>=></code> as well as by
85 * <code>:</code>.</li>
86 * <li>Values can be followed by <code>;</code> <small>(semicolon)</small> as
87 * well as by <code>,</code> <small>(comma)</small>.</li>
93 public class LOGJSONObject {
95 * The maximum number of keys in the key pool.
97 private static final int keyPoolSize = 100;
100 * Key pooling is like string interning, but without permanently tying up
101 * memory. To help conserve memory, storage of duplicated key strings in
102 * JSONObjects will be avoided by using a key pool to manage unique key
103 * string objects. This is used by JSONObject.put(string, object).
105 private static Map<String, Object> keyPool = new LinkedHashMap<String, Object>(keyPoolSize);
108 * JSONObject.NULL is equivalent to the value that JavaScript calls null,
109 * whilst Java's null is equivalent to the value that JavaScript calls
112 private static final class Null {
115 * There is only intended to be a single instance of the NULL object,
116 * so the clone method returns itself.
120 protected final Object clone() {
125 * A Null object is equal to the null value and to itself.
127 * @param object An object to test for nullness.
128 * @return true if the object parameter is the JSONObject.NULL object
131 public boolean equals(Object object) {
132 return object == null || object == this;
136 * Returns a hash code value for the object. This method is
137 * supported for the benefit of hash tables such as those provided by
140 * The general contract of {@code hashCode} is:
142 * <li>Whenever it is invoked on the same object more than once during
143 * an execution of a Java application, the {@code hashCode} method
144 * must consistently return the same integer, provided no information
145 * used in {@code equals} comparisons on the object is modified.
146 * This integer need not remain consistent from one execution of an
147 * application to another execution of the same application.
148 * <li>If two objects are equal according to the {@code equals(Object)}
149 * method, then calling the {@code hashCode} method on each of
150 * the two objects must produce the same integer result.
151 * <li>It is <em>not</em> required that if two objects are unequal
152 * according to the {@link Object#equals(Object)}
153 * method, then calling the {@code hashCode} method on each of the
154 * two objects must produce distinct integer results. However, the
155 * programmer should be aware that producing distinct integer results
156 * for unequal objects may improve the performance of hash tables.
159 * As much as is reasonably practical, the hashCode method defined by
160 * class {@code Object} does return distinct integers for distinct
161 * objects. (This is typically implemented by converting the internal
162 * address of the object into an integer, but this implementation
163 * technique is not required by the
164 * Java™ programming language.)
166 * @return a hash code value for this object.
167 * @see Object#equals(Object)
168 * @see System#identityHashCode
171 public int hashCode() {
172 return super.hashCode();
176 * Get the "null" string value.
178 * @return The string "null".
180 public String toString() {
187 * The map where the JSONObject's properties are kept.
189 private final Map<String, Object> map;
193 * It is sometimes more convenient and less ambiguous to have a
194 * <code>NULL</code> object than to use Java's <code>null</code> value.
195 * <code>JSONObject.NULL.equals(null)</code> returns <code>true</code>.
196 * <code>JSONObject.NULL.toString()</code> returns <code>"null"</code>.
198 public static final Object NULL = new Null();
202 * Construct an empty JSONObject.
204 public LOGJSONObject() {
205 this.map = new LinkedHashMap<String, Object>();
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.
216 * @throws JSONException
217 * @throws JSONException If a value is a non-finite number or if a name is duplicated.
219 public LOGJSONObject(LOGJSONObject jo, String[] names) {
221 for (int i = 0; i < names.length; i += 1) {
223 this.putOnce(names[i], jo.opt(names[i]));
224 } catch (Exception ignore) {
231 * Construct a JSONObject from a JSONTokener.
233 * @param x A JSONTokener object containing the source string.
234 * @throws JSONException If there is a syntax error in the source string
235 * or a duplicated key.
237 public LOGJSONObject(JSONTokener x) throws JSONException {
242 if (x.nextClean() != '{') {
243 throw x.syntaxError("A JSONObject text must begin with '{'");
249 throw x.syntaxError("A JSONObject text must end with '}'");
254 key = x.nextValue().toString();
257 // The key is followed by ':'. We will also tolerate '=' or '=>'.
261 if (x.next() != '>') {
264 } else if (c != ':') {
265 throw x.syntaxError("Expected a ':' after a key");
267 this.putOnce(key, x.nextValue());
269 // Pairs are separated by ','. We will also tolerate ';'.
271 switch (x.nextClean()) {
274 if (x.nextClean() == '}') {
282 throw x.syntaxError("Expected a ',' or '}'");
289 * Construct a JSONObject from a Map.
291 * @param map A map object that can be used to initialize the contents of
293 * @throws JSONException
295 public LOGJSONObject(Map<String, Object> map) {
296 this.map = new LinkedHashMap<String, Object>();
298 Iterator<Map.Entry<String, Object>> i = map.entrySet().iterator();
299 while (i.hasNext()) {
300 Map.Entry<String, Object> e = i.next();
301 Object value = e.getValue();
303 this.map.put(e.getKey(), wrap(value));
311 * Construct a JSONObject from an Object using bean getters.
312 * It reflects on all of the public methods of the object.
313 * For each of the methods with no parameters and a name starting
314 * with <code>"get"</code> or <code>"is"</code> followed by an uppercase letter,
315 * the method is invoked, and a key and the value returned from the getter method
316 * are put into the new JSONObject.
318 * The key is formed by removing the <code>"get"</code> or <code>"is"</code> prefix.
319 * If the second remaining character is not upper case, then the first
320 * character is converted to lower case.
322 * For example, if an object has a method named <code>"getName"</code>, and
323 * if the result of calling <code>object.getName()</code> is <code>"Larry Fine"</code>,
324 * then the JSONObject will contain <code>"name": "Larry Fine"</code>.
326 * @param bean An object that has getter methods that should be used
327 * to make a JSONObject.
329 public LOGJSONObject(Object bean) {
331 this.populateMap(bean);
336 * Construct a JSONObject from an Object, using reflection to find the
337 * public members. The resulting JSONObject's keys will be the strings
338 * from the names array, and the values will be the field values associated
339 * with those keys in the object. If a key is not found or not visible,
340 * then it will not be copied into the new JSONObject.
342 * @param object An object that has fields that should be used to make a
344 * @param names An array of strings, the names of the fields to be obtained
347 public LOGJSONObject(Object object, String names[]) {
349 Class<? extends Object> c = object.getClass();
350 for (int i = 0; i < names.length; i += 1) {
351 String name = names[i];
353 this.putOpt(name, c.getField(name).get(object));
354 } catch (Exception ignore) {
361 * Construct a JSONObject from a source JSON text string.
362 * This is the most commonly used JSONObject constructor.
364 * @param source A string beginning
365 * with <code>{</code> <small>(left brace)</small> and ending
366 * with <code>}</code> <small>(right brace)</small>.
367 * @throws JSONException If there is a syntax error in the source
368 * string or a duplicated key.
370 public LOGJSONObject(String source) throws JSONException {
371 this(new JSONTokener(source));
376 * Construct a JSONObject from a ResourceBundle.
378 * @param baseName The ResourceBundle base name.
379 * @param locale The Locale to load the ResourceBundle for.
380 * @throws JSONException If any JSONExceptions are detected.
382 public LOGJSONObject(String baseName, Locale locale) throws JSONException {
384 ResourceBundle bundle = ResourceBundle.getBundle(baseName, locale,
385 Thread.currentThread().getContextClassLoader());
387 // Iterate through the keys in the bundle.
389 Enumeration<?> keys = bundle.getKeys();
390 while (keys.hasMoreElements()) {
391 Object key = keys.nextElement();
392 if (key instanceof String) {
394 // Go through the path, ensuring that there is a nested JSONObject for each
395 // segment except the last. Add the value using the last segment's name into
396 // the deepest nested JSONObject.
398 String[] path = ((String) key).split("\\.");
399 int last = path.length - 1;
400 LOGJSONObject target = this;
401 for (int i = 0; i < last; i += 1) {
402 String segment = path[i];
403 LOGJSONObject nextTarget = target.optJSONObject(segment);
404 if (nextTarget == null) {
405 nextTarget = new LOGJSONObject();
406 target.put(segment, nextTarget);
410 target.put(path[last], bundle.getString((String) key));
417 * Accumulate values under a key. It is similar to the put method except
418 * that if there is already an object stored under the key then a
419 * JSONArray is stored under the key to hold all of the accumulated values.
420 * If there is already a JSONArray, then the new value is appended to it.
421 * In contrast, the put method replaces the previous value.
423 * If only one value is accumulated that is not a JSONArray, then the
424 * result will be the same as using put. But if multiple values are
425 * accumulated, then the result will be like append.
427 * @param key A key string.
428 * @param value An object to be accumulated under the key.
430 * @throws JSONException If the value is an invalid number
431 * or if the key is null.
433 public LOGJSONObject accumulate(
436 ) throws JSONException {
438 Object object = this.opt(key);
439 if (object == null) {
440 this.put(key, value instanceof JSONArray
441 ? new JSONArray().put(value)
443 } else if (object instanceof JSONArray) {
444 ((JSONArray) object).put(value);
446 this.put(key, new JSONArray().put(object).put(value));
453 * Append values to the array under a key. If the key does not exist in the
454 * JSONObject, then the key is put in the JSONObject with its value being a
455 * JSONArray containing the value parameter. If the key was already
456 * associated with a JSONArray, then the value parameter is appended to it.
458 * @param key A key string.
459 * @param value An object to be accumulated under the key.
461 * @throws JSONException If the key is null or if the current value
462 * associated with the key is not a JSONArray.
464 public LOGJSONObject append(String key, Object value) throws JSONException {
466 Object object = this.opt(key);
467 if (object == null) {
468 this.put(key, new JSONArray().put(value));
469 } else if (object instanceof JSONArray) {
470 this.put(key, ((JSONArray) object).put(value));
472 throw new JSONException("JSONObject[" + key +
473 "] is not a JSONArray.");
480 * Produce a string from a double. The string "null" will be returned if
481 * the number is not finite.
486 public static String doubleToString(double d) {
487 if (Double.isInfinite(d) || Double.isNaN(d)) {
491 // Shave off trailing zeros and decimal point, if possible.
493 String string = Double.toString(d);
494 if (string.indexOf('.') > 0 && string.indexOf('e') < 0 &&
495 string.indexOf('E') < 0) {
496 while (string.endsWith("0")) {
497 string = string.substring(0, string.length() - 1);
499 if (string.endsWith(".")) {
500 string = string.substring(0, string.length() - 1);
508 * Get the value object associated with a key.
510 * @param key A key string.
511 * @return The object associated with the key.
512 * @throws JSONException if the key is not found.
514 public Object get(String key) throws JSONException {
516 throw new JSONException("Null key.");
518 Object object = this.opt(key);
519 if (object == null) {
520 throw new JSONException("JSONObject[" + quote(key) +
528 * Get the boolean value associated with a key.
530 * @param key A key string.
532 * @throws JSONException if the value is not a Boolean or the String "true" or "false".
534 public boolean getBoolean(String key) throws JSONException {
535 Object object = this.get(key);
536 if (object.equals(Boolean.FALSE) ||
537 (object instanceof String &&
538 ((String) object).equalsIgnoreCase("false"))) {
540 } else if (object.equals(Boolean.TRUE) ||
541 (object instanceof String &&
542 ((String) object).equalsIgnoreCase("true"))) {
545 throw new JSONException("JSONObject[" + quote(key) +
546 "] is not a Boolean.");
551 * Get the double value associated with a key.
553 * @param key A key string.
554 * @return The numeric value.
555 * @throws JSONException if the key is not found or
556 * if the value is not a Number object and cannot be converted to a number.
558 public double getDouble(String key) throws JSONException {
559 Object object = this.get(key);
561 return object instanceof Number
562 ? ((Number) object).doubleValue()
563 : Double.parseDouble((String) object);
564 } catch (Exception e) {
565 throw new JSONException("JSONObject[" + quote(key) +
566 "] is not a number.");
572 * Get the int value associated with a key.
574 * @param key A key string.
575 * @return The integer value.
576 * @throws JSONException if the key is not found or if the value cannot
577 * be converted to an integer.
579 public int getInt(String key) throws JSONException {
580 Object object = this.get(key);
582 return object instanceof Number
583 ? ((Number) object).intValue()
584 : Integer.parseInt((String) object);
585 } catch (Exception e) {
586 throw new JSONException("JSONObject[" + quote(key) +
593 * Get the JSONArray value associated with a key.
595 * @param key A key string.
596 * @return A JSONArray which is the value.
597 * @throws JSONException if the key is not found or
598 * if the value is not a JSONArray.
600 public JSONArray getJSONArray(String key) throws JSONException {
601 Object object = this.get(key);
602 if (object instanceof JSONArray) {
603 return (JSONArray) object;
605 throw new JSONException("JSONObject[" + quote(key) +
606 "] is not a JSONArray.");
611 * Get the JSONObject value associated with a key.
613 * @param key A key string.
614 * @return A JSONObject which is the value.
615 * @throws JSONException if the key is not found or
616 * if the value is not a JSONObject.
618 public LOGJSONObject getJSONObject(String key) throws JSONException {
619 Object object = this.get(key);
620 if (object instanceof LOGJSONObject) {
621 return (LOGJSONObject) object;
623 throw new JSONException("JSONObject[" + quote(key) +
624 "] is not a JSONObject.");
629 * Get the long value associated with a key.
631 * @param key A key string.
632 * @return The long value.
633 * @throws JSONException if the key is not found or if the value cannot
634 * be converted to a long.
636 public long getLong(String key) throws JSONException {
637 Object object = this.get(key);
639 return object instanceof Number
640 ? ((Number) object).longValue()
641 : Long.parseLong((String) object);
642 } catch (Exception e) {
643 throw new JSONException("JSONObject[" + quote(key) +
650 * Get an array of field names from a JSONObject.
652 * @return An array of field names, or null if there are no names.
654 public static String[] getNames(LOGJSONObject jo) {
655 int length = jo.length();
659 Iterator<String> iterator = jo.keys();
660 String[] names = new String[length];
662 while (iterator.hasNext()) {
663 names[i] = iterator.next();
671 * Get an array of field names from an Object.
673 * @return An array of field names, or null if there are no names.
675 public static String[] getNames(Object object) {
676 if (object == null) {
679 Class<? extends Object> klass = object.getClass();
680 Field[] fields = klass.getFields();
681 int length = fields.length;
685 String[] names = new String[length];
686 for (int i = 0; i < length; i += 1) {
687 names[i] = fields[i].getName();
694 * Get the string associated with a key.
696 * @param key A key string.
697 * @return A string which is the value.
698 * @throws JSONException if there is no string value for the key.
700 public String getString(String key) throws JSONException {
701 Object object = this.get(key);
702 if (object instanceof String) {
703 return (String) object;
705 throw new JSONException("JSONObject[" + quote(key) +
711 * Determine if the JSONObject contains a specific key.
713 * @param key A key string.
714 * @return true if the key exists in the JSONObject.
716 public boolean has(String key) {
717 return this.map.containsKey(key);
722 * Increment a property of a JSONObject. If there is no such property,
723 * create one with a value of 1. If there is such a property, and if
724 * it is an Integer, Long, Double, or Float, then add one to it.
726 * @param key A key string.
728 * @throws JSONException If there is already a property with this name
729 * that is not an Integer, Long, Double, or Float.
731 public LOGJSONObject increment(String key) throws JSONException {
732 Object value = this.opt(key);
735 } else if (value instanceof Integer) {
736 this.put(key, ((Integer) value).intValue() + 1);
737 } else if (value instanceof Long) {
738 this.put(key, ((Long) value).longValue() + 1);
739 } else if (value instanceof Double) {
740 this.put(key, ((Double) value).doubleValue() + 1);
741 } else if (value instanceof Float) {
742 this.put(key, ((Float) value).floatValue() + 1);
744 throw new JSONException("Unable to increment [" + quote(key) + "].");
751 * Determine if the value associated with the key is null or if there is
754 * @param key A key string.
755 * @return true if there is no value associated with the key or if
756 * the value is the JSONObject.NULL object.
758 public boolean isNull(String key) {
759 return LOGJSONObject.NULL.equals(this.opt(key));
764 * Get an enumeration of the keys of the JSONObject.
766 * @return An iterator of the keys.
768 public Iterator<String> keys() {
769 return this.keySet().iterator();
774 * Get a set of keys of the JSONObject.
778 public Set<String> keySet() {
779 return this.map.keySet();
784 * Get the number of keys stored in the JSONObject.
786 * @return The number of keys in the JSONObject.
788 public int length() {
789 return this.map.size();
794 * Produce a JSONArray containing the names of the elements of this
797 * @return A JSONArray containing the key strings, or null if the JSONObject
800 public JSONArray names() {
801 JSONArray ja = new JSONArray();
802 Iterator<String> keys = this.keys();
803 while (keys.hasNext()) {
806 return ja.length() == 0 ? null : ja;
810 * Produce a string from a Number.
812 * @param number A Number
814 * @throws JSONException If n is a non-finite number.
816 public static String numberToString(Number number)
817 throws JSONException {
818 if (number == null) {
819 throw new JSONException("Null pointer");
821 testValidity(number);
823 // Shave off trailing zeros and decimal point, if possible.
825 String string = number.toString();
826 if (string.indexOf('.') > 0 && string.indexOf('e') < 0 &&
827 string.indexOf('E') < 0) {
828 while (string.endsWith("0")) {
829 string = string.substring(0, string.length() - 1);
831 if (string.endsWith(".")) {
832 string = string.substring(0, string.length() - 1);
840 * Get an optional value associated with a key.
842 * @param key A key string.
843 * @return An object which is the value, or null if there is no value.
845 public Object opt(String key) {
846 return key == null ? null : this.map.get(key);
851 * Get an optional boolean associated with a key.
852 * It returns false if there is no such key, or if the value is not
853 * Boolean.TRUE or the String "true".
855 * @param key A key string.
858 public boolean optBoolean(String key) {
859 return this.optBoolean(key, false);
864 * Get an optional boolean associated with a key.
865 * It returns the defaultValue if there is no such key, or if it is not
866 * a Boolean or the String "true" or "false" (case insensitive).
868 * @param key A key string.
869 * @param defaultValue The default.
872 public boolean optBoolean(String key, boolean defaultValue) {
874 return this.getBoolean(key);
875 } catch (Exception e) {
882 * Get an optional double associated with a key,
883 * or NaN if there is no such key or if its value is not a number.
884 * If the value is a string, an attempt will be made to evaluate it as
887 * @param key A string which is the key.
888 * @return An object which is the value.
890 public double optDouble(String key) {
891 return this.optDouble(key, Double.NaN);
896 * Get an optional double associated with a key, or the
897 * defaultValue if there is no such key or if its value is not a number.
898 * If the value is a string, an attempt will be made to evaluate it as
901 * @param key A key string.
902 * @param defaultValue The default.
903 * @return An object which is the value.
905 public double optDouble(String key, double defaultValue) {
907 return this.getDouble(key);
908 } catch (Exception e) {
915 * Get an optional int value associated with a key,
916 * or zero if there is no such key or if the value is not a number.
917 * If the value is a string, an attempt will be made to evaluate it as
920 * @param key A key string.
921 * @return An object which is the value.
923 public int optInt(String key) {
924 return this.optInt(key, 0);
929 * Get an optional int value associated with a key,
930 * or the default if there is no such key or if the value is not a number.
931 * If the value is a string, an attempt will be made to evaluate it as
934 * @param key A key string.
935 * @param defaultValue The default.
936 * @return An object which is the value.
938 public int optInt(String key, int defaultValue) {
940 return this.getInt(key);
941 } catch (Exception e) {
948 * Get an optional JSONArray associated with a key.
949 * It returns null if there is no such key, or if its value is not a
952 * @param key A key string.
953 * @return A JSONArray which is the value.
955 public JSONArray optJSONArray(String key) {
956 Object o = this.opt(key);
957 return o instanceof JSONArray ? (JSONArray) o : null;
962 * Get an optional JSONObject associated with a key.
963 * It returns null if there is no such key, or if its value is not a
966 * @param key A key string.
967 * @return A JSONObject which is the value.
969 public LOGJSONObject optJSONObject(String key) {
970 Object object = this.opt(key);
971 return object instanceof LOGJSONObject ? (LOGJSONObject) object : null;
976 * Get an optional long value associated with a key,
977 * or zero if there is no such key or if the value is not a number.
978 * If the value is a string, an attempt will be made to evaluate it as
981 * @param key A key string.
982 * @return An object which is the value.
984 public long optLong(String key) {
985 return this.optLong(key, 0);
990 * Get an optional long value associated with a key,
991 * or the default if there is no such key or if the value is not a number.
992 * If the value is a string, an attempt will be made to evaluate it as
995 * @param key A key string.
996 * @param defaultValue The default.
997 * @return An object which is the value.
999 public long optLong(String key, long defaultValue) {
1001 return this.getLong(key);
1002 } catch (Exception e) {
1003 return defaultValue;
1009 * Get an optional string associated with a key.
1010 * It returns an empty string if there is no such key. If the value is not
1011 * a string and is not null, then it is converted to a string.
1013 * @param key A key string.
1014 * @return A string which is the value.
1016 public String optString(String key) {
1017 return this.optString(key, "");
1022 * Get an optional string associated with a key.
1023 * It returns the defaultValue if there is no such key.
1025 * @param key A key string.
1026 * @param defaultValue The default.
1027 * @return A string which is the value.
1029 public String optString(String key, String defaultValue) {
1030 Object object = this.opt(key);
1031 return NULL.equals(object) ? defaultValue : object.toString();
1035 private void populateMap(Object bean) {
1036 Class<? extends Object> klass = bean.getClass();
1038 // If klass is a System class then set includeSuperClass to false.
1040 boolean includeSuperClass = klass.getClassLoader() != null;
1042 Method[] methods = includeSuperClass
1043 ? klass.getMethods()
1044 : klass.getDeclaredMethods();
1045 for (int i = 0; i < methods.length; i += 1) {
1047 Method method = methods[i];
1048 if (Modifier.isPublic(method.getModifiers())) {
1049 String name = method.getName();
1051 if (name.startsWith("get")) {
1052 if ("getClass".equals(name) ||
1053 "getDeclaringClass".equals(name)) {
1056 key = name.substring(3);
1058 } else if (name.startsWith("is")) {
1059 key = name.substring(2);
1061 if (key.length() > 0 &&
1062 Character.isUpperCase(key.charAt(0)) &&
1063 method.getParameterTypes().length == 0) {
1064 if (key.length() == 1) {
1065 key = key.toLowerCase();
1066 } else if (!Character.isUpperCase(key.charAt(1))) {
1067 key = key.substring(0, 1).toLowerCase() +
1071 Object result = method.invoke(bean, (Object[]) null);
1072 if (result != null) {
1073 this.map.put(key, wrap(result));
1077 } catch (Exception ignore) {
1084 * Put a key/boolean pair in the JSONObject.
1086 * @param key A key string.
1087 * @param value A boolean which is the value.
1089 * @throws JSONException If the key is null.
1091 public LOGJSONObject put(String key, boolean value) throws JSONException {
1092 this.put(key, value ? Boolean.TRUE : Boolean.FALSE);
1098 * Put a key/value pair in the JSONObject, where the value will be a
1099 * JSONArray which is produced from a Collection.
1101 * @param key A key string.
1102 * @param value A Collection value.
1104 * @throws JSONException
1106 public LOGJSONObject put(String key, Collection<Object> value) throws JSONException {
1107 this.put(key, new JSONArray(value));
1113 * Put a key/double pair in the JSONObject.
1115 * @param key A key string.
1116 * @param value A double which is the value.
1118 * @throws JSONException If the key is null or if the number is invalid.
1120 public LOGJSONObject put(String key, double value) throws JSONException {
1121 this.put(key, new Double(value));
1127 * Put a key/int pair in the JSONObject.
1129 * @param key A key string.
1130 * @param value An int which is the value.
1132 * @throws JSONException If the key is null.
1134 public LOGJSONObject put(String key, int value) throws JSONException {
1135 this.put(key, new Integer(value));
1141 * Put a key/long pair in the JSONObject.
1143 * @param key A key string.
1144 * @param value A long which is the value.
1146 * @throws JSONException If the key is null.
1148 public LOGJSONObject put(String key, long value) throws JSONException {
1149 this.put(key, new Long(value));
1155 * Put a key/value pair in the JSONObject, where the value will be a
1156 * JSONObject which is produced from a Map.
1158 * @param key A key string.
1159 * @param value A Map value.
1161 * @throws JSONException
1163 public LOGJSONObject put(String key, Map<String, Object> value) throws JSONException {
1164 this.put(key, new LOGJSONObject(value));
1170 * Put a key/value pair in the JSONObject. If the value is null,
1171 * then the key will be removed from the JSONObject if it is present.
1173 * @param key A key string.
1174 * @param value An object which is the value. It should be of one of these
1175 * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, String,
1176 * or the JSONObject.NULL object.
1178 * @throws JSONException If the value is non-finite number
1179 * or if the key is null.
1181 public LOGJSONObject put(String key, Object value) throws JSONException {
1184 throw new JSONException("Null key.");
1186 if (value != null) {
1187 testValidity(value);
1188 pooled = (String) keyPool.get(key);
1189 if (pooled == null) {
1190 if (keyPool.size() >= keyPoolSize) {
1191 keyPool = new LinkedHashMap<String, Object>(keyPoolSize);
1193 keyPool.put(key, key);
1197 this.map.put(key, value);
1206 * Put a key/value pair in the JSONObject, but only if the key and the
1207 * value are both non-null, and only if there is not already a member
1213 * @throws JSONException if the key is a duplicate
1215 public LOGJSONObject putOnce(String key, Object value) throws JSONException {
1216 if (key != null && value != null) {
1217 if (this.opt(key) != null) {
1218 throw new JSONException("Duplicate key \"" + key + "\"");
1220 this.put(key, value);
1227 * Put a key/value pair in the JSONObject, but only if the
1228 * key and the value are both non-null.
1230 * @param key A key string.
1231 * @param value An object which is the value. It should be of one of these
1232 * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, String,
1233 * or the JSONObject.NULL object.
1235 * @throws JSONException If the value is a non-finite number.
1237 public LOGJSONObject putOpt(String key, Object value) throws JSONException {
1238 if (key != null && value != null) {
1239 this.put(key, value);
1246 * Produce a string in double quotes with backslash sequences in all the
1247 * right places. A backslash will be inserted within </, producing <\/,
1248 * allowing JSON text to be delivered in HTML. In JSON text, a string
1249 * cannot contain a control character or an unescaped quote or backslash.
1251 * @param string A String
1252 * @return A String correctly formatted for insertion in a JSON text.
1254 public static String quote(String string) {
1255 StringWriter sw = new StringWriter();
1256 synchronized (sw.getBuffer()) {
1258 return quote(string, sw).toString();
1259 } catch (IOException ignored) {
1260 // will never happen - we are writing to a string writer
1266 public static Writer quote(String string, Writer w) throws IOException {
1267 if (string == null || string.length() == 0) {
1276 int len = string.length();
1279 for (i = 0; i < len; i += 1) {
1281 c = string.charAt(i);
1310 if (c < ' ' || (c >= '\u0080' && c < '\u00a0')
1311 || (c >= '\u2000' && c < '\u2100')) {
1313 hhhh = Integer.toHexString(c);
1314 w.write("0000", 0, 4 - hhhh.length());
1326 * Remove a name and its value, if present.
1328 * @param key The name to be removed.
1329 * @return The value that was associated with the name,
1330 * or null if there was no value.
1332 public Object remove(String key) {
1333 return this.map.remove(key);
1337 * Try to convert a string into a number, boolean, or null. If the string
1338 * can't be converted, return the string.
1340 * @param string A String.
1341 * @return A simple JSON value.
1343 public static Object stringToValue(String string) {
1345 if (string.equals("")) {
1348 if (string.equalsIgnoreCase("true")) {
1349 return Boolean.TRUE;
1351 if (string.equalsIgnoreCase("false")) {
1352 return Boolean.FALSE;
1354 if (string.equalsIgnoreCase("null")) {
1355 return LOGJSONObject.NULL;
1359 * If it might be a number, try converting it.
1360 * If a number cannot be produced, then the value will just
1361 * be a string. Note that the plus and implied string
1362 * conventions are non-standard. A JSON parser may accept
1363 * non-JSON forms as long as it accepts all correct JSON forms.
1366 char b = string.charAt(0);
1367 if ((b >= '0' && b <= '9') || b == '.' || b == '-' || b == '+') {
1369 if (string.indexOf('.') > -1 ||
1370 string.indexOf('e') > -1 || string.indexOf('E') > -1) {
1371 d = Double.valueOf(string);
1372 if (!d.isInfinite() && !d.isNaN()) {
1376 Long myLong = new Long(string);
1377 if (myLong.longValue() == myLong.intValue()) {
1378 return new Integer(myLong.intValue());
1383 } catch (Exception ignore) {
1391 * Throw an exception if the object is a NaN or infinite number.
1393 * @param o The object to test.
1394 * @throws JSONException If o is a non-finite number.
1396 public static void testValidity(Object o) throws JSONException {
1398 if (o instanceof Double) {
1399 if (((Double) o).isInfinite() || ((Double) o).isNaN()) {
1400 throw new JSONException(
1401 "JSON does not allow non-finite numbers.");
1403 } else if (o instanceof Float) {
1404 if (((Float) o).isInfinite() || ((Float) o).isNaN()) {
1405 throw new JSONException(
1406 "JSON does not allow non-finite numbers.");
1414 * Produce a JSONArray containing the values of the members of this
1417 * @param names A JSONArray containing a list of key strings. This
1418 * determines the sequence of the values in the result.
1419 * @return A JSONArray of values.
1420 * @throws JSONException If any of the values are non-finite numbers.
1422 public JSONArray toJSONArray(JSONArray names) throws JSONException {
1423 if (names == null || names.length() == 0) {
1426 JSONArray ja = new JSONArray();
1427 for (int i = 0; i < names.length(); i += 1) {
1428 ja.put(this.opt(names.getString(i)));
1434 * Make a JSON text of this JSONObject. For compactness, no whitespace
1435 * is added. If this would not result in a syntactically correct JSON text,
1436 * then null will be returned instead.
1438 * Warning: This method assumes that the data structure is acyclical.
1440 * @return a printable, displayable, portable, transmittable
1441 * representation of the object, beginning
1442 * with <code>{</code> <small>(left brace)</small> and ending
1443 * with <code>}</code> <small>(right brace)</small>.
1445 public String toString() {
1447 return this.toString(0);
1448 } catch (Exception e) {
1455 * Make a prettyprinted JSON text of this JSONObject.
1457 * Warning: This method assumes that the data structure is acyclical.
1459 * @param indentFactor The number of spaces to add to each level of
1461 * @return a printable, displayable, portable, transmittable
1462 * representation of the object, beginning
1463 * with <code>{</code> <small>(left brace)</small> and ending
1464 * with <code>}</code> <small>(right brace)</small>.
1465 * @throws JSONException If the object contains an invalid number.
1467 public String toString(int indentFactor) throws JSONException {
1468 StringWriter w = new StringWriter();
1469 synchronized (w.getBuffer()) {
1470 return this.write(w, indentFactor, 0).toString();
1475 * Make a JSON text of an Object value. If the object has an
1476 * value.toJSONString() method, then that method will be used to produce
1477 * the JSON text. The method is required to produce a strictly
1478 * conforming text. If the object does not contain a toJSONString
1479 * method (which is the most common case), then a text will be
1480 * produced by other means. If the value is an array or Collection,
1481 * then a JSONArray will be made from it and its toJSONString method
1482 * will be called. If the value is a MAP, then a JSONObject will be made
1483 * from it and its toJSONString method will be called. Otherwise, the
1484 * value's toString method will be called, and the result will be quoted.
1487 * Warning: This method assumes that the data structure is acyclical.
1489 * @param value The value to be serialized.
1490 * @return a printable, displayable, transmittable
1491 * representation of the object, beginning
1492 * with <code>{</code> <small>(left brace)</small> and ending
1493 * with <code>}</code> <small>(right brace)</small>.
1494 * @throws JSONException If the value is or contains an invalid number.
1496 @SuppressWarnings("unchecked")
1497 public static String valueToString(Object value) throws JSONException {
1498 if (value == null || value.equals(null)) {
1501 if (value instanceof JSONString) {
1504 object = ((JSONString) value).toJSONString();
1505 } catch (Exception e) {
1506 throw new JSONException(e);
1508 if (object instanceof String) {
1509 return (String) object;
1511 throw new JSONException("Bad value from toJSONString: " + object);
1513 if (value instanceof Number) {
1514 return numberToString((Number) value);
1516 if (value instanceof Boolean || value instanceof LOGJSONObject ||
1517 value instanceof JSONArray) {
1518 return value.toString();
1520 if (value instanceof Map) {
1521 return new LOGJSONObject((Map<String, Object>) value).toString();
1523 if (value instanceof Collection) {
1524 return new JSONArray((Collection<Object>) value).toString();
1526 if (value.getClass().isArray()) {
1527 return new JSONArray(value).toString();
1529 return quote(value.toString());
1533 * Wrap an object, if necessary. If the object is null, return the NULL
1534 * object. If it is an array or collection, wrap it in a JSONArray. If
1535 * it is a map, wrap it in a JSONObject. If it is a standard property
1536 * (Double, String, et al) then it is already wrapped. Otherwise, if it
1537 * comes from one of the java packages, turn it into a string. And if
1538 * it doesn't, try to wrap it in a JSONObject. If the wrapping fails,
1539 * then null is returned.
1541 * @param object The object to wrap
1542 * @return The wrapped value
1544 @SuppressWarnings("unchecked")
1545 public static Object wrap(Object object) {
1547 if (object == null) {
1550 if (object instanceof LOGJSONObject || object instanceof JSONArray ||
1551 NULL.equals(object) || object instanceof JSONString ||
1552 object instanceof Byte || object instanceof Character ||
1553 object instanceof Short || object instanceof Integer ||
1554 object instanceof Long || object instanceof Boolean ||
1555 object instanceof Float || object instanceof Double ||
1556 object instanceof String) {
1560 if (object instanceof Collection) {
1561 return new JSONArray((Collection<Object>) object);
1563 if (object.getClass().isArray()) {
1564 return new JSONArray(object);
1566 if (object instanceof Map) {
1567 return new LOGJSONObject((Map<String, Object>) object);
1569 Package objectPackage = object.getClass().getPackage();
1570 String objectPackageName = objectPackage != null
1571 ? objectPackage.getName()
1574 objectPackageName.startsWith("java.") ||
1575 objectPackageName.startsWith("javax.") ||
1576 object.getClass().getClassLoader() == null
1578 return object.toString();
1580 return new LOGJSONObject(object);
1581 } catch (Exception exception) {
1588 * Write the contents of the JSONObject as JSON text to a writer.
1589 * For compactness, no whitespace is added.
1591 * Warning: This method assumes that the data structure is acyclical.
1593 * @return The writer.
1594 * @throws JSONException
1596 public Writer write(Writer writer) throws JSONException {
1597 return this.write(writer, 0, 0);
1601 @SuppressWarnings("unchecked")
1602 static final Writer writeValue(Writer writer, Object value,
1603 int indentFactor, int indent) throws JSONException, IOException {
1604 if (value == null || value.equals(null)) {
1605 writer.write("null");
1606 } else if (value instanceof LOGJSONObject) {
1607 ((LOGJSONObject) value).write(writer, indentFactor, indent);
1608 } else if (value instanceof JSONArray) {
1609 ((JSONArray) value).write(writer, indentFactor, indent);
1610 } else if (value instanceof Map) {
1611 new LOGJSONObject((Map<String, Object>) value).write(writer, indentFactor, indent);
1612 } else if (value instanceof Collection) {
1613 new JSONArray((Collection<Object>) value).write(writer, indentFactor,
1615 } else if (value.getClass().isArray()) {
1616 new JSONArray(value).write(writer, indentFactor, indent);
1617 } else if (value instanceof Number) {
1618 writer.write(numberToString((Number) value));
1619 } else if (value instanceof Boolean) {
1620 writer.write(value.toString());
1621 } else if (value instanceof JSONString) {
1624 o = ((JSONString) value).toJSONString();
1625 } catch (Exception e) {
1626 throw new JSONException(e);
1628 writer.write(o != null ? o.toString() : quote(value.toString()));
1630 quote(value.toString(), writer);
1635 static final void indent(Writer writer, int indent) throws IOException {
1636 for (int i = 0; i < indent; i += 1) {
1642 * Write the contents of the JSONObject as JSON text to a writer. For
1643 * compactness, no whitespace is added.
1645 * Warning: This method assumes that the data structure is acyclical.
1647 * @return The writer.
1648 * @throws JSONException
1650 Writer write(Writer writer, int indentFactor, int indent)
1651 throws JSONException {
1653 boolean commanate = false;
1654 final int length = this.length();
1655 Iterator<String> keys = this.keys();
1659 Object key = keys.next();
1660 writer.write(quote(key.toString()));
1662 if (indentFactor > 0) {
1665 writeValue(writer, this.map.get(key), indentFactor, indent);
1666 } else if (length != 0) {
1667 final int newindent = indent + indentFactor;
1668 while (keys.hasNext()) {
1669 Object key = keys.next();
1673 if (indentFactor > 0) {
1676 indent(writer, newindent);
1677 writer.write(quote(key.toString()));
1679 if (indentFactor > 0) {
1682 writeValue(writer, this.map.get(key), indentFactor,
1686 if (indentFactor > 0) {
1689 indent(writer, indent);
1693 } catch (IOException exception) {
1694 throw new JSONException(exception);