ExcelContext.java

1
package pro.verron.officestamper;
2
3
import org.docx4j.openpackaging.exceptions.Docx4JException;
4
import org.docx4j.openpackaging.packages.SpreadsheetMLPackage;
5
import org.docx4j.openpackaging.parts.SpreadsheetML.WorkbookPart;
6
import org.docx4j.openpackaging.parts.SpreadsheetML.WorksheetPart;
7
import org.docx4j.openpackaging.parts.relationships.RelationshipsPart;
8
import org.jspecify.annotations.NonNull;
9
import org.xlsx4j.org.apache.poi.ss.usermodel.DataFormatter;
10
import org.xlsx4j.sml.Row;
11
import org.xlsx4j.sml.Sheet;
12
import org.xlsx4j.sml.Workbook;
13
import org.xlsx4j.sml.Worksheet;
14
import pro.verron.officestamper.api.OfficeStamperException;
15
16
import java.io.InputStream;
17
import java.util.*;
18
19
import static java.util.Collections.emptyList;
20
21
/// Context for Excel-based data.
22
public class ExcelContext
23
        extends AbstractMap<String, List<Map<String, String>>> {
24
25
    /// Data formatter for Excel cells.
26
    public static final DataFormatter formatter = new DataFormatter();
27
    private final Map<String, List<Map<String, String>>> source;
28
29
    /// Constructs an ExcelContext from the specified spreadsheet package.
30
    ///
31
    /// @param spreadsheetPackage the spreadsheet package
32
    public ExcelContext(SpreadsheetMLPackage spreadsheetPackage) {
33
        var workbookPart = spreadsheetPackage.getWorkbookPart();
34
        var workbook = getWorkbook(workbookPart);
35
        var allSheets = workbook.getSheets();
36
        var sheets = allSheets.getSheet();
37
38
        var relationshipsPart = workbookPart.getRelationshipsPart();
39
40
        var excel = new TreeMap<String, List<Map<String, String>>>();
41
42
        for (var sheet : sheets) {
43
            var worksheetPart = extractWorksheetPart(sheet, relationshipsPart);
44
            var worksheet = extractWorksheet(worksheetPart);
45
            var sheetDate = worksheet.getSheetData();
46
            var rows = sheetDate.getRow();
47
            excel.put(sheet.getName(), extractRecords(rows));
48
        }
49
50
        source = Collections.unmodifiableMap(excel);
51
    }
52
53
    private static Workbook getWorkbook(WorkbookPart workbookPart) {
54
        try {
55 1 1. getWorkbook : replaced return value with null for pro/verron/officestamper/ExcelContext::getWorkbook → NO_COVERAGE
            return workbookPart.getContents();
56
        } catch (Docx4JException e) {
57
            throw new OfficeStamperException(e);
58
        }
59
    }
60
61
    private WorksheetPart extractWorksheetPart(Sheet sheet, RelationshipsPart relationshipsPart) {
62 1 1. extractWorksheetPart : replaced return value with null for pro/verron/officestamper/ExcelContext::extractWorksheetPart → NO_COVERAGE
        return (WorksheetPart) relationshipsPart.getPart(sheet.getId());
63
    }
64
65
    private Worksheet extractWorksheet(WorksheetPart worksheetPart) {
66
        try {
67 1 1. extractWorksheet : replaced return value with null for pro/verron/officestamper/ExcelContext::extractWorksheet → NO_COVERAGE
            return worksheetPart.getContents();
68
        } catch (Docx4JException e) {
69
            throw new OfficeStamperException(e);
70
        }
71
    }
72
73
    private List<Map<String, String>> extractRecords(List<Row> rows) {
74 1 1. extractRecords : negated conditional → NO_COVERAGE
        if (rows.isEmpty()) return emptyList();
75
        var headers = extractHeaders(rows.getFirst());
76 1 1. extractRecords : replaced return value with Collections.emptyList for pro/verron/officestamper/ExcelContext::extractRecords → NO_COVERAGE
        return extractRecords(headers, rows.subList(1, rows.size()));
77
    }
78
79
    private List<String> extractHeaders(Row row) {
80 1 1. extractHeaders : replaced return value with Collections.emptyList for pro/verron/officestamper/ExcelContext::extractHeaders → NO_COVERAGE
        return row.getC()
81
                  .stream()
82
                  .map(formatter::formatCellValue)
83
                  .toList();
84
    }
85
86
    private List<Map<String, String>> extractRecords(List<String> headers, List<Row> rows) {
87
        List<Map<String, String>> list = new ArrayList<>();
88
        for (var row : rows) {
89
            Map<String, String> rec = new TreeMap<>();
90 2 1. extractRecords : changed conditional boundary → NO_COVERAGE
2. extractRecords : negated conditional → NO_COVERAGE
            for (var i = 0; i < headers.size(); i++) {
91
                rec.put(headers.get(i), formatCellValueAt(row, i));
92
            }
93
            list.add(rec);
94
        }
95 1 1. extractRecords : replaced return value with Collections.emptyList for pro/verron/officestamper/ExcelContext::extractRecords → NO_COVERAGE
        return list;
96
    }
97
98
    private static String formatCellValueAt(Row row, int i) {
99
        var cells = row.getC();
100 2 1. formatCellValueAt : changed conditional boundary → NO_COVERAGE
2. formatCellValueAt : negated conditional → NO_COVERAGE
        if (i >= cells.size()) return "";
101 1 1. formatCellValueAt : replaced return value with "" for pro/verron/officestamper/ExcelContext::formatCellValueAt → NO_COVERAGE
        return formatter.formatCellValue(cells.get(i));
102
    }
103
104
    /// Creates an ExcelContext from the specified input stream.
105
    ///
106
    /// @param inputStream the input stream
107
    ///
108
    /// @return the ExcelContext
109
    public static Object from(InputStream inputStream) {
110
        try {
111 1 1. from : replaced return value with null for pro/verron/officestamper/ExcelContext::from → NO_COVERAGE
            return from(SpreadsheetMLPackage.load(inputStream));
112
        } catch (Docx4JException e) {
113
            throw new OfficeStamperException(e);
114
        }
115
    }
116
117
    private static Object from(SpreadsheetMLPackage spreadsheetPackage) {
118 1 1. from : replaced return value with null for pro/verron/officestamper/ExcelContext::from → NO_COVERAGE
        return new ExcelContext(spreadsheetPackage);
119
    }
120
121
    @Override
122
    public @NonNull Set<Entry<String, List<Map<String, String>>>> entrySet() {
123 1 1. entrySet : replaced return value with Collections.emptySet for pro/verron/officestamper/ExcelContext::entrySet → NO_COVERAGE
        return source.entrySet();
124
    }
125
126
    @Override
127
    public boolean equals(Object o) {
128 2 1. equals : replaced boolean return with true for pro/verron/officestamper/ExcelContext::equals → NO_COVERAGE
2. equals : replaced boolean return with false for pro/verron/officestamper/ExcelContext::equals → NO_COVERAGE
        return super.equals(o);
129
    }
130
131
    @Override
132
    public int hashCode() {
133 1 1. hashCode : replaced int return with 0 for pro/verron/officestamper/ExcelContext::hashCode → NO_COVERAGE
        return super.hashCode();
134
    }
135
}

Mutations

55

1.1
Location : getWorkbook
Killed by : none
replaced return value with null for pro/verron/officestamper/ExcelContext::getWorkbook → NO_COVERAGE

62

1.1
Location : extractWorksheetPart
Killed by : none
replaced return value with null for pro/verron/officestamper/ExcelContext::extractWorksheetPart → NO_COVERAGE

67

1.1
Location : extractWorksheet
Killed by : none
replaced return value with null for pro/verron/officestamper/ExcelContext::extractWorksheet → NO_COVERAGE

74

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

76

1.1
Location : extractRecords
Killed by : none
replaced return value with Collections.emptyList for pro/verron/officestamper/ExcelContext::extractRecords → NO_COVERAGE

80

1.1
Location : extractHeaders
Killed by : none
replaced return value with Collections.emptyList for pro/verron/officestamper/ExcelContext::extractHeaders → NO_COVERAGE

90

1.1
Location : extractRecords
Killed by : none
changed conditional boundary → NO_COVERAGE

2.2
Location : extractRecords
Killed by : none
negated conditional → NO_COVERAGE

95

1.1
Location : extractRecords
Killed by : none
replaced return value with Collections.emptyList for pro/verron/officestamper/ExcelContext::extractRecords → NO_COVERAGE

100

1.1
Location : formatCellValueAt
Killed by : none
changed conditional boundary → NO_COVERAGE

2.2
Location : formatCellValueAt
Killed by : none
negated conditional → NO_COVERAGE

101

1.1
Location : formatCellValueAt
Killed by : none
replaced return value with "" for pro/verron/officestamper/ExcelContext::formatCellValueAt → NO_COVERAGE

111

1.1
Location : from
Killed by : none
replaced return value with null for pro/verron/officestamper/ExcelContext::from → NO_COVERAGE

118

1.1
Location : from
Killed by : none
replaced return value with null for pro/verron/officestamper/ExcelContext::from → NO_COVERAGE

123

1.1
Location : entrySet
Killed by : none
replaced return value with Collections.emptySet for pro/verron/officestamper/ExcelContext::entrySet → NO_COVERAGE

128

1.1
Location : equals
Killed by : none
replaced boolean return with true for pro/verron/officestamper/ExcelContext::equals → NO_COVERAGE

2.2
Location : equals
Killed by : none
replaced boolean return with false for pro/verron/officestamper/ExcelContext::equals → NO_COVERAGE

133

1.1
Location : hashCode
Killed by : none
replaced int return with 0 for pro/verron/officestamper/ExcelContext::hashCode → NO_COVERAGE

Active mutators

Tests examined


Report generated by PIT 1.22.0