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: * Contributor(s):
017: * Norris Boyd
018: *
019: * Alternatively, the contents of this file may be used under the terms of
020: * the GNU General Public License Version 2 or later (the "GPL"), in which
021: * case the provisions of the GPL are applicable instead of those above. If
022: * you wish to allow use of your version of this file only under the terms of
023: * the GPL and not to allow others to use your version of this file under the
024: * MPL, indicate your decision by deleting the provisions above and replacing
025: * them with the notice and other provisions required by the GPL. If you do
026: * not delete the provisions above, a recipient may use your version of this
027: * file under either the MPL or the GPL.
028: *
029: * ***** END LICENSE BLOCK ***** */
030:
031: package org.mozilla.javascript;
032:
033: import java.util.Iterator;
034:
035: /**
036: * This class implements iterator objects. See
037: * http://developer.mozilla.org/en/docs/New_in_JavaScript_1.7#Iterators
038: *
039: * @author Norris Boyd
040: */
041: public final class NativeIterator extends IdScriptableObject {
042: private static final Object ITERATOR_TAG = new Object();
043:
044: static void init(ScriptableObject scope, boolean sealed) {
045: // Iterator
046: NativeIterator iterator = new NativeIterator();
047: iterator.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
048:
049: // Generator
050: NativeGenerator.init(scope, sealed);
051:
052: // StopIteration
053: NativeObject obj = new StopIteration();
054: obj.setPrototype(getObjectPrototype(scope));
055: obj.setParentScope(scope);
056: if (sealed) {
057: obj.sealObject();
058: }
059: ScriptableObject.defineProperty(scope, STOP_ITERATION, obj,
060: ScriptableObject.DONTENUM);
061: // Use "associateValue" so that generators can continue to
062: // throw StopIteration even if the property of the global
063: // scope is replaced or deleted.
064: scope.associateValue(ITERATOR_TAG, obj);
065: }
066:
067: /**
068: * Only for constructing the prototype object.
069: */
070: private NativeIterator() {
071: }
072:
073: private NativeIterator(Object objectIterator) {
074: this .objectIterator = objectIterator;
075: }
076:
077: /**
078: * Get the value of the "StopIteration" object. Note that this value
079: * is stored in the top-level scope using "associateValue" so the
080: * value can still be found even if a script overwrites or deletes
081: * the global "StopIteration" property.
082: * @param scope a scope whose parent chain reaches a top-level scope
083: * @return the StopIteration object
084: */
085: public static Object getStopIterationObject(Scriptable scope) {
086: Scriptable top = ScriptableObject.getTopLevelScope(scope);
087: return ScriptableObject.getTopScopeValue(top, ITERATOR_TAG);
088: }
089:
090: private static final String STOP_ITERATION = "StopIteration";
091: public static final String ITERATOR_PROPERTY_NAME = "__iterator__";
092:
093: static class StopIteration extends NativeObject {
094: public String getClassName() {
095: return STOP_ITERATION;
096: }
097:
098: /* StopIteration has custom instanceof behavior since it
099: * doesn't have a constructor.
100: */
101: public boolean hasInstance(Scriptable instance) {
102: return instance instanceof StopIteration;
103: }
104: }
105:
106: public String getClassName() {
107: return "Iterator";
108: }
109:
110: protected void initPrototypeId(int id) {
111: String s;
112: int arity;
113: switch (id) {
114: case Id_constructor:
115: arity = 2;
116: s = "constructor";
117: break;
118: case Id_next:
119: arity = 0;
120: s = "next";
121: break;
122: case Id___iterator__:
123: arity = 1;
124: s = ITERATOR_PROPERTY_NAME;
125: break;
126: default:
127: throw new IllegalArgumentException(String.valueOf(id));
128: }
129: initPrototypeMethod(ITERATOR_TAG, id, s, arity);
130: }
131:
132: public Object execIdCall(IdFunctionObject f, Context cx,
133: Scriptable scope, Scriptable this Obj, Object[] args) {
134: if (!f.hasTag(ITERATOR_TAG)) {
135: return super .execIdCall(f, cx, scope, this Obj, args);
136: }
137: int id = f.methodId();
138:
139: if (id == Id_constructor) {
140: return jsConstructor(cx, scope, this Obj, args);
141: }
142:
143: if (!(this Obj instanceof NativeIterator))
144: throw incompatibleCallError(f);
145:
146: NativeIterator iterator = (NativeIterator) this Obj;
147:
148: switch (id) {
149:
150: case Id_next:
151: return iterator.next(cx, scope);
152:
153: case Id___iterator__:
154: /// XXX: what about argument? SpiderMonkey apparently ignores it
155: return this Obj;
156:
157: default:
158: throw new IllegalArgumentException(String.valueOf(id));
159: }
160: }
161:
162: /* the javascript constructor */
163: private static Object jsConstructor(Context cx, Scriptable scope,
164: Scriptable this Obj, Object[] args) {
165: if (args.length == 0 || args[0] == null
166: || args[0] == Undefined.instance) {
167: throw ScriptRuntime.typeError1("msg.no.properties",
168: ScriptRuntime.toString(args[0]));
169: }
170: Scriptable obj = ScriptRuntime.toObject(scope, args[0]);
171: boolean keyOnly = args.length > 1
172: && ScriptRuntime.toBoolean(args[1]);
173: if (this Obj != null) {
174: // Called as a function. Convert to iterator if possible.
175:
176: // For objects that implement java.lang.Iterable or
177: // java.util.Iterator, have JavaScript Iterator call the underlying
178: // iteration methods
179: Iterator iterator = VMBridge.instance.getJavaIterator(cx,
180: scope, obj);
181: if (iterator != null) {
182: scope = ScriptableObject.getTopLevelScope(scope);
183: return cx.getWrapFactory().wrap(cx, scope,
184: new WrappedJavaIterator(iterator, scope),
185: WrappedJavaIterator.class);
186: }
187:
188: // Otherwise, just call the runtime routine
189: Scriptable jsIterator = ScriptRuntime.toIterator(cx, scope,
190: obj, keyOnly);
191: if (jsIterator != null) {
192: return jsIterator;
193: }
194: }
195:
196: // Otherwise, just set up to iterate over the properties of the object.
197: // Do not call __iterator__ method.
198: Object objectIterator = ScriptRuntime.enumInit(obj, cx,
199: keyOnly ? ScriptRuntime.ENUMERATE_KEYS_NO_ITERATOR
200: : ScriptRuntime.ENUMERATE_ARRAY_NO_ITERATOR);
201: ScriptRuntime.setEnumNumbers(objectIterator, true);
202: NativeIterator result = new NativeIterator(objectIterator);
203: result.setPrototype(NativeIterator.getClassPrototype(scope,
204: result.getClassName()));
205: result.setParentScope(scope);
206: return result;
207: }
208:
209: private Object next(Context cx, Scriptable scope) {
210: Boolean b = ScriptRuntime.enumNext(this .objectIterator);
211: if (!b.booleanValue()) {
212: // Out of values. Throw StopIteration.
213: throw new JavaScriptException(NativeIterator
214: .getStopIterationObject(scope), null, 0);
215: }
216: return ScriptRuntime.enumId(this .objectIterator, cx);
217: }
218:
219: static public class WrappedJavaIterator {
220: WrappedJavaIterator(Iterator iterator, Scriptable scope) {
221: this .iterator = iterator;
222: this .scope = scope;
223: }
224:
225: public Object next() {
226: if (!iterator.hasNext()) {
227: // Out of values. Throw StopIteration.
228: throw new JavaScriptException(NativeIterator
229: .getStopIterationObject(scope), null, 0);
230: }
231: return iterator.next();
232: }
233:
234: public Object __iterator__(boolean b) {
235: return this ;
236: }
237:
238: private Iterator iterator;
239: private Scriptable scope;
240: }
241:
242: // #string_id_map#
243:
244: protected int findPrototypeId(String s) {
245: int id;
246: // #generated# Last update: 2007-06-11 09:43:19 EDT
247: L0: {
248: id = 0;
249: String X = null;
250: int s_length = s.length();
251: if (s_length == 4) {
252: X = "next";
253: id = Id_next;
254: } else if (s_length == 11) {
255: X = "constructor";
256: id = Id_constructor;
257: } else if (s_length == 12) {
258: X = "__iterator__";
259: id = Id___iterator__;
260: }
261: if (X != null && X != s && !X.equals(s))
262: id = 0;
263: break L0;
264: }
265: // #/generated#
266: return id;
267: }
268:
269: private static final int Id_constructor = 1, Id_next = 2,
270: Id___iterator__ = 3, MAX_PROTOTYPE_ID = 3;
271:
272: // #/string_id_map#
273:
274: private Object objectIterator;
275: }
|