From cd5e1f6a9536a410ed05149a7278e4b7246e28b5 Mon Sep 17 00:00:00 2001 From: Ramesh Babu Prudhvi Date: Wed, 29 Jun 2022 01:35:16 +0530 Subject: [PATCH] Excel mapper (#152) * [Excel Mapper] Added support to parse Excel sheet to a POJO class * [Excel Mapper] Minor code changes * [Excel Mapper] Minor code changes * [Excel Mapper] Read data stream * [Excel Mapper] Early exist for non xlsx files * [Snapshot] Using ReflectionHelper * [Excel] Sonar checks --- .../commons/helper/ReflectionHelper.java | 68 ++++++++++++ .../databind/annotation/DataFile.java | 2 + .../io/github/selcukes/excel/ExcelMapper.java | 47 ++++++++ .../selcukes/excel/annotation/Column.java | 35 ++++++ .../excel/converters/BooleanConverter.java | 33 ++++++ .../selcukes/excel/converters/Converter.java | 29 +++++ .../excel/converters/DefaultConverter.java | 33 ++++++ .../excel/converters/DoubleConverter.java | 37 ++++++ .../excel/converters/IntegerConverter.java | 33 ++++++ .../excel/converters/LocalDateConverter.java | 37 ++++++ .../converters/LocalDateTimeConverter.java | 37 ++++++ .../excel/converters/StringConverter.java | 24 ++++ .../selcukes/excel/parser/ExcelCell.java | 102 +++++++++++++++++ .../selcukes/excel/parser/ExcelData.java | 105 ++++++++++++++++++ .../selcukes/excel/ExcelMapperTest.java | 60 ++++++++++ .../selcukes/snapshot/PageSnapshot.java | 16 +-- 16 files changed, 690 insertions(+), 8 deletions(-) create mode 100644 selcukes-commons/src/main/java/io/github/selcukes/commons/helper/ReflectionHelper.java create mode 100644 selcukes-excel-runner/src/main/java/io/github/selcukes/excel/ExcelMapper.java create mode 100644 selcukes-excel-runner/src/main/java/io/github/selcukes/excel/annotation/Column.java create mode 100644 selcukes-excel-runner/src/main/java/io/github/selcukes/excel/converters/BooleanConverter.java create mode 100644 selcukes-excel-runner/src/main/java/io/github/selcukes/excel/converters/Converter.java create mode 100644 selcukes-excel-runner/src/main/java/io/github/selcukes/excel/converters/DefaultConverter.java create mode 100644 selcukes-excel-runner/src/main/java/io/github/selcukes/excel/converters/DoubleConverter.java create mode 100644 selcukes-excel-runner/src/main/java/io/github/selcukes/excel/converters/IntegerConverter.java create mode 100644 selcukes-excel-runner/src/main/java/io/github/selcukes/excel/converters/LocalDateConverter.java create mode 100644 selcukes-excel-runner/src/main/java/io/github/selcukes/excel/converters/LocalDateTimeConverter.java create mode 100644 selcukes-excel-runner/src/main/java/io/github/selcukes/excel/converters/StringConverter.java create mode 100644 selcukes-excel-runner/src/main/java/io/github/selcukes/excel/parser/ExcelCell.java create mode 100644 selcukes-excel-runner/src/main/java/io/github/selcukes/excel/parser/ExcelData.java create mode 100644 selcukes-excel-runner/src/test/java/io/github/selcukes/excel/ExcelMapperTest.java diff --git a/selcukes-commons/src/main/java/io/github/selcukes/commons/helper/ReflectionHelper.java b/selcukes-commons/src/main/java/io/github/selcukes/commons/helper/ReflectionHelper.java new file mode 100644 index 000000000..0b1e8e39b --- /dev/null +++ b/selcukes-commons/src/main/java/io/github/selcukes/commons/helper/ReflectionHelper.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) Ramesh Babu Prudhvi. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.github.selcukes.commons.helper; + +import lombok.experimental.UtilityClass; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +@UtilityClass +public class ReflectionHelper { + @SuppressWarnings("all") + public static T newInstance(Class clazz) { + try { + Constructor constructor = clazz.getDeclaredConstructor(); + if ((!Modifier.isPublic(constructor.getModifiers()) || + !Modifier.isPublic(constructor.getDeclaringClass().getModifiers())) && + !constructor.isAccessible()) { + constructor.setAccessible(true); + } + return constructor.newInstance(); + } catch (Exception e) { + throw new IllegalArgumentException(e); + } + + } + + @SuppressWarnings("squid:S3011") + public static void setField(Object object, String fieldName, Object value) { + try { + Class clazz = object == null ? Object.class : object.getClass(); + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + field.set(object, value); + } catch (Exception e) { + throw new IllegalArgumentException(e); + } + + } + + @SuppressWarnings("squid:S3011") + public static Method getDeclaredMethod(Class clazz, String name, Class... param) { + try { + Method method = clazz.getDeclaredMethod(name, param); + method.setAccessible(true); + return method; + } catch (Exception e) { + throw new IllegalArgumentException(e); + } + } +} + diff --git a/selcukes-databind/src/main/java/io/github/selcukes/databind/annotation/DataFile.java b/selcukes-databind/src/main/java/io/github/selcukes/databind/annotation/DataFile.java index 19dc1c3f7..50668c0e6 100644 --- a/selcukes-databind/src/main/java/io/github/selcukes/databind/annotation/DataFile.java +++ b/selcukes-databind/src/main/java/io/github/selcukes/databind/annotation/DataFile.java @@ -35,5 +35,7 @@ String rootFolder() default ""; boolean streamLoader() default false; + + String sheetName() default ""; } diff --git a/selcukes-excel-runner/src/main/java/io/github/selcukes/excel/ExcelMapper.java b/selcukes-excel-runner/src/main/java/io/github/selcukes/excel/ExcelMapper.java new file mode 100644 index 000000000..1545f5916 --- /dev/null +++ b/selcukes-excel-runner/src/main/java/io/github/selcukes/excel/ExcelMapper.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) Ramesh Babu Prudhvi. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.github.selcukes.excel; + +import io.github.selcukes.databind.exception.DataMapperException; +import io.github.selcukes.databind.utils.DataFileHelper; +import io.github.selcukes.excel.parser.ExcelData; +import lombok.experimental.UtilityClass; + +import java.util.stream.Stream; + +@UtilityClass +public class ExcelMapper { + /** + * Parses the Excel file to an Entity Class. + * + * @param the Class type. + * @param entityClass the entity class + * @return the Stream of Entity class objects + */ + public static Stream parse(final Class entityClass) { + final DataFileHelper dataFile = DataFileHelper.getInstance(entityClass); + final String fileName = dataFile.getFileName(); + int extensionIndex = fileName.lastIndexOf('.'); + final String extension = fileName.substring(extensionIndex + 1); + if (!extension.equalsIgnoreCase("xlsx")) { + throw new DataMapperException(String.format("File [%s] not found.", + fileName.substring(0, extensionIndex) + ".xlsx")); + } + ExcelData excelMapper = new ExcelData<>(entityClass); + return excelMapper.parse(dataFile.getPath()); + } +} diff --git a/selcukes-excel-runner/src/main/java/io/github/selcukes/excel/annotation/Column.java b/selcukes-excel-runner/src/main/java/io/github/selcukes/excel/annotation/Column.java new file mode 100644 index 000000000..a9e0c2442 --- /dev/null +++ b/selcukes-excel-runner/src/main/java/io/github/selcukes/excel/annotation/Column.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) Ramesh Babu Prudhvi. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.github.selcukes.excel.annotation; + + +import io.github.selcukes.excel.converters.StringConverter; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Column { + String name(); + + String format() default ""; + + Class converter() default StringConverter.class; +} diff --git a/selcukes-excel-runner/src/main/java/io/github/selcukes/excel/converters/BooleanConverter.java b/selcukes-excel-runner/src/main/java/io/github/selcukes/excel/converters/BooleanConverter.java new file mode 100644 index 000000000..5afbb5fd6 --- /dev/null +++ b/selcukes-excel-runner/src/main/java/io/github/selcukes/excel/converters/BooleanConverter.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) Ramesh Babu Prudhvi. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.github.selcukes.excel.converters; + +import java.lang.reflect.Type; + +import static java.lang.Boolean.parseBoolean; + +public class BooleanConverter extends DefaultConverter { + @Override + public Boolean convert(final String value) { + return parseBoolean(value); + } + + @Override + public Type getType() { + return Boolean.TYPE; + } +} diff --git a/selcukes-excel-runner/src/main/java/io/github/selcukes/excel/converters/Converter.java b/selcukes-excel-runner/src/main/java/io/github/selcukes/excel/converters/Converter.java new file mode 100644 index 000000000..78cf3c11b --- /dev/null +++ b/selcukes-excel-runner/src/main/java/io/github/selcukes/excel/converters/Converter.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) Ramesh Babu Prudhvi. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.github.selcukes.excel.converters; + +import java.lang.reflect.Type; + +public interface Converter { + T convert(String value); + + Type getType(); + + default T convert(final String value, final String format) { + return convert(value); + } +} diff --git a/selcukes-excel-runner/src/main/java/io/github/selcukes/excel/converters/DefaultConverter.java b/selcukes-excel-runner/src/main/java/io/github/selcukes/excel/converters/DefaultConverter.java new file mode 100644 index 000000000..099ac7226 --- /dev/null +++ b/selcukes-excel-runner/src/main/java/io/github/selcukes/excel/converters/DefaultConverter.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) Ramesh Babu Prudhvi. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.github.selcukes.excel.converters; + +import java.lang.reflect.Type; + +public abstract class DefaultConverter implements Converter { + private final Type type; + + @SafeVarargs + protected DefaultConverter(final T... values) { + this.type = values.getClass().getComponentType(); + } + + @Override + public Type getType() { + return type; + } +} diff --git a/selcukes-excel-runner/src/main/java/io/github/selcukes/excel/converters/DoubleConverter.java b/selcukes-excel-runner/src/main/java/io/github/selcukes/excel/converters/DoubleConverter.java new file mode 100644 index 000000000..ab0668bd6 --- /dev/null +++ b/selcukes-excel-runner/src/main/java/io/github/selcukes/excel/converters/DoubleConverter.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) Ramesh Babu Prudhvi. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.github.selcukes.excel.converters; + + +import lombok.SneakyThrows; + +import java.lang.reflect.Type; +import java.text.NumberFormat; +import java.util.Locale; + +public class DoubleConverter extends DefaultConverter { + @SneakyThrows + @Override + public Double convert(final String value) { + return NumberFormat.getInstance(Locale.getDefault()).parse(value).doubleValue(); + } + + @Override + public Type getType() { + return Double.TYPE; + } +} diff --git a/selcukes-excel-runner/src/main/java/io/github/selcukes/excel/converters/IntegerConverter.java b/selcukes-excel-runner/src/main/java/io/github/selcukes/excel/converters/IntegerConverter.java new file mode 100644 index 000000000..2da5e4e77 --- /dev/null +++ b/selcukes-excel-runner/src/main/java/io/github/selcukes/excel/converters/IntegerConverter.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) Ramesh Babu Prudhvi. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.github.selcukes.excel.converters; + +import java.lang.reflect.Type; + +import static java.lang.Integer.parseInt; + +public class IntegerConverter extends DefaultConverter { + @Override + public Integer convert(final String value) { + return parseInt(value); + } + + @Override + public Type getType() { + return Integer.TYPE; + } +} diff --git a/selcukes-excel-runner/src/main/java/io/github/selcukes/excel/converters/LocalDateConverter.java b/selcukes-excel-runner/src/main/java/io/github/selcukes/excel/converters/LocalDateConverter.java new file mode 100644 index 000000000..d4bc253db --- /dev/null +++ b/selcukes-excel-runner/src/main/java/io/github/selcukes/excel/converters/LocalDateConverter.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) Ramesh Babu Prudhvi. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.github.selcukes.excel.converters; + +import java.time.LocalDate; + +import static java.time.LocalDate.parse; +import static java.time.format.DateTimeFormatter.ofPattern; +import static java.util.Optional.ofNullable; + +public class LocalDateConverter extends DefaultConverter { + private static final String DEFAULT_FORMAT = "yyyy-MM-dd"; + + @Override + public LocalDate convert(final String value) { + return convert(value, DEFAULT_FORMAT); + } + + @Override + public LocalDate convert(final String value, final String format) { + return parse(value, ofPattern(ofNullable(format).filter(f -> !f.isEmpty()).orElse(DEFAULT_FORMAT))); + } +} diff --git a/selcukes-excel-runner/src/main/java/io/github/selcukes/excel/converters/LocalDateTimeConverter.java b/selcukes-excel-runner/src/main/java/io/github/selcukes/excel/converters/LocalDateTimeConverter.java new file mode 100644 index 000000000..edd38bd7a --- /dev/null +++ b/selcukes-excel-runner/src/main/java/io/github/selcukes/excel/converters/LocalDateTimeConverter.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) Ramesh Babu Prudhvi. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.github.selcukes.excel.converters; + +import java.time.LocalDateTime; + +import static java.time.LocalDateTime.parse; +import static java.time.format.DateTimeFormatter.ofPattern; +import static java.util.Optional.ofNullable; + +public class LocalDateTimeConverter extends DefaultConverter { + public static final String DEFAULT_FORMAT = "yyyy-MM-dd HH:mm:ss"; + + @Override + public LocalDateTime convert(final String value) { + return convert(value, DEFAULT_FORMAT); + } + + @Override + public LocalDateTime convert(final String value, final String format) { + return parse(value, ofPattern(ofNullable(format).filter(f -> !f.isEmpty()).orElse(DEFAULT_FORMAT))); + } +} diff --git a/selcukes-excel-runner/src/main/java/io/github/selcukes/excel/converters/StringConverter.java b/selcukes-excel-runner/src/main/java/io/github/selcukes/excel/converters/StringConverter.java new file mode 100644 index 000000000..58ffbb6ed --- /dev/null +++ b/selcukes-excel-runner/src/main/java/io/github/selcukes/excel/converters/StringConverter.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) Ramesh Babu Prudhvi. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.github.selcukes.excel.converters; + +public class StringConverter extends DefaultConverter { + @Override + public String convert(final String value) { + return value; + } +} diff --git a/selcukes-excel-runner/src/main/java/io/github/selcukes/excel/parser/ExcelCell.java b/selcukes-excel-runner/src/main/java/io/github/selcukes/excel/parser/ExcelCell.java new file mode 100644 index 000000000..afc8e5f56 --- /dev/null +++ b/selcukes-excel-runner/src/main/java/io/github/selcukes/excel/parser/ExcelCell.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) Ramesh Babu Prudhvi. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.github.selcukes.excel.parser; + +import io.github.selcukes.excel.annotation.Column; +import io.github.selcukes.excel.converters.Converter; +import org.apache.poi.ss.usermodel.DataFormatter; +import org.apache.poi.ss.usermodel.Row; + +import java.lang.reflect.Field; +import java.lang.reflect.Type; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import static io.github.selcukes.commons.helper.ReflectionHelper.newInstance; +import static io.github.selcukes.commons.helper.ReflectionHelper.setField; +import static java.lang.String.format; +import static java.util.Optional.ofNullable; +import static org.apache.poi.ss.usermodel.Row.MissingCellPolicy.RETURN_BLANK_AS_NULL; + + +class ExcelCell { + private final int index; + private final Field field; + private final Converter converter; + private final DataFormatter formatter; + private final List> defaultIConverters; + + private T convertedValue; + + public ExcelCell( + final Field field, + final Map headers, + final List> defaultIConverters + ) { + this.field = field; + this.defaultIConverters = defaultIConverters; + this.index = getColumn() + .map(Column::name) + .filter(headers::containsKey) + .map(headers::get) + .orElseThrow(() -> new IllegalArgumentException(format("Column %s not found", field.getName()))); + this.converter = findMatchingConverter(); + this.formatter = new DataFormatter(); + } + + public ExcelCell parse(final Row row) { + var cellValue = ofNullable(row.getCell(index, RETURN_BLANK_AS_NULL)) + .map(cell -> formatter.formatCellValue(cell).trim()) + .orElse(""); + this.convertedValue = converter.convert(cellValue, getColumn().map(Column::format).orElse("")); + return this; + } + + public void assignValue(final R instance) { + ofNullable(convertedValue) + .ifPresent(value -> setField(instance, getFieldName(), value)); + } + + private String getFieldName() { + return field.getName(); + } + + private Type getFieldType() { + return field.getType(); + } + + private Optional getColumn() { + return ofNullable(field.getDeclaredAnnotation(Column.class)); + } + + @SuppressWarnings("unchecked") + private Converter findMatchingConverter() { + return getColumn() + .map(Column::converter) + .map(converterClass -> (Converter) newInstance(converterClass)) + .filter(converterInstance -> converterInstance.getType().equals(getFieldType())) + .orElseGet(() -> defaultIConverters.stream() + .filter(converterInstance -> converterInstance.getType().equals(getFieldType())) + .findFirst() + .orElseThrow(() -> new IllegalStateException(format( + "There's no matching converter found for %s field of type %s", getFieldName(), getFieldType())) + ) + ); + } +} + diff --git a/selcukes-excel-runner/src/main/java/io/github/selcukes/excel/parser/ExcelData.java b/selcukes-excel-runner/src/main/java/io/github/selcukes/excel/parser/ExcelData.java new file mode 100644 index 000000000..79e34c2c4 --- /dev/null +++ b/selcukes-excel-runner/src/main/java/io/github/selcukes/excel/parser/ExcelData.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) Ramesh Babu Prudhvi. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.github.selcukes.excel.parser; + + +import io.github.selcukes.commons.helper.CollectionUtils; +import io.github.selcukes.databind.annotation.DataFile; +import io.github.selcukes.databind.exception.DataMapperException; +import io.github.selcukes.excel.converters.*; +import lombok.Getter; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.DataFormatter; +import org.apache.poi.ss.usermodel.WorkbookFactory; + +import java.io.FileInputStream; +import java.nio.file.Path; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static io.github.selcukes.commons.helper.ReflectionHelper.newInstance; +import static java.lang.String.format; +import static java.util.Optional.ofNullable; + + +public class ExcelData { + @Getter + private final Class entityClass; + + private final List> defaultIConverters; + + public ExcelData(final Class entityClass) { + this.entityClass = entityClass; + + this.defaultIConverters = defaultConverters(); + } + + @SuppressWarnings("unchecked") + public static List> defaultConverters() { + return Stream.of( + BooleanConverter.class, + StringConverter.class, + IntegerConverter.class, + DoubleConverter.class, + LocalDateConverter.class, + LocalDateTimeConverter.class + ) + .map(cls -> (Converter) newInstance(cls)) + .collect(Collectors.toList()); + } + + public Stream parse(Path filePath) { + try (var workbook = WorkbookFactory.create(new FileInputStream(filePath.toFile()))) { + var formatter = new DataFormatter(); + var startIndex = 0; + var skip = 1; + + var sheet = ofNullable(entityClass.getDeclaredAnnotation(DataFile.class)) + .map(annotation -> workbook.getSheet(annotation.sheetName())) + .orElse(workbook.getSheetAt(startIndex)); + + var headers = CollectionUtils.toStream(sheet.getRow(startIndex).cellIterator()) + .collect(Collectors.toMap(cell -> formatter.formatCellValue(cell).trim(), Cell::getColumnIndex)); + + var cellMappers = Stream.of(entityClass.getDeclaredFields()) + .map(field -> new ExcelCell<>(field, headers, defaultIConverters)) + .collect(Collectors.toList()); + + return CollectionUtils.toStream(sheet.iterator()) + .skip(skip) + .map(row -> cellMappers.stream().map(cellMapper -> cellMapper.parse(row)).collect(Collectors.toList())) + .map(this::initEntity); + } catch (Exception ex) { + throw new DataMapperException(format("Unable to parse Excel data to %s.", entityClass), ex); + } + } + + public T initEntity(final List> mappers) { + var hasDefaultConstructor = Stream.of(entityClass.getDeclaredConstructors()) + .anyMatch(constructor -> constructor.getParameterCount() == 0); + if (!hasDefaultConstructor) { + throw new IllegalStateException(format("%s must have default constructor.", entityClass.getSimpleName())); + } + + var entity = newInstance(entityClass); + mappers.forEach(mapper -> mapper.assignValue(entity)); + + return entity; + } +} + diff --git a/selcukes-excel-runner/src/test/java/io/github/selcukes/excel/ExcelMapperTest.java b/selcukes-excel-runner/src/test/java/io/github/selcukes/excel/ExcelMapperTest.java new file mode 100644 index 000000000..7fd18eaaf --- /dev/null +++ b/selcukes-excel-runner/src/test/java/io/github/selcukes/excel/ExcelMapperTest.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) Ramesh Babu Prudhvi. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.github.selcukes.excel; + +import io.github.selcukes.databind.annotation.DataFile; +import io.github.selcukes.databind.exception.DataMapperException; +import io.github.selcukes.excel.annotation.Column; +import lombok.Data; +import org.testng.annotations.Test; + +import java.util.stream.Stream; + +public class ExcelMapperTest { + + @Data + @DataFile(fileName = "TestData.xlsx", sheetName = "Smoke") + static class Pojo { + @Column(name = "Screen") + private String screen; + @Column(name = "Feature") + private String feature; + @Column(name = "Test") + private String test; + @Column(name = "Run") + private String run; + } + + @Test + public void excelMapperTest() { + Stream pojoStream = ExcelMapper.parse(Pojo.class); + pojoStream.forEach(System.out::println); + } + + @Test(expectedExceptions = DataMapperException.class) + public void excelMapperNegativeTest() { + ExcelMapper.parse(Selcukes.class); + + } + + @Data + @DataFile(sheetName = "Smoke") + static class Selcukes { + + } + +} diff --git a/selcukes-snapshot/src/main/java/io/github/selcukes/snapshot/PageSnapshot.java b/selcukes-snapshot/src/main/java/io/github/selcukes/snapshot/PageSnapshot.java index 81c88747b..866c05d22 100644 --- a/selcukes-snapshot/src/main/java/io/github/selcukes/snapshot/PageSnapshot.java +++ b/selcukes-snapshot/src/main/java/io/github/selcukes/snapshot/PageSnapshot.java @@ -37,6 +37,8 @@ import java.util.Map; import java.util.concurrent.TimeUnit; +import static io.github.selcukes.commons.helper.ReflectionHelper.getDeclaredMethod; + class PageSnapshot extends DefaultPageSnapshot { private WebDriver driver; @@ -87,26 +89,24 @@ private X getFullScreenshot(OutputType outputType) { return outputType.convertFromBase64Png(base64EncodedPng); } - @SuppressWarnings("squid:S3011") + private void defineCustomCommand(CommandInfo info) { try { unwrapDriver(); - Method defineCommand = HttpCommandExecutor.class.getDeclaredMethod("defineCommand", String.class, CommandInfo.class); - defineCommand.setAccessible(true); + Method defineCommand = getDeclaredMethod(HttpCommandExecutor.class, "defineCommand", String.class, CommandInfo.class); defineCommand.invoke(((RemoteWebDriver) this.driver).getCommandExecutor(), "sendCommand", info); - } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) { + } catch (InvocationTargetException | IllegalAccessException e) { throw new SnapshotException(e); } } - @SuppressWarnings("squid:S3011") + private Object sendCommand(String cmd, Object params) { try { - Method execute = RemoteWebDriver.class.getDeclaredMethod("execute", String.class, Map.class); - execute.setAccessible(true); + Method execute = getDeclaredMethod(RemoteWebDriver.class, "execute", String.class, Map.class); Response res = (Response) execute.invoke(driver, "sendCommand", Map.of("cmd", cmd, "params", params)); return res.getValue(); - } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { + } catch (InvocationTargetException | IllegalAccessException e) { throw new SnapshotException(e); } }