View Javadoc
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.IOException;
21  import java.lang.reflect.Field;
22  import java.util.Collections;
23  import java.util.List;
24  
25  import com.orangesignal.csv.CsvReader;
26  import com.orangesignal.csv.bean.CsvBeanTemplate;
27  import com.orangesignal.csv.bean.FieldUtils;
28  
29  /**
30   * Java プログラム要素で区切り文字形式データアクセスを行う区切り文字形式入力ストリームを提供します。
31   * 
32   * @param <T> Java プログラム要素の型
33   * @author Koji Sugisawa
34   * @since 1.4.0
35   */
36  public class CsvBeanReader<T> implements Closeable {
37  
38  	/**
39  	 * 区切り文字形式入力ストリームを保持します。
40  	 */
41  	private CsvReader reader;
42  
43  	/**
44  	 * Java プログラム要素操作の簡素化ヘルパーを保持します。
45  	 */
46  	private final CsvBeanTemplate<T> template;
47  
48  	/**
49  	 * 項目名のリストを保持します。
50  	 */
51  	private List<String> columnNames;
52  
53  	// ------------------------------------------------------------------------
54  	// 利便性のための静的メソッド
55  
56  	/**
57  	 * 新しい {@link CsvBeanReader} のインスタンスを返します。
58  	 * このメソッドは利便性のために提供しています。
59  	 * 
60  	 * @param reader 区切り文字形式入力ストリーム
61  	 * @param type Java プログラム要素の型
62  	 * @return 新しい {@link CsvBeanReader} のインスタンス
63  	 * @throws IllegalArgumentException {@code reader} または {@code type} が {@code null} の場合。
64  	 */
65  	public static <T> CsvBeanReader<T> newInstance(final CsvReader reader, final Class<T> type) {
66  		return new CsvBeanReader<T>(reader, type);
67  	}
68  
69  	/**
70  	 * 新しい {@link CsvBeanReader} のインスタンスを返します。
71  	 * このメソッドは利便性のために提供しています。
72  	 * 
73  	 * @param reader 区切り文字形式入力ストリーム
74  	 * @param template Java プログラム要素操作の簡素化ヘルパー
75  	 * @return 新しい {@link CsvBeanReader} のインスタンス
76  	 * @throws IllegalArgumentException {@code reader} または {@code template} が {@code null} の場合。
77  	 */
78  	public static <T> CsvBeanReader<T> newInstance(final CsvReader reader, final CsvBeanTemplate<T> template) {
79  		return new CsvBeanReader<T>(reader, template);
80  	}
81  
82  	// ------------------------------------------------------------------------
83  	// コンストラクタ
84  
85  	/**
86  	 * 指定された区切り文字形式入力ストリームと Java プログラム要素の型を使用して、このクラスを構築するコンストラクタです。
87  	 * 
88  	 * @param reader 区切り文字形式入力ストリーム
89  	 * @param type Java プログラム要素の型
90  	 * @throws IllegalArgumentException {@code reader} または {@code type} が {@code null} の場合。
91  	 */
92  	public CsvBeanReader(final CsvReader reader, final Class<T> type) {
93  		this(reader, new CsvBeanTemplate<T>(type));
94  	}
95  
96  	/**
97  	 * 指定された区切り文字形式入力ストリームと Java プログラム要素操作の簡素化ヘルパーを使用して、このクラスを構築するコンストラクタです。
98  	 * 
99  	 * @param reader 区切り文字形式入力ストリーム
100 	 * @param template Java プログラム要素操作の簡素化ヘルパー
101 	 * @throws IllegalArgumentException {@code reader} または {@code template} が {@code null} の場合。
102 	 */
103 	public CsvBeanReader(final CsvReader reader, final CsvBeanTemplate<T> template) {
104 		if (reader == null) {
105 			throw new IllegalArgumentException("CsvReader must not be null");
106 		}
107 		if (template == null) {
108 			throw new IllegalArgumentException("CsvBeanTemplate must not be null");
109 		}
110 		this.reader = reader;
111 		this.template = template;
112 	}
113 
114 	// ------------------------------------------------------------------------
115 	// プライベート メソッド
116 
117 	/**
118 	 * 区切り文字形式入力ストリームが閉じられていないことを確認するためにチェックします。
119 	 * 
120 	 * @throws IOException 区切り文字形式入力ストリームが閉じられている場合
121 	 */
122 	private void ensureOpen() throws IOException {
123 		if (reader == null) {
124 			throw new IOException("CsvReader closed");
125 		}
126 	}
127 
128 	private void ensureHeader() throws IOException {
129 		synchronized (this) {
130 			if (columnNames == null) {
131 				columnNames = Collections.unmodifiableList(reader.readValues());
132 				if (columnNames == null) {
133 					// ヘッダがない場合は例外をスローします。
134 					throw new IOException("No header is available");
135 				}
136 			}
137 		}
138 	}
139 
140 	// ------------------------------------------------------------------------
141 	// オーバーライド メソッド
142 
143 	@Override
144 	public void close() throws IOException {
145 		synchronized (this) {
146 			ensureOpen();
147 			reader.close();
148 			reader = null;
149 			columnNames = null;
150 		}
151 	}
152 
153 	// ------------------------------------------------------------------------
154 	// パブリック メソッド
155 
156 	/**
157 	 * 項目名のリストを返します。
158 	 * 
159 	 * @return 項目名のリスト
160 	 * @throws IOException 入出力エラーが発生した場合
161 	 */
162 	public List<String> getHeader() throws IOException {
163 		synchronized (this) {
164 			ensureOpen();
165 			ensureHeader();
166 			return columnNames;
167 		}
168 	}
169 
170 	/**
171 	 * 論理行を読込み Java プログラム要素として返します。
172 	 *
173 	 * @return Java プログラム要素。ストリームの終わりに達した場合は {@code null}
174 	 * @throws IOException 入出力エラーが発生した場合
175 	 */
176 	public T read() throws IOException {
177 		synchronized (this) {
178 			ensureOpen();
179 			ensureHeader();
180 			final List<String> values = nextValues();
181 			if (values == null) {
182 				return null;
183 			}
184 			return convert(values);
185 		}
186 	}
187 
188 	/**
189 	 * 論理行を読込み CSV トークンの値をリストとして返します。
190 	 * 
191 	 * @return CSV トークンの値をリスト。ストリームの終わりに達している場合は {@code null}
192 	 * @throws IOException 入出力エラーが発生した場合
193 	 */
194 	public List<String> readValues() throws IOException {
195 		synchronized (this) {
196 			ensureOpen();
197 			ensureHeader();
198 			return nextValues();
199 		}
200 	}
201 
202 	/**
203 	 * 指定された CSV トークンの値をリストを Java プログラム要素へ変換して返します。
204 	 * 
205 	 * @param values CSV トークンの値をリスト
206 	 * @return 変換された Java プログラム要素
207 	 * @throws IOException 入出力エラーが発生した場合
208 	 */
209 	public T toBean(final List<String> values) throws IOException {
210 		synchronized (this) {
211 			ensureOpen();
212 			ensureHeader();
213 			return convert(values);
214 		}
215 	}
216 
217 	private List<String> nextValues() throws IOException {
218 		List<String> values;
219 		while ((values = reader.readValues()) != null) {
220 			if (!template.isAccept(columnNames, values)) {
221 				return values;
222 			}
223 		}
224 		return null;
225 	}
226 
227 	private T convert(final List<String> values) throws IOException {
228 		final T bean = template.createBean();
229 		final int len = Math.min(columnNames.size(), values.size());
230 		for (int pos = 0; pos < len; pos++) {
231 			final String name = columnNames.get(pos);
232 			if (!template.isTargetName(name)) {
233 				continue;
234 			}
235 			final Field f = FieldUtils.getField(template.getType(), name);
236 			final Object o = template.stringToObject(f, values.get(pos));
237 			if (o != null) {
238 				FieldUtils.setFieldValue(bean, f, o);
239 			}
240 		}
241 		return bean;
242 	}
243 
244 	// ------------------------------------------------------------------------
245 	// getter / setter
246 
247 	/**
248 	 * Java プログラム要素操作の簡素化ヘルパーを返します。
249 	 * 
250 	 * @return Java プログラム要素操作の簡素化ヘルパー
251 	 * @since 2.1
252 	 */
253 	public CsvBeanTemplate<T> getTemplate() {
254 		return template;
255 	}
256 
257 }