1 /*
2 * Copyright 2013 the original author or authors.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package com.orangesignal.csv.io;
18
19 import java.io.Closeable;
20 import java.io.Flushable;
21 import java.io.IOException;
22 import java.lang.reflect.Field;
23 import java.util.Arrays;
24 import java.util.Collections;
25 import java.util.List;
26 import java.util.Map;
27
28 import com.orangesignal.csv.CsvWriter;
29 import com.orangesignal.csv.bean.CsvColumnPositionMappingBeanTemplate;
30 import com.orangesignal.csv.bean.FieldUtils;
31
32 /**
33 * 区切り文字形式データの項目位置を基準として Java プログラム要素と区切り文字形式データアクセスを行う区切り文字形式出力ストリームを提供します。
34 *
35 * @author Koji Sugisawa
36 * @since 1.4.0
37 */
38 public class CsvColumnPositionMappingBeanWriter<T> implements Closeable, Flushable {
39
40 /**
41 * 区切り文字形式出力ストリームを保持します。
42 */
43 private CsvWriter writer;
44
45 /**
46 * Java プログラム要素操作の簡素化ヘルパーを保持します。
47 */
48 private final CsvColumnPositionMappingBeanTemplate<T> template;
49
50 /**
51 * 区切り文字形式データの列見出し (ヘッダ) 行を出力するかどうかを保持します。
52 *
53 * @since 2.1
54 */
55 private final boolean header;
56
57 /**
58 * 項目名のリストを保持します。
59 */
60 private List<String> columnNames;
61
62 /**
63 * 項目名の数を保存します。
64 */
65 private int columnCount = -1;
66
67 // ------------------------------------------------------------------------
68 // 利便性のための静的メソッド
69
70 /**
71 * 新しい {@link CsvColumnPositionMappingBeanWriter} のインスタンスを返します。
72 * このメソッドは利便性のために提供しています。
73 *
74 * @param writer 区切り文字形式出力ストリーム
75 * @param type Java プログラム要素の型
76 * @return 新しい {@link CsvColumnPositionMappingBeanWriter} のインスタンス
77 * @throws IllegalArgumentException {@code writer} または {@code type} が {@code null} の場合。
78 */
79 public static <T> CsvColumnPositionMappingBeanWriter<T> newInstance(final CsvWriter writer, final Class<T> type) {
80 return new CsvColumnPositionMappingBeanWriter<T>(writer, type);
81 }
82
83 /**
84 * 新しい {@link CsvColumnPositionMappingBeanWriter} のインスタンスを返します。
85 * このメソッドは利便性のために提供しています。
86 *
87 * @param writer 区切り文字形式出力ストリーム
88 * @param type Java プログラム要素の型
89 * @param header 区切り文字形式データの列見出し (ヘッダ) 行を出力するかどうか
90 * @return 新しい {@link CsvColumnPositionMappingBeanWriter} のインスタンス
91 * @throws IllegalArgumentException {@code writer} または {@code type} が {@code null} の場合。
92 * @since 2.1
93 */
94 public static <T> CsvColumnPositionMappingBeanWriter<T> newInstance(final CsvWriter writer, final Class<T> type, final boolean header) {
95 return new CsvColumnPositionMappingBeanWriter<T>(writer, type, header);
96 }
97
98 /**
99 * 新しい {@link CsvColumnPositionMappingBeanWriter} のインスタンスを返します。
100 * このメソッドは利便性のために提供しています。
101 *
102 * @param writer 区切り文字形式出力ストリーム
103 * @param template Java プログラム要素操作の簡素化ヘルパー
104 * @return 新しい {@link CsvColumnPositionMappingBeanWriter} のインスタンス
105 * @throws IllegalArgumentException {@code writer} または {@code template} が {@code null} の場合。
106 */
107 public static <T> CsvColumnPositionMappingBeanWriter<T> newInstance(final CsvWriter writer, final CsvColumnPositionMappingBeanTemplate<T> template) {
108 return new CsvColumnPositionMappingBeanWriter<T>(writer, template);
109 }
110
111 /**
112 * 新しい {@link CsvColumnPositionMappingBeanWriter} のインスタンスを返します。
113 * このメソッドは利便性のために提供しています。
114 *
115 * @param writer 区切り文字形式出力ストリーム
116 * @param template Java プログラム要素操作の簡素化ヘルパー
117 * @param header 区切り文字形式データの列見出し (ヘッダ) 行を出力するかどうか
118 * @return 新しい {@link CsvColumnPositionMappingBeanWriter} のインスタンス
119 * @throws IllegalArgumentException {@code writer} または {@code template} が {@code null} の場合。
120 * @since 2.1
121 */
122 public static <T> CsvColumnPositionMappingBeanWriter<T> newInstance(final CsvWriter writer, final CsvColumnPositionMappingBeanTemplate<T> template, final boolean header) {
123 return new CsvColumnPositionMappingBeanWriter<T>(writer, template, header);
124 }
125
126 // ------------------------------------------------------------------------
127 // コンストラクタ
128
129 /**
130 * 指定された区切り文字形式出力ストリームと Java プログラム要素の型を使用して、このクラスを構築するコンストラクタです。
131 *
132 * @param writer 区切り文字形式出力ストリーム
133 * @param type Java プログラム要素の型
134 * @throws IllegalArgumentException {@code writer} または {@code type} が {@code null} の場合。
135 */
136 public CsvColumnPositionMappingBeanWriter(final CsvWriter writer, final Class<T> type) {
137 this(writer, new CsvColumnPositionMappingBeanTemplate<T>(type), true);
138 }
139
140 /**
141 * 指定された区切り文字形式出力ストリームと Java プログラム要素の型を使用して、このクラスを構築するコンストラクタです。
142 *
143 * @param writer 区切り文字形式出力ストリーム
144 * @param type Java プログラム要素の型
145 * @param header 区切り文字形式データの列見出し (ヘッダ) 行を出力するかどうか
146 * @throws IllegalArgumentException {@code writer} または {@code type} が {@code null} の場合。
147 * @since 2.1
148 */
149 public CsvColumnPositionMappingBeanWriter(final CsvWriter writer, final Class<T> type, final boolean header) {
150 this(writer, new CsvColumnPositionMappingBeanTemplate<T>(type), header);
151 }
152
153 /**
154 * 指定された区切り文字形式出力ストリームと Java プログラム要素操作の簡素化ヘルパーを使用して、このクラスを構築するコンストラクタです。
155 *
156 * @param writer 区切り文字形式出力ストリーム
157 * @param template Java プログラム要素操作の簡素化ヘルパー
158 * @throws IllegalArgumentException {@code writer} または {@code template} が {@code null} の場合。
159 */
160 public CsvColumnPositionMappingBeanWriter(final CsvWriter writer, final CsvColumnPositionMappingBeanTemplate<T> template) {
161 this(writer, template, true);
162 }
163
164 /**
165 * 指定された区切り文字形式出力ストリームと Java プログラム要素操作の簡素化ヘルパーを使用して、このクラスを構築するコンストラクタです。
166 *
167 * @param writer 区切り文字形式出力ストリーム
168 * @param template Java プログラム要素操作の簡素化ヘルパー
169 * @param header 区切り文字形式データの列見出し (ヘッダ) 行を出力するかどうか
170 * @throws IllegalArgumentException {@code writer} または {@code template} が {@code null} の場合。
171 * @since 2.1
172 */
173 public CsvColumnPositionMappingBeanWriter(final CsvWriter writer, final CsvColumnPositionMappingBeanTemplate<T> template, final boolean header) {
174 if (writer == null) {
175 throw new IllegalArgumentException("CsvWriter must not be null");
176 }
177 if (template == null) {
178 throw new IllegalArgumentException("CsvColumnPositionMappingBeanTemplate must not be null");
179 }
180 this.writer = writer;
181 this.template = template;
182 this.header = header;
183 }
184
185 // ------------------------------------------------------------------------
186 // プライベート メソッド
187
188 /**
189 * Checks to make sure that the stream has not been closed
190 */
191 private void ensureOpen() throws IOException {
192 if (writer == null) {
193 throw new IOException("CsvWriter closed");
194 }
195 }
196
197 private void ensureHeader() throws IOException {
198 synchronized (this) {
199 if (columnNames == null) {
200 // 項目位置とフィールド名のマップが指定されていない場合は、フィールドからマップを作成します。
201 if (template.getMaxColumnPosition() == -1) {
202 for (final Field f : template.getType().getDeclaredFields()) {
203 template.column(f.getName());
204 }
205 }
206 columnCount = template.getMaxColumnPosition() + 1;
207
208 // ヘッダ部を処理します。
209 final List<String> names = Collections.unmodifiableList(template.createColumnNames());
210 if (header) {
211 writer.writeValues(names);
212 }
213 columnNames = names;
214 }
215 }
216 }
217
218 // ------------------------------------------------------------------------
219 // オーバーライド メソッド
220
221 @Override
222 public void flush() throws IOException {
223 synchronized (this) {
224 ensureOpen();
225 writer.flush();
226 }
227 }
228
229 @Override
230 public void close() throws IOException {
231 synchronized (this) {
232 ensureOpen();
233 writer.close();
234 writer = null;
235 columnNames = null;
236 columnCount = -1;
237 }
238 }
239
240 // ------------------------------------------------------------------------
241 // パブリック メソッド
242
243 /**
244 * 可能であれば項目名を書き込みます。項目名が既に書き込まれている場合、このメソッドは何も行いません。
245 *
246 * @throws IOException 入出力エラーが発生した場合
247 */
248 public void writeHeader() throws IOException {
249 synchronized (this) {
250 ensureOpen();
251 ensureHeader();
252 }
253 }
254
255 /**
256 * 指定された Java プログラム要素を区切り文字形式で書き込みます。
257 * {@code null} が指定された場合は空行が書き込まれます。
258 *
259 * @param bean 書き込む Java プログラム要素。または {@code null}
260 * @return データの出力を行った場合は {@code true} それ以外の場合 (フィルタにより書き込みがスキップされた場合) は {@code false}
261 * @throws IOException 入出力エラーが発生した場合
262 */
263 public boolean write(final T bean) throws IOException {
264 synchronized (this) {
265 ensureOpen();
266 ensureHeader();
267
268 // 要素が null の場合は null 出力します。
269 if (bean == null) {
270 writer.writeValues(null);
271 return true;
272 }
273
274 final List<String> values = toValues(bean);
275 if (template.isAccept(values)) {
276 return false;
277 }
278 writer.writeValues(values);
279 return true;
280 }
281 }
282
283 private List<String> toValues(final T bean) throws IOException {
284 final Class<?> type = bean.getClass();
285 final String[] values = new String[columnCount];
286 for (final Map.Entry<Integer, String> e : template.columnMappingEntrySet()) {
287 final int pos = e.getKey();
288 if (pos == -1) {
289 continue;
290 }
291 final Field f = FieldUtils.getField(type, e.getValue());
292 values[pos] = template.objectToString(Integer.valueOf(pos), FieldUtils.getFieldValue(bean, f));
293 }
294 return Arrays.asList(values);
295 }
296
297 // ------------------------------------------------------------------------
298 // getter / setter
299
300 /**
301 * Java プログラム要素操作の簡素化ヘルパーを返します。
302 *
303 * @return Java プログラム要素操作の簡素化ヘルパー
304 * @since 2.1
305 */
306 public CsvColumnPositionMappingBeanTemplate<T> getTemplate() {
307 return template;
308 }
309
310 }