/* * $Id$ * * Copyright 2006, The jCoderZ.org Project. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * Neither the name of the jCoderZ.org Project nor the names of * its contributors may be used to endorse or promote products * derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.jcoderz.commons.logging; import java.nio.CharBuffer; import java.text.ParseException; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * This helper class is used to parse a CharBuffer containing data of one * line of a StackTrace. * A Stacktrace line is one of: * <ul> * <li><code>at packagename.classname.methodname(</code>...<code>)</code> * <li><code>Caused by: </code>... * <li><code>...</code> nn <code>more</code> * <li><code>exception message</code> * </ul> * */ public final class StackTraceElementParser { private static final Pattern FULL_LOCATION_PATTERN = Pattern.compile( "^\\s*at\\s+[^\\s\\.]([^\\s\\(]+\\.)*[^\\s\\(]+\\(.*\\)\\s*$"); private static final Pattern LOCATION_PATTERN = Pattern.compile( "^\\s*at\\s+"); private static final Pattern CLASS_PATTERN = Pattern.compile( "([^\\s\\(]+\\.)*"); private static final Pattern METHOD_PATTERN = Pattern.compile( "[^\\s\\(]+\\s*\\("); private static final Pattern LINE_PATTERN = Pattern.compile( "[^\\d\\)]+"); private static final Pattern FULL_CAUSE_PATTERN = Pattern.compile( "^\\s*Caused by\\:\\s*.*$"); private static final Pattern CAUSE_PATTERN = Pattern.compile( "^\\s*Caused by\\:\\s*"); private static final Pattern FULL_MORE_PATTERN = Pattern.compile( "^\\s*\\.+\\s*\\d+\\s+more\\s*$"); private static final Pattern MORE_PATTERN = Pattern.compile( "^\\s*\\.+\\s*"); private static final Pattern FULL_EXCEPTION_TEXT_PATTERN = Pattern.compile( "^\\s*.*$"); private static final Pattern EXCEPTION_TEXT_PATTERN = Pattern.compile( "^\\s*"); /** * Hide the default constructor. */ private StackTraceElementParser () { // nop } /** * Parses the supplied buffer for the parameters of a stacktrace element and * returns the information as StackTraceInfo. * * @param buffer The buffer containing data of one stack trace line. * * @return A new instance of StackTraceInfo containing the information of the * parsed line. * * @throws ParseException if an error occurs. */ public static StackTraceInfo parse (final CharBuffer buffer) throws ParseException { StackTraceInfo rc = null; StackTraceInfo stInfo = null; if ((stInfo = parseLocation(buffer)) != null) { rc = stInfo; } else if ((stInfo = parseCause(buffer)) != null) { rc = stInfo; } else if ((stInfo = parseMore(buffer)) != null) { rc = stInfo; } else if ((stInfo = parseMore(buffer)) != null) { rc = stInfo; } else if ((stInfo = parseExceptionText(buffer)) != null) { rc = stInfo; } else { throw new ParseException( "Buffer does not match any of the defined patterns: " + buffer, buffer.position()); } return rc; } private static StackTraceInfo parseLocation (final CharBuffer buffer) throws ParseException { StackTraceInfo rc = null; Matcher matcher = FULL_LOCATION_PATTERN.matcher(buffer); if (matcher.matches()) { final int savePos = buffer.position(); matcher = LOCATION_PATTERN.matcher(buffer); if (! matcher.lookingAt()) { throw new ParseException("Cannot parse correctly: " + buffer, buffer.position()); } int pos = matcher.end(); buffer.position(buffer.position() + pos); matcher = CLASS_PATTERN.matcher(buffer); if (! matcher.lookingAt()) { throw new ParseException("Cannot parse correctly: " + buffer, buffer.position()); } pos = matcher.end(); final CharBuffer classname = buffer.asReadOnlyBuffer(); classname.limit(classname.position() + pos - 1); buffer.position(buffer.position() + pos); matcher = METHOD_PATTERN.matcher(buffer); if (! matcher.lookingAt()) { throw new ParseException("Cannot parse correctly: " + buffer, buffer.position()); } pos = matcher.end(); final CharBuffer methodname = buffer.asReadOnlyBuffer(); methodname.limit(methodname.position() + pos - 1); int line = -1; matcher = LINE_PATTERN.matcher(buffer); // this time there need not to be a match if (matcher.lookingAt()) { pos = matcher.end(); buffer.position(buffer.position() + pos); boolean digit = false; int i = 0; while (Character.isDigit(buffer.charAt(i))) { ++i; digit = true; } if (digit) { line = Integer.parseInt(buffer.subSequence(0, i).toString()); } } buffer.position(savePos); rc = new StackTraceInfo( buffer.asReadOnlyBuffer(), classname, methodname, line); } return rc; } private static StackTraceInfo parseCause (final CharBuffer buffer) throws ParseException { StackTraceInfo rc = null; Matcher matcher = FULL_CAUSE_PATTERN.matcher(buffer); if (matcher.matches()) { final int savePos = buffer.position(); matcher = CAUSE_PATTERN.matcher(buffer); if (! matcher.lookingAt()) { throw new ParseException("Cannot parse a caused-by correctly: " + buffer, buffer.position()); } final int pos = matcher.end(); buffer.position(buffer.position() + pos); final CharBuffer cause = buffer.asReadOnlyBuffer(); buffer.position(savePos); rc = new StackTraceInfo(buffer.asReadOnlyBuffer(), cause, true); } return rc; } private static StackTraceInfo parseExceptionText (final CharBuffer buffer) throws ParseException { StackTraceInfo rc = null; Matcher matcher = FULL_EXCEPTION_TEXT_PATTERN.matcher(buffer); if (matcher.matches()) { final int savePos = buffer.position(); matcher = EXCEPTION_TEXT_PATTERN.matcher(buffer); if (! matcher.lookingAt()) { throw new ParseException( "Cannot parse an exception text correctly: " + buffer, buffer.position()); } final int pos = matcher.end(); buffer.position(buffer.position() + pos); final CharBuffer exceptionText = buffer.asReadOnlyBuffer(); buffer.position(savePos); rc = new StackTraceInfo( buffer.asReadOnlyBuffer(), exceptionText, false); } return rc; } private static StackTraceInfo parseMore (final CharBuffer buffer) throws ParseException { StackTraceInfo rc = null; Matcher matcher = FULL_MORE_PATTERN.matcher(buffer); if (matcher.matches()) { final int savePos = buffer.position(); matcher = MORE_PATTERN.matcher(buffer); if (! matcher.lookingAt()) { throw new ParseException("Cannot parse a more-line correctly: " + buffer, buffer.position()); } final int pos = matcher.end(); buffer.position(buffer.position() + pos); boolean digit = false; int i = 0; while (Character.isDigit(buffer.charAt(i))) { ++i; digit = true; } if (! digit) { throw new ParseException("Number of more lines is missing: " + buffer, buffer.position()); } final int moreLines = Integer.parseInt( buffer.subSequence(0, i).toString()); buffer.position(savePos); rc = new StackTraceInfo(buffer.asReadOnlyBuffer(), moreLines); } return rc; } }