package org.enumerable.lambda.support.javascript;
import org.enumerable.lambda.Fn0;
import org.enumerable.lambda.Fn1;
import org.enumerable.lambda.Fn2;
import org.enumerable.lambda.Fn3;
import org.enumerable.lambda.annotation.NewLambda;
import org.enumerable.lambda.exception.LambdaWeavingNotEnabledException;
import sun.org.mozilla.javascript.internal.BaseFunction;
import sun.org.mozilla.javascript.internal.Context;
import sun.org.mozilla.javascript.internal.Function;
import sun.org.mozilla.javascript.internal.Scriptable;
/**
* This is class is similar {@link org.enumerable.lambda.Lambda}, but instead of creating
* lambdas inheriting from {@link org.enumerable.lambda.Fn0} it creates lambdas extending
* {@link Function} to be used together with Rhino.
*/
@SuppressWarnings("serial")
public class LambdaJavaScript {
public static abstract class BaseFunctionFn extends BaseFunction {
public BaseFunctionFn() {
Fn0.getAndCheckArityForMethod(getImplementingClass(), "call");
}
Class<?> getImplementingClass() {
return getClass();
}
public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
Object result = null;
if (args.length == 0)
result = call();
else if (args.length == 1)
result = call(args[0]);
else if (args.length == 2)
result = call(args[0], args[1]);
else if (args.length == 3)
result = call(args[0], args[1], args[2]);
if (result instanceof Number)
return Context.toNumber(result);
return Context.javaToJS(result, scope);
}
protected Object call() {
throw new UnsupportedOperationException();
}
protected Object call(Object a1) {
throw new UnsupportedOperationException();
}
protected Object call(Object a1, Object a2) {
throw new UnsupportedOperationException();
}
protected Object call(Object a1, Object a2, Object a3) {
throw new UnsupportedOperationException();
}
}
public static abstract class FunctionFn0 extends BaseFunctionFn {
public abstract Object call();
}
public static abstract class FunctionFn1 extends FunctionFn0 {
public Object call() {
return call(default$1());
}
protected Object default$1() {
return null;
}
public abstract Object call(Object a1);
}
public static abstract class FunctionFn2 extends FunctionFn1 {
public Object call(Object a1) {
return call(a1, default$2());
}
protected Object default$2() {
return null;
}
public abstract Object call(Object a1, Object a2);
}
public static abstract class FunctionFn3 extends FunctionFn2 {
public Object call(Object a1, Object a2) {
return call(a1, a2, default$3());
}
protected Object default$3() {
return null;
}
public abstract Object call(Object a1, Object a2, Object a3);
}
/**
* Creates a new function implementing {@link Function} taking no arguments.
*/
@NewLambda
public static FunctionFn0 function(Object block) {
throw new LambdaWeavingNotEnabledException();
}
/**
* Creates a new function implementing {@link Function} taking one argument.
*/
@NewLambda
public static FunctionFn1 function(Object a1, Object block) {
throw new LambdaWeavingNotEnabledException();
}
/**
* Creates a new function implementing {@link Function} taking two
* arguments.
*/
@NewLambda
public static FunctionFn2 function(Object a1, Object a2, Object block) {
throw new LambdaWeavingNotEnabledException();
}
/**
* Creates a new function implementing {@link Function} taking three
* arguments.
*/
@NewLambda
public static FunctionFn3 function(Object a1, Object a2, Object a3, Object block) {
throw new LambdaWeavingNotEnabledException();
}
/**
* Wraps the {@link Function} in a {@link Fn0}.
*/
public static Fn0<Object> toFn0(final Function function) {
return new Fn0<Object>() {
public Object call() {
Context.enter();
try {
return function.call(Context.getCurrentContext(), function.getParentScope(), function
.getParentScope(), new Object[0]);
} finally {
Context.exit();
}
}
};
}
/**
* Wraps the {@link Function} in a {@link Fn1}.
*/
public static Fn1<Object, Object> toFn1(final Function function) {
return new Fn1<Object, Object>() {
public Object call(Object a1) {
Context.enter();
try {
return function.call(Context.getCurrentContext(), function.getParentScope(), function
.getParentScope(), new Object[] { a1 });
} finally {
Context.exit();
}
}
};
}
/**
* Wraps the {@link Function} in a {@link Fn2}.
*/
public static Fn2<Object, Object, Object> toFn2(final Function function) {
return new Fn2<Object, Object, Object>() {
public Object call(Object a1, Object a2) {
Context.enter();
try {
return function.call(Context.getCurrentContext(), function.getParentScope(), function
.getParentScope(), new Object[] { a1, a2 });
} finally {
Context.exit();
}
}
};
}
/**
* Wraps the {@link Function} in a {@link Fn3}.
*/
public static Fn3<Object, Object, Object, Object> toFn3(final Function function) {
return new Fn3<Object, Object, Object, Object>() {
public Object call(Object a1, Object a2, Object a3) {
Context.enter();
try {
return function.call(Context.getCurrentContext(), function.getParentScope(), function
.getParentScope(), new Object[] { a1, a2, a3 });
} finally {
Context.exit();
}
}
};
}
/**
* Wraps the {@link Fn0} in a {@link Function}.
*/
@SuppressWarnings("rawtypes")
public static FunctionFn0 toFunction(final Fn0 fn) {
return new FunctionFn0() {
public Object call() {
return fn.call();
}
};
}
/**
* Wraps the {@link Fn1} in a {@link Function}.
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public static FunctionFn1 toFunction(final Fn1 fn) {
return new FunctionFn1() {
public Object call() {
return fn.call();
}
public Object call(Object a1) {
return fn.call(a1);
}
Class<?> getImplementingClass() {
return fn.getClass();
}
};
}
/**
* Wraps the {@link Fn2} in a {@link Function}.
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public static FunctionFn2 toFunction(final Fn2 fn) {
return new FunctionFn2() {
public Object call() {
return fn.call();
}
public Object call(Object a1) {
return fn.call(a1);
}
public Object call(Object a1, Object a2) {
return fn.call(a1, a2);
}
Class<?> getImplementingClass() {
return fn.getClass();
}
};
}
/**
* Wraps the {@link Fn3} in a {@link Function}.
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public static Function toFunction(final Fn3 fn) {
return new FunctionFn3() {
public Object call() {
return fn.call();
}
public Object call(Object a1) {
return fn.call(a1);
}
public Object call(Object a1, Object a2) {
return fn.call(a1, a2);
}
public Object call(Object a1, Object a2, Object a3) {
return fn.call(a1, a2, a3);
}
Class<?> getImplementingClass() {
return fn.getClass();
}
};
}
}