-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Add convenience methods in FlatFileItemWriterBuilder to generate delimited/fixedLength files #631
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
01cf593
58a8573
4da50ba
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,5 @@ | ||
| /* | ||
| * Copyright 2016 the original author or authors. | ||
| * Copyright 2016-2018 the original author or authors. | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
|
|
@@ -15,18 +15,29 @@ | |
| */ | ||
| package org.springframework.batch.item.file.builder; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.Arrays; | ||
| import java.util.List; | ||
| import java.util.Locale; | ||
|
|
||
| import org.springframework.batch.item.file.FlatFileFooterCallback; | ||
| import org.springframework.batch.item.file.FlatFileHeaderCallback; | ||
| import org.springframework.batch.item.file.FlatFileItemWriter; | ||
| import org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor; | ||
| import org.springframework.batch.item.file.transform.DelimitedLineAggregator; | ||
| import org.springframework.batch.item.file.transform.FieldExtractor; | ||
| import org.springframework.batch.item.file.transform.FormatterLineAggregator; | ||
| import org.springframework.batch.item.file.transform.LineAggregator; | ||
| import org.springframework.core.io.Resource; | ||
| import org.springframework.util.Assert; | ||
| import org.springframework.util.StringUtils; | ||
|
|
||
| /** | ||
| * A builder implementation for the {@link FlatFileItemWriter} | ||
| * | ||
| * @author Michael Minella | ||
| * @author Glenn Renfro | ||
| * @author Mahmoud Ben Hassine | ||
| * @since 4.0 | ||
| * @see FlatFileItemWriter | ||
| */ | ||
|
|
@@ -58,6 +69,10 @@ public class FlatFileItemWriterBuilder<T> { | |
|
|
||
| private String name; | ||
|
|
||
| private DelimitedBuilder<T> delimitedBuilder; | ||
|
|
||
| private FormattedBuilder<T> formattedBuilder; | ||
|
|
||
| /** | ||
| * Configure if the state of the {@link org.springframework.batch.item.ItemStreamSupport} | ||
| * should be persisted within the {@link org.springframework.batch.item.ExecutionContext} | ||
|
|
@@ -155,7 +170,7 @@ public FlatFileItemWriterBuilder<T> encoding(String encoding) { | |
| } | ||
|
|
||
| /** | ||
| * If set to true, once the step is complete, if the resource previously provdied is | ||
| * If set to true, once the step is complete, if the resource previously provided is | ||
| * empty, it will be deleted. | ||
| * | ||
| * @param shouldDelete defaults to false | ||
|
|
@@ -234,14 +249,245 @@ public FlatFileItemWriterBuilder<T> transactional(boolean transactional) { | |
| return this; | ||
| } | ||
|
|
||
| /** | ||
| * Returns an instance of a {@link DelimitedBuilder} for building a | ||
| * {@link DelimitedLineAggregator}. The {@link DelimitedLineAggregator} configured by | ||
| * this builder will only be used if one is not explicitly configured via | ||
| * {@link FlatFileItemWriterBuilder#lineAggregator} | ||
| * | ||
| * @return a {@link DelimitedBuilder} | ||
| * | ||
| */ | ||
| public DelimitedBuilder<T> delimited() { | ||
| this.delimitedBuilder = new DelimitedBuilder<>(this); | ||
| return this.delimitedBuilder; | ||
| } | ||
|
|
||
| /** | ||
| * Returns an instance of a {@link FormattedBuilder} for building a | ||
| * {@link FormatterLineAggregator}. The {@link FormatterLineAggregator} configured by | ||
| * this builder will only be used if one is not explicitly configured via | ||
| * {@link FlatFileItemWriterBuilder#lineAggregator} | ||
| * | ||
| * @return a {@link FormattedBuilder} | ||
| * | ||
| */ | ||
| public FormattedBuilder<T> formatted() { | ||
| this.formattedBuilder = new FormattedBuilder<>(this); | ||
| return this.formattedBuilder; | ||
| } | ||
|
|
||
| /** | ||
| * A builder for constructing a {@link FormatterLineAggregator}. | ||
| * | ||
| * @param <T> the type of the parent {@link FlatFileItemWriterBuilder} | ||
| */ | ||
| public static class FormattedBuilder<T> { | ||
|
|
||
| private FlatFileItemWriterBuilder<T> parent; | ||
|
|
||
| private String format; | ||
|
|
||
| private Locale locale = Locale.getDefault(); | ||
|
|
||
| private int maximumLength = 0; | ||
|
|
||
| private int minimumLength = 0; | ||
|
|
||
| private FieldExtractor<T> fieldExtractor; | ||
|
|
||
| private List<String> names = new ArrayList<>(); | ||
|
|
||
| protected FormattedBuilder(FlatFileItemWriterBuilder<T> parent) { | ||
| this.parent = parent; | ||
| } | ||
|
|
||
| /** | ||
| * Set the format string used to aggregate items | ||
| * @param format used to aggregate items | ||
| * @return The instance of the builder for chaining. | ||
| */ | ||
| public FormattedBuilder<T> format(String format) { | ||
| this.format = format; | ||
| return this; | ||
| } | ||
|
|
||
| /** | ||
| * Set the locale. | ||
| * @param locale to use | ||
| * @return The instance of the builder for chaining. | ||
| */ | ||
| public FormattedBuilder<T> locale(Locale locale) { | ||
| this.locale = locale; | ||
| return this; | ||
| } | ||
|
|
||
| /** | ||
| * Set the minimum length of the formatted string. If this is not set | ||
| * the default is to allow any length. | ||
| * @param minimumLength of the formatted string | ||
| * @return The instance of the builder for chaining. | ||
| */ | ||
| public FormattedBuilder<T> minimumLength(int minimumLength) { | ||
| this.minimumLength = minimumLength; | ||
| return this; | ||
| } | ||
|
|
||
| /** | ||
| * Set the maximum length of the formatted string. If this is not set | ||
| * the default is to allow any length. | ||
| * @param maximumLength of the formatted string | ||
| * @return The instance of the builder for chaining. | ||
| */ | ||
| public FormattedBuilder<T> maximumLength(int maximumLength) { | ||
| this.maximumLength = maximumLength; | ||
| return this; | ||
| } | ||
|
|
||
| /** | ||
| * Set the {@link FieldExtractor} to use to extract fields from each item. | ||
| * @param fieldExtractor to use to extract fields from each item | ||
| * @return The current instance of the builder | ||
| */ | ||
| public FlatFileItemWriterBuilder<T> fieldExtractor(FieldExtractor<T> fieldExtractor) { | ||
| this.fieldExtractor = fieldExtractor; | ||
| return this.parent; | ||
| } | ||
|
|
||
| /** | ||
| * Names of each of the fields within the fields that are returned in the order | ||
| * they occur within the formatted file. These names will be used to create | ||
| * a {@link BeanWrapperFieldExtractor} only if no explicit field extractor | ||
| * is set via {@link FormattedBuilder#fieldExtractor(FieldExtractor)}. | ||
| * | ||
| * @param names names of each field | ||
| * @return The parent {@link FlatFileItemWriterBuilder} | ||
| * @see BeanWrapperFieldExtractor#setNames(String[]) | ||
| */ | ||
| public FlatFileItemWriterBuilder<T> names(String[] names) { | ||
| this.names.addAll(Arrays.asList(names)); | ||
| return this.parent; | ||
| } | ||
|
|
||
| public FormatterLineAggregator<T> build() { | ||
| Assert.notNull(this.format, "A format is required"); | ||
| Assert.isTrue((this.names != null && !this.names.isEmpty()) || this.fieldExtractor != null, | ||
| "A list of field names or a field extractor is required"); | ||
|
|
||
| FormatterLineAggregator<T> formatterLineAggregator = new FormatterLineAggregator<>(); | ||
| formatterLineAggregator.setFormat(this.format); | ||
| formatterLineAggregator.setLocale(this.locale); | ||
| formatterLineAggregator.setMinimumLength(this.minimumLength); | ||
| formatterLineAggregator.setMaximumLength(this.maximumLength); | ||
|
|
||
| if (this.fieldExtractor == null) { | ||
| BeanWrapperFieldExtractor<T> beanWrapperFieldExtractor = new BeanWrapperFieldExtractor<>(); | ||
| beanWrapperFieldExtractor.setNames(this.names.toArray(new String[this.names.size()])); | ||
| try { | ||
| beanWrapperFieldExtractor.afterPropertiesSet(); | ||
| } | ||
| catch (Exception e) { | ||
| throw new IllegalStateException("Unable to initialize FormatterLineAggregator", e); | ||
| } | ||
| this.fieldExtractor = beanWrapperFieldExtractor; | ||
| } | ||
|
|
||
| formatterLineAggregator.setFieldExtractor(this.fieldExtractor); | ||
| return formatterLineAggregator; | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * A builder for constructing a {@link DelimitedLineAggregator} | ||
| * | ||
| * @param <T> the type of the parent {@link FlatFileItemWriterBuilder} | ||
| */ | ||
| public static class DelimitedBuilder<T> { | ||
|
|
||
| private FlatFileItemWriterBuilder<T> parent; | ||
|
|
||
| private List<String> names = new ArrayList<>(); | ||
|
|
||
| private String delimiter = ","; | ||
|
|
||
| private FieldExtractor<T> fieldExtractor; | ||
|
|
||
| protected DelimitedBuilder(FlatFileItemWriterBuilder<T> parent) { | ||
| this.parent = parent; | ||
| } | ||
|
|
||
| /** | ||
| * Define the delimiter for the file. | ||
| * | ||
| * @param delimiter String used as a delimiter between fields. | ||
| * @return The instance of the builder for chaining. | ||
| * @see DelimitedLineAggregator#setDelimiter(String) | ||
| */ | ||
| public DelimitedBuilder<T> delimiter(String delimiter) { | ||
| this.delimiter = delimiter; | ||
| return this; | ||
| } | ||
|
|
||
| /** | ||
| * Names of each of the fields within the fields that are returned in the order | ||
| * they occur within the delimited file. These names will be used to create | ||
| * a {@link BeanWrapperFieldExtractor} only if no explicit field extractor | ||
| * is set via {@link DelimitedBuilder#fieldExtractor(FieldExtractor)}. | ||
| * | ||
| * @param names names of each field | ||
| * @return The parent {@link FlatFileItemWriterBuilder} | ||
| * @see BeanWrapperFieldExtractor#setNames(String[]) | ||
| */ | ||
| public FlatFileItemWriterBuilder<T> names(String[] names) { | ||
| this.names.addAll(Arrays.asList(names)); | ||
| return this.parent; | ||
| } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be nice to have a vargs option as well...
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I took the same approach as the We can add this for both reader and writer builders in another step. |
||
|
|
||
| /** | ||
| * Set the {@link FieldExtractor} to use to extract fields from each item. | ||
| * @param fieldExtractor to use to extract fields from each item | ||
| * @return The parent {@link FlatFileItemWriterBuilder} | ||
| */ | ||
| public FlatFileItemWriterBuilder<T> fieldExtractor(FieldExtractor<T> fieldExtractor) { | ||
| this.fieldExtractor = fieldExtractor; | ||
| return this.parent; | ||
| } | ||
|
|
||
| public DelimitedLineAggregator<T> build() { | ||
| Assert.isTrue((this.names != null && !this.names.isEmpty()) || this.fieldExtractor != null, | ||
| "A list of field names or a field extractor is required"); | ||
|
|
||
| DelimitedLineAggregator<T> delimitedLineAggregator = new DelimitedLineAggregator<>(); | ||
| if (StringUtils.hasLength(this.delimiter)) { | ||
| delimitedLineAggregator.setDelimiter(this.delimiter); | ||
| } | ||
|
|
||
| if (this.fieldExtractor == null) { | ||
| BeanWrapperFieldExtractor<T> beanWrapperFieldExtractor = new BeanWrapperFieldExtractor<>(); | ||
| beanWrapperFieldExtractor.setNames(this.names.toArray(new String[this.names.size()])); | ||
| try { | ||
| beanWrapperFieldExtractor.afterPropertiesSet(); | ||
| } | ||
| catch (Exception e) { | ||
| throw new IllegalStateException("Unable to initialize DelimitedLineAggregator", e); | ||
| } | ||
| this.fieldExtractor = beanWrapperFieldExtractor; | ||
| } | ||
|
|
||
| delimitedLineAggregator.setFieldExtractor(this.fieldExtractor); | ||
| return delimitedLineAggregator; | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Validates and builds a {@link FlatFileItemWriter}. | ||
| * | ||
| * @return a {@link FlatFileItemWriter} | ||
| */ | ||
| public FlatFileItemWriter<T> build() { | ||
|
|
||
| Assert.notNull(this.lineAggregator, "A LineAggregator is required"); | ||
| Assert.isTrue(this.lineAggregator != null || this.delimitedBuilder != null || this.formattedBuilder != null, | ||
| "A LineAggregator or a DelimitedBuilder or a FormattedBuilder is required"); | ||
| Assert.notNull(this.resource, "A Resource is required"); | ||
|
|
||
| if(this.saveState) { | ||
|
|
@@ -256,6 +502,16 @@ public FlatFileItemWriter<T> build() { | |
| writer.setFooterCallback(this.footerCallback); | ||
| writer.setForceSync(this.forceSync); | ||
| writer.setHeaderCallback(this.headerCallback); | ||
| if (this.lineAggregator == null) { | ||
| Assert.state(this.delimitedBuilder == null || this.formattedBuilder == null, | ||
| "Either a DelimitedLineAggregator or a FormatterLineAggregator should be provided, but not both"); | ||
| if (this.delimitedBuilder != null) { | ||
| this.lineAggregator = this.delimitedBuilder.build(); | ||
| } | ||
| else { | ||
| this.lineAggregator = this.formattedBuilder.build(); | ||
| } | ||
| } | ||
| writer.setLineAggregator(this.lineAggregator); | ||
| writer.setLineSeparator(this.lineSeparator); | ||
| writer.setResource(this.resource); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice to have a vargs option as well...