package nl.wldelft.fews.pi;
import com.sun.org.apache.xml.internal.serialize.OutputFormatnl.wldelft.util.Arguments;
import nl.wldelft.util.BinaryUtils;
import nl.wldelft.util.Clasz;
import com.sun.org.apache.xml.internal.serialize.XMLSerializernl.wldelft.util.FastDateFormat;
import nl.wldelft.util.FileUtils;
import nl.wldelft.util.MathUtils;
import nl.wldelft.util.FastDateFormatNumberType;
import nl.wldelft.util.FileUtilsObjectUtils;
import nl.wldelft.util.Period;
import nl.wldelft.util.TimeUnitProperties;
import nl.wldelft.util.io.LineWriterTextUtils;
import nl.wldelft.util.io.LittleEndianDataOutputStreamTimeZoneUtils;
import nl.wldelft.util.iofunction.TextSerializerSupplier;
import nl.wldelft.util.io.VirtualOutputDir;
import nl.wldelft.util.io.VirtualOutputDirConsumer;
import nl.wldelft.util.timeseriesio.TimeSeriesContentXmlChunkedSerializer;
import nl.wldelft.util.timeseries.TimeSeriesHeaderProduct;
import nl.wldelft.util.timeseries.TimeStepProductInfo;
import orgnl.xmlwldelft.util.saxtimeseries.ContentHandlerStatistics;
import orgnl.wldelft.xmlutil.saxtimeseries.SAXExceptionTimeSeriesContent;
import orgnl.xmlwldelft.saxutil.helperstimeseries.AttributesImplTimeSeriesHeader;
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;import nl.wldelft.util.timeseries.TimeStep;
import nl.wldelft.util.timeseries.TimesOfDayDaylightSavingTimeStep;
import nl.wldelft.util.timeseries.TimesOfDayTimeStep;
import nl.wldelft.util.timeseries.TimesOfHourTimeStep;
import nl.wldelft.util.timeseries.ValueSource;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteOrder;
import java.util.Arrays;
import java.util.Locale;
import java.util.TimeZone;
public class PiTimeSeriesSerializer implements XmlChunkedSerializer<TimeSeriesContent>, VirtualOutputDirConsumer {
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_2 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_2);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_3 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_3);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_4 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_4);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_5 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_5);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_6 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_6);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_7 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_7);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_8 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_8);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_9 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_9);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_10 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_10);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_11 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_11);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_12 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_12);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_13 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_13);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_14 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_14);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_15 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_15);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_16 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_16);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_17 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_17);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_18 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_18);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_19 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_19);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_20 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_20);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_21 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_21);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_22 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_22);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_23 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_23);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_24 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_24);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_25 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_25);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_26 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_26);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_27 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_27);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_28 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_28);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_29 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_29);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_30 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_30);
public static final Supplier<PiTimeSeriesSerializer, Error> VERSION_1_31 = () -> new PiTimeSeriesSerializer(PiVersion.VERSION_1_31);
private static final int BUFFER_SIZE = 2048;
private TimeZone timeZone = null;
private String missingValueText = "NaN";
private float missingValue = Float.NaN;
private String virtualFileName = null;
private boolean skipEmptyTimeSeries = false;
public void setSkipEmptyTimeSeries(boolean skipEmptyTimeSeries) {
this.skipEmptyTimeSeries = skipEmptyTimeSeries;
}
public void setUseMilliseconds (boolean useMilliseconds) {this.useMilliseconds = useMilliseconds; }
public enum EventDestination {
XML_EMBEDDED, SEPARATE_BINARY_FILE, ONLY_HEADERS
}
public enum EnsembleMemberFormat {
INDEX, ID, HIDE
}
private EventDestination eventDestination = EventDestination.XML_EMBEDDED;
private PiVersion version = PiVersion.VERSION_1_2;
private EnsembleMemberFormat ensembleMemberFormat = EnsembleMemberFormat.INDEX;
private boolean anyTimeSeriesWritten = false;
private FastDateFormat dateFormat = null;
private FastDateFormat timeFormat = null;
private boolean useMilliseconds = false;
private TimeSeriesContent timeSeriesContent = null;
private XMLStreamWriter writer = null;
private VirtualOutputDir virtualOutputDir = null;
private OutputStream binaryOutputSteam = null;
private byte[] byteBuffer = null;
private char[] charBuffer = null;
private float[] floatBuffer = null;
private int bufferPos = 0;
private long[] cachedTimes = new long[6];
private String[] cachedDateStrings = new String[6];
private String[] cachedTimeStrings = new String[6];
private int cachedSeconds = -1;
private String cachedSecondsString = null;
private float[][] domainAxesValues = null;
private float[][] newDomainValues = null;
public PiTimeSeriesSerializer() {
}
public PiTimeSeriesSerializer(PiVersion version) {
this.version = version;
}
public void setEventDestination(EventDestination eventDestination) {
this.eventDestination = eventDestination;
}
public PiVersion getVersion() {
return version;
}
public void setVersion(PiVersion version) {
Arguments.require.notNull(version);
this.version = version;
}
public void setEnsembleMemberFormat(EnsembleMemberFormat ensembleMemberFormat) {
Arguments.require.notNull(ensembleMemberFormat);
this.ensembleMemberFormat = ensembleMemberFormat;
}
@Override
public void setVirtualOutputDir(VirtualOutputDir virtualOutputDir) {
this.virtualOutputDir = virtualOutputDir;
}
@Override
public void serializeStartDocument(XMLStreamWriter streamWriter, String virtualFileName) throws Exception {
anyTimeSeriesWritten = false;
this.virtualFileName = virtualFileName;
this.writer = streamWriter;
if (ensembleMemberFormat == EnsembleMemberFormat.ID && version.getIntId() < PiVersion.VERSION_1_10.getIntId()) {
throw new IOException("ensembleMemberId not supported for pi version " + version);
}
writeStartDocument();
String binFileName = FileUtils.getPathWithOtherExtension(virtualFileName, "bin");
if (virtualOutputDir != null) virtualOutputDir.delete(binFileName);
if (eventDestination != EventDestination.SEPARATE_BINARY_FILE) return;
if (virtualOutputDir == null)
throw new IllegalStateException("virtualOutputDir == null");
binaryOutputSteam = virtualOutputDir.getOutputStream(binFileName);
bufferPos = 0;
if (byteBuffer != null) return;
byteBuffer = new byte[BUFFER_SIZE * NumberType.FLOAT_SIZE];
floatBuffer = new float[BUFFER_SIZE];
}
public void writeStartDocument() throws XMLStreamException {
writer.writeStartDocument();
writer.writeStartElement("TimeSeries");
writer.setDefaultNamespace("http://www.wldelft.nl/fews/PI");
writer.writeNamespace("", "http://www.wldelft.nl/fews/PI");
writer.writeAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
// writer.writeAttribute("xmlns", "http://www.wldelft.nl/fews/PI");
writer.writeAttribute("xsi:schemaLocation", PiSchemaLocations.get("pi_timeseries.xsd"));
writer.writeAttribute("version", version.toString());
if (version.getIntId() >= PiVersion.VERSION_1_19.getIntId()) {
writer.writeAttribute("xmlns:fs", "http://www.wldelft.nl/fews/fs");
}
}
@Override
public void serializeContent(TimeSeriesContent timeSeriesContent) throws Exception {
if (timeSeriesContent.getTimeSeriesCount() == 0) return;
this.timeSeriesContent = timeSeriesContent;
timeSeriesContent.setRequireEnsembleMemberIndices(ensembleMemberFormat == EnsembleMemberFormat.INDEX);
if (!anyTimeSeriesWritten) {
anyTimeSeriesWritten = true;
initMissingValueText();
initTimeZone();
writeTimeZone();
}
for (int i = 0, n = timeSeriesContent.getTimeSeriesCount(); i < n; i++) {
timeSeriesContent.setTimeSeriesIndex(i);
if (skipEmptyTimeSeries && timeSeriesContent.isTimeSeriesEmpty()) continue;
if (!timeSeriesContent.isTimeSeriesEmpty()) timeSeriesContent.setContentTimeIndex(0);
writer.writeStartElement("series");
writeHeader();
writeEvents();
writer.writeEndElement();
}
}
@Override
public void serializeEndDocument() throws Exception {
if (!anyTimeSeriesWritten) throw new PiTimeSeriesNotFoundException("No TimeSeries to be Written to " + virtualFileName);
writer.writeEndElement();
writer.writeEndDocument();
if (eventDestination != EventDestination.SEPARATE_BINARY_FILE) return;
flushBinEvents();
binaryOutputSteam.close();
binaryOutputSteam = null;
}
private void initMissingValueText() {
missingValueText = timeSeriesContent.getDefaultMissingValue('.');
try {
missingValue = Float.parseFloat(missingValueText);
} catch (NumberFormatException e) {
timeSeriesContent.setMissingValue(Float.NaN);
missingValue = Float.NaN;
missingValueText = "NaN";
}
}
private void initTimeZone() {
if (!ObjectUtils.equals(timeZone, this.timeSeriesContent.getDefaultTimeZone())) {
Arrays.fill(cachedTimes, Long.MIN_VALUE);
timeZone = timeSeriesContent.getDefaultTimeZone();
}
dateFormat = FastDateFormat.getInstance("yyyy-MM-dd", timeZone, Locale.US, dateFormat);
timeFormat = useMilliseconds?FastDateFormat.getInstance("HH:mm:ss.SSS", timeZone, Locale.US, timeFormat) : FastDateFormat.getInstance("HH:mm:ss", timeZone, Locale.US, timeFormat);
}
private void writeTimeZone() throws XMLStreamException {
if (timeZone.useDaylightTime()) {
PiSerializerUtils.writeElement(writer, "daylightSavingObservingTimeZone", timeZone.getID());
} else {
PiSerializerUtils.writeElement(writer, "timeZone", String.valueOf(TimeZoneUtils.getTimeZoneOffsetHours(timeZone)));
}
}
private void writeHeader() throws Exception {
TimeSeriesHeader header = timeSeriesContent.getTimeSeriesHeader();
PiTimeSeriesHeader piHeader = header instanceof PiTimeSeriesHeader ? (PiTimeSeriesHeader) header : new PiTimeSeriesHeader();
writer.writeStartElement("header");
PiSerializerUtils.writeElement(writer, "type", header.getParameterType() == null ? "instantaneous" : header.getParameterType().getName());
if (version.getIntId() >= PiVersion.VERSION_1_17.getIntId() && header.getModuleInstanceId() != null) PiSerializerUtils.writeElement(writer, "moduleInstanceId", header.getModuleInstanceId());
PiSerializerUtils.writeElement(writer, "locationId", header.getLocationId() == null ? "unknown" : header.getLocationId());
PiSerializerUtils.writeElement(writer, "parameterId", header.getParameterId() == null ? "unknown" : header.getParameterId());
if (version.getIntId() >= PiVersion.VERSION_1_4.getIntId()) {
for (int i = 0, n = header.getQualifierCount(); i < n; i++) {
PiSerializerUtils.writeElement(writer, "qualifierId", header.getQualifierId(i));
}
}
if (version.getIntId() >= PiVersion.VERSION_1_4.getIntId() && header.getEnsembleId() != null && !header.getEnsembleId().equals("main")) {
if (ensembleMemberFormat != EnsembleMemberFormat.HIDE) {
writeOptionalElement("ensembleId", header.getEnsembleId());
writeOptionalElement(ensembleMemberFormat == EnsembleMemberFormat.INDEX
? "ensembleMemberIndex" : "ensembleMemberId", header.getEnsembleMemberId());
}
}
writeTimeStep(header);
writePeriod();
if (version.getIntId() >= PiVersion.VERSION_1_5.getIntId()) writeTime("forecastDate", header.getForecastTime(), 2);
if (version.getIntId() >= PiVersion.VERSION_1_20.getIntId()) writeTime("approvedDate", header.getApprovedTime(), 4);
PiSerializerUtils.writeElement(writer, "missVal", timeSeriesContent.getDefaultMissingValue('.'));
writeOptionalElement("longName", piHeader.getLongName());
writeOptionalElement("stationName", header.getLocationName());
if (version.getIntId() >= PiVersion.VERSION_1_7.getIntId()) PiSerializerUtils.writeCoordinates(writer, header, version);
writeOptionalElement("units", header.getUnit());
for (int i = 0, n = header.getDomainParameterCount(); i < n; i++) {
writeDomainAxis(header.getDomainParameterId(i), header.getDomainUnit(i));
}
writeOptionalElement("sourceOrganisation", piHeader.getSourceOrganisation());
writeOptionalElement("sourceSystem", piHeader.getSourceSystem());
writeOptionalElement("fileDescription", piHeader.getFileDescription());
if (header.getCreationTime() != Long.MIN_VALUE) {
updateDateTimeStringCache(header.getCreationTime(), 3);
PiSerializerUtils.writeElement(writer, "creationDate", cachedDateStrings[3]);
PiSerializerUtils.writeElement(writer, "creationTime", cachedTimeStrings[3]);
}
writeOptionalElement("region", piHeader.getRegion());
if (header.getHighLevelThresholdCount() > 0) writeHighThresholds(header);
if (version.getIntId() >= PiVersion.VERSION_1_16.getIntId() && timeSeriesContent.hasStatistics()) writeTimeSeriesStatistics();
if (version.getIntId() >= PiVersion.VERSION_1_26.getIntId() && timeSeriesContent.getProduct() != null) writeProduct();
writer.writeEndElement();
}
private void writeProduct() throws XMLStreamException {
Product product = timeSeriesContent.getProduct();
writer.writeStartElement("product");
writer.writeAttribute("id", product.getId());
writer.writeAttribute("name", product.getName());
writeTime("productDate", product.getProductTime(), 5);
writer.writeEmptyElement("category");
writer.writeAttribute("id", product.getCategory().getId());
writer.writeAttribute("name", product.getCategory().getName());
if (product.getProductInfos() != null && product.getProductInfos().length > 0) {
for (int i = 0; i < product.getProductInfos().length; i++) {
ProductInfo productInfo = product.getProductInfos()[i];
writer.writeStartElement("productInfo");
writer.writeStartElement("user");
writer.writeCharacters(productInfo.getUser());
writer.writeEndElement();
writer.writeStartElement("confidence");
writer.writeCharacters(productInfo.getConfidence());
writer.writeEndElement();
writer.writeStartElement("classification");
writer.writeCharacters(productInfo.getClassification());
writer.writeEndElement();
writer.writeStartElement("comment");
writer.writeCharacters(productInfo.getComment());
writer.writeEndElement();
writer.writeEndElement(); // productInfo
}
}
writer.writeEndElement(); // product
}
private void writeTimeSeriesStatistics() throws XMLStreamException {
Statistics timeSeriesInfoStatistics = timeSeriesContent.getStatistics();
if (timeSeriesInfoStatistics == null) return;
if (timeSeriesInfoStatistics.hasFirstValueTime()) writeTime("firstValueTime", timeSeriesInfoStatistics.getFirstValueTime(), 0);
if (timeSeriesInfoStatistics.hasLastValueTime()) writeTime("lastValueTime", timeSeriesInfoStatistics.getLastValueTime(), 1);
if (timeSeriesInfoStatistics.hasMaxValue()) PiSerializerUtils.writeElement(writer, "maxValue", timeSeriesInfoStatistics.getMaxValue('.'));
if (timeSeriesInfoStatistics.hasMinValue()) PiSerializerUtils.writeElement(writer, "minValue", timeSeriesInfoStatistics.getMinValue('.'));
if (timeSeriesInfoStatistics.hasValueCount()) PiSerializerUtils.writeElement(writer,"valueCount", Integer.toString(timeSeriesInfoStatistics.getValueCount()));
if (timeSeriesInfoStatistics.hasMaxWarningLevelName()) PiSerializerUtils.writeElement(writer, "maxWarningLevelName", timeSeriesInfoStatistics.getMaxWarningLevelName());
}
private void writeHighThresholds(TimeSeriesHeader header) throws XMLStreamException {
boolean thresholdsAvailable = false;
boolean firstThreshold = true;
for (int i = 0; i < header.getHighLevelThresholdCount(); i++) {
TimeSeriesHeader.Threshold threshold = header.getHighLevelThreshold(i);
String id = threshold.getId();
String name = threshold.getName();
//Note: here value can be NaN for LevelThresholdValues that have different values for different aggregationTimeSpans configured.
float value = threshold.getValue();
if (Float.isNaN(value)) {
continue;
}
thresholdsAvailable = true;
if (firstThreshold) {
writer.writeStartElement("thresholds");
firstThreshold = false;
}
if (version.getIntId() >= PiVersion.VERSION_1_14.getIntId()) {
String label = null;
String comment = null;
String description = null;
if (version.getIntId() >= PiVersion.VERSION_1_22.getIntId()){
label = threshold.getLabel();
}
if (version.getIntId() >= PiVersion.VERSION_1_27.getIntId()){
comment = threshold.getComment();
description = threshold.getDescription();
}
if (threshold.getGroupCount() > 0) {
for (int j = 0, m = threshold.getGroupCount(); j < m; j++) {
String groupId = threshold.getGroupId(j);
String groupName = threshold.getGroupName(j);
if (threshold.getGroupCount() == 1 && groupId != null && groupId.equalsIgnoreCase("DeprecatedThresholdsConfigured")) {
groupId = null;
groupName = null;
}
writeThreshold(id, name, value, groupId, groupName, label, description, comment);
}
} else {
writeThreshold(id, name, value, null, null, label, description, comment);
}
} else {
writeThreshold(id, name, value, null, null, null, null, null);
}
}
if (thresholdsAvailable) {
writer.writeEndElement();
}
}
private void writeThreshold(String id, String name, float value, String groupId, String groupName, String label, String description, String comment) throws XMLStreamException {
writer.writeStartElement("highLevelThreshold");
writer.writeAttribute("id", id);
writer.writeAttribute("name", name);
if (label != null) writer.writeAttribute("label", label);
if (description != null) writer.writeAttribute("description", description);
if (comment != null) writer.writeAttribute("comment", comment);
writer.writeAttribute("value", Float.toString(value));
if (groupId != null) writer.writeAttribute("groupId", groupId);
if (groupName != null) writer.writeAttribute("groupName", groupName);
writer.writeEndElement();
}
private void writePeriod() throws XMLStreamException {
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(), 0);
writeTime("endDate", headerPeriod.getEndTime(), 1);
}
private void updateDateTimeStringCache(long time, int cacheIndex) {
if (cachedTimes[cacheIndex] == time) return;
cachedTimes[cacheIndex] = time;
cachedDateStrings[cacheIndex] = dateFormat.format(time);
cachedTimeStrings[cacheIndex] = timeFormat.format(time);
}
private void writeTime(String name, long time, int cacheIndex) throws XMLStreamException {
if (time == Long.MIN_VALUE) return;
writer.writeEmptyElement(name);
updateDateTimeStringCache(time, cacheIndex);
writer.writeAttribute("date", cachedDateStrings[cacheIndex]);
writer.writeAttribute("time", cachedTimeStrings[cacheIndex]);
}
private void writeTimeStep(TimeSeriesHeader header) throws XMLStreamException {
writer.writeEmptyElement("timeStep");
TimeStep timeStep = header.getTimeStep();
// todo add support for month time step
if (timeStep.isEquidistantMillis()) {
writer.writeAttribute("unit", "second");
int seconds = (int) (timeStep.getStepMillis() / 1000);
if (cachedSeconds != seconds) {
cachedSecondsString = TextUtils.toString(seconds);
cachedSeconds = seconds;
}
writer.writeAttribute("multiplier", cachedSecondsString);
} else if (timeStep instanceof TimesOfDayDaylightSavingTimeStep && version.getIntId() >= PiVersion.VERSION_1_18.getIntId()) {
writer.writeAttribute("times", PiCastorUtils.getTimesOfDayString((TimesOfDayDaylightSavingTimeStep) timeStep));
} else if (timeStep instanceof TimesOfDayTimeStep && version.getIntId() >= PiVersion.VERSION_1_18.getIntId()) {
writer.writeAttribute("times", PiCastorUtils.getTimesOfDayString((TimesOfDayTimeStep) timeStep));
} else if (timeStep instanceof TimesOfHourTimeStep && version.getIntId() >= PiVersion.VERSION_1_24.getIntId()) {
writer.writeAttribute("minutes", PiCastorUtils.getTimesOfHourString((TimesOfHourTimeStep) timeStep));
} else {
writer.writeAttribute("unit", "nonequidistant");
}
}
private void writeEvents() throws Exception {
switch (eventDestination) {
case ONLY_HEADERS:
return;
case SEPARATE_BINARY_FILE:
writeBinEvents();
return;
case XML_EMBEDDED:
writeXmlEvents();
}
}
publicprivate void setVersionwriteBinEvents(PiVersion version)) throws Exception {
iffor (versionint i = 0, n = null) timeSeriesContent.getContentTimeCount(); i < n; i++) {
throw new IllegalArgumentException("version == null");
timeSeriesContent.setContentTimeIndex(i);
this.version = version;
if }
(!timeSeriesContent.isTimeAvailable()) continue;
@Override
public void setVirtualOutputDir(VirtualOutputDir virtualOutputDir) {
if (bufferPos == this.virtualOutputDir = virtualOutputDirBUFFER_SIZE) flushBinEvents();
}
@Override
public void serialize(TimeSeriesContent timeSeriesContent, LineWriter writer, String virtualFileName) throws Exception {
floatBuffer[bufferPos++] = timeSeriesContent.getValue();
}
}
private void this.timeSeriesContent = timeSeriesContent;
flushBinEvents() throws IOException {
if this.writer(bufferPos == writer0) return;
dateFormatBinaryUtils.setTimeZone(this.timeSeriesContent.getDefaultTimeZone());
timeFormat.setTimeZone(this.timeSeriesContent.getDefaultTimeZone());
copy(floatBuffer, 0, bufferPos, byteBuffer, 0, bufferPos * NumberType.FLOAT_SIZE, ByteOrder.LITTLE_ENDIAN);
if (eventDestination == EventDestination.SEPARATE_BINARY_FILE) {binaryOutputSteam.write(byteBuffer, 0, bufferPos * NumberType.FLOAT_SIZE);
bufferPos = 0;
if (virtualOutputDir == null) }
private void writeXmlEvents() throws XMLStreamException {
throw new IllegalStateException("virtualOutputDirProperties properties == null") Properties.NONE;
TimeSeriesHeader binaryOutputSteamheader = new LittleEndianDataOutputStream(virtualOutputDir.getOutputStream(FileUtils.getPathWithOtherExtension(virtualFileName, "bin")))timeSeriesContent.getTimeSeriesHeader();
domainAxesValues try {= null;
int domainParameterCount serialize= header.getDomainParameterCount();
float[] floatBuffer } finally {= this.floatBuffer;
for (int i = 0, n = binaryOutputSteamtimeSeriesContent.closegetContentTimeCount();
i < n; i++) {
}
timeSeriesContent.setContentTimeIndex(i);
return;
if }
(!timeSeriesContent.isTimeAvailable()) continue;
binaryOutputSteam = null;
Properties newProperties = serializetimeSeriesContent.getProperties();
}
private void serialize() throws Exceptionif (!newProperties.equals(properties)) {
XMLSerializer serializer = new XMLSerializer();
properties = newProperties;
OutputFormat of = new OutputFormat("XML", "UTF-8", true);
serializerPiSerializerUtils.setOutputFormat(of);
serializer.setOutputCharStream(writerwriteProperties(properties, version, writer, dateFormat, timeFormat);
xmlContentHandler = serializer.asContentHandler();
}
boolean valueMissing = xmlContentHandlertimeSeriesContent.startDocumentisValueMissing();
attributesBuffer.clear();
if (domainParameterCount > addAttribute("xmlns", "http://www.wldelft.nl/fews/PI");0 && !valueMissing) {
addAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
int addAttribute("xsi:schemaLocation", PiSchemaLocations.get("pi_timeseries.xsd"));
valueCount = writeAxisValues();
if addAttribute("version", version.toString());
(floatBuffer == null || floatBuffer.length != valueCount) {
floatBuffer = xmlContentHandler.startElement("", "TimeSeries", "TimeSeries", attributesBuffer);
new float[valueCount];
writeElement("timeZone", String.valueOf((double) timeSeriesContent.getDefaultTimeZone().getRawOffset() / (double) TimeUnit.HOUR_MILLIS))this.floatBuffer = floatBuffer;
for (int i = 0, n = timeSeriesContent.getTimeSeriesCount(); i < n; i++) {
}
timeSeriesContent.setTimeSeriesIndexreadValues(ifloatBuffer);
}
xmlContentHandler.startElement(null, null, "series", null);
if (domainParameterCount == 0 || valueMissing) {
writeHeader();
writeEvents(writer.writeEmptyElement("event");
xmlContentHandler.endElement(null, null, "series");} else {
}
xmlContentHandlerwriter.endElement(null, null, "TimeSerieswriteStartElement("event");
xmlContentHandler.endDocument();
}
private void writeEventswriteTime();
throws Exception {
forif (intdomainParameterCount i == 0, n = timeSeriesContent.getContentTimeCount) writeValue(); i < n; i++) {
timeSeriesContentPiSerializerUtils.setContentTimeIndex(iwriteFlagsUserComment(writer, timeSeriesContent, version);
if (!timeSeriesContent.isTimeAvailable())domainParameterCount == 0 || valueMissing) continue;
writeEvent(timeSeriesContent.getTime());
}int valuesPerRow = domainParameterCount == 1 ? 1 : domainAxesValues[1].length;
}
private void writeHeader() throws Exception {
writeValues(floatBuffer, valuesPerRow);
TimeSeriesHeader header = timeSeriesContentwriter.getTimeSeriesHeaderwriteEndElement();
PiTimeSeriesHeader}
piHeader = header instanceof PiTimeSeriesHeader ? (PiTimeSeriesHeader) header : new PiTimeSeriesHeader(); }
private void xmlContentHandler.startElement(null, null, "header", null);
writeValue() throws XMLStreamException {
writeElement("type", header.getParameterType() == null ? "instantaneous" : header.getParameterType().getName()) String stringValue = timeSeriesContent.getValue('.');
writeElementwriter.writeAttribute("locationIdvalue", header.getLocationId() == null ? "unknown" : header.getLocationId());
writeElement("parameterId", header.getParameterIdstringValue);
if (version.ordinal() >= PiVersion.VERSION_1_31.ordinal() && timeSeriesContent.getValueSource() == null ? "unknown" : header.getParameterId())ValueSource.MANUAL) writer.writeAttribute("valueSource", "MAN");
if (version.ordinal() >=< PiVersion.VERSION_1_423.ordinal()) {return;
if (timeSeriesContent.isValueMissing()) return;
for (int i = 0,float nvalue = headertimeSeriesContent.getQualifierCountgetValue(); i < n; i++) {
if (timeSeriesContent.getMinValue() != value) writer.writeAttribute("minValue", timeSeriesContent.getMinValue('.'));
if (timeSeriesContent.getMaxValue() != writeElementvalue) writer.writeAttribute("qualifierIdmaxValue", headertimeSeriesContent.getQualifierIdgetMaxValue(i'.'));
if (version.ordinal() >= }PiVersion.VERSION_1_29.ordinal() && !"".equals(timeSeriesContent.getDetectionSymbol())) writer.writeAttribute("detection", timeSeriesContent.getDetectionSymbol());
}
private }
void writeTime() throws XMLStreamException {
if (version.ordinal() >= PiVersion.VERSION_1_4.ordinal() && header.getEnsembleId() != null && !header.getEnsembleId().equals("main")) { long time = timeSeriesContent.getTime();
String dateText writeOptionalElement("ensembleId", header.getEnsembleId()= dateFormat.format(time);
writeOptionalElementwriter.writeAttribute("ensembleMemberIndexdate", header.getEnsembleMemberIndex(dateText));
}
String timeText writeTimeStep(header= timeFormat.format(time);
writePeriod(writer.writeAttribute("time", timeText);
if (version.ordinal() >=< PiVersion.VERSION_1_523.ordinal()) writeTime("forecastDate", header.getForecastTime())return;
long writeElement("missVal", Float.toString(timeSeriesContent.getDefaultMissingValue()));
startTime = timeSeriesContent.getRangeStartTime();
long writeOptionalElement("longName", piHeader.getLongName());
endTime = timeSeriesContent.getRangeEndTime();
writeAttribute("startDate", dateFormat, writeOptionalElement("stationName", header.getLocationName()startTime, time, dateText);
writeOptionalElementwriteAttribute("unitsstartTime", timeFormat, header.getUnit())startTime, time, timeText);
writeOptionalElementwriteAttribute("sourceOrganisationendDate", piHeader.getSourceOrganisation()dateFormat, endTime, time, dateText);
writeOptionalElementwriteAttribute("sourceSystemendTime" , piHeader.getSourceSystem()timeFormat, endTime, time, timeText);
}
writeOptionalElement("fileDescription", piHeader.getFileDescription());
if (header.getCreationTime() != Long.MIN_VALUE)private void writeAttribute(String attributeName, FastDateFormat dateFormat, long time, long defaultTime, String defaultText) throws XMLStreamException {
if (time == writeElement("creationDate", dateFormat.format(header.getCreationTime()))defaultTime) return;
String text writeElement("creationTime", timeFormat= dateFormat.format(header.getCreationTime())time);
}
writeOptionalElement("region", piHeader.getRegion());
if (TextUtils.equals(text, defaultText)) return;
xmlContentHandlerwriter.endElementwriteAttribute(nullattributeName, null, "header"text);
}
private voidint writePeriodwriteAxisValues() throws SAXExceptionXMLStreamException {
TimeStepTimeSeriesHeader timeStepheader = timeSeriesContent.getTimeSeriesHeader().getTimeStep();
Periodif period(domainAxesValues == timeSeriesContent.getTimeSeriesPeriod();
Period headerPeriod;
if (period == Period.NEVER) {
null) domainAxesValues = new float[header.getDomainParameterCount()][];
if (newDomainValues == null) newDomainValues = // create a dummy period
new float[header.getDomainParameterCount()][];
assert !timeSeriesContent.isValueMissing();
long int nowres = timeStep.nearestTime(System.currentTimeMillis())1;
for (int i = headerPeriod0, n = new Period(now, nowheader.getDomainParameterCount();
i < } elsen; i++) {
float[] headerPeriodvalues = periodnewDomainValues[i];
}
int valueCount writeTime("startDate", headerPeriod.getStartTime())= timeSeriesContent.getDomainAxisValueCount(i);
writeTime("endDate", headerPeriod.getEndTime());
}
res *= valueCount;
private void writeTime(String name, long time) throws SAXException {
if (values == null || if (time == Long.MIN_VALUE) return;
values.length != valueCount) {
attributesBuffer.clear();
values addAttribute("date", dateFormat.format(time));
= new float[valueCount];
addAttribute("time", timeFormat.format(time));
newDomainValues[i] = writeAttributes(name)values;
}
private void writeTimeStep(TimeSeriesHeader header) throws SAXException {
}
TimeStep timeStep = headertimeSeriesContent.getTimeStepreadDomainAxisValues();
i, values);
if attributesBuffer(Arrays.clear()equals(values, domainAxesValues[i])) continue;
// todo add support for month time step
writer.writeStartElement("domainAxisValues");
if (timeStep.isEquidistantMillis()) {writer.writeAttribute("parameterId", header.getDomainParameterId(i));
longint secondsvaluePerRow = timeStep.getStepMillis() / 1000;
addAttribute("unit","second")i == 0 ? 1 : values.length;
addAttributewriteValues("multiplier"values, String.valueOf(seconds)valuePerRow);
} else {domainAxesValues[i] = Clasz.floats.copyOfArray(values);
addAttribute("unit", "nonequidistant"writer.writeEndElement();
}
writeAttributes("timeStep")return res;
}
private void writeEvent(long timewriteValues(float[] values, int valuesPerRow) throws Exception { XMLStreamException {
assert !timeSeriesContent.isValueMissing();
if (eventDestination =char[] buffer = EventDestination.ONLY_HEADERS) returnthis.charBuffer;
if (eventDestinationbuffer == EventDestination.SEPARATE_BINARY_FILEnull) {
binaryOutputSteam.writeFloat(timeSeriesContent.getValue());
buffer = new char[15];
this.charBuffer = returnbuffer;
}
for (int i = 0; i < attributesBuffer.clear();
values.length; i++) {
addAttribute("date", dateFormat.format(time));
addAttribute("time", timeFormat.format(time));
buffer[0] = i % valuesPerRow == 0 ? '\n' : '\t';
float addAttribute("value", timeSeriesContent.getValue('.'));
value = values[i];
addAttribute("flag", timeSeriesContent.getStringFlag());
int endPos;
if (versionMathUtils.ordinalequals() >= PiVersion.VERSION_1_3.ordinal(value, missingValue)) {
String comment = timeSeriesContent.getComment();
missingValueText.getChars(0, missingValueText.length(), buffer, 1);
if (comment != null) addAttribute("comment", timeSeriesContent.getComment());
endPos = missingValueText.length() + 1;
}
} writeAttributes("event");
}
else {
private void writeOptionalElement(String elementName, int index) throws SAXException {
endPos = TextUtils.format(buffer, 1, if (index == -1) returnvalue, '.', 0, 10);
writeElement(elementName, Integer.toString(index));
}
}
private void writeOptionalElement(String elementName, String s) throws SAXException {
writer.writeCharacters(buffer, 0, endPos); // start with space
if (s == null) return;}
if (s.trim().length() == 0) return;
writer.writeCharacters("\n writeElement(elementName, s");
}
private void writeElementwriteOptionalElement(String nameelementName, String values) throws SAXExceptionXMLStreamException {
if xmlContentHandler.startElement(null, null, name, s == null) return;
if xmlContentHandler.characters(values.toCharArraytrim(), 0, value.length.isEmpty()) return;
xmlContentHandler PiSerializerUtils.endElementwriteElement(nullwriter, nullelementName, names);
}
private void writeAttributeswriteDomainAxis(String parameterId, String nameunit) throws SAXExceptionXMLStreamException {
xmlContentHandlerwriter.startElementwriteEmptyElement("", name, name, attributesBufferdomainAxis");
xmlContentHandlerwriter.endElementwriteAttribute(null"parameterId", null, nameparameterId);
}
private voidif addAttribute(Stringunit name, String value) {
attributesBuffer.addAttribute("", name, name, "CDATA", value!= null) writer.writeAttribute("units", unit);
}
}
|