Skip to content

Commit 958838d

Browse files
authored
Allow deleting rows in xls files after expanding template (#363)
* Allow deleting rows in xls files after expanding template * Update user guide and release notes
1 parent e2462ab commit 958838d

2 files changed

Lines changed: 80 additions & 0 deletions

File tree

core/src/main/java/com/devonfw/tools/solicitor/writer/xls/ExcelWriter.java

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,8 @@ public void writeReport(WriterConfig config, String target, Map<String, DataTabl
414414
iterateFromCell(wb, cell, dataTable, label, styleCache);
415415
}
416416

417+
findAndRemoveRowsToBeDeleted(wb);
418+
417419
// force reevaluation of all formulas
418420
wb.setForceFormulaRecalculation(true);
419421

@@ -431,6 +433,81 @@ public void writeReport(WriterConfig config, String target, Map<String, DataTabl
431433

432434
}
433435

436+
/**
437+
* Find any rows with cells that contain "_row_to_be_deleted_" and remove them from the workbook. This can be used to
438+
* insert rows into sheets which might act as anchors to range evaluations and delete them at the end of template
439+
* expansion.
440+
*
441+
* @param wb the workbook to process
442+
*/
443+
private void findAndRemoveRowsToBeDeleted(Workbook wb) {
444+
445+
outer: while (true) {
446+
for (Sheet sheet : wb) {
447+
for (Row row : sheet) {
448+
for (Cell cell : row) {
449+
if (cell.getCellType() == CellType.STRING && "_row_to_be_deleted_".equals(cell.getStringCellValue())) {
450+
LOG.debug("Removing row " + row.getRowNum() + " in sheet '" + sheet.getSheetName()
451+
+ "' which was marked for deletion.");
452+
removeRowOfCell(cell);
453+
continue outer;
454+
}
455+
}
456+
}
457+
}
458+
break;
459+
}
460+
}
461+
462+
/**
463+
* Removes the row of the given cell from the workbook.
464+
*
465+
* @param cell the cell whose row should be removed
466+
*/
467+
private static void removeRowOfCell(Cell cell) {
468+
469+
Sheet sheet = cell.getSheet();
470+
int lastRowNum = cell.getSheet().getLastRowNum();
471+
Row row = cell.getRow();
472+
int rowIndex = row.getRowNum();
473+
474+
// 1) Remove merged regions that intersect the row
475+
removeMergedRegionsInRow(sheet, rowIndex);
476+
477+
// 2) Remove row cells (optional but safer to avoid leftovers)
478+
if (row != null) {
479+
for (int cn = row.getFirstCellNum(); cn < row.getLastCellNum(); cn++) {
480+
Cell cellToBeRemoved = row.getCell(cn);
481+
if (cellToBeRemoved != null)
482+
row.removeCell(cellToBeRemoved);
483+
}
484+
}
485+
if (row != null)
486+
sheet.removeRow(row);
487+
488+
// 3) Shift rows up if not deleting the last row
489+
if (rowIndex < lastRowNum) {
490+
sheet.shiftRows(rowIndex + 1, lastRowNum, -1, true, false);
491+
}
492+
}
493+
494+
/**
495+
* Removes all merged regions in the given sheet that intersect the given row index. This is necessary to avoid
496+
* exceptions when shifting rows up after removing a row that is part of a merged region.
497+
*
498+
* @param sheet the sheet to process
499+
* @param rowIndex the index of the row for which intersecting merged regions should be removed
500+
*/
501+
private static void removeMergedRegionsInRow(Sheet sheet, int rowIndex) {
502+
503+
for (int i = sheet.getNumMergedRegions() - 1; i >= 0; i--) {
504+
CellRangeAddress region = sheet.getMergedRegion(i);
505+
if (region.getFirstRow() <= rowIndex && region.getLastRow() >= rowIndex) {
506+
sheet.removeMergedRegion(i);
507+
}
508+
}
509+
}
510+
434511
/**
435512
* Protects/locks all sheets in the workbook if the value given by {@link WriterConfig#getProtectionPassword()} is not
436513
* <code>null</code> or empty. If the configured value is literal "RANDOM", a random password will be generated and

documentation/master-solicitor.asciidoc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1311,6 +1311,8 @@ Within each row which was copied in the previous step the templating logic searc
13111311
NOTE: Performance hint: Using Hyperlinks in "template rows" might result in degraded performance when Solicitor processes the XLS template. This is
13121312
due to the fact that copying cells that are marked to be hyperlinks might be slow if the XLS file contains a lot of hyperlinks. This performance issue might be avoided if hyperlinks are represented as formulas like `=HYPERLINK("https://www.google.com")` or `=HYPERLINK("$someUrlPlaceholder$")` instead of defining a hyperlink via the "Link" context menu entry of Excel.
13131313

1314+
===== Optional deletion of rows after template expansion
1315+
After the template has been expanded the ExcelWriter will look for any cells in the Excel-Sheet which contain the text `\_row_to_be_deleted_`. All rows containing such cells will be deleted.
13141316

13151317
===== Representation of Diff Information
13161318
In case that a difference processing (new vs. old model data) was done this will be represented
@@ -2098,6 +2100,7 @@ creating a rule template file, defining the decision table as XLS or CSV file an
20982100
== Release Notes
20992101
Changes in 1.49.0::
21002102
* https://github.com/devonfw/solicitor/issues/360: Allow alignment of groupId, artifactId and version to PackageURL when reading component lists with Readers. See <<Deriving groupId, ArtifactId and Version from the PackageURL>>.
2103+
* https://github.com/devonfw/solicitor/pull/363: Allow to delete rows in XLS files generated by the ExcelWriter. See <<Optional deletion of rows after template expansion>>.
21012104

21022105
Changes in 1.48.0::
21032106
* https://github.com/devonfw/solicitor/pull/357: Refactor model import logic.

0 commit comments

Comments
 (0)