1 | /* |
2 | * Copyright 2006-2007 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 org.springframework.batch.item.file.mapping; |
18 | |
19 | import java.math.BigDecimal; |
20 | import java.text.ParseException; |
21 | import java.text.SimpleDateFormat; |
22 | import java.util.Arrays; |
23 | import java.util.Date; |
24 | import java.util.List; |
25 | import java.util.Properties; |
26 | |
27 | import org.springframework.util.Assert; |
28 | import org.springframework.util.StringUtils; |
29 | |
30 | /** |
31 | * Default implementation of {@link FieldSet} using Java using Java primitive |
32 | * and standard types and utilities. Strings are trimmed before parsing by |
33 | * default, and so are plain String values. |
34 | * |
35 | * @author Rob Harrop |
36 | * @author Dave Syer |
37 | */ |
38 | public class DefaultFieldSet implements FieldSet { |
39 | |
40 | private final static String DEFAULT_DATE_PATTERN = "yyyy-MM-dd"; |
41 | |
42 | /** |
43 | * The fields wrapped by this '<code>FieldSet</code>' instance. |
44 | */ |
45 | private String[] tokens; |
46 | |
47 | private List names; |
48 | |
49 | public DefaultFieldSet(String[] tokens) { |
50 | this.tokens = tokens == null ? null : (String[]) tokens.clone(); |
51 | } |
52 | |
53 | public DefaultFieldSet(String[] tokens, String[] names) { |
54 | Assert.notNull(tokens); |
55 | Assert.notNull(names); |
56 | if (tokens.length != names.length) { |
57 | throw new IllegalArgumentException("Field names must be same length as values: names=" |
58 | + Arrays.asList(names) + ", values=" + Arrays.asList(tokens)); |
59 | } |
60 | this.tokens = (String[]) tokens.clone(); |
61 | this.names = Arrays.asList(names); |
62 | } |
63 | |
64 | /* |
65 | * (non-Javadoc) |
66 | * @see org.springframework.batch.io.file.mapping.IFieldSet#getNames() |
67 | */ |
68 | public String[] getNames() { |
69 | if (names == null) { |
70 | throw new IllegalStateException("Field names are not known"); |
71 | } |
72 | return (String[]) names.toArray(); |
73 | } |
74 | |
75 | /* |
76 | * (non-Javadoc) |
77 | * @see org.springframework.batch.io.file.mapping.IFieldSet#getValues() |
78 | */ |
79 | public String[] getValues() { |
80 | return tokens; |
81 | } |
82 | |
83 | /* |
84 | * (non-Javadoc) |
85 | * @see org.springframework.batch.io.file.mapping.IFieldSet#readString(int) |
86 | */ |
87 | public String readString(int index) { |
88 | return readAndTrim(index); |
89 | } |
90 | |
91 | /* |
92 | * (non-Javadoc) |
93 | * @see org.springframework.batch.io.file.mapping.IFieldSet#readString(java.lang.String) |
94 | */ |
95 | public String readString(String name) { |
96 | return readString(indexOf(name)); |
97 | } |
98 | |
99 | /* |
100 | * (non-Javadoc) |
101 | * @see org.springframework.batch.io.file.mapping.IFieldSet#readRawString(int) |
102 | */ |
103 | public String readRawString(int index) { |
104 | return tokens[index]; |
105 | } |
106 | |
107 | /* |
108 | * (non-Javadoc) |
109 | * @see org.springframework.batch.io.file.mapping.IFieldSet#readRawString(java.lang.String) |
110 | */ |
111 | public String readRawString(String name) { |
112 | return readRawString(indexOf(name)); |
113 | } |
114 | |
115 | /* |
116 | * (non-Javadoc) |
117 | * @see org.springframework.batch.io.file.mapping.IFieldSet#readBoolean(int) |
118 | */ |
119 | public boolean readBoolean(int index) { |
120 | return readBoolean(index, "true"); |
121 | } |
122 | |
123 | /* |
124 | * (non-Javadoc) |
125 | * @see org.springframework.batch.io.file.mapping.IFieldSet#readBoolean(java.lang.String) |
126 | */ |
127 | public boolean readBoolean(String name) { |
128 | return readBoolean(indexOf(name)); |
129 | } |
130 | |
131 | /* |
132 | * (non-Javadoc) |
133 | * @see org.springframework.batch.io.file.mapping.IFieldSet#readBoolean(int, |
134 | * java.lang.String) |
135 | */ |
136 | public boolean readBoolean(int index, String trueValue) { |
137 | Assert.notNull(trueValue, "'trueValue' cannot be null."); |
138 | |
139 | String value = readAndTrim(index); |
140 | |
141 | return trueValue.equals(value) ? true : false; |
142 | } |
143 | |
144 | /* |
145 | * (non-Javadoc) |
146 | * @see org.springframework.batch.io.file.mapping.IFieldSet#readBoolean(java.lang.String, |
147 | * java.lang.String) |
148 | */ |
149 | public boolean readBoolean(String name, String trueValue) { |
150 | return readBoolean(indexOf(name), trueValue); |
151 | } |
152 | |
153 | /* |
154 | * (non-Javadoc) |
155 | * @see org.springframework.batch.io.file.mapping.IFieldSet#readChar(int) |
156 | */ |
157 | public char readChar(int index) { |
158 | String value = readAndTrim(index); |
159 | |
160 | Assert.isTrue(value.length() == 1, "Cannot convert field value '" + value + "' to char."); |
161 | |
162 | return value.charAt(0); |
163 | } |
164 | |
165 | /* |
166 | * (non-Javadoc) |
167 | * @see org.springframework.batch.io.file.mapping.IFieldSet#readChar(java.lang.String) |
168 | */ |
169 | public char readChar(String name) { |
170 | return readChar(indexOf(name)); |
171 | } |
172 | |
173 | /* |
174 | * (non-Javadoc) |
175 | * @see org.springframework.batch.io.file.mapping.IFieldSet#readByte(int) |
176 | */ |
177 | public byte readByte(int index) { |
178 | return Byte.parseByte(readAndTrim(index)); |
179 | } |
180 | |
181 | /* |
182 | * (non-Javadoc) |
183 | * @see org.springframework.batch.io.file.mapping.IFieldSet#readByte(java.lang.String) |
184 | */ |
185 | public byte readByte(String name) { |
186 | return readByte(indexOf(name)); |
187 | } |
188 | |
189 | /* |
190 | * (non-Javadoc) |
191 | * @see org.springframework.batch.io.file.mapping.IFieldSet#readShort(int) |
192 | */ |
193 | public short readShort(int index) { |
194 | return Short.parseShort(readAndTrim(index)); |
195 | } |
196 | |
197 | /* |
198 | * (non-Javadoc) |
199 | * @see org.springframework.batch.io.file.mapping.IFieldSet#readShort(java.lang.String) |
200 | */ |
201 | public short readShort(String name) { |
202 | return readShort(indexOf(name)); |
203 | } |
204 | |
205 | /* |
206 | * (non-Javadoc) |
207 | * @see org.springframework.batch.io.file.mapping.IFieldSet#readInt(int) |
208 | */ |
209 | public int readInt(int index) { |
210 | return Integer.parseInt(readAndTrim(index)); |
211 | } |
212 | |
213 | /* |
214 | * (non-Javadoc) |
215 | * @see org.springframework.batch.io.file.mapping.IFieldSet#readInt(java.lang.String) |
216 | */ |
217 | public int readInt(String name) { |
218 | return readInt(indexOf(name)); |
219 | } |
220 | |
221 | /* |
222 | * (non-Javadoc) |
223 | * @see org.springframework.batch.io.file.mapping.IFieldSet#readInt(int, |
224 | * int) |
225 | */ |
226 | public int readInt(int index, int defaultValue) { |
227 | String value = readAndTrim(index); |
228 | |
229 | return StringUtils.hasLength(value) ? Integer.parseInt(value) : defaultValue; |
230 | } |
231 | |
232 | /* |
233 | * (non-Javadoc) |
234 | * @see org.springframework.batch.io.file.mapping.IFieldSet#readInt(java.lang.String, |
235 | * int) |
236 | */ |
237 | public int readInt(String name, int defaultValue) { |
238 | return readInt(indexOf(name), defaultValue); |
239 | } |
240 | |
241 | /* |
242 | * (non-Javadoc) |
243 | * @see org.springframework.batch.io.file.mapping.IFieldSet#readLong(int) |
244 | */ |
245 | public long readLong(int index) { |
246 | return Long.parseLong(readAndTrim(index)); |
247 | } |
248 | |
249 | /* |
250 | * (non-Javadoc) |
251 | * @see org.springframework.batch.io.file.mapping.IFieldSet#readLong(java.lang.String) |
252 | */ |
253 | public long readLong(String name) { |
254 | return readLong(indexOf(name)); |
255 | } |
256 | |
257 | /* |
258 | * (non-Javadoc) |
259 | * @see org.springframework.batch.io.file.mapping.IFieldSet#readLong(int, |
260 | * long) |
261 | */ |
262 | public long readLong(int index, long defaultValue) { |
263 | String value = readAndTrim(index); |
264 | |
265 | return StringUtils.hasLength(value) ? Long.parseLong(value) : defaultValue; |
266 | } |
267 | |
268 | /* |
269 | * (non-Javadoc) |
270 | * @see org.springframework.batch.io.file.mapping.IFieldSet#readLong(java.lang.String, |
271 | * long) |
272 | */ |
273 | public long readLong(String name, long defaultValue) { |
274 | return readLong(indexOf(name), defaultValue); |
275 | } |
276 | |
277 | /* |
278 | * (non-Javadoc) |
279 | * @see org.springframework.batch.io.file.mapping.IFieldSet#readFloat(int) |
280 | */ |
281 | public float readFloat(int index) { |
282 | return Float.parseFloat(readAndTrim(index)); |
283 | } |
284 | |
285 | /* |
286 | * (non-Javadoc) |
287 | * @see org.springframework.batch.io.file.mapping.IFieldSet#readFloat(java.lang.String) |
288 | */ |
289 | public float readFloat(String name) { |
290 | return readFloat(indexOf(name)); |
291 | } |
292 | |
293 | /* |
294 | * (non-Javadoc) |
295 | * @see org.springframework.batch.io.file.mapping.IFieldSet#readDouble(int) |
296 | */ |
297 | public double readDouble(int index) { |
298 | return Double.parseDouble(readAndTrim(index)); |
299 | } |
300 | |
301 | /* |
302 | * (non-Javadoc) |
303 | * @see org.springframework.batch.io.file.mapping.IFieldSet#readDouble(java.lang.String) |
304 | */ |
305 | public double readDouble(String name) { |
306 | return readDouble(indexOf(name)); |
307 | } |
308 | |
309 | /* |
310 | * (non-Javadoc) |
311 | * @see org.springframework.batch.io.file.mapping.IFieldSet#readBigDecimal(int) |
312 | */ |
313 | public BigDecimal readBigDecimal(int index) { |
314 | return readBigDecimal(index, null); |
315 | } |
316 | |
317 | /* |
318 | * (non-Javadoc) |
319 | * @see org.springframework.batch.io.file.mapping.IFieldSet#readBigDecimal(java.lang.String) |
320 | */ |
321 | public BigDecimal readBigDecimal(String name) { |
322 | return readBigDecimal(name, null); |
323 | } |
324 | |
325 | /* |
326 | * (non-Javadoc) |
327 | * @see org.springframework.batch.io.file.mapping.IFieldSet#readBigDecimal(int, |
328 | * java.math.BigDecimal) |
329 | */ |
330 | public BigDecimal readBigDecimal(int index, BigDecimal defaultValue) { |
331 | String candidate = readAndTrim(index); |
332 | |
333 | try { |
334 | return (StringUtils.hasText(candidate)) ? new BigDecimal(candidate) : defaultValue; |
335 | } |
336 | catch (NumberFormatException e) { |
337 | throw new IllegalArgumentException("Unparseable number: " + candidate); |
338 | } |
339 | } |
340 | |
341 | /* |
342 | * (non-Javadoc) |
343 | * @see org.springframework.batch.io.file.mapping.IFieldSet#readBigDecimal(java.lang.String, |
344 | * java.math.BigDecimal) |
345 | */ |
346 | public BigDecimal readBigDecimal(String name, BigDecimal defaultValue) { |
347 | try { |
348 | return readBigDecimal(indexOf(name), defaultValue); |
349 | } |
350 | catch (IllegalArgumentException e) { |
351 | throw new IllegalArgumentException(e.getMessage() + ", name: [" + name + "]"); |
352 | } |
353 | } |
354 | |
355 | /* |
356 | * (non-Javadoc) |
357 | * @see org.springframework.batch.io.file.mapping.IFieldSet#readDate(int) |
358 | */ |
359 | public Date readDate(int index) { |
360 | return readDate(index, DEFAULT_DATE_PATTERN); |
361 | } |
362 | |
363 | /* |
364 | * (non-Javadoc) |
365 | * @see org.springframework.batch.io.file.mapping.IFieldSet#readDate(java.lang.String) |
366 | */ |
367 | public Date readDate(String name) { |
368 | return readDate(name, DEFAULT_DATE_PATTERN); |
369 | } |
370 | |
371 | /* |
372 | * (non-Javadoc) |
373 | * @see org.springframework.batch.io.file.mapping.IFieldSet#readDate(int, |
374 | * java.lang.String) |
375 | */ |
376 | public Date readDate(int index, String pattern) { |
377 | SimpleDateFormat sdf = new SimpleDateFormat(pattern); |
378 | sdf.setLenient(false); |
379 | Date date; |
380 | String value = readAndTrim(index); |
381 | try { |
382 | date = sdf.parse(value); |
383 | } |
384 | catch (ParseException e) { |
385 | throw new IllegalArgumentException(e.getMessage() + ", pattern: [" + pattern + "]"); |
386 | } |
387 | return date; |
388 | } |
389 | |
390 | /* |
391 | * (non-Javadoc) |
392 | * @see org.springframework.batch.io.file.mapping.IFieldSet#readDate(java.lang.String, |
393 | * java.lang.String) |
394 | */ |
395 | public Date readDate(String name, String pattern) { |
396 | try { |
397 | return readDate(indexOf(name), pattern); |
398 | } |
399 | catch (IllegalArgumentException e) { |
400 | throw new IllegalArgumentException(e.getMessage() + ", name: [" + name + "]"); |
401 | } |
402 | } |
403 | |
404 | /* |
405 | * (non-Javadoc) |
406 | * @see org.springframework.batch.io.file.mapping.IFieldSet#getFieldCount() |
407 | */ |
408 | public int getFieldCount() { |
409 | return tokens.length; |
410 | } |
411 | |
412 | /** |
413 | * Read and trim the {@link String} value at '<code>index</code>'. |
414 | * |
415 | * @throws NullPointerException if the field value is <code>null</code>. |
416 | */ |
417 | protected String readAndTrim(int index) { |
418 | String value = tokens[index]; |
419 | |
420 | if (value != null) { |
421 | return value.trim(); |
422 | } |
423 | else { |
424 | return null; |
425 | } |
426 | } |
427 | |
428 | /** |
429 | * Read and trim the {@link String} value from column with given '<code>name</code>. |
430 | * |
431 | * @throws IllegalArgumentException if a column with given name is not |
432 | * defined. |
433 | */ |
434 | protected int indexOf(String name) { |
435 | if (names == null) { |
436 | throw new IllegalArgumentException("Cannot access columns by name without meta data"); |
437 | } |
438 | int index = names.indexOf(name); |
439 | if (index >= 0) { |
440 | return index; |
441 | } |
442 | throw new IllegalArgumentException("Cannot access column [" + name + "] from " + names); |
443 | } |
444 | |
445 | public String toString() { |
446 | if (names != null) { |
447 | return getProperties().toString(); |
448 | } |
449 | |
450 | return tokens == null ? "" : Arrays.asList(tokens).toString(); |
451 | } |
452 | |
453 | /** |
454 | * @see java.lang.Object#equals(java.lang.Object) |
455 | */ |
456 | public boolean equals(Object object) { |
457 | if (object instanceof DefaultFieldSet) { |
458 | DefaultFieldSet fs = (DefaultFieldSet) object; |
459 | |
460 | if (this.tokens == null) { |
461 | return fs.tokens == null; |
462 | } |
463 | else { |
464 | return Arrays.equals(this.tokens, fs.tokens); |
465 | } |
466 | } |
467 | |
468 | return false; |
469 | } |
470 | |
471 | public int hashCode() { |
472 | // this algorithm was taken from java 1.5 jdk Arrays.hashCode(Object[]) |
473 | if (tokens == null) { |
474 | return 0; |
475 | } |
476 | |
477 | int result = 1; |
478 | |
479 | for (int i = 0; i < tokens.length; i++) { |
480 | result = 31 * result + (tokens[i] == null ? 0 : tokens[i].hashCode()); |
481 | } |
482 | |
483 | return result; |
484 | } |
485 | |
486 | /* |
487 | * (non-Javadoc) |
488 | * @see org.springframework.batch.io.file.mapping.IFieldSet#getProperties() |
489 | */ |
490 | public Properties getProperties() { |
491 | if (names == null) { |
492 | throw new IllegalStateException("Cannot create properties without meta data"); |
493 | } |
494 | Properties props = new Properties(); |
495 | for (int i = 0; i < tokens.length; i++) { |
496 | String value = readAndTrim(i); |
497 | if (value != null) { |
498 | props.setProperty((String) names.get(i), value); |
499 | } |
500 | } |
501 | return props; |
502 | } |
503 | |
504 | } |