Merge "Count unresolved threads within thread groups rather than by leaves" into stable-2.15
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
index dfcc999..ba182d2 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
@@ -18,7 +18,6 @@
 import static com.google.gerrit.server.ApprovalsUtil.sortApprovals;
 import static java.util.stream.Collectors.toList;
 import static java.util.stream.Collectors.toMap;
-import static java.util.stream.Collectors.toSet;
 
 import com.google.auto.value.AutoValue;
 import com.google.common.base.MoreObjects;
@@ -882,15 +881,48 @@
 
       List<Comment> comments =
           Stream.concat(publishedComments().stream(), robotComments().stream()).collect(toList());
-      Set<String> nonLeafSet = comments.stream().map(c -> c.parentUuid).collect(toSet());
 
-      Long count =
-          comments.stream().filter(c -> (c.unresolved && !nonLeafSet.contains(c.key.uuid))).count();
-      unresolvedCommentCount = count.intValue();
+      // Build a map of uuid to list of direct descendants.
+      Map<String, List<Comment>> forest = new HashMap<>();
+      for (Comment comment : comments) {
+        List<Comment> siblings = forest.get(comment.parentUuid);
+        if (siblings == null) {
+          siblings = new ArrayList<>();
+          forest.put(comment.parentUuid, siblings);
+        }
+        siblings.add(comment);
+      }
+
+      // Find latest comment in each thread and apply to unresolved counter.
+      int unresolved = 0;
+      if (forest.containsKey(null)) {
+        for (Comment root : forest.get(null)) {
+          if (getLatestComment(forest, root).unresolved) {
+            unresolved++;
+          }
+        }
+      }
+      unresolvedCommentCount = unresolved;
     }
+
     return unresolvedCommentCount;
   }
 
+  protected Comment getLatestComment(Map<String, List<Comment>> forest, Comment root) {
+    List<Comment> children = forest.get(root.key.uuid);
+    if (children == null) {
+      return root;
+    }
+    Comment latest = null;
+    for (Comment comment : children) {
+      Comment branchLatest = getLatestComment(forest, comment);
+      if (latest == null || branchLatest.writtenOn.after(latest.writtenOn)) {
+        latest = branchLatest;
+      }
+    }
+    return latest;
+  }
+
   public void setUnresolvedCommentCount(Integer count) {
     this.unresolvedCommentCount = count;
   }