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;
32 import java.util.Collection;
33 import java.util.Enumeration;
34 import java.util.LinkedHashMap;
35 import java.util.Iterator;
36 import java.util.Locale;
38 import java.util.ResourceBundle;
41 import org.json.JSONArray;
42 import org.json.JSONException;
43 import org.json.JSONString;
44 import org.json.JSONTokener;
47 * A JSONObject is an unordered collection of name/value pairs. Its external
48 * form is a string wrapped in curly braces with colons between the names and
49 * values, and commas between the values and names. The internal form is an
50 * object having <code>get</code> and <code>opt</code> methods for accessing the
51 * values by name, and <code>put</code> methods for adding or replacing values
52 * by name. The values can be any of these types: <code>Boolean</code>,
53 * <code>JSONArray</code>, <code>JSONObject</code>, <code>Number</code>,
54 * <code>String</code>, or the <code>JSONObject.NULL</code> object. A JSONObject
55 * constructor can be used to convert an external form JSON text into an
56 * internal form whose values can be retrieved with the <code>get</code> and
57 * <code>opt</code> methods, or to convert values into a JSON text using the
58 * <code>put</code> and <code>toString</code> methods. A <code>get</code> method
59 * returns a value if one can be found, and throws an exception if one cannot be
60 * found. An <code>opt</code> method returns a default value instead of throwing
61 * an exception, and so is useful for obtaining optional values.
63 * The generic <code>get()</code> and <code>opt()</code> methods return an
64 * object, which you can cast or query for type. There are also typed
65 * <code>get</code> and <code>opt</code> methods that do type checking and type
66 * coercion for you. The opt methods differ from the get methods in that they do
67 * not throw. Instead, they return a specified value, such as null.
69 * The <code>put</code> methods add or replace values in an object. For example,
72 * myString = new JSONObject().put("JSON", "Hello, World!").toString();
75 * produces the string <code>{"JSON": "Hello, World"}</code>.
77 * The texts produced by the <code>toString</code> methods strictly conform to
78 * the JSON syntax rules. The constructors are more forgiving in the texts they
81 * <li>An extra <code>,</code> <small>(comma)</small> may appear just
82 * before the closing brace.</li>
83 * <li>Strings may be quoted with <code>'</code> <small>(single
84 * quote)</small>.</li>
85 * <li>Strings do not need to be quoted at all if they do not begin with a quote
86 * or single quote, and if they do not contain leading or trailing spaces, and
87 * if they do not contain any of these characters:
88 * <code>{ } [ ] / \ : , = ; #</code> and if they do not look like numbers and
89 * if they are not the reserved words <code>true</code>, <code>false</code>, or
90 * <code>null</code>.</li>
91 * <li>Keys can be followed by <code>=</code> or <code>=></code> as well as by
92 * <code>:</code>.</li>
93 * <li>Values can be followed by <code>;</code> <small>(semicolon)</small> as
94 * well as by <code>,</code> <small>(comma)</small>.</li>
100 public class LOGJSONObject {
102 * The maximum number of keys in the key pool.
104 private static final int keyPoolSize = 100;
107 * Key pooling is like string interning, but without permanently tying up
108 * memory. To help conserve memory, storage of duplicated key strings in
109 * JSONObjects will be avoided by using a key pool to manage unique key
110 * string objects. This is used by JSONObject.put(string, object).
112 private static Map<String,Object> keyPool = new LinkedHashMap<String,Object>(keyPoolSize);
115 * JSONObject.NULL is equivalent to the value that JavaScript calls null,
116 * whilst Java's null is equivalent to the value that JavaScript calls
119 private static final class Null {
122 * There is only intended to be a single instance of the NULL object,
123 * so the clone method returns itself.
126 protected final Object clone() {
131 * A Null object is equal to the null value and to itself.
132 * @param object An object to test for nullness.
133 * @return true if the object parameter is the JSONObject.NULL object
136 public boolean equals(Object object) {
137 return object == null || object == this;
141 * Get the "null" string value.
142 * @return The string "null".
144 public String toString() {
151 * The map where the JSONObject's properties are kept.
153 private final Map<String,Object> map;
157 * It is sometimes more convenient and less ambiguous to have a
158 * <code>NULL</code> object than to use Java's <code>null</code> value.
159 * <code>JSONObject.NULL.equals(null)</code> returns <code>true</code>.
160 * <code>JSONObject.NULL.toString()</code> returns <code>"null"</code>.
162 public static final Object NULL = new Null();
166 * Construct an empty JSONObject.
168 public LOGJSONObject() {
169 this.map = new LinkedHashMap<String,Object>();
174 * Construct a JSONObject from a subset of another JSONObject.
175 * An array of strings is used to identify the keys that should be copied.
176 * Missing keys are ignored.
177 * @param jo A JSONObject.
178 * @param names An array of strings.
179 * @throws JSONException
180 * @exception JSONException If a value is a non-finite number or if a name is duplicated.
182 public LOGJSONObject(LOGJSONObject jo, String[] names) {
184 for (int i = 0; i < names.length; i += 1) {
186 this.putOnce(names[i], jo.opt(names[i]));
187 } catch (Exception ignore) {
194 * Construct a JSONObject from a JSONTokener.
195 * @param x A JSONTokener object containing the source string.
196 * @throws JSONException If there is a syntax error in the source string
197 * or a duplicated key.
199 public LOGJSONObject(JSONTokener x) throws JSONException {
204 if (x.nextClean() != '{') {
205 throw x.syntaxError("A JSONObject text must begin with '{'");
211 throw x.syntaxError("A JSONObject text must end with '}'");
216 key = x.nextValue().toString();
219 // The key is followed by ':'. We will also tolerate '=' or '=>'.
223 if (x.next() != '>') {
226 } else if (c != ':') {
227 throw x.syntaxError("Expected a ':' after a key");
229 this.putOnce(key, x.nextValue());
231 // Pairs are separated by ','. We will also tolerate ';'.
233 switch (x.nextClean()) {
236 if (x.nextClean() == '}') {
244 throw x.syntaxError("Expected a ',' or '}'");
251 * Construct a JSONObject from a Map.
253 * @param map A map object that can be used to initialize the contents of
255 * @throws JSONException
257 public LOGJSONObject(Map<String,Object> map) {
258 this.map = new LinkedHashMap<String,Object>();
260 Iterator<Map.Entry<String,Object>> i = map.entrySet().iterator();
261 while (i.hasNext()) {
262 Map.Entry<String,Object> e = i.next();
263 Object value = e.getValue();
265 this.map.put(e.getKey(), wrap(value));
273 * Construct a JSONObject from an Object using bean getters.
274 * It reflects on all of the public methods of the object.
275 * For each of the methods with no parameters and a name starting
276 * with <code>"get"</code> or <code>"is"</code> followed by an uppercase letter,
277 * the method is invoked, and a key and the value returned from the getter method
278 * are put into the new JSONObject.
280 * The key is formed by removing the <code>"get"</code> or <code>"is"</code> prefix.
281 * If the second remaining character is not upper case, then the first
282 * character is converted to lower case.
284 * For example, if an object has a method named <code>"getName"</code>, and
285 * if the result of calling <code>object.getName()</code> is <code>"Larry Fine"</code>,
286 * then the JSONObject will contain <code>"name": "Larry Fine"</code>.
288 * @param bean An object that has getter methods that should be used
289 * to make a JSONObject.
291 public LOGJSONObject(Object bean) {
293 this.populateMap(bean);
298 * Construct a JSONObject from an Object, using reflection to find the
299 * public members. The resulting JSONObject's keys will be the strings
300 * from the names array, and the values will be the field values associated
301 * with those keys in the object. If a key is not found or not visible,
302 * then it will not be copied into the new JSONObject.
303 * @param object An object that has fields that should be used to make a
305 * @param names An array of strings, the names of the fields to be obtained
308 public LOGJSONObject(Object object, String names[]) {
310 Class<? extends Object> c = object.getClass();
311 for (int i = 0; i < names.length; i += 1) {
312 String name = names[i];
314 this.putOpt(name, c.getField(name).get(object));
315 } catch (Exception ignore) {
322 * Construct a JSONObject from a source JSON text string.
323 * This is the most commonly used JSONObject constructor.
324 * @param source A string beginning
325 * with <code>{</code> <small>(left brace)</small> and ending
326 * with <code>}</code> <small>(right brace)</small>.
327 * @exception JSONException If there is a syntax error in the source
328 * string or a duplicated key.
330 public LOGJSONObject(String source) throws JSONException {
331 this(new JSONTokener(source));
336 * Construct a JSONObject from a ResourceBundle.
337 * @param baseName The ResourceBundle base name.
338 * @param locale The Locale to load the ResourceBundle for.
339 * @throws JSONException If any JSONExceptions are detected.
341 public LOGJSONObject(String baseName, Locale locale) throws JSONException {
343 ResourceBundle bundle = ResourceBundle.getBundle(baseName, locale,
344 Thread.currentThread().getContextClassLoader());
346 // Iterate through the keys in the bundle.
348 Enumeration<?> keys = bundle.getKeys();
349 while (keys.hasMoreElements()) {
350 Object key = keys.nextElement();
351 if (key instanceof String) {
353 // Go through the path, ensuring that there is a nested JSONObject for each
354 // segment except the last. Add the value using the last segment's name into
355 // the deepest nested JSONObject.
357 String[] path = ((String)key).split("\\.");
358 int last = path.length - 1;
359 LOGJSONObject target = this;
360 for (int i = 0; i < last; i += 1) {
361 String segment = path[i];
362 LOGJSONObject nextTarget = target.optJSONObject(segment);
363 if (nextTarget == null) {
364 nextTarget = new LOGJSONObject();
365 target.put(segment, nextTarget);
369 target.put(path[last], bundle.getString((String)key));
376 * Accumulate values under a key. It is similar to the put method except
377 * that if there is already an object stored under the key then a
378 * JSONArray is stored under the key to hold all of the accumulated values.
379 * If there is already a JSONArray, then the new value is appended to it.
380 * In contrast, the put method replaces the previous value.
382 * If only one value is accumulated that is not a JSONArray, then the
383 * result will be the same as using put. But if multiple values are
384 * accumulated, then the result will be like append.
385 * @param key A key string.
386 * @param value An object to be accumulated under the key.
388 * @throws JSONException If the value is an invalid number
389 * or if the key is null.
391 public LOGJSONObject accumulate(
394 ) throws JSONException {
396 Object object = this.opt(key);
397 if (object == null) {
398 this.put(key, value instanceof JSONArray
399 ? new JSONArray().put(value)
401 } else if (object instanceof JSONArray) {
402 ((JSONArray)object).put(value);
404 this.put(key, new JSONArray().put(object).put(value));
411 * Append values to the array under a key. If the key does not exist in the
412 * JSONObject, then the key is put in the JSONObject with its value being a
413 * JSONArray containing the value parameter. If the key was already
414 * associated with a JSONArray, then the value parameter is appended to it.
415 * @param key A key string.
416 * @param value An object to be accumulated under the key.
418 * @throws JSONException If the key is null or if the current value
419 * associated with the key is not a JSONArray.
421 public LOGJSONObject append(String key, Object value) throws JSONException {
423 Object object = this.opt(key);
424 if (object == null) {
425 this.put(key, new JSONArray().put(value));
426 } else if (object instanceof JSONArray) {
427 this.put(key, ((JSONArray)object).put(value));
429 throw new JSONException("JSONObject[" + key +
430 "] is not a JSONArray.");
437 * Produce a string from a double. The string "null" will be returned if
438 * the number is not finite.
442 public static String doubleToString(double d) {
443 if (Double.isInfinite(d) || Double.isNaN(d)) {
447 // Shave off trailing zeros and decimal point, if possible.
449 String string = Double.toString(d);
450 if (string.indexOf('.') > 0 && string.indexOf('e') < 0 &&
451 string.indexOf('E') < 0) {
452 while (string.endsWith("0")) {
453 string = string.substring(0, string.length() - 1);
455 if (string.endsWith(".")) {
456 string = string.substring(0, string.length() - 1);
464 * Get the value object associated with a key.
466 * @param key A key string.
467 * @return The object associated with the key.
468 * @throws JSONException if the key is not found.
470 public Object get(String key) throws JSONException {
472 throw new JSONException("Null key.");
474 Object object = this.opt(key);
475 if (object == null) {
476 throw new JSONException("JSONObject[" + quote(key) +
484 * Get the boolean value associated with a key.
486 * @param key A key string.
488 * @throws JSONException
489 * if the value is not a Boolean or the String "true" or "false".
491 public boolean getBoolean(String key) throws JSONException {
492 Object object = this.get(key);
493 if (object.equals(Boolean.FALSE) ||
494 (object instanceof String &&
495 ((String)object).equalsIgnoreCase("false"))) {
497 } else if (object.equals(Boolean.TRUE) ||
498 (object instanceof String &&
499 ((String)object).equalsIgnoreCase("true"))) {
502 throw new JSONException("JSONObject[" + quote(key) +
503 "] is not a Boolean.");
508 * Get the double value associated with a key.
509 * @param key A key string.
510 * @return The numeric value.
511 * @throws JSONException if the key is not found or
512 * if the value is not a Number object and cannot be converted to a number.
514 public double getDouble(String key) throws JSONException {
515 Object object = this.get(key);
517 return object instanceof Number
518 ? ((Number)object).doubleValue()
519 : Double.parseDouble((String)object);
520 } catch (Exception e) {
521 throw new JSONException("JSONObject[" + quote(key) +
522 "] is not a number.");
528 * Get the int value associated with a key.
530 * @param key A key string.
531 * @return The integer value.
532 * @throws JSONException if the key is not found or if the value cannot
533 * be converted to an integer.
535 public int getInt(String key) throws JSONException {
536 Object object = this.get(key);
538 return object instanceof Number
539 ? ((Number)object).intValue()
540 : Integer.parseInt((String)object);
541 } catch (Exception e) {
542 throw new JSONException("JSONObject[" + quote(key) +
549 * Get the JSONArray value associated with a key.
551 * @param key A key string.
552 * @return A JSONArray which is the value.
553 * @throws JSONException if the key is not found or
554 * if the value is not a JSONArray.
556 public JSONArray getJSONArray(String key) throws JSONException {
557 Object object = this.get(key);
558 if (object instanceof JSONArray) {
559 return (JSONArray)object;
561 throw new JSONException("JSONObject[" + quote(key) +
562 "] is not a JSONArray.");
567 * Get the JSONObject value associated with a key.
569 * @param key A key string.
570 * @return A JSONObject which is the value.
571 * @throws JSONException if the key is not found or
572 * if the value is not a JSONObject.
574 public LOGJSONObject getJSONObject(String key) throws JSONException {
575 Object object = this.get(key);
576 if (object instanceof LOGJSONObject) {
577 return (LOGJSONObject)object;
579 throw new JSONException("JSONObject[" + quote(key) +
580 "] is not a JSONObject.");
585 * Get the long value associated with a key.
587 * @param key A key string.
588 * @return The long value.
589 * @throws JSONException if the key is not found or if the value cannot
590 * be converted to a long.
592 public long getLong(String key) throws JSONException {
593 Object object = this.get(key);
595 return object instanceof Number
596 ? ((Number)object).longValue()
597 : Long.parseLong((String)object);
598 } catch (Exception e) {
599 throw new JSONException("JSONObject[" + quote(key) +
606 * Get an array of field names from a JSONObject.
608 * @return An array of field names, or null if there are no names.
610 public static String[] getNames(LOGJSONObject jo) {
611 int length = jo.length();
615 Iterator<String> iterator = jo.keys();
616 String[] names = new String[length];
618 while (iterator.hasNext()) {
619 names[i] = iterator.next();
627 * Get an array of field names from an Object.
629 * @return An array of field names, or null if there are no names.
631 public static String[] getNames(Object object) {
632 if (object == null) {
635 Class<? extends Object> klass = object.getClass();
636 Field[] fields = klass.getFields();
637 int length = fields.length;
641 String[] names = new String[length];
642 for (int i = 0; i < length; i += 1) {
643 names[i] = fields[i].getName();
650 * Get the string associated with a key.
652 * @param key A key string.
653 * @return A string which is the value.
654 * @throws JSONException if there is no string value for the key.
656 public String getString(String key) throws JSONException {
657 Object object = this.get(key);
658 if (object instanceof String) {
659 return (String)object;
661 throw new JSONException("JSONObject[" + quote(key) +
667 * Determine if the JSONObject contains a specific key.
668 * @param key A key string.
669 * @return true if the key exists in the JSONObject.
671 public boolean has(String key) {
672 return this.map.containsKey(key);
677 * Increment a property of a JSONObject. If there is no such property,
678 * create one with a value of 1. If there is such a property, and if
679 * it is an Integer, Long, Double, or Float, then add one to it.
680 * @param key A key string.
682 * @throws JSONException If there is already a property with this name
683 * that is not an Integer, Long, Double, or Float.
685 public LOGJSONObject increment(String key) throws JSONException {
686 Object value = this.opt(key);
689 } else if (value instanceof Integer) {
690 this.put(key, ((Integer)value).intValue() + 1);
691 } else if (value instanceof Long) {
692 this.put(key, ((Long)value).longValue() + 1);
693 } else if (value instanceof Double) {
694 this.put(key, ((Double)value).doubleValue() + 1);
695 } else if (value instanceof Float) {
696 this.put(key, ((Float)value).floatValue() + 1);
698 throw new JSONException("Unable to increment [" + quote(key) + "].");
705 * Determine if the value associated with the key is null or if there is
707 * @param key A key string.
708 * @return true if there is no value associated with the key or if
709 * the value is the JSONObject.NULL object.
711 public boolean isNull(String key) {
712 return LOGJSONObject.NULL.equals(this.opt(key));
717 * Get an enumeration of the keys of the JSONObject.
719 * @return An iterator of the keys.
721 public Iterator<String> keys() {
722 return this.keySet().iterator();
727 * Get a set of keys of the JSONObject.
731 public Set<String> keySet() {
732 return this.map.keySet();
737 * Get the number of keys stored in the JSONObject.
739 * @return The number of keys in the JSONObject.
741 public int length() {
742 return this.map.size();
747 * Produce a JSONArray containing the names of the elements of this
749 * @return A JSONArray containing the key strings, or null if the JSONObject
752 public JSONArray names() {
753 JSONArray ja = new JSONArray();
754 Iterator<String> keys = this.keys();
755 while (keys.hasNext()) {
758 return ja.length() == 0 ? null : ja;
762 * Produce a string from a Number.
763 * @param number A Number
765 * @throws JSONException If n is a non-finite number.
767 public static String numberToString(Number number)
768 throws JSONException {
769 if (number == null) {
770 throw new JSONException("Null pointer");
772 testValidity(number);
774 // Shave off trailing zeros and decimal point, if possible.
776 String string = number.toString();
777 if (string.indexOf('.') > 0 && string.indexOf('e') < 0 &&
778 string.indexOf('E') < 0) {
779 while (string.endsWith("0")) {
780 string = string.substring(0, string.length() - 1);
782 if (string.endsWith(".")) {
783 string = string.substring(0, string.length() - 1);
791 * Get an optional value associated with a key.
792 * @param key A key string.
793 * @return An object which is the value, or null if there is no value.
795 public Object opt(String key) {
796 return key == null ? null : this.map.get(key);
801 * Get an optional boolean associated with a key.
802 * It returns false if there is no such key, or if the value is not
803 * Boolean.TRUE or the String "true".
805 * @param key A key string.
808 public boolean optBoolean(String key) {
809 return this.optBoolean(key, false);
814 * Get an optional boolean associated with a key.
815 * It returns the defaultValue if there is no such key, or if it is not
816 * a Boolean or the String "true" or "false" (case insensitive).
818 * @param key A key string.
819 * @param defaultValue The default.
822 public boolean optBoolean(String key, boolean defaultValue) {
824 return this.getBoolean(key);
825 } catch (Exception e) {
832 * Get an optional double associated with a key,
833 * or NaN if there is no such key or if its value is not a number.
834 * If the value is a string, an attempt will be made to evaluate it as
837 * @param key A string which is the key.
838 * @return An object which is the value.
840 public double optDouble(String key) {
841 return this.optDouble(key, Double.NaN);
846 * Get an optional double associated with a key, or the
847 * defaultValue if there is no such key or if its value is not a number.
848 * If the value is a string, an attempt will be made to evaluate it as
851 * @param key A key string.
852 * @param defaultValue The default.
853 * @return An object which is the value.
855 public double optDouble(String key, double defaultValue) {
857 return this.getDouble(key);
858 } catch (Exception e) {
865 * Get an optional int value associated with a key,
866 * or zero if there is no such key or if the value is not a number.
867 * If the value is a string, an attempt will be made to evaluate it as
870 * @param key A key string.
871 * @return An object which is the value.
873 public int optInt(String key) {
874 return this.optInt(key, 0);
879 * Get an optional int value associated with a key,
880 * or the default if there is no such key or if the value is not a number.
881 * If the value is a string, an attempt will be made to evaluate it as
884 * @param key A key string.
885 * @param defaultValue The default.
886 * @return An object which is the value.
888 public int optInt(String key, int defaultValue) {
890 return this.getInt(key);
891 } catch (Exception e) {
898 * Get an optional JSONArray associated with a key.
899 * It returns null if there is no such key, or if its value is not a
902 * @param key A key string.
903 * @return A JSONArray which is the value.
905 public JSONArray optJSONArray(String key) {
906 Object o = this.opt(key);
907 return o instanceof JSONArray ? (JSONArray)o : null;
912 * Get an optional JSONObject associated with a key.
913 * It returns null if there is no such key, or if its value is not a
916 * @param key A key string.
917 * @return A JSONObject which is the value.
919 public LOGJSONObject optJSONObject(String key) {
920 Object object = this.opt(key);
921 return object instanceof LOGJSONObject ? (LOGJSONObject)object : null;
926 * Get an optional long value associated with a key,
927 * or zero if there is no such key or if the value is not a number.
928 * If the value is a string, an attempt will be made to evaluate it as
931 * @param key A key string.
932 * @return An object which is the value.
934 public long optLong(String key) {
935 return this.optLong(key, 0);
940 * Get an optional long value associated with a key,
941 * or the default if there is no such key or if the value is not a number.
942 * If the value is a string, an attempt will be made to evaluate it as
945 * @param key A key string.
946 * @param defaultValue The default.
947 * @return An object which is the value.
949 public long optLong(String key, long defaultValue) {
951 return this.getLong(key);
952 } catch (Exception e) {
959 * Get an optional string associated with a key.
960 * It returns an empty string if there is no such key. If the value is not
961 * a string and is not null, then it is converted to a string.
963 * @param key A key string.
964 * @return A string which is the value.
966 public String optString(String key) {
967 return this.optString(key, "");
972 * Get an optional string associated with a key.
973 * It returns the defaultValue if there is no such key.
975 * @param key A key string.
976 * @param defaultValue The default.
977 * @return A string which is the value.
979 public String optString(String key, String defaultValue) {
980 Object object = this.opt(key);
981 return NULL.equals(object) ? defaultValue : object.toString();
985 private void populateMap(Object bean) {
986 Class<? extends Object> klass = bean.getClass();
988 // If klass is a System class then set includeSuperClass to false.
990 boolean includeSuperClass = klass.getClassLoader() != null;
992 Method[] methods = includeSuperClass
994 : klass.getDeclaredMethods();
995 for (int i = 0; i < methods.length; i += 1) {
997 Method method = methods[i];
998 if (Modifier.isPublic(method.getModifiers())) {
999 String name = method.getName();
1001 if (name.startsWith("get")) {
1002 if ("getClass".equals(name) ||
1003 "getDeclaringClass".equals(name)) {
1006 key = name.substring(3);
1008 } else if (name.startsWith("is")) {
1009 key = name.substring(2);
1011 if (key.length() > 0 &&
1012 Character.isUpperCase(key.charAt(0)) &&
1013 method.getParameterTypes().length == 0) {
1014 if (key.length() == 1) {
1015 key = key.toLowerCase();
1016 } else if (!Character.isUpperCase(key.charAt(1))) {
1017 key = key.substring(0, 1).toLowerCase() +
1021 Object result = method.invoke(bean, (Object[])null);
1022 if (result != null) {
1023 this.map.put(key, wrap(result));
1027 } catch (Exception ignore) {
1034 * Put a key/boolean pair in the JSONObject.
1036 * @param key A key string.
1037 * @param value A boolean which is the value.
1039 * @throws JSONException If the key is null.
1041 public LOGJSONObject put(String key, boolean value) throws JSONException {
1042 this.put(key, value ? Boolean.TRUE : Boolean.FALSE);
1048 * Put a key/value pair in the JSONObject, where the value will be a
1049 * JSONArray which is produced from a Collection.
1050 * @param key A key string.
1051 * @param value A Collection value.
1053 * @throws JSONException
1055 public LOGJSONObject put(String key, Collection<Object> value) throws JSONException {
1056 this.put(key, new JSONArray(value));
1062 * Put a key/double pair in the JSONObject.
1064 * @param key A key string.
1065 * @param value A double which is the value.
1067 * @throws JSONException If the key is null or if the number is invalid.
1069 public LOGJSONObject put(String key, double value) throws JSONException {
1070 this.put(key, new Double(value));
1076 * Put a key/int pair in the JSONObject.
1078 * @param key A key string.
1079 * @param value An int which is the value.
1081 * @throws JSONException If the key is null.
1083 public LOGJSONObject put(String key, int value) throws JSONException {
1084 this.put(key, new Integer(value));
1090 * Put a key/long pair in the JSONObject.
1092 * @param key A key string.
1093 * @param value A long which is the value.
1095 * @throws JSONException If the key is null.
1097 public LOGJSONObject put(String key, long value) throws JSONException {
1098 this.put(key, new Long(value));
1104 * Put a key/value pair in the JSONObject, where the value will be a
1105 * JSONObject which is produced from a Map.
1106 * @param key A key string.
1107 * @param value A Map value.
1109 * @throws JSONException
1111 public LOGJSONObject put(String key, Map<String, Object> value) throws JSONException {
1112 this.put(key, new LOGJSONObject(value));
1118 * Put a key/value pair in the JSONObject. If the value is null,
1119 * then the key will be removed from the JSONObject if it is present.
1120 * @param key A key string.
1121 * @param value An object which is the value. It should be of one of these
1122 * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, String,
1123 * or the JSONObject.NULL object.
1125 * @throws JSONException If the value is non-finite number
1126 * or if the key is null.
1128 public LOGJSONObject put(String key, Object value) throws JSONException {
1131 throw new JSONException("Null key.");
1133 if (value != null) {
1134 testValidity(value);
1135 pooled = (String)keyPool.get(key);
1136 if (pooled == null) {
1137 if (keyPool.size() >= keyPoolSize) {
1138 keyPool = new LinkedHashMap<String, Object>(keyPoolSize);
1140 keyPool.put(key, key);
1144 this.map.put(key, value);
1153 * Put a key/value pair in the JSONObject, but only if the key and the
1154 * value are both non-null, and only if there is not already a member
1159 * @throws JSONException if the key is a duplicate
1161 public LOGJSONObject putOnce(String key, Object value) throws JSONException {
1162 if (key != null && value != null) {
1163 if (this.opt(key) != null) {
1164 throw new JSONException("Duplicate key \"" + key + "\"");
1166 this.put(key, value);
1173 * Put a key/value pair in the JSONObject, but only if the
1174 * key and the value are both non-null.
1175 * @param key A key string.
1176 * @param value An object which is the value. It should be of one of these
1177 * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, String,
1178 * or the JSONObject.NULL object.
1180 * @throws JSONException If the value is a non-finite number.
1182 public LOGJSONObject putOpt(String key, Object value) throws JSONException {
1183 if (key != null && value != null) {
1184 this.put(key, value);
1191 * Produce a string in double quotes with backslash sequences in all the
1192 * right places. A backslash will be inserted within </, producing <\/,
1193 * allowing JSON text to be delivered in HTML. In JSON text, a string
1194 * cannot contain a control character or an unescaped quote or backslash.
1195 * @param string A String
1196 * @return A String correctly formatted for insertion in a JSON text.
1198 public static String quote(String string) {
1199 StringWriter sw = new StringWriter();
1200 synchronized (sw.getBuffer()) {
1202 return quote(string, sw).toString();
1203 } catch (IOException ignored) {
1204 // will never happen - we are writing to a string writer
1210 public static Writer quote(String string, Writer w) throws IOException {
1211 if (string == null || string.length() == 0) {
1220 int len = string.length();
1223 for (i = 0; i < len; i += 1) {
1225 c = string.charAt(i);
1254 if (c < ' ' || (c >= '\u0080' && c < '\u00a0')
1255 || (c >= '\u2000' && c < '\u2100')) {
1257 hhhh = Integer.toHexString(c);
1258 w.write("0000", 0, 4 - hhhh.length());
1270 * Remove a name and its value, if present.
1271 * @param key The name to be removed.
1272 * @return The value that was associated with the name,
1273 * or null if there was no value.
1275 public Object remove(String key) {
1276 return this.map.remove(key);
1280 * Try to convert a string into a number, boolean, or null. If the string
1281 * can't be converted, return the string.
1282 * @param string A String.
1283 * @return A simple JSON value.
1285 public static Object stringToValue(String string) {
1287 if (string.equals("")) {
1290 if (string.equalsIgnoreCase("true")) {
1291 return Boolean.TRUE;
1293 if (string.equalsIgnoreCase("false")) {
1294 return Boolean.FALSE;
1296 if (string.equalsIgnoreCase("null")) {
1297 return LOGJSONObject.NULL;
1301 * If it might be a number, try converting it.
1302 * If a number cannot be produced, then the value will just
1303 * be a string. Note that the plus and implied string
1304 * conventions are non-standard. A JSON parser may accept
1305 * non-JSON forms as long as it accepts all correct JSON forms.
1308 char b = string.charAt(0);
1309 if ((b >= '0' && b <= '9') || b == '.' || b == '-' || b == '+') {
1311 if (string.indexOf('.') > -1 ||
1312 string.indexOf('e') > -1 || string.indexOf('E') > -1) {
1313 d = Double.valueOf(string);
1314 if (!d.isInfinite() && !d.isNaN()) {
1318 Long myLong = new Long(string);
1319 if (myLong.longValue() == myLong.intValue()) {
1320 return new Integer(myLong.intValue());
1325 } catch (Exception ignore) {
1333 * Throw an exception if the object is a NaN or infinite number.
1334 * @param o The object to test.
1335 * @throws JSONException If o is a non-finite number.
1337 public static void testValidity(Object o) throws JSONException {
1339 if (o instanceof Double) {
1340 if (((Double)o).isInfinite() || ((Double)o).isNaN()) {
1341 throw new JSONException(
1342 "JSON does not allow non-finite numbers.");
1344 } else if (o instanceof Float) {
1345 if (((Float)o).isInfinite() || ((Float)o).isNaN()) {
1346 throw new JSONException(
1347 "JSON does not allow non-finite numbers.");
1355 * Produce a JSONArray containing the values of the members of this
1357 * @param names A JSONArray containing a list of key strings. This
1358 * determines the sequence of the values in the result.
1359 * @return A JSONArray of values.
1360 * @throws JSONException If any of the values are non-finite numbers.
1362 public JSONArray toJSONArray(JSONArray names) throws JSONException {
1363 if (names == null || names.length() == 0) {
1366 JSONArray ja = new JSONArray();
1367 for (int i = 0; i < names.length(); i += 1) {
1368 ja.put(this.opt(names.getString(i)));
1374 * Make a JSON text of this JSONObject. For compactness, no whitespace
1375 * is added. If this would not result in a syntactically correct JSON text,
1376 * then null will be returned instead.
1378 * Warning: This method assumes that the data structure is acyclical.
1380 * @return a printable, displayable, portable, transmittable
1381 * representation of the object, beginning
1382 * with <code>{</code> <small>(left brace)</small> and ending
1383 * with <code>}</code> <small>(right brace)</small>.
1385 public String toString() {
1387 return this.toString(0);
1388 } catch (Exception e) {
1395 * Make a prettyprinted JSON text of this JSONObject.
1397 * Warning: This method assumes that the data structure is acyclical.
1398 * @param indentFactor The number of spaces to add to each level of
1400 * @return a printable, displayable, portable, transmittable
1401 * representation of the object, beginning
1402 * with <code>{</code> <small>(left brace)</small> and ending
1403 * with <code>}</code> <small>(right brace)</small>.
1404 * @throws JSONException If the object contains an invalid number.
1406 public String toString(int indentFactor) throws JSONException {
1407 StringWriter w = new StringWriter();
1408 synchronized (w.getBuffer()) {
1409 return this.write(w, indentFactor, 0).toString();
1414 * Make a JSON text of an Object value. If the object has an
1415 * value.toJSONString() method, then that method will be used to produce
1416 * the JSON text. The method is required to produce a strictly
1417 * conforming text. If the object does not contain a toJSONString
1418 * method (which is the most common case), then a text will be
1419 * produced by other means. If the value is an array or Collection,
1420 * then a JSONArray will be made from it and its toJSONString method
1421 * will be called. If the value is a MAP, then a JSONObject will be made
1422 * from it and its toJSONString method will be called. Otherwise, the
1423 * value's toString method will be called, and the result will be quoted.
1426 * Warning: This method assumes that the data structure is acyclical.
1427 * @param value The value to be serialized.
1428 * @return a printable, displayable, transmittable
1429 * representation of the object, beginning
1430 * with <code>{</code> <small>(left brace)</small> and ending
1431 * with <code>}</code> <small>(right brace)</small>.
1432 * @throws JSONException If the value is or contains an invalid number.
1434 @SuppressWarnings("unchecked")
1435 public static String valueToString(Object value) throws JSONException {
1436 if (value == null || value.equals(null)) {
1439 if (value instanceof JSONString) {
1442 object = ((JSONString)value).toJSONString();
1443 } catch (Exception e) {
1444 throw new JSONException(e);
1446 if (object instanceof String) {
1447 return (String)object;
1449 throw new JSONException("Bad value from toJSONString: " + object);
1451 if (value instanceof Number) {
1452 return numberToString((Number) value);
1454 if (value instanceof Boolean || value instanceof LOGJSONObject ||
1455 value instanceof JSONArray) {
1456 return value.toString();
1458 if (value instanceof Map) {
1459 return new LOGJSONObject((Map<String, Object>)value).toString();
1461 if (value instanceof Collection) {
1462 return new JSONArray((Collection<Object>)value).toString();
1464 if (value.getClass().isArray()) {
1465 return new JSONArray(value).toString();
1467 return quote(value.toString());
1471 * Wrap an object, if necessary. If the object is null, return the NULL
1472 * object. If it is an array or collection, wrap it in a JSONArray. If
1473 * it is a map, wrap it in a JSONObject. If it is a standard property
1474 * (Double, String, et al) then it is already wrapped. Otherwise, if it
1475 * comes from one of the java packages, turn it into a string. And if
1476 * it doesn't, try to wrap it in a JSONObject. If the wrapping fails,
1477 * then null is returned.
1479 * @param object The object to wrap
1480 * @return The wrapped value
1482 @SuppressWarnings("unchecked")
1483 public static Object wrap(Object object) {
1485 if (object == null) {
1488 if (object instanceof LOGJSONObject || object instanceof JSONArray ||
1489 NULL.equals(object) || object instanceof JSONString ||
1490 object instanceof Byte || object instanceof Character ||
1491 object instanceof Short || object instanceof Integer ||
1492 object instanceof Long || object instanceof Boolean ||
1493 object instanceof Float || object instanceof Double ||
1494 object instanceof String) {
1498 if (object instanceof Collection) {
1499 return new JSONArray((Collection<Object>)object);
1501 if (object.getClass().isArray()) {
1502 return new JSONArray(object);
1504 if (object instanceof Map) {
1505 return new LOGJSONObject((Map<String, Object>)object);
1507 Package objectPackage = object.getClass().getPackage();
1508 String objectPackageName = objectPackage != null
1509 ? objectPackage.getName()
1512 objectPackageName.startsWith("java.") ||
1513 objectPackageName.startsWith("javax.") ||
1514 object.getClass().getClassLoader() == null
1516 return object.toString();
1518 return new LOGJSONObject(object);
1519 } catch(Exception exception) {
1526 * Write the contents of the JSONObject as JSON text to a writer.
1527 * For compactness, no whitespace is added.
1529 * Warning: This method assumes that the data structure is acyclical.
1531 * @return The writer.
1532 * @throws JSONException
1534 public Writer write(Writer writer) throws JSONException {
1535 return this.write(writer, 0, 0);
1539 @SuppressWarnings("unchecked")
1540 static final Writer writeValue(Writer writer, Object value,
1541 int indentFactor, int indent) throws JSONException, IOException {
1542 if (value == null || value.equals(null)) {
1543 writer.write("null");
1544 } else if (value instanceof LOGJSONObject) {
1545 ((LOGJSONObject) value).write(writer, indentFactor, indent);
1546 } else if (value instanceof JSONArray) {
1547 ((JSONArray) value).write(writer, indentFactor, indent);
1548 } else if (value instanceof Map) {
1549 new LOGJSONObject((Map<String, Object>) value).write(writer, indentFactor, indent);
1550 } else if (value instanceof Collection) {
1551 new JSONArray((Collection<Object>) value).write(writer, indentFactor,
1553 } else if (value.getClass().isArray()) {
1554 new JSONArray(value).write(writer, indentFactor, indent);
1555 } else if (value instanceof Number) {
1556 writer.write(numberToString((Number) value));
1557 } else if (value instanceof Boolean) {
1558 writer.write(value.toString());
1559 } else if (value instanceof JSONString) {
1562 o = ((JSONString) value).toJSONString();
1563 } catch (Exception e) {
1564 throw new JSONException(e);
1566 writer.write(o != null ? o.toString() : quote(value.toString()));
1568 quote(value.toString(), writer);
1573 static final void indent(Writer writer, int indent) throws IOException {
1574 for (int i = 0; i < indent; i += 1) {
1580 * Write the contents of the JSONObject as JSON text to a writer. For
1581 * compactness, no whitespace is added.
1583 * Warning: This method assumes that the data structure is acyclical.
1585 * @return The writer.
1586 * @throws JSONException
1588 Writer write(Writer writer, int indentFactor, int indent)
1589 throws JSONException {
1591 boolean commanate = false;
1592 final int length = this.length();
1593 Iterator<String> keys = this.keys();
1597 Object key = keys.next();
1598 writer.write(quote(key.toString()));
1600 if (indentFactor > 0) {
1603 writeValue(writer, this.map.get(key), indentFactor, indent);
1604 } else if (length != 0) {
1605 final int newindent = indent + indentFactor;
1606 while (keys.hasNext()) {
1607 Object key = keys.next();
1611 if (indentFactor > 0) {
1614 indent(writer, newindent);
1615 writer.write(quote(key.toString()));
1617 if (indentFactor > 0) {
1620 writeValue(writer, this.map.get(key), indentFactor,
1624 if (indentFactor > 0) {
1627 indent(writer, indent);
1631 } catch (IOException exception) {
1632 throw new JSONException(exception);