Java tutorial
/* * Copyright 2002-2006 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.sf.json; import net.sf.ezmorph.MorphUtils; import net.sf.ezmorph.MorpherRegistry; import net.sf.json.regexp.RegexpMatcher; import net.sf.json.regexp.RegexpUtils; import org.apache.commons.lang.ArrayUtils; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; /** * Provides useful methods on java objects. * * @author Andres Almiray <aalmiray@users.sourceforge.net> * @version 4 */ public final class JSONUtils { /** * DOCUMENT ME! */ private static RegexpMatcher FUNCTION_HEADER_MATCHER; /** * DOCUMENT ME! */ private static final String FUNCTION_HEADER_PATTERN = "^function[ ]?\\(.*\\)$"; /** * DOCUMENT ME! */ private static RegexpMatcher FUNCTION_MACTHER; /** * DOCUMENT ME! */ private static RegexpMatcher FUNCTION_PARAMS_MATCHER; /** * DOCUMENT ME! */ private static final String FUNCTION_PARAMS_PATTERN = "^function[ ]?\\((.*?)\\)$"; /** * DOCUMENT ME! */ private static final String FUNCTION_PATTERN = "^function[ ]?\\(.*\\)[ ]?\\{.*\\}$"; /** * DOCUMENT ME! */ private static final MorpherRegistry morpherRegistry = new MorpherRegistry(); static { FUNCTION_HEADER_MATCHER = RegexpUtils.getMatcher(FUNCTION_HEADER_PATTERN); FUNCTION_PARAMS_MATCHER = RegexpUtils.getMatcher(FUNCTION_PARAMS_PATTERN); FUNCTION_MACTHER = RegexpUtils.getMatcher(FUNCTION_PATTERN); // register standard morphers MorphUtils.registerStandardMorphers(morpherRegistry); } /** * Creates a new JSONUtils object. */ private JSONUtils() { super(); } /** * Produce a string from a double. The string "null" will be returned if the * number is not finite. * * @param d A double. * @return A String. */ public static String doubleToString(double d) { if (Double.isInfinite(d) || Double.isNaN(d)) { return "null"; } // Shave off trailing zeros and decimal point, if possible. String s = Double.toString(d); if ((s.indexOf('.') > 0) && (s.indexOf('e') < 0) && (s.indexOf('E') < 0)) { while (s.endsWith("0")) { s = s.substring(0, s.length() - 1); } if (s.endsWith(".")) { s = s.substring(0, s.length() - 1); } } return s; } /** * Returns the number of dimensions suited for a java array. */ public static int[] getDimensions(JSONArray jsonArray) { // short circuit for empty arrays if ((jsonArray == null) || jsonArray.isEmpty()) { return new int[] { 0 }; } List dims = new ArrayList(); JSONUtils.processArrayDimensions(jsonArray, dims, 0); int[] dimensions = new int[dims.size()]; int j = 0; for (Iterator i = dims.iterator(); i.hasNext();) { dimensions[j++] = ((Integer) i.next()).intValue(); } return dimensions; } /** * Returns the params of a function literal. */ public static String getFunctionParams(String function) { return FUNCTION_PARAMS_MATCHER.getGroupIfMatches(function, 1); } /** * DOCUMENT ME! * * @param type DOCUMENT ME! * * @return DOCUMENT ME! */ public static Class getInnerComponentType(Class type) { if (!type.isArray()) { return type; } return getInnerComponentType(type.getComponentType()); } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public static MorpherRegistry getMorpherRegistry() { return morpherRegistry; } /** * Creates a Map with all the properties of the JSONObject. */ public static Map getProperties(JSONObject jsonObject) { Map properties = new HashMap(); for (Iterator keys = jsonObject.keys(); keys.hasNext();) { String key = (String) keys.next(); properties.put(key, getTypeClass(jsonObject.get(key))); } return properties; } /** * Returns the JSON type. */ public static Class getTypeClass(Object obj) { if (isNull(obj)) { return Object.class; } else if (isArray(obj)) { return List.class; } else if (isFunction(obj)) { return JSONFunction.class; } else if (isBoolean(obj)) { return Boolean.class; } else if (isNumber(obj)) { return Double.class; } else if (isString(obj)) { return String.class; } else if (isObject(obj)) { return Object.class; } else { throw new JSONException("Unsupported type"); } } /** * Tests if obj is an array or Collection. */ public static boolean isArray(Object obj) { if ((obj != null) && obj.getClass().isArray()) { return true; } if (obj instanceof Collection) { return true; } return false; } /** * Tests if obj is a Boolean or primitive boolean */ public static boolean isBoolean(Object obj) { if (obj instanceof Boolean) { return true; } if ((obj != null) && (obj.getClass() == Boolean.TYPE)) { return true; } return false; } /** * Tests if obj is javaScript function.<br> * Obj must ba a non-null String and match <nowrap>"^function[ ]?\\(.*\\)[ * ]?\\{.*\\}$"</nowrap> */ public static boolean isFunction(Object obj) { if ((obj != null) && obj instanceof String) { String str = (String) obj; return FUNCTION_MACTHER.matches(str); } if ((obj != null) && obj instanceof JSONFunction) { return true; } return false; } /** * Tests if obj is javaScript function header.<br> * Obj must ba a non-null String and match "^function[ ]?\\(.*\\)$" */ public static boolean isFunctionHeader(Object obj) { if ((obj != null) && obj instanceof String) { String str = (String) obj; return FUNCTION_HEADER_MATCHER.matches(str); } return false; } /** * Tests if the obj is a javaScript null. */ public static boolean isNull(Object obj) { if (obj instanceof JSONObject) { return ((JSONObject) obj).isNullObject(); } return JSONNull.getInstance().equals(obj); } /** * Tests if obj is a primitive number or wrapper.<br> */ public static boolean isNumber(Object obj) { if (((obj != null) && (obj.getClass() == Byte.TYPE)) || ((obj != null) && (obj.getClass() == Short.TYPE)) || ((obj != null) && (obj.getClass() == Integer.TYPE)) || ((obj != null) && (obj.getClass() == Long.TYPE)) || ((obj != null) && (obj.getClass() == Float.TYPE)) || ((obj != null) && (obj.getClass() == Double.TYPE))) { return true; } if ((obj instanceof Byte) || (obj instanceof Short) || (obj instanceof Integer) || (obj instanceof Long) || (obj instanceof Float) || (obj instanceof Double)) { return true; } return false; } /** * Tests if obj is not a boolean, number, string or array. */ public static boolean isObject(Object obj) { return (!isNumber(obj) && !isString(obj) && !isBoolean(obj) && !isArray(obj)) || isNull(obj); } /** * Tests if obj is a String or a char */ public static boolean isString(Object obj) { if (obj instanceof String) { return true; } if (obj instanceof Character) { return true; } if ((obj != null) && (obj.getClass() == Character.TYPE)) { return true; } return false; } /** * Produce a string from a Number. * * @param n A Number * @return A String. * @throws JSONException If n is a non-finite number. */ public static String numberToString(Number n) { if (n == null) { throw new JSONException("Null pointer"); } JSONUtils.testValidity(n); // Shave off trailing zeros and decimal point, if possible. String s = n.toString(); if ((s.indexOf('.') > 0) && (s.indexOf('e') < 0) && (s.indexOf('E') < 0)) { while (s.endsWith("0")) { s = s.substring(0, s.length() - 1); } if (s.endsWith(".")) { s = s.substring(0, s.length() - 1); } } return s; } /** * Produce a string in double quotes with backslash sequences in all the * right places. A backslash will be inserted within </, allowing JSON text * to be delivered in HTML. In JSON text, a string cannot contain a control * character or an unescaped quote or backslash.<br> * <strong>CAUTION:</strong> if <code>string</code> represents a * javascript function, translation of characters will not take place. This * will produce a non-conformant JSON text. * * @param string A String * @return A String correctly formatted for insertion in a JSON text. */ public static String quote(String string) { if (JSONUtils.isFunction(string)) { return string; } if ((string == null) || (string.length() == 0)) { return "\"\""; } char b; char c = 0; int i; int len = string.length(); StringBuffer sb = new StringBuffer(len + 4); String t; sb.append('"'); for (i = 0; i < len; i += 1) { b = c; c = string.charAt(i); switch (c) { case '\\': case '"': sb.append('\\'); sb.append(c); break; case '/': if (b == '<') { sb.append('\\'); } sb.append(c); break; case '\b': sb.append("\\b"); break; case '\t': sb.append("\\t"); break; case '\n': sb.append("\\n"); break; case '\f': sb.append("\\f"); break; case '\r': sb.append("\\r"); break; default: if (c < ' ') { t = "000" + Integer.toHexString(c); sb.append("\\u").append(t.substring(t.length() - 4)); } else { sb.append(c); } } } sb.append('"'); return sb.toString(); } /** * Throw an exception if the object is an NaN or infinite number. * * @param o The object to test. * @throws JSONException If o is a non-finite number. */ public static void testValidity(Object o) { if (o != null) { if (o instanceof Double) { if (((Double) o).isInfinite() || ((Double) o).isNaN()) { throw new JSONException("JSON does not allow non-finite numbers"); } } else if (o instanceof Float) { if (((Float) o).isInfinite() || ((Float) o).isNaN()) { throw new JSONException("JSON does not allow non-finite numbers."); } } } } /** * Converts an array of primitive chars to objects.<br> * <p> * <strong>This method is not in ArrayUtils. (commons-lang 2.1)</strong> * </p> * <p> * This method returns <code>null</code> for a <code>null</code> input * array. * </p> * * @param array a <code>char</code> array * @return a <code>Character</code> array, <code>null</code> if null * array input */ public static Object[] toObject(char[] array) { if (array == null) { return null; } else if (array.length == 0) { return ArrayUtils.EMPTY_CHARACTER_OBJECT_ARRAY; } final Character[] result = new Character[array.length]; for (int i = 0; i < array.length; i++) { result[i] = new Character(array[i]); } return result; } /** * Make a JSON text of an Object value. If the object has an * value.toJSONString() method, then that method will be used to produce the * JSON text. The method is required to produce a strictly conforming text. * If the object does not contain a toJSONString method (which is the most * common case), then a text will be produced by the rules. * <p> * Warning: This method assumes that the data structure is acyclical. * * @param value The value to be serialized. * @return a printable, displayable, transmittable representation of the * object, beginning with <code>{</code> <small>(left brace)</small> * and ending with <code>}</code> <small>(right brace)</small>. * @throws JSONException If the value is or contains an invalid number. */ public static String valueToString(Object value) { if ((value == null) || isNull(value)) { return "null"; } if (value instanceof JSONFunction) { return ((JSONFunction) value).toString(); } if (value instanceof JSONString) { Object o; try { o = ((JSONString) value).toJSONString(); } catch (Exception e) { throw new JSONException(e); } if (o instanceof String) { return (String) o; } throw new JSONException("Bad value from toJSONString: " + o); } if (value instanceof Number) { return numberToString((Number) value); } if (value instanceof Boolean || value instanceof JSONObject || value instanceof JSONArray) { return value.toString(); } return quote(value.toString()); } /** * Make a prettyprinted JSON text of an object value. * <p> * Warning: This method assumes that the data structure is acyclical. * * @param value The value to be serialized. * @param indentFactor The number of spaces to add to each level of * indentation. * @param indent The indentation of the top level. * @return a printable, displayable, transmittable representation of the * object, beginning with <code>{</code> <small>(left brace)</small> * and ending with <code>}</code> <small>(right brace)</small>. * @throws JSONException If the object contains an invalid number. */ public static String valueToString(Object value, int indentFactor, int indent) { if ((value == null) || isNull(value)) { return "null"; } if (value instanceof JSONFunction) { return ((JSONFunction) value).toString(); } try { if (value instanceof JSONString) { Object o = ((JSONString) value).toJSONString(); if (o instanceof String) { return (String) o; } } } catch (Exception e) { /* forget about it */ } if (value instanceof Number) { return numberToString((Number) value); } if (value instanceof Boolean) { return value.toString(); } if (value instanceof JSONObject) { return ((JSONObject) value).toString(indentFactor, indent); } if (value instanceof JSONArray) { return ((JSONArray) value).toString(indentFactor, indent); } return quote(value.toString()); } /** * DOCUMENT ME! * * @param jsonArray DOCUMENT ME! * @param dims DOCUMENT ME! * @param index DOCUMENT ME! */ static void processArrayDimensions(JSONArray jsonArray, List dims, int index) { if (dims.size() <= index) { dims.add(new Integer(jsonArray.length())); } else { int i = ((Integer) dims.get(index)).intValue(); if (jsonArray.length() > i) { dims.set(index, new Integer(jsonArray.length())); } } for (Iterator i = jsonArray.iterator(); i.hasNext();) { Object item = i.next(); if (item instanceof JSONArray) { processArrayDimensions((JSONArray) item, dims, index + 1); } } } }