Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/maven-settings.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<settings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
https://maven.apache.org/xsd/settings-1.0.0.xsd">
<servers>
Expand Down
11 changes: 9 additions & 2 deletions .github/workflows/quality_and_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,12 @@ jobs:
restore-keys: |
${{ runner.os }}-maven

- name: Build and Run Tests
run: mvn -B clean test
- name: Run Tests with Coverage Enforcement
run: mvn -B clean verify

- name: Upload JaCoCo Report (Optional)
if: always()
uses: actions/upload-artifact@v4
with:
name: jacoco-report
path: target/site/jacoco/index.html
7 changes: 7 additions & 0 deletions .idea/dictionaries/project.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

43 changes: 25 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
# Unified Data Parser and Report Generator

This project provides a **pluggable**, **extensible**, and **framework-agnostic** solution to convert structured input data formats like **XLSX**, **CSV**, **JSON**, and **ByteStream** into a **Unified Data Format**, which can then be used to generate reports in **PDF**, **HTML**, or **XML** using **JasperReports** or similar tools.

This project provides a **pluggable**, **extensible**, and **framework-agnostic** solution to convert structured input
data formats like **XLSX**, **CSV**, **JSON**, and **ByteStream** into a **Unified Data Format**, which can then be used
to generate reports in **PDF**, **HTML**, or **XML** using **JasperReports** or similar tools.

### 📦 Package & Build Status

[![Maven Package](https://img.shields.io/badge/github--package-0.0.1--SNAPSHOT-blue)](https://github.com/docflex/UnifiedReporter/packages)


[![Build & Tests](https://github.com/docflex/UnifiedReporter/actions/workflows/build.yml/badge.svg)](https://github.com/docflex/UnifiedReporter/actions/workflows/build.yml)




## Architecture Overview

### 🔁 Proposed Flow & Design
Expand Down Expand Up @@ -59,7 +56,7 @@ This project provides a **pluggable**, **extensible**, and **framework-agnostic*
## 🏗 Project Modules

| Module | Purpose |
| ------------------ | --------------------------------------------- |
|--------------------|-----------------------------------------------|
| `formats` | Parsers for CSV, XLSX, JSON, ByteStream |
| `common` | Error codes, exceptions, and utility classes |
| `jasper-engine` | Report rendering using JasperReports |
Expand All @@ -73,12 +70,14 @@ This project provides a **pluggable**, **extensible**, and **framework-agnostic*
### ✅ As a **Library**

```java
try (InputStream is = new FileInputStream("/path/to/file.xlsx")) {
UnifiedFormat format = new XLSXFormat(is, "MySource");
List<Map<String, Object>> data = format.getDataRows();

// pass to Jasper engine or use for validation
documentCreator.generatePdf(data, templatePath);
try(InputStream is = new FileInputStream("/path/to/file.xlsx")){
UnifiedFormat format = new XLSXFormat(is, "MySource");
List<Map<String, Object>> data = format.getDataRows();

// pass to Jasper engine or use for validation
documentCreator.

generatePdf(data, templatePath);
}
```

Expand All @@ -97,8 +96,15 @@ try (InputStream is = new FileInputStream("/path/to/file.xlsx")) {
```java
public interface UnifiedFormat {
List<Map<String, Object>> getDataRows();
default List<String> getColumnOrder() { return null; }
default String getSourceName() { return "unnamed"; }

default List<String> getColumnOrder() {
return null;
}

default String getSourceName() {
return "unnamed";
}

default void validateFields(List<String> requiredColumns) throws FormatException;
}
```
Expand Down Expand Up @@ -136,6 +142,7 @@ Unit tests are written using **JUnit 5** with in-memory test files for:
* Field validation

```java

@Test
void testValidXlsxParsing() {
InputStream inputStream = getClass().getResourceAsStream("/valid.xlsx");
Expand All @@ -154,9 +161,9 @@ If required, you can introduce fluent builders for complex configuration like:

```java
UnifiedFormat format = XLSXFormat.builder()
.withInputStream(stream)
.withSourceName("Sheet1")
.build();
.withInputStream(stream)
.withSourceName("Sheet1")
.build();
```

---
Expand Down
90 changes: 71 additions & 19 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">

Expand Down Expand Up @@ -49,6 +49,10 @@
<jasperreports.version>6.21.0</jasperreports.version>
<opencsv.version>5.9</opencsv.version>
<maven.build.encoding>UTF-8</maven.build.encoding>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>

<repositories>
Expand All @@ -59,18 +63,18 @@
</repositories>

<dependencies>
<!-- Spring Core Context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>

<!-- JasperReports -->
<dependency>
<groupId>net.sf.jasperreports</groupId>
<artifactId>jasperreports</artifactId>
<version>${jasperreports.version}</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>

<!-- OpenCSV -->
Expand All @@ -87,11 +91,19 @@
<version>5.4.0</version>
</dependency>

<!-- Aspects and Annotations -->
<!-- For Excel reading -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>6.1.6</version>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.4.0</version>
</dependency>

<!-- For Unit Testing -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>


Expand All @@ -102,13 +114,6 @@
<version>6.2.8</version>
</dependency>

<!-- SLF4J API -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.13</version>
</dependency>

<!-- Log4j 2 implementation for SLF4J -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
Expand Down Expand Up @@ -146,6 +151,14 @@
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>


<!-- For Safe Extraction -->
<dependency>
<groupId>commons-io</groupId>
Expand All @@ -156,6 +169,45 @@

<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.11</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>verify</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
<execution>
<id>check</id>
<goals>
<goal>check</goal>
</goals>
<configuration>
<rules>
<rule>
<element>BUNDLE</element>
<limits>
<limit>
<counter>LINE</counter>
<value>COVEREDRATIO</value>
<minimum>0.80</minimum>
</limit>
</limits>
</rule>
</rules>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.3.1</version>
Expand Down
107 changes: 107 additions & 0 deletions src/main/java/org/unified/ReportGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package org.unified;

import lombok.extern.slf4j.Slf4j;
import net.sf.jasperreports.engine.JasperReport;
import org.unified.common.enums.FileExportFormat;
import org.unified.common.exceptions.ReportException;
import org.unified.formats.UnifiedFormat;
import org.unified.utils.ReportExporter;
import org.unified.utils.ReportValidators;

import java.io.InputStream;
import java.util.Map;

/**
* Central class responsible for generating reports from structured data and Jasper templates.
* <p>
* This utility orchestrates the validation of input data, compilation/loading of report templates,
* and final export of reports into multiple formats (PDF, XLSX, HTML, XML).
* <p>
* It abstracts the complexity behind parsing data, compiling templates, and exporting formats,
* providing a single entry point for report generation.
*
* <h2>Supported Input</h2>
* This class currently expects a single data source:
* <ul>
* <li>{@link UnifiedFormat} object - a normalized wrapper over parsed tabular input (e.g., XLSX, CSV)</li>
* </ul>
* Other input types like {@code InputStream} or {@code byte[]} are not yet supported for dynamic format inference.
*
* <h2>Usage Example</h2>
* <pre>{@code
* InputStream templateStream = getClass().getResourceAsStream("/invoice_template.jasper");
* Map<String, Object> parameters = Map.of("CompanyName", "ACME Corp.");
*
* UnifiedFormat file = new XLSXFormat(myExcelStream, "InvoiceData");
*
* byte[] pdfBytes = ReportGenerator.generateReport(
* file,
* templateStream,
* parameters,
* FileExportFormat.PDF
* );
*
* Files.write(Paths.get("invoice.pdf"), pdfBytes);
* }</pre>
*
* <h2>Exceptions</h2>
* This method throws {@link ReportException} for known validation or export issues,
* and wraps unexpected errors in a {@link RuntimeException}.
*
* @author Unified
*/
@Slf4j
public class ReportGenerator {

/**
* Generates a Jasper report from structured tabular input using the specified template and export format.
* <p>
* This method follows a 3-step process:
* <ol>
* <li>Validates and extracts data from the input file</li>
* <li>Validates and compiles (or loads) the Jasper template</li>
* <li>Exports the filled report into the requested output format</li>
* </ol>
*
* @param file A valid {@link UnifiedFormat} instance (e.g., XLSXFormat)
* @param jasperReportTemplateStream Input stream of the Jasper template (.jasper or .jrxml)
* @param additionalReportParameters Additional parameters for the Jasper report (e.g., metadata, dynamic values)
* @param exportFormat Desired file export format (PDF, XLSX, HTML, XML)
* @return A byte array representing the generated report file
* @throws ReportException if the input is invalid, template fails to load, or export fails
* @throws RuntimeException if an unexpected error occurs during generation
*/
public static byte[] generateReport(
Object file,
InputStream jasperReportTemplateStream,
Map<String, Object> additionalReportParameters,
FileExportFormat exportFormat
) {
long startTime = System.nanoTime();

try {
UnifiedFormat inputFile = ReportValidators.validateInputFile(file);

JasperReport reportTemplate = ReportValidators.validateJasperReport(jasperReportTemplateStream);

byte[] output = ReportExporter.export(
inputFile.getDataRows(),
reportTemplate,
additionalReportParameters,
exportFormat
);

long endTime = System.nanoTime();
long durationMillis = (endTime - startTime) / 1_000_000;

log.info("✅ Report generated successfully in {} ms (Format: {})", durationMillis, exportFormat);
return output;

} catch (ReportException rex) {
throw rex;
} catch (Exception e) {
log.error("❌ Unexpected error during report generation", e);
throw new RuntimeException("Report generation failed", e);
}
}
}
Loading