Coverage Report - liquibase.util.csv.opencsv.CSVReader
 
Classes in this File Line Coverage Branch Coverage Complexity
CSVReader
65%
38/58
71%
33/46
3.556
 
 1  
 package liquibase.util.csv.opencsv;
 2  
 
 3  
 /**
 4  
  * Copyright 2005 Bytecode Pty Ltd.
 5  
  * 
 6  
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
 7  
  * the License. You may obtain a copy of the License at
 8  
  * 
 9  
  * http://www.apache.org/licenses/LICENSE-2.0
 10  
  * 
 11  
  * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 12  
  * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 13  
  * specific language governing permissions and limitations under the License.
 14  
  */
 15  
 
 16  
 import java.io.BufferedReader;
 17  
 import java.io.IOException;
 18  
 import java.io.Reader;
 19  
 import java.util.ArrayList;
 20  
 import java.util.List;
 21  
 
 22  
 /**
 23  
  * A very simple CSV reader released under a commercial-friendly license.
 24  
  * 
 25  
  * @author Glen Smith
 26  
  * 
 27  
  */
 28  
 public class CSVReader {
 29  
 
 30  
     private BufferedReader br;
 31  
 
 32  10
     private boolean hasNext = true;
 33  
 
 34  
     private char separator;
 35  
 
 36  
     private char quotechar;
 37  
 
 38  
     private int skipLines;
 39  
 
 40  
     private boolean linesSkiped;
 41  
 
 42  
     /** The default separator to use if none is supplied to the constructor. */
 43  
     public static final char DEFAULT_SEPARATOR = ',';
 44  
 
 45  
     /**
 46  
      * The default quote character to use if none is supplied to the constructor.
 47  
      */
 48  
     public static final char DEFAULT_QUOTE_CHARACTER = '"';
 49  
 
 50  
     /**
 51  
      * The default line to start reading.
 52  
      */
 53  
     public static final int DEFAULT_SKIP_LINES = 0;
 54  
 
 55  
     /**
 56  
      * Constructs CSVReader using a comma for the separator.
 57  
      * 
 58  
      * @param reader
 59  
      *            the reader to an underlying CSV source.
 60  
      */
 61  
     public CSVReader(Reader reader) {
 62  0
         this(reader, DEFAULT_SEPARATOR);
 63  0
     }
 64  
 
 65  
     /**
 66  
      * Constructs CSVReader with supplied separator.
 67  
      * 
 68  
      * @param reader
 69  
      *            the reader to an underlying CSV source.
 70  
      * @param separator
 71  
      *            the delimiter to use for separating entries.
 72  
      */
 73  
     public CSVReader(Reader reader, char separator) {
 74  0
         this(reader, separator, DEFAULT_QUOTE_CHARACTER);
 75  0
     }
 76  
 
 77  
     /**
 78  
      * Constructs CSVReader with supplied separator and quote char.
 79  
      * 
 80  
      * @param reader
 81  
      *            the reader to an underlying CSV source.
 82  
      * @param separator
 83  
      *            the delimiter to use for separating entries
 84  
      * @param quotechar
 85  
      *            the character to use for quoted elements
 86  
      */
 87  
     public CSVReader(Reader reader, char separator, char quotechar) {
 88  10
         this(reader, separator, quotechar, DEFAULT_SKIP_LINES);
 89  10
     }
 90  
 
 91  
     /**
 92  
      * Constructs CSVReader with supplied separator and quote char.
 93  
      * 
 94  
      * @param reader
 95  
      *            the reader to an underlying CSV source.
 96  
      * @param separator
 97  
      *            the delimiter to use for separating entries
 98  
      * @param quotechar
 99  
      *            the character to use for quoted elements
 100  
      * @param line
 101  
      *            the line number to skip for start reading
 102  
      */
 103  10
     public CSVReader(Reader reader, char separator, char quotechar, int line) {
 104  10
         this.br = new BufferedReader(reader);
 105  10
         this.separator = separator;
 106  10
         this.quotechar = quotechar;
 107  10
         this.skipLines = line;
 108  10
     }
 109  
 
 110  
     /**
 111  
      * Reads the entire file into a List with each element being a String[] of tokens.
 112  
      * 
 113  
      * @return a List of String[], with each String[] representing a line of the file.
 114  
      * 
 115  
      * @throws IOException
 116  
      *             if bad things happen during the read
 117  
      */
 118  
     public List readAll() throws IOException {
 119  
 
 120  0
         List allElements = new ArrayList();
 121  0
         while (hasNext) {
 122  0
             String[] nextLineAsTokens = readNext();
 123  0
             if (nextLineAsTokens != null)
 124  0
                 allElements.add(nextLineAsTokens);
 125  0
         }
 126  0
         return allElements;
 127  
 
 128  
     }
 129  
 
 130  
     /**
 131  
      * Reads the next line from the buffer and converts to a string array.
 132  
      * 
 133  
      * @return a string array with each comma-separated element as a separate entry.
 134  
      * 
 135  
      * @throws IOException
 136  
      *             if bad things happen during the read
 137  
      */
 138  
     public String[] readNext() throws IOException {
 139  
 
 140  40
         String nextLine = getNextLine();
 141  40
         return hasNext ? parseLine(nextLine) : null;
 142  
     }
 143  
 
 144  
     /**
 145  
      * Reads the next line from the file.
 146  
      * 
 147  
      * @return the next line from the file without trailing newline
 148  
      * @throws IOException
 149  
      *             if bad things happen during the read
 150  
      */
 151  
     private String getNextLine() throws IOException {
 152  40
         if (!this.linesSkiped) {
 153  10
             for (int i = 0; i < skipLines; i++) {
 154  0
                 br.readLine();
 155  
             }
 156  10
             this.linesSkiped = true;
 157  
         }
 158  40
         String nextLine = br.readLine();
 159  40
         if (nextLine == null) {
 160  10
             hasNext = false;
 161  
         }
 162  40
         return hasNext ? nextLine : null;
 163  
     }
 164  
 
 165  
     /**
 166  
      * Parses an incoming String and returns an array of elements.
 167  
      * 
 168  
      * @param nextLine
 169  
      *            the string to parse
 170  
      * @return the comma-tokenized list of elements, or null if nextLine is null
 171  
      * @throws IOException
 172  
      *             if bad things happen during the read
 173  
      */
 174  
     private String[] parseLine(String nextLine) throws IOException {
 175  
 
 176  30
         if (nextLine == null) {
 177  0
             return null;
 178  
         }
 179  
 
 180  30
         List tokensOnThisLine = new ArrayList();
 181  30
         StringBuffer sb = new StringBuffer();
 182  30
         boolean inQuotes = false;
 183  
         do {
 184  30
             if (inQuotes) {
 185  
                 // continuing a quoted section, reappend newline
 186  0
                 sb.append("\n");
 187  0
                 nextLine = getNextLine();
 188  0
                 if (nextLine == null)
 189  0
                     break;
 190  
             }
 191  522
             for (int i = 0; i < nextLine.length(); i++) {
 192  
 
 193  492
                 char c = nextLine.charAt(i);
 194  492
                 if (c == quotechar) {
 195  
                     // this gets complex... the quote may end a quoted block, or escape another quote.
 196  
                     // do a 1-char lookahead:
 197  4
                     if (inQuotes // we are in quotes, therefore there can be escaped quotes in here.
 198  
                             && nextLine.length() > (i + 1) // there is indeed another character to check.
 199  
                             && nextLine.charAt(i + 1) == quotechar) { // ..and that char. is a quote also.
 200  
                         // we have two quote chars in a row == one quote char, so consume them both and
 201  
                         // put one on the token. we do *not* exit the quoted text.
 202  0
                         sb.append(nextLine.charAt(i + 1));
 203  0
                         i++;
 204  
                     } else {
 205  4
                         inQuotes = !inQuotes;
 206  
                         // the tricky case of an embedded quote in the middle: a,bc"d"ef,g
 207  4
                         if (i > 2 // not on the begining of the line
 208  
                                 && nextLine.charAt(i - 1) != this.separator // not at the begining of an escape sequence
 209  
                                 && nextLine.length() > (i + 1) && nextLine.charAt(i + 1) != this.separator // not at the
 210  
                                                                                                            // end of an
 211  
                                                                                                            // escape
 212  
                                                                                                            // sequence
 213  
                         ) {
 214  0
                             sb.append(c);
 215  
                         }
 216  
                     }
 217  488
                 } else if (c == separator && !inQuotes) {
 218  36
                     tokensOnThisLine.add(sb.toString());
 219  36
                     sb = new StringBuffer(); // start work on next token
 220  
                 } else {
 221  452
                     sb.append(c);
 222  
                 }
 223  
             }
 224  30
         } while (inQuotes);
 225  30
         tokensOnThisLine.add(sb.toString());
 226  30
         return (String[]) tokensOnThisLine.toArray(new String[0]);
 227  
 
 228  
     }
 229  
 
 230  
     /**
 231  
      * Closes the underlying reader.
 232  
      * 
 233  
      * @throws IOException
 234  
      *             if the close fails
 235  
      */
 236  
     public void close() throws IOException {
 237  10
         br.close();
 238  10
     }
 239  
 
 240  
 }