// Copyright (C) 2014 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.server.change; import static com.google.gerrit.server.CommentsUtil.COMMENT_INFO_ORDER; import static java.util.stream.Collectors.toList; import com.google.common.base.Strings; import com.google.common.collect.FluentIterable; import com.google.gerrit.common.Nullable; import com.google.gerrit.extensions.client.Comment.Range; import com.google.gerrit.extensions.client.Side; import com.google.gerrit.extensions.common.CommentInfo; import com.google.gerrit.extensions.common.FixReplacementInfo; import com.google.gerrit.extensions.common.FixSuggestionInfo; import com.google.gerrit.extensions.common.RobotCommentInfo; import com.google.gerrit.extensions.restapi.Url; import com.google.gerrit.reviewdb.client.Comment; import com.google.gerrit.reviewdb.client.FixReplacement; import com.google.gerrit.reviewdb.client.FixSuggestion; import com.google.gerrit.reviewdb.client.RobotComment; import com.google.gerrit.server.account.AccountLoader; import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.TreeMap; class CommentJson { private final AccountLoader.Factory accountLoaderFactory; private boolean fillAccounts = true; private boolean fillPatchSet; @Inject CommentJson(AccountLoader.Factory accountLoaderFactory) { this.accountLoaderFactory = accountLoaderFactory; } CommentJson setFillAccounts(boolean fillAccounts) { this.fillAccounts = fillAccounts; return this; } CommentJson setFillPatchSet(boolean fillPatchSet) { this.fillPatchSet = fillPatchSet; return this; } public CommentFormatter newCommentFormatter() { return new CommentFormatter(); } public RobotCommentFormatter newRobotCommentFormatter() { return new RobotCommentFormatter(); } private abstract class BaseCommentFormatter<F extends Comment, T extends CommentInfo> { public T format(F comment) throws OrmException { AccountLoader loader = fillAccounts ? accountLoaderFactory.create(true) : null; T info = toInfo(comment, loader); if (loader != null) { loader.fill(); } return info; } public Map<String, List<T>> format(Iterable<F> comments) throws OrmException { AccountLoader loader = fillAccounts ? accountLoaderFactory.create(true) : null; Map<String, List<T>> out = new TreeMap<>(); for (F c : comments) { T o = toInfo(c, loader); List<T> list = out.get(o.path); if (list == null) { list = new ArrayList<>(); out.put(o.path, list); } o.path = null; list.add(o); } for (List<T> list : out.values()) { Collections.sort(list, COMMENT_INFO_ORDER); } if (loader != null) { loader.fill(); } return out; } public List<T> formatAsList(Iterable<F> comments) throws OrmException { AccountLoader loader = fillAccounts ? accountLoaderFactory.create(true) : null; List<T> out = FluentIterable.from(comments) .transform(c -> toInfo(c, loader)) .toSortedList(COMMENT_INFO_ORDER); if (loader != null) { loader.fill(); } return out; } protected abstract T toInfo(F comment, AccountLoader loader); protected void fillCommentInfo(Comment c, CommentInfo r, AccountLoader loader) { if (fillPatchSet) { r.patchSet = c.key.patchSetId; } r.id = Url.encode(c.key.uuid); r.path = c.key.filename; if (c.side <= 0) { r.side = Side.PARENT; if (c.side < 0) { r.parent = -c.side; } } if (c.lineNbr > 0) { r.line = c.lineNbr; } r.inReplyTo = Url.encode(c.parentUuid); r.message = Strings.emptyToNull(c.message); r.updated = c.writtenOn; r.range = toRange(c.range); r.tag = c.tag; r.unresolved = c.unresolved; if (loader != null) { r.author = loader.get(c.author.getId()); } } protected Range toRange(Comment.Range commentRange) { Range range = null; if (commentRange != null) { range = new Range(); range.startLine = commentRange.startLine; range.startCharacter = commentRange.startChar; range.endLine = commentRange.endLine; range.endCharacter = commentRange.endChar; } return range; } } class CommentFormatter extends BaseCommentFormatter<Comment, CommentInfo> { @Override protected CommentInfo toInfo(Comment c, AccountLoader loader) { CommentInfo ci = new CommentInfo(); fillCommentInfo(c, ci, loader); return ci; } private CommentFormatter() {} } class RobotCommentFormatter extends BaseCommentFormatter<RobotComment, RobotCommentInfo> { @Override protected RobotCommentInfo toInfo(RobotComment c, AccountLoader loader) { RobotCommentInfo rci = new RobotCommentInfo(); rci.robotId = c.robotId; rci.robotRunId = c.robotRunId; rci.url = c.url; rci.properties = c.properties; rci.fixSuggestions = toFixSuggestionInfos(c.fixSuggestions); fillCommentInfo(c, rci, loader); return rci; } private List<FixSuggestionInfo> toFixSuggestionInfos( @Nullable List<FixSuggestion> fixSuggestions) { if (fixSuggestions == null || fixSuggestions.isEmpty()) { return null; } return fixSuggestions.stream().map(this::toFixSuggestionInfo).collect(toList()); } private FixSuggestionInfo toFixSuggestionInfo(FixSuggestion fixSuggestion) { FixSuggestionInfo fixSuggestionInfo = new FixSuggestionInfo(); fixSuggestionInfo.fixId = fixSuggestion.fixId; fixSuggestionInfo.description = fixSuggestion.description; fixSuggestionInfo.replacements = fixSuggestion.replacements.stream().map(this::toFixReplacementInfo).collect(toList()); return fixSuggestionInfo; } private FixReplacementInfo toFixReplacementInfo(FixReplacement fixReplacement) { FixReplacementInfo fixReplacementInfo = new FixReplacementInfo(); fixReplacementInfo.path = fixReplacement.path; fixReplacementInfo.range = toRange(fixReplacement.range); fixReplacementInfo.replacement = fixReplacement.replacement; return fixReplacementInfo; } private RobotCommentFormatter() {} } }