blob: 207643ec4398435b9ba510ffdbd3588253c2d647 [file] [log] [blame]
// Copyright (C) 2016 The Android Open Source Project
//
// 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.gerrit.reviewdb.client;
import java.sql.Timestamp;
import java.util.Comparator;
import java.util.Objects;
/**
* This class represents inline comments in NoteDb. This means it determines the JSON format for
* inline comments in the revision notes that NoteDb uses to persist inline comments.
*
* <p>Changing fields in this class changes the storage format of inline comments in NoteDb and may
* require a corresponding data migration (adding new optional fields is generally okay).
*
* <p>{@link PatchLineComment} also represents inline comments, but in ReviewDb. There are a few
* notable differences:
*
* <ul>
* <li>PatchLineComment knows the comment status (published or draft). For comments in NoteDb the
* status is determined by the branch in which they are stored (published comments are stored
* in the change meta ref; draft comments are store in refs/draft-comments branches in
* All-Users). Hence Comment doesn't need to contain the status, but the status is implicitly
* known by where the comments are read from.
* <li>PatchLineComment knows the change ID. For comments in NoteDb, the change ID is determined
* by the branch in which they are stored (the ref name contains the change ID). Hence Comment
* doesn't need to contain the change ID, but the change ID is implicitly known by where the
* comments are read from.
* </ul>
*
* <p>For all utility classes and middle layer functionality using Comment over PatchLineComment is
* preferred, as PatchLineComment will go away together with ReviewDb. This means Comment should be
* used everywhere and only for storing inline comment in ReviewDb a conversion to PatchLineComment
* is done. Converting Comments to PatchLineComments and vice verse is done by
* CommentsUtil#toPatchLineComments(Change.Id, PatchLineComment.Status, Iterable) and
* CommentsUtil#toComments(String, Iterable).
*/
public class Comment {
public static class Key {
public String uuid;
public String filename;
public int patchSetId;
public Key(Key k) {
this(k.uuid, k.filename, k.patchSetId);
}
public Key(String uuid, String filename, int patchSetId) {
this.uuid = uuid;
this.filename = filename;
this.patchSetId = patchSetId;
}
@Override
public String toString() {
return new StringBuilder()
.append("Comment.Key{")
.append("uuid=")
.append(uuid)
.append(',')
.append("filename=")
.append(filename)
.append(',')
.append("patchSetId=")
.append(patchSetId)
.append('}')
.toString();
}
@Override
public boolean equals(Object o) {
if (o instanceof Key) {
Key k = (Key) o;
return Objects.equals(uuid, k.uuid)
&& Objects.equals(filename, k.filename)
&& Objects.equals(patchSetId, k.patchSetId);
}
return false;
}
@Override
public int hashCode() {
return Objects.hash(uuid, filename, patchSetId);
}
}
public static class Identity {
int id;
public Identity(Account.Id id) {
this.id = id.get();
}
public Account.Id getId() {
return new Account.Id(id);
}
@Override
public boolean equals(Object o) {
if (o instanceof Identity) {
return Objects.equals(id, ((Identity) o).id);
}
return false;
}
@Override
public int hashCode() {
return Objects.hash(id);
}
@Override
public String toString() {
return new StringBuilder()
.append("Comment.Identity{")
.append("id=")
.append(id)
.append('}')
.toString();
}
}
public static class Range implements Comparable<Range> {
private static final Comparator<Range> RANGE_COMPARATOR =
Comparator.<Range>comparingInt(range -> range.startLine)
.thenComparingInt(range -> range.startChar)
.thenComparingInt(range -> range.endLine)
.thenComparingInt(range -> range.endChar);
public int startLine; // 1-based, inclusive
public int startChar; // 0-based, inclusive
public int endLine; // 1-based, exclusive
public int endChar; // 0-based, exclusive
public Range(Range r) {
this(r.startLine, r.startChar, r.endLine, r.endChar);
}
public Range(com.google.gerrit.extensions.client.Comment.Range r) {
this(r.startLine, r.startCharacter, r.endLine, r.endCharacter);
}
public Range(int startLine, int startChar, int endLine, int endChar) {
this.startLine = startLine;
this.startChar = startChar;
this.endLine = endLine;
this.endChar = endChar;
}
@Override
public boolean equals(Object o) {
if (o instanceof Range) {
Range r = (Range) o;
return Objects.equals(startLine, r.startLine)
&& Objects.equals(startChar, r.startChar)
&& Objects.equals(endLine, r.endLine)
&& Objects.equals(endChar, r.endChar);
}
return false;
}
@Override
public int hashCode() {
return Objects.hash(startLine, startChar, endLine, endChar);
}
@Override
public String toString() {
return new StringBuilder()
.append("Comment.Range{")
.append("startLine=")
.append(startLine)
.append(',')
.append("startChar=")
.append(startChar)
.append(',')
.append("endLine=")
.append(endLine)
.append(',')
.append("endChar=")
.append(endChar)
.append('}')
.toString();
}
@Override
public int compareTo(Range otherRange) {
return RANGE_COMPARATOR.compare(this, otherRange);
}
}
public Key key;
public int lineNbr;
public Identity author;
protected Identity realAuthor;
public Timestamp writtenOn;
public short side;
public String message;
public String parentUuid;
public Range range;
public String tag;
// Hex commit SHA1 of the commit of the patchset to which this comment applies.
public String revId;
public String serverId;
public boolean unresolved;
/**
* Whether the comment was parsed from a JSON representation (false) or the legacy custom notes
* format (true).
*/
public transient boolean legacyFormat;
public Comment(Comment c) {
this(
new Key(c.key),
c.author.getId(),
new Timestamp(c.writtenOn.getTime()),
c.side,
c.message,
c.serverId,
c.unresolved);
this.lineNbr = c.lineNbr;
this.realAuthor = c.realAuthor;
this.range = c.range != null ? new Range(c.range) : null;
this.tag = c.tag;
this.revId = c.revId;
this.unresolved = c.unresolved;
}
public Comment(
Key key,
Account.Id author,
Timestamp writtenOn,
short side,
String message,
String serverId,
boolean unresolved) {
this.key = key;
this.author = new Comment.Identity(author);
this.realAuthor = this.author;
this.writtenOn = writtenOn;
this.side = side;
this.message = message;
this.serverId = serverId;
this.unresolved = unresolved;
}
public void setLineNbrAndRange(
Integer lineNbr, com.google.gerrit.extensions.client.Comment.Range range) {
this.lineNbr = lineNbr != null ? lineNbr : range != null ? range.endLine : 0;
if (range != null) {
this.range = new Comment.Range(range);
}
}
public void setRange(CommentRange range) {
this.range = range != null ? range.asCommentRange() : null;
}
public void setRevId(RevId revId) {
this.revId = revId != null ? revId.get() : null;
}
public void setRealAuthor(Account.Id id) {
realAuthor = id != null && id.get() != author.id ? new Comment.Identity(id) : null;
}
public Identity getRealAuthor() {
return realAuthor != null ? realAuthor : author;
}
@Override
public boolean equals(Object o) {
if (o instanceof Comment) {
return Objects.equals(key, ((Comment) o).key);
}
return false;
}
@Override
public int hashCode() {
return key.hashCode();
}
@Override
public String toString() {
return new StringBuilder()
.append("Comment{")
.append("key=")
.append(key)
.append(',')
.append("lineNbr=")
.append(lineNbr)
.append(',')
.append("author=")
.append(author.getId().get())
.append(',')
.append("realAuthor=")
.append(realAuthor != null ? realAuthor.getId().get() : "")
.append(',')
.append("writtenOn=")
.append(writtenOn.toString())
.append(',')
.append("side=")
.append(side)
.append(',')
.append("message=")
.append(Objects.toString(message, ""))
.append(',')
.append("parentUuid=")
.append(Objects.toString(parentUuid, ""))
.append(',')
.append("range=")
.append(Objects.toString(range, ""))
.append(',')
.append("revId=")
.append(revId != null ? revId : "")
.append(',')
.append("tag=")
.append(Objects.toString(tag, ""))
.append(',')
.append("unresolved=")
.append(unresolved)
.append('}')
.toString();
}
}