diff --git a/fesod-sheet/src/main/java/org/apache/fesod/sheet/util/ClassUtils.java b/fesod-sheet/src/main/java/org/apache/fesod/sheet/util/ClassUtils.java index 3116e9bf9..897553bc0 100644 --- a/fesod-sheet/src/main/java/org/apache/fesod/sheet/util/ClassUtils.java +++ b/fesod-sheet/src/main/java/org/apache/fesod/sheet/util/ClassUtils.java @@ -343,7 +343,7 @@ private static FieldCache doDeclaredFields(Class clazz, ConfigurationHolder c FieldWrapper field = entry.getValue(); // The current field needs to be ignored - if (writeHolder.ignore(field.getFieldName(), entry.getKey())) { + if (writeHolder.ignore(field.getField().getName(), entry.getKey())) { ignoreSet.add(field.getFieldName()); // indexFieldMap is keyed by the field's explicit @ExcelProperty(index), which for // explicit-index fields equals the sortedFieldMap position (entry.getKey()); remove @@ -384,26 +384,31 @@ private static void resortField(WriteHolder writeHolder, FieldCache fieldCache) Collection includeColumnFieldNames = writeHolder.includeColumnFieldNames(); if (!CollectionUtils.isEmpty(includeColumnFieldNames)) { - // Field sorted map + // Field sorted map, keyed by the Java field name, which is what the user configures Map filedIndexMap = MapUtils.newHashMap(); - int fieldIndex = 0; - for (String includeColumnFieldName : includeColumnFieldNames) { - filedIndexMap.put(includeColumnFieldName, fieldIndex++); - } + fieldCache + .getSortedFieldMap() + .forEach( + (index, field) -> filedIndexMap.put(field.getField().getName(), index)); // rebuild sortedFieldMap Map tempSortedFieldMap = MapUtils.newHashMap(); - fieldCache.getSortedFieldMap().forEach((index, field) -> { - Integer tempFieldIndex = filedIndexMap.get(field.getFieldName()); - if (tempFieldIndex != null) { - tempSortedFieldMap.put(tempFieldIndex, field); + int fieldIndex = 0; + for (String includeColumnFieldName : includeColumnFieldNames) { + Integer index = filedIndexMap.get(includeColumnFieldName); + // The name is not a field of the class, the column is ignored, it must not hold a position + if (index == null) { + continue; + } + FieldWrapper field = fieldCache.getSortedFieldMap().get(index); + tempSortedFieldMap.put(fieldIndex, field); - // The user has redefined the ordering and the ordering of annotations needs to be invalidated - if (!tempFieldIndex.equals(index)) { - indexFieldMap.remove(index); - } + // The user has redefined the ordering and the ordering of annotations needs to be invalidated + if (fieldIndex != index) { + indexFieldMap.remove(index); } - }); + fieldIndex++; + } fieldCache.setSortedFieldMap(tempSortedFieldMap); return; } diff --git a/fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/CamelCaseFieldData.java b/fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/CamelCaseFieldData.java new file mode 100644 index 000000000..3ba83e7d7 --- /dev/null +++ b/fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/CamelCaseFieldData.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +/* + * This file is part of the Apache Fesod (Incubating) project, which was derived from Alibaba EasyExcel. + * + * Copyright (C) 2018-2024 Alibaba Group Holding Ltd. + */ + +package org.apache.fesod.sheet.converter; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import org.apache.fesod.sheet.annotation.ExcelProperty; + +/** + * Data model whose field names start with a lower case letter followed by an upper case letter. + * + *

Field names such as {@code xRealIp} are common in generated DTOs, for example when a column + * named {@code X_REAL_IP} is converted to camel case. The name kept by the cglib bean map of such a + * field differs from the Java field name, which is why these names are covered explicitly. + */ +@Getter +@Setter +@EqualsAndHashCode +public class CamelCaseFieldData { + @ExcelProperty("name") + private String name; + + @ExcelProperty("xRealIp") + private String xRealIp; + + @ExcelProperty("pName") + private String pName; + + @ExcelProperty("attackType") + private String attackType; +} diff --git a/fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/CamelCaseFieldDataTest.java b/fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/CamelCaseFieldDataTest.java new file mode 100644 index 000000000..f0aaaa95e --- /dev/null +++ b/fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/CamelCaseFieldDataTest.java @@ -0,0 +1,176 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +/* + * This file is part of the Apache Fesod (Incubating) project, which was derived from Alibaba EasyExcel. + * + * Copyright (C) 2018-2024 Alibaba Group Holding Ltd. + */ + +package org.apache.fesod.sheet.converter; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import org.apache.fesod.sheet.FesodSheet; +import org.apache.fesod.sheet.testkit.Tags; +import org.apache.fesod.sheet.testkit.base.AbstractExcelTest; +import org.apache.fesod.sheet.testkit.enums.ExcelFormat; +import org.apache.fesod.sheet.testkit.params.ExcelFormatSource; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.params.ParameterizedTest; + +/** + * Tests column include/exclude for field names that start with a lower case letter followed by an + * upper case letter, for example {@code xRealIp} or {@code pName}. + * + *

The name kept by the internal cglib bean map of such a field is not the Java field name, so + * filtering by field name must not be based on that name. + */ +@Tag(Tags.ROUND_TRIP) +class CamelCaseFieldDataTest extends AbstractExcelTest { + + @ParameterizedTest + @ExcelFormatSource + void writeAllCamelCaseFields(ExcelFormat format) throws Exception { + File file = createTempFile(format); + FesodSheet.write(file, CamelCaseFieldData.class).sheet().doWrite(data()); + List> dataMap = FesodSheet.read(file).sheet().doReadSync(); + Assertions.assertEquals(1, dataMap.size()); + Map record = dataMap.get(0); + Assertions.assertEquals(4, record.size()); + Assertions.assertEquals("name1", record.get(0)); + Assertions.assertEquals("xRealIp1", record.get(1)); + Assertions.assertEquals("pName1", record.get(2)); + Assertions.assertEquals("attackType1", record.get(3)); + } + + @ParameterizedTest + @ExcelFormatSource + void writeHeadOfCamelCaseFields(ExcelFormat format) throws Exception { + File file = createTempFile(format); + FesodSheet.write(file, CamelCaseFieldData.class).sheet().doWrite(data()); + List> dataMap = + FesodSheet.read(file).headRowNumber(0).sheet().doReadSync(); + Assertions.assertEquals(2, dataMap.size()); + Map record = dataMap.get(0); + Assertions.assertEquals(4, record.size()); + Assertions.assertEquals("name", record.get(0)); + Assertions.assertEquals("xRealIp", record.get(1)); + Assertions.assertEquals("pName", record.get(2)); + Assertions.assertEquals("attackType", record.get(3)); + } + + @ParameterizedTest + @ExcelFormatSource + void includeColumnFieldNames(ExcelFormat format) throws Exception { + File file = createTempFile(format); + FesodSheet.write(file, CamelCaseFieldData.class) + .includeColumnFieldNames(Arrays.asList("name", "xRealIp", "pName", "attackType")) + .sheet() + .doWrite(data()); + List> dataMap = FesodSheet.read(file).sheet().doReadSync(); + Assertions.assertEquals(1, dataMap.size()); + Map record = dataMap.get(0); + Assertions.assertEquals(4, record.size()); + Assertions.assertEquals("name1", record.get(0)); + Assertions.assertEquals("xRealIp1", record.get(1)); + Assertions.assertEquals("pName1", record.get(2)); + Assertions.assertEquals("attackType1", record.get(3)); + } + + @ParameterizedTest + @ExcelFormatSource + void includePartOfCamelCaseFieldNames(ExcelFormat format) throws Exception { + File file = createTempFile(format); + FesodSheet.write(file, CamelCaseFieldData.class) + .includeColumnFieldNames(Arrays.asList("name", "xRealIp")) + .sheet() + .doWrite(data()); + List> dataMap = FesodSheet.read(file).sheet().doReadSync(); + Assertions.assertEquals(1, dataMap.size()); + Map record = dataMap.get(0); + Assertions.assertEquals(2, record.size()); + Assertions.assertEquals("name1", record.get(0)); + Assertions.assertEquals("xRealIp1", record.get(1)); + } + + @ParameterizedTest + @ExcelFormatSource + void excludeCamelCaseFieldNames(ExcelFormat format) throws Exception { + File file = createTempFile(format); + FesodSheet.write(file, CamelCaseFieldData.class) + .excludeColumnFieldNames(Arrays.asList("xRealIp", "attackType")) + .sheet() + .doWrite(data()); + List> dataMap = FesodSheet.read(file).sheet().doReadSync(); + Assertions.assertEquals(1, dataMap.size()); + Map record = dataMap.get(0); + Assertions.assertEquals(2, record.size()); + Assertions.assertEquals("name1", record.get(0)); + Assertions.assertEquals("pName1", record.get(1)); + } + + @ParameterizedTest + @ExcelFormatSource + void includeUnknownColumnFieldName(ExcelFormat format) throws Exception { + File file = createTempFile(format); + FesodSheet.write(file, CamelCaseFieldData.class) + .includeColumnFieldNames(Arrays.asList("name", "notExist", "pName")) + .sheet() + .doWrite(data()); + List> dataMap = FesodSheet.read(file).sheet().doReadSync(); + Assertions.assertEquals(1, dataMap.size()); + Map record = dataMap.get(0); + Assertions.assertEquals(2, record.size()); + Assertions.assertEquals("name1", record.get(0)); + Assertions.assertEquals("pName1", record.get(1)); + } + + @ParameterizedTest + @ExcelFormatSource + void includeUnknownColumnFieldNameOrderByIncludeColumn(ExcelFormat format) throws Exception { + File file = createTempFile(format); + FesodSheet.write(file, CamelCaseFieldData.class) + .includeColumnFieldNames(Arrays.asList("name", "notExist", "pName")) + .orderByIncludeColumn(true) + .sheet() + .doWrite(data()); + List> dataMap = FesodSheet.read(file).sheet().doReadSync(); + Assertions.assertEquals(1, dataMap.size()); + Map record = dataMap.get(0); + Assertions.assertEquals(2, record.size()); + Assertions.assertEquals("name1", record.get(0)); + Assertions.assertEquals("pName1", record.get(1)); + } + + private List data() { + List list = new ArrayList(); + CamelCaseFieldData data = new CamelCaseFieldData(); + data.setName("name1"); + data.setXRealIp("xRealIp1"); + data.setPName("pName1"); + data.setAttackType("attackType1"); + list.add(data); + return list; + } +}