/**
* 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.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import net.opengis.gml.x32.FeaturePropertyType;
import net.opengis.sos.x20.SosInsertionMetadataPropertyType;
import net.opengis.sos.x20.SosInsertionMetadataType;
import net.opengis.swes.x20.DeleteSensorDocument;
import net.opengis.swes.x20.DeleteSensorType;
import net.opengis.swes.x20.DescribeSensorDocument;
import net.opengis.swes.x20.DescribeSensorType;
import net.opengis.swes.x20.InsertSensorDocument;
import net.opengis.swes.x20.InsertSensorType;
import net.opengis.swes.x20.InsertSensorType.Metadata;
import net.opengis.swes.x20.InsertSensorType.RelatedFeature;
import net.opengis.swes.x20.SensorDescriptionType;
import net.opengis.swes.x20.UpdateSensorDescriptionDocument;
import net.opengis.swes.x20.UpdateSensorDescriptionType;
import net.opengis.swes.x20.UpdateSensorDescriptionType.Description;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
import org.n52.sos.coding.CodingRepository;
import org.n52.sos.exception.ows.InvalidParameterValueException;
import org.n52.sos.exception.ows.NoApplicableCodeException;
import org.n52.sos.exception.ows.concrete.DecoderResponseUnsupportedException;
import org.n52.sos.exception.ows.concrete.UnsupportedDecoderInputException;
import org.n52.sos.ogc.OGCConstants;
import org.n52.sos.ogc.gml.CodeType;
import org.n52.sos.ogc.gml.CodeWithAuthority;
import org.n52.sos.ogc.gml.time.Time;
import org.n52.sos.ogc.om.features.samplingFeatures.SamplingFeature;
import org.n52.sos.ogc.ows.OwsExceptionReport;
import org.n52.sos.ogc.sos.Sos2Constants;
import org.n52.sos.ogc.sos.Sos2Constants.UpdateSensorDescriptionParams;
import org.n52.sos.ogc.sos.SosConstants;
import org.n52.sos.ogc.sos.SosInsertionMetadata;
import org.n52.sos.ogc.sos.SosProcedureDescription;
import org.n52.sos.ogc.swes.SwesConstants;
import org.n52.sos.ogc.swes.SwesFeatureRelationship;
import org.n52.sos.request.AbstractServiceRequest;
import org.n52.sos.request.DeleteSensorRequest;
import org.n52.sos.request.DescribeSensorRequest;
import org.n52.sos.request.InsertSensorRequest;
import org.n52.sos.request.UpdateSensorRequest;
import org.n52.sos.service.AbstractServiceCommunicationObject;
import org.n52.sos.service.ServiceConstants.SupportedTypeKey;
import org.n52.sos.util.CodingHelper;
import org.n52.sos.util.CollectionHelper;
import org.n52.sos.util.SosHelper;
import org.n52.sos.util.XmlHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
/**
* @since 4.0.0
*
*/
public class SwesDecoderv20 extends AbstractSwesDecoderv20 implements Decoder<AbstractServiceCommunicationObject, XmlObject> {
private static final Logger LOGGER = LoggerFactory.getLogger(SwesDecoderv20.class);
@SuppressWarnings("unchecked")
private static final Set<DecoderKey> DECODER_KEYS = CollectionHelper.union(CodingHelper.decoderKeysForElements(
SwesConstants.NS_SWES_20, DescribeSensorDocument.class, InsertSensorDocument.class,
UpdateSensorDescriptionDocument.class, DeleteSensorDocument.class), CodingHelper
.xmlDecoderKeysForOperation(SosConstants.SOS, Sos2Constants.SERVICEVERSION,
SosConstants.Operations.DescribeSensor, Sos2Constants.Operations.InsertSensor,
Sos2Constants.Operations.UpdateSensorDescription, Sos2Constants.Operations.DeleteSensor));
public SwesDecoderv20() {
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.emptyMap();
}
@Override
public Set<String> getConformanceClasses() {
return Collections.emptySet();
}
@Override
public AbstractServiceRequest<?> decode(final XmlObject xmlObject) throws OwsExceptionReport {
LOGGER.debug("REQUESTTYPE:" + xmlObject.getClass());
XmlHelper.validateDocument(xmlObject);
if (xmlObject instanceof DescribeSensorDocument) {
return parseDescribeSensor((DescribeSensorDocument) xmlObject);
} else if (xmlObject instanceof InsertSensorDocument) {
return parseInsertSensor((InsertSensorDocument) xmlObject);
} else if (xmlObject instanceof UpdateSensorDescriptionDocument) {
return parseUpdateSensorDescription((UpdateSensorDescriptionDocument) xmlObject);
} else if (xmlObject instanceof DeleteSensorDocument) {
return parseDeleteSensor((DeleteSensorDocument) xmlObject);
} else {
throw new UnsupportedDecoderInputException(this, xmlObject);
}
}
/**
* parses the passes XmlBeans document and creates a SOS describeSensor
* request
*
* @param xbDescSenDoc
* XmlBeans document representing the describeSensor request
* @return Returns SOS describeSensor request
*
*
* @throws OwsExceptionReport
* * if validation of the request failed
*/
private AbstractServiceRequest<?> parseDescribeSensor(final DescribeSensorDocument xbDescSenDoc)
throws OwsExceptionReport {
final DescribeSensorRequest descSensorRequest = new DescribeSensorRequest();
final DescribeSensorType xbDescSensor = xbDescSenDoc.getDescribeSensor();
descSensorRequest.setService(xbDescSensor.getService());
descSensorRequest.setVersion(xbDescSensor.getVersion());
descSensorRequest.setProcedure(xbDescSensor.getProcedure());
descSensorRequest.setProcedureDescriptionFormat(xbDescSensor.getProcedureDescriptionFormat());
if (xbDescSensor.isSetValidTime()) {
descSensorRequest.setValidTime(getValidTime(xbDescSensor.getValidTime()));
}
// extensions
descSensorRequest.setExtensions(parseExtensibleRequest(xbDescSensor));
return descSensorRequest;
}
private AbstractServiceRequest<?> parseInsertSensor(final InsertSensorDocument xbInsSensDoc)
throws OwsExceptionReport {
final InsertSensorRequest request = new InsertSensorRequest();
final InsertSensorType xbInsertSensor = xbInsSensDoc.getInsertSensor();
request.setService(xbInsertSensor.getService());
request.setVersion(xbInsertSensor.getVersion());
// format
request.setProcedureDescriptionFormat(xbInsertSensor.getProcedureDescriptionFormat());
// observable properties
if (CollectionHelper.isNotNullOrEmpty(xbInsertSensor.getObservablePropertyArray())) {
request.setObservableProperty(Arrays.asList(xbInsertSensor.getObservablePropertyArray()));
}
// related features
if (CollectionHelper.isNotNullOrEmpty(xbInsertSensor.getRelatedFeatureArray())) {
request.setRelatedFeature(parseRelatedFeature(xbInsertSensor.getRelatedFeatureArray()));
}
// metadata
if (CollectionHelper.isNotNullOrEmpty(xbInsertSensor.getMetadataArray())) {
request.setMetadata(parseMetadata(xbInsertSensor.getMetadataArray()));
}
// extensions
request.setExtensions(parseExtensibleRequest(xbInsertSensor));
try {
final XmlObject xbProcedureDescription =
XmlObject.Factory.parse(getNodeFromNodeList(xbInsertSensor.getProcedureDescription().getDomNode()
.getChildNodes()));
final Decoder<?, XmlObject> decoder =
CodingRepository.getInstance().getDecoder(
new XmlNamespaceDecoderKey(xbInsertSensor.getProcedureDescriptionFormat(),
xbProcedureDescription.getClass()));
if (decoder == null) {
SosHelper.checkProcedureDescriptionFormat(xbInsertSensor.getProcedureDescriptionFormat(),
request.getService(), request.getVersion());
// if
// (StringHelper.isNullOrEmpty(xbInsertSensor.getProcedureDescriptionFormat()))
// {
//
// } else {
// throw new InvalidParameterValueException().at(
// Sos2Constants.InsertSensorParams.procedureDescriptionFormat).withMessage(
// "The requested %s is not supported!",
// Sos2Constants.InsertSensorParams.procedureDescriptionFormat.name());
// }
}
final Object decodedProcedureDescription = decoder.decode(xbProcedureDescription);
if (decodedProcedureDescription instanceof SosProcedureDescription) {
request.setProcedureDescription((SosProcedureDescription) decodedProcedureDescription);
}
} catch (final XmlException xmle) {
throw new NoApplicableCodeException().causedBy(xmle).withMessage(
"Error while parsing procedure description of InsertSensor request!");
}
return request;
}
private AbstractServiceRequest<?> parseDeleteSensor(final DeleteSensorDocument xbDelSenDoc) throws OwsExceptionReport {
final DeleteSensorRequest request = new DeleteSensorRequest();
DeleteSensorType deleteSensor = xbDelSenDoc.getDeleteSensor();
request.setService(deleteSensor.getService());
request.setVersion(deleteSensor.getVersion());
request.setProcedureIdentifier(deleteSensor.getProcedure());
// extensions
request.setExtensions(parseExtensibleRequest(deleteSensor));
return request;
}
/**
* parses the Xmlbeans UpdateSensorDescription document to a SOS request.
*
* @param xbUpSenDoc
* UpdateSensorDescription document
* @return SOS UpdateSensor request
*
*
* @throws OwsExceptionReport
* * if an error occurs.
*/
private AbstractServiceRequest<?> parseUpdateSensorDescription(final UpdateSensorDescriptionDocument xbUpSenDoc)
throws OwsExceptionReport {
final UpdateSensorRequest request = new UpdateSensorRequest();
final UpdateSensorDescriptionType xbUpdateSensor = xbUpSenDoc.getUpdateSensorDescription();
request.setService(xbUpdateSensor.getService());
request.setVersion(xbUpdateSensor.getVersion());
request.setProcedureIdentifier(xbUpdateSensor.getProcedure());
request.setProcedureDescriptionFormat(xbUpdateSensor.getProcedureDescriptionFormat());
// extensions
request.setExtensions(parseExtensibleRequest(xbUpdateSensor));
for (final Description description : xbUpdateSensor.getDescriptionArray()) {
SensorDescriptionType sensorDescription = description.getSensorDescription();
try {
// TODO exception if valid time is set
final XmlObject xmlObject =
XmlObject.Factory.parse(getNodeFromNodeList(sensorDescription.getData().getDomNode()
.getChildNodes()));
final Decoder<?, XmlObject> decoder =
CodingRepository.getInstance().getDecoder(CodingHelper.getDecoderKey(xmlObject));
if (decoder == null) {
throw new InvalidParameterValueException().at(
UpdateSensorDescriptionParams.procedureDescriptionFormat).withMessage(
"The requested procedureDescritpionFormat is not supported!");
}
final Object decodedObject = decoder.decode(xmlObject);
if (decodedObject instanceof SosProcedureDescription) {
SosProcedureDescription sosProcedureDescription = (SosProcedureDescription) decodedObject;
if (sensorDescription.isSetValidTime()) {
sosProcedureDescription.setValidTime(getValidTime(sensorDescription.getValidTime()));
}
request.addProcedureDescriptionString(sosProcedureDescription);
}
} catch (final XmlException xmle) {
throw new NoApplicableCodeException().causedBy(xmle).withMessage(
"Error while parsing procedure description of UpdateSensor request!");
}
}
return request;
}
private SosInsertionMetadata parseMetadata(final Metadata[] metadataArray) throws OwsExceptionReport {
final SosInsertionMetadata sosMetadata = new SosInsertionMetadata();
try {
for (final Metadata metadata : metadataArray) {
SosInsertionMetadataType xbSosInsertionMetadata = null;
if (metadata.getInsertionMetadata() != null
&& metadata.getInsertionMetadata().schemaType() == SosInsertionMetadataType.type) {
xbSosInsertionMetadata = (SosInsertionMetadataType) metadata.getInsertionMetadata();
} else {
if (metadata.getDomNode().hasChildNodes()) {
final Node node = getNodeFromNodeList(metadata.getDomNode().getChildNodes());
final SosInsertionMetadataPropertyType xbMetadata =
SosInsertionMetadataPropertyType.Factory.parse(node);
xbSosInsertionMetadata = xbMetadata.getSosInsertionMetadata();
}
}
if (xbSosInsertionMetadata != null) {
// featureOfInterest types
if (xbSosInsertionMetadata.getFeatureOfInterestTypeArray() != null) {
sosMetadata.setFeatureOfInterestTypes(Arrays.asList(xbSosInsertionMetadata
.getFeatureOfInterestTypeArray()));
}
// observation types
if (xbSosInsertionMetadata.getObservationTypeArray() != null) {
sosMetadata
.setObservationTypes(Arrays.asList(xbSosInsertionMetadata.getObservationTypeArray()));
}
}
}
} catch (final XmlException xmle) {
throw new NoApplicableCodeException().causedBy(xmle).withMessage(
"An error occurred while parsing the metadata in the http post request");
}
return sosMetadata;
}
private List<SwesFeatureRelationship> parseRelatedFeature(final RelatedFeature[] relatedFeatureArray)
throws OwsExceptionReport {
final List<SwesFeatureRelationship> sosRelatedFeatures =
new ArrayList<SwesFeatureRelationship>(relatedFeatureArray.length);
for (final RelatedFeature relatedFeature : relatedFeatureArray) {
final SwesFeatureRelationship sosFeatureRelationship = new SwesFeatureRelationship();
final FeaturePropertyType fpt = relatedFeature.getFeatureRelationship().getTarget();
if (fpt.getHref() != null && !fpt.getHref().isEmpty()) {
final String identifier = fpt.getHref();
final SamplingFeature feature = new SamplingFeature(new CodeWithAuthority(identifier));
if (fpt.getTitle() != null && !fpt.getTitle().isEmpty()) {
feature.setName(Lists.newArrayList(new CodeType(fpt.getTitle())));
}
if (checkForRequestUrl(fpt.getHref())) {
feature.setUrl(fpt.getHref());
}
feature.setFeatureType(OGCConstants.UNKNOWN);
sosFeatureRelationship.setFeature(feature);
} else {
final Object decodedObject = CodingHelper.decodeXmlElement(fpt);
if (decodedObject instanceof SamplingFeature) {
sosFeatureRelationship.setFeature((SamplingFeature) decodedObject);
} else {
throw new DecoderResponseUnsupportedException(fpt.xmlText(), decodedObject);
}
}
sosFeatureRelationship.setRole(relatedFeature.getFeatureRelationship().getRole());
sosRelatedFeatures.add(sosFeatureRelationship);
}
return sosRelatedFeatures;
}
private boolean checkForRequestUrl(final String href) {
return href.toLowerCase().contains("request=");
}
private Node getNodeFromNodeList(final NodeList nodeList) {
if (nodeList != null && nodeList.getLength() > 0) {
for (int i = 0; i < nodeList.getLength(); i++) {
if (nodeList.item(i).getNodeType() == Node.ELEMENT_NODE) {
return nodeList.item(i);
}
}
}
return null;
}
private Time getValidTime(net.opengis.swes.x20.DescribeSensorType.ValidTime validTime) throws OwsExceptionReport {
Object decodeXmlElement = CodingHelper.decodeXmlElement(validTime.getAbstractTimeGeometricPrimitive());
if (decodeXmlElement instanceof Time) {
return (Time) decodeXmlElement;
} else {
throw new InvalidParameterValueException().at(Sos2Constants.DescribeSensorParams.validTime).withMessage(
"The validTime element ({}) is not supported",
validTime.getAbstractTimeGeometricPrimitive().schemaType());
}
}
private Time getValidTime(net.opengis.swes.x20.SensorDescriptionType.ValidTime validTime)
throws OwsExceptionReport {
Object decodeXmlElement = CodingHelper.decodeXmlElement(validTime.getAbstractTimeGeometricPrimitive());
if (decodeXmlElement instanceof Time) {
return (Time) decodeXmlElement;
} else {
throw new InvalidParameterValueException().at(Sos2Constants.UpdateSensorDescriptionParams.validTime)
.withMessage("The validTime element ({}) is not supported",
validTime.getAbstractTimeGeometricPrimitive().schemaType());
}
}
}