From d14993f532e6c8a7522be712f33b03513722b63f Mon Sep 17 00:00:00 2001 From: Pascal Breuer Date: Wed, 6 May 2020 14:20:05 +0200 Subject: [PATCH 1/8] Added methods to read excel properties from excel files --- .../com/poiji/annotation/ExcelProperty.java | 24 ++++++ .../poiji/annotation/ExcelUnknownCells.java | 2 +- src/main/java/com/poiji/bind/Poiji.java | 60 +++++++++++++- .../com/poiji/bind/mapping/PoijiHandler.java | 23 ++---- .../poiji/bind/mapping/PropertyHandler.java | 77 ++++++++++++++++++ .../poiji/bind/mapping/XSSFUnmarshaller.java | 16 ---- .../bind/mapping/XSSFUnmarshallerFile.java | 23 +----- .../bind/mapping/XSSFUnmarshallerStream.java | 23 +----- .../java/com/poiji/util/AnnotationUtil.java | 4 +- .../com/poiji/util/ExcelFileOpenUtil.java | 55 +++++++++++++ src/main/java/com/poiji/util/ReflectUtil.java | 16 +++- .../metadata/CorePropertyTest.java | 26 ++++++ .../metadata/model/CorePropertyEntity.java | 18 ++++ src/test/resources/corePropertiesSet.xlsx | Bin 0 -> 8760 bytes .../core_properties_set_password.xlsx | Bin 0 -> 15360 bytes 15 files changed, 289 insertions(+), 78 deletions(-) create mode 100644 src/main/java/com/poiji/annotation/ExcelProperty.java create mode 100644 src/main/java/com/poiji/bind/mapping/PropertyHandler.java create mode 100644 src/main/java/com/poiji/util/ExcelFileOpenUtil.java create mode 100644 src/test/java/com/poiji/deserialize/metadata/CorePropertyTest.java create mode 100644 src/test/java/com/poiji/deserialize/metadata/model/CorePropertyEntity.java create mode 100644 src/test/resources/corePropertiesSet.xlsx create mode 100644 src/test/resources/core_properties_set_password.xlsx diff --git a/src/main/java/com/poiji/annotation/ExcelProperty.java b/src/main/java/com/poiji/annotation/ExcelProperty.java new file mode 100644 index 0000000..6fa702f --- /dev/null +++ b/src/main/java/com/poiji/annotation/ExcelProperty.java @@ -0,0 +1,24 @@ +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; + +/** + * Indicates, that a field contains an excel property (e.g. author, title, custom properties, ...) + * + * @author breucode + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +@Documented +public @interface ExcelProperty { + /** + * If this value is set, the property will be read from property field with this name instead of your field name. + * + * @return the name of the property field to read from + */ + String propertyName() default ""; +} diff --git a/src/main/java/com/poiji/annotation/ExcelUnknownCells.java b/src/main/java/com/poiji/annotation/ExcelUnknownCells.java index fabd30b..38edc3b 100644 --- a/src/main/java/com/poiji/annotation/ExcelUnknownCells.java +++ b/src/main/java/com/poiji/annotation/ExcelUnknownCells.java @@ -9,7 +9,7 @@ /** * This annotations allows you to put every unknown cell (neither mapped by name, nor by index) into a {@code Map} * - * @author Pascal Breuer + * @author breucode */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) diff --git a/src/main/java/com/poiji/bind/Poiji.java b/src/main/java/com/poiji/bind/Poiji.java index d7b5499..ec20c1f 100644 --- a/src/main/java/com/poiji/bind/Poiji.java +++ b/src/main/java/com/poiji/bind/Poiji.java @@ -1,5 +1,6 @@ package com.poiji.bind; +import com.poiji.bind.mapping.PropertyHandler; import com.poiji.bind.mapping.UnmarshallerHelper; import com.poiji.exception.IllegalCastException; import com.poiji.exception.InvalidExcelFileExtension; @@ -7,9 +8,13 @@ import com.poiji.exception.PoijiException; import com.poiji.option.PoijiOptions; import com.poiji.option.PoijiOptions.PoijiOptionsBuilder; +import com.poiji.util.ExcelFileOpenUtil; import com.poiji.util.Files; +import org.apache.poi.openxml4j.opc.OPCPackage; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; import java.io.File; +import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; @@ -40,6 +45,57 @@ public final class Poiji { private Poiji() { } + public static T fromExcelProperties(final File file, final Class type) { + return fromExcelProperties(file, type, PoijiOptionsBuilder.settings().build()); + } + + public static T fromExcelProperties(final InputStream inputStream, + PoijiExcelType excelType, + final Class type) { + return fromExcelProperties(inputStream, excelType, type, PoijiOptionsBuilder.settings().build()); + } + + public static T fromExcelProperties(final File file, final Class type, final PoijiOptions options) { + String extension = files.getExtension(file.getName()); + + if (XLSX_EXTENSION.equals(extension)) { + try (OPCPackage open = ExcelFileOpenUtil.openXlsxFile(file, options); + XSSFWorkbook xssfWorkbook = new XSSFWorkbook(open)) { + + return PropertyHandler.unmarshal(type, xssfWorkbook.getProperties()); + + } catch (IOException e) { + throw new PoijiException("Problem occurred while reading data", e); + } + } else if (XLS_EXTENSION.equals(extension)) { + throw new InvalidExcelFileExtension("Reading metadata from (" + extension + "), is not supported"); + } else { + throw new InvalidExcelFileExtension("Invalid file extension (" + extension + "), expected .xlsx"); + } + } + + public static T fromExcelProperties(final InputStream inputStream, + PoijiExcelType excelType, + final Class type, + PoijiOptions options) { + Objects.requireNonNull(excelType); + + if (excelType == PoijiExcelType.XLSX) { + try (OPCPackage open = ExcelFileOpenUtil.openXlsxFile(inputStream, options); + XSSFWorkbook xssfWorkbook = new XSSFWorkbook(open)) { + + return PropertyHandler.unmarshal(type, xssfWorkbook.getProperties()); + + } catch (IOException e) { + throw new PoijiException("Problem occurred while reading data", e); + } + } else if (excelType == PoijiExcelType.XLS) { + throw new InvalidExcelFileExtension("Reading metadata from (" + excelType + "), is not supported"); + } else { + throw new InvalidExcelFileExtension("Invalid file extension (" + excelType + "), expected .xlsx"); + } + } + /** * converts excel rows into a list of objects * @@ -218,7 +274,7 @@ private static Unmarshaller deserializer(final File file, final PoijiOptions opt } else if (XLSX_EXTENSION.equals(extension)) { return UnmarshallerHelper.XSSFInstance(poijiFile, options); } else { - throw new InvalidExcelFileExtension("Invalid file extension (" + extension + "), excepted .xls or .xlsx"); + throw new InvalidExcelFileExtension("Invalid file extension (" + extension + "), expected .xls or .xlsx"); } } @@ -230,7 +286,7 @@ private static Unmarshaller deserializer(final InputStream inputStream, PoijiExc } else if (excelType == PoijiExcelType.XLSX) { return UnmarshallerHelper.XSSFInstance(poijiInputStream, options); } else { - throw new InvalidExcelFileExtension("Invalid file extension (" + excelType + "), excepted .xls or .xlsx"); + throw new InvalidExcelFileExtension("Invalid file extension (" + excelType + "), expected .xls or .xlsx"); } } diff --git a/src/main/java/com/poiji/bind/mapping/PoijiHandler.java b/src/main/java/com/poiji/bind/mapping/PoijiHandler.java index 8e942b7..72f1d2b 100644 --- a/src/main/java/com/poiji/bind/mapping/PoijiHandler.java +++ b/src/main/java/com/poiji/bind/mapping/PoijiHandler.java @@ -90,7 +90,7 @@ private boolean setValue(String content, Class type, int column) { ExcelRow excelRow = field.getAnnotation(ExcelRow.class); if (excelRow != null) { Object o = casting.castValue(field.getType(), valueOf(internalRow), internalRow, column, options); - setFieldData(field, o, instance); + ReflectUtil.setFieldData(field, o, instance); columnToField.put(-1, field); } ExcelCellRange range = field.getAnnotation(ExcelCellRange.class); @@ -99,7 +99,7 @@ private boolean setValue(String content, Class type, int column) { ins = getInstance(field); for (Field f : field.getType().getDeclaredFields()) { if (setValue(f, column, content, ins)) { - setFieldData(field, ins, instance); + ReflectUtil.setFieldData(field, ins, instance); columnToField.put(column, f); columnToSuperClassField.put(column, field); } @@ -119,7 +119,7 @@ private boolean setValue(String content, Class type, int column) { field.setAccessible(true); if (field.get(instance) == null) { excelUnknownCellsMap = new HashMap<>(); - setFieldData(field, excelUnknownCellsMap, instance); + ReflectUtil.setFieldData(field, excelUnknownCellsMap, instance); } else { excelUnknownCellsMap = (Map) field.get(instance); } @@ -133,14 +133,14 @@ private boolean setValue(String content, Class type, int column) { if (columnToField.containsKey(-1)) { Field field = columnToField.get(-1); Object o = casting.castValue(field.getType(), valueOf(internalRow), internalRow, column, options); - setFieldData(field, o, instance); + ReflectUtil.setFieldData(field, o, instance); } if (columnToField.containsKey(column) && columnToSuperClassField.containsKey(column)) { Field field = columnToField.get(column); Object ins; ins = getInstance(columnToSuperClassField.get(column)); if (setValue(field, column, content, ins)) { - setFieldData(columnToSuperClassField.get(column), ins, instance); + ReflectUtil.setFieldData(columnToSuperClassField.get(column), ins, instance); return true; } return setValue(field, column, content, instance); @@ -154,7 +154,7 @@ private boolean setValue(Field field, int column, String content, Object ins) { Class fieldType = field.getType(); if (column == index.value()) { Object o = casting.castValue(fieldType, content, internalRow, column, options); - setFieldData(field, o, ins); + ReflectUtil.setFieldData(field, o, ins); return true; } } else { @@ -169,7 +169,7 @@ private boolean setValue(Field field, int column, String content, Object ins) { //Fix both columns mapped to name passing this condition below if (titleColumn != null && titleColumn == column) { Object o = casting.castValue(fieldType, content, internalRow, column, options); - setFieldData(field, o, ins); + ReflectUtil.setFieldData(field, o, ins); return true; } } @@ -177,15 +177,6 @@ private boolean setValue(Field field, int column, String content, Object ins) { return false; } - private void setFieldData(Field field, Object o, Object instance) { - try { - field.setAccessible(true); - field.set(instance, o); - } catch (IllegalAccessException e) { - throw new IllegalCastException("Unexpected cast type {" + o + "} of field" + field.getName()); - } - } - @Override public void startRow(int rowNum) { if (rowNum + 1 > options.skip()) { diff --git a/src/main/java/com/poiji/bind/mapping/PropertyHandler.java b/src/main/java/com/poiji/bind/mapping/PropertyHandler.java new file mode 100644 index 0000000..40ff68a --- /dev/null +++ b/src/main/java/com/poiji/bind/mapping/PropertyHandler.java @@ -0,0 +1,77 @@ +package com.poiji.bind.mapping; + +import com.poiji.annotation.ExcelProperty; +import com.poiji.util.ReflectUtil; +import org.apache.poi.ooxml.POIXMLProperties; + +import java.lang.reflect.Field; +import java.util.stream.Stream; + +public final class PropertyHandler { + + public static T unmarshal(Class type, POIXMLProperties poixmlProperties) { + + T unmarshalledObject = ReflectUtil.newInstanceOf(type); + + Stream.of(type.getDeclaredFields()) + .filter(field -> field.getAnnotation(ExcelProperty.class) != null) + .forEach(excelPropertyField -> { + String propertyName = getPropertyName(excelPropertyField); + + setPropertyValueOnTarget(propertyName, poixmlProperties, excelPropertyField, unmarshalledObject); + }); + + return unmarshalledObject; + } + + private static String getPropertyName(Field excelPropertyField) { + String propertyName = excelPropertyField.getAnnotation(ExcelProperty.class).propertyName(); + + if (propertyName.isEmpty()) { + propertyName = excelPropertyField.getName(); + } + + return propertyName; + } + + private static void setPropertyValueOnTarget(String propertyName, POIXMLProperties poixmlProperties, Field targetField, Object targetObject) { + switch (propertyName) { + case "category": + ReflectUtil.setFieldData(targetField, poixmlProperties.getCoreProperties().getCategory(), targetObject); + break; + case "contentStatus": + ReflectUtil.setFieldData(targetField, poixmlProperties.getCoreProperties().getContentStatus(), targetObject); + break; + case "created": + ReflectUtil.setFieldData(targetField, poixmlProperties.getCoreProperties().getCreated(), targetObject); + break; + case "creator": + ReflectUtil.setFieldData(targetField, poixmlProperties.getCoreProperties().getCreator(), targetObject); + break; + case "description": + ReflectUtil.setFieldData(targetField, poixmlProperties.getCoreProperties().getDescription(), targetObject); + break; + case "keywords": + ReflectUtil.setFieldData(targetField, poixmlProperties.getCoreProperties().getKeywords(), targetObject); + break; + case "lastPrinted": + ReflectUtil.setFieldData(targetField, poixmlProperties.getCoreProperties().getLastPrinted(), targetObject); + break; + case "modified": + ReflectUtil.setFieldData(targetField, poixmlProperties.getCoreProperties().getModified(), targetObject); + break; + case "subject": + ReflectUtil.setFieldData(targetField, poixmlProperties.getCoreProperties().getSubject(), targetObject); + break; + case "title": + ReflectUtil.setFieldData(targetField, poixmlProperties.getCoreProperties().getTitle(), targetObject); + break; + case "revision": + ReflectUtil.setFieldData(targetField, poixmlProperties.getCoreProperties().getRevision(), targetObject); + break; + default: + ReflectUtil.setFieldData(targetField, poixmlProperties.getCustomProperties().getProperty(propertyName).getLpwstr(), targetObject); + break; + } + } +} diff --git a/src/main/java/com/poiji/bind/mapping/XSSFUnmarshaller.java b/src/main/java/com/poiji/bind/mapping/XSSFUnmarshaller.java index 1ca638f..65c58fb 100644 --- a/src/main/java/com/poiji/bind/mapping/XSSFUnmarshaller.java +++ b/src/main/java/com/poiji/bind/mapping/XSSFUnmarshaller.java @@ -6,8 +6,6 @@ import com.poiji.option.PoijiOptions; import org.apache.poi.openxml4j.exceptions.OpenXML4JException; import org.apache.poi.openxml4j.opc.OPCPackage; -import org.apache.poi.poifs.filesystem.DocumentFactoryHelper; -import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.ss.usermodel.DataFormatter; import org.apache.poi.util.IOUtils; import org.apache.poi.util.XMLHelper; @@ -124,19 +122,5 @@ private void processSheet(StylesTable styles, } } - protected void listOfEncryptedItems(Class type, Consumer consumer, POIFSFileSystem fs) throws IOException { - InputStream stream = DocumentFactoryHelper.getDecryptedStream(fs, options.getPassword()); - - try (OPCPackage open = OPCPackage.open(stream)) { - unmarshal0(type, consumer, open); - - } catch (ParserConfigurationException | SAXException | IOException | OpenXML4JException e) { - IOUtils.closeQuietly(fs); - throw new PoijiException("Problem occurred while reading data", e); - } - } - protected abstract void returnFromExcelFile(Class type, Consumer consumer); - - protected abstract void returnFromEncryptedFile(Class type, Consumer consumer); } \ No newline at end of file diff --git a/src/main/java/com/poiji/bind/mapping/XSSFUnmarshallerFile.java b/src/main/java/com/poiji/bind/mapping/XSSFUnmarshallerFile.java index 28e7b84..ee160e6 100644 --- a/src/main/java/com/poiji/bind/mapping/XSSFUnmarshallerFile.java +++ b/src/main/java/com/poiji/bind/mapping/XSSFUnmarshallerFile.java @@ -3,10 +3,9 @@ import com.poiji.bind.PoijiFile; import com.poiji.exception.PoijiException; import com.poiji.option.PoijiOptions; +import com.poiji.util.ExcelFileOpenUtil; import org.apache.poi.openxml4j.exceptions.OpenXML4JException; import org.apache.poi.openxml4j.opc.OPCPackage; -import org.apache.poi.openxml4j.opc.PackageAccess; -import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.xml.sax.SAXException; import javax.xml.parsers.ParserConfigurationException; @@ -28,16 +27,12 @@ final class XSSFUnmarshallerFile extends XSSFUnmarshaller { @Override public void unmarshal(Class type, Consumer consumer) { - if (options.getPassword() != null) { - returnFromEncryptedFile(type, consumer); - return; - } - returnFromExcelFile(type,consumer); + returnFromExcelFile(type, consumer); } public void returnFromExcelFile(Class type, Consumer consumer) { - try (OPCPackage open = OPCPackage.open(poijiFile.file(), PackageAccess.READ)) { + try (OPCPackage open = ExcelFileOpenUtil.openXlsxFile(poijiFile.file(), options)) { unmarshal0(type, consumer, open); @@ -45,16 +40,4 @@ public void returnFromExcelFile(Class type, Consumer consumer) throw new PoijiException("Problem occurred while reading data", e); } } - - public void returnFromEncryptedFile(Class type, Consumer consumer) { - - try (POIFSFileSystem fs = new POIFSFileSystem(poijiFile.file(), true)) { - - listOfEncryptedItems(type, consumer, fs); - - } catch (IOException e) { - throw new PoijiException("Problem occurred while reading data", e); - } - } - } diff --git a/src/main/java/com/poiji/bind/mapping/XSSFUnmarshallerStream.java b/src/main/java/com/poiji/bind/mapping/XSSFUnmarshallerStream.java index 41127c2..3c9f3eb 100644 --- a/src/main/java/com/poiji/bind/mapping/XSSFUnmarshallerStream.java +++ b/src/main/java/com/poiji/bind/mapping/XSSFUnmarshallerStream.java @@ -3,9 +3,9 @@ import com.poiji.bind.PoijiInputStream; import com.poiji.exception.PoijiException; import com.poiji.option.PoijiOptions; +import com.poiji.util.ExcelFileOpenUtil; import org.apache.poi.openxml4j.exceptions.OpenXML4JException; import org.apache.poi.openxml4j.opc.OPCPackage; -import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.xml.sax.SAXException; import javax.xml.parsers.ParserConfigurationException; @@ -26,19 +26,13 @@ final class XSSFUnmarshallerStream extends XSSFUnmarshaller { @Override public void unmarshal(Class type, Consumer consumer) { - - if (options.getPassword() != null) { - returnFromEncryptedFile(type, consumer); - return; - } - returnFromExcelFile(type, consumer); } @Override public void returnFromExcelFile(Class type, Consumer consumer) { - try (OPCPackage open = OPCPackage.open(poijiInputStream.stream())) { + try (OPCPackage open = ExcelFileOpenUtil.openXlsxFile(poijiInputStream.stream(), options)) { unmarshal0(type, consumer, open); @@ -46,17 +40,4 @@ public void returnFromExcelFile(Class type, Consumer consumer) throw new PoijiException("Problem occurred while reading data", e); } } - - @Override - public void returnFromEncryptedFile(Class type, Consumer consumer) { - - try (POIFSFileSystem fs = new POIFSFileSystem(poijiInputStream.stream())) { - - listOfEncryptedItems(type, consumer, fs); - - } catch (IOException e) { - throw new PoijiException("Problem occurred while reading data", e); - } - } - } diff --git a/src/main/java/com/poiji/util/AnnotationUtil.java b/src/main/java/com/poiji/util/AnnotationUtil.java index d458d58..e2b066f 100644 --- a/src/main/java/com/poiji/util/AnnotationUtil.java +++ b/src/main/java/com/poiji/util/AnnotationUtil.java @@ -12,8 +12,10 @@ /** * Created by hakan on 2.05.2020 */ -public class AnnotationUtil { +public final class AnnotationUtil { + private AnnotationUtil() { + } /** * Validate that all headers specified via @ExcelCellName annotations are present in the list of header names. diff --git a/src/main/java/com/poiji/util/ExcelFileOpenUtil.java b/src/main/java/com/poiji/util/ExcelFileOpenUtil.java new file mode 100644 index 0000000..8f32a5a --- /dev/null +++ b/src/main/java/com/poiji/util/ExcelFileOpenUtil.java @@ -0,0 +1,55 @@ +package com.poiji.util; + +import com.poiji.exception.PoijiException; +import com.poiji.option.PoijiOptions; +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; +import org.apache.poi.openxml4j.opc.OPCPackage; +import org.apache.poi.openxml4j.opc.PackageAccess; +import org.apache.poi.poifs.filesystem.DocumentFactoryHelper; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +public final class ExcelFileOpenUtil { + + private ExcelFileOpenUtil() { + } + + public static OPCPackage openXlsxFile(final File file, final PoijiOptions options) { + if (options.getPassword() != null) { + try (POIFSFileSystem fs = new POIFSFileSystem(file, true); + InputStream decryptedStream = DocumentFactoryHelper.getDecryptedStream(fs, options.getPassword())) { + + return OPCPackage.open(decryptedStream); + } catch (IOException | InvalidFormatException e) { + throw new PoijiException("Problem occurred while reading data", e); + } + } else { + try { + return OPCPackage.open(file, PackageAccess.READ); + } catch (InvalidFormatException e) { + throw new PoijiException("Problem occurred while reading data", e); + } + } + } + + public static OPCPackage openXlsxFile(final InputStream inputStream, final PoijiOptions options) { + if (options.getPassword() != null) { + try (POIFSFileSystem fs = new POIFSFileSystem(inputStream); + InputStream decryptedStream = DocumentFactoryHelper.getDecryptedStream(fs, options.getPassword())) { + + return OPCPackage.open(decryptedStream); + } catch (IOException | InvalidFormatException e) { + throw new PoijiException("Problem occurred while reading data", e); + } + } else { + try { + return OPCPackage.open(inputStream); + } catch (InvalidFormatException | IOException e) { + throw new PoijiException("Problem occurred while reading data", e); + } + } + } +} diff --git a/src/main/java/com/poiji/util/ReflectUtil.java b/src/main/java/com/poiji/util/ReflectUtil.java index 212fa9c..82beeed 100644 --- a/src/main/java/com/poiji/util/ReflectUtil.java +++ b/src/main/java/com/poiji/util/ReflectUtil.java @@ -1,6 +1,7 @@ package com.poiji.util; import com.poiji.annotation.ExcelCellRange; +import com.poiji.exception.IllegalCastException; import com.poiji.exception.PoijiInstantiationException; import java.lang.annotation.Annotation; @@ -10,7 +11,11 @@ import java.util.Collection; import java.util.List; -public class ReflectUtil { +public final class ReflectUtil { + + private ReflectUtil() { + } + public static T newInstanceOf(Class type) { T obj; try { @@ -49,4 +54,13 @@ public static Collection findRecursivePoijiAnnotati return annotations; } + + public static void setFieldData(Field field, Object o, Object instance) { + try { + field.setAccessible(true); + field.set(instance, o); + } catch (IllegalAccessException e) { + throw new IllegalCastException("Unexpected cast type {" + o + "} of field" + field.getName()); + } + } } diff --git a/src/test/java/com/poiji/deserialize/metadata/CorePropertyTest.java b/src/test/java/com/poiji/deserialize/metadata/CorePropertyTest.java new file mode 100644 index 0000000..6c65430 --- /dev/null +++ b/src/test/java/com/poiji/deserialize/metadata/CorePropertyTest.java @@ -0,0 +1,26 @@ +package com.poiji.deserialize.metadata; + +import com.poiji.bind.Poiji; +import com.poiji.deserialize.metadata.model.CorePropertyEntity; +import com.poiji.deserialize.model.byid.Person; +import com.poiji.option.PoijiOptions; +import org.junit.Test; + +import java.io.File; +import java.util.List; + +public class CorePropertyTest { + + @Test + public void readCoreProperties() { + CorePropertyEntity deserializedProperties = Poiji.fromExcelProperties(new File("src/test/resources/corePropertiesSet.xlsx"), + CorePropertyEntity.class); + } + + @Test + public void readCorePropertiesWithPassword() { + PoijiOptions options = PoijiOptions.PoijiOptionsBuilder.settings().password("testPassword").build(); + CorePropertyEntity deserializedProperties = Poiji.fromExcelProperties(new File("src/test/resources/core_properties_set_password.xlsx"), + CorePropertyEntity.class, options); + } +} diff --git a/src/test/java/com/poiji/deserialize/metadata/model/CorePropertyEntity.java b/src/test/java/com/poiji/deserialize/metadata/model/CorePropertyEntity.java new file mode 100644 index 0000000..6a82c27 --- /dev/null +++ b/src/test/java/com/poiji/deserialize/metadata/model/CorePropertyEntity.java @@ -0,0 +1,18 @@ +package com.poiji.deserialize.metadata.model; + +import com.poiji.annotation.ExcelProperty; + +import java.util.Date; + +public class CorePropertyEntity { + + @ExcelProperty + private String title; + + @ExcelProperty + private Date modified; + + public String getTitle() { + return title; + } +} diff --git a/src/test/resources/corePropertiesSet.xlsx b/src/test/resources/corePropertiesSet.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..b1caef521450431da94d40ceb4fa562a9474b35a GIT binary patch literal 8760 zcmeHsg;ShK_x0egc(C9M5Zv9}-Q9w_%;4@$fPpX}XaWR>K!56)tN+)paTE^Ai(+{&(Q(_07ydy00;o+ zh^De0?%sCp-j;d+o_1h!4u3aSnwQ9kOoaeM`1}8j|KS~|${g0{<-(J{RJxVjU{_hH zmqO>?j~sZwqAk(Ym(gEgW18>ebf2GagD3NX$XT%N!Prs|&+&+3y}MgeWPJZS9jv5? z!7hCZazW1b1AC7;Nk}0e6XU}|LUJi?QcKGe`y9YCueZHAd~)m3Dph2TM8p!|pYs+5 z4DeU`Td*oj%7h0OyVo=fR`7*BluzM(o*R zX#rhBRXe~;&Ac!x8^jf^*tB+><U?DN<%wq%CjDtHtewGT!GWDjPm<2G zu3H6&94*;R^Q9tyk&|-(EtjMQ;%XlEv5-DVq9V27qaK>&n@8&e6n(4YwypW8X|@^< zY2Db?N(S=fG=&9OB120Dhnj`DH1};Ng56(Dplu+_U)>X4;P14acFEukbph!^;`Y}D zcSM8s#y3r)4vw>S!g#);0s!~-NC2(Bxn-k17u^}0Yie+>!-Tt~CCJVd%*pZN`JX%f z5BuO>mR*X-rOiak`ijT^j#EvMnhsQO7Oy#wlnhAS`OHl`HOlP`BNQsC*4MIkAM zwTIq*SX>rM*&PI4eB!H0dO#>j*W_0n3%&O8L1SS6Whr=7t@q*k!p>nA*@`LwOup@@ zES1e=FI0zC=@h1p{Y>{MlHw1T49y5? zZW+r=yJ4L+zVr0zK8@<{tnc`@u0DPw1z(3QHZF*-N7hTsOeB=J6muHO-z*y4w|z*F znvL!Yi7}nYFbboQ2@AV8D44?j?B~*dzz5ituM*V3Nq0)D2T~FP7^004U7Bc&WE})Rh?IVY^BBx{Z;`_Kuqj(MjDTfyUoM4vf3xdbii` zioIoC*|4|w2;YxIx4Nd$XwXjnQMvVU9P?rt31%<_B)!S1(Jk!p&epv2sG(VwzUm#RnFLI1<0MzVuyfG0 z5gQr_EDSChX%>BY1Gr3jJTj!@pPqgrgZ&~{nVai6g*Xx#gZu?E+?tLvfhm87xRV32 zs>Q*fFQgVf@Z{9}7VH`>l4|c`pXrLBFn*+nal(sD19 zcO-q1B1R`|;fQQZC9=bFej0O+)V5}!$0PQ^z7`>d^y`xw%&A8!qr|rIbk+HzVGS3O zvgeZgZ}#I(^p<@EGhO>KOs4e~Ltliv7pQZmA#V%t7hYED=WKvr53)B<%+;ITK*twR zAJPe8>AQKFawnY+(8wq84I}ny$gX+@M|b)0gUmUNH@^7hzeq^`mOSZQi`ZB`dtFX! z;IF=rMbq2^tFHWnX+u@6dnF(9>UM-PtC?N;t|CVucCcQyG0Jrl4OmJQtr$TAssgY6 zLfLD=Vb+TwvxuG><5sL;&uJc(%}!uwrxZf^veQda<2rRq^NIDm6^9`O)bHp;D}zrY zilfsPyrU2e0k5b&6M#G;nz}GL-&6X0n`Yw<&ydU#RlzXY%B9*>e^HdJGl6bUG)Pxo zbc7DJbpz|QZa1BC*p;7`pUqCZ`M!U@c!kmCF_fNjnnNe}ikj09njsLn6E2Km)R@8c zQJCG~{$D*aLLw7-3irrXxO|ZT&=KIC`J00M)j$7GGX!{Y3AggUJ*srnRr|Q`+A+V! zarx!=65-E#bJFhVf5k-~YG7Vu0P=>Ot&+2}nj6iiaw2)e1{{tI_+Ikkd_p2P?`ADa ze1IHC;Cd*E;xTeMf`o3pUriOMh(hpSZ})5EJ|@m{FhPq1WRNYNP-yFl=@DsM(JNZH zvzEzNKIc&`A0B7$IOPX;G#_r*2)~Zj-K&mlc>9*-IkWxdeT(ek1!_TL0QJNvDTcBb zb^u^vdm6}k)h&O4e$sYpuO2Y!(p_|owMkK_N>#xx9vtgU6E~Ka zEpzjMxpVP^ZTRfaeq(3=whBM#e+KiQA_Qd>I9=pW0Dy<^H+~Q1U`IPUZ!qVt6Zem( zo||dvk;6qeu*$eEY80x86}^O~wb^>{c(UQ^8ylQ%7At#ELH(Np(X^Pz3JraHrT(xR zzZzeEKMoKG*{zs`ZWo7ArI3&%eN*D4WQPYDuS)W!T@P4aMvl{J+x+%e)?ep(!WNK4 z^MVh5q^cURKY6fl&AyCAYiJ3{`9O#fCD4t36syEGSh^ zg$T74Smk6#*{?l#jI+tB@jSYT%p|EQBDCaV;`2V1wDW1H{DLpGr&{ujW1xAgW(-Bx zC0P7cL6CRk7E?8b(1i5q0uV9}`vP4bo6q%{fLQe3GbDfI#a1*XIU?O`AA97tRNv+> zx1IM|8MAV#Bq~ac_IjgsVAQq497`Yv=DRkQbzrqqT^cpyx*nkF$G@1&5avk1@PO4! zQJ$|D-@LgyO4XP`;Olz;R>lqop;5SWfWYQq|f`JGNeA>w%k={T+&=-zn|wu zweZtOGn=tOZenZYIqP?$ad8izaQ2}!3O!rYYm-oO=L&OkphyhdMFWiOQ_E}2~2)bQztHG+-W|#DX zaSK{jh9}b~CK^nDge-;^IVe=e8olps*?{a~WsMr-eHituWI)cXFF z%KW0{GX4G?$vvE5|Ky($n&6HG8UWBm^W!4?EC0M5?cD4*e|`Q+y*=ZRByvH*PLgX$ zEU@1#|L4?4D{Et}8O!vxd6^WAO?%n~ygZOLastFh!sj|n#U)S~5An=NDJ1Onvs4WF zh8^k&#*sO?>P`9_R}~Xq>?FnS?WLuc`JsWQ-p7;e&jJ;nVfH|>EIYJL#B-D9p3zTb zg}DeZ@daxtWhUd>VYq+8WV=;eEC>bRyA7ca%prY~bA6FTtt`CALi`>*HodvaUMOOf zA$dTlhpvElfPyk=?npq_tL12oI2M&w~?o!Mv?etc67P;qs|Y+Y?Ia zEFu{;IVk85eIg5&H+lXTZ#APsF+J@_c9?TVva>m)F20Ee$RapM212@n4%s1o?oG_k z(8M*!K$U(ty})`$CbcP2V|h4x-S|`jTdD}Zim&ngjB%}Z6qZZ)WG&8edtj|e(^2gA z-RC3wS|Mz0s3q4?{I^!)D4Zt#*eb{CWD}3}0tLU^Bm0L~yHC{uoIVquH0uMZD9kG@}@#~G$}Dwj$mkT87T z9JB1Wy-S@fk0bh0?p3=7;FU)kF3ipEt=j`72$4iYrR!t|a?#l700 zkHx3WLQa%=E9+yaKzy`-s7+E6ScZhr2V`jUF|6Em=%wlzw#WC23^1Lz0$Xm(x?txP4p_ZV6E*C~|Ndq&HY zGHjg(>G%$j>cxI>{*-dL9HbB=)^yMd$_KJ$#2iS#=`4>6_aI6I^U8yDI&zFfMD4g^ zSv7V+3k7h3HS_~8v3%#qoSqJveuF8&;}rq~PU@;<&r;g}&3I;ot7g%!^{pQI&0TKO zHl>?EWTB(xea{m1>U!OyKV?tZL*y7Pc)ie>6WhquTGxU;t^jvQKZQe5);rW+6CDT= zbhFbKP8EwuPji9~;y3L+?%g#P5=5J`O>Nb^uW{4JouA0ote#$Z|Lmm=U;o^@^X%S8 zoE&3`v6PMn2$g$QhSuiNzBLtJkTLgmTJ7^_{Ssd5_)=@mqXu0NGsZhaKpq~eKd+UZ zuAYw39aS!w-Ps;TwxH9xlI{@71DS{8(6!v;G_1s@`S_MQtdGv3y!~`35QKHHxjc2C zn$}&KgOQR*B-RYD|>ai_Mlv{bl=r)%r zs3)v+&x?f1LF=fQs8h52O@fZE2BA=n=Y4nmtGKNpY%Inh5r;fO{xA9|_no!Q0{fe% zCR=S;!nJm09+uR_Swq7lWv(Y2sR!Ts$W0{(WOh2twAxs`XBE51NBjX%t&}A}kV@h; zw#ZuJsG%Np;Yl7UhZ8Vu(H;6oA-~?t$Y}YXo@R`o4Wfubppl^VF~)N1Az$j{oY1~^ z)^J5-cB>Xmb}F<7Cgfdin^*%oJy=$0KZw)tf%-UwL&Q_?`)!->_2cl*H)-V7eH6Bz z<|PoR#}Q$dgpyX+5gOJ2rVO+r4Bc@prcFa17E{_f_Dcsy704cAFZEqZ7Hy;$_Y?nKh0##dIuCN zTDiijGoS}Wk~>=Sps2Tf&a$kG^(^3#d6Oj>@q{}##UV{prrif95`zg%-`I%_BeBo+ z0tP9`mG|=R6uaUPB(x|hFP*?n4Ckc?Mt}=VAQbfVBK{hThLbu{H0ifd$&3VI5c|0l z{LP&+{D%^ozkI1Rxn1s7fs6o`ZYHkgL<_;81k#By=k=hqXXwpBOr{}UcN!MV@y=uVm8az{y=fpa1wExSxrjOde9S9E@HKMGaYiOT`lI2 zW*=&AQuR_pT`)VeS$5?$i=-TTrOxim;WX`+)-A|a8tuH&K5~uaw?cSWQzt`gQL0mk~gZ0_C|Ic$+<7(`4*GwnWat@D}y7_vN3>KkX#(`O4 za2_oggOTMe1Cg>PQC-u6cq;MNa|FK>Z?1C2>=&A1yo=GH+arl|)?(r7@ogN(>2+?Q z&QfjH*hHXU<4GKT9H=cB&WhDKSo8J#YT;=}TU$vBm$g^=4(qp<0%n%;P}V1`K^|r9 zTOp}{ouyiR)Dh=5x~zPa`_jN|AF>$xw=>LV&dY%w<6mP_w$>sJDV;pWYkZ?jI8fxV%6NCJ4IW7I6b_$7a;6ACMCP7j*NOHzy4?Yp zl_+AF(=%DcXg=DVOVU7|fAJ;JGdU>x?84D3dS>)+$oYG-_QH50pu(K*1<~%y>Ao`F zQu_8wd8#4SqH6ag?6n8Wd~Md8JEMciKo6YtYiKfwnKSa(+DmMO^4;WH@xhD@`UGN zGnHD)RNbD1xzxOVhgxosH7PzL92>Ne;lvLBOh$l-Os4UxLPByn{AG zBeiWs*y7x8d74X95wF3$#nbFAf~Rn1c`ka~W$kwJ>mqm*slCit)^t68lo~9yP+&Y_ zN?K6Van$ZBF84(7CS#UGB?M=tMUxm6%V3%;|8oIu67HK#SyHqqPi}06w`>^pL)N79 z88`1i#-lw#ZTCLM$ZhKg+m=!YhC+x-*9Yi- zi8>KTj#O4D?dEG&kK{@CWG7-QMsEk|77y5z#fLDF2HLr&dKS&Sa`ZPm0skGOGz6Mcm{Eot(*NeSLS57}8 ztxf8y)ai|cHfP;nRKl3LWm454jMz#^3)Fo%>r6VPy&*Og2}_B6K-u*IrM;cG z8Q4~L|jWZA0siewRxU%Z3GdZflR z=82X(q9mtSyhb;<=emr9JfIbYxKB9>!76B{sse?%Z$HH>g?zV?(Hv-ox^|;^c^~Fc zq_{0IxUCGJf8)LF2n^gYO({7zNJM%Wv(~3-iB#S7ts;lWzsiT@ZC9{O`L~AGNQ;%F zCVubu!J$M{*6h+t)T2!<%;VQHsPyqn(T`+Z7&qC&{HwC!H71qGO8p+|&1b6VLdGOp zGs5-{dvH;F#P09=k~iL9e!`H#3_gFn{qHiu)lYno2PYvHyey3OR}wz<^!$&5aPs{= zax=Q!7k@MlkS=gy4^nb{nPR03w2LVNjp_hiK@PSe_zbGCePqf74j%O+MFKE4_G1?RRNB*^~ll_H020yLjX@8lMefTboc1BLkeqGul3$x+Db2SU&cdpu&E? z-MqaWre0|k*l}h?Wr8|WfrVVtfW2!rr2Eo+amE;_uXHwbrY}uKeg1BxP#46)^2(wp zAR!BG6~ z?Y=6&AiK99r@h*bZK6Vcd5Md!@TkQ_FV^$Ao3Cm0EZA{vt*uV*p_7r&7dCcbwa#54 zxohMgiT6|w6}It9qp?NeK2I~RdxG-MM?*-T6W>O}*qlvWukMty_fq0@xSh9^&4|Vu z9m`X^4P`4svvBlo9|EC;Cc#X{6kmiPjeW)~{4)FImgURWyy;9aai1Tl(Qk1T0g)Z9 z8vov-|KkY%nE$0?uci8T1Ap%({sH_kL*ah%m)_#fz@IxTe?r^fdD>6?mY>0YF9QDw z1po|w`4#;CDGC2<=jY1cpOzA^{@+LZqfYpE$4h7UIF|U5CuE}+}$Gq5dJX!2-=7F z^^OSu!2bSzcYlBXYxps=g9t_F(0}0n0S(+jWrTy;>%oQ(wub+`PXcWp<{l6k+DCyR zDiqP6hz>;zC_-HTfCWWtDB?g77m9dL#D^jQ6bYe71Vv&fKF}xqy@huF1OJz3KobCj z{@eiM02zQYv~>kQpjP_xfDiSUUq^ZHlm8{)-En79O~O3nk${Ip0fh&y7C*Uypxmth zmH>5t8Fa(}`m}+*|1+S3W}#pFAKHJm{$U;oiu#nltwaDwLpixYQ40Y2$=M13{%z_{ zJqe*1@fZJxIQmDB&9V9O+#~kKKifa9(S@GT70S^LY6a&%PW;I}CIA-Z7ymzd{s+>^ zztexn=MS;@V3UXTS04D4#~li&F~$oe1nKd%8{s6AAm-uDoLK&T%A z0oKrHU={>$)iS9sWO`+NI9fEj~!oCB(D4(JFwfDgd=bDb|h6-w{V z+U0L`_1oG1k*vS$^w9o-zt@op^o*ci)Bm(3hUTA#-0^U4c<|2*~Glex4f?aP%*q|L^7dTMpd6>;J*_58u^)1i$!WCLjJCe+20F-W8f- zoSvjk%l6l;l)vp21pN!U9jqWSzwrjTvVqLNU{9c{>7O@|*v(|IxsIH1PkQ24pCI%kt^CfLvM-&;?>} zhnV)bNI*P$)2sb{0ZTh;9?TguGcc7q-;qN61;s1cvE!{Z@v(ZV;>->DQq?l3c0jd| zm7+JDd`AZEzY6)$iryCiMr4KAo( zs@n+B%?9G0<-lp@`mhajNq_Fq;CbX_pfSk;x(`D9F6mG zpSQp@2&(Yn-u5LTC)>^DqrRjhu&Xzzk1X0*W4P6&Du3Fm1&>QQcv`;?K8=X;QHII) z0%`4HJrNS+5)D1>>KAbXu>vqUJuSFD4ypmMx&_BuWSSjO=IVHBlv`zPQ@$6D&(<&E z+GCu**7`bzq$(URs+L-cthX6z+Kc%XOr#$rwv;gY65OeTE~h^oJc(bVUNt=0(?Cy$ zJ=#xGM~r!Ai1sp?=PZA=ymTjxTSL3wiMwf7RWfSy)2>J?tm(Sia(Utq+2sj||iB_;3PKu^Rg$eI4ZwFrNoU4Z^TxHgbl4SqqY>mW{(>!(A zy8BK>uuiNlX4Bo_KVlJgq&673PTcoVBolKh*hTr}lz9K?djDy<1bl*$zwTGhzd-q~(bp z5YUvaui(Q{`8rd%jwi=a^TmhdIb-pVhmY(~3mk)6VhB#Y*{~1we58FM-RpD7%YdBr zG>I5#Pb;EdkmR=edGGXhKDIan8~PO7Co9;L`{jt$#?3TRcfuK>&%#vN^3=8CMNpaM z9MASjan$OO-#C7!rEF}Q@-ZPbpkG|Yyepi76XJp2AHf+zP2t={5KkHb()c&z>f!3T7px zpWxuuB-#8c?fugESfR}W0S4lTTl&ysG;68Kq46~`PB6r#0fb&g4^ntc*u8 zl{+S|r2cBOu zg;oZL@e$-0;pyT(E2~M0r5dlu&-lv zuu7+hIF4tY0|S2fQZg7dQ?Kbd6y^QPR}vioa56f(KA4zA*e8xW6z|wng;>y6YibHl znC4!VS8Fx(w=)PIuLg6kjCn6|bL1!3nt2^HG~RHGnBG6Z-EC+KId@+7(vq%Bw$z2F zJtIk|O;aMomGn-DyZs0In|Cn!JcYYW5Hb zIaJoen4BcU!4_D-wZ7(^DSxJvN{T>HBHlf|byvi>6EU*XvU$kvOzVvR+YaA1f)7n! z)Z6ZYX^o@$Ik;2X6(fVgZ8^usq!(tsR5xlbGt(*)8#_;?;H675=$=oU1nnNvH+gBS z%mfRA;Puk=U)X+SKsFNDQRQ$jW>abyucON5v5dtNZC@Z@)MTx%cFq?w$~U6nclKva zbaaWVP<%{>{p~&Sn0ITQ)9cUrKCH&Dcs2$XDc)Glcle3gk;p08s6gy%Ki1WRC-n9L zxZ72q+zTBPII#%xBl{Kuxo4?2fGf!E1@IuY0QOBcCKf$e!_$%vJlw=zs?>YfOtYpT*xsRj~5f;5ZT;Sg$2Fr^NW0HbmDb=+qqpJ7R@IX^$_5kxQ!^ zF1~bYo_0Tg>d?Bd#a6=6?eX38U3s`$ipwZAd&H3*S5Y02s;vP2fE8;*Rw|uOL}g zLbEy?N{kezVVU-QHls99z-gSs%a6GnDR54w z#*eHuU+!r?*?BPT(>U5%2_Aj?)TSZfC9@dlvm*sKdPV!489y|L+}1C-6Kz zO89Go(%g#l{GxNk&#zZm8M#_<+e^I$zB&47yfY6UR*$2)E<+e;xu(DU{5oNCGY7IA zhwhso7^LY}%oM#@BalOoZgV@fG0SnZ9l zjIZ01+dS$Eo<8b~kv9_BE@PSX=6%h`fJ}hoO~;L&!Rn5kAg*?U!-UkjN~O)8Z!{lE5Tdj?66m>K zM#WKJdt@fPgX^{&2lYV!>l`Fl*Og^Wbn!)`Q@7nSq9L-`ItT`B0g9(+u=n$4*$$pvHA! z7-N9;nLx(p7Xpk02X~c^wA(|^6EqdeA2qUdRC!`q3}Hsp?sSSJc4G%s;Y(X4imNCb z!1Nx-zbKW`M}47T_uWNS;mMC%4BJZ(Q&nvVf@O!YAxK51{zYFzvdHFv`$vga6HhTd zxN`CsmN?z8-uM4#A#h8Wx~$nL4jGLM*8kE1bQ$g1cjF@rht*F4`m;vx3V%X^tLJ{# ziODMDnJhsET~8z|5fwSQg<>TOeLZcc>%HBc)70F7t2|5V zy9NpVFuhD#7kcm-d!Sc9sCDiD8%GulFL_ylA!2Z-RJaE9{(=AULAse9dgg1<@gG}V ztNHKp@dss;@>p(SRwvw-Bj%e{Js6uVAr zR&Upe?g`;>@3ncT;2&Lhhv^5|PC3OjdWTgwg%UUcciQ zsAw1#<$azZ;>L9C^l2j6mTe# zylG)nL8Z^-5b|M4yGoipATCXXL+D9HkWE6^m_LB!L2nBjhH_DD*1*% zlAe25b)6j~t+XO67+Gvx5EnC~q^-yr$D+jCe%f-u_7O}RHz z>n96O#EFEFp3;BUSyP}fw=v0A+N5II%UDI~q(X{m4P3jdzp;_Na|*Iq>d1Ltu;FXU z0kGZ3=oAJThm(eU$-ieLp_0`5`r53`R5n||N#`!9wi;~!+&@U<3<`6p(AB!>Z8cdY zsMe}G`#7k462Lf9DaB*BKICZOBpSc!SCRjUl$2P_d`;bKDeF9I*YIT8IOmA?rZ?i~ z9w|6Z&N{MBaE13C54GOxz$}n)g@mhKztY-XPv4j06;W8H`iy&Bdm;w}5k8zBy=g|h zS1!=n!&5f&!r!J;XqhyrWOKJGiXQJp0FCOE$$;U-dyW!=!_YEaX+%fJ^Jk8<^wcU~ zVIRHG$5xVTV^J_PKy7c$<`7hitnVAkf-(D!(>@V1*y6bs9-Vf$P6VyK3r&D4Xi`EM6AjOmV4LUDRQrg=!*D<%`P0 z%L3mWW7RR%vpG;mgq5mw?z}L>U$5vno=}=cD&uMd$*)yPc=4F_A@(%0!Cy(ve zyo&K%2Mb9WdzmhIZXouK_*gk?dgkqp)sM(tJt6)*lZ+3kkiIzV?FeGZDTm%XNNUDh zJLUYd{oF?Fu$9%vZiwo44#a0Tv78F4BqW$-=o852bOL$-gpeope02p|Td5L_V?LkV zYR-~g6jgf$c+bWk>oi`!VZ669V7-t9IZ>u(OmD^8Y#%Rpv`$}4?M|Hc>sIvi(ynEH z`DBrNav@oK0S`E%STYa9RqnOm8rk*LE&%aI>j~^GWhCDe`GEY9CR1EO=wQi8kER+N zV=matFH?Wun#MEOpBs`ZQqX1h(`VKndT>p%e8(KMS)Tlsj@z?rZ=@KV>s9ZrQ)N#R zMjB)G-K6{*u4N#;9+-p>0dH{71;e-;U;w~hK`f($t5QMT$>K*El}%7H;ktQ7{R4(N zE53{-^Teq4HQ$&;({)QTou_JL$(lsKo4c?Y)kg{1C{cLHd6e9NMsX#Ww%q6vQEE8x z;Ff@Wq0!2%9JQ3Jx?Rr$6Vv>kr{_j3vfi+t3E{CEa43?4amVCU z6ZUon_lX@PBk18)Z?x2V&o}8*9)F_iTO82|s?|c=stv$0F~}cW-I;Y_=|Io@gktEM zfLDjlD65T@TKpNItnSu;kbRk+CzH@50A5*q>{M&$mA5;gBw?oYh}!LY*t01{mroeX z>IcUxY2saPhu~2WKj`}Hfj3@Bs^E?}xGHWh`q14D^Guj_rd9+soxrE@cfq{$R^EtX zc!i_jX5H%%=8$je$#d$VbHEoN%vbzY^x)AvLnBtK5?5(GLdhT&=nFoA1YtD7hsH(u zqq=7f&H^Jtyi&K@9l3Py3pS`W$F^@d$^S_N-N251sFpYOQ}fV`*;2(}t%#J?wV83I;1B#LgEijv>I1$%+lBT?9+t z+Yqu$`wrel&lj;d5`toptrB$fu6*7Djs@3gUQ%=}*yrTZ)*<*T?CIGjS#-xlc}#}G zf!tz3;kD~(GAuSs{3oiXF^Tt%NA3z)h)s|bP6qduIMssr&Z(vPK~&mrgKVp)P9rmm z0f~0Ho@I7yl`h4Z2254<((3VnDIfbPvnhhEYcb&*jxk(5FcnG$NMAGPrwcacYsBwa zM=10R#7C zsAO_1emhGI4K8D&T_!V`dZ6F1;sgH_Ep~0vOR&YVC{weu@jGV7Srw{=vTAjdSNZ0d z)V<7Tq208dN^FHj?$;RjesTU)rVS6-MdYmm_t3qXQg3qkRW1zFy_f9=bK@c7)V!#I zeTWxhO@4|vA7A9%naa66v58e{mvLsa{_XA;U8`;RucCur+kn!>j_Y5N!+ zb?S_!y|C>te6{V!sZk&T3H@YkLNilQ!Lh2gjtRDnd2b{azg(`1i1k7|mdut+%$t^W zWBQ#!B&4TJthQ5xmS#HnpWngxO*A2XFxaQVnnb+_7__CJaEThWRVuscm+;AIgOl1z z<+iK)QL~GJ#iyA42BnVq$!dvCqj%;=5%jO;pfLLVcOtq%t270f56HMu1a^61`6Q!& z8@GJt9*`I2O;$4~)@+2OYjGvGUP^#;TTeo@C&|qIMwqMjb-qX)wTVdwwY5>UoAdg| z!m)2;&rpa}ZWuXkk>C&K@2JBhk^MM77P=EE)f+ztQwjoc0Cd?bumUX>a}x7uG^I-@ zBZw_#l9rg6u}#9lyl@?i_FBdvbT*6CBtSzi>|v$KM}cfPA8XEI2j7N;NXHdp_bliv z7qqy5$RMmFvHVfsE;YlZ1^JX@=C82jmPxe^&7{P{c=JT~2n=R{StQL`z12y@%i}Hr zwkk9ew4ZTB3s1Fd_;_dcz;a}o3XNQMZ|KPH8bth3YLLY)nWBfqh0A#8a?+CpRf6t(L3u(wCP5raSW=Zyr?o*RWi0jat$*`ZZ&z-iVMb$&92H70F{2Vor-X>_BwyA{@#nMrt# zq$?@cSv)iub_3lo%rvrMaG>cyumR&!>r%IdLGY3ub55?~ogmsWQ_aOI%OqL9S2{^Y zRm~6xrb>Hse!7T!XiSBx**>A5408j1iQ+)`__p(c8J6E1B%4rCbirz6q)RzXCHBXm zTO$1)2Yun7O^@b2HY!Wo$9cJPd}W2OCwc{?-qI;8N{_ebDRVxV=qn{A2gm9UV;U{o zISQw~N+7$O_K;9cRi4SK8Qr_~$}iY#FAYY#&|yq8>dY4}Mz7dDe+u&XRMWOOe?_h3 zNoLyl27)Re&qOB?9~gbIwCDX6i&+xw>gjis$HMU0qLy@6tuja-7QYn`(!EuCuHk-z zbdBbHqK8}qVN(~m`Wh-HRAwy7XY261kly^#i_&-|UYf^X$V9I0FxQUiY1Z3`yQe-0 zc(lfSrrW|c{8_HlN;D?*N3-wLO{Ec(Ken$;9VCvyqKhkXpKQj4vc#kxqal`two%w4 zNYV9H1VkRw)gGf)Gor_n7= zU|&5Cj}D=|gHgH3gK=&rdUMsg@_JeG9%k9P`+6ckdG+Pc_$BH3!niwQJNY6XcDEY5 z?rSwj9*0nR+NX%rX!``0+ja3ygqD*#T4DqIF{|x2wzT4@N5=2GG<-hQ2C42DAaL>Z zre>jMDumDZONY0sJhsxFP$x2RazVDwutlQRg`c^s`j$3-&b%t(l^0yq+dz43ADo=B zkT;9kK)?ObBz7h7cv)RzGw1rP8TH4?<##aGuXHLP^IncpjpQ=f4IkQLq&}_Tsgs4i z4#*E#aPgb+N79Xf{-yP!ORmF{D|Z(baQevT6jnhQK+20ACs@-xxMG=R1KW`Cu7ji6 z@!2l$eq%hd`v`Giu}@hX$3v&OVer)mO-iyELa2a>PjqEEB0*1=0tonq^ca2P5*nX;+0S zYcCxgR4whe!pH07m8T>zfxo@}CawbN)n1OLQ zK8_dScDLe^{UBZt`E9p?$6hEdVo;(SXw>`sJKrO*h)|G=t9<6Zo$NKrT%t8ra4SNa z6@AaJ)eh43y*RbZJ(|GLR5t(Q6010v+_F{2<%G?P(d^kX)f!iIWD^A+a`PEC`w0_C zr{Et{E7W&x|6up6V&{rOma*J?H?65NTQue;LLv8z8AUY{s(@HHsW zF09lZE33aK5raQ9d^zqZrY{b8rN}pWPId5@GousDZ){&eO|W0gfa2LW8I~_RlCGT=+qISctMKKAm5UGqBMe0`o5E6*H#k%QLp*6o5V}VdNq62;jgQqq9rC z@G_UR8*}@!oH!oy>HJmR%SF}w<(Nl5=rC#JR4=f81dzZ#bNudr5On;UUapH)D&|6h z44A~JB!WG|p3>dg5aS58=5njlt9H?ysLwuuyx8ip#MFG6G^a)7wdk})NCZUTfj#%y zV0-rbasBdADE=V5cgeFP_o`A{KZ>Z)CpXUW?%i3|X7X$C&QsH0vQuPn!Ld2xwH+Bl zxx=G02tL7J`ph@OWJqSxPFO=h~Q8IXW2Yi?+14V1zFr(jrk^{4J8& zK-44?7I}^N=8;H{lY#_=E=op3vR-~sxnlQR!Lx#@(P1z40E>&kcDERhD*3k4BKyvZ z^6&tz+$JX19&vwy>X;+r@x~vU_rv&YW3x2Zt`a1e!+4e7F}RbTA5-^6!hsd^uO;jW zDG?)@jy9VbT5rk3Do9klC=WW}&zfY~-s|^&zU#H6UDuA$F_jxSC}_3ceZ3j0?Qo)y z+bsdNmC;(?bq3z%C_I#J759`>BV{VF&2Hv18!n=Qt$ySEO-9%vrQuCMM>l4WBr%>2 z!Ylfb3b6*VbT%8Xg>Fx3mVHmC{z-=XSyGUL&%Ay(Ca(tN)EO)=V-br|&B+Gqu*RtU z6C3L0$86yAP0%H7N2s=hR&>vzoUb6~aoC)S?-*le{&^FJhDU2Rr)NlztMO+e|FqQr z&!^9M_}H>xg28xKPP&*JN8`J8gGz++8bNenuj=kTAcRGFVg{DoOI#(7stAD>R~ty2 znAOV_KTMkwqc<6rdYm#C91!k|+204{1azBjRO^*HD`u{CMTWdA%;`e-3QTz#1BiY9 za)SWFj&{fEx!s{~io@CnVl=?kmA`bl#PZF22x6Y(XBx4`A#FWgbVaXda#NHcWVe`J zo9{CwrK(#3YQ$Xw@a}ZTK0LP)qBBGE9W~lGGUwvD;4Hdzf0SV~6j)THxZ9HeuheTS z7c6Sbucm0s80@A&qUAIE6JUlT)Xup&+9rBjdv5 z>|(1bVj`p!;n%2J%1 za=dI3T#9O5_Ez2sHrm#yE%sqyQVIirxTMT zm`xc3;q;Pl;AFGYP?dDI=T=c?V)BIY{##zZS1KCvK{ORJfR&I6M?I9e8CFr8yQ`i zO9@Vhp`Dhto}I16Zzug@4Ji0$x#nL=Ldo4-?f%^&(7#lZiVA}S|Dr#r?#RI)J7+1N zyR#d#5|sS`ek;oSYcc5m4Yi>fhSJiUdU`S(k}hB;RdzQnTMb(#iNCRru9Yja%GJtM z9$Kxc;0$tyo=4w?R}#c2Z6$B10kxd0g$;)eC(nbP|7z7g?)u%juIln^8de5uG8{lF zYXw_!S3Ow)cO6A(b_hG0wzaaGwVO5%M4Hb==&cdDI{p{4|Yny?YC#Q7v=qW3tL&fuqwGoBiovvS}AQ zgQIeJH54cMMQ_u3ycDb$S9!=ynJH*9EF(dW=XA*W_ RPvYpn4ShbG`K~(I{{a|P-46f& literal 0 HcmV?d00001 From 2d71369ff0b1aa409584ce8e2ba2d1f1c34f7a12 Mon Sep 17 00:00:00 2001 From: Pascal Breuer Date: Wed, 6 May 2020 15:58:54 +0200 Subject: [PATCH 2/8] Added unit tests for reading excel core_properties_set * Set the locale to en-US for tests, as tests break on non en-US machines --- pom.xml | 1 + .../poiji/bind/mapping/PropertyHandler.java | 5 +- .../metadata/CorePropertyTest.java | 26 ------ .../deserialize/metadata/PropertyTest.java | 76 ++++++++++++++++++ .../metadata/model/CorePropertyEntity.java | 74 +++++++++++++++++ src/test/resources/corePropertiesSet.xlsx | Bin 8760 -> 0 bytes src/test/resources/core_properties_set.xlsx | Bin 0 -> 16820 bytes .../core_properties_set_password.xlsx | Bin 15360 -> 23040 bytes 8 files changed, 155 insertions(+), 27 deletions(-) delete mode 100644 src/test/java/com/poiji/deserialize/metadata/CorePropertyTest.java create mode 100644 src/test/java/com/poiji/deserialize/metadata/PropertyTest.java delete mode 100644 src/test/resources/corePropertiesSet.xlsx create mode 100644 src/test/resources/core_properties_set.xlsx diff --git a/pom.xml b/pom.xml index 1692626..e127581 100644 --- a/pom.xml +++ b/pom.xml @@ -94,6 +94,7 @@ ${add.opens} -Djava.awt.headless=true + -Duser.language=en -Duser.region=US diff --git a/src/main/java/com/poiji/bind/mapping/PropertyHandler.java b/src/main/java/com/poiji/bind/mapping/PropertyHandler.java index 40ff68a..0d3af45 100644 --- a/src/main/java/com/poiji/bind/mapping/PropertyHandler.java +++ b/src/main/java/com/poiji/bind/mapping/PropertyHandler.java @@ -70,7 +70,10 @@ private static void setPropertyValueOnTarget(String propertyName, POIXMLProperti ReflectUtil.setFieldData(targetField, poixmlProperties.getCoreProperties().getRevision(), targetObject); break; default: - ReflectUtil.setFieldData(targetField, poixmlProperties.getCustomProperties().getProperty(propertyName).getLpwstr(), targetObject); + if (poixmlProperties.getCustomProperties().getProperty(propertyName) != null) { + ReflectUtil.setFieldData(targetField, poixmlProperties.getCustomProperties().getProperty(propertyName).getLpwstr(), targetObject); + } + break; } } diff --git a/src/test/java/com/poiji/deserialize/metadata/CorePropertyTest.java b/src/test/java/com/poiji/deserialize/metadata/CorePropertyTest.java deleted file mode 100644 index 6c65430..0000000 --- a/src/test/java/com/poiji/deserialize/metadata/CorePropertyTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.poiji.deserialize.metadata; - -import com.poiji.bind.Poiji; -import com.poiji.deserialize.metadata.model.CorePropertyEntity; -import com.poiji.deserialize.model.byid.Person; -import com.poiji.option.PoijiOptions; -import org.junit.Test; - -import java.io.File; -import java.util.List; - -public class CorePropertyTest { - - @Test - public void readCoreProperties() { - CorePropertyEntity deserializedProperties = Poiji.fromExcelProperties(new File("src/test/resources/corePropertiesSet.xlsx"), - CorePropertyEntity.class); - } - - @Test - public void readCorePropertiesWithPassword() { - PoijiOptions options = PoijiOptions.PoijiOptionsBuilder.settings().password("testPassword").build(); - CorePropertyEntity deserializedProperties = Poiji.fromExcelProperties(new File("src/test/resources/core_properties_set_password.xlsx"), - CorePropertyEntity.class, options); - } -} diff --git a/src/test/java/com/poiji/deserialize/metadata/PropertyTest.java b/src/test/java/com/poiji/deserialize/metadata/PropertyTest.java new file mode 100644 index 0000000..2b35824 --- /dev/null +++ b/src/test/java/com/poiji/deserialize/metadata/PropertyTest.java @@ -0,0 +1,76 @@ +package com.poiji.deserialize.metadata; + +import com.poiji.bind.Poiji; +import com.poiji.deserialize.metadata.model.CorePropertyEntity; +import com.poiji.exception.PoijiExcelType; +import com.poiji.option.PoijiOptions; +import org.junit.Test; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.util.Date; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; + +public class PropertyTest { + + @Test + public void readProperties() { + CorePropertyEntity deserializedProperties = Poiji.fromExcelProperties( + new File("src/test/resources/core_properties_set.xlsx"), + CorePropertyEntity.class); + + assertExcelProperties(deserializedProperties, 1588771680000L); + } + + @Test + public void readPropertiesInputStream() throws FileNotFoundException { + CorePropertyEntity deserializedProperties = Poiji.fromExcelProperties(new FileInputStream( + new File("src/test/resources/core_properties_set.xlsx")), + PoijiExcelType.XLSX, + CorePropertyEntity.class); + + assertExcelProperties(deserializedProperties, 1588771680000L); + } + + @Test + public void readCorePropertiesWithPassword() { + PoijiOptions options = PoijiOptions.PoijiOptionsBuilder.settings().password("testPassword").build(); + CorePropertyEntity deserializedProperties = Poiji.fromExcelProperties( + new File("src/test/resources/core_properties_set_password.xlsx"), + CorePropertyEntity.class, options); + + assertExcelProperties(deserializedProperties, 1588772003000L); + } + + @Test + public void readCorePropertiesInputStreamWithPassword() throws FileNotFoundException { + PoijiOptions options = PoijiOptions.PoijiOptionsBuilder.settings().password("testPassword").build(); + CorePropertyEntity deserializedProperties = Poiji.fromExcelProperties(new FileInputStream( + new File("src/test/resources/core_properties_set_password.xlsx")), + PoijiExcelType.XLSX, + CorePropertyEntity.class, options); + + assertExcelProperties(deserializedProperties, 1588772003000L); + } + + private void assertExcelProperties(CorePropertyEntity deserializedProperties, long modified) { + assertThat(deserializedProperties.getTitle(), is("TestTitle")); + assertThat(deserializedProperties.getCategory(), is("TestCategory")); + assertThat(deserializedProperties.getContentStatus(), is("TestStatus")); + assertThat(deserializedProperties.getCreator(), is("TestAuthor")); + assertThat(deserializedProperties.getCreated(), is(new Date(1588689638000L))); + assertThat(deserializedProperties.getDescription(), is("TestDescription")); + assertThat(deserializedProperties.getKeywords(), is("TestKeywords")); + assertThat(deserializedProperties.getLastPrinted(), is(new Date(1588768892000L))); + assertThat(deserializedProperties.getModified(), is(new Date(modified))); + assertThat(deserializedProperties.getSubject(), is("TestSubject")); + assertThat(deserializedProperties.getCustomProperty(), is("customValue")); + //Only testing, if field is null, as newer Excel versions don't set this field any more. + // An older excel version is required to set this field. + assertThat(deserializedProperties.getRevision(), is(nullValue())); + } +} diff --git a/src/test/java/com/poiji/deserialize/metadata/model/CorePropertyEntity.java b/src/test/java/com/poiji/deserialize/metadata/model/CorePropertyEntity.java index 6a82c27..15bcfc0 100644 --- a/src/test/java/com/poiji/deserialize/metadata/model/CorePropertyEntity.java +++ b/src/test/java/com/poiji/deserialize/metadata/model/CorePropertyEntity.java @@ -12,7 +12,81 @@ public class CorePropertyEntity { @ExcelProperty private Date modified; + @ExcelProperty + private String category; + + @ExcelProperty + private String contentStatus; + + @ExcelProperty + private Date created; + + @ExcelProperty + private String creator; + + @ExcelProperty + private String description; + + @ExcelProperty + private String keywords; + + @ExcelProperty + private Date lastPrinted; + + @ExcelProperty + private String subject; + + @ExcelProperty + private String revision; + + @ExcelProperty + private String customProperty; + public String getTitle() { return title; } + + public Date getModified() { + return modified; + } + + public String getCategory() { + return category; + } + + public String getContentStatus() { + return contentStatus; + } + + public Date getCreated() { + return created; + } + + public String getCreator() { + return creator; + } + + public String getDescription() { + return description; + } + + public String getKeywords() { + return keywords; + } + + public Date getLastPrinted() { + return lastPrinted; + } + + public String getSubject() { + return subject; + } + + public String getRevision() { + return revision; + } + + public String getCustomProperty() { + return customProperty; + } } diff --git a/src/test/resources/corePropertiesSet.xlsx b/src/test/resources/corePropertiesSet.xlsx deleted file mode 100644 index b1caef521450431da94d40ceb4fa562a9474b35a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8760 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 PepW!CzvajF1K9rok9y2D literal 0 HcmV?d00001 diff --git a/src/test/resources/core_properties_set_password.xlsx b/src/test/resources/core_properties_set_password.xlsx index da16ba5558ab6d8dd0420a70e9c289ae86427645..78445f2c8bfe7bfdc9a3cffe450a7bb2d43797c0 100644 GIT binary patch delta 17843 zcmV)OK(@btc!0D4uowafDgXcg{r~^}{gWXEQ#Bp{0018V001BW001EX001HY001KZ z001Na001Qb001Tc001Wd001Ze001cf0092~|NkqqaRyfbk_2o8uni#Us*`a9hXS}k zlllZ|4X}j_>Z&8w0YDlW>Z-GG1#<-r;A;=+sw36`kQou`s*~{w^9`V}6Y8oX)&bxz z8S1K&@e6Yeps^F`sw36`kZc(0sUB8x_4)6;W%9;~<+)f$uM4Q8+$UqY*HKP0f)#u?pqCZQ2G?pwk{259+Sh|>g zb*Pg$$6>T1uy;E2D;PlDG1CRH*ygD>_(Jll;)N_2Dy0jBAdB`LoeN$TgGdZ=M}#B| zf8_tXb2!QR62TCEER7(Z;z2quPUP{e@i5y;DvATf+0HEDw*LmqUFUmSWhCT<)1gg* zM1Urxt_TWLBWQATAeEegzyvCaRONfs<`T4?<7Hk4HT%ek~&ho61y0A zFVrIh1iY1h%)Ex2usc%Ym=xaonP1h4w*!BqOp6p=w{b9~DMh8(tc~AS8c%1|3p_s#=$eQiOKL}r zDdaJO=`}}GS%8;tDh<02eC|4&-O^|OXEoD7m{lczj}$9J5k3FHQ+nTh5Vc~G>CGL< z`{&k0XJ=OM$@6r#D_$|${M|*{tSS*W^B&{?)LFdyd(e*Kfu4f~(u`WtI!N2ogY{RG z&m!WH*QaEph79;ecxO6}*(oAp%>fr^>|DyiK`e|iP;p-YKi#bLzx42ivC8Z%Oco4= zhlp5ze}hN^tg8GtZ)pA>S=`2=o^CSzfE+7LPwKN1CLOd>8_yl^Rp7C`{tz8L?b<-^ zlN^HcZgFNZ@@}Yp-Z1@h@n~|ol?fPUzh+&*_zG<0tEh()wOebS*jV&rnYhc|Uo_wl zfJQj2X8hZOZwl6C7P*1_5;)xy36_Mh8=lL5|78Td-BxG^3qA{4daG^-{^7uA06I@P z&o=%sj+9zWW=lNsarx$=C_yQfyS2eMLqEC-`d2e$SJJ{v<#wEs4L!5@)p&P~?B0*c zE6IU7xC;Z0!SluY@s<1kQp&#LeiMu@qMdZ;S+|afwm*pE(2?~U^j*a(1xj+iXjz_r zA=o~aB{=~y%(0ufv~8CRmz7ldHQ>C4R*9F$_XzXb(w$Kcb6~}4gj8eraxng?^m(a) zi4_1PjzdTpC0+W^@s@wyj0IY=B`0-!@yh3fBGiPB0??g^ZML zfHl#orL-W#71z}_dtUFcE%$5mN;`XhEb%G84q15NjZK&(9qc!J0ovQcJjvod2zs+` zL%Z7WFK)Bi>tz$PxK~f+l6uM@N`AUu@!y~aMX-_a-S;C5!L&hGOva$nOO!9%@W(AP zZ_T|s=#!n+bV>3ha6?4vZ3EIl(%vvtKLpoN@z;7nyAYMvGXlIf*x!G-o1#g7_LtCn zRC;um;m(W&LV(ogUnfTJ{hO+Lq#0+)Rowj$H%>=D{wG7bx%e$omM3+t+j|OV5qj}Q z2zUtF=+o5GGQjL2y*E(EfqxLfPfG^MMS!|*neF#&pLI#%5jg?Z^N?`%M*&T zVNK&4Si>m5{xGKcM^h2PZ1w|x$EJ%JI)75MdLbN6Y`*KGlht& zO4Q_}DIvvG%gxTZCZ|%}a9gdU{KBM7HRv!GMs<4|8nz}QJ#c(F1J^xEhnR|+_#j`i zH;q346ThfkB5HT~b~CKnDCpeO9_tGB@#nx;IxzHdZBrr4b+BMl4=_?2paXyGe+tes zgft8#d(2_)vhdjgNAe4QL1RsX_^-89`6H>aD06FQjGSz}0i*jE{+nqK2H7b+uRoht zj;$^fB;PN%extf}2Q;aAuCUL-nib3aKuC1RWOX<-aps3wF&Qj{%#hbCDHyi7O4cz5 zXAE)?Iv^XUm`PKZOMpq8qc!4rz`Wt!zn7}@<33+95Uchmd=xx?QgC?4dLv$Y<}+Aw zgJOegQ_bJSmw3P-l3qzl^)NMEP#Nue%-aM!h%PHP?g!Q!iYTb%6UL}X?WKJ?SjCY2 z5Es?Wu4aJB%z$VSXg2mRHh6}@6P3-EBq#}sT{9*Y;U9x&A_W`y7n}}Ty z?d1I@RFu>zK=@XF+7%;&#`r?!Ei50H;KG$+sAkM>Nm`K6>O%5Z$HDQ)saNtKepmu1%jP_wUo7-NK%$^{08+6rj}tH68P zeID1!!+U3uVkjw;;}|bYPt8h4Po&JFP>EgNFb{%r(ePMZpUe8rXlG>b77ad|EA+r{l6(I)u(Mx8 zZ7M}4GD_FM0Lj->cH6%yIen8~nDfeQ^6uH`V5V zw&buwF)PRs0R!DT5e)`g#VyQB{~l*3v_dGcN&bk>zs_lAu>+ zUL`DaV!q#r^{GI&aEf8T^)c#c=HxO1M^%5}A=go0m=hOU*#@hJ+CH4w6TGMwpxvS{ zMYe}a%3(1>)(=R9}KRI)iG9p z$1l(~)*6(aYyvM>V6AOa8#cmW;l8tP111 zK@{_ox^;=1TW9S$UHE!ePNZ-Z2vh45JjT?n{9WS%y3TriM^_sbA9Dp~|EgE0GV7&I z=kZjk6IeMHs-F z%Q}QkHJT0BUru?%KX3O1#haEq)>$z$okct)Q?4oxpQicGlXkO@_8c>KHp@3A@lM{0 zg5I+sO{Na&lq@@4!nVGN*gy0S!%*>-`EA^jcP4IdbQWL8pM*yGMvy(QuKq2!`>ADkj6QG%DAUbWaFsr`3)~_r2RF#pX|Fx7AR<|4Y1Q9PzUXELy0(M zH=?Hnn;l{}sr(QaF5g9;{+^uKk9zpnV8}g2>fy;fif`1EM`r+$xbprllK9UfjvZ&_ zznIM5ss*Z?Ye2rHQFv@bqJc4{=`0u@40M+N?i*5n6P~(()Lt}ycK%m)E9O)oPViG5 z)yAZ;5Er{??|DJkUsKclW5w;XfIA;n*XYtF+6BN6+Fw{VcP+a-vJ#KKfQs-|LG{7u zi$%g%M$P7$b^g0f8_8MBbtngSjfF(>kZDEnA8SZ5?*t$!O$IE5>CaDTsUrU%8OA#%m;D<^uyJZVk4QPh8q)~l`6&`^n%R6Elfoiu?(bv$uQu} z>|r$;^}*g`aAT9L`NIc&7uQQ_1@))FeMNutVUCh4{aCzz2RIVp(OvhzX3fWNDuBe0 zr|$eIUp_dNgG!QZ_`VnG;(YHjCSt1U^hw0_mvwzoqYtB1d|7~|rKS30?WBHgNxl=Y z#ZF2iVTbDFh&rWe{hPZnL;30Efg-)Zfe10w=w;w1@;>>X=C-*=~roGZj9+q}!wD9!&0iHs8 ziM6rlS)`sC!4@s_Fg0^=-~`(-&?@j?OIaXl-MBJ;#l@fXt{vi|N`C7k<9FS@apvVQ zVqATz+u5e2(wvXG|;*A5Z%-Gb8e*U=RkPLt|^m5a9HU>7XU6xwor*S z5D|QTDM*&!?d3n|7)3xT1;#xi09e=Lwxb{6dB7>lQzMk!XtAulZ3nk2%;5~nr5vxR zKfVibUEmkKONtt@JYwU?cCrnlT>0#ouThbTf*iO2XNvytItOZ%?Oa$$l2Y z6y`KvTvq6Mok~dKN|={=x4_FXhuEioJiFJX zIVynZu1OK;la0ARl}j7M!)~4QguDbDDddNL>|$_kDbGBSZyp@8kJ8-@1eN0z_jf*j zcV@4T_oVz;=w7a4(xxk{8pIRk-{6|v*aq2x`5vZiW+DfEr!m#)Aa*sQf>Q&T#SmTI zn`3Aeu*2!g%Cr+B=irFMx9sI~qUV{DXfXc8qnlMnpERkm55(m)-{)!ZT+9!x0rO0= zaXQOO`(K6GVvfm7zLCI>86DQT=Jg1FwdjI74!wGS^J6TP!=C^WkZP!Y6Wtu~LOY{i z#blibQSPL5d>C>#r#byRqk8N9;!2AvqD0H|n^W?ttUfkA*}*B4SOCj^GSO~>Q^%ngx*vcJ+FgX4U>+PPURPnO+)hFS+S3=7 zF-d|Pr5jb~;}o|8;FkgaoGzq=&ZQOhgt%vx>Z7hF8-JKyg-d3K>u78a`QQ!kQlM`Q zK;895i$cxe+W8o%*X0|cSmcduU0!&d2*e^!hyisPhZLs?XF)_4UJEjRgHTY63i9Zl zGtoo=FW7+G2)2;AmnezLi*GGi%~Do-RKElr|3GTXVs>lH90&w9DG%?tlu_I+Z&L#Em zxgKX(Ul!^XJwc=#Q+Uygx%US?E+`ZTmNbFl&>+i@t?wC$-F4tTEm+AF#lI%F#QdQxsa)X+1FYKs{orZ*J`VK< zfXHrkFs8pA3yEcazi1n$eT+S)!$8#7zUXOeIL}U8H$`@ip~`j16i8z1NNU$**1DoYr9U1L$W^$?+Un-c7)0afdSuA zh86{Q837)DW=RmLdniPmEtgv}^$!08#!~Y6_leaZ>t?8lW$@NSMME(D_XiTa4YgHf zjqNN=Q|&FygF1CYHAO4*H4T`Q#{|Ni2`x4t>9sabnB4L6=}3=uw+dq9Z?i7#>C4$x zUvKqGo`)>Prj`@62W9h9MC679b^LZhcj)~Mi<{Yh{5d#XUEhNw=`Rw`*_|7(4r=@UF~tNpl-sLx5r65cx;t{s?<%tjr{stjR{M;t4eE}GfRpui6G*hiyH=1v}2 zZCUJpsMouC3($Scq+Rwl9uKjJP-R`?)9a8v<~A%C>jzYd8C;#Y>TLYBcg!<~a&P+N zTG~Rlv6XAx$PAEQKHe|LS_>aWc!wF=fWtn?7vLhlzqLaOm;{%0zGJ3#lxUsD4~#B{WRxQ$0>qLhn}1ro3J zr9!8T+3R3Kp=|Uzq9HW>cbZDvHw!GovaUm=F6TE5(Ym-gt_62J0-FhI9@nlxXU+#9 zg89E3LZe%QvBo6FX|cJfH+e2&Kbs;OI;Amylk74dleA1rGnLvZ6O$K6*&Eob1;Jf^ zRV#fDzav4+=8)ua8che2pSPixop zel?KKU@2Q2S#ev+O1t;y0QC`EMD^hyS@!Y*(jZm}9C9w;TZNPN!Uz!5m0U*KXWj-<$qvh&ka^&4 z=H)XW91No?P7zYx%I61=4&-xxlaCxjo!+d)d?H)Mj&=PoN1`%q17tM9kum+w2%h?& z{!Yf9kF$|`?wK3q`BayF8&lRowCaV<_(E`I zlln2etxxeBO8j%zmPqj@4{KC$22z=&NaMsUd^r}&0&u{V5-gbSyO878{)vQx>&jWW zg+YXJhLMe<6w4a`=5%6byNYCDH>2PfvfOtdzgbAP9FgA2(s7G>I&Ql=Z!O^e677*E zl{Kp5;Vyozh7@A%1A29Ty$yrj6rTx;R?-p0VZgGjw6-jIZkJG1EV>PlYKXA3tsACP z-Y_cXCBaMrn-Vp+ztW{r1df57uF3^8hFrQE|M20ZyFsJC^8q>5>AWcj<0&{UGZFr&New^Aa#Fadn>^f zd!--3fy}2o5daXCdMYASf6rjZ7<+6C(c8Vgmd$B}wYM9fWKid7$jWt_sh)_1(joHJIAtEc}(0U}rh0vUbw##8Z3Yn)z%@gP^)R5t5fXrxn7hCA={;I!=p z`*j&E=~hMw2cwh(e8|R1Mf70CLbouu9~Jh1UdrKwF)eJ0HzR&f>!zhf-)w}V&jRgu zQ;N6vrtlAcuAo5MZhX2F$ngU1Ms5ZOXfU@s}oc_RCl#tUI1#5TAcWc7Y@d zOKRIbMZjC3C%ZUxa0a8h&RkVVcRQH5**@Zmhw}GI9BfT+>>7;1QzEwhA<$`=CXs_Q z6Jj!#R)7|{Ae0HP?D<#qSj-}F0a*+CU#c&`Y38~8p-(+-SoAOqz39rNssY!5JkxN@Q$YpQ80^96o??K@b|KLyN?J_n`1L~`l#8g;y?L_^f- zUicu9M{q&7dg3E7o#&Yd+w?u5Crhi4fxu^1vtSvL(EVLYcmF3=QB^^L83>HGj&Ldo zMe&u<{?LME5Qh~{N2$S4(BOF#&B@~H;QuA<#E|$)4{AV+{u47j88RIZiu7yjn>7i4 z@Z0GDdU{C!jYQugd{!VkW&7&N@yKkM2x{1~BSq5gOiNtCv;*?Fb;m%6*;&Ec9$)u*jL0J<&&yDysB&1#9~8lJ_A`>`7!TzEFt8y zbG6w0Lg$0MRXd9#u@eQP;WZty?R<-WCh#m*KGO-mB3}-^LY{K| z$IYMAGv7{^zlDwf6;`>t^ZnUkJj-G(EjjGoW?X@OkdbiD>Le`4TjowBG8|i8%_vBj)WfSW*~B1fmZ?~&^N9F$%cG6o3z!NNwZIcKRExw_d98SD5ZUT z9PQS;4p%6kuo3k9d}GFvF#pDoU1X-Q>-Sd9CMR-sLE*P(=?yi_XV!xIbt4T*A!8=W z{uy*(Y~YWFlZO^&WG+l{(TYUYY&be zwr$vFs76GC9JfrhqO<%#IUSRK$fZv2Oa(Dz{=R{?{Z?s(arSFEa%~@6Sk+Onpbuo^ zWCK{z%+GJ~eT@Kd})6tr`9W15TI^HhSHr{aAGg zu3k_~5_GC_7#{PIX(PI9_Nx*sxW4uMD&Lz@juRo>{}2j-DyX?oT?rU}YjCaS0eRk% z3+qw1L??l1$LxMoD~luu#t&|G9A%yZ9{pvNVtu{-p%L~mI48rjHT|q7LUYX|0Q^pFA4DfVR`*I5%DBb}-BYsHw z;MYbwc7LUCv+wALfguP5Y5}{kJVPfsAlb$zKJZduHMxu)8&RQDbAF_1iR6^+e2`NM z`C@$}eL^osfYpiZZibLR3_{KB!BmL#E4@Q%TzFny$Zb`!;=BUo3bba(pMX{a3%GIjPz%5OGWW6< z$xRTR%(ZD9H3z1Yz;8!QLB9!2_I3fG(O+afrzT?KAwSc9Cz`h}2C?yJpeDqFmu6=% zv`SGOKpkj6|Lc*fpGDc+j2MJTaixyg0Z0doryo1oXD4L1o~9iPPhcWXXD(_9^p7F; zA{OqTB5ey7H-e8r>&Vi}l84#<;%tnz*`lwN&VOZJh95_aSw5UR0jcxlXoHeuxg%J< zXkhXW+2W3WNz(3R=i89EYUd)GwN0;=KCHY`>8kkYO{Nk#bOM-Qfj=kkun4tJJ7K#dfpYCNoc(QUZ?vyr>A_D_Isp3{?gaqCt}RZX7A9j8@sLZ?M5E>1$)&>-o9rHuH?5;(11^ITYvQKU#<52D62Iy1qJ`Y4Y66g9-+%ov7 zTf{D>=^(Oohs*GRaie<0)1-Y|Pm{I41UegqohtpA?L{rlFyb1rG(FQQ*bh>-612Fw z5uxE3vHl0*?cL}#gWDFJjVE%+LN5IS*kD)y7@N+RT>es-P;S=Pq^3@9of*6r}aP#gGa+d zcBfK^eI^BF<{Q!;T3!kHC&6+pbJ(|cEd$zSb~ zPGWCMxY|)mzeLpbgC+;_Z@_3O6=vcCuf|eqS7XR%#`a^WGI-ncnMQ6%)W z4!V|tC#h6rVDgrgSuwZOA)H8mLOSGsG6iv;jBTn01pwg)Xc&1!`5~o43PBfOAumnRT6fOcYBz?`%zYWu8`*TjkKn z*VCk^EPEg18`&xljFOsdf1v?XnND_CjBRP%?hfPbsnOsNfIL)fVi}o#asM^sY--Sn571Rs&CZ?@IYFd5aALJ}_dn&5A zOcazB8Z9s<&$_uDOZNGHK?jsj%@^DjDi^yoG3%p^9+C0eF~l9GCN4kaM>a3+VnN9_ zajg63iaKaaK|}FGct!`x!)-rKysbnA__ziuAx~5;^xS5)ZfEDR?sCvQ8)O~7TATw+ zL+OHH`PxIXRr^KM#-F*IXFDC7nniuaIR|4fR}5P0bDcw0iDuY;yC-&4YZ~{Xd^JXX z5p%A1YfW8c8}VMD3`Q|ZmoCJQV`<%qg=zBmsdXMD?*W;tsS@pH-C*d5io?ttcO#va z_=Grh#jdouvBJI(g)OEb=gKn$-?|Ol@BTe1@uWu?hzUy~_D;5#)l) zpq7i7cNnpIzZtd;&t}aGpqniY^$>-ImnVx`@TfutnBv4fe&sEXwXMMjvkbQ;wrC|k zcwL#cyGu0P7JPYZ=o;`j@FttD{{0s8LrnYs*ZWFjhScH}VbIk!2p1Io^9=Pcr<&Wx7A zTq&ALYZ-9(iwntOCUf-qktceRP|cRO-RY92ZPF^OU_=QzTi*;HWo$n~T=_Izq0R#^ zH}Gs?p2ysQR??%1Sl!@-R-Nw+2X1h843N>d3906Pcc4El@t;KxHh7QxHNgGa@B;~t z+khCrj3CZ^i z5MMxk8UZcIOSe%=E_iLAEtQAbS+be>Do5_)D_2;{5kfsiSZUB-=U1aHrG_?+*nO&g*Y$z z5?(|+2GoWkt8JSAg%U)UN`qYFh#na4tf;p*<(c4?ZzBRw5SmMlhlF6o16aQ!sWzWC zOd!!y>o#7-ESLKr;rl~c6?zE_M9+oYs5Q)g&}YRyZhGNk@Qh1^uN?(dX5)ZIaiUI$ z!X(*x=GOV2bNzU?C#O5*0}tPc9XKl`KaB7lb0yyw?eQC~D|l0Q2IRM7^lTl=@FG}W zikBcCD!LKtVjgLZJWegW5kVP2x0}`2ieMc1Sz;?Qt^^4&;l-2rbU$C97Jr`Yi4r$| zeg=;YBfs``N)0!=M}Ix+o$^zq_0PRmKtjuN>7ZCKSh$8%$s$!{C+fSoEMSLSayJ; zt<<339QE5;r4?~kAP`ta+zfrB5b68pAFlp(mDU#+*D|k153^gO5 zD7Ch23$kPv{iMrv4!!`?hna_x^}q%)_h(c4mvMzc7V6DVK!O_74FqnVvmzk`;y3Mu zT;z@%N@c@3;g6oybu3DrsaNoyV1453>c{X6sa__il$78Qpy=MCR(7`>nYQ8C%V*r; zp!SR}fmjcY?R6Hgy+#N6`f9I#u(ixRktjJBqV4n_l^96?WgHu|9S9hz2|G`sB@dUm zqmtu~S5+mHY=1QFEE6?d&eIT`a^0dvHXQtfP&ggktX03IJpL8%geFxYxgW)dg(FvoKXAkAdj5tUfFBXQ?uT+z?!$_FYdAy~N@l_0fN+1Bm=5)D?Kv@cfWyixb$QG~RC@k2w1Kf=y>x zz_0~}*$RW-*d&tzJk1T~12)@Dl3K*Y*WisJQ-I8t-k`I6$1#QqC-KHDk3i z`jK!63w+V27`|a9G`ll@;xTXc{Kn*D(_REg*{qD8{c8`VUW7AvyzTeHaMN5WK4cH` z)!tL8AKT?@-9y%S*)R_+;G{0&CAd?u-1J9>*?X#9)m>u{x>-{z?Iv1;^-W&x4#Ygj zIJ~lD03ZMReV3PetvWedxM)XPdzJ_#h*toTbR)#OsgwClFW{ShZLg%9S{RV^2Gt+O zd$6ZDy$n0mQD9Q^%uiekQH(C@`<4-awftd+Qd1Fz1b+h&zKK8{DRX|RkcTYyeCV-{ z(42iPMLK?s{CJXBBt41!5#)w38#sjL)%gH?*-sHL;lN2Juq~d+?@2v;k&4)86*S2^ zr`&|JO)!JO!D^|0IF)y;e|Ir8W|WVGnKmTEG=LKw5qL@ooAZP{_A53GWIyv}+rc#2A3hLjG+%*u zVzjxSRmV!Pz_fnaiLaRrvpJfJ0O7RoQ?aLCuV^Jev`zGX0qdqU#U9Scp~nbzv4+}B zaDsjcVMbOd9Os=g6-*eriZYCqqR=_~p>U9DoaVtU;C`8X3a}bZN%)#ua5~4EJhSq~5r(SHgJ$=XrYiSSW z%bA|I8Gg;C3vfB`T24?d--~diNXaEnhVI7HofKQjB|0u&5o)6y}Gvii-*sllo z=)&z6vK)e@BU~CH^kG!Y_@K$y`%g+Ucrn1+Tq}A+boLw9X>W!)O8$S1D>BPrM(x?Q z`A7m457kcW$VSlj+w8TE7^H(WP18`e&CddnOQ9zcO{9lf6;`A!Qh*u*>V6!gCyA>V zj6B_cW>IH6N8jUtIo!)ezU4hH(o*8$kXb}Vq(Z&HsqgQEn%)tc{-8H}$h zD3mCjA%VhK1bNAka>Hzz%qN|ia<64HVJ621=6h>a4jR%c16tdq5ms=tUFDW5h`h-- zg0o_x`kHghC}3@879l6>hc?v`s@h-WF=xHL-y(&=pEYd^OQk3*6xEJW=^b%@u6L{J zc?2zkDF;!>3x=JHB1Qo;xfvT;RLy|>Ktnz_8HLQyG!9Girej{5AWkK#lR5y<@&jRd z1dICY03?IBQ_k&?BV{qjRKz%fUe&mW^Tl$LDYyGk8i{(D2eq+62}cYCkh;mVz?rtw zG{en!O_ZlRkoYuj-C{z!<@*1BB~QZKzLKwP!k`dt66Z z5ED|HAse|S&lffzD*8#Z=%f|vVFyC{$L4oObeDjSf6600|5uIciCh1F*#7gcwhWzl z-J#CmD!9w&qxtyNx{AdY&ZSN=)^xZW*FCOb7Iv?3b<^O1-hj$s4kUYo0Z7J7*rZQ) zVVW{pW8a?KCh~;SxwD^>MwSqv*6h5Gd<`)Q?VRe5YjB9~1l0GmbPb!k5V3N;2?*9$ z4cQ6S_}`Iths6Q!zsq@lMJfQvu(sf?>RDdlm8a;hpv5c}(Qt}$7 z`oq)85HdgZKe^WHmRoH7@W5k}uuhEsK4^q@o=qCb0<6x)-*31VYd3XY1`FMC6o+)7 zTC?7X%TSTSmd0u~=2HM3dzVLZ?hPQ(LBrhi4f)y6(eLka&Y3h(JKJoXob_<%Hxvyz zda#H4frTHAxdg?3+;;t$JSKt>+-^|AnfV$L6eOQYj=7)rR_{C1xcOqyzZSAiYASbg z*%uyk`u^pRSyy(yHY6EPA^v8C=_hHrb_BF8Y=S%<;7v$yU)R9Oe1Y)DCv{jiq&k=EdfjJX-`u@Z2^XY=_aGa`_ zQ%`JnKw$TcRJ_Lv-<0tNZ+Z(WRpQdLx*J17i*h5FM?S8PnC>R*G$|W7kv8kC~lVBwL^s$v`_$}5;At|J*K^?;_j`W+Dj++Y6K@( zv3b4c_S0xmF%2c=h`btvy0q=s z`n|t!3sEnh(z1Psr&YrCw5<|QoD)3!Au;+gtb_&w$phUkjb%aV41s-U3W==hY<))a zJ(!k`JY9tihh#~jOWVfz@lbGKUDG2o==W9?FRDC$z9N2-37YEg*qv&T@}j7z<0V(d zEQ~qcym5ja!e9a{Stm3>NfGQ8`Jre=29MAIUt}S;{qB!Xvte0WIJLc$j&Y${ z(Q?s!1a{D%zq~3K4#(tDda#}!v5>`oc`U^#oBE@(5twmNCus>9+h%=1;-S?y9!7pl z;4hFSvqU!B}2Zqyz?2VH{prwOL&S`L&BU|4zT$D)GpA($M(*8Gpe`WvfkZ=;`f6AnvtFt5 zO2V*Ms(+c59aDi4cGXrH;sbXm`r#5;GecW{7NThMRSzvc_zuH08lg7QuUYMplqXJz zBV0YWqhZ2kg+049CGyyk6MOe{y*5etRXm*UR~qetdIBT?956;b=t`dmHHvM_Tj)1kSA$p%w1BVdxhti%l~FC|R$3F&hFtiM&y(cGQCYs0$ivug8 zb2f{o0$M`Ceyr-MK^{zv0t~rHg1B#|wCg65oCxW{rZBmE25*W4EL&)5-ecg@s2cZll=5i#xf%18z?kQ=!P{so^7{(FbiCa zCsh7-C(RIOXHWPL3xN8k~Wpl zLu<<&$hoy(0E5?ovHQ1DFfDTv>5`}+dcUqM^=a4ZO)z@oC?NfK7nqE~s~mGA=>B(4 z=y#3%B3>m+C{1F5;H3jR^Y%p+Y#;>ho3pA zd}4T6fkFnoM~1|Jpq)~N7WO$V%@TFSyJD)h_E6~P&6tQ+!i!;t7k3Rl4 z?RG|xIWKXES?a0u@y0uy}0Z0Rk=kyg7I8U4EnvGz?H!Ljfv+gi32_B7uN`c zVnOrb>hHbQY|6KPs}+^NG}0ldi#4?D19TKBslefJy%)#f2pTQ2ACjXyKDpNfE z-{ieW>@u_aHa3m^mkBj)H3YnrBa~Xeu(>3jeX9_H5DcP!Vj7PfT)mT^wZp#L{AzjIOPNTGe_jiKXdTP7{6SCo;J12G^P|dBewx&PQjAd0&>qqY+(A{q-ZkqjXjDrg?pnJb=w%UHBm1?A|{I35EvF~2cy_cUr9Lu0gMAjsC556uOx9ouLV z#|MI^a-Di?qVb<0+$kh$c9H5)m*yVJqZ+`sh87Pw~#Wq$YRt zfoZ!iRXdKNl2FJ6Pns=T**MIs@h1pydnZkzhflQt3s6Sq8hT@+5_@tw>}(dZ1s3&Z zb6X{j%dB|N6QryV$@6Lv0IOyA1}v zz%*8W^p=80;FjE#`yVO-M}QPj1u@jiH{M5eXr9|TqHyXqusW+nW(d0itv?eEYe6mx z)0{w`uWRG@!%bEtjsUK|bT0^B0t|oI!D|)a6_~eiEV%zi(ltb~je1tzdn#xtn z`y$h9YFCBO*=T-yhGs}$>P^)mO(M9b<_!ja9X;OglN=gOL!D?AmXwf!VFR0ia70QA z`e}-99s~_?-Ya-cfc9wiwdK#We?%vT@o}>XRq`LXYkWJeAMe?YjiMs_)Iq`3pHvSK zyZxHOQ&)(Ee=kkI?}A0(>8t9VWzMKbJM0{K!`&4ExNyaTr86jFyGa7d%&Wx3Wn&$G zA@Z9Gvz~G5Y;%X(0}6D?AN04z>ww2Jl*lbAJ9o#9QgXko&HXOUUJVI~(#L7wYcNAn zZ!R#=oHY7xDgMyu^^vLug_sS`#e${PW3FW~rP`BO+|W}t@|p=oZ=migjj9`6bWjh< zJsxDAAleAaewFI1A-UnaxC_Kk<4^E??u_AWdfUzQFY!GTTK_OnwL1qK- zj3DiDG&uZWk{u$lhUGDqoce1IrxDtImcAtfRbQ;0747=yDR0ioD7BVNwsw zc`<=6F*R0`4kCY+D2^oqLV#s|t~Mox*w1>B%AkNo0cr)6`W)>}kuio;nxLss6iIr( zinF5db@-1Z%R#IHVZbOorS7I*u=9FUxk4f!j1yp@Vj>>^wHxoH=uM?Xf1w;KI5qld z&)kF>7BkW(DNP_Gfg-f%h6@#lcq9%C*koMW`Ap6WBvq>Q@6d_yPOvfJ$qu%~4}odh=Vz@;x-cw&J8b-XH!}4M z)x=;mPv}8}m@eX{ykCYdfAA)1e@;3z=GKCGd{kb>gnz3MZ`+{zZ%AvBMLnqpWstyc zbAQTecPPl&zgd||nBI2zpDKEafxccBP1U-)Z~FY7-etr^#(30{qWp*aSE$@9bKe9_@#tGdwfQ(a3)8T?BuMzf3fwv=psqD zYv<$>%%S)Uq2JW<{l(w{b%&zcOx}RAVQcj<;bL;MS#;^=XL!Xnd_q8Ap~|Ln^xOJx z$VT) zj4}pr{FQfJMa@5QODk>9+ELB6B{a&EiVbz8q6tufrcZy%mgwQ^e`!byQM^nj_z&A@ z9BPVb9BH{YS^hxO^Hh5JcZmW@>tBS&FGgj?a}n7$N{m__qC2m_c2JWRQaBX+^gz)? zh$3x}!^i=Kk=Ph4?;T%{IAo#iX07X`^=>=|xq<76z_pS3HC2n+g=f@_IsH?f)i3)@ zW+6is+T%dT^UYC(6nOvaY=J*mV`WZoFGFW>Gi*pWGLsHCCx1^)FKBLOcu8kbWh+*4 zQ*Bjtb~br+I5kRUa&=)jWlB_6LvC_KP(mFq)NkMi?YIi|yN=0)~Jv|~IWo~0~d2n=PWJqmcV^(2ob!9ywLpWJ>XKQI% zc3F5rWO_DLWJOnXR7_@6QAA;7SyOOTZ&O)KD`$0UaYIc|Hg!WYYdA+yVR$l7H%DY^ zV{cG-FmG#6NJVZ@+Bh~@1$>}tnlkp1k4ItaBmhUv4vvCZF0|!QUOJr+CRI>pP{{w&jMIrzI00000niK${ zCU9?Yd0GAlnFpvjK%nP%!2jJ5RG-{)GK1O7*P`6Pmm10oc#dqron{*GP`*mp`wri- z;BdCgPNyy*$eJMPMFh0gK!GBZ&g0uOrv{2RZ(PEz>Trc_bNeVyb@6H4Ehh4~5)WPR zT-wy>6{VHQ{jh(JHA}ALehi|RiqgG(A2ZQ2E11G!eYYSr=%2qR`F}}6oh12L^S%o$ zp?iVkAyMmEn3gW9;T5Xkf18fY&Q6TrO&~6zO5Ddo!Vi%e=Vo}N)U63VPVr?rYqD=X9e6mvbxV@-5(p!=bY2Y!KMnq zv${~<+wbM$L{|o7DMj2izg2FZ@?1O4i^!vqPTc$uyMSgU;FA~$th^rUukBwfcgYI4 zsI&lVU8H}aV7&ynI!!hd{mGOHmTB6h41yvTX}DgvjHv45Dg9O>tslr!1r!|3^SJGE z^NW#uOaY~Ma8v6M9XmcVK8Nwc%RO{(E&vb0cVjfP%};L;#5&WxMZ9ba+qX8gOuj|^ zm-wgC+PK~nY7g~@Q`OQ5PCS6oPnfF;SLcUc$Pj;>a~W8TKi`wuf^))44o6tngw__Q zP_oa_$GIpc64A>gw;B-sS`C7ZHT0$0t*h#pG*MK`Z8W>jPC|{*#p^wg0#3#^QPU$d zQOp1jkrdPSR>{MDP`X2wZ)?q%CPEWUZ~&+sLdcap2OJ&P(=bWrK_|$zjI8{)FpJSS z=RJSQ`45BNdXUUJ^KkFE!O`LWsfz?}8uC+wX%m-HQLkP$&o;poklS*0Uj(q9*-Jvp z#-LG@s`I2!Mz{aMDsxqkRuH@X@65i1b_2dEV?7TIo5z*!Ljns)MP1$6HFVI_^$qrw ztB)#Fp-amGA_S3T`Zq?B*|kAZJ~h6_l&^nc2x>C60)hNcH%8a&gm-XR^~A{N*F2zW z-9)*91E?1?|ui(hQd}>jpba z&gRA};TY?>+^8tm!h^~F>z7(KAd{YL9`m_&BFE=DGA-^tS(-{2aXX_9&(Zgi&L*Kr z6Uk~Nm9hchm$|Jz)zqv3y6Z-39AbZlbKxG2B9{;6FUxv5PY)TnxL<%lmIRjL+xI&& z@{cQdDpT<)?nz}#hujcZL9LM7!Do;#bPD9ptf*_t?!aSn^nf^V42EN`P_ta^)hn78 zGlEOsBdd(5Nz38|Js@TUi9N+x$IOYvAQhzQPm&UgxJu3HNu{5jZk3F$QzJp0y_s-eWOPyqpJub^hyu+h6x5y2d@ zJ)XmOxJ(^&N_Oz8b+b>0{6&8_|Dm+O`B_wlA&cJr7u)NtoRw=)AW!pjS#g1Rd$ViL zo6U=*O}OIBVEQYEuz)`|7ol7gRu)^VwUv+~)32qZ_)xBe+md`m6SHFk!J`%H+Bs6q z!w!0_t>y>|ZNZ7KZHtjHTX)RNex*Z254G2bC&5Zcf34w^YybpBRqKCz5D=&m@@zFB zqc2W7Ee_(fwWsnc+k&mLQ@hK+DLnDwgf!vOdfqfKrITo3c<{Np`!Ud8{~Z+Txxa?> zZsmAWM6i}*R(Vfb87da0OSISbJ#IDGFRY>P|6hT`xq7T%}wkIBtI|lx%T~uSy3e663B4(t5t6ZHUHLd@g@o0u^Uj_Lq7QZvGaORE!Fj zpbT#k`hG6rlN~5(ITY!##dW7^Niq=_rHskmfi&7A=5OH&b*2SyFMu%TbSo`aMq2Z# zu{AUr%CeuGnC)iqzrq}@G@y@con0mg+@CnVBc|bqS@DbxZ@5!O+e<2lCWcmAgY$I{ z5{uy*OYDC)p(-;|!~~M7=-ejlfFti_r@d%FY;*_y*#51GbeD0_5-^MKS2L)$8BS<9 z7R+TXi=UV(aRvMOBqOrfPz?Y)c=VAN(eb4OzDA{6JdfZWzNl4RC8s_#H2v<#^)aF* z4zWl5vZSwQjn!~QE?v6Py_^Q%i}|&?D%m2W?_7WW|0pKab-zn8O06Vi_?M#|nc*bt z%xBE%28&iT3-YCZ!Mt&~UZ(Bbd~W4~s7?YbO!kJ-_d#Yf-?9y5@6MErKqW2MeOuW} z%%(D3l{PpK;EXx-Cr>yEQ{1h0YSzN;(|o$ozOZxA2Onp-lY%TiaK>yQbB^IuqhUB2 zGf#gg2xnLR4hzn}1%2LGZ*XU-V+LdcjYWcZm92}Z=b|-mM*X5a!|1~7Crhj$W zMS;b{ym-IPcQGEq;gih)Okbf6i;ZO?847=k`QQ+gY#wPoFwLu`o<%rZR?DL3J#tV~ zM8#|y|BJrs$_3+u=JeCm7OcI*>`&!1`6VyQ^=)IDJKBztQ5=MRT}*q6Ea!% zi#qz%M*~KQJ?KC5erV!ynNiyGLJ?6-r)7@t^kNiNH1DJlWJ<{P%5J5tcRu2vYmR>> z3z6;rn(Ik-a=eu<7ue^~P-(<^Z(tD7zpI>D_1cj+*Ko&OZ63~VtlcIjE+QY9{)DU? z21s3#2`y27_`#L(1vPw27Ua!DG)gEqKHI5a`rNeG z-hcejP=h0X(o2_@5x}|vDu}?AzjnB$9|OXVNL)MVtS#DlHi#-I3Kt1_Co~qDE_D)< zKu`M*<0D#t+`3|@eXs$1l59)OUW@LgbXKVu->8Kc zFa(6woKgot7z@5^7gu%njgcV|cAk}^1jj(SQSMzO8%kYJvXTr>M3dk~)`GwqWSQK4 z+-ETw{ZY2o!{-9B`H!n7GwH62tX`Mg@8z;V-FD~ApX+Pq?(f@1b{BvB;3*|C?Okyz zEbmMt=V5SQRcSm*S+#QA&kxKdTREG?dpRnm?*6a?RKSPzlTu2p1G+E4vUd?<&Jc^X z>cT&i#1ezD7DQx}Kut;R0m$x1d#giO40};&_;E%_9sKp46 z<(baIu4(LT*W2!LNG-mjxj{IISDq$uD0Ad6fp~z2W}?h5F`s_{HXy8%S_p)PLyA!( z?e2fz?wVm&4xWfU)BNbe;-v)!l(N}K8A@NU^C4nC9JnBEsgRw%_t`!wE|R95PJ_&C z4V0k3EpSKpM}?yakmVX-xM{u)cW^#|`AeA*s&Z1QJ-niG^mQWE6WsFC#37&TquycSqK?jnnOH5LE*_iI^6aL;;4cDa?*7<)13#D*}3nA|u6_ufqJR&dN zL#)<{!0u`zwGu3^KW6o%^*dR+P>vE&6%;cs^O0~29bNcTH7Rx(pmH+iNvm$Fi%vVE zwZD)R4PmEW&h@XA_E!HEvuYE)B7vF_9#Kh(fr*8kDLKbobST)kB1=ToAIO78aW+n5 zrjc4mgT#M!o(?8I+=_^yuue3Y?`L}pgIKF*ef~1?tJ9t5%>~YV^V9g9*EUG@5WiiY;a|ZC z<6JqiWiw; zYQ5F0ezQLWzm|sTUOJHZ6(9=;HG9q?s*0>l|H05eq7BCpzMBHXb%1(1kCQ3Z+(f)J zFAD(46WL>+#7D1Fri#ykFkEI@Fow6dK{Z8Kq9}Q*%DFPbj#ICimtg>WW{EXGH8Xz^ z8{Gf@9><{O-n8<}Y^c*VNcx&t|Egb_^=K zt5gQacgcPP^Sk7v zw6DCxb*Z=eq8F!WkMR)G>RDj=9o-#389WIeDEL+6NhM-uUZqRtBP;Bi;t9ed36H*k z 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 Y$AuDOHO3n-eRmp;f#z5@6WqhMMq@J=*8l(j From 6e61982a55eb8524525af74075df0011e733eedc Mon Sep 17 00:00:00 2001 From: Pascal Breuer Date: Wed, 6 May 2020 16:47:29 +0200 Subject: [PATCH 3/8] Added tests for reading excel properties --- src/main/java/com/poiji/bind/Poiji.java | 5 ++-- .../{metadata => property}/PropertyTest.java | 27 +++++++++++++++++-- .../model/CorePropertyEntity.java | 2 +- 3 files changed, 28 insertions(+), 6 deletions(-) rename src/test/java/com/poiji/deserialize/{metadata => property}/PropertyTest.java (77%) rename src/test/java/com/poiji/deserialize/{metadata => property}/model/CorePropertyEntity.java (97%) diff --git a/src/main/java/com/poiji/bind/Poiji.java b/src/main/java/com/poiji/bind/Poiji.java index ec20c1f..46ea9f1 100644 --- a/src/main/java/com/poiji/bind/Poiji.java +++ b/src/main/java/com/poiji/bind/Poiji.java @@ -89,10 +89,9 @@ public static T fromExcelProperties(final InputStream inputStream, } catch (IOException e) { throw new PoijiException("Problem occurred while reading data", e); } - } else if (excelType == PoijiExcelType.XLS) { - throw new InvalidExcelFileExtension("Reading metadata from (" + excelType + "), is not supported"); } else { - throw new InvalidExcelFileExtension("Invalid file extension (" + excelType + "), expected .xlsx"); + //Must be PoijiExcelType.XLS in this case + throw new InvalidExcelFileExtension("Reading metadata from (" + excelType + "), is not supported"); } } diff --git a/src/test/java/com/poiji/deserialize/metadata/PropertyTest.java b/src/test/java/com/poiji/deserialize/property/PropertyTest.java similarity index 77% rename from src/test/java/com/poiji/deserialize/metadata/PropertyTest.java rename to src/test/java/com/poiji/deserialize/property/PropertyTest.java index 2b35824..eb3f05a 100644 --- a/src/test/java/com/poiji/deserialize/metadata/PropertyTest.java +++ b/src/test/java/com/poiji/deserialize/property/PropertyTest.java @@ -1,7 +1,8 @@ -package com.poiji.deserialize.metadata; +package com.poiji.deserialize.property; import com.poiji.bind.Poiji; -import com.poiji.deserialize.metadata.model.CorePropertyEntity; +import com.poiji.deserialize.property.model.CorePropertyEntity; +import com.poiji.exception.InvalidExcelFileExtension; import com.poiji.exception.PoijiExcelType; import com.poiji.option.PoijiOptions; import org.junit.Test; @@ -57,6 +58,28 @@ public void readCorePropertiesInputStreamWithPassword() throws FileNotFoundExcep assertExcelProperties(deserializedProperties, 1588772003000L); } + @Test(expected = InvalidExcelFileExtension.class) + public void readPropertiesXls() { + Poiji.fromExcelProperties( + new File("src/test/resources/cars.xls"), + CorePropertyEntity.class); + } + + @Test(expected = InvalidExcelFileExtension.class) + public void readPropertiesInputStreamXls() throws FileNotFoundException { + Poiji.fromExcelProperties( + new FileInputStream(new File("src/test/resources/cars.xls")), + PoijiExcelType.XLS, + CorePropertyEntity.class); + } + + @Test(expected = InvalidExcelFileExtension.class) + public void readPropertiesInvalidType() { + Poiji.fromExcelProperties( + new File("src/test/resources/cars.xl"), + CorePropertyEntity.class); + } + private void assertExcelProperties(CorePropertyEntity deserializedProperties, long modified) { assertThat(deserializedProperties.getTitle(), is("TestTitle")); assertThat(deserializedProperties.getCategory(), is("TestCategory")); diff --git a/src/test/java/com/poiji/deserialize/metadata/model/CorePropertyEntity.java b/src/test/java/com/poiji/deserialize/property/model/CorePropertyEntity.java similarity index 97% rename from src/test/java/com/poiji/deserialize/metadata/model/CorePropertyEntity.java rename to src/test/java/com/poiji/deserialize/property/model/CorePropertyEntity.java index 15bcfc0..0362b31 100644 --- a/src/test/java/com/poiji/deserialize/metadata/model/CorePropertyEntity.java +++ b/src/test/java/com/poiji/deserialize/property/model/CorePropertyEntity.java @@ -1,4 +1,4 @@ -package com.poiji.deserialize.metadata.model; +package com.poiji.deserialize.property.model; import com.poiji.annotation.ExcelProperty; From 3c6e461bdc51eca6decc1cd9d9c712769145aafb Mon Sep 17 00:00:00 2001 From: Pascal Breuer Date: Wed, 6 May 2020 17:44:10 +0200 Subject: [PATCH 4/8] Updated README * Added file for built-in excel property names --- README.adoc | 26 +++++++++++++ .../poiji/bind/mapping/PropertyHandler.java | 38 +++++++++++++------ .../poiji/util/DefaultExcelProperties.java | 22 +++++++++++ .../deserialize/property/PropertyTest.java | 26 ++++++------- ...ropertyEntity.java => PropertyEntity.java} | 2 +- 5 files changed, 89 insertions(+), 25 deletions(-) create mode 100644 src/main/java/com/poiji/util/DefaultExcelProperties.java rename src/test/java/com/poiji/deserialize/property/model/{CorePropertyEntity.java => PropertyEntity.java} (97%) diff --git a/README.adoc b/README.adoc index c3452e5..6ebed61 100644 --- a/README.adoc +++ b/README.adoc @@ -680,6 +680,32 @@ model.getDate(); 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. +=== Feature 12 + +Since Poiji 2.8.0, it is possible to read excel properties from xlsx files. +To achieve that, create a class with fields annotated with `@ExcelProperty`. + +Example: + +[source,java] +---- +public class ExcelProperties { + @ExcelProperty + private String title; + + @ExcelProperty + private String customProperty; +} +---- + +The field name corresponds to the name of the property inside the Excel file. +To use a different one than the field name, you can specify a `propertyName` (e.g. `@ExcelProperty(propertyName = "customPropertyName")`) + +The list of built-in (e.g. non-custom) properties in an Excel file, which can be read by Poiji can be found in the class `DefaultExcelProperties`. + +Poiji can only read Text properties from an Excel file, so you have to use a `String` to read them. +This does not apply to "modified", "lastPrinted" and "created", which are deserialized into a `Date`. + == 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`. diff --git a/src/main/java/com/poiji/bind/mapping/PropertyHandler.java b/src/main/java/com/poiji/bind/mapping/PropertyHandler.java index 0d3af45..17cfb7d 100644 --- a/src/main/java/com/poiji/bind/mapping/PropertyHandler.java +++ b/src/main/java/com/poiji/bind/mapping/PropertyHandler.java @@ -7,8 +7,24 @@ import java.lang.reflect.Field; import java.util.stream.Stream; +import static com.poiji.util.DefaultExcelProperties.CATEGORY; +import static com.poiji.util.DefaultExcelProperties.CONTENT_STATUS; +import static com.poiji.util.DefaultExcelProperties.CREATED; +import static com.poiji.util.DefaultExcelProperties.CREATOR; +import static com.poiji.util.DefaultExcelProperties.DESCRIPTION; +import static com.poiji.util.DefaultExcelProperties.KEYWORDS; +import static com.poiji.util.DefaultExcelProperties.LAST_PRINTED; +import static com.poiji.util.DefaultExcelProperties.MODIFIED; +import static com.poiji.util.DefaultExcelProperties.REVISION; +import static com.poiji.util.DefaultExcelProperties.SUBJECT; +import static com.poiji.util.DefaultExcelProperties.TITLE; + public final class PropertyHandler { + private PropertyHandler() { + + } + public static T unmarshal(Class type, POIXMLProperties poixmlProperties) { T unmarshalledObject = ReflectUtil.newInstanceOf(type); @@ -36,37 +52,37 @@ private static String getPropertyName(Field excelPropertyField) { private static void setPropertyValueOnTarget(String propertyName, POIXMLProperties poixmlProperties, Field targetField, Object targetObject) { switch (propertyName) { - case "category": + case CATEGORY: ReflectUtil.setFieldData(targetField, poixmlProperties.getCoreProperties().getCategory(), targetObject); break; - case "contentStatus": + case CONTENT_STATUS: ReflectUtil.setFieldData(targetField, poixmlProperties.getCoreProperties().getContentStatus(), targetObject); break; - case "created": + case CREATED: ReflectUtil.setFieldData(targetField, poixmlProperties.getCoreProperties().getCreated(), targetObject); break; - case "creator": + case CREATOR: ReflectUtil.setFieldData(targetField, poixmlProperties.getCoreProperties().getCreator(), targetObject); break; - case "description": + case DESCRIPTION: ReflectUtil.setFieldData(targetField, poixmlProperties.getCoreProperties().getDescription(), targetObject); break; - case "keywords": + case KEYWORDS: ReflectUtil.setFieldData(targetField, poixmlProperties.getCoreProperties().getKeywords(), targetObject); break; - case "lastPrinted": + case LAST_PRINTED: ReflectUtil.setFieldData(targetField, poixmlProperties.getCoreProperties().getLastPrinted(), targetObject); break; - case "modified": + case MODIFIED: ReflectUtil.setFieldData(targetField, poixmlProperties.getCoreProperties().getModified(), targetObject); break; - case "subject": + case SUBJECT: ReflectUtil.setFieldData(targetField, poixmlProperties.getCoreProperties().getSubject(), targetObject); break; - case "title": + case TITLE: ReflectUtil.setFieldData(targetField, poixmlProperties.getCoreProperties().getTitle(), targetObject); break; - case "revision": + case REVISION: ReflectUtil.setFieldData(targetField, poixmlProperties.getCoreProperties().getRevision(), targetObject); break; default: diff --git a/src/main/java/com/poiji/util/DefaultExcelProperties.java b/src/main/java/com/poiji/util/DefaultExcelProperties.java new file mode 100644 index 0000000..c485708 --- /dev/null +++ b/src/main/java/com/poiji/util/DefaultExcelProperties.java @@ -0,0 +1,22 @@ +package com.poiji.util; + +/* + This file contains the built-in excel properties, which can be read with Poiji + Each of these properties has corresponds to a certain type. + */ +public final class DefaultExcelProperties { + private DefaultExcelProperties() { + } + + public static final String CATEGORY = "category"; //java.util.String + public static final String CONTENT_STATUS = "contentStatus"; //java.util.String + public static final String CREATED = "created"; //java.util.Date + public static final String CREATOR = "creator"; //java.util.String + public static final String DESCRIPTION = "description"; //java.util.String + public static final String KEYWORDS = "keywords"; //java.util.String + public static final String LAST_PRINTED = "lastPrinted"; //java.util.Date + public static final String MODIFIED = "modified"; //java.util.Date + public static final String SUBJECT = "subject"; //java.util.String + public static final String TITLE = "title"; //java.util.String + public static final String REVISION = "revision"; //java.util.String +} diff --git a/src/test/java/com/poiji/deserialize/property/PropertyTest.java b/src/test/java/com/poiji/deserialize/property/PropertyTest.java index eb3f05a..c1eab62 100644 --- a/src/test/java/com/poiji/deserialize/property/PropertyTest.java +++ b/src/test/java/com/poiji/deserialize/property/PropertyTest.java @@ -1,7 +1,7 @@ package com.poiji.deserialize.property; import com.poiji.bind.Poiji; -import com.poiji.deserialize.property.model.CorePropertyEntity; +import com.poiji.deserialize.property.model.PropertyEntity; import com.poiji.exception.InvalidExcelFileExtension; import com.poiji.exception.PoijiExcelType; import com.poiji.option.PoijiOptions; @@ -20,19 +20,19 @@ public class PropertyTest { @Test public void readProperties() { - CorePropertyEntity deserializedProperties = Poiji.fromExcelProperties( + PropertyEntity deserializedProperties = Poiji.fromExcelProperties( new File("src/test/resources/core_properties_set.xlsx"), - CorePropertyEntity.class); + PropertyEntity.class); assertExcelProperties(deserializedProperties, 1588771680000L); } @Test public void readPropertiesInputStream() throws FileNotFoundException { - CorePropertyEntity deserializedProperties = Poiji.fromExcelProperties(new FileInputStream( + PropertyEntity deserializedProperties = Poiji.fromExcelProperties(new FileInputStream( new File("src/test/resources/core_properties_set.xlsx")), PoijiExcelType.XLSX, - CorePropertyEntity.class); + PropertyEntity.class); assertExcelProperties(deserializedProperties, 1588771680000L); } @@ -40,9 +40,9 @@ public void readPropertiesInputStream() throws FileNotFoundException { @Test public void readCorePropertiesWithPassword() { PoijiOptions options = PoijiOptions.PoijiOptionsBuilder.settings().password("testPassword").build(); - CorePropertyEntity deserializedProperties = Poiji.fromExcelProperties( + PropertyEntity deserializedProperties = Poiji.fromExcelProperties( new File("src/test/resources/core_properties_set_password.xlsx"), - CorePropertyEntity.class, options); + PropertyEntity.class, options); assertExcelProperties(deserializedProperties, 1588772003000L); } @@ -50,10 +50,10 @@ public void readCorePropertiesWithPassword() { @Test public void readCorePropertiesInputStreamWithPassword() throws FileNotFoundException { PoijiOptions options = PoijiOptions.PoijiOptionsBuilder.settings().password("testPassword").build(); - CorePropertyEntity deserializedProperties = Poiji.fromExcelProperties(new FileInputStream( + PropertyEntity deserializedProperties = Poiji.fromExcelProperties(new FileInputStream( new File("src/test/resources/core_properties_set_password.xlsx")), PoijiExcelType.XLSX, - CorePropertyEntity.class, options); + PropertyEntity.class, options); assertExcelProperties(deserializedProperties, 1588772003000L); } @@ -62,7 +62,7 @@ public void readCorePropertiesInputStreamWithPassword() throws FileNotFoundExcep public void readPropertiesXls() { Poiji.fromExcelProperties( new File("src/test/resources/cars.xls"), - CorePropertyEntity.class); + PropertyEntity.class); } @Test(expected = InvalidExcelFileExtension.class) @@ -70,17 +70,17 @@ public void readPropertiesInputStreamXls() throws FileNotFoundException { Poiji.fromExcelProperties( new FileInputStream(new File("src/test/resources/cars.xls")), PoijiExcelType.XLS, - CorePropertyEntity.class); + PropertyEntity.class); } @Test(expected = InvalidExcelFileExtension.class) public void readPropertiesInvalidType() { Poiji.fromExcelProperties( new File("src/test/resources/cars.xl"), - CorePropertyEntity.class); + PropertyEntity.class); } - private void assertExcelProperties(CorePropertyEntity deserializedProperties, long modified) { + private void assertExcelProperties(PropertyEntity deserializedProperties, long modified) { assertThat(deserializedProperties.getTitle(), is("TestTitle")); assertThat(deserializedProperties.getCategory(), is("TestCategory")); assertThat(deserializedProperties.getContentStatus(), is("TestStatus")); diff --git a/src/test/java/com/poiji/deserialize/property/model/CorePropertyEntity.java b/src/test/java/com/poiji/deserialize/property/model/PropertyEntity.java similarity index 97% rename from src/test/java/com/poiji/deserialize/property/model/CorePropertyEntity.java rename to src/test/java/com/poiji/deserialize/property/model/PropertyEntity.java index 0362b31..40eb376 100644 --- a/src/test/java/com/poiji/deserialize/property/model/CorePropertyEntity.java +++ b/src/test/java/com/poiji/deserialize/property/model/PropertyEntity.java @@ -4,7 +4,7 @@ import java.util.Date; -public class CorePropertyEntity { +public class PropertyEntity { @ExcelProperty private String title; From b6684e930e80d304b2d2d91a4c18f198ded17cd7 Mon Sep 17 00:00:00 2001 From: Pascal Breuer Date: Wed, 6 May 2020 18:08:51 +0200 Subject: [PATCH 5/8] Added JavaDocs --- src/main/java/com/poiji/bind/Poiji.java | 64 +++++++++++++++++-- .../poiji/bind/mapping/PropertyHandler.java | 8 ++- 2 files changed, 65 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/poiji/bind/Poiji.java b/src/main/java/com/poiji/bind/Poiji.java index 46ea9f1..f906fb4 100644 --- a/src/main/java/com/poiji/bind/Poiji.java +++ b/src/main/java/com/poiji/bind/Poiji.java @@ -45,16 +45,54 @@ public final class Poiji { private Poiji() { } + /** + * converts excel properties into an object + * + * @param file excel file ending with .xlsx. + * @param type type of the root object. + * @param type of the root object. + * @return the newly created objects + * @throws PoijiException if an internal exception occurs during the mapping process. + * @throws InvalidExcelFileExtension if the specified excel file extension is invalid. + * @throws IllegalCastException if this Field object is enforcing Java language access control and the underlying field is either inaccessible or final. + * @see Poiji#fromExcelProperties(File, Class, PoijiOptions) + */ public static T fromExcelProperties(final File file, final Class type) { return fromExcelProperties(file, type, PoijiOptionsBuilder.settings().build()); } + /** + * converts excel properties into an object + * + * @param inputStream excel file stream + * @param excelType type of the excel file, xlsx only! + * @param type type of the root object. + * @param type of the root object. + * @return the newly created object + * @throws PoijiException if an internal exception occurs during the mapping process. + * @throws InvalidExcelFileExtension if the specified excel file extension is invalid. + * @throws IllegalCastException if this Field object is enforcing Java language access control and the underlying field is either inaccessible or final. + * @see Poiji#fromExcelProperties(InputStream, PoijiExcelType, Class, PoijiOptions) + */ public static T fromExcelProperties(final InputStream inputStream, PoijiExcelType excelType, final Class type) { return fromExcelProperties(inputStream, excelType, type, PoijiOptionsBuilder.settings().build()); } + /** + * converts excel properties into an object + * + * @param file excel file ending with .xlsx. + * @param type type of the root object. + * @param type of the root object. + * @param options specifies to change the default behaviour of the poiji. In this case, only the password has an effect + * @return the newly created object + * @throws PoijiException if an internal exception occurs during the mapping process. + * @throws InvalidExcelFileExtension if the specified excel file extension is invalid. + * @throws IllegalCastException if this Field object is enforcing Java language access control and the underlying field is either inaccessible or final. + * @see Poiji#fromExcelProperties(File, Class) + */ public static T fromExcelProperties(final File file, final Class type, final PoijiOptions options) { String extension = files.getExtension(file.getName()); @@ -74,6 +112,20 @@ public static T fromExcelProperties(final File file, final Class type, fi } } + /** + * converts excel properties into an object + * + * @param inputStream excel file stream + * @param excelType type of the excel file, xlsx only! + * @param type type of the root object. + * @param type of the root object. + * @param options specifies to change the default behaviour of the poiji. In this case, only the password has an effect + * @return the newly created object + * @throws PoijiException if an internal exception occurs during the mapping process. + * @throws InvalidExcelFileExtension if the specified excel file extension is invalid. + * @throws IllegalCastException if this Field object is enforcing Java language access control and the underlying field is either inaccessible or final. + * @see Poiji#fromExcelProperties(InputStream, PoijiExcelType, Class) + */ public static T fromExcelProperties(final InputStream inputStream, PoijiExcelType excelType, final Class type, @@ -101,7 +153,7 @@ public static T fromExcelProperties(final InputStream inputStream, * @param file excel file ending with .xls or .xlsx. * @param type type of the root object. * @param type of the root object. - * @return the newly created a list of objects + * @return the newly created list of objects * @throws PoijiException if an internal exception occurs during the mapping process. * @throws InvalidExcelFileExtension if the specified excel file extension is invalid. * @throws IllegalCastException if this Field object is enforcing Java language access control and the underlying field is either inaccessible or final. @@ -139,11 +191,11 @@ public static void fromExcel(final File file, final Class type, final Con * @param excelType type of the excel file, xls or xlsx * @param type type of the root object. * @param type of the root object. - * @return the newly created a list of objects + * @return the newly created list of objects * @throws PoijiException if an internal exception occurs during the mapping process. * @throws InvalidExcelFileExtension if the specified excel file extension is invalid. * @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) + * @see Poiji#fromExcel(InputStream, PoijiExcelType, Class, PoijiOptions) */ public static List fromExcel(final InputStream inputStream, PoijiExcelType excelType, @@ -183,7 +235,7 @@ public static void fromExcel(final InputStream inputStream, * @param type type of the root object. * @param type of the root object. * @param options specifies to change the default behaviour of the poiji. - * @return the newly created a list of objects + * @return the newly created list of objects * @throws PoijiException if an internal exception occurs during the mapping process. * @throws InvalidExcelFileExtension if the specified excel file extension is invalid. * @throws IllegalCastException if this Field object is enforcing Java language access control and the underlying field is either inaccessible or final. @@ -221,11 +273,11 @@ public static void fromExcel(final File file, final Class type, final Poi * @param type type of the root object. * @param type of the root object. * @param options specifies to change the default behaviour of the poiji. - * @return the newly created a list of objects + * @return the newly created list of objects * @throws PoijiException if an internal exception occurs during the mapping process. * @throws InvalidExcelFileExtension if the specified excel file extension is invalid. * @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) + * @see Poiji#fromExcel(InputStream, PoijiExcelType, Class) */ public static List fromExcel(final InputStream inputStream, final PoijiExcelType excelType, diff --git a/src/main/java/com/poiji/bind/mapping/PropertyHandler.java b/src/main/java/com/poiji/bind/mapping/PropertyHandler.java index 17cfb7d..8668c56 100644 --- a/src/main/java/com/poiji/bind/mapping/PropertyHandler.java +++ b/src/main/java/com/poiji/bind/mapping/PropertyHandler.java @@ -25,6 +25,13 @@ private PropertyHandler() { } + /** + * Creates an instance of {@code type} and deserializes the {@code poixmlProperties} into the fields annotated with {@link ExcelProperty} + * @param type The type to deserialize into + * @param poixmlProperties The properties to read from + * @param The type to deserialize into + * @return An instance of {@code type} + */ public static T unmarshal(Class type, POIXMLProperties poixmlProperties) { T unmarshalledObject = ReflectUtil.newInstanceOf(type); @@ -89,7 +96,6 @@ private static void setPropertyValueOnTarget(String propertyName, POIXMLProperti if (poixmlProperties.getCustomProperties().getProperty(propertyName) != null) { ReflectUtil.setFieldData(targetField, poixmlProperties.getCustomProperties().getProperty(propertyName).getLpwstr(), targetObject); } - break; } } From c331a895a4f3a74864bd33c4cc9738a09ae461b0 Mon Sep 17 00:00:00 2001 From: Pascal Breuer Date: Mon, 11 May 2020 10:44:27 +0200 Subject: [PATCH 6/8] Added private constructor to SheetNameExtractor --- .../java/com/poiji/bind/mapping/SheetNameExtractor.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/poiji/bind/mapping/SheetNameExtractor.java b/src/main/java/com/poiji/bind/mapping/SheetNameExtractor.java index b63741c..48d65f6 100644 --- a/src/main/java/com/poiji/bind/mapping/SheetNameExtractor.java +++ b/src/main/java/com/poiji/bind/mapping/SheetNameExtractor.java @@ -8,7 +8,12 @@ /** * Utility class to extract the sheet name. */ -class SheetNameExtractor { +final class SheetNameExtractor { + + private 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. Poiji first looks at {@link ExcelSheet} then {@link PoijiOptions}. From a8385b31cffee4f72c1ba8d0722fb0997f26a6db Mon Sep 17 00:00:00 2001 From: Hakan Date: Mon, 25 May 2020 14:13:32 +0300 Subject: [PATCH 7/8] refactory property feature Signed-off-by: Hakan --- src/main/java/com/poiji/bind/Poiji.java | 47 ++++++------- .../com/poiji/bind/PropertyUnmarshaller.java | 13 ++++ .../java/com/poiji/bind/Unmarshaller.java | 15 +++++ .../poiji/bind/mapping/HSSFPropertyFile.java | 66 +++++++++++++++++++ .../bind/mapping/HSSFPropertyStream.java | 65 ++++++++++++++++++ .../poiji/bind/mapping/HSSFUnmarshaller.java | 13 ++-- .../bind/mapping/PoijiPropertyHelper.java | 23 +++++++ .../com/poiji/bind/mapping/PoijiWorkBook.java | 11 ++++ .../poiji/bind/mapping/PropertyHandler.java | 10 +-- .../bind/mapping/SheetNameExtractor.java | 35 ---------- .../poiji/bind/mapping/XSSFUnmarshaller.java | 18 ++++- .../bind/mapping/XSSFUnmarshallerFile.java | 23 ++++++- .../bind/mapping/XSSFUnmarshallerStream.java | 23 ++++++- .../java/com/poiji/config/DefaultCasting.java | 6 +- .../com/poiji/util/ExcelFileOpenUtil.java | 55 ---------------- .../deserialize/CaseInsensitiveTest.java | 8 +-- .../deserialize/ReadExcelBySheetNameTest.java | 17 +++-- .../poiji/util/DecimalSeparatorParseTest.java | 13 ++-- .../com/poiji/util/DefaultCastingTest.java | 15 ++--- .../java/com/poiji/util/ReflectUtilTest.java | 13 ++-- 20 files changed, 313 insertions(+), 176 deletions(-) create mode 100644 src/main/java/com/poiji/bind/PropertyUnmarshaller.java create mode 100644 src/main/java/com/poiji/bind/mapping/HSSFPropertyFile.java create mode 100644 src/main/java/com/poiji/bind/mapping/HSSFPropertyStream.java create mode 100644 src/main/java/com/poiji/bind/mapping/PoijiPropertyHelper.java create mode 100644 src/main/java/com/poiji/bind/mapping/PoijiWorkBook.java delete mode 100644 src/main/java/com/poiji/bind/mapping/SheetNameExtractor.java delete mode 100644 src/main/java/com/poiji/util/ExcelFileOpenUtil.java diff --git a/src/main/java/com/poiji/bind/Poiji.java b/src/main/java/com/poiji/bind/Poiji.java index f906fb4..64429fd 100644 --- a/src/main/java/com/poiji/bind/Poiji.java +++ b/src/main/java/com/poiji/bind/Poiji.java @@ -1,6 +1,8 @@ package com.poiji.bind; -import com.poiji.bind.mapping.PropertyHandler; +import com.poiji.bind.mapping.HSSFPropertyFile; +import com.poiji.bind.mapping.HSSFPropertyStream; +import com.poiji.bind.mapping.PoijiPropertyHelper; import com.poiji.bind.mapping.UnmarshallerHelper; import com.poiji.exception.IllegalCastException; import com.poiji.exception.InvalidExcelFileExtension; @@ -8,13 +10,9 @@ import com.poiji.exception.PoijiException; import com.poiji.option.PoijiOptions; import com.poiji.option.PoijiOptions.PoijiOptionsBuilder; -import com.poiji.util.ExcelFileOpenUtil; import com.poiji.util.Files; -import org.apache.poi.openxml4j.opc.OPCPackage; -import org.apache.poi.xssf.usermodel.XSSFWorkbook; import java.io.File; -import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; @@ -94,17 +92,14 @@ public static T fromExcelProperties(final InputStream inputStream, * @see Poiji#fromExcelProperties(File, Class) */ public static T fromExcelProperties(final File file, final Class type, final PoijiOptions options) { - String extension = files.getExtension(file.getName()); + HSSFPropertyFile hssfPropertyFile = deserializerPropertyFile(file, options); + return hssfPropertyFile.unmarshal(type); + } + private static HSSFPropertyFile deserializerPropertyFile(final File file, PoijiOptions options) { + String extension = files.getExtension(file.getName()); if (XLSX_EXTENSION.equals(extension)) { - try (OPCPackage open = ExcelFileOpenUtil.openXlsxFile(file, options); - XSSFWorkbook xssfWorkbook = new XSSFWorkbook(open)) { - - return PropertyHandler.unmarshal(type, xssfWorkbook.getProperties()); - - } catch (IOException e) { - throw new PoijiException("Problem occurred while reading data", e); - } + return PoijiPropertyHelper.createPoijiPropertyFile(file, options); } else if (XLS_EXTENSION.equals(extension)) { throw new InvalidExcelFileExtension("Reading metadata from (" + extension + "), is not supported"); } else { @@ -112,6 +107,14 @@ public static T fromExcelProperties(final File file, final Class type, fi } } + private static HSSFPropertyStream deserializerPropertyStream(PoijiExcelType excelType, InputStream inputStream, PoijiOptions options) { + if (excelType == PoijiExcelType.XLSX) { + return PoijiPropertyHelper.createPoijiPropertyStream(inputStream, options); + } else { + throw new InvalidExcelFileExtension("Reading metadata from (" + excelType + "), is not supported"); + } + } + /** * converts excel properties into an object * @@ -131,20 +134,8 @@ public static T fromExcelProperties(final InputStream inputStream, final Class type, PoijiOptions options) { Objects.requireNonNull(excelType); - - if (excelType == PoijiExcelType.XLSX) { - try (OPCPackage open = ExcelFileOpenUtil.openXlsxFile(inputStream, options); - XSSFWorkbook xssfWorkbook = new XSSFWorkbook(open)) { - - return PropertyHandler.unmarshal(type, xssfWorkbook.getProperties()); - - } catch (IOException e) { - throw new PoijiException("Problem occurred while reading data", e); - } - } else { - //Must be PoijiExcelType.XLS in this case - throw new InvalidExcelFileExtension("Reading metadata from (" + excelType + "), is not supported"); - } + HSSFPropertyStream hssfPropertyStream = deserializerPropertyStream(excelType, inputStream, options); + return hssfPropertyStream.unmarshal(type); } /** diff --git a/src/main/java/com/poiji/bind/PropertyUnmarshaller.java b/src/main/java/com/poiji/bind/PropertyUnmarshaller.java new file mode 100644 index 0000000..8e1846b --- /dev/null +++ b/src/main/java/com/poiji/bind/PropertyUnmarshaller.java @@ -0,0 +1,13 @@ +package com.poiji.bind; + +/** + * Created by hakan on 25.05.2020 + */ +public interface PropertyUnmarshaller { + + T unmarshal(Class type); + + T returnFromExcelFile(Class type); + + T returnFromEncryptedFile(Class type); +} diff --git a/src/main/java/com/poiji/bind/Unmarshaller.java b/src/main/java/com/poiji/bind/Unmarshaller.java index 783983e..003c090 100644 --- a/src/main/java/com/poiji/bind/Unmarshaller.java +++ b/src/main/java/com/poiji/bind/Unmarshaller.java @@ -1,5 +1,9 @@ package com.poiji.bind; +import com.poiji.annotation.ExcelSheet; +import com.poiji.option.PoijiOptions; + +import java.util.Optional; import java.util.function.Consumer; /** @@ -8,4 +12,15 @@ public interface Unmarshaller { void unmarshal(Class type, Consumer consumer); + + default Optional getSheetName(Class type, PoijiOptions options) { + if (type.isAnnotationPresent(ExcelSheet.class)) { + ExcelSheet excelSheet = type.getAnnotation(ExcelSheet.class); + String annotatedSheetName = excelSheet.value(); + return Optional.of(annotatedSheetName); + } + + String configuredSheetName = options.getSheetName(); + return Optional.ofNullable(configuredSheetName); + } } diff --git a/src/main/java/com/poiji/bind/mapping/HSSFPropertyFile.java b/src/main/java/com/poiji/bind/mapping/HSSFPropertyFile.java new file mode 100644 index 0000000..ace616b --- /dev/null +++ b/src/main/java/com/poiji/bind/mapping/HSSFPropertyFile.java @@ -0,0 +1,66 @@ +package com.poiji.bind.mapping; + +import com.poiji.bind.PropertyUnmarshaller; +import com.poiji.exception.PoijiException; +import com.poiji.option.PoijiOptions; +import org.apache.poi.openxml4j.exceptions.OpenXML4JException; +import org.apache.poi.openxml4j.opc.OPCPackage; +import org.apache.poi.openxml4j.opc.PackageAccess; +import org.apache.poi.poifs.filesystem.DocumentFactoryHelper; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; +import org.apache.poi.util.IOUtils; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +/** + * Created by hakan on 24.05.2020 + */ +public final class HSSFPropertyFile implements PropertyUnmarshaller { + + private File file; + private PoijiOptions options; + + HSSFPropertyFile(File file, PoijiOptions options) { + this.file = file; + this.options = options; + } + + @Override + public T unmarshal(Class type) { + if (options.getPassword() != null) { + return returnFromEncryptedFile(type); + } + return returnFromExcelFile(type); + } + + @Override + public T returnFromExcelFile(Class type) { + try (OPCPackage open = OPCPackage.open(file, PackageAccess.READ)) { + XSSFWorkbook xssfWorkbook = new XSSFWorkbook(open); + PropertyHandler propertyHandler = new PropertyHandler(); + return propertyHandler.unmarshal(type, xssfWorkbook.getProperties()); + } catch (IOException | OpenXML4JException e) { + throw new PoijiException("Problem occurred while reading data", e); + } + } + + @Override + public T returnFromEncryptedFile(Class type) { + try (POIFSFileSystem fs = new POIFSFileSystem(file, true)) { + InputStream stream = DocumentFactoryHelper.getDecryptedStream(fs, options.getPassword()); + try (OPCPackage open = OPCPackage.open(stream)) { + XSSFWorkbook xssfWorkbook = new XSSFWorkbook(open); + PropertyHandler propertyHandler = new PropertyHandler(); + return propertyHandler.unmarshal(type, xssfWorkbook.getProperties()); + } catch (IOException | OpenXML4JException e) { + IOUtils.closeQuietly(fs); + throw new PoijiException("Problem occurred while reading data", e); + } + } catch (IOException e) { + throw new PoijiException("Problem occurred while reading data", e); + } + } +} diff --git a/src/main/java/com/poiji/bind/mapping/HSSFPropertyStream.java b/src/main/java/com/poiji/bind/mapping/HSSFPropertyStream.java new file mode 100644 index 0000000..17cbcd4 --- /dev/null +++ b/src/main/java/com/poiji/bind/mapping/HSSFPropertyStream.java @@ -0,0 +1,65 @@ +package com.poiji.bind.mapping; + +import com.poiji.bind.PropertyUnmarshaller; +import com.poiji.exception.PoijiException; +import com.poiji.option.PoijiOptions; +import org.apache.poi.openxml4j.exceptions.OpenXML4JException; +import org.apache.poi.openxml4j.opc.OPCPackage; +import org.apache.poi.poifs.filesystem.DocumentFactoryHelper; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; +import org.apache.poi.util.IOUtils; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Created by hakan on 24.05.2020 + */ +public final class HSSFPropertyStream implements PropertyUnmarshaller { + + private final PoijiOptions options; + private InputStream inputStream; + + HSSFPropertyStream(InputStream inputStream, PoijiOptions options) { + this.inputStream = inputStream; + this.options = options; + } + + @Override + public T unmarshal(Class type) { + if (options.getPassword() != null) { + return returnFromEncryptedFile(type); + } + return returnFromExcelFile(type); + } + + @Override + public T returnFromExcelFile(Class type) { + try (OPCPackage open = OPCPackage.open(inputStream)) { + PropertyHandler propertyHandler = new PropertyHandler(); + XSSFWorkbook xssfWorkbook = new XSSFWorkbook(open); + return propertyHandler.unmarshal(type, xssfWorkbook.getProperties()); + } catch (IOException | OpenXML4JException e) { + throw new PoijiException("Problem occurred while reading data", e); + } + } + + @Override + public T returnFromEncryptedFile(Class type) { + try (POIFSFileSystem fs = new POIFSFileSystem(inputStream)) { + InputStream stream = DocumentFactoryHelper.getDecryptedStream(fs, options.getPassword()); + try (OPCPackage open = OPCPackage.open(stream)) { + PropertyHandler propertyHandler = new PropertyHandler(); + XSSFWorkbook xssfWorkbook = new XSSFWorkbook(open); + return propertyHandler.unmarshal(type, xssfWorkbook.getProperties()); + } catch (IOException | OpenXML4JException e) { + IOUtils.closeQuietly(fs); + throw new PoijiException("Problem occurred while reading data", e); + } + + } catch (IOException e) { + throw new PoijiException("Problem occurred while reading data", e); + } + } +} diff --git a/src/main/java/com/poiji/bind/mapping/HSSFUnmarshaller.java b/src/main/java/com/poiji/bind/mapping/HSSFUnmarshaller.java index 548d3b3..bffb1c1 100644 --- a/src/main/java/com/poiji/bind/mapping/HSSFUnmarshaller.java +++ b/src/main/java/com/poiji/bind/mapping/HSSFUnmarshaller.java @@ -36,7 +36,7 @@ * 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 { +abstract class HSSFUnmarshaller extends PoijiWorkBook implements Unmarshaller { private final DataFormatter dataFormatter; protected final PoijiOptions options; @@ -60,7 +60,7 @@ abstract class HSSFUnmarshaller implements Unmarshaller { @Override public void unmarshal(Class type, Consumer consumer) { Workbook workbook = workbook(); - Optional maybeSheetName = SheetNameExtractor.getSheetName(type, options); + Optional maybeSheetName = this.getSheetName(type, options); Sheet sheet = this.getSheetToProcess(workbook, options, maybeSheetName.orElse(null)); @@ -120,8 +120,8 @@ private void loadColumnTitles(Sheet sheet, int maxPhysicalNumberOfRows) { final int columnIndex = cell.getColumnIndex(); caseSensitiveTitlePerColumnIndex.put(columnIndex, getTitleNameForMap(cell.getStringCellValue(), columnIndex)); final String titleName = options.getCaseInsensitive() - ? cell.getStringCellValue().toLowerCase() - : cell.getStringCellValue(); + ? cell.getStringCellValue().toLowerCase() + : cell.getStringCellValue(); columnIndexPerTitle.put(titleName, columnIndex); titlePerColumnIndex.put(columnIndex, getTitleNameForMap(titleName, columnIndex)); } @@ -199,8 +199,8 @@ private Integer getFieldColumn(final Field field) { ExcelCellName excelCellName = field.getAnnotation(ExcelCellName.class); if (excelCellName != null) { final String titleName = options.getCaseInsensitive() - ? excelCellName.value().toLowerCase() - : excelCellName.value(); + ? excelCellName.value().toLowerCase() + : excelCellName.value(); column = columnIndexPerTitle.get(titleName); } } @@ -246,5 +246,4 @@ private boolean isRowEmpty(Row row) { return true; } - protected abstract Workbook workbook(); } diff --git a/src/main/java/com/poiji/bind/mapping/PoijiPropertyHelper.java b/src/main/java/com/poiji/bind/mapping/PoijiPropertyHelper.java new file mode 100644 index 0000000..ca4a979 --- /dev/null +++ b/src/main/java/com/poiji/bind/mapping/PoijiPropertyHelper.java @@ -0,0 +1,23 @@ +package com.poiji.bind.mapping; + +import com.poiji.option.PoijiOptions; + +import java.io.File; +import java.io.InputStream; + +/** + * Created by hakan on 24.05.2020 + */ +public class PoijiPropertyHelper { + + private PoijiPropertyHelper() { + } + + public static HSSFPropertyFile createPoijiPropertyFile(File file, PoijiOptions options) { + return new HSSFPropertyFile(file, options); + } + + public static HSSFPropertyStream createPoijiPropertyStream(InputStream inputStream, PoijiOptions options) { + return new HSSFPropertyStream(inputStream, options); + } +} diff --git a/src/main/java/com/poiji/bind/mapping/PoijiWorkBook.java b/src/main/java/com/poiji/bind/mapping/PoijiWorkBook.java new file mode 100644 index 0000000..6ac32d3 --- /dev/null +++ b/src/main/java/com/poiji/bind/mapping/PoijiWorkBook.java @@ -0,0 +1,11 @@ +package com.poiji.bind.mapping; + +import org.apache.poi.ss.usermodel.Workbook; + +/** + * Created by hakan on 24.05.2020 + */ +abstract class PoijiWorkBook { + + protected abstract Workbook workbook(); +} diff --git a/src/main/java/com/poiji/bind/mapping/PropertyHandler.java b/src/main/java/com/poiji/bind/mapping/PropertyHandler.java index 8668c56..72bf4d2 100644 --- a/src/main/java/com/poiji/bind/mapping/PropertyHandler.java +++ b/src/main/java/com/poiji/bind/mapping/PropertyHandler.java @@ -21,10 +21,6 @@ public final class PropertyHandler { - private PropertyHandler() { - - } - /** * Creates an instance of {@code type} and deserializes the {@code poixmlProperties} into the fields annotated with {@link ExcelProperty} * @param type The type to deserialize into @@ -32,7 +28,7 @@ private PropertyHandler() { * @param The type to deserialize into * @return An instance of {@code type} */ - public static T unmarshal(Class type, POIXMLProperties poixmlProperties) { + T unmarshal(Class type, POIXMLProperties poixmlProperties) { T unmarshalledObject = ReflectUtil.newInstanceOf(type); @@ -47,7 +43,7 @@ public static T unmarshal(Class type, POIXMLProperties poixmlProperties) return unmarshalledObject; } - private static String getPropertyName(Field excelPropertyField) { + private String getPropertyName(Field excelPropertyField) { String propertyName = excelPropertyField.getAnnotation(ExcelProperty.class).propertyName(); if (propertyName.isEmpty()) { @@ -57,7 +53,7 @@ private static String getPropertyName(Field excelPropertyField) { return propertyName; } - private static void setPropertyValueOnTarget(String propertyName, POIXMLProperties poixmlProperties, Field targetField, Object targetObject) { + private void setPropertyValueOnTarget(String propertyName, POIXMLProperties poixmlProperties, Field targetField, Object targetObject) { switch (propertyName) { case CATEGORY: ReflectUtil.setFieldData(targetField, poixmlProperties.getCoreProperties().getCategory(), targetObject); diff --git a/src/main/java/com/poiji/bind/mapping/SheetNameExtractor.java b/src/main/java/com/poiji/bind/mapping/SheetNameExtractor.java deleted file mode 100644 index 48d65f6..0000000 --- a/src/main/java/com/poiji/bind/mapping/SheetNameExtractor.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.poiji.bind.mapping; - -import com.poiji.annotation.ExcelSheet; -import com.poiji.option.PoijiOptions; - -import java.util.Optional; - -/** - * Utility class to extract the sheet name. - */ -final class SheetNameExtractor { - - private 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. Poiji first looks at {@link ExcelSheet} then {@link PoijiOptions}. - * @param type The class instance of the object model. - * @param options The Poiji options. - * @param The type of the object model. - * @return an Optional sheet name - */ - public static Optional getSheetName(Class type, PoijiOptions options) { - if (type.isAnnotationPresent(ExcelSheet.class)) { - ExcelSheet excelSheet = type.getAnnotation(ExcelSheet.class); - String annotatedSheetName = excelSheet.value(); - return Optional.ofNullable(annotatedSheetName); - } - - String configuredSheetName = options.getSheetName(); - return Optional.ofNullable(configuredSheetName); - } -} diff --git a/src/main/java/com/poiji/bind/mapping/XSSFUnmarshaller.java b/src/main/java/com/poiji/bind/mapping/XSSFUnmarshaller.java index 65c58fb..2801aaf 100644 --- a/src/main/java/com/poiji/bind/mapping/XSSFUnmarshaller.java +++ b/src/main/java/com/poiji/bind/mapping/XSSFUnmarshaller.java @@ -6,6 +6,8 @@ import com.poiji.option.PoijiOptions; import org.apache.poi.openxml4j.exceptions.OpenXML4JException; import org.apache.poi.openxml4j.opc.OPCPackage; +import org.apache.poi.poifs.filesystem.DocumentFactoryHelper; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.ss.usermodel.DataFormatter; import org.apache.poi.util.IOUtils; import org.apache.poi.util.XMLHelper; @@ -58,7 +60,7 @@ protected void unmarshal0(Class type, Consumer consumer, OPCPa SheetIterator iter = (SheetIterator) workbookReader.getSheetsData(); int sheetCounter = 0; - Optional maybeSheetName = SheetNameExtractor.getSheetName(type, options); + Optional maybeSheetName = this.getSheetName(type, options); if (!maybeSheetName.isPresent()) { int requestedIndex = options.sheetIndex(); @@ -122,5 +124,19 @@ private void processSheet(StylesTable styles, } } + void listOfEncryptedItems(Class type, Consumer consumer, POIFSFileSystem fs) throws IOException { + InputStream stream = DocumentFactoryHelper.getDecryptedStream(fs, options.getPassword()); + + try (OPCPackage open = OPCPackage.open(stream)) { + unmarshal0(type, consumer, open); + + } catch (ParserConfigurationException | SAXException | IOException | OpenXML4JException e) { + IOUtils.closeQuietly(fs); + throw new PoijiException("Problem occurred while reading data", e); + } + } + protected abstract void returnFromExcelFile(Class type, Consumer consumer); + + protected abstract void returnFromEncryptedFile(Class type, Consumer consumer); } \ No newline at end of file diff --git a/src/main/java/com/poiji/bind/mapping/XSSFUnmarshallerFile.java b/src/main/java/com/poiji/bind/mapping/XSSFUnmarshallerFile.java index ee160e6..2289733 100644 --- a/src/main/java/com/poiji/bind/mapping/XSSFUnmarshallerFile.java +++ b/src/main/java/com/poiji/bind/mapping/XSSFUnmarshallerFile.java @@ -3,9 +3,10 @@ import com.poiji.bind.PoijiFile; import com.poiji.exception.PoijiException; import com.poiji.option.PoijiOptions; -import com.poiji.util.ExcelFileOpenUtil; import org.apache.poi.openxml4j.exceptions.OpenXML4JException; import org.apache.poi.openxml4j.opc.OPCPackage; +import org.apache.poi.openxml4j.opc.PackageAccess; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.xml.sax.SAXException; import javax.xml.parsers.ParserConfigurationException; @@ -27,12 +28,17 @@ final class XSSFUnmarshallerFile extends XSSFUnmarshaller { @Override public void unmarshal(Class type, Consumer consumer) { + if (options.getPassword() != null) { + returnFromEncryptedFile(type, consumer); + return; + } returnFromExcelFile(type, consumer); } + @Override public void returnFromExcelFile(Class type, Consumer consumer) { - try (OPCPackage open = ExcelFileOpenUtil.openXlsxFile(poijiFile.file(), options)) { + try (OPCPackage open = OPCPackage.open(poijiFile.file(), PackageAccess.READ)) { unmarshal0(type, consumer, open); @@ -40,4 +46,17 @@ public void returnFromExcelFile(Class type, Consumer consumer) throw new PoijiException("Problem occurred while reading data", e); } } + + @Override + public void returnFromEncryptedFile(Class type, Consumer consumer) { + + try (POIFSFileSystem fs = new POIFSFileSystem(poijiFile.file(), true)) { + + listOfEncryptedItems(type, consumer, fs); + + } catch (IOException e) { + throw new PoijiException("Problem occurred while reading data", e); + } + } + } diff --git a/src/main/java/com/poiji/bind/mapping/XSSFUnmarshallerStream.java b/src/main/java/com/poiji/bind/mapping/XSSFUnmarshallerStream.java index 3c9f3eb..41127c2 100644 --- a/src/main/java/com/poiji/bind/mapping/XSSFUnmarshallerStream.java +++ b/src/main/java/com/poiji/bind/mapping/XSSFUnmarshallerStream.java @@ -3,9 +3,9 @@ import com.poiji.bind.PoijiInputStream; import com.poiji.exception.PoijiException; import com.poiji.option.PoijiOptions; -import com.poiji.util.ExcelFileOpenUtil; import org.apache.poi.openxml4j.exceptions.OpenXML4JException; import org.apache.poi.openxml4j.opc.OPCPackage; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.xml.sax.SAXException; import javax.xml.parsers.ParserConfigurationException; @@ -26,13 +26,19 @@ final class XSSFUnmarshallerStream extends XSSFUnmarshaller { @Override public void unmarshal(Class type, Consumer consumer) { + + if (options.getPassword() != null) { + returnFromEncryptedFile(type, consumer); + return; + } + returnFromExcelFile(type, consumer); } @Override public void returnFromExcelFile(Class type, Consumer consumer) { - try (OPCPackage open = ExcelFileOpenUtil.openXlsxFile(poijiInputStream.stream(), options)) { + try (OPCPackage open = OPCPackage.open(poijiInputStream.stream())) { unmarshal0(type, consumer, open); @@ -40,4 +46,17 @@ public void returnFromExcelFile(Class type, Consumer consumer) throw new PoijiException("Problem occurred while reading data", e); } } + + @Override + public void returnFromEncryptedFile(Class type, Consumer consumer) { + + try (POIFSFileSystem fs = new POIFSFileSystem(poijiInputStream.stream())) { + + listOfEncryptedItems(type, consumer, fs); + + } catch (IOException e) { + throw new PoijiException("Problem occurred while reading data", e); + } + } + } diff --git a/src/main/java/com/poiji/config/DefaultCasting.java b/src/main/java/com/poiji/config/DefaultCasting.java index 3a7a618..4ec74c0 100644 --- a/src/main/java/com/poiji/config/DefaultCasting.java +++ b/src/main/java/com/poiji/config/DefaultCasting.java @@ -1,5 +1,8 @@ package com.poiji.config; +import com.poiji.option.PoijiOptions; +import com.poiji.parser.Parsers; + import java.math.BigDecimal; import java.text.ParseException; import java.text.SimpleDateFormat; @@ -13,9 +16,6 @@ import java.util.Date; import java.util.List; -import com.poiji.option.PoijiOptions; -import com.poiji.parser.Parsers; - /** * Created by hakan on 22/01/2017. */ diff --git a/src/main/java/com/poiji/util/ExcelFileOpenUtil.java b/src/main/java/com/poiji/util/ExcelFileOpenUtil.java deleted file mode 100644 index 8f32a5a..0000000 --- a/src/main/java/com/poiji/util/ExcelFileOpenUtil.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.poiji.util; - -import com.poiji.exception.PoijiException; -import com.poiji.option.PoijiOptions; -import org.apache.poi.openxml4j.exceptions.InvalidFormatException; -import org.apache.poi.openxml4j.opc.OPCPackage; -import org.apache.poi.openxml4j.opc.PackageAccess; -import org.apache.poi.poifs.filesystem.DocumentFactoryHelper; -import org.apache.poi.poifs.filesystem.POIFSFileSystem; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; - -public final class ExcelFileOpenUtil { - - private ExcelFileOpenUtil() { - } - - public static OPCPackage openXlsxFile(final File file, final PoijiOptions options) { - if (options.getPassword() != null) { - try (POIFSFileSystem fs = new POIFSFileSystem(file, true); - InputStream decryptedStream = DocumentFactoryHelper.getDecryptedStream(fs, options.getPassword())) { - - return OPCPackage.open(decryptedStream); - } catch (IOException | InvalidFormatException e) { - throw new PoijiException("Problem occurred while reading data", e); - } - } else { - try { - return OPCPackage.open(file, PackageAccess.READ); - } catch (InvalidFormatException e) { - throw new PoijiException("Problem occurred while reading data", e); - } - } - } - - public static OPCPackage openXlsxFile(final InputStream inputStream, final PoijiOptions options) { - if (options.getPassword() != null) { - try (POIFSFileSystem fs = new POIFSFileSystem(inputStream); - InputStream decryptedStream = DocumentFactoryHelper.getDecryptedStream(fs, options.getPassword())) { - - return OPCPackage.open(decryptedStream); - } catch (IOException | InvalidFormatException e) { - throw new PoijiException("Problem occurred while reading data", e); - } - } else { - try { - return OPCPackage.open(inputStream); - } catch (InvalidFormatException | IOException e) { - throw new PoijiException("Problem occurred while reading data", e); - } - } - } -} diff --git a/src/test/java/com/poiji/deserialize/CaseInsensitiveTest.java b/src/test/java/com/poiji/deserialize/CaseInsensitiveTest.java index 9b93682..8d64935 100644 --- a/src/test/java/com/poiji/deserialize/CaseInsensitiveTest.java +++ b/src/test/java/com/poiji/deserialize/CaseInsensitiveTest.java @@ -1,17 +1,15 @@ package com.poiji.deserialize; import com.poiji.bind.Poiji; -import com.poiji.deserialize.model.byid.OrgWithUnknownCells; import com.poiji.deserialize.model.byname.OrgWithUnknownCellsByName; import com.poiji.option.PoijiOptions; -import java.io.File; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; 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.is; import static org.hamcrest.CoreMatchers.notNullValue; diff --git a/src/test/java/com/poiji/deserialize/ReadExcelBySheetNameTest.java b/src/test/java/com/poiji/deserialize/ReadExcelBySheetNameTest.java index 9ddf384..db4956b 100644 --- a/src/test/java/com/poiji/deserialize/ReadExcelBySheetNameTest.java +++ b/src/test/java/com/poiji/deserialize/ReadExcelBySheetNameTest.java @@ -1,7 +1,11 @@ package com.poiji.deserialize; -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; +import com.poiji.bind.Poiji; +import com.poiji.deserialize.model.byid.Calculation; +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.time.LocalDate; @@ -11,13 +15,8 @@ import java.util.Arrays; import java.util.List; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - -import com.poiji.bind.Poiji; -import com.poiji.deserialize.model.byid.Calculation; -import com.poiji.option.PoijiOptions; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; @RunWith(Parameterized.class) public class ReadExcelBySheetNameTest { diff --git a/src/test/java/com/poiji/util/DecimalSeparatorParseTest.java b/src/test/java/com/poiji/util/DecimalSeparatorParseTest.java index 1b6d69f..1197f2c 100644 --- a/src/test/java/com/poiji/util/DecimalSeparatorParseTest.java +++ b/src/test/java/com/poiji/util/DecimalSeparatorParseTest.java @@ -1,17 +1,16 @@ package com.poiji.util; -import java.io.InputStream; -import java.util.List; -import java.util.Locale; - -import org.junit.After; -import org.junit.Test; - import com.poiji.annotation.ExcelCellName; import com.poiji.bind.Poiji; import com.poiji.exception.PoijiExcelType; import com.poiji.option.PoijiOptions; +import org.junit.After; +import org.junit.Test; + +import java.io.InputStream; +import java.util.List; +import java.util.Locale; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; diff --git a/src/test/java/com/poiji/util/DefaultCastingTest.java b/src/test/java/com/poiji/util/DefaultCastingTest.java index 9b30ca3..2f49124 100644 --- a/src/test/java/com/poiji/util/DefaultCastingTest.java +++ b/src/test/java/com/poiji/util/DefaultCastingTest.java @@ -1,5 +1,12 @@ package com.poiji.util; +import com.poiji.config.DefaultCasting; +import com.poiji.option.PoijiOptions; +import com.poiji.option.PoijiOptions.PoijiOptionsBuilder; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + import java.math.BigDecimal; import java.text.SimpleDateFormat; import java.time.LocalDate; @@ -7,14 +14,6 @@ import java.util.Date; import java.util.Locale; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import com.poiji.config.DefaultCasting; -import com.poiji.option.PoijiOptions; -import com.poiji.option.PoijiOptions.PoijiOptionsBuilder; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; diff --git a/src/test/java/com/poiji/util/ReflectUtilTest.java b/src/test/java/com/poiji/util/ReflectUtilTest.java index 4a79c4b..c0e6014 100644 --- a/src/test/java/com/poiji/util/ReflectUtilTest.java +++ b/src/test/java/com/poiji/util/ReflectUtilTest.java @@ -1,15 +1,14 @@ package com.poiji.util; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assume.assumeTrue; - -import java.lang.reflect.ReflectPermission; - +import com.poiji.exception.PoijiExcelType; +import com.poiji.exception.PoijiInstantiationException; import org.junit.BeforeClass; import org.junit.Test; -import com.poiji.exception.PoijiExcelType; -import com.poiji.exception.PoijiInstantiationException; +import java.lang.reflect.ReflectPermission; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assume.assumeTrue; public class ReflectUtilTest { From fd7693d137a1eb95ac8a8f19f8b80f3880fe529f Mon Sep 17 00:00:00 2001 From: Hakan Date: Mon, 25 May 2020 14:31:28 +0300 Subject: [PATCH 8/8] refactor code to increase coverage Signed-off-by: Hakan --- .../java/com/poiji/bind/mapping/PoijiNumberFormat.java | 4 ---- .../com/poiji/bind/mapping/PoijiPropertyHelper.java | 5 +---- .../java/com/poiji/bind/mapping/XSSFUnmarshaller.java | 5 +---- .../com/poiji/exception/LimitCrossedException.java | 9 --------- src/main/java/com/poiji/parser/BigDecimalParser.java | 10 +++------- 5 files changed, 5 insertions(+), 28 deletions(-) delete mode 100644 src/main/java/com/poiji/exception/LimitCrossedException.java diff --git a/src/main/java/com/poiji/bind/mapping/PoijiNumberFormat.java b/src/main/java/com/poiji/bind/mapping/PoijiNumberFormat.java index e990d68..510cca9 100644 --- a/src/main/java/com/poiji/bind/mapping/PoijiNumberFormat.java +++ b/src/main/java/com/poiji/bind/mapping/PoijiNumberFormat.java @@ -23,10 +23,6 @@ public String getNumberFormatAt(short fmtId) { return numberFormats.get(fmtId); } - public int size() { - return numberFormats.size(); - } - void overrideExcelNumberFormats(final StylesTable styles) { for (Short fmtId : numberFormats.keySet()) { String format = numberFormats.get(fmtId); diff --git a/src/main/java/com/poiji/bind/mapping/PoijiPropertyHelper.java b/src/main/java/com/poiji/bind/mapping/PoijiPropertyHelper.java index ca4a979..4706f0f 100644 --- a/src/main/java/com/poiji/bind/mapping/PoijiPropertyHelper.java +++ b/src/main/java/com/poiji/bind/mapping/PoijiPropertyHelper.java @@ -8,10 +8,7 @@ /** * Created by hakan on 24.05.2020 */ -public class PoijiPropertyHelper { - - private PoijiPropertyHelper() { - } +public final class PoijiPropertyHelper { public static HSSFPropertyFile createPoijiPropertyFile(File file, PoijiOptions options) { return new HSSFPropertyFile(file, options); diff --git a/src/main/java/com/poiji/bind/mapping/XSSFUnmarshaller.java b/src/main/java/com/poiji/bind/mapping/XSSFUnmarshaller.java index 2801aaf..b856130 100644 --- a/src/main/java/com/poiji/bind/mapping/XSSFUnmarshaller.java +++ b/src/main/java/com/poiji/bind/mapping/XSSFUnmarshaller.java @@ -1,7 +1,6 @@ package com.poiji.bind.mapping; import com.poiji.bind.Unmarshaller; -import com.poiji.exception.LimitCrossedException; import com.poiji.exception.PoijiException; import com.poiji.option.PoijiOptions; import org.apache.poi.openxml4j.exceptions.OpenXML4JException; @@ -116,10 +115,8 @@ private void processSheet(StylesTable styles, options); reader.setContentHandler(contentHandler); reader.parse(sheetSource); - } catch (LimitCrossedException e) { - IOUtils.closeQuietly(sheetInputStream); - // swallowing the exception for good :) } catch (SAXException | IOException e) { + IOUtils.closeQuietly(sheetInputStream); throw new PoijiException("Problem occurred while reading data", e); } } diff --git a/src/main/java/com/poiji/exception/LimitCrossedException.java b/src/main/java/com/poiji/exception/LimitCrossedException.java deleted file mode 100644 index 45041c1..0000000 --- a/src/main/java/com/poiji/exception/LimitCrossedException.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.poiji.exception; - -public class LimitCrossedException extends PoijiException { - - public LimitCrossedException(String message) { - super(message); - } - -} diff --git a/src/main/java/com/poiji/parser/BigDecimalParser.java b/src/main/java/com/poiji/parser/BigDecimalParser.java index ea59cce..7c57dcd 100644 --- a/src/main/java/com/poiji/parser/BigDecimalParser.java +++ b/src/main/java/com/poiji/parser/BigDecimalParser.java @@ -14,13 +14,9 @@ public class BigDecimalParser implements Parser { private DecimalFormat getDecimalFormatInstance() { NumberFormat numberFormat = NumberFormat.getInstance(); - if (numberFormat instanceof DecimalFormat) { - DecimalFormat decimalFormat = (DecimalFormat) numberFormat; - decimalFormat.setParseBigDecimal(true); - return decimalFormat; - } else { - throw new IllegalStateException(numberFormat.getClass().getName()); - } + DecimalFormat decimalFormat = (DecimalFormat) numberFormat; + decimalFormat.setParseBigDecimal(true); + return decimalFormat; } @Override