Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhanced to read and write formatted dates and formulas. #48

Open
wants to merge 21 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
92a2ea6
Enhanced to read and write formatted dates and formulas.
Harsha0211 Oct 2, 2023
9327ba4
Issue-27 fix
Harsha0211 Oct 3, 2023
bfdb650
minor variable name change
Harsha0211 Oct 3, 2023
1a44805
Enhanced to read and write formatted dates and formulas-modified acc.…
Harsha0211 Oct 3, 2023
41ff52e
minor code refactor
Harsha0211 Oct 4, 2023
00efbc6
executed DateTest.java->sample-files changed
Harsha0211 Oct 4, 2023
78995e4
Enhanced to write new sheet to existing file without clearing prev. s…
Harsha0211 Oct 6, 2023
8944d87
Revert "Enhanced to write new sheet to existing file without clearing…
Harsha0211 Oct 6, 2023
ee9b2bf
[TestCase Update] updated test cases with Date format checks
Harsha0211 Oct 10, 2023
114501c
[SheetColumn] removed 'isFormated' field from sheet column
Harsha0211 Oct 10, 2023
1d48dce
[DateFormat] added support for localDate and Date
Harsha0211 Oct 10, 2023
6cd9cbb
[RMVD-Formula part] Removed all the formula related code
Harsha0211 Oct 10, 2023
b45ad03
[Final keyword] Added final keyword at necessary lines
Harsha0211 Oct 10, 2023
fdfb8a5
[LocalDate read] Modified code to support LocalDate
Harsha0211 Oct 11, 2023
0819f0b
[Test Cases] Added and modified files for Dates TestCases
Harsha0211 Oct 11, 2023
6037107
[Finla keyword]Added final keyword at necessary lines
Harsha0211 Oct 11, 2023
fba7611
[Unused imports] Removed unused imports
Harsha0211 Oct 11, 2023
851936e
[Revert] reverted sheet and rowListeners file from develop branch
Harsha0211 Oct 11, 2023
2e44848
[Code refactor] Replaced SheetColumn,SSReader,Beans,XlsxReader files …
Harsha0211 Oct 11, 2023
5f44494
[Code Refactor] Added minor changes to replaced files in prev commit
Harsha0211 Oct 11, 2023
d59ac27
[Final] Added final keyword
Harsha0211 Oct 11, 2023
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
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public interface RowListener<T> {
* This method will be called after every row by the {@link SpreadsheetReader} implementation.
*
* @param rowNum the Row Number in the sheet. (indexed from 0)
* @param rowObj the java bean constructed using the Row data.
* @param rowObj the java bean constructed using the Row data.
*/
void row(int rowNum, T rowObj);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@


/**
* Marker annotation that can be used to define a "type" for a sheet. The value of this annotation
* will be used to map the sheet of the workbook to this bean definition.
* Marker annotation that can be used to define a "type" for a sheet. The value of this annotation will be used to map
* the sheet of the workbook to this bean definition.
*
* <p>
* Default value ("") indicates that the default sheet name to be used without any modifications,
* but it can be specified to non-empty value to specify different name.
* Default value ("") indicates that the default sheet name to be used without any modifications, but it can be
* specified to non-empty value to specify different name.
* </p>
*
* <p>
Expand All @@ -26,6 +26,7 @@

/**
* Name of the sheet.
*
* @return the Sheet name
*/
String value() default "";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@


/**
* Marker annotation that can be used to define a non-static method as a "setter" or "getter" for a
* column, or non-static field to be used as a column.
* Marker annotation that can be used to define a non-static method as a "setter" or "getter" for a column, or
* non-static field to be used as a column.
*
* <p>
* Default value ("") indicates that the field name is used as the column name without any
* modifications, but it can be specified to non-empty value to specify different name.
* Default value ("") indicates that the field name is used as the column name without any modifications, but it can be
* specified to non-empty value to specify different name.
* </p>
*/
@Retention(RetentionPolicy.RUNTIME)
Expand All @@ -26,9 +26,15 @@
*/
String value() default "";

boolean isFormatted() default false;

String format() default "dd/MM/yyyy";

boolean isFormula() default false;

/**
* Setting this to <code>false</code> will enable the null check on the Column values, to ensure
* non-null values for the field.
* Setting this to <code>false</code> will enable the null check on the Column values, to ensure non-null values for
* the field.
*
* default is <code>true</code>. i.e., null values are allowed.
*
Expand Down
86 changes: 44 additions & 42 deletions src/main/java/io/github/millij/poi/ss/reader/SpreadsheetReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,62 +9,64 @@


/**
* An Abstract representation of a Spreadsheet Reader. Any reader implementation (HSSF or XSSF) is
* expected to implement and provide the below APIs.
* An Abstract representation of a Spreadsheet Reader. Any reader implementation (HSSF or XSSF) is expected to implement
* and provide the below APIs.
*
*/
public interface SpreadsheetReader {

public static final String DEFAULT_DATE_FORMAT = "dd/MM/yyyy";


// Read with Custom RowListener

/**
* Reads the spreadsheet file to beans of the given type. This method will attempt to read all
* the available sheets of the file and creates the objects of the passed type.
* Reads the spreadsheet file to beans of the given type. This method will attempt to read all the available sheets
* of the file and creates the objects of the passed type.
*
* <p>
* The {@link RowListener} implementation callback gets triggered after reading each Row. Best
* Suited for reading Large files in restricted memory environments.
* The {@link RowListener} implementation callback gets triggered after reading each Row. Best Suited for reading
* Large files in restricted memory environments.
* </p>
*
* @param <T> The Parameterized bean Class.
* @param beanClz The Class type to deserialize the rows data
* @param file {@link File} object of the spreadsheet file
* @param listener Custom {@link RowListener} implementation for row data callbacks.
*
* @throws SpreadsheetReadException an exception is thrown in cases where the file data is not
* readable or row data to bean mapping failed.
* @throws SpreadsheetReadException an exception is thrown in cases where the file data is not readable or row data
* to bean mapping failed.
*/
<T> void read(Class<T> beanClz, File file, RowListener<T> listener) throws SpreadsheetReadException;


/**
* Reads the spreadsheet file to beans of the given type. This method will attempt to read all
* the available sheets of the file and creates the objects of the passed type.
* Reads the spreadsheet file to beans of the given type. This method will attempt to read all the available sheets
* of the file and creates the objects of the passed type.
*
* <p>
* The {@link RowListener} implementation callback gets triggered after reading each Row. Best
* Suited for reading Large files in restricted memory environments.
* The {@link RowListener} implementation callback gets triggered after reading each Row. Best Suited for reading
* Large files in restricted memory environments.
* </p>
*
* @param <T> The Parameterized bean Class.
* @param beanClz The Class type to deserialize the rows data
* @param is {@link InputStream} of the spreadsheet file
* @param listener Custom {@link RowListener} implementation for row data callbacks.
*
* @throws SpreadsheetReadException an exception is thrown in cases where the file data is not
* readable or row data to bean mapping failed.
* @throws SpreadsheetReadException an exception is thrown in cases where the file data is not readable or row data
* to bean mapping failed.
*/
<T> void read(Class<T> beanClz, InputStream is, RowListener<T> listener) throws SpreadsheetReadException;


/**
* Reads the spreadsheet file to beans of the given type. Note that only the requested sheet
* (sheet numbers are indexed from 0) will be read.
* Reads the spreadsheet file to beans of the given type. Note that only the requested sheet (sheet numbers are
* indexed from 0) will be read.
*
* <p>
* The {@link RowListener} implementation callback gets triggered after reading each Row. Best
* Suited for reading Large files in restricted memory environments.
* The {@link RowListener} implementation callback gets triggered after reading each Row. Best Suited for reading
* Large files in restricted memory environments.
* </p>
*
* @param <T> The Parameterized bean Class.
Expand All @@ -73,19 +75,19 @@ public interface SpreadsheetReader {
* @param sheetNo index of the Sheet to be read (index starts from 0)
* @param listener Custom {@link RowListener} implementation for row data callbacks.
*
* @throws SpreadsheetReadException an exception is thrown in cases where the file data is not
* readable or row data to bean mapping failed.
* @throws SpreadsheetReadException an exception is thrown in cases where the file data is not readable or row data
* to bean mapping failed.
*/
<T> void read(Class<T> beanClz, File file, int sheetNo, RowListener<T> listener) throws SpreadsheetReadException;


/**
* Reads the spreadsheet file to beans of the given type. Note that only the requested sheet
* (sheet numbers are indexed from 0) will be read.
* Reads the spreadsheet file to beans of the given type. Note that only the requested sheet (sheet numbers are
* indexed from 0) will be read.
*
* <p>
* The {@link RowListener} implementation callback gets triggered after reading each Row. Best
* Suited for reading Large files in restricted memory environments.
* The {@link RowListener} implementation callback gets triggered after reading each Row. Best Suited for reading
* Large files in restricted memory environments.
* </p>
*
* @param <T> The Parameterized bean Class.
Expand All @@ -94,8 +96,8 @@ public interface SpreadsheetReader {
* @param sheetNo index of the Sheet to be read (index starts from 0)
* @param listener Custom {@link RowListener} implementation for row data callbacks.
*
* @throws SpreadsheetReadException an exception is thrown in cases where the file data is not
* readable or row data to bean mapping failed.
* @throws SpreadsheetReadException an exception is thrown in cases where the file data is not readable or row data
* to bean mapping failed.
*/
<T> void read(Class<T> beanClz, InputStream is, int sheetNo, RowListener<T> listener)
throws SpreadsheetReadException;
Expand All @@ -105,40 +107,40 @@ <T> void read(Class<T> beanClz, InputStream is, int sheetNo, RowListener<T> list
// Read with default RowListener

/**
* Reads the spreadsheet file to beans of the given type. This method will attempt to read all
* the available sheets of the file and creates the objects of the passed type.
* Reads the spreadsheet file to beans of the given type. This method will attempt to read all the available sheets
* of the file and creates the objects of the passed type.
*
* @param <T> The Parameterized bean Class.
* @param beanClz The Class type to deserialize the rows data
* @param file {@link File} object of the spreadsheet file
*
* @return a {@link List} of objects of the parameterized type
*
* @throws SpreadsheetReadException an exception is thrown in cases where the file data is not
* readable or row data to bean mapping failed.
* @throws SpreadsheetReadException an exception is thrown in cases where the file data is not readable or row data
* to bean mapping failed.
*/
<T> List<T> read(Class<T> beanClz, File file) throws SpreadsheetReadException;


/**
* Reads the spreadsheet file to beans of the given type. This method will attempt to read all
* the available sheets of the file and creates the objects of the passed type.
* Reads the spreadsheet file to beans of the given type. This method will attempt to read all the available sheets
* of the file and creates the objects of the passed type.
*
* @param <T> The Parameterized bean Class.
* @param beanClz The Class type to deserialize the rows data
* @param is {@link InputStream} of the spreadsheet file
*
* @return a {@link List} of objects of the parameterized type
*
* @throws SpreadsheetReadException an exception is thrown in cases where the file data is not
* readable or row data to bean mapping failed.
* @throws SpreadsheetReadException an exception is thrown in cases where the file data is not readable or row data
* to bean mapping failed.
*/
<T> List<T> read(Class<T> beanClz, InputStream is) throws SpreadsheetReadException;


/**
* Reads the spreadsheet file to beans of the given type. Note that only the requested sheet
* (sheet numbers are indexed from 0) will be read.
* Reads the spreadsheet file to beans of the given type. Note that only the requested sheet (sheet numbers are
* indexed from 0) will be read.
*
* @param <T> The Parameterized bean Class.
* @param beanClz beanClz The Class type to deserialize the rows data
Expand All @@ -147,15 +149,15 @@ <T> void read(Class<T> beanClz, InputStream is, int sheetNo, RowListener<T> list
*
* @return a {@link List} of objects of the parameterized type
*
* @throws SpreadsheetReadException SpreadsheetReadException an exception is thrown in cases
* where the file data is not readable or row data to bean mapping failed.
* @throws SpreadsheetReadException SpreadsheetReadException an exception is thrown in cases where the file data is
* not readable or row data to bean mapping failed.
*/
<T> List<T> read(Class<T> beanClz, File file, int sheetNo) throws SpreadsheetReadException;


/**
* Reads the spreadsheet file to beans of the given type. Note that only the requested sheet
* (sheet numbers are indexed from 0) will be read.
* Reads the spreadsheet file to beans of the given type. Note that only the requested sheet (sheet numbers are
* indexed from 0) will be read.
*
* @param <T> The Parameterized bean Class.
* @param beanClz beanClz The Class type to deserialize the rows data
Expand All @@ -164,8 +166,8 @@ <T> void read(Class<T> beanClz, InputStream is, int sheetNo, RowListener<T> list
*
* @return a {@link List} of objects of the parameterized type
*
* @throws SpreadsheetReadException SpreadsheetReadException an exception is thrown in cases
* where the file data is not readable or row data to bean mapping failed.
* @throws SpreadsheetReadException SpreadsheetReadException an exception is thrown in cases where the file data is
* not readable or row data to bean mapping failed.
*/
<T> List<T> read(Class<T> beanClz, InputStream is, int sheetNo) throws SpreadsheetReadException;

Expand Down
43 changes: 35 additions & 8 deletions src/main/java/io/github/millij/poi/ss/reader/XlsReader.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
package io.github.millij.poi.ss.reader;

import static io.github.millij.poi.util.Beans.isInstantiableType;

import io.github.millij.poi.SpreadsheetReadException;
import io.github.millij.poi.ss.handler.RowListener;
import io.github.millij.poi.ss.writer.SpreadsheetWriter;
import io.github.millij.poi.util.Spreadsheet;

import java.io.InputStream;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
Expand Down Expand Up @@ -69,7 +76,7 @@ public <T> void read(Class<T> beanClz, InputStream is, RowListener<T> listener)
LOGGER.error(errMsg, ex);
throw new SpreadsheetReadException(errMsg, ex);
}

}

@Override
Expand Down Expand Up @@ -99,12 +106,12 @@ public <T> void read(Class<T> beanClz, InputStream is, int sheetNo, RowListener<


// Sheet Process

protected <T> void processSheet(Class<T> beanClz, HSSFSheet sheet, int headerRowNo, RowListener<T> eventHandler) {
// Header column - name mapping
HSSFRow headerRow = sheet.getRow(headerRowNo);
Map<Integer, String> headerMap = this.extractCellHeaderMap(headerRow);

// Bean Properties - column name mapping
Map<String, String> cellPropMapping = Spreadsheet.getColumnToPropertyMap(beanClz);

Expand All @@ -117,7 +124,7 @@ protected <T> void processSheet(Class<T> beanClz, HSSFSheet sheet, int headerRow
continue; // Skip Header row
}

Map<String, Object> rowDataMap = this.extractRowDataAsMap(row, headerMap);
final Map<String, Object> rowDataMap = this.extractRowDataAsMap(beanClz, row, headerMap);
if (rowDataMap == null || rowDataMap.isEmpty()) {
continue;
}
Expand Down Expand Up @@ -169,7 +176,8 @@ private Map<Integer, String> extractCellHeaderMap(HSSFRow headerRow) {
return cellHeaderMap;
}

private Map<String, Object> extractRowDataAsMap(HSSFRow row, Map<Integer, String> columnHeaderMap) {
private <T> Map<String, Object> extractRowDataAsMap(Class<T> beanClz, HSSFRow row,
Map<Integer, String> columnHeaderMap) {
// Sanity checks
if (row == null) {
return new HashMap<String, Object>();
Expand All @@ -190,12 +198,32 @@ private Map<String, Object> extractRowDataAsMap(HSSFRow row, Map<Integer, String
rowDataMap.put(cellColName, cell.getStringCellValue());
break;
case NUMERIC:
rowDataMap.put(cellColName, cell.getNumericCellValue());
break;
if (DateUtil.isCellDateFormatted(cell)) {

final Map<String, String> formats = SpreadsheetWriter.getFormats(beanClz);
final String cellFormat = formats.get(cellColName);

final Date date = cell.getDateCellValue();
final LocalDateTime localDateTime =
LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());

final DateTimeFormatter formatter = cellFormat != null ? DateTimeFormatter.ofPattern(cellFormat)
: DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT);

final String formattedDateTime = localDateTime.format(formatter);
rowDataMap.put(cellColName, formattedDateTime);
break;

} else {
rowDataMap.put(cellColName, cell.getNumericCellValue());
break;
}

case BOOLEAN:
rowDataMap.put(cellColName, cell.getBooleanCellValue());
break;
case FORMULA:
rowDataMap.put(cellColName, cell.getCellFormula());
case BLANK:
case ERROR:
break;
Expand All @@ -209,5 +237,4 @@ private Map<String, Object> extractRowDataAsMap(HSSFRow row, Map<Integer, String




}
Loading