001: /*
002: * Copyright (c) 2002-2008 Gargoyle Software Inc. All rights reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * 1. Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: * 2. Redistributions in binary form must reproduce the above copyright notice,
010: * this list of conditions and the following disclaimer in the documentation
011: * and/or other materials provided with the distribution.
012: * 3. The end-user documentation included with the redistribution, if any, must
013: * include the following acknowledgment:
014: *
015: * "This product includes software developed by Gargoyle Software Inc.
016: * (http://www.GargoyleSoftware.com/)."
017: *
018: * Alternately, this acknowledgment may appear in the software itself, if
019: * and wherever such third-party acknowledgments normally appear.
020: * 4. The name "Gargoyle Software" must not be used to endorse or promote
021: * products derived from this software without prior written permission.
022: * For written permission, please contact info@GargoyleSoftware.com.
023: * 5. Products derived from this software may not be called "HtmlUnit", nor may
024: * "HtmlUnit" appear in their name, without prior written permission of
025: * Gargoyle Software Inc.
026: *
027: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
028: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
029: * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GARGOYLE
030: * SOFTWARE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
031: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
032: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
033: * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
034: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
035: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
036: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
037: */
038: package com.gargoylesoftware.htmlunit;
039:
040: import java.io.BufferedReader;
041: import java.io.IOException;
042: import java.io.PrintStream;
043: import java.io.PrintWriter;
044: import java.io.StringReader;
045: import java.io.StringWriter;
046: import java.util.StringTokenizer;
047:
048: import org.mozilla.javascript.EcmaError;
049: import org.mozilla.javascript.JavaScriptException;
050: import org.mozilla.javascript.RhinoException;
051: import org.mozilla.javascript.WrappedException;
052:
053: import com.gargoylesoftware.htmlunit.html.HtmlPage;
054:
055: /**
056: * An exception that will be thrown if an error occurs during the processing of
057: * a script.
058: *
059: * @version $Revision: 2132 $
060: * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
061: * @author Marc Guillemot
062: */
063: public class ScriptException extends RuntimeException {
064:
065: private static final long serialVersionUID = 4788896649084231283L;
066: private final String scriptSourceCode_;
067: private final HtmlPage page_;
068:
069: /**
070: * Creates an instance.
071: * @param page the page in which the script causing this exception was executed
072: * @param throwable The exception that was thrown from the script engine.
073: * @param scriptSourceCode The code that was being executed when this exception
074: * was thrown. This may be null if the exception was not caused by execution
075: * of javascript.
076: */
077: public ScriptException(final HtmlPage page,
078: final Throwable throwable, final String scriptSourceCode) {
079: super (getMessageFrom(throwable), throwable);
080: scriptSourceCode_ = scriptSourceCode;
081: page_ = page;
082: }
083:
084: private static String getMessageFrom(final Throwable throwable) {
085: if (throwable == null) {
086: return "null";
087: } else {
088: return throwable.getMessage();
089: }
090: }
091:
092: /**
093: * Create an instance
094: * @param page the page in which the script causing this exception was executed
095: * @param throwable The exception that was thrown from the script engine.
096: */
097: public ScriptException(final HtmlPage page,
098: final Throwable throwable) {
099: this (page, throwable, null);
100: }
101:
102: /**
103: * Print the stack trace to System.out. If this exception contains another
104: * exception then the stack traces for both will be printed.
105: */
106: public void printStackTrace() {
107: printStackTrace(System.out);
108: }
109:
110: /**
111: * Print the stack trace. If this exception contains another exception then
112: * the stack traces for both will be printed.
113: *
114: * @param writer Where the stack trace will be written
115: */
116: public void printStackTrace(final PrintWriter writer) {
117: writer.write(createPrintableStackTrace());
118: }
119:
120: /**
121: * Print the stack trace. If this exception contains another exception then
122: * the stack traces for both will be printed.
123: *
124: * @param stream Where the stack trace will be written
125: */
126: public void printStackTrace(final PrintStream stream) {
127: stream.print(createPrintableStackTrace());
128: }
129:
130: private String createPrintableStackTrace() {
131: final StringWriter stringWriter = new StringWriter();
132: final PrintWriter printWriter = new PrintWriter(stringWriter);
133:
134: printWriter.println("======= EXCEPTION START ========");
135:
136: if (getCause() != null) {
137: if (getCause() instanceof EcmaError) {
138: final EcmaError ecmaError = (EcmaError) getCause();
139: printWriter.print("EcmaError: ");
140: printWriter.print("lineNumber=[");
141: printWriter.print(ecmaError.lineNumber());
142: printWriter.print("] column=[");
143: printWriter.print(ecmaError.columnNumber());
144: printWriter.print("] lineSource=[");
145: printWriter.print(getFailingLine());
146: printWriter.print("] name=[");
147: printWriter.print(ecmaError.getName());
148: printWriter.print("] sourceName=[");
149: printWriter.print(ecmaError.sourceName());
150: printWriter.print("] message=[");
151: printWriter.print(ecmaError.getMessage());
152: printWriter.print("]");
153: printWriter.println();
154: } else {
155: printWriter.println("Exception class=["
156: + getCause().getClass().getName() + "]");
157: }
158: }
159:
160: super .printStackTrace(printWriter);
161: if (getCause() != null
162: && getCause() instanceof JavaScriptException) {
163: final Object value = ((JavaScriptException) getCause())
164: .getValue();
165:
166: printWriter.print("JavaScriptException value = ");
167: if (value instanceof Throwable) {
168: ((Throwable) value).printStackTrace(printWriter);
169: } else {
170: printWriter.println(value);
171: }
172: } else if (getCause() != null
173: && getCause() instanceof WrappedException) {
174: final WrappedException wrappedException = (WrappedException) getCause();
175: printWriter.print("WrappedException: ");
176: wrappedException.printStackTrace(printWriter);
177:
178: final Throwable innerException = wrappedException
179: .getWrappedException();
180: if (innerException == null) {
181: printWriter.println("Inside wrapped exception: null");
182: } else {
183: printWriter.println("Inside wrapped exception:");
184: innerException.printStackTrace(printWriter);
185: }
186: } else if (getCause() != null) {
187: printWriter.println("Enclosed exception: ");
188: getCause().printStackTrace(printWriter);
189: }
190:
191: if (scriptSourceCode_ != null && scriptSourceCode_.length() > 0) {
192: printWriter.println("== CALLING JAVASCRIPT ==");
193: printWriter.println(scriptSourceCode_);
194: }
195: printWriter.println("======= EXCEPTION END ========");
196:
197: return stringWriter.toString();
198: }
199:
200: /**
201: * Return the source code line that failed
202: * @return the source code line that failed
203: */
204: public String getScriptSourceCode() {
205: return scriptSourceCode_;
206: }
207:
208: /**
209: * Return the line of source that was being executed when this exception was
210: * thrown.
211: *
212: * @return The line of source or an empty string if the exception was not thrown
213: * due to the execution of a script.
214: */
215: public String getFailingLine() {
216: final int lineNumber = getFailingLineNumber();
217: if (lineNumber == -1 || scriptSourceCode_ == null) {
218: return "<no source>";
219: }
220:
221: try {
222: final BufferedReader reader = new BufferedReader(
223: new StringReader(scriptSourceCode_));
224: for (int i = 0; i < lineNumber - 1; i++) {
225: reader.readLine();
226: }
227: final String result = reader.readLine();
228: reader.close();
229: return result;
230: } catch (final IOException e) {
231: // Theoretically impossible
232: e.printStackTrace();
233: }
234: return "";
235: }
236:
237: /**
238: * Return the line number of the source that was executing at the time of the
239: * exception.
240: *
241: * @return The line number or -1 if the exception was not thrown due to the
242: * execution of a script.
243: */
244: public int getFailingLineNumber() {
245: if (getCause() instanceof RhinoException) {
246: final RhinoException cause = (RhinoException) getCause();
247: return cause.lineNumber();
248: }
249:
250: return -1;
251: }
252:
253: /**
254: * Gets the html page in which the script error occurred.<br/>
255: * Caution: this page may be only partially parsed if the exception occurred in a script
256: * executed at parsing time.
257: * @return the page
258: */
259: public HtmlPage getPage() {
260: return page_;
261: }
262:
263: /**
264: * Print the script stack trace.
265: * This represents only the script calls.
266: * @param writer where the stack trace will be written
267: */
268: public void printScriptStackTrace(final PrintWriter writer) {
269: final StringWriter stringWriter = new StringWriter();
270: final PrintWriter printWriter = new PrintWriter(stringWriter);
271:
272: getCause().printStackTrace(printWriter);
273:
274: writer.print(getCause().getMessage());
275: final StringTokenizer st = new StringTokenizer(stringWriter
276: .toString(), "\r\n");
277: while (st.hasMoreTokens()) {
278: final String line = st.nextToken();
279: if (line.indexOf("at script") != -1) {
280: writer.println();
281: writer.print(line.replaceFirst("at script\\.?", "at "));
282: }
283: }
284: }
285:
286: }
|