/* This file belongs to the Servoy development and deployment environment, Copyright (C) 1997-2010 Servoy BV This program is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program; if not, see http://www.gnu.org/licenses or write to the Free Software Foundation,Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 */ package com.servoy.j2db.util.serialize; import java.util.Iterator; import org.jabsorb.serializer.AbstractSerializer; import org.jabsorb.serializer.MarshallException; import org.jabsorb.serializer.ObjectMatch; import org.jabsorb.serializer.SerializerState; import org.jabsorb.serializer.UnmarshallException; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.mozilla.javascript.IdScriptableObject; import org.mozilla.javascript.NativeArray; import org.mozilla.javascript.NativeFunction; import org.mozilla.javascript.NativeObject; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.Undefined; import com.servoy.j2db.scripting.JSMap; import com.servoy.j2db.util.Utils; /** * Rhino NativeObject JSON serializer * * @author gboros */ public class NativeObjectSerializer extends AbstractSerializer { private static final long serialVersionUID = 1L; private static final String PROPERTY_MARK = "_"; //$NON-NLS-1$ private static Class[] _serializableClasses = new Class[] { NativeObject.class, NativeObject[].class, NativeArray.class }; private static Class[] _JSONClasses = new Class[] { JSONObject.class, JSONArray.class }; public NativeObjectSerializer() { } public Class[] getJSONClasses() { return _JSONClasses; } public Class[] getSerializableClasses() { return _serializableClasses; } public Object marshall(SerializerState state, Object parent, Object o) throws MarshallException { if (!(o instanceof NativeObject || o instanceof NativeArray)) { throw new MarshallException("cannot marshall NativeObject using class " + o.getClass()); //$NON-NLS-1$ } if (o instanceof NativeArray) { NativeArray nativeArray = (NativeArray)o; JSONArray jsonArray = new JSONArray(); long length = nativeArray.getLength(); for (int i = 0; i < length; i++) { Object elem = nativeArray.get(i, nativeArray); jsonArray.put(ser.marshall(state, o, elem == Scriptable.NOT_FOUND ? null : elem, new Integer(i))); } return jsonArray; } // else NativeObject IdScriptableObject no = (IdScriptableObject)o; JSONObject obj = new JSONObject(); Object[] noIDs = no.getIds(); String propertyKey; Object propertyValue; for (Object element : noIDs) { // id can be Integer or String if (element instanceof Integer) { propertyKey = ((Integer)element).toString(); propertyValue = no.get(((Integer)element).intValue(), no); } else if (element instanceof String) { propertyKey = (String)element; propertyValue = no.get((String)element, no); } else { // should not happen continue; } if (propertyValue instanceof NativeFunction) { continue; } propertyValue = JSONSerializerWrapper.wrapToJSON(propertyValue); try { obj.put(propertyKey, ser.marshall(state, o, propertyValue, propertyKey)); } catch (JSONException e) { throw new MarshallException("JSONException: " + e.getMessage(), e); //$NON-NLS-1$ } } return obj; } @SuppressWarnings("nls") public ObjectMatch tryUnmarshall(SerializerState state, Class clazz, Object json) throws UnmarshallException { if (json instanceof JSONArray || json instanceof JSONObject) { return ObjectMatch.OKAY; } throw new UnmarshallException("not a NativeObject"); } @SuppressWarnings("nls") public Object unmarshall(SerializerState state, Class clazz, Object json) throws UnmarshallException { if (json instanceof JSONArray) { return unmarshallJSONArray(state, (JSONArray)json); } // else JSONObject return unmarshallJSONObject(state, clazz, (JSONObject)json); } public Object unmarshallJSONObject(SerializerState state, Class clazz, JSONObject jso) throws UnmarshallException { boolean hasJavaClass = jso.has("javaClass"); // legacy, remove prefixes JSMap<Object, Object> no = new JSMap<Object, Object>(NativeArray.class.equals(clazz) ? "Array" : null); for (String jsonKey : Utils.iterate((Iterator<String>)jso.keys())) { String jsonProperty = jsonKey; if (hasJavaClass) { // legacy if (jsonKey.equals("javaClass")) continue; if (jsonKey.startsWith(PROPERTY_MARK)) { jsonProperty = jsonKey.substring(PROPERTY_MARK.length()); } } Object jsonValue; try { jsonValue = jso.get(jsonKey); } catch (JSONException e) { throw new UnmarshallException("JSONException: " + e.getMessage(), e); //$NON-NLS-1$ } jsonValue = getUnmarshalled(state, jsonValue); try { int jsonIntKey = Integer.parseInt(jsonProperty); no.put(Integer.valueOf(jsonIntKey), jsonValue); } catch (NumberFormatException ex) { // property key is a string no.put(jsonProperty, jsonValue); } } return no; } public Object unmarshallJSONArray(SerializerState state, JSONArray jso) throws UnmarshallException { int length = jso.length(); Object[] array = new Object[length]; for (int i = 0; i < length; i++) { Object jsonValue; try { jsonValue = jso.get(i); } catch (JSONException e) { throw new UnmarshallException("JSONException: " + e.getMessage(), e); //$NON-NLS-1$ } array[i] = getUnmarshalled(state, jsonValue); } return array; } private Object getUnmarshalled(SerializerState state, Object jsonValue) throws UnmarshallException { Object unmarshalled; if (jsonValue instanceof JSONObject) { JSONObject jsonObjectValue = (JSONObject)jsonValue; Class< ? > clazz = null; if (jsonObjectValue.has("javaClass")) //$NON-NLS-1$ { String classHint; try { classHint = jsonObjectValue.getString("javaClass"); //$NON-NLS-1$ } catch (JSONException e) { throw new UnmarshallException("JSONException: " + e.getMessage(), e); //$NON-NLS-1$ } try { if (Undefined.class.getName().equals(classHint)) { return Undefined.instance; } else { clazz = Class.forName(classHint); } } catch (ClassNotFoundException ex) { throw new UnmarshallException("cannot find class for " + classHint); //$NON-NLS-1$ } } unmarshalled = ser.unmarshall(state, clazz, jsonValue); } else if (jsonValue instanceof JSONArray) { unmarshalled = unmarshallJSONArray(state, (JSONArray)jsonValue); } else { unmarshalled = jsonValue; } return JSONSerializerWrapper.unwrapFromJSON(unmarshalled); } }