org.chromium.debug.core/src/org/chromium/debug/core/model/BreakpointRegistry.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/BreakpointRegistry.java	Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,227 @@
+// 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.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Map;
+
+import org.chromium.sdk.Breakpoint;
+import org.chromium.sdk.Script;
+
+/**
+ * A registry of existing breakpoints associated with their script locations. It
+ * is used to restore
+ */
+public class BreakpointRegistry {
+
+  /**
+   * Script identifier for a breakpoint location.
+   */
+  public static class ScriptIdentifier {
+    private final String name;
+
+    private final long id;
+
+    private final int startLine;
+
+    private final int endLine;
+
+    public static ScriptIdentifier forScript(Script script) {
+      String name = script.getName();
+      return new ScriptIdentifier(
+          name,
+          name != null ? -1 : script.getId(),
+          script.getStartLine(),
+          script.getEndLine());
+    }
+
+    private ScriptIdentifier(String name, long id, int startLine, int endLine) {
+      this.name = name;
+      this.id = id;
+      this.startLine = startLine;
+      this.endLine = endLine;
+    }
+
+    @Override
+    public int hashCode() {
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + (int) (id ^ (id >>> 32));
+      result = prime * result + ((name == null) ? 0 : name.hashCode());
+      result = prime * result + 17 * startLine + 19 * endLine;
+      return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      if (!(obj instanceof ScriptIdentifier)) {
+        return false;
+      }
+      ScriptIdentifier that = (ScriptIdentifier) obj;
+      if (this.startLine != that.startLine || this.endLine != that.endLine) {
+        return false;
+      }
+      if (name == null) {
+        // an unnamed script, only id is known
+        return that.name == null && this.id == that.id;
+      }
+      // a named script
+      return this.name.equals(that.name);
+    }
+  }
+
+  static class BreakpointLocation {
+    private final ScriptIdentifier scriptIdentifier;
+
+    private final int line;
+
+    public BreakpointLocation(ScriptIdentifier scriptIdentifier, int line) {
+      this.scriptIdentifier = scriptIdentifier;
+      this.line = line;
+    }
+
+    public ScriptIdentifier getScriptIdentifier() {
+      return scriptIdentifier;
+    }
+
+    public int getLine() {
+      return line;
+    }
+
+    @Override
+    public int hashCode() {
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + line;
+      result = prime * result + ((scriptIdentifier == null)
+          ? 0
+          : scriptIdentifier.hashCode());
+      return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      if (!(obj instanceof BreakpointLocation)) {
+        return false;
+      }
+      BreakpointLocation that = (BreakpointLocation) obj;
+      return (this.line == that.line && eq(this.scriptIdentifier, that.scriptIdentifier));
+    }
+  }
+
+  /**
+   * A breakpoint accompanied by its line number in the corresponding enclosing
+   * resource.
+   */
+  public static class BreakpointEntry {
+    public final Breakpoint breakpoint;
+
+    public final int line;
+
+    private BreakpointEntry(Breakpoint breakpoint, int line) {
+      this.breakpoint = breakpoint;
+      this.line = line;
+    }
+
+    boolean isWithinScriptRange(Script script) {
+      return line >= script.getStartLine() && line <= script.getEndLine();
+    }
+
+    @Override
+    public int hashCode() {
+      return 31 * line + 17 * breakpoint.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      if (!(obj instanceof BreakpointEntry)) {
+        return false;
+      }
+      BreakpointEntry that = (BreakpointEntry) obj;
+      return this.line == that.line && this.breakpoint.equals(that.breakpoint);
+    }
+  }
+
+  private final Map<ScriptIdentifier, Collection<BreakpointEntry>> scriptIdToBreakpointEntries =
+      new HashMap<ScriptIdentifier, Collection<BreakpointEntry>>();
+
+  /**
+   * Adds the given line breakpoint.
+   *
+   * @param script where the breakpoint is set
+   * @param line (0-based, like in V8) in the script
+   * @param breakpoint
+   */
+  public void add(Script script, int line, Breakpoint breakpoint) {
+    ScriptIdentifier scriptId = ScriptIdentifier.forScript(script);
+    Collection<BreakpointEntry> entries = scriptIdToBreakpointEntries.get(scriptId);
+    if (entries == null) {
+      entries = new HashSet<BreakpointEntry>();
+      scriptIdToBreakpointEntries.put(scriptId, entries);
+    }
+    entries.add(new BreakpointEntry(breakpoint, line));
+  }
+
+  /**
+   * Gets breakpoint entries for the given script. An empty collection for a
+   * {@code null} script.
+   *
+   * @param script to extract the breakpoints for
+   * @return the breakpoints that fall within the given script line range
+   */
+  public Collection<? extends BreakpointEntry> getBreakpointEntries(Script script) {
+    if (script == null) {
+      return Collections.emptySet();
+    }
+    Collection<BreakpointEntry> entries =
+        scriptIdToBreakpointEntries.get(ScriptIdentifier.forScript(script));
+    if (entries == null) {
+      return Collections.emptySet();
+    }
+    Collection<BreakpointEntry> scriptBreakpoints = new LinkedList<BreakpointEntry>();
+    // Linear search should work fairly well for a reasonable number of
+    // breakpoints per script
+    for (BreakpointEntry entry : entries) {
+      if (entry.isWithinScriptRange(script)) {
+        scriptBreakpoints.add(entry);
+      }
+    }
+    return scriptBreakpoints;
+  }
+
+  /**
+   * Removes the given line breakpoint in the given script. Does nothing for a
+   * {@code null} script (which may be the case after a navigation + disconnect
+   * when the resource referenced by the breakpoint marker is absent.)
+   *
+   * @param script where the breakpoint is set
+   * @param line (0-based, like in V8) in the script
+   * @param breakpoint
+   */
+  public void remove(Script script, int line, Breakpoint breakpoint) {
+    if (script == null) {
+      return;
+    }
+    ScriptIdentifier scriptId = ScriptIdentifier.forScript(script);
+    Collection<BreakpointEntry> entries = scriptIdToBreakpointEntries.get(scriptId);
+    if (entries == null) {
+      return;
+    }
+    if (entries.size() == 1) {
+      scriptIdToBreakpointEntries.remove(scriptId);
+    } else {
+      entries.remove(new BreakpointEntry(breakpoint, line));
+    }
+  }
+
+  protected static boolean eq(Object left, Object right) {
+    return left == right || (left != null && left.equals(right));
+  }
+
+}