/*
ESXX - The friendly ECMAscript/XML Application Server
Copyright (C) 2007-2015 Martin Blom <martin@blom.org>
This program is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation, either version 3
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.esxx.util;
import java.io.File;
import java.io.FilenameFilter;
import java.util.Date;
import java.util.regex.Pattern;
import org.esxx.ESXX;
import org.esxx.ESXXException;
import org.mozilla.javascript.*;
public abstract class JS {
public static Scriptable evaluateObjectExpr(String object_expr, Scriptable scope) {
Scriptable object = scope;
if (object_expr != null) {
String path[] = dotPattern.split(object_expr, 0);
for (String p : path) {
Object o = ScriptableObject.getProperty(object, p);
if (o == Scriptable.NOT_FOUND || !(o instanceof Scriptable)) {
return null;
}
object = (Scriptable) o;
}
}
return object;
}
public static Object callJSMethod(String expr,
Object[] args, String identifier,
Context cx, Scriptable scope) {
String object;
String method;
int dot = expr.lastIndexOf('.');
if (dot == -1) {
object = null;
method = expr;
}
else {
object = expr.substring(0, dot);
method = expr.substring(dot + 1);
}
return callJSMethod(object, method, args, identifier, cx, scope, true);
}
public static Object callJSMethod(String object_expr, String method,
Object[] args, String identifier,
Context cx, Scriptable scope,
boolean throw_if_not_found) {
Scriptable object = evaluateObjectExpr(object_expr, scope);
String function_name = object == scope ? method : object_expr + "." + method;
if (object == null) {
throw new ESXXException(object_expr + " cannot be evalualted.");
}
Object m = ScriptableObject.getProperty(object, method);
if (m == Scriptable.NOT_FOUND) {
if (throw_if_not_found) {
throw new ESXXException(identifier + " method " + function_name + " not found");
}
else {
return null;
}
}
if (!(m instanceof Function)) {
throw new ESXXException(identifier + " " + function_name + " is not a function.");
}
return ((Function) m).call(cx, scope, object, args);
}
public static void dumpScriptState(Scriptable scope) {
System.err.println("Scope trace:");
printScopeTrace(scope);
System.err.println();
System.err.flush();
}
public static void dumpScope(Scriptable scope) {
System.err.println("Dump of scope " + scope);
for (Object i : ((ScriptableObject) scope).getAllIds()) {
if (i instanceof Number) {
System.err.println(i + ": " + scope.get((Integer) i, scope));
}
else {
System.err.println("'" + i + "': " + scope.get((String) i, scope));
}
}
}
public static void printScriptStackTrace() {
StackTraceElement[] trace = Thread.currentThread().getStackTrace();
JSFilenameFilter filter = new JSFilenameFilter ();
for (StackTraceElement e : trace) {
File f = new File(e.getFileName());
if (filter.accept(f.getParentFile(), f.getName())) {
System.err.println(e);
}
}
}
public static void printScopeTrace(Scriptable scope) {
if (scope == null) {
return;
}
printPrototypeTrace(scope);
printScopeTrace(scope.getParentScope());
}
public static void printPrototypeTrace(Scriptable scope) {
if (scope == null) {
System.err.println();
}
else {
System.err.print(defaultToString(scope) + " ");
printPrototypeTrace(scope.getPrototype());
}
}
public static void printObject(Context cx, Scriptable scope, Object object) {
if (object instanceof RhinoException) {
RhinoException ex = (RhinoException) object;
do {
System.out.println("-> " + ex.getClass().getSimpleName() + " from "
+ ex.sourceName() + ", line " + ex.lineNumber()
+ ", column " + ex.columnNumber()
+ (ex.lineSource() != null ?
", near " + ex.lineSource() : "")
+ ":");
if (object instanceof JavaScriptException) {
object = ((JavaScriptException) object).getValue();
}
else if (object instanceof WrappedException) {
object = ((WrappedException) object).getWrappedException();
}
else {
object = ex.details();
}
}
while (object instanceof JavaScriptException || object instanceof WrappedException);
}
while (object instanceof Wrapper) {
System.out.println("-> " + object.getClass().getSimpleName() + ":");
object = ((Wrapper) object).unwrap();
}
if (object instanceof Throwable) {
System.out.println("-> " + object.getClass().getSimpleName() + ":");
object = ((Throwable) object).getMessage();
}
if (object instanceof String) {
System.out.println("-> `" + object + "\u00b4");
}
else if (object instanceof Number || object instanceof Boolean) {
System.out.println("-> " + object);
}
else if (object == Context.getUndefinedValue()) {
System.out.println("-> undefined");
}
else if (object instanceof Scriptable) {
Scriptable thiz = (Scriptable) object;
System.out.println("-> JavaScript " + thiz.getClassName() + ":");
Object to_source = ScriptableObject.getProperty(thiz, "toSource");
if (to_source == Scriptable.NOT_FOUND) {
to_source = ScriptableObject.getProperty(thiz, "toXMLString"); // Fallback for XMLList
}
if (to_source == Scriptable.NOT_FOUND) {
to_source = ScriptableObject.getProperty(thiz, "toString");
}
if (to_source instanceof Function) {
Function ts = (Function) to_source;
try {
System.out.println("-> "
+ ts.call(cx, scope, thiz, Context.emptyArgs).toString().trim());
}
catch (Exception ignored) {
printPlainObject(object);
}
}
else if (object instanceof Function) {
System.out.println("-> " + cx.decompileFunction((Function) object, 3).trim());
}
else {
printPlainObject(object);
}
}
else {
printPlainObject(object);
}
}
private static void printPlainObject(Object object) {
try {
if (object.getClass().getMethod("toString").getDeclaringClass() != Object.class) {
System.out.println("-> " + defaultToString(object) + ":");
}
}
catch (Exception ignored) {}
System.out.println("-> " + object);
}
public static String defaultToString(Object o) {
return o.getClass().getName() + "@" + Integer.toHexString(o.hashCode());
}
public static String toStringOrNull(Object prop) {
if (prop != null &&
prop != Scriptable.NOT_FOUND &&
prop != Context.getUndefinedValue()) {
return Context.toString(prop);
}
else {
return null;
}
}
public static String toStringOrNull(Scriptable scope, String name) {
return toStringOrNull(scope.get(name, scope));
}
public static boolean toBoolean(Object prop) {
return prop == Scriptable.NOT_FOUND ? false : Context.toBoolean(prop);
}
public static boolean toBoolean(Scriptable scope, String name) {
return toBoolean(scope.get(name, scope));
}
public static Object unwrap(Object object) {
// Unwrap wrapped objects and exceptions
while (true) {
if (object instanceof JavaScriptException) {
object = ((JavaScriptException) object).getValue();
}
else if (object instanceof WrappedException) {
object = ((WrappedException) object).getWrappedException();
}
else if (object instanceof Wrapper) {
object = ((Wrapper) object).unwrap();
}
else {
break;
}
}
return object;
}
public static Object toJavaObject(Object object) {
object = unwrap(object);
// Convert to "primitive" types
if (object instanceof Scriptable) {
Scriptable js = (Scriptable) object;
if (js instanceof org.mozilla.javascript.xml.XMLObject) {
if (!"XMLList".equals(js.getClassName()) || !js.has(1, js)) {
object = ESXX.e4xToDOM(js);
}
}
else if ("Date".equals(js.getClassName())) {
object = Context.jsToJava(js, Date.class);
}
}
return object;
}
public static Scriptable toJSArray(Context cx, Scriptable scope, Object[] array) {
Object[] plain = new Object[array.length];
for (int i = 0; i < plain.length; ++i) {
plain[i] = array[i];
}
return cx.newArray(scope, plain);
}
public static class JSFilenameFilter
implements FilenameFilter {
public boolean accept(File dir, String name) {
boolean is_java = name.matches(".*\\.java");
return !is_java;
}
}
private static Pattern dotPattern = Pattern.compile("\\.");
}