/*
 * DIFReader.java
 *
 * Created on January 29, 2008, 10:42 AM
 *
 * Copyright 2008 Clarke, Solomou & Associates Microsystems Ltd.
 *
 * This file is part of DIF
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 */

package com.csam.dif;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * This class reads a DIF file, thus producing a DIFSheet object
 * containing the single sheet of information stored in the file.
 * @author Nathan Crause
 */
public class DIFReader {
    
    private SimpleDateFormat usFmt = new SimpleDateFormat("MM/dd/yyyy");
    
    /** Creates a new instance of DIFReader */
    public DIFReader() {
        
    }
    
    /**
     * Reads in a DIF layout from the stream and reproduces the data in the supplied
     * spreadsheet object. The original data in the spreadsheet is wiped out.
     * @param sheet the spreadsheet (represented as a DIFSheet object) to read the
     * data into
     * @param inStream the stream from which to read the data
     * @return the original spreadsheet object, but now containing the new data
     * @throws java.io.IOException if an I/O error occurs while reading the spreadsheet
     * @throws com.csam.dif.DIFKeywordException if an anticipated keyword is missing
     * @throws com.csam.dif.DIFDataPairParsingException if a data pair (e.g. 0,0) cannot be properly parsed
     * @throws com.csam.dif.DIFNumberPairInfoException if the data in a data pair (which is expected to be 2 numbers) is not what is
     * expected
     * @throws com.csam.dif.DIFStringFormatException if a string value is malformed in the file
     * @throws java.text.ParseException if there is some problem parsing data, such as dates
     */
    public static DIFSheet readSheet(DIFSheet sheet, InputStream inStream)
    throws IOException, DIFKeywordException, DIFDataPairParsingException, 
            DIFNumberPairInfoException, DIFStringFormatException, ParseException {
        DIFReader reader = new DIFReader();
        return reader.read(sheet, inStream);
    }
    
    /**
     * Reads in a DIF layout from the stream and reproduces the data in the supplied
     * spreadsheet object. The original data in the spreadsheet is wiped out.
     * @param sheet the spreadsheet (represented as a DIFSheet object) to read the
     * data into
     * @param inStream the stream from which to read the data
     * @return the original spreadsheet object, but now containing the new data
     * @throws java.io.IOException if an I/O error occurs while reading the spreadsheet
     * @throws com.csam.dif.DIFKeywordException if an anticipated keyword is missing
     * @throws com.csam.dif.DIFDataPairParsingException if a data pair (e.g. 0,0) cannot be properly parsed
     * @throws com.csam.dif.DIFNumberPairInfoException if the data in a data pair (which is expected to be 2 numbers) is not what is
     * expected
     * @throws com.csam.dif.DIFStringFormatException if a string value is malformed in the file
     * @throws java.text.ParseException if there is some problem parsing data, such as dates
     */
    public DIFSheet read(DIFSheet sheet, InputStream inStream)
    throws IOException, DIFKeywordException, DIFDataPairParsingException, 
            DIFNumberPairInfoException, DIFStringFormatException, ParseException {
        
        sheet.cleanUp();
        
        BufferedReader reader = new BufferedReader(new InputStreamReader(inStream));
        readTitle(reader, sheet);
        int vectors = readVectors(reader);
        int tuples = readTuples(reader);
        readData(reader);
        
        // construct the data area
        try {
            for (int i = 0; i < tuples; ++i) {
                DIFRow row = sheet.createRow(i);
                for (int j = 0; j < vectors; ++j)
                    row.createCell(j);
            }
        } catch (DIFException ex) {
            throw new RuntimeException("PANIC!", ex);
        }
        
        // we should start with "-1,0"
        int rowCount = 0;
        readExpectedNumberPair(reader, -1, 0);
        while(true) {
            String line = reader.readLine();
            
            if (line.equals(DIFKeywords.EOD.name()))
                break;
            if (!line.equals(DIFKeywords.BOT.name()))
                throw new DIFKeywordException("Unknown keyword '" + line + "'", "BOT/EOD", line);
            
            DIFRow row = sheet.getRow(rowCount++);
            int colCount = 0;
            while (true) {
                DIFCell cell = row.getCell(colCount++);
                
                line = reader.readLine();
                String[] parts = simpleSplit(line);
                int code = Integer.parseInt(parts[0]);
                    
                if (code == -1) {
                    if (Integer.parseInt(parts[1]) != 0)
                        throw new DIFNumberPairInfoException(-1, 0, code, Integer.parseInt(parts[1]));
                    break;
                } else if (code == 1) {
                    if (Integer.parseInt(parts[1]) != 0)
                        throw new DIFNumberPairInfoException(-1, 0, code, Integer.parseInt(parts[1]));
                    
                    String val = readString(reader);
                    cell.setCellValue(val);
                } else if (code == 0) {
                    String indicator = reader.readLine();
                    if (indicator.equals("V")) {
                        if (parts[1].matches("\\A[0-9]{2}/[0-9]{2}/[0-9]{4}\\z")) {
                            Date parsed = usFmt.parse(parts[1]);
                            cell.setCellValue(new java.sql.Date(parsed.getTime()));
                        } else {
                            cell.setCellValue(new BigDecimal(parts[1]));
                        }
                    } else if (indicator.equals("TRUE")) {
                        if (Integer.parseInt(parts[1]) != 1)
                            throw new DIFNumberPairInfoException(code, 1, code, Integer.parseInt(parts[1]));
                        cell.setCellValue(true);
                    } else if (indicator.equals("FALSE")) {
                        if (Integer.parseInt(parts[1]) != 0)
                            throw new DIFNumberPairInfoException(code, 0, code, Integer.parseInt(parts[1]));
                        cell.setCellValue(false);
                    } else {
                        throw new DIFKeywordException("Unsupported type indicator", "V/TRUE/FALSE", indicator);
                    }
                }
            }
        }
        
        return sheet;
    }
    
    /**
     * Reads in a DIF layout from the stream and reproduces the data in a new
     * spreadsheet object.
     * @return the original spreadsheet object, but now containing the new data
     * @param inStream the stream from which to read the data
     * @throws java.io.IOException if an I/O error occurs while reading the spreadsheet
     * @throws com.csam.dif.DIFKeywordException if an anticipated keyword is missing
     * @throws com.csam.dif.DIFDataPairParsingException if a data pair (e.g. 0,0) cannot be properly parsed
     * @throws com.csam.dif.DIFNumberPairInfoException if the data in a data pair (which is expected to be 2 numbers) is not what is
     * expected
     * @throws com.csam.dif.DIFStringFormatException if a string value is malformed in the file
     * @throws java.text.ParseException if there is some problem parsing data, such as dates
     */
    public DIFSheet read(InputStream inStream) 
    throws IOException, DIFKeywordException, DIFDataPairParsingException, 
            DIFNumberPairInfoException, DIFStringFormatException, ParseException {
        return read(new DIFSheet(), inStream);
    }
    
    private String readExpectedKeyword(BufferedReader reader, String expected) 
    throws IOException, DIFKeywordException {
        String actual = reader.readLine();
        if (!actual.equals(expected))
            throw new DIFKeywordException("Expected keyword '" + expected + "' but found '" + actual + "'", expected, actual);
        
        return actual;
    }
    
    private String[] simpleSplit(String line) throws DIFDataPairParsingException {
        int index = line.indexOf(',');
        if (index == -1)
            throw new DIFDataPairParsingException("Missing ','", line);
        
        return new String[] {
            line.substring(0, index),
            line.substring(index + 1)
        };
    }
    
    private int[] readNumberPair(BufferedReader reader) 
    throws IOException, DIFDataPairParsingException {
        String line = reader.readLine();
        String[] parts = simpleSplit(line);
        
        try {
            int[] result = new int[parts.length];
            for (int i = 0; i < result.length; ++i)
                result[i] = Integer.parseInt(parts[i]);
            
            return result;
        } catch (NumberFormatException ex) {
            throw new DIFDataPairParsingException(ex, line);
        }
    }
    
    private int[] readExpectedNumberPair(BufferedReader reader, int vector, int tuple) 
    throws IOException, DIFDataPairParsingException, DIFNumberPairInfoException {
        int[] numbers = readNumberPair(reader);
        if (numbers[0] != vector || numbers[1] != tuple)
            throw new DIFNumberPairInfoException("Bad vector/tuple info", vector, tuple, numbers[0], numbers[1]);
        
        return numbers;
    }
    
    private String readString(BufferedReader reader) 
    throws IOException, DIFStringFormatException {
        String line = reader.readLine();
        if (!line.startsWith("\"") || !line.startsWith("\""))
            throw new DIFStringFormatException("Missing leading and trailing '\"' character");
        return line.substring(1, line.length() - 1).replace("\"\"", "\"");
    }
    
    private void readTitle(BufferedReader reader, DIFSheet sheet) 
    throws IOException, DIFKeywordException, DIFDataPairParsingException, 
            DIFNumberPairInfoException, DIFStringFormatException {
        
        readExpectedKeyword(reader, DIFKeywords.TABLE.name());
        readExpectedNumberPair(reader, 0, 1);
        sheet.setTitle(readString(reader));
        sheet.setVersion(1);
    }
    
    private int readVectors(BufferedReader reader) 
    throws IOException, DIFKeywordException, DIFDataPairParsingException, 
            DIFNumberPairInfoException, DIFStringFormatException {
        
        readExpectedKeyword(reader, DIFKeywords.VECTORS.name());
        int[] np = readNumberPair(reader);
        if (np[0] != 0)
            throw new DIFNumberPairInfoException("Invalid formatted 'VECTORS' info",
                    0, np[1], np[0], np[1]);
        
        if (readString(reader).length() != 0)
            throw new DIFStringFormatException("Expected empty string");
        
        return np[1];
    }
    
    private int readTuples(BufferedReader reader) 
    throws IOException, DIFKeywordException, DIFDataPairParsingException, 
            DIFNumberPairInfoException, DIFStringFormatException {
        
        readExpectedKeyword(reader, DIFKeywords.TUPLES.name());
        int[] np = readNumberPair(reader);
        if (np[0] != 0)
            throw new DIFNumberPairInfoException("Invalid formatted 'TUPLES' info",
                    0, np[1], np[0], np[1]);
        
        if (readString(reader).length() != 0)
            throw new DIFStringFormatException("Expected empty string");
        
        return np[1];
    }
    
    private void readData(BufferedReader reader) 
    throws IOException, DIFKeywordException, DIFDataPairParsingException, 
            DIFNumberPairInfoException, DIFStringFormatException {
        
        readExpectedKeyword(reader, DIFKeywords.DATA.name());
        readExpectedNumberPair(reader, 0, 0);
        if (readString(reader).length() != 0)
            throw new DIFStringFormatException("Expected empty string");
    }
}

 

Ads help maintain this website.