/* * Scriptographer * * This file is part of Scriptographer, a Scripting Plugin for Adobe Illustrator * http://scriptographer.org/ * * Copyright (c) 2002-2010, Juerg Lehni * http://scratchdisk.com/ * * All rights reserved. See LICENSE file for details. * * File created on Apr 10, 2007. */ package com.scratchdisk.script.rhino; import java.io.File; import java.io.PrintWriter; import java.io.StringWriter; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.mozilla.javascript.JavaScriptException; import org.mozilla.javascript.RhinoException; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.ScriptableObject; import org.mozilla.javascript.WrappedException; import org.mozilla.javascript.Wrapper; import com.scratchdisk.script.ScriptException; import com.scratchdisk.util.StringUtils; /** * ScriptException for Rhino, preferably called RhinoException, but * that's already used by Rhino (org.mozilla.javascript.RhinoException). */ public class RhinoScriptException extends ScriptException { private RhinoEngine engine; private static Throwable getCause(Throwable cause) { // Unwrap multiple wrapped exceptions, but make sure we have one // WrappedException that contains information about script and line // number. if (cause instanceof WrappedException) { Throwable wrapped = ((WrappedException) cause).getWrappedException(); // Unwrap wrapped RhinoScriptExceptions if wrapped more than once if (wrapped instanceof RhinoScriptException) cause = ((RhinoScriptException) wrapped).getCause(); // Unwrapped multiply wrapped Rhino Exceptions if (wrapped instanceof RhinoException) cause = wrapped; } else if (cause instanceof JavaScriptException) { JavaScriptException jse = (JavaScriptException) cause; Object value = jse.getValue(); if (value instanceof Wrapper) { value = ((Wrapper) value).unwrap(); } else if (value instanceof Scriptable) { value = ScriptableObject.getProperty((Scriptable) value, "exception"); } if (value instanceof Throwable) return (Throwable) value; } return cause; } private static String getMessage(Throwable cause) { // Do not use RhinoException#getMessage for short messages // since it adds line number information. Use #details instead. if (cause instanceof RhinoException) { return ((RhinoException) cause).details(); } else { return cause.getMessage(); } } public String getFullMessage() { Throwable cause = getCause(); String separator = System.getProperty("file.separator"); if (cause instanceof RhinoException) { RhinoException re = (RhinoException) cause; StringWriter buf = new StringWriter(); PrintWriter writer = new PrintWriter(buf); if (re instanceof WrappedException) { // Make sure we're not printing the "Wrapped ...Exception:" part writer.println(((WrappedException) re).getWrappedException() .getMessage()); } else { writer.println(re.details()); } String[] stackTrace = re.getScriptStackTrace().split("\\r\\n|\\n|\\r"); String sourceName = re.sourceName(); if (sourceName != null) { int lineNumber = re.lineNumber(); // Report sourceName / lineNumber if it is not in the stack // trace already. // TODO Why is this needed? Rhino bug? if (stackTrace.length == 0 || stackTrace[0].indexOf( sourceName + ":" + lineNumber) == -1) { String[] path = engine.getScriptPath(new File(sourceName)); if (path != null) writer.println("\tat " + StringUtils.join(path, separator) + ":" + lineNumber); } } // Parse the lines for filename:linenumber Pattern pattern = Pattern.compile("\\s+at\\s+(.+):(\\d+)"); for (int i = 0; i < stackTrace.length; i++) { String line = stackTrace[i]; Matcher matcher = pattern.matcher(line); if (matcher.find()) { String file = matcher.group(1); // Filter out hidden scripts. Only report scripts // that are located in base: if (file.indexOf(separator + "__") == -1) { String[] path = engine.getScriptPath(new File(file)); if (path != null) { writer.println("\tat " + StringUtils.join(path, separator) + ":" + matcher.group(2)); } } } } return buf.toString().trim(); } else { String message = cause.getMessage(); String error = cause.getClass().getSimpleName(); if (message != null && message.length() != 0) error += ": " + message; return error; } } public Throwable getWrappedException() { Throwable cause = getCause(); if (cause instanceof WrappedException) cause = ((WrappedException) cause).getWrappedException(); return cause; } public RhinoScriptException(RhinoEngine engine, Throwable cause) { super(getMessage(getCause(cause)), getCause(cause)); this.engine = engine; } }