11/*
2- * Copyright 2016 the original author or authors.
2+ * Copyright 2016-2018 the original author or authors.
33 *
44 * Licensed under the Apache License, Version 2.0 (the "License");
55 * you may not use this file except in compliance with the License.
1515 */
1616package org .springframework .batch .item .file .builder ;
1717
18+ import java .util .ArrayList ;
19+ import java .util .Arrays ;
20+ import java .util .List ;
21+ import java .util .Locale ;
22+
1823import org .springframework .batch .item .file .FlatFileFooterCallback ;
1924import org .springframework .batch .item .file .FlatFileHeaderCallback ;
2025import org .springframework .batch .item .file .FlatFileItemWriter ;
26+ import org .springframework .batch .item .file .transform .BeanWrapperFieldExtractor ;
27+ import org .springframework .batch .item .file .transform .DelimitedLineAggregator ;
28+ import org .springframework .batch .item .file .transform .FieldExtractor ;
29+ import org .springframework .batch .item .file .transform .FormatterLineAggregator ;
2130import org .springframework .batch .item .file .transform .LineAggregator ;
2231import org .springframework .core .io .Resource ;
2332import org .springframework .util .Assert ;
33+ import org .springframework .util .StringUtils ;
2434
2535/**
2636 * A builder implementation for the {@link FlatFileItemWriter}
2737 *
2838 * @author Michael Minella
2939 * @author Glenn Renfro
40+ * @author Mahmoud Ben Hassine
3041 * @since 4.0
3142 * @see FlatFileItemWriter
3243 */
@@ -58,6 +69,10 @@ public class FlatFileItemWriterBuilder<T> {
5869
5970 private String name ;
6071
72+ private DelimitedBuilder <T > delimitedBuilder ;
73+
74+ private FormattedBuilder <T > formattedBuilder ;
75+
6176 /**
6277 * Configure if the state of the {@link org.springframework.batch.item.ItemStreamSupport}
6378 * should be persisted within the {@link org.springframework.batch.item.ExecutionContext}
@@ -155,7 +170,7 @@ public FlatFileItemWriterBuilder<T> encoding(String encoding) {
155170 }
156171
157172 /**
158- * If set to true, once the step is complete, if the resource previously provdied is
173+ * If set to true, once the step is complete, if the resource previously provided is
159174 * empty, it will be deleted.
160175 *
161176 * @param shouldDelete defaults to false
@@ -234,14 +249,245 @@ public FlatFileItemWriterBuilder<T> transactional(boolean transactional) {
234249 return this ;
235250 }
236251
252+ /**
253+ * Returns an instance of a {@link DelimitedBuilder} for building a
254+ * {@link DelimitedLineAggregator}. The {@link DelimitedLineAggregator} configured by
255+ * this builder will only be used if one is not explicitly configured via
256+ * {@link FlatFileItemWriterBuilder#lineAggregator}
257+ *
258+ * @return a {@link DelimitedBuilder}
259+ *
260+ */
261+ public DelimitedBuilder <T > delimited () {
262+ this .delimitedBuilder = new DelimitedBuilder <>(this );
263+ return this .delimitedBuilder ;
264+ }
265+
266+ /**
267+ * Returns an instance of a {@link FormattedBuilder} for building a
268+ * {@link FormatterLineAggregator}. The {@link FormatterLineAggregator} configured by
269+ * this builder will only be used if one is not explicitly configured via
270+ * {@link FlatFileItemWriterBuilder#lineAggregator}
271+ *
272+ * @return a {@link FormattedBuilder}
273+ *
274+ */
275+ public FormattedBuilder <T > formatted () {
276+ this .formattedBuilder = new FormattedBuilder <>(this );
277+ return this .formattedBuilder ;
278+ }
279+
280+ /**
281+ * A builder for constructing a {@link FormatterLineAggregator}.
282+ *
283+ * @param <T> the type of the parent {@link FlatFileItemWriterBuilder}
284+ */
285+ public static class FormattedBuilder <T > {
286+
287+ private FlatFileItemWriterBuilder <T > parent ;
288+
289+ private String format ;
290+
291+ private Locale locale = Locale .getDefault ();
292+
293+ private int maximumLength = 0 ;
294+
295+ private int minimumLength = 0 ;
296+
297+ private FieldExtractor <T > fieldExtractor ;
298+
299+ private List <String > names = new ArrayList <>();
300+
301+ protected FormattedBuilder (FlatFileItemWriterBuilder <T > parent ) {
302+ this .parent = parent ;
303+ }
304+
305+ /**
306+ * Set the format string used to aggregate items
307+ * @param format used to aggregate items
308+ * @return The instance of the builder for chaining.
309+ */
310+ public FormattedBuilder <T > format (String format ) {
311+ this .format = format ;
312+ return this ;
313+ }
314+
315+ /**
316+ * Set the locale.
317+ * @param locale to use
318+ * @return The instance of the builder for chaining.
319+ */
320+ public FormattedBuilder <T > locale (Locale locale ) {
321+ this .locale = locale ;
322+ return this ;
323+ }
324+
325+ /**
326+ * Set the minimum length of the formatted string. If this is not set
327+ * the default is to allow any length.
328+ * @param minimumLength of the formatted string
329+ * @return The instance of the builder for chaining.
330+ */
331+ public FormattedBuilder <T > minimumLength (int minimumLength ) {
332+ this .minimumLength = minimumLength ;
333+ return this ;
334+ }
335+
336+ /**
337+ * Set the maximum length of the formatted string. If this is not set
338+ * the default is to allow any length.
339+ * @param maximumLength of the formatted string
340+ * @return The instance of the builder for chaining.
341+ */
342+ public FormattedBuilder <T > maximumLength (int maximumLength ) {
343+ this .maximumLength = maximumLength ;
344+ return this ;
345+ }
346+
347+ /**
348+ * Set the {@link FieldExtractor} to use to extract fields from each item.
349+ * @param fieldExtractor to use to extract fields from each item
350+ * @return The current instance of the builder
351+ */
352+ public FlatFileItemWriterBuilder <T > fieldExtractor (FieldExtractor <T > fieldExtractor ) {
353+ this .fieldExtractor = fieldExtractor ;
354+ return this .parent ;
355+ }
356+
357+ /**
358+ * Names of each of the fields within the fields that are returned in the order
359+ * they occur within the formatted file. These names will be used to create
360+ * a {@link BeanWrapperFieldExtractor} only if no explicit field extractor
361+ * is set via {@link FormattedBuilder#fieldExtractor(FieldExtractor)}.
362+ *
363+ * @param names names of each field
364+ * @return The parent {@link FlatFileItemWriterBuilder}
365+ * @see BeanWrapperFieldExtractor#setNames(String[])
366+ */
367+ public FlatFileItemWriterBuilder <T > names (String [] names ) {
368+ this .names .addAll (Arrays .asList (names ));
369+ return this .parent ;
370+ }
371+
372+ public FormatterLineAggregator <T > build () {
373+ Assert .notNull (this .format , "A format is required" );
374+ Assert .isTrue ((this .names != null && !this .names .isEmpty ()) || this .fieldExtractor != null ,
375+ "A list of field names or a field extractor is required" );
376+
377+ FormatterLineAggregator <T > formatterLineAggregator = new FormatterLineAggregator <>();
378+ formatterLineAggregator .setFormat (this .format );
379+ formatterLineAggregator .setLocale (this .locale );
380+ formatterLineAggregator .setMinimumLength (this .minimumLength );
381+ formatterLineAggregator .setMaximumLength (this .maximumLength );
382+
383+ if (this .fieldExtractor == null ) {
384+ BeanWrapperFieldExtractor <T > beanWrapperFieldExtractor = new BeanWrapperFieldExtractor <>();
385+ beanWrapperFieldExtractor .setNames (this .names .toArray (new String [this .names .size ()]));
386+ try {
387+ beanWrapperFieldExtractor .afterPropertiesSet ();
388+ }
389+ catch (Exception e ) {
390+ throw new IllegalStateException ("Unable to initialize FormatterLineAggregator" , e );
391+ }
392+ this .fieldExtractor = beanWrapperFieldExtractor ;
393+ }
394+
395+ formatterLineAggregator .setFieldExtractor (this .fieldExtractor );
396+ return formatterLineAggregator ;
397+ }
398+ }
399+
400+ /**
401+ * A builder for constructing a {@link DelimitedLineAggregator}
402+ *
403+ * @param <T> the type of the parent {@link FlatFileItemWriterBuilder}
404+ */
405+ public static class DelimitedBuilder <T > {
406+
407+ private FlatFileItemWriterBuilder <T > parent ;
408+
409+ private List <String > names = new ArrayList <>();
410+
411+ private String delimiter = "," ;
412+
413+ private FieldExtractor <T > fieldExtractor ;
414+
415+ protected DelimitedBuilder (FlatFileItemWriterBuilder <T > parent ) {
416+ this .parent = parent ;
417+ }
418+
419+ /**
420+ * Define the delimiter for the file.
421+ *
422+ * @param delimiter String used as a delimiter between fields.
423+ * @return The instance of the builder for chaining.
424+ * @see DelimitedLineAggregator#setDelimiter(String)
425+ */
426+ public DelimitedBuilder <T > delimiter (String delimiter ) {
427+ this .delimiter = delimiter ;
428+ return this ;
429+ }
430+
431+ /**
432+ * Names of each of the fields within the fields that are returned in the order
433+ * they occur within the delimited file. These names will be used to create
434+ * a {@link BeanWrapperFieldExtractor} only if no explicit field extractor
435+ * is set via {@link DelimitedBuilder#fieldExtractor(FieldExtractor)}.
436+ *
437+ * @param names names of each field
438+ * @return The parent {@link FlatFileItemWriterBuilder}
439+ * @see BeanWrapperFieldExtractor#setNames(String[])
440+ */
441+ public FlatFileItemWriterBuilder <T > names (String [] names ) {
442+ this .names .addAll (Arrays .asList (names ));
443+ return this .parent ;
444+ }
445+
446+ /**
447+ * Set the {@link FieldExtractor} to use to extract fields from each item.
448+ * @param fieldExtractor to use to extract fields from each item
449+ * @return The parent {@link FlatFileItemWriterBuilder}
450+ */
451+ public FlatFileItemWriterBuilder <T > fieldExtractor (FieldExtractor <T > fieldExtractor ) {
452+ this .fieldExtractor = fieldExtractor ;
453+ return this .parent ;
454+ }
455+
456+ public DelimitedLineAggregator <T > build () {
457+ Assert .isTrue ((this .names != null && !this .names .isEmpty ()) || this .fieldExtractor != null ,
458+ "A list of field names or a field extractor is required" );
459+
460+ DelimitedLineAggregator <T > delimitedLineAggregator = new DelimitedLineAggregator <>();
461+ if (StringUtils .hasLength (this .delimiter )) {
462+ delimitedLineAggregator .setDelimiter (this .delimiter );
463+ }
464+
465+ if (this .fieldExtractor == null ) {
466+ BeanWrapperFieldExtractor <T > beanWrapperFieldExtractor = new BeanWrapperFieldExtractor <>();
467+ beanWrapperFieldExtractor .setNames (this .names .toArray (new String [this .names .size ()]));
468+ try {
469+ beanWrapperFieldExtractor .afterPropertiesSet ();
470+ }
471+ catch (Exception e ) {
472+ throw new IllegalStateException ("Unable to initialize DelimitedLineAggregator" , e );
473+ }
474+ this .fieldExtractor = beanWrapperFieldExtractor ;
475+ }
476+
477+ delimitedLineAggregator .setFieldExtractor (this .fieldExtractor );
478+ return delimitedLineAggregator ;
479+ }
480+ }
481+
237482 /**
238483 * Validates and builds a {@link FlatFileItemWriter}.
239484 *
240485 * @return a {@link FlatFileItemWriter}
241486 */
242487 public FlatFileItemWriter <T > build () {
243488
244- Assert .notNull (this .lineAggregator , "A LineAggregator is required" );
489+ Assert .isTrue (this .lineAggregator != null || this .delimitedBuilder != null || this .formattedBuilder != null ,
490+ "A LineAggregator or a DelimitedBuilder or a FormattedBuilder is required" );
245491 Assert .notNull (this .resource , "A Resource is required" );
246492
247493 if (this .saveState ) {
@@ -256,6 +502,16 @@ public FlatFileItemWriter<T> build() {
256502 writer .setFooterCallback (this .footerCallback );
257503 writer .setForceSync (this .forceSync );
258504 writer .setHeaderCallback (this .headerCallback );
505+ if (this .lineAggregator == null ) {
506+ Assert .state (this .delimitedBuilder == null || this .formattedBuilder == null ,
507+ "Either a DelimitedLineAggregator or a FormatterLineAggregator should be provided, but not both" );
508+ if (this .delimitedBuilder != null ) {
509+ this .lineAggregator = this .delimitedBuilder .build ();
510+ }
511+ else {
512+ this .lineAggregator = this .formattedBuilder .build ();
513+ }
514+ }
259515 writer .setLineAggregator (this .lineAggregator );
260516 writer .setLineSeparator (this .lineSeparator );
261517 writer .setResource (this .resource );
0 commit comments