View Javadoc
1   package com.github.mygreen.supercsv.io;
2   
3   import java.io.IOException;
4   import java.io.Writer;
5   import java.util.Collection;
6   import java.util.Objects;
7   
8   import org.supercsv.encoder.CsvEncoder;
9   import org.supercsv.exception.SuperCsvException;
10  import org.supercsv.prefs.CsvPreference;
11  import org.supercsv.util.CsvContext;
12  
13  import com.github.mygreen.supercsv.exception.SuperCsvBindingException;
14  
15  /**
16   * アノテーションを元に固定長のCSVファイルを読み込むためのクラス。
17   *
18   * @since 2.5
19   * @param <T> マッピング対象のBeanのクラスタイプ
20   * @author T.TSUCHIE
21   *
22   */
23  public class FixedSizeCsvAnnotationBeanWriter<T> extends AbstractCsvAnnotationBeanWriter<T> {
24  
25      private final Writer writer;
26      
27      private final CsvPreference preference;
28      
29      private final CsvEncoder encoder;
30      
31      /**
32       * ファイルに実際に書き込んだ行番号
33       * <p>ヘッダー行を含む値。</p>
34       * <p>本クラスで更新するため、AbstractCsvWriter とは別の値となります。</p>
35       */
36      private int lineNumber = 0;
37      
38      /**
39       * 論理的な行番号。
40       * <p>ヘッダー行を含まない値。</p>
41       * <p>本クラスで更新するため、AbstractCsvWriter とは別の値となります。</p>
42       */
43      private int rowNumber = 0;
44      
45      /**
46       * 書き込んだ列番号。
47       * <p>1から始まり、行を書き込むごとにリセットされます。</p>
48       * <p>本クラスで更新するため、AbstractCsvWriter とは別の値となります。</p>
49       */
50      private int columnNumber = 0;
51      
52      public FixedSizeCsvAnnotationBeanWriter(final Writer writer, final FixedSizeCsvPreference<T> preference) {
53          super(writer, preference.getCsvPreference());
54          
55          this.writer = writer;
56          this.preference = preference.getCsvPreference();
57          this.encoder = this.preference.getEncoder();
58          
59          this.beanMappingCache = preference.getBeanMappingCache();
60          this.validators.addAll(beanMappingCache.getOriginal().getValidators());
61      }
62      
63      /**
64       * ヘッダー情報を書き込みます。
65       * <p>ただし、列番号を省略され、定義がされていないカラムは、{@literal column[カラム番号]}の形式となります。</p>
66       * @throws IOException ファイルの出力に失敗した場合。
67       */
68      public void writeHeader() throws IOException {
69          writeHeader(getDefinedHeader());
70      }
71      
72      @Override
73      protected void writeRow(final String... columns) throws IOException {
74          
75          if( columns == null ) {
76              throw new NullPointerException(String.format("columns to write should not be null on line %d", lineNumber));
77          } else if( columns.length == 0 ) {
78              throw new IllegalArgumentException(String.format("columns to write should not be empty on line %d", lineNumber));
79          }
80          
81          StringBuilder builder = new StringBuilder();
82          for( int i = 0; i < columns.length; i++ ) {
83              
84              this.columnNumber = i + 1; // column no used by CsvEncoder
85              
86              // 区切り文字はなく、カラムを出力する。
87              final String csvElement = columns[i];
88              if (csvElement != null) {
89                  try {
90                      final CsvContext context = new CsvContext(lineNumber, rowNumber, columnNumber);
91                      final String escapedCsv = encoder.encode(csvElement, context, preference);
92                      builder.append(escapedCsv);
93                      this.lineNumber = context.getLineNumber(); // line number can increment when encoding multi-line columns
94                  } catch(SuperCsvException e) {
95                      errorMessages.addAll(exceptionConverter.convertAndFormat(e, beanMappingCache.getOriginal()));
96                      throw e;
97                  }
98              }
99              
100         }
101         
102         builder.append(preference.getEndOfLineSymbols()); // EOL
103         writer.write(builder.toString());
104     }
105     
106     /**
107      * {@inheritDoc}
108      */
109     @Override
110     protected void incrementRowAndLineNo() {
111         lineNumber++;
112         rowNumber++;
113     }
114     
115     /**
116      * {@inheritDoc}
117      */
118     @Override
119     public int getLineNumber() {
120         return lineNumber;
121     }
122     
123     /**
124      * {@inheritDoc}
125      */
126     @Override
127     public int getRowNumber() {
128         return rowNumber;
129     }
130     
131     /**
132      * レコードのデータを全て書き込みます。
133      * <p>ヘッダー行も自動的に処理されます。2回目以降に呼び出した場合、ヘッダー情報は書き込まれません。</p>
134      * <p>レコード処理中に例外が発生した場合、その時点で処理を終了します。</p>
135      * 
136      * @param sources 書き込むレコードのデータ。
137      * @throws NullPointerException sources is null.
138      * @throws IOException レコードの出力に失敗した場合。
139      * @throws SuperCsvBindingException セルの値に問題がある場合
140      * @throws SuperCsvException 設定など、その他に問題がある場合
141      * 
142      */
143     public void writeAll(final Collection<T> sources) throws IOException {
144         writeAll(sources, false);
145     }
146     
147     /**
148      * レコードのデータを全て書き込みます。
149      * <p>ヘッダー行も自動的に処理されます。2回目以降に呼び出した場合、ヘッダー情報は書き込まれません。</p>
150      * 
151      * @param sources 書き込むレコードのデータ。
152      * @param continueOnError continueOnError レコードの処理中に、
153      *        例外{@link SuperCsvBindingException}が発生しても、続行するかどうか指定します。
154      *        trueの場合、例外が発生しても、次の処理を行います。
155      * @throws NullPointerException sources is null.
156      * @throws IOException レコードの出力に失敗した場合。
157      * @throws SuperCsvBindingException セルの値に問題がある場合
158      * @throws SuperCsvException 設定など、その他に問題がある場合
159      * 
160      */
161     public void writeAll(final Collection<T> sources, final boolean continueOnError) throws IOException {
162         
163         Objects.requireNonNull(sources, "sources should not be null.");
164         
165         if(beanMappingCache.getOriginal().isHeader() && getLineNumber() == 0) {
166             writeHeader();
167         }
168         
169         for(T record : sources) {
170             try {
171                 write(record);
172             } catch(SuperCsvBindingException e) {
173                 if(!continueOnError) {
174                     throw e;
175                 }
176             }
177         }
178         
179         flush();
180         
181     }
182 }