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.ArrayList;
24 import java.util.Arrays;
25 import java.util.Collections;
26 import java.util.List;
27
28 import com.orangesignal.csv.CsvWriter;
29 import com.orangesignal.csv.bean.CsvBeanTemplate;
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 CsvBeanWriter<T> implements Closeable, Flushable {
39
40 /**
41 * 区切り文字形式出力ストリームを保持します。
42 */
43 private CsvWriter writer;
44
45 /**
46 * Java プログラム要素操作の簡素化ヘルパーを保持します。
47 */
48 private final CsvBeanTemplate<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 /**
66 * 新しい {@link CsvBeanWriter} のインスタンスを返します。
67 * このメソッドは利便性のために提供しています。
68 *
69 * @param writer 区切り文字形式出力ストリーム
70 * @param type Java プログラム要素の型
71 * @return 新しい {@link CsvBeanWriter} のインスタンス
72 * @throws IllegalArgumentException {@code writer} または {@code type} が {@code null} の場合。
73 */
74 public static <T> CsvBeanWriter<T> newInstance(final CsvWriter writer, final Class<T> type) {
75 return new CsvBeanWriter<T>(writer, type);
76 }
77
78 /**
79 * 新しい {@link CsvBeanWriter} のインスタンスを返します。
80 * このメソッドは利便性のために提供しています。
81 *
82 * @param writer 区切り文字形式出力ストリーム
83 * @param type Java プログラム要素の型
84 * @param header 区切り文字形式データの列見出し (ヘッダ) 行を出力するかどうか
85 * @return 新しい {@link CsvBeanWriter} のインスタンス
86 * @throws IllegalArgumentException {@code writer} または {@code type} が {@code null} の場合。
87 * @since 2.1
88 */
89 public static <T> CsvBeanWriter<T> newInstance(final CsvWriter writer, final Class<T> type, final boolean header) {
90 return new CsvBeanWriter<T>(writer, type, header);
91 }
92
93 /**
94 * 新しい {@link CsvBeanWriter} のインスタンスを返します。
95 * このメソッドは利便性のために提供しています。
96 *
97 * @param writer 区切り文字形式出力ストリーム
98 * @param template Java プログラム要素操作の簡素化ヘルパー
99 * @return 新しい {@link CsvBeanWriter} のインスタンス
100 * @throws IllegalArgumentException {@code writer} または {@code template} が {@code null} の場合。
101 */
102 public static <T> CsvBeanWriter<T> newInstance(final CsvWriter writer, final CsvBeanTemplate<T> template) {
103 return new CsvBeanWriter<T>(writer, template);
104 }
105
106 /**
107 * 新しい {@link CsvBeanWriter} のインスタンスを返します。
108 * このメソッドは利便性のために提供しています。
109 *
110 * @param writer 区切り文字形式出力ストリーム
111 * @param template Java プログラム要素操作の簡素化ヘルパー
112 * @param header 区切り文字形式データの列見出し (ヘッダ) 行を出力するかどうか
113 * @return 新しい {@link CsvBeanWriter} のインスタンス
114 * @throws IllegalArgumentException {@code writer} または {@code template} が {@code null} の場合。
115 * @since 2.1
116 */
117 public static <T> CsvBeanWriter<T> newInstance(final CsvWriter writer, final CsvBeanTemplate<T> template, final boolean header) {
118 return new CsvBeanWriter<T>(writer, template, header);
119 }
120
121 // ------------------------------------------------------------------------
122 // コンストラクタ
123
124 /**
125 * 指定された区切り文字形式出力ストリームと Java プログラム要素の型を使用して、このクラスを構築するコンストラクタです。
126 *
127 * @param writer 区切り文字形式出力ストリーム
128 * @param type Java プログラム要素の型
129 * @throws IllegalArgumentException {@code writer} または {@code type} が {@code null} の場合。
130 */
131 public CsvBeanWriter(final CsvWriter writer, final Class<T> type) {
132 this(writer, new CsvBeanTemplate<T>(type), true);
133 }
134
135 /**
136 * 指定された区切り文字形式出力ストリームと Java プログラム要素の型を使用して、このクラスを構築するコンストラクタです。
137 *
138 * @param writer 区切り文字形式出力ストリーム
139 * @param type Java プログラム要素の型
140 * @param header 区切り文字形式データの列見出し (ヘッダ) 行を出力するかどうか
141 * @throws IllegalArgumentException {@code writer} または {@code type} が {@code null} の場合。
142 * @since 2.1
143 */
144 public CsvBeanWriter(final CsvWriter writer, final Class<T> type, final boolean header) {
145 this(writer, new CsvBeanTemplate<T>(type), header);
146 }
147
148 /**
149 * 指定された区切り文字形式出力ストリームと Java プログラム要素操作の簡素化ヘルパーを使用して、このクラスを構築するコンストラクタです。
150 *
151 * @param writer 区切り文字形式出力ストリーム
152 * @param template Java プログラム要素操作の簡素化ヘルパー
153 * @throws IllegalArgumentException {@code writer} または {@code template} が {@code null} の場合。
154 */
155 public CsvBeanWriter(final CsvWriter writer, final CsvBeanTemplate<T> template) {
156 this(writer, template, true);
157 }
158
159 /**
160 * 指定された区切り文字形式出力ストリームと Java プログラム要素操作の簡素化ヘルパーを使用して、このクラスを構築するコンストラクタです。
161 *
162 * @param writer 区切り文字形式出力ストリーム
163 * @param template Java プログラム要素操作の簡素化ヘルパー
164 * @param header 区切り文字形式データの列見出し (ヘッダ) 行を出力するかどうか
165 * @throws IllegalArgumentException {@code writer} または {@code template} が {@code null} の場合。
166 * @since 2.1
167 */
168 public CsvBeanWriter(final CsvWriter writer, final CsvBeanTemplate<T> template, final boolean header) {
169 if (writer == null) {
170 throw new IllegalArgumentException("CsvWriter must not be null");
171 }
172 if (template == null) {
173 throw new IllegalArgumentException("CsvBeanTemplate must not be null");
174 }
175 this.writer = writer;
176 this.template = template;
177 this.header = header;
178 }
179
180 // ------------------------------------------------------------------------
181 // プライベート メソッド
182
183 /**
184 * Checks to make sure that the stream has not been closed
185 */
186 private void ensureOpen() throws IOException {
187 if (writer == null) {
188 throw new IOException("CsvWriter closed");
189 }
190 }
191
192 private void ensureHeader() throws IOException {
193 synchronized (this) {
194 if (columnNames == null) {
195 final List<String> names = new ArrayList<String>();
196 for (final Field f : template.getType().getDeclaredFields()) {
197 final String name = f.getName();
198 if (template.isTargetName(name)) {
199 names.add(name);
200 }
201 }
202 if (header) {
203 writer.writeValues(names);
204 }
205 columnNames = Collections.unmodifiableList(names);
206 }
207 }
208 }
209
210 // ------------------------------------------------------------------------
211 // オーバーライド メソッド
212
213 @Override
214 public void flush() throws IOException {
215 synchronized (this) {
216 ensureOpen();
217 writer.flush();
218 }
219 }
220
221 @Override
222 public void close() throws IOException {
223 synchronized (this) {
224 ensureOpen();
225 writer.close();
226 writer = null;
227 columnNames = null;
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 int columnCount = columnNames.size();
276 final String[] values = new String[columnCount];
277 for (int i = 0; i < columnCount; i++) {
278 final String name = columnNames.get(i);
279 if (name == null) {
280 continue;
281 }
282 final Field f = FieldUtils.getField(bean.getClass(), name);
283 values[i] = template.objectToString(name, FieldUtils.getFieldValue(bean, f));
284 }
285 return Arrays.asList(values);
286 }
287
288 // ------------------------------------------------------------------------
289 // getter / setter
290
291 /**
292 * Java プログラム要素操作の簡素化ヘルパーを返します。
293 *
294 * @return Java プログラム要素操作の簡素化ヘルパー
295 * @since 2.1
296 */
297 public CsvBeanTemplate<T> getTemplate() {
298 return template;
299 }
300
301 }