Skip to content

Instantly share code, notes, and snippets.

@tschaub
Created August 8, 2012 02:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tschaub/3291399 to your computer and use it in GitHub Desktop.
Save tschaub/3291399 to your computer and use it in GitHub Desktop.
Example of a JavaScript iterator created with Rhino.
/**
* Demonstrates how a JavaScript iterator can be created with Rhino. This
* simple Range class can be defined as a JavaScript constructor.
*
* Example use:
*
* >> // set up Range constructor and prototype
* >> defineClass(Packages.com.example.Range)
*
* >> var range = new Range(3, 5)
* >> range.high
* 5
* >> range.low
* 3
*
* >> for (var i in range) {
* .. print("got: " + i);
* .. }
* got: 3
* got: 4
* got: 5
*
* >> var range = new Range()
* Error: Call constructor with two numbers. (<stdin>#15)
*
* >> var range = Range()
* Error: Call constructor with new keyword. (<stdin>#16)
*
*/
package com.example;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.JavaScriptException;
import org.mozilla.javascript.NativeIterator;
import org.mozilla.javascript.ScriptRuntime;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.annotations.JSConstructor;
import org.mozilla.javascript.annotations.JSFunction;
import org.mozilla.javascript.annotations.JSGetter;
public class Range extends ScriptableObject {
/** serialVersionUID */
private static final long serialVersionUID = 2468077032059444220L;
/**
* Low value of range (inclusive).
*/
int low;
/**
* High value of range (inclusive).
*/
int high;
/**
* Current value.
*/
int current;
/**
* JavaScript prototype constructor.
*/
public Range() {
}
/**
* Create a new range with the given integer values.
* @param low
* @param high
*/
private Range(int low, int high) {
this.low = low;
this.high = high;
current = low - 1;
}
/**
* JavaScript constructor for a range.
* @param cx
* @param args
* @param ctorObj
* @param inNewExpr
* @return
*/
@JSConstructor
public static Object constructor(Context cx, Object[] args, Function ctorObj, boolean inNewExpr) {
if (!inNewExpr) {
throw ScriptRuntime.constructError("Error", "Call constructor with new keyword.");
}
if (args.length != 2) {
throw ScriptRuntime.constructError("Error", "Call constructor with two numbers.");
}
return new Range((int) Context.toNumber(args[0]), (int) Context.toNumber(args[1]));
}
/**
* JavaScript getter for high value.
* @return
*/
@JSGetter
public int getHigh() {
return high;
}
/**
* JavaScript getter for low value.
* @return
*/
@JSGetter
public int getLow() {
return low;
}
/**
* JavaScript method for accessing the next value in the range. Throws
* StopIteration when the range is exhausted.
* @return
*/
@JSFunction
public int next() {
++current;
if (current > high) {
throw new JavaScriptException(
NativeIterator.getStopIterationObject(getParentScope()), null, 0);
}
return current;
}
/**
* Magic method to allow for...in syntax.
* @param b
* @return
*/
@JSFunction
public Object __iterator__(boolean b) {
return this;
}
/**
* Name for JavaScript constructor.
*/
@Override
public String getClassName() {
return "Range";
}
}
@DavidGoldman
Copy link

If you remove the "force construction using new keyword" and attempt to construct it without using "new", the Range does not seem to work correctly. Why?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment