1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.struts.flow.core.location;
17
18 import java.util.ArrayList;
19 import java.util.Collections;
20 import java.util.List;
21
22 import org.apache.commons.lang.exception.ExceptionUtils;
23 import org.apache.commons.lang.exception.NestableException;
24
25 /***
26 * A cascading and located <code>Exception</code>. It is also {@link MultiLocatable} to easily build
27 * stack traces.
28 *
29 * @since 2.1.8
30 * @version $Id: LocatedException.java 291771 2005-09-26 22:32:55Z vgritsenko $
31 */
32 public class LocatedException extends NestableException
33 implements LocatableException, MultiLocatable {
34
35 private List locations;
36
37 public LocatedException(String message) {
38 this(message, null, null);
39 }
40
41 public LocatedException(String message, Throwable cause) {
42 this(message, cause, null);
43 }
44
45 public LocatedException(String message, Location location) {
46 this(message, null, location);
47 }
48
49 public LocatedException(String message, Throwable cause, Location location) {
50 super(message, cause);
51 ensureCauseChainIsSet(cause);
52 addCauseLocations(this, cause);
53 addLocation(location);
54 }
55
56 /***
57 * Crawl the cause chain and ensure causes are properly set using {@ link Throwable#initCause}.
58 * This is needed because some exceptions (e.g. SAXException) don't have a {@ link Throwable#getCause}
59 * that is used to print stacktraces.
60 */
61 public static void ensureCauseChainIsSet(Throwable thr) {
62
63 while (thr != null && !(thr instanceof LocatedRuntimeException) && !(thr instanceof LocatedException)) {
64 Throwable parent = ExceptionUtils.getCause(thr);
65 if (thr.getCause() == null && parent != null) {
66 thr.initCause(parent);
67 }
68 thr = parent;
69 }
70 }
71
72 /***
73 * Add to the location stack all locations of an exception chain. This allows to have all possible
74 * location information in the stacktrace, as some exceptions like SAXParseException don't output
75 * their location in printStackTrace().
76 * <p>
77 * Traversal of the call chain stops at the first <code>Locatable</code> exception which is supposed
78 * to handle the loction of its causes by itself.
79 * <p>
80 * This method is static as a convenience for {@link LocatedRuntimeException other implementations}
81 * of locatable exceptions.
82 *
83 * @param self the current locatable exception
84 * @param cause a cause of <code>self</code>
85 */
86 public static void addCauseLocations(MultiLocatable self, Throwable cause) {
87 if (cause == null || cause instanceof Locatable) {
88
89 return;
90 }
91
92 addCauseLocations(self, ExceptionUtils.getCause(cause));
93
94 Location loc = LocationUtils.getLocation(cause);
95 if (LocationUtils.isKnown(loc)) {
96
97 String name = cause.getClass().getName();
98 int pos = name.lastIndexOf('.');
99 if (pos != -1) {
100 name = name.substring(pos+1);
101 }
102 loc = new LocationImpl("[" + name + "]", loc.getURI(), loc.getLineNumber(), loc.getColumnNumber());
103 self.addLocation(loc);
104 }
105 }
106
107 public Location getLocation() {
108 return locations == null ? null : (Location)locations.get(0);
109 }
110
111 public List getLocations() {
112 return locations == null ? Collections.EMPTY_LIST : locations;
113 }
114
115 public String getRawMessage() {
116 return super.getMessage();
117 }
118
119 /***
120 * Standard way of building the message of a {@link LocatableException}, as a Java-like
121 * stack trace of locations.
122 *
123 * @param message the exception's message, given by <code>super.getMessage()</code> (can be null)
124 * @param locations the location list (can be null)
125 *
126 * @return the message, or <code>null</code> no message and locations were given.
127 */
128 public static String getMessage(String message, List locations) {
129 if (locations == null || locations.isEmpty()) {
130 return message;
131 }
132
133
134 StringBuffer buf = message == null ? new StringBuffer() : new StringBuffer(message);
135 for (int i = 0; i < locations.size(); i++) {
136 buf.append("\n\tat ").append(LocationUtils.toString((Location)locations.get(i)));
137 }
138 return buf.toString();
139 }
140
141 public String getMessage() {
142 return getMessage(super.getMessage(), locations);
143 }
144
145 public void addLocation(Location loc) {
146 if (LocationUtils.isUnknown(loc))
147 return;
148
149 if (locations == null) {
150 this.locations = new ArrayList(1);
151 }
152 locations.add(LocationImpl.get(loc));
153 }
154 }