2003/05/12 05:10:30
[org.ibex.core.git] / src / org / mozilla / javascript / FlattenedObject.java
1 /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
2  *\r
3  * The contents of this file are subject to the Netscape Public\r
4  * License Version 1.1 (the "License"); you may not use this file\r
5  * except in compliance with the License. You may obtain a copy of\r
6  * the License at http://www.mozilla.org/NPL/\r
7  *\r
8  * Software distributed under the License is distributed on an "AS\r
9  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
10  * implied. See the License for the specific language governing\r
11  * rights and limitations under the License.\r
12  *\r
13  * The Original Code is Rhino code, released\r
14  * May 6, 1999.\r
15  *\r
16  * The Initial Developer of the Original Code is Netscape\r
17  * Communications Corporation.  Portions created by Netscape are\r
18  * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
19  * Rights Reserved.\r
20  *\r
21  * Contributor(s): \r
22  * Norris Boyd\r
23  * Roger Lawrence\r
24  *\r
25  * Alternatively, the contents of this file may be used under the\r
26  * terms of the GNU Public License (the "GPL"), in which case the\r
27  * provisions of the GPL are applicable instead of those above.\r
28  * If you wish to allow use of your version of this file only\r
29  * under the terms of the GPL and not to allow others to use your\r
30  * version of this file under the NPL, indicate your decision by\r
31  * deleting the provisions above and replace them with the notice\r
32  * and other provisions required by the GPL.  If you do not delete\r
33  * the provisions above, a recipient may use your version of this\r
34  * file under either the NPL or the GPL.\r
35  */\r
36 \r
37 package org.mozilla.javascript;\r
38 \r
39 import java.util.Hashtable;\r
40 import java.util.Enumeration;\r
41 \r
42 /**\r
43  * Manipulate a Scriptable object as if its prototype chain were flattened.\r
44  * <p>\r
45  * This class has been deprecated in favor of the static methods \r
46  * <code>getProperty</code>, <code>putProperty</code>, and \r
47  * <code>deleteProperty</code> of ScripableObject. Those methods provide the\r
48  * same functionality without the confusing and inefficient need to construct\r
49  * a new object instance.\r
50  *\r
51  * @see org.mozilla.javascript.ScriptableObject\r
52  * @deprecated\r
53  * @author Norris Boyd\r
54  */\r
55 \r
56 public class FlattenedObject {\r
57 \r
58     /**\r
59      * Construct a new FlattenedObject.\r
60      *\r
61      * @param object the object to be viewed with flattened properties\r
62      * @deprecated\r
63      */\r
64     public FlattenedObject(Scriptable object) {\r
65         this.obj = object;\r
66     }\r
67 \r
68     /**\r
69      * Get the associated Scriptable object.\r
70      * @deprecated\r
71      */\r
72     public Scriptable getObject() {\r
73         return obj;\r
74     }\r
75 \r
76     /**\r
77      * Determine if a property exists in an object.\r
78      *\r
79      * This is a more convenient (and less efficient) form than\r
80      * <code>Scriptable.has()</code>.\r
81      * It returns true if and only if the property\r
82      * exists in this object or any of the objects in its prototype\r
83      * chain.\r
84      *\r
85      * @param id the property index, which may be either a String or a\r
86      *           Number\r
87      * @return true if and only if the property exists in the prototype\r
88      *         chain\r
89      * @see org.mozilla.javascript.Scriptable#has\r
90      * @deprecated As of 1.5R2, replaced by ScriptableObject.getProperty\r
91      */\r
92     public boolean hasProperty(Object id) {\r
93         String stringId = ScriptRuntime.toString(id);\r
94         String s = ScriptRuntime.getStringId(stringId);\r
95         if (s == null)\r
96             return getBase(obj, ScriptRuntime.getIntId(stringId)) != null;\r
97         return getBase(obj, s) != null;\r
98     }\r
99 \r
100     /**\r
101      * Get a property of an object.\r
102      * <p>\r
103      * This is a more convenient (and less efficient) form than\r
104      * <code>Scriptable.get()</code>. It corresponds exactly to the\r
105      * expression <code>obj[id]</code> in JavaScript. This method\r
106      * will traverse the prototype chain of an object to find the\r
107      * property.<p>\r
108      *\r
109      * If the property does not exist in the object or its prototype\r
110      * chain, the undefined value will be returned.\r
111      *\r
112      * @param id the property index; can be a String or a Number; the\r
113      *           String may contain characters representing a number\r
114      * @return the value of the property or the undefined value\r
115      * @see org.mozilla.javascript.Scriptable#get\r
116      * @see org.mozilla.javascript.Context#getUndefinedValue\r
117      * @deprecated As of 1.5R2, replaced by ScriptableObject.getProperty\r
118      */\r
119     public Object getProperty(Object id) {\r
120         String s = ScriptRuntime.getStringId(id);\r
121         int index = s == null ? ScriptRuntime.getIntId(id) : 0;\r
122         Scriptable m = obj;\r
123         Object result;\r
124         for(;;) {\r
125             result = s == null ? m.get(index, obj) : m.get(s, obj);\r
126             if (result != Scriptable.NOT_FOUND)\r
127                 break;\r
128             m = m.getPrototype();\r
129             if (m == null)\r
130                 return Undefined.instance;\r
131         }\r
132         if (result instanceof Scriptable)\r
133             return new FlattenedObject((Scriptable) result);\r
134         return result;\r
135     }\r
136 \r
137     /**\r
138      * Set a property of an object.\r
139      *\r
140      * This is a more convenient (and less efficient) form than that\r
141      * provided in Scriptable. It corresponds exactly to the\r
142      * expression <code>obj[id] = val</code> in JavaScript.<p>\r
143      *\r
144      * @param id the property index, which may be either a String or\r
145      *           a Number\r
146      * @param value the value of the property\r
147      * @see org.mozilla.javascript.Scriptable#put\r
148      * @deprecated As of 1.5R2, replaced by ScriptableObject.putProperty\r
149      */\r
150     public void putProperty(Object id, Object value) {\r
151         String s = ScriptRuntime.getStringId(id);\r
152         if (value instanceof FlattenedObject)\r
153             value = ((FlattenedObject) value).getObject();\r
154         Scriptable x;\r
155         if (s == null) {\r
156             int index = ScriptRuntime.getIntId(id);\r
157             x = getBase(obj, index);\r
158             if (x == null)\r
159                 x = obj;\r
160             x.put(index, obj, value);\r
161             return;\r
162         }\r
163         x = getBase(obj, s);\r
164         if (x == null)\r
165             x = obj;\r
166         x.put(s, obj, value);\r
167     }\r
168 \r
169     /**\r
170      * Remove a property.\r
171      *\r
172      * This method provides the functionality of the <code>delete</code>\r
173      * operator in JavaScript.\r
174      *\r
175      * @param id the property index, which may be either a String or\r
176      *           a Number\r
177      * @return true if the property didn't exist, or existed and was removed\r
178      * @see org.mozilla.javascript.Scriptable#delete\r
179      * @deprecated as of 1.5R2, replaced by ScriptableObject.deleteProperty\r
180      */\r
181     public boolean deleteProperty(Object id) {\r
182         String s = ScriptRuntime.getStringId(id);\r
183         if (s == null) {\r
184             int index = ScriptRuntime.getIntId(id);\r
185             Scriptable base = getBase(obj, index);\r
186             if (base == null)\r
187                 return true;\r
188             base.delete(index);\r
189             return !base.has(index, base);\r
190         }\r
191         Scriptable base = getBase(obj, s);\r
192         if (base == null)\r
193             return true;\r
194         base.delete(s);\r
195         return !base.has(s, base);\r
196     }\r
197 \r
198     /**\r
199      * Return an array that contains the ids of the properties.\r
200      *\r
201      * <p>This method will walk the prototype chain and collect the\r
202      * ids of all objects in the prototype chain.<p>\r
203      *\r
204      * If an id appears in more than one object in the prototype chain,\r
205      * it will only be in the array once. (So all the entries in the\r
206      * array will be unique respective to equals().)\r
207      *\r
208      * @see org.mozilla.javascript.Scriptable#getIds\r
209      * @deprecated\r
210      */\r
211     public Object[] getIds() {\r
212         Hashtable h = new Hashtable(11);\r
213         Scriptable m = obj;\r
214         while (m != null) {\r
215             Object[] e = m.getIds();\r
216             for (int i=0; i < e.length; i++) {\r
217                 h.put(e[i], Boolean.TRUE);\r
218             }\r
219             m = m.getPrototype();\r
220         }\r
221         Enumeration keys = h.keys();\r
222         Object elem;\r
223         Object[] result = new Object[h.size()];\r
224         int index = 0;\r
225         while (keys.hasMoreElements()) {\r
226             elem = keys.nextElement();\r
227             result[index++] = elem;\r
228         }\r
229         return result;\r
230     }\r
231 \r
232     /**\r
233      * Consider this object to be a function, and call it.\r
234      *\r
235      * @param cx the current Context for this thread\r
236      * @param thisObj the JavaScript 'this' for the call\r
237      * @param args the arguments for the call\r
238      * @return the result of the JavaScript function call\r
239      * @exception NotAFunctionException if this object is not a function\r
240      * @exception JavaScriptException if an uncaught JavaScript exception\r
241      *            occurred while executing the function\r
242      * @see org.mozilla.javascript.Function#call\r
243      * @deprecated\r
244      */\r
245     public Object call(Context cx, Scriptable thisObj, Object[] args)\r
246         throws NotAFunctionException,\r
247                JavaScriptException\r
248     {\r
249         if (!(obj instanceof Function)) {\r
250             throw new NotAFunctionException();\r
251         }\r
252         return ScriptRuntime.call(cx, obj, thisObj, args, (Function) obj);\r
253     }\r
254 \r
255     /**\r
256      * Consider this object to be a function, and invoke it as a\r
257      * constructor call.\r
258      *\r
259      * @param cx the current Context for this thread\r
260      * @param args the arguments for the constructor call\r
261      * @return the allocated object\r
262      * @exception NotAFunctionException if this object is not a function\r
263      * @exception JavaScriptException if an uncaught JavaScript exception\r
264      *            occurred while executing the constructor\r
265      * @see org.mozilla.javascript.Function#construct\r
266      * @deprecated\r
267      */\r
268     public Scriptable construct(Context cx, Object[] args)\r
269         throws NotAFunctionException,\r
270                JavaScriptException\r
271     {\r
272         if (!(obj instanceof Function)) {\r
273             throw new NotAFunctionException();\r
274         }\r
275         return ScriptRuntime.newObject(cx, obj, args, null);\r
276     }\r
277 \r
278     /**\r
279      * Get the property indicated by the id, and invoke it with the\r
280      * specified arguments.\r
281      * <p>\r
282      * For example, for a FlattenedObject <code>obj</code>,\r
283      * and a Java array <code>a</code> consisting of a single string\r
284      * <code>"hi"</code>, the call <pre>\r
285      * obj.callMethod("m", a)</pre>\r
286      * is equivalent to the JavaScript code <code>obj.m("hi")</code>.<p>\r
287      *\r
288      * If the property is not found or is not a function, an\r
289      * exception will be thrown.\r
290      *\r
291      * @param id the Number or String to use to find the function property\r
292      *           to call\r
293      * @param args the arguments for the constructor call\r
294      * @return the result of the call\r
295      * @exception PropertyException if the designated property\r
296      *            was not found\r
297      * @exception NotAFunctionException if this object is not a function\r
298      * @exception JavaScriptException if an uncaught JavaScript exception\r
299      *            occurred while executing the method\r
300      * @see org.mozilla.javascript.Function#call\r
301      * @deprecated\r
302      */\r
303     public Object callMethod(Object id, Object[] args)\r
304         throws PropertyException,\r
305                NotAFunctionException,\r
306                JavaScriptException\r
307     {\r
308         if (!hasProperty(id)) {\r
309             throw PropertyException.withMessage0("msg.prop.not.found");\r
310         }\r
311         Object o = getProperty(id);\r
312         if (o instanceof FlattenedObject)\r
313             return ((FlattenedObject) o).call(Context.getContext(), obj, args);\r
314         throw new NotAFunctionException();\r
315     }\r
316 \r
317     /****** End of API *******/\r
318 \r
319     private static Scriptable getBase(Scriptable obj, String s) {\r
320         Scriptable m = obj;\r
321         while (m != null) {\r
322             if (m.has(s, obj))\r
323                 return m;\r
324             m = m.getPrototype();\r
325         }\r
326         return null;\r
327     }\r
328 \r
329     private static Scriptable getBase(Scriptable obj, int index) {\r
330         Scriptable m = obj;\r
331         while (m != null) {\r
332             if (m.has(index, obj))\r
333                 return m;\r
334             m = m.getPrototype();\r
335         }\r
336         return null;\r
337     }\r
338 \r
339     private Scriptable obj;\r
340 }\r
341 \r