sling-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From bdelacre...@apache.org
Subject svn commit: r1612850 - in /sling/trunk/bundles/commons/json/src: main/java/org/apache/sling/commons/json/ main/java/org/apache/sling/commons/json/io/ test/java/org/apache/sling/commons/json/io/
Date Wed, 23 Jul 2014 15:24:19 GMT
Author: bdelacretaz
Date: Wed Jul 23 15:24:18 2014
New Revision: 1612850

URL: http://svn.apache.org/r1612850
Log:
SLING-3786 - extract JSON formatting code to JSONRenderer and add some missing tests. All
unit and related integration tests pass

Added:
    sling/trunk/bundles/commons/json/src/main/java/org/apache/sling/commons/json/io/JSONRenderer.java
    sling/trunk/bundles/commons/json/src/test/java/org/apache/sling/commons/json/io/
    sling/trunk/bundles/commons/json/src/test/java/org/apache/sling/commons/json/io/JSONRendererTest.java
Modified:
    sling/trunk/bundles/commons/json/src/main/java/org/apache/sling/commons/json/JSONArray.java
    sling/trunk/bundles/commons/json/src/main/java/org/apache/sling/commons/json/JSONObject.java

Modified: sling/trunk/bundles/commons/json/src/main/java/org/apache/sling/commons/json/JSONArray.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/json/src/main/java/org/apache/sling/commons/json/JSONArray.java?rev=1612850&r1=1612849&r2=1612850&view=diff
==============================================================================
--- sling/trunk/bundles/commons/json/src/main/java/org/apache/sling/commons/json/JSONArray.java
(original)
+++ sling/trunk/bundles/commons/json/src/main/java/org/apache/sling/commons/json/JSONArray.java
Wed Jul 23 15:24:18 2014
@@ -24,12 +24,13 @@ OUT OF OR IN CONNECTION WITH THE SOFTWAR
 SOFTWARE.
 */
 
-import java.io.IOException;
 import java.io.Writer;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Map;
 
+import org.apache.sling.commons.json.io.JSONRenderer;
+
 /**
  * A JSONArray is an ordered sequence of values. Its external text form is a
  * string wrapped in square brackets with commas separating the values. The
@@ -82,6 +83,8 @@ import java.util.Map;
  */
 public class JSONArray {
 
+    /** Default renderer */
+    private static final JSONRenderer renderer = new JSONRenderer();
 
     /**
      * The arrayList where the JSONArray's properties are kept.
@@ -320,24 +323,11 @@ public class JSONArray {
 
 
     /**
-     * Make a string from the contents of this JSONArray. The
-     * <code>separator</code> string is inserted between each element.
-     * Warning: This method assumes that the data structure is acyclical.
-     * @param separator A string that will be inserted between the elements.
-     * @return a string.
-     * @throws JSONException If the array contains an invalid number.
+     * Make a string from the contents of this JSONArray 
+     * using {@link JSONRenderer#join}
      */
     public String join(String separator) throws JSONException {
-        int len = length();
-        StringBuffer sb = new StringBuffer();
-
-        for (int i = 0; i < len; i += 1) {
-            if (i > 0) {
-                sb.append(separator);
-            }
-            sb.append(JSONObject.valueToString(this.myArrayList.get(i)));
-        }
-        return sb.toString();
+        return renderer.join(this, separator);
     }
 
 
@@ -774,25 +764,13 @@ public class JSONArray {
 
 
     /**
-     * Make a JSON text of this JSONArray. For compactness, no
-     * unnecessary whitespace is added. If it is not possible to produce a
-     * syntactically correct JSON text then null will be returned instead. This
-     * could occur if the array contains an invalid number.
-     * <p>
-     * Warning: This method assumes that the data structure is acyclical.
-     *
-     * @return a printable, displayable, transmittable
-     *  representation of the array.
+     * Make a JSON text of this JSONArray using 
+     * {@link JSONRenderer#toString(JSONArray)}
      */
     public String toString() {
-        try {
-            return '[' + join(",") + ']';
-        } catch (Exception e) {
-            return null;
-        }
+        return renderer.toString(this);
     }
 
-
     /**
      * Make a prettyprinted JSON text of this JSONArray.
      * Warning: This method assumes that the data structure is acyclical.
@@ -820,72 +798,15 @@ public class JSONArray {
      * @throws JSONException
      */
     String toString(int indentFactor, int indent) throws JSONException {
-        int len = length();
-        if (len == 0) {
-            return "[]";
-        }
-        int i;
-        StringBuffer sb = new StringBuffer("[");
-        if (len == 1) {
-            sb.append(JSONObject.valueToString(this.myArrayList.get(0),
-                    indentFactor, indent));
-        } else {
-            int newindent = indent + indentFactor;
-            sb.append('\n');
-            for (i = 0; i < len; i += 1) {
-                if (i > 0) {
-                    sb.append(",\n");
-                }
-                for (int j = 0; j < newindent; j += 1) {
-                    sb.append(' ');
-                }
-                sb.append(JSONObject.valueToString(this.myArrayList.get(i),
-                        indentFactor, newindent));
-            }
-            sb.append('\n');
-            for (i = 0; i < indent; i += 1) {
-                sb.append(' ');
-            }
-        }
-        sb.append(']');
-        return sb.toString();
+        return renderer.prettyPrint(this, 
+                renderer.options().withIndent(indentFactor).withInitialIndent(indent));
     }
 
-
     /**
-     * Write the contents of the JSONArray as JSON text to a writer.
-     * For compactness, no whitespace is added.
-     * <p>
-     * Warning: This method assumes that the data structure is acyclical.
-     *
-     * @return The writer.
-     * @throws JSONException
+     * Write the contents of the JSONObject as JSON text to a writer
+     * using {@link JSONRenderer#write(JSONArray)}
      */
     public Writer write(Writer writer) throws JSONException {
-        try {
-            boolean b = false;
-            int     len = length();
-
-            writer.write('[');
-
-            for (int i = 0; i < len; i += 1) {
-                if (b) {
-                    writer.write(',');
-                }
-                Object v = this.myArrayList.get(i);
-                if (v instanceof JSONObject) {
-                    ((JSONObject)v).write(writer);
-                } else if (v instanceof JSONArray) {
-                    ((JSONArray)v).write(writer);
-                } else {
-                    writer.write(JSONObject.valueToString(v));
-                }
-                b = true;
-            }
-            writer.write(']');
-            return writer;
-        } catch (IOException e) {
-           throw new JSONException(e);
-        }
+        return renderer.write(writer, this);
     }
 }
\ No newline at end of file

Modified: sling/trunk/bundles/commons/json/src/main/java/org/apache/sling/commons/json/JSONObject.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/json/src/main/java/org/apache/sling/commons/json/JSONObject.java?rev=1612850&r1=1612849&r2=1612850&view=diff
==============================================================================
--- sling/trunk/bundles/commons/json/src/main/java/org/apache/sling/commons/json/JSONObject.java
(original)
+++ sling/trunk/bundles/commons/json/src/main/java/org/apache/sling/commons/json/JSONObject.java
Wed Jul 23 15:24:18 2014
@@ -24,7 +24,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWAR
 SOFTWARE.
 */
 
-import java.io.IOException;
 import java.io.Writer;
 import java.lang.reflect.Field;
 import java.util.Collection;
@@ -32,6 +31,8 @@ import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.Map;
 
+import org.apache.sling.commons.json.io.JSONRenderer;
+
 /**
  * A JSONObject is an unordered collection of name/value pairs. Its
  * external form is a string wrapped in curly braces with colons between the
@@ -86,6 +87,11 @@ import java.util.Map;
  * @version 2
  */
 public class JSONObject {
+    
+    /** A renderer with default settings, used to avoid having to create
+     *  one for simple rendering operations
+     */
+    private static final JSONRenderer renderer = new JSONRenderer();
 
     /**
      * JSONObject.NULL is equivalent to the value that JavaScript calls null,
@@ -334,31 +340,12 @@ public class JSONObject {
 
 
     /**
-     * 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.
+     * Produce a string from a double using {@link JSONRenderer#doubleToString}
      */
     static public 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;
+        return renderer.doubleToString(d);
     }
 
-
     /**
      * Get the value object associated with a key.
      *
@@ -564,25 +551,8 @@ public class JSONObject {
      * @return A String.
      * @throws JSONException If n is a non-finite number.
      */
-    static public String numberToString(Number n)
-            throws JSONException {
-        if (n == null) {
-            throw new JSONException("Null pointer");
-        }
-        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;
+    static public String numberToString(Number n) throws JSONException {
+        return renderer.numberToString(n);
     }
 
 
@@ -919,60 +889,7 @@ public class JSONObject {
      * @return  A String correctly formatted for insertion in a JSON text.
      */
     public static String quote(String 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 < ' ' || (c >= '\u0080' && c < '\u00a0') ||
-                               (c >= '\u2000' && c < '\u2100')) {
-                    t = "000" + Integer.toHexString(c);
-                    sb.append("\\u" + t.substring(t.length() - 4));
-                } else {
-                    sb.append(c);
-                }
-            }
-        }
-        sb.append('"');
-        return sb.toString();
+        return renderer.quote(string);
     }
 
     /**
@@ -992,22 +909,9 @@ public class JSONObject {
      * @throws JSONException If o is a non-finite number.
      */
     static void testValidity(Object o) throws JSONException {
-        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.");
-                }
-            }
-        }
+        renderer.testNumberValidity(o);
     }
 
-
     /**
      * Produce a JSONArray containing the values of the members of this
      * JSONObject.
@@ -1028,39 +932,12 @@ public class JSONObject {
     }
 
     /**
-     * Make a JSON text of this JSONObject. For compactness, no whitespace
-     * is added. If this would not result in a syntactically correct JSON text,
-     * then null will be returned instead.
-     * <p>
-     * Warning: This method assumes that the data structure is acyclical.
-     *
-     * @return a printable, displayable, portable, transmittable
-     *  representation of the object, beginning
-     *  with <code>{</code>&nbsp;<small>(left brace)</small>
and ending
-     *  with <code>}</code>&nbsp;<small>(right brace)</small>.
+     * Make a JSON text of this JSONObject using {@link JSONRenderer#toString}
      */
     public String toString() {
-        try {
-            Iterator<String>     keys = keys();
-            StringBuffer sb = new StringBuffer("{");
-
-            while (keys.hasNext()) {
-                if (sb.length() > 1) {
-                    sb.append(',');
-                }
-                String o = keys.next();
-                sb.append(quote(o));
-                sb.append(':');
-                sb.append(valueToString(this.myHashMap.get(o)));
-            }
-            sb.append('}');
-            return sb.toString();
-        } catch (Exception e) {
-            return null;
-        }
+        return renderer.toString(this);
     }
 
-
     /**
      * Make a prettyprinted JSON text of this JSONObject.
      * <p>
@@ -1079,59 +956,12 @@ public class JSONObject {
 
 
     /**
-     * Make a prettyprinted JSON text of this JSONObject.
-     * <p>
-     * Warning: This method assumes that the data structure is acyclical.
-     * @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>&nbsp;<small>(left brace)</small>
and ending
-     *  with <code>}</code>&nbsp;<small>(right brace)</small>.
-     * @throws JSONException If the object contains an invalid number.
+     * Make a prettyprinted JSON text of this JSONObject using
+     * {@link JSONRenderer#prettyPrint}
      */
-    String toString(int indentFactor, int indent) throws JSONException {
-        int          i;
-        int          n = length();
-        if (n == 0) {
-            return "{}";
-        }
-        Iterator<String>     keys = keys();
-        StringBuffer sb = new StringBuffer("{");
-        int          newindent = indent + indentFactor;
-        String       o;
-        if (n == 1) {
-            o = keys.next();
-            sb.append(quote(o));
-            sb.append(": ");
-            sb.append(valueToString(this.myHashMap.get(o), indentFactor,
-                    indent));
-        } else {
-            while (keys.hasNext()) {
-                o = keys.next();
-                if (sb.length() > 1) {
-                    sb.append(",\n");
-                } else {
-                    sb.append('\n');
-                }
-                for (i = 0; i < newindent; i += 1) {
-                    sb.append(' ');
-                }
-                sb.append(quote(o.toString()));
-                sb.append(": ");
-                sb.append(valueToString(this.myHashMap.get(o), indentFactor,
-                        newindent));
-            }
-            if (sb.length() > 1) {
-                sb.append('\n');
-                for (i = 0; i < indent; i += 1) {
-                    sb.append(' ');
-                }
-            }
-        }
-        sb.append('}');
-        return sb.toString();
+    String toString(int indentFactor, int initialIndent) throws JSONException {
+        return renderer.prettyPrint(this, 
+                renderer.options().withIndent(indentFactor).withInitialIndent(initialIndent));
     }
 
 
@@ -1152,113 +982,24 @@ public class JSONObject {
      * @throws JSONException If the value is or contains an invalid number.
      */
     public static String valueToString(Object value) throws JSONException {
-        if (value == null || value.equals(null)) {
-            return "null";
-        }
-        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());
+        return renderer.valueToString(value);
     }
 
-
     /**
-     * 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>&nbsp;<small>(left brace)</small>
and ending
-     *  with <code>}</code>&nbsp;<small>(right brace)</small>.
-     * @throws JSONException If the object contains an invalid number.
+     * Make a prettyprinted JSON text of an object value
+     * using {@link JSONRenderer#valueToString}
      */
-     static String valueToString(Object value, int indentFactor, int indent)
+     static String valueToString(Object value, int indentFactor, int initialIndent)
             throws JSONException {
-        if (value == null || value.equals(null)) {
-            return "null";
-        }
-        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());
+         return renderer.valueToString(value, 
+                 renderer.options().withIndent(indentFactor).withInitialIndent(initialIndent));
     }
 
-
      /**
-      * Write the contents of the JSONObject as JSON text to a writer.
-      * For compactness, no whitespace is added.
-      * <p>
-      * Warning: This method assumes that the data structure is acyclical.
-      *
-      * @return The writer.
-      * @throws JSONException
+      * Write the contents of the JSONObject as JSON text to a writer
+      * using {@link JSONRenderer#write(JSONObject)}
       */
      public Writer write(Writer writer) throws JSONException {
-        try {
-            boolean  b = false;
-            Iterator<String> keys = keys();
-            writer.write('{');
-
-            while (keys.hasNext()) {
-                if (b) {
-                    writer.write(',');
-                }
-                String k = keys.next();
-                writer.write(quote(k));
-                writer.write(':');
-                Object v = this.myHashMap.get(k);
-                if (v instanceof JSONObject) {
-                    ((JSONObject)v).write(writer);
-                } else if (v instanceof JSONArray) {
-                    ((JSONArray)v).write(writer);
-                } else {
-                    writer.write(valueToString(v));
-                }
-                b = true;
-            }
-            writer.write('}');
-            return writer;
-        } catch (IOException e) {
-            throw new JSONException(e);
-        }
+         return renderer.write(writer, this);
      }
 }
\ No newline at end of file

Added: sling/trunk/bundles/commons/json/src/main/java/org/apache/sling/commons/json/io/JSONRenderer.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/json/src/main/java/org/apache/sling/commons/json/io/JSONRenderer.java?rev=1612850&view=auto
==============================================================================
--- sling/trunk/bundles/commons/json/src/main/java/org/apache/sling/commons/json/io/JSONRenderer.java
(added)
+++ sling/trunk/bundles/commons/json/src/main/java/org/apache/sling/commons/json/io/JSONRenderer.java
Wed Jul 23 15:24:18 2014
@@ -0,0 +1,513 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.sling.commons.json.io;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.Iterator;
+
+import org.apache.sling.commons.json.JSONArray;
+import org.apache.sling.commons.json.JSONException;
+import org.apache.sling.commons.json.JSONObject;
+import org.apache.sling.commons.json.JSONString;
+
+/** Various JSON-to-String primitives, used by other classes
+ *  when outputting/formatting JSON.
+ *  
+ *  Streaming variants of some methods are provided.
+ *  The existing code in this module is often not streaming, but
+ *  we should write newer code using streams, as much as
+ *  possible.
+ */
+public class JSONRenderer {
+    
+    /** Rendering options */
+    static public class Options {
+        int indent;
+        int initialIndent;
+        
+        /** Clients use JSONRenderer.options() to create objects */
+        private Options() {
+        }
+        
+        public Options withIndent(int n) {
+            indent = n;
+            return this;
+        }
+        
+        public Options withInitialIndent(int n) {
+            initialIndent = n;
+            return this;
+        }
+    }
+
+    /** Return an Options object with default values */
+    public Options options() {
+        return new Options();
+    }
+
+    /** Render the supplied JSONObject to a String, in
+     *  the simplest possible way.
+     */
+    public String toString(JSONObject jo) {
+        try {
+            final Iterator<String> keys = jo.keys();
+            final StringBuffer sb = new StringBuffer("{");
+
+            while (keys.hasNext()) {
+                if (sb.length() > 1) {
+                    sb.append(',');
+                }
+                String o = keys.next();
+                sb.append(quote(o));
+                sb.append(':');
+                sb.append(valueToString(jo.get(o)));
+            }
+            sb.append('}');
+            return sb.toString();
+        } catch (Exception e) {
+            return null;
+        }
+    }
+    
+    /** Make a JSON text of the supplied JSONArray. For compactness, no
+     *  unnecessary whitespace is added. If it is not possible to produce a
+     *  syntactically correct JSON text then null will be returned instead. This
+     *  could occur if the array contains an invalid number.
+     *  <p>Warning: This method assumes that the data structure is acyclical.
+     *
+     *  @return a printable, displayable, transmittable
+     *  representation of the array.
+     */
+    public String toString(JSONArray ja) {
+        try {
+            return '[' + join(ja,",") + ']';
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    /** Quote the supplied string for JSON */
+    public String quote(String string) {
+        final StringWriter sw = new StringWriter();
+        try {
+            quote(sw, string);
+        } catch(IOException ioex) {
+            throw new RuntimeException("IOException in quote()", ioex);
+        }
+        return sw.toString();
+    }
+    
+    /** Quote the supplied string for JSON, to the supplied Writer */
+    public void quote(Writer w, String string) throws IOException {
+        if (string == null || string.length() == 0) {
+            w.write("\"\"");
+            return;
+        }
+    
+        char         b;
+        char         c = 0;
+        int          i;
+        int          len = string.length();
+        String       t;
+    
+        w.write('"');
+        for (i = 0; i < len; i += 1) {
+            b = c;
+            c = string.charAt(i);
+            switch (c) {
+            case '\\':
+            case '"':
+                w.write('\\');
+                w.write(c);
+                break;
+            case '/':
+                if (b == '<') {
+                    w.write('\\');
+                }
+                w.write(c);
+                break;
+            case '\b':
+                w.write("\\b");
+                break;
+            case '\t':
+                w.write("\\t");
+                break;
+            case '\n':
+                w.write("\\n");
+                break;
+            case '\f':
+                w.write("\\f");
+                break;
+            case '\r':
+                w.write("\\r");
+                break;
+            default:
+                if (c < ' ' || (c >= '\u0080' && c < '\u00a0') ||
+                               (c >= '\u2000' && c < '\u2100')) {
+                    t = "000" + Integer.toHexString(c);
+                    w.write("\\u" + t.substring(t.length() - 4));
+                } else {
+                    w.write(c);
+                }
+            }
+        }
+        w.write('"');
+    }
+    
+    /**
+     * 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>&nbsp;<small>(left brace)</small>
and ending
+     *  with <code>}</code>&nbsp;<small>(right brace)</small>.
+     * @throws JSONException If the value is or contains an invalid number.
+     */
+    public String valueToString(Object value) throws JSONException {
+        // TODO call the other valueToString instead
+        if (value == null || value.equals(null)) {
+            return "null";
+        }
+        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 JSON String of an Object value, with rendering options
+     * <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>&nbsp;<small>(left brace)</small>
and ending
+     *  with <code>}</code>&nbsp;<small>(right brace)</small>.
+     * @throws JSONException If the object contains an invalid number.
+     */
+    public String valueToString(Object value, Options opt) throws JSONException {
+        if (value == null || value.equals(null)) {
+            return "null";
+        }
+        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 prettyPrint((JSONObject)value, opt);
+        }
+        if (value instanceof JSONArray) {
+            return prettyPrint((JSONArray)value, opt);
+        }
+        return quote(value.toString());
+        
+    }
+    
+    /**
+     * Produce a string from a Number.
+     * @param  n A Number
+     * @return A String.
+     * @throws JSONException If n is a non-finite number.
+     */
+    public String numberToString(Number n)
+            throws JSONException {
+        if (n == null) {
+            throw new JSONException("Null pointer");
+        }
+        testNumberValidity(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;
+    }
+    
+    /**
+     * Make a prettyprinted JSON text of this JSONObject.
+     * <p>
+     * Warning: This method assumes that the data structure is acyclical.
+     * @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>&nbsp;<small>(left brace)</small>
and ending
+     *  with <code>}</code>&nbsp;<small>(right brace)</small>.
+     * @throws JSONException If the object contains an invalid number.
+     */
+    public String prettyPrint(JSONObject jo, Options opt) throws JSONException {
+        int i;
+        int n = jo.length();
+        if (n == 0) {
+            return "{}";
+        }
+        Iterator<String> keys = jo.keys();
+        StringBuffer sb = new StringBuffer("{");
+        int newindent = opt.initialIndent + opt.indent;
+        String       o;
+        if (n == 1) {
+            o = keys.next();
+            sb.append(quote(o));
+            sb.append(": ");
+            sb.append(valueToString(jo.get(o), opt));
+        } else {
+            while (keys.hasNext()) {
+                o = keys.next();
+                if (sb.length() > 1) {
+                    sb.append(",\n");
+                } else {
+                    sb.append('\n');
+                }
+                for (i = 0; i < newindent; i += 1) {
+                    sb.append(' ');
+                }
+                sb.append(quote(o.toString()));
+                sb.append(": ");
+                sb.append(valueToString(jo.get(o), 
+                        options().withIndent(opt.indent).withInitialIndent(newindent)));
+            }
+            if (sb.length() > 1) {
+                sb.append('\n');
+                for (i = 0; i < opt.indent; i += 1) {
+                    sb.append(' ');
+                }
+            }
+        }
+        sb.append('}');
+        return sb.toString();
+    }
+
+    public String prettyPrint(JSONArray ja, Options opt) throws JSONException {
+        int len = ja.length();
+        if (len == 0) {
+            return "[]";
+        }
+        int i;
+        StringBuffer sb = new StringBuffer("[");
+        if (len == 1) {
+            sb.append(valueToString(ja.get(0), opt));
+        } else {
+            final int newindent = opt.initialIndent + opt.indent;
+            sb.append('\n');
+            for (i = 0; i < len; i += 1) {
+                if (i > 0) {
+                    sb.append(",\n");
+                }
+                for (int j = 0; j < newindent; j += 1) {
+                    sb.append(' ');
+                }
+                sb.append(valueToString(ja.get(i), opt));
+            }
+            sb.append('\n');
+            for (i = 0; i < opt.initialIndent; i += 1) {
+                sb.append(' ');
+            }
+        }
+        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 void testNumberValidity(Object o) throws JSONException {
+        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.");
+                }
+            }
+        }
+    }
+    
+    /**
+     * Make a string from the contents of this JSONArray. The
+     * <code>separator</code> string is inserted between each element.
+     * Warning: This method assumes that the data structure is acyclical.
+     * @param separator A string that will be inserted between the elements.
+     * @return a string.
+     * @throws JSONException If the array contains an invalid number.
+     */
+    public String join(JSONArray ja, String separator) throws JSONException {
+        final int len = ja.length();
+        StringBuffer sb = new StringBuffer();
+
+        for (int i = 0; i < len; i += 1) {
+            if (i > 0) {
+                sb.append(separator);
+            }
+            sb.append(JSONObject.valueToString(ja.get(i)));
+        }
+        return sb.toString();
+    }
+    
+    /**
+     * Write the contents of the supplied JSONObject as JSON text to a writer.
+     * For compactness, no whitespace is added.
+     * <p>
+     * Warning: This method assumes that the data structure is acyclical.
+     *
+     * @return The writer.
+     * @throws JSONException
+     */
+    public Writer write(Writer writer, JSONObject jo) throws JSONException {
+       try {
+           boolean  b = false;
+           Iterator<String> keys = jo.keys();
+           writer.write('{');
+
+           while (keys.hasNext()) {
+               if (b) {
+                   writer.write(',');
+               }
+               String k = keys.next();
+               writer.write(quote(k));
+               writer.write(':');
+               final Object v = jo.get(k);
+               if (v instanceof JSONObject) {
+                   ((JSONObject)v).write(writer);
+               } else if (v instanceof JSONArray) {
+                   ((JSONArray)v).write(writer);
+               } else {
+                   writer.write(valueToString(v));
+               }
+               b = true;
+           }
+           writer.write('}');
+           return writer;
+       } catch (IOException e) {
+           throw new JSONException(e);
+       }
+    }
+    
+    /**
+     * Write the contents of the supplied JSONArray as JSON text to a writer.
+     * For compactness, no whitespace is added.
+     * <p>
+     * Warning: This method assumes that the data structure is acyclical.
+     *
+     * @return The writer.
+     * @throws JSONException
+     */
+    public Writer write(Writer writer, JSONArray ja) throws JSONException {
+        try {
+            boolean b = false;
+            int len = ja.length();
+
+            writer.write('[');
+
+            for (int i = 0; i < len; i += 1) {
+                if (b) {
+                    writer.write(',');
+                }
+                final Object v = ja.get(i);
+                if (v instanceof JSONObject) {
+                    ((JSONObject)v).write(writer);
+                } else if (v instanceof JSONArray) {
+                    ((JSONArray)v).write(writer);
+                } else {
+                    writer.write(JSONObject.valueToString(v));
+                }
+                b = true;
+            }
+            writer.write(']');
+            return writer;
+        } catch (IOException e) {
+           throw new JSONException(e);
+        }
+    }
+    
+    /**
+     * 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 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;
+    }
+}
\ No newline at end of file

Added: sling/trunk/bundles/commons/json/src/test/java/org/apache/sling/commons/json/io/JSONRendererTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/json/src/test/java/org/apache/sling/commons/json/io/JSONRendererTest.java?rev=1612850&view=auto
==============================================================================
--- sling/trunk/bundles/commons/json/src/test/java/org/apache/sling/commons/json/io/JSONRendererTest.java
(added)
+++ sling/trunk/bundles/commons/json/src/test/java/org/apache/sling/commons/json/io/JSONRendererTest.java
Wed Jul 23 15:24:18 2014
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.sling.commons.json.io;
+
+import static org.junit.Assert.assertEquals;
+
+import org.apache.sling.commons.json.JSONArray;
+import org.apache.sling.commons.json.JSONException;
+import org.junit.Test;
+
+
+/** Most of the JSONRenderer code is tested indirectly
+ *  via existing JSONObject and JSONArray tests - this
+ *  tests what's left */
+public class JSONRendererTest {
+    private final JSONRenderer renderer = new JSONRenderer();
+    
+    @Test
+    public void testA() {
+        assertEquals("12.34", renderer.doubleToString(12.34));
+    }
+    
+    @Test
+    public void testB() {
+        assertEquals("123", renderer.doubleToString(123));
+    }
+    
+    @Test
+    public void testC() {
+        assertEquals("null", renderer.doubleToString(Double.POSITIVE_INFINITY));
+    }
+    
+    @Test
+    public void testD() {
+        assertEquals("null", renderer.doubleToString(Double.NEGATIVE_INFINITY));
+    }
+    
+    @Test
+    public void testE() {
+        assertEquals("null", renderer.doubleToString(Double.NaN));
+    }
+    
+    @Test(expected=JSONException.class)
+    public void testF() throws JSONException {
+        renderer.numberToString(null);
+    }
+    
+    @Test
+    public void testEmptyJSONArray() throws JSONException {
+        assertEquals("[]", renderer.prettyPrint(new JSONArray(), renderer.options()));
+    }
+    
+    @Test
+    public void testSingleJSONArray() throws JSONException {
+        final JSONArray ja = new JSONArray();
+        ja.put(42);
+        assertEquals("[42]", renderer.prettyPrint(ja, renderer.options()));
+    }
+    
+}
\ No newline at end of file



Mime
View raw message