All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opengis.cite.iso19142.util.ServiceMetadataUtils Maven / Gradle / Ivy

There is a newer version: 2.0-r18
Show newest version
package org.opengis.cite.iso19142.util;

import java.net.URI;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.xml.namespace.QName;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.opengis.cite.geomatics.Extents;
import org.opengis.cite.geomatics.SpatialOperator;
import org.opengis.cite.iso19142.ConformanceClass;
import org.opengis.cite.iso19142.FeatureTypeInfo;
import org.opengis.cite.iso19142.Namespaces;
import org.opengis.cite.iso19142.ProtocolBinding;
import org.opengis.cite.iso19142.WFS2;
import org.opengis.geometry.Envelope;
import org.opengis.util.FactoryException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * Provides various utility methods for accessing service metadata.
 */
public class ServiceMetadataUtils {

    private static final Logger LOGR = Logger.getLogger(ServiceMetadataUtils.class.getPackage().getName());

    /**
     * Gets the title of the service.
     * 
     * @param wfsMetadata
     *            The service capabilities document.
     * @return The actual title or "Not specified" if no title appears.
     */
    public static String getServiceTitle(final Document wfsMetadata) {
        Node titleNode = wfsMetadata.getElementsByTagNameNS(Namespaces.OWS, "Title").item(0);
        return (null != titleNode) ? titleNode.getTextContent() : "Not specified";
    }

    /**
     * Extracts a request endpoint from a WFS capabilities document. If the
     * request URI contains a query component it is removed (but not from the
     * source document).
     * 
     * @param wfsMetadata
     *            A DOM Document node containing service metadata (OGC
     *            capabilities document).
     * @param opName
     *            The operation (request) name.
     * @param binding
     *            The message binding to use (if {@code null} any supported
     *            binding will be used).
     * @return A URI referring to a request endpoint; the URI is empty if no
     *         matching endpoint is found.
     */
    public static URI getOperationEndpoint(final Document wfsMetadata, String opName, ProtocolBinding binding) {
        if (null == binding || binding.equals(ProtocolBinding.ANY)) {
            binding = getOperationBindings(wfsMetadata, opName).iterator().next();
        }
        ProtocolBinding originalBinding = binding;
        if (binding.equals(ProtocolBinding.SOAP)) {
            // use POST method for SOAP request
            binding = ProtocolBinding.POST;
        }
        // method name in OGC capabilities doc has initial capital
        StringBuilder method = new StringBuilder(binding.toString());
        method.replace(1, method.length(), method.substring(1).toLowerCase());
        NamespaceBindings nsBindings = new NamespaceBindings();
        nsBindings.addNamespaceBinding(Namespaces.OWS, "ows");
        nsBindings.addNamespaceBinding(Namespaces.XLINK, "xlink");
        String expr = String.format("//ows:Operation[@name='%s']//ows:%s/@xlink:href", opName, method.toString());
        XPath xpath = XPathFactory.newInstance().newXPath();
        xpath.setNamespaceContext(nsBindings);
        URI endpoint = null;
        try {
            String pathToEvaluate = String.format("//ows:Operation[@name='%s']//ows:%s/ows:Constraint/ows:AllowedValues/ows:Value", opName, method.toString());
            Object evalResult = xpath.evaluate(pathToEvaluate, wfsMetadata, XPathConstants.NODESET);
            NodeList allowedValues = (NodeList) evalResult;

            // To get SOAP end point on basis of allowed values
            if (originalBinding.equals(ProtocolBinding.SOAP) && allowedValues.getLength() > 0) {
                evalResult = xpath.evaluate(expr, wfsMetadata, XPathConstants.NODESET);
                NodeList hrefList = (NodeList) evalResult;

                for (int i = 0; i < hrefList.getLength(); i++) {
                    if (allowedValues.item(i).getTextContent().equalsIgnoreCase("soap")) {
                        String href = hrefList.item(i).getNodeValue();
                        endpoint = URI.create(href);
                    }
                }
                if (null == endpoint) {
                    String href = xpath.evaluate(expr, wfsMetadata);
                    endpoint = URI.create(href);
                }
            } else {
                String href = xpath.evaluate(expr, wfsMetadata);
                endpoint = URI.create(href);
            }
        } catch (XPathExpressionException ex) {
            // XPath expression is correct
            TestSuiteLogger.log(Level.INFO, ex.getMessage());
        }
        String queryString = endpoint.getQuery();
        if ( null != queryString ) {
            String uri = endpoint.toString();
            if( queryString.trim().isEmpty() ) {
                // remove trailing '?'
                endpoint = URI.create(uri.substring(0, uri.indexOf('?')));
            } else if (!uri.endsWith("&")) {
                // make sure the query component is ready for appending extra params
                endpoint = URI.create(uri + "&");
            }
        }
        return endpoint;
    }

    /**
     * Returns a Map containing the HTTP endpoints for a given service request.
     * 
     * @param wfsMetadata
     *            A DOM Document node containing service metadata (WFS
     *            capabilities document).
     * @param reqName
     *            The (local) name of the service request.
     * @return A {@literal Map} object that associates an HTTP
     *         method name with a URI, or {@code null} if the request is not
     *         implemented.
     */
    public static Map getRequestEndpoints(final Document wfsMetadata, String reqName) {
        NamespaceBindings nsBindings = NamespaceBindings.withStandardBindings();
        String expr = String.format("//ows:Operation[@name='%s']/descendant::*[@xlink:href]", reqName);
        XPath xpath = XPathFactory.newInstance().newXPath();
        xpath.setNamespaceContext(nsBindings);
        Map endpoints = null;
        try {
            NodeList methodNodes = (NodeList) xpath.evaluate(expr, wfsMetadata, XPathConstants.NODESET);
            if ((null == methodNodes) || methodNodes.getLength() == 0) {
                return null;
            }
            endpoints = new HashMap();
            for (int i = 0; i < methodNodes.getLength(); i++) {
                Element methodElem = (Element) methodNodes.item(i);
                String methodName = methodElem.getLocalName().toUpperCase();
                String href = methodElem.getAttributeNS(Namespaces.XLINK, "href");
                if (href.indexOf('?') > 0) {
                    // prune query component if present
                    href = href.substring(0, href.indexOf('?'));
                }
                endpoints.put(methodName, URI.create(href));
            }
        } catch (XPathExpressionException xpe) {
            TestSuiteLogger.log(Level.INFO, xpe.getMessage());
        }
        return endpoints;
    }

    /**
     * Extracts the list of feature type names from a WFS capabilities document.
     * 
     * @param wfsMetadata
     *            A service capabilities document (wfs:WFS_Capabilities).
     * @return A List containing one or more QName items.
     */
    public static List getFeatureTypes(final Document wfsMetadata) {
        String xpath = "//wfs:FeatureType/wfs:Name";
        Map nsBindings = new HashMap();
        nsBindings.put(Namespaces.WFS, "wfs");
        NodeList typeNames = null;
        try {
            typeNames = XMLUtils.evaluateXPath(wfsMetadata, xpath, nsBindings);
        } catch (XPathExpressionException xpe) {
            TestSuiteLogger.log(Level.INFO, "Failed to evaluate XPath expression: " + xpath, xpe);
        }
        List featureTypes = new ArrayList();
        for (int i = 0; i < typeNames.getLength(); i++) {
            Node typeName = typeNames.item(i);
            featureTypes.add(buildQName(typeName));
        }
        LOGR.fine(featureTypes.toString());
        return featureTypes;
    }

    /**
     * Extracts information about feature types from the service metadata
     * document. The following information items are collected for each feature
     * type:
     * 
     * 
    *
  • Qualified type name (wfs:Name)
  • *
  • Supported CRS identifiers (wfs:DefaultCRS, wfs:OtherCRS)
  • *
  • Spatial extent (ows:WGS84BoundingBox)
  • *
* * @param wfsCapabilities * A Document (wfs:WFS_Capabilities). * @return A Map containing one or more entries where a feature type name * (QName) is associated with a FeatureTypeInfo value object. */ public static Map extractFeatureTypeInfo(final Document wfsCapabilities) { Map featureInfo = new HashMap(); NodeList featureTypes = wfsCapabilities.getElementsByTagNameNS(Namespaces.WFS, "FeatureType"); for (int i = 0; i < featureTypes.getLength(); i++) { FeatureTypeInfo typeInfo = new FeatureTypeInfo(); Element featureTypeElem = (Element) featureTypes.item(i); Node nameNode = featureTypeElem.getElementsByTagNameNS(WFS2.NS_URI, "Name").item(0); QName typeName = buildQName(nameNode); typeInfo.setTypeName(typeName); Node defaultCRSNode = featureTypeElem.getElementsByTagNameNS(WFS2.NS_URI, "DefaultCRS").item(0); if (null != defaultCRSNode) { typeInfo.addCRSIdentifiers(defaultCRSNode.getTextContent()); } NodeList otherCRSNodes = featureTypeElem.getElementsByTagNameNS(WFS2.NS_URI, "OtherCRS"); if (otherCRSNodes.getLength() > 0) { for (int n = 0; n < otherCRSNodes.getLength(); n++) { typeInfo.addCRSIdentifiers(otherCRSNodes.item(n).getTextContent()); } } Node bboxNode = featureTypeElem.getElementsByTagNameNS(Namespaces.OWS, "WGS84BoundingBox").item(0); try { if (null != bboxNode) { Envelope envelope = Extents.createEnvelope(bboxNode); typeInfo.setSpatialExtent(envelope); } } catch (FactoryException e) { TestSuiteLogger.log(Level.WARNING, e.getMessage()); } featureInfo.put(typeInfo.getTypeName(), typeInfo); } return featureInfo; } /** * Builds a QName representing the qualified name conveyed by a node with * text content. * * @param node * A DOM node (Element) containing a qualified name (xsd:QName * value); if it is an unprefixed name, a default namespace * binding should be in scope. * @return A QName object. */ public static QName buildQName(Node node) { String localPart; String nsName = null; String prefix = null; String name = node.getTextContent(); int indexOfColon = name.indexOf(':'); if (indexOfColon > 0) { localPart = name.substring(indexOfColon + 1); nsName = node.lookupNamespaceURI(name.substring(0, indexOfColon)); prefix = node.lookupPrefix(nsName); } else { localPart = name; // return default namespace URI if any nsName = node.lookupNamespaceURI(null); } if(null != prefix) { return new QName(nsName, localPart, prefix); } return new QName(nsName, localPart); } /** * Discovers which protocol bindings are broadly implemented by a WFS. These * global constraints may be overridden for a particular operation. The * values of the standard request encoding constraints are checked: * *
    *
  • KVPEncoding
  • *
  • XMLEncoding
  • *
  • SOAPEncoding
  • *
* * @param wfsMetadata * A service metadata document (wfs:WFS_Capabilities). * @return A Set of protocol bindings implemented by the SUT. */ public static Set getGlobalBindings(final Document wfsMetadata) { if (null == wfsMetadata) { throw new NullPointerException("WFS metadata document is null."); } Set globalBindings = EnumSet.noneOf(ProtocolBinding.class); String xpath = "//ows:OperationsMetadata/ows:Constraint[@name='%s' and (ows:DefaultValue = 'TRUE')]"; Map nsBindings = new HashMap(); nsBindings.put(Namespaces.OWS, "ows"); try { if (XMLUtils.evaluateXPath(wfsMetadata, String.format(xpath, WFS2.KVP_ENC), nsBindings).getLength() > 0) { globalBindings.add(ProtocolBinding.GET); } if (XMLUtils.evaluateXPath(wfsMetadata, String.format(xpath, WFS2.XML_ENC), nsBindings).getLength() > 0) { globalBindings.add(ProtocolBinding.POST); } if (XMLUtils.evaluateXPath(wfsMetadata, String.format(xpath, WFS2.SOAP_ENC), nsBindings).getLength() > 0) { globalBindings.add(ProtocolBinding.SOAP); } } catch (XPathExpressionException xpe) { throw new RuntimeException("Error evaluating XPath expression against capabilities doc. ", xpe); } return globalBindings; } /** * Determines which protocol bindings are supported for a given operation. * This method will currently not handle the case where a global binding is * disabled for an operation (a per-operation constraint overrides a global * constraint). * * @param wfsMetadata * A service metadata document (wfs:WFS_Capabilities). * @param opName * The name of a WFS operation. * @return A Set of protocol bindings supported for the operation. */ public static Set getOperationBindings(final Document wfsMetadata, String opName) { Set protoBindings = new HashSet(); String expr = "//ows:Operation[@name='%s']/ows:Constraint[@name='%s' and (ows:DefaultValue = 'TRUE')]"; for (ProtocolBinding binding : EnumSet.allOf(ProtocolBinding.class)) { String xpath = String.format(expr, opName, binding.getConstraintName()); try { if (XMLUtils.evaluateXPath(wfsMetadata, xpath, null).getLength() > 0) { protoBindings.add(ProtocolBinding.GET); } } catch (XPathExpressionException xpe) { throw new RuntimeException("Error evaluating XPath expression against capabilities doc. ", xpe); } } // union with globally declared bindings protoBindings.addAll(getGlobalBindings(wfsMetadata)); if (opName.equals(WFS2.TRANSACTION)) { // KVP content type not defined for Transaction requests protoBindings.remove(ProtocolBinding.GET); } return protoBindings; } /** * Returns a set of conformance classes that the WFS under test claims to * satisfy. * * @param wfsMetadata * A service metadata document (wfs:WFS_Capabilities). * @return A Set containing at least two members: a fundamental conformance * level and a message binding. * * @see "ISO 19142:2010, Geographic information -- Web Feature Service: Table 13" */ public static Set getConformanceClaims(final Document wfsMetadata) { Set conformanceSet = EnumSet.allOf(ConformanceClass.class); String expr = "//ows:Constraint[@name='%s' and (ows:DefaultValue = 'TRUE')]"; Iterator itr = conformanceSet.iterator(); while (itr.hasNext()) { ConformanceClass conformClass = itr.next(); String xpath = String.format(expr, conformClass.getConstraintName()); NodeList result; try { result = XMLUtils.evaluateXPath(wfsMetadata, xpath, null); } catch (XPathExpressionException xpe) { throw new RuntimeException("Error evaluating XPath expression against capabilities doc. " + xpath, xpe); } if (result.getLength() == 0) { conformanceSet.remove(conformClass); } } return conformanceSet; } /** * Indicates whether or not the specified spatial operator is supported. The * standard operators are listed below. *
    *
  • BBOX (mandatory)
  • *
  • Equals
  • *
  • Disjoint
  • *
  • Intersects
  • *
  • Touches
  • *
  • Crosses
  • *
  • Within
  • *
  • Contains
  • *
  • Overlaps
  • *
  • Beyond
  • *
  • DWithin
  • *
* * @param wfsMetadata * A WFS capabilities document. * @param operatorName * The name of a spatial operator. * @return true if the operator is supported; false if not. */ public static boolean implementsSpatialOperator(final Document wfsMetadata, String operatorName) { String expr = String.format("//fes:SpatialOperator[@name='%s']", operatorName); NodeList results = null; try { results = XMLUtils.evaluateXPath(wfsMetadata, expr, null); } catch (XPathExpressionException e) { // expr ok } return results.getLength() > 0; } /** * Gets the spatial capabilities supported by a WFS: specifically, the set * of implemented spatial operators and their associated geometry operands * (some of which may be common to all operators). * * @param wfsMetadata * A WFS capabilities document. * @return A Map with one entry for each implemented spatial operator (the * key); the value is a set of supported geometry type names * (represented as a QName). */ public static Map> getSpatialCapabilities(final Document wfsMetadata) { NodeList nodeList = null; try { nodeList = XMLUtils.evaluateXPath(wfsMetadata, "//fes:Spatial_Capabilities/fes:GeometryOperands/fes:GeometryOperand", null); } catch (XPathExpressionException e) { // valid expression } Set commonOperands = geometryOperands(nodeList); Map> spatialCapabilities = new EnumMap<>(SpatialOperator.class); nodeList = wfsMetadata.getElementsByTagNameNS(Namespaces.FES, "SpatialOperator"); for (int i = 0; i < nodeList.getLength(); i++) { Element operator = (Element) nodeList.item(i); SpatialOperator op = SpatialOperator.valueOf(operator.getAttribute("name").toUpperCase()); NodeList operands = operator.getElementsByTagNameNS(Namespaces.FES, "GeometryOperand"); Set specificOperands = geometryOperands(operands); specificOperands.addAll(commonOperands); spatialCapabilities.put(op, specificOperands); } return spatialCapabilities; } /** * Returns a set of geometry type names identified in the given list of * geometry operands. * * @param operandList * A list of fes:GeometryOperand elements. * @return A set of qualified names. */ public static Set geometryOperands(NodeList operandList) { Set operands = new HashSet<>(); for (int i = 0; i < operandList.getLength(); i++) { Element operand = (Element) operandList.item(i); // name attribute value is xsd:QName (e.g. gml:Point) String[] geomType = operand.getAttribute("name").split(":"); if (geomType.length != 2) continue; // missing name attribute or invalid QName operands.add(new QName(operand.lookupNamespaceURI(geomType[0]), geomType[1])); } return operands; } /** * Indicates whether or not the specified temporal operator is supported. * The standard operators are listed below. *
    *
  • During (mandatory)
  • *
  • After
  • *
  • Before
  • *
  • Begins
  • *
  • BegunBy
  • *
  • TContains
  • *
  • TEquals
  • *
  • TOverlaps
  • *
  • Meets
  • *
  • OverlappedBy
  • *
  • MetBy
  • *
  • Ends
  • *
  • EndedBy
  • *
* * @param wfsMetadata * A WFS capabilities document. * @param operatorName * The name of a temporal operator. * @return true if the operator is supported; false if not. */ public static boolean implementsTemporalOperator(final Document wfsMetadata, String operatorName) { String expr = String.format("//fes:TemporalOperator[@name='%s']", operatorName); NodeList results = null; try { results = XMLUtils.evaluateXPath(wfsMetadata, expr, null); } catch (XPathExpressionException e) { // expr ok } return results.getLength() > 0; } /** * Indicates whether or not the given service description claims that the * specified WFS or FES conformance class has been implemented. * * @param wfsMetadata * A WFS capabilities document. * @param conformanceClass * The name of a constraint that identifies a conformance class. * @return true if the conformance class is implemented; false if not. * * @see Service * and operation constraints */ public static boolean implementsConformanceClass(final Document wfsMetadata, String conformanceClass) { String expr = String.format( "(//ows:Constraint | //fes:Constraint)[@name='%s' and (.//ows:Value = 'TRUE' or ows:DefaultValue = 'TRUE')]", conformanceClass); NodeList result = null; try { result = XMLUtils.evaluateXPath(wfsMetadata, expr, null); } catch (XPathExpressionException e) { // valid expression } return result.getLength() > 0; } /** * Gets the effective value of the specified service or operation * constraint. The default value or the first allowed value is returned. * * @param wfsMetadata * A WFS capabilities document. * @param constraintName * The name of the constraint. * @return A String denoting the effective constraint value; this is an * empty string if the constraint does not occur in the capabilities * document. */ public static String getConstraintValue(final Document wfsMetadata, String constraintName) { String xpath = " (//ows:Value[1] | //ows:DefaultValue)[ancestor::ows:Constraint[@name='%s']]"; NodeList items = null; try { items = XMLUtils.evaluateXPath(wfsMetadata, String.format(xpath, constraintName), null); } catch (XPathExpressionException e) { // valid expression } String value = ""; for (int i = 0; i < items.getLength(); i++) { Node valueNode = items.item(i); if (valueNode.getLocalName().equals("DefaultValue")) { value = valueNode.getTextContent().trim(); break; } value = valueNode.getTextContent().trim(); } return value; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy