/** * Copyright 2009 Google Inc. * * Licensed 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 net.appjet.oui; import org.mozilla.javascript.{Context,Scriptable,ScriptableObject}; import org.json.{JSONStringer,JSONObject,JSONArray}; object FastJSON { def stringify(rhinoObj: Scriptable): String = { return FastJSONStringify.stringify(rhinoObj); } def parse(exctx: ExecutionContext, source: String): Scriptable = { return (new FastJSONParser(exctx)).parse(source); } } //---------------------------------------------------------------- // FastJSONStringify //---------------------------------------------------------------- object FastJSONStringify { def stringify(rhinoObj: Scriptable): String = { val stringer = new JSONStringer(); stringerizeScriptable(stringer, rhinoObj); return stringer.toString(); } private def stringerize(s: JSONStringer, v: Object) { if (v == Context.getUndefinedValue) { return; } v match { case (o:Scriptable) => stringerizeScriptable(s, o); case (o:Number) => { val d = o.doubleValue; if (d.toLong.toDouble == d) { s.value(d.toLong); } else { s.value(o); } } case o => s.value(o); } } private def stringerizeScriptable(stringer: JSONStringer, rhinoObj: Scriptable) { if (rhinoObj.getClassName() == "Array") { stringerizeArray(stringer, rhinoObj); } else { stringerizeObj(stringer, rhinoObj); } } private def stringerizeObj(stringer: JSONStringer, rhinoObj: Scriptable) { stringer.`object`(); for (id <- rhinoObj.getIds()) { val k = id.toString(); var v:Object = null; id match { case (s:String) => { v = rhinoObj.get(s, rhinoObj); } case (n:Number) => { v = rhinoObj.get(n.intValue, rhinoObj); } case _ => {} } if (v != null && v != Scriptable.NOT_FOUND && v != Context.getUndefinedValue) { stringer.key(k); stringerize(stringer, v); } } stringer.endObject(); } private def stringerizeArray(stringer: JSONStringer, rhinoArray: Scriptable) { stringer.`array`(); val ids:Array[Object] = rhinoArray.getIds(); var x = 0; for (i <- 0 until ids.length) { // we ignore string keys on js arrays. crockford's "offical" // json library does this as well. if (ids(i).isInstanceOf[Number]) { val id:Int = ids(i).asInstanceOf[Number].intValue; while (x < id) { stringer.value(null); x += 1; } val v:Object = rhinoArray.get(id, rhinoArray); stringerize(stringer, v); x += 1; } } stringer.endArray(); } } //---------------------------------------------------------------- // FastJSONParse //---------------------------------------------------------------- class FastJSONParser(val ctx:ExecutionContext) { def parse(source: String): Scriptable = { if (source(0) == '[') { jsonToRhino(new JSONArray(source)).asInstanceOf[Scriptable]; } else { jsonToRhino(new JSONObject(source)).asInstanceOf[Scriptable]; } } private def newObj(): Scriptable = { Context.getCurrentContext().newObject(ctx.runner.globalScope); } private def newArray(): Scriptable = { Context.getCurrentContext().newArray(ctx.runner.globalScope, 0); } private def jsonToRhino(json: Object): Object = { json match { case (o:JSONArray) => jsonArrayToRhino(o); case (o:JSONObject) => jsonObjectToRhino(o); case o if (o == JSONObject.NULL) => null; case o => o; } } private def jsonArrayToRhino(json: JSONArray): Scriptable = { val o:Scriptable = newArray(); for (i <- 0 until json.length()) { o.put(i, o, jsonToRhino(json.get(i))); } return o; } private def jsonObjectToRhino(json: JSONObject): Scriptable = { val o:Scriptable = newObj(); val names:Array[String] = JSONObject.getNames(json); if (names != null) { for (n <- names) { val i = try { Some(n.toInt); } catch { case (e:NumberFormatException) => None }; if (i.isDefined) { o.put(i.get, o, jsonToRhino(json.get(n))); } else { o.put(n, o, jsonToRhino(json.get(n))); } } } return o; } }