| 1 | package pro.verron.officestamper.asciidoc; | |
| 2 | ||
| 3 | import org.jspecify.annotations.NonNull; | |
| 4 | ||
| 5 | import java.util.List; | |
| 6 | import java.util.Map; | |
| 7 | import java.util.function.Function; | |
| 8 | import java.util.stream.Collectors; | |
| 9 | ||
| 10 | import static pro.verron.officestamper.asciidoc.AsciiDocModel.*; | |
| 11 | ||
| 12 | public final class AsciiDocToText | |
| 13 | implements Function<AsciiDocModel, String> { | |
| 14 | ||
| 15 | private static String renderInlines(List<@NonNull Inline> inlines) { | |
| 16 | var sb = new StringBuilder(); | |
| 17 | for (Inline inline : inlines) { | |
| 18 | sb.append(switch (inline) { | |
| 19 | case Text(String text) -> text; | |
| 20 | case Bold(List<Inline> children) -> "*%s*".formatted(renderInlines(children)); | |
| 21 | case Italic(List<Inline> children) -> "_%s_".formatted(renderInlines(children)); | |
| 22 | case Sup(List<Inline> children) -> "^%s^".formatted(renderInlines(children)); | |
| 23 | case Sub(List<Inline> children) -> "~%s~".formatted(renderInlines(children)); | |
| 24 | case Tab _ -> sb.append("\t"); | |
| 25 | case Link(String url, String text) -> "%s[%s]".formatted(url, text); | |
| 26 | case InlineImage(String path, Map<String, String> map) -> "image:%s[%s]".formatted(path, | |
| 27 | map.entrySet() | |
| 28 | .stream() | |
| 29 |
1
1. lambda$renderInlines$0 : replaced return value with "" for pro/verron/officestamper/asciidoc/AsciiDocToText::lambda$renderInlines$0 → NO_COVERAGE |
.map(e -> e.getKey() + "=" + e.getValue()) |
| 30 | .collect(Collectors.joining(", "))); | |
| 31 | case Styled(String role, List<Inline> children) -> "[%s]#%s#".formatted(role, renderInlines(children)); | |
| 32 | case InlineMacro(String name, String id, List<String> list) -> | |
| 33 | "%s:%s[%s]".formatted(name, id, String.join(", ", list)); | |
| 34 | }); | |
| 35 | } | |
| 36 |
1
1. renderInlines : replaced return value with "" for pro/verron/officestamper/asciidoc/AsciiDocToText::renderInlines → KILLED |
return sb.toString(); |
| 37 | } | |
| 38 | ||
| 39 | private static String renderCellContent(Cell cell, boolean isAsciidoc, int level) { | |
| 40 | var blockList = cell.blocks(); | |
| 41 |
1
1. renderCellContent : negated conditional → KILLED |
if (!isAsciidoc) { |
| 42 |
1
1. renderCellContent : negated conditional → KILLED |
if (blockList.isEmpty()) return ""; |
| 43 | Paragraph p = (Paragraph) blockList.getFirst(); | |
| 44 |
1
1. renderCellContent : replaced return value with "" for pro/verron/officestamper/asciidoc/AsciiDocToText::renderCellContent → KILLED |
return renderInlines(p.inlines()); |
| 45 | } | |
| 46 | else { | |
| 47 |
1
1. renderCellContent : replaced return value with "" for pro/verron/officestamper/asciidoc/AsciiDocToText::renderCellContent → KILLED |
return blockList.stream() |
| 48 |
1
1. lambda$renderCellContent$0 : replaced return value with "" for pro/verron/officestamper/asciidoc/AsciiDocToText::lambda$renderCellContent$0 → KILLED |
.map(block -> renderBlock(block, level)) |
| 49 | .collect(Collectors.joining()) | |
| 50 | .trim(); | |
| 51 | } | |
| 52 | } | |
| 53 | ||
| 54 | private static String renderBlock(Block block, int tableLevel) { | |
| 55 |
1
1. renderBlock : replaced return value with "" for pro/verron/officestamper/asciidoc/AsciiDocToText::renderBlock → KILLED |
return switch (block) { |
| 56 |
1
1. renderBlock : negated conditional → NO_COVERAGE |
case Heading(_, int level, List<Inline> inlines) -> renderHeading(level, inlines); |
| 57 | case Paragraph(List<String> header, List<Inline> inlines) -> renderHeader(header) + renderInlines(inlines); | |
| 58 | case UnorderedList(List<ListItem> items1) -> renderList(items1, "* "); | |
| 59 | case OrderedList(List<ListItem> items) -> renderList(items, ". "); | |
| 60 | case Table(List<Row> rows) -> renderTable(rows, tableLevel); | |
| 61 | case Blockquote(List<Inline> inlines) -> renderBlockquote(inlines); | |
| 62 | case CodeBlock(String language, String content) -> renderCodeBlock(language, content); | |
| 63 | case ImageBlock(String url, String altText) -> renderImageBlock(url, altText); | |
| 64 | case OpenBlock openBlock -> render(openBlock); | |
| 65 | case MacroBlock(String name, String id, List<String> list) -> | |
| 66 | "%s::%s[%s]".formatted(name, id, String.join(", ", list)); | |
| 67 | case Break _ -> "<<<"; | |
| 68 | case CommentLine(String comment) -> ("// %s").formatted(comment); | |
| 69 | } + "\n\n"; | |
| 70 | } | |
| 71 | ||
| 72 | private static String render(OpenBlock openBlock) { | |
| 73 | var sb = new StringBuilder(); | |
| 74 | sb.append("[%s]\n".formatted(String.join(", ", openBlock.header()))); | |
| 75 | sb.append("--\n"); | |
| 76 | openBlock.content() | |
| 77 | .stream() | |
| 78 |
1
1. lambda$render$0 : replaced return value with "" for pro/verron/officestamper/asciidoc/AsciiDocToText::lambda$render$0 → NO_COVERAGE |
.map(p -> renderBlock(p, 0)) |
| 79 |
1
1. render : removed call to java/util/stream/Stream::forEach → NO_COVERAGE |
.forEach(sb::append); |
| 80 | sb.append("--\n"); | |
| 81 |
1
1. render : replaced return value with "" for pro/verron/officestamper/asciidoc/AsciiDocToText::render → NO_COVERAGE |
return sb.toString(); |
| 82 | } | |
| 83 | ||
| 84 | private static String renderTable(List<Row> rows, int level) { | |
| 85 | var cellDelimiter = switch (level) { | |
| 86 | case 0 -> "|"; | |
| 87 | case 1 -> "!"; | |
| 88 | default -> throw new IllegalArgumentException("Table nesting level must be between 0 and 1"); | |
| 89 | }; | |
| 90 | var tableDelimiter = cellDelimiter + "==="; | |
| 91 | var sb = new StringBuilder(); | |
| 92 | sb.append(tableDelimiter); | |
| 93 | sb.append("\n"); | |
| 94 | for (Row row : rows) { | |
| 95 | var style = row.style(); | |
| 96 |
1
1. renderTable : removed call to java/util/Optional::ifPresent → SURVIVED |
style.ifPresent(s -> sb.append("[%s]\n".formatted(s))); |
| 97 | for (Cell cell : row.cells()) { | |
| 98 | var blockList = cell.blocks(); | |
| 99 | var size = blockList.size(); | |
| 100 |
4
1. renderTable : negated conditional → SURVIVED 2. renderTable : changed conditional boundary → KILLED 3. renderTable : negated conditional → KILLED 4. renderTable : negated conditional → KILLED |
boolean isAsciidoc = size > 1 || (size == 1 && !(blockList.getFirst() instanceof Paragraph)); |
| 101 | cell.style() | |
| 102 |
1
1. renderTable : removed call to java/util/Optional::ifPresent → SURVIVED |
.ifPresent(s -> sb.append("[%s]\n".formatted(s))); |
| 103 |
2
1. renderTable : negated conditional → KILLED 2. renderTable : Replaced integer addition with subtraction → KILLED |
sb.append(isAsciidoc ? "a" + cellDelimiter : cellDelimiter) |
| 104 | .append(renderCellContent(cell, isAsciidoc, level + 1)) | |
| 105 | .append("\n"); | |
| 106 | } | |
| 107 | } | |
| 108 | sb.append(tableDelimiter); | |
| 109 |
1
1. renderTable : replaced return value with "" for pro/verron/officestamper/asciidoc/AsciiDocToText::renderTable → KILLED |
return sb.toString(); |
| 110 | } | |
| 111 | ||
| 112 | private static String renderImageBlock(String url, String altText) { | |
| 113 |
1
1. renderImageBlock : replaced return value with "" for pro/verron/officestamper/asciidoc/AsciiDocToText::renderImageBlock → NO_COVERAGE |
return "image::" + url + "[" + altText + "]"; |
| 114 | } | |
| 115 | ||
| 116 | private static String renderCodeBlock(String language, String content) { | |
| 117 |
2
1. renderCodeBlock : negated conditional → NO_COVERAGE 2. renderCodeBlock : replaced return value with "" for pro/verron/officestamper/asciidoc/AsciiDocToText::renderCodeBlock → NO_COVERAGE |
return (language.isEmpty() ? "" : "[source," + language + "]\n") + "----\n" + content + "\n----"; |
| 118 | } | |
| 119 | ||
| 120 | private static String renderBlockquote(List<Inline> inlines) { | |
| 121 |
1
1. renderBlockquote : replaced return value with "" for pro/verron/officestamper/asciidoc/AsciiDocToText::renderBlockquote → NO_COVERAGE |
return "____\n" + renderInlines(inlines) + "\n____"; |
| 122 | } | |
| 123 | ||
| 124 | private static String renderList(List<ListItem> items1, String x) { | |
| 125 |
1
1. renderList : replaced return value with "" for pro/verron/officestamper/asciidoc/AsciiDocToText::renderList → NO_COVERAGE |
return items1.stream() |
| 126 |
1
1. lambda$renderList$0 : replaced return value with "" for pro/verron/officestamper/asciidoc/AsciiDocToText::lambda$renderList$0 → NO_COVERAGE |
.map(item -> x + renderInlines(item.inlines()) + "\n") |
| 127 | .collect(Collectors.joining("\n")); | |
| 128 | } | |
| 129 | ||
| 130 | private static String renderHeading(int level, List<Inline> inlines) { | |
| 131 |
1
1. renderHeading : replaced return value with "" for pro/verron/officestamper/asciidoc/AsciiDocToText::renderHeading → NO_COVERAGE |
return "=".repeat(level) + " " + renderInlines(inlines); |
| 132 | } | |
| 133 | ||
| 134 | private static String renderHeader(List<String> header) { | |
| 135 |
1
1. renderHeader : negated conditional → KILLED |
if (header.isEmpty()) return ""; |
| 136 |
1
1. renderHeader : replaced return value with "" for pro/verron/officestamper/asciidoc/AsciiDocToText::renderHeader → NO_COVERAGE |
return "[%s]\n".formatted(String.join(", ", header)); |
| 137 | } | |
| 138 | ||
| 139 | public String apply(AsciiDocModel model) { | |
| 140 |
1
1. apply : replaced return value with "" for pro/verron/officestamper/asciidoc/AsciiDocToText::apply → KILLED |
return model.getBlocks() |
| 141 | .stream() | |
| 142 |
1
1. lambda$apply$0 : replaced return value with "" for pro/verron/officestamper/asciidoc/AsciiDocToText::lambda$apply$0 → KILLED |
.map((Block block) -> renderBlock(block, 0)) |
| 143 | .collect(Collectors.joining()); | |
| 144 | } | |
| 145 | } | |
Mutations | ||
| 29 |
1.1 |
|
| 36 |
1.1 |
|
| 41 |
1.1 |
|
| 42 |
1.1 |
|
| 44 |
1.1 |
|
| 47 |
1.1 |
|
| 48 |
1.1 |
|
| 55 |
1.1 |
|
| 56 |
1.1 |
|
| 78 |
1.1 |
|
| 79 |
1.1 |
|
| 81 |
1.1 |
|
| 96 |
1.1 |
|
| 100 |
1.1 2.2 3.3 4.4 |
|
| 102 |
1.1 |
|
| 103 |
1.1 2.2 |
|
| 109 |
1.1 |
|
| 113 |
1.1 |
|
| 117 |
1.1 2.2 |
|
| 121 |
1.1 |
|
| 125 |
1.1 |
|
| 126 |
1.1 |
|
| 131 |
1.1 |
|
| 135 |
1.1 |
|
| 136 |
1.1 |
|
| 140 |
1.1 |
|
| 142 |
1.1 |