org.mozilla.javascript.NativeIterator Maven / Gradle / Ivy
The newest version!
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.javascript;
import java.util.Iterator;
/**
* This class implements iterator objects. See
* http://developer.mozilla.org/en/docs/New_in_JavaScript_1.7#Iterators
*
* @author Norris Boyd
*/
public final class NativeIterator extends IdScriptableObject {
private static final long serialVersionUID = -4136968203581667681L;
private static final Object ITERATOR_TAG = "Iterator";
static void init(ScriptableObject scope, boolean sealed) {
// Iterator
NativeIterator iterator = new NativeIterator();
iterator.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
// Generator
NativeGenerator.init(scope, sealed);
// StopIteration
NativeObject obj = new StopIteration();
obj.setPrototype(getObjectPrototype(scope));
obj.setParentScope(scope);
if (sealed) { obj.sealObject(); }
ScriptableObject.defineProperty(scope, STOP_ITERATION, obj,
ScriptableObject.DONTENUM);
// Use "associateValue" so that generators can continue to
// throw StopIteration even if the property of the global
// scope is replaced or deleted.
scope.associateValue(ITERATOR_TAG, obj);
}
/**
* Only for constructing the prototype object.
*/
private NativeIterator() {
}
private NativeIterator(Object objectIterator) {
this.objectIterator = objectIterator;
}
/**
* Get the value of the "StopIteration" object. Note that this value
* is stored in the top-level scope using "associateValue" so the
* value can still be found even if a script overwrites or deletes
* the global "StopIteration" property.
* @param scope a scope whose parent chain reaches a top-level scope
* @return the StopIteration object
*/
public static Object getStopIterationObject(Scriptable scope) {
Scriptable top = ScriptableObject.getTopLevelScope(scope);
return ScriptableObject.getTopScopeValue(top, ITERATOR_TAG);
}
private static final String STOP_ITERATION = "StopIteration";
public static final String ITERATOR_PROPERTY_NAME = "__iterator__";
static class StopIteration extends NativeObject {
private static final long serialVersionUID = 2485151085722377663L;
@Override
public String getClassName() {
return STOP_ITERATION;
}
/* StopIteration has custom instanceof behavior since it
* doesn't have a constructor.
*/
@Override
public boolean hasInstance(Scriptable instance) {
return instance instanceof StopIteration;
}
}
@Override
public String getClassName() {
return "Iterator";
}
@Override
protected void initPrototypeId(int id) {
String s;
int arity;
switch (id) {
case Id_constructor: arity=2; s="constructor"; break;
case Id_next: arity=0; s="next"; break;
case Id___iterator__: arity=1; s=ITERATOR_PROPERTY_NAME; break;
default: throw new IllegalArgumentException(String.valueOf(id));
}
initPrototypeMethod(ITERATOR_TAG, id, s, arity);
}
@Override
public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope,
Scriptable thisObj, Object[] args)
{
if (!f.hasTag(ITERATOR_TAG)) {
return super.execIdCall(f, cx, scope, thisObj, args);
}
int id = f.methodId();
if (id == Id_constructor) {
return jsConstructor(cx, scope, thisObj, args);
}
if (!(thisObj instanceof NativeIterator))
throw incompatibleCallError(f);
NativeIterator iterator = (NativeIterator) thisObj;
switch (id) {
case Id_next:
return iterator.next(cx, scope);
case Id___iterator__:
/// XXX: what about argument? SpiderMonkey apparently ignores it
return thisObj;
default:
throw new IllegalArgumentException(String.valueOf(id));
}
}
/* The JavaScript constructor */
private static Object jsConstructor(Context cx, Scriptable scope,
Scriptable thisObj, Object[] args)
{
if (args.length == 0 || args[0] == null ||
args[0] == Undefined.instance)
{
Object argument = args.length == 0 ? Undefined.instance : args[0];
throw ScriptRuntime.typeError1("msg.no.properties",
ScriptRuntime.toString(argument));
}
Scriptable obj = ScriptRuntime.toObject(cx, scope, args[0]);
boolean keyOnly = args.length > 1 && ScriptRuntime.toBoolean(args[1]);
if (thisObj != null) {
// Called as a function. Convert to iterator if possible.
// For objects that implement java.lang.Iterable or
// java.util.Iterator, have JavaScript Iterator call the underlying
// iteration methods
Iterator> iterator =
VMBridge.instance.getJavaIterator(cx, scope, obj);
if (iterator != null) {
scope = ScriptableObject.getTopLevelScope(scope);
return cx.getWrapFactory().wrap(cx, scope,
new WrappedJavaIterator(iterator, scope),
WrappedJavaIterator.class);
}
// Otherwise, just call the runtime routine
Scriptable jsIterator = ScriptRuntime.toIterator(cx, scope, obj,
keyOnly);
if (jsIterator != null) {
return jsIterator;
}
}
// Otherwise, just set up to iterate over the properties of the object.
// Do not call __iterator__ method.
Object objectIterator = ScriptRuntime.enumInit(obj, cx, scope,
keyOnly ? ScriptRuntime.ENUMERATE_KEYS_NO_ITERATOR
: ScriptRuntime.ENUMERATE_ARRAY_NO_ITERATOR);
ScriptRuntime.setEnumNumbers(objectIterator, true);
NativeIterator result = new NativeIterator(objectIterator);
result.setPrototype(ScriptableObject.getClassPrototype(scope,
result.getClassName()));
result.setParentScope(scope);
return result;
}
private Object next(Context cx, Scriptable scope) {
Boolean b = ScriptRuntime.enumNext(this.objectIterator);
if (!b.booleanValue()) {
// Out of values. Throw StopIteration.
throw new JavaScriptException(
NativeIterator.getStopIterationObject(scope), null, 0);
}
return ScriptRuntime.enumId(this.objectIterator, cx);
}
static public class WrappedJavaIterator
{
WrappedJavaIterator(Iterator> iterator, Scriptable scope) {
this.iterator = iterator;
this.scope = scope;
}
public Object next() {
if (!iterator.hasNext()) {
// Out of values. Throw StopIteration.
throw new JavaScriptException(
NativeIterator.getStopIterationObject(scope), null, 0);
}
return iterator.next();
}
public Object __iterator__(boolean b) {
return this;
}
private Iterator> iterator;
private Scriptable scope;
}
// #string_id_map#
@Override
protected int findPrototypeId(String s) {
int id;
// #generated# Last update: 2007-06-11 09:43:19 EDT
L0: { id = 0; String X = null;
int s_length = s.length();
if (s_length==4) { X="next";id=Id_next; }
else if (s_length==11) { X="constructor";id=Id_constructor; }
else if (s_length==12) { X="__iterator__";id=Id___iterator__; }
if (X!=null && X!=s && !X.equals(s)) id = 0;
break L0;
}
// #/generated#
return id;
}
private static final int
Id_constructor = 1,
Id_next = 2,
Id___iterator__ = 3,
MAX_PROTOTYPE_ID = 3;
// #/string_id_map#
private Object objectIterator;
}