| 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 |
|
| 62 |
1.1 |
|
| 67 |
1.1 |
|
| 74 |
1.1 |
|
| 76 |
1.1 |
|
| 80 |
1.1 |
|
| 90 |
1.1 2.2 |
|
| 95 |
1.1 |
|
| 100 |
1.1 2.2 |
|
| 101 |
1.1 |
|
| 111 |
1.1 |
|
| 118 |
1.1 |
|
| 123 |
1.1 |
|
| 128 |
1.1 2.2 |
|
| 133 |
1.1 |