001: /******************************************************************************
002: * LZReturnObjectJSON.java
003: * ****************************************************************************/package org.openlaszlo.remote;
004:
005: import java.io.*;
006: import java.lang.reflect.*;
007: import java.util.*;
008: import java.lang.reflect.*;
009: import javax.servlet.http.*;
010: import org.openlaszlo.iv.flash.util.*;
011: import org.openlaszlo.iv.flash.api.action.*;
012: import org.openlaszlo.iv.flash.api.*;
013: import org.openlaszlo.utils.*;
014: import org.openlaszlo.xml.internal.*;
015: import org.openlaszlo.sc.ScriptCompiler;
016: import org.apache.log4j.*;
017: import org.apache.commons.beanutils.PropertyUtils;
018:
019: /**
020: * Utility class to create JSON based on a server object.
021: */
022: public class LZReturnObjectJSON {
023: public static Logger mLogger = Logger
024: .getLogger(LZReturnObjectJSON.class);
025: public static final int RETTYPE_POJO = 0;
026: public static final int RETTYPE_JAVA_BEAN = 1;
027:
028: StringBuffer body;
029: int mObjRetType;
030:
031: public LZReturnObjectJSON(String objectReturnType) {
032: body = new StringBuffer();
033: if (objectReturnType == null) {
034: mObjRetType = RETTYPE_POJO;
035: } else if ("javabean".equals(objectReturnType)) {
036: mObjRetType = RETTYPE_JAVA_BEAN;
037: } else {
038: mObjRetType = RETTYPE_POJO;
039: }
040: }
041:
042: void pushInteger(int i) {
043: mLogger.debug("pushInteger");
044: body.append(i);
045: }
046:
047: void pushFloat(float f) {
048: mLogger.debug("pushFloat");
049: body.append(f);
050: }
051:
052: void pushString(String s) {
053: mLogger.debug("pushString");
054: body.append(ScriptCompiler.quote(s));
055: }
056:
057: void pushDouble(double d) {
058: mLogger.debug("pushDouble");
059: body.append(d);
060: }
061:
062: void pushBoolean(boolean b) {
063: mLogger.debug("pushBoolean");
064: body.append(b ? "true" : "false");
065: }
066:
067: void pushArray(Object object) {
068: mLogger.debug("pushArray");
069: body.append("[");
070: int length = Array.getLength(object);
071: for (int i = 0; i < length; i++) {
072: if (i > 0) {
073: body.append(",");
074: }
075: createReturnValue(Array.get(object, i));
076: }
077: body.append("]");
078: }
079:
080: void pushList(Object object) {
081: mLogger.debug("pushList");
082: body.append("[");
083: List list = (List) object;
084: int length = list.size();
085: for (int i = 0; i < length; i++) {
086: if (i > 0) {
087: body.append(",");
088: }
089: createReturnValue(list.get(i));
090: }
091: body.append("]");
092: }
093:
094: void pushNull() {
095: body.append("null");
096: }
097:
098: void pushObject(Object object) {
099: Class cl = object.getClass();
100: String classname = cl.getName();
101:
102: //------------------------------------------------------------
103: // {class: classname, key1: val1, key2: val2, ...}
104: //------------------------------------------------------------
105: // varname.class = classname
106: body.append("{");
107: body.append("class: " + ScriptCompiler.quote(classname));
108:
109: if (mObjRetType == RETTYPE_JAVA_BEAN) {
110: pushObjectJavaBean(object);
111: } else {
112: pushObjectPOJO(object);
113: }
114:
115: body.append("}");
116:
117: }
118:
119: /**
120: * Create JSON for an instance
121: */
122: void pushObjectPOJO(Object object) {
123: Class cl = object.getClass();
124: Field[] fields = cl.getFields();
125: for (int i = 0; i < fields.length; i++) {
126: if (!Modifier.isPublic(fields[i].getModifiers()))
127: continue;
128:
129: String fieldName = fields[i].getName();
130: Object value;
131: try {
132: value = fields[i].get(object);
133: } catch (IllegalAccessException e) {
134: mLogger.error("IllegalAccessException", e);
135: continue;
136: }
137: if (mLogger.isDebugEnabled()) {
138: mLogger.debug("add field name " + fieldName + ", "
139: + (value != null ? value.getClass() : null));
140: }
141: body.append(",");
142: body.append(ScriptCompiler.quote(fieldName) + ": ");
143: createReturnValue(value);
144: }
145: }
146:
147: /**
148: * Create JSON for an object that conforms to JavaBean spec.
149: */
150: void pushObjectJavaBean(Object object) {
151: //------------------------------------------------------------
152: // Just get the fields from the objects and add it to this object
153: Map beanProps = null;
154: try {
155: //Use jakarta-commons beanutils to inspect the object
156: beanProps = PropertyUtils.describe(object);
157: } catch (IllegalAccessException e) {
158: mLogger.error("IllegalAccessException", e);
159: } catch (InvocationTargetException e) {
160: mLogger.error("InvocationTargetException", e);
161: } catch (NoSuchMethodException e) {
162: mLogger.error("NoSuchMethodException", e);
163: }
164:
165: if (beanProps != null) {
166: Set keys = beanProps.keySet();
167: Iterator iter = keys.iterator();
168: while (iter.hasNext()) {
169: String fieldName = (String) iter.next();
170: //Don't add the class property as it is already set by the method
171: if (!"class".equals(fieldName)) {
172: Object value = beanProps.get(fieldName);
173: if (mLogger.isDebugEnabled()) {
174: mLogger.debug("add field name "
175: + fieldName
176: + ", "
177: + ((value != null) ? value.getClass()
178: : null));
179: }
180: body.append(", ");
181: body.append(ScriptCompiler.quote(fieldName) + ": ");
182: createReturnValue(value);
183:
184: }
185: }
186: }
187: }
188:
189: void pushMap(Map map) {
190:
191: Iterator iter = map.keySet().iterator();
192: int i = 0;
193:
194: body.append("{");
195: while (iter.hasNext()) {
196: String key = (String) iter.next();
197: if (i++ > 0) {
198: body.append(", ");
199: }
200:
201: body.append(ScriptCompiler.quote(key) + ": ");
202: createReturnValue(map.get(key));
203: }
204: body.append("}");
205:
206: }
207:
208: /**
209: * Recurse through this function to create return value
210: */
211: void createReturnValue(Object object) {
212: mLogger.debug("createReturnValue");
213: if (object == null) {
214: pushNull();
215: return;
216: }
217:
218: Class cl = object.getClass();
219: if (cl.isArray()) {
220: pushArray(object);
221: } else if (List.class.isInstance(object)) {
222: pushList(object);
223: } else if (Map.class.isInstance(object)) {
224: pushMap((Map) object);
225: } else if (cl == Integer.class) {
226: pushInteger(((Integer) object).intValue());
227: } else if (cl == Long.class) {
228: //------------------------------------------------------------
229: // From: http://developer.irt.org/script/1031.htm
230: //
231: // In JavaScript all numbers are floating-point numbers.
232: //
233: // JavaScript uses the standard 8 byte IEEE floating-point numeric
234: // format, which means the range is from:
235: //
236: // +/- 1.7976931348623157x10^308 - very large, and +/- 5x10^-324 -
237: // very small.
238: //
239: // As JavaScript uses floating-point numbers the accuracy is only
240: // assured for integers between: -9,007,199,254,740,992 (-2^53) and
241: // 9,007,199,254,740,992 (2^53)
242: //
243: // All the above from "JavaScript The Definitive Guide" - O'Reilly.
244: //
245: //------------------------------------------------------------
246: // Java long:
247: // 8 bytes signed (two's complement). Ranges from
248: // -9,223,372,036,854,775,808 to +9,223,372,036,854,775,807.
249: //------------------------------------------------------------
250:
251: // possible rounding inaccuracy
252: pushInteger(((Long) object).intValue());
253:
254: } else if (cl == Short.class) {
255: pushInteger(((Short) object).intValue());
256: } else if (cl == Byte.class) {
257: // push as number for now
258: pushInteger(((Byte) object).intValue());
259: } else if (cl == Character.class) {
260: pushString(((Character) object).toString());
261: } else if (cl == Float.class) {
262: pushFloat(((Float) object).floatValue());
263: } else if (cl == Double.class) {
264: pushDouble(((Double) object).doubleValue());
265: } else if (cl == Boolean.class) {
266: pushBoolean(((Boolean) object).booleanValue());
267: } else if (cl == String.class) {
268: pushString((String) object);
269: } else {
270: pushObject(object);
271: }
272: }
273:
274: /**
275: *
276: */
277: public String createObjectProgram(Object object) {
278: mLogger.debug("createObjectProgram(" + object + ")");
279: createReturnValue(object);
280: return body.toString();
281: }
282:
283: /**
284: * @param objectReturnType One of 'pojo' (returns public member values) or
285: * 'javabean' (returns members that have associated getters). Will default
286: * to 'pojo'.
287: */
288: public static byte[] createObject(Object object,
289: String objectReturnType) throws IOException {
290: mLogger.debug("createObject(" + object + ")");
291:
292: try {
293: String buf = new LZReturnObjectJSON(objectReturnType)
294: .createObjectProgram(object);
295: mLogger.debug("LZReturnObject:");
296: mLogger.debug(buf);
297: return buf.getBytes("UTF-8");
298: } catch (IOException e) {
299: mLogger.error("io error creating object SWF: "
300: + e.getMessage());
301: throw e;
302: }
303: }
304:
305: }
|