001: package org.vraptor.remote.json;
002:
003: import java.lang.reflect.Array;
004: import java.lang.reflect.InvocationTargetException;
005: import java.lang.reflect.Method;
006: import java.text.CharacterIterator;
007: import java.text.StringCharacterIterator;
008: import java.util.Calendar;
009: import java.util.Date;
010: import java.util.Iterator;
011: import java.util.Map;
012: import java.util.Map.Entry;
013:
014: import org.apache.log4j.Logger;
015: import org.vraptor.reflection.ReflectionUtil;
016:
017: /**
018: *
019: * Based on initial code from StringTree:
020: * http://www.stringtree.org/downloads/JSONWriter.java
021: *
022: * Main differences: unit tested, names refactored, java 5 wraps, java 5
023: * enhanced fors, cyclic dependecies problemas avoided and maximum deep checks.
024: *
025: * @author Paulo Silveira and StringTree
026: *
027: *
028: */
029: public class JSONWriter {
030:
031: private static final Logger LOG = Logger
032: .getLogger(JSONWriter.class);
033:
034: private final StringBuilder serialized;
035:
036: private final int maximumDepth;
037:
038: private int depth = 0;
039:
040: public JSONWriter(int maximum) {
041: if (maximum <= 0) {
042: throw new IllegalArgumentException(
043: "serialization depth must be > 0");
044: }
045: this .maximumDepth = maximum;
046: this .serialized = new StringBuilder();
047: }
048:
049: public CharSequence write(Object object) {
050: delegateToProperSerializeMethod(object);
051: return serialized;
052: }
053:
054: private void delegateToProperSerializeMethod(Object object) {
055: if (this .depth > this .maximumDepth) {
056: if (LOG.isDebugEnabled()) {
057: LOG.debug("not writing object " + object
058: + ", maximum deepth reached " + maximumDepth);
059: }
060: addAsString("{}");
061: return;
062: }
063: this .depth++;
064:
065: if (object == null)
066: addAsString("null");
067: else if (object instanceof Class)
068: addAsStringScapingCharacters(object);
069: else if (object instanceof Number)
070: addAsString(object);
071: else if (object instanceof Boolean)
072: addAsString(object);
073: else if (object instanceof String)
074: addAsStringScapingCharacters(object);
075: else if (object instanceof Character)
076: addAsStringScapingCharacters(object);
077: else if (object instanceof Map)
078: serializeMap((Map) object);
079: else if (object.getClass().isArray())
080: serializeArray(object);
081: else if (object instanceof Iterable)
082: serializeIterable(((Iterable) object));
083: else if (object instanceof Calendar)
084: addAsString(((Calendar) object).getTimeInMillis());
085: else if (object instanceof Date)
086: addAsString(((Date) object).getTime());
087: else
088: serializeBean(object);
089:
090: this .depth--;
091: }
092:
093: private void serializeBean(Object object) {
094: addAsString("{");
095: try {
096:
097: Map<String, Method> methods = ReflectionUtil
098: .getGetters(object.getClass());
099: for (Entry<String, Method> entry : methods.entrySet()) {
100: Method accessor = entry.getValue();
101: Object value = accessor.invoke(object, (Object[]) null);
102: writeProperty(entry.getKey(), value);
103: addCharacter(',');
104: }
105: if (!methods.isEmpty()) {
106: serialized.deleteCharAt(serialized.length() - 1);
107: }
108: } catch (IllegalAccessException e) {
109: throw new RuntimeException("Cannot render object " + object
110: + " as JSON", e);
111: } catch (InvocationTargetException e) {
112: throw new RuntimeException("Cannot render object " + object
113: + " as JSON", e);
114: }
115:
116: addAsString("}");
117:
118: }
119:
120: private void writeProperty(String name, Object value) {
121: addCharacter('"');
122: addAsString(name);
123: addAsString("\":");
124: delegateToProperSerializeMethod(value);
125: }
126:
127: private void serializeMap(Map map) {
128: addAsString("{");
129: Iterator it = map.keySet().iterator();
130: while (it.hasNext()) {
131: Object key = it.next();
132: delegateToProperSerializeMethod(key);
133: addAsString(":");
134: delegateToProperSerializeMethod(map.get(key));
135: if (it.hasNext())
136: addAsString(",");
137: }
138: addAsString("}");
139: }
140:
141: private void serializeIterable(Iterable iterable) {
142: addAsString("[");
143: Iterator it = iterable.iterator();
144: while (it.hasNext()) {
145: delegateToProperSerializeMethod(it.next());
146: if (it.hasNext())
147: addAsString(",");
148: }
149: addAsString("]");
150: }
151:
152: private void serializeArray(Object object) {
153: addAsString("[");
154: int length = Array.getLength(object);
155: for (int i = 0; i < length; ++i) {
156: delegateToProperSerializeMethod(Array.get(object, i));
157: if (i < length - 1)
158: addCharacter(',');
159: }
160: addAsString("]");
161: }
162:
163: private void addAsStringScapingCharacters(Object obj) {
164: addCharacter('"');
165: CharacterIterator it = new StringCharacterIterator(obj
166: .toString());
167: for (char c = it.first(); c != CharacterIterator.DONE; c = it
168: .next()) {
169: if (c == '"')
170: addAsString("\\\"");
171: else if (c == '\\')
172: addAsString("\\\\");
173: else if (c == '/')
174: addAsString("\\/");
175: else if (c == '\b')
176: addAsString("\\b");
177: else if (c == '\f')
178: addAsString("\\f");
179: else if (c == '\n')
180: addAsString("\\n");
181: else if (c == '\r')
182: addAsString("\\r");
183: else if (c == '\t')
184: addAsString("\\t");
185: else {
186: addCharacter(c);
187: }
188: }
189: addCharacter('"');
190: }
191:
192: private void addAsString(Object obj) {
193: serialized.append(obj);
194: }
195:
196: private void addCharacter(char c) {
197: serialized.append(c);
198: }
199: }
|