001/*
002Copyright (c) 2011+, HL7, Inc
003All rights reserved.
004
005Redistribution and use in source and binary forms, with or without modification, 
006are permitted provided that the following conditions are met:
007
008 * Redistributions of source code must retain the above copyright notice, this 
009   list of conditions and the following disclaimer.
010 * Redistributions in binary form must reproduce the above copyright notice, 
011   this list of conditions and the following disclaimer in the documentation 
012   and/or other materials provided with the distribution.
013 * Neither the name of HL7 nor the names of its contributors may be used to 
014   endorse or promote products derived from this software without specific 
015   prior written permission.
016
017THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
018ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
019WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
020IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
021INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
022NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
023PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
024WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
025ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
026POSSIBILITY OF SUCH DAMAGE.
027
028*/
029package org.hl7.fhir.utilities;
030
031import java.io.IOException;
032import java.io.InputStream;
033import java.io.InputStreamReader;
034import java.io.UnsupportedEncodingException;
035import java.util.ArrayList;
036import java.util.List;
037
038import org.hl7.fhir.exceptions.FHIRException;
039
040/**
041 * Baseclass for readers that read data from files in comma separated file format
042 * @author Ewout
043 *
044 */
045public class CSVReader extends InputStreamReader {
046        
047        public CSVReader(InputStream in) throws UnsupportedEncodingException {
048                super(in, "UTF-8");
049        }
050
051        private String[] cols;
052  private String[] cells;
053  
054        public void readHeaders() throws IOException, FHIRException {
055    cols = parseLine();  
056        }
057        
058
059  public boolean line() throws IOException, FHIRException {
060    if (ready()) {
061      cells = parseLine();
062      return true;
063    }  else
064      return false;
065  }
066
067  public boolean has(String name) {
068    return cell(name) != null;
069  }
070  
071  public String cell(String name) {
072    int index = -1;
073    for (int i = 0; i < cols.length; i++) {
074      if (name.equals(cols[i]))
075        index = i;
076    }
077    if (index == -1)
078      throw new Error("no cell "+name);
079    String s = cells.length >= index ? cells[index] : null;
080    if (Utilities.noString(s))
081      return null;
082    return s;
083  }
084    
085        protected boolean parseBoolean(String column) {
086                if (column == null)
087                        return false;
088                else if (column.equalsIgnoreCase("y") || column.equalsIgnoreCase("yes") || column.equalsIgnoreCase("true") || column.equalsIgnoreCase("1"))
089                        return true;
090                else
091                        return false;
092        }
093
094        protected static String getColumn(String[] titles, String[] values, String column)  {
095                int c = -1;
096        //      String s = "";
097                for (int i = 0; i < titles.length; i++) {
098                //      s = s + ","+titles[i];
099                        if (titles[i].equalsIgnoreCase(column))
100                                c = i;
101                }
102                if (c == -1)
103                        return ""; // throw new Exception("unable to find column "+column+" in "+s.substring(1));
104                else if (values.length <= c)
105                        return "";
106                else
107                        return values[c];
108        }
109
110        
111        /**
112         * Split one line in a CSV file into its rows. Comma's appearing in double quoted strings will
113         * not be seen as a separator.
114         * @return
115         * @throws IOException 
116         * @throws FHIRException 
117         * @
118         */
119        public String[] parseLine() throws IOException, FHIRException  {
120                List<String> res = new ArrayList<String>();
121                StringBuilder b = new StringBuilder();
122                boolean inQuote = false;
123
124                while (inQuote || (peek() != '\r' && peek() != '\n')) {
125                        char c = peek();
126                        next();
127                        if (c == '"') 
128                                inQuote = !inQuote;
129                        else if (!inQuote && c == ',') {
130                                res.add(b.toString().trim());
131                                b = new StringBuilder();
132                        }
133                        else 
134                                b.append(c);
135                }
136                res.add(b.toString().trim());
137                while (ready() && (peek() == '\r' || peek() == '\n')) {
138                        next();
139                }
140                
141                String[] r = new String[] {};
142                r = res.toArray(r);
143                return r;
144                
145        }
146
147        private int state = 0;
148        private char pc;
149        
150        private char peek() throws FHIRException, IOException 
151        {
152          if (state == 0)
153                  next();
154          if (state == 1)
155                  return pc;
156          else
157                  throw new FHIRException("read past end of source");
158        }
159        
160        private void next() throws FHIRException, IOException 
161        {
162                  if (state == 2)
163                          throw new FHIRException("read past end of source");
164          state = 1;
165                  int i = read();
166                  if (i == -1)
167                          state = 2;
168                  else 
169                          pc = (char) i;
170        }
171
172
173}