LCOV - code coverage report
Current view: top level - server/mail/send - CommentFormatter.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 70 71 98.6 %
Date: 2022-11-19 15:00:39 Functions: 10 11 90.9 %

          Line data    Source code
       1             : // Copyright (C) 2016 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.mail.send;
      16             : 
      17             : import static com.google.common.base.Strings.isNullOrEmpty;
      18             : 
      19             : import com.google.common.base.Splitter;
      20             : import com.google.common.collect.ImmutableList;
      21             : import com.google.gerrit.common.Nullable;
      22             : import java.util.ArrayList;
      23             : import java.util.List;
      24             : 
      25           0 : public class CommentFormatter {
      26          66 :   public enum BlockType {
      27          66 :     LIST,
      28          66 :     PARAGRAPH,
      29          66 :     PRE_FORMATTED,
      30          66 :     QUOTE
      31             :   }
      32             : 
      33          66 :   public static class Block {
      34             :     public BlockType type;
      35             :     public String text;
      36             :     public List<String> items; // For the items of list blocks.
      37             :     public List<Block> quotedBlocks; // For the contents of quote blocks.
      38             :   }
      39             : 
      40             :   /**
      41             :    * Take a string of comment text that was written using the wiki-Like format and emit a list of
      42             :    * blocks that can be rendered to block-level HTML. This method does not escape HTML.
      43             :    *
      44             :    * <p>Adapted from the {@code wikify} method found in:
      45             :    * com.google.gwtexpui.safehtml.client.SafeHtml
      46             :    *
      47             :    * @param source The raw, unescaped comment in the Gerrit wiki-like format.
      48             :    * @return List of block objects, each with unescaped comment content.
      49             :    */
      50             :   public static ImmutableList<Block> parse(@Nullable String source) {
      51          66 :     if (isNullOrEmpty(source)) {
      52          62 :       return ImmutableList.of();
      53             :     }
      54             : 
      55          66 :     ImmutableList.Builder<Block> result = ImmutableList.builder();
      56          66 :     for (String p : Splitter.on("\n\n").split(source)) {
      57          66 :       if (isQuote(p)) {
      58           1 :         result.add(makeQuote(p));
      59          66 :       } else if (isPreFormat(p)) {
      60           1 :         result.add(makePre(p));
      61          66 :       } else if (isList(p)) {
      62           1 :         makeList(p, result);
      63          66 :       } else if (!p.isEmpty()) {
      64          66 :         result.add(makeParagraph(p));
      65             :       }
      66          66 :     }
      67          66 :     return result.build();
      68             :   }
      69             : 
      70             :   /**
      71             :    * Take a block of comment text that contains a list and potentially paragraphs (but does not
      72             :    * contain blank lines), generate appropriate block elements and append them to the output list.
      73             :    *
      74             :    * <p>In simple cases, this will generate a single list block. For example, on the following
      75             :    * input.
      76             :    *
      77             :    * <p>* Item one. * Item two. * item three.
      78             :    *
      79             :    * <p>However, if the list is adjacent to a paragraph, it will need to also generate that
      80             :    * paragraph. Consider the following input.
      81             :    *
      82             :    * <p>A bit of text describing the context of the list: * List item one. * List item two. * Et
      83             :    * cetera.
      84             :    *
      85             :    * <p>In this case, {@code makeList} generates a paragraph block object containing the
      86             :    * non-bullet-prefixed text, followed by a list block.
      87             :    *
      88             :    * <p>Adapted from the {@code wikifyList} method found in:
      89             :    * com.google.gwtexpui.safehtml.client.SafeHtml
      90             :    *
      91             :    * @param p The block containing the list (as well as potential paragraphs).
      92             :    * @param out The list of blocks to append to.
      93             :    */
      94             :   private static void makeList(String p, ImmutableList.Builder<Block> out) {
      95           1 :     Block block = null;
      96           1 :     StringBuilder textBuilder = null;
      97           1 :     boolean inList = false;
      98           1 :     boolean inParagraph = false;
      99             : 
     100           1 :     for (String line : Splitter.on('\n').split(p)) {
     101           1 :       if (line.startsWith("-") || line.startsWith("*")) {
     102             :         // The next line looks like a list item. If not building a list already,
     103             :         // then create one. Remove the list item marker (* or -) from the line.
     104           1 :         if (!inList) {
     105           1 :           if (inParagraph) {
     106             :             // Add the finished paragraph block to the result.
     107           1 :             inParagraph = false;
     108           1 :             block.text = textBuilder.toString();
     109           1 :             out.add(block);
     110             :           }
     111             : 
     112           1 :           inList = true;
     113           1 :           block = new Block();
     114           1 :           block.type = BlockType.LIST;
     115           1 :           block.items = new ArrayList<>();
     116             :         }
     117           1 :         line = line.substring(1).trim();
     118             : 
     119           1 :       } else if (!inList) {
     120             :         // Otherwise, if a list has not yet been started, but the next line does
     121             :         // not look like a list item, then add the line to a paragraph block. If
     122             :         // a paragraph block has not yet been started, then create one.
     123           1 :         if (!inParagraph) {
     124           1 :           inParagraph = true;
     125           1 :           block = new Block();
     126           1 :           block.type = BlockType.PARAGRAPH;
     127           1 :           textBuilder = new StringBuilder();
     128             :         } else {
     129           1 :           textBuilder.append(" ");
     130             :         }
     131           1 :         textBuilder.append(line);
     132           1 :         continue;
     133             :       }
     134             : 
     135           1 :       block.items.add(line);
     136           1 :     }
     137             : 
     138           1 :     if (block != null) {
     139           1 :       out.add(block);
     140             :     }
     141           1 :   }
     142             : 
     143             :   private static Block makeQuote(String p) {
     144           1 :     String quote = p.replaceAll("\n\\s?>\\s?", "\n");
     145           1 :     if (quote.startsWith("> ")) {
     146           1 :       quote = quote.substring(2);
     147           1 :     } else if (quote.startsWith(" > ")) {
     148           1 :       quote = quote.substring(3);
     149             :     }
     150             : 
     151           1 :     Block block = new Block();
     152           1 :     block.type = BlockType.QUOTE;
     153           1 :     block.quotedBlocks = CommentFormatter.parse(quote);
     154           1 :     return block;
     155             :   }
     156             : 
     157             :   private static Block makePre(String p) {
     158           1 :     Block block = new Block();
     159           1 :     block.type = BlockType.PRE_FORMATTED;
     160           1 :     block.text = p;
     161           1 :     return block;
     162             :   }
     163             : 
     164             :   private static Block makeParagraph(String p) {
     165          66 :     Block block = new Block();
     166          66 :     block.type = BlockType.PARAGRAPH;
     167          66 :     block.text = p;
     168          66 :     return block;
     169             :   }
     170             : 
     171             :   private static boolean isQuote(String p) {
     172          66 :     return p.startsWith("> ") || p.startsWith(" > ");
     173             :   }
     174             : 
     175             :   private static boolean isPreFormat(String p) {
     176          66 :     return p.startsWith(" ") || p.startsWith("\t") || p.contains("\n ") || p.contains("\n\t");
     177             :   }
     178             : 
     179             :   private static boolean isList(String p) {
     180          66 :     return p.startsWith("- ") || p.startsWith("* ") || p.contains("\n- ") || p.contains("\n* ");
     181             :   }
     182             : }

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