blob: 4f510b26e6bec1a4feecd6579ac420bc86045a02 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2019 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
* Paul Pazderski - Bug 545769: fixed rare UTF-8 character corruption bug
* Paul Pazderski - Bug 552015: console finished signaled to late if input is connected to file
*******************************************************************************/
package org.eclipse.debug.internal.ui.views.console;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IStorage;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.PlatformObject;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.variables.IStringVariableManager;
import org.eclipse.core.variables.VariablesPlugin;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.IDebugEventSetListener;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationType;
import org.eclipse.debug.core.IStreamListener;
import org.eclipse.debug.core.model.IFlushableStreamMonitor;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.debug.core.model.IStreamMonitor;
import org.eclipse.debug.core.model.IStreamsProxy;
import org.eclipse.debug.core.sourcelookup.containers.LocalFileStorage;
import org.eclipse.debug.internal.core.IInternalDebugCoreConstants;
import org.eclipse.debug.internal.ui.DebugPluginImages;
import org.eclipse.debug.internal.ui.DebugUIPlugin;
import org.eclipse.debug.internal.ui.IDebugHelpContextIds;
import org.eclipse.debug.internal.ui.preferences.IDebugPreferenceConstants;
import org.eclipse.debug.ui.DebugUITools;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.debug.ui.console.IConsole;
import org.eclipse.debug.ui.console.IConsoleColorProvider;
import org.eclipse.debug.ui.console.IConsoleHyperlink;
import org.eclipse.debug.ui.console.IConsoleLineTracker;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IPersistableElement;
import org.eclipse.ui.IStorageEditorInput;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.console.ConsolePlugin;
import org.eclipse.ui.console.IHyperlink;
import org.eclipse.ui.console.IOConsole;
import org.eclipse.ui.console.IOConsoleInputStream;
import org.eclipse.ui.console.IOConsoleOutputStream;
import org.eclipse.ui.console.IPatternMatchListener;
import org.eclipse.ui.console.PatternMatchEvent;
import org.eclipse.ui.console.TextConsole;
import org.eclipse.ui.editors.text.EditorsUI;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.progress.UIJob;
import com.ibm.icu.text.MessageFormat;
/**
* A console for a system process with standard I/O streams.
*
* @since 3.0
*/
@SuppressWarnings("deprecation")
public class ProcessConsole extends IOConsole implements IConsole, IDebugEventSetListener, IPropertyChangeListener {
private IProcess fProcess = null;
private List<StreamListener> fStreamListeners = new ArrayList<>();
private IConsoleColorProvider fColorProvider;
/**
* The input stream which can supply user input in console to the system process
* stdin.
*/
private IOConsoleInputStream fUserInput;
/**
* The stream connected to the system processe's stdin. May be the
* <i>fUserInput</i> stream to supply user input or a FileInputStream to supply
* input from a file.
*/
private volatile InputStream fInput;
private FileOutputStream fFileOutputStream;
private boolean fAllocateConsole = true;
private String fStdInFile = null;
private volatile boolean fStreamsClosed = false;
/**
* Create process console with default encoding.
*
* @param process the process to associate with this console
* @param colorProvider the colour provider for this console
*/
public ProcessConsole(IProcess process, IConsoleColorProvider colorProvider) {
this(process, colorProvider, null);
}
/**
* Create process console.
*
* @param process the process to associate with this console
* @param colorProvider the colour provider for this console
* @param encoding the desired encoding for this console
*/
public ProcessConsole(IProcess process, IConsoleColorProvider colorProvider, String encoding) {
super(IInternalDebugCoreConstants.EMPTY_STRING, IDebugUIConstants.ID_PROCESS_CONSOLE_TYPE, null, encoding, true);
fProcess = process;
fUserInput = getInputStream();
ILaunchConfiguration configuration = process.getLaunch().getLaunchConfiguration();
String file = null;
boolean append = false;
if (configuration != null) {
try {
file = configuration.getAttribute(IDebugUIConstants.ATTR_CAPTURE_IN_FILE, (String) null);
fStdInFile = configuration.getAttribute(IDebugUIConstants.ATTR_CAPTURE_STDIN_FILE, (String) null);
if (file != null || fStdInFile != null) {
IStringVariableManager stringVariableManager = VariablesPlugin.getDefault().getStringVariableManager();
if (file != null) {
file = stringVariableManager.performStringSubstitution(file);
append = configuration.getAttribute(IDebugUIConstants.ATTR_APPEND_TO_FILE, false);
}
if (fStdInFile != null) {
fStdInFile = stringVariableManager.performStringSubstitution(fStdInFile);
}
}
} catch (CoreException e) {
}
}
if (file != null && configuration != null) {
IWorkspace workspace = ResourcesPlugin.getWorkspace();
IWorkspaceRoot root = workspace.getRoot();
Path path = new Path(file);
IFile ifile = root.getFileForLocation(path);
String message = null;
try {
String fileLoc = null;
if (ifile != null) {
if (append && ifile.exists()) {
ifile.appendContents(new ByteArrayInputStream(new byte[0]), true, true, new NullProgressMonitor());
} else {
if (ifile.exists()) {
ifile.delete(true, new NullProgressMonitor());
}
ifile.create(new ByteArrayInputStream(new byte[0]), true, new NullProgressMonitor());
}
}
File outputFile = new File(file);
fFileOutputStream = new FileOutputStream(outputFile, append);
fileLoc = outputFile.getAbsolutePath();
message = MessageFormat.format(ConsoleMessages.ProcessConsole_1, new Object[] { fileLoc });
addPatternMatchListener(new ConsoleLogFilePatternMatcher(fileLoc));
} catch (FileNotFoundException e) {
message = MessageFormat.format(ConsoleMessages.ProcessConsole_2, new Object[] { file });
} catch (CoreException e) {
DebugUIPlugin.log(e);
}
if (message != null) {
try (IOConsoleOutputStream stream = newOutputStream()) {
stream.write(message);
} catch (IOException e) {
DebugUIPlugin.log(e);
}
}
try {
fAllocateConsole = configuration.getAttribute(IDebugUIConstants.ATTR_CAPTURE_IN_CONSOLE, true);
} catch (CoreException e) {
}
}
if (fStdInFile != null && configuration != null) {
String message = null;
try {
fInput = new FileInputStream(new File(fStdInFile));
if (fInput != null) {
setInputStream(fInput);
}
} catch (FileNotFoundException e) {
message = MessageFormat.format(ConsoleMessages.ProcessConsole_3, new Object[] { fStdInFile });
}
if (message != null) {
try (IOConsoleOutputStream stream = newOutputStream()) {
stream.write(message);
} catch (IOException e) {
DebugUIPlugin.log(e);
}
}
}
fColorProvider = colorProvider;
if (fInput == null) {
fInput = getInputStream();
}
colorProvider.connect(fProcess, this);
setName(computeName());
Color color = fColorProvider.getColor(IDebugUIConstants.ID_STANDARD_INPUT_STREAM);
if (fInput instanceof IOConsoleInputStream) {
((IOConsoleInputStream)fInput).setColor(color);
}
IConsoleLineTracker[] lineTrackers = DebugUIPlugin.getDefault().getProcessConsoleManager().getLineTrackers(process);
if (lineTrackers.length > 0) {
addPatternMatchListener(new ConsoleLineNotifier());
}
}
/**
* Computes and returns the image descriptor for this console.
*
* @return an image descriptor for this console or <code>null</code>
*/
protected ImageDescriptor computeImageDescriptor() {
ILaunchConfiguration configuration = getProcess().getLaunch().getLaunchConfiguration();
if (configuration != null) {
ILaunchConfigurationType type;
try {
type = configuration.getType();
return DebugPluginImages.getImageDescriptor(type.getIdentifier());
} catch (CoreException e) {
DebugUIPlugin.log(e);
}
}
return null;
}
/**
* Computes and returns the current name of this console.
*
* @return a name for this console
*/
protected String computeName() {
String label = null;
IProcess process = getProcess();
ILaunchConfiguration config = process.getLaunch().getLaunchConfiguration();
label = process.getAttribute(IProcess.ATTR_PROCESS_LABEL);
if (label == null) {
if (config == null) {
label = process.getLabel();
} else {
// check if PRIVATE config
if (DebugUITools.isPrivate(config)) {
label = process.getLabel();
} else {
String type = null;
try {
type = config.getType().getName();
} catch (CoreException e) {
}
StringBuilder buffer = new StringBuilder();
buffer.append(config.getName());
if (type != null) {
buffer.append(" ["); //$NON-NLS-1$
buffer.append(type);
buffer.append("] "); //$NON-NLS-1$
}
buffer.append(process.getLabel());
label = buffer.toString();
}
}
}
if (process.isTerminated()) {
return MessageFormat.format(ConsoleMessages.ProcessConsole_0, new Object[] { label });
}
return label;
}
/**
* @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
*/
@Override
public void propertyChange(PropertyChangeEvent evt) {
String property = evt.getProperty();
IPreferenceStore store = DebugUIPlugin.getDefault().getPreferenceStore();
if (property.equals(IDebugPreferenceConstants.CONSOLE_WRAP) || property.equals(IDebugPreferenceConstants.CONSOLE_WIDTH)) {
boolean fixedWidth = store.getBoolean(IDebugPreferenceConstants.CONSOLE_WRAP);
if (fixedWidth) {
int width = store.getInt(IDebugPreferenceConstants.CONSOLE_WIDTH);
setConsoleWidth(width);
} else {
setConsoleWidth(-1);
}
} else if (property.equals(IDebugPreferenceConstants.CONSOLE_LIMIT_CONSOLE_OUTPUT) || property.equals(IDebugPreferenceConstants.CONSOLE_HIGH_WATER_MARK) || property.equals(IDebugPreferenceConstants.CONSOLE_LOW_WATER_MARK)) {
boolean limitBufferSize = store.getBoolean(IDebugPreferenceConstants.CONSOLE_LIMIT_CONSOLE_OUTPUT);
if (limitBufferSize) {
int highWater = store.getInt(IDebugPreferenceConstants.CONSOLE_HIGH_WATER_MARK);
int lowWater = store.getInt(IDebugPreferenceConstants.CONSOLE_LOW_WATER_MARK);
if (highWater > lowWater) {
setWaterMarks(lowWater, highWater);
}
} else {
setWaterMarks(-1, -1);
}
} else if (property.equals(IDebugPreferenceConstants.CONSOLE_TAB_WIDTH)) {
int tabWidth = store.getInt(IDebugPreferenceConstants.CONSOLE_TAB_WIDTH);
setTabWidth(tabWidth);
} else if (property.equals(IDebugPreferenceConstants.CONSOLE_OPEN_ON_OUT)) {
boolean activateOnOut = store.getBoolean(IDebugPreferenceConstants.CONSOLE_OPEN_ON_OUT);
@SuppressWarnings("resource")
IOConsoleOutputStream stream = getStream(IDebugUIConstants.ID_STANDARD_OUTPUT_STREAM);
if (stream != null) {
stream.setActivateOnWrite(activateOnOut);
}
} else if (property.equals(IDebugPreferenceConstants.CONSOLE_OPEN_ON_ERR)) {
boolean activateOnErr = store.getBoolean(IDebugPreferenceConstants.CONSOLE_OPEN_ON_ERR);
@SuppressWarnings("resource")
IOConsoleOutputStream stream = getStream(IDebugUIConstants.ID_STANDARD_ERROR_STREAM);
if (stream != null) {
stream.setActivateOnWrite(activateOnErr);
}
} else if (property.equals(IDebugPreferenceConstants.CONSOLE_SYS_OUT_COLOR)) {
@SuppressWarnings("resource")
IOConsoleOutputStream stream = getStream(IDebugUIConstants.ID_STANDARD_OUTPUT_STREAM);
if (stream != null) {
stream.setColor(fColorProvider.getColor(IDebugUIConstants.ID_STANDARD_OUTPUT_STREAM));
}
} else if (property.equals(IDebugPreferenceConstants.CONSOLE_SYS_ERR_COLOR)) {
@SuppressWarnings("resource")
IOConsoleOutputStream stream = getStream(IDebugUIConstants.ID_STANDARD_ERROR_STREAM);
if (stream != null) {
stream.setColor(fColorProvider.getColor(IDebugUIConstants.ID_STANDARD_ERROR_STREAM));
}
} else if (property.equals(IDebugPreferenceConstants.CONSOLE_SYS_IN_COLOR)) {
if (fInput != null && fInput instanceof IOConsoleInputStream) {
((IOConsoleInputStream) fInput).setColor(fColorProvider.getColor(IDebugUIConstants.ID_STANDARD_INPUT_STREAM));
}
} else if (property.equals(IDebugUIConstants.PREF_CONSOLE_FONT)) {
setFont(JFaceResources.getFont(IDebugUIConstants.PREF_CONSOLE_FONT));
} else if (property.equals(IDebugPreferenceConstants.CONSOLE_BAKGROUND_COLOR)) {
setBackground(DebugUIPlugin.getPreferenceColor(IDebugPreferenceConstants.CONSOLE_BAKGROUND_COLOR));
} else if (property.equals(IDebugPreferenceConstants.CONSOLE_INTERPRET_CONTROL_CHARACTERS)) {
setHandleControlCharacters(store.getBoolean(IDebugPreferenceConstants.CONSOLE_INTERPRET_CONTROL_CHARACTERS));
} else if (property.equals(IDebugPreferenceConstants.CONSOLE_INTERPRET_CR_AS_CONTROL_CHARACTER)) {
setCarriageReturnAsControlCharacter(store.getBoolean(IDebugPreferenceConstants.CONSOLE_INTERPRET_CR_AS_CONTROL_CHARACTER));
}
}
@Override
public IOConsoleOutputStream getStream(String streamIdentifier) {
if (streamIdentifier == null) {
return null;
}
for (StreamListener listener : fStreamListeners) {
if (streamIdentifier.equals(listener.fStreamId)) {
return listener.fStream;
}
}
return null;
}
/**
* @see org.eclipse.debug.ui.console.IConsole#getProcess()
*/
@Override
public IProcess getProcess() {
return fProcess;
}
/**
* @see org.eclipse.ui.console.IOConsole#dispose()
*/
@Override
protected void dispose() {
super.dispose();
fColorProvider.disconnect();
DebugPlugin.getDefault().removeDebugEventListener(this);
DebugUIPlugin.getDefault().getPreferenceStore().removePropertyChangeListener(this);
JFaceResources.getFontRegistry().removeListener(this);
closeStreams();
disposeStreams();
}
/**
* cleanup method to close all of the open stream to this console
*/
private synchronized void closeStreams() {
if (fStreamsClosed) {
return;
}
for (StreamListener listener : fStreamListeners) {
listener.closeStream();
}
if (fFileOutputStream != null) {
synchronized (fFileOutputStream) {
try {
fFileOutputStream.flush();
fFileOutputStream.close();
} catch (IOException e) {
}
}
}
try {
fInput.close();
} catch (IOException e) {
}
if (fInput != fUserInput) {
try {
fUserInput.close();
} catch (IOException e) {
}
}
fStreamsClosed = true;
}
/**
* disposes the listeners for each of the stream associated with this
* console
*/
private synchronized void disposeStreams() {
for (StreamListener listener : fStreamListeners) {
listener.dispose();
}
fFileOutputStream = null;
fInput = null;
fUserInput = null;
}
/**
* @see org.eclipse.ui.console.AbstractConsole#init()
*/
@Override
protected void init() {
super.init();
DebugPlugin.getDefault().addDebugEventListener(this);
if (fProcess.isTerminated()) {
closeStreams();
resetName();
DebugPlugin.getDefault().removeDebugEventListener(this);
}
IPreferenceStore store = DebugUIPlugin.getDefault().getPreferenceStore();
store.addPropertyChangeListener(this);
JFaceResources.getFontRegistry().addListener(this);
if (store.getBoolean(IDebugPreferenceConstants.CONSOLE_WRAP)) {
setConsoleWidth(store.getInt(IDebugPreferenceConstants.CONSOLE_WIDTH));
}
setTabWidth(store.getInt(IDebugPreferenceConstants.CONSOLE_TAB_WIDTH));
if (store.getBoolean(IDebugPreferenceConstants.CONSOLE_LIMIT_CONSOLE_OUTPUT)) {
int highWater = store.getInt(IDebugPreferenceConstants.CONSOLE_HIGH_WATER_MARK);
int lowWater = store.getInt(IDebugPreferenceConstants.CONSOLE_LOW_WATER_MARK);
setWaterMarks(lowWater, highWater);
}
setHandleControlCharacters(store.getBoolean(IDebugPreferenceConstants.CONSOLE_INTERPRET_CONTROL_CHARACTERS));
setCarriageReturnAsControlCharacter(store.getBoolean(IDebugPreferenceConstants.CONSOLE_INTERPRET_CR_AS_CONTROL_CHARACTER));
DebugUIPlugin.getStandardDisplay().asyncExec(() -> {
setFont(JFaceResources.getFont(IDebugUIConstants.PREF_CONSOLE_FONT));
setBackground(DebugUIPlugin.getPreferenceColor(IDebugPreferenceConstants.CONSOLE_BAKGROUND_COLOR));
});
}
/**
* Notify listeners when name changes.
*
* @see org.eclipse.debug.core.IDebugEventSetListener#handleDebugEvents(org.eclipse.debug.core.DebugEvent[])
*/
@Override
public void handleDebugEvents(DebugEvent[] events) {
for (int i = 0; i < events.length; i++) {
DebugEvent event = events[i];
if (event.getSource().equals(getProcess())) {
if (event.getKind() == DebugEvent.TERMINATE) {
closeStreams();
DebugPlugin.getDefault().removeDebugEventListener(this);
}
resetName();
}
}
}
/**
* resets the name of this console to the original computed name
*/
private synchronized void resetName() {
final String newName = computeName();
String name = getName();
if (!name.equals(newName)) {
UIJob job = new UIJob("Update console title") { //$NON-NLS-1$
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
ProcessConsole.this.setName(newName);
warnOfContentChange();
return Status.OK_STATUS;
}
};
job.setSystem(true);
job.schedule();
}
}
/**
* send notification of a change of content in this console
*/
private void warnOfContentChange() {
ConsolePlugin.getDefault().getConsoleManager().warnOfContentChange(DebugUITools.getConsole(fProcess));
}
/**
* @see org.eclipse.debug.ui.console.IConsole#connect(org.eclipse.debug.core.model.IStreamsProxy)
*/
@Override
public void connect(IStreamsProxy streamsProxy) {
IPreferenceStore store = DebugUIPlugin.getDefault().getPreferenceStore();
IStreamMonitor streamMonitor = streamsProxy.getErrorStreamMonitor();
if (streamMonitor != null) {
connect(streamMonitor, IDebugUIConstants.ID_STANDARD_ERROR_STREAM,
store.getBoolean(IDebugPreferenceConstants.CONSOLE_OPEN_ON_ERR));
}
streamMonitor = streamsProxy.getOutputStreamMonitor();
if (streamMonitor != null) {
connect(streamMonitor, IDebugUIConstants.ID_STANDARD_OUTPUT_STREAM,
store.getBoolean(IDebugPreferenceConstants.CONSOLE_OPEN_ON_OUT));
}
InputReadJob readJob = new InputReadJob(streamsProxy);
readJob.setSystem(true);
readJob.schedule();
}
/**
* @see org.eclipse.debug.ui.console.IConsole#connect(org.eclipse.debug.core.model.IStreamMonitor, java.lang.String)
*/
@Override
public void connect(IStreamMonitor streamMonitor, String streamIdentifier) {
connect(streamMonitor, streamIdentifier, false);
}
/**
* Connects the given stream monitor to a new output stream with the given identifier.
*
* @param streamMonitor stream monitor
* @param streamIdentifier stream identifier
* @param activateOnWrite whether the stream should displayed when written to
*/
@SuppressWarnings("resource")
private void connect(IStreamMonitor streamMonitor, String streamIdentifier, boolean activateOnWrite) {
IOConsoleOutputStream stream = null;
if (fAllocateConsole) {
stream = newOutputStream();
Color color = fColorProvider.getColor(streamIdentifier);
stream.setColor(color);
stream.setActivateOnWrite(activateOnWrite);
}
synchronized (streamMonitor) {
StreamListener listener = new StreamListener(streamIdentifier, streamMonitor, stream);
fStreamListeners.add(listener);
}
}
/**
* @see org.eclipse.debug.ui.console.IConsole#addLink(org.eclipse.debug.ui.console.IConsoleHyperlink, int, int)
*/
@Override
public void addLink(IConsoleHyperlink link, int offset, int length) {
try {
addHyperlink(link, offset, length);
} catch (BadLocationException e) {
DebugUIPlugin.log(e);
}
}
/**
* @see org.eclipse.debug.ui.console.IConsole#addLink(org.eclipse.ui.console.IHyperlink, int, int)
*/
@Override
public void addLink(IHyperlink link, int offset, int length) {
try {
addHyperlink(link, offset, length);
} catch (BadLocationException e) {
DebugUIPlugin.log(e);
}
}
/**
* @see org.eclipse.debug.ui.console.IConsole#getRegion(org.eclipse.debug.ui.console.IConsoleHyperlink)
*/
@Override
public IRegion getRegion(IConsoleHyperlink link) {
return super.getRegion(link);
}
/**
* This class listens to a specified IO stream
*/
private class StreamListener implements IStreamListener {
private IOConsoleOutputStream fStream;
private IStreamMonitor fStreamMonitor;
private String fStreamId;
private boolean fFlushed = false;
private boolean fListenerRemoved = false;
public StreamListener(String streamIdentifier, IStreamMonitor monitor, IOConsoleOutputStream stream) {
this.fStreamId = streamIdentifier;
this.fStreamMonitor = monitor;
this.fStream = stream;
fStreamMonitor.addListener(this);
//fix to bug 121454. Ensure that output to fast processes is processed.
streamAppended(null, monitor);
}
@Override
public void streamAppended(String text, IStreamMonitor monitor) {
String encoding = getEncoding();
if (fFlushed) {
try {
if (fStream != null) {
if (encoding == null) {
fStream.write(text);
} else {
fStream.write(text.getBytes(encoding));
}
}
if (fFileOutputStream != null) {
synchronized (fFileOutputStream) {
if (encoding == null) {
fFileOutputStream.write(text.getBytes());
} else {
fFileOutputStream.write(text.getBytes(encoding));
}
}
}
} catch (IOException e) {
DebugUIPlugin.log(e);
}
} else {
String contents = null;
synchronized (fStreamMonitor) {
fFlushed = true;
contents = fStreamMonitor.getContents();
if (fStreamMonitor instanceof IFlushableStreamMonitor) {
IFlushableStreamMonitor m = (IFlushableStreamMonitor) fStreamMonitor;
m.flushContents();
m.setBuffered(false);
}
}
try {
if (contents != null && contents.length() > 0) {
if (fStream != null) {
fStream.write(contents);
}
if (fFileOutputStream != null) {
synchronized (fFileOutputStream) {
fFileOutputStream.write(contents.getBytes());
}
}
}
} catch (IOException e) {
DebugUIPlugin.log(e);
}
}
}
public void closeStream() {
if (fStreamMonitor == null) {
return;
}
synchronized (fStreamMonitor) {
fStreamMonitor.removeListener(this);
if (!fFlushed) {
String contents = fStreamMonitor.getContents();
streamAppended(contents, fStreamMonitor);
}
fListenerRemoved = true;
try {
if (fStream != null) {
fStream.close();
}
} catch (IOException e) {
}
}
}
public void dispose() {
if (!fListenerRemoved) {
closeStream();
}
fStream = null;
fStreamMonitor = null;
fStreamId = null;
}
}
private class InputReadJob extends Job {
private IStreamsProxy streamsProxy;
/**
* The {@link InputStream} this job is currently reading from or maybe blocking
* on. May be <code>null</code>.
*/
private InputStream readingStream;
InputReadJob(IStreamsProxy streamsProxy) {
super("Process Console Input Job"); //$NON-NLS-1$
this.streamsProxy = streamsProxy;
}
@Override
protected void canceling() {
super.canceling();
if (readingStream != null) {
// Close stream or job may not be able to cancel.
// This is primary for IOConsoleInputStream because there is no guarantee an
// arbitrary InputStream will release a blocked read() on close.
try {
readingStream.close();
} catch (IOException e) {
DebugUIPlugin.log(e);
}
}
}
@Override
public boolean belongsTo(Object family) {
return ProcessConsole.class == family;
}
@Override
protected IStatus run(IProgressMonitor monitor) {
if (fInput == null || fStreamsClosed) {
return monitor.isCanceled() ? Status.CANCEL_STATUS : Status.OK_STATUS;
}
Charset encoding = getCharset();
readingStream = fInput;
InputStreamReader streamReader = (encoding == null ? new InputStreamReader(readingStream)
: new InputStreamReader(readingStream, encoding));
try {
char[] cbuf = new char[1024];
int charRead = 0;
while (charRead >= 0 && !monitor.isCanceled()) {
if (fInput == null || fStreamsClosed) {
break;
}
if (fInput != readingStream) {
readingStream = fInput;
streamReader = (encoding == null ? new InputStreamReader(readingStream)
: new InputStreamReader(readingStream, encoding));
}
charRead = streamReader.read(cbuf);
if (charRead > 0) {
String s = new String(cbuf, 0, charRead);
streamsProxy.write(s);
}
}
} catch (IOException e) {
DebugUIPlugin.log(e);
}
readingStream = null;
return monitor.isCanceled() ? Status.CANCEL_STATUS : Status.OK_STATUS;
}
}
@Override
public ImageDescriptor getImageDescriptor() {
if (super.getImageDescriptor() == null) {
setImageDescriptor(computeImageDescriptor());
}
return super.getImageDescriptor();
}
private class ConsoleLogFilePatternMatcher implements IPatternMatchListener {
String fFilePath;
public ConsoleLogFilePatternMatcher(String filePath) {
fFilePath = escape(filePath);
}
private String escape(String path) {
StringBuilder buffer = new StringBuilder(path);
int index = buffer.indexOf("\\"); //$NON-NLS-1$
while (index >= 0) {
buffer.insert(index, '\\');
index = buffer.indexOf("\\", index+2); //$NON-NLS-1$
}
return buffer.toString();
}
@Override
public String getPattern() {
return fFilePath;
}
@Override
public void matchFound(PatternMatchEvent event) {
try {
addHyperlink(new ConsoleLogFileHyperlink(fFilePath), event.getOffset(), event.getLength());
removePatternMatchListener(this);
} catch (BadLocationException e) {
}
}
@Override
public int getCompilerFlags() {
return 0;
}
@Override
public String getLineQualifier() {
return null;
}
@Override
public void connect(TextConsole console) {
}
@Override
public void disconnect() {
}
}
private class ConsoleLogFileHyperlink implements IHyperlink {
String fFilePath;
ConsoleLogFileHyperlink(String filePath) {
fFilePath = filePath;
}
@Override
public void linkActivated() {
IEditorInput input;
Path path = new Path(fFilePath);
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
IFile ifile = root.getFileForLocation(path);
if (ifile == null) { // The file is not in the workspace
File file = new File(fFilePath);
LocalFileStorage lfs = new LocalFileStorage(file);
input = new StorageEditorInput(lfs, file);
} else {
input = new FileEditorInput(ifile);
}
IWorkbenchPage activePage = DebugUIPlugin.getActiveWorkbenchWindow().getActivePage();
try {
activePage.openEditor(input, EditorsUI.DEFAULT_TEXT_EDITOR_ID, true);
} catch (PartInitException e) {
}
}
@Override
public void linkEntered() {
}
@Override
public void linkExited() {
}
}
class StorageEditorInput extends PlatformObject implements IStorageEditorInput {
private File fFile;
private IStorage fStorage;
public StorageEditorInput(IStorage storage, File file) {
fStorage = storage;
fFile = file;
}
@Override
public IStorage getStorage() {
return fStorage;
}
@Override
public ImageDescriptor getImageDescriptor() {
return null;
}
@Override
public String getName() {
return getStorage().getName();
}
@Override
public IPersistableElement getPersistable() {
return null;
}
@Override
public String getToolTipText() {
return getStorage().getFullPath().toOSString();
}
@Override
public boolean equals(Object object) {
return object instanceof StorageEditorInput &&
getStorage().equals(((StorageEditorInput)object).getStorage());
}
@Override
public int hashCode() {
return getStorage().hashCode();
}
@Override
public boolean exists() {
return fFile.exists();
}
}
@Override
public String getHelpContextId() {
return IDebugHelpContextIds.PROCESS_CONSOLE;
}
}