| StackTrace.java |
1 /**
2 * Distribution License:
3 * JSword is free software; you can redistribute it and/or modify it under
4 * the terms of the GNU Lesser General Public License, version 2.1 or later
5 * as published by the Free Software Foundation. This program is distributed
6 * in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
7 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
8 * See the GNU Lesser General Public License for more details.
9 *
10 * The License is available on the internet at:
11 * http://www.gnu.org/copyleft/lgpl.html
12 * or by writing to:
13 * Free Software Foundation, Inc.
14 * 59 Temple Place - Suite 330
15 * Boston, MA 02111-1307, USA
16 *
17 * © CrossWire Bible Society, 2005 - 2016
18 *
19 */
20 package org.crosswire.common.util;
21
22 import java.io.PrintWriter;
23 import java.io.StringWriter;
24 import java.util.Iterator;
25 import java.util.NoSuchElementException;
26
27 /**
28 * Unscramble the current stack, and present the data from it to the user in
29 * various forms. This code is slightly dodgy in that it makes use of the way
30 * exceptions print their stack traces, however it is probably a safe enough
31 * assumption for the moment.
32 *
33 * @see gnu.lgpl.License The GNU Lesser General Public License for details.
34 * @author Joe Walker
35 */
36 public final class StackTrace {
37 /**
38 * Generate a stack trace an model it
39 */
40 public StackTrace() {
41 init(new Throwable(), 2);
42 }
43
44 /**
45 * We already have an Exception that we'd like to model
46 *
47 * @param ex
48 * The Exception to model
49 */
50 public StackTrace(Throwable ex) {
51 init(ex, 1);
52 }
53
54 /**
55 * Create a stack trace of the code at this point
56 *
57 * @param exception
58 * The Throwable containing the Stack Trace
59 * @param discard
60 * The number of uppermost stack frames to ignore
61 */
62 private void init(Throwable exception, int discard) {
63 StringWriter sout = new StringWriter();
64 exception.printStackTrace(new PrintWriter(sout));
65 String msg = new String(sout.getBuffer());
66 String[] calls = StringUtil.split(msg, "\n\r");
67
68 int total = 0;
69 for (int i = 0; i < calls.length - discard; i++) {
70 String call = calls[i + discard];
71
72 if (!(call.startsWith("Caused") || call.indexOf("...") >= 0)) {
73 total++;
74 }
75 }
76
77 classNames = new String[total];
78 methodNames = new String[total];
79 fileNames = new String[total];
80 lineNumbers = new int[total];
81
82 int j = 0;
83 for (int i = 0; i < calls.length - discard; i++) {
84 String call = calls[i + discard];
85 boolean oops = false;
86 try {
87 if (!(call.startsWith("Caused") || call.indexOf("...") >= 0)) {
88 int spcIndex = call.indexOf(' ');
89 int lhsIndex = call.indexOf('(');
90 int clnIndex = call.indexOf(':');
91 int rhsIndex = call.indexOf(')');
92
93 String fullFn = call.substring(spcIndex + 1, lhsIndex).trim();
94 int lastDot = fullFn.lastIndexOf('.');
95
96 classNames[j] = fullFn.substring(0, lastDot).replace('/', '.');
97 methodNames[j] = fullFn.substring(lastDot + 1);
98
99 if (clnIndex != -1 && lhsIndex < clnIndex) {
100 fileNames[j] = call.substring(lhsIndex + 1, clnIndex);
101 lineNumbers[j] = Integer.parseInt(call.substring(clnIndex + 1, rhsIndex));
102 } else {
103 fileNames[j] = call.substring(lhsIndex + 1, rhsIndex);
104 lineNumbers[j] = 0;
105 }
106 j++;
107 }
108 } catch (NumberFormatException ex) {
109 oops = true;
110 } catch (StringIndexOutOfBoundsException ex) {
111 // For whatever reason, Java 7 under Web Start is throwing this on
112 // call.substring(spcIndex + 1, lhsIndex) with a -56 being passed.
113 oops = true;
114 }
115 if (oops) {
116 classNames[j] = "ParseError: ";
117 methodNames[j] = call;
118 fileNames[j] = "Error";
119 lineNumbers[j] = 0;
120 j++;
121 }
122 }
123 }
124
125 /**
126 * How many stack elements are there?
127 *
128 * @return the number of stack elements
129 */
130 public int countStackElements() {
131 return methodNames.length;
132 }
133
134 /**
135 * Get the name of a function
136 *
137 * @param level
138 * Number of calling function
139 * @return the function name
140 */
141 public String getFunctionName(int level) {
142 return methodNames[level];
143 }
144
145 /**
146 * Get the name of a function including class name
147 *
148 * @param level
149 * Number of calling function
150 * @return the full function name
151 */
152 public String getFullFunctionName(int level) {
153 return classNames[level] + '.' + methodNames[level] + "()";
154 }
155
156 /**
157 * Get the name of a class
158 *
159 * @param level
160 * Number of calling function
161 * @return the class name
162 */
163 public String getClassName(int level) {
164 return classNames[level];
165 }
166
167 /**
168 * Get the name of a file
169 *
170 * @param level
171 * Number of calling function
172 * @return the file name
173 */
174 public String getFileName(int level) {
175 return fileNames[level];
176 }
177
178 /**
179 * Get the line number within a file
180 *
181 * @param level
182 * Number of calling function
183 * @return the line number
184 */
185 public int getLineNumber(int level) {
186 return lineNumbers[level];
187 }
188
189 /**
190 * Get the count of classes
191 *
192 * @return the number of classes
193 */
194 public int getClassCount() {
195 return classNames.length;
196 }
197
198 /**
199 * Get the Class that owns the function
200 *
201 * @param level
202 * Number of calling function
203 * @return the function owner
204 */
205 public Class<?> getClass(int level) {
206 try {
207 return ClassUtil.forName(classNames[level]);
208 } catch (ClassNotFoundException ex) {
209 assert false : ex;
210 return null;
211 }
212 }
213
214 /**
215 * Base class for the real enumeration implementations below
216 * @param <T> the type of the object in the stack
217 */
218 public abstract class AbstractStackIterator<T> implements Iterator<T> {
219 /* (non-Javadoc)
220 * @see java.util.Iterator#hasNext()
221 */
222 public boolean hasNext() {
223 return level < getClassCount();
224 }
225
226 /* (non-Javadoc)
227 * @see java.util.Iterator#remove()
228 */
229 public void remove() {
230 throw new UnsupportedOperationException();
231 }
232
233 /**
234 * @return the level.
235 * @throws NoSuchElementException
236 */
237 public int getAndIncrementLevel() throws NoSuchElementException {
238 return level++;
239 }
240
241 /**
242 * Are there more stack levels
243 */
244 private int level;
245 }
246
247 /**
248 * To iterate over the class names
249 *
250 * @return an iterator of class names
251 */
252 public Iterator<String> getClassNameElements() {
253 return new AbstractStackIterator<String>() {
254 public String next() throws NoSuchElementException {
255 if (!hasNext()) {
256 throw new NoSuchElementException();
257 }
258 return getClassName(getAndIncrementLevel());
259 }
260 };
261 }
262
263 /**
264 * To iterate over the function names
265 *
266 * @return an iterator of function names
267 */
268 public Iterator<String> getFunctionNameElements() {
269 return new AbstractStackIterator<String>() {
270 public String next() throws NoSuchElementException {
271 if (!hasNext()) {
272 throw new NoSuchElementException();
273 }
274 return getFunctionName(getAndIncrementLevel());
275 }
276 };
277 }
278
279 /**
280 * To iterate over the full function names
281 *
282 * @return an iterator of full function names
283 */
284 public Iterator<String> getFullFunctionNameElements() {
285 return new AbstractStackIterator<String>() {
286 public String next() throws NoSuchElementException {
287 if (!hasNext()) {
288 throw new NoSuchElementException();
289 }
290 return getFullFunctionName(getAndIncrementLevel());
291 }
292 };
293 }
294
295 /**
296 * Array containing the class names
297 */
298 private String[] classNames;
299
300 /**
301 * Array containing the method names
302 */
303 private String[] methodNames;
304
305 /**
306 * Array containing the file names
307 */
308 private String[] fileNames;
309
310 /**
311 * Array containing the line numbers
312 */
313 private int[] lineNumbers;
314 }
315