107 |
* |
* |
108 |
* @author Brian Jones |
* @author Brian Jones |
109 |
* @author John Keiser |
* @author John Keiser |
110 |
|
* @author Mark Wielaard |
111 |
* @author Eric Blake <ebb9@email.byu.edu> |
* @author Eric Blake <ebb9@email.byu.edu> |
112 |
* @since 1.0 |
* @since 1.0 |
113 |
* @status still missing 1.4 functionality |
* @status still missing 1.4 functionality |
123 |
* The detail message. |
* The detail message. |
124 |
* |
* |
125 |
* @serial specific details about the exception, may be null |
* @serial specific details about the exception, may be null |
|
* @XXX for serialization, renaming this detailMessage would be nice |
|
126 |
*/ |
*/ |
127 |
private String message = null; |
private String detailMessage; |
128 |
|
|
129 |
/** |
/** |
130 |
* The cause of the throwable, including null for an unknown or non-chained |
* The cause of the throwable, including null for an unknown or non-chained |
133 |
* |
* |
134 |
* @serial the cause, or null if unknown, or this if not yet set |
* @serial the cause, or null if unknown, or this if not yet set |
135 |
* @since 1.4 |
* @since 1.4 |
|
* @XXX for 1.4 compatibility, add this field |
|
|
private Throwable cause = this; |
|
136 |
*/ |
*/ |
137 |
|
private Throwable cause = this; |
138 |
|
|
139 |
/** |
/** |
140 |
* The stack trace, in a serialized form. |
* The stack trace, in a serialized form. |
142 |
* @serial the elements of the stack trace; this is non-null, and has |
* @serial the elements of the stack trace; this is non-null, and has |
143 |
* no null entries |
* no null entries |
144 |
* @since 1.4 |
* @since 1.4 |
|
* @XXX for 1.4 compatibility, add this field |
|
|
private StackTraceElement[] stackTrace; |
|
145 |
*/ |
*/ |
146 |
|
// XXX Don't initialize this, once fillInStackTrace() does it. |
147 |
|
private StackTraceElement[] stackTrace = {}; |
148 |
|
|
149 |
/** |
/** |
150 |
* Instantiate this Throwable with an empty message. The cause remains |
* Instantiate this Throwable with an empty message. The cause remains |
153 |
*/ |
*/ |
154 |
public Throwable() |
public Throwable() |
155 |
{ |
{ |
156 |
this(null); |
this((String) null); |
157 |
} |
} |
158 |
|
|
159 |
/** |
/** |
166 |
public Throwable(String message) |
public Throwable(String message) |
167 |
{ |
{ |
168 |
fillInStackTrace(); |
fillInStackTrace(); |
169 |
this.message = message; |
detailMessage = message; |
170 |
} |
} |
171 |
|
|
172 |
/** |
/** |
177 |
* @param message the message to associate with the Throwable |
* @param message the message to associate with the Throwable |
178 |
* @param cause the cause, may be null |
* @param cause the cause, may be null |
179 |
* @since 1.4 |
* @since 1.4 |
180 |
* @XXX for 1.4 compatibility, add this constructor |
*/ |
181 |
public Throwable(String message, Throwable cause) |
public Throwable(String message, Throwable cause) |
182 |
{ |
{ |
183 |
this(message); |
this(message); |
184 |
initCause(cause); |
initCause(cause); |
185 |
} |
} |
|
*/ |
|
186 |
|
|
187 |
/** |
/** |
188 |
* Instantiate this Throwable with the given cause. The message is then |
* Instantiate this Throwable with the given cause. The message is then |
191 |
* |
* |
192 |
* @param cause the cause, may be null |
* @param cause the cause, may be null |
193 |
* @since 1.4 |
* @since 1.4 |
194 |
* @XXX for 1.4 compatibility, add this constructor |
*/ |
195 |
public Throwable(Throwable cause) |
public Throwable(Throwable cause) |
196 |
{ |
{ |
197 |
this(cause == null ? null : cause.toString(), cause); |
this(cause == null ? null : cause.toString(), cause); |
198 |
} |
} |
|
*/ |
|
199 |
|
|
200 |
/** |
/** |
201 |
* Get the message associated with this Throwable. |
* Get the message associated with this Throwable. |
204 |
*/ |
*/ |
205 |
public String getMessage() |
public String getMessage() |
206 |
{ |
{ |
207 |
return message; |
return detailMessage; |
208 |
} |
} |
209 |
|
|
210 |
/** |
/** |
229 |
* |
* |
230 |
* @return the cause of this Throwable |
* @return the cause of this Throwable |
231 |
* @since 1.4 |
* @since 1.4 |
232 |
* @XXX for 1.4 compatibility, add this method |
*/ |
233 |
public Throwable getCause() |
public Throwable getCause() |
234 |
{ |
{ |
235 |
return cause == this ? null : cause; |
return cause == this ? null : cause; |
236 |
} |
} |
|
*/ |
|
237 |
|
|
238 |
/** |
/** |
239 |
* Initialize the cause of this Throwable. This may only be called once |
* Initialize the cause of this Throwable. This may only be called once |
246 |
* its own cause!) |
* its own cause!) |
247 |
* @throws IllegalStateException if the cause has already been set |
* @throws IllegalStateException if the cause has already been set |
248 |
* @since 1.4 |
* @since 1.4 |
249 |
* @XXX for 1.4 compatibility, add this method |
*/ |
250 |
public Throwable initCause(Throwable cause) |
public Throwable initCause(Throwable cause) |
251 |
{ |
{ |
252 |
if (cause == this) |
if (cause == this) |
256 |
this.cause = cause; |
this.cause = cause; |
257 |
return this; |
return this; |
258 |
} |
} |
|
*/ |
|
259 |
|
|
260 |
/** |
/** |
261 |
* Get a human-readable representation of this Throwable. With a null |
* Get a human-readable representation of this Throwable. With a null |
267 |
*/ |
*/ |
268 |
public String toString() |
public String toString() |
269 |
{ |
{ |
270 |
return getClass().getName() + (message != null ? ": " + message : ""); |
return getClass().getName() |
271 |
|
+ (detailMessage == null ? "" : ": " + detailMessage); |
272 |
} |
} |
273 |
|
|
274 |
/** |
/** |
306 |
* static void b() throws MidLevelException |
* static void b() throws MidLevelException |
307 |
* { |
* { |
308 |
* c(); |
* c(); |
309 |
* } |
* } |
310 |
* static void c() throws MidLevelException |
* static void c() throws MidLevelException |
311 |
* { |
* { |
312 |
* try |
* try |
369 |
*/ |
*/ |
370 |
public void printStackTrace(PrintStream s) |
public void printStackTrace(PrintStream s) |
371 |
{ |
{ |
372 |
s.println(toString()); |
printStackTrace(new PrintWriter(s)); |
|
printStackTrace0(s); |
|
373 |
} |
} |
374 |
|
|
375 |
/** |
/** |
381 |
*/ |
*/ |
382 |
public void printStackTrace(PrintWriter w) |
public void printStackTrace(PrintWriter w) |
383 |
{ |
{ |
384 |
w.println(toString()); |
//XXX - should be printStackTrace1(w); |
385 |
printStackTrace0(w); |
printStackTrace0(w); |
386 |
} |
} |
387 |
|
|
388 |
/** |
/** |
389 |
* The implentation for printing a stack trace. |
* The implentation for printing a stack trace. |
390 |
* |
* |
391 |
* @param stream either a PrintStream or PrintWriter |
* @param stream the destination stream |
392 |
*/ |
*/ |
393 |
private native void printStackTrace0 (Object stream); |
private native void printStackTrace0(PrintWriter stream); |
394 |
|
|
395 |
|
/** |
396 |
|
* Alternative printStackTrace that uses <code>getStrackTrace0()</code> |
397 |
|
* to print the exception, stack trace and cause (recursively). Not used |
398 |
|
* since <code>getStackTrace()</code> does not yet work. |
399 |
|
* |
400 |
|
* <p>Prints the exception, the detailed message and the stack trace |
401 |
|
* associated with this Throwable to the given <code>PrintWriter</code>. |
402 |
|
* The actual output written is implemention specific. Use the result of |
403 |
|
* <code>getStackTrace()</code> when more precise information is needed. |
404 |
|
* |
405 |
|
* <p>This implementation first prints a line with the result of this |
406 |
|
* object's <code>toString()</code> method. |
407 |
|
* <br> |
408 |
|
* Then for all elements given by <code>getStackTrace</code> it prints |
409 |
|
* a line containing a tab, the string "at " and the result of calling the |
410 |
|
* <code>toString()</code> method on the <code>StackTraceElement</code> |
411 |
|
* object. If <code>getStackTrace()</code> returns an empty array it prints |
412 |
|
* a line containing a tab and the string "<<No stacktrace available>>". |
413 |
|
* <br> |
414 |
|
* Then if <code>getCause()</code> doesn't return null it adds a line |
415 |
|
* starting with "Caused by: " and the result of calling |
416 |
|
* <code>toString()</code> on the cause. |
417 |
|
* <br> |
418 |
|
* Then for every cause (of a cause, etc) the stacktrace is printed the |
419 |
|
* same as for the top level <code>Throwable</code> except that as soon |
420 |
|
* as all the remaining stack frames of the cause are the same as the |
421 |
|
* the last stack frames of the throwable that the cause is wrapped in |
422 |
|
* then a line starting with a tab and the string "... X more" is printed, |
423 |
|
* where X is the number of remaining stackframes. |
424 |
|
*/ |
425 |
|
private void printStackTrace1(PrintWriter pw) |
426 |
|
{ |
427 |
|
// First line |
428 |
|
pw.println(toString()); |
429 |
|
|
430 |
|
// The stacktrace |
431 |
|
StackTraceElement[] stack = getStackTrace(); |
432 |
|
for (int i = 0; i < stack.length; i++) |
433 |
|
pw.println("\tat " + stack[i]); |
434 |
|
|
435 |
|
// The cause(s) |
436 |
|
Throwable cause = getCause(); |
437 |
|
while (cause != null) |
438 |
|
{ |
439 |
|
// Cause first line |
440 |
|
pw.println("Caused by: " + cause); |
441 |
|
|
442 |
|
// Cause stacktrace |
443 |
|
StackTraceElement[] parentStack = stack; |
444 |
|
stack = cause.getStackTrace(); |
445 |
|
boolean equal = false; // Is rest of stack equal to parent frame? |
446 |
|
for (int i = 0; i < stack.length && ! equal; i++) |
447 |
|
{ |
448 |
|
// Check if we already printed the rest of the stack |
449 |
|
// since it was the tail of the parent stack |
450 |
|
int remaining = stack.length - i; |
451 |
|
int element = i; |
452 |
|
int parentElement = parentStack.length - remaining; |
453 |
|
equal = parentElement >= 0 |
454 |
|
&& parentElement < parentStack.length; // be optimistic |
455 |
|
while (equal && element < stack.length) |
456 |
|
{ |
457 |
|
if (stack[element].equals(parentStack[parentElement])) |
458 |
|
{ |
459 |
|
element++; |
460 |
|
parentElement++; |
461 |
|
} |
462 |
|
else |
463 |
|
equal = false; |
464 |
|
} |
465 |
|
// Print stacktrace element or indicate the rest is equal |
466 |
|
if (! equal) |
467 |
|
pw.println("\tat " + stack[i]); |
468 |
|
else |
469 |
|
{ |
470 |
|
pw.println("\t..." + remaining + " more"); |
471 |
|
break; // from stack printing for loop |
472 |
|
} |
473 |
|
} |
474 |
|
cause = cause.getCause(); |
475 |
|
} |
476 |
|
} |
477 |
|
|
478 |
/** |
/** |
479 |
* Fill in the stack trace with the current execution stack. |
* Fill in the stack trace with the current execution stack. |
495 |
* |
* |
496 |
* @return an array of stack trace information, as available from the VM |
* @return an array of stack trace information, as available from the VM |
497 |
* @since 1.4 |
* @since 1.4 |
498 |
* @XXX for 1.4 compatibility, add this method |
*/ |
499 |
public StackTraceElement[] getStackTrace() |
public StackTraceElement[] getStackTrace() |
500 |
{ |
{ |
501 |
return stackTrace; |
return stackTrace; |
502 |
} |
} |
|
*/ |
|
503 |
|
|
504 |
/** |
/** |
505 |
* Change the stack trace manually. This method is designed for remote |
* Change the stack trace manually. This method is designed for remote |
509 |
* @param stackTrace the new trace to use |
* @param stackTrace the new trace to use |
510 |
* @throws NullPointerException if stackTrace is null or has null elements |
* @throws NullPointerException if stackTrace is null or has null elements |
511 |
* @since 1.4 |
* @since 1.4 |
512 |
* @XXX for 1.4 compatibility, add this method |
*/ |
513 |
public void setStackTrace(StackTraceElement[] stackTrace) |
public void setStackTrace(StackTraceElement[] stackTrace) |
514 |
{ |
{ |
515 |
for (int i = stackTrace.length; --i >= 0; ) |
for (int i = stackTrace.length; --i >= 0; ) |
517 |
throw new NullPointerException(); |
throw new NullPointerException(); |
518 |
this.stackTrace = stackTrace; |
this.stackTrace = stackTrace; |
519 |
} |
} |
|
*/ |
|
|
|
|
|
/** |
|
|
* Serialize the object in a manner binary compatible with the JDK 1.2. |
|
|
* |
|
|
* @param s the stream to write to |
|
|
* @throws IOException if the write fails |
|
|
*/ |
|
|
private void writeObject(ObjectOutputStream s) |
|
|
throws IOException |
|
|
{ |
|
|
ObjectOutputStream.PutField oFields; |
|
|
oFields = s.putFields(); |
|
|
oFields.put("detailMessage", message); |
|
|
s.writeFields(); |
|
|
} |
|
|
|
|
|
/** |
|
|
* Deserialize the object in a manner binary compatible with the JDK 1.2. |
|
|
* |
|
|
* @param s the stream to read from |
|
|
* @throws IOException if the read fails |
|
|
* @throws ClassNotFoundException if deserialization fails |
|
|
*/ |
|
|
private void readObject(ObjectInputStream s) |
|
|
throws IOException, ClassNotFoundException |
|
|
{ |
|
|
ObjectInputStream.GetField oFields; |
|
|
oFields = s.readFields(); |
|
|
message = (String)oFields.get("detailMessage", (String)null); |
|
|
} |
|
520 |
} |
} |