org.chromium.debug.core/src/org/chromium/debug/core/model/ConsolePseudoProcess.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/model/ConsolePseudoProcess.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,260 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.core.model;
+
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.chromium.debug.core.ChromiumDebugPlugin;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.PlatformObject;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.core.DebugEvent;
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.IStreamListener;
+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.model.ITerminate;
+
+/**
+ * This process corresponds to a Debugger-Chrome connection and its main
+ * purpose is to expose connection log (see process console in UI).
+ */
+public class ConsolePseudoProcess extends PlatformObject implements IProcess {
+
+ private final ILaunch launch;
+ private final Retransmitter outputMonitor;
+ private final ITerminate connectionTerminate;
+ private final String name;
+ private Map<String, String> attributes = null;
+
+ private final IStreamsProxy streamsProxy = new IStreamsProxy() {
+ public IStreamMonitor getErrorStreamMonitor() {
+ return NullStreamMonitor.INSTANCE;
+ }
+ public IStreamMonitor getOutputStreamMonitor() {
+ return outputMonitor;
+ }
+ public void write(String input) {
+ // ignore
+ }
+ };
+
+ /**
+ * Constructs a ConsolePseudoProcess, adding this process to the given launch.
+ *
+ * @param launch the parent launch of this process
+ * @param name the label used for this process
+ */
+ public ConsolePseudoProcess(ILaunch launch, String name, Retransmitter retransmitter,
+ ITerminate connectionTerminate) {
+ this.launch = launch;
+ this.name = name;
+ this.outputMonitor = retransmitter;
+ outputMonitor.consolePseudoProcess = this;
+ this.connectionTerminate = connectionTerminate;
+
+ this.launch.addProcess(this);
+ fireCreationEvent();
+ }
+
+ /**
+ * @return writer which directs its contents to process console
+ */
+ public Writer getOutputWriter() {
+ return outputMonitor;
+ }
+
+ public String getLabel() {
+ return name;
+ }
+
+ public ILaunch getLaunch() {
+ return launch;
+ }
+
+ public boolean isTerminated() {
+ return connectionTerminate.isTerminated();
+ }
+
+ public void terminate() throws DebugException {
+ connectionTerminate.terminate();
+ }
+
+ public boolean canTerminate() {
+ return connectionTerminate.canTerminate();
+ }
+
+ public IStreamsProxy getStreamsProxy() {
+ return streamsProxy;
+ }
+
+ /*
+ * We do not expect intensive usage of attributes for this class. In fact, other option was to
+ * keep this method no-op.
+ */
+ public synchronized void setAttribute(String key, String value) {
+ if (attributes == null) {
+ attributes = new HashMap<String, String>(1);
+ }
+ String origVal = attributes.get(key);
+ if (origVal != null && origVal.equals(value)) {
+ return;
+ }
+
+ attributes.put(key, value);
+ fireChangeEvent();
+ }
+
+ /*
+ * We do not expect intensive usage of attributes for this class. In fact, other option was to
+ * put a stub here.
+ */
+ public synchronized String getAttribute(String key) {
+ if (attributes == null) {
+ return null;
+ }
+ return attributes.get(key);
+ }
+
+ public int getExitValue() throws DebugException {
+ if (isTerminated()) {
+ return 0;
+ }
+ throw new DebugException(new Status(IStatus.ERROR, ChromiumDebugPlugin.PLUGIN_ID,
+ "Process hasn't been terminated yet")); //$NON-NLS-1$
+ }
+
+ private void fireCreationEvent() {
+ fireEvent(new DebugEvent(this, DebugEvent.CREATE));
+ }
+
+ private void fireEvent(DebugEvent event) {
+ DebugPlugin manager = DebugPlugin.getDefault();
+ if (manager != null) {
+ manager.fireDebugEventSet(new DebugEvent[] { event });
+ }
+ }
+
+ private void fireTerminateEvent() {
+ outputMonitor.flush();
+ fireEvent(new DebugEvent(this, DebugEvent.TERMINATE));
+ }
+
+ private void fireChangeEvent() {
+ fireEvent(new DebugEvent(this, DebugEvent.CHANGE));
+ }
+
+ @Override
+ public Object getAdapter(Class adapter) {
+ if (adapter.equals(IProcess.class)) {
+ return this;
+ }
+ if (adapter.equals(ILaunch.class)) {
+ return getLaunch();
+ }
+ if (adapter.equals(ILaunchConfiguration.class)) {
+ return getLaunch().getLaunchConfiguration();
+ }
+ return super.getAdapter(adapter);
+ }
+
+
+ private static class NullStreamMonitor implements IStreamMonitor {
+ public void addListener(IStreamListener listener) {
+ }
+ public String getContents() {
+ return null;
+ }
+ public void removeListener(IStreamListener listener) {
+ }
+ static final NullStreamMonitor INSTANCE = new NullStreamMonitor();
+ }
+
+ /**
+ * Responsible for getting text as {@link Writer} and retransmitting it
+ * as {@link IStreamMonitor} to whoever is interested.
+ * However in its initial state it only receives signal (the text) and saves it in a buffer.
+ * For {@link Retransmitter} to start giving the signal away one should
+ * call {@link #startFlushing} method.
+ */
+ public static class Retransmitter extends Writer implements IStreamMonitor {
+ private StringWriter writer = new StringWriter();
+ private boolean isFlushing = false;
+ private final List<IStreamListener> listeners = new ArrayList<IStreamListener>(2);
+ private volatile ConsolePseudoProcess consolePseudoProcess = null;
+
+ public synchronized void addListener(IStreamListener listener) {
+ listeners.add(listener);
+ }
+
+ public String getContents() {
+ return null;
+ }
+
+ public synchronized void removeListener(IStreamListener listener) {
+ listeners.remove(listener);
+ }
+
+ @Override
+ public synchronized void flush() {
+ if (!isFlushing) {
+ return;
+ }
+ String text = writer.toString();
+ int lastLinePos;
+ final boolean flushOnlyFullLines = true;
+ if (flushOnlyFullLines) {
+ int pos = text.lastIndexOf('\n');
+ if (pos == -1) {
+ // No full line in the buffer.
+ return;
+ }
+ lastLinePos = pos + 1;
+ } else {
+ lastLinePos = text.length();
+ }
+ String readyText = text.substring(0, lastLinePos);
+ writer = new StringWriter();
+ if (lastLinePos != text.length()) {
+ String rest = text.substring(lastLinePos);
+ writer.append(rest);
+ }
+ for (IStreamListener listener : listeners) {
+ listener.streamAppended(readyText, this);
+ }
+ }
+
+ @Override
+ public synchronized void close() {
+ // do nothing
+ }
+
+ @Override
+ public synchronized void write(char[] cbuf, int off, int len) {
+ writer.write(cbuf, off, len);
+ }
+
+ public synchronized void startFlushing() {
+ isFlushing = true;
+ flush();
+ }
+
+ public void processClosed() {
+ ConsolePseudoProcess consolePseudoProcess0 = this.consolePseudoProcess;
+ if (consolePseudoProcess0 != null) {
+ consolePseudoProcess0.fireTerminateEvent();
+ }
+ }
+ }
+}