+ * All properties are bound under the prefix {@code fesod}. + * Example (application.yml): + *
+ * fesod: + * global: + * auto-trim: true + * locale: en_US + * reader: + * ignore-empty-row: true + * writer: + * in-memory: true + *+ */ +@ConfigurationProperties(prefix = "fesod") +public class FesodProperties { + + /** Global shared configuration applied to both reading and writing. */ + private Global global = new Global(); + /** Reader specific configuration. */ + private Reader reader = new Reader(); + /** Writer specific configuration. */ + private Writer writer = new Writer(); + + public Global getGlobal() { + return global; + } + + public void setGlobal(Global global) { + this.global = global; + } + + public Reader getReader() { + return reader; + } + + public void setReader(Reader reader) { + this.reader = reader; + } + + public Writer getWriter() { + return writer; + } + + public void setWriter(Writer writer) { + this.writer = writer; + } + + /** + * Global configuration options that affect both reading and writing operations. + */ + public static class Global { + /** + * Whether to automatically trim leading and trailing whitespace from cell string values. + */ + private Boolean autoTrim; + /** + * Whether to automatically remove invisible / special control characters (strip) from cell values. + */ + private Boolean autoStrip; + /** + * Whether to interpret dates using the 1904 date windowing (common in older Mac Excel files). + */ + private Boolean use1904Windowing; + /** + * Whether numeric values which are large or small should be written using scientific notation. + */ + private Boolean useScientificFormat; + /** + * The default locale to use for formatting and parsing (e.g. number, date patterns). If not set, JVM default is used. + */ + private Locale locale; + /** + * The cache used when parsing fields such as head. + */ + private CacheLocationEnum filedCacheLocation; + + public Boolean getAutoTrim() { + return autoTrim; + } + + public void setAutoTrim(Boolean autoTrim) { + this.autoTrim = autoTrim; + } + + public Boolean getAutoStrip() { + return autoStrip; + } + + public void setAutoStrip(Boolean autoStrip) { + this.autoStrip = autoStrip; + } + + public Boolean getUse1904Windowing() { + return use1904Windowing; + } + + public void setUse1904Windowing(Boolean use1904Windowing) { + this.use1904Windowing = use1904Windowing; + } + + public Boolean getUseScientificFormat() { + return useScientificFormat; + } + + public void setUseScientificFormat(Boolean useScientificFormat) { + this.useScientificFormat = useScientificFormat; + } + + public Locale getLocale() { + return locale; + } + + public void setLocale(Locale locale) { + this.locale = locale; + } + + public CacheLocationEnum getFiledCacheLocation() { + return filedCacheLocation; + } + + public void setFiledCacheLocation(CacheLocationEnum filedCacheLocation) { + this.filedCacheLocation = filedCacheLocation; + } + } + + /** + * Reader specific configuration options. + */ + public static class Reader { + /** + * Whether empty rows (all cells empty) should be ignored during reading. + */ + private Boolean ignoreEmptyRow; + /** + * Whether the underlying input stream should be closed automatically after reading completes. + */ + private Boolean autoCloseStream; + /** + * Whether reading must be performed strictly from an InputStream (instead of file path / other sources). + */ + private Boolean mandatoryUseInputStream; + + public Boolean getIgnoreEmptyRow() { + return ignoreEmptyRow; + } + + public void setIgnoreEmptyRow(Boolean ignoreEmptyRow) { + this.ignoreEmptyRow = ignoreEmptyRow; + } + + public Boolean getAutoCloseStream() { + return autoCloseStream; + } + + public void setAutoCloseStream(Boolean autoCloseStream) { + this.autoCloseStream = autoCloseStream; + } + + public Boolean getMandatoryUseInputStream() { + return mandatoryUseInputStream; + } + + public void setMandatoryUseInputStream(Boolean mandatoryUseInputStream) { + this.mandatoryUseInputStream = mandatoryUseInputStream; + } + } + + /** + * Writer specific configuration options. + */ + public static class Writer { + /** + * Whether the underlying output stream should be closed automatically after writing completes. + */ + private Boolean autoCloseStream; + /** + * Whether to write a UTF-8 BOM (Byte Order Mark) for CSV/text outputs where applicable. + */ + private Boolean withBom; + /** + * Whether writing operations should be performed entirely in memory (might increase memory usage for large files). + */ + private Boolean inMemory; + /** + * Whether the Excel file should still be written/flushed when an exception occurs during writing. + */ + private Boolean writeExcelOnException; + + public Boolean getAutoCloseStream() { + return autoCloseStream; + } + + public void setAutoCloseStream(Boolean autoCloseStream) { + this.autoCloseStream = autoCloseStream; + } + + public Boolean getWithBom() { + return withBom; + } + + public void setWithBom(Boolean withBom) { + this.withBom = withBom; + } + + public Boolean getInMemory() { + return inMemory; + } + + public void setInMemory(Boolean inMemory) { + this.inMemory = inMemory; + } + + public Boolean getWriteExcelOnException() { + return writeExcelOnException; + } + + public void setWriteExcelOnException(Boolean writeExcelOnException) { + this.writeExcelOnException = writeExcelOnException; + } + } +} diff --git a/fesod-spring-boot-starter/src/main/java/org/apache/fesod/spring/boot/autoconfigure/FesodReaderBuilderCustomizer.java b/fesod-spring-boot-starter/src/main/java/org/apache/fesod/spring/boot/autoconfigure/FesodReaderBuilderCustomizer.java new file mode 100644 index 000000000..e2a9962d1 --- /dev/null +++ b/fesod-spring-boot-starter/src/main/java/org/apache/fesod/spring/boot/autoconfigure/FesodReaderBuilderCustomizer.java @@ -0,0 +1,32 @@ +/* + * 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. + */ + +package org.apache.fesod.spring.boot.autoconfigure; + +import org.apache.fesod.excel.read.builder.ExcelReaderBuilder; + +/** + * Callback interface that allows customization of the {@link ExcelReaderBuilder} created by + * {@link org.apache.fesod.spring.boot.FesodTemplate}. + */ +@FunctionalInterface +public interface FesodReaderBuilderCustomizer { + + void customize(ExcelReaderBuilder builder); +} diff --git a/fesod-spring-boot-starter/src/main/java/org/apache/fesod/spring/boot/autoconfigure/FesodWriterBuilderCustomizer.java b/fesod-spring-boot-starter/src/main/java/org/apache/fesod/spring/boot/autoconfigure/FesodWriterBuilderCustomizer.java new file mode 100644 index 000000000..2166f243c --- /dev/null +++ b/fesod-spring-boot-starter/src/main/java/org/apache/fesod/spring/boot/autoconfigure/FesodWriterBuilderCustomizer.java @@ -0,0 +1,32 @@ +/* + * 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. + */ + +package org.apache.fesod.spring.boot.autoconfigure; + +import org.apache.fesod.excel.write.builder.ExcelWriterBuilder; + +/** + * Callback interface that allows customization of the {@link ExcelWriterBuilder} created by + * {@link org.apache.fesod.spring.boot.FesodTemplate}. + */ +@FunctionalInterface +public interface FesodWriterBuilderCustomizer { + + void customize(ExcelWriterBuilder builder); +} diff --git a/fesod-spring-boot-starter/src/main/resources/META-INF/spring.factories b/fesod-spring-boot-starter/src/main/resources/META-INF/spring.factories new file mode 100644 index 000000000..124f01e33 --- /dev/null +++ b/fesod-spring-boot-starter/src/main/resources/META-INF/spring.factories @@ -0,0 +1,17 @@ +# 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. + +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +org.apache.fesod.spring.boot.autoconfigure.FesodAutoConfiguration diff --git a/fesod-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/fesod-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 000000000..38ca5d844 --- /dev/null +++ b/fesod-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,2 @@ +org.apache.fesod.spring.boot.autoconfigure.FesodAutoConfiguration + diff --git a/fesod-spring-boot-starter/src/test/java/org/apache/fesod/spring/boot/autoconfigure/FesodAutoConfigurationTest.java b/fesod-spring-boot-starter/src/test/java/org/apache/fesod/spring/boot/autoconfigure/FesodAutoConfigurationTest.java new file mode 100644 index 000000000..704abfe16 --- /dev/null +++ b/fesod-spring-boot-starter/src/test/java/org/apache/fesod/spring/boot/autoconfigure/FesodAutoConfigurationTest.java @@ -0,0 +1,96 @@ +/* + * 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. + */ + +package org.apache.fesod.spring.boot.autoconfigure; + +import static org.assertj.core.api.Assertions.assertThat; +import org.apache.fesod.excel.read.builder.ExcelReaderBuilder; +import org.apache.fesod.excel.read.metadata.ReadWorkbook; +import org.apache.fesod.excel.write.builder.ExcelWriterBuilder; +import org.apache.fesod.excel.write.metadata.WriteWorkbook; +import org.apache.fesod.spring.boot.FesodTemplate; +import org.junit.jupiter.api.Test; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.test.util.ReflectionTestUtils; + +class FesodAutoConfigurationTest { + + private final ApplicationContextRunner contextRunner = + new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(FesodAutoConfiguration.class)); + + @Test + void contextProvidesFesodTemplate() { + contextRunner.run(context -> assertThat(context).hasSingleBean(FesodTemplate.class)); + } + + @Test + void globalAndSectionPropertiesAreApplied() { + contextRunner + .withPropertyValues( + "fesod.global.auto-trim=false", + "fesod.global.use1904-windowing=true", + "fesod.writer.with-bom=false", + "fesod.reader.ignore-empty-row=false") + .run(context -> { + FesodTemplate template = context.getBean(FesodTemplate.class); + + ExcelWriterBuilder writerBuilder = template.writer(); + WriteWorkbook writeWorkbook = + (WriteWorkbook) ReflectionTestUtils.getField(writerBuilder, "writeWorkbook"); + assertThat(writeWorkbook).isNotNull(); + assertThat(writeWorkbook.getAutoTrim()).isFalse(); + assertThat(writeWorkbook.getUse1904windowing()).isTrue(); + assertThat(writeWorkbook.getWithBom()).isFalse(); + + ExcelReaderBuilder readerBuilder = template.reader(); + ReadWorkbook readWorkbook = + (ReadWorkbook) ReflectionTestUtils.getField(readerBuilder, "readWorkbook"); + assertThat(readWorkbook).isNotNull(); + assertThat(readWorkbook.getIgnoreEmptyRow()).isFalse(); + assertThat(readWorkbook.getAutoTrim()).isFalse(); + }); + } + + @Test + void customizersAreInvoked() { + contextRunner + .withBean( + "writerCustomizer", + FesodWriterBuilderCustomizer.class, + () -> builder -> builder.inMemory(Boolean.TRUE)) + .withBean( + "readerCustomizer", + FesodReaderBuilderCustomizer.class, + () -> builder -> builder.mandatoryUseInputStream(Boolean.TRUE)) + .run(context -> { + FesodTemplate template = context.getBean(FesodTemplate.class); + + ExcelWriterBuilder writerBuilder = template.writer(); + WriteWorkbook writeWorkbook = + (WriteWorkbook) ReflectionTestUtils.getField(writerBuilder, "writeWorkbook"); + assertThat(writeWorkbook.getInMemory()).isTrue(); + + ExcelReaderBuilder readerBuilder = template.reader(); + ReadWorkbook readWorkbook = + (ReadWorkbook) ReflectionTestUtils.getField(readerBuilder, "readWorkbook"); + assertThat(readWorkbook.getMandatoryUseInputStream()).isTrue(); + }); + } +} diff --git a/fesod-spring-boot-starter/src/test/java/org/apache/fesod/spring/boot/autoconfigure/FesodPropertiesBindingTest.java b/fesod-spring-boot-starter/src/test/java/org/apache/fesod/spring/boot/autoconfigure/FesodPropertiesBindingTest.java new file mode 100644 index 000000000..cdc360d94 --- /dev/null +++ b/fesod-spring-boot-starter/src/test/java/org/apache/fesod/spring/boot/autoconfigure/FesodPropertiesBindingTest.java @@ -0,0 +1,59 @@ +/* + * 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. + */ + +package org.apache.fesod.spring.boot.autoconfigure; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +/** + * Basic binding test to verify {@link FesodProperties} is bound from environment properties. + */ +@ExtendWith(SpringExtension.class) +@SpringBootTest( + classes = FesodPropertiesBindingTest.TestConfig.class, + properties = {"fesod.global.auto-trim=true", "fesod.reader.ignore-empty-row=true", "fesod.writer.in-memory=true" + }) +class FesodPropertiesBindingTest { + + @Autowired + private FesodProperties properties; + + @Test + void propertiesAreBound() { + assertNotNull(properties); + assertNotNull(properties.getGlobal()); + assertTrue(properties.getGlobal().getAutoTrim(), "autoTrim should bind to true"); + assertNotNull(properties.getReader()); + assertTrue(properties.getReader().getIgnoreEmptyRow(), "ignoreEmptyRow should bind to true"); + assertNotNull(properties.getWriter()); + assertTrue(properties.getWriter().getInMemory(), "inMemory should bind to true"); + } + + @Configuration + @EnableConfigurationProperties(FesodProperties.class) + static class TestConfig {} +} diff --git a/pom.xml b/pom.xml index 153e13d96..fd24492e4 100644 --- a/pom.xml +++ b/pom.xml @@ -40,6 +40,7 @@