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