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.IOUtilsPeriod;
import nl.wldelft.util.PeriodTimeUnit;
import nl.wldelft.util.TimeUnitio.LineWriter;
import nl.wldelft.util.io.FileSerializerLittleEndianDataOutputStream;
import nl.wldelft.util.io.LineWriterTextSerializer;
import nl.wldelft.util.io.LittleEndianDataOutputStreamVirtualOutputDir;
import nl.wldelft.util.io.TextSerializerVirtualOutputDirConsumer;
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;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
public class PiTimeSeriesSerializer implements FileSerializer<TimeSeriesContent>, TextSerializer<TimeSeriesContent> {
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 serializesetVirtualOutputDir(TimeSeriesContentVirtualOutputDir content, File file) throws Exception {
virtualOutputDir) {
this.virtualOutputDir = virtualOutputDir;
File binFile = FileUtils.getFileWithOtherExtension(file, "bin"); }
@Override
public ifvoid (eventDestination != EventDestination.SEPARATE_BINARY_FILE )serialize(TimeSeriesContent timeSeriesContent, LineWriter writer, String virtualFileName) throws Exception {
this.timeSeriesContent = timeSeriesContent;
binFile.delete();
this.writer = writer;
if (binFile.existsdateFormat.setTimeZone(this.timeSeriesContent.getDefaultTimeZone()) ;
throw new IOException("Can not delete " + binFiletimeFormat.setTimeZone(this.timeSeriesContent.getDefaultTimeZone());
if LineWriter writer = new LineWriter(new OutputStreamWriter(new FileOutputStream(file), IOUtils.UTF8_CHARSET));
try {
serialize(content, writer, file.getPath());
} finally {
writer.close();
}
return;
}
LineWriter writer = new LineWriter(new OutputStreamWriter(new FileOutputStream(file), IOUtils.UTF8_CHARSET));
try {
binaryOutputSteam = new LittleEndianDataOutputStream(new BufferedOutputStream(new FileOutputStream(binFile)));
try {
serialize(content, writer, file.getPath());
} finally {
binaryOutputSteam.close();
}
} finally {
writer.close();
}
}
public void serialize(LineWriter writer, OutputStream binOutputStream, String fileName, TimeSeriesContent content) throws Exception {
if (writer == null)
throw new IllegalArgumentException("writer == null");
if (binOutputStream == null)
throw new IllegalArgumentException("binaryOutputSteam == null");
if (content == null)
throw new IllegalArgumentException("content == null");
(eventDestination == EventDestination.SEPARATE_BINARY_FILE) {
binaryOutputSteam = newif LittleEndianDataOutputStream(binOutputStream);
(virtualOutputDir == null)
try {
throw new serializeIllegalStateException(content, writer, fileName"virtualOutputDir == null");
} finally {
binaryOutputSteam = new binaryOutputSteam.flush(LittleEndianDataOutputStream(virtualOutputDir.getOutputStream(FileUtils.getPathWithOtherExtension(virtualFileName, "bin")));
}
}try {
@Override
public void serialize(TimeSeriesContent content, LineWriter writer, String virtualFileNameserialize();
throws Exception {
if (writer} ==finally null){
throw new IllegalArgumentException("writer == null" binaryOutputSteam.close();
}
if (content == null)
return;
throw new IllegalArgumentException("content == null"); }
this.timeSeriesContentbinaryOutputSteam = contentnull;
dateFormat.setTimeZone(content.getDefaultTimeZone())serialize();
}
timeFormat.setTimeZone(content.getDefaultTimeZone());
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) contenttimeSeriesContent.getDefaultTimeZone().getRawOffset() / (double) TimeUnit.HOUR_MILLIS));
for (int i = 0, n = contenttimeSeriesContent.getTimeSeriesCount(); i < n; i++) {
contenttimeSeriesContent.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);
}
}
|