EMMA Coverage Report (generated Thu Jan 24 13:37:04 CST 2013)
[all classes][org.springframework.batch.item.file]

COVERAGE SUMMARY FOR SOURCE FILE [FlatFileItemReader.java]

nameclass, %method, %block, %line, %
FlatFileItemReader.java100% (1/1)79%  (15/19)79%  (336/424)80%  (80/100)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class FlatFileItemReader100% (1/1)79%  (15/19)79%  (336/424)80%  (80/100)
setBufferedReaderFactory (BufferedReaderFactory): void 0%   (0/1)0%   (0/4)0%   (0/2)
setComments (String []): void 0%   (0/1)0%   (0/14)0%   (0/3)
setEncoding (String): void 0%   (0/1)0%   (0/4)0%   (0/2)
setSkippedLinesCallback (LineCallbackHandler): void 0%   (0/1)0%   (0/4)0%   (0/2)
readLine (): String 100% (1/1)41%  (30/73)56%  (10/18)
doOpen (): void 100% (1/1)84%  (91/108)90%  (18/20)
isComment (String): boolean 100% (1/1)92%  (23/25)75%  (3/4)
<static initializer> 100% (1/1)100% (7/7)100% (2/2)
FlatFileItemReader (): void 100% (1/1)100% (40/40)100% (11/11)
afterPropertiesSet (): void 100% (1/1)100% (5/5)100% (2/2)
applyRecordSeparatorPolicy (String): String 100% (1/1)100% (50/50)100% (9/9)
doClose (): void 100% (1/1)100% (10/10)100% (4/4)
doRead (): Object 100% (1/1)100% (49/49)100% (8/8)
jumpToItem (int): void 100% (1/1)100% (11/11)100% (3/3)
setLineMapper (LineMapper): void 100% (1/1)100% (4/4)100% (2/2)
setLinesToSkip (int): void 100% (1/1)100% (4/4)100% (2/2)
setRecordSeparatorPolicy (RecordSeparatorPolicy): void 100% (1/1)100% (4/4)100% (2/2)
setResource (Resource): void 100% (1/1)100% (4/4)100% (2/2)
setStrict (boolean): void 100% (1/1)100% (4/4)100% (2/2)

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 
17package org.springframework.batch.item.file;
18 
19import java.io.BufferedReader;
20import java.io.IOException;
21import java.nio.charset.Charset;
22 
23import org.apache.commons.logging.Log;
24import org.apache.commons.logging.LogFactory;
25import org.springframework.batch.item.ItemReader;
26import org.springframework.batch.item.ReaderNotOpenException;
27import org.springframework.batch.item.file.separator.RecordSeparatorPolicy;
28import org.springframework.batch.item.file.separator.SimpleRecordSeparatorPolicy;
29import org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader;
30import org.springframework.beans.factory.InitializingBean;
31import org.springframework.core.io.Resource;
32import org.springframework.util.Assert;
33import org.springframework.util.ClassUtils;
34import org.springframework.util.StringUtils;
35 
36/**
37 * Restartable {@link ItemReader} that reads lines from input {@link #setResource(Resource)}. Line is defined by the
38 * {@link #setRecordSeparatorPolicy(RecordSeparatorPolicy)} and mapped to item using {@link #setLineMapper(LineMapper)}.
39 * If an exception is thrown during line mapping it is rethrown as {@link FlatFileParseException} adding information
40 * about the problematic line and its line number.
41 * 
42 * @author Robert Kasanicky
43 */
44public class FlatFileItemReader<T> extends AbstractItemCountingItemStreamItemReader<T> implements
45                ResourceAwareItemReaderItemStream<T>, InitializingBean {
46 
47        private static final Log logger = LogFactory.getLog(FlatFileItemReader.class);
48 
49        // default encoding for input files
50        public static final String DEFAULT_CHARSET = Charset.defaultCharset().name();
51 
52        private RecordSeparatorPolicy recordSeparatorPolicy = new SimpleRecordSeparatorPolicy();
53 
54        private Resource resource;
55 
56        private BufferedReader reader;
57 
58        private int lineCount = 0;
59 
60        private String[] comments = new String[] { "#" };
61 
62        private boolean noInput = false;
63 
64        private String encoding = DEFAULT_CHARSET;
65 
66        private LineMapper<T> lineMapper;
67 
68        private int linesToSkip = 0;
69 
70        private LineCallbackHandler skippedLinesCallback;
71 
72        private boolean strict = true;
73 
74        private BufferedReaderFactory bufferedReaderFactory = new DefaultBufferedReaderFactory();
75 
76        public FlatFileItemReader() {
77                setName(ClassUtils.getShortName(FlatFileItemReader.class));
78        }
79 
80        /**
81         * In strict mode the reader will throw an exception on
82         * {@link #open(org.springframework.batch.item.ExecutionContext)} if the input resource does not exist.
83         * @param strict <code>true</code> by default
84         */
85        public void setStrict(boolean strict) {
86                this.strict = strict;
87        }
88 
89        /**
90         * @param skippedLinesCallback will be called for each one of the initial skipped lines before any items are read.
91         */
92        public void setSkippedLinesCallback(LineCallbackHandler skippedLinesCallback) {
93                this.skippedLinesCallback = skippedLinesCallback;
94        }
95 
96        /**
97         * Public setter for the number of lines to skip at the start of a file. Can be used if the file contains a header
98         * without useful (column name) information, and without a comment delimiter at the beginning of the lines.
99         * 
100         * @param linesToSkip the number of lines to skip
101         */
102        public void setLinesToSkip(int linesToSkip) {
103                this.linesToSkip = linesToSkip;
104        }
105 
106        /**
107         * Setter for line mapper. This property is required to be set.
108         * @param lineMapper maps line to item
109         */
110        public void setLineMapper(LineMapper<T> lineMapper) {
111                this.lineMapper = lineMapper;
112        }
113 
114        /**
115         * Setter for the encoding for this input source. Default value is {@link #DEFAULT_CHARSET}.
116         * 
117         * @param encoding a properties object which possibly contains the encoding for this input file;
118         */
119        public void setEncoding(String encoding) {
120                this.encoding = encoding;
121        }
122 
123        /**
124         * Factory for the {@link BufferedReader} that will be used to extract lines from the file. The default is fine for
125         * plain text files, but this is a useful strategy for binary files where the standard BufferedReaader from java.io
126         * is limiting.
127         * 
128         * @param bufferedReaderFactory the bufferedReaderFactory to set
129         */
130        public void setBufferedReaderFactory(BufferedReaderFactory bufferedReaderFactory) {
131                this.bufferedReaderFactory = bufferedReaderFactory;
132        }
133 
134        /**
135         * Setter for comment prefixes. Can be used to ignore header lines as well by using e.g. the first couple of column
136         * names as a prefix.
137         * 
138         * @param comments an array of comment line prefixes.
139         */
140        public void setComments(String[] comments) {
141                this.comments = new String[comments.length];
142                System.arraycopy(comments, 0, this.comments, 0, comments.length);
143        }
144 
145        /**
146         * Public setter for the input resource.
147         */
148        public void setResource(Resource resource) {
149                this.resource = resource;
150        }
151 
152        /**
153         * Public setter for the recordSeparatorPolicy. Used to determine where the line endings are and do things like
154         * continue over a line ending if inside a quoted string.
155         * 
156         * @param recordSeparatorPolicy the recordSeparatorPolicy to set
157         */
158        public void setRecordSeparatorPolicy(RecordSeparatorPolicy recordSeparatorPolicy) {
159                this.recordSeparatorPolicy = recordSeparatorPolicy;
160        }
161 
162        /**
163         * @return string corresponding to logical record according to
164         * {@link #setRecordSeparatorPolicy(RecordSeparatorPolicy)} (might span multiple lines in file).
165         */
166        @Override
167        protected T doRead() throws Exception {
168                if (noInput) {
169                        return null;
170                }
171 
172                String line = readLine();
173 
174                if (line == null) {
175                        return null;
176                }
177                else {
178                        try {
179                                return lineMapper.mapLine(line, lineCount);
180                        }
181                        catch (Exception ex) {
182                                throw new FlatFileParseException("Parsing error at line: " + lineCount + " in resource=["
183                                                + resource.getDescription() + "], input=[" + line + "]", ex, line, lineCount);
184                        }
185                }
186        }
187 
188        /**
189         * @return next line (skip comments).getCurrentResource
190         */
191        private String readLine() {
192 
193                if (reader == null) {
194                        throw new ReaderNotOpenException("Reader must be open before it can be read.");
195                }
196 
197                String line = null;
198 
199                try {
200                        line = this.reader.readLine();
201                        if (line == null) {
202                                return null;
203                        }
204                        lineCount++;
205                        while (isComment(line)) {
206                                line = reader.readLine();
207                                if (line == null) {
208                                        return null;
209                                }
210                                lineCount++;
211                        }
212 
213                        line = applyRecordSeparatorPolicy(line);
214                }
215                catch (IOException e) {
216                        // Prevent IOException from recurring indefinitely
217                        // if client keeps catching and re-calling
218                        noInput = true;
219                        throw new NonTransientFlatFileException("Unable to read from resource: [" + resource + "]", e, line,
220                                        lineCount);
221                }
222                return line;
223        }
224 
225        private boolean isComment(String line) {
226                for (String prefix : comments) {
227                        if (line.startsWith(prefix)) {
228                                return true;
229                        }
230                }
231                return false;
232        }
233 
234        @Override
235        protected void doClose() throws Exception {
236                lineCount = 0;
237                if (reader != null) {
238                        reader.close();
239                }
240        }
241 
242        @Override
243        protected void doOpen() throws Exception {
244                Assert.notNull(resource, "Input resource must be set");
245                Assert.notNull(recordSeparatorPolicy, "RecordSeparatorPolicy must be set");
246 
247                noInput = true;
248                if (!resource.exists()) {
249                        if (strict) {
250                                throw new IllegalStateException("Input resource must exist (reader is in 'strict' mode): " + resource);
251                        }
252                        logger.warn("Input resource does not exist " + resource.getDescription());
253                        return;
254                }
255 
256                if (!resource.isReadable()) {
257                        if (strict) {
258                                throw new IllegalStateException("Input resource must be readable (reader is in 'strict' mode): "
259                                                + resource);
260                        }
261                        logger.warn("Input resource is not readable " + resource.getDescription());
262                        return;
263                }
264 
265                reader = bufferedReaderFactory.create(resource, encoding);
266                for (int i = 0; i < linesToSkip; i++) {
267                        String line = readLine();
268                        if (skippedLinesCallback != null) {
269                                skippedLinesCallback.handleLine(line);
270                        }
271                }
272                noInput = false;
273        }
274 
275        public void afterPropertiesSet() throws Exception {
276                Assert.notNull(lineMapper, "LineMapper is required");
277        }
278 
279        @Override
280        protected void jumpToItem(int itemIndex) throws Exception {
281                for (int i = 0; i < itemIndex; i++) {
282                        readLine();
283                }
284        }
285 
286        private String applyRecordSeparatorPolicy(String line) throws IOException {
287 
288                String record = line;
289                while (line != null && !recordSeparatorPolicy.isEndOfRecord(record)) {
290                        line = this.reader.readLine();
291                        if (line == null) {
292                                if (StringUtils.hasText(record)) {
293                                        // A record was partially complete since it hasn't ended but
294                                        // the line is null
295                                        throw new FlatFileParseException("Unexpected end of file before record complete", record, lineCount);
296                                }
297                                else {
298                                        // Record has no text but it might still be post processed
299                                        // to something (skipping preProcess since that was already
300                                        // done)
301                                        break;
302                                }
303                        }
304                        else {
305                                lineCount++;
306                        }
307                        record = recordSeparatorPolicy.preProcess(record) + line;
308                }
309 
310                return recordSeparatorPolicy.postProcess(record);
311 
312        }
313 
314}

[all classes][org.springframework.batch.item.file]
EMMA 2.0.5312 (C) Vladimir Roubtsov