LCOV - code coverage report
Current view: top level - server/restapi/change - CommentJson.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 132 132 100.0 %
Date: 2022-11-19 15:00:39 Functions: 28 28 100.0 %

          Line data    Source code
       1             : // Copyright (C) 2014 The Android Open Source Project
       2             : //
       3             : // Licensed under the Apache License, Version 2.0 (the "License");
       4             : // you may not use this file except in compliance with the License.
       5             : // You may obtain a copy of the License at
       6             : //
       7             : // http://www.apache.org/licenses/LICENSE-2.0
       8             : //
       9             : // Unless required by applicable law or agreed to in writing, software
      10             : // distributed under the License is distributed on an "AS IS" BASIS,
      11             : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12             : // See the License for the specific language governing permissions and
      13             : // limitations under the License.
      14             : 
      15             : package com.google.gerrit.server.restapi.change;
      16             : 
      17             : import static com.google.common.collect.ImmutableList.toImmutableList;
      18             : import static com.google.gerrit.server.CommentsUtil.COMMENT_INFO_ORDER;
      19             : import static java.util.stream.Collectors.toList;
      20             : 
      21             : import com.google.common.base.Strings;
      22             : import com.google.common.collect.ImmutableList;
      23             : import com.google.common.collect.ImmutableMap;
      24             : import com.google.common.collect.Streams;
      25             : import com.google.gerrit.common.Nullable;
      26             : import com.google.gerrit.entities.Change;
      27             : import com.google.gerrit.entities.Comment;
      28             : import com.google.gerrit.entities.CommentContext;
      29             : import com.google.gerrit.entities.FixReplacement;
      30             : import com.google.gerrit.entities.FixSuggestion;
      31             : import com.google.gerrit.entities.HumanComment;
      32             : import com.google.gerrit.entities.Project;
      33             : import com.google.gerrit.entities.RobotComment;
      34             : import com.google.gerrit.extensions.client.Comment.Range;
      35             : import com.google.gerrit.extensions.client.Side;
      36             : import com.google.gerrit.extensions.common.CommentInfo;
      37             : import com.google.gerrit.extensions.common.ContextLineInfo;
      38             : import com.google.gerrit.extensions.common.FixReplacementInfo;
      39             : import com.google.gerrit.extensions.common.FixSuggestionInfo;
      40             : import com.google.gerrit.extensions.common.RobotCommentInfo;
      41             : import com.google.gerrit.extensions.restapi.Url;
      42             : import com.google.gerrit.server.account.AccountLoader;
      43             : import com.google.gerrit.server.comment.CommentContextCache;
      44             : import com.google.gerrit.server.comment.CommentContextKey;
      45             : import com.google.gerrit.server.permissions.PermissionBackendException;
      46             : import com.google.inject.Inject;
      47             : import java.util.ArrayList;
      48             : import java.util.Collection;
      49             : import java.util.List;
      50             : import java.util.Map;
      51             : import java.util.TreeMap;
      52             : 
      53             : public class CommentJson {
      54             : 
      55             :   private final AccountLoader.Factory accountLoaderFactory;
      56             :   private final CommentContextCache commentContextCache;
      57             : 
      58             :   private Project.NameKey project;
      59             :   private Change.Id changeId;
      60             : 
      61          24 :   private boolean fillAccounts = true;
      62             :   private boolean fillPatchSet;
      63             :   private boolean fillCommentContext;
      64             :   private int contextPadding;
      65             : 
      66             :   @Inject
      67          24 :   CommentJson(AccountLoader.Factory accountLoaderFactory, CommentContextCache commentContextCache) {
      68          24 :     this.accountLoaderFactory = accountLoaderFactory;
      69          24 :     this.commentContextCache = commentContextCache;
      70          24 :   }
      71             : 
      72             :   CommentJson setFillAccounts(boolean fillAccounts) {
      73          24 :     this.fillAccounts = fillAccounts;
      74          24 :     return this;
      75             :   }
      76             : 
      77             :   CommentJson setFillPatchSet(boolean fillPatchSet) {
      78          15 :     this.fillPatchSet = fillPatchSet;
      79          15 :     return this;
      80             :   }
      81             : 
      82             :   CommentJson setFillCommentContext(boolean fillCommentContext) {
      83          13 :     this.fillCommentContext = fillCommentContext;
      84          13 :     return this;
      85             :   }
      86             : 
      87             :   CommentJson setContextPadding(int contextPadding) {
      88          13 :     this.contextPadding = contextPadding;
      89          13 :     return this;
      90             :   }
      91             : 
      92             :   CommentJson setProjectKey(Project.NameKey project) {
      93          13 :     this.project = project;
      94          13 :     return this;
      95             :   }
      96             : 
      97             :   CommentJson setChangeId(Change.Id changeId) {
      98          13 :     this.changeId = changeId;
      99          13 :     return this;
     100             :   }
     101             : 
     102             :   public HumanCommentFormatter newHumanCommentFormatter() {
     103          23 :     return new HumanCommentFormatter();
     104             :   }
     105             : 
     106             :   public RobotCommentFormatter newRobotCommentFormatter() {
     107           6 :     return new RobotCommentFormatter();
     108             :   }
     109             : 
     110          24 :   private abstract class BaseCommentFormatter<F extends Comment, T extends CommentInfo> {
     111             :     public T format(F comment) throws PermissionBackendException {
     112          20 :       AccountLoader loader = fillAccounts ? accountLoaderFactory.create(true) : null;
     113          20 :       T info = toInfo(comment, loader);
     114          20 :       if (loader != null) {
     115          10 :         loader.fill();
     116             :       }
     117          20 :       return info;
     118             :     }
     119             : 
     120             :     public Map<String, List<T>> format(Iterable<F> comments) throws PermissionBackendException {
     121          20 :       AccountLoader loader = fillAccounts ? accountLoaderFactory.create(true) : null;
     122             : 
     123          20 :       Map<String, List<T>> out = new TreeMap<>();
     124             : 
     125          20 :       for (F c : comments) {
     126          19 :         T o = toInfo(c, loader);
     127          19 :         List<T> list = out.get(o.path);
     128          19 :         if (list == null) {
     129          19 :           list = new ArrayList<>();
     130          19 :           out.put(o.path, list);
     131             :         }
     132          19 :         list.add(o);
     133          19 :       }
     134             : 
     135          20 :       out.values().forEach(l -> l.sort(COMMENT_INFO_ORDER));
     136             : 
     137          20 :       if (loader != null) {
     138          19 :         loader.fill();
     139             :       }
     140             : 
     141          20 :       List<T> allComments = out.values().stream().flatMap(Collection::stream).collect(toList());
     142          20 :       if (fillCommentContext) {
     143           1 :         addCommentContext(allComments);
     144             :       }
     145          20 :       allComments.forEach(c -> c.path = null); // we don't need path since it exists in the map keys
     146          20 :       return out;
     147             :     }
     148             : 
     149             :     public ImmutableList<T> formatAsList(Iterable<F> comments) throws PermissionBackendException {
     150          10 :       AccountLoader loader = fillAccounts ? accountLoaderFactory.create(true) : null;
     151             : 
     152          10 :       ImmutableList<T> out =
     153          10 :           Streams.stream(comments)
     154          10 :               .map(c -> toInfo(c, loader))
     155          10 :               .sorted(COMMENT_INFO_ORDER)
     156          10 :               .collect(toImmutableList());
     157             : 
     158          10 :       if (loader != null) {
     159           9 :         loader.fill();
     160             :       }
     161             : 
     162          10 :       if (fillCommentContext) {
     163           1 :         addCommentContext(out);
     164             :       }
     165             : 
     166          10 :       return out;
     167             :     }
     168             : 
     169             :     protected void addCommentContext(List<T> allComments) {
     170           1 :       List<CommentContextKey> keys =
     171           1 :           allComments.stream().map(this::createCommentContextKey).collect(toList());
     172           1 :       ImmutableMap<CommentContextKey, CommentContext> allContext = commentContextCache.getAll(keys);
     173           1 :       for (T c : allComments) {
     174           1 :         CommentContextKey contextKey = createCommentContextKey(c);
     175           1 :         CommentContext commentContext = allContext.get(contextKey);
     176           1 :         c.contextLines = toContextLineInfoList(commentContext);
     177           1 :         c.sourceContentType = commentContext.contentType();
     178           1 :       }
     179           1 :     }
     180             : 
     181             :     protected List<ContextLineInfo> toContextLineInfoList(CommentContext commentContext) {
     182           1 :       List<ContextLineInfo> result = new ArrayList<>();
     183           1 :       for (Map.Entry<Integer, String> e : commentContext.lines().entrySet()) {
     184           1 :         result.add(new ContextLineInfo(e.getKey(), e.getValue()));
     185           1 :       }
     186           1 :       return result;
     187             :     }
     188             : 
     189             :     protected CommentContextKey createCommentContextKey(T r) {
     190           1 :       return CommentContextKey.builder()
     191           1 :           .project(project)
     192           1 :           .changeId(changeId)
     193           1 :           .id(Url.decode(r.id)) // We reverse the encoding done while filling comment info
     194           1 :           .path(r.path)
     195           1 :           .patchset(r.patchSet)
     196           1 :           .contextPadding(contextPadding)
     197           1 :           .build();
     198             :     }
     199             : 
     200             :     protected abstract T toInfo(F comment, AccountLoader loader);
     201             : 
     202             :     protected void fillCommentInfo(Comment c, CommentInfo r, AccountLoader loader) {
     203          23 :       if (fillPatchSet) {
     204          14 :         r.patchSet = c.key.patchSetId;
     205             :       }
     206          23 :       r.id = Url.encode(c.key.uuid);
     207          23 :       r.path = c.key.filename;
     208          23 :       if (c.side <= 0) {
     209           3 :         r.side = Side.PARENT;
     210           3 :         if (c.side < 0) {
     211           3 :           r.parent = -c.side;
     212             :         }
     213             :       }
     214          23 :       if (c.lineNbr > 0) {
     215          22 :         r.line = c.lineNbr;
     216             :       }
     217          23 :       r.inReplyTo = Url.encode(c.parentUuid);
     218          23 :       r.message = Strings.emptyToNull(c.message);
     219          23 :       r.updated = c.writtenOn;
     220          23 :       r.range = toRange(c.range);
     221          23 :       r.tag = c.tag;
     222          23 :       if (loader != null) {
     223          21 :         r.author = loader.get(c.author.getId());
     224             :       }
     225          23 :       r.commitId = c.getCommitId().getName();
     226          23 :     }
     227             : 
     228             :     protected Range toRange(Comment.Range commentRange) {
     229          23 :       Range range = null;
     230          23 :       if (commentRange != null) {
     231           9 :         range = new Range();
     232           9 :         range.startLine = commentRange.startLine;
     233           9 :         range.startCharacter = commentRange.startChar;
     234           9 :         range.endLine = commentRange.endLine;
     235           9 :         range.endCharacter = commentRange.endChar;
     236             :       }
     237          23 :       return range;
     238             :     }
     239             :   }
     240             : 
     241             :   public class HumanCommentFormatter extends BaseCommentFormatter<HumanComment, CommentInfo> {
     242             :     @Override
     243             :     protected CommentInfo toInfo(HumanComment c, AccountLoader loader) {
     244          22 :       CommentInfo ci = new CommentInfo();
     245          22 :       fillCommentInfo(c, ci, loader);
     246          22 :       ci.unresolved = c.unresolved;
     247          22 :       return ci;
     248             :     }
     249             : 
     250          23 :     private HumanCommentFormatter() {}
     251             :   }
     252             : 
     253             :   class RobotCommentFormatter extends BaseCommentFormatter<RobotComment, RobotCommentInfo> {
     254             :     @Override
     255             :     protected RobotCommentInfo toInfo(RobotComment c, AccountLoader loader) {
     256           6 :       RobotCommentInfo rci = new RobotCommentInfo();
     257           6 :       rci.robotId = c.robotId;
     258           6 :       rci.robotRunId = c.robotRunId;
     259           6 :       rci.url = c.url;
     260           6 :       rci.properties = c.properties;
     261           6 :       rci.fixSuggestions = toFixSuggestionInfos(c.fixSuggestions);
     262           6 :       fillCommentInfo(c, rci, loader);
     263           6 :       return rci;
     264             :     }
     265             : 
     266             :     @Nullable
     267             :     private List<FixSuggestionInfo> toFixSuggestionInfos(
     268             :         @Nullable List<FixSuggestion> fixSuggestions) {
     269           6 :       if (fixSuggestions == null || fixSuggestions.isEmpty()) {
     270           6 :         return null;
     271             :       }
     272             : 
     273           2 :       return fixSuggestions.stream().map(this::toFixSuggestionInfo).collect(toList());
     274             :     }
     275             : 
     276             :     private FixSuggestionInfo toFixSuggestionInfo(FixSuggestion fixSuggestion) {
     277           2 :       FixSuggestionInfo fixSuggestionInfo = new FixSuggestionInfo();
     278           2 :       fixSuggestionInfo.fixId = fixSuggestion.fixId;
     279           2 :       fixSuggestionInfo.description = fixSuggestion.description;
     280           2 :       fixSuggestionInfo.replacements =
     281           2 :           fixSuggestion.replacements.stream().map(this::toFixReplacementInfo).collect(toList());
     282           2 :       return fixSuggestionInfo;
     283             :     }
     284             : 
     285             :     private FixReplacementInfo toFixReplacementInfo(FixReplacement fixReplacement) {
     286           2 :       FixReplacementInfo fixReplacementInfo = new FixReplacementInfo();
     287           2 :       fixReplacementInfo.path = fixReplacement.path;
     288           2 :       fixReplacementInfo.range = toRange(fixReplacement.range);
     289           2 :       fixReplacementInfo.replacement = fixReplacement.replacement;
     290           2 :       return fixReplacementInfo;
     291             :     }
     292             : 
     293           6 :     private RobotCommentFormatter() {}
     294             :   }
     295             : }

Generated by: LCOV version 1.16+git.20220603.dfeb750