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.io.IOException;
import java.util.CollectionArrayList;
import java.util.LinkedHashMapCollection;
import java.util.MapList;
/**
*
* @autor pelgrim
* @author ekkelenk
* Date: 8/9/13
* Time: 9:35 AM
* Import of content reviewer.the warnings from the AifsML format
*/
public class AifsMLTimeSeriesParser implements XmlParser<TimeSeriesContentHandler> {
public static final String HYFS_MARKER = "[HYFS]";
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 sequenceNumberexternalForecastTime = null;
private@Override
String externalForecastTime = 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");
parseIdentifier();
parseForecastTime();
reader.require(XMLStreamConstants.START_ELEMENT, null, if (!XmlStreamReaderUtils.goTo(reader, "warning")); {
reader.nextTag();
// There has to reader.require(XMLStreamConstants.START_ELEMENT, null, "warning-info");be a warning element.
parseWarningInfo();
throw new IOException("Input parseWarningAreas();
}
/**file doesn't contain a warning element: " + virtualFileName);
* Retrieve the identifier}
from the source element
*reader.require(XMLStreamConstants.START_ELEMENT, null, "warning");
* @throws XMLStreamExceptionreader.nextTag();
*/
private void parseIdentifier() throws XMLStreamException {reader.require(XMLStreamConstants.START_ELEMENT, null, "warning-info");
goToparseWarningInfo("identifier");
if (TextUtils.equals(reader.getLocalName(), "identifier")) {
this.identifier = reader.getElementText()parseWarningAreas();
}
}
/**
* Retrieve the identifier from the source element
*
* @throws XMLStreamException;
*/
private void parseForecastTimeparseIdentifier() throws XMLStreamException {
XmlStreamReaderUtils.goTo("issue-time-utcreader, "identifier");
if (TextUtils.equals(reader.getLocalName(), "issue-time-utcidentifier")) {
this.externalForecastTimeidentifier = reader.getElementText();
}
goTo("warning");
}
/**
* 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 (!XmlStreamReaderUtils.goTo(reader, "text", "warning-info")) {
// no more text areas, return.
return;
}
// we only need the sequencethe number andwarning the title.
if (this.sequenceNumber == null || this.title == null) {
String type = reader.getAttributeValue(null, "type");
if (TextUtils.equals("warning_sequencetitle", type)) {
this.sequenceNumbertitle = reader.getElementText();
}
else if (TextUtils.equals("warning_title_text", type)) { }
if (this.title != reader.getElementText();null) {
}
}
// we're finished with the text elements, skip to the areas
if (this.sequenceNumber != null && this.title != null) {
return;
}
} // we're finished with the text elements, skip to the areaswhile (reader.hasNext());
}
/**
* Retrieve the locationId from the area element(s) and call separate return;
method to parse forecast data
}*
* } while (reader.hasNext())@throws XMLStreamException;
}
/** */
private *void RetrieveparseWarningAreas() thethrows locationIdXMLStreamException from{
the area element(s) and call separate method to// parse forecastalle data
areas binnen een warning *element.
* @throwsdo XMLStreamException;{
*/
private void parseWarningAreas() throws XMLStreamExceptionif (!XmlStreamReaderUtils.goTo(reader, "area", "warning")) {
// parse alle areas binnen een warning element.
// alle area's have been processed or doan {
end tag warning has been found.
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;
// an area can have }
String locationId = reader.getAttributeValue(null, "aac");
// an area can have more more that one fore-cast periods.
parseForecastData(locationId);
} while (reader.hasNext());
}
/**
* helper class to store forecast data.
*/
private static class ForecastData {
private String parameter = null;
private String value = null;
private String unitlocalTime = null;
private String localTimecomment = 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 XMLStreamException;
*/
private void parseForecastData(String locationId) throws XMLStreamException {
// not every area has a forecast-period. Try to find the next forecast-period,
// but stop if not found, or an end-tag area has been found.
List<ForecastData> forecastDataList = new ArrayList<>();
while (XmlStreamReaderUtils.goTo(reader, "forecast-period", "area")) {
// 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();this.externalForecastTime = reader.getAttributeValue(null, "start-time-utc");
// inside the forecast-period we need to process the text elements first.
do {
// process all elements until we reach the end tag forecast-period.
if (!XmlStreamReaderUtils.goTo(reader, "elementtext", "forecast-period")) {
// break so we can write this forecast as a timeseries.
break;
}
String sequence = reader.getAttributeValue(null, "sequencetype");
if (sequence == null!TextUtils.equals("river_level_prediction", sequence)) {
// we only want elementsthe whereriver sequence have been filledlevel prediction.
breakcontinue;
}
// ForecastDatawe forecastDatafound = forecastSequenceMap.get(sequence);a river_level_prediction.
ifString (forecastDatavalue == null) {;
String comment forecastData = new ForecastData()null;
String time = forecastSequenceMap.put(sequence, forecastData);
null;
String }riverLevelPredictionText = reader.getElementText();
Stringint typeindex = readerriverLevelPredictionText.getAttributeValue(null, "type"indexOf(HYFS_MARKER);
Stringif units(index != reader.getAttributeValue(null, "units");-1) {
if (TextUtils.equals("prediction", type)) {
String valueComment = riverLevelPredictionText.substring(index + HYFS_MARKER.length());
// time-local will be set fromint thesplit prediction= element. See comment in [FEWS-11311]valueComment.indexOf(",");
Stringif timeLocal(split != reader.getAttributeValue(null, "time-local");-1) {
String value = readervalueComment.getElementTextsubstring(0, split);
forecastData.parameter = "prediction";
comment = valueComment.substring(split + 1);
forecastData.localTime = timeLocal;
time = parseTime();
forecastData.comment = value;
}
} else if (TextUtils.equals("value", type)) {
}
if forecastData.value(time != reader.getElementText();
null) {
ForecastData forecastData.unit = unitsnew ForecastData();
} forecastData.comment = comment;
} while (reader.hasNext());
forecastData.localTime = time;
// All events have been collected
writeTimeSerie(locationId, forecastSequenceMap.values())forecastData.value = value;
}
}
/**
forecastData.parameter * Write a time serie header and it's events based on the forecastData.
= "prediction";
* @param locationId location of the timeserie forecastDataList.add(forecastData);
* @param externalForecastTime forecast time.
* @param forecastCollection}
collection of forecast events.
*/
} private void writeTimeSerie(String locationId, Collection<ForecastData> forecastCollection) {
while (reader.hasNext());
//noinspection SpellCheckingInspection
All events have been collected
header.setForecastTime(TimeZoneUtils.GMT, "yyyy-MM-dd'T'HH:mm:ss'Z'", this.externalForecastTimewriteTimeSerie(locationId, forecastDataList);
header.setLocationId(locationId);
}
}
private forString parseTime(ForecastData) forecastDatathrows :XMLStreamException forecastCollection) {
// if we contentHandler.setComment(identifier + " #" + sequenceNumber + ": " + title + '\n' + forecastData.comment);
get here, a river_level_prediction was found. No we need to parse all elements until we find the end of the forecast period
header.setParameterId(forecastData.parameter);do {
contentHandler.setValue('.', forecastData.value);
header.setUnit(forecastData.unit);// process all elements until we reach the end tag forecast-period.
if (!XmlStreamReaderUtils.goTo(reader, "element", "forecast-period")) {
long rawTimeZoneOffset = TimeZoneUtils.parseRawTimeZoneOffset(forecastData.localTime.substring(forecastData.localTime.length() - 6));// no element found
contentHandler.setTime(TimeZoneUtils.getTimeZone(rawTimeZoneOffset), "yyyy-MM-dd'T'HH:mm:ss", forecastData.localTime.substring(0, forecastData.localTime.length() - 6)) return null;
contentHandler.setTimeSeriesHeader(header);}
contentHandler.applyCurrentFields();
String typeAttribute = reader.getAttributeValue(null, "type");
}
}
if (TextUtils.equals("time", /**typeAttribute)) {
* This method skips elements until a start element with the correspondingString localtimeString name is found and returns true, if not returns false
= reader.getElementText();
* Warning: ifint theindex element is not found the reader is skipped until the end of the document
= timeString.indexOf(HYFS_MARKER);
if *
(index != -1) {
* @param localName local element name
* @return boolean return true if the localname wasreturn foundtimeString.
substring(index + HYFS_MARKER.length());
* @throws XMLStreamException
*/
private boolean goTo(String localName) throws XMLStreamException {
}
do {}
} while if (!reader.hasNext()) return false;
reader.next()return null;
}
}/**
while (!reader.isStartElement() || !TextUtils.equals(reader.getLocalName(), localName));
return true;
}
* Write a time serie header and it's events based on the forecastData.
/**
@param locationId location of *the Thistimeserie
method skips elements until a* start@param elementexternalForecastTime with the corresponding local name is found and returns true,forecast time.
* If@param anforecastCollection endcollection tagof with name endName is found, return falseforecast events.
*/
Otherwise returns false
private void writeTimeSerie(String locationId, *Collection<ForecastData> Warning: if the element is not found and the end tag is not found, the reader is skipped until the end of the document
*
forecastCollection) {
//noinspection SpellCheckingInspection
header.setForecastTime(TimeZoneUtils.GMT, "yyyy-MM-dd'T'HH:mm:ss'Z'", this.externalForecastTime);
header.setLocationId(locationId);
* @param localNamefor local(ForecastData nameforecastData to: lookforecastCollection) for{
* @param endName local name of end tag to find.
* @return boolean return true if localName cannot be found or the endName end tag has been found.contentHandler.setComment(identifier + ": " + title + '\n' + forecastData.comment);
header.setParameterId(forecastData.parameter);
* @throws XMLStreamException
*/
contentHandler.setValue('.', forecastData.value);
private boolean goTo(String localName, String endName) throws XMLStreamException {// header.setUnit(forecastData.unit);
do {
long rawTimeZoneOffset if (!reader.hasNext()) return false= TimeZoneUtils.parseRawTimeZoneOffset(forecastData.localTime.substring(forecastData.localTime.length() - 6));
if (reader.isEndElement() && TextUtils.equals(reader.getLocalName(), endName)) return falsecontentHandler.setTime(TimeZoneUtils.getTimeZone(rawTimeZoneOffset), "yyyy-MM-dd'T'HH:mm:ss", forecastData.localTime.substring(0, forecastData.localTime.length() - 6));
readercontentHandler.nextsetTimeSeriesHeader(header);
} while (!reader.isStartElement() || !TextUtils.equals(reader.getLocalName(), localName)contentHandler.applyCurrentFields();
return true;}
}
}
|