Skip to content

Commit

Permalink
Merge cf99716 into 12283d9
Browse files Browse the repository at this point in the history
  • Loading branch information
vaa25 committed Feb 10, 2020
2 parents 12283d9 + cf99716 commit 5cd510d
Show file tree
Hide file tree
Showing 29 changed files with 1,695 additions and 379 deletions.
21 changes: 21 additions & 0 deletions src/main/java/com/poiji/annotation/ExcelCellName.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,31 @@
@Documented
public @interface ExcelCellName {

int ABSENT_ORDER = -1;

/**
* Specifies the column name where the corresponding value is mapped from the excel data
*
* @return column name
*/
String value();

/**
* Specifies the column order in saved file
*
* @return column order
*/
int order() default ABSENT_ORDER;

/**
* Delimeter for column multiname.
* <p>
* Example: @ExcelCellName(value = "id,identifier", delimerer = ",")
* reading: column with name 'id' will be mapped into field, or if no column 'id',
* then column 'identifier' will be mapped into field.
* writing: field will be saved into column 'id'
*
* @return delimeter for column multiname.
*/
String delimeter() default "";
}
20 changes: 14 additions & 6 deletions src/main/java/com/poiji/bind/Poiji.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
import com.poiji.exception.PoijiException;
import com.poiji.option.PoijiOptions;
import com.poiji.option.PoijiOptions.PoijiOptionsBuilder;
import com.poiji.save.FileSaverFactory;
import com.poiji.util.Files;

import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
Expand Down Expand Up @@ -207,17 +207,25 @@ 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 synchronized <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);
unmarshaller.unmarshal(type, consumer);
}

public static <T> void toExcel(final File file, final Class<T> clazz, final List<T> data) {
toExcel(file, clazz, data, PoijiOptionsBuilder.settings().build());
}

public static <T> void toExcel(
final File file, final Class<T> clazz, final List<T> data, final PoijiOptions options
) {
new FileSaverFactory<>(clazz, options).toFile(file).save(data);
}

private static Unmarshaller deserializer(final File file, final PoijiOptions options) {
final PoijiFile<?> poijiFile = new PoijiFile<>(file);

Expand Down
39 changes: 39 additions & 0 deletions src/main/java/com/poiji/bind/mapping/HSSFReadMappedFields.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.poiji.bind.mapping;

import com.poiji.option.PoijiOptions;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.ss.usermodel.Row;

public final class HSSFReadMappedFields extends ReadMappedFields{

private final DataFormatter dataFormatter;

public HSSFReadMappedFields(final Class<?> entity, final PoijiOptions options) {
super(entity, options);
dataFormatter = new DataFormatter();
}

@Override
public HSSFReadMappedFields parseEntity(){
super.parseEntity();
return this;
}

public <T> T parseRow(final Row row, final T instance) {
setRowInInstance(row, instance);
return instance;
}

private <T> void setRowInInstance(final Row row, final T instance) {
for (short columnOrder = row.getFirstCellNum(); columnOrder < row.getLastCellNum(); columnOrder++) {
setCellInInstance(row.getRowNum(), columnOrder, dataFormatter.formatCellValue(row.getCell(columnOrder)), instance);
}
}

public void parseColumnNames(final Row row) {
for (short columnOrder = row.getFirstCellNum(); columnOrder < row.getLastCellNum(); columnOrder++) {
parseColumnName(columnOrder, row.getCell(columnOrder).getStringCellValue());
}
}

}
158 changes: 8 additions & 150 deletions src/main/java/com/poiji/bind/mapping/HSSFUnmarshaller.java
Original file line number Diff line number Diff line change
@@ -1,59 +1,29 @@
package com.poiji.bind.mapping;

import com.poiji.annotation.ExcelCell;
import com.poiji.annotation.ExcelCellName;
import com.poiji.annotation.ExcelCellRange;
import com.poiji.annotation.ExcelRow;
import com.poiji.annotation.ExcelUnknownCells;
import com.poiji.bind.Unmarshaller;
import com.poiji.config.Casting;
import com.poiji.exception.IllegalCastException;
import com.poiji.option.PoijiOptions;
import com.poiji.util.ReflectUtil;
import java.util.Optional;
import java.util.function.Consumer;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

import static java.lang.String.valueOf;

/**
* This is the main class that converts the excel sheet fromExcel Java object
* Created by hakan on 16/01/2017.
*/
abstract class HSSFUnmarshaller implements Unmarshaller {

private final DataFormatter dataFormatter;
protected final PoijiOptions options;
private final Casting casting;
private final Map<String, Integer> columnIndexPerTitle;
private final Map<Integer, String> titlePerColumnIndex;
private final Map<Integer, String> caseSensitiveTitlePerColumnIndex;
private final int limit;
private int internalCount;

HSSFUnmarshaller(PoijiOptions options) {
this.options = options;
this.limit = options.getLimit();
dataFormatter = new DataFormatter();
columnIndexPerTitle = new HashMap<>();
titlePerColumnIndex = new HashMap<>();
caseSensitiveTitlePerColumnIndex = new HashMap<>();
casting = options.getCasting();
}

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

loadColumnTitles(sheet, maxPhysicalNumberOfRows);
final HSSFReadMappedFields readMappedFields = loadColumnTitles(sheet, maxPhysicalNumberOfRows, type);

for (Row currentRow : sheet) {
if (!skip(currentRow, skip) && !isRowEmpty(currentRow)) {
Expand All @@ -75,7 +45,7 @@ public <T> void unmarshal(Class<T> type, Consumer<? super T> consumer) {
if (limit != 0 && internalCount > limit)
return;

T instance = deserializeRowToInstance(currentRow, type);
T instance = readMappedFields.parseRow(currentRow, ReflectUtil.newInstanceOf(type));
consumer.accept(instance);
}
}
Expand Down Expand Up @@ -110,124 +80,12 @@ private Sheet getSheetToProcess(Workbook workbook, PoijiOptions options, String
return sheet;
}

private void loadColumnTitles(Sheet sheet, int maxPhysicalNumberOfRows) {
private HSSFReadMappedFields loadColumnTitles(Sheet sheet, int maxPhysicalNumberOfRows, final Class<?> type) {
final HSSFReadMappedFields readMappedFields = new HSSFReadMappedFields(type, options).parseEntity();
if (maxPhysicalNumberOfRows > 0) {
int row = options.getHeaderStart();
Row firstRow = sheet.getRow(row);
for (Cell cell : firstRow) {
final int columnIndex = cell.getColumnIndex();
caseSensitiveTitlePerColumnIndex.put(columnIndex, getTitleNameForMap(cell.getStringCellValue(), columnIndex));
final String titleName = options.getCaseInsensitive()
? cell.getStringCellValue().toLowerCase()
: cell.getStringCellValue();
columnIndexPerTitle.put(titleName, columnIndex);
titlePerColumnIndex.put(columnIndex, getTitleNameForMap(titleName, columnIndex));
}
}
}

private String getTitleNameForMap(String cellContent, int columnIndex) {
String titleName;
if (titlePerColumnIndex.containsValue(cellContent)
|| cellContent.isEmpty()) {
titleName = cellContent + "@" + columnIndex;
} else {
titleName = cellContent;
}
return titleName;
}

private <T> T deserializeRowToInstance(Row currentRow, Class<T> type) {
T instance = ReflectUtil.newInstanceOf(type);
return setFieldValuesFromRowIntoInstance(currentRow, type, instance);
}

private <T> T tailSetFieldValue(Row currentRow, Class<? super T> type, T instance) {
List<Integer> mappedColumnIndices = new ArrayList<>();
List<Field> unknownCells = new ArrayList<>();

for (Field field : type.getDeclaredFields()) {
if (field.getAnnotation(ExcelRow.class) != null) {
final int rowNum = currentRow.getRowNum();
final Object data = casting.castValue(field.getType(), valueOf(rowNum), rowNum, -1, options);
setFieldData(instance, field, data);
} else if (field.getAnnotation(ExcelCellRange.class) != null) {

Class<?> fieldType = field.getType();
Object fieldInstance = ReflectUtil.newInstanceOf(fieldType);
for (Field fieldField : fieldType.getDeclaredFields()) {
mappedColumnIndices.add(tailSetFieldValue(currentRow, fieldInstance, fieldField));
}
setFieldData(instance, field, fieldInstance);
} else if (field.getAnnotation(ExcelUnknownCells.class) != null) {
unknownCells.add(field);
} else {
mappedColumnIndices.add(tailSetFieldValue(currentRow, instance, field));
}
readMappedFields.parseColumnNames(sheet.getRow(options.getHeaderStart()));
}

Map<String, String> excelUnknownCellsMap = StreamSupport
.stream(Spliterators.spliteratorUnknownSize(currentRow.cellIterator(), Spliterator.ORDERED), false)
.filter(cell -> !mappedColumnIndices.contains(cell.getColumnIndex()))
.filter(cell -> !cell.toString().isEmpty())
.collect(Collectors.toMap(
cell -> caseSensitiveTitlePerColumnIndex.get(cell.getColumnIndex()),
Object::toString
));

unknownCells.forEach(field -> setFieldData(instance, field, excelUnknownCellsMap));

return instance;
}

private <T> Integer tailSetFieldValue(Row currentRow, T instance, Field field) {
final Integer column = getFieldColumn(field);
if (column != null) {
constructTypeValue(currentRow, instance, field, column);
}
return column;
}

private Integer getFieldColumn(final Field field) {
ExcelCell index = field.getAnnotation(ExcelCell.class);
Integer column = null;
if (index != null) {
column = index.value();
} else {
ExcelCellName excelCellName = field.getAnnotation(ExcelCellName.class);
if (excelCellName != null) {
final String titleName = options.getCaseInsensitive()
? excelCellName.value().toLowerCase()
: excelCellName.value();
column = columnIndexPerTitle.get(titleName);
}
}
return column;
}

private <T> void constructTypeValue(Row currentRow, T instance, Field field, int column) {
Cell cell = currentRow.getCell(column);

if (cell != null) {
String value = dataFormatter.formatCellValue(cell);
Object data = casting.castValue(field.getType(), value, currentRow.getRowNum(), column, options);
setFieldData(instance, field, data);
}
}

private <T> void setFieldData(T instance, Field field, Object data) {
try {
field.setAccessible(true);
field.set(instance, data);
} catch (IllegalAccessException e) {
throw new IllegalCastException("Unexpected cast type {" + data + "} of field" + field.getName());
}
}

private <T> T setFieldValuesFromRowIntoInstance(Row currentRow, Class<? super T> subclass, T instance) {
return subclass == null
? instance
: tailSetFieldValue(currentRow, subclass, setFieldValuesFromRowIntoInstance(currentRow, subclass.getSuperclass(), instance));
return readMappedFields;
}

private boolean skip(final Row currentRow, int skip) {
Expand Down
Loading

0 comments on commit 5cd510d

Please sign in to comment.