/** * Copyright (C) 2012-2017 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 * License 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.sos.decode; import java.util.Collections; import java.util.Map; import java.util.Set; import net.opengis.om.x20.NamedValuePropertyType; import net.opengis.om.x20.NamedValueType; import net.opengis.om.x20.OMObservationType; import net.opengis.om.x20.TimeObjectPropertyType; import org.apache.xmlbeans.XmlBoolean; import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlInteger; import org.apache.xmlbeans.XmlObject; import org.apache.xmlbeans.XmlString; import org.apache.xmlbeans.impl.values.XmlAnyTypeImpl; import org.n52.sos.exception.CodedException; import org.n52.sos.exception.ows.InvalidParameterValueException; import org.n52.sos.exception.ows.MissingParameterValueException; import org.n52.sos.exception.ows.OwsExceptionCode; import org.n52.sos.exception.ows.concrete.UnsupportedDecoderInputException; import org.n52.sos.ogc.gml.AbstractFeature; import org.n52.sos.ogc.gml.AbstractGeometry; import org.n52.sos.ogc.gml.CodeWithAuthority; import org.n52.sos.ogc.gml.GmlMeasureType; import org.n52.sos.ogc.gml.ReferenceType; import org.n52.sos.ogc.gml.time.Time; import org.n52.sos.ogc.gml.time.Time.NilReason; import org.n52.sos.ogc.gml.time.Time.TimeIndeterminateValue; import org.n52.sos.ogc.gml.time.TimeInstant; import org.n52.sos.ogc.gml.time.TimePeriod; import org.n52.sos.ogc.om.AbstractPhenomenon; import org.n52.sos.ogc.om.NamedValue; import org.n52.sos.ogc.om.ObservationValue; import org.n52.sos.ogc.om.OmConstants; import org.n52.sos.ogc.om.OmObservableProperty; import org.n52.sos.ogc.om.OmObservation; import org.n52.sos.ogc.om.OmObservationConstellation; import org.n52.sos.ogc.om.SingleObservationValue; import org.n52.sos.ogc.om.values.BooleanValue; import org.n52.sos.ogc.om.values.CategoryValue; import org.n52.sos.ogc.om.values.CountValue; import org.n52.sos.ogc.om.values.GeometryValue; import org.n52.sos.ogc.om.values.HrefAttributeValue; import org.n52.sos.ogc.om.values.NilTemplateValue; import org.n52.sos.ogc.om.values.QuantityValue; import org.n52.sos.ogc.om.values.ReferenceValue; import org.n52.sos.ogc.om.values.SweDataArrayValue; import org.n52.sos.ogc.om.values.TextValue; import org.n52.sos.ogc.ows.OwsExceptionReport; import org.n52.sos.ogc.sensorML.SensorML; import org.n52.sos.ogc.sos.ConformanceClasses; import org.n52.sos.ogc.sos.Sos2Constants; import org.n52.sos.ogc.sos.SosProcedureDescription; import org.n52.sos.ogc.swe.SweDataArray; import org.n52.sos.service.ServiceConstants.SupportedTypeKey; import org.n52.sos.util.CodingHelper; import org.n52.sos.util.Constants; import org.n52.sos.w3c.xlink.W3CHrefAttribute; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.vividsolutions.jts.geom.Geometry; /** * @since 4.0.0 * */ public class OmDecoderv20 implements Decoder<Object, Object> { private static final Logger LOGGER = LoggerFactory.getLogger(OmDecoderv20.class); private static final Set<DecoderKey> DECODER_KEYS = CodingHelper.decoderKeysForElements(OmConstants.NS_OM_2, OMObservationType.class, NamedValuePropertyType.class, NamedValuePropertyType[].class); private static final Map<SupportedTypeKey, Set<String>> SUPPORTED_TYPES = ImmutableMap.of( SupportedTypeKey.ObservationType, (Set<String>) ImmutableSet.of(OmConstants.OBS_TYPE_GEOMETRY_OBSERVATION, OmConstants.OBS_TYPE_CATEGORY_OBSERVATION, OmConstants.OBS_TYPE_COUNT_OBSERVATION, OmConstants.OBS_TYPE_MEASUREMENT, OmConstants.OBS_TYPE_TEXT_OBSERVATION, OmConstants.OBS_TYPE_TRUTH_OBSERVATION, OmConstants.OBS_TYPE_SWE_ARRAY_OBSERVATION)); private static final Set<String> CONFORMANCE_CLASSES = ImmutableSet.of(ConformanceClasses.OM_V2_MEASUREMENT, ConformanceClasses.OM_V2_CATEGORY_OBSERVATION, ConformanceClasses.OM_V2_COUNT_OBSERVATION, ConformanceClasses.OM_V2_TRUTH_OBSERVATION, // ConformanceClasses.OM_V2_GEOMETRY_OBSERVATION, ConformanceClasses.OM_V2_TEXT_OBSERVATION); public OmDecoderv20() { LOGGER.debug("Decoder for the following keys initialized successfully: {}!", Joiner.on(", ") .join(DECODER_KEYS)); } @Override public Set<DecoderKey> getDecoderKeyTypes() { return Collections.unmodifiableSet(DECODER_KEYS); } @Override public Map<SupportedTypeKey, Set<String>> getSupportedTypes() { return Collections.unmodifiableMap(SUPPORTED_TYPES); } @Override public Set<String> getConformanceClasses() { return Collections.unmodifiableSet(CONFORMANCE_CLASSES); } @Override public Object decode(Object object) throws OwsExceptionReport { // validate document // XmlHelper.validateDocument((XmlObject) object); if (object instanceof OMObservationType) { return parseOmObservation((OMObservationType) object); } else if (object instanceof NamedValuePropertyType) { return parseNamedValueType((NamedValuePropertyType) object); } else if (object instanceof NamedValuePropertyType[]) { return parseNamedValueTypeArray((NamedValuePropertyType[]) object); } throw new UnsupportedDecoderInputException(this, object); } private OmObservation parseOmObservation(OMObservationType omObservation) throws OwsExceptionReport { Map<String, AbstractFeature> featureMap = Maps.newHashMap(); OmObservation sosObservation = new OmObservation(); sosObservation.setIdentifier(getIdentifier(omObservation)); if (omObservation.isSetDescription()) { sosObservation.setDescription(omObservation.getDescription().getStringValue()); } OmObservationConstellation observationConstallation = getObservationConstellation(omObservation); sosObservation.setObservationConstellation(observationConstallation); sosObservation.setResultTime(getResultTime(omObservation)); sosObservation.setValidTime(getValidTime(omObservation)); if (omObservation.getParameterArray() != null) { sosObservation.setParameter(parseNamedValueTypeArray(omObservation.getParameterArray())); } sosObservation.setValue(getObservationValue(omObservation)); try { Object decodeXmlElement = CodingHelper.decodeXmlElement(omObservation.getFeatureOfInterest()); if (decodeXmlElement instanceof AbstractFeature) { AbstractFeature featureOfInterest = (AbstractFeature) decodeXmlElement; observationConstallation.setFeatureOfInterest(checkFeatureWithMap(featureOfInterest, featureMap)); } } catch (OwsExceptionReport e) { if (sosObservation.getValue() != null && sosObservation.getValue().getPhenomenonTime() != null && sosObservation.getPhenomenonTime().isSetNilReason() && sosObservation.getValue().getPhenomenonTime().getNilReason().equals(NilReason.template)) { for (CodedException exception : e.getExceptions()) { if (exception.getCode().equals(OwsExceptionCode.InvalidParameterValue)) { throw new InvalidParameterValueException().at(exception.getLocator()).withMessage( exception.getMessage()); } else if (exception.getCode().equals(OwsExceptionCode.MissingParameterValue)) { throw new MissingParameterValueException(exception.getLocator()); } } } throw e; } // TODO: later for spatial filtering profile // omObservation.getParameterArray(); return sosObservation; } private Set<NamedValue<?>> parseNamedValueTypeArray(NamedValuePropertyType[] namedValuePropertyArray) throws OwsExceptionReport { Set<NamedValue<?>> parameters = Sets.newHashSet(); for (NamedValuePropertyType namedValueProperty : namedValuePropertyArray) { parameters.add(parseNamedValueType(namedValueProperty)); } return parameters; } private NamedValue<?> parseNamedValueType(NamedValuePropertyType namedValueProperty) throws OwsExceptionReport { if (namedValueProperty.isSetNamedValue()) { NamedValueType namedValue = namedValueProperty.getNamedValue(); NamedValue<?> sosNamedValue = parseNamedValueValue(namedValue.getValue()); ReferenceType referenceType = (ReferenceType) CodingHelper.decodeXmlObject(namedValue.getName()); sosNamedValue.setName(referenceType); return sosNamedValue; } else if (namedValueProperty.isSetHref()) { NamedValue<?> sosNamedValue = new NamedValue<ReferenceType>(); ReferenceType referenceType = new ReferenceType(namedValueProperty.getHref()); if (namedValueProperty.isSetTitle()) { referenceType.setTitle(namedValueProperty.getTitle()); } sosNamedValue.setName(referenceType); return sosNamedValue; } else { throw new UnsupportedDecoderInputException(this, namedValueProperty); } } private NamedValue<?> parseNamedValueValue(XmlObject xmlObject) throws OwsExceptionReport { if (xmlObject.schemaType() == XmlAnyTypeImpl.type) { try { xmlObject = XmlObject.Factory.parse(xmlObject.xmlText().trim()); } catch (XmlException e) { LOGGER.error("Error while parsing NamedValueValue", e); } } Object value = CodingHelper.decodeXmlObject(xmlObject); if (value instanceof BooleanValue) { NamedValue<Boolean> namedValue = new NamedValue<Boolean>(); namedValue.setValue((BooleanValue) value); return namedValue; } else if (value instanceof CategoryValue) { NamedValue<String> namedValue = new NamedValue<String>(); namedValue.setValue((CategoryValue) value); return namedValue; } else if (value instanceof CountValue) { NamedValue<Integer> namedValue = new NamedValue<Integer>(); namedValue.setValue((CountValue) value); return namedValue; } else if (value instanceof GeometryValue) { NamedValue<Geometry> namedValue = new NamedValue<Geometry>(); namedValue.setValue((GeometryValue) value); return namedValue; } else if (value instanceof QuantityValue) { NamedValue<Double> namedValue = new NamedValue<Double>(); namedValue.setValue((QuantityValue) value); return namedValue; } else if (value instanceof TextValue) { NamedValue<String> namedValue = new NamedValue<String>(); namedValue.setValue((TextValue) value); return namedValue; } else if (value instanceof AbstractGeometry) { NamedValue<Geometry> namedValue = new NamedValue<Geometry>(); namedValue.setValue(new GeometryValue((AbstractGeometry)value)); return namedValue; } else if (value instanceof ReferenceType) { NamedValue<ReferenceType> namedValue = new NamedValue<ReferenceType>(); namedValue.setValue(new ReferenceValue((ReferenceType)value)); return namedValue; } else if (value instanceof W3CHrefAttribute) { NamedValue<W3CHrefAttribute> namedValue = new NamedValue<W3CHrefAttribute>(); namedValue.setValue(new HrefAttributeValue((W3CHrefAttribute)value)); return namedValue; } else { throw new UnsupportedDecoderInputException(this, xmlObject); } } private CodeWithAuthority getIdentifier(OMObservationType omObservation) throws OwsExceptionReport { if (omObservation.getIdentifier() != null) { Object decodedObject = CodingHelper.decodeXmlObject(omObservation.getIdentifier()); if (decodedObject instanceof CodeWithAuthority) { return (CodeWithAuthority) decodedObject; } } return null; } private OmObservationConstellation getObservationConstellation(OMObservationType omObservation) throws OwsExceptionReport { OmObservationConstellation observationConstellation = new OmObservationConstellation(); observationConstellation.setObservationType(getObservationType(omObservation)); observationConstellation.setProcedure(createProcedure(getProcedure(omObservation))); observationConstellation.setObservableProperty(getObservableProperty(omObservation)); return observationConstellation; } private String getObservationType(OMObservationType omObservation) { if (omObservation.getType() != null) { return omObservation.getType().getHref(); } return null; } private String getProcedure(OMObservationType omObservation) { if (omObservation.getProcedure() != null) { return omObservation.getProcedure().getHref(); } return null; } private AbstractPhenomenon getObservableProperty(OMObservationType omObservation) { if (omObservation.getObservedProperty() != null) { return new OmObservableProperty(omObservation.getObservedProperty().getHref()); } return null; } private Time getPhenomenonTime(OMObservationType omObservation) throws OwsExceptionReport { TimeObjectPropertyType phenomenonTime = omObservation.getPhenomenonTime(); if (phenomenonTime.isSetHref() && phenomenonTime.getHref().startsWith(Constants.NUMBER_SIGN_STRING)) { TimeInstant timeInstant = new TimeInstant(); timeInstant.setGmlId(phenomenonTime.getHref()); return timeInstant; } else if (phenomenonTime.isSetNilReason() && phenomenonTime.getNilReason() instanceof String && ((String) phenomenonTime.getNilReason()).equals(TimeIndeterminateValue.template.name())) { TimeInstant timeInstant = new TimeInstant(); timeInstant.setIndeterminateValue(TimeIndeterminateValue.getEnumForString((String) phenomenonTime .getNilReason())); return timeInstant; } else if (phenomenonTime.isSetAbstractTimeObject()) { Object decodedObject = CodingHelper.decodeXmlObject(phenomenonTime.getAbstractTimeObject()); if (decodedObject instanceof Time) { return (Time) decodedObject; } // FIXME else } throw new InvalidParameterValueException().at(Sos2Constants.InsertObservationParams.observation).withMessage( "The requested phenomenonTime type is not supported by this service!"); } private TimeInstant getResultTime(OMObservationType omObservation) throws OwsExceptionReport { if (omObservation.getResultTime().isSetHref()) { TimeInstant timeInstant = new TimeInstant(); timeInstant.setGmlId(omObservation.getResultTime().getHref()); if (omObservation.getResultTime().getHref().charAt(0) == Constants.NUMBER_SIGN_CHAR) { // document internal link // TODO parse linked element timeInstant.setReference(Sos2Constants.EN_PHENOMENON_TIME); } else { timeInstant.setReference(omObservation.getResultTime().getHref()); } return timeInstant; } else if (omObservation.getResultTime().isSetNilReason() && omObservation.getResultTime().getNilReason() instanceof String && NilReason.template.equals(NilReason.getEnumForString((String) omObservation.getResultTime() .getNilReason()))) { TimeInstant timeInstant = new TimeInstant(); timeInstant .setNilReason(NilReason.getEnumForString((String) omObservation.getResultTime().getNilReason())); return timeInstant; } else if (omObservation.getResultTime().isSetTimeInstant()) { Object decodedObject = CodingHelper.decodeXmlObject(omObservation.getResultTime().getTimeInstant()); if (decodedObject instanceof TimeInstant) { return (TimeInstant) decodedObject; } throw new InvalidParameterValueException().at(Sos2Constants.InsertObservationParams.observation) .withMessage("The requested resultTime type is not supported by this service!"); } else { throw new InvalidParameterValueException().at(Sos2Constants.InsertObservationParams.observation) .withMessage("The requested resultTime type is not supported by this service!"); } } private TimePeriod getValidTime(OMObservationType omObservation) throws OwsExceptionReport { if (omObservation.isSetValidTime()) { Object decodedObject = CodingHelper.decodeXmlObject(omObservation.getValidTime().getTimePeriod()); if (decodedObject instanceof TimePeriod) { return (TimePeriod) decodedObject; } throw new InvalidParameterValueException().at(Sos2Constants.InsertObservationParams.observation) .withMessage("The requested validTime type is not supported by this service!"); } return null; } private ObservationValue<?> getObservationValue(OMObservationType omObservation) throws OwsExceptionReport { Time phenomenonTime = getPhenomenonTime(omObservation); ObservationValue<?> observationValue; if (!omObservation.getResult().getDomNode().hasChildNodes() && phenomenonTime.isSetNilReason() && phenomenonTime.getNilReason().equals(NilReason.template)) { observationValue = new SingleObservationValue<String>(new NilTemplateValue()); } else { observationValue = getResult(omObservation); } observationValue.setPhenomenonTime(phenomenonTime); return observationValue; } private ObservationValue<?> getResult(OMObservationType omObservation) throws OwsExceptionReport { XmlObject xbResult = omObservation.getResult(); if (xbResult.schemaType() == XmlAnyTypeImpl.type) { // Template observation for InsertResultTemplate operation if (!xbResult.getDomNode().hasChildNodes()) { return new SingleObservationValue<String>(new NilTemplateValue()); } else { try { xbResult = XmlObject.Factory.parse(xbResult.xmlText().trim()); } catch (XmlException e) { LOGGER.error("Error while parsing NamedValueValue", e); } } } // // Template observation for InsertResultTemplate operation // if (omObservation.getResult().schemaType() == XmlAnyTypeImpl.type && // !omObservation.getResult().getDomNode().hasChildNodes()) { // return new SingleObservationValue<String>(new NilTemplateValue()); // } // TruthObservation if (xbResult.schemaType() == XmlBoolean.type) { XmlBoolean xbBoolean = (XmlBoolean) xbResult; BooleanValue booleanValue = new BooleanValue(xbBoolean.getBooleanValue()); return new SingleObservationValue<Boolean>(booleanValue); } // CountObservation else if (xbResult.schemaType() == XmlInteger.type) { XmlInteger xbInteger = (XmlInteger) xbResult; CountValue countValue = new CountValue(Integer.parseInt(xbInteger.getBigIntegerValue().toString())); return new SingleObservationValue<Integer>(countValue); } // TextObservation else if (xbResult.schemaType() == XmlString.type) { XmlString xbString = (XmlString) xbResult; TextValue stringValue = new TextValue(xbString.getStringValue()); return new SingleObservationValue<String>(stringValue); } // result elements with other encoding like SWE_ARRAY_OBSERVATION else { Object decodedObject = CodingHelper.decodeXmlObject(xbResult); if (decodedObject instanceof ObservationValue) { return (ObservationValue) decodedObject; } else if (decodedObject instanceof GmlMeasureType) { SingleObservationValue<Double> result = new SingleObservationValue<Double>(); GmlMeasureType measureType = (GmlMeasureType) decodedObject; QuantityValue quantitiyValue = new QuantityValue(measureType.getValue(), measureType.getUnit()); result.setValue(quantitiyValue); return result; } else if (decodedObject instanceof ReferenceType) { SingleObservationValue<String> result = new SingleObservationValue<String>(); result.setValue(new CategoryValue(((ReferenceType) decodedObject).getHref())); return result; } else if (decodedObject instanceof Geometry) { SingleObservationValue<Geometry> result = new SingleObservationValue<Geometry>(); result.setValue(new GeometryValue((Geometry) decodedObject)); return result; } else if (decodedObject instanceof AbstractGeometry) { SingleObservationValue<Geometry> result = new SingleObservationValue<Geometry>(); result.setValue(new GeometryValue(((AbstractGeometry) decodedObject).getGeometry())); return result; } else if (decodedObject instanceof SweDataArray) { SweDataArrayValue value = new SweDataArrayValue(); value.setValue((SweDataArray) decodedObject); SingleObservationValue<SweDataArray> result = new SingleObservationValue<SweDataArray>(); result.setValue(value); return result; } throw new InvalidParameterValueException().at(Sos2Constants.InsertObservationParams.observation) .withMessage("The requested result type is not supported by this service!"); } } private AbstractFeature checkFeatureWithMap(AbstractFeature featureOfInterest, Map<String, AbstractFeature> featureMap) { if (featureOfInterest.getGmlId() != null && !featureOfInterest.getGmlId().isEmpty()) { if (featureMap.containsKey(featureOfInterest.getGmlId())) { return featureMap.get(featureOfInterest.getGmlId()); } else { featureMap.put(featureOfInterest.getGmlId(), featureOfInterest); } } return featureOfInterest; } private SosProcedureDescription createProcedure(String procedureIdentifier) { SensorML procedure = new SensorML(); procedure.setIdentifier(procedureIdentifier); return procedure; } }