blob: a3c8f314affba3fb86656e9abaa2d6f41638865c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2016 IBM Corporation and others.
*
* 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/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.debug.ui.console;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.StringTokenizer;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.IJobManager;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.jdt.internal.debug.ui.IJavaDebugHelpContextIds;
import org.eclipse.jdt.internal.debug.ui.JDIDebugUIPlugin;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.rules.FastPartitioner;
import org.eclipse.jface.text.rules.RuleBasedPartitionScanner;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.graphics.Font;
import org.eclipse.ui.console.IConsoleDocumentPartitioner;
import org.eclipse.ui.console.IConsoleView;
import org.eclipse.ui.console.TextConsole;
import org.eclipse.ui.part.IPageBookViewPage;
import org.eclipse.ui.progress.WorkbenchJob;
/**
* Provides a stack trace console for Java stack traces
*/
public class JavaStackTraceConsole extends TextConsole {
/**
* Provides a partitioner for this console type
*/
class JavaStackTraceConsolePartitioner extends FastPartitioner implements IConsoleDocumentPartitioner {
public JavaStackTraceConsolePartitioner() {
super(new RuleBasedPartitionScanner(), null);
getDocument().setDocumentPartitioner(this);
}
@Override
public boolean isReadOnly(int offset) {
return false;
}
@Override
public StyleRange[] getStyleRanges(int offset, int length) {
return null;
}
}
public final static String CONSOLE_TYPE = "javaStackTraceConsole"; //$NON-NLS-1$
public final static String FILE_NAME = JDIDebugUIPlugin.getDefault().getStateLocation().toOSString() + File.separator + "stackTraceConsole.txt"; //$NON-NLS-1$
private JavaStackTraceConsolePartitioner partitioner = new JavaStackTraceConsolePartitioner();
private IPropertyChangeListener propertyListener = new IPropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent event) {
String property = event.getProperty();
if (property.equals(IDebugUIConstants.PREF_CONSOLE_FONT)) {
setFont(JFaceResources.getFont(IDebugUIConstants.PREF_CONSOLE_FONT));
}
}
};
/**
* Constructor
*/
public JavaStackTraceConsole() {
super(ConsoleMessages.JavaStackTraceConsoleFactory_0, CONSOLE_TYPE, null, true);
Font font = JFaceResources.getFont(IDebugUIConstants.PREF_CONSOLE_FONT);
setFont(font);
partitioner.connect(getDocument());
}
/**
* inits the document backing this console
*/
public void initializeDocument() {
File file = new File(FILE_NAME);
if (file.exists()) {
try (InputStream fin = new BufferedInputStream(new FileInputStream(file))) {
int len = (int) file.length();
byte[] b = new byte[len];
int read = 0;
while (read < len) {
read += fin.read(b);
}
getDocument().set(new String(b));
} catch (IOException e) {
}
} else {
getDocument().set(ConsoleMessages.JavaStackTraceConsole_0);
}
}
/**
* @see org.eclipse.ui.console.AbstractConsole#init()
*/
@Override
protected void init() {
JFaceResources.getFontRegistry().addListener(propertyListener);
}
/**
* @see org.eclipse.ui.console.TextConsole#dispose()
*/
@Override
protected void dispose() {
saveDocument();
JFaceResources.getFontRegistry().removeListener(propertyListener);
super.dispose();
}
/**
* Saves the backing document for this console
*/
public void saveDocument() {
try (FileOutputStream fout = new FileOutputStream(FILE_NAME)) {
IDocument document = getDocument();
if (document != null) {
if (document.getLength() > 0) {
String contents = document.get();
fout.write(contents.getBytes());
} else {
File file = new File(FILE_NAME);
file.delete();
}
}
}
catch (IOException e) {}
}
/**
* @see org.eclipse.ui.console.TextConsole#getPartitioner()
*/
@Override
protected IConsoleDocumentPartitioner getPartitioner() {
return partitioner;
}
/**
* @see org.eclipse.ui.console.AbstractConsole#getHelpContextId()
*/
@Override
public String getHelpContextId() {
return IJavaDebugHelpContextIds.STACK_TRACE_CONSOLE;
}
/**
* @see org.eclipse.ui.console.TextConsole#createPage(org.eclipse.ui.console.IConsoleView)
*/
@Override
public IPageBookViewPage createPage(IConsoleView view) {
return new JavaStackTraceConsolePage(this, view);
}
/**
* performs the formatting of the stacktrace console
*/
public void format() {
WorkbenchJob job = new WorkbenchJob(ConsoleMessages.JavaStackTraceConsole_1) {
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
IJobManager jobManager = Job.getJobManager();
try {
jobManager.join(this, monitor);
} catch (OperationCanceledException e1) {
return Status.CANCEL_STATUS;
} catch (InterruptedException e1) {
return Status.CANCEL_STATUS;
}
IDocument document = getDocument();
String orig = document.get();
if (orig != null && orig.length() > 0) {
document.set(format(orig));
}
return Status.OK_STATUS;
}
};
job.setSystem(true);
job.schedule();
}
/**
* Underlying format operation
* @param trace the stack trace to format
* @return the formatted stack trace for this console
*/
private String format(String trace) {
StringTokenizer tokenizer = new StringTokenizer(trace, " \t\n\r\f", true); //$NON-NLS-1$
StringBuilder formattedTrace = new StringBuilder();
boolean insideAt = false;
boolean newLine = true;
int pendingSpaces = 0;
boolean antTrace = false;
while (tokenizer.hasMoreTokens()) {
String token = tokenizer.nextToken();
if (token.length() == 0)
{
continue; // paranoid
}
char c = token.charAt(0);
// handle delimiters
switch (c) {
case ' ':
if (newLine) {
pendingSpaces++;
} else {
pendingSpaces = 1;
}
continue;
case '\t':
if (newLine) {
pendingSpaces += 4;
} else {
pendingSpaces = 1;
}
continue;
case '\n':
case '\r':
case '\f':
if (insideAt) {
pendingSpaces = 1;
} else {
pendingSpaces = 0;
newLine = true;
}
continue;
}
// consider newlines only before token starting with char '\"' or
// token "at" or "-".
if (newLine || antTrace) {
if (c == '\"') { // leading thread name, e.g. "Worker-124"
// prio=5
formattedTrace.append("\n\n"); //$NON-NLS-1$ print 2 lines to break between threads
} else if ("-".equals(token)) { //$NON-NLS-1$ - locked ...
formattedTrace.append("\n"); //$NON-NLS-1$
formattedTrace.append(" "); //$NON-NLS-1$
formattedTrace.append(token);
pendingSpaces = 0;
continue;
} else if ("at".equals(token)) { //$NON-NLS-1$ at ...
if (!antTrace) {
formattedTrace.append("\n"); //$NON-NLS-1$
formattedTrace.append(" "); //$NON-NLS-1$
} else {
formattedTrace.append(' ');
}
insideAt = true;
formattedTrace.append(token);
pendingSpaces = 0;
continue;
} else if (c == '[') {
if(antTrace) {
formattedTrace.append("\n"); //$NON-NLS-1$
}
formattedTrace.append(token);
pendingSpaces = 0;
newLine = false;
antTrace = true;
continue;
}
newLine = false;
}
if (pendingSpaces > 0) {
for (int i = 0; i < pendingSpaces; i++) {
formattedTrace.append(' ');
}
pendingSpaces = 0;
}
formattedTrace.append(token);
insideAt = false;
}
return formattedTrace.toString();
}
}