EMMA Coverage Report (generated Thu May 22 12:08:10 CDT 2014)
[all classes][org.springframework.batch.item.file.transform]

COVERAGE SUMMARY FOR SOURCE FILE [DelimitedLineTokenizer.java]

nameclass, %method, %block, %line, %
DelimitedLineTokenizer.java100% (1/1)90%  (9/10)99%  (321/325)97%  (67/69)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class DelimitedLineTokenizer100% (1/1)90%  (9/10)99%  (321/325)97%  (67/69)
setDelimiter (String): void 0%   (0/1)0%   (0/4)0%   (0/2)
DelimitedLineTokenizer (): void 100% (1/1)100% (4/4)100% (2/2)
DelimitedLineTokenizer (String): void 100% (1/1)100% (25/25)100% (7/7)
doTokenize (String): List 100% (1/1)100% (143/143)100% (31/31)
isDelimiter (char [], int, String, int): boolean 100% (1/1)100% (36/36)100% (7/7)
isQuoteCharacter (char): boolean 100% (1/1)100% (8/8)100% (1/1)
isQuoted (String): boolean 100% (1/1)100% (14/14)100% (3/3)
maybeStripQuotes (String): String 100% (1/1)100% (49/49)100% (9/9)
setIncludedFields (int []): void 100% (1/1)100% (28/28)100% (4/4)
setQuoteCharacter (char): void 100% (1/1)100% (14/14)100% (3/3)

1/*
2 * Copyright 2006-2014 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.transform;
18 
19import java.util.ArrayList;
20import java.util.Collection;
21import java.util.HashSet;
22import java.util.List;
23 
24import org.springframework.util.Assert;
25import org.springframework.util.StringUtils;
26 
27/**
28 * A {@link LineTokenizer} implementation that splits the input String on a
29 * configurable delimiter. This implementation also supports the use of an
30 * escape character to escape delimiters and line endings.
31 *
32 * @author Rob Harrop
33 * @author Dave Syer
34 * @author Michael Minella
35 */
36public class DelimitedLineTokenizer extends AbstractLineTokenizer {
37        /**
38         * Convenient constant for the common case of a tab delimiter.
39         */
40        public static final String DELIMITER_TAB = "\t";
41 
42        /**
43         * Convenient constant for the common case of a comma delimiter.
44         */
45        public static final String DELIMITER_COMMA = ",";
46 
47        /**
48         * Convenient constant for the common case of a " character used to escape
49         * delimiters or line endings.
50         */
51        public static final char DEFAULT_QUOTE_CHARACTER = '"';
52 
53        // the delimiter character used when reading input.
54        private String delimiter;
55 
56        private char quoteCharacter = DEFAULT_QUOTE_CHARACTER;
57 
58        private String quoteString;
59 
60        private Collection<Integer> includedFields = null;
61 
62        /**
63         * Create a new instance of the {@link DelimitedLineTokenizer} class for the
64         * common case where the delimiter is a {@link #DELIMITER_COMMA comma}.
65         *
66         * @see #DelimitedLineTokenizer(String)
67         * @see #DELIMITER_COMMA
68         */
69        public DelimitedLineTokenizer() {
70                this(DELIMITER_COMMA);
71        }
72 
73        /**
74         * Create a new instance of the {@link DelimitedLineTokenizer} class.
75         *
76         * @param delimiter the desired delimiter
77         */
78        public DelimitedLineTokenizer(String delimiter) {
79                Assert.state(!delimiter.equals(String.valueOf(DEFAULT_QUOTE_CHARACTER)), "[" + DEFAULT_QUOTE_CHARACTER
80                                + "] is not allowed as delimiter for tokenizers.");
81 
82                this.delimiter = delimiter;
83                setQuoteCharacter(DEFAULT_QUOTE_CHARACTER);
84        }
85 
86        /**
87         * Setter for the delimiter character.
88         *
89         * @param delimiter
90         */
91        public void setDelimiter(String delimiter) {
92                this.delimiter = delimiter;
93        }
94 
95        /**
96         * The fields to include in the output by position (starting at 0). By
97         * default all fields are included, but this property can be set to pick out
98         * only a few fields from a larger set. Note that if field names are
99         * provided, their number must match the number of included fields.
100         *
101         * @param includedFields the included fields to set
102         */
103        public void setIncludedFields(int[] includedFields) {
104                this.includedFields = new HashSet<Integer>();
105                for (int i : includedFields) {
106                        this.includedFields.add(i);
107                }
108        }
109 
110        /**
111         * Public setter for the quoteCharacter. The quote character can be used to
112         * extend a field across line endings or to enclose a String which contains
113         * the delimiter. Inside a quoted token the quote character can be used to
114         * escape itself, thus "a""b""c" is tokenized to a"b"c.
115         *
116         * @param quoteCharacter the quoteCharacter to set
117         *
118         * @see #DEFAULT_QUOTE_CHARACTER
119         */
120        public void setQuoteCharacter(char quoteCharacter) {
121                this.quoteCharacter = quoteCharacter;
122                this.quoteString = "" + quoteCharacter;
123        }
124 
125        /**
126         * Yields the tokens resulting from the splitting of the supplied
127         * <code>line</code>.
128         *
129         * @param line the line to be tokenized
130         *
131         * @return the resulting tokens
132         */
133        @Override
134        protected List<String> doTokenize(String line) {
135 
136                List<String> tokens = new ArrayList<String>();
137 
138                // line is never null in current implementation
139                // line is checked in parent: AbstractLineTokenizer.tokenize()
140                char[] chars = line.toCharArray();
141                boolean inQuoted = false;
142                int lastCut = 0;
143                int length = chars.length;
144                int fieldCount = 0;
145                int endIndexLastDelimiter = -1;
146 
147                for (int i = 0; i < length; i++) {
148                        char currentChar = chars[i];
149                        boolean isEnd = (i == (length - 1));
150 
151                        boolean isDelimiter = isDelimiter(chars, i, delimiter, endIndexLastDelimiter);
152 
153                        if ((isDelimiter && !inQuoted) || isEnd) {
154                                endIndexLastDelimiter = i;
155                                int endPosition = (isEnd ? (length - lastCut) : (i - lastCut));
156 
157                                if (isEnd && isDelimiter) {
158                                        endPosition = endPosition - delimiter.length();
159                                }
160                                else if (!isEnd){
161                                        endPosition = (endPosition - delimiter.length()) + 1;
162                                }
163 
164                                if (includedFields == null || includedFields.contains(fieldCount)) {
165                                        String value = maybeStripQuotes(new String(chars, lastCut, endPosition));
166                                        tokens.add(value);
167                                }
168 
169                                fieldCount++;
170 
171                                if (isEnd && (isDelimiter)) {
172                                        if (includedFields == null || includedFields.contains(fieldCount)) {
173                                                tokens.add("");
174                                        }
175                                        fieldCount++;
176                                }
177 
178                                lastCut = i + 1;
179                        }
180                        else if (isQuoteCharacter(currentChar)) {
181                                inQuoted = !inQuoted;
182                        }
183 
184                }
185 
186                return tokens;
187        }
188 
189        /**
190         * If the string is quoted strip (possibly with whitespace outside the
191         * quotes (which will be stripped), replace escaped quotes inside the
192         * string. Quotes are escaped with double instances of the quote character.
193         *
194         * @param string
195         * @return the same string but stripped and unescaped if necessary
196         */
197        private String maybeStripQuotes(String string) {
198                String value = string.trim();
199                if (isQuoted(value)) {
200                        value = StringUtils.replace(value, "" + quoteCharacter + quoteCharacter, "" + quoteCharacter);
201                        int endLength = value.length() - 1;
202                        // used to deal with empty quoted values
203                        if (endLength == 0) {
204                                endLength = 1;
205                        }
206                        value = value.substring(1, endLength);
207                        return value;
208                }
209                return string;
210        }
211 
212        /**
213         * Is this string surrounded by quote characters?
214         *
215         * @param value
216         * @return true if the value starts and ends with the
217         * {@link #quoteCharacter}
218         */
219        private boolean isQuoted(String value) {
220                if (value.startsWith(quoteString) && value.endsWith(quoteString)) {
221                        return true;
222                }
223                return false;
224        }
225 
226        /**
227         * Is the supplied character the delimiter character?
228         *
229         * @param chars the character array to be checked
230         * @return <code>true</code> if the supplied character is the delimiter
231         * character
232         * @see DelimitedLineTokenizer#DelimitedLineTokenizer(String)
233         */
234        private boolean isDelimiter(char[] chars, int i, String token, int endIndexLastDelimiter) {
235                boolean result = false;
236 
237                if(i-endIndexLastDelimiter >= delimiter.length()) {
238                        if(i >= token.length() - 1) {
239                                String end = new String(chars, (i-token.length()) + 1, token.length());
240                                if(token.equals(end)) {
241                                        result = true;
242                                }
243                        }
244                }
245 
246                return result;
247        }
248 
249        /**
250         * Is the supplied character a quote character?
251         *
252         * @param c the character to be checked
253         * @return <code>true</code> if the supplied character is an quote character
254         * @see #setQuoteCharacter(char)
255         */
256        protected boolean isQuoteCharacter(char c) {
257                return c == quoteCharacter;
258        }
259}

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