diff --git a/README.adoc b/README.adoc index 357715a..4e544d1 100644 --- a/README.adoc +++ b/README.adoc @@ -3,7 +3,7 @@ This is an advanced fork of https://github.com/ozlerhakan/poiji. + Many thanks to ozlerhakan for inspiration. -Apache POI library required as dependency to work with xlsx and xls. Tested with dependency 'org.apache.poi:poi-ooxml:4.1.2'. +Apache POI library required as dependency to work with xlsx and xls. Tested with dependency 'org.apache.poi:poi-ooxml:5.2.5'. In your Maven/Gradle project, first add the corresponding dependency: @@ -14,7 +14,7 @@ In your Maven/Gradle project, first add the corresponding dependency: io.github.vaa25 poiji2 - 1.4.0 + 1.4.1 org.apache.poi @@ -29,7 +29,7 @@ In your Maven/Gradle project, first add the corresponding dependency: [source,groovy] ---- dependencies { - implementation 'io.github.vaa25:poiji2:1.4.0' + implementation 'io.github.vaa25:poiji2:1.4.1' implementation 'org.apache.poi:poi-ooxml:5.2.5' } ---- @@ -51,3 +51,4 @@ Also: - Poiji2 can read lists in row (use `@ExcelList` on `List`) - Poiji2 can read and write huge xlsx files (see HugeTest.java) - Poiji2 (since v1.4.0) can work with immutable java classes (see IgnoreTest.java). lombok @Value and java records applicable also. +- Poiji2 (since v1.4.1) can work with immutable java classes with many constructors (see ExcelConstructorTest.java). Apply @ExcelConstructor to choose one of. diff --git a/src/main/java/com/poiji/annotation/ExcelConstructor.java b/src/main/java/com/poiji/annotation/ExcelConstructor.java new file mode 100644 index 0000000..b49c378 --- /dev/null +++ b/src/main/java/com/poiji/annotation/ExcelConstructor.java @@ -0,0 +1,15 @@ +package com.poiji.annotation; + +import java.lang.annotation.*; + +/** + * Marks constructor what should be used by Poiji. + * + * Created by vaa25 on 16.03.24. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.CONSTRUCTOR) +@Documented +public @interface ExcelConstructor { + +} diff --git a/src/main/java/com/poiji/bind/mapping/ReadMappedFields.java b/src/main/java/com/poiji/bind/mapping/ReadMappedFields.java index 139fa41..b782d3d 100644 --- a/src/main/java/com/poiji/bind/mapping/ReadMappedFields.java +++ b/src/main/java/com/poiji/bind/mapping/ReadMappedFields.java @@ -183,9 +183,7 @@ private List parseExcelRow(final List fields) { final ExcelRow annotation = field.getAnnotation(ExcelRow.class); if (annotation != null) { this.excelRow.add(field); - if (!field.isAccessible()) { - field.setAccessible(true); - } + ReflectUtil.setAccessible(field); } else { rest.add(field); } @@ -199,9 +197,7 @@ private List parseExcelError(final List fields) { final ExcelParseExceptions annotation = field.getAnnotation(ExcelParseExceptions.class); if (annotation != null) { this.excelParseException.add(field); - if (!field.isAccessible()) { - field.setAccessible(true); - } + ReflectUtil.setAccessible(field); } else { rest.add(field); } @@ -223,9 +219,7 @@ private List parseExcelCellName(final List fields) { final String name = options.getFormatting().transform(options, possibleFieldName); namedFields.put(name, field); } - if (!field.isAccessible()) { - field.setAccessible(true); - } + ReflectUtil.setAccessible(field); } else { rest.add(field); } @@ -243,9 +237,7 @@ private List parseUnknownCells(final List fields) { for (final Field field : fields) { if (field.getAnnotation(ExcelUnknownCells.class) != null && field.getType().isAssignableFrom(Map.class)) { unknownFields.add(field); - if (!field.isAccessible()) { - field.setAccessible(true); - } + ReflectUtil.setAccessible(field); } else { rest.add(field); } @@ -258,9 +250,7 @@ private List parseExcelCellRange(final List fields) { for (final Field field : fields) { if (field.getAnnotation(ExcelCellRange.class) != null) { rangeFields.put(field, new ReadMappedFields(field.getType(), options).parseEntity()); - if (!field.isAccessible()) { - field.setAccessible(true); - } + ReflectUtil.setAccessible(field); } else { rest.add(field); } @@ -275,9 +265,7 @@ private List parseExcelList(final List fields) { if (field.getType().isAssignableFrom(List.class) && annotation != null) { final Class entity = (Class) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0]; listFields.put(field, new ReadMappedList(annotation, entity, options)); - if (!field.isAccessible()) { - field.setAccessible(true); - } + ReflectUtil.setAccessible(field); } else { rest.add(field); } @@ -291,9 +279,7 @@ private List parseExcelCell(final List fields) { if (field.getAnnotation(ExcelCell.class) != null) { final Integer excelOrder = field.getAnnotation(ExcelCell.class).value(); orderedFields.put(excelOrder, field); - if (!field.isAccessible()) { - field.setAccessible(true); - } + ReflectUtil.setAccessible(field); } else { rest.add(field); } diff --git a/src/main/java/com/poiji/save/MappedFields.java b/src/main/java/com/poiji/save/MappedFields.java index 24cc78e..01d872a 100644 --- a/src/main/java/com/poiji/save/MappedFields.java +++ b/src/main/java/com/poiji/save/MappedFields.java @@ -7,6 +7,8 @@ import com.poiji.bind.mapping.SheetNameExtractor; import com.poiji.exception.PoijiException; import com.poiji.option.PoijiOptions; +import com.poiji.util.ReflectUtil; + import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collection; @@ -50,14 +52,10 @@ public MappedFields parseEntity() { final String name = field.getName(); orders.put(field, excelOrder); names.put(field, name); - if (!field.isAccessible()) { - field.setAccessible(true); - } + ReflectUtil.setAccessible(field); } else if (field.getAnnotation(ExcelUnknownCells.class) != null) { unknownCells.add(field); - if (!field.isAccessible()) { - field.setAccessible(true); - } + ReflectUtil.setAccessible(field); } else { final ExcelCellName annotation = field.getAnnotation(ExcelCellName.class); if (annotation != null) { @@ -72,9 +70,7 @@ public MappedFields parseEntity() { orders.put(field, order); } names.put(field, excelName); - if (!field.isAccessible()) { - field.setAccessible(true); - } + ReflectUtil.setAccessible(field); } } } diff --git a/src/main/java/com/poiji/util/ConstructorFieldMapper.java b/src/main/java/com/poiji/util/ConstructorFieldMapper.java index 6237e4d..ce30b05 100644 --- a/src/main/java/com/poiji/util/ConstructorFieldMapper.java +++ b/src/main/java/com/poiji/util/ConstructorFieldMapper.java @@ -2,6 +2,7 @@ import com.poiji.exception.PoijiInstantiationException; +import java.beans.ConstructorProperties; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; @@ -18,40 +19,85 @@ static Object[] getConstructorFields(Constructor constructor) { } private static Object[] getConstructorMapping(Constructor constructor) { + final Object[] constructorMapping = new Object[constructor.getParameterCount()]; + mapFieldsWithConstructorProperties(constructor, constructorMapping); + findNotMappedFieldsWithExamining(constructor, constructorMapping); + fillNotMappedParametersWithDefaults(constructor, constructorMapping); + return constructorMapping; + } + + /** + * ConstructorProperties is used by lombok. It allows to map any custom fields easy without examining. + */ + private static Object[] mapFieldsWithConstructorProperties(Constructor constructor, Object[] constructorMapping) { + final ConstructorProperties constructorProperties = constructor.getAnnotation(ConstructorProperties.class); + if (constructorProperties != null) { + final Field[] fields = constructor.getDeclaringClass().getDeclaredFields(); + final String[] parameterNames = constructorProperties.value(); + final Class[] parameterTypes = constructor.getParameterTypes(); + for (int i = 0; i < parameterNames.length; i++) { + final String parameterName = parameterNames[i]; + for (Field field : fields) { + if (field.getName().equals(parameterName) && field.getType() == parameterTypes[i]) { + ReflectUtil.setAccessible(field); + constructorMapping[i] = field; + break; + } + } + } + } + return constructorMapping; + } + + /** + * The only way to find mapping between constructor parameters and instance fields is to pass special value + * into constructor and look it up in every field in instance. + * Knowing what parameter was passed and what field was found in we can define one mapping. + */ + private static Object[] findNotMappedFieldsWithExamining(Constructor constructor, Object[] constructorMapping) { final Class entity = constructor.getDeclaringClass(); final Class[] parameterTypes = constructor.getParameterTypes(); - final Object[] defaults = fieldDefaultsMapping.computeIfAbsent(constructor, ignored -> getDefaultValues(parameterTypes)); - final Object[] constructorMapping = new Object[parameterTypes.length]; + final Object[] defaultConstructorParameters = fieldDefaultsMapping.computeIfAbsent(constructor, ignored -> getDefaultValues(parameterTypes)); + final Field[] fields = entity.getDeclaredFields(); for (int i = 0; i < parameterTypes.length; i++) { - final Object[] clone = defaults.clone(); - final Class parameterType = parameterTypes[i]; - final Object example = ImmutableInstanceRegistrar.getEmptyInstance(parameterType); - clone[i] = example; - try { - final Object instance = constructor.newInstance(clone); - final Field[] fields = entity.getDeclaredFields(); - for (Field field : fields) { - if (!field.isAccessible()) { - field.setAccessible(true); - } - try { - if (isFieldHasExampleValue(field, instance, example)) { - constructorMapping[i] = field; - break; + if (constructorMapping[i] == null) { + final Object[] instanceConstructorParameters = defaultConstructorParameters.clone(); + final Class parameterType = parameterTypes[i]; + final Object parameterToExamine = ImmutableInstanceRegistrar.getEmptyInstance(parameterType); + instanceConstructorParameters[i] = parameterToExamine; + try { + final Object instance = constructor.newInstance(instanceConstructorParameters); + for (Field field : fields) { + ReflectUtil.setAccessible(field); + try { + if (isFieldHasExampleValue(field, instance, parameterToExamine)) { + constructorMapping[i] = field; + break; + } + } catch (IllegalAccessException e) { + throw new PoijiInstantiationException("Can't get field " + field, e); } - } catch (IllegalAccessException e) { - throw new PoijiInstantiationException("Can't get field " + field, e); } + } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { + throw new PoijiInstantiationException("Can't create instance " + entity, e); } - } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { - throw new PoijiInstantiationException("Can't create instance " + entity, e); } + } + return constructorMapping; + } + + /** + * Sometimes constructor can have parameter that not corresponds to any field. + * We have to fill it anyway to construct instance successfully. + */ + private static Object[] fillNotMappedParametersWithDefaults(Constructor constructor, Object[] constructorMapping) { + final Object[] defaultConstructorParameters = fieldDefaultsMapping.computeIfAbsent(constructor, ignored -> getDefaultValues(constructor.getParameterTypes())); + for (int i = 0; i < constructorMapping.length; i++) { if (constructorMapping[i] == null) { - constructorMapping[i] = defaults[i]; + constructorMapping[i] = defaultConstructorParameters[i]; } } return constructorMapping; - } private static boolean isFieldHasExampleValue(Field field, Object instance, Object example) throws IllegalAccessException { diff --git a/src/main/java/com/poiji/util/ConstructorSelector.java b/src/main/java/com/poiji/util/ConstructorSelector.java new file mode 100644 index 0000000..b89f2e5 --- /dev/null +++ b/src/main/java/com/poiji/util/ConstructorSelector.java @@ -0,0 +1,53 @@ +package com.poiji.util; + +import com.poiji.annotation.ExcelConstructor; +import com.poiji.exception.PoijiInstantiationException; + +import java.lang.reflect.Constructor; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class ConstructorSelector { + + private static final Map, Constructor> fieldDefaultsMapping = new ConcurrentHashMap<>(); + + public static Constructor selectConstructor(Class type) { + return fieldDefaultsMapping.computeIfAbsent(type, ignored -> setAccessible(defineConstructor(type))); + } + + private static Constructor setAccessible(Constructor constructor) { + if (!constructor.isAccessible()) { + constructor.setAccessible(true); + } + return constructor; + } + + private static Constructor defineConstructor(Class type) { + final Constructor[] constructors = type.getDeclaredConstructors(); + if (constructors.length > 1) { + final Constructor constructor = getMarkedConstructor(type, constructors); + if (constructor == null) { + final String annotation = ExcelConstructor.class.getSimpleName(); + final String message = String.format("Several constructors were found in %s. Mark one of it with @%s please.", type.getName(), annotation); + throw new PoijiInstantiationException(message, null); + } + return constructor; + } + return constructors[0]; + } + + private static Constructor getMarkedConstructor(Class type, Constructor[] constructors) { + Constructor result = null; + for (Constructor constructor : constructors) { + if (constructor.isAnnotationPresent(ExcelConstructor.class)) { + if (result != null) { + final String annotation = ExcelConstructor.class.getSimpleName(); + final String message = String.format("Several constructors are marked with @%s in %s. Mark only one of it please.", annotation, type.getName()); + throw new PoijiInstantiationException(message, null); + } + result = constructor; + } + } + return result; + } +} diff --git a/src/main/java/com/poiji/util/ImmutableInstanceRegistrar.java b/src/main/java/com/poiji/util/ImmutableInstanceRegistrar.java index e11e1df..bbb9aa3 100644 --- a/src/main/java/com/poiji/util/ImmutableInstanceRegistrar.java +++ b/src/main/java/com/poiji/util/ImmutableInstanceRegistrar.java @@ -1,6 +1,7 @@ package com.poiji.util; import com.poiji.exception.PoijiException; +import com.poiji.exception.PoijiInstantiationException; import java.math.BigDecimal; import java.time.LocalDate; @@ -51,6 +52,6 @@ static T getEmptyInstance(Class type) { } final String message = String.format("Use %s.register() to register empty instance for '%s' first", ImmutableInstanceRegistrar.class.getName(), type.getName()); - throw new PoijiException(message); + throw new PoijiInstantiationException(message, null); } } diff --git a/src/main/java/com/poiji/util/ReflectUtil.java b/src/main/java/com/poiji/util/ReflectUtil.java index 9d75894..4d703c8 100644 --- a/src/main/java/com/poiji/util/ReflectUtil.java +++ b/src/main/java/com/poiji/util/ReflectUtil.java @@ -3,6 +3,7 @@ import com.poiji.annotation.ExcelCellRange; import com.poiji.bind.mapping.Data; import com.poiji.exception.IllegalCastException; +import com.poiji.exception.PoijiException; import com.poiji.exception.PoijiInstantiationException; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; @@ -39,15 +40,14 @@ public static T newInstanceOf(Data data) { public static T newInstanceOf(Class type, Data data) { try { - final Constructor constructor = type.getDeclaredConstructors()[0]; - if (!constructor.isAccessible()) { - constructor.setAccessible(true); - } + final Constructor constructor = ConstructorSelector.selectConstructor(type); if (constructor.getParameterCount() == 0) { return createInstanceUsingDefaultConstructor(data, constructor); } else { return createInstanceUsingParameterizedConstructor(data, constructor); } + } catch (PoijiInstantiationException exception) { + throw exception; } catch (Exception ex) { throw new PoijiInstantiationException("Cannot create a new instance of " + type.getName(), ex); } @@ -158,12 +158,16 @@ public static Collection findRecursivePoijiAnnotati public static void setFieldData(Field field, Object o, Object instance) { try { - if (!field.isAccessible()) { - field.setAccessible(true); - } + setAccessible(field); field.set(instance, o); } catch (IllegalAccessException e) { throw new IllegalCastException("Unexpected cast type {" + o + "} of field" + field.getName()); } } + + public static void setAccessible(Field field) { + if (!field.isAccessible()) { + field.setAccessible(true); + } + } } diff --git a/src/test/java/com/poiji/deserialize/ExcelConstructorTest.java b/src/test/java/com/poiji/deserialize/ExcelConstructorTest.java new file mode 100644 index 0000000..e53b59f --- /dev/null +++ b/src/test/java/com/poiji/deserialize/ExcelConstructorTest.java @@ -0,0 +1,64 @@ +package com.poiji.deserialize; + +import com.poiji.bind.Poiji; +import com.poiji.deserialize.model.*; +import com.poiji.exception.PoijiInstantiationException; +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.List; + +import static java.util.Arrays.asList; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.*; + +@RunWith(Parameterized.class) +public class ExcelConstructorTest { + + private final String path; + + public ExcelConstructorTest(final String path) { + this.path = path; + } + + @Parameterized.Parameters + public static List excel() { + return asList( + "src/test/resources/excel-list.xlsx", + "src/test/resources/excel-list.xls", + "src/test/resources/excel-list.csv" + ); + } + + @Test + public void selectConstructorWithAnnotation() { + final List expected = asList( + new OneExcelConstructor(1), + new OneExcelConstructor(2) + ); + + final PoijiOptions options = PoijiOptions.PoijiOptionsBuilder.settings().headerCount(2).preferNullOverDefault(true).build(); + final List read = Poiji.fromExcel(new File(path), OneExcelConstructor.class, options); + assertThat(read, equalTo(expected)); + } + + @Test + public void trySelectConstructorWithoutAnnotation() { + final PoijiOptions options = PoijiOptions.PoijiOptionsBuilder.settings().headerCount(2).preferNullOverDefault(true).build(); + final PoijiInstantiationException exception = assertThrows(PoijiInstantiationException.class, + () -> Poiji.fromExcel(new File(path), NoExcelConstructor.class, options)); + assertEquals("Several constructors were found in com.poiji.deserialize.model.NoExcelConstructor. Mark one of it with @ExcelConstructor please.", exception.getMessage()); + } + + @Test + public void trySelectConstructorWithManyAnnotations() { + final PoijiOptions options = PoijiOptions.PoijiOptionsBuilder.settings().headerCount(2).preferNullOverDefault(true).build(); + final PoijiInstantiationException exception = assertThrows(PoijiInstantiationException.class, + () -> Poiji.fromExcel(new File(path), ManyExcelConstructor.class, options)); + assertEquals("Several constructors are marked with @ExcelConstructor in com.poiji.deserialize.model.ManyExcelConstructor. Mark only one of it please.", exception.getMessage()); + } + +} diff --git a/src/test/java/com/poiji/deserialize/ReadExcelListTest.java b/src/test/java/com/poiji/deserialize/ReadExcelListTest.java index f12d374..3e1b35e 100644 --- a/src/test/java/com/poiji/deserialize/ReadExcelListTest.java +++ b/src/test/java/com/poiji/deserialize/ReadExcelListTest.java @@ -1,11 +1,8 @@ package com.poiji.deserialize; import com.poiji.bind.Poiji; +import com.poiji.deserialize.model.*; import com.poiji.util.ImmutableInstanceRegistrar; -import com.poiji.deserialize.model.ListElement; -import com.poiji.deserialize.model.ListElementImmutable; -import com.poiji.deserialize.model.ListEntity; -import com.poiji.deserialize.model.ListImmutable; import com.poiji.option.PoijiOptions; import java.io.File; import java.io.FileNotFoundException; @@ -71,4 +68,38 @@ public void readImmutable() { assertThat(read, equalTo(expected)); } + @Test + public void readConstructorProperties() { + final ListElementConstructorProperties person11 = new ListElementConstructorProperties(2, "test", ListElementConstructorProperties.Gender.male, 10); + final ListElementConstructorProperties person12 = new ListElementConstructorProperties(2, "gogo", ListElementConstructorProperties.Gender.female,20); + final ListElementConstructorProperties person21 = new ListElementConstructorProperties(3, "abc", ListElementConstructorProperties.Gender.male, 30); + final ListElementConstructorProperties person22 = new ListElementConstructorProperties(3, "vivi", ListElementConstructorProperties.Gender.female, 40); + final ListElementConstructorProperties person23 = new ListElementConstructorProperties(3, "vava", ListElementConstructorProperties.Gender.female, 40); + final List expected = asList( + new ListConstructorProperties(1, asList(person11, person12)), + new ListConstructorProperties(2, asList(person21, person22, person23)) + ); + + final PoijiOptions options = PoijiOptions.PoijiOptionsBuilder.settings().headerCount(2).preferNullOverDefault(true).build(); + final List read = Poiji.fromExcel(new File(path), ListConstructorProperties.class, options); + assertThat(read, equalTo(expected)); + } + + @Test + public void readConstructorProperties2() { + final ListElementConstructorProperties2 person11 = new ListElementConstructorProperties2(2, "test", ListElementConstructorProperties2.Gender.male, 10, '0'); + final ListElementConstructorProperties2 person12 = new ListElementConstructorProperties2(2, "gogo", ListElementConstructorProperties2.Gender.female,20, '0'); + final ListElementConstructorProperties2 person21 = new ListElementConstructorProperties2(3, "abc", ListElementConstructorProperties2.Gender.male, 30, '0'); + final ListElementConstructorProperties2 person22 = new ListElementConstructorProperties2(3, "vivi", ListElementConstructorProperties2.Gender.female, 40, '0'); + final ListElementConstructorProperties2 person23 = new ListElementConstructorProperties2(3, "vava", ListElementConstructorProperties2.Gender.female, 40, '0'); + final List expected = asList( + new ListConstructorProperties2(1, asList(person11, person12)), + new ListConstructorProperties2(2, asList(person21, person22, person23)) + ); + + final PoijiOptions options = PoijiOptions.PoijiOptionsBuilder.settings().headerCount(2).preferNullOverDefault(true).build(); + final List read = Poiji.fromExcel(new File(path), ListConstructorProperties2.class, options); + assertThat(read, equalTo(expected)); + } + } diff --git a/src/test/java/com/poiji/deserialize/model/ListConstructorProperties.java b/src/test/java/com/poiji/deserialize/model/ListConstructorProperties.java new file mode 100644 index 0000000..1920940 --- /dev/null +++ b/src/test/java/com/poiji/deserialize/model/ListConstructorProperties.java @@ -0,0 +1,44 @@ +package com.poiji.deserialize.model; + +import com.poiji.annotation.ExcelCell; +import com.poiji.annotation.ExcelList; + +import java.beans.ConstructorProperties; +import java.util.List; +import java.util.Objects; + +public final class ListConstructorProperties { + + @ExcelCell(0) + private final Integer data; + @ExcelList(elementSize = 3, listStart = 1) + private final List elements; + + @ConstructorProperties({"data", "elements"}) + public ListConstructorProperties(Integer data, List elements) { + this.data = data; + this.elements = elements; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final ListConstructorProperties that = (ListConstructorProperties) o; + return Objects.equals(data, that.data) && Objects.equals(elements, that.elements); + } + + @Override + public int hashCode() { + return Objects.hash(data, elements); + } + + @Override + public String toString() { + return "ListEntity{" + "data=" + data + ", elements=" + elements + '}'; + } +} diff --git a/src/test/java/com/poiji/deserialize/model/ListConstructorProperties2.java b/src/test/java/com/poiji/deserialize/model/ListConstructorProperties2.java new file mode 100644 index 0000000..de227a7 --- /dev/null +++ b/src/test/java/com/poiji/deserialize/model/ListConstructorProperties2.java @@ -0,0 +1,44 @@ +package com.poiji.deserialize.model; + +import com.poiji.annotation.ExcelCell; +import com.poiji.annotation.ExcelList; + +import java.beans.ConstructorProperties; +import java.util.List; +import java.util.Objects; + +public final class ListConstructorProperties2 { + + @ExcelCell(0) + private final Integer data; + @ExcelList(elementSize = 3, listStart = 1) + private final List elements; + + @ConstructorProperties({"elements", "elements", "redundant"}) + public ListConstructorProperties2(Integer data, List elements) { + this.data = data; + this.elements = elements; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final ListConstructorProperties2 that = (ListConstructorProperties2) o; + return Objects.equals(data, that.data) && Objects.equals(elements, that.elements); + } + + @Override + public int hashCode() { + return Objects.hash(data, elements); + } + + @Override + public String toString() { + return "ListEntity{" + "data=" + data + ", elements=" + elements + '}'; + } +} diff --git a/src/test/java/com/poiji/deserialize/model/ListElementConstructorProperties.java b/src/test/java/com/poiji/deserialize/model/ListElementConstructorProperties.java new file mode 100644 index 0000000..2477779 --- /dev/null +++ b/src/test/java/com/poiji/deserialize/model/ListElementConstructorProperties.java @@ -0,0 +1,57 @@ +package com.poiji.deserialize.model; + +import com.poiji.annotation.ExcelCellName; +import com.poiji.annotation.ExcelRow; + +import java.beans.ConstructorProperties; +import java.util.Objects; + +public final class ListElementConstructorProperties { + + @ExcelRow + private final int row; + @ExcelCellName("Name") + private final String name; + @ExcelCellName("Gender") + private final Gender gender; + @ExcelCellName("Age") + private final Integer age; + + @ConstructorProperties({"now", "name", "gender", "age"}) + public ListElementConstructorProperties(int row, String name, Gender gender, Integer age) { + this.row = row; + this.name = name; + this.gender = gender; + this.age = age; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final ListElementConstructorProperties that = (ListElementConstructorProperties) o; + return row == that.row && Objects.equals(name, that.name) && Objects.equals(gender, that.gender) && + Objects.equals(age, that.age); + } + + @Override + public int hashCode() { + return Objects.hash(row, name, gender, age); + } + + @Override + public String toString() { + return "ListElement{" + "row=" + row + ", name='" + name + '\'' + ", gender='" + gender + '\'' + ", age='" + + age + '\'' + '}'; + } + + public enum Gender { + + male, female + + } +} diff --git a/src/test/java/com/poiji/deserialize/model/ListElementConstructorProperties2.java b/src/test/java/com/poiji/deserialize/model/ListElementConstructorProperties2.java new file mode 100644 index 0000000..94e19f0 --- /dev/null +++ b/src/test/java/com/poiji/deserialize/model/ListElementConstructorProperties2.java @@ -0,0 +1,57 @@ +package com.poiji.deserialize.model; + +import com.poiji.annotation.ExcelCellName; +import com.poiji.annotation.ExcelRow; + +import java.beans.ConstructorProperties; +import java.util.Objects; + +public final class ListElementConstructorProperties2 { + + @ExcelRow + private final int row; + @ExcelCellName("Name") + private final String name; + @ExcelCellName("Gender") + private final Gender gender; + @ExcelCellName("Age") + private final Integer age; + + @ConstructorProperties({"wrong", "wrong", "gender"}) + public ListElementConstructorProperties2(int row, String name, Gender gender, Integer age, char column) { + this.row = row; + this.name = name; + this.gender = gender; + this.age = age; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final ListElementConstructorProperties2 that = (ListElementConstructorProperties2) o; + return row == that.row && Objects.equals(name, that.name) && Objects.equals(gender, that.gender) && + Objects.equals(age, that.age); + } + + @Override + public int hashCode() { + return Objects.hash(row, name, gender, age); + } + + @Override + public String toString() { + return "ListElement{" + "row=" + row + ", name='" + name + '\'' + ", gender='" + gender + '\'' + ", age='" + + age + '\'' + '}'; + } + + public enum Gender { + + male, female + + } +} diff --git a/src/test/java/com/poiji/deserialize/model/ManyExcelConstructor.java b/src/test/java/com/poiji/deserialize/model/ManyExcelConstructor.java new file mode 100644 index 0000000..1b11be8 --- /dev/null +++ b/src/test/java/com/poiji/deserialize/model/ManyExcelConstructor.java @@ -0,0 +1,48 @@ +package com.poiji.deserialize.model; + +import com.poiji.annotation.ExcelCell; +import com.poiji.annotation.ExcelConstructor; + +import java.util.Objects; + +public final class ManyExcelConstructor { + + @ExcelCell(0) + private final Integer data; + + public ManyExcelConstructor(Integer data, String arg2) { + throw new RuntimeException("Wrong constructor selected"); + } + + @ExcelConstructor + public ManyExcelConstructor(Integer data) { + this.data = data; + } + + @ExcelConstructor + public ManyExcelConstructor() { + throw new RuntimeException("Wrong constructor selected"); + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final ManyExcelConstructor that = (ManyExcelConstructor) o; + return Objects.equals(data, that.data); + } + + @Override + public int hashCode() { + return Objects.hash(data); + } + + @Override + public String toString() { + return "ListEntity{" + "data=" + data + '}'; + } +} diff --git a/src/test/java/com/poiji/deserialize/model/NoExcelConstructor.java b/src/test/java/com/poiji/deserialize/model/NoExcelConstructor.java new file mode 100644 index 0000000..606fb5f --- /dev/null +++ b/src/test/java/com/poiji/deserialize/model/NoExcelConstructor.java @@ -0,0 +1,46 @@ +package com.poiji.deserialize.model; + +import com.poiji.annotation.ExcelCell; +import com.poiji.annotation.ExcelConstructor; + +import java.util.Objects; + +public final class NoExcelConstructor { + + @ExcelCell(0) + private final Integer data; + + public NoExcelConstructor(Integer data, String arg2) { + throw new RuntimeException("Wrong constructor selected"); + } + + public NoExcelConstructor(Integer data) { + this.data = data; + } + + public NoExcelConstructor() { + throw new RuntimeException("Wrong constructor selected"); + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final NoExcelConstructor that = (NoExcelConstructor) o; + return Objects.equals(data, that.data); + } + + @Override + public int hashCode() { + return Objects.hash(data); + } + + @Override + public String toString() { + return "ListEntity{" + "data=" + data + '}'; + } +} diff --git a/src/test/java/com/poiji/deserialize/model/OneExcelConstructor.java b/src/test/java/com/poiji/deserialize/model/OneExcelConstructor.java new file mode 100644 index 0000000..7226569 --- /dev/null +++ b/src/test/java/com/poiji/deserialize/model/OneExcelConstructor.java @@ -0,0 +1,47 @@ +package com.poiji.deserialize.model; + +import com.poiji.annotation.ExcelCell; +import com.poiji.annotation.ExcelConstructor; + +import java.util.Objects; + +public final class OneExcelConstructor { + + @ExcelCell(0) + private final Integer data; + + public OneExcelConstructor(Integer data, String arg2) { + throw new RuntimeException("Wrong constructor selected"); + } + + @ExcelConstructor + public OneExcelConstructor(Integer data) { + this.data = data; + } + + public OneExcelConstructor() { + throw new RuntimeException("Wrong constructor selected"); + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final OneExcelConstructor that = (OneExcelConstructor) o; + return Objects.equals(data, that.data); + } + + @Override + public int hashCode() { + return Objects.hash(data); + } + + @Override + public String toString() { + return "ListEntity{" + "data=" + data + '}'; + } +} diff --git a/src/test/resources/ignore.xls b/src/test/resources/ignore.xls index 9b037b7..5d1a937 100644 Binary files a/src/test/resources/ignore.xls and b/src/test/resources/ignore.xls differ diff --git a/src/test/resources/ignore.xlsx b/src/test/resources/ignore.xlsx index 6ddaf7a..6855322 100644 Binary files a/src/test/resources/ignore.xlsx and b/src/test/resources/ignore.xlsx differ diff --git a/src/test/resources/write.xls b/src/test/resources/write.xls index 5ceae6c..bf64ee0 100644 Binary files a/src/test/resources/write.xls and b/src/test/resources/write.xls differ diff --git a/src/test/resources/writeStream.xls b/src/test/resources/writeStream.xls index 1614c62..47192ab 100644 Binary files a/src/test/resources/writeStream.xls and b/src/test/resources/writeStream.xls differ