Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
Expand All @@ -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
*/
Expand Down Expand Up @@ -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}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
}
Copy link
Member

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...


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;
}
Copy link
Member

@mminella mminella Aug 31, 2018

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...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I took the same approach as the FlatFileItemReaderBuilder to be consistent.

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) {
Expand All @@ -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);
Expand Down
Loading