/** * 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.eml.v001.util; import java.util.Collection; import java.util.Map; import java.util.Vector; import javax.xml.namespace.QName; import net.opengis.em.x020.DerivedEventDocument; import net.opengis.em.x020.DerivedEventType; import net.opengis.em.x020.EventDocument; import net.opengis.em.x020.EventEventRelationshipType; import net.opengis.em.x020.EventType; import net.opengis.em.x020.EventType.EventTime; import net.opengis.em.x020.NamedValueType; import net.opengis.eml.x001.EMLDocument; import net.opengis.eml.x001.EMLDocument.EML; import net.opengis.gml.FeaturePropertyType; import net.opengis.gml.TimeInstantType; import net.opengis.gml.TimePeriodType; import org.apache.muse.ws.notification.NotificationMessage; import org.apache.xmlbeans.XmlCursor; import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlObject; import org.joda.time.DateTime; import org.n52.ses.api.event.MapEvent; import org.n52.ses.api.ws.INotificationMessage; import org.n52.ses.io.parser.GML31Parser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Element; import org.w3c.dom.Text; /** * @author Matthes Rieke <m.rieke@uni-muenster.de> * * Output generator using the OGC Event Model * */ public class EventModelGenerator { private static final Logger logger = LoggerFactory .getLogger(EventModelGenerator.class); private static final String CAUSE_STRING = "http://www.opengis.net/em/roles/0.2/cause"; // private Logger logger = Logger.getLogger(EventModelGenerator.class.getName()); private MapEvent eventMap; private EventDocument resultEventDoc; private EventType resultEventType; private DerivedEventDocument resultDerivedEventDoc; private DerivedEventType resultDerivedEventType; private boolean firstRecursion = true; /** * @param resultEvent MapEvent to generate the model from */ public EventModelGenerator(MapEvent resultEvent) { this.eventMap = resultEvent; if (this.eventMap.containsKey(MapEvent.ORIGNIAL_MESSAGE_KEY)) { this.resultEventDoc = EventDocument.Factory.newInstance(); this.resultEventType = this.resultEventDoc.addNewEvent(); } else { this.resultDerivedEventDoc = DerivedEventDocument.Factory.newInstance(); this.resultDerivedEventType = this.resultDerivedEventDoc.addNewDerivedEvent(); } } /** * @return XmlObject holding the {@link EventType} */ public XmlObject generateEventDocument() { return this.generateEventDocument(null); } private EventType generateFromEventType(MapEvent recursiveEvent, EventType preEvent) { /* * If the value of the event is of * type MapEvent in the FIRST call * of this method then on the event * stored under value shall be exported. * * This happens if SelectEvent is used. */ if (this.firstRecursion) { this.firstRecursion = false; if (recursiveEvent.get(MapEvent.VALUE_KEY) instanceof MapEvent) { return this.generateFromEventType((MapEvent) recursiveEvent.get(MapEvent.VALUE_KEY), preEvent); } } for (String key : recursiveEvent.keySet()) { if (key.equals(MapEvent.CAUSALITY_KEY) || key.equals(MapEvent.THIS_KEY) || key.equals(MapEvent.END_KEY) || key.equals(MapEvent.STRING_VALUE_KEY) || key.equals(MapEvent.DOUBLE_VALUE_KEY)) { /* * Ignore following keys: * * causality (is handled later) * end time (is handled within start time) * stringValue and doubleValue (contain the same as value) * this (would just repeat the whole event) */ // this.logger.info("ignoring key: " + key); continue; } else if (key.equals(MapEvent.START_KEY)) { /* * time */ // this.logger.info("adding time"); EventTime time; //instant if (recursiveEvent.get(key) == recursiveEvent.get(MapEvent.END_KEY)) { TimeInstantType timeInstant = TimeInstantType.Factory.newInstance(); timeInstant.addNewTimePosition().setStringValue( new DateTime(recursiveEvent.get(key)).toString()); /* * workaround for xsi:type attribute */ time = preEvent.addNewEventTime(); time.setTimePrimitive(timeInstant); XmlCursor cursor = time.getTimePrimitive().newCursor(); cursor.setName(new QName(GML31Parser.GML_3_1_1_NAME, "TimeInstant")); cursor.removeAttribute(new QName("http://www.w3.org/2001/XMLSchema-instance", "type")); } //period else { TimePeriodType timePeriod = TimePeriodType.Factory.newInstance(); timePeriod.addNewBeginPosition().setStringValue( new DateTime(recursiveEvent.get(key)).toString()); timePeriod.addNewEndPosition().setStringValue( new DateTime(recursiveEvent.get(MapEvent.END_KEY)).toString()); /* * workaround for xsi:type attribute */ time = preEvent.addNewEventTime(); time.setTimePrimitive(timePeriod); XmlCursor cursor = time.getTimePrimitive().newCursor(); cursor.setName(new QName(GML31Parser.GML_3_1_1_NAME, "TimePeriod")); cursor.removeAttribute(new QName("http://www.w3.org/2001/XMLSchema-instance", "type")); } } else if (recursiveEvent.get(key) instanceof Map<?, ?> && ((Map<?, ?>)recursiveEvent.get(key)).containsKey(MapEvent.ORIGNIAL_MESSAGE_KEY)) { /* * value of key is a MapEvent containing * an original message -> just use original * message */ // this.logger.info("the event contains a map with an original message at key '" + key + "'. Using only the original message."); Object origMess = ((Map<?, ?>)recursiveEvent.get(key)).get(MapEvent.ORIGNIAL_MESSAGE_KEY); try { XmlObject xo = null; if (origMess instanceof INotificationMessage) { //TODO: seems kind of dirty, as this class should not know NotficiationMessage, but INotificationMessage NotificationMessage nM = (NotificationMessage) ((INotificationMessage) origMess).getNotificationMessage(); //get first available message content Element elem; for (Object qn : nM.getMessageContentNames()) { elem = nM.getMessageContent((QName) qn); xo = XmlObject.Factory.parse(elem); break; } } else { xo = XmlObject.Factory.parse(origMess.toString()); } if (xo != null) { NamedValueType namedVal = preEvent.addNewAttribute().addNewNamedValue(); namedVal.addNewName().setStringValue(key); namedVal.addNewValue().set(xo); } } catch (Throwable e) { //log exception logger.warn(e.getMessage(), e); //forward exception throw new RuntimeException(e); } } else { /* * other values */ NamedValueType namedVal = preEvent.addNewAttribute().addNewNamedValue(); String value = recursiveEvent.get(key).toString(); try { Text text = namedVal.getDomNode().getOwnerDocument().createTextNode(value); XmlObject xo = XmlObject.Factory.parse(text); namedVal.addNewName().setStringValue(key); namedVal.addNewValue().set(xo); } catch (Throwable e) { logger.warn(e.getMessage(), e); //forward exception throw new RuntimeException(e); } } } //get the cause. should be available cause no Original Message was set. Object cause = recursiveEvent.get(MapEvent.CAUSALITY_KEY); if (cause != null && cause instanceof Vector<?>) { Vector<?> causalities = (Vector<?>) cause; if (!(preEvent instanceof DerivedEventType)) { EventModelGenerator.logger.warn("No DerviedEvent. continue without adding causality."); } else { DerivedEventType derEvent = (DerivedEventType) preEvent; for (Object object : causalities) { if (object instanceof MapEvent) { EventEventRelationshipType eventRelation = derEvent.addNewMember().addNewEventEventRelationship(); eventRelation.addNewRole().setStringValue(CAUSE_STRING); /* * add the original message as the target of * the EventEventRelationship */ if (((MapEvent) object).containsKey(MapEvent.ORIGNIAL_MESSAGE_KEY)) { //TODO: seems kind of dirty, as this class should not know NotficiationMessage, but INotificationMessage NotificationMessage notify = (NotificationMessage) ((INotificationMessage)((MapEvent) object).get(MapEvent.ORIGNIAL_MESSAGE_KEY)).getNotificationMessage(); Collection<?> contents = notify.getMessageContentNames(); //TODO handle multiple contents if (contents.iterator().hasNext()) { try { XmlObject xobj = XmlObject.Factory.parse(notify.getMessageContent( (QName) contents.iterator().next())); eventRelation.addNewTarget().set(xobj); } catch (XmlException e) { logger.warn(e.getMessage(), e); } } } /* * recursive call because we do not have a * original message */ else { EventType causingEvent = generateFromEventType((MapEvent) object, DerivedEventType.Factory.newInstance()); FeaturePropertyType target = eventRelation.addNewTarget(); target.addNewFeature().set(causingEvent); /* * workaround for xsi:type (abstract elements) */ XmlCursor cursor = target.newCursor(); cursor.toFirstChild(); cursor.setName(new QName("http://www.opengis.net/em/0.2.0", "DerivedEvent")); cursor.removeAttribute(new QName("http://www.w3.org/2001/XMLSchema-instance", "type")); } } } } } return preEvent; } /** * @param eml if eml available put it in the procedure * @return XmlObject holding the {@link EventType} */ public XmlObject generateEventDocument(EML eml) { if (this.resultEventType != null) { generateFromEventType(this.eventMap, this.resultEventType); return this.resultEventDoc; } generateFromEventType(this.eventMap, this.resultDerivedEventType); if (eml != null) { EMLDocument doc = EMLDocument.Factory.newInstance(); doc.setEML(eml); this.resultDerivedEventType.setProcedure(doc); } else { this.resultDerivedEventType.addNewProcedure(); } return this.resultDerivedEventDoc; } /** * @param args not used */ public static void main(String[] args) { // String eventA = "<om:Observation xmlns:xlink=\"http://www.w3.org/1999/xlink\" xmlns:om=\"http://www.opengis.net/om/1.0\" xmlns:gml=\"http://www.opengis.net/gml\" xmlns:swe=\"http://www.opengis.netswe/1.0.1\" xmlns:sa=\"http://www.opengis.net/sampling/1.0\"> <om:samplingTime> <gml:TimeInstant> <gml:timePosition>2010-04-05T15:59:00+02:00</gml:timePosition> </gml:TimeInstant> </om:samplingTime> <om:procedure xlink:href=\"MY-SML-Described-SYSTEM\"/> <om:observedProperty> <swe:CompositePhenomenon gml:id=\"ID1\" dimension=\"3\"> <gml:name>resultComponents</gml:name> <swe:component xlink:href=\"urn:ogc:def:property:OGC::speed\"/> <swe:component xlink:href=\"urn:ogc:def:property:OGC::trueHeading\"/> <swe:component xlink:href=\"urn:ogc:def:property:OGC::location-accuracy\"/> </swe:CompositePhenomenon> </om:observedProperty> <om:featureOfInterest> <sa:SamplingPoint> <sa:sampledFeature xlink:href=\"http://my.org/vehicle_track.xml\"/> <sa:position> <gml:Point> <gml:pos srsName=\"urn:ogc:def:crs:EPSG:7.4.1:4979\">34.8 -86.7 239.0</gml:pos> </gml:Point> </sa:position> </sa:SamplingPoint> </om:featureOfInterest> <om:result> <swe:DataRecord> <swe:field name=\"speed\"> <swe:Quantity definition=\"urn:ogc:def:property:OGC::speed\"> <gml:description>The magnitude of velocity in theforward direction</gml:description> <swe:uom code=\"m/s2\"/> <swe:value>0.0</swe:value> </swe:Quantity> </swe:field> <swe:field name=\"direction\"> <swe:Quantity definition=\"urn:ogc:def:property:OGC::trueHeading\"> <gml:description>The true heading direction of theplatform</gml:description> <swe:uom code=\"deg\"/> <swe:value>127.265625</swe:value> </swe:Quantity> </swe:field> <swe:field name=\"accuracy\"> <swe:Quantity definition=\"urn:ogc:def:property:OGC::location-accuracy\"> <gml:description>The accuracy of the GPSlocation</gml:description> <swe:uom code=\"m\"/> <swe:value>48.0</swe:value> </swe:Quantity> </swe:field> </swe:DataRecord> </om:result> </om:Observation>"; // // MapEvent m1 = new MapEvent(1, 2); // SimpleNotificationMessage mess = new SimpleNotificationMessage(); // try { // mess.addMessageContent(XmlUtils.createDocument(eventA).getDocumentElement()); // } catch (IOException e) { // // TODO Auto-generated catch block // e.printStackTrace(); // } catch (SAXException e) { // // TODO Auto-generated catch block // e.printStackTrace(); // } // m1.put(MapEvent.ORIGNIAL_MESSAGE_KEY, mess); // // MapEvent m2a = new MapEvent(3, 4); // m2a.addCausalAncestor(m1); // // MapEvent m3 = new MapEvent(5, 6); // m3.addCausalAncestor(m2a); // // EventModelGenerator gem = new EventModelGenerator(m3); // EML eml = EML.Factory.newInstance(); // eml.addNewComplexPatterns(); // eml.addNewRepetitivePatterns(); // eml.addNewSimplePatterns(); // eml.addNewTimerPatterns(); // System.out.println(gem.generateEventDocument(eml)); } }