org.chromium.debug.core/src/org/chromium/debug/core/model/ConsolePseudoProcess.java
changeset 2 e4420d2515f1
--- /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();
+      }
+    }
+  }
+}