| /******************************************************************************* |
| * Copyright (c) 2005, 2016 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| *******************************************************************************/ |
| package org.eclipse.dltk.console.ui; |
| |
| import java.io.BufferedReader; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.util.ArrayList; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| import org.eclipse.core.runtime.ListenerList; |
| import org.eclipse.debug.core.DebugPlugin; |
| import org.eclipse.debug.core.ILaunch; |
| import org.eclipse.debug.core.ILaunchesListener2; |
| import org.eclipse.debug.core.IStreamListener; |
| import org.eclipse.debug.core.model.IFlushableStreamMonitor; |
| import org.eclipse.debug.core.model.IStreamMonitor; |
| import org.eclipse.debug.core.model.IStreamsProxy; |
| import org.eclipse.debug.ui.IDebugUIConstants; |
| import org.eclipse.dltk.compiler.util.Util; |
| import org.eclipse.dltk.console.IScriptConsoleInterpreter; |
| import org.eclipse.dltk.console.IScriptExecResult; |
| import org.eclipse.dltk.console.IScriptInterpreter; |
| import org.eclipse.dltk.console.ScriptConsoleHistory; |
| import org.eclipse.dltk.console.ScriptConsolePrompt; |
| import org.eclipse.dltk.console.ScriptExecResult; |
| import org.eclipse.dltk.console.ui.internal.ICommandHandler; |
| import org.eclipse.dltk.console.ui.internal.ScriptConsolePage; |
| import org.eclipse.dltk.console.ui.internal.ScriptConsoleSession; |
| import org.eclipse.dltk.console.ui.internal.ScriptConsoleViewer; |
| import org.eclipse.dltk.console.ui.internal.ScriptConsoleViewer.ConsoleDocumentListener; |
| import org.eclipse.dltk.core.DLTKCore; |
| import org.eclipse.dltk.launching.process.IScriptProcess; |
| import org.eclipse.jface.resource.ImageDescriptor; |
| import org.eclipse.jface.text.IDocument; |
| import org.eclipse.jface.text.ITextHover; |
| import org.eclipse.jface.text.TextUtilities; |
| import org.eclipse.jface.text.contentassist.IContentAssistProcessor; |
| import org.eclipse.jface.text.source.SourceViewerConfiguration; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.ui.console.IConsoleDocumentPartitioner; |
| import org.eclipse.ui.console.IConsoleView; |
| import org.eclipse.ui.console.TextConsole; |
| import org.eclipse.ui.part.IPageBookViewPage; |
| |
| public class ScriptConsole extends TextConsole |
| implements ICommandHandler, IScriptConsole { |
| private ILaunch launch = null; |
| private ILaunchesListener2 listener = null; |
| |
| private class ScriptConsoleLaunchListener implements ILaunchesListener2 { |
| @Override |
| public void launchesTerminated(ILaunch[] launches) { |
| if (terminated) { |
| return; |
| } |
| for (int i = 0; i < launches.length; i++) { |
| if (launches[i].equals(launch)) { |
| final ScriptConsoleViewer consoleViewer = (ScriptConsoleViewer) page |
| .getViewer(); |
| page.getControl().getDisplay().asyncExec(new Runnable() { |
| @Override |
| public void run() { |
| if (consoleViewer != null) { |
| consoleViewer.disableProcessing(); |
| // TODO use different color |
| updateText(consoleViewer, |
| Messages.ScriptConsole_processTerminated); |
| consoleViewer.setEditable(false); |
| } |
| } |
| }); |
| } |
| } |
| } |
| |
| @Override |
| public void launchesAdded(ILaunch[] launches) { |
| } |
| |
| @Override |
| public void launchesChanged(ILaunch[] launches) { |
| } |
| |
| @Override |
| public void launchesRemoved(ILaunch[] launches) { |
| } |
| } |
| |
| private final class InitialStreamReader implements Runnable { |
| private final IScriptInterpreter interpreter; |
| |
| private InitialStreamReader(IScriptInterpreter interpreter) { |
| this.interpreter = interpreter; |
| } |
| |
| @Override |
| public void run() { |
| // We need to be sure what page is already created |
| while (page == null || (page != null && page.getViewer() == null)) { |
| try { |
| Thread.sleep(50); |
| } catch (InterruptedException e) { |
| if (DLTKCore.DEBUG) { |
| e.printStackTrace(); |
| } |
| } |
| } |
| final ScriptConsoleViewer viewer = (ScriptConsoleViewer) page |
| .getViewer(); |
| InputStream stream = interpreter.getInitialOutputStream(); |
| if (stream == null) { |
| return; |
| } |
| final BufferedReader reader = new BufferedReader( |
| new InputStreamReader(stream)); |
| Thread readerThread = new Thread() { |
| @Override |
| public void run() { |
| while (!terminated) { |
| String readLine; |
| try { |
| readLine = reader.readLine(); |
| if (readLine != null) { |
| updateText(viewer, readLine); |
| } else { |
| break; |
| } |
| } catch (IOException e) { |
| if (DLTKCore.DEBUG) { |
| e.printStackTrace(); |
| } |
| break; |
| } |
| } |
| enableEdit(viewer); |
| } |
| |
| }; |
| readerThread.start(); |
| } |
| |
| } |
| |
| protected void enableEdit(final ScriptConsoleViewer viewer) { |
| Control control = viewer.getControl(); |
| if (control == null) { |
| return; |
| } |
| control.getDisplay().asyncExec(new Runnable() { |
| @Override |
| public void run() { |
| viewer.setEditable(true); |
| } |
| }); |
| } |
| |
| private void updateText(final ScriptConsoleViewer viewer, |
| final String text) { |
| Control control = viewer.getControl(); |
| if (control == null) { |
| return; |
| } |
| control.getDisplay().asyncExec(new Runnable() { |
| @Override |
| public void run() { |
| IDocument document = getDocument(); |
| final String delim = TextUtilities |
| .getDefaultLineDelimiter(document); |
| getDocumentListener().write(text + delim, false); |
| } |
| }); |
| } |
| |
| private ScriptConsolePage page; |
| |
| private ScriptConsolePartitioner partitioner; |
| |
| private IContentAssistProcessor processor; |
| |
| private ITextHover hover; |
| |
| private IScriptInterpreter interpreter; |
| |
| private ScriptConsoleSession session; |
| |
| private ListenerList consoleListeners; |
| |
| private ScriptConsolePrompt prompt; |
| |
| private ScriptConsoleHistory history; |
| |
| private boolean terminated = false; |
| |
| @Override |
| protected IConsoleDocumentPartitioner getPartitioner() { |
| return partitioner; |
| } |
| |
| public ScriptConsolePage getPage() { |
| return page; |
| } |
| |
| public ScriptConsole(String consoleName, String consoleType, |
| ImageDescriptor image) { |
| super(consoleName, consoleType, image, true); |
| |
| this.consoleListeners = new ListenerList(ListenerList.IDENTITY); |
| this.prompt = new ScriptConsolePrompt("=>", "->"); //$NON-NLS-1$ //$NON-NLS-2$ |
| this.history = new ScriptConsoleHistory(); |
| |
| this.session = new ScriptConsoleSession(); |
| addListener(this.session); |
| |
| partitioner = new ScriptConsolePartitioner(); |
| getDocument().setDocumentPartitioner(partitioner); |
| partitioner.connect(getDocument()); |
| } |
| |
| public ScriptConsole(String consoleName, String consoleType) { |
| this(consoleName, consoleType, null); |
| } |
| |
| public IScriptConsoleSession getSession() { |
| return session; |
| } |
| |
| public void addListener(IScriptConsoleListener listener) { |
| consoleListeners.add(listener); |
| } |
| |
| public void removeListener(IScriptConsoleListener listener) { |
| consoleListeners.remove(listener); |
| } |
| |
| protected void setContentAssistProcessor( |
| IContentAssistProcessor processor) { |
| this.processor = processor; |
| } |
| |
| protected void setInterpreter(final IScriptInterpreter interpreter) { |
| this.interpreter = interpreter; |
| interpreter.addInitialListenerOperation( |
| new InitialStreamReader(interpreter)); |
| } |
| |
| public void setPrompt(ScriptConsolePrompt prompt) { |
| this.prompt = prompt; |
| } |
| |
| public ScriptConsolePrompt getPrompt() { |
| return prompt; |
| } |
| |
| public ScriptConsoleHistory getHistory() { |
| return history; |
| } |
| |
| protected void setTextHover(ITextHover hover) { |
| this.hover = hover; |
| } |
| |
| private ConsoleDocumentListener documentListener; |
| |
| public ConsoleDocumentListener getDocumentListener() { |
| if (documentListener == null) { |
| documentListener = new ConsoleDocumentListener(this, |
| this.getPrompt(), this.getHistory()); |
| documentListener.setDocument(getDocument()); |
| |
| } |
| |
| return documentListener; |
| } |
| |
| @Override |
| public IPageBookViewPage createPage(IConsoleView view) { |
| SourceViewerConfiguration cfg = new ScriptConsoleSourceViewerConfiguration( |
| processor, hover); |
| page = createPage(view, cfg); |
| return page; |
| } |
| |
| protected ScriptConsolePage createPage(IConsoleView view, |
| SourceViewerConfiguration cfg) { |
| return new ScriptConsolePage(this, view, cfg); |
| } |
| |
| @Override |
| public void clearConsole() { |
| page.clearConsolePage(); |
| } |
| |
| /** |
| * @since 2.0 |
| */ |
| @Override |
| public void insertText(String line) { |
| page.insertText(line); |
| } |
| |
| public int getState() { |
| return interpreter.getState(); |
| } |
| |
| @Override |
| public IScriptExecResult handleCommand(String userInput) |
| throws IOException { |
| if (this.interpreter == null || !this.interpreter.isValid()) { |
| return new ScriptExecResult(Util.EMPTY_STRING); |
| } |
| Object[] listeners = consoleListeners.getListeners(); |
| for (int i = 0; i < listeners.length; i++) { |
| ((IScriptConsoleListener) listeners[i]).userRequest(userInput); |
| } |
| |
| IScriptExecResult output = interpreter.exec(userInput); |
| |
| if (interpreter |
| .getState() == IScriptConsoleInterpreter.WAIT_NEW_COMMAND) { |
| prompt.setMode(true); |
| } else { |
| prompt.setMode(false); |
| } |
| |
| for (int i = 0; i < listeners.length; i++) { |
| ((IScriptConsoleListener) listeners[i]).interpreterResponse(output); |
| } |
| |
| return output; |
| } |
| |
| /** |
| * Executes the specified code and displays the results |
| * |
| * @param command |
| */ |
| public void executeCommand(String command) { |
| getDocumentListener().executeCommand(command); |
| } |
| |
| @Override |
| public void terminate() { |
| terminated = true; |
| try { |
| interpreter.close(); |
| } catch (IOException e) { |
| if (DLTKCore.DEBUG) { |
| e.printStackTrace(); |
| } |
| } |
| } |
| |
| @Override |
| public void dispose() { |
| partitioner.clearRanges(); |
| |
| terminate(); |
| if (listener != null) { |
| DebugPlugin.getDefault().getLaunchManager() |
| .removeLaunchListener(listener); |
| listener = null; |
| } |
| closeStreams(); |
| disposeStreams(); |
| |
| super.dispose(); |
| } |
| |
| public void setLaunch(ILaunch launch) { |
| this.launch = launch; |
| if (this.launch != null && this.listener == null) { |
| this.listener = new ScriptConsoleLaunchListener(); |
| DebugPlugin.getDefault().getLaunchManager() |
| .addLaunchListener(listener); |
| } |
| } |
| |
| /** |
| * @return the launch |
| */ |
| public ILaunch getLaunch() { |
| return launch; |
| } |
| |
| private Set<IScriptProcess> connectedProcesses; |
| |
| /** |
| * @param process |
| */ |
| public synchronized void connect(IScriptProcess process) { |
| if (connectedProcesses == null) { |
| connectedProcesses = new HashSet<IScriptProcess>(); |
| } |
| if (connectedProcesses.add(process)) { |
| final IStreamsProxy proxy = process.getScriptStreamsProxy(); |
| if (proxy == null) { |
| return; |
| } |
| connect(proxy); |
| } |
| } |
| |
| @Override |
| public void connect(final IStreamsProxy proxy) { |
| IStreamMonitor streamMonitor = proxy.getErrorStreamMonitor(); |
| if (streamMonitor != null) { |
| connect(streamMonitor, IDebugUIConstants.ID_STANDARD_ERROR_STREAM); |
| } |
| streamMonitor = proxy.getOutputStreamMonitor(); |
| if (streamMonitor != null) { |
| connect(streamMonitor, IDebugUIConstants.ID_STANDARD_OUTPUT_STREAM); |
| } |
| } |
| |
| private List<StreamListener> fStreamListeners = new ArrayList<StreamListener>(); |
| |
| /** |
| * @param streamMonitor |
| * @param idStandardErrorStream |
| */ |
| private void connect(IStreamMonitor streamMonitor, |
| String streamIdentifier) { |
| synchronized (streamMonitor) { |
| StreamListener listener = new StreamListener(streamIdentifier, |
| streamMonitor); |
| fStreamListeners.add(listener); |
| } |
| } |
| |
| /** |
| * cleanup method to close all of the open stream to this console |
| */ |
| private synchronized void closeStreams() { |
| for (StreamListener listener : fStreamListeners) { |
| listener.closeStream(); |
| } |
| } |
| |
| /** |
| * disposes of the listeners for each of the stream associated with this |
| * console |
| */ |
| private synchronized void disposeStreams() { |
| for (StreamListener listener : fStreamListeners) { |
| listener.dispose(); |
| } |
| } |
| |
| /** |
| * This class listens to a specified IO stream |
| */ |
| private class StreamListener implements IStreamListener { |
| private IStreamMonitor fStreamMonitor; |
| private String fStreamId; |
| private boolean fFlushed = false; |
| private boolean fListenerRemoved = false; |
| |
| public StreamListener(String streamIdentifier, IStreamMonitor monitor) { |
| this.fStreamId = streamIdentifier; |
| this.fStreamMonitor = monitor; |
| 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) { |
| if (fFlushed) { |
| getDocumentListener().write(text, |
| IDebugUIConstants.ID_STANDARD_ERROR_STREAM |
| .equals(fStreamId)); |
| } else { |
| String contents = null; |
| synchronized (fStreamMonitor) { |
| fFlushed = true; |
| contents = fStreamMonitor.getContents(); |
| if (fStreamMonitor instanceof IFlushableStreamMonitor) { |
| IFlushableStreamMonitor m = (IFlushableStreamMonitor) fStreamMonitor; |
| m.flushContents(); |
| m.setBuffered(false); |
| } |
| } |
| if (contents != null && contents.length() > 0) { |
| getDocumentListener().write(contents, |
| IDebugUIConstants.ID_STANDARD_ERROR_STREAM |
| .equals(fStreamId)); |
| } |
| } |
| } |
| |
| public void closeStream() { |
| if (fStreamMonitor == null) { |
| return; |
| } |
| synchronized (fStreamMonitor) { |
| fStreamMonitor.removeListener(this); |
| if (!fFlushed) { |
| String contents = fStreamMonitor.getContents(); |
| streamAppended(contents, fStreamMonitor); |
| } |
| fListenerRemoved = true; |
| } |
| } |
| |
| public void dispose() { |
| if (!fListenerRemoved) { |
| closeStream(); |
| } |
| fStreamMonitor = null; |
| fStreamId = null; |
| } |
| } |
| } |