package nl.wldelft.fews.system.plugin.dataImport;

import nl.wldelft.util.IOUtils;
import nl.wldelft.util.TextUtils;
import nl.wldelft.util.io.LineReader;
import nl.wldelft.util.io.TextParser;
import nl.wldelft.util.timeseries.DefaultTimeSeriesHeader;
import nl.wldelft.util.timeseries.TimeSeriesContentHandler;
import org.apache.log4j.Logger;

import java.io.IOException;


public class MswTimeSeriesParser implements TextParser<TimeSeriesContentHandler> {
    private static final Logger log = Logger.getLogger(MswTimeSeriesParser.class);

    private LineReader reader = null;
    private String virtualFileName = null;
    private TimeSeriesContentHandler contentHandler = null;
    private String fileUnit = null;
    private DefaultTimeSeriesHeader header = new DefaultTimeSeriesHeader();

    @Override
    public void parse(LineReader reader, String virtualFileName, TimeSeriesContentHandler contentHandler) throws Exception {
        this.contentHandler = contentHandler;
        this.virtualFileName = virtualFileName;

        contentHandler.addMissingValue("-999");
        this.reader = reader;
        this.reader.setCommentLinePrefix('#');

        parseHeader();
        if (contentHandler.isCurrentTimeSeriesHeaderForAllTimesRejected()) return;
        parseData();
    }

    /**
     * Read exactly 6 header-lines and extract:
     * from line 2:  location Id
     * from line 3:  parameter Id
     */
    private void parseHeader() throws Exception {
        String[] headerLines = IOUtils.readLines(reader, 6);
        if (headerLines.length < 6) {
            throw new Exception("Import.Error: Header of the file " + this.virtualFileName + " has unknown format.");
        }
        header.setLocationId(TextUtils.rightFrom(headerLines[1], '='));
        header.setParameterId(TextUtils.rightFrom(headerLines[2], '='));
        contentHandler.setTimeSeriesHeader(header);
    }


    /**
     * Reads the file and put read data to the TimeSeriesContentHandler
     * @return true if at least 1 line is read, otherwise false
     * @throws IOException  if any unexpected error occur while reading the file
     */
    private void parseData() throws Exception {

        //Read first data line
        String[] firstLine = this.reader.readLine(';');
        if (firstLine == null ){
            throw new Exception("File contains no lines with data: "+this.virtualFileName);
        }

        //Get the unit from the first data line
        if (firstLine.length < 4) {
            throw new Exception("File contains no unit specification : " + this.virtualFileName);
        }

        TextUtils.trimElements(firstLine);

        this.fileUnit = firstLine[3];

        //Put unit to the header and ask if this header is wanted (i.e. are data from this file wanted ?)
        header.setUnit(this.fileUnit);
        this.contentHandler.setNewTimeSeriesHeader(header);
        if (this.contentHandler.isCurrentTimeSeriesHeaderForAllTimesRejected()) return;

        //Parse other data from this data line and put them to the timeseries handler
        parseDataLine(firstLine);

        //Read remaining lines,  parse the data and put them to the  timeseries handler
        for (String[] line; (line = this.reader.readLine(';')) != null;) {
            TextUtils.trimElements(line);
            parseDataLine(line);
        }
   }


    /**
     * Parse from each line the following data:
     * from column 1: date
     * from column 2: time
     * from column 4: unit
     * from column 5: flag
     * from column 6: value
     *
     * Unit must be the same in all records, i.e. equal to this.fileUnit that is read from the first data record.
     */
    private void parseDataLine(String[] line) throws IOException {

        //Check whether the line contains the obligatory 6 columns
        if (line.length != 6) {
            log.warn("Import.Error: Line contains less than 6 columns at line "+ reader.getLineNumber());
            return;
        }

        contentHandler.setTime(contentHandler.getDefaultTimeZone(), "yyyy/MM/dd", line[0], "HH:mm", line[1]);
        if (contentHandler.isCurrentTimeSeriesHeaderForCurrentTimeRejected()) return;

        //Check unit (only if the unit already read)
        if (!line[3].equals(this.fileUnit)) {
            log.warn("Import.Error: Line contains an unexpected unit at line " + reader.getLineNumber() +
                                     ",   "+this.fileUnit+ " wil be used.");
        }

        this.contentHandler.setFlag(line[4]);
        this.contentHandler.setValue('.', line[5]);
        this.contentHandler.applyCurrentFields();
    }
}

  • No labels