001package org.hl7.fhir.utilities;
002
003/*
004  Copyright (c) 2011+, HL7, Inc.
005  All rights reserved.
006  
007  Redistribution and use in source and binary forms, with or without modification, 
008  are permitted provided that the following conditions are met:
009    
010   * Redistributions of source code must retain the above copyright notice, this 
011     list of conditions and the following disclaimer.
012   * Redistributions in binary form must reproduce the above copyright notice, 
013     this list of conditions and the following disclaimer in the documentation 
014     and/or other materials provided with the distribution.
015   * Neither the name of HL7 nor the names of its contributors may be used to 
016     endorse or promote products derived from this software without specific 
017     prior written permission.
018  
019  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
020  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
021  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
022  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
023  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
024  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
025  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
026  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
027  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
028  POSSIBILITY OF SUCH DAMAGE.
029  
030 */
031
032
033
034
035import java.io.IOException;
036import java.io.InputStream;
037import java.io.InputStreamReader;
038import java.io.OutputStream;
039import java.io.OutputStreamWriter;
040import java.io.UnsupportedEncodingException;
041
042import org.hl7.fhir.exceptions.FHIRException;
043
044/**
045 * A file processor that reads a templated source file with markers ([%columnname%]), reads data
046 * from a CSV file and inserts data from that CSV file into those markers. Supports loops to
047 * interate through the CSV file.
048 * @author Ewout
049 *
050 */
051public class CSVProcessor {
052
053  public class DataReader extends CSVReader {
054
055    public DataReader(InputStream data) throws FHIRException, IOException {
056      super(data);
057    }
058
059    public void process() throws IOException, FHIRException  {
060      String[] titles = parseLine();
061      while (ready())
062      {
063        String[] values = parseLine();
064        processLine(titles, values);
065      }     
066      close();
067    }
068
069    private void processLine(String[] titles, String[] values) throws FHIRException  {
070      count++;
071      String src = loop;
072      while (src.contains("[%")) {
073        int i1 = src.indexOf("[%");
074        int i2 = src.indexOf("%]");
075        String s1 = src.substring(0, i1);
076        String s2 = src.substring(i1 + 2, i2).trim();
077        String s3 = src.substring(i2+2);
078        if ("count".equals(s2))
079          src = s1+Integer.toString(count)+s3;
080        else {
081          boolean b = false;
082          for (String t : titles) {
083            if (t.equals(s2)) {
084              src = s1+getColumn(titles, values, s2)+s3;
085              b = true;
086            }
087          }
088          if (!b)
089            throw new FHIRException("unknown column: '"+s2+"'");
090        }
091      }
092      dest.append(src);
093    }
094  }
095
096  private InputStream source;
097  private DataReader data;
098  private OutputStreamWriter out;
099
100  private String start;
101  private String loop;
102  private int count = 0;
103  private String stop;
104  private StringBuilder dest;
105  
106  public void setSource(InputStream source) {
107    this.source = source;
108  }
109
110  public void setData(InputStream data) throws FHIRException, IOException {
111    try {
112      this.data = new DataReader(data);
113    } catch (UnsupportedEncodingException e) {
114      // DataReader is fixed to "UTF-8", so this exception cannot really occur
115    }   
116  }
117
118  public void setOutput(OutputStream out) throws UnsupportedEncodingException {
119    this.out = new OutputStreamWriter(out, "UTF-8");   
120  }
121
122  public void process() throws IOException, FHIRException  {
123    buildTemplate(readSource());
124    dest = new StringBuilder();
125    dest.append(start);
126    data.process();
127    dest.append(stop);
128    out.write(dest.toString());
129    out.close();
130  }
131
132  private void buildTemplate(String template) throws FHIRException  {
133    int i = template.indexOf("[%loop");
134    if (i < 0)
135      throw new FHIRException("Unable to process template - didn't find [%loop");
136    start = template.substring(0, i);
137    template = template.substring(i+6);
138    i = template.indexOf("%]");
139    if (i < 0)
140      throw new FHIRException("Unable to process template - didn't find %] matching [%loop");
141    String tmp = template.substring(0, i);
142    if (tmp != null && !tmp.equals("")) {
143      if (!tmp.startsWith(" count="))
144        throw new FHIRException("Unable to process template - unrecognised content on [%loop");
145      count = Integer.parseInt(tmp.substring(7));
146    }
147    
148    template = template.substring(i+2);
149    i = template.indexOf("[%endloop%]");
150    if (i < 0)
151      throw new FHIRException("Unable to process template - didn't find [%endloop%]");
152    loop = template.substring(0, i);
153    stop = template.substring(i+11);
154  }
155
156  private String readSource() throws IOException  {
157    StringBuilder s = new StringBuilder();
158    InputStreamReader r = new InputStreamReader(source,"UTF-8");
159    while (r.ready()) {
160      s.append((char) r.read()); 
161    }
162    r.close();
163    return s.toString();
164  }
165
166}