Chagne term 'contents' -> 'body', Write simple test code

This commit is contained in:
lannstark
2020-09-17 08:07:14 +09:00
parent 9573534169
commit 65437131b1
20 changed files with 207 additions and 72 deletions

1
.gitignore vendored
View File

@@ -1,2 +1,3 @@
.idea/
.gradle/
out/

View File

@@ -36,7 +36,7 @@ dependencies {
// Rendered Field Order is same as Dto field order
public class ExcelDto {
// Annotation Case 1. no headerStyle and contentsStyle
// Annotation Case 1. no headerStyle and bodyStyle
@ExcelColumn(headerName = "User Name")
private String name;
@@ -46,24 +46,24 @@ public class ExcelDto {
)
private int age;
// Annotation Case 3. You can also configure contents style
// Annotation Case 3. You can also configure bodyStyle style
@ExcelColumn(headerName = "Happy BirthDay",
contentsStyle = @ExcelColumnStyle(excelCellStyleClass = DefaultExcelCellStyle.class, enumName = "CONTENTS")
bodyStyle = @ExcelColumnStyle(excelCellStyleClass = DefaultExcelCellStyle.class, enumName = "BODY")
)
private LocalDate birthDay;
}
```
If you want to config default style in class, you should use @DefaultHeaderStyle or @DefaultContentsStyle.
If you want to config default style in class, you should use @DefaultHeaderStyle or @DefaultBodyStyle.
This style will be applied to all fields having not field style in this class.
```java
@DefaultHeaderStyle(
style = @ExcelColumnStyle(excelCellStyleClass = DefaultExcelCellStyle.class, enumName = "BLUE_HEADER")
)
@DefaultContentsStyle(
style = @ExcelColumnStyle(excelCellStyleClass = DefaultExcelCellStyle.class, enumName = "CONTENTS")
@DefaultBodyStyle(
style = @ExcelColumnStyle(excelCellStyleClass = DefaultExcelCellStyle.class, enumName = "BODY")
)
public class ExcelDto {
@@ -192,7 +192,7 @@ public enum CustomCellStyle implements ExcelCellStyle {
public class ExcelDto {
@ExcelColumn(headerName = "Field Header Title",
contentsStyle = @ExcelColumnStyle(excelCellStyleClass = CustomCellStyle.class, enumName = "CUSTOM_HEADER")
bodyStyle = @ExcelColumnStyle(excelCellStyleClass = CustomCellStyle.class, enumName = "CUSTOM_HEADER")
)
private String field1;

View File

@@ -14,4 +14,8 @@ repositories {
dependencies {
compile 'org.apache.poi:poi:4.1.2'
compile 'org.apache.poi:poi-ooxml:4.1.2'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.3.1'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.3.1'
testCompile group: 'org.assertj', name: 'assertj-core', version: '3.6.1'
}

View File

@@ -7,7 +7,7 @@ import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DefaultContentsStyle {
public @interface DefaultBodyStyle {
ExcelColumnStyle style();

View File

@@ -14,6 +14,6 @@ public @interface ExcelColumn {
String headerName() default "";
ExcelColumnStyle headerStyle() default @ExcelColumnStyle(excelCellStyleClass = NoExcelCellStyle.class);
ExcelColumnStyle contentsStyle() default @ExcelColumnStyle(excelCellStyleClass = NoExcelCellStyle.class);
ExcelColumnStyle bodyStyle() default @ExcelColumnStyle(excelCellStyleClass = NoExcelCellStyle.class);
}

View File

@@ -2,9 +2,12 @@ package com.lannstark.excel;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
public interface ExcelFile {
public interface ExcelFile<T> {
void write(OutputStream stream) throws IOException;
void addRows(List<T> data);
}

View File

@@ -15,11 +15,12 @@ import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.List;
import static com.lannstark.utils.SuperClassReflectionUtils.getField;
public abstract class SXSSFExcelFile<T> implements ExcelFile {
public abstract class SXSSFExcelFile<T> implements ExcelFile<T> {
protected static final SpreadsheetVersion supplyExcelVersion = SpreadsheetVersion.EXCEL2007;
@@ -28,28 +29,37 @@ public abstract class SXSSFExcelFile<T> implements ExcelFile {
protected ExcelRenderResource resource;
/**
* OneSheetExcelFile
*SXSSFExcelFile
* @param type Class type to be rendered
*/
public SXSSFExcelFile(Class<T> type) {
this(Collections.emptyList(), type, new DefaultDataFormatDecider());
}
/**
* SXSSFExcelFile
* @param data List Data to render excel file. data should have at least one @ExcelColumn on fields
* @param type Class type to be rendered
*/
public SXSSFExcelFile(List<T> data, Class<T> type) {
this.wb = new SXSSFWorkbook();
this.resource = ExcelRenderResourceFactory.prepareRenderResource(type, wb, new DefaultDataFormatDecider());
renderExcel(data);
this(data, type, new DefaultDataFormatDecider());
}
/**
* OneSheetExcelFile
* SXSSFExcelFile
* @param data List Data to render excel file. data should have at least one @ExcelColumn on fields
* @param type Class type to be rendered
* @param dataFormatDecider Custom DataFormatDecider
*/
public SXSSFExcelFile(List<T> data, Class<T> type, DataFormatDecider dataFormatDecider) {
validateData(data);
this.wb = new SXSSFWorkbook();
this.resource = ExcelRenderResourceFactory.prepareRenderResource(type, wb, dataFormatDecider);
renderExcel(data);
}
protected void validateData(List<T> data) { }
protected abstract void renderExcel(List<T> data);
protected void renderHeadersWithNewSheet(Sheet sheet, int rowIndex, int columnStartIndex) {
@@ -62,7 +72,7 @@ public abstract class SXSSFExcelFile<T> implements ExcelFile {
}
}
protected void renderContent(Object data, int rowIndex, int columnStartIndex) {
protected void renderBody(Object data, int rowIndex, int columnStartIndex) {
Row row = sheet.createRow(rowIndex);
int columnIndex = columnStartIndex;
for (String dataFieldName : resource.getDataFieldNames()) {
@@ -70,7 +80,7 @@ public abstract class SXSSFExcelFile<T> implements ExcelFile {
try {
Field field = getField(data.getClass(), (dataFieldName));
field.setAccessible(true);
cell.setCellStyle(resource.getCellStyle(dataFieldName, ExcelRenderLocation.CONTENTS));
cell.setCellStyle(resource.getCellStyle(dataFieldName, ExcelRenderLocation.BODY));
Object cellValue = field.get(data);
renderCellValue(cell, cellValue);
} catch (Exception e) {

View File

@@ -12,13 +12,19 @@ import java.util.List;
* - support Excel Version over 2007
* - support multi sheet rendering
* - support Dffierent DataFormat by Class Type
* - support Custom CellStyle according to (header or contents) and data field
* - support Custom CellStyle according to (header or body) and data field
*/
public class MultiSheetExcelFile<T> extends SXSSFExcelFile<T> {
private static final int maxRowCanBeRendered = supplyExcelVersion.getMaxRows() - 1;
private static final int ROW_START_INDEX = 0;
private static final int COLUMN_START_INDEX = 0;
private int currentRowIndex = ROW_START_INDEX;
public MultiSheetExcelFile(Class<T> type) {
super(type);
wb.setZip64Mode(Zip64Mode.Always);
}
/*
* If you use SXSSF with hug data, you need to set zip mode
@@ -42,16 +48,17 @@ public class MultiSheetExcelFile<T> extends SXSSFExcelFile<T> {
return ;
}
// 2. Render body
createNewSheetWithHeader();
int renderedDataCnt = 0;
int rowIndex = ROW_START_INDEX + 1;
for (Object renderedData : data) {
renderContent(renderedData, rowIndex++, COLUMN_START_INDEX);
renderedDataCnt ++;
addRows(data);
}
if (renderedDataCnt == maxRowCanBeRendered) {
renderedDataCnt = 0;
rowIndex = 1;
@Override
public void addRows(List<T> data) {
for (Object renderedData : data) {
renderBody(renderedData, currentRowIndex++, COLUMN_START_INDEX);
if (currentRowIndex == maxRowCanBeRendered) {
currentRowIndex = 1;
createNewSheetWithHeader();
}
}
@@ -60,6 +67,7 @@ public class MultiSheetExcelFile<T> extends SXSSFExcelFile<T> {
private void createNewSheetWithHeader() {
sheet = wb.createSheet();
renderHeadersWithNewSheet(sheet, ROW_START_INDEX, COLUMN_START_INDEX);
currentRowIndex++;
}
}

View File

@@ -10,25 +10,29 @@ import java.util.List;
*
* - support Excel Version over 2007
* - support one sheet rendering
* - support Dffierent DataFormat by Class Type
* - support Custom CellStyle according to (header or contents) and data field
* - support different DataFormat by Class Type
* - support Custom CellStyle according to (header or body) and data field
*/
public final class OneSheetExcelFile<T> extends SXSSFExcelFile<T> {
private static final int ROW_START_INDEX = 0;
private static final int COLUMN_START_INDEX = 0;
private int currentRowIndex = ROW_START_INDEX;
public OneSheetExcelFile(Class<T> type) {
super(type);
}
public OneSheetExcelFile(List<T> data, Class<T> type) {
super(data, type);
validateMaxRow(data);
}
public OneSheetExcelFile(List<T> data, Class<T> type, DataFormatDecider dataFormatDecider) {
super(data, type, dataFormatDecider);
validateMaxRow(data);
}
private void validateMaxRow(List<?> data) {
@Override
protected void validateData(List<T> data) {
int maxRows = supplyExcelVersion.getMaxRows();
if (data.size() > maxRows) {
throw new IllegalArgumentException(
@@ -40,17 +44,21 @@ public final class OneSheetExcelFile<T> extends SXSSFExcelFile<T> {
public void renderExcel(List<T> data) {
// 1. Create sheet and renderHeader
sheet = wb.createSheet();
renderHeadersWithNewSheet(sheet, ROW_START_INDEX, COLUMN_START_INDEX);
renderHeadersWithNewSheet(sheet, currentRowIndex++, COLUMN_START_INDEX);
if (data.isEmpty()) {
return;
}
// 2. Render Contents
int rowIndex = ROW_START_INDEX + 1;
// 2. Render Body
for (Object renderedData : data) {
renderContent(renderedData, rowIndex++, COLUMN_START_INDEX);
renderBody(renderedData, currentRowIndex++, COLUMN_START_INDEX);
}
}
@Override
public void addRows(List<T> data) {
renderBody(data, currentRowIndex++, COLUMN_START_INDEX);
}
}

View File

@@ -8,26 +8,37 @@ import java.util.List;
public class DefaultDataFormatDecider implements DataFormatDecider {
private static final String CURRENT_FORMAT = "#,##0";
private static final String FLOAT_FORMAT_2_DECIMAL_PLACES = "#,##0.00";
private static final String DEFAULT_FORMAT = "";
@Override
public short getDataFormat(DataFormat dataFormat, Class<?> type) {
if (isNumericType(type)) {
if (isFloatType(type)) {
return dataFormat.getFormat(FLOAT_FORMAT_2_DECIMAL_PLACES);
}
if (isIntegerType(type)) {
return dataFormat.getFormat(CURRENT_FORMAT);
}
return dataFormat.getFormat(DEFAULT_FORMAT);
}
private boolean isNumericType(Class<?> type) {
List<Class<?>> numericTypes = Arrays.asList(
Byte.class, byte.class,
Short.class, short.class,
Integer.class, int.class,
Long.class, long.class,
private boolean isFloatType(Class<?> type) {
List<Class<?>> floatTypes = Arrays.asList(
Float.class, float.class,
Double.class, double.class
);
return numericTypes.contains(type);
return floatTypes.contains(type);
}
private boolean isIntegerType(Class<?> type) {
List<Class<?>> integerTypes = Arrays.asList(
Byte.class, byte.class,
Short.class, short.class,
Integer.class, int.class,
Long.class, long.class
);
return integerTypes.contains(type);
}
}

View File

@@ -2,6 +2,6 @@ package com.lannstark.resource;
public enum ExcelRenderLocation {
HEADER, CONTENTS
HEADER, BODY
}

View File

@@ -29,14 +29,6 @@ public class ExcelRenderResource {
return excelHeaderNames.get(dataFieldName);
}
public PreCalculatedCellStyleMap getStyleMap() {
return styleMap;
}
public Map<String, String> getExcelHeaderNames() {
return excelHeaderNames;
}
public List<String> getDataFieldNames() {
return dataFieldNames;
}

View File

@@ -1,6 +1,6 @@
package com.lannstark.resource;
import com.lannstark.DefaultContentsStyle;
import com.lannstark.DefaultBodyStyle;
import com.lannstark.DefaultHeaderStyle;
import com.lannstark.ExcelColumn;
import com.lannstark.ExcelColumnStyle;
@@ -34,7 +34,7 @@ public final class ExcelRenderResourceFactory {
List<String> fieldNames = new ArrayList<>();
ExcelColumnStyle classDefinedHeaderStyle = getHeaderExcelColumnStyle(type);
ExcelColumnStyle classDefinedContentsStyle = getContentsExcelColumnStyle(type);
ExcelColumnStyle classDefinedBodyStyle = getBodyExcelColumnStyle(type);
for (Field field : getAllFields(type)) {
if (field.isAnnotationPresent(ExcelColumn.class)) {
@@ -46,8 +46,8 @@ public final class ExcelRenderResourceFactory {
Class<?> fieldType = field.getType();
styleMap.put(
fieldType,
ExcelCellKey.of(field.getName(), ExcelRenderLocation.CONTENTS),
getCellStyle(decideAppliedStyleAnnotation(classDefinedContentsStyle, annotation.contentsStyle())), wb);
ExcelCellKey.of(field.getName(), ExcelRenderLocation.BODY),
getCellStyle(decideAppliedStyleAnnotation(classDefinedBodyStyle, annotation.bodyStyle())), wb);
fieldNames.add(field.getName());
headerNamesMap.put(field.getName(), annotation.headerName());
}
@@ -67,12 +67,12 @@ public final class ExcelRenderResourceFactory {
return ((DefaultHeaderStyle) annotation).style();
}
private static ExcelColumnStyle getContentsExcelColumnStyle(Class<?> clazz) {
Annotation annotation = getAnnotation(clazz, DefaultContentsStyle.class);
private static ExcelColumnStyle getBodyExcelColumnStyle(Class<?> clazz) {
Annotation annotation = getAnnotation(clazz, DefaultBodyStyle.class);
if (annotation == null) {
return null;
}
return ((DefaultContentsStyle) annotation).style();
return ((DefaultBodyStyle) annotation).style();
}
private static ExcelColumnStyle decideAppliedStyleAnnotation(ExcelColumnStyle classAnnotation,
@@ -100,7 +100,7 @@ public final class ExcelRenderResourceFactory {
}
@SuppressWarnings("unchecked")
private static ExcelCellStyle findExcelCellStyle(Class excelCellStyles, String enumName) {
private static ExcelCellStyle findExcelCellStyle(Class<?> excelCellStyles, String enumName) {
try {
return (ExcelCellStyle) Enum.valueOf((Class<Enum>) excelCellStyles, enumName);
} catch (NullPointerException e) {

View File

@@ -14,15 +14,15 @@ import java.util.Map;
* PreCalculatedCellStyleMap
*
* Determines cell's style
* In currently, PreCalculatedCellSylteMap determines {org.apache.poi.ss.usermodel.DataFormat}
* In currently, PreCalculatedCellStyleMap determines {org.apache.poi.ss.usermodel.DataFormat}
*
*/
public class PreCalculatedCellStyleMap {
private final DataFormatDecider store;
private final DataFormatDecider dataFormatDecider;
public PreCalculatedCellStyleMap(DataFormatDecider store) {
this.store = store;
public PreCalculatedCellStyleMap(DataFormatDecider dataFormatDecider) {
this.dataFormatDecider = dataFormatDecider;
}
private final Map<ExcelCellKey, CellStyle> cellStyleMap = new HashMap<>();
@@ -30,7 +30,7 @@ public class PreCalculatedCellStyleMap {
public void put(Class<?> fieldType, ExcelCellKey excelCellKey, ExcelCellStyle excelCellStyle, Workbook wb) {
CellStyle cellStyle = wb.createCellStyle();
DataFormat dataFormat = wb.createDataFormat();
cellStyle.setDataFormat(store.getDataFormat(dataFormat, fieldType));
cellStyle.setDataFormat(dataFormatDecider.getDataFormat(dataFormat, fieldType));
excelCellStyle.apply(cellStyle);
cellStyleMap.put(excelCellKey, cellStyle);
}

View File

@@ -17,7 +17,7 @@ public enum DefaultExcelCellStyle implements ExcelCellStyle {
DefaultExcelBorders.newInstance(ExcelBorderStyle.THIN), DefaultExcelAlign.CENTER_CENTER),
BLUE_HEADER(DefaultExcelColor.rgb(223, 235, 246),
DefaultExcelBorders.newInstance(ExcelBorderStyle.THIN), DefaultExcelAlign.CENTER_CENTER),
CONTENTS(DefaultExcelColor.rgb(255, 255, 255),
BODY(DefaultExcelColor.rgb(255, 255, 255),
DefaultExcelBorders.newInstance(ExcelBorderStyle.THIN), DefaultExcelAlign.RIGHT_CENTER);
private final ExcelColor backgroundColor;

View File

@@ -15,7 +15,7 @@ public final class SuperClassReflectionUtils {
public static List<Field> getAllFields(Class<?> clazz) {
List<Field> fields = new ArrayList<>();
for (Class clazzInClasses : getAllClassesIncludingSuperClasses(clazz, true)) {
for (Class<?> clazzInClasses : getAllClassesIncludingSuperClasses(clazz, true)) {
fields.addAll(Arrays.asList(clazzInClasses.getDeclaredFields()));
}
return fields;
@@ -23,7 +23,7 @@ public final class SuperClassReflectionUtils {
public static Annotation getAnnotation(Class<?> clazz,
Class<? extends Annotation> targetAnnotation) {
for (Class clazzInClasses : getAllClassesIncludingSuperClasses(clazz, false)) {
for (Class<?> clazzInClasses : getAllClassesIncludingSuperClasses(clazz, false)) {
if (clazzInClasses.isAnnotationPresent(targetAnnotation)) {
return clazzInClasses.getAnnotation(targetAnnotation);
}
@@ -32,7 +32,7 @@ public final class SuperClassReflectionUtils {
}
public static Field getField(Class<?> clazz, String name) throws Exception {
for (Class clazzInClasses : getAllClassesIncludingSuperClasses(clazz, false)) {
for (Class<?> clazzInClasses : getAllClassesIncludingSuperClasses(clazz, false)) {
for (Field field : clazzInClasses.getDeclaredFields()) {
if (field.getName().equals(name)) {
return clazzInClasses.getDeclaredField(name);
@@ -42,8 +42,8 @@ public final class SuperClassReflectionUtils {
throw new NoSuchFieldException();
}
private static List<Class> getAllClassesIncludingSuperClasses(Class<?> clazz, boolean fromSuper) {
List<Class> classes = new ArrayList<>();
private static List<Class<?>> getAllClassesIncludingSuperClasses(Class<?> clazz, boolean fromSuper) {
List<Class<?>> classes = new ArrayList<>();
while (clazz != null) {
classes.add(clazz);
clazz = clazz.getSuperclass();

View File

@@ -0,0 +1,21 @@
package com.lannstark.dto;
import com.lannstark.DefaultHeaderStyle;
import com.lannstark.ExcelColumn;
import com.lannstark.ExcelColumnStyle;
import com.lannstark.style.BlueHeaderStyle;
import com.lannstark.style.BlackHeaderStyle;
@DefaultHeaderStyle(style = @ExcelColumnStyle(excelCellStyleClass = BlueHeaderStyle.class))
public class ExcelDto {
@ExcelColumn(headerName = "name")
private String name;
private String hideColumn;
@ExcelColumn(headerName = "age",
headerStyle = @ExcelColumnStyle(excelCellStyleClass = BlackHeaderStyle.class))
private int age;
}

View File

@@ -0,0 +1,43 @@
package com.lannstark.resource;
import com.lannstark.dto.ExcelDto;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFColor;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import static org.assertj.core.api.Assertions.assertThat;
public class ExcelRenderResourceFactoryTest {
@Test
public void excelRenderResourceCreationTest() {
// given & when
ExcelRenderResource resource
= ExcelRenderResourceFactory.prepareRenderResource(ExcelDto.class, new SXSSFWorkbook(), new DefaultDataFormatDecider());
// then
assertThat(resource.getDataFieldNames()).isEqualTo(Arrays.asList("name", "age"));
assertCenterThinCellStyle(resource.getCellStyle("name", ExcelRenderLocation.HEADER), (byte) 223, (byte) 235, (byte) 246);
assertCenterThinCellStyle(resource.getCellStyle("age", ExcelRenderLocation.HEADER), (byte) 0, (byte) 0, (byte) 0);
}
private void assertCenterThinCellStyle(CellStyle cellStyle,
byte red, byte green, byte blue) {
assertThat(cellStyle.getAlignment()).isEqualTo(HorizontalAlignment.CENTER);
assertThat(cellStyle.getVerticalAlignment()).isEqualTo(VerticalAlignment.CENTER);
assertThat(cellStyle.getBorderTop()).isEqualTo(BorderStyle.THIN);
assertThat(cellStyle.getBorderRight()).isEqualTo(BorderStyle.THIN);
assertThat(cellStyle.getBorderLeft()).isEqualTo(BorderStyle.THIN);
assertThat(cellStyle.getBorderBottom()).isEqualTo(BorderStyle.THIN);
XSSFColor nameHeaderCellColor = (XSSFColor) cellStyle.getFillForegroundColorColor();
assertThat(nameHeaderCellColor.getRGB()).isEqualTo(new Byte[]{red, green, blue});
}
}

View File

@@ -0,0 +1,17 @@
package com.lannstark.style;
import com.lannstark.style.align.DefaultExcelAlign;
import com.lannstark.style.border.DefaultExcelBorders;
import com.lannstark.style.border.ExcelBorderStyle;
import com.lannstark.style.configurer.ExcelCellStyleConfigurer;
public class BlackHeaderStyle extends CustomExcelCellStyle {
@Override
public void configure(ExcelCellStyleConfigurer configurer) {
configurer.foregroundColor(0, 0, 0)
.excelBorders(DefaultExcelBorders.newInstance(ExcelBorderStyle.THIN))
.excelAlign(DefaultExcelAlign.CENTER_CENTER);
}
}

View File

@@ -0,0 +1,17 @@
package com.lannstark.style;
import com.lannstark.style.align.DefaultExcelAlign;
import com.lannstark.style.border.DefaultExcelBorders;
import com.lannstark.style.border.ExcelBorderStyle;
import com.lannstark.style.configurer.ExcelCellStyleConfigurer;
public class BlueHeaderStyle extends CustomExcelCellStyle {
@Override
public void configure(ExcelCellStyleConfigurer configurer) {
configurer.foregroundColor(223, 235, 246)
.excelBorders(DefaultExcelBorders.newInstance(ExcelBorderStyle.THIN))
.excelAlign(DefaultExcelAlign.CENTER_CENTER);
}
}