All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.mozilla.javascript.IteratorLikeIterable Maven / Gradle / Ivy

Go to download

Rhino is an open-source implementation of JavaScript written entirely in Java. It is typically embedded into Java applications to provide scripting to end users.

There is a newer version: 1.7.15
Show newest version
/* 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.io.Closeable;
import java.util.Iterator;
import java.util.NoSuchElementException;

/**
 * This is a class that makes it easier to iterate over "iterator-like" objects as defined
 * in the ECMAScript spec. The caller is responsible for retrieving an object that implements
 * the "iterator" pattern. This class will follow that pattern and throw appropriate
 * JavaScript exceptions.
 *
 * The pattern that the target class should follow is:
 * * It must have a function property called "next"
 * * The function must return an object with a boolean value called "done".
 * * If "done" is true, then the returned object should also contain a "value" property.
 * * If it has a function property called "return" then it will be called
 *   when the caller is done iterating.
 */
public class IteratorLikeIterable
    implements Iterable, Closeable
{
    private final Context cx;
    private final Scriptable scope;
    private final Callable next;
    private final Callable returnFunc;
    private final Scriptable iterator;
    private boolean closed;

    public IteratorLikeIterable(Context cx, Scriptable scope, Object target) {
        this.cx = cx;
        this.scope = scope;
        // This will throw if "next" is not a function or undefined
        next = ScriptRuntime.getPropFunctionAndThis(target, ES6Iterator.NEXT_METHOD, cx, scope);
        iterator = ScriptRuntime.lastStoredScriptable(cx);
        Object retObj = ScriptRuntime.getObjectPropNoWarn(target, ES6Iterator.RETURN_PROPERTY, cx, scope);
        // We only care about "return" if it is not null or undefined
        if ((retObj != null) && !Undefined.isUndefined(retObj)) {
            if (!(retObj instanceof Callable)) {
                throw ScriptRuntime.notFunctionError(target, retObj, ES6Iterator.RETURN_PROPERTY);
            }
            returnFunc = (Callable)retObj;
        } else {
            returnFunc = null;
        }
    }

    @Override
    public void close() {
        if (!closed) {
            closed = true;
            if (returnFunc != null) {
                returnFunc.call(cx, scope, iterator, ScriptRuntime.emptyArgs);
            }
        }
    }

    @Override
    public Itr iterator() {
        return new Itr();
    }

    public final class Itr
        implements Iterator
    {
        private Object nextVal;
        private boolean isDone;

        @Override
        public boolean hasNext() {
            Object val = next.call(cx, scope, iterator, ScriptRuntime.emptyArgs);
            // This will throw if "val" is not an object. 
            // "getObjectPropNoWarn" won't, so do this as follows.
            Object doneval = ScriptableObject.getProperty(
                ScriptableObject.ensureScriptable(val), ES6Iterator.DONE_PROPERTY);
            if (doneval == Scriptable.NOT_FOUND) {
                doneval = Undefined.instance;
            }
            // It's OK if done is undefined.
            if (ScriptRuntime.toBoolean(doneval)) {
                isDone = true;
                return false;
            }
            nextVal = ScriptRuntime.getObjectPropNoWarn(val, ES6Iterator.VALUE_PROPERTY, cx, scope);
            return true;
        }

        @Override
        public Object next() {
            if (isDone) {
                throw new NoSuchElementException();
            }
            return nextVal;
        }
    }
}