Build blame cache as own java_library

This is a first step towards properly packaging of blame cache in Gerrit
server which is needed for [1]. [1] currently (PS-44) depends on the
whole gitiles-servlet and this leads to a:

  java.lang.NoClassDefFoundError: com/google/template/soy/SoyFileSet

caused by packaging the gitiles-servlet in the Gerrit core but without
all of its dependencies.

[1] https://gerrit-review.googlesource.com/72798

Change-Id: I9326b30962c21082b3d152eb6397ddcf767d1ce3
diff --git a/gitiles-servlet/BUCK b/gitiles-servlet/BUCK
index 8a0de1f..81c06e2 100644
--- a/gitiles-servlet/BUCK
+++ b/gitiles-servlet/BUCK
@@ -1,6 +1,7 @@
 SRCS = glob(['src/main/java/**/*.java'])
 RSRC = glob(['src/main/resources/**/*'])
 DEPS = [
+  '//blame-cache:lib',
   '//lib:commons-lang',
   '//lib:grappa',
   '//lib:gson',
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/blame/BlameCache.java b/gitiles-servlet/src/main/java/com/google/gitiles/blame/BlameCache.java
deleted file mode 100644
index d0c6fe5..0000000
--- a/gitiles-servlet/src/main/java/com/google/gitiles/blame/BlameCache.java
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (C) 2014 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gitiles.blame;
-
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Repository;
-
-import java.io.IOException;
-import java.util.List;
-
-public interface BlameCache {
-  /** @return the blame of a path at a given commit. */
-  public List<Region> get(Repository repo, ObjectId commitId, String path) throws IOException;
-
-  /**
-   * @return the last commit that modified a path, starting at the given
-   *     commit.
-   */
-  public ObjectId findLastCommit(Repository repo, ObjectId commitId, String path)
-      throws IOException;
-}
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/blame/BlameCacheImpl.java b/gitiles-servlet/src/main/java/com/google/gitiles/blame/BlameCacheImpl.java
deleted file mode 100644
index fc6ed6c..0000000
--- a/gitiles-servlet/src/main/java/com/google/gitiles/blame/BlameCacheImpl.java
+++ /dev/null
@@ -1,218 +0,0 @@
-// Copyright (C) 2014 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gitiles.blame;
-
-import static com.google.common.base.Preconditions.checkState;
-import static java.util.Objects.hash;
-
-import com.google.common.cache.Cache;
-import com.google.common.cache.CacheBuilder;
-import com.google.common.cache.Weigher;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Interner;
-import com.google.common.collect.Interners;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-
-import org.eclipse.jgit.blame.BlameGenerator;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.treewalk.filter.AndTreeFilter;
-import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
-import org.eclipse.jgit.treewalk.filter.TreeFilter;
-import org.eclipse.jgit.util.QuotedString;
-
-import java.io.IOException;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-
-/** Guava implementation of BlameCache, weighted by number of blame regions. */
-public class BlameCacheImpl implements BlameCache {
-  public static CacheBuilder<Key, List<Region>> defaultBuilder() {
-    return weigher(CacheBuilder.newBuilder()).maximumWeight(10 << 10);
-  }
-
-  public static CacheBuilder<Key, List<Region>> weigher(
-      CacheBuilder<? super Key, ? super List<Region>> builder) {
-    return builder.weigher(new Weigher<Key, List<Region>>() {
-      @Override
-      public int weigh(Key key, List<Region> value) {
-        return value.size();
-      }
-    });
-  }
-
-  public static class Key {
-    private final ObjectId commitId;
-    private final String path;
-
-    public Key(ObjectId commitId, String path) {
-      this.commitId = commitId;
-      this.path = path;
-    }
-
-    public ObjectId getCommitId() {
-      return commitId;
-    }
-
-    public String getPath() {
-      return path;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-      if (o instanceof Key) {
-        Key k = (Key) o;
-        return Objects.equals(commitId, k.commitId)
-            && Objects.equals(path, k.path);
-      }
-      return false;
-    }
-
-    @Override
-    public int hashCode() {
-      return hash(commitId, path);
-    }
-
-    @Override
-    public String toString() {
-      return commitId.name() + ":" + QuotedString.GIT_PATH.quote(path);
-    }
-  }
-
-  private final Cache<Key, List<Region>> cache;
-
-  public BlameCacheImpl() {
-    this(defaultBuilder());
-  }
-
-  public Cache<Key, List<Region>> getCache() {
-    return cache;
-  }
-
-  public Callable<List<Region>> newLoader(final Key key, final Repository repo) {
-    return new Callable<List<Region>>() {
-      @Override
-      public List<Region> call() throws IOException {
-        return loadBlame(key, repo);
-      }
-    };
-  }
-
-  public BlameCacheImpl(CacheBuilder<? super Key, ? super List<Region>> builder) {
-    this.cache = builder.build();
-  }
-
-  @Override
-  public List<Region> get(Repository repo, ObjectId commitId, String path)
-      throws IOException {
-    try {
-      Key key = new Key(commitId, path);
-      return cache.get(key, newLoader(key, repo));
-    } catch (ExecutionException e) {
-      throw new IOException(e);
-    }
-  }
-
-  @Override
-  public ObjectId findLastCommit(Repository repo, ObjectId commitId, String path)
-      throws IOException {
-    // Default implementation does no caching.
-    try (RevWalk rw = new RevWalk(repo)) {
-      rw.markStart(rw.parseCommit(commitId));
-      rw.setRewriteParents(false);
-      // Don't use rename detection, even though BlameGenerator does. It is not
-      // possible for a commit to modify a path when not doing rename detection
-      // but to not modify the same path when taking renames into account.
-      rw.setTreeFilter(AndTreeFilter.create(
-          PathFilterGroup.createFromStrings(path),
-          TreeFilter.ANY_DIFF));
-      return rw.next();
-    }
-  }
-
-  public static List<Region> loadBlame(Key key, Repository repo) throws IOException {
-    try (BlameGenerator gen = new BlameGenerator(repo, key.path)) {
-      gen.push(null, key.commitId);
-      return loadRegions(gen);
-    }
-  }
-
-  private static class PooledCommit {
-    final ObjectId commit;
-    final PersonIdent author;
-
-    private PooledCommit(ObjectId commit, PersonIdent author) {
-      this.commit = commit;
-      this.author = author;
-    }
-  }
-
-  private static List<Region> loadRegions(BlameGenerator gen) throws IOException {
-    Map<ObjectId, PooledCommit> commits = Maps.newHashMap();
-    Interner<String> strings = Interners.newStrongInterner();
-    int lineCount = gen.getResultContents().size();
-
-    List<Region> regions = Lists.newArrayList();
-    while (gen.next()) {
-      String path = gen.getSourcePath();
-      PersonIdent author = gen.getSourceAuthor();
-      ObjectId commit = gen.getSourceCommit();
-      checkState(path != null && author != null && commit != null);
-
-      PooledCommit pc = commits.get(commit);
-      if (pc == null) {
-        pc = new PooledCommit(commit.copy(),
-            new PersonIdent(
-              strings.intern(author.getName()),
-              strings.intern(author.getEmailAddress()),
-              author.getWhen(),
-              author.getTimeZone()));
-        commits.put(pc.commit, pc);
-      }
-      path = strings.intern(path);
-      commit = pc.commit;
-      author = pc.author;
-      regions.add(new Region(path, commit, author, gen.getResultStart(), gen.getResultEnd()));
-    }
-    Collections.sort(regions);
-
-    // Fill in any gaps left by bugs in JGit, since rendering code assumes the
-    // full set of contiguous regions.
-    List<Region> result = Lists.newArrayListWithExpectedSize(regions.size());
-    Region last = null;
-    for (Region r : regions) {
-      if (last != null) {
-        checkState(last.getEnd() <= r.getStart());
-        if (last.getEnd() < r.getStart()) {
-          result.add(new Region(null, null, null, last.getEnd(), r.getStart()));
-        }
-      }
-      result.add(r);
-      last = r;
-    }
-    if (last != null && last.getEnd() != lineCount) {
-      result.add(new Region(null, null, null, last.getEnd(), lineCount));
-    }
-
-    return ImmutableList.copyOf(result);
-  }
-}
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/blame/Region.java b/gitiles-servlet/src/main/java/com/google/gitiles/blame/Region.java
deleted file mode 100644
index d6f928d..0000000
--- a/gitiles-servlet/src/main/java/com/google/gitiles/blame/Region.java
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright (C) 2014 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gitiles.blame;
-
-import static com.google.common.base.Preconditions.checkArgument;
-
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.PersonIdent;
-
-import java.io.Serializable;
-
-/** Region of the blame of a file. */
-public class Region implements Serializable, Comparable<Region> {
-  private static final long serialVersionUID = 1L;
-
-  private final String sourcePath;
-  private final ObjectId sourceCommit;
-  private final PersonIdent sourceAuthor;
-  private final int count;
-  private transient int start;
-
-  public Region(String path, ObjectId commit, PersonIdent author, int start, int end) {
-    checkArgument((path != null && commit != null && author != null)
-        || (path == null && commit == null && author == null),
-        "expected all null or none: %s, %s, %s", path, commit, author);
-    this.sourcePath = path;
-    this.sourceCommit = commit;
-    this.sourceAuthor = author;
-    this.start = start;
-    this.count = end - start;
-  }
-
-  void setStart(int start) {
-    this.start = start;
-  }
-
-  int getStart() {
-    return start;
-  }
-
-  int getEnd() {
-    return start + count;
-  }
-
-  public int getCount() {
-    return count;
-  }
-
-  public String getSourcePath() {
-    return sourcePath;
-  }
-
-  public ObjectId getSourceCommit() {
-    return sourceCommit;
-  }
-
-  public PersonIdent getSourceAuthor() {
-    return sourceAuthor;
-  }
-
-  @Override
-  public int compareTo(Region o) {
-    return start - o.start;
-  }
-
-  @Override
-  public String toString() {
-    StringBuilder sb = new StringBuilder();
-    if (sourceCommit != null) {
-      sb.append(sourceCommit.name(), 0, 7)
-          .append(' ')
-          .append(sourceAuthor.toExternalString())
-          .append(" (").append(sourcePath).append(')');
-    } else {
-      sb.append("<unblamed region>");
-    }
-    sb.append(' ')
-        .append("start=").append(start)
-        .append(", count=").append(count);
-    return sb.toString();
-  }
-}