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.ArrayList; import java.util.List; /** * Created with IntelliJ IDEA. * User: pelgrim * Date: 8/9/13 * Time: 9:35 AM * To change this template use File | Settings | File Templates. */ public class AifsMLTimeSeriesParser implements XmlParser<TimeSeriesContentHandler> { private TimeSeriesContentHandler contentHandler = null; private String virtualFileName = null; private XMLStreamReader reader = null; private DefaultTimeSeriesHeader header = null; //Use for all private String externalForecastTime = null; private String productId = null; private String sequenceNumber = null; @Override public void parse(XMLStreamReader reader, String virtualFileName, TimeSeriesContentHandler contentHandler) throws Exception { this.reader = reader; this.virtualFileName = virtualFileName; this.contentHandler = contentHandler; header = new DefaultTimeSeriesHeader(); reader.require(XMLStreamConstants.START_DOCUMENT, null, null); reader.nextTag(); reader.require(XMLStreamConstants.START_ELEMENT, null, "aifsml"); reader.nextTag(); reader.require(XMLStreamConstants.START_ELEMENT, null, "metadata"); parseMetadata(); parseForeCasts(); } /** * Retrieve the productId, externalForecastTime and sequenceNumber from the elements within the metadata element * * @throws javax.xml.stream.XMLStreamException; */ private void parseMetadata() throws XMLStreamException { reader.nextTag(); if (TextUtils.equals(reader.getLocalName(), "product-id")) { productId = reader.getElementText(); } goTo("issue-time-utc"); externalForecastTime = reader.getElementText(); goTo("sequence-number"); sequenceNumber = reader.getElementText(); } /** * Retrieve the locationId from the forecast element(s) and call separate method to parse forecast data * * @throws javax.xml.stream.XMLStreamException; */ private void parseForeCasts() throws XMLStreamException { do { if (!goTo("forecast")) return; String locationId = reader.getAttributeValue(null, "locationId"); parseForecastData(locationId); } while (reader.hasNext()); } /** * Retrieve the variable, value, unit and time from the forecast data element(s) * Retrieve comment text from forecast text element * @throws javax.xml.stream.XMLStreamException; */ private void parseForecastData(String locationId) throws XMLStreamException { goTo("forecast_data"); //Read in variable, value, unit and time-local for all forecast data elements //Store them in an array of strings for each data element for later use by the contentHandler List<String[]> forecastDataList = new ArrayList<String[]>(); while (TextUtils.equals(reader.getLocalName(), "forecast_data")) { String[] forecastData = new String[4]; forecastData[0] = reader.getAttributeValue(null, "variable"); forecastData[1] = reader.getAttributeValue(null, "value"); forecastData[2] = reader.getAttributeValue(null, "unit"); forecastData[3] = reader.getAttributeValue(null, "time-local"); forecastDataList.add(forecastData); while(XMLStreamReader.START_ELEMENT != reader.nextTag()); } String text = null; if (TextUtils.equals(reader.getLocalName(), "forecast_text")){ text = reader.getElementText(); } //After the forecast text is read, info is ready to be applied //ForecastTime, productId, sequenceNumber is the same for the entire import //locationId and "text" for the comment is the same for the coming list of forecastData //The forecastDataList contains ParameterId, Value, Unit and Time which can differ every time header.setForecastTime(TimeZoneUtils.GMT, "yyyy-MM-dd'T'HH:mm:ss'Z'", externalForecastTime); header.setLocationId(locationId); contentHandler.setComment(productId + " " + sequenceNumber + " " + text); for (int i = 0; i < forecastDataList.size(); i++){ String[] fcd = forecastDataList.get(i); header.setParameterId(fcd[0]); contentHandler.setValue('.', fcd[1]); header.setUnit(fcd[2]); long rawTimeZoneOffset = TimeZoneUtils.parseRawTimeZoneOffset(fcd[3].substring(fcd[3].length()-6)); contentHandler.setTime(TimeZoneUtils.getTimeZone(rawTimeZoneOffset), "yyyy-MM-dd'T'HH:mm:ss", fcd[3].substring(0, fcd[3].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 * @return boolean * @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; } }