Merge "Add documentation of the changes REST API /related endpoint"
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/AbstractChangeUpdate.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/AbstractChangeUpdate.java
index aa1ff47..0d637f5 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/AbstractChangeUpdate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/AbstractChangeUpdate.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.server.notedb;
 
+import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.gerrit.server.notedb.ChangeNoteUtil.GERRIT_PLACEHOLDER_HOST;
 
 import com.google.gerrit.reviewdb.client.Account;
@@ -70,6 +71,12 @@
     return (IdentifiedUser) ctl.getCurrentUser();
   }
 
+  public void setPatchSetId(PatchSet.Id psId) {
+    checkArgument(psId == null
+        || psId.getParentKey().equals(getChange().getId()));
+    this.psId = psId;
+  }
+
   private void load() throws IOException {
     if (migration.write() && getRevision() == null) {
       Repository repo = repoManager.openRepository(getProjectName());
@@ -130,6 +137,11 @@
   }
 
   @Override
+  public RevCommit commit(MetaDataUpdate md) throws IOException {
+    throw new UnsupportedOperationException("use commit()");
+  }
+
+  @Override
   protected void onLoad() throws IOException, ConfigInvalidException {
     //Do nothing; just reads the current revision.
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotes.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotes.java
index 7da564c..b242d7c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotes.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotes.java
@@ -44,6 +44,7 @@
 import com.google.gerrit.reviewdb.client.ChangeMessage;
 import com.google.gerrit.reviewdb.client.PatchLineComment;
 import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gerrit.reviewdb.client.PatchLineComment.Status;
 import com.google.gerrit.reviewdb.client.PatchSet.Id;
 import com.google.gerrit.reviewdb.client.PatchSetApproval;
 import com.google.gerrit.reviewdb.client.PatchSetApproval.LabelId;
@@ -102,8 +103,8 @@
       String filename1 = c1.getKey().getParentKey().get();
       String filename2 = c2.getKey().getParentKey().get();
       return ComparisonChain.start()
-          .compare(c1.getLine(), c2.getLine())
           .compare(filename1, filename2)
+          .compare(c1.getLine(), c2.getLine())
           .compare(c1.getWrittenOn(), c2.getWrittenOn())
           .result();
     }
@@ -323,7 +324,7 @@
         throws IOException, ConfigInvalidException, ParseException {
       commentNoteMap = CommentsInNotesUtil.parseCommentsFromNotes(repo,
           ChangeNoteUtil.changeRefName(changeId), walk, changeId,
-          commentsForBase, commentsForPs);
+          commentsForBase, commentsForPs, Status.PUBLISHED);
     }
 
     private void parseApproval(PatchSet.Id psId, Account.Id accountId,
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeUpdate.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeUpdate.java
index 7b9ef90..be3a44d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeUpdate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeUpdate.java
@@ -170,12 +170,6 @@
     this.subject = subject;
   }
 
-  public void setPatchSetId(PatchSet.Id psId) {
-    checkArgument(psId == null
-        || psId.getParentKey().equals(getChange().getId()));
-    this.psId = psId;
-  }
-
   public void setChangeMessage(String changeMessage) {
     this.changeMessage = changeMessage;
   }
@@ -238,11 +232,6 @@
     return noteMap.writeTree(inserter);
   }
 
-  @Override
-  public RevCommit commit(MetaDataUpdate md) throws IOException {
-    throw new UnsupportedOperationException("use commit()");
-  }
-
   public RevCommit commit() throws IOException {
     BatchMetaDataUpdate batch = openUpdate();
     try {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/CommentsInNotesUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/CommentsInNotesUtil.java
index ec3f3a8..ede979fe 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/CommentsInNotesUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/CommentsInNotesUtil.java
@@ -28,6 +28,7 @@
 import com.google.gerrit.reviewdb.client.CommentRange;
 import com.google.gerrit.reviewdb.client.Patch;
 import com.google.gerrit.reviewdb.client.PatchLineComment;
+import com.google.gerrit.reviewdb.client.PatchLineComment.Status;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.client.RevId;
 import com.google.gerrit.server.GerritPersonIdent;
@@ -82,7 +83,8 @@
   public static NoteMap parseCommentsFromNotes(Repository repo, String refName,
       RevWalk walk, Change.Id changeId,
       Multimap<PatchSet.Id, PatchLineComment> commentsForBase,
-      Multimap<PatchSet.Id, PatchLineComment> commentsForPs)
+      Multimap<PatchSet.Id, PatchLineComment> commentsForPs,
+      Status status)
       throws IOException, ConfigInvalidException {
     Ref ref = repo.getRef(refName);
     if (ref == null) {
@@ -94,7 +96,7 @@
     for (Note note: noteMap) {
       byte[] bytes = walk.getObjectReader().open(
           note.getData(), Constants.OBJ_BLOB).getBytes();
-      List<PatchLineComment> result = parseNote(bytes, changeId);
+      List<PatchLineComment> result = parseNote(bytes, changeId, status);
       if ((result == null) || (result.isEmpty())) {
         continue;
       }
@@ -110,7 +112,7 @@
   }
 
   public static List<PatchLineComment> parseNote(byte[] note,
-      Change.Id changeId) throws ConfigInvalidException {
+      Change.Id changeId, Status status) throws ConfigInvalidException {
     List<PatchLineComment> result = Lists.newArrayList();
     int sizeOfNote = note.length;
     Charset enc = RawParseUtils.parseEncoding(note);
@@ -131,7 +133,7 @@
       String previousFileName = c == null ?
           null : c.getKey().getParentKey().getFileName();
       c = parseComment(note, curr, previousFileName, psId, revId,
-          isForBase, enc);
+          isForBase, enc, status);
       result.add(c);
     }
     return result;
@@ -150,7 +152,7 @@
 
   private static PatchLineComment parseComment(byte[] note, MutableInteger curr,
       String currentFileName, PatchSet.Id psId, RevId revId, boolean isForBase,
-      Charset enc)
+      Charset enc, Status status)
           throws ConfigInvalidException {
     Change.Id changeId = psId.getParentKey();
 
@@ -195,6 +197,7 @@
       plc.setRange(range);
     }
     plc.setRevId(revId);
+    plc.setStatus(status);
 
     curr.value = RawParseUtils.nextLF(note, curr.value + commentLength);
     curr.value = RawParseUtils.nextLF(note, curr.value);
@@ -425,14 +428,12 @@
    *            byte array.
    *            All of the comments in this list must have the same side and
    *            must share the same PatchSet.Id.
+   *            This list must not be empty because we cannot build a note
+   *            for no comments.
    * @return the note. Null if there are no comments in the list.
    */
   public byte[] buildNote(List<PatchLineComment> comments)
       throws OrmException, IOException {
-    if (comments.isEmpty()) {
-      return null;
-    }
-
     ByteArrayOutputStream buf = new ByteArrayOutputStream();
     OutputStreamWriter streamWriter = new OutputStreamWriter(buf, UTF_8);
     PrintWriter writer = new PrintWriter(streamWriter);
@@ -517,6 +518,8 @@
   public void writeCommentsToNoteMap(NoteMap noteMap,
       List<PatchLineComment> allComments, ObjectInserter inserter)
         throws OrmException, IOException {
+    checkArgument(!allComments.isEmpty(),
+        "No comments to write; to delete, use removeNoteFromNoteMap().");
     ObjectId commitOID =
         ObjectId.fromString(allComments.get(0).getRevId().get());
     Collections.sort(allComments, ChangeNotes.PatchLineCommentComparator);
@@ -524,4 +527,9 @@
     ObjectId noteId = inserter.insert(Constants.OBJ_BLOB, note, 0, note.length);
     noteMap.set(commitOID, noteId);
   }
+
+  public void removeNote(NoteMap noteMap, RevId commitId)
+      throws IOException {
+    noteMap.remove(ObjectId.fromString(commitId.get()));
+  }
 }
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/notedb/ChangeNotesTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/notedb/ChangeNotesTest.java
index ed65306..29d2449 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/notedb/ChangeNotesTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/notedb/ChangeNotesTest.java
@@ -38,6 +38,7 @@
 import com.google.gerrit.reviewdb.client.CommentRange;
 import com.google.gerrit.reviewdb.client.Patch;
 import com.google.gerrit.reviewdb.client.PatchLineComment;
+import com.google.gerrit.reviewdb.client.PatchLineComment.Status;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.client.PatchSetApproval;
 import com.google.gerrit.reviewdb.client.Project;
@@ -853,8 +854,8 @@
     Timestamp time3 = TimeUtil.nowTs();
     PatchSet.Id psId = c.currentPatchSetId();
 
-    PatchLineComment comment1 = newPatchLineComment(psId, "file1", uuid,
-        range1, range1.getEndLine(), otherUser, null, time1, message1,
+    PatchLineComment comment1 = newPublishedPatchLineComment(psId, "file1",
+        uuid, range1, range1.getEndLine(), otherUser, null, time1, message1,
         (short) 1, "abcd1234abcd1234abcd1234abcd1234abcd1234");
     update.setPatchSetId(psId);
     update.putComment(comment1);
@@ -862,8 +863,8 @@
 
     update = newUpdate(c, otherUser);
     CommentRange range2 = new CommentRange(2, 1, 3, 1);
-    PatchLineComment comment2 = newPatchLineComment(psId, "file1", uuid,
-        range2, range2.getEndLine(), otherUser, null, time2, message2,
+    PatchLineComment comment2 = newPublishedPatchLineComment(psId, "file1",
+        uuid, range2, range2.getEndLine(), otherUser, null, time2, message2,
         (short) 1, "abcd1234abcd1234abcd1234abcd1234abcd1234");
     update.setPatchSetId(psId);
     update.putComment(comment2);
@@ -871,8 +872,8 @@
 
     update = newUpdate(c, otherUser);
     CommentRange range3 = new CommentRange(3, 1, 4, 1);
-    PatchLineComment comment3 = newPatchLineComment(psId, "file2", uuid,
-        range3, range3.getEndLine(), otherUser, null, time3, message3,
+    PatchLineComment comment3 = newPublishedPatchLineComment(psId, "file2",
+        uuid, range3, range3.getEndLine(), otherUser, null, time3, message3,
         (short) 1, "abcd1234abcd1234abcd1234abcd1234abcd1234");
     update.setPatchSetId(psId);
     update.putComment(comment3);
@@ -931,8 +932,8 @@
     Timestamp time2 = TimeUtil.nowTs();
     PatchSet.Id psId = c.currentPatchSetId();
 
-    PatchLineComment comment1 = newPatchLineComment(psId, "file1", uuid,
-        range1, range1.getEndLine(), otherUser, null, time1, message1,
+    PatchLineComment comment1 = newPublishedPatchLineComment(psId, "file1",
+        uuid, range1, range1.getEndLine(), otherUser, null, time1, message1,
         (short) 0, "abcd1234abcd1234abcd1234abcd1234abcd1234");
     update.setPatchSetId(psId);
     update.putComment(comment1);
@@ -940,8 +941,8 @@
 
     update = newUpdate(c, otherUser);
     CommentRange range2 = new CommentRange(2, 1, 3, 1);
-    PatchLineComment comment2 = newPatchLineComment(psId, "file1", uuid,
-        range2, range2.getEndLine(), otherUser, null, time2, message2,
+    PatchLineComment comment2 = newPublishedPatchLineComment(psId, "file1",
+        uuid, range2, range2.getEndLine(), otherUser, null, time2, message2,
         (short) 0, "abcd1234abcd1234abcd1234abcd1234abcd1234");
     update.setPatchSetId(psId);
     update.putComment(comment2);
@@ -993,7 +994,7 @@
     PatchSet.Id psId = c.currentPatchSetId();
 
     PatchLineComment commentForBase =
-        newPatchLineComment(psId, "filename", uuid,
+        newPublishedPatchLineComment(psId, "filename", uuid,
         range, range.getEndLine(), otherUser, null, now, messageForBase,
         (short) 0, "abcd1234abcd1234abcd1234abcd1234abcd1234");
     update.setPatchSetId(psId);
@@ -1002,7 +1003,7 @@
 
     update = newUpdate(c, otherUser);
     PatchLineComment commentForPS =
-        newPatchLineComment(psId, "filename", uuid,
+        newPublishedPatchLineComment(psId, "filename", uuid,
         range, range.getEndLine(), otherUser, null, now, messageForPS,
         (short) 1, "abcd4567abcd4567abcd4567abcd4567abcd4567");
     update.setPatchSetId(psId);
@@ -1035,17 +1036,17 @@
     ChangeUpdate update = newUpdate(c, otherUser);
     Timestamp timeForComment1 = TimeUtil.nowTs();
     Timestamp timeForComment2 = TimeUtil.nowTs();
-    PatchLineComment comment1 = newPatchLineComment(psId, filename, uuid, range,
-        range.getEndLine(), otherUser, null, timeForComment1, "comment 1", side,
-        "abcd1234abcd1234abcd1234abcd1234abcd1234");
+    PatchLineComment comment1 = newPublishedPatchLineComment(psId, filename,
+        uuid, range, range.getEndLine(), otherUser, null, timeForComment1,
+        "comment 1", side, "abcd1234abcd1234abcd1234abcd1234abcd1234");
     update.setPatchSetId(psId);
     update.putComment(comment1);
     update.commit();
 
     update = newUpdate(c, otherUser);
-    PatchLineComment comment2 = newPatchLineComment(psId, filename, uuid, range,
-        range.getEndLine(), otherUser, null, timeForComment2, "comment 2", side,
-        "abcd1234abcd1234abcd1234abcd1234abcd1234");
+    PatchLineComment comment2 = newPublishedPatchLineComment(psId, filename,
+        uuid, range, range.getEndLine(), otherUser, null, timeForComment2,
+        "comment 2", side, "abcd1234abcd1234abcd1234abcd1234abcd1234");
     update.setPatchSetId(psId);
     update.putComment(comment2);
     update.commit();
@@ -1081,17 +1082,17 @@
 
     ChangeUpdate update = newUpdate(c, otherUser);
     Timestamp now = TimeUtil.nowTs();
-    PatchLineComment comment1 = newPatchLineComment(psId, filename1, uuid,
-        range, range.getEndLine(), otherUser, null, now, "comment 1", side,
-        "abcd1234abcd1234abcd1234abcd1234abcd1234");
+    PatchLineComment comment1 = newPublishedPatchLineComment(psId, filename1,
+        uuid, range, range.getEndLine(), otherUser, null, now, "comment 1",
+        side, "abcd1234abcd1234abcd1234abcd1234abcd1234");
     update.setPatchSetId(psId);
     update.putComment(comment1);
     update.commit();
 
     update = newUpdate(c, otherUser);
-    PatchLineComment comment2 = newPatchLineComment(psId, filename2, uuid,
-        range, range.getEndLine(), otherUser, null, now, "comment 2", side,
-        "abcd1234abcd1234abcd1234abcd1234abcd1234");
+    PatchLineComment comment2 = newPublishedPatchLineComment(psId, filename2,
+        uuid, range, range.getEndLine(), otherUser, null, now, "comment 2",
+        side, "abcd1234abcd1234abcd1234abcd1234abcd1234");
     update.setPatchSetId(psId);
     update.putComment(comment2);
     update.commit();
@@ -1125,9 +1126,9 @@
 
     ChangeUpdate update = newUpdate(c, otherUser);
     Timestamp now = TimeUtil.nowTs();
-    PatchLineComment comment1 = newPatchLineComment(ps1, filename, uuid,
-        range, range.getEndLine(), otherUser, null, now, "comment on ps1", side,
-        "abcd1234abcd1234abcd1234abcd1234abcd1234");
+    PatchLineComment comment1 = newPublishedPatchLineComment(ps1, filename,
+        uuid, range, range.getEndLine(), otherUser, null, now, "comment on ps1",
+        side, "abcd1234abcd1234abcd1234abcd1234abcd1234");
     update.setPatchSetId(ps1);
     update.putComment(comment1);
     update.commit();
@@ -1137,9 +1138,9 @@
 
     update = newUpdate(c, otherUser);
     now = TimeUtil.nowTs();
-    PatchLineComment comment2 = newPatchLineComment(ps2, filename, uuid,
-        range, range.getEndLine(), otherUser, null, now, "comment on ps2", side,
-        "abcd4567abcd4567abcd4567abcd4567abcd4567");
+    PatchLineComment comment2 = newPublishedPatchLineComment(ps2, filename,
+        uuid, range, range.getEndLine(), otherUser, null, now, "comment on ps2",
+        side, "abcd4567abcd4567abcd4567abcd4567abcd4567");
     update.setPatchSetId(ps2);
     update.putComment(comment2);
     update.commit();
@@ -1168,10 +1169,18 @@
     return TestChanges.newChange(project, changeOwner);
   }
 
-  private PatchLineComment newPatchLineComment(PatchSet.Id psId,
+  private PatchLineComment newPublishedPatchLineComment(PatchSet.Id psId,
       String filename, String UUID, CommentRange range, int line,
       IdentifiedUser commenter, String parentUUID, Timestamp t,
       String message, short side, String commitSHA1) {
+    return newPatchLineComment(psId, filename, UUID, range, line, commenter,
+        parentUUID, t, message, side, commitSHA1, Status.PUBLISHED);
+  }
+
+  private PatchLineComment newPatchLineComment(PatchSet.Id psId,
+      String filename, String UUID, CommentRange range, int line,
+      IdentifiedUser commenter, String parentUUID, Timestamp t,
+      String message, short side, String commitSHA1, Status status) {
     PatchLineComment comment = new PatchLineComment(
         new PatchLineComment.Key(
             new Patch.Key(psId, filename), UUID),
@@ -1180,6 +1189,7 @@
     comment.setMessage(message);
     comment.setRange(range);
     comment.setRevId(new RevId(commitSHA1));
+    comment.setStatus(status);
     return comment;
   }