001: /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
002: *
003: * ***** BEGIN LICENSE BLOCK *****
004: * Version: MPL 1.1/GPL 2.0
005: *
006: * The contents of this file are subject to the Mozilla Public License Version
007: * 1.1 (the "License"); you may not use this file except in compliance with
008: * the License. You may obtain a copy of the License at
009: * http://www.mozilla.org/MPL/
010: *
011: * Software distributed under the License is distributed on an "AS IS" basis,
012: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
013: * for the specific language governing rights and limitations under the
014: * License.
015: *
016: * The Original Code is Rhino serialization code, released
017: * Sept. 25, 2001.
018: *
019: * The Initial Developer of the Original Code is
020: * Norris Boyd.
021: * Portions created by the Initial Developer are Copyright (C) 2001
022: * the Initial Developer. All Rights Reserved.
023: *
024: * Contributor(s):
025: * Norris Boyd
026: * Attila Szegedi
027: *
028: * Alternatively, the contents of this file may be used under the terms of
029: * the GNU General Public License Version 2 or later (the "GPL"), in which
030: * case the provisions of the GPL are applicable instead of those above. If
031: * you wish to allow use of your version of this file only under the terms of
032: * the GPL and not to allow others to use your version of this file under the
033: * MPL, indicate your decision by deleting the provisions above and replacing
034: * them with the notice and other provisions required by the GPL. If you do
035: * not delete the provisions above, a recipient may use your version of this
036: * file under either the MPL or the GPL.
037: *
038: * ***** END LICENSE BLOCK ***** */
039:
040: package org.mozilla.javascript.serialize;
041:
042: import java.util.Hashtable;
043: import java.util.StringTokenizer;
044: import java.io.*;
045:
046: import org.mozilla.javascript.*;
047:
048: /**
049: * Class ScriptableOutputStream is an ObjectOutputStream used
050: * to serialize JavaScript objects and functions. Note that
051: * compiled functions currently cannot be serialized, only
052: * interpreted functions. The top-level scope containing the
053: * object is not written out, but is instead replaced with
054: * another top-level object when the ScriptableInputStream
055: * reads in this object. Also, object corresponding to names
056: * added to the exclude list are not written out but instead
057: * are looked up during deserialization. This approach avoids
058: * the creation of duplicate copies of standard objects
059: * during deserialization.
060: *
061: * @author Norris Boyd
062: */
063:
064: // API class
065: public class ScriptableOutputStream extends ObjectOutputStream {
066:
067: /**
068: * ScriptableOutputStream constructor.
069: * Creates a ScriptableOutputStream for use in serializing
070: * JavaScript objects. Calls excludeStandardObjectNames.
071: *
072: * @param out the OutputStream to write to.
073: * @param scope the scope containing the object.
074: */
075: public ScriptableOutputStream(OutputStream out, Scriptable scope)
076: throws IOException {
077: super (out);
078: this .scope = scope;
079: table = new Hashtable(31);
080: table.put(scope, "");
081: enableReplaceObject(true);
082: excludeStandardObjectNames();
083: }
084:
085: /**
086: * Adds a qualified name to the list of object to be excluded from
087: * serialization. Names excluded from serialization are looked up
088: * in the new scope and replaced upon deserialization.
089: * @param name a fully qualified name (of the form "a.b.c", where
090: * "a" must be a property of the top-level object). The object
091: * need not exist, in which case the name is ignored.
092: * @throws IllegalArgumentException if the object is not a
093: * {@link Scriptable}.
094: */
095: public void addOptionalExcludedName(String name) {
096: Object obj = lookupQualifiedName(scope, name);
097: if (obj != null && obj != UniqueTag.NOT_FOUND) {
098: if (!(obj instanceof Scriptable)) {
099: throw new IllegalArgumentException(
100: "Object for excluded name " + name
101: + " is not a Scriptable, it is "
102: + obj.getClass().getName());
103: }
104: table.put(obj, name);
105: }
106: }
107:
108: /**
109: * Adds a qualified name to the list of object to be excluded from
110: * serialization. Names excluded from serialization are looked up
111: * in the new scope and replaced upon deserialization.
112: * @param name a fully qualified name (of the form "a.b.c", where
113: * "a" must be a property of the top-level object)
114: * @throws IllegalArgumentException if the object is not found or is not
115: * a {@link Scriptable}.
116: */
117: public void addExcludedName(String name) {
118: Object obj = lookupQualifiedName(scope, name);
119: if (!(obj instanceof Scriptable)) {
120: throw new IllegalArgumentException(
121: "Object for excluded name " + name + " not found.");
122: }
123: table.put(obj, name);
124: }
125:
126: /**
127: * Returns true if the name is excluded from serialization.
128: */
129: public boolean hasExcludedName(String name) {
130: return table.get(name) != null;
131: }
132:
133: /**
134: * Removes a name from the list of names to exclude.
135: */
136: public void removeExcludedName(String name) {
137: table.remove(name);
138: }
139:
140: /**
141: * Adds the names of the standard objects and their
142: * prototypes to the list of excluded names.
143: */
144: public void excludeStandardObjectNames() {
145: String[] names = { "Object",
146: "Object.prototype",
147: "Function",
148: "Function.prototype",
149: "String",
150: "String.prototype",
151: "Math", // no Math.prototype
152: "Array", "Array.prototype", "Error", "Error.prototype",
153: "Number", "Number.prototype", "Date", "Date.prototype",
154: "RegExp", "RegExp.prototype", "Script",
155: "Script.prototype", "Continuation",
156: "Continuation.prototype", };
157: for (int i = 0; i < names.length; i++) {
158: addExcludedName(names[i]);
159: }
160:
161: String[] optionalNames = { "XML", "XML.prototype", "XMLList",
162: "XMLList.prototype", };
163: for (int i = 0; i < optionalNames.length; i++) {
164: addOptionalExcludedName(optionalNames[i]);
165: }
166: }
167:
168: static Object lookupQualifiedName(Scriptable scope,
169: String qualifiedName) {
170: StringTokenizer st = new StringTokenizer(qualifiedName, ".");
171: Object result = scope;
172: while (st.hasMoreTokens()) {
173: String s = st.nextToken();
174: result = ScriptableObject.getProperty((Scriptable) result,
175: s);
176: if (result == null || !(result instanceof Scriptable))
177: break;
178: }
179: return result;
180: }
181:
182: static class PendingLookup implements Serializable {
183: static final long serialVersionUID = -2692990309789917727L;
184:
185: PendingLookup(String name) {
186: this .name = name;
187: }
188:
189: String getName() {
190: return name;
191: }
192:
193: private String name;
194: }
195:
196: protected Object replaceObject(Object obj) throws IOException {
197: String name = (String) table.get(obj);
198: if (name == null)
199: return obj;
200: return new PendingLookup(name);
201: }
202:
203: private Scriptable scope;
204: private Hashtable table;
205: }
|