AsciiDocModel.java

1
package pro.verron.officestamper.asciidoc;
2
3
import java.util.*;
4
5
import static java.util.Collections.emptyList;
6
7
/// Represents a minimal in-memory model of an AsciiDoc document.
8
///
9
/// This model intentionally supports a compact subset sufficient for rendering to WordprocessingML and JavaFX Scene: -
10
/// Headings (levels 1..6) using leading '=' markers - Paragraphs separated by blank lines - Inline emphasis for bold
11
/// and italic using AsciiDoc-like markers: *bold*, _italic_
12
public final class AsciiDocModel {
13
    private final List<Block> blocks;
14
15
    private AsciiDocModel(List<Block> blocks) {
16
        this.blocks = List.copyOf(blocks);
17
    }
18
19
    /// Creates a new [AsciiDocModel] from the provided blocks.
20
    ///
21
    /// @param blocks ordered content blocks
22
    ///
23
    /// @return immutable AsciiDocModel
24
    public static AsciiDocModel of(List<Block> blocks) {
25
        Objects.requireNonNull(blocks, "blocks");
26 1 1. of : replaced return value with null for pro/verron/officestamper/asciidoc/AsciiDocModel::of → KILLED
        return new AsciiDocModel(new ArrayList<>(blocks));
27
    }
28
29
    /// Returns the ordered list of blocks comprising the document.
30
    ///
31
    /// @return immutable list of blocks
32
    public List<Block> getBlocks() {
33 1 1. getBlocks : replaced return value with Collections.emptyList for pro/verron/officestamper/asciidoc/AsciiDocModel::getBlocks → KILLED
        return blocks;
34
    }
35
36
    /// Marker interface for document blocks.
37
    public sealed interface Block
38
            permits Blockquote, Break, CodeBlock, CommentLine, Heading, ImageBlock, MacroBlock, OpenBlock,
39
            OrderedList, Paragraph, Table, UnorderedList {
40
        int size();
41
    }
42
43
    /// Inline fragment inside a paragraph/heading.
44
    public sealed interface Inline
45
            permits Bold, InlineImage, InlineMacro, Italic, Link, Styled, Sub, Sup, Tab, Text {
46
        /// Returns the text of the inline fragment.
47
        ///
48
        /// @return text
49
        String text();
50
    }
51
52
    /// Heading block (levels 1..6).
53
    ///
54
    /// @param level heading level
55
    /// @param inlines inline fragments
56
    public record Heading(List<String> header, int level, List<Inline> inlines)
57
            implements Block {
58
        public Heading(int level, List<Inline> inlines) {
59
            this(emptyList(), level, inlines);
60
        }
61
62
        /// Constructor.
63
        ///
64
        /// @param level heading level
65
        /// @param inlines inline fragments
66
        public Heading(List<String> header, int level, List<Inline> inlines) {
67
            if (level < 1 || level > 6) {
68
                throw new IllegalArgumentException("Heading level must be between 1 and 6");
69
            }
70
            this.header = List.copyOf(header);
71
            this.level = level;
72
            this.inlines = List.copyOf(inlines);
73
        }
74
75
        @Override
76
        public int size() {
77 1 1. size : replaced int return with 0 for pro/verron/officestamper/asciidoc/AsciiDocModel$Heading::size → NO_COVERAGE
            return 1;
78
        }
79
    }
80
81
    /// Paragraph block.
82
    ///
83
    /// @param inlines inline fragments
84
    public record Paragraph(List<String> header, List<Inline> inlines)
85
            implements Block {
86
        public Paragraph(List<Inline> inlines) {
87
            this(emptyList(), inlines);
88
        }
89
90
        /// Constructor.
91
        ///
92
        /// @param inlines inline fragments
93
        public Paragraph(List<String> header, List<Inline> inlines) {
94
            this.header = List.copyOf(header);
95
            this.inlines = List.copyOf(inlines);
96
        }
97
98
        @Override
99
        public int size() {
100 1 1. size : replaced int return with 0 for pro/verron/officestamper/asciidoc/AsciiDocModel$Paragraph::size → SURVIVED
            return 1;
101
        }
102
    }
103
104
    /// Text fragment.
105
    ///
106
    /// @param text text
107
    public record Text(String text)
108
            implements Inline {}
109
110
    /// Bold inline that can contain nested inlines.
111
    ///
112
    /// @param children nested inline fragments
113
    public record Bold(List<Inline> children)
114
            implements Inline {
115
        /// Constructor.
116
        ///
117
        /// @param children nested inline fragments
118
        public Bold(List<Inline> children) {
119
            this.children = List.copyOf(children);
120
        }
121
122
        @Override
123
        public String text() {
124
            StringBuilder sb = new StringBuilder();
125
            for (Inline in : children) sb.append(in.text());
126 1 1. text : replaced return value with "" for pro/verron/officestamper/asciidoc/AsciiDocModel$Bold::text → KILLED
            return sb.toString();
127
        }
128
    }
129
130
    public record Sup(List<Inline> children)
131
            implements Inline {
132
        public Sup(List<Inline> children) {
133
            this.children = List.copyOf(children);
134
        }
135
136
        @Override
137
        public String text() {
138
            StringBuilder sb = new StringBuilder();
139
            for (Inline in : children) sb.append(in.text());
140 1 1. text : replaced return value with "" for pro/verron/officestamper/asciidoc/AsciiDocModel$Sup::text → NO_COVERAGE
            return sb.toString();
141
        }
142
    }
143
144
    public record Sub(List<Inline> children)
145
            implements Inline {
146
        public Sub(List<Inline> children) {
147
            this.children = List.copyOf(children);
148
        }
149
150
        @Override
151
        public String text() {
152
            StringBuilder sb = new StringBuilder();
153
            for (Inline in : children) sb.append(in.text());
154 1 1. text : replaced return value with "" for pro/verron/officestamper/asciidoc/AsciiDocModel$Sub::text → NO_COVERAGE
            return sb.toString();
155
        }
156
    }
157
158
    /// Italic inline that can contain nested inlines.
159
    ///
160
    /// @param children nested inline fragments
161
    public record Italic(List<Inline> children)
162
            implements Inline {
163
        /// Constructor.
164
        ///
165
        /// @param children nested inline fragments
166
        public Italic(List<Inline> children) {
167
            this.children = List.copyOf(children);
168
        }
169
170
        @Override
171
        public String text() {
172
            StringBuilder sb = new StringBuilder();
173
            for (Inline in : children) sb.append(in.text());
174 1 1. text : replaced return value with "" for pro/verron/officestamper/asciidoc/AsciiDocModel$Italic::text → KILLED
            return sb.toString();
175
        }
176
    }
177
178
    /// Inline tab marker to be rendered as a DOCX tab stop.
179
    public record Tab()
180
            implements Inline {
181
        @Override
182
        public String text() {
183 1 1. text : replaced return value with "" for pro/verron/officestamper/asciidoc/AsciiDocModel$Tab::text → NO_COVERAGE
            return "\t";
184
        }
185
    }
186
187
    /// Simple table block: list of rows; each row is a list of cells; each cell contains inline content.
188
    ///
189
    /// @param rows table rows
190
    public record Table(List<Row> rows)
191
            implements Block {
192
        /// Constructor.
193
        ///
194
        /// @param rows table rows
195
        public Table(List<Row> rows) {
196
            this.rows = List.copyOf(rows);
197
        }
198
199
        @Override
200
        public int size() {
201 1 1. size : replaced int return with 0 for pro/verron/officestamper/asciidoc/AsciiDocModel$Table::size → SURVIVED
            return rows.stream()
202
                       .map(Row::cells)
203
                       .flatMap(Collection::stream)
204
                       .map(Cell::blocks)
205
                       .flatMap(Collection::stream)
206
                       .mapToInt(Block::size)
207
                       .sum();
208
        }
209
    }
210
211
    /// Table row.
212
    ///
213
    /// @param cells table cells
214
    public record Row(List<Cell> cells, Optional<String> style) {
215
        /// Constructor.
216
        ///
217
        /// @param cells table cells
218
        public Row(List<Cell> cells) {
219
            this(cells, Optional.empty());
220
        }
221
222
        public Row(List<Cell> cells, Optional<String> style) {
223
            this.cells = List.copyOf(cells);
224
            this.style = style;
225
        }
226
227
        public static List<Row> listOf() {
228 1 1. listOf : replaced return value with Collections.emptyList for pro/verron/officestamper/asciidoc/AsciiDocModel$Row::listOf → KILLED
            return List.of(of(Cell.listOf()));
229
        }
230
231
        private static Row of(List<Cell> cells) {
232 1 1. of : replaced return value with null for pro/verron/officestamper/asciidoc/AsciiDocModel$Row::of → KILLED
            return new Row(cells);
233
        }
234
    }
235
236
    /// Table cell.
237
    ///
238
    /// @param blocks cell content blocks
239
    public record Cell(List<Block> blocks, Optional<String> style) {
240
        public Cell(List<Block> blocks) {
241
            this(blocks, Optional.empty());
242
        }
243
244
        /// Constructor.
245
        ///
246
        /// @param blocks cell content blocks
247
        public Cell(List<Block> blocks, Optional<String> style) {
248
            this.blocks = List.copyOf(blocks);
249
            this.style = style;
250
        }
251
252
        private static List<Cell> listOf() {
253 1 1. listOf : replaced return value with Collections.emptyList for pro/verron/officestamper/asciidoc/AsciiDocModel$Cell::listOf → KILLED
            return List.of(ofInlines(List.of(new Text("A"))), ofInlines(List.of(new Text("B"))));
254
        }
255
256
        public static Cell ofInlines(List<Inline> inlines) {
257 1 1. ofInlines : replaced return value with null for pro/verron/officestamper/asciidoc/AsciiDocModel$Cell::ofInlines → KILLED
            return new Cell(List.of(new Paragraph(inlines)));
258
        }
259
    }
260
261
    /// Unordered list.
262
    ///
263
    /// @param items list items
264
    public record UnorderedList(List<ListItem> items)
265
            implements Block {
266
        @Override
267
        public int size() {
268 1 1. size : replaced int return with 0 for pro/verron/officestamper/asciidoc/AsciiDocModel$UnorderedList::size → NO_COVERAGE
            return items.size();
269
        }
270
    }
271
272
    /// Ordered list.
273
    ///
274
    /// @param items list items
275
    public record OrderedList(List<ListItem> items)
276
            implements Block {
277
        @Override
278
        public int size() {
279 1 1. size : replaced int return with 0 for pro/verron/officestamper/asciidoc/AsciiDocModel$OrderedList::size → NO_COVERAGE
            return items.size();
280
        }
281
    }
282
283
    /// List item.
284
    ///
285
    /// @param inlines inline fragments
286
    public record ListItem(List<Inline> inlines) {
287
        /// Constructor.
288
        ///
289
        /// @param inlines inline fragments
290
        public ListItem(List<Inline> inlines) {
291
            this.inlines = List.copyOf(inlines);
292
        }
293
    }
294
295
    /// Blockquote.
296
    ///
297
    /// @param inlines inline fragments
298
    public record Blockquote(List<Inline> inlines)
299
            implements Block {
300
        /// Constructor.
301
        ///
302
        /// @param inlines inline fragments
303
        public Blockquote(List<Inline> inlines) {
304
            this.inlines = List.copyOf(inlines);
305
        }
306
307
        @Override
308
        public int size() {
309 1 1. size : replaced int return with 0 for pro/verron/officestamper/asciidoc/AsciiDocModel$Blockquote::size → NO_COVERAGE
            return 1;
310
        }
311
    }
312
313
    /// Code block.
314
    ///
315
    /// @param language language
316
    /// @param content code content
317
    public record CodeBlock(String language, String content)
318
            implements Block {
319
        @Override
320
        public int size() {
321 1 1. size : replaced int return with 0 for pro/verron/officestamper/asciidoc/AsciiDocModel$CodeBlock::size → NO_COVERAGE
            return 1;
322
        }
323
    }
324
325
    /// Image block.
326
    ///
327
    /// @param url image URL
328
    /// @param altText alternative text
329
    public record ImageBlock(String url, String altText)
330
            implements Block {
331
        @Override
332
        public int size() {
333 1 1. size : replaced int return with 0 for pro/verron/officestamper/asciidoc/AsciiDocModel$ImageBlock::size → NO_COVERAGE
            return 1;
334
        }
335
    }
336
337
    /// Link inline.
338
    ///
339
    /// @param url link URL
340
    /// @param text link text
341
    public record Link(String url, String text)
342
            implements Inline {}
343
344
    /// Inline image.
345
    ///
346
    /// @param path image path
347
    /// @param map alternative text
348
    public record InlineImage(String path, Map<String, String> map)
349
            implements Inline {
350
351
        public InlineImage(String path, Map<String, String> map) {
352
            this.path = path;
353
            this.map = Collections.unmodifiableMap(new TreeMap<>(map));
354
        }
355
356
        @Override
357
        public String text() {
358 1 1. text : replaced return value with "" for pro/verron/officestamper/asciidoc/AsciiDocModel$InlineImage::text → NO_COVERAGE
            return path;
359
        }
360
    }
361
362
363
    public record OpenBlock(List<String> header, List<Block> content)
364
            implements Block {
365
        @Override
366
        public int size() {
367 1 1. size : replaced int return with 0 for pro/verron/officestamper/asciidoc/AsciiDocModel$OpenBlock::size → NO_COVERAGE
            return content.stream()
368
                          .mapToInt(Block::size)
369
                          .sum();
370
        }
371
    }
372
373
    public record Break()
374
            implements Block {
375
        @Override
376
        public int size() {
377
            return 0;
378
        }
379
    }
380
381
    public record CommentLine(String comment)
382
            implements Block {
383
        @Override
384
        public int size() {
385
            return 0;
386
        }
387
    }
388
389
    public record Styled(String role, List<Inline> children)
390
            implements Inline {
391
        @Override
392
        public String text() {
393
            StringBuilder sb = new StringBuilder();
394
            for (Inline in : children) sb.append(in.text());
395 1 1. text : replaced return value with "" for pro/verron/officestamper/asciidoc/AsciiDocModel$Styled::text → NO_COVERAGE
            return sb.toString();
396
        }
397
    }
398
399
    public record MacroBlock(String name, String id, List<String> list)
400
            implements Block {
401
        @Override
402
        public int size() {
403 1 1. size : replaced int return with 0 for pro/verron/officestamper/asciidoc/AsciiDocModel$MacroBlock::size → NO_COVERAGE
            return 1;
404
        }
405
    }
406
407
    public record InlineMacro(String name, String id, List<String> list)
408
            implements Inline {
409
410
        @Override
411
        public String text() {
412 1 1. text : replaced return value with "" for pro/verron/officestamper/asciidoc/AsciiDocModel$InlineMacro::text → NO_COVERAGE
            return String.join("", list);
413
        }
414
    }
415
}

Mutations

26

1.1
Location : of
Killed by : pro.verron.officestamper.asciidoc.test.AsciiDocParserTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.asciidoc.test.AsciiDocParserTest]/[test-template:parse_shouldReturnEmptyModel_whenInputIsNull(java.lang.String)]/[test-template-invocation:#2]
replaced return value with null for pro/verron/officestamper/asciidoc/AsciiDocModel::of → KILLED

33

1.1
Location : getBlocks
Killed by : pro.verron.officestamper.asciidoc.test.AsciiDocParserTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.asciidoc.test.AsciiDocParserTest]/[method:parse_shouldParseImageBlock()]
replaced return value with Collections.emptyList for pro/verron/officestamper/asciidoc/AsciiDocModel::getBlocks → KILLED

77

1.1
Location : size
Killed by : none
replaced int return with 0 for pro/verron/officestamper/asciidoc/AsciiDocModel$Heading::size → NO_COVERAGE

100

1.1
Location : size
Killed by : none
replaced int return with 0 for pro/verron/officestamper/asciidoc/AsciiDocModel$Paragraph::size → SURVIVED
Covering tests

126

1.1
Location : text
Killed by : pro.verron.officestamper.asciidoc.test.AsciiDocParserTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.asciidoc.test.AsciiDocParserTest]/[method:parse_shouldParseInlines()]
replaced return value with "" for pro/verron/officestamper/asciidoc/AsciiDocModel$Bold::text → KILLED

140

1.1
Location : text
Killed by : none
replaced return value with "" for pro/verron/officestamper/asciidoc/AsciiDocModel$Sup::text → NO_COVERAGE

154

1.1
Location : text
Killed by : none
replaced return value with "" for pro/verron/officestamper/asciidoc/AsciiDocModel$Sub::text → NO_COVERAGE

174

1.1
Location : text
Killed by : pro.verron.officestamper.asciidoc.test.AsciiDocParserTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.asciidoc.test.AsciiDocParserTest]/[method:parse_shouldParseInlines()]
replaced return value with "" for pro/verron/officestamper/asciidoc/AsciiDocModel$Italic::text → KILLED

183

1.1
Location : text
Killed by : none
replaced return value with "" for pro/verron/officestamper/asciidoc/AsciiDocModel$Tab::text → NO_COVERAGE

201

1.1
Location : size
Killed by : none
replaced int return with 0 for pro/verron/officestamper/asciidoc/AsciiDocModel$Table::size → SURVIVED
Covering tests

228

1.1
Location : listOf
Killed by : pro.verron.officestamper.asciidoc.test.DocxToAsciiDocTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.asciidoc.test.DocxToAsciiDocTest]/[method:shouldRenderNestedTable()]
replaced return value with Collections.emptyList for pro/verron/officestamper/asciidoc/AsciiDocModel$Row::listOf → KILLED

232

1.1
Location : of
Killed by : pro.verron.officestamper.asciidoc.test.DocxToAsciiDocTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.asciidoc.test.DocxToAsciiDocTest]/[method:shouldRenderNestedTable()]
replaced return value with null for pro/verron/officestamper/asciidoc/AsciiDocModel$Row::of → KILLED

253

1.1
Location : listOf
Killed by : pro.verron.officestamper.asciidoc.test.DocxToAsciiDocTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.asciidoc.test.DocxToAsciiDocTest]/[method:shouldRenderNestedTable()]
replaced return value with Collections.emptyList for pro/verron/officestamper/asciidoc/AsciiDocModel$Cell::listOf → KILLED

257

1.1
Location : ofInlines
Killed by : pro.verron.officestamper.asciidoc.test.AsciiDocParserTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.asciidoc.test.AsciiDocParserTest]/[method:parse_shouldParseTable()]
replaced return value with null for pro/verron/officestamper/asciidoc/AsciiDocModel$Cell::ofInlines → KILLED

268

1.1
Location : size
Killed by : none
replaced int return with 0 for pro/verron/officestamper/asciidoc/AsciiDocModel$UnorderedList::size → NO_COVERAGE

279

1.1
Location : size
Killed by : none
replaced int return with 0 for pro/verron/officestamper/asciidoc/AsciiDocModel$OrderedList::size → NO_COVERAGE

309

1.1
Location : size
Killed by : none
replaced int return with 0 for pro/verron/officestamper/asciidoc/AsciiDocModel$Blockquote::size → NO_COVERAGE

321

1.1
Location : size
Killed by : none
replaced int return with 0 for pro/verron/officestamper/asciidoc/AsciiDocModel$CodeBlock::size → NO_COVERAGE

333

1.1
Location : size
Killed by : none
replaced int return with 0 for pro/verron/officestamper/asciidoc/AsciiDocModel$ImageBlock::size → NO_COVERAGE

358

1.1
Location : text
Killed by : none
replaced return value with "" for pro/verron/officestamper/asciidoc/AsciiDocModel$InlineImage::text → NO_COVERAGE

367

1.1
Location : size
Killed by : none
replaced int return with 0 for pro/verron/officestamper/asciidoc/AsciiDocModel$OpenBlock::size → NO_COVERAGE

395

1.1
Location : text
Killed by : none
replaced return value with "" for pro/verron/officestamper/asciidoc/AsciiDocModel$Styled::text → NO_COVERAGE

403

1.1
Location : size
Killed by : none
replaced int return with 0 for pro/verron/officestamper/asciidoc/AsciiDocModel$MacroBlock::size → NO_COVERAGE

412

1.1
Location : text
Killed by : none
replaced return value with "" for pro/verron/officestamper/asciidoc/AsciiDocModel$InlineMacro::text → NO_COVERAGE

Active mutators

Tests examined


Report generated by PIT 1.22.1