AsciiDocToDocx.java

1
package pro.verron.officestamper.asciidoc;
2
3
import org.docx4j.jaxb.Context;
4
import org.docx4j.openpackaging.exceptions.Docx4JException;
5
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
6
import org.docx4j.wml.*;
7
8
import java.math.BigInteger;
9
import java.util.List;
10
11
import static pro.verron.officestamper.asciidoc.AsciiDocModel.*;
12
13
/// Renders [AsciiDocModel] into a [WordprocessingMLPackage] using docx4j.
14
public final class AsciiDocToDocx {
15
    private AsciiDocToDocx() {}
16
17
    /// Creates a new WordprocessingMLPackage and fills it with content from the model.
18
    ///
19
    /// @param model parsed AsciiDoc model
20
    ///
21
    /// @return package containing the rendered document
22
    public static WordprocessingMLPackage compileToPackage(AsciiDocModel model) {
23
        try {
24
            var pkg = WordprocessingMLPackage.createPackage();
25
            var factory = Context.getWmlObjectFactory();
26
27
            for (Block block : model.getBlocks()) {
28 1 1. compileToPackage : negated conditional → NO_COVERAGE
                if (block instanceof Heading h) {
29
                    pkg.getMainDocumentPart()
30 1 1. compileToPackage : removed call to org/docx4j/openpackaging/parts/WordprocessingML/MainDocumentPart::addObject → NO_COVERAGE
                       .addObject(createHeading(factory, h));
31
                }
32 1 1. compileToPackage : negated conditional → NO_COVERAGE
                else if (block instanceof Paragraph p) {
33
                    pkg.getMainDocumentPart()
34 1 1. compileToPackage : removed call to org/docx4j/openpackaging/parts/WordprocessingML/MainDocumentPart::addObject → NO_COVERAGE
                       .addObject(createParagraph(factory, p));
35
                }
36 1 1. compileToPackage : negated conditional → NO_COVERAGE
                else if (block instanceof Table t) {
37
                    pkg.getMainDocumentPart()
38 1 1. compileToPackage : removed call to org/docx4j/openpackaging/parts/WordprocessingML/MainDocumentPart::addObject → NO_COVERAGE
                       .addObject(createTable(factory, t));
39
                }
40
            }
41 1 1. compileToPackage : replaced return value with null for pro/verron/officestamper/asciidoc/AsciiDocToDocx::compileToPackage → NO_COVERAGE
            return pkg;
42
        } catch (Docx4JException e) {
43
            throw new IllegalStateException("Unable to create WordprocessingMLPackage", e);
44
        }
45
    }
46
47
    private static P createHeading(ObjectFactory factory, Heading heading) {
48
        P p = factory.createP();
49
        PPr ppr = factory.createPPr();
50
        PPrBase.PStyle pStyle = factory.createPPrBasePStyle();
51 1 1. createHeading : removed call to org/docx4j/wml/PPrBase$PStyle::setVal → NO_COVERAGE
        pStyle.setVal("Heading" + heading.level());
52 1 1. createHeading : removed call to org/docx4j/wml/PPr::setPStyle → NO_COVERAGE
        ppr.setPStyle(pStyle);
53 1 1. createHeading : removed call to org/docx4j/wml/P::setPPr → NO_COVERAGE
        p.setPPr(ppr);
54
55
        RPr headingRunPr = factory.createRPr();
56
        // Increase size a bit relative to level if heading styles are missing
57
        HpsMeasure sz = factory.createHpsMeasure();
58
        int base = switch (heading.level()) {
59
            case 1 -> 32; // 16pt
60
            case 2 -> 28;
61
            case 3 -> 26;
62
            case 4 -> 24;
63
            case 5 -> 22;
64
            default -> 20;
65
        };
66 1 1. createHeading : removed call to org/docx4j/wml/HpsMeasure::setVal → NO_COVERAGE
        sz.setVal(BigInteger.valueOf(base));
67 1 1. createHeading : removed call to org/docx4j/wml/RPr::setSz → NO_COVERAGE
        headingRunPr.setSz(sz);
68 1 1. createHeading : removed call to org/docx4j/wml/RPr::setSzCs → NO_COVERAGE
        headingRunPr.setSzCs(sz);
69
70 1 1. createHeading : removed call to pro/verron/officestamper/asciidoc/AsciiDocToDocx::addInlines → NO_COVERAGE
        addInlines(factory, p, heading.inlines(), headingRunPr);
71 1 1. createHeading : replaced return value with null for pro/verron/officestamper/asciidoc/AsciiDocToDocx::createHeading → NO_COVERAGE
        return p;
72
    }
73
74
    private static P createParagraph(ObjectFactory factory, Paragraph paragraph) {
75
        P p = factory.createP();
76 1 1. createParagraph : removed call to pro/verron/officestamper/asciidoc/AsciiDocToDocx::addInlines → NO_COVERAGE
        addInlines(factory, p, paragraph.inlines(), null);
77 1 1. createParagraph : replaced return value with null for pro/verron/officestamper/asciidoc/AsciiDocToDocx::createParagraph → NO_COVERAGE
        return p;
78
    }
79
80
    private static void addInlines(ObjectFactory factory, P p, List<Inline> inlines, RPr base) {
81
        for (Inline inline : inlines) {
82 1 1. addInlines : removed call to pro/verron/officestamper/asciidoc/AsciiDocToDocx::emitInline → NO_COVERAGE
            emitInline(factory, p, inline, base);
83
        }
84
    }
85
86
    private static Tbl createTable(ObjectFactory factory, Table table) {
87
        Tbl tbl = factory.createTbl();
88
        // Minimal table without explicit grid; Word will auto-fit columns
89
        for (Row row : table.rows()) {
90
            Tr tr = factory.createTr();
91
            for (Cell cell : row.cells()) {
92
                Tc tc = factory.createTc();
93
                // Add a single paragraph per cell for now
94
                P p = factory.createP();
95 1 1. createTable : removed call to pro/verron/officestamper/asciidoc/AsciiDocToDocx::addInlines → NO_COVERAGE
                addInlines(factory, p, cell.inlines(), null);
96
                tc.getContent()
97
                  .add(p);
98
                tr.getContent()
99
                  .add(tc);
100
            }
101
            tbl.getContent()
102
               .add(tr);
103
        }
104 1 1. createTable : replaced return value with null for pro/verron/officestamper/asciidoc/AsciiDocToDocx::createTable → NO_COVERAGE
        return tbl;
105
    }
106
107
    private static void emitInline(ObjectFactory factory, P p, Inline inline, RPr base) {
108 1 1. emitInline : negated conditional → NO_COVERAGE
        if (inline instanceof AsciiDocModel.Text(String text)) {
109
            R r = factory.createR();
110 1 1. emitInline : negated conditional → NO_COVERAGE
            RPr rpr = base != null ? deepCopy(factory, base) : factory.createRPr();
111
            org.docx4j.wml.Text tx = factory.createText();
112 1 1. emitInline : removed call to org/docx4j/wml/Text::setValue → NO_COVERAGE
            tx.setValue(text);
113
            // Preserve spaces/tabs if present within text segments
114 1 1. emitInline : removed call to org/docx4j/wml/Text::setSpace → NO_COVERAGE
            tx.setSpace("preserve");
115
            r.getContent()
116
             .add(tx);
117 1 1. emitInline : removed call to org/docx4j/wml/R::setRPr → NO_COVERAGE
            r.setRPr(rpr);
118
            p.getContent()
119
             .add(r);
120
            return;
121
        }
122
123 1 1. emitInline : negated conditional → NO_COVERAGE
        if (inline instanceof Bold(List<Inline> children)) {
124 1 1. emitInline : negated conditional → NO_COVERAGE
            RPr next = base != null ? deepCopy(factory, base) : factory.createRPr();
125 1 1. emitInline : removed call to org/docx4j/wml/RPr::setB → NO_COVERAGE
            next.setB(new BooleanDefaultTrue());
126
            for (Inline child : children) {
127 1 1. emitInline : removed call to pro/verron/officestamper/asciidoc/AsciiDocToDocx::emitInline → NO_COVERAGE
                emitInline(factory, p, child, next);
128
            }
129
            return;
130
        }
131
132 1 1. emitInline : negated conditional → NO_COVERAGE
        if (inline instanceof Italic(List<Inline> children)) {
133 1 1. emitInline : negated conditional → NO_COVERAGE
            RPr next = base != null ? deepCopy(factory, base) : factory.createRPr();
134 1 1. emitInline : removed call to org/docx4j/wml/RPr::setI → NO_COVERAGE
            next.setI(new BooleanDefaultTrue());
135
            for (Inline child : children) {
136 1 1. emitInline : removed call to pro/verron/officestamper/asciidoc/AsciiDocToDocx::emitInline → NO_COVERAGE
                emitInline(factory, p, child, next);
137
            }
138
            return;
139
        }
140
141 1 1. emitInline : negated conditional → NO_COVERAGE
        if (inline instanceof AsciiDocModel.Tab) {
142
            R r = factory.createR();
143
            R.Tab tab = factory.createRTab();
144
            r.getContent()
145
             .add(tab);
146
            p.getContent()
147
             .add(r);
148
        }
149
    }
150
151
    private static RPr deepCopy(ObjectFactory factory, RPr src) {
152
        // Minimal copy of relevant props; docx4j doesn't offer a trivial clone here.
153
        RPr c = factory.createRPr();
154 1 1. deepCopy : negated conditional → NO_COVERAGE
        if (src.getB() != null) {
155
            BooleanDefaultTrue b = new BooleanDefaultTrue();
156 1 1. deepCopy : removed call to org/docx4j/wml/RPr::setB → NO_COVERAGE
            c.setB(b);
157
        }
158 1 1. deepCopy : negated conditional → NO_COVERAGE
        if (src.getI() != null) {
159
            BooleanDefaultTrue i = new BooleanDefaultTrue();
160 1 1. deepCopy : removed call to org/docx4j/wml/RPr::setI → NO_COVERAGE
            c.setI(i);
161
        }
162 1 1. deepCopy : negated conditional → NO_COVERAGE
        if (src.getSz() != null) {
163
            HpsMeasure sz = factory.createHpsMeasure();
164 1 1. deepCopy : removed call to org/docx4j/wml/HpsMeasure::setVal → NO_COVERAGE
            sz.setVal(src.getSz()
165
                         .getVal());
166 1 1. deepCopy : removed call to org/docx4j/wml/RPr::setSz → NO_COVERAGE
            c.setSz(sz);
167
            HpsMeasure szCs = factory.createHpsMeasure();
168 1 1. deepCopy : removed call to org/docx4j/wml/HpsMeasure::setVal → NO_COVERAGE
            szCs.setVal(src.getSz()
169
                           .getVal());
170 1 1. deepCopy : removed call to org/docx4j/wml/RPr::setSzCs → NO_COVERAGE
            c.setSzCs(szCs);
171
        }
172 1 1. deepCopy : replaced return value with null for pro/verron/officestamper/asciidoc/AsciiDocToDocx::deepCopy → NO_COVERAGE
        return c;
173
    }
174
}

Mutations

28

1.1
Location : compileToPackage
Killed by : none
negated conditional → NO_COVERAGE

30

1.1
Location : compileToPackage
Killed by : none
removed call to org/docx4j/openpackaging/parts/WordprocessingML/MainDocumentPart::addObject → NO_COVERAGE

32

1.1
Location : compileToPackage
Killed by : none
negated conditional → NO_COVERAGE

34

1.1
Location : compileToPackage
Killed by : none
removed call to org/docx4j/openpackaging/parts/WordprocessingML/MainDocumentPart::addObject → NO_COVERAGE

36

1.1
Location : compileToPackage
Killed by : none
negated conditional → NO_COVERAGE

38

1.1
Location : compileToPackage
Killed by : none
removed call to org/docx4j/openpackaging/parts/WordprocessingML/MainDocumentPart::addObject → NO_COVERAGE

41

1.1
Location : compileToPackage
Killed by : none
replaced return value with null for pro/verron/officestamper/asciidoc/AsciiDocToDocx::compileToPackage → NO_COVERAGE

51

1.1
Location : createHeading
Killed by : none
removed call to org/docx4j/wml/PPrBase$PStyle::setVal → NO_COVERAGE

52

1.1
Location : createHeading
Killed by : none
removed call to org/docx4j/wml/PPr::setPStyle → NO_COVERAGE

53

1.1
Location : createHeading
Killed by : none
removed call to org/docx4j/wml/P::setPPr → NO_COVERAGE

66

1.1
Location : createHeading
Killed by : none
removed call to org/docx4j/wml/HpsMeasure::setVal → NO_COVERAGE

67

1.1
Location : createHeading
Killed by : none
removed call to org/docx4j/wml/RPr::setSz → NO_COVERAGE

68

1.1
Location : createHeading
Killed by : none
removed call to org/docx4j/wml/RPr::setSzCs → NO_COVERAGE

70

1.1
Location : createHeading
Killed by : none
removed call to pro/verron/officestamper/asciidoc/AsciiDocToDocx::addInlines → NO_COVERAGE

71

1.1
Location : createHeading
Killed by : none
replaced return value with null for pro/verron/officestamper/asciidoc/AsciiDocToDocx::createHeading → NO_COVERAGE

76

1.1
Location : createParagraph
Killed by : none
removed call to pro/verron/officestamper/asciidoc/AsciiDocToDocx::addInlines → NO_COVERAGE

77

1.1
Location : createParagraph
Killed by : none
replaced return value with null for pro/verron/officestamper/asciidoc/AsciiDocToDocx::createParagraph → NO_COVERAGE

82

1.1
Location : addInlines
Killed by : none
removed call to pro/verron/officestamper/asciidoc/AsciiDocToDocx::emitInline → NO_COVERAGE

95

1.1
Location : createTable
Killed by : none
removed call to pro/verron/officestamper/asciidoc/AsciiDocToDocx::addInlines → NO_COVERAGE

104

1.1
Location : createTable
Killed by : none
replaced return value with null for pro/verron/officestamper/asciidoc/AsciiDocToDocx::createTable → NO_COVERAGE

108

1.1
Location : emitInline
Killed by : none
negated conditional → NO_COVERAGE

110

1.1
Location : emitInline
Killed by : none
negated conditional → NO_COVERAGE

112

1.1
Location : emitInline
Killed by : none
removed call to org/docx4j/wml/Text::setValue → NO_COVERAGE

114

1.1
Location : emitInline
Killed by : none
removed call to org/docx4j/wml/Text::setSpace → NO_COVERAGE

117

1.1
Location : emitInline
Killed by : none
removed call to org/docx4j/wml/R::setRPr → NO_COVERAGE

123

1.1
Location : emitInline
Killed by : none
negated conditional → NO_COVERAGE

124

1.1
Location : emitInline
Killed by : none
negated conditional → NO_COVERAGE

125

1.1
Location : emitInline
Killed by : none
removed call to org/docx4j/wml/RPr::setB → NO_COVERAGE

127

1.1
Location : emitInline
Killed by : none
removed call to pro/verron/officestamper/asciidoc/AsciiDocToDocx::emitInline → NO_COVERAGE

132

1.1
Location : emitInline
Killed by : none
negated conditional → NO_COVERAGE

133

1.1
Location : emitInline
Killed by : none
negated conditional → NO_COVERAGE

134

1.1
Location : emitInline
Killed by : none
removed call to org/docx4j/wml/RPr::setI → NO_COVERAGE

136

1.1
Location : emitInline
Killed by : none
removed call to pro/verron/officestamper/asciidoc/AsciiDocToDocx::emitInline → NO_COVERAGE

141

1.1
Location : emitInline
Killed by : none
negated conditional → NO_COVERAGE

154

1.1
Location : deepCopy
Killed by : none
negated conditional → NO_COVERAGE

156

1.1
Location : deepCopy
Killed by : none
removed call to org/docx4j/wml/RPr::setB → NO_COVERAGE

158

1.1
Location : deepCopy
Killed by : none
negated conditional → NO_COVERAGE

160

1.1
Location : deepCopy
Killed by : none
removed call to org/docx4j/wml/RPr::setI → NO_COVERAGE

162

1.1
Location : deepCopy
Killed by : none
negated conditional → NO_COVERAGE

164

1.1
Location : deepCopy
Killed by : none
removed call to org/docx4j/wml/HpsMeasure::setVal → NO_COVERAGE

166

1.1
Location : deepCopy
Killed by : none
removed call to org/docx4j/wml/RPr::setSz → NO_COVERAGE

168

1.1
Location : deepCopy
Killed by : none
removed call to org/docx4j/wml/HpsMeasure::setVal → NO_COVERAGE

170

1.1
Location : deepCopy
Killed by : none
removed call to org/docx4j/wml/RPr::setSzCs → NO_COVERAGE

172

1.1
Location : deepCopy
Killed by : none
replaced return value with null for pro/verron/officestamper/asciidoc/AsciiDocToDocx::deepCopy → NO_COVERAGE

Active mutators

Tests examined


Report generated by PIT 1.22.0