Skip to content

Commit

Permalink
Merge 2f442d1 into 6e73e56
Browse files Browse the repository at this point in the history
  • Loading branch information
ozlerhakan committed May 1, 2020
2 parents 6e73e56 + 2f442d1 commit dd1f07e
Show file tree
Hide file tree
Showing 25 changed files with 878 additions and 57 deletions.
123 changes: 112 additions & 11 deletions README.adoc
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
= Poiji
:version: v2.6.0
:version: v2.7.0

image:https://travis-ci.org/ozlerhakan/poiji.svg?branch=master["Build Status", link="https://travis-ci.org/ozlerhakan/poiji"] image:https://api.codacy.com/project/badge/Grade/6587e90886184da29a1b7c5634695c9d["Codacy code quality", link="https://www.codacy.com/app/ozlerhakan/poiji?utm_source=github.com&utm_medium=referral&utm_content=ozlerhakan/poiji&utm_campaign=Badge_Grade"] image:https://coveralls.io/repos/github/ozlerhakan/poiji/badge.svg?branch=master["Coverage Status", link="https://coveralls.io/github/ozlerhakan/poiji?branch=master"] image:https://img.shields.io/badge/apache.poi-4.1.2-brightgreen.svg[] image:https://img.shields.io/badge/gitter-join%20chat-blue.svg["Gitter", link="https://gitter.im/poiji/Lobby"] image:https://img.shields.io/badge/license-MIT-blue.svg[]

Expand All @@ -15,15 +15,15 @@ In your Maven/Gradle project, first add the corresponding dependency:
<dependency>
<groupId>com.github.ozlerhakan</groupId>
<artifactId>poiji</artifactId>
<version>2.6.0</version>
<version>2.7.0</version>
</dependency>
----

.gradle
[source,groovy]
----
dependencies {
compile 'com.github.ozlerhakan:poiji:2.6.0'
compile 'com.github.ozlerhakan:poiji:2.7.0'
}
----

Expand Down Expand Up @@ -60,9 +60,12 @@ com.poiji.option.PoijiOptions.PoijiOptionsBuilder#trimCellValue(boolean)
com.poiji.option.PoijiOptions.PoijiOptionsBuilder#headerStart(int)
com.poiji.option.PoijiOptions.PoijiOptionsBuilder#withCasting(Casting)
com.poiji.option.PoijiOptions.PoijiOptionsBuilder#caseInsensitive(boolean)
com.poiji.option.PoijiOptions.PoijiOptionsBuilder#namedHeaderMandatory(boolean)
com.poiji.option.PoijiOptions.PoijiOptionsBuilder#poijiNumberFormat(PoijiNumberFormat)
com.poiji.option.PoijiOptions.PoijiOptionsBuilder#poijiCellFormat(PoijiLogCellFormat)
----

=== Example 1
=== Feature 1

Create your object model:

Expand Down Expand Up @@ -183,7 +186,7 @@ PoijiOptions options = PoijiOptionsBuilder.settings()
----
1. a field that is of type either `java.util.Date`, `Float`, `Double`, `Integer`, `Long` or `String` will have a `null` value.

=== Example 2
=== Feature 2
Poiji allows specifying the sheet name using annotation

[source,java]
Expand Down Expand Up @@ -224,7 +227,7 @@ PoijiOptions options = PoijiOptionsBuilder.settings()
List<Employee> employees = Poiji.fromExcel(new File("employees.xls"), Employee.class, options);
----

=== Example 3
=== Feature 3

The version `1.11` introduces a new annotation called `ExcelCellName` so that we can read the values by column names directly.

Expand Down Expand Up @@ -298,7 +301,7 @@ public class Person {
}
----

=== Example 4
=== Feature 4

Your object model may be derived from a super class:

Expand Down Expand Up @@ -347,7 +350,7 @@ Car car = cars.get(0);
// 4
----

=== Example 5
=== Feature 5

Consider you have a table like below:

Expand Down Expand Up @@ -441,7 +444,7 @@ GroupA firstRowPerson1 = firstRowGroups.getGroupA();
GroupB secondRowPerson2 = firstRowGroups.getGroupB();
----

=== Example 6
=== Feature 6

As of 1.14, Poiji supports Consumer Interface. As https://github.com/ozlerhakan/poiji/pull/39#issuecomment-409521808[@fmarazita] explained the usage, there are several benefits of having a Consumer:

Expand Down Expand Up @@ -494,7 +497,7 @@ private void dbInsertion(Calculation siCalculation) {
}
----

=== Example 7
=== Feature 7

Since Poiji 1.19.1, you can create your own casting implementation without relying on the default Poiji casting configuration using the `Casting` interface.

Expand Down Expand Up @@ -532,7 +535,7 @@ Then you can add your custom implementation with the `withCasting` method:
List<Person> people = Poiji.fromExcel(excel, Person.class, options);
----

=== Example 8
=== Feature 8

Since Poiji 2.3.0, you can annotate a `Map<String, String>` with `@ExcelUnknownCells` to parse all entries,
which are not mapped in any other way (for example by index or by name).
Expand Down Expand Up @@ -579,6 +582,104 @@ This is the excel file we want to parse:
The object corresponding to the first row of the excel sheet then has a map with `{ENCODING=mp3, BITRATE=256}`
and the one for the second row has `{ENCODING=flac, BITRATE=1500}`.

=== Feature 9

Poiji 2.7.0 introduced the Option `namedHeaderMandatory`. If set to true, Poiji will check that all field annotated with `@ExcelCellName` must have a corresponding column in the Excel sheet. If any column is missing a `HeaderMissingException` will be thrown.

[source,java]
----
public class MusicTrack {
@ExcelCellName("ID")
private String employeeId;
@ExcelCellName("AUTHOR")
private String author;
}
----

This is the excel file we want to parse:

|===
|ID | Artist

|123923
|Joe Doe

|56437
|Jane Doe
|===

In the default setting of Poiji (`namedHeaderMandatory=false`), the author field will be null for both objects.
With `namedHeaderMandatory=true`, a `HeaderMissingException` will be thrown.

=== Feature 10

As of 2.7.0, we can observe each cell format of a given excel file. Assume that we have an excel file like below:

|===
|Date
|12/31/2020 12.00 AM
|===

We can get all the list of cell formats using `PoijiLogCellFormat` with `PoijiOptions`:

----
PoijiLogCellFormat log = new PoijiLogCellFormat();
PoijiOptions options = PoijiOptions.PoijiOptionsBuilder.settings()
.poijiCellFormat(log)
.build();
List<Model> dates = Poiji.fromExcel(stream, poijiExcelType, Model.class, options);
Model model = rows.get(0)
model.getDate();
// 12.00
----

Hmm, It looks like we did not achieve the correct date format when we get the date value (`12.00`). Let's see how internally the excel file parses the value of the cell via `PoijiLogCellFormat`:

----
List<InternalCellFormat> formats = log.formats();
InternalCellFormat cell10 = formats.get(1);
cell10.getFormatString()
// mm:ss.0
cell10.getFormatIndex()
// 47
----

Now that we know the reason of why we don't see the expected date value, it's because the default format of the date cell is the `mm:ss.0` format with a given index 47, we need to change the default format of index (i.e. `47`). This format was automatically assigned to the cell having a number, but almost certainly with a special style or format. Note that this option should be used for debugging purpose only.

=== Feature 11

Using 2.7.0, we can change the default format of a cell using `PoijiNumberFormat`. Recall `feature 10`, we are unable to see the correct cell format what's more the excel file uses another format which we do not want to.

|===
|Date
|12/31/2020 12.00 AM
|===

Using `PoijiNumberFormat` option, we are able to change the behavior of the format of a specific index:

----
PoijiNumberFormat numberFormat = new PoijiNumberFormat();
numberFormat.putNumberFormat((short) 47, "mm/dd/yyyy hh.mm aa");
PoijiOptions options = PoijiOptions.PoijiOptionsBuilder.settings()
.poijiNumberFormat(numberFormat)
.build();
List<Model> rows = Poiji.fromExcel(stream, poijiExcelType, Model.class, options);
Model model = rows.get(0)
model.getDate();
// 12/31/2020 12.00 AM <1>
----
1. Voila!

We know that the index 47 uses the format `mm:ss.0` by default in the given excel file, thus we're able to override its format with `mm/dd/yyyy hh.mm aa` using the `putNumberFormat` method.

== Try with JShell

Since we have a new pedagogic tool, Java 9 REPL, you can try Poiji in JShell. Clone the repo and follow the steps below. JShell should open up a new jshell session once loading the startup scripts and the specified jars that must be in the classpath. You must first import and create related packages and classes before using Poiji. We are able to use directly Poiji and Employee classes because they are already imported from `jshell/snippets` with `try-with-jshell.sh`.
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>com.github.ozlerhakan</groupId>
<artifactId>poiji</artifactId>
<version>2.6.0</version>
<version>2.7.0</version>
<packaging>jar</packaging>

<name>poiji</name>
Expand Down
41 changes: 21 additions & 20 deletions src/main/java/com/poiji/bind/Poiji.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ private Poiji() {
* @throws IllegalCastException if this Field object is enforcing Java language access control and the underlying field is either inaccessible or final.
* @see Poiji#fromExcel(File, Class, PoijiOptions)
*/
public static synchronized <T> List<T> fromExcel(final File file, final Class<T> type) {
public static <T> List<T> fromExcel(final File file, final Class<T> type) {
final ArrayList<T> list = new ArrayList<>();
fromExcel(file, type, list::add);
return list;
Expand All @@ -72,7 +72,7 @@ public static synchronized <T> List<T> fromExcel(final File file, final Class<T>
* @throws IllegalCastException if this Field object is enforcing Java language access control and the underlying field is either inaccessible or final.
* @see Poiji#fromExcel(File, Class, PoijiOptions)
*/
public static synchronized <T> void fromExcel(final File file, final Class<T> type, final Consumer<? super T> consumer) {
public static <T> void fromExcel(final File file, final Class<T> type, final Consumer<? super T> consumer) {
final Unmarshaller unmarshaller = deserializer(file, PoijiOptionsBuilder.settings().build());
unmarshaller.unmarshal(type, consumer);
}
Expand All @@ -90,9 +90,9 @@ public static synchronized <T> void fromExcel(final File file, final Class<T> ty
* @throws IllegalCastException if this Field object is enforcing Java language access control and the underlying field is either inaccessible or final.
* @see Poiji#fromExcel(File, Class, PoijiOptions)
*/
public static synchronized <T> List<T> fromExcel(final InputStream inputStream,
PoijiExcelType excelType,
final Class<T> type) {
public static <T> List<T> fromExcel(final InputStream inputStream,
PoijiExcelType excelType,
final Class<T> type) {
final ArrayList<T> list = new ArrayList<>();
fromExcel(inputStream, excelType, type, list::add);
return list;
Expand All @@ -111,10 +111,10 @@ public static synchronized <T> List<T> fromExcel(final InputStream inputStream,
* @throws IllegalCastException if this Field object is enforcing Java language access control and the underlying field is either inaccessible or final.
* @see Poiji#fromExcel(File, Class, PoijiOptions)
*/
public static synchronized <T> void fromExcel(final InputStream inputStream,
PoijiExcelType excelType,
final Class<T> type,
final Consumer<? super T> consumer) {
public static <T> void fromExcel(final InputStream inputStream,
PoijiExcelType excelType,
final Class<T> type,
final Consumer<? super T> consumer) {
Objects.requireNonNull(excelType);

final Unmarshaller unmarshaller = deserializer(inputStream, excelType, PoijiOptionsBuilder.settings().build());
Expand All @@ -134,7 +134,7 @@ public static synchronized <T> void fromExcel(final InputStream inputStream,
* @throws IllegalCastException if this Field object is enforcing Java language access control and the underlying field is either inaccessible or final.
* @see Poiji#fromExcel(File, Class)
*/
public static synchronized <T> List<T> fromExcel(final File file, final Class<T> type, final PoijiOptions options) {
public static <T> List<T> fromExcel(final File file, final Class<T> type, final PoijiOptions options) {
final ArrayList<T> list = new ArrayList<>();
fromExcel(file, type, options, list::add);
return list;
Expand All @@ -153,7 +153,7 @@ public static synchronized <T> List<T> fromExcel(final File file, final Class<T>
* @throws IllegalCastException if this Field object is enforcing Java language access control and the underlying field is either inaccessible or final.
* @see Poiji#fromExcel(File, Class)
*/
public static synchronized <T> void fromExcel(final File file, final Class<T> type, final PoijiOptions options, final Consumer<? super T> consumer) {
public static <T> void fromExcel(final File file, final Class<T> type, final PoijiOptions options, final Consumer<? super T> consumer) {
final Unmarshaller unmarshaller = deserializer(file, options);
unmarshaller.unmarshal(type, consumer);
}
Expand All @@ -172,10 +172,10 @@ public static synchronized <T> void fromExcel(final File file, final Class<T> ty
* @throws IllegalCastException if this Field object is enforcing Java language access control and the underlying field is either inaccessible or final.
* @see Poiji#fromExcel(File, Class)
*/
public static synchronized <T> List<T> fromExcel(final InputStream inputStream,
final PoijiExcelType excelType,
final Class<T> type,
final PoijiOptions options) {
public static <T> List<T> fromExcel(final InputStream inputStream,
final PoijiExcelType excelType,
final Class<T> type,
final PoijiOptions options) {
Objects.requireNonNull(excelType);
final ArrayList<T> list = new ArrayList<>();
fromExcel(inputStream, excelType, type, options, list::add);
Expand All @@ -197,11 +197,11 @@ public static synchronized <T> List<T> fromExcel(final InputStream inputStream,
* language access control and the underlying field is either inaccessible or final.
* @see Poiji#fromExcel(File, Class)
*/
public static synchronized <T> void fromExcel(final InputStream inputStream,
final PoijiExcelType excelType,
final Class<T> type,
final PoijiOptions options,
final Consumer<? super T> consumer) {
public static <T> void fromExcel(final InputStream inputStream,
final PoijiExcelType excelType,
final Class<T> type,
final PoijiOptions options,
final Consumer<? super T> consumer) {
Objects.requireNonNull(excelType);

final Unmarshaller unmarshaller = deserializer(inputStream, excelType, options);
Expand Down Expand Up @@ -233,4 +233,5 @@ private static Unmarshaller deserializer(final InputStream inputStream, PoijiExc
throw new InvalidExcelFileExtension("Invalid file extension (" + excelType + "), excepted .xls or .xlsx");
}
}

}
2 changes: 2 additions & 0 deletions src/main/java/com/poiji/bind/mapping/HSSFUnmarshaller.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.poiji.config.Casting;
import com.poiji.exception.IllegalCastException;
import com.poiji.option.PoijiOptions;
import com.poiji.util.AnnotationUtil;
import com.poiji.util.ReflectUtil;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
Expand Down Expand Up @@ -67,6 +68,7 @@ public <T> void unmarshal(Class<T> type, Consumer<? super T> consumer) {
int maxPhysicalNumberOfRows = sheet.getPhysicalNumberOfRows() + 1 - skip;

loadColumnTitles(sheet, maxPhysicalNumberOfRows);
AnnotationUtil.validateMandatoryNameColumns(options, type, columnIndexPerTitle.keySet());

for (Row currentRow : sheet) {
if (!skip(currentRow, skip) && !isRowEmpty(currentRow)) {
Expand Down
13 changes: 12 additions & 1 deletion src/main/java/com/poiji/bind/mapping/PoijiHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,17 @@
import com.poiji.config.Casting;
import com.poiji.exception.IllegalCastException;
import com.poiji.option.PoijiOptions;
import com.poiji.util.AnnotationUtil;
import com.poiji.util.ReflectUtil;
import org.apache.poi.ss.util.CellAddress;
import org.apache.poi.xssf.eventusermodel.XSSFSheetXMLHandler.SheetContentsHandler;
import org.apache.poi.xssf.usermodel.XSSFComment;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Stream;

Expand All @@ -42,6 +45,7 @@ final class PoijiHandler<T> implements SheetContentsHandler {
private Map<String, Object> fieldInstances;
private Map<Integer, Field> columnToField;
private Map<Integer, Field> columnToSuperClassField;
private Set<ExcelCellName> excelCellNames;

PoijiHandler(Class<T> type, PoijiOptions options, Consumer<? super T> consumer) {
this.type = type;
Expand All @@ -53,6 +57,7 @@ final class PoijiHandler<T> implements SheetContentsHandler {
titlePerColumnIndex = new HashMap<>();
columnToField = new HashMap<>();
columnToSuperClassField = new HashMap<>();
excelCellNames = new HashSet<>();
}

private void setFieldValue(String content, Class<? super T> subclass, int column) {
Expand Down Expand Up @@ -155,6 +160,7 @@ private boolean setValue(Field field, int column, String content, Object ins) {
} else {
ExcelCellName excelCellName = field.getAnnotation(ExcelCellName.class);
if (excelCellName != null) {
excelCellNames.add(excelCellName);
Class<?> fieldType = field.getType();
final String titleName = options.getCaseInsensitive()
? excelCellName.value().toLowerCase()
Expand Down Expand Up @@ -237,4 +243,9 @@ private String getTitleNameForMap(String cellContent, int columnIndex) {
public void headerFooter(String text, boolean isHeader, String tagName) {
//no-op
}
}

@Override
public void endSheet() {
AnnotationUtil.validateMandatoryNameColumns(options, type, columnIndexPerTitle.keySet());
}
}
Loading

0 comments on commit dd1f07e

Please sign in to comment.