/** * Copyright (C) 2008 - 2014 52°North Initiative for Geospatial Open Source * Software GmbH * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * If the program is linked with libraries which are licensed under one of * the following licenses, the combination of the program with the linked * library is not considered a "derivative work" of the program: * * - Apache License, version 2.0 * - Apache Software License, version 1.0 * - GNU Lesser General Public License, version 3 * - Mozilla Public License, versions 1.0, 1.1 and 2.0 * - Common Development and Distribution License (CDDL), version 1.0 * * Therefore the distribution of the program linked with libraries licensed * under the aforementioned licenses, is permitted by the copyright holders * if the distribution is compliant with both the GNU General Public * icense version 2 and the aforementioned licenses. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General * Public License for more details. */ package org.n52.ses.io.parser; import java.util.ArrayList; import java.util.List; import javax.xml.namespace.QName; import org.apache.muse.ws.notification.NotificationMessage; import org.apache.xmlbeans.XmlObject; import org.joda.time.DateTime; import org.joda.time.format.DateTimeFormatter; import org.joda.time.format.DateTimeFormatterBuilder; import org.n52.oxf.xmlbeans.parser.XMLBeansParser; import org.n52.ses.api.AbstractParser; import org.n52.ses.api.event.MapEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Element; /** * Parser for notification messages encoded as SAS (RFC, 06-028r5) alerts. * * As the alert structure is not known to the parser * it is currently build only for specific SAS alerts. * The alert data has to be formatted in the following way: * <code><AlertData>%TIME,%LAT,%LON,%VALUE</AlertData></code> * where %VALUE has to be in a base (SI) unit, * %LAT and %LON are in WGS 84 and define a point, * all entries have to be separated by a comma (","). * * @author Thomas Everding * */ public class SASParser extends AbstractParser { /** * XML namespace of the SAS (RFC; 06-028r5) */ public static final String SAS_NAMESPACE = "http://www.opengis.net/sas/0.0"; /** * XML namespace of the SAS as used by some implementers (e.g. RSA in GENESIS Project) */ public static final String SAS_NAMESPACE_090 = "http://www.opengis.net/sas/0.9.0"; private String value; private String geometry; private String sensorID; private String timestamp; private String alertdata; private static final Logger logger = LoggerFactory .getLogger(SASParser.class); /** * Parses an SAS alert * * @param xmlObj the SAS alert as DOM node * * @return A list of {@link MapEvent}s containing the alert as first entity. */ public List<MapEvent> parseXML (XmlObject xmlObj) { //parse string based this.parseAlertDocumentText(xmlObj.toString()); //parse time stamp text DateTimeFormatter dtf = this.buildDTFormatter(); DateTime dt = dtf.parseDateTime(this.timestamp); long timeStamp = dt.getMillis(); //parse alert data this.parseAlertData(this.alertdata); //build result list List<MapEvent> result = new ArrayList<MapEvent>(); //build MapEvent MapEvent event = new MapEvent(timeStamp, timeStamp); //set value event.put(MapEvent.VALUE_KEY, this.value); //set geometry event.put(MapEvent.GEOMETRY_KEY, this.geometry); //set sensor ID event.put(MapEvent.SENSORID_KEY, this.sensorID); //set original message event.put(MapEvent.ORIGNIAL_MESSAGE_KEY, xmlObj.toString()); //add event to result result.add(event); SASParser.logger.info("SAS alert parsed"); return result; } /** * extracts the content of a SAS alert * * @param xmlText the alert text */ private void parseAlertDocumentText(String xmlText) { //split alert String[]entries = xmlText.split(">"); /* * content of "entries": * * 0: <Alert... * 1: <SensorID * 2: urn:ogc...</SensorID * 3: <Timestamp * 4: 2010-04...</Timestamp * 5: <AlertData * 6: 2010-04..., 47....</AlertData * 7: </Alert */ //remove closing tags this.sensorID = entries[2].substring(0, entries[2].indexOf("<")); this.timestamp = entries[4].substring(0, entries[4].indexOf("<")); this.alertdata = entries[6].substring(0, entries[6].indexOf("<")); } /** * parses the alert data * * @param data an AlertData element content */ private void parseAlertData(String data) { //split data string String[] parts = data.split(","); //store value this.value = parts[3]; //store geometry as WKT this.geometry = "POINT (" + parts[1] + " " + parts[2] + ")"; //time key is repeated at parts[0] but parsed from the time specific element } /** * * @return a formatter for common ISO strings */ private DateTimeFormatter buildDTFormatter() { //build a parser for time stamps return new DateTimeFormatterBuilder() .appendYear(4, 4) //4 digit year (YYYY) .appendLiteral("-") .appendMonthOfYear(2) //2 digit month (MM) .appendLiteral("-") .appendDayOfMonth(2) //2 digit day (DD) .appendLiteral("T") .appendHourOfDay(2) //2 digit hour (hh) .appendLiteral(":") .appendMinuteOfHour(2) //2 digit minute (mm) .appendLiteral(":") .appendSecondOfMinute(2)//2 digit second (ss) //optional 3 digit milliseconds of second .appendOptional(new DateTimeFormatterBuilder() .appendLiteral(".") .appendMillisOfSecond(3) .toParser()) //optional time zone offset as (+|-)hh:mm .appendOptional(new DateTimeFormatterBuilder() .appendTimeZoneOffset("", true, 2, 2) .toParser()) //optional 'Z' at the end of the time string .appendOptional(new DateTimeFormatterBuilder() .appendLiteral("Z") .toParser()) .toFormatter(); } @Override public boolean accept(NotificationMessage message) { //namespace as defined in RFC Specification QName sasQName = new QName(SAS_NAMESPACE, "Alert"); Element content = message.getMessageContent(sasQName); if (content != null) { return true; } //alternative namespace sasQName = new QName(SAS_NAMESPACE_090, "Alert"); content = message.getMessageContent(sasQName); if (content != null) { return true; } return false; } @Override public List<MapEvent> parse(NotificationMessage message) throws Exception { //namespace as defined in RFC Specification QName sasQName = new QName(SAS_NAMESPACE, "Alert"); Element content = message.getMessageContent(sasQName); if (content != null) { XmlObject obj = XMLBeansParser.parse(content, false); return this.parseXML(obj); } //alternative namespace sasQName = new QName(SASParser.SAS_NAMESPACE_090, "Alert"); content = message.getMessageContent(sasQName); if (content != null) { XmlObject obj = XMLBeansParser.parse(content, false); return this.parseXML(obj); } return null; } @Override protected String getName() { return "SASParser"; } }