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.List;
25
26 import com.orangesignal.csv.CsvWriter;
27 import com.orangesignal.csv.bean.CsvColumnNameMappingBeanTemplate;
28 import com.orangesignal.csv.bean.FieldUtils;
29
30 /**
31 * 区切り文字形式データの項目名を基準として Java プログラム要素と区切り文字形式データアクセスを行う区切り文字形式出力ストリームを提供します。
32 *
33 * @author Koji Sugisawa
34 * @since 1.4.0
35 */
36 public class CsvColumnNameMappingBeanWriter<T> implements Closeable, Flushable {
37
38 /**
39 * 区切り文字形式出力ストリームを保持します。
40 */
41 private CsvWriter writer;
42
43 /**
44 * Java プログラム要素操作の簡素化ヘルパーを保持します。
45 */
46 private final CsvColumnNameMappingBeanTemplate<T> template;
47
48 /**
49 * 区切り文字形式データの列見出し (ヘッダ) 行を出力するかどうかを保持します。
50 *
51 * @since 2.1
52 */
53 private final boolean header;
54
55 /**
56 * 項目名のリストを保持します。
57 */
58 private List<String> columnNames;
59
60 /**
61 * 項目名の数を保存します。
62 */
63 private int columnCount = -1;
64
65 // ------------------------------------------------------------------------
66 // 利便性のための静的メソッド
67
68 /**
69 * 新しい {@link CsvColumnNameMappingBeanWriter} のインスタンスを返します。
70 * このメソッドは利便性のために提供しています。
71 *
72 * @param writer 区切り文字形式出力ストリーム
73 * @param type Java プログラム要素の型
74 * @return 新しい {@link CsvColumnNameMappingBeanWriter} のインスタンス
75 * @throws IllegalArgumentException {@code writer} または {@code type} が {@code null} の場合。
76 */
77 public static <T> CsvColumnNameMappingBeanWriter<T> newInstance(final CsvWriter writer, final Class<T> type) {
78 return new CsvColumnNameMappingBeanWriter<T>(writer, type);
79 }
80
81 /**
82 * 新しい {@link CsvColumnNameMappingBeanWriter} のインスタンスを返します。
83 * このメソッドは利便性のために提供しています。
84 *
85 * @param writer 区切り文字形式出力ストリーム
86 * @param type Java プログラム要素の型
87 * @param header 区切り文字形式データの列見出し (ヘッダ) 行を出力するかどうか
88 * @return 新しい {@link CsvColumnNameMappingBeanWriter} のインスタンス
89 * @throws IllegalArgumentException {@code writer} または {@code type} が {@code null} の場合。
90 * @since 2.1
91 */
92 public static <T> CsvColumnNameMappingBeanWriter<T> newInstance(final CsvWriter writer, final Class<T> type, final boolean header) {
93 return new CsvColumnNameMappingBeanWriter<T>(writer, type, header);
94 }
95
96 /**
97 * 新しい {@link CsvColumnNameMappingBeanWriter} のインスタンスを返します。
98 * このメソッドは利便性のために提供しています。
99 *
100 * @param writer 区切り文字形式出力ストリーム
101 * @param template Java プログラム要素操作の簡素化ヘルパー
102 * @return 新しい {@link CsvColumnNameMappingBeanWriter} のインスタンス
103 * @throws IllegalArgumentException {@code writer} または {@code template} が {@code null} の場合。
104 */
105 public static <T> CsvColumnNameMappingBeanWriter<T> newInstance(final CsvWriter writer, final CsvColumnNameMappingBeanTemplate<T> template) {
106 return new CsvColumnNameMappingBeanWriter<T>(writer, template);
107 }
108
109 /**
110 * 新しい {@link CsvColumnNameMappingBeanWriter} のインスタンスを返します。
111 * このメソッドは利便性のために提供しています。
112 *
113 * @param writer 区切り文字形式出力ストリーム
114 * @param template Java プログラム要素操作の簡素化ヘルパー
115 * @param header 区切り文字形式データの列見出し (ヘッダ) 行を出力するかどうか
116 * @return 新しい {@link CsvColumnNameMappingBeanWriter} のインスタンス
117 * @throws IllegalArgumentException {@code writer} または {@code template} が {@code null} の場合。
118 * @since 2.1
119 */
120 public static <T> CsvColumnNameMappingBeanWriter<T> newInstance(final CsvWriter writer, final CsvColumnNameMappingBeanTemplate<T> template, final boolean header) {
121 return new CsvColumnNameMappingBeanWriter<T>(writer, template, header);
122 }
123
124 // ------------------------------------------------------------------------
125 // コンストラクタ
126
127 /**
128 * 指定された区切り文字形式出力ストリームと Java プログラム要素の型を使用して、このクラスを構築するコンストラクタです。
129 *
130 * @param writer 区切り文字形式出力ストリーム
131 * @param type Java プログラム要素の型
132 * @throws IllegalArgumentException {@code writer} または {@code type} が {@code null} の場合。
133 */
134 public CsvColumnNameMappingBeanWriter(final CsvWriter writer, final Class<T> type) {
135 this(writer, new CsvColumnNameMappingBeanTemplate<T>(type), true);
136 }
137
138 /**
139 * 指定された区切り文字形式出力ストリームと Java プログラム要素の型を使用して、このクラスを構築するコンストラクタです。
140 *
141 * @param writer 区切り文字形式出力ストリーム
142 * @param type Java プログラム要素の型
143 * @param header 区切り文字形式データの列見出し (ヘッダ) 行を出力するかどうか
144 * @throws IllegalArgumentException {@code writer} または {@code type} が {@code null} の場合。
145 * @since 2.1
146 */
147 public CsvColumnNameMappingBeanWriter(final CsvWriter writer, final Class<T> type, final boolean header) {
148 this(writer, new CsvColumnNameMappingBeanTemplate<T>(type), header);
149 }
150
151 /**
152 * 指定された区切り文字形式出力ストリームと Java プログラム要素操作の簡素化ヘルパーを使用して、このクラスを構築するコンストラクタです。
153 *
154 * @param writer 区切り文字形式出力ストリーム
155 * @param template Java プログラム要素操作の簡素化ヘルパー
156 * @throws IllegalArgumentException {@code writer} または {@code template} が {@code null} の場合。
157 */
158 public CsvColumnNameMappingBeanWriter(final CsvWriter writer, final CsvColumnNameMappingBeanTemplate<T> template) {
159 this(writer, template, true);
160 }
161
162 /**
163 * 指定された区切り文字形式出力ストリームと Java プログラム要素操作の簡素化ヘルパーを使用して、このクラスを構築するコンストラクタです。
164 *
165 * @param writer 区切り文字形式出力ストリーム
166 * @param template Java プログラム要素操作の簡素化ヘルパー
167 * @param header 区切り文字形式データの列見出し (ヘッダ) 行を出力するかどうか
168 * @throws IllegalArgumentException {@code writer} または {@code template} が {@code null} の場合。
169 * @since 2.1
170 */
171 public CsvColumnNameMappingBeanWriter(final CsvWriter writer, final CsvColumnNameMappingBeanTemplate<T> template, final boolean header) {
172 if (writer == null) {
173 throw new IllegalArgumentException("CsvWriter must not be null");
174 }
175 if (template == null) {
176 throw new IllegalArgumentException("CsvColumnNameMappingBeanTemplate must not be null");
177 }
178 this.writer = writer;
179 this.template = template;
180 this.header = header;
181 }
182
183 // ------------------------------------------------------------------------
184 // プライベート メソッド
185
186 /**
187 * Checks to make sure that the stream has not been closed
188 */
189 private void ensureOpen() throws IOException {
190 if (writer == null) {
191 throw new IOException("CsvWriter closed");
192 }
193 }
194
195 private void ensureHeader() throws IOException {
196 synchronized (this) {
197 if (columnNames == null) {
198 template.setupColumnMappingIfNeed();
199 final List<String> names = template.createColumnNames();
200 if (header) {
201 writer.writeValues(names);
202 }
203 columnNames = names;
204 columnCount = names.size();
205 }
206 }
207 }
208
209 // ------------------------------------------------------------------------
210 // オーバーライド メソッド
211
212 @Override
213 public void flush() throws IOException {
214 synchronized (this) {
215 ensureOpen();
216 writer.flush();
217 }
218 }
219
220 @Override
221 public void close() throws IOException {
222 synchronized (this) {
223 ensureOpen();
224 writer.close();
225 writer = null;
226 columnNames = null;
227 columnCount = -1;
228 }
229 }
230
231 // ------------------------------------------------------------------------
232 // パブリック メソッド
233
234 /**
235 * 可能であれば項目名を書き込みます。項目名が既に書き込まれている場合、このメソッドは何も行いません。
236 *
237 * @throws IOException 入出力エラーが発生した場合
238 */
239 public void writeHeader() throws IOException {
240 synchronized (this) {
241 ensureOpen();
242 ensureHeader();
243 }
244 }
245
246 /**
247 * 指定された Java プログラム要素を区切り文字形式で書き込みます。
248 * {@code null} が指定された場合は空行が書き込まれます。
249 *
250 * @param bean 書き込む Java プログラム要素。または {@code null}
251 * @return データの出力を行った場合は {@code true} それ以外の場合 (フィルタにより書き込みがスキップされた場合) は {@code false}
252 * @throws IOException 入出力エラーが発生した場合
253 */
254 public boolean write(final T bean) throws IOException {
255 synchronized (this) {
256 ensureOpen();
257 ensureHeader();
258
259 // 要素が null の場合は null 出力します。
260 if (bean == null) {
261 writer.writeValues(null);
262 return true;
263 }
264
265 final List<String> values = toValues(bean);
266 if (template.isAccept(columnNames, values)) {
267 return false;
268 }
269 writer.writeValues(values);
270 return true;
271 }
272 }
273
274 private List<String> toValues(final T bean) throws IOException {
275 final String[] values = new String[columnCount];
276 for (int i = 0; i < columnCount; i++) {
277 final String columnName = columnNames.get(i);
278 if (columnName == null) {
279 continue;
280 }
281 final String fieldName = template.getFieldName(columnName);
282 if (fieldName == null) {
283 continue;
284 }
285 final Field f = FieldUtils.getField(bean.getClass(), fieldName);
286 values[i] = template.objectToString(columnName, FieldUtils.getFieldValue(bean, f));
287 }
288 return Arrays.asList(values);
289 }
290
291 // ------------------------------------------------------------------------
292 // getter / setter
293
294 /**
295 * Java プログラム要素操作の簡素化ヘルパーを返します。
296 *
297 * @return Java プログラム要素操作の簡素化ヘルパー
298 * @since 2.1
299 */
300 public CsvColumnNameMappingBeanTemplate<T> getTemplate() {
301 return template;
302 }
303
304 }