blob: bc42903735d6bfb2e45f707d834838f4b836fba1 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2012, 2013 Oracle. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0, which accompanies this distribution
* and is available at https://www.eclipse.org/legal/epl-2.0/.
*
* Contributors:
* Oracle - initial API and implementation
******************************************************************************/
package org.eclipse.jpt.common.utility.internal;
import java.io.IOException;
import java.io.Serializable;
import org.eclipse.jpt.common.utility.internal.iterable.IterableTools;
/**
* Container for holding and printing the {@StackTraceElement stack trace
* elements} produced by a thread or exception.
*
* @see Thread#getStackTrace()
* @see Throwable#getStackTrace()
*/
public class StackTrace
implements Serializable
{
private final Thread thread;
private final StackTraceElement[] elements;
private volatile String string;
private static final long serialVersionUID = 1L;
public StackTrace() {
this(StringTools.EMPTY_STRING_ARRAY);
}
public StackTrace(Class<?>... traceClasses) {
this(convertToNames(traceClasses));
}
public StackTrace(String... traceClasses) {
this(Thread.currentThread(), traceClasses);
}
public StackTrace(Thread thread) {
this(thread, StringTools.EMPTY_STRING_ARRAY);
}
public StackTrace(Thread thread, Class<?>... traceClasses) {
this(thread, convertToNames(traceClasses));
}
public StackTrace(Thread thread, String... traceClasses) {
super();
if ((thread == null) || (traceClasses == null)) {
throw new NullPointerException();
}
this.thread = thread;
this.elements = this.buildElements(traceClasses);
}
private static String[] convertToNames(Class<?>[] classes) {
int len = classes.length;
if (len == 0) {
return StringTools.EMPTY_STRING_ARRAY;
}
String[] names = new String[len];
for (int i = 0; i < len; i++) {
names[i] = classes[i].getName();
}
return names;
}
/**
* Strip off all the elements associated with this class, the specified
* "trace" classes, and the {@link Thread} class.
* The "trace" classes are specified by the client at construction time and
* are those classes that should not appear on the top of the stack trace.
* The "trace" classes can appear elsewhere in the stack trace, just not at
* the top.
*/
private StackTraceElement[] buildElements(String[] traceClasses) {
StackTraceElement[] result = this.thread.getStackTrace();
int len = result.length;
if (len == 0) {
return result;
}
traceClasses = ArrayTools.add(traceClasses, this.getClass().getName());
boolean found = false;
int i = 0;
while (i < len) {
if (ArrayTools.contains(traceClasses, result[i].getClassName())) {
found = true;
} else {
if (found) {
break;
}
}
i++;
}
return found ? ArrayTools.subArray(result, i, len) : result;
}
public Thread getThread() {
return this.thread;
}
public Iterable<StackTraceElement> getElements() {
return IterableTools.iterable(this.elements);
}
/**
* Append the stack trace to the specified stream.
*/
public void appendTo(Appendable stream) throws IOException {
this.appendTo(stream, null);
}
/**
* Append the stack trace to the specified stream, prefixing each line
* with the specified prefix.
*/
public void appendTo(Appendable stream, String prefix) throws IOException {
for (StackTraceElement element : this.elements) {
if (prefix != null) {
stream.append(prefix);
}
stream.append(String.valueOf(element)).append(StringTools.CR);
}
}
@Override
public String toString() {
return this.getString();
}
private String getString() {
if (this.string == null) {
synchronized (this) {
if (this.string == null) {
this.string = this.buildString();
}
}
}
return this.string;
}
private String buildString() {
StringBuffer sb = new StringBuffer(1000);
try {
this.appendTo(sb);
} catch (IOException ex) {
throw new RuntimeException(ex); // should not happen with a StringBuffer
}
return sb.toString();
}
}