From ede84e683eb0672a5c71cc760078c6124ce59d4d Mon Sep 17 00:00:00 2001 From: Pinaki Poddar Date: Tue, 21 May 2013 18:47:15 +0000 Subject: [PATCH] refactor json-only classes git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@1484901 13f79535-47bb-0310-9956-ffa450edef68 --- .../java/org/apache/openjpa/json/JSON.java | 54 ++++ .../org/apache/openjpa/json/JSONObject.java | 241 ++++++++++++++++++ .../persistence/jest/JSONObjectFormatter.java | 2 + 3 files changed, 297 insertions(+) create mode 100644 openjpa-jest/src/main/java/org/apache/openjpa/json/JSON.java create mode 100644 openjpa-jest/src/main/java/org/apache/openjpa/json/JSONObject.java diff --git a/openjpa-jest/src/main/java/org/apache/openjpa/json/JSON.java b/openjpa-jest/src/main/java/org/apache/openjpa/json/JSON.java new file mode 100644 index 000000000..0d4004084 --- /dev/null +++ b/openjpa-jest/src/main/java/org/apache/openjpa/json/JSON.java @@ -0,0 +1,54 @@ +/* + * 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.openjpa.json; + +/** + * A generic interface for a JSON encoded instance. + * + * @author Pinaki Poddar + * + */ +public interface JSON { + /** + * Render into a string buffer. + * + * @param level level at which this instance is being rendered + * @return a mutable buffer + */ + public StringBuilder asString(int level); + + public static final char FIELD_SEPARATOR = ','; + public static final char MEMBER_SEPARATOR = ','; + public static final char VALUE_SEPARATOR = ':'; + public static final char IOR_SEPARTOR = '-'; + public static final char QUOTE = '"'; + public static final char SPACE = ' '; + public static final char OBJECT_START = '{'; + public static final char OBJECT_END = '}'; + public static final char ARRAY_START = '['; + public static final char ARRAY_END = ']'; + + public static final String NEWLINE = "\r\n"; + public static final String NULL_LITERAL = "null"; + public static final String REF_MARKER = "$ref"; + public static final String ID_MARKER = "$id"; + public static final String ARRAY_EMPTY = "[]"; + +} diff --git a/openjpa-jest/src/main/java/org/apache/openjpa/json/JSONObject.java b/openjpa-jest/src/main/java/org/apache/openjpa/json/JSONObject.java new file mode 100644 index 000000000..3c852165c --- /dev/null +++ b/openjpa-jest/src/main/java/org/apache/openjpa/json/JSONObject.java @@ -0,0 +1,241 @@ +/* + * 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.openjpa.json; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * A JSON instance for persistence. + *
+ * Persistent instances have a persistent identity that extends beyond the process lifetime unlike other common + * identity such as {@linkplain System#identityHashCode(Object) identity hash code} for a Java instance in a JVM. + *
+ * A JSONObject instance must need such a persistent identity. + * + * @author Pinaki Poddar + * + */ +public class JSONObject implements JSON { + private final String _type; + private final String _id; + private final boolean _ref; + private final Map _values; + + public JSONObject(String type, Object id, boolean ref) { + _type = type; + _id = id.toString(); + _ref = ref; + _values = new LinkedHashMap(); + } + + public void set(String key, Object value) { + _values.put(key, value); + } + + public void write(PrintWriter writer) { + writer.println(toString()); + } + public String toString() { + return asString(0).toString(); + } + + public StringBuilder asString(int indent) { + StringBuilder buf = new StringBuilder().append(OBJECT_START); + buf.append(encodeField(_ref ? REF_MARKER : ID_MARKER, ior(), 0)); + if (_ref) { + return buf.append(OBJECT_END); + } + StringBuilder tab = newIndent(indent+1); + for (Map.Entry e : _values.entrySet()) { + buf.append(FIELD_SEPARATOR).append(NEWLINE); + buf.append(tab).append(encodeField(e.getKey(), e.getValue(), indent+1)); + } + buf.append(NEWLINE) + .append(newIndent(indent)) + .append(OBJECT_END); + return buf; + } + + /** + * Encoding a JSON field is a quoted field name, followed by a :, followed by a value (which itself can be JSON) + * @param field + * @param value + * @param indent + * @return + */ + private static StringBuilder encodeField(String field, Object value, int indent) { + return new StringBuilder() + .append(quoteFieldName(field)) + .append(VALUE_SEPARATOR) + .append(quoteFieldValue(value, indent)); + } + + private static StringBuilder newIndent(int indent) { + char[] tabs = new char[indent*4]; + Arrays.fill(tabs, SPACE); + return new StringBuilder().append(tabs); + } + + + StringBuilder ior() { + return new StringBuilder(_type).append('-').append(_id); + } + + private static StringBuilder quoteFieldName(String s) { + return new StringBuilder().append(QUOTE).append(s).append(QUOTE); + } + + /** + * Creates a StringBuilder for the given value. + * If the value is null, outputs null without quote + * If the value is Number, outputs the value without quote + * If the value is JSON, outputs the string rendition of value + * Otherwise quoted value + * @param o + * @return + */ + private static StringBuilder quoteFieldValue(Object o, int indent) { + if (o == null) return new StringBuilder(NULL_LITERAL); + if (o instanceof Number) return new StringBuilder(o.toString()); + if (o instanceof JSON) return ((JSON)o).asString(indent); + return quoted(o.toString()); + } + + private static StringBuilder quoted(Object o) { + if (o == null) return new StringBuilder(NULL_LITERAL); + return new StringBuilder().append(QUOTE).append(o.toString()).append(QUOTE); + } + + /** + * An array of objects. Members can be JSON too. + * + * @author Pinaki Poddar + * + */ + public static class Array implements JSON { + private List _members = new ArrayList(); + + public void add(Object o) { + _members.add(o); + } + + public String toString() { + return asString(0).toString(); + } + + public StringBuilder asString(int indent) { + StringBuilder buf = new StringBuilder().append(ARRAY_START); + StringBuilder tab = JSONObject.newIndent(indent+1); + for (Object o : _members) { + if (buf.length() > 1) buf.append(MEMBER_SEPARATOR); + buf.append(NEWLINE).append(tab); + if (o instanceof JSON) + buf.append(((JSON)o).asString(indent+1)); + else + buf.append(o); + } + buf.append(NEWLINE) + .append(JSONObject.newIndent(indent)) + .append(ARRAY_END); + + return buf; + } + } + + /** + * A map whose key or value can be JSON. + * A map is encoded as JSON as an array of entries. Each entry is a key value pair separated with : + * + * @author Pinaki Poddar + * + */ + public static class KVMap implements JSON { + private Map _entries = new LinkedHashMap(); + + public void put(Object k, Object v) { + _entries.put(k,v); + } + + public String toString() { + return asString(0).toString(); + } + + public StringBuilder asString(int indent) { + StringBuilder buf = new StringBuilder().append(ARRAY_START); + StringBuilder tab = JSONObject.newIndent(indent+1); + for (Map.Entry e : _entries.entrySet()) { + if (buf.length() > 1) buf.append(MEMBER_SEPARATOR); + buf.append(NEWLINE).append(tab); + Object key = e.getKey(); + if (key instanceof JSON) { + buf.append(((JSON)key).asString(indent+1)); + } else { + buf.append(key); + } + buf.append(VALUE_SEPARATOR); + Object value = e.getValue(); + if (value instanceof JSON) { + buf.append(((JSON)value).asString(indent+2)); + } else { + buf.append(value); + } + + } + buf.append(NEWLINE) + .append(JSONObject.newIndent(indent)) + .append(ARRAY_END); + return buf; + } + } + + public static void main(String[] args) throws Exception { + JSONObject o = new JSONObject("Person", 1234, false); + JSONObject r = new JSONObject("Person", 1234, true); + JSONObject f = new JSONObject("Person", 2345, false); + Array a = new Array(); + a.add(f); + a.add(3456); + a.add(null); + a.add(r); + a.add(null); + KVMap map = new KVMap(); + map.put("k1", r); + map.put("k2", f); + map.put("k3", null); + map.put("k4", 3456); + map.put(null, 6789); + + f.set("name", "Mary"); + f.set("age", 30); + f.set("friend", r); + o.set("name", "John"); + o.set("age", 20); + o.set("friend", f); + o.set("friends", a); + o.set("map", map); + + System.err.println(o); + } +} diff --git a/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/JSONObjectFormatter.java b/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/JSONObjectFormatter.java index 880428ec8..828c40b63 100644 --- a/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/JSONObjectFormatter.java +++ b/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/JSONObjectFormatter.java @@ -38,6 +38,8 @@ import java.util.Set; import javax.persistence.metamodel.Attribute; import javax.persistence.metamodel.Metamodel; +import org.apache.openjpa.json.JSON; +import org.apache.openjpa.json.JSONObject; import org.apache.openjpa.kernel.OpenJPAStateManager; import org.apache.openjpa.kernel.StoreContext; import org.apache.openjpa.meta.FieldMetaData;