Skip to content

Commit

Permalink
Merge pull request #96 from thiyagu-7/sheetname
Browse files Browse the repository at this point in the history
Supporting configuring Excel sheet via annotation
  • Loading branch information
ozlerhakan committed Oct 17, 2019
2 parents 3f9a0dd + f7d8435 commit 2ffcf59
Show file tree
Hide file tree
Showing 10 changed files with 296 additions and 15 deletions.
40 changes: 35 additions & 5 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,36 @@ 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
Poiji allows specifying the sheet name using annotation

[source,java]
----
@ExcelSheet("Sheet2") (1)
public class Student {
@ExcelCell(0)
private String name;
@ExcelCell(1)
private String id;
@ExcelCell(2)
private String phone;
@Override
public String toString() {
return "Student {" +
" name=" + name +
", id=" + id + "'" +
", phone='" + phone + "'" +
'}';
}
}
----
<1> With the `ExcelSheet` annotation we are configuring the name of the sheet to read data from. The other sheets will be ignored.

=== Encrypted Excel Files

Consider that your excel file is protected with a password, you can define the password via `PoijiOptionsBuilder` to read rows:
Expand All @@ -192,7 +222,7 @@ PoijiOptions options = PoijiOptionsBuilder.settings()
List<Employee> employees = Poiji.fromExcel(new File("employees.xls"), Employee.class, options);
----

=== Example 2
=== Example 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 @@ -266,7 +296,7 @@ public class Person {
}
----

=== Example 3
=== Example 4

Your object model may be derived from a super class:

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

=== Example 4
=== Example 5

Consider you have a table like below:

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

=== Example 5
=== Example 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 @@ -462,7 +492,7 @@ private void dbInsertion(Calculation siCalculation) {
}
----

=== Example 6
=== Example 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
21 changes: 21 additions & 0 deletions src/main/java/com/poiji/annotation/ExcelSheet.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.poiji.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Annotation to set the sheet name to be processed.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface ExcelSheet {
/**
* Specifies the name of the sheet that is to be processed.
* @return the sheet name.
*/
String value();
}
16 changes: 8 additions & 8 deletions src/main/java/com/poiji/bind/mapping/HSSFUnmarshaller.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import com.poiji.bind.Unmarshaller;
import com.poiji.config.Casting;
import com.poiji.exception.IllegalCastException;
import com.poiji.exception.PoijiInstantiationException;
import com.poiji.option.PoijiOptions;
import com.poiji.util.ReflectUtil;

Expand All @@ -19,9 +18,9 @@
import org.apache.poi.ss.usermodel.Workbook;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Consumer;

import static java.lang.String.valueOf;
Expand All @@ -47,8 +46,9 @@ abstract class HSSFUnmarshaller implements Unmarshaller {
@Override
public <T> void unmarshal(Class<T> type, Consumer<? super T> consumer) {
Workbook workbook = workbook();
Optional<String> maybeSheetName = SheetNameExtractor.getSheetName(type, options);

Sheet sheet = this.getSheetToProcess(workbook, options);
Sheet sheet = this.getSheetToProcess(workbook, options, maybeSheetName.orElse(null));

int skip = options.skip();
int maxPhysicalNumberOfRows = sheet.getPhysicalNumberOfRows() + 1 - skip;
Expand All @@ -63,30 +63,30 @@ public <T> void unmarshal(Class<T> type, Consumer<? super T> consumer) {
}
}

private Sheet getSheetToProcess(Workbook workbook, PoijiOptions options) {
private Sheet getSheetToProcess(Workbook workbook, PoijiOptions options, String sheetName) {
int nonHiddenSheetIndex = 0;
int requestedIndex = options.sheetIndex();
Sheet sheet = null;
if (options.ignoreHiddenSheets()) {
for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
if (!workbook.isSheetHidden(i) && !workbook.isSheetVeryHidden(i)) {
if (options.getSheetName() == null) {
if (sheetName == null) {
if (nonHiddenSheetIndex == requestedIndex) {
return workbook.getSheetAt(i);
}
} else {
if (workbook.getSheetName(i).equalsIgnoreCase(options.getSheetName())) {
if (workbook.getSheetName(i).equalsIgnoreCase(sheetName)) {
return workbook.getSheetAt(i);
}
}
nonHiddenSheetIndex++;
}
}
} else {
if (options.getSheetName() == null) {
if (sheetName == null) {
sheet = workbook.getSheetAt(requestedIndex);
} else {
sheet = workbook.getSheet(options.getSheetName());
sheet = workbook.getSheet(sheetName);
}
}
return sheet;
Expand Down
41 changes: 41 additions & 0 deletions src/main/java/com/poiji/bind/mapping/SheetNameExtractor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.poiji.bind.mapping;

import com.poiji.annotation.ExcelSheet;
import com.poiji.exception.PoijiException;
import com.poiji.option.PoijiOptions;

import java.util.Optional;

/**
* Utility class to extract the sheet name.
*/
class SheetNameExtractor {
/**
* Extracts the sheet name from either the annotated value {@link ExcelSheet} from the model class or from the sheet name set
* in the Poiji Options. Returns Optional.empty when sheet name is not configured at either place.
* @param type The class instance of the object model.
* @param options The Poiji options.
* @param <T> The type of the object model.
* @return an Optional sheet name
* @throws PoijiException when the configured sheet name in the PoijiOptions and the annotated sheet name do not match.
*/
public static <T> Optional<String> getSheetName(Class<T> type, PoijiOptions options) {
String annotatedSheetName = null;
String configuredSheetName = options.getSheetName();

if (type.isAnnotationPresent(ExcelSheet.class)) {
ExcelSheet excelSheet = type.getAnnotation(ExcelSheet.class);
annotatedSheetName = excelSheet.value();
}
if (annotatedSheetName != null && configuredSheetName != null
&& !annotatedSheetName.equals(options.getSheetName())) {
throw new PoijiException(String.format("The configured sheet name in PoijiOptions (%s) and the annotated sheet name "
+ "(%s) do not match", configuredSheetName, annotatedSheetName));
}

if (annotatedSheetName != null) {
return Optional.of(annotatedSheetName);
}
return Optional.ofNullable(configuredSheetName);
}
}
8 changes: 6 additions & 2 deletions src/main/java/com/poiji/bind/mapping/XSSFUnmarshaller.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;

import static org.apache.poi.xssf.eventusermodel.XSSFReader.SheetIterator;
Expand Down Expand Up @@ -56,7 +57,10 @@ protected <T> void unmarshal0(Class<T> type, Consumer<? super T> consumer, OPCPa
List<WorkBookSheet> sheets = wbch.getSheets();
SheetIterator iter = (SheetIterator) workbookReader.getSheetsData();
int sheetCounter = 0;
if (options.getSheetName() == null) {

Optional<String> maybeSheetName = SheetNameExtractor.getSheetName(type, options);

if (!maybeSheetName.isPresent()) {
int requestedIndex = options.sheetIndex();
int nonHiddenSheetIndex = 0;
while (iter.hasNext()) {
Expand All @@ -73,7 +77,7 @@ protected <T> void unmarshal0(Class<T> type, Consumer<? super T> consumer, OPCPa
sheetCounter++;
}
} else {
String sheetName = options.getSheetName();
String sheetName = maybeSheetName.get();
while (iter.hasNext()) {
try (InputStream stream = iter.next()) {
WorkBookSheet wbs = sheets.get(sheetCounter);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package com.poiji.deserialize;

import com.poiji.bind.Poiji;
import com.poiji.deserialize.model.Student;
import com.poiji.exception.PoijiException;
import com.poiji.option.PoijiOptions;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

import java.io.File;
import java.util.Arrays;
import java.util.List;

import static com.poiji.util.Data.unmarshallingStudents;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;

/**
* Test for Reading excel files using annotated sheet name
*/
@RunWith(Parameterized.class)
public class ReadExcelByAnnotatedSheetNameTest {
private String path;
private List<Student> expectedStudents;
private PoijiOptions options;
private Class<?> expectedException;

public ReadExcelByAnnotatedSheetNameTest(String path, List<Student> expectedStudents, PoijiOptions options,
Class<?> expectedException) {
this.path = path;
this.expectedStudents = expectedStudents;
this.options = options;
this.expectedException = expectedException;
}

@Parameterized.Parameters(name = "{index}: ({0})={1}")
public static Iterable<Object[]> queries() {
return Arrays.asList(new Object[][]{
{"src/test/resources/student.xlsx", unmarshallingStudents(), null, null},
{"src/test/resources/student.xls", unmarshallingStudents(), null, null},
{"src/test/resources/student.xlsx", unmarshallingStudents(), PoijiOptions.PoijiOptionsBuilder.settings()
.sheetName("Sheet1")
.build(),
PoijiException.class},
});
}

@Test
public void shouldReadAnnotatedSheetNameFromStudent() {

try {
List<Student> actualStudents;
if (options == null) {
actualStudents = Poiji.fromExcel(new File(path), Student.class);
} else {
actualStudents = Poiji.fromExcel(new File(path), Student.class, options);
}

assertThat(actualStudents, notNullValue());
assertThat(actualStudents.size(), not(0));
assertThat(actualStudents.size(), is(expectedStudents.size()));

Student actualStudent1 = actualStudents.get(0);
Student actualStudent2 = actualStudents.get(1);

Student expectedStudent1 = expectedStudents.get(0);
Student expectedStudent2 = expectedStudents.get(1);

assertEquals(actualStudent1, expectedStudent1);
assertEquals(actualStudent2, expectedStudent2);
} catch (Exception e) {
if (expectedException == null) {
fail(e.getMessage());
} else {
assertThat(e, instanceOf(expectedException));
assertEquals("The configured sheet name in PoijiOptions (Sheet1) and the annotated sheet name "
+ "(Sheet2) do not match", e.getMessage());
}
}
}

}

0 comments on commit 2ffcf59

Please sign in to comment.