blob: c12dc424c0db503ce0a7e3077f2255285658c3f6 [file] [log] [blame]
/*
* 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.servlets.get.impl.util;
import java.util.Iterator;
import java.util.Map;
import javax.json.Json;
import javax.json.JsonArray;
import javax.json.JsonArrayBuilder;
import javax.json.JsonObject;
import javax.json.JsonObjectBuilder;
import javax.json.JsonValue;
public class JsonToText
{
/** Rendering options */
static public class Options {
int indent;
private boolean indentIsPositive;
int initialIndent;
boolean arraysForChildren;
public static final String DEFAULT_CHILDREN_KEY = "__children__";
public static final String DEFAULT_CHILD_NAME_KEY = "__name__";
String childrenKey = DEFAULT_CHILDREN_KEY;
String childNameKey = DEFAULT_CHILD_NAME_KEY;
/** Clients use JSONRenderer.options() to create objects */
private Options() {
}
Options(Options opt) {
this.indent = opt.indent;
this.indentIsPositive = opt.indentIsPositive;
this.initialIndent = opt.initialIndent;
this.arraysForChildren = opt.arraysForChildren;
}
public Options withIndent(int n) {
indent = n;
indentIsPositive = indent > 0;
return this;
}
public Options withInitialIndent(int n) {
initialIndent = n;
return this;
}
public Options withArraysForChildren(boolean b) {
arraysForChildren = b;
return this;
}
public Options withChildNameKey(String key) {
childNameKey = key;
return this;
}
public Options withChildrenKey(String key) {
childrenKey = key;
return this;
}
boolean hasIndent() {
return indentIsPositive;
}
}
/** Return an Options object with default values */
public Options options() {
return new Options();
}
/** Write N spaces to sb for indentation */
private void indent(StringBuilder sb, int howMuch) {
for (int i=0; i < howMuch; i++) {
sb.append(' ');
}
}
/** Quote the supplied string for JSON */
private String quote(String string) {
if (string == null || string.length() == 0) {
return "\"\"";
}
char b;
char c = 0;
int i;
int len = string.length();
StringBuilder sb = new StringBuilder(len + 2);
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").append(t.substring(t.length() - 4));
} else {
sb.append(c);
}
}
}
sb.append('"');
return sb.toString();
}
/** Make a JSON String of an Object value, with rendering options
*/
private String valueToString(JsonValue value, Options opt) {
if (value instanceof JsonObject || value instanceof JsonArray) {
return prettyPrint((JsonArray)value, opt);
}
return value.toString();
}
/** Decide whether o must be skipped and added to a, when rendering a JSONObject */
private boolean skipChildObject(JsonArrayBuilder a, Options opt, String key, Object value) {
if(opt.arraysForChildren && (value instanceof JsonObject)) {
JsonObjectBuilder builder = Json.createObjectBuilder();
builder.add(opt.childNameKey, key);
for (Map.Entry<String, JsonValue> entry : ((JsonObject) value).entrySet()) {
builder.add(entry.getKey(), entry.getValue());
}
a.add(builder);
return true;
}
return false;
}
/**
* Make a prettyprinted JSON text of this JSONObject.
* <p>
* Warning: This method assumes that the data structure is acyclical.
* @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 IllegalArgumentException If the object contains an invalid number.
*/
public String prettyPrint(JsonObject jo, Options opt) {
int n = jo.size();
if (n == 0) {
return "{}";
}
final JsonArrayBuilder children = Json.createArrayBuilder();
Iterator<String> keys = jo.keySet().iterator();
StringBuilder sb = new StringBuilder("{");
int newindent = opt.initialIndent + opt.indent;
String o;
if (n == 1) {
o = keys.next();
final JsonValue v = jo.get(o);
if(!skipChildObject(children, opt, o, v)) {
sb.append(quote(o));
sb.append(": ");
sb.append(valueToString(v, opt));
}
} else {
while (keys.hasNext()) {
o = keys.next();
final JsonValue v = jo.get(o);
if(skipChildObject(children, opt, o, v)) {
continue;
}
if (sb.length() > 1) {
sb.append(",\n");
} else {
sb.append('\n');
}
indent(sb, newindent);
sb.append(quote(o.toString()));
sb.append(": ");
sb.append(valueToString(v,
options().withIndent(opt.indent).withInitialIndent(newindent)));
}
if (sb.length() > 1) {
sb.append('\n');
indent(sb, newindent);
}
}
/** Render children if any were skipped (in "children in arrays" mode) */
JsonArray childrenArray = children.build();
if(childrenArray.size() > 0) {
if (sb.length() > 1) {
sb.append(",\n");
} else {
sb.append('\n');
}
final Options childOpt = new Options(opt);
childOpt.withInitialIndent(childOpt.initialIndent + newindent);
indent(sb, childOpt.initialIndent);
sb.append(quote(opt.childrenKey)).append(":");
sb.append(prettyPrint(childrenArray, childOpt));
}
sb.append('}');
return sb.toString();
}
/** Pretty-print a JSONArray */
public String prettyPrint(JsonArray ja, Options opt) {
int len = ja.size();
if (len == 0) {
return "[]";
}
int i;
StringBuilder sb = new StringBuilder("[");
if (len == 1) {
sb.append(valueToString(ja.get(0), opt));
} else {
final int newindent = opt.initialIndent + opt.indent;
if(opt.hasIndent()) {
sb.append('\n');
}
for (i = 0; i < len; i += 1) {
if (i > 0) {
sb.append(',');
if(opt.hasIndent()) {
sb.append('\n');
}
}
indent(sb, newindent);
sb.append(valueToString(ja.get(i), opt));
}
if(opt.hasIndent()) {
sb.append('\n');
}
indent(sb, opt.initialIndent);
}
sb.append(']');
return sb.toString();
}
}