View Javadoc
1   package com.github.mygreen.supercsv.io;
2   
3   import java.io.Reader;
4   import java.io.Writer;
5   import java.util.Objects;
6   import java.util.Optional;
7   import java.util.function.Supplier;
8   
9   import org.supercsv.comment.CommentMatcher;
10  import org.supercsv.encoder.CsvEncoder;
11  import org.supercsv.io.ITokenizer;
12  import org.supercsv.prefs.CsvPreference;
13  import org.supercsv.quote.NormalQuoteMode;
14  import org.supercsv.quote.QuoteMode;
15  
16  import com.github.mygreen.supercsv.builder.BeanMapping;
17  import com.github.mygreen.supercsv.builder.Configuration;
18  import com.github.mygreen.supercsv.builder.FixedSizeBeanMappingFactory;
19  import com.github.mygreen.supercsv.util.ArgUtils;
20  import com.github.mygreen.supercsv.util.MemorizingSupplier;
21  
22  /**
23   * 固定長のCSV設定。
24   * 
25   * @since 2.5
26   * @param <T> マッピング対象のBeanのクラスタイプ
27   * @author T.TSUCHIE
28   *
29   */
30  public class FixedSizeCsvPreference<T> {
31  
32      /**
33       * CSVの設定情報。
34       * ※QuoteModeやエンコーダなどの設定は、{@link FixedSizeCsvEncoder}に引き継がれます。
35       */
36      private CsvPreference csvPreference;
37  
38      private BeanMappingCache<T> beanMappingCache;
39  
40      private TokenizerFactory<T> tokenizerFactory;
41  
42      private Configuration configuration;
43      
44      private FixedSizeCsvPreference(final Builder<T> builder) {
45          Objects.requireNonNull(builder, "builder should not be null.");
46          
47          this.csvPreference = buildCsvPreference(builder);
48          this.tokenizerFactory = builder.tokenizerFactory;
49          this.configuration = builder.configuration;
50          
51          FixedSizeBeanMappingFactoryanMappingFactory.html#FixedSizeBeanMappingFactory">FixedSizeBeanMappingFactory factory = new FixedSizeBeanMappingFactory();
52          factory.setConfiguration(configuration);
53          this.beanMappingCache = BeanMappingCache.create(factory.create(builder.beanType, builder.groups));
54      }
55      
56      /**
57       * 固定長用のCSV設定情報をもとに、通常のCSV設定情報を作成します。
58       * 
59       * @param builder 固定長用のCSV設定情報
60       * @return 通常のCSV設定情報
61       */
62      private CsvPreference buildCsvPreference(final Builder<T> builder) {
63          
64          // クオート文字列、区切り文字列は固定長の場合は不要だが、CsvPreferenceでは必須のため仮値として指定する。
65          CsvPreference.Builder preferenceBuilder = new CsvPreference.Builder('"', ',', builder.endOfLineSymbols)
66                  .useQuoteMode(builder.quoteMode)
67                  .useEncoder(builder.encoderFactory.create(MemorizingSupplier.of(() -> beanMappingCache.getOriginal())))
68                  .ignoreEmptyLines(builder.ignoreEmptyLines)
69                  .surroundingSpacesNeedQuotes(false);
70  
71          builder.commentMatcher.ifPresent(matcher -> {
72              preferenceBuilder.skipComments(matcher);
73          });
74          
75          return preferenceBuilder.build();
76      }
77      
78      /**
79       * {@link FixedSizeCsvPreference}の ビルダークラスを取得します。
80       * @param <T> Beanのクラスタイプ
81       * @param beanType Beanのクラスタイプ
82       * @param groups グループ情報。適用するアノテーションを切り替える際に指定します。
83       * @return {@link FixedSizeCsvPreference}の ビルダークラスのインスタンス。
84       */
85      public static <T> Builder<T> builder(final Class<T> beanType, final Class<?>... groups) {
86          return new Builder<>(beanType, groups);
87      }
88      
89      /**
90       * CSVの設定情報を取得します。
91       * @return CSVの設定情報
92       */
93      public CsvPreference getCsvPreference() {
94          return csvPreference;
95      }
96  
97      /**
98       * Beanのマッピング情報を取得します。
99       * @return Beanのマッピング情報
100      */
101     public BeanMappingCache<T> getBeanMappingCache() {
102         return beanMappingCache;
103     }
104     
105     /**
106      * 設定情報を取得します。
107      * @return 設定情報
108      */
109     public Configuration getConfiguration() {
110         return configuration;
111     }
112     
113     /**
114      * 固定長CSVを読み込み時のTokenizerを作成します。
115      * 
116      * @param reader Reader
117      * @return Tokenizerのインスタンス。
118      */
119     public ITokenizer createTokenizer(final Reader reader) {
120         return tokenizerFactory.create(reader, csvPreference, beanMappingCache.getOriginal());
121     }
122 
123     /**
124      * 固定長CSVの読み込みを行う {@link FixedSizeCsvAnnotationBeanReader}を作成します。
125      * @param reader Reader
126      * @return {@link FixedSizeCsvAnnotationBeanReader}のインスタンス。
127      */
128     public FixedSizeCsvAnnotationBeanReader<T> csvReader(final Reader reader) {
129         return new FixedSizeCsvAnnotationBeanReader<>(reader, this);
130     }
131     
132     /**
133      * 固定長CSVの書き込みを行う {@link FixedSizeCsvAnnotationBeanWriter}を作成します。
134      * @param writer Writer
135      * @return {@link FixedSizeCsvAnnotationBeanWriter}のインスタンス。
136      */
137     public FixedSizeCsvAnnotationBeanWriter<T> csvWriter(final Writer writer) {
138         return new FixedSizeCsvAnnotationBeanWriter<>(writer, this);
139     }
140     
141     /**
142      * Tokenizerのインスタンスを作成する。
143      * 
144      * @param <T> マッピング対象のBeanのクラスタイプ
145      */
146     public interface TokenizerFactory<T> {
147         
148         /**
149          * Tokenizerのインスタンスを作成する。
150          * 
151          * @param reader 読み込み元のリーダー
152          * @param preference CSVの設定情報
153          * @param beanMapping Beanのマッピング情報
154          * @return Tokenizerのインスタンス
155          */
156         ITokenizer create(Reader reader, CsvPreference preference, BeanMapping<T> beanMapping);
157     }
158     
159     /**
160      * CsvEncoderのインスタンスを作成する。
161      *
162      *
163     * @param <T> マッピング対象のBeanのクラスタイプ
164      */
165     public interface CsvEncoderFactory<T> {
166         
167         /**
168          * CsvEncoderのインスタンスを作成する。
169          * <p>CsvEncoderのインスタンス作成時は、BeanMappingのインスタンスは未作成のため遅延評価する必要があるため、Supplierを使用する。
170          * @param beanMappingSupplier Beanのマッピング情報を取得する処理。
171          * @return CsvEncoderのインスタンス
172          */
173         CsvEncoder create(Supplier<BeanMapping<T>> beanMappingSupplier);
174     }
175 
176     
177     /**
178      * {@link FixedSizeCsvPreference} のビルダー。
179      * 
180      * @param <T> マッピング対象のBeanのクラスタイプ
181      *
182      */
183     public static class Builder<T> {
184         
185         private final Class<T> beanType;
186         
187         private final Class<?>[] groups;
188         
189         /** EOL(改行コード) */
190         private String endOfLineSymbols;
191         
192         /** 空行を無視するかどうか */
193         private boolean ignoreEmptyLines;
194         
195         private QuoteMode quoteMode;
196         
197         /** 読み込み時に固定長カラムサイズ定義に従い分解して処理する */
198         private TokenizerFactory<T> tokenizerFactory;
199         
200         /** 書き込み時に固定長カラムサイズ定義に従い処理する */
201         private CsvEncoderFactory<T> encoderFactory;
202 
203         /**
204          * コメント行かの判定処理。
205          */
206         private Optional<CommentMatcher> commentMatcher;
207         
208         private Configuration configuration;
209         
210         /**
211          * コンストラクタ。
212          * @param beanType Beanのクラスタイプ
213          * @param groups グループ情報。適用するアノテーションを切り替える際に指定します。
214          * @throws NullPointerException beanType is null.
215          */
216         public Builder(final Class<T> beanType, final Class<?>... groups) {
217             Objects.requireNonNull(beanType, "beanType should not be null.");
218             
219             this.beanType = beanType;
220             this.groups = groups;
221         }
222         
223         /**
224          * EOL(改行コード)を設定します。
225          * @param endOfLineSymbols EOL(改行コード)。デフォルトは、{@literal \r\n}。
226          * @return Builder自身のインスタンス。
227          * @throws NullPointerException endOfLineSymbols is null.
228          * @throws IllegalArgumentException endOfLineSymbols is empty.
229          */
230         public Builder<T> endOfLineSymbols(final String endOfLineSymbols) {
231             ArgUtils.notEmpty(endOfLineSymbols, "endOfLineSymbols");
232             
233             this.endOfLineSymbols = endOfLineSymbols;
234             return this;
235         }
236         
237         /**
238          * 空行を無視するかどうかを設定します。
239          * @param ignoreEmptyLines 空行を無視する場合は{@literal true}を指定します。
240          * @return Builder自身のインスタンス。
241          */
242         public Builder<T> ignoreEmptyLines(final boolean ignoreEmptyLines) {
243             this.ignoreEmptyLines = ignoreEmptyLines;
244             return this;
245         }
246         
247         /**
248          * クォートモードを設定します。
249          * @param quoteMode クォートモード。デフォルトは、{@link NormalQuoteMode}。
250          * @return Builder自身のインスタンス。
251          * @throws NullPointerException quoteMode is null.
252          */
253         public Builder<T> quoteMode(final QuoteMode quoteMode) {
254             Objects.requireNonNull(quoteMode, "quoteMode should not be null.");
255             
256             this.quoteMode = quoteMode;
257             return this;
258         }
259         
260         /**
261          * Tokenizerを作成する処理を設定します。
262          * @param tokenizerFactory Tokenizerを作成する処理。デフォルトは、{@link FixedSizeTokenizer}のインスタンスです。
263          * @return Builder自身のインスタンス。
264          * @throws NullPointerException tokenizerFactory is null.
265          */
266         public Builder<T> tokenizerFactory(final TokenizerFactory<T> tokenizerFactory) {
267             Objects.requireNonNull(tokenizerFactory, "tokenizerFactory should not be null.");
268             
269             this.tokenizerFactory = tokenizerFactory;
270             return this;
271         }
272         
273         /**
274          * CsvEncoderを作成する処理を設定します。
275          * @param encoderFactory CsvEncoderを作成する処理を設定します。デフォルトは、{@link FixedSizeCsvEncoder}のインスタンスです。
276          * @return Builder自身のインスタンス。
277          * @throws NullPointerException csvEncoderFactory is null.
278          */
279         public Builder<T> encoderFactory(final CsvEncoderFactory<T> encoderFactory) {
280             Objects.requireNonNull(encoderFactory, "encoderFactory should not be null.");
281             
282             this.encoderFactory = encoderFactory;
283             return this;
284         }
285         
286         /**
287          * コメント行かの判定処理を設定します。
288          * @param commentMatcher コメント行かの判定処理。デフォルトは {@literal null}で何もしない。
289          * @return Builder自身のインスタンス。
290          */
291         public Builder<T> skipComment(final CommentMatcher commentMatcher) {
292             this.commentMatcher = Optional.of(commentMatcher);
293             return this;
294             
295         }
296         
297         /**
298          * 設定情報を設定します。
299          * @param configuration 設定情報
300          * @return Builder自身のインスタンス。
301          * @throws NullPointerException configuration is null.
302          */
303         public Builder<T> configuration(final Configuration configuration) {
304             Objects.requireNonNull(configuration, "configuration should not be null.");
305             
306             this.configuration = configuration;
307             return this;
308         }
309         
310         /**
311          * {@link FixedSizeCsvPreference}のインスタンスを組み立てます。
312          * 
313          * @return {@link FixedSizeCsvPreference}のインスタンス。
314          */
315         public FixedSizeCsvPreference<T> build() {
316             
317             if (endOfLineSymbols == null) {
318                 endOfLineSymbols = "\r\n";
319             }
320             
321             if (quoteMode == null) {
322                 quoteMode = new NormalQuoteMode();
323             }
324             
325             if (commentMatcher == null) {
326                 commentMatcher = Optional.empty();
327             }
328             
329             if (tokenizerFactory == null) {
330                 tokenizerFactory = new TokenizerFactory<T>() {
331                     
332                     @Override
333                     public ITokenizer create(Reader reader, CsvPreference preference, BeanMapping<T> beanMapping) {
334                         return new FixedSizeTokenizer(reader, preference, beanMapping);
335                     }
336                 };
337                 
338             }
339             
340             if (encoderFactory == null) {
341                 encoderFactory = new CsvEncoderFactory<T>() {
342                     
343                    @Override
344                     public CsvEncoder create(Supplier<BeanMapping<T>> beanMappingSupplier) {
345                         return new FixedSizeCsvEncoder<T>(beanMappingSupplier);
346                     }
347                 };
348                 
349             }
350             
351             if (configuration == null) {
352                 configuration = new Configuration();
353             }
354             
355             return new FixedSizeCsvPreference<>(this);
356         }
357         
358     }
359 
360 }