package nl.wldelft.timeseriesparsers;
package nl.wldelft.timeseriesparsers;
import nl.wldelft.util.TextUtils;
import nl.wldelft.util.TimeZoneUtils;
import nl.wldelft.util.io.XmlParser;
import nl.wldelft.util.timeseries.DefaultTimeSeriesHeader;
import nl.wldelft.util.timeseries.TimeSeriesContentHandler;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
/**
*
*
* @autor pelgrim
* @author ekkelenk
* Date: 8/9/13
* Time: 9:35 AM
* Import of content reviewer.
*/
public class AifsMLTimeSeriesParser implements XmlParser<TimeSeriesContentHandler> {
private TimeSeriesContentHandler contentHandler = null;
private XMLStreamReader reader = null;
private DefaultTimeSeriesHeader header = null;
//Use for all
private String title = null;
private String identifier = null;
private String sequenceNumber = null;
@Override
public void parse(XMLStreamReader reader, String virtualFileName, TimeSeriesContentHandler contentHandler) throws Exception {
this.reader = reader;
this.contentHandler = contentHandler;
header = new DefaultTimeSeriesHeader();
reader.require(XMLStreamConstants.START_DOCUMENT, null, null);
reader.nextTag();
reader.require(XMLStreamConstants.START_ELEMENT, null, "product");
reader.nextTag();
//noinspection SpellCheckingInspection
reader.require(XMLStreamConstants.START_ELEMENT, null, "amoc");
reader.nextTag();
reader.require(XMLStreamConstants.START_ELEMENT, null, "source");
parseIdentifier();
reader.require(XMLStreamConstants.START_ELEMENT, null, "warning");
reader.nextTag();
reader.require(XMLStreamConstants.START_ELEMENT, null, "warning-info");
parseWarningInfo();
parseAreasparseWarningAreas();
}
/**
* filter the warning sequence and warning title text attributes from the text elements.
* they will be used for all areas.
*
* @throws XMLStreamException
*/
private void parseWarningInfo() throws XMLStreamException {
do {
if (!goTo("text","warning-info")) {
// no more text areas, return.
return;
}
// we only need the sequence number and the title.
if (this.sequenceNumber == null || this.title == null) {
String type = reader.getAttributeValue(null, "type");
if (TextUtils.equals("warning_sequence", type)) {
this.sequenceNumber = reader.getElementText();
} else if (TextUtils.equals("warning_title_text", type)) {
this.title = reader.getElementText();
}
}
if (this.sequenceNumber != null && this.title != null) {
// we're finished with the text elements, skip to the areas
goTo("area");
return;
}
} while (reader.hasNext());
}
/**
* Retrieve the identifier from the source element
*
* @throws javax.xml.stream.XMLStreamException;
*/
private void parseIdentifier() throws XMLStreamException {
goTo("identifier");
if (TextUtils.equals(reader.getLocalName(), "identifier")) {
this.identifier = reader.getElementText();
}
// only identifier inside the source tag. Jump to warning.
goTo("warning");
}
/**
* Retrieve the locationId from the forecastarea element(s) and call separate method to parse forecast data
*
* @throws javax.xml.stream.XMLStreamException;
*/
private void parseAreasparseWarningAreas() throws XMLStreamException {
do// {
parse alle areas binnen een warning element.
do {
if (!goTo("area","warning")) {
// return;
}alle area's have been processed or an end tag warning has been found.
String locationId = reader.getAttributeValue(null, "aac") return;
parseForecastData(locationId);
}
String }locationId while= (reader.hasNext()getAttributeValue(null, "aac");
}
/**
// an area *can helperhave classmore tothat storeone forecastfore-cast dataperiods.
*/
private static class ForecastData {
parseForecastData(locationId);
} private String parameter = null;
while (reader.hasNext());
}
/**
* helper privateclass Stringto valuestore =forecast null;data.
*/
private Stringstatic unitclass =ForecastData null;{
private String localTimeparameter = null;
private String value = null;
private String unit = null;
private String localTime = null;
private String comment = null;
}
/**
* Retrieve the variable, value, unit, time and comment from the forecast data element(s)
* Retrieve comment text from forecast text element
*
return true if more * forecast periods are available
*
* @throws javax.xml.stream.XMLStreamException;
*/
private void parseForecastData(String locationId) throws XMLStreamException {
// not every area has a forecast-period.
Try to find the if (!goTo("next forecast-period")) {,
// but stop if return;
not found, or an end-tag area has been }found.
/while (goTo("forecast-period","area")) {
// get the start-time-utc
String externalForecastTime = reader.getAttributeValue(null, "start-time-utc");
// the sequences are split over different elements. We have to merge them. We'll use a sequence map with String[].
Map<String, ForecastData> forecastSequenceMap = new LinkedHashMap();
do {
if (!goTo("element")) break;
// process all elements until we reach the end tag Stringforecast-period.
sequence = reader.getAttributeValue(null, "sequence");
ForecastData forecastData = forecastSequenceMap.get(sequence);if (!goTo("element", "forecast-period")) {
if (forecastData == null) {
// break so we can write this forecast as a timeseries.
forecastData = new ForecastData();
break;
forecastSequenceMap.put(sequence, forecastData);
}
String typesequence = reader.getAttributeValue(null, "typesequence");
String units = reader.getAttributeValue(null, "units");
if (sequence == null) {
// 0 = parameter, 1 = value 2 = unit// we 3only =want localelements timewhere sequence have 4been commentfilled.
if (TextUtils.equals("prediction", type)) {
break;
// time-local will be set from}
the prediction element. See comment in [FEWS-11311]
String timeLocalForecastData forecastData = readerforecastSequenceMap.getAttributeValue(null, "time-local"get(sequence);
Stringif value(forecastData == reader.getElementText();
null) {
forecastData.parameter = "prediction" new ForecastData();
forecastData.localTime = timeLocal;
forecastSequenceMap.put(sequence, forecastData);
}
forecastData.comment = value;
}String elsetype if= (TextUtilsreader.equalsgetAttributeValue("value"null, "type"));
{
String forecastData.valueunits = reader.getElementText(getAttributeValue(null, "units");
forecastData.unit = units;
if (TextUtils.equals("prediction", type)) {
}
// time-local will be set from }the whileprediction (reader.hasNext());element. See comment in [FEWS-11311]
//noinspection SpellCheckingInspection
header.setForecastTime(TimeZoneUtils.GMT String timeLocal = reader.getAttributeValue(null, "yyyy-MM-dd'T'HH:mm:ss'Z'", externalForecastTime);
time-local");
String value = headerreader.setLocationIdgetElementText(locationId);
// The forecast sequence map contains a string array with theforecastData.parameter following elements:
= "prediction";
// 0 = parameter, 1 = value 2 = unit 3forecastData.localTime = local timetimeLocal;
4 comment
for (ForecastData forecastData : forecastSequenceMap.values()) {
forecastData.comment = value;
contentHandler.setComment(identifier + " #" + sequenceNumber + ": " +} titleelse + '\n' + forecastData.comment);
if (TextUtils.equals("value", type)) {
header.setParameterId(forecastData.parameter);
forecastData.value = contentHandlerreader.setValue('.', forecastData.valuegetElementText();
header.setUnit(forecastData.unit) = units;
long rawTimeZoneOffset = TimeZoneUtils.parseRawTimeZoneOffset(forecastData.localTime.substring(forecastData.localTime.length() - 6));
}
} while contentHandler(reader.setTime(TimeZoneUtils.getTimeZone(rawTimeZoneOffset), "yyyy-MM-dd'T'HH:mm:ss", forecastData.localTime.substring(0, forecastData.localTime.length() - 6));
hasNext());
// All events have contentHandler.setTimeSeriesHeader(header);
been collected
writeTimeSerie(locationId, externalForecastTime, forecastSequenceMap.values());
}
}
/**
* Write a time serie header and it's events based on the forecastData.
* @param locationId location of the timeserie
* @param externalForecastTime forecast time.
* @param forecastCollection collection of forecast events.
*/
private void writeTimeSerie(String locationId, String externalForecastTime, Collection<ForecastData> forecastCollection) {
//noinspection SpellCheckingInspection
header.setForecastTime(TimeZoneUtils.GMT, "yyyy-MM-dd'T'HH:mm:ss'Z'", externalForecastTime);
header.setLocationId(locationId);
for (ForecastData forecastData : forecastCollection) {
contentHandler.setComment(identifier + " #" + sequenceNumber + ": " + title + '\n' + forecastData.comment);
header.setParameterId(forecastData.parameter);
contentHandler.setValue('.', forecastData.value);
header.setUnit(forecastData.unit);
long rawTimeZoneOffset = TimeZoneUtils.parseRawTimeZoneOffset(forecastData.localTime.substring(forecastData.localTime.length() - 6));
contentHandler.setTime(TimeZoneUtils.getTimeZone(rawTimeZoneOffset), "yyyy-MM-dd'T'HH:mm:ss", forecastData.localTime.substring(0, forecastData.localTime.length() - 6));
contentHandler.setTimeSeriesHeader(header);
contentHandler.applyCurrentFields();
}
}
/**
* This method skips elements until a start element with the corresponding local name is found and returns true, if not returns false
* Warning: if the element is not found the reader is skipped until the end of the document
*
* @param localName local element name
* @return boolean return true if the localname was found.
* @throws XMLStreamException
*/
private boolean goTo(String localName) throws XMLStreamException {
do {
if (!reader.hasNext()) return false;
reader.next();
} while (!reader.isStartElement() || !TextUtils.equals(reader.getLocalName(), localName));
return true;
}
/**
* This method skips elements until a start element with the corresponding local name is found and returns true,
* If an end tag with name endName is found, return contentHandler.applyCurrentFields();false.
* Otherwise returns }false
}
* Warning: if /**
the element is not found *and Thisthe methodend skipstag elementsis untilnot afound, startthe elementreader withis theskipped correspondinguntil localthe nameend isof found and returns true, if not returns falsethe document
*
* Warning: if the element is not found the reader is skipped until the end of the document @param localName local name to look for
* @param endName local name of end tag to find.
*
* @param localName
* @return boolean @return boolean return true if localName cannot be found or the endName end tag has been found.
* @throws XMLStreamException
*/
private boolean goTo(String localName, String endName) throws XMLStreamException {
do {
if (!reader.hasNext()) return false;
if (reader.isEndElement() && TextUtils.equals(reader.getLocalName(), endName)) return false;
reader.next();
} while (!reader.isStartElement() || !TextUtils.equals(reader.getLocalName(), localName));
return true;
}
}
|