use of com.google.gerrit.reviewdb.client.Comment in project gerrit by GerritCodeReview.
the class HtmlParser method parse.
/**
* Parses comments from html email.
*
* @param email MailMessage as received from the email service.
* @param comments A specific set of comments as sent out in the original notification email.
* Comments are expected to be in the same order as they were sent out to in the email
* @param changeUrl Canonical change URL that points to the change on this Gerrit instance.
* Example: https://go-review.googlesource.com/#/c/91570
* @return List of MailComments parsed from the html part of the email.
*/
public static List<MailComment> parse(MailMessage email, Collection<Comment> comments, String changeUrl) {
// TODO(hiesel) Add support for Gmail Mobile
// TODO(hiesel) Add tests for other popular email clients
// This parser goes though all html elements in the email and checks for
// matching patterns. It keeps track of the last file and comments it
// encountered to know in which context a parsed comment belongs.
// It uses the href attributes of <a> tags to identify comments sent out by
// Gerrit as these are generally more reliable then the text captions.
List<MailComment> parsedComments = new ArrayList<>();
Document d = Jsoup.parse(email.htmlContent());
PeekingIterator<Comment> iter = Iterators.peekingIterator(comments.iterator());
String lastEncounteredFileName = null;
Comment lastEncounteredComment = null;
for (Element e : d.body().getAllElements()) {
String elementName = e.tagName();
boolean isInBlockQuote = e.parents().stream().filter(p -> p.tagName().equals("blockquote")).findAny().isPresent();
if (elementName.equals("a")) {
String href = e.attr("href");
// this <a> tag
if (!iter.hasNext()) {
continue;
}
Comment perspectiveComment = iter.peek();
if (href.equals(ParserUtil.filePath(changeUrl, perspectiveComment))) {
if (lastEncounteredFileName == null || !lastEncounteredFileName.equals(perspectiveComment.key.filename)) {
// Not a file-level comment, but users could have typed a comment
// right after this file annotation to create a new file-level
// comment. If this file has a file-level comment, we have already
// set lastEncounteredComment to that file-level comment when we
// encountered the file link and should not reset it now.
lastEncounteredFileName = perspectiveComment.key.filename;
lastEncounteredComment = null;
} else if (perspectiveComment.lineNbr == 0) {
// This was originally a file-level comment
lastEncounteredComment = perspectiveComment;
iter.next();
}
} else if (ParserUtil.isCommentUrl(href, changeUrl, perspectiveComment)) {
// This is a regular inline comment
lastEncounteredComment = perspectiveComment;
iter.next();
}
} else if (!isInBlockQuote && elementName.equals("div") && !e.className().startsWith("gmail")) {
// This is a comment typed by the user
// Replace non-breaking spaces and trim string
String content = e.ownText().replace(' ', ' ').trim();
if (!Strings.isNullOrEmpty(content)) {
if (lastEncounteredComment == null && lastEncounteredFileName == null) {
// Remove quotation line, email signature and
// "Sent from my xyz device"
content = ParserUtil.trimQuotation(content);
// TODO(hiesel) Add more sanitizer
if (!Strings.isNullOrEmpty(content)) {
parsedComments.add(new MailComment(content, null, null, MailComment.CommentType.CHANGE_MESSAGE));
}
} else if (lastEncounteredComment == null) {
parsedComments.add(new MailComment(content, lastEncounteredFileName, null, MailComment.CommentType.FILE_COMMENT));
} else {
parsedComments.add(new MailComment(content, null, lastEncounteredComment, MailComment.CommentType.INLINE_COMMENT));
}
}
}
}
return parsedComments;
}
use of com.google.gerrit.reviewdb.client.Comment in project gerrit by GerritCodeReview.
the class ChangeData method draftRefs.
public Map<Account.Id, Ref> draftRefs() throws OrmException {
if (draftsByUser == null) {
if (!lazyLoad) {
return Collections.emptyMap();
}
Change c = change();
if (c == null) {
return Collections.emptyMap();
}
draftsByUser = new HashMap<>();
if (notesMigration.readChanges()) {
for (Ref ref : commentsUtil.getDraftRefs(notes.getChangeId())) {
Account.Id account = Account.Id.fromRefSuffix(ref.getName());
if (account != null && // this point.
!notes().getDraftComments(account, ref).isEmpty()) {
draftsByUser.put(account, ref);
}
}
} else {
for (Comment sc : commentsUtil.draftByChange(db, notes())) {
draftsByUser.put(sc.author.getId(), null);
}
}
}
return draftsByUser;
}
use of com.google.gerrit.reviewdb.client.Comment in project gerrit by GerritCodeReview.
the class DeleteDraftPatchSetIT method deleteDraftPS1.
@Test
public void deleteDraftPS1() throws Exception {
String changeId = createDraftChangeWith2PS();
ReviewInput rin = new ReviewInput();
rin.message = "Change message";
CommentInput cin = new CommentInput();
cin.line = 1;
cin.patchSet = 1;
cin.path = PushOneCommit.FILE_NAME;
cin.side = Side.REVISION;
cin.message = "Inline comment";
rin.comments = new HashMap<>();
rin.comments.put(cin.path, ImmutableList.of(cin));
gApi.changes().id(changeId).revision(1).review(rin);
ChangeData cd = getChange(changeId);
PatchSet.Id delPsId = new PatchSet.Id(cd.getId(), 1);
PatchSet ps = cd.patchSet(delPsId);
deletePatchSet(changeId, ps);
cd = getChange(changeId);
assertThat(cd.patchSets()).hasSize(1);
assertThat(Iterables.getOnlyElement(cd.patchSets()).getId().get()).isEqualTo(2);
// Other entities based on deleted patch sets are also deleted.
for (ChangeMessage m : cd.messages()) {
assertThat(m.getPatchSetId()).named(m.toString()).isNotEqualTo(delPsId);
}
for (Comment c : cd.publishedComments()) {
assertThat(c.key.patchSetId).named(c.toString()).isNotEqualTo(delPsId.get());
}
}
use of com.google.gerrit.reviewdb.client.Comment in project gerrit by GerritCodeReview.
the class DeleteDraftPatchSetIT method deleteDraftPS2.
@Test
public void deleteDraftPS2() throws Exception {
String changeId = createDraftChangeWith2PS();
ReviewInput rin = new ReviewInput();
rin.message = "Change message";
CommentInput cin = new CommentInput();
cin.line = 1;
cin.patchSet = 1;
cin.path = PushOneCommit.FILE_NAME;
cin.side = Side.REVISION;
cin.message = "Inline comment";
rin.comments = new HashMap<>();
rin.comments.put(cin.path, ImmutableList.of(cin));
gApi.changes().id(changeId).revision(1).review(rin);
ChangeData cd = getChange(changeId);
PatchSet.Id delPsId = new PatchSet.Id(cd.getId(), 2);
PatchSet ps = cd.patchSet(delPsId);
deletePatchSet(changeId, ps);
cd = getChange(changeId);
assertThat(cd.patchSets()).hasSize(1);
assertThat(Iterables.getOnlyElement(cd.patchSets()).getId().get()).isEqualTo(1);
// Other entities based on deleted patch sets are also deleted.
for (ChangeMessage m : cd.messages()) {
assertThat(m.getPatchSetId()).named(m.toString()).isNotEqualTo(delPsId);
}
for (Comment c : cd.publishedComments()) {
assertThat(c.key.patchSetId).named(c.toString()).isNotEqualTo(delPsId.get());
}
}
use of com.google.gerrit.reviewdb.client.Comment in project gerrit by GerritCodeReview.
the class CommentDetail method index.
private Map<Integer, List<Comment>> index(List<Comment> in) {
HashMap<Integer, List<Comment>> r = new HashMap<>();
for (Comment p : in) {
List<Comment> l = r.get(p.lineNbr);
if (l == null) {
l = new ArrayList<>();
r.put(p.lineNbr, l);
}
l.add(p);
}
return r;
}
Aggregations