001: /*
002: * @(#)StackTraceLineParser.java
003: *
004: * Copyright (C) 2002-2003 Matt Albrecht
005: * groboclown@users.sourceforge.net
006: * http://groboutils.sourceforge.net
007: *
008: * Part of the GroboUtils package at:
009: * http://groboutils.sourceforge.net
010: *
011: * Permission is hereby granted, free of charge, to any person obtaining a
012: * copy of this software and associated documentation files (the "Software"),
013: * to deal in the Software without restriction, including without limitation
014: * the rights to use, copy, modify, merge, publish, distribute, sublicense,
015: * and/or sell copies of the Software, and to permit persons to whom the
016: * Software is furnished to do so, subject to the following conditions:
017: *
018: * The above copyright notice and this permission notice shall be included in
019: * all copies or substantial portions of the Software.
020: *
021: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
022: * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
023: * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
024: * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
025: * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
026: * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
027: * DEALINGS IN THE SOFTWARE.
028: */
029: package net.sourceforge.groboutils.util.throwable.v1;
030:
031: import java.lang.reflect.Method;
032:
033: //import org.apache.log4j.Logger;
034:
035: /**
036: * Encompasses the line on which a Stack Trace occurs. Should not call
037: * this "StackTraceElement", since that's a JDK 1.4 class in the java.lang
038: * package.
039: *
040: * @author Matt Albrecht <a href="mailto:groboclown@users.sourceforge.net">groboclown@users.sourceforge.net</a>
041: * @since March 17, 2002
042: * @version $Date: 2003/09/29 21:10:36 $
043: */
044: public class StackTraceLineParser {
045: //private static final Logger LOG = Logger.getLogger(
046: // StackTraceLineParser.class.getName() );
047:
048: private String className;
049: private String methodName;
050: private int lineNumber;
051:
052: private static final String JDK14_STE_CLASS = "java.lang.StackTraceElement";
053: private static final String JDK14_STE_GCN = "getClassName";
054: private static final Class[] JDK14_STE_GCN_ARGS = new Class[0];
055: private static final String JDK14_STE_GLN = "getLineNumber";
056: private static final Class[] JDK14_STE_GLN_ARGS = new Class[0];
057: private static final String JDK14_STE_GMN = "getMethodName";
058: private static final Class[] JDK14_STE_GMN_ARGS = new Class[0];
059:
060: private static final String ST_START = "at ";
061:
062: // Check if we are running in IBM's visual age.
063: // - it has a different stack trace
064: private static boolean IN_VISUAL_AGE = false;
065: static {
066: try {
067: Class dummy = Class
068: .forName("com.ibm.uvm.tools.DebugSupport");
069: IN_VISUAL_AGE = true;
070: //LOG.info("Using VisualAge style stack traces.");
071: } catch (Throwable e) {
072: // ignore. We're not in visual age.
073: //LOG.info("Using standard JDK style stack traces.");
074: }
075: }
076:
077: public StackTraceLineParser(String className, String methodName) {
078: this (className, methodName, -1);
079: }
080:
081: public StackTraceLineParser(String className, String methodName,
082: int lineNumber) {
083: if (className == null || methodName == null) {
084: throw new IllegalArgumentException("no null arguments");
085: }
086: if (lineNumber < 0) {
087: lineNumber = -1;
088: }
089: this .lineNumber = lineNumber;
090: this .className = className;
091: this .methodName = methodName;
092: }
093:
094: /**
095: * Parses a JDK 1.4 StackTraceElement or a stack trace line.
096: *
097: * @exception IllegalArgumentException if this is not a recognizable stack
098: * trace line, or is <tt>null</tt>, or is not a StackTraceElement.
099: */
100: public StackTraceLineParser(Object obj) {
101: if (obj == null) {
102: throw new IllegalArgumentException("no null arguments");
103: }
104:
105: if (obj instanceof String) {
106: parseLine((String) obj);
107: } else {
108: parseElement(obj);
109: }
110: }
111:
112: public String getClassName() {
113: return this .className;
114: }
115:
116: public String getMethodName() {
117: return this .methodName;
118: }
119:
120: public int getLineNumber() {
121: return this .lineNumber;
122: }
123:
124: public String toString() {
125: return getClassName() + '#' + getMethodName() + " (line "
126: + getLineNumber() + ')';
127: }
128:
129: //-------------------------------------------------------------------------
130: // Protected methods
131:
132: protected void parseLine(String line) {
133: String nl = line.trim();
134: //LOG.debug("parsing trace line '"+line+"'.");
135: int startPos = 0;
136: if (!IN_VISUAL_AGE) {
137: if (nl.startsWith(ST_START)) {
138: startPos += ST_START.length();
139: } else {
140: //LOG.debug("trace line does not start with '"+ST_START+"'.");
141: notValidStackTraceLine(line);
142: }
143: }
144:
145: int endNamePos = nl.lastIndexOf('(');
146: if (endNamePos < startPos) {
147: //LOG.debug("no '(' in trace line.");
148: notValidStackTraceLine(line);
149: }
150:
151: int lastDot = nl.lastIndexOf('.', endNamePos);
152: if (lastDot < startPos) {
153: //LOG.debug("No '.' in trace line before last '(', which is at "+
154: // endNamePos+".");
155: notValidStackTraceLine(line);
156: }
157:
158: this .methodName = findMethodName(nl, startPos, lastDot,
159: endNamePos);
160: this .className = findClassName(nl, startPos, lastDot,
161: endNamePos);
162: this .lineNumber = findLineNumber(nl, startPos, lastDot,
163: endNamePos);
164: }
165:
166: /**
167: *
168: */
169: protected String findMethodName(String line, int startPos,
170: int lastDot, int endNamePos) {
171: String name = line.substring(lastDot + 1, endNamePos).trim();
172: //LOG.debug("Parsed method name out to '"+name+"'.");
173: return name;
174: }
175:
176: /**
177: *
178: */
179: protected String findClassName(String line, int startPos,
180: int lastDot, int endNamePos) {
181: String name = line.substring(startPos, lastDot).trim();
182: //LOG.debug("Parsed class name out to '"+name+"'.");
183: return name;
184: }
185:
186: /**
187: *
188: */
189: protected int findLineNumber(String line, int startPos,
190: int lastDot, int endNamePos) {
191: int ln = -1;
192: if (!IN_VISUAL_AGE) {
193: int lineNumberStart = line.indexOf(':', endNamePos);
194: if (lineNumberStart > 0) {
195: //LOG.debug("line number is at position "+lineNumberStart+".");
196: int lineNumberEnd = line.indexOf(')', lineNumberStart);
197: if (lineNumberEnd > 0) {
198: //LOG.debug("final ')', after line number, is at "+
199: // lineNumberEnd+".");
200: try {
201: String lineNumber = line.substring(
202: lineNumberStart + 1, lineNumberEnd);
203: //LOG.debug("line number text is "+lineNumber+".");
204: ln = Integer.parseInt(lineNumber);
205: } catch (NumberFormatException e) {
206: //LOG.debug("line number text is not a number.");
207: ln = -1;
208: }
209: } else {
210: //LOG.debug("stack trace text does not have a final ')'");
211: }
212: } else {
213: //LOG.debug("stack trace text did not provide line number "+
214: // "information.");
215: }
216: }
217: return ln;
218: }
219:
220: /**
221: * We may not be in JDK 1.4 during compilation or runtime, but we
222: * can support it through reflection anyway.
223: */
224: protected void parseElement(Object el) {
225: Class c = el.getClass();
226: if (String.class.equals(c)) {
227: // specialized method for internal usage checks.
228: throw new IllegalStateException("Incorrect method invoked.");
229: }
230: if (!JDK14_STE_CLASS.equals(c.getName())) {
231: throw new IllegalArgumentException("Object " + el
232: + " is not of type " + JDK14_STE_CLASS
233: + ", but rather of type " + c.getName());
234: }
235:
236: Method gcn;
237: Method gln;
238: Method gmn;
239: try {
240: gcn = c
241: .getDeclaredMethod(JDK14_STE_GCN,
242: JDK14_STE_GCN_ARGS);
243: gln = c
244: .getDeclaredMethod(JDK14_STE_GLN,
245: JDK14_STE_GLN_ARGS);
246: gmn = c
247: .getDeclaredMethod(JDK14_STE_GMN,
248: JDK14_STE_GMN_ARGS);
249: } catch (NoSuchMethodException nsme) {
250: throw new IllegalArgumentException("Object " + el
251: + " does not support the JDK 1.4 specification of "
252: + JDK14_STE_CLASS);
253: }
254:
255: String cn;
256: int ln;
257: String mn;
258: try {
259: cn = (String) gcn.invoke(el, null);
260: ln = ((Integer) gln.invoke(el, null)).intValue();
261: mn = (String) gmn.invoke(el, null);
262: } catch (Exception e) {
263: throw new IllegalArgumentException(
264: "Object "
265: + el
266: + " could not retrieve values for JDK 1.4 specification of "
267: + JDK14_STE_CLASS);
268: }
269:
270: this .className = cn;
271: this .lineNumber = ln;
272: this .methodName = mn;
273: }
274:
275: protected void notValidStackTraceLine(String line) {
276: throw new IllegalArgumentException("Line [" + line
277: + "] is not a valid stack-trace line");
278: }
279: }
|