-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add PowerPoint and Excel stamping functionality, CLI improvements
Introduce `PowerPointStamper` and `ExcelContext` to enable processing and stamping of PowerPoint and Excel files. Implement CLI enhancements for dynamic stamper type selection and file context handling. The change enables better extensibility and user interaction, while streamlining support for additional file formats in the stamping process.
- Loading branch information
1 parent
c0c34b5
commit 8e046c8
Showing
8 changed files
with
337 additions
and
64 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
52 changes: 0 additions & 52 deletions
52
examples/src/main/java/pro/verron/officestamper/Examples.java
This file was deleted.
Oops, something went wrong.
114 changes: 114 additions & 0 deletions
114
examples/src/main/java/pro/verron/officestamper/ExcelContext.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
package pro.verron.officestamper; | ||
|
||
import org.docx4j.openpackaging.exceptions.Docx4JException; | ||
import org.docx4j.openpackaging.packages.SpreadsheetMLPackage; | ||
import org.docx4j.openpackaging.parts.SpreadsheetML.WorkbookPart; | ||
import org.docx4j.openpackaging.parts.SpreadsheetML.WorksheetPart; | ||
import org.docx4j.openpackaging.parts.relationships.RelationshipsPart; | ||
import org.xlsx4j.org.apache.poi.ss.usermodel.DataFormatter; | ||
import org.xlsx4j.sml.Row; | ||
import org.xlsx4j.sml.Sheet; | ||
import org.xlsx4j.sml.Workbook; | ||
import org.xlsx4j.sml.Worksheet; | ||
import pro.verron.officestamper.api.OfficeStamperException; | ||
|
||
import java.io.InputStream; | ||
import java.util.*; | ||
|
||
import static java.util.Collections.emptyList; | ||
|
||
public class ExcelContext | ||
extends AbstractMap<String, List<Map<String, String>>> { | ||
|
||
public static final DataFormatter formatter = new DataFormatter(); | ||
private final Map<String, List<Map<String, String>>> source; | ||
|
||
public ExcelContext(SpreadsheetMLPackage spreadsheetPackage) { | ||
var workbookPart = spreadsheetPackage.getWorkbookPart(); | ||
var workbook = getWorkbook(workbookPart); | ||
var allSheets = workbook.getSheets(); | ||
var sheets = allSheets.getSheet(); | ||
|
||
var relationshipsPart = workbookPart.getRelationshipsPart(); | ||
|
||
var excel = new TreeMap<String, List<Map<String, String>>>(); | ||
|
||
for (var sheet : sheets) { | ||
var worksheetPart = extractWorksheetPart(sheet, relationshipsPart); | ||
var worksheet = extractWorksheet(worksheetPart); | ||
var sheetDate = worksheet.getSheetData(); | ||
var rows = sheetDate.getRow(); | ||
excel.put(sheet.getName(), extractRecords(rows)); | ||
} | ||
|
||
source = Collections.unmodifiableMap(excel); | ||
} | ||
|
||
private static Workbook getWorkbook(WorkbookPart workbookPart) { | ||
try { | ||
return workbookPart.getContents(); | ||
} catch (Docx4JException e) { | ||
throw new OfficeStamperException(e); | ||
} | ||
} | ||
|
||
private WorksheetPart extractWorksheetPart(Sheet sheet, RelationshipsPart relationshipsPart) { | ||
return (WorksheetPart) relationshipsPart.getPart(sheet.getId()); | ||
} | ||
|
||
private Worksheet extractWorksheet(WorksheetPart worksheetPart) { | ||
try { | ||
return worksheetPart.getContents(); | ||
} catch (Docx4JException e) { | ||
throw new OfficeStamperException(e); | ||
} | ||
} | ||
|
||
private List<Map<String, String>> extractRecords(List<Row> rows) { | ||
if (rows.isEmpty()) return emptyList(); | ||
var headers = extractHeaders(rows.getFirst()); | ||
return extractRecords(headers, rows.subList(1, rows.size())); | ||
} | ||
|
||
private List<String> extractHeaders(Row row) { | ||
return row.getC() | ||
.stream() | ||
.map(formatter::formatCellValue) | ||
.toList(); | ||
} | ||
|
||
private List<Map<String, String>> extractRecords(List<String> headers, List<Row> rows) { | ||
List<Map<String, String>> list = new ArrayList<>(); | ||
for (var row : rows) { | ||
Map<String, String> rec = new TreeMap<>(); | ||
for (var i = 0; i < headers.size(); i++) { | ||
rec.put(headers.get(i), formatCellValueAt(row, i)); | ||
} | ||
list.add(rec); | ||
} | ||
return list; | ||
} | ||
|
||
private static String formatCellValueAt(Row row, int i) { | ||
var cells = row.getC(); | ||
if (i >= cells.size()) return ""; | ||
return formatter.formatCellValue(cells.get(i)); | ||
} | ||
|
||
public static Object from(InputStream inputStream) { | ||
try { | ||
return from(SpreadsheetMLPackage.load(inputStream)); | ||
} catch (Docx4JException e) { | ||
throw new OfficeStamperException(e); | ||
} | ||
} | ||
|
||
private static Object from(SpreadsheetMLPackage spreadsheetPackage) { | ||
return new ExcelContext(spreadsheetPackage); | ||
} | ||
|
||
@Override | ||
public Set<Entry<String, List<Map<String, String>>>> entrySet() { | ||
return source.entrySet(); | ||
} | ||
} |
148 changes: 138 additions & 10 deletions
148
examples/src/main/java/pro/verron/officestamper/Main.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,28 +1,156 @@ | ||
package pro.verron.officestamper; | ||
|
||
|
||
import picocli.CommandLine; | ||
import picocli.CommandLine.Command; | ||
import picocli.CommandLine.Option; | ||
import pro.verron.officestamper.api.OfficeStamperException; | ||
|
||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.io.OutputStream; | ||
import java.nio.file.Files; | ||
import java.nio.file.Path; | ||
import java.util.TreeMap; | ||
import java.util.logging.Level; | ||
import java.util.logging.Logger; | ||
|
||
import static pro.verron.officestamper.Examples.stampDiagnostic; | ||
import static java.nio.file.Files.newOutputStream; | ||
|
||
public class Main { | ||
@Command(name = "officestamper", mixinStandardHelpOptions = true, description = "Office Stamper CLI tool") | ||
public class Main | ||
implements Runnable { | ||
|
||
private static final Logger logger = Utils.getLogger(); | ||
|
||
public static void main(String[] args) | ||
throws Exception { | ||
stampDiagnostic(createOutStream("Diagnostic-")); | ||
@Option(names = {"-i", "--input-file"}, | ||
required = true, | ||
description = "Input file path (csv, properties, html, xml, json, excel) or a keyword for documented data" | ||
+ " sources") private String inputFile; | ||
|
||
@Option(names = {"-t", "--template-file"}, | ||
required = true, | ||
description = "Template file path or a keyword for documented template packages") private String templateFile; | ||
|
||
@Option(names = {"-o", "--output-path"}, | ||
defaultValue = "default-output.docx", | ||
description = "Output file path") private String outputPath; | ||
|
||
@Option(names = {"-s", "--stamper"}, | ||
defaultValue = "diagnostic", | ||
description = "Stamper type (diagnostic, powerpoint)") private String stamperType; | ||
|
||
public static void main(String[] args) { | ||
var main = new Main(); | ||
int exitCode = new CommandLine(main).execute(args); | ||
System.exit(exitCode); | ||
} | ||
|
||
@Override | ||
public void run() { | ||
if (inputFile == null || templateFile == null) { | ||
logger.log(Level.SEVERE, "Input file and template file must be provided"); | ||
return; | ||
} | ||
|
||
stamperType = stamperType.toLowerCase(); | ||
|
||
logger.log(Level.INFO, "Input File: {}", inputFile); | ||
logger.log(Level.INFO, "Template File: {}", templateFile); | ||
logger.log(Level.INFO, "Output Path: {}", outputPath); | ||
logger.log(Level.INFO, "Stamper Type: {}", stamperType); | ||
|
||
final var context = switch (inputFile) { | ||
case "diagnostic" -> createDiagnosticContext(); | ||
default -> contextualise(Path.of(inputFile)); | ||
}; | ||
|
||
final var templateStream = switch (templateFile) { | ||
case "diagnostic" -> loadDiagnosticTemplate(); | ||
default -> streamFile(Path.of(templateFile)); | ||
}; | ||
|
||
final var outputStream = createOutputStream(Path.of(outputPath)); | ||
|
||
final var stamper = switch (stamperType) { | ||
case "word" -> new WordStamper(); | ||
case "powerpoint" -> new PowerPointStamper(); | ||
default -> throw new OfficeStamperException("Invalid stamper type: " + stamperType); | ||
}; | ||
|
||
stamper.stamp(context, templateStream, outputStream); | ||
} | ||
|
||
private static Object createDiagnosticContext() { | ||
logger.info(""" | ||
Create a context with: \ | ||
system environment variables, \ | ||
jvm properties, \ | ||
and user preferences"""); | ||
var diagnosticMaker = new Diagnostic(); | ||
var map = new TreeMap<String, Object>(); | ||
map.put("reportDate", diagnosticMaker.date()); | ||
map.put("reportUser", diagnosticMaker.user()); | ||
map.put("environment", diagnosticMaker.environmentVariables()); | ||
map.put("properties", diagnosticMaker.jvmProperties()); | ||
map.put("preferences", diagnosticMaker.userPreferences()); | ||
return map; | ||
} | ||
|
||
private static OutputStream createOutStream(String prefix) | ||
throws IOException { | ||
var outputPath = Files.createTempFile(prefix, ".docx"); | ||
logger.log(Level.INFO, "Stamping to file: ", outputPath); | ||
return Files.newOutputStream(outputPath); | ||
private Object contextualise(Path path) { | ||
String fileName = path.getFileName() | ||
.toString() | ||
.toLowerCase(); | ||
if (fileName.endsWith(".csv")) return processCsv(path); | ||
if (fileName.endsWith(".properties")) return processProperties(path); | ||
if (fileName.endsWith(".html") || fileName.endsWith(".xml")) return processXmlOrHtml(path); | ||
if (fileName.endsWith(".json")) return processJson(path); | ||
if (fileName.endsWith(".xlsx")) return processExcel(path); | ||
throw new OfficeStamperException("Unsupported file type: " + fileName); | ||
} | ||
|
||
private static InputStream loadDiagnosticTemplate() { | ||
logger.info("Load the internally packaged 'Diagnostic.docx' template resource"); | ||
return Utils.streamResource("Diagnostic.docx"); | ||
} | ||
|
||
private static InputStream streamFile(Path path) { | ||
try { | ||
return Files.newInputStream(path); | ||
} catch (IOException e) { | ||
throw new OfficeStamperException(e); | ||
} | ||
} | ||
|
||
private OutputStream createOutputStream(Path path) { | ||
try { | ||
return newOutputStream(path); | ||
} catch (IOException e) { | ||
throw new OfficeStamperException(e); | ||
} | ||
} | ||
|
||
private Object processCsv(Path path) { | ||
throw new OfficeStamperException("Not yet implemented."); | ||
} | ||
|
||
private Object processProperties(Path path) { | ||
throw new OfficeStamperException("Not yet implemented."); | ||
} | ||
|
||
private Object processXmlOrHtml(Path path) { | ||
throw new OfficeStamperException("Not yet implemented."); | ||
} | ||
|
||
private Object processJson(Path path) { | ||
throw new OfficeStamperException("Not yet implemented."); | ||
} | ||
|
||
private Object processExcel(Path path) { | ||
try { | ||
return ExcelContext.from(Files.newInputStream(path)); | ||
} catch (IOException e) { | ||
throw new OfficeStamperException(e); | ||
} | ||
} | ||
} |
Oops, something went wrong.