package org.eclipse.debug.internal.ui; | |
/* | |
* Licensed Materials - Property of IBM, | |
* WebSphere Studio Workbench | |
* (c) Copyright IBM Corp 1999, 2000 | |
*/ | |
import java.io.IOException; | |
import java.util.ArrayList; | |
import java.util.Collections; | |
import java.util.List; | |
import org.eclipse.debug.core.*; | |
import org.eclipse.debug.core.model.IProcess; | |
import org.eclipse.debug.core.model.IStreamMonitor; | |
import org.eclipse.debug.core.model.IStreamsProxy; | |
import org.eclipse.jface.text.*; | |
import org.eclipse.swt.custom.StyleRange; | |
import org.eclipse.swt.graphics.Color; | |
import org.eclipse.swt.widgets.Display; | |
public class ConsoleDocument extends AbstractDocument implements IDebugEventListener { | |
public final static int fgMinimumSize= 500; | |
public final static int fgMinMaxRation= 5; | |
private boolean fClosed= false; | |
private int fMinSize= fgMinimumSize; | |
private int fMaxSize= fgMinimumSize * fgMinMaxRation; | |
protected IProcess fProcess; | |
private IStreamsProxy fProxy; | |
private int fLastStreamWriteEnd= 0; | |
private int fNewStreamWriteEnd= 0; | |
protected boolean fNeedsToStartReading= true; | |
public static final int OUT= 0; | |
public static final int ERR= 1; | |
protected List fStyleRanges= new ArrayList(2); | |
protected ConsoleViewer fConsoleViewer= null; | |
protected IStreamListener fSystemOutListener= new IStreamListener() { | |
public void streamAppended(String newText, IStreamMonitor monitor) { | |
systemOutAppended(newText); | |
} | |
}; | |
protected IStreamListener fSystemErrListener= new IStreamListener() { | |
public void streamAppended(String newText, IStreamMonitor monitor) { | |
systemErrAppended(newText); | |
} | |
}; | |
public ConsoleDocument(IProcess process) { | |
super(); | |
fProcess= process; | |
setTextStore(new ConsoleOutputTextStore(fMaxSize)); | |
setLineTracker(new DefaultLineTracker()); | |
if (process != null) { | |
fProxy= process.getStreamsProxy(); | |
} | |
if (process != null) { | |
DebugPlugin.getDefault().addDebugEventListener(this); | |
} | |
completeInitialization(); | |
} | |
public void close() { | |
stopReading(); | |
DebugPlugin.getDefault().removeDebugEventListener(this); | |
fClosed= true; | |
fStyleRanges= Collections.EMPTY_LIST; | |
set(""); | |
} | |
/** | |
* If the buffer is longer than fMaxSize, | |
* trim it back to fMinSize. | |
*/ | |
protected void ensureSizeConstraints() { | |
if (getLength() > fMaxSize) { | |
replace(0, getLength() - fMinSize, ""); | |
} | |
} | |
/** | |
* Fires the <code>DocumentEvent</code>, but also | |
* writes to the proxy if the user is entering input and | |
* has hit "Enter". | |
*/ | |
protected void fireDocumentChanged(DocumentEvent event) { | |
super.fireDocumentChanged(event); | |
String eventText= event.getText(); | |
if (eventText == null || 0 >= eventText.length() || eventText.length() > 2) { | |
return; | |
} | |
String[] lineDelimiters= event.getDocument().getLegalLineDelimiters(); | |
for (int i= 0; i < lineDelimiters.length; i++) { | |
if (lineDelimiters[i].equals(eventText)) { | |
try { | |
String inText= event.getDocument().get(); | |
inText= inText.substring(fNewStreamWriteEnd, inText.length()); | |
if (inText.length() == 0) { | |
return; | |
} | |
fProxy.write(inText); | |
fLastStreamWriteEnd= getLength(); | |
return; | |
} catch (IOException ioe) { | |
DebugUIUtils.logError(ioe); | |
} | |
} | |
} | |
} | |
public boolean isClosed() { | |
return fClosed; | |
} | |
public void replace(int pos, int replaceLength, String text) { | |
if (isReadOnly() || pos < getStartOfEditableContent()) { | |
return; | |
} | |
replace0(pos, replaceLength, text); | |
int docLength= getLength(); | |
if (docLength == fNewStreamWriteEnd) { | |
//removed all of the user input text | |
fStyleRanges.remove(fStyleRanges.size() - 1); | |
} else { | |
updateInputStyleRange(docLength); | |
//notify the viewer that the style ranges have changed. | |
fireDocumentChanged(new DocumentEvent(this, 0, 0, "")); | |
} | |
} | |
/** | |
* Replace text used to add content from streams even though | |
* the process is terminated (and therefore the doc is "read only") | |
*/ | |
protected void replace0(int pos, int replaceLength, String text) { | |
try { | |
super.replace(pos, replaceLength, text); | |
} catch (BadLocationException ble) { | |
DebugUIUtils.logError(ble); | |
} | |
if (text != null && text.length() - replaceLength > fMaxSize / 2) { | |
ensureSizeConstraints(); | |
} | |
} | |
public void setBufferSize(int minSize, int maxSize) { | |
fMinSize= (minSize < fgMinimumSize ? fgMinimumSize : minSize); | |
fMaxSize= (maxSize < minSize * fgMinMaxRation ? minSize * fgMinMaxRation : maxSize); | |
if (getStore() instanceof ConsoleOutputTextStore) | |
((ConsoleOutputTextStore) getStore()).setMinimalBufferSize(fMinSize); | |
ensureSizeConstraints(); | |
} | |
public void set(String text) { | |
fNewStreamWriteEnd= text.length(); | |
super.set(text); | |
fLastStreamWriteEnd= fNewStreamWriteEnd; | |
ensureSizeConstraints(); | |
} | |
protected void startReading() { | |
if (fProxy == null) { | |
return; | |
} | |
if (!fNeedsToStartReading) { | |
return; | |
} | |
fNeedsToStartReading= false; | |
IStreamMonitor monitor= fProxy.getOutputStreamMonitor(); | |
if (monitor != null) { | |
monitor.addListener(fSystemOutListener); | |
String contents= monitor.getContents(); | |
if (contents.length() > 0) { | |
fNewStreamWriteEnd= getLength() + contents.length(); | |
replace0(getLength(), 0, contents); | |
updateOutputStyleRanges(OUT); | |
fLastStreamWriteEnd= getLength(); | |
} | |
} | |
monitor= fProxy.getErrorStreamMonitor(); | |
if (monitor != null) { | |
monitor.addListener(fSystemErrListener); | |
String contents= monitor.getContents(); | |
if (contents.length() > 0) { | |
fNewStreamWriteEnd= getLength() + contents.length(); | |
replace0(getLength(), 0, contents); | |
updateOutputStyleRanges(ERR); | |
fLastStreamWriteEnd= getLength(); | |
} | |
} | |
} | |
protected void stopReading() { | |
if (fProxy == null) { | |
return; | |
} | |
fNeedsToStartReading= true; | |
IStreamMonitor monitor= fProxy.getOutputStreamMonitor(); | |
monitor.removeListener(fSystemOutListener); | |
monitor= fProxy.getErrorStreamMonitor(); | |
monitor.removeListener(fSystemErrListener); | |
} | |
/** | |
* System out or System error has had text append to it. | |
* Adds the new text to the document. | |
*/ | |
protected void streamAppended(final String text, final int source) { | |
update(new Runnable() { | |
public void run() { | |
int appendedLength= text.length(); | |
fNewStreamWriteEnd= fLastStreamWriteEnd + appendedLength; | |
ConsoleDocument.this.replace0(fLastStreamWriteEnd, 0, text); | |
updateOutputStyleRanges(source); | |
fLastStreamWriteEnd= fNewStreamWriteEnd; | |
} | |
}); | |
} | |
/** | |
* @see IInputStreamListener | |
*/ | |
protected void systemErrAppended(String text) { | |
streamAppended(text, ERR); | |
} | |
/** | |
* @see IInputStreamListener | |
*/ | |
protected void systemOutAppended(String text) { | |
streamAppended(text, OUT); | |
} | |
public boolean equals(Object obj) { | |
boolean correctInstance= obj instanceof ConsoleDocument; | |
if (fProcess != null) { | |
return correctInstance && fProcess.equals(((ConsoleDocument)obj).fProcess); | |
} else { | |
return correctInstance && ((ConsoleDocument)obj).fProcess == null; | |
} | |
} | |
public int hashCode() { | |
return (fProcess != null) ? fProcess.hashCode() : super.hashCode(); | |
} | |
protected StyleRange[] getStyleRanges() { | |
if (fStyleRanges.isEmpty()) { | |
return new StyleRange[]{}; | |
} | |
StyleRange[] sRanges= new StyleRange[fStyleRanges.size()]; | |
return (StyleRange[])fStyleRanges.toArray(sRanges); | |
} | |
/** | |
* Coalese that last two style ranges if they are similar | |
*/ | |
protected void coaleseRanges() { | |
int size= fStyleRanges.size(); | |
if (size > 1) { | |
StyleRange last= (StyleRange) fStyleRanges.get(size - 1); | |
StyleRange nextToLast= (StyleRange) fStyleRanges.get(size - 2); | |
if (last.similarTo(nextToLast)) {//same color? | |
StyleRange newRange= new StyleRange(nextToLast.start, last.length + nextToLast.length, last.foreground, null); | |
fStyleRanges.remove(size - 1); | |
fStyleRanges.remove(size - 2); | |
addNewStyleRange(newRange); | |
} | |
} | |
} | |
/** | |
* Returns whether the document's underlying process is | |
* terminated. | |
*/ | |
protected boolean isReadOnly() { | |
return (fProcess != null) ? fProcess.isTerminated() : true; | |
} | |
/** | |
* Updates the current input style range. | |
*/ | |
protected void updateInputStyleRange(int docLength) { | |
if (fClosed) { | |
return; | |
} | |
if (docLength != fNewStreamWriteEnd) { | |
StyleRange input= | |
new StyleRange(fNewStreamWriteEnd, docLength - fNewStreamWriteEnd, | |
ConsolePreferencePage.getPreferenceColor(ConsolePreferencePage.CONSOLE_SYS_IN_RGB), | |
null); | |
if (!fStyleRanges.isEmpty()) { | |
if (((StyleRange)fStyleRanges.get(fStyleRanges.size() - 1)).similarTo(input)) { | |
//remove the top "input" range...continuing input | |
fStyleRanges.remove(fStyleRanges.size() - 1); | |
} | |
} | |
addNewStyleRange(input); | |
} | |
} | |
protected void updateOutputStyleRanges(int sourceStream) { | |
if (fClosed) { | |
return; | |
} | |
int docLength= getLength(); | |
if (docLength == 0) { | |
return; | |
} | |
if ((fNewStreamWriteEnd == 0) && (0 == fLastStreamWriteEnd)) { | |
return; | |
} | |
if (fNewStreamWriteEnd == fLastStreamWriteEnd) { | |
return; | |
} | |
Color newRangeColor= | |
(sourceStream == ConsoleDocument.OUT) ? ConsolePreferencePage.getPreferenceColor(ConsolePreferencePage.CONSOLE_SYS_OUT_RGB) : ConsolePreferencePage.getPreferenceColor(ConsolePreferencePage.CONSOLE_SYS_ERR_RGB); | |
StyleRange newRange= new StyleRange(fLastStreamWriteEnd, fNewStreamWriteEnd - fLastStreamWriteEnd, newRangeColor, null); | |
if (!fStyleRanges.isEmpty()) { | |
if ((docLength != fNewStreamWriteEnd) && | |
((StyleRange)fStyleRanges.get(fStyleRanges.size() - 1)).foreground == | |
ConsolePreferencePage.getPreferenceColor(ConsolePreferencePage.CONSOLE_SYS_IN_RGB)) { | |
//remove the top "input" range..it will get recalculated in updateInputStyleRanges | |
fStyleRanges.remove(fStyleRanges.size() - 1); | |
} | |
} | |
addNewStyleRange(newRange); | |
coaleseRanges(); | |
updateInputStyleRange(docLength); | |
//notify the viewer that the style ranges have changed. | |
fireDocumentChanged(new DocumentEvent(this, 0, 0, null)); | |
} | |
/** | |
* Adds a new style range if the document is not closed. | |
* Note that the document can be closed by a separate thread. | |
* This is the reason for the copy of the style ranges. | |
*/ | |
protected void addNewStyleRange(StyleRange newRange) { | |
List tempRanges= fStyleRanges; | |
if (fClosed) { | |
return; | |
} | |
tempRanges.add(newRange); | |
} | |
protected void setStyleRanges(List ranges) { | |
fStyleRanges= ranges; | |
} | |
protected void clearDocument() { | |
fStyleRanges= new ArrayList(2); | |
set(""); | |
} | |
/** | |
* Returns the position after which editing of the | |
* content is allowable. | |
*/ | |
protected int getStartOfEditableContent() { | |
return fLastStreamWriteEnd; | |
} | |
/** | |
* Make visible to the ConsoleViewer | |
*/ | |
protected ITextStore getStore() { | |
return super.getStore(); | |
} | |
/** | |
* @see IDebugEventListener | |
*/ | |
public void handleDebugEvent(DebugEvent event) { | |
if (fProcess == null) { | |
return; | |
} | |
if (event.getKind() == DebugEvent.TERMINATE) { | |
Object element= event.getSource(); | |
if (element != null && element.equals(fProcess)) { | |
update( new Runnable() { | |
public void run() { | |
fireDocumentChanged(new DocumentEvent(ConsoleDocument.this, 0, 0, null)); | |
} | |
}); | |
} | |
} | |
} | |
/** | |
* Posts the update code "behind" the running operation if the | |
* UI will be updated. | |
*/ | |
protected void update(Runnable runnable) { | |
if (fConsoleViewer != null) { | |
fConsoleViewer.getControl().getDisplay().asyncExec(runnable); | |
} else { | |
Display display= DebugUIPlugin.getDefault().getDisplay(); | |
if (display != null) { | |
display.asyncExec(runnable); | |
} | |
} | |
} | |
/** | |
* Sets the console viewer that this document is viewed within. | |
* Can be set to <code>null</code> if no longer currently being | |
* viewed. | |
*/ | |
protected void setConsoleViewer(ConsoleViewer viewer) { | |
fConsoleViewer = viewer; | |
} | |
} | |