You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 4 Next »

package nl.wldelft.fews.pi;

import com.sun.org.apache.xml.internal.serialize.OutputFormat;
import com.sun.org.apache.xml.internal.serialize.XMLSerializer;
import nl.wldelft.util.FastDateFormat;
import nl.wldelft.util.FileUtils;
import nl.wldelft.util.Period;
import nl.wldelft.util.TimeUnit;
import nl.wldelft.util.io.LineWriter;
import nl.wldelft.util.io.LittleEndianDataOutputStream;
import nl.wldelft.util.io.TextSerializer;
import nl.wldelft.util.io.VirtualOutputDir;
import nl.wldelft.util.io.VirtualOutputDirConsumer;
import nl.wldelft.util.timeseries.TimeSeriesContent;
import nl.wldelft.util.timeseries.TimeSeriesHeader;
import nl.wldelft.util.timeseries.TimeStep;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;

public class PiTimeSeriesSerializer implements TextSerializer<TimeSeriesContent>, VirtualOutputDirConsumer {
    public enum EventDestination {XML_EMBEDDED, SEPARATE_BINARY_FILE, ONLY_HEADERS}

    private EventDestination eventDestination = EventDestination.XML_EMBEDDED;
    private PiVersion version = PiVersion.VERSION_1_2;

    private final FastDateFormat dateFormat = new FastDateFormat("yyyy-MM-dd");
    private final FastDateFormat timeFormat = new FastDateFormat("HH:mm:ss");
    private TimeSeriesContent timeSeriesContent = null;
    private LineWriter writer = null;
    private ContentHandler xmlContentHandler = null;
    private VirtualOutputDir virtualOutputDir = null;
    private LittleEndianDataOutputStream binaryOutputSteam = null;

    private final AttributesImpl attributesBuffer = new AttributesImpl();

    public PiTimeSeriesSerializer() {
    }

    public EventDestination getEventDestination() {
        return eventDestination;
    }

    public void setEventDestination(EventDestination eventDestination) {
        this.eventDestination = eventDestination;
    }

    public PiVersion getVersion() {
        return version;
    }

    public void setVersion(PiVersion version) {
        if (version == null)
            throw new IllegalArgumentException("version == null");

        this.version = version;
    }

    @Override
    public void setVirtualOutputDir(VirtualOutputDir virtualOutputDir) {
        this.virtualOutputDir = virtualOutputDir;
    }

    @Override
    public void serialize(TimeSeriesContent timeSeriesContent, LineWriter writer, String virtualFileName) throws Exception {
        this.timeSeriesContent = timeSeriesContent;
        this.writer = writer;

        dateFormat.setTimeZone(this.timeSeriesContent.getDefaultTimeZone());
        timeFormat.setTimeZone(this.timeSeriesContent.getDefaultTimeZone());

        if (eventDestination == EventDestination.SEPARATE_BINARY_FILE) {
            if (virtualOutputDir == null)
                throw new IllegalStateException("virtualOutputDir == null");
            binaryOutputSteam = new LittleEndianDataOutputStream(virtualOutputDir.getOutputStream(FileUtils.getPathWithOtherExtension(virtualFileName, "bin")));
            try {
                serialize();
            } finally {
                binaryOutputSteam.close();
            }
            return;
        }

        binaryOutputSteam = null;
        serialize();
    }

    private void serialize() throws Exception {
        XMLSerializer serializer = new XMLSerializer();
        OutputFormat of = new OutputFormat("XML", "UTF-8", true);
        serializer.setOutputFormat(of);
        serializer.setOutputCharStream(writer);
        xmlContentHandler = serializer.asContentHandler();

        xmlContentHandler.startDocument();
        attributesBuffer.clear();
        addAttribute("xmlns", "http://www.wldelft.nl/fews/PI");
        addAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
        addAttribute("xsi:schemaLocation", PiSchemaLocations.get("pi_timeseries.xsd"));
        addAttribute("version", version.toString());
        xmlContentHandler.startElement("", "TimeSeries", "TimeSeries", attributesBuffer);
        writeElement("timeZone", String.valueOf((double) timeSeriesContent.getDefaultTimeZone().getRawOffset() / (double) TimeUnit.HOUR_MILLIS));
        for (int i = 0, n = timeSeriesContent.getTimeSeriesCount(); i < n; i++) {
            timeSeriesContent.setTimeSeriesIndex(i);
            xmlContentHandler.startElement(null, null, "series", null);
            writeHeader();
            writeEvents();
            xmlContentHandler.endElement(null, null, "series");
        }
        xmlContentHandler.endElement(null, null, "TimeSeries");
        xmlContentHandler.endDocument();
    }

    private void writeEvents() throws Exception {
        for (int i = 0, n = timeSeriesContent.getContentTimeCount(); i < n; i++) {
            timeSeriesContent.setContentTimeIndex(i);
            if (!timeSeriesContent.isTimeAvailable()) continue;
            writeEvent(timeSeriesContent.getTime());
        }
    }

    private void writeHeader() throws Exception {
        TimeSeriesHeader header = timeSeriesContent.getTimeSeriesHeader();
        PiTimeSeriesHeader piHeader = header instanceof PiTimeSeriesHeader ? (PiTimeSeriesHeader) header : new PiTimeSeriesHeader();

        xmlContentHandler.startElement(null, null, "header", null);
        writeElement("type", header.getParameterType() == null ? "instantaneous" : header.getParameterType().getName());
        writeElement("locationId", header.getLocationId() == null ? "unknown" : header.getLocationId());
        writeElement("parameterId", header.getParameterId() == null ? "unknown" : header.getParameterId());
        if (version.ordinal() >= PiVersion.VERSION_1_4.ordinal()) {
            for (int i = 0, n = header.getQualifierCount(); i < n; i++) {
                writeElement("qualifierId", header.getQualifierId(i));
            }
        }
        if (version.ordinal() >= PiVersion.VERSION_1_4.ordinal() && header.getEnsembleId() != null && !header.getEnsembleId().equals("main")) {
            writeOptionalElement("ensembleId", header.getEnsembleId());
            writeOptionalElement("ensembleMemberIndex", header.getEnsembleMemberIndex());
        }

        writeTimeStep(header);
        writePeriod();
        if (version.ordinal() >= PiVersion.VERSION_1_5.ordinal()) writeTime("forecastDate", header.getForecastTime());

        writeElement("missVal", Float.toString(timeSeriesContent.getDefaultMissingValue()));
        writeOptionalElement("longName", piHeader.getLongName());
        writeOptionalElement("stationName", header.getLocationName());
        writeOptionalElement("units", header.getUnit());
        writeOptionalElement("sourceOrganisation", piHeader.getSourceOrganisation());
        writeOptionalElement("sourceSystem", piHeader.getSourceSystem());
        writeOptionalElement("fileDescription", piHeader.getFileDescription());

        if (header.getCreationTime() != Long.MIN_VALUE) {
            writeElement("creationDate", dateFormat.format(header.getCreationTime()));
            writeElement("creationTime", timeFormat.format(header.getCreationTime()));
        }

        writeOptionalElement("region", piHeader.getRegion());

        xmlContentHandler.endElement(null, null, "header");
    }

    private void writePeriod() throws SAXException {
        TimeStep timeStep = timeSeriesContent.getTimeSeriesHeader().getTimeStep();
        Period period = timeSeriesContent.getTimeSeriesPeriod();
        Period headerPeriod;
        if (period == Period.NEVER) {
            // create a dummy period
            long now = timeStep.nearestTime(System.currentTimeMillis());
            headerPeriod = new Period(now, now);
        } else {
            headerPeriod = period;
        }

        writeTime("startDate", headerPeriod.getStartTime());
        writeTime("endDate", headerPeriod.getEndTime());
    }

    private void writeTime(String name, long time) throws SAXException {
        if (time == Long.MIN_VALUE) return;
        attributesBuffer.clear();
        addAttribute("date", dateFormat.format(time));
        addAttribute("time", timeFormat.format(time));
        writeAttributes(name);
    }

    private void writeTimeStep(TimeSeriesHeader header) throws SAXException {
        TimeStep timeStep = header.getTimeStep();
        attributesBuffer.clear();

        // todo add support for month time step
        if (timeStep.isEquidistantMillis()) {
            long seconds = timeStep.getStepMillis() / 1000;
            addAttribute("unit","second");
            addAttribute("multiplier", String.valueOf(seconds));
        } else {
            addAttribute("unit", "nonequidistant");
        }

        writeAttributes("timeStep");
    }

    private void writeEvent(long time) throws Exception {
        if (eventDestination == EventDestination.ONLY_HEADERS) return;
        if (eventDestination == EventDestination.SEPARATE_BINARY_FILE) {
            binaryOutputSteam.writeFloat(timeSeriesContent.getValue());
            return;
        }

        attributesBuffer.clear();

        addAttribute("date", dateFormat.format(time));
        addAttribute("time", timeFormat.format(time));
        addAttribute("value", timeSeriesContent.getValue('.'));
        addAttribute("flag", timeSeriesContent.getStringFlag());
        if (version.ordinal() >= PiVersion.VERSION_1_3.ordinal()) {
            String comment = timeSeriesContent.getComment();
            if (comment != null) addAttribute("comment", timeSeriesContent.getComment());
        }
        writeAttributes("event");
    }

    private void writeOptionalElement(String elementName, int index) throws SAXException {
        if (index == -1) return;
        writeElement(elementName, Integer.toString(index));
    }

    private void writeOptionalElement(String elementName, String s) throws SAXException {
        if (s == null) return;
        if (s.trim().length() == 0) return;
        writeElement(elementName, s);
    }

    private  void writeElement(String name, String value) throws SAXException {
       xmlContentHandler.startElement(null, null, name, null);
       xmlContentHandler.characters(value.toCharArray(), 0, value.length());
       xmlContentHandler.endElement(null, null, name);
    }

    private void writeAttributes(String name) throws SAXException {
        xmlContentHandler.startElement("", name, name, attributesBuffer);
        xmlContentHandler.endElement(null, null, name);
    }

    private void addAttribute(String name, String value) {
        attributesBuffer.addAttribute("", name, name, "CDATA", value);
    }
}
  • No labels