diff --git a/pom.xml b/pom.xml index 14f6c716..c2b96229 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ Open Geospatial Consortium - http://www.opengeospatial.org/ + https://www.ogc.org/ scm:git:https://github.com/opengeospatial/ets-wfs20.git @@ -41,6 +41,11 @@ https://github.com/rjmartell Canada/Pacific + + D. Stenger + https://github.com/dstenger + Europe/Berlin + @@ -130,6 +135,7 @@ jaxb-runtime + @@ -140,64 +146,21 @@ org.opengis.cite.iso19142.TestNGController - - ${basedir}/src/assembly/deps.xml - ${basedir}/src/assembly/ctl-scripts.xml - ${basedir}/src/assembly/aio.xml - - - - - package - - single - - - - - - maven-surefire-plugin - - - maven-compiler-plugin - - 17 - 17 - - maven-jar-plugin - - - org.codehaus.mojo - buildnumber-maven-plugin - maven-scm-publish-plugin - 3.2.1 - - gh-pages - + com.smartbear.soapui soapui-maven-plugin - 5.7.2 - - - com.jgoodies - forms - 1.2.1 - - src/test/resources/soapui/ets-wfs20-soapui-project.xml src/test/resources/soapui/ets-wfs20-soapui-settings.xml - ${project.build.directory}/soapui - true ${soapui.test.fail.ignore} teamengine.endpoint=${soapui.teamengine.endpoint} @@ -213,31 +176,13 @@ io.fabric8 docker-maven-plugin - 0.43.4 - ogccite/${project.artifactId} - ${project.basedir}/src/docker ${project.version}-teamengine-${docker.teamengine.version} - - - - - ${project.build.directory} - . - - dependency/*teamengine-*.war - dependency/*teamengine-*.zip - *ets-*.zip - - - - - @@ -256,7 +201,6 @@ maven-dependency-plugin - 3.6.1 @@ -287,32 +231,6 @@ - - release - - - - maven-gpg-plugin - 3.1.0 - - - sign-artifacts - verify - - sign - - - - --pinentry-mode - loopback - - - - - - - - integration-tests @@ -408,17 +326,6 @@ - - - sonatype-nexus-staging - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - site - scm:git:git@github.com:opengeospatial/ets-wfs20.git - - - @@ -428,4 +335,11 @@ default + + + + site + scm:git:git@github.com:opengeospatial/ets-wfs20.git + + diff --git a/src/main/java/org/opengis/cite/iso19142/BaseFixture.java b/src/main/java/org/opengis/cite/iso19142/BaseFixture.java index f140101a..5e756c90 100644 --- a/src/main/java/org/opengis/cite/iso19142/BaseFixture.java +++ b/src/main/java/org/opengis/cite/iso19142/BaseFixture.java @@ -33,275 +33,274 @@ import jakarta.ws.rs.core.Response; - /** - * A supporting base class that provides common configuration methods and data - * providers. The configuration methods are invoked before any that may be - * defined in a subclass. + * A supporting base class that provides common configuration methods and data providers. + * The configuration methods are invoked before any that may be defined in a subclass. */ public class BaseFixture { - /** Root ETS package. */ - protected static final String ETS_PKG = "/org/opengis/cite/iso19142"; - /** Maximum length of response (string) added as result attribute. */ - private static final int MAX_RSP_ATTR_LENGTH = 4096; - private static final String REQ_ATTR = "request"; - private static final String REQ_POST_ATTR = "post-request"; - private static final String RSP_ATTR = "response"; - /** A DOM document containing service metadata (OGC capabilities). */ - protected Document wfsMetadata; - /** - * A client component for interacting with a WFS. - */ - protected WFSClient wfsClient; - protected Set supportedBindings; - protected List featureTypes; - protected Map featureInfo; - /** A DOM parser. */ - protected DocumentBuilder docBuilder; - protected static final String TNS_PREFIX = "tns"; - /** A Document representing the content of the request message. */ - protected Document reqEntity; - /** A Document representing the content of the response message. */ - protected Document rspEntity; - /** Highest version supported by the IUT. */ - protected String wfsVersion; + /** Root ETS package. */ + protected static final String ETS_PKG = "/org/opengis/cite/iso19142"; + + /** Maximum length of response (string) added as result attribute. */ + private static final int MAX_RSP_ATTR_LENGTH = 4096; + + private static final String REQ_ATTR = "request"; + + private static final String REQ_POST_ATTR = "post-request"; + + private static final String RSP_ATTR = "response"; + + /** A DOM document containing service metadata (OGC capabilities). */ + protected Document wfsMetadata; + + /** + * A client component for interacting with a WFS. + */ + protected WFSClient wfsClient; + + protected Set supportedBindings; + + protected List featureTypes; - public void setWfsClient(WFSClient wfsClient) { - this.wfsClient = wfsClient; - } + protected Map featureInfo; - /** - * Sets up the base fixture. The service metadata document is obtained from - * the ISuite context. The suite attribute - * {@link SuiteAttribute#TEST_SUBJECT testSubject} should yield a DOM - * Document node having {http://www.opengis.net/wfs/2.0}WFS_Capabilities as - * the document element. - * - * The set of implemented protocol bindings is determined from the service - * metadata by checking the values of the following service constraints: - * - *
    - *
  • KVPEncoding
  • - *
  • XMLEncoding
  • - *
  • SOAPEncoding
  • - *
- * - * @param testContext - * The test (set) context. - */ - @BeforeClass(alwaysRun = true) - @SuppressWarnings("unchecked") - public void initBaseFixture(ITestContext testContext) { - if (null != this.featureTypes && !this.featureTypes.isEmpty()) { - return; - } - this.wfsVersion = (String) testContext.getSuite().getAttribute(SuiteAttribute.WFS_VERSION.getName()); - this.wfsMetadata = (Document) testContext.getSuite().getAttribute(SuiteAttribute.TEST_SUBJECT.getName()); - this.wfsClient = new WFSClient(this.wfsMetadata); - Set protoBindings = ServiceMetadataUtils.getGlobalBindings(wfsMetadata); - if (protoBindings.isEmpty()) { - TestSuiteLogger.log(Level.WARNING, "No protocol bindings found in capabilities document."); - } - this.supportedBindings = protoBindings; - this.featureTypes = ServiceMetadataUtils.getFeatureTypes(wfsMetadata); - this.featureInfo = (Map) testContext.getSuite() - .getAttribute(SuiteAttribute.FEATURE_INFO.getName()); - } + /** A DOM parser. */ + protected DocumentBuilder docBuilder; - /** - * Initializes the (namespace-aware) DOM parser. - */ - @BeforeClass - public void initParser() { - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - factory.setNamespaceAware(true); - try { - this.docBuilder = factory.newDocumentBuilder(); - } catch (ParserConfigurationException e) { - TestSuiteLogger.log(Level.WARNING, "Failed to create DOM parser", e); - } - } + protected static final String TNS_PREFIX = "tns"; - /** - * Augments the test result with supplementary attributes in the event that - * a test method failed. The "request" attribute contains a String - * representing the request entity (POST method) or query component (GET - * method). The "response" attribute contains the content of the response - * entity. - * - * @param result - * A description of the test result. - */ - @AfterMethod - public void addAttributesOnTestFailure(ITestResult result) { - if (result.getStatus() != ITestResult.FAILURE) { - return; - } - if (null != this.reqEntity) { - String request = ""; - Object[] params = result.getParameters(); - if (WFSMessage.containsGetProtocolBinding(params)) { - request = WFSMessage.transformEntityToKVP(new DOMSource(this.reqEntity)); - } else { - // https://github.com/opengeospatial/ets-wfs20/issues/233 - // Get requested URI endpoint from ProtocolBinding, assume POST - ProtocolBinding binding = ProtocolBinding.POST; - try { - String methodName = this.reqEntity.getFirstChild().getLocalName(); - URI endpoint = ServiceMetadataUtils.getOperationEndpoint( - this.wfsMetadata, methodName, binding); - result.setAttribute(REQ_POST_ATTR, endpoint.toString()); - } catch (Exception e) { - TestSuiteLogger.log(Level.WARNING, "Could not get POST endpoint URI", e); + /** A Document representing the content of the request message. */ + protected Document reqEntity; + + /** A Document representing the content of the response message. */ + protected Document rspEntity; + + /** Highest version supported by the IUT. */ + protected String wfsVersion; + + public void setWfsClient(WFSClient wfsClient) { + this.wfsClient = wfsClient; + } + + /** + * Sets up the base fixture. The service metadata document is obtained from the ISuite + * context. The suite attribute {@link SuiteAttribute#TEST_SUBJECT testSubject} should + * yield a DOM Document node having {http://www.opengis.net/wfs/2.0}WFS_Capabilities + * as the document element. + * + * The set of implemented protocol bindings is determined from the service metadata by + * checking the values of the following service constraints: + * + *
    + *
  • KVPEncoding
  • + *
  • XMLEncoding
  • + *
  • SOAPEncoding
  • + *
+ * @param testContext The test (set) context. + */ + @BeforeClass(alwaysRun = true) + @SuppressWarnings("unchecked") + public void initBaseFixture(ITestContext testContext) { + if (null != this.featureTypes && !this.featureTypes.isEmpty()) { + return; + } + this.wfsVersion = (String) testContext.getSuite().getAttribute(SuiteAttribute.WFS_VERSION.getName()); + this.wfsMetadata = (Document) testContext.getSuite().getAttribute(SuiteAttribute.TEST_SUBJECT.getName()); + this.wfsClient = new WFSClient(this.wfsMetadata); + Set protoBindings = ServiceMetadataUtils.getGlobalBindings(wfsMetadata); + if (protoBindings.isEmpty()) { + TestSuiteLogger.log(Level.WARNING, "No protocol bindings found in capabilities document."); + } + this.supportedBindings = protoBindings; + this.featureTypes = ServiceMetadataUtils.getFeatureTypes(wfsMetadata); + this.featureInfo = (Map) testContext.getSuite() + .getAttribute(SuiteAttribute.FEATURE_INFO.getName()); + } + + /** + * Initializes the (namespace-aware) DOM parser. + */ + @BeforeClass + public void initParser() { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + try { + this.docBuilder = factory.newDocumentBuilder(); + } + catch (ParserConfigurationException e) { + TestSuiteLogger.log(Level.WARNING, "Failed to create DOM parser", e); + } + } + + /** + * Augments the test result with supplementary attributes in the event that a test + * method failed. The "request" attribute contains a String representing the request + * entity (POST method) or query component (GET method). The "response" attribute + * contains the content of the response entity. + * @param result A description of the test result. + */ + @AfterMethod + public void addAttributesOnTestFailure(ITestResult result) { + if (result.getStatus() != ITestResult.FAILURE) { + return; + } + if (null != this.reqEntity) { + String request = ""; + Object[] params = result.getParameters(); + if (WFSMessage.containsGetProtocolBinding(params)) { + request = WFSMessage.transformEntityToKVP(new DOMSource(this.reqEntity)); + } + else { + // https://github.com/opengeospatial/ets-wfs20/issues/233 + // Get requested URI endpoint from ProtocolBinding, assume POST + ProtocolBinding binding = ProtocolBinding.POST; + try { + String methodName = this.reqEntity.getFirstChild().getLocalName(); + URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, methodName, binding); + result.setAttribute(REQ_POST_ATTR, endpoint.toString()); + } + catch (Exception e) { + TestSuiteLogger.log(Level.WARNING, "Could not get POST endpoint URI", e); } - request = XMLUtils.writeNodeToString(this.reqEntity); - } - result.setAttribute(REQ_ATTR, request); - } - if (null != this.rspEntity) { - StringBuilder response = new StringBuilder(XMLUtils.writeNodeToString(this.rspEntity)); - if (response.length() > MAX_RSP_ATTR_LENGTH) { - response.delete(MAX_RSP_ATTR_LENGTH, response.length()); - } - result.setAttribute(RSP_ATTR, response.toString()); - } - } + request = XMLUtils.writeNodeToString(this.reqEntity); + } + result.setAttribute(REQ_ATTR, request); + } + if (null != this.rspEntity) { + StringBuilder response = new StringBuilder(XMLUtils.writeNodeToString(this.rspEntity)); + if (response.length() > MAX_RSP_ATTR_LENGTH) { + response.delete(MAX_RSP_ATTR_LENGTH, response.length()); + } + result.setAttribute(RSP_ATTR, response.toString()); + } + } - /** - * A DataProvider that supplies the set of application protocol bindings - * supported by the SUT. - * - * @param testContext - * Supplies details about the test run. - * - * @return An Object[][] array containing a ProtocolBinding object in each - * row (first dimension). - */ - @DataProvider(name = "protocol-binding") - public Object[][] getProtocolBindings(ITestContext testContext) { - if (null == this.supportedBindings) { - // BeforeClass method may not have been called yet - initBaseFixture(testContext); - } - Object[][] bindings = new Object[this.supportedBindings.size()][]; - Iterator itr = this.supportedBindings.iterator(); - for (int i = 0; i < bindings.length; i++) { - bindings[i] = new Object[] { itr.next() }; - } - return bindings; - } + /** + * A DataProvider that supplies the set of application protocol bindings supported by + * the SUT. + * @param testContext Supplies details about the test run. + * @return An Object[][] array containing a ProtocolBinding object in each row (first + * dimension). + */ + @DataProvider(name = "protocol-binding") + public Object[][] getProtocolBindings(ITestContext testContext) { + if (null == this.supportedBindings) { + // BeforeClass method may not have been called yet + initBaseFixture(testContext); + } + Object[][] bindings = new Object[this.supportedBindings.size()][]; + Iterator itr = this.supportedBindings.iterator(); + for (int i = 0; i < bindings.length; i++) { + bindings[i] = new Object[] { itr.next() }; + } + return bindings; + } - /** - * A DataProvider that supplies the complete set of feature types recognized - * by the SUT. - * - * @return An Object[][] array containing a QName object in each row (first - * dimension). - */ - @DataProvider(name = "feature-types") - public Object[][] getFeatureTypes() { - Object[][] typeNames = new Object[this.featureTypes.size()][]; - Iterator itr = this.featureTypes.iterator(); - for (int i = 0; i < typeNames.length; i++) { - typeNames[i] = new Object[] { itr.next() }; - } - return typeNames; - } + /** + * A DataProvider that supplies the complete set of feature types recognized by the + * SUT. + * @return An Object[][] array containing a QName object in each row (first + * dimension). + */ + @DataProvider(name = "feature-types") + public Object[][] getFeatureTypes() { + Object[][] typeNames = new Object[this.featureTypes.size()][]; + Iterator itr = this.featureTypes.iterator(); + for (int i = 0; i < typeNames.length; i++) { + typeNames[i] = new Object[] { itr.next() }; + } + return typeNames; + } - /** - * A DataProvider that supplies feature type names for which instances - * exist. - * - * @return An Iterator over an array containing a QName object representing - * the qualified name of a feature type. - */ - @DataProvider(name = "instantiated-feature-types") - public Iterator getInstantiatedFeatureTypes() { - List data = new ArrayList(); - for (QName typeName : this.featureTypes) { - if (this.featureInfo.get(typeName).isInstantiated()) { - data.add(new Object[] { typeName }); - } - } - return data.iterator(); - } + /** + * A DataProvider that supplies feature type names for which instances exist. + * @return An Iterator over an array containing a QName object representing the + * qualified name of a feature type. + */ + @DataProvider(name = "instantiated-feature-types") + public Iterator getInstantiatedFeatureTypes() { + List data = new ArrayList(); + for (QName typeName : this.featureTypes) { + if (this.featureInfo.get(typeName).isInstantiated()) { + data.add(new Object[] { typeName }); + } + } + return data.iterator(); + } - /** - * A DataProvider that supplies parameters specifying a supported protocol - * binding and a feature type. The resulting set is given by the Cartesian - * product of the sets {bindings} x {featureTypes}; its cardinality is equal - * to the product of the cardinalities of the two input sets. - * - * @return An {@literal Iterator} over the set of - * (ProtocolBinding, QName) pairs. - */ - @DataProvider(name = "all-protocols-featureTypes") - public Iterator allProtocolsAndFeatureTypes() { - List params = new ArrayList(); - for (ProtocolBinding binding : supportedBindings) { - for (QName typeName : featureTypes) { - params.add(new Object[] { binding, typeName }); - } - } - return params.iterator(); - } + /** + * A DataProvider that supplies parameters specifying a supported protocol binding and + * a feature type. The resulting set is given by the Cartesian product of the sets + * {bindings} x {featureTypes}; its cardinality is equal to the product of the + * cardinalities of the two input sets. + * @return An {@literal Iterator} over the set of (ProtocolBinding, QName) + * pairs. + */ + @DataProvider(name = "all-protocols-featureTypes") + public Iterator allProtocolsAndFeatureTypes() { + List params = new ArrayList(); + for (ProtocolBinding binding : supportedBindings) { + for (QName typeName : featureTypes) { + params.add(new Object[] { binding, typeName }); + } + } + return params.iterator(); + } + + /** + * A DataProvider that supplies a collection of parameter tuples (a product set) where + * each tuple has two elements: + *
    + *
  1. ProtocolBinding - a supported request binding
  2. + *
  3. QName - the name of a feature type for which data are available
  4. + *
+ * @param testContext The ITestContext object for the test run. + * @return {@literal Iterator} An iterator over a collection of parameter + * tuples (ProtocolBinding, QName). + */ + @DataProvider(name = "protocol-featureType") + public Iterator bindingAndAvailFeatureTypeProductSet(ITestContext testContext) { + ISuite suite = testContext.getSuite(); + Document wfsMetadata = (Document) suite.getAttribute(SuiteAttribute.TEST_SUBJECT.getName()); + if (null == wfsMetadata) { + throw new NullPointerException("Service description not found in ITestContext"); + } + Set globalBindings = ServiceMetadataUtils.getGlobalBindings(wfsMetadata); + DataSampler sampler = (DataSampler) suite.getAttribute(SuiteAttribute.SAMPLER.getName()); + Map featureInfo = sampler.getFeatureTypeInfo(); + List paramList = new ArrayList(); + for (ProtocolBinding binding : globalBindings) { + for (FeatureTypeInfo typeInfo : featureInfo.values()) { + if (typeInfo.isInstantiated()) { + Object[] tuple = { binding, typeInfo.getTypeName() }; + paramList.add(tuple); + } + } + } + return paramList.iterator(); + } - /** - * A DataProvider that supplies a collection of parameter tuples (a product - * set) where each tuple has two elements: - *
    - *
  1. ProtocolBinding - a supported request binding
  2. - *
  3. QName - the name of a feature type for which data are available
  4. - *
- * - * @param testContext - * The ITestContext object for the test run. - * @return {@literal Iterator} An iterator over a collection of - * parameter tuples (ProtocolBinding, QName). - */ - @DataProvider(name = "protocol-featureType") - public Iterator bindingAndAvailFeatureTypeProductSet(ITestContext testContext) { - ISuite suite = testContext.getSuite(); - Document wfsMetadata = (Document) suite.getAttribute(SuiteAttribute.TEST_SUBJECT.getName()); - if (null == wfsMetadata) { - throw new NullPointerException("Service description not found in ITestContext"); - } - Set globalBindings = ServiceMetadataUtils.getGlobalBindings(wfsMetadata); - DataSampler sampler = (DataSampler) suite.getAttribute(SuiteAttribute.SAMPLER.getName()); - Map featureInfo = sampler.getFeatureTypeInfo(); - List paramList = new ArrayList(); - for (ProtocolBinding binding : globalBindings) { - for (FeatureTypeInfo typeInfo : featureInfo.values()) { - if (typeInfo.isInstantiated()) { - Object[] tuple = { binding, typeInfo.getTypeName() }; - paramList.add(tuple); - } - } - } - return paramList.iterator(); - } + /** + * Extracts the body of the response message as a DOM Document node. For a SOAP + * response this will contain the content of the SOAP body element. + * @param rsp A Response representing an HTTP response message. + * @return A Document representing the response entity, or {@code null} if it could + * not be parsed. + */ + protected Document extractBodyAsDocument(Response rsp) { + Document entity = rsp.readEntity(Document.class); + try { + Element soapBody = (Element) XMLUtils.evaluateXPath(entity, "//soap11:Body/*[1] | //soap:Body/*[1]", null, + XPathConstants.NODE); + if (null != soapBody) { + entity.replaceChild(soapBody, entity.getDocumentElement()); + } + } + catch (XPathExpressionException xpe) { + throw new RuntimeException(xpe); + } + return entity; + } - /** - * Extracts the body of the response message as a DOM Document node. For a - * SOAP response this will contain the content of the SOAP body element. - * - * @param rsp - * A Response representing an HTTP response message. - * @return A Document representing the response entity, or {@code null} if - * it could not be parsed. - */ - protected Document extractBodyAsDocument(Response rsp) { - Document entity = rsp.readEntity(Document.class); - try { - Element soapBody = (Element) XMLUtils.evaluateXPath(entity, "//soap11:Body/*[1] | //soap:Body/*[1]", null, - XPathConstants.NODE); - if (null != soapBody) { - entity.replaceChild(soapBody, entity.getDocumentElement()); - } - } catch (XPathExpressionException xpe) { - throw new RuntimeException(xpe); - } - return entity; - } } diff --git a/src/main/java/org/opengis/cite/iso19142/CommandLineArguments.java b/src/main/java/org/opengis/cite/iso19142/CommandLineArguments.java index 2f017cce..3455ab1a 100644 --- a/src/main/java/org/opengis/cite/iso19142/CommandLineArguments.java +++ b/src/main/java/org/opengis/cite/iso19142/CommandLineArguments.java @@ -9,9 +9,8 @@ import java.util.List; /** - * Declares supported command line arguments that are parsed using the - * JCommander library. All arguments are optional. The default values are as - * follows: + * Declares supported command line arguments that are parsed using the JCommander library. + * All arguments are optional. The default values are as follows: *
    *
  • XML properties file: ${user.home}/test-run-props.xml
  • *
  • outputDir: ${user.home}
  • @@ -28,36 +27,38 @@ */ public class CommandLineArguments { - @Parameter(description = "Properties file") - private List xmlProps; - - @Parameter(names = { "-o", "--outputDir" }, description = "Output directory") - private String outputDir; - - @Parameter(names = { "-d", - "--deleteSubjectOnFinish" }, description = "Delete file containing representation of test subject when finished") - private boolean deleteSubjectOnFinish = false; - - public CommandLineArguments() { - this.xmlProps = new ArrayList<>(); - } - - public File getPropertiesFile() { - File fileRef; - if (xmlProps.isEmpty()) { - fileRef = new File(FilenameUtils.normalize(System.getProperty("user.home")), "test-run-props.xml"); - } else { - String propsFile = xmlProps.get(0); - fileRef = (propsFile.startsWith("file:")) ? new File(URI.create(propsFile)) : new File(propsFile); - } - return fileRef; - } - - public String getOutputDir() { - return (null != outputDir) ? outputDir : System.getProperty("user.home"); - } - - public boolean doDeleteSubjectOnFinish() { - return deleteSubjectOnFinish; - } + @Parameter(description = "Properties file") + private List xmlProps; + + @Parameter(names = { "-o", "--outputDir" }, description = "Output directory") + private String outputDir; + + @Parameter(names = { "-d", "--deleteSubjectOnFinish" }, + description = "Delete file containing representation of test subject when finished") + private boolean deleteSubjectOnFinish = false; + + public CommandLineArguments() { + this.xmlProps = new ArrayList<>(); + } + + public File getPropertiesFile() { + File fileRef; + if (xmlProps.isEmpty()) { + fileRef = new File(FilenameUtils.normalize(System.getProperty("user.home")), "test-run-props.xml"); + } + else { + String propsFile = xmlProps.get(0); + fileRef = (propsFile.startsWith("file:")) ? new File(URI.create(propsFile)) : new File(propsFile); + } + return fileRef; + } + + public String getOutputDir() { + return (null != outputDir) ? outputDir : System.getProperty("user.home"); + } + + public boolean doDeleteSubjectOnFinish() { + return deleteSubjectOnFinish; + } + } diff --git a/src/main/java/org/opengis/cite/iso19142/ConformanceClass.java b/src/main/java/org/opengis/cite/iso19142/ConformanceClass.java index d0407330..c50aa79a 100644 --- a/src/main/java/org/opengis/cite/iso19142/ConformanceClass.java +++ b/src/main/java/org/opengis/cite/iso19142/ConformanceClass.java @@ -8,7 +8,7 @@ *
  • Transactional WFS
  • *
  • Locking WFS
  • * - * + * *

    * Sources *

    @@ -45,4 +45,5 @@ private ConformanceClass(String constraintName) { public String getConstraintName() { return constraintName; } + } diff --git a/src/main/java/org/opengis/cite/iso19142/ETSAssert.java b/src/main/java/org/opengis/cite/iso19142/ETSAssert.java index 7f924c78..6ce19f97 100644 --- a/src/main/java/org/opengis/cite/iso19142/ETSAssert.java +++ b/src/main/java/org/opengis/cite/iso19142/ETSAssert.java @@ -42,393 +42,346 @@ */ public class ETSAssert { - private final static Logger LOGR = Logger.getLogger(ETSAssert.class.getName()); + private final static Logger LOGR = Logger.getLogger(ETSAssert.class.getName()); - private ETSAssert() { - } + private ETSAssert() { + } - /** - * Asserts that the qualified name of a DOM Node matches the expected value. - * - * @param node - * The Node to check. - * @param qName - * A QName object containing a namespace name (URI) and a local - * part. - */ - public static void assertQualifiedName(Node node, QName qName) { - Assert.assertEquals(node.getLocalName(), qName.getLocalPart(), ErrorMessage.get(ErrorMessageKeys.LOCAL_NAME)); - Assert.assertEquals(node.getNamespaceURI(), qName.getNamespaceURI(), - ErrorMessage.get(ErrorMessageKeys.NAMESPACE_NAME)); - } + /** + * Asserts that the qualified name of a DOM Node matches the expected value. + * @param node The Node to check. + * @param qName A QName object containing a namespace name (URI) and a local part. + */ + public static void assertQualifiedName(Node node, QName qName) { + Assert.assertEquals(node.getLocalName(), qName.getLocalPart(), ErrorMessage.get(ErrorMessageKeys.LOCAL_NAME)); + Assert.assertEquals(node.getNamespaceURI(), qName.getNamespaceURI(), + ErrorMessage.get(ErrorMessageKeys.NAMESPACE_NAME)); + } - /** - * Asserts that an XPath 1.0 expression holds true for the given evaluation - * context. The following standard namespace bindings do not need to be - * explicitly declared: - * - *
      - *
    • wfs: {@value org.opengis.cite.iso19142.Namespaces#WFS}
    • - *
    • fes: {@value org.opengis.cite.iso19142.Namespaces#FES}
    • - *
    • ows: {@value org.opengis.cite.iso19142.Namespaces#OWS}
    • - *
    • xlink: {@value org.opengis.cite.iso19142.Namespaces#XLINK}
    • - *
    • gml: {@value org.opengis.cite.iso19142.Namespaces#GML}
    • - *
    • soap: {@value org.opengis.cite.iso19142.Namespaces#SOAP_ENV}
    • - *
    • xsi: - * {@value javax.xml.XMLConstants#W3C_XML_SCHEMA_INSTANCE_NS_URI}
    • - *
    - * - * The method arguments will be logged at level FINE or lower. - * - * @param expr - * A valid XPath 1.0 expression. - * @param context - * The context node. - * @param nsBindings - * A collection of namespace bindings for the XPath expression, - * where each entry maps a namespace URI (key) to a prefix - * (value). It may be {@code null}. - */ - public static void assertXPath(String expr, Node context, Map nsBindings) { - boolean result = evaluateXPathToBoolean(expr, context, nsBindings); - Assert.assertTrue(result, ErrorMessage.format(ErrorMessageKeys.XPATH_RESULT, context.getNodeName(), expr)); - } + /** + * Asserts that an XPath 1.0 expression holds true for the given evaluation context. + * The following standard namespace bindings do not need to be explicitly declared: + * + *
      + *
    • wfs: {@value org.opengis.cite.iso19142.Namespaces#WFS}
    • + *
    • fes: {@value org.opengis.cite.iso19142.Namespaces#FES}
    • + *
    • ows: {@value org.opengis.cite.iso19142.Namespaces#OWS}
    • + *
    • xlink: {@value org.opengis.cite.iso19142.Namespaces#XLINK}
    • + *
    • gml: {@value org.opengis.cite.iso19142.Namespaces#GML}
    • + *
    • soap: {@value org.opengis.cite.iso19142.Namespaces#SOAP_ENV}
    • + *
    • xsi: {@value javax.xml.XMLConstants#W3C_XML_SCHEMA_INSTANCE_NS_URI}
    • + *
    + * + * The method arguments will be logged at level FINE or lower. + * @param expr A valid XPath 1.0 expression. + * @param context The context node. + * @param nsBindings A collection of namespace bindings for the XPath expression, + * where each entry maps a namespace URI (key) to a prefix (value). It may be + * {@code null}. + */ + public static void assertXPath(String expr, Node context, Map nsBindings) { + boolean result = evaluateXPathToBoolean(expr, context, nsBindings); + Assert.assertTrue(result, ErrorMessage.format(ErrorMessageKeys.XPATH_RESULT, context.getNodeName(), expr)); + } - /** - * The XPath is evaluated for given expr and context. - * - * @param expr - * A valid XPath expression. - * @param context - * A context node. - * @param nsBindings - * The list of namespace required for the expr. - * @return True if XPath is evaluated successfully otherwise false. - */ - public static boolean evaluateXPathToBoolean(String expr, Node context, Map nsBindings ) { - if ( null == context ) { - throw new NullPointerException( "Context node is null." ); - } - LOGR.log( Level.FINE, "Evaluating \"{0}\" against context node:\n{1}", - new Object[] { expr, XMLUtils.writeNodeToString( context ) } ); - try { - boolean result = (boolean) XMLUtils.evaluateXPath( context, expr, nsBindings, XPathConstants.BOOLEAN ); - LOGR.log( Level.FINE, "XPath result: " + result ); - return result; - } catch ( XPathExpressionException e ) { - String msg = ErrorMessage.format( ErrorMessageKeys.XPATH_ERROR, expr ); - LOGR.log( Level.WARNING, msg, e ); - throw new AssertionError( msg ); - } - } + /** + * The XPath is evaluated for given expr and context. + * @param expr A valid XPath expression. + * @param context A context node. + * @param nsBindings The list of namespace required for the expr. + * @return True if XPath is evaluated successfully otherwise false. + */ + public static boolean evaluateXPathToBoolean(String expr, Node context, Map nsBindings) { + if (null == context) { + throw new NullPointerException("Context node is null."); + } + LOGR.log(Level.FINE, "Evaluating \"{0}\" against context node:\n{1}", + new Object[] { expr, XMLUtils.writeNodeToString(context) }); + try { + boolean result = (boolean) XMLUtils.evaluateXPath(context, expr, nsBindings, XPathConstants.BOOLEAN); + LOGR.log(Level.FINE, "XPath result: " + result); + return result; + } + catch (XPathExpressionException e) { + String msg = ErrorMessage.format(ErrorMessageKeys.XPATH_ERROR, expr); + LOGR.log(Level.WARNING, msg, e); + throw new AssertionError(msg); + } + } - /** - * Asserts that an XPath 2.0 expression evaluates to {@code true} for the - * given XML source. That is, the result set is not empty. - * - * @param expr - * An XPath 2.0 expression. - * @param source - * A Source object representing an XML resource. - * @param namespaceBindings - * A collection of namespace bindings for the XPath expression, - * where each entry maps a namespace URI (key) to a prefix - * (value). It may be {@code null}. - */ - public static void assertXPath2(String expr, Source source, Map namespaceBindings) { - if (TestSuiteLogger.isLoggable(Level.FINE)) { - TestSuiteLogger.log(Level.FINE, "Asserting XPath expression {0} against {1} ({2})", - new Object[] { expr, source.getClass().getName(), source.getSystemId() }); - } - XdmValue result = null; - try { - result = XMLUtils.evaluateXPath2(source, expr, namespaceBindings); - } catch (SaxonApiException e) { - throw new AssertionError(ErrorMessage.format(ErrorMessageKeys.XPATH_ERROR, expr + e.getMessage())); - } - Assert.assertTrue(result.size() > 0, - ErrorMessage.format(ErrorMessageKeys.XPATH_RESULT, source.getSystemId(), expr)); - } + /** + * Asserts that an XPath 2.0 expression evaluates to {@code true} for the given XML + * source. That is, the result set is not empty. + * @param expr An XPath 2.0 expression. + * @param source A Source object representing an XML resource. + * @param namespaceBindings A collection of namespace bindings for the XPath + * expression, where each entry maps a namespace URI (key) to a prefix (value). It may + * be {@code null}. + */ + public static void assertXPath2(String expr, Source source, Map namespaceBindings) { + if (TestSuiteLogger.isLoggable(Level.FINE)) { + TestSuiteLogger.log(Level.FINE, "Asserting XPath expression {0} against {1} ({2})", + new Object[] { expr, source.getClass().getName(), source.getSystemId() }); + } + XdmValue result = null; + try { + result = XMLUtils.evaluateXPath2(source, expr, namespaceBindings); + } + catch (SaxonApiException e) { + throw new AssertionError(ErrorMessage.format(ErrorMessageKeys.XPATH_ERROR, expr + e.getMessage())); + } + Assert.assertTrue(result.size() > 0, + ErrorMessage.format(ErrorMessageKeys.XPATH_RESULT, source.getSystemId(), expr)); + } - /** - * Asserts that an XML resource is schema-valid. - * - * @param validator - * The Validator to use. - * @param source - * The XML Source to be validated. - */ - public static void assertSchemaValid(Validator validator, Source source) { - ValidationErrorHandler errHandler = new ValidationErrorHandler(); - validator.setErrorHandler(errHandler); - try { - validator.validate(source); - } catch (Exception e) { - throw new AssertionError(ErrorMessage.format(ErrorMessageKeys.XML_ERROR, e.getMessage())); - } - Assert.assertFalse(errHandler.errorsDetected(), ErrorMessage.format(ErrorMessageKeys.NOT_SCHEMA_VALID, - errHandler.getErrorCount(), errHandler.toString())); - } + /** + * Asserts that an XML resource is schema-valid. + * @param validator The Validator to use. + * @param source The XML Source to be validated. + */ + public static void assertSchemaValid(Validator validator, Source source) { + ValidationErrorHandler errHandler = new ValidationErrorHandler(); + validator.setErrorHandler(errHandler); + try { + validator.validate(source); + } + catch (Exception e) { + throw new AssertionError(ErrorMessage.format(ErrorMessageKeys.XML_ERROR, e.getMessage())); + } + Assert.assertFalse(errHandler.errorsDetected(), ErrorMessage.format(ErrorMessageKeys.NOT_SCHEMA_VALID, + errHandler.getErrorCount(), errHandler.toString())); + } - /** - * Asserts that an XML resource satisfies all applicable constraints - * specified in a Schematron (ISO 19757-3) schema. The "xslt2" query - * language binding is supported. All patterns are checked. - * - * @param schemaRef - * A URL that denotes the location of a Schematron schema. - * @param xmlSource - * The XML Source to be validated. - */ - public static void assertSchematronValid(URL schemaRef, Source xmlSource) { - SchematronValidator validator; - try { - validator = new SchematronValidator(new StreamSource(schemaRef.toString()), "#ALL"); - } catch (Exception e) { - StringBuilder msg = new StringBuilder("Failed to process Schematron schema at "); - msg.append(schemaRef).append('\n'); - msg.append(e.getMessage()); - throw new AssertionError(msg); - } - Result result = validator.validate(xmlSource); - Assert.assertFalse(validator.ruleViolationsDetected(), ErrorMessage.format(ErrorMessageKeys.NOT_SCHEMA_VALID, - validator.getRuleViolationCount(), XMLUtils.resultToString(result))); - } + /** + * Asserts that an XML resource satisfies all applicable constraints specified in a + * Schematron (ISO 19757-3) schema. The "xslt2" query language binding is supported. + * All patterns are checked. + * @param schemaRef A URL that denotes the location of a Schematron schema. + * @param xmlSource The XML Source to be validated. + */ + public static void assertSchematronValid(URL schemaRef, Source xmlSource) { + SchematronValidator validator; + try { + validator = new SchematronValidator(new StreamSource(schemaRef.toString()), "#ALL"); + } + catch (Exception e) { + StringBuilder msg = new StringBuilder("Failed to process Schematron schema at "); + msg.append(schemaRef).append('\n'); + msg.append(e.getMessage()); + throw new AssertionError(msg); + } + Result result = validator.validate(xmlSource); + Assert.assertFalse(validator.ruleViolationsDetected(), ErrorMessage.format(ErrorMessageKeys.NOT_SCHEMA_VALID, + validator.getRuleViolationCount(), XMLUtils.resultToString(result))); + } - /** - * Asserts the availability of a feature having the specified resource - * identifier. The following XPath expression is checked against the - * GetFeature response entity: - * - * {@code //wfs:member/*[@gml:id = '$id']} - * - * @param id - * The feature identifier (assigned by the system). - * @param isAvailable - * A boolean value asserting that the feature either is ( - * {@code true}) or is not ({@code false}) available. - * @param wfsClient - * A WFSClient component that interacts with the SUT. - */ - public static void assertFeatureAvailability(String id, boolean isAvailable, WFSClient wfsClient) { - Map params = new HashMap(); - params.put("id", id); - Document rsp = wfsClient.invokeStoredQuery(WFS2.QRY_GET_FEATURE_BY_ID, params); - String xpath = String.format("/*[@gml:id = '%s']", id); - NodeList result; - try { - result = XMLUtils.evaluateXPath(rsp, xpath, null); - } catch (XPathExpressionException e) { - throw new RuntimeException(e); - } - if (isAvailable && result.getLength() == 0) { - throw new AssertionError( - ErrorMessage.format(ErrorMessageKeys.FEATURE_AVAILABILITY, result.getLength(), id)); - } - if (!isAvailable && result.getLength() > 0) { - throw new AssertionError( - ErrorMessage.format(ErrorMessageKeys.FEATURE_AVAILABILITY, result.getLength(), id)); - } - } + /** + * Asserts the availability of a feature having the specified resource identifier. The + * following XPath expression is checked against the GetFeature response entity: + * + * {@code //wfs:member/*[@gml:id = '$id']} + * @param id The feature identifier (assigned by the system). + * @param isAvailable A boolean value asserting that the feature either is ( + * {@code true}) or is not ({@code false}) available. + * @param wfsClient A WFSClient component that interacts with the SUT. + */ + public static void assertFeatureAvailability(String id, boolean isAvailable, WFSClient wfsClient) { + Map params = new HashMap(); + params.put("id", id); + Document rsp = wfsClient.invokeStoredQuery(WFS2.QRY_GET_FEATURE_BY_ID, params); + String xpath = String.format("/*[@gml:id = '%s']", id); + NodeList result; + try { + result = XMLUtils.evaluateXPath(rsp, xpath, null); + } + catch (XPathExpressionException e) { + throw new RuntimeException(e); + } + if (isAvailable && result.getLength() == 0) { + throw new AssertionError( + ErrorMessage.format(ErrorMessageKeys.FEATURE_AVAILABILITY, result.getLength(), id)); + } + if (!isAvailable && result.getLength() > 0) { + throw new AssertionError( + ErrorMessage.format(ErrorMessageKeys.FEATURE_AVAILABILITY, result.getLength(), id)); + } + } - /** - * Asserts that one or more simple properties of a feature have the expected - * values. If a property occurs more than once, only the first occurrence is - * checked. - * - * @param feature - * An Element node representing a GML feature. - * @param expectedValues - * A collection of feature properties containing {prop: value} - * entries, where the prop key is an element - * declaration (XSElementDeclaration) denoting a property. - * @param nsBindings - * A collection of namespace bindings for the supplied - * properties, where each entry is a {namespace-name: prefix} - * pair. A binding is not required for standard GML properties. - */ - public static void assertSimpleProperties(Element feature, Map expectedValues, - Map nsBindings) { - for (Map.Entry property : expectedValues.entrySet()) { - XSElementDeclaration propDecl = property.getKey(); - XSSimpleTypeDefinition propType; - if (propDecl.getTypeDefinition().getTypeCategory() == XSTypeDefinition.SIMPLE_TYPE) { - propType = (XSSimpleTypeDefinition) propDecl.getTypeDefinition(); - } else { - XSComplexTypeDefinition complexType = (XSComplexTypeDefinition) propDecl.getTypeDefinition(); - propType = complexType.getSimpleType(); - } - String expectedValue = XMLUtils.expandReferencesInText(property.getValue().toString()); - String expr = null; - String nsPrefix = (propDecl.getNamespace().equals(Namespaces.GML)) ? "gml" - : nsBindings.get(propDecl.getNamespace()); - if (propType.getNumeric()) { - expr = String.format("number(%s:%s[1]) eq %s", nsPrefix, propDecl.getName(), expectedValue); - } else { // string comparison - expr = String.format("%s:%s[1] eq \"%s\"", nsPrefix, propDecl.getName(), expectedValue); - } - assertXPath2(expr, new DOMSource(feature), nsBindings); - } - } + /** + * Asserts that one or more simple properties of a feature have the expected values. + * If a property occurs more than once, only the first occurrence is checked. + * @param feature An Element node representing a GML feature. + * @param expectedValues A collection of feature properties containing {prop: value} + * entries, where the prop key is an element declaration + * (XSElementDeclaration) denoting a property. + * @param nsBindings A collection of namespace bindings for the supplied properties, + * where each entry is a {namespace-name: prefix} pair. A binding is not required for + * standard GML properties. + */ + public static void assertSimpleProperties(Element feature, Map expectedValues, + Map nsBindings) { + for (Map.Entry property : expectedValues.entrySet()) { + XSElementDeclaration propDecl = property.getKey(); + XSSimpleTypeDefinition propType; + if (propDecl.getTypeDefinition().getTypeCategory() == XSTypeDefinition.SIMPLE_TYPE) { + propType = (XSSimpleTypeDefinition) propDecl.getTypeDefinition(); + } + else { + XSComplexTypeDefinition complexType = (XSComplexTypeDefinition) propDecl.getTypeDefinition(); + propType = complexType.getSimpleType(); + } + String expectedValue = XMLUtils.expandReferencesInText(property.getValue().toString()); + String expr = null; + String nsPrefix = (propDecl.getNamespace().equals(Namespaces.GML)) ? "gml" + : nsBindings.get(propDecl.getNamespace()); + if (propType.getNumeric()) { + expr = String.format("number(%s:%s[1]) eq %s", nsPrefix, propDecl.getName(), expectedValue); + } + else { // string comparison + expr = String.format("%s:%s[1] eq \"%s\"", nsPrefix, propDecl.getName(), expectedValue); + } + assertXPath2(expr, new DOMSource(feature), nsBindings); + } + } - /** - * Asserts that the given XML entity contains the expected number of - * descendant elements having the specified name. - * - * @param xmlEntity - * A Document representing an XML entity. - * @param elementName - * The qualified name of the element. - * @param expectedCount - * The expected number of occurrences. - */ - public static void assertDescendantElementCount(Document xmlEntity, QName elementName, int expectedCount) { - NodeList features = xmlEntity.getElementsByTagNameNS(elementName.getNamespaceURI(), elementName.getLocalPart()); - Assert.assertEquals(features.getLength(), expectedCount, - String.format("Unexpected number of %s descendant elements.", elementName)); - } + /** + * Asserts that the given XML entity contains the expected number of descendant + * elements having the specified name. + * @param xmlEntity A Document representing an XML entity. + * @param elementName The qualified name of the element. + * @param expectedCount The expected number of occurrences. + */ + public static void assertDescendantElementCount(Document xmlEntity, QName elementName, int expectedCount) { + NodeList features = xmlEntity.getElementsByTagNameNS(elementName.getNamespaceURI(), elementName.getLocalPart()); + Assert.assertEquals(features.getLength(), expectedCount, + String.format("Unexpected number of %s descendant elements.", elementName)); + } - /** - * Asserts that the actual HTTP status code matches one of the expected - * status codes. - * - * @param actualCode - * The actual status code. - * @param expectedCodes - * An int[] array containing the expected status codes. - */ - public static void assertStatusCode(int actualCode, int[] expectedCodes) { - Arrays.sort(expectedCodes); // precondition for binary search - Assert.assertTrue(Arrays.binarySearch(expectedCodes, actualCode) >= 0, String - .format("Expected status code(s) %s but received %d.", Arrays.toString(expectedCodes), actualCode)); + /** + * Asserts that the actual HTTP status code matches one of the expected status codes. + * @param actualCode The actual status code. + * @param expectedCodes An int[] array containing the expected status codes. + */ + public static void assertStatusCode(int actualCode, int[] expectedCodes) { + Arrays.sort(expectedCodes); // precondition for binary search + Assert.assertTrue(Arrays.binarySearch(expectedCodes, actualCode) >= 0, String + .format("Expected status code(s) %s but received %d.", Arrays.toString(expectedCodes), actualCode)); - } + } - /** - * Asserts that the given DOM document contains a description of a "Simple - * WFS" implementation. - * - * @param doc - * A Document node representing a WFS capabilities document - * (wfs:WFS_Capabilities}. - */ - public static void assertSimpleWFSCapabilities(Document doc) { - SchematronValidator validator = ValidationUtils.buildSchematronValidator("wfs-capabilities-2.0.sch", - "SimpleWFSPhase"); - Result result = validator.validate(new DOMSource(doc, doc.getDocumentURI()), false); - Assert.assertFalse(validator.ruleViolationsDetected(), ErrorMessage.format(ErrorMessageKeys.NOT_SCHEMA_VALID, - validator.getRuleViolationCount(), XMLUtils.resultToString(result))); - } + /** + * Asserts that the given DOM document contains a description of a "Simple WFS" + * implementation. + * @param doc A Document node representing a WFS capabilities document + * (wfs:WFS_Capabilities}. + */ + public static void assertSimpleWFSCapabilities(Document doc) { + SchematronValidator validator = ValidationUtils.buildSchematronValidator("wfs-capabilities-2.0.sch", + "SimpleWFSPhase"); + Result result = validator.validate(new DOMSource(doc, doc.getDocumentURI()), false); + Assert.assertFalse(validator.ruleViolationsDetected(), ErrorMessage.format(ErrorMessageKeys.NOT_SCHEMA_VALID, + validator.getRuleViolationCount(), XMLUtils.resultToString(result))); + } - /** - * Asserts that the given GetFeature response entity contains the expected - * number of feature instances having the specified type name. - * - * @param featureCollection - * A Document representing a GetFeature response - * (wfs:FeatureCollection). - * @param featureType - * The qualified name of a feature type; this may be null, in - * which case the actual type is ignored. - * @param expectedCount - * The expected number of feature instances. - */ - public static void assertFeatureCount(Document featureCollection, QName featureType, int expectedCount) { - NodeList features; - if (null != featureType) { - features = featureCollection.getElementsByTagNameNS(featureType.getNamespaceURI(), - featureType.getLocalPart()); - Assert.assertEquals(features.getLength(), expectedCount, - String.format("Unexpected number of %s feature instances in response.", featureType)); - } else { - features = featureCollection.getElementsByTagNameNS(WFS2.NS_URI, "member"); - Assert.assertEquals(features.getLength(), expectedCount, - "Unexpected number of feature members in response."); - } - } + /** + * Asserts that the given GetFeature response entity contains the expected number of + * feature instances having the specified type name. + * @param featureCollection A Document representing a GetFeature response + * (wfs:FeatureCollection). + * @param featureType The qualified name of a feature type; this may be null, in which + * case the actual type is ignored. + * @param expectedCount The expected number of feature instances. + */ + public static void assertFeatureCount(Document featureCollection, QName featureType, int expectedCount) { + NodeList features; + if (null != featureType) { + features = featureCollection.getElementsByTagNameNS(featureType.getNamespaceURI(), + featureType.getLocalPart()); + Assert.assertEquals(features.getLength(), expectedCount, + String.format("Unexpected number of %s feature instances in response.", featureType)); + } + else { + features = featureCollection.getElementsByTagNameNS(WFS2.NS_URI, "member"); + Assert.assertEquals(features.getLength(), expectedCount, + "Unexpected number of feature members in response."); + } + } - /** - * Asserts that the given response message contains an OGC exception report. - * The message body must contain an XML document that has a document element - * with the following properties: - * - *
      - *
    • [local name] = "ExceptionReport"
    • - *
    • [namespace name] = "http://www.opengis.net/ows/1.1"
    • - *
    - * - * @param rspEntity - * A Document node representing an HTTP response entity. - * @param exceptionCode - * The expected OGC exception code. - * @param locator - * A case-insensitive string value expected to occur in the - * locator attribute (e.g. a parameter name); the attribute value - * will be ignored if the argument is null or empty. - */ - public static void assertExceptionReport(Document rspEntity, String exceptionCode, String locator) { - String expr = String.format("//ows11:Exception[@exceptionCode = '%s']", exceptionCode); - NodeList nodeList = null; - try { - nodeList = XMLUtils.evaluateXPath(rspEntity, expr, Collections.singletonMap(Namespaces.OWS, "ows11")); - } catch (XPathExpressionException xpe) { - // won't happen - } - Assert.assertTrue(nodeList.getLength() > 0, "Exception not found in response: " + expr); - if (null != locator && !locator.isEmpty()) { - Element exception = (Element) nodeList.item(0); - String locatorValue = exception.getAttribute("locator").toLowerCase(); - Assert.assertTrue(locatorValue.contains(locator.toLowerCase()), - String.format("Expected locator attribute to contain '%s']", locator)); - } - } + /** + * Asserts that the given response message contains an OGC exception report. The + * message body must contain an XML document that has a document element with the + * following properties: + * + *
      + *
    • [local name] = "ExceptionReport"
    • + *
    • [namespace name] = "http://www.opengis.net/ows/1.1"
    • + *
    + * @param rspEntity A Document node representing an HTTP response entity. + * @param exceptionCode The expected OGC exception code. + * @param locator A case-insensitive string value expected to occur in the locator + * attribute (e.g. a parameter name); the attribute value will be ignored if the + * argument is null or empty. + */ + public static void assertExceptionReport(Document rspEntity, String exceptionCode, String locator) { + String expr = String.format("//ows11:Exception[@exceptionCode = '%s']", exceptionCode); + NodeList nodeList = null; + try { + nodeList = XMLUtils.evaluateXPath(rspEntity, expr, Collections.singletonMap(Namespaces.OWS, "ows11")); + } + catch (XPathExpressionException xpe) { + // won't happen + } + Assert.assertTrue(nodeList.getLength() > 0, "Exception not found in response: " + expr); + if (null != locator && !locator.isEmpty()) { + Element exception = (Element) nodeList.item(0); + String locatorValue = exception.getAttribute("locator").toLowerCase(); + Assert.assertTrue(locatorValue.contains(locator.toLowerCase()), + String.format("Expected locator attribute to contain '%s']", locator)); + } + } - /** - * Asserts that the specified spatial reference occurs in the given XML - * entity. In general the reference is conveyed by the srsName attribute - * that may appear on any geometry element or gml:Envelope. All occurrences - * must match. - * - * @param entity - * A Document representing an XML entity such as a GML document - * or a WFS GetFeature response. - * @param crsId - * A CRS identifier (an absolute URI value). - */ - public static void assertSpatialReference(Document entity, String crsId) { - NodeList srsNameNodes = null; - try { - srsNameNodes = XMLUtils.evaluateXPath(entity, "//*/@srsName", null); - } catch (XPathExpressionException e) { - throw new AssertionError(e.getMessage()); - } - for (int i = 0; i < srsNameNodes.getLength(); i++) { - Attr attr = (Attr) srsNameNodes.item(i); - Assert.assertEquals(attr.getValue(), crsId, String.format("Unexpected @srsName value on element %s", - new QName(attr.getNamespaceURI(), attr.getLocalName()))); - } - } + /** + * Asserts that the specified spatial reference occurs in the given XML entity. In + * general the reference is conveyed by the srsName attribute that may appear on any + * geometry element or gml:Envelope. All occurrences must match. + * @param entity A Document representing an XML entity such as a GML document or a WFS + * GetFeature response. + * @param crsId A CRS identifier (an absolute URI value). + */ + public static void assertSpatialReference(Document entity, String crsId) { + NodeList srsNameNodes = null; + try { + srsNameNodes = XMLUtils.evaluateXPath(entity, "//*/@srsName", null); + } + catch (XPathExpressionException e) { + throw new AssertionError(e.getMessage()); + } + for (int i = 0; i < srsNameNodes.getLength(); i++) { + Attr attr = (Attr) srsNameNodes.item(i); + Assert.assertEquals(attr.getValue(), crsId, String.format("Unexpected @srsName value on element %s", + new QName(attr.getNamespaceURI(), attr.getLocalName()))); + } + } + + /** + * Asserts that the given response entity contains at least one feature instance of + * the specified type. + * @param entity A Document representing a GetFeature response entity + * (wfs:FeatureCollection). + * @param featureType A QName that identifies the expected feature type; if null, any + * type is acceptable. + */ + public static void assertResultSetNotEmpty(Document entity, QName featureType) { + NodeList features; + if (null != featureType) { + features = entity.getElementsByTagNameNS(featureType.getNamespaceURI(), featureType.getLocalPart()); + } + else { + features = entity.getElementsByTagNameNS(WFS2.NS_URI, "member"); + } + Assert.assertTrue(features.getLength() > 0, + String.format("Expected one or more feature instances in response (type: %s).", + (null != featureType) ? featureType : "any")); + } - /** - * Asserts that the given response entity contains at least one feature - * instance of the specified type. - * - * @param entity - * A Document representing a GetFeature response entity - * (wfs:FeatureCollection). - * @param featureType - * A QName that identifies the expected feature type; if null, - * any type is acceptable. - */ - public static void assertResultSetNotEmpty(Document entity, QName featureType) { - NodeList features; - if (null != featureType) { - features = entity.getElementsByTagNameNS(featureType.getNamespaceURI(), featureType.getLocalPart()); - } else { - features = entity.getElementsByTagNameNS(WFS2.NS_URI, "member"); - } - Assert.assertTrue(features.getLength() > 0, - String.format("Expected one or more feature instances in response (type: %s).", - (null != featureType) ? featureType : "any")); - } } diff --git a/src/main/java/org/opengis/cite/iso19142/ErrorMessage.java b/src/main/java/org/opengis/cite/iso19142/ErrorMessage.java index 714e6cdb..b8a105d9 100644 --- a/src/main/java/org/opengis/cite/iso19142/ErrorMessage.java +++ b/src/main/java/org/opengis/cite/iso19142/ErrorMessage.java @@ -4,45 +4,39 @@ import java.util.ResourceBundle; /** - * Utility class for retrieving and formatting localized error messages that - * describe failed assertions. + * Utility class for retrieving and formatting localized error messages that describe + * failed assertions. */ public class ErrorMessage { - private static final String BASE_NAME = "org.opengis.cite.iso19142.MessageBundle"; - private static ResourceBundle msgResources = ResourceBundle - .getBundle(BASE_NAME); + private static final String BASE_NAME = "org.opengis.cite.iso19142.MessageBundle"; - /** - * Produces a formatted error message using the supplied substitution - * arguments and the current locale. The arguments should reflect the order - * of the placeholders in the message template. - * - * @param msgKey - * The key identifying the message template; it should be a - * member of {@code ErrorMessageKeys}. - * @param args - * An array of arguments to be formatted and substituted in the - * content of the message. - * @return A String containing the message content. If no message is found - * for the given key, a {@link java.util.MissingResourceException} - * is thrown. - */ - public static String format(String msgKey, Object... args) { - return MessageFormat.format(msgResources.getString(msgKey), args); - } + private static ResourceBundle msgResources = ResourceBundle.getBundle(BASE_NAME); + + /** + * Produces a formatted error message using the supplied substitution arguments and + * the current locale. The arguments should reflect the order of the placeholders in + * the message template. + * @param msgKey The key identifying the message template; it should be a member of + * {@code ErrorMessageKeys}. + * @param args An array of arguments to be formatted and substituted in the content of + * the message. + * @return A String containing the message content. If no message is found for the + * given key, a {@link java.util.MissingResourceException} is thrown. + */ + public static String format(String msgKey, Object... args) { + return MessageFormat.format(msgResources.getString(msgKey), args); + } + + /** + * Retrieves a simple message according to the current locale. + * @param msgKey The key identifying the message; it should be a member of + * {@code ErrorMessageKeys}. + * @return A String containing the message content. If no message is found for the + * given key, a {@link java.util.MissingResourceException} is thrown. + */ + public static String get(String msgKey) { + return msgResources.getString(msgKey); + } - /** - * Retrieves a simple message according to the current locale. - * - * @param msgKey - * The key identifying the message; it should be a member of - * {@code ErrorMessageKeys}. - * @return A String containing the message content. If no message is found - * for the given key, a {@link java.util.MissingResourceException} - * is thrown. - */ - public static String get(String msgKey) { - return msgResources.getString(msgKey); - } } diff --git a/src/main/java/org/opengis/cite/iso19142/ErrorMessageKeys.java b/src/main/java/org/opengis/cite/iso19142/ErrorMessageKeys.java index d1a889b2..a5200f22 100644 --- a/src/main/java/org/opengis/cite/iso19142/ErrorMessageKeys.java +++ b/src/main/java/org/opengis/cite/iso19142/ErrorMessageKeys.java @@ -1,34 +1,57 @@ package org.opengis.cite.iso19142; /** - * Defines keys used to access localized messages for assertion errors. The - * messages are stored in Properties files that are encoded in ISO-8859-1 - * (Latin-1). For some languages the {@code native2ascii} tool must be used to - * process the files and produce escaped Unicode characters. + * Defines keys used to access localized messages for assertion errors. The messages are + * stored in Properties files that are encoded in ISO-8859-1 (Latin-1). For some languages + * the {@code native2ascii} tool must be used to process the files and produce escaped + * Unicode characters. */ public class ErrorMessageKeys { - public static final String NOT_SCHEMA_VALID = "NotSchemaValid"; - public static final String UNEXPECTED_STATUS = "UnexpectedStatus"; - public static final String MISSING_XML_ENTITY = "MissingXMLEntity"; - public static final String MISSING_INFOSET_ITEM = "MissingInfosetItem"; - public static final String XML_ERROR = "XMLError"; - public static final String XPATH_ERROR = "XPathError"; - public static final String XPATH_RESULT = "XPathResult"; - public static final String NAMESPACE_NAME = "NamespaceName"; - public static final String LOCAL_NAME = "LocalName"; - public static final String NO_MEMBERS = "NoMembers"; - public static final String FEATURE_AVAILABILITY = "FeatureAvailability"; - public static final String PREDICATE_NOT_SATISFIED = "PredicateNotSatisfied"; - public static final String DATA_UNAVAILABLE = "DataNotAvailable"; - public static final String UNEXPECTED_ID = "UnexpectedIdentifier"; - public static final String FID_NOT_FOUND = "FidNotFound"; - public static final String CAPABILITY_NOT_TESTED = "CapabilityNotTested"; - public static final String NOT_IMPLEMENTED = "CapabilityNotImplemented"; - public static final String QRY_LANG_NOT_SUPPORTED = "QueryLangNotSupported"; - public static final String NUM_RETURNED = "UnexpectedNumReturned"; - public static final String NUM_MATCHED = "UnexpectedNumMatched"; - public static final String VERSION_STATE = "UnexpectedVersionState"; - public static final String UNEXPECTED_VALUE = "UnexpectedValue"; - public static final String TM_EXTENT_LEN = "TemporalExtentLength"; + public static final String NOT_SCHEMA_VALID = "NotSchemaValid"; + + public static final String UNEXPECTED_STATUS = "UnexpectedStatus"; + + public static final String MISSING_XML_ENTITY = "MissingXMLEntity"; + + public static final String MISSING_INFOSET_ITEM = "MissingInfosetItem"; + + public static final String XML_ERROR = "XMLError"; + + public static final String XPATH_ERROR = "XPathError"; + + public static final String XPATH_RESULT = "XPathResult"; + + public static final String NAMESPACE_NAME = "NamespaceName"; + + public static final String LOCAL_NAME = "LocalName"; + + public static final String NO_MEMBERS = "NoMembers"; + + public static final String FEATURE_AVAILABILITY = "FeatureAvailability"; + + public static final String PREDICATE_NOT_SATISFIED = "PredicateNotSatisfied"; + + public static final String DATA_UNAVAILABLE = "DataNotAvailable"; + + public static final String UNEXPECTED_ID = "UnexpectedIdentifier"; + + public static final String FID_NOT_FOUND = "FidNotFound"; + + public static final String CAPABILITY_NOT_TESTED = "CapabilityNotTested"; + + public static final String NOT_IMPLEMENTED = "CapabilityNotImplemented"; + + public static final String QRY_LANG_NOT_SUPPORTED = "QueryLangNotSupported"; + + public static final String NUM_RETURNED = "UnexpectedNumReturned"; + + public static final String NUM_MATCHED = "UnexpectedNumMatched"; + + public static final String VERSION_STATE = "UnexpectedVersionState"; + + public static final String UNEXPECTED_VALUE = "UnexpectedValue"; + + public static final String TM_EXTENT_LEN = "TemporalExtentLength"; + } diff --git a/src/main/java/org/opengis/cite/iso19142/ExceptionCode.java b/src/main/java/org/opengis/cite/iso19142/ExceptionCode.java index db94b54b..3ede74ed 100644 --- a/src/main/java/org/opengis/cite/iso19142/ExceptionCode.java +++ b/src/main/java/org/opengis/cite/iso19142/ExceptionCode.java @@ -1,9 +1,9 @@ package org.opengis.cite.iso19142; /** - * An enumerated type defining OGC exception codes that appear in an exception - * report. Each error code has an associated HTTP status code. - * + * An enumerated type defining OGC exception codes that appear in an exception report. + * Each error code has an associated HTTP status code. + * * @see "OGC 09-025r2, Table D.2" */ public enum ExceptionCode { @@ -12,6 +12,7 @@ public enum ExceptionCode { INVALID_PARAM_VALUE("InvalidParameterValue", 400); private final String ogcCode; + private final int httpStatus; private ExceptionCode(String ogcCode, int httpStatus) { diff --git a/src/main/java/org/opengis/cite/iso19142/FES2.java b/src/main/java/org/opengis/cite/iso19142/FES2.java index 1dd687ab..9187a8ea 100644 --- a/src/main/java/org/opengis/cite/iso19142/FES2.java +++ b/src/main/java/org/opengis/cite/iso19142/FES2.java @@ -1,36 +1,45 @@ package org.opengis.cite.iso19142; /** - * Contains various constants pertaining to standard filter expressions - * specified in ISO 19143:2010. - * + * Contains various constants pertaining to standard filter expressions specified in ISO + * 19143:2010. + * * @see "ISO 19143:2010, Geographic information -- Filter encoding" */ public class FES2 { - private FES2() { - } - - /** Namespace name. */ - public static final String NS = "http://www.opengis.net/fes/2.0"; - /** PropertyIsEqualTo operator. */ - public static final String EQUAL = "PropertyIsEqualTo"; - /** PropertyIsNotEqualTo operator. */ - public static final String NOT_EQUAL = "PropertyIsNotEqualTo"; - /** PropertyIsLessThan operator. */ - public static final String LESS_THAN = "PropertyIsLessThan"; - /** PropertyIsGreaterThan operator. */ - public static final String GREATER_THAN = "PropertyIsGreaterThan"; - /** PropertyIsLessThanOrEqualTo operator. */ - public static final String LESS_THAN_OR_EQUAL = "PropertyIsLessThanOrEqualTo"; - /** PropertyIsGreaterThanOrEqualTo operator. */ - public static final String GREATER_THAN_OR_EQUAL = "PropertyIsGreaterThanOrEqualTo"; - /** ResourceId operator. */ - public static final String RESOURCE_ID = "ResourceId"; - - /** VersionAction is used to filter the version chain in ResourceId. */ - public enum VersionAction { - FIRST, LAST, PREVIOUS, NEXT, ALL - } + private FES2() { + } + + /** Namespace name. */ + public static final String NS = "http://www.opengis.net/fes/2.0"; + + /** PropertyIsEqualTo operator. */ + public static final String EQUAL = "PropertyIsEqualTo"; + + /** PropertyIsNotEqualTo operator. */ + public static final String NOT_EQUAL = "PropertyIsNotEqualTo"; + + /** PropertyIsLessThan operator. */ + public static final String LESS_THAN = "PropertyIsLessThan"; + + /** PropertyIsGreaterThan operator. */ + public static final String GREATER_THAN = "PropertyIsGreaterThan"; + + /** PropertyIsLessThanOrEqualTo operator. */ + public static final String LESS_THAN_OR_EQUAL = "PropertyIsLessThanOrEqualTo"; + + /** PropertyIsGreaterThanOrEqualTo operator. */ + public static final String GREATER_THAN_OR_EQUAL = "PropertyIsGreaterThanOrEqualTo"; + + /** ResourceId operator. */ + public static final String RESOURCE_ID = "ResourceId"; + + /** VersionAction is used to filter the version chain in ResourceId. */ + public enum VersionAction { + + FIRST, LAST, PREVIOUS, NEXT, ALL + + } } diff --git a/src/main/java/org/opengis/cite/iso19142/FeatureTypeInfo.java b/src/main/java/org/opengis/cite/iso19142/FeatureTypeInfo.java index cc6ccf97..3f364232 100644 --- a/src/main/java/org/opengis/cite/iso19142/FeatureTypeInfo.java +++ b/src/main/java/org/opengis/cite/iso19142/FeatureTypeInfo.java @@ -22,223 +22,210 @@ import org.testng.SkipException; /** - * Provides information about a feature type managed by a WFS. Much of the - * information is gleaned from the content of the service description - * (wfs:WFS_Capabilities). + * Provides information about a feature type managed by a WFS. Much of the information is + * gleaned from the content of the service description (wfs:WFS_Capabilities). */ public class FeatureTypeInfo { - private QName typeName; - private Envelope spatialExtent; - private boolean instantiated; - private List supportedCRSList; - private File sampleData; - private Period temporalExtent; - - public FeatureTypeInfo() { - this.supportedCRSList = new ArrayList(); - } - - /** - * Returns of list of supported CRS identifiers. The first entry denotes the - * default CRS. - * - * @return A list of CRS identifiers (absolute URI values). - */ - public List getSupportedCRSIdentifiers() { - return supportedCRSList; - } - - /** - * Adds the given sequence of identifiers to the list of supported - * coordinate reference systems. - * - * @param crsIdentifiers - * A sequence of CRS identifiers (absolute URI values that comply - * with OGC 09-048r3, 4.4). - */ - public void addCRSIdentifiers(String... crsIdentifiers) { - this.supportedCRSList.addAll(Arrays.asList(crsIdentifiers)); - } - - /** - * Get the qualified name of the feature type. - * - * @return A QName object. - */ - public QName getTypeName() { - return typeName; - } - - /** - * Sets the feature type name. - * - * @param typeName - * A QName object. - */ - public void setTypeName(QName typeName) { - this.typeName = typeName; - } - - /** - * Indicates whether or not there are any instances of this feature type - * available in the data store. - * - * @return {@code true} if at least one feature instance exists; - * {@code false} otherwise. - */ - public boolean isInstantiated() { - return instantiated; - } - - /** - * Sets the availability of this feature type. - * - * @param available - * A boolean value indicating if any instances of this type are - * available in the data store. - */ - public void setInstantiated(boolean available) { - this.instantiated = available; - } - - /** - * Gets the identifier of the default CRS for this feature type. - * - * @return A String representing a CRS reference (an absolute URI value). - */ - public String getDefaultCRS() { - return this.supportedCRSList.get(0); - } - - /** - * Gets the geographic extent for the instances of this feature type. The - * spatial extent is typically set as follows: - *
      - *
    1. using the first ows:WGS84BoundingBox element appearing in the WFS - * capabilities document;
    2. - *
    3. from the valid area of the default CRS.
    4. - *
    - * - * @return An Envelope defining a bounding box. - */ - public Envelope getSpatialExtent() { - if (null == spatialExtent) { - this.spatialExtent = getValidAreaOfCRS(getDefaultCRS()); - } - return spatialExtent; - } - - /** - * Sets the geographic extent of the feature instances. The CRS of the given - * envelope will be changed to the default CRS if necessary. - * - * @param geoExtent - * An envelope defining a bounding box in some CRS. - */ - public void setSpatialExtent(Envelope geoExtent) { - CoordinateReferenceSystem defaultCRS = null; - try { - // http-based identifier is not recognized by Geotk v3 - String crsId = GeodesyUtils.getAbbreviatedCRSIdentifier(getDefaultCRS()); - defaultCRS = CRS.forCode(crsId); - } catch (FactoryException fex) { - throw new RuntimeException("Default CRS not recognized. " + fex.getMessage()); - }catch (IllegalArgumentException iae) { - throw new SkipException("Default CRS is not valid. " + iae.getMessage()); - } - if (!geoExtent.getCoordinateReferenceSystem().equals(defaultCRS)) { - Envelope bbox = null; - try { - bbox = Envelopes.transform(geoExtent, defaultCRS); - } catch (TransformException e) { - throw new IllegalArgumentException("Failed to transform envelope coordinates to CRS " + getDefaultCRS(), - e); - } - this.spatialExtent = new ImmutableEnvelope(bbox); - } else { - this.spatialExtent = geoExtent; - } - } - - /** - * Returns a File containing sample data. This is an XML entity where the - * document element is wfs:FeatureCollection. - * - * @return A File for reading the GML data, or {@code null} if no data are - * available. - */ - public File getSampleData() { - return sampleData; - } - - /** - * Sets the location of a sample data file containing instances of this - * feature type. - * - * @param sampleData - * A File object. - */ - public void setSampleData(File sampleData) { - this.sampleData = sampleData; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder("FeatureTypeInfo {"); - sb.append("\n typeName: '").append(typeName); - sb.append("',\n supportedCRS: '").append(supportedCRSList); - sb.append("',\n instantiated: ").append(instantiated); -// sb.append(",\n spatial extent: '").append(Envelopes.toPolygonWKT(getSpatialExtent())); - if (temporalExtent != null) { - sb.append("',\n temporal extent: '"); - sb.append(TemporalUtils.temporalGeometricPrimitiveToString(temporalExtent)); - } - if (sampleData != null && sampleData.exists()) { - sb.append("',\n data: '").append(sampleData.toString()); - } - sb.append("'\n}"); - return sb.toString(); - } - - /** - * Gets the temporal extent for the instances of this feature type. - * - * @return A period representing a time interval, or null if the feature has - * no temporal properties. - */ - public Period getTemporalExtent() { - return this.temporalExtent; - } - - /** - * Sets the temporal extent of the feature instances. - * - * @param period - * A period representing a time interval. - */ - public void setTemporalExtent(Period period) { - this.temporalExtent = period; - } - - /** - * Creates an envelope representing the valid area of use for the specified - * coordinate reference system (CRS). - * - * @param crsRef - * An absolute URI ('http' or 'urn' scheme) that identifies a CRS - * in accord with OGC 09-048r3. - * - * @return An ImmutableEnvelope defining the domain of validity for the CRS, - * or {@code null} if no CRS definition can be found. - */ - ImmutableEnvelope getValidAreaOfCRS(String crsRef) { - ImmutableEnvelope envelope = null; - try { - envelope = GeodesyUtils.getDomainOfValidity(crsRef); - } catch (FactoryException e) { - TestSuiteLogger.log(Level.WARNING, "Cannot determine domain of validity for CRS " + crsRef, e); - } - return envelope; - } + + private QName typeName; + + private Envelope spatialExtent; + + private boolean instantiated; + + private List supportedCRSList; + + private File sampleData; + + private Period temporalExtent; + + public FeatureTypeInfo() { + this.supportedCRSList = new ArrayList(); + } + + /** + * Returns of list of supported CRS identifiers. The first entry denotes the default + * CRS. + * @return A list of CRS identifiers (absolute URI values). + */ + public List getSupportedCRSIdentifiers() { + return supportedCRSList; + } + + /** + * Adds the given sequence of identifiers to the list of supported coordinate + * reference systems. + * @param crsIdentifiers A sequence of CRS identifiers (absolute URI values that + * comply with OGC 09-048r3, 4.4). + */ + public void addCRSIdentifiers(String... crsIdentifiers) { + this.supportedCRSList.addAll(Arrays.asList(crsIdentifiers)); + } + + /** + * Get the qualified name of the feature type. + * @return A QName object. + */ + public QName getTypeName() { + return typeName; + } + + /** + * Sets the feature type name. + * @param typeName A QName object. + */ + public void setTypeName(QName typeName) { + this.typeName = typeName; + } + + /** + * Indicates whether or not there are any instances of this feature type available in + * the data store. + * @return {@code true} if at least one feature instance exists; {@code false} + * otherwise. + */ + public boolean isInstantiated() { + return instantiated; + } + + /** + * Sets the availability of this feature type. + * @param available A boolean value indicating if any instances of this type are + * available in the data store. + */ + public void setInstantiated(boolean available) { + this.instantiated = available; + } + + /** + * Gets the identifier of the default CRS for this feature type. + * @return A String representing a CRS reference (an absolute URI value). + */ + public String getDefaultCRS() { + return this.supportedCRSList.get(0); + } + + /** + * Gets the geographic extent for the instances of this feature type. The spatial + * extent is typically set as follows: + *
      + *
    1. using the first ows:WGS84BoundingBox element appearing in the WFS capabilities + * document;
    2. + *
    3. from the valid area of the default CRS.
    4. + *
    + * @return An Envelope defining a bounding box. + */ + public Envelope getSpatialExtent() { + if (null == spatialExtent) { + this.spatialExtent = getValidAreaOfCRS(getDefaultCRS()); + } + return spatialExtent; + } + + /** + * Sets the geographic extent of the feature instances. The CRS of the given envelope + * will be changed to the default CRS if necessary. + * @param geoExtent An envelope defining a bounding box in some CRS. + */ + public void setSpatialExtent(Envelope geoExtent) { + CoordinateReferenceSystem defaultCRS = null; + try { + // http-based identifier is not recognized by Geotk v3 + String crsId = GeodesyUtils.getAbbreviatedCRSIdentifier(getDefaultCRS()); + defaultCRS = CRS.forCode(crsId); + } + catch (FactoryException fex) { + throw new RuntimeException("Default CRS not recognized. " + fex.getMessage()); + } + catch (IllegalArgumentException iae) { + throw new SkipException("Default CRS is not valid. " + iae.getMessage()); + } + if (!geoExtent.getCoordinateReferenceSystem().equals(defaultCRS)) { + Envelope bbox = null; + try { + bbox = Envelopes.transform(geoExtent, defaultCRS); + } + catch (TransformException e) { + throw new IllegalArgumentException("Failed to transform envelope coordinates to CRS " + getDefaultCRS(), + e); + } + this.spatialExtent = new ImmutableEnvelope(bbox); + } + else { + this.spatialExtent = geoExtent; + } + } + + /** + * Returns a File containing sample data. This is an XML entity where the document + * element is wfs:FeatureCollection. + * @return A File for reading the GML data, or {@code null} if no data are available. + */ + public File getSampleData() { + return sampleData; + } + + /** + * Sets the location of a sample data file containing instances of this feature type. + * @param sampleData A File object. + */ + public void setSampleData(File sampleData) { + this.sampleData = sampleData; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("FeatureTypeInfo {"); + sb.append("\n typeName: '").append(typeName); + sb.append("',\n supportedCRS: '").append(supportedCRSList); + sb.append("',\n instantiated: ").append(instantiated); + // sb.append(",\n spatial extent: + // '").append(Envelopes.toPolygonWKT(getSpatialExtent())); + if (temporalExtent != null) { + sb.append("',\n temporal extent: '"); + sb.append(TemporalUtils.temporalGeometricPrimitiveToString(temporalExtent)); + } + if (sampleData != null && sampleData.exists()) { + sb.append("',\n data: '").append(sampleData.toString()); + } + sb.append("'\n}"); + return sb.toString(); + } + + /** + * Gets the temporal extent for the instances of this feature type. + * @return A period representing a time interval, or null if the feature has no + * temporal properties. + */ + public Period getTemporalExtent() { + return this.temporalExtent; + } + + /** + * Sets the temporal extent of the feature instances. + * @param period A period representing a time interval. + */ + public void setTemporalExtent(Period period) { + this.temporalExtent = period; + } + + /** + * Creates an envelope representing the valid area of use for the specified coordinate + * reference system (CRS). + * @param crsRef An absolute URI ('http' or 'urn' scheme) that identifies a CRS in + * accord with OGC 09-048r3. + * @return An ImmutableEnvelope defining the domain of validity for the CRS, or + * {@code null} if no CRS definition can be found. + */ + ImmutableEnvelope getValidAreaOfCRS(String crsRef) { + ImmutableEnvelope envelope = null; + try { + envelope = GeodesyUtils.getDomainOfValidity(crsRef); + } + catch (FactoryException e) { + TestSuiteLogger.log(Level.WARNING, "Cannot determine domain of validity for CRS " + crsRef, e); + } + return envelope; + } } diff --git a/src/main/java/org/opengis/cite/iso19142/Namespaces.java b/src/main/java/org/opengis/cite/iso19142/Namespaces.java index b5bc7e7b..53fa3961 100644 --- a/src/main/java/org/opengis/cite/iso19142/Namespaces.java +++ b/src/main/java/org/opengis/cite/iso19142/Namespaces.java @@ -4,34 +4,42 @@ /** * XML namespace names. - * + * * @see Namespaces in XML 1.0 */ public class Namespaces { - private Namespaces() { - } - - /** Legacy SOAP 1.1 message envelopes. */ - public static final String SOAP11 = "http://schemas.xmlsoap.org/soap/envelope/"; - /** SOAP 1.2 message envelopes. */ - public static final String SOAP_ENV = "http://www.w3.org/2003/05/soap-envelope"; - /** W3C XLink */ - public static final String XLINK = "http://www.w3.org/1999/xlink"; - /** XML Schema instance namespace */ - public static final String XSI = "http://www.w3.org/2001/XMLSchema-instance"; - /** OGC 06-121r3 (OWS 1.1) */ - public static final String OWS = "http://www.opengis.net/ows/1.1"; - /** ISO 19136:2007 (GML 3.2) */ - public static final String GML = "http://www.opengis.net/gml/3.2"; - /** ISO 19142:2010 (WFS 2.0) */ - public static final String WFS = "http://www.opengis.net/wfs/2.0"; - /** ISO 19143:2010 (FES 2.0) */ - public static final String FES = "http://www.opengis.net/fes/2.0"; - /** W3C XML Schema namespace */ - public static final URI XSD = URI - .create("http://www.w3.org/2001/XMLSchema"); - /** Schematron (ISO 19757-3) namespace */ - public static final URI SCH = URI - .create("http://purl.oclc.org/dsdl/schematron"); + private Namespaces() { + } + + /** Legacy SOAP 1.1 message envelopes. */ + public static final String SOAP11 = "http://schemas.xmlsoap.org/soap/envelope/"; + + /** SOAP 1.2 message envelopes. */ + public static final String SOAP_ENV = "http://www.w3.org/2003/05/soap-envelope"; + + /** W3C XLink */ + public static final String XLINK = "http://www.w3.org/1999/xlink"; + + /** XML Schema instance namespace */ + public static final String XSI = "http://www.w3.org/2001/XMLSchema-instance"; + + /** OGC 06-121r3 (OWS 1.1) */ + public static final String OWS = "http://www.opengis.net/ows/1.1"; + + /** ISO 19136:2007 (GML 3.2) */ + public static final String GML = "http://www.opengis.net/gml/3.2"; + + /** ISO 19142:2010 (WFS 2.0) */ + public static final String WFS = "http://www.opengis.net/wfs/2.0"; + + /** ISO 19143:2010 (FES 2.0) */ + public static final String FES = "http://www.opengis.net/fes/2.0"; + + /** W3C XML Schema namespace */ + public static final URI XSD = URI.create("http://www.w3.org/2001/XMLSchema"); + + /** Schematron (ISO 19757-3) namespace */ + public static final URI SCH = URI.create("http://purl.oclc.org/dsdl/schematron"); + } diff --git a/src/main/java/org/opengis/cite/iso19142/ProtocolBinding.java b/src/main/java/org/opengis/cite/iso19142/ProtocolBinding.java index 1f52c06f..4d9d738d 100644 --- a/src/main/java/org/opengis/cite/iso19142/ProtocolBinding.java +++ b/src/main/java/org/opengis/cite/iso19142/ProtocolBinding.java @@ -1,42 +1,42 @@ package org.opengis.cite.iso19142; /** - * An enumerated type that indicates how a request message is bound to an - * application protocol. In effect, a binding prescribes how the message content - * is mapped into a concrete exchange format. The WFS v2 specification defines - * three conformance classes pertaining to protocol bindings; a conforming - * service must implement at least one of these and advertise all supported - * bindings in the service capabilities document. - * + * An enumerated type that indicates how a request message is bound to an application + * protocol. In effect, a binding prescribes how the message content is mapped into a + * concrete exchange format. The WFS v2 specification defines three conformance classes + * pertaining to protocol bindings; a conforming service must implement at least one of + * these and advertise all supported bindings in the service capabilities document. + * *
      *
    • HTTP GET (constraint name: 'KVPEncoding')
    • *
    • HTTP POST (constraint name : 'XMLEncoding')
    • *
    • SOAP (constraint name: 'SOAPEncoding')
    • *
    - * + * * @see "ISO 19142:2010, Geographic information -- Web Feature Service" * @see RFC 2616 - * @see SOAP HTTP - * Binding - * + * @see SOAP HTTP Binding + * */ public enum ProtocolBinding { - /** HTTP GET method */ - GET(WFS2.KVP_ENC), - /** HTTP POST method */ - POST(WFS2.XML_ENC), - /** SOAP HTTP binding */ - SOAP(WFS2.SOAP_ENC), - /** Any supported binding */ - ANY(""); - private final String constraintName; + /** HTTP GET method */ + GET(WFS2.KVP_ENC), + /** HTTP POST method */ + POST(WFS2.XML_ENC), + /** SOAP HTTP binding */ + SOAP(WFS2.SOAP_ENC), + /** Any supported binding */ + ANY(""); - private ProtocolBinding(String constraintName) { - this.constraintName = constraintName; - } + private final String constraintName; + + private ProtocolBinding(String constraintName) { + this.constraintName = constraintName; + } + + public String getConstraintName() { + return constraintName; + } - public String getConstraintName() { - return constraintName; - } } diff --git a/src/main/java/org/opengis/cite/iso19142/SuiteAttribute.java b/src/main/java/org/opengis/cite/iso19142/SuiteAttribute.java index 801728f9..e50b907c 100644 --- a/src/main/java/org/opengis/cite/iso19142/SuiteAttribute.java +++ b/src/main/java/org/opengis/cite/iso19142/SuiteAttribute.java @@ -9,25 +9,24 @@ import org.w3c.dom.Document; /** - * An enumerated type defining ISuite attributes that may be set to constitute a - * shared test fixture. + * An enumerated type defining ISuite attributes that may be set to constitute a shared + * test fixture. */ @SuppressWarnings("rawtypes") public enum SuiteAttribute { /** - * A DOM Document describing the WFS under test. This is typically a WFS - * capabilities document. + * A DOM Document describing the WFS under test. This is typically a WFS capabilities + * document. */ TEST_SUBJECT("testSubject", Document.class), /** - * An immutable Schema object representing the complete WFS 2.0 schema - * (wfs.xsd). + * An immutable Schema object representing the complete WFS 2.0 schema (wfs.xsd). */ WFS_SCHEMA("wfsSchema", Schema.class), /** - * A {@literal Map} containing one or more - * entries providing information about managed feature types. + * A {@literal Map} containing one or more entries + * providing information about managed feature types. */ FEATURE_INFO("featureInfo", Map.class), /** @@ -35,15 +34,16 @@ public enum SuiteAttribute { */ SAMPLER("sampler", DataSampler.class), /** - * A {@literal List} of test suite preconditions that were not - * satisfied. + * A {@literal List} of test suite preconditions that were not satisfied. */ FAILED_PRECONDITIONS("failedPreconditions", List.class), /** * The highest specification version supported by the IUT. */ WFS_VERSION("wfsVersion", String.class); + private final Class attrType; + private final String attrName; private SuiteAttribute(String attrName, Class attrType) { @@ -65,4 +65,5 @@ public String toString() { sb.append('(').append(attrType.getName()).append(')'); return sb.toString(); } + } diff --git a/src/main/java/org/opengis/cite/iso19142/SuiteFixtureListener.java b/src/main/java/org/opengis/cite/iso19142/SuiteFixtureListener.java index 134a464f..922c80e4 100644 --- a/src/main/java/org/opengis/cite/iso19142/SuiteFixtureListener.java +++ b/src/main/java/org/opengis/cite/iso19142/SuiteFixtureListener.java @@ -26,117 +26,114 @@ import org.xml.sax.SAXException; /** - * A listener that performs various tasks before and after a test suite is run, - * usually concerned with maintaining a shared test suite fixture. Since this - * listener is loaded using the ServiceLoader mechanism, its methods will be - * called before those of other suite listeners listed in the test suite - * definition and before any annotated configuration methods. - * - * Attributes set on an ISuite instance are not inherited by constituent test - * group contexts (ITestContext). However, suite attributes are still accessible - * from lower contexts. - * + * A listener that performs various tasks before and after a test suite is run, usually + * concerned with maintaining a shared test suite fixture. Since this listener is loaded + * using the ServiceLoader mechanism, its methods will be called before those of other + * suite listeners listed in the test suite definition and before any annotated + * configuration methods. + * + * Attributes set on an ISuite instance are not inherited by constituent test group + * contexts (ITestContext). However, suite attributes are still accessible from lower + * contexts. + * * @see org.testng.ISuite ISuite interface */ public class SuiteFixtureListener implements ISuiteListener { - private final static Logger LOGR = Logger.getLogger(SuiteFixtureListener.class.getName()); + private final static Logger LOGR = Logger.getLogger(SuiteFixtureListener.class.getName()); - @Override - public void onStart(ISuite suite) { - Schema wfsSchema = ValidationUtils.createWFSSchema(); - if (null != wfsSchema) { - suite.setAttribute(SuiteAttribute.WFS_SCHEMA.getName(), wfsSchema); - } - processWfsParameter(suite); - setAppSchemaParameter(suite); - LOGR.log(Level.CONFIG, "Initial test run parameters:\n{0}", suite.getXmlSuite().getAllParameters()); - } + @Override + public void onStart(ISuite suite) { + Schema wfsSchema = ValidationUtils.createWFSSchema(); + if (null != wfsSchema) { + suite.setAttribute(SuiteAttribute.WFS_SCHEMA.getName(), wfsSchema); + } + processWfsParameter(suite); + setAppSchemaParameter(suite); + LOGR.log(Level.CONFIG, "Initial test run parameters:\n{0}", suite.getXmlSuite().getAllParameters()); + } - @Override - public void onFinish(ISuite suite) { - } + @Override + public void onFinish(ISuite suite) { + } - /** - * Processes the "wfs" test suite parameter that specifies a URI reference - * for the service description (capabilities document). The URI is - * dereferenced and the entity is parsed; the resulting Document object is - * set as the value of the {@link SuiteAttribute#TEST_SUBJECT testSubject} - * suite attribute. - * - * The {@link SuiteAttribute#FEATURE_INFO featureInfo} suite attribute is - * also set; its value is a {@literal Map} object - * that provides summary information about available feature types, mostly - * gleaned from the service description. - * - * @param suite - * An ISuite object representing a TestNG test suite. - */ - void processWfsParameter(ISuite suite) { - Map params = suite.getXmlSuite().getParameters(); - // metadata may have been submitted as entity body in POST request - String iutRef = params.get(TestRunArg.IUT.toString()); - String wfsRef = (null != iutRef) ? iutRef : params.get(TestRunArg.WFS.toString()); - if ((null == wfsRef) || wfsRef.isEmpty()) { - throw new IllegalArgumentException("Required parameter not found"); - } - URI wfsURI = URI.create(wfsRef); - Document doc = null; - try { - doc = URIUtils.resolveURIAsDocument(wfsURI); - Element docElem = doc.getDocumentElement(); - QName qName = new QName(docElem.getNamespaceURI(), docElem.getLocalName()); - if (!qName.equals(WFS2.QNAME_WFS_CAPABILITIES)) { - throw new RuntimeException("Not a WFS2 capabilities document: " + qName); - } - suite.setAttribute(SuiteAttribute.WFS_VERSION.getName(), docElem.getAttribute("version")); - } catch (ConnectException | FileNotFoundException | UnknownHostException | MalformedURLException e) { - throw new RuntimeException("Failed to connect to resource located at " + wfsURI, e); - } catch (SAXException | IOException ex) { - // push exception up through TestNG ISuiteListener interface - throw new RuntimeException("Failed to parse resource located at " + wfsURI, ex); - } - if (null != doc) { - suite.setAttribute(SuiteAttribute.TEST_SUBJECT.getName(), doc); - Map featureInfo = ServiceMetadataUtils.extractFeatureTypeInfo(doc); - suite.setAttribute(SuiteAttribute.FEATURE_INFO.getName(), featureInfo); - LOGR.log(Level.FINER, "Parsed resource from {0}\n{1}", - new Object[] { wfsURI, XMLUtils.writeNodeToString(doc) }); - } - } + /** + * Processes the "wfs" test suite parameter that specifies a URI reference for the + * service description (capabilities document). The URI is dereferenced and the entity + * is parsed; the resulting Document object is set as the value of the + * {@link SuiteAttribute#TEST_SUBJECT testSubject} suite attribute. + * + * The {@link SuiteAttribute#FEATURE_INFO featureInfo} suite attribute is also set; + * its value is a {@literal Map} object that provides summary + * information about available feature types, mostly gleaned from the service + * description. + * @param suite An ISuite object representing a TestNG test suite. + */ + void processWfsParameter(ISuite suite) { + Map params = suite.getXmlSuite().getParameters(); + // metadata may have been submitted as entity body in POST request + String iutRef = params.get(TestRunArg.IUT.toString()); + String wfsRef = (null != iutRef) ? iutRef : params.get(TestRunArg.WFS.toString()); + if ((null == wfsRef) || wfsRef.isEmpty()) { + throw new IllegalArgumentException("Required parameter not found"); + } + URI wfsURI = URI.create(wfsRef); + Document doc = null; + try { + doc = URIUtils.resolveURIAsDocument(wfsURI); + Element docElem = doc.getDocumentElement(); + QName qName = new QName(docElem.getNamespaceURI(), docElem.getLocalName()); + if (!qName.equals(WFS2.QNAME_WFS_CAPABILITIES)) { + throw new RuntimeException("Not a WFS2 capabilities document: " + qName); + } + suite.setAttribute(SuiteAttribute.WFS_VERSION.getName(), docElem.getAttribute("version")); + } + catch (ConnectException | FileNotFoundException | UnknownHostException | MalformedURLException e) { + throw new RuntimeException("Failed to connect to resource located at " + wfsURI, e); + } + catch (SAXException | IOException ex) { + // push exception up through TestNG ISuiteListener interface + throw new RuntimeException("Failed to parse resource located at " + wfsURI, ex); + } + if (null != doc) { + suite.setAttribute(SuiteAttribute.TEST_SUBJECT.getName(), doc); + Map featureInfo = ServiceMetadataUtils.extractFeatureTypeInfo(doc); + suite.setAttribute(SuiteAttribute.FEATURE_INFO.getName(), featureInfo); + LOGR.log(Level.FINER, "Parsed resource from {0}\n{1}", + new Object[] { wfsURI, XMLUtils.writeNodeToString(doc) }); + } + } + + /** + * Sets the value of the "xsd" suite parameter, the value of which is the request URI + * used to retrieve the GML application schema(s) supported by the WFS under test. The + * URI corresponds to a DescribeFeatureType request; its value is derived from + * information in the service metadata document (GET method). + * @param suite An ISuite object representing a TestNG test suite. The value of the + * attribute {@link SuiteAttribute#TEST_SUBJECT} should be a Document node + * representing service metadata. + */ + void setAppSchemaParameter(ISuite suite) { + if (null == suite.getAttribute(SuiteAttribute.TEST_SUBJECT.getName())) { + return; + } + Document wfsMetadata = (Document) suite.getAttribute(SuiteAttribute.TEST_SUBJECT.getName()); + URI endpoint = ServiceMetadataUtils.getOperationEndpoint(wfsMetadata, WFS2.DESCRIBE_FEATURE_TYPE, + ProtocolBinding.GET); + if (endpoint.toString().isEmpty()) { + throw new RuntimeException("DescribeFeatureType request endpoint (GET method) not found in capabilities."); + } + LOGR.log(Level.CONFIG, "DescribeFeatureType request endpoint: {0}", endpoint); + StringBuilder reqURI = new StringBuilder(endpoint.toString()); + reqURI.append("?service=WFS&version=2.0.0&request=DescribeFeatureType"); + Map params = suite.getXmlSuite().getParameters(); + params.put(org.opengis.cite.iso19136.TestRunArg.XSD.toString(), reqURI.toString()); + LOGR.log(Level.CONFIG, "Set suite parameter {0}: {1}", + new Object[] { org.opengis.cite.iso19136.TestRunArg.XSD, reqURI }); + // GML tests read this attribute + Set schemaURIs = new HashSet(); + schemaURIs.add(URI.create(reqURI.toString())); + suite.setAttribute(org.opengis.cite.iso19136.SuiteAttribute.SCHEMA_LOC_SET.getName(), schemaURIs); + } - /** - * Sets the value of the "xsd" suite parameter, the value of which is the - * request URI used to retrieve the GML application schema(s) supported by - * the WFS under test. The URI corresponds to a DescribeFeatureType request; - * its value is derived from information in the service metadata document - * (GET method). - * - * @param suite - * An ISuite object representing a TestNG test suite. The value - * of the attribute {@link SuiteAttribute#TEST_SUBJECT} should be - * a Document node representing service metadata. - */ - void setAppSchemaParameter(ISuite suite) { - if (null == suite.getAttribute(SuiteAttribute.TEST_SUBJECT.getName())) { - return; - } - Document wfsMetadata = (Document) suite.getAttribute(SuiteAttribute.TEST_SUBJECT.getName()); - URI endpoint = ServiceMetadataUtils.getOperationEndpoint(wfsMetadata, WFS2.DESCRIBE_FEATURE_TYPE, - ProtocolBinding.GET); - if (endpoint.toString().isEmpty()) { - throw new RuntimeException("DescribeFeatureType request endpoint (GET method) not found in capabilities."); - } - LOGR.log(Level.CONFIG, "DescribeFeatureType request endpoint: {0}", endpoint); - StringBuilder reqURI = new StringBuilder(endpoint.toString()); - reqURI.append("?service=WFS&version=2.0.0&request=DescribeFeatureType"); - Map params = suite.getXmlSuite().getParameters(); - params.put(org.opengis.cite.iso19136.TestRunArg.XSD.toString(), reqURI.toString()); - LOGR.log(Level.CONFIG, "Set suite parameter {0}: {1}", - new Object[] { org.opengis.cite.iso19136.TestRunArg.XSD, reqURI }); - // GML tests read this attribute - Set schemaURIs = new HashSet(); - schemaURIs.add(URI.create(reqURI.toString())); - suite.setAttribute(org.opengis.cite.iso19136.SuiteAttribute.SCHEMA_LOC_SET.getName(), schemaURIs); - } } diff --git a/src/main/java/org/opengis/cite/iso19142/SuitePreconditions.java b/src/main/java/org/opengis/cite/iso19142/SuitePreconditions.java index e03caac1..6c5154b9 100644 --- a/src/main/java/org/opengis/cite/iso19142/SuitePreconditions.java +++ b/src/main/java/org/opengis/cite/iso19142/SuitePreconditions.java @@ -18,108 +18,106 @@ import org.w3c.dom.Element; /** - * Includes tests to confirm the readiness of the SUT to undergo testing. If any - * of these test methods fail then its name is added to the suite attribute - * {@link org.opengis.cite.iso19142.SuiteAttribute#FAILED_PRECONDITIONS}. The - * presence of the attribute can be checked in a @BeforeTest method - * to determine whether or not a set of tests should be skipped if one or more - * preconditions were not satisfied. - * + * Includes tests to confirm the readiness of the SUT to undergo testing. If any of these + * test methods fail then its name is added to the suite attribute + * {@link org.opengis.cite.iso19142.SuiteAttribute#FAILED_PRECONDITIONS}. The presence of + * the attribute can be checked in a @BeforeTest method to determine whether + * or not a set of tests should be skipped if one or more preconditions were not + * satisfied. + * */ public class SuitePreconditions { - private static final Logger LOGR = Logger.getLogger(SuitePreconditions.class.getName()); + private static final Logger LOGR = Logger.getLogger(SuitePreconditions.class.getName()); - @AfterMethod - @SuppressWarnings("unchecked") - public void preconditionNotSatisfied(ITestResult result) { - if (!result.isSuccess()) { - Object failedPreconditions = result.getTestContext().getSuite() - .getAttribute(SuiteAttribute.FAILED_PRECONDITIONS.getName()); - if (null == failedPreconditions) { - failedPreconditions = new ArrayList(); - result.getTestContext().getSuite().setAttribute(SuiteAttribute.FAILED_PRECONDITIONS.getName(), - failedPreconditions); - } - ArrayList.class.cast(failedPreconditions).add(result.getName()); - } - } + @AfterMethod + @SuppressWarnings("unchecked") + public void preconditionNotSatisfied(ITestResult result) { + if (!result.isSuccess()) { + Object failedPreconditions = result.getTestContext() + .getSuite() + .getAttribute(SuiteAttribute.FAILED_PRECONDITIONS.getName()); + if (null == failedPreconditions) { + failedPreconditions = new ArrayList(); + result.getTestContext() + .getSuite() + .setAttribute(SuiteAttribute.FAILED_PRECONDITIONS.getName(), failedPreconditions); + } + ArrayList.class.cast(failedPreconditions).add(result.getName()); + } + } - /** - * [{@literal @Test}] Verifies that the test subject is a WFS 2.0 service. - * The document element in the supplied metadata resource must be - * "{http://www.opengis.net/wfs/2.0}WFS_Capabilities". - * - * @param testContext - * The test run context (ITestContext). - */ - @Test(description = "Test subject is WFS 2.0 service") - public void verifyServiceDescription(ITestContext testContext) { - Document wfsMetadata = (Document) testContext.getSuite().getAttribute(SuiteAttribute.TEST_SUBJECT.getName()); - ETSAssert.assertQualifiedName(wfsMetadata.getDocumentElement(), - new QName(Namespaces.WFS, WFS2.WFS_CAPABILITIES)); - } + /** + * [{@literal @Test}] Verifies that the test subject is a WFS 2.0 service. The + * document element in the supplied metadata resource must be + * "{http://www.opengis.net/wfs/2.0}WFS_Capabilities". + * @param testContext The test run context (ITestContext). + */ + @Test(description = "Test subject is WFS 2.0 service") + public void verifyServiceDescription(ITestContext testContext) { + Document wfsMetadata = (Document) testContext.getSuite().getAttribute(SuiteAttribute.TEST_SUBJECT.getName()); + ETSAssert.assertQualifiedName(wfsMetadata.getDocumentElement(), + new QName(Namespaces.WFS, WFS2.WFS_CAPABILITIES)); + } - /** - * [{@literal @Test}] Confirms that the SUT is available and produces a - * service description in response to a basic GetCapabilities request. The - * document element is expected to have the following infoset properties: - *
      - *
    • [local name] = "WFS_Capabilities"
    • - *
    • [namespace name] = "http://www.opengis.net/wfs/2.0"
    • - *
    - * - * @param testContext - * Supplies details about the test run. - */ - @Test(description = "SUT produces GetCapabilities response", dependsOnMethods = { "verifyServiceDescription" }) - public void serviceIsAvailable(ITestContext testContext) { - Document wfsMetadata = (Document) testContext.getSuite().getAttribute(SuiteAttribute.TEST_SUBJECT.getName()); - WFSClient wfsClient = new WFSClient(wfsMetadata); - Document capabilities = wfsClient.getCapabilities(); - Assert.assertNotNull(capabilities, "No GetCapabilities response from SUT."); - Element docElement = capabilities.getDocumentElement(); - Assert.assertEquals(docElement.getLocalName(), WFS2.WFS_CAPABILITIES, - "Capabilities document element has unexpected [local name]."); - Assert.assertEquals(docElement.getNamespaceURI(), Namespaces.WFS, - "Capabilities document element has unexpected [namespace name]."); - } + /** + * [{@literal @Test}] Confirms that the SUT is available and produces a service + * description in response to a basic GetCapabilities request. The document element is + * expected to have the following infoset properties: + *
      + *
    • [local name] = "WFS_Capabilities"
    • + *
    • [namespace name] = "http://www.opengis.net/wfs/2.0"
    • + *
    + * @param testContext Supplies details about the test run. + */ + @Test(description = "SUT produces GetCapabilities response", dependsOnMethods = { "verifyServiceDescription" }) + public void serviceIsAvailable(ITestContext testContext) { + Document wfsMetadata = (Document) testContext.getSuite().getAttribute(SuiteAttribute.TEST_SUBJECT.getName()); + WFSClient wfsClient = new WFSClient(wfsMetadata); + Document capabilities = wfsClient.getCapabilities(); + Assert.assertNotNull(capabilities, "No GetCapabilities response from SUT."); + Element docElement = capabilities.getDocumentElement(); + Assert.assertEquals(docElement.getLocalName(), WFS2.WFS_CAPABILITIES, + "Capabilities document element has unexpected [local name]."); + Assert.assertEquals(docElement.getNamespaceURI(), Namespaces.WFS, + "Capabilities document element has unexpected [namespace name]."); + } + + /** + * [{@literal @Test}] Confirms that the SUT can supply data for at least one + * advertised feature type. + * @param testContext Supplies details about the test run. + */ + @Test(description = "SUT has data for at least one advertised feature type", + dependsOnMethods = { "verifyServiceDescription" }) + public void dataAreAvailable(ITestContext testContext) { + ISuite suite = testContext.getSuite(); + Document wfsMetadata = (Document) suite.getAttribute(SuiteAttribute.TEST_SUBJECT.getName()); + DataSampler sampler = new DataSampler(wfsMetadata); + try { + sampler.acquireFeatureData(); + } + catch (RuntimeException rx) { + StringBuilder msg = new StringBuilder("Failed to acquire feature data ("); + msg.append(rx.getClass().getName()).append("): "); + msg.append(rx.getMessage()); + throw new AssertionError(msg.toString()); + } + Map featureTypeInfo = sampler.getFeatureTypeInfo(); + boolean sutHasData = false; + for (FeatureTypeInfo typeInfo : featureTypeInfo.values()) { + if (typeInfo.isInstantiated()) { + sutHasData = true; + break; + } + } + if (!sutHasData) { + String msg = ErrorMessage.get(ErrorMessageKeys.DATA_UNAVAILABLE); + LOGR.warning(msg + featureTypeInfo.toString()); + throw new AssertionError(msg); + } + suite.setAttribute(SuiteAttribute.FEATURE_INFO.getName(), featureTypeInfo); + suite.setAttribute(SuiteAttribute.SAMPLER.getName(), sampler); + } - /** - * [{@literal @Test}] Confirms that the SUT can supply data for at least one - * advertised feature type. - * - * @param testContext - * Supplies details about the test run. - */ - @Test(description = "SUT has data for at least one advertised feature type", dependsOnMethods = { - "verifyServiceDescription" }) - public void dataAreAvailable(ITestContext testContext) { - ISuite suite = testContext.getSuite(); - Document wfsMetadata = (Document) suite.getAttribute(SuiteAttribute.TEST_SUBJECT.getName()); - DataSampler sampler = new DataSampler(wfsMetadata); - try { - sampler.acquireFeatureData(); - } catch (RuntimeException rx) { - StringBuilder msg = new StringBuilder("Failed to acquire feature data ("); - msg.append(rx.getClass().getName()).append("): "); - msg.append(rx.getMessage()); - throw new AssertionError(msg.toString()); - } - Map featureTypeInfo = sampler.getFeatureTypeInfo(); - boolean sutHasData = false; - for (FeatureTypeInfo typeInfo : featureTypeInfo.values()) { - if (typeInfo.isInstantiated()) { - sutHasData = true; - break; - } - } - if (!sutHasData) { - String msg = ErrorMessage.get(ErrorMessageKeys.DATA_UNAVAILABLE); - LOGR.warning(msg + featureTypeInfo.toString()); - throw new AssertionError(msg); - } - suite.setAttribute(SuiteAttribute.FEATURE_INFO.getName(), featureTypeInfo); - suite.setAttribute(SuiteAttribute.SAMPLER.getName(), sampler); - } } diff --git a/src/main/java/org/opengis/cite/iso19142/TestNGController.java b/src/main/java/org/opengis/cite/iso19142/TestNGController.java index e92bc01d..2aef235a 100644 --- a/src/main/java/org/opengis/cite/iso19142/TestNGController.java +++ b/src/main/java/org/opengis/cite/iso19142/TestNGController.java @@ -31,145 +31,143 @@ */ public class TestNGController implements TestSuiteController { - private TestRunExecutor executor; - private Properties etsProperties = new Properties(); - - /** - * A convenience method for running the test suite using a command-line - * interface. The default values of the test run arguments are as follows: - *
      - *
    • XML properties file: ${user.home}/test-run-props.xml
    • - *
    • outputDir: ${user.home}
    • - *
    • deleteSubjectOnFinish: false
    • - *
    - *

    - * Synopsis - *

    - * - *
    -     * ets-*-aio.jar [-o|--outputDir $TMPDIR] [-d|--deleteSubjectOnFinish] [test-run-props.xml]
    -     * 
    - * - * @param args - * Test run arguments (optional). The first argument must refer - * to an XML properties file containing the expected set of test - * run arguments. If no argument is supplied, the file located at - * ${user.home}/test-run-props.xml will be used. - * @throws Exception - * If the test run cannot be executed (usually due to - * unsatisfied pre-conditions). - */ - public static void main(String[] args) throws Exception { - CommandLineArguments testRunArgs = new CommandLineArguments(); - JCommander cmd = new JCommander(testRunArgs); - try { - cmd.parse(args); - } catch (ParameterException px) { - System.out.println(px.getMessage()); - cmd.usage(); - } - if (testRunArgs.doDeleteSubjectOnFinish()) { - System.setProperty("deleteSubjectOnFinish", "true"); - } - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - DocumentBuilder db = dbf.newDocumentBuilder(); - File xmlArgs = testRunArgs.getPropertiesFile(); - if (!xmlArgs.exists()) { - throw new IllegalArgumentException("Test run arguments not found at " + xmlArgs); - } - Document testRunProps = db.parse(xmlArgs); - TestNGController controller = new TestNGController(testRunArgs.getOutputDir()); - Source testResults = controller.doTestRun(testRunProps); - System.out.println("Test results: " + testResults.getSystemId()); - } - - /** - * Default constructor uses the location given by the "java.io.tmpdir" - * system property as the root output directory. - */ - public TestNGController() { - this(System.getProperty("java.io.tmpdir")); - } - - /** - * Construct a controller that writes results to the given output directory. - * - * @param outputDir - * The location of the directory in which test results will be - * written (a file system path or a 'file' URI). It will be - * created if it does not exist. - */ - public TestNGController(String outputDir) { - InputStream is = getClass().getResourceAsStream("ets.properties"); - try { - this.etsProperties.load(is); - } catch (IOException ex) { - TestSuiteLogger.log(Level.WARNING, "Unable to load ets.properties. " + ex.getMessage()); - } - URL tngSuite = TestNGController.class.getResource("testng.xml"); - File resultsDir; - if (null == outputDir || outputDir.isEmpty()) { - resultsDir = new File(FilenameUtils.normalize(System.getProperty("user.home"))); - } else if (outputDir.startsWith("file:")) { - resultsDir = new File(URI.create(outputDir)); - } else { - resultsDir = new File(outputDir); - } - TestSuiteLogger.log(Level.CONFIG, "Using TestNG config: " + tngSuite); - TestSuiteLogger.log(Level.INFO, "Using outputDirPath: " + resultsDir.getAbsolutePath()); - // NOTE: setting third argument to 'true' enables the default listeners - this.executor = new TestNGExecutor(tngSuite.toString(), resultsDir.getAbsolutePath(), false); - } - - @Override - public String getCode() { - return etsProperties.getProperty("ets-code"); - } - - @Override - public String getVersion() { - return etsProperties.getProperty("ets-version"); - } - - @Override - public String getTitle() { - return etsProperties.getProperty("ets-title"); - } - - @Override - public Source doTestRun(Document testRunArgs) throws Exception { - validateTestRunArgs(testRunArgs); - return executor.execute(testRunArgs); - } - - /** - * Validates the given set of test run arguments. The test run is aborted if - * any checks fail. At least one of the test run arguments ( - * {@link org.opengis.cite.iso19142.TestRunArg#WFS}, {link - * org.opengis.cite.iso19142.TestRunArg#IUT}) must be supplied. - * - * @param testRunArgs - * A DOM Document containing a set of XML properties (key-value - * pairs). - * @throws IllegalArgumentException - * If any arguments are missing or invalid for some reason. - */ - void validateTestRunArgs(Document testRunArgs) { - if (null == testRunArgs || !testRunArgs.getDocumentElement().getNodeName().equals("properties")) { - throw new IllegalArgumentException("Input is not an XML properties document."); - } - NodeList entries = testRunArgs.getDocumentElement().getElementsByTagName("entry"); - if (entries.getLength() == 0) { - throw new IllegalArgumentException("No test run arguments found."); - } - Map args = new HashMap(); - for (int i = 0; i < entries.getLength(); i++) { - Element entry = (Element) entries.item(i); - args.put(entry.getAttribute("key"), entry.getTextContent().trim()); - } - if (!(args.containsKey(TestRunArg.IUT.toString()) || args.containsKey(TestRunArg.WFS.toString()))) { - throw new IllegalArgumentException( - String.format("Missing argument: '%s' or '%s' must be present.", TestRunArg.IUT, TestRunArg.WFS)); - } - } + private TestRunExecutor executor; + + private Properties etsProperties = new Properties(); + + /** + * A convenience method for running the test suite using a command-line interface. The + * default values of the test run arguments are as follows: + *
      + *
    • XML properties file: ${user.home}/test-run-props.xml
    • + *
    • outputDir: ${user.home}
    • + *
    • deleteSubjectOnFinish: false
    • + *
    + *

    + * Synopsis + *

    + * + *
    +	 * ets-*-aio.jar [-o|--outputDir $TMPDIR] [-d|--deleteSubjectOnFinish] [test-run-props.xml]
    +	 * 
    + * @param args Test run arguments (optional). The first argument must refer to an XML + * properties file containing the expected set of test run arguments. If no argument + * is supplied, the file located at ${user.home}/test-run-props.xml will be used. + * @throws Exception If the test run cannot be executed (usually due to unsatisfied + * pre-conditions). + */ + public static void main(String[] args) throws Exception { + CommandLineArguments testRunArgs = new CommandLineArguments(); + JCommander cmd = new JCommander(testRunArgs); + try { + cmd.parse(args); + } + catch (ParameterException px) { + System.out.println(px.getMessage()); + cmd.usage(); + } + if (testRunArgs.doDeleteSubjectOnFinish()) { + System.setProperty("deleteSubjectOnFinish", "true"); + } + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + DocumentBuilder db = dbf.newDocumentBuilder(); + File xmlArgs = testRunArgs.getPropertiesFile(); + if (!xmlArgs.exists()) { + throw new IllegalArgumentException("Test run arguments not found at " + xmlArgs); + } + Document testRunProps = db.parse(xmlArgs); + TestNGController controller = new TestNGController(testRunArgs.getOutputDir()); + Source testResults = controller.doTestRun(testRunProps); + System.out.println("Test results: " + testResults.getSystemId()); + } + + /** + * Default constructor uses the location given by the "java.io.tmpdir" system property + * as the root output directory. + */ + public TestNGController() { + this(System.getProperty("java.io.tmpdir")); + } + + /** + * Construct a controller that writes results to the given output directory. + * @param outputDir The location of the directory in which test results will be + * written (a file system path or a 'file' URI). It will be created if it does not + * exist. + */ + public TestNGController(String outputDir) { + InputStream is = getClass().getResourceAsStream("ets.properties"); + try { + this.etsProperties.load(is); + } + catch (IOException ex) { + TestSuiteLogger.log(Level.WARNING, "Unable to load ets.properties. " + ex.getMessage()); + } + URL tngSuite = TestNGController.class.getResource("testng.xml"); + File resultsDir; + if (null == outputDir || outputDir.isEmpty()) { + resultsDir = new File(FilenameUtils.normalize(System.getProperty("user.home"))); + } + else if (outputDir.startsWith("file:")) { + resultsDir = new File(URI.create(outputDir)); + } + else { + resultsDir = new File(outputDir); + } + TestSuiteLogger.log(Level.CONFIG, "Using TestNG config: " + tngSuite); + TestSuiteLogger.log(Level.INFO, "Using outputDirPath: " + resultsDir.getAbsolutePath()); + // NOTE: setting third argument to 'true' enables the default listeners + this.executor = new TestNGExecutor(tngSuite.toString(), resultsDir.getAbsolutePath(), false); + } + + @Override + public String getCode() { + return etsProperties.getProperty("ets-code"); + } + + @Override + public String getVersion() { + return etsProperties.getProperty("ets-version"); + } + + @Override + public String getTitle() { + return etsProperties.getProperty("ets-title"); + } + + @Override + public Source doTestRun(Document testRunArgs) throws Exception { + validateTestRunArgs(testRunArgs); + return executor.execute(testRunArgs); + } + + /** + * Validates the given set of test run arguments. The test run is aborted if any + * checks fail. At least one of the test run arguments ( + * {@link org.opengis.cite.iso19142.TestRunArg#WFS}, {link + * org.opengis.cite.iso19142.TestRunArg#IUT}) must be supplied. + * @param testRunArgs A DOM Document containing a set of XML properties (key-value + * pairs). + * @throws IllegalArgumentException If any arguments are missing or invalid for some + * reason. + */ + void validateTestRunArgs(Document testRunArgs) { + if (null == testRunArgs || !testRunArgs.getDocumentElement().getNodeName().equals("properties")) { + throw new IllegalArgumentException("Input is not an XML properties document."); + } + NodeList entries = testRunArgs.getDocumentElement().getElementsByTagName("entry"); + if (entries.getLength() == 0) { + throw new IllegalArgumentException("No test run arguments found."); + } + Map args = new HashMap(); + for (int i = 0; i < entries.getLength(); i++) { + Element entry = (Element) entries.item(i); + args.put(entry.getAttribute("key"), entry.getTextContent().trim()); + } + if (!(args.containsKey(TestRunArg.IUT.toString()) || args.containsKey(TestRunArg.WFS.toString()))) { + throw new IllegalArgumentException( + String.format("Missing argument: '%s' or '%s' must be present.", TestRunArg.IUT, TestRunArg.WFS)); + } + } + } diff --git a/src/main/java/org/opengis/cite/iso19142/TestRunArg.java b/src/main/java/org/opengis/cite/iso19142/TestRunArg.java index 2580cc2f..15fe500c 100644 --- a/src/main/java/org/opengis/cite/iso19142/TestRunArg.java +++ b/src/main/java/org/opengis/cite/iso19142/TestRunArg.java @@ -5,28 +5,28 @@ */ public enum TestRunArg { - /** A string that identifies an available feature instance. */ - FID, - /** - * An absolute URI that refers to a representation of the test subject or - * metadata about it. - */ - IUT, - /** - * An absolute URI referring to metadata about the WFS implementation under - * test. This is expected to be a WFS capabilities document where the - * document element is {@code http://www.opengis.net/wfs/2.0} - * WFS_Capabilities}. - */ - WFS, - /** - * An implementation conformance statement: a comma-separated list - * indicating which conformance classes are supported. - */ - ICS; + /** A string that identifies an available feature instance. */ + FID, + /** + * An absolute URI that refers to a representation of the test subject or metadata + * about it. + */ + IUT, + /** + * An absolute URI referring to metadata about the WFS implementation under test. This + * is expected to be a WFS capabilities document where the document element is + * {@code http://www.opengis.net/wfs/2.0} WFS_Capabilities}. + */ + WFS, + /** + * An implementation conformance statement: a comma-separated list indicating which + * conformance classes are supported. + */ + ICS; + + @Override + public String toString() { + return name().toLowerCase(); + } - @Override - public String toString() { - return name().toLowerCase(); - } } diff --git a/src/main/java/org/opengis/cite/iso19142/WFS2.java b/src/main/java/org/opengis/cite/iso19142/WFS2.java index 2ad8cd59..323253a3 100644 --- a/src/main/java/org/opengis/cite/iso19142/WFS2.java +++ b/src/main/java/org/opengis/cite/iso19142/WFS2.java @@ -3,135 +3,201 @@ import javax.xml.namespace.QName; /** - * Contains various constants pertaining to WFS 2.0 service interfaces as - * specified in ISO 19142:2010 and related standards. - * + * Contains various constants pertaining to WFS 2.0 service interfaces as specified in ISO + * 19142:2010 and related standards. + * * @see "ISO 19142:2010, Geographic information -- Web Feature Service" */ public class WFS2 { - private WFS2() { - } - - public static final String NS_URI = "http://www.opengis.net/wfs/2.0"; - public static final String SCHEMA_URI = "http://schemas.opengis.net/wfs/2.0/wfs.xsd"; - public static final String SERVICE_TYPE_CODE = "WFS"; - public static final String VERSION = "2.0"; - public static final String V2_0_0 = "2.0.0"; - public static final String GET_CAPABILITIES = "GetCapabilities"; - public static final String DESCRIBE_FEATURE_TYPE = "DescribeFeatureType"; - public static final String LIST_STORED_QUERIES = "ListStoredQueries"; - public static final String DESC_STORED_QUERIES = "DescribeStoredQueries"; - public static final String GET_FEATURE = "GetFeature"; - public static final String GET_PROP_VALUE = "GetPropertyValue"; - public static final String REQUEST_PARAM = "request"; - public static final String SERVICE_PARAM = "service"; - public static final String VERSION_PARAM = "version"; - public static final String TYPENAMES_PARAM = "typenames"; - public static final String NAMESPACES_PARAM = "namespaces"; - public static final String STOREDQUERY_ID_PARAM = "storedquery_id"; - public static final String ID_PARAM = "id"; - public static final String START_INDEX_PARAM = "startIndex"; - public static final String COUNT_PARAM = "count"; - public static final String SRSNAME_PARAM = "srsName"; - /** Stored query identifier: GetFeatureById */ - public static final String QRY_GET_FEATURE_BY_ID = "http://www.opengis.net/def/query/OGC-WFS/0/GetFeatureById"; - /** Stored query identifier: GetFeatureById (deprecated in v2.0.2 */ - public static final String QRY_GET_FEATURE_BY_ID_URN = "urn:ogc:def:query:OGC-WFS::GetFeatureById"; - /** Service constraint indicating support for HTTP GET method bindings. */ - public static final String KVP_ENC = "KVPEncoding"; - /** Service constraint indicating support for HTTP POST method bindings. */ - public static final String XML_ENC = "XMLEncoding"; - /** Service constraint indicating support for SOAP message bindings. */ - public static final String SOAP_ENC = "SOAPEncoding"; - public static final String SOAP_VERSION = "1.1"; - public static final String SOAP_VERSION_1_2 = "1.2"; - /** Local name of document element in WFS capabilities document. */ - public static final String WFS_CAPABILITIES = "WFS_Capabilities"; - /** Qualified name of document element in WFS capabilities document. */ - public static final QName QNAME_WFS_CAPABILITIES = new QName(Namespaces.WFS, WFS_CAPABILITIES); - /** Local name of ad hoc Query element. */ - public static final String QUERY_ELEM = "Query"; - /** Local name of StoredQuery element. */ - public static final String STORED_QRY_ELEM = "StoredQuery"; - /** Local name of StoredQueryId element. */ - public static final String STORED_QRY_ID_ELEM = "StoredQueryId"; - /** Local name of Parameter element in a StoredQuery. */ - public static final String PARAM_ELEM = "Parameter"; - /** Local name of TypeName element in DescribeFeatureType. */ - public static final String TYPENAME_ELEM = "TypeName"; - /** ValueCollection element. */ - public static final String VALUE_COLLECTION = "ValueCollection"; - /** FeatureCollection element. */ - public static final String FEATURE_COLLECTION = "FeatureCollection"; - /** member element. */ - public static final String MEMBER = "member"; - /** Transaction element. */ - public static final String TRANSACTION = "Transaction"; - /** Update element (Transaction). */ - public static final String UPDATE = "Update"; - /** Insert element (Transaction). */ - public static final String INSERT = "Insert"; - /** Delete element (Transaction). */ - public static final String DELETE = "Delete"; - /** Replace element (Transaction). */ - public static final String REPLACE = "Replace"; - /** Native element (Transaction). */ - public static final String NATIVE = "Native"; - /** TransactionResponse element. */ - public static final String TRANSACTION_RSP = "TransactionResponse"; - /** TransactionResponse/TransactionSummary element. */ - public static final String TRANSACTION_SUMMARY = "TransactionSummary"; - /** TransactionSummary/totalInserted element. */ - public static final String TOTAL_INS = "totalInserted"; - /** TransactionSummary/totalReplaced element. */ - public static final String TOTAL_REPL = "totalReplaced"; - /** TransactionSummary/totalUpdated element. */ - public static final String TOTAL_UPD = "totalUpdated"; - /** TransactionSummary/totalDeleted element. */ - public static final String TOTAL_DEL = "totalDeleted"; - /** Media type for SOAP 1.2 message envelopes (RFC 3902). */ - public static final String APPLICATION_SOAP = "application/soap+xml"; - /** - * Service constraint corresponding to the 'Basic WFS' conformance class. - */ - public static final String BASIC_WFS = "ImplementsBasicWFS"; - /** - * Service constraint corresponding to the 'Locking WFS' conformance class. - */ - public static final String LOCKING_WFS = "ImplementsLockingWFS"; - /** LockFeature request element. */ - public static final String LOCK_FEATURE = "LockFeature"; - /** LockFeature response element. */ - public static final String LOCK_FEATURE_RSP = "LockFeatureResponse"; - /** GetFeatureWithLock request element. */ - public static final String GET_FEATURE_WITH_LOCK = "GetFeatureWithLock"; - /** CRS: EPSG 4326 (see cl. 7.9.2.4.4) */ - public static final String EPSG_4326 = "urn:ogc:def:crs:EPSG::4326"; - /** Service constraint for 'Transactional WFS' conformance class. */ - public static final String TRX_WFS = "ImplementsTransactionalWFS"; - /** CreateStoredQuery request. */ - public static final String CREATE_STORED_QRY = "CreateStoredQuery"; - /** DropStoredQuery request. */ - public static final String DROP_STORED_QRY = "DropStoredQuery"; - - /** VersionState indicates the state of a feature version. */ - public enum VersionState { - VALID, SUPERSEDED, RETIRED, FUTURE; - @Override - public String toString() { - return this.name().toLowerCase(); - } - } - - /** The action embodied by a transaction. */ - public enum Transaction { - INSERT, UPDATE, DELETE, REPLACE, NATIVE; - @Override - public String toString() { - StringBuilder action = new StringBuilder(this.name().toLowerCase()); - action.setCharAt(0, Character.toUpperCase(action.charAt(0))); - return action.toString(); - } - } + private WFS2() { + } + + public static final String NS_URI = "http://www.opengis.net/wfs/2.0"; + + public static final String SCHEMA_URI = "http://schemas.opengis.net/wfs/2.0/wfs.xsd"; + + public static final String SERVICE_TYPE_CODE = "WFS"; + + public static final String VERSION = "2.0"; + + public static final String V2_0_0 = "2.0.0"; + + public static final String GET_CAPABILITIES = "GetCapabilities"; + + public static final String DESCRIBE_FEATURE_TYPE = "DescribeFeatureType"; + + public static final String LIST_STORED_QUERIES = "ListStoredQueries"; + + public static final String DESC_STORED_QUERIES = "DescribeStoredQueries"; + + public static final String GET_FEATURE = "GetFeature"; + + public static final String GET_PROP_VALUE = "GetPropertyValue"; + + public static final String REQUEST_PARAM = "request"; + + public static final String SERVICE_PARAM = "service"; + + public static final String VERSION_PARAM = "version"; + + public static final String TYPENAMES_PARAM = "typenames"; + + public static final String NAMESPACES_PARAM = "namespaces"; + + public static final String STOREDQUERY_ID_PARAM = "storedquery_id"; + + public static final String ID_PARAM = "id"; + + public static final String START_INDEX_PARAM = "startIndex"; + + public static final String COUNT_PARAM = "count"; + + public static final String SRSNAME_PARAM = "srsName"; + + /** Stored query identifier: GetFeatureById */ + public static final String QRY_GET_FEATURE_BY_ID = "http://www.opengis.net/def/query/OGC-WFS/0/GetFeatureById"; + + /** Stored query identifier: GetFeatureById (deprecated in v2.0.2 */ + public static final String QRY_GET_FEATURE_BY_ID_URN = "urn:ogc:def:query:OGC-WFS::GetFeatureById"; + + /** Service constraint indicating support for HTTP GET method bindings. */ + public static final String KVP_ENC = "KVPEncoding"; + + /** Service constraint indicating support for HTTP POST method bindings. */ + public static final String XML_ENC = "XMLEncoding"; + + /** Service constraint indicating support for SOAP message bindings. */ + public static final String SOAP_ENC = "SOAPEncoding"; + + public static final String SOAP_VERSION = "1.1"; + + public static final String SOAP_VERSION_1_2 = "1.2"; + + /** Local name of document element in WFS capabilities document. */ + public static final String WFS_CAPABILITIES = "WFS_Capabilities"; + + /** Qualified name of document element in WFS capabilities document. */ + public static final QName QNAME_WFS_CAPABILITIES = new QName(Namespaces.WFS, WFS_CAPABILITIES); + + /** Local name of ad hoc Query element. */ + public static final String QUERY_ELEM = "Query"; + + /** Local name of StoredQuery element. */ + public static final String STORED_QRY_ELEM = "StoredQuery"; + + /** Local name of StoredQueryId element. */ + public static final String STORED_QRY_ID_ELEM = "StoredQueryId"; + + /** Local name of Parameter element in a StoredQuery. */ + public static final String PARAM_ELEM = "Parameter"; + + /** Local name of TypeName element in DescribeFeatureType. */ + public static final String TYPENAME_ELEM = "TypeName"; + + /** ValueCollection element. */ + public static final String VALUE_COLLECTION = "ValueCollection"; + + /** FeatureCollection element. */ + public static final String FEATURE_COLLECTION = "FeatureCollection"; + + /** member element. */ + public static final String MEMBER = "member"; + + /** Transaction element. */ + public static final String TRANSACTION = "Transaction"; + + /** Update element (Transaction). */ + public static final String UPDATE = "Update"; + + /** Insert element (Transaction). */ + public static final String INSERT = "Insert"; + + /** Delete element (Transaction). */ + public static final String DELETE = "Delete"; + + /** Replace element (Transaction). */ + public static final String REPLACE = "Replace"; + + /** Native element (Transaction). */ + public static final String NATIVE = "Native"; + + /** TransactionResponse element. */ + public static final String TRANSACTION_RSP = "TransactionResponse"; + + /** TransactionResponse/TransactionSummary element. */ + public static final String TRANSACTION_SUMMARY = "TransactionSummary"; + + /** TransactionSummary/totalInserted element. */ + public static final String TOTAL_INS = "totalInserted"; + + /** TransactionSummary/totalReplaced element. */ + public static final String TOTAL_REPL = "totalReplaced"; + + /** TransactionSummary/totalUpdated element. */ + public static final String TOTAL_UPD = "totalUpdated"; + + /** TransactionSummary/totalDeleted element. */ + public static final String TOTAL_DEL = "totalDeleted"; + + /** Media type for SOAP 1.2 message envelopes (RFC 3902). */ + public static final String APPLICATION_SOAP = "application/soap+xml"; + + /** + * Service constraint corresponding to the 'Basic WFS' conformance class. + */ + public static final String BASIC_WFS = "ImplementsBasicWFS"; + + /** + * Service constraint corresponding to the 'Locking WFS' conformance class. + */ + public static final String LOCKING_WFS = "ImplementsLockingWFS"; + + /** LockFeature request element. */ + public static final String LOCK_FEATURE = "LockFeature"; + + /** LockFeature response element. */ + public static final String LOCK_FEATURE_RSP = "LockFeatureResponse"; + + /** GetFeatureWithLock request element. */ + public static final String GET_FEATURE_WITH_LOCK = "GetFeatureWithLock"; + + /** CRS: EPSG 4326 (see cl. 7.9.2.4.4) */ + public static final String EPSG_4326 = "urn:ogc:def:crs:EPSG::4326"; + + /** Service constraint for 'Transactional WFS' conformance class. */ + public static final String TRX_WFS = "ImplementsTransactionalWFS"; + + /** CreateStoredQuery request. */ + public static final String CREATE_STORED_QRY = "CreateStoredQuery"; + + /** DropStoredQuery request. */ + public static final String DROP_STORED_QRY = "DropStoredQuery"; + + /** VersionState indicates the state of a feature version. */ + public enum VersionState { + + VALID, SUPERSEDED, RETIRED, FUTURE; + + @Override + public String toString() { + return this.name().toLowerCase(); + } + + } + + /** The action embodied by a transaction. */ + public enum Transaction { + + INSERT, UPDATE, DELETE, REPLACE, NATIVE; + + @Override + public String toString() { + StringBuilder action = new StringBuilder(this.name().toLowerCase()); + action.setCharAt(0, Character.toUpperCase(action.charAt(0))); + return action.toString(); + } + + } + } diff --git a/src/main/java/org/opengis/cite/iso19142/basic/BasicCapabilitiesTests.java b/src/main/java/org/opengis/cite/iso19142/basic/BasicCapabilitiesTests.java index 190fcc80..e6fed03f 100644 --- a/src/main/java/org/opengis/cite/iso19142/basic/BasicCapabilitiesTests.java +++ b/src/main/java/org/opengis/cite/iso19142/basic/BasicCapabilitiesTests.java @@ -29,80 +29,75 @@ import jakarta.ws.rs.core.Response; import jakarta.xml.soap.SOAPException; - /** - * Tests the service response to a GetCapabilities request for "Basic WFS" - * conformance. + * Tests the service response to a GetCapabilities request for "Basic WFS" conformance. * * @see "ISO 19142:2010, cl. 8: GetCapabilities operation" */ public class BasicCapabilitiesTests extends BaseFixture { - static final String BASIC_WFS_PHASE = "BasicWFSPhase"; - private static final String SCHEMATRON_METADATA = "wfs-capabilities-2.0.sch"; + static final String BASIC_WFS_PHASE = "BasicWFSPhase"; + + private static final String SCHEMATRON_METADATA = "wfs-capabilities-2.0.sch"; + + @BeforeTest + public void checkSuitePreconditions(ITestContext context) { + Object failedPreconditions = context.getSuite().getAttribute(SuiteAttribute.FAILED_PRECONDITIONS.getName()); + if (null != failedPreconditions) { + throw new SkipException("One or more test suite preconditions were not satisfied: " + failedPreconditions); + } + } - @BeforeTest - public void checkSuitePreconditions(ITestContext context) { - Object failedPreconditions = context.getSuite().getAttribute(SuiteAttribute.FAILED_PRECONDITIONS.getName()); - if (null != failedPreconditions) { - throw new SkipException("One or more test suite preconditions were not satisfied: " + failedPreconditions); - } - } + /** + * Run the tests for the "Basic WFS" conformance class only if the service constraint + * {@value org.opengis.cite.iso19142.WFS2#BASIC_WFS} has the value 'TRUE'. Otherwise + * the constituent tests will be skipped. + * @param testContext The test (set) context. + */ + @BeforeTest + public void implementsBasicWFS(ITestContext testContext) { + this.wfsMetadata = (Document) testContext.getSuite().getAttribute(SuiteAttribute.TEST_SUBJECT.getName()); + String xpath = String.format("//ows:Constraint[@name='%s']/ows:DefaultValue = 'TRUE'", WFS2.BASIC_WFS); + ETSAssert.assertXPath(xpath, this.wfsMetadata, null); + } - /** - * Run the tests for the "Basic WFS" conformance class only if the service - * constraint {@value org.opengis.cite.iso19142.WFS2#BASIC_WFS} has the - * value 'TRUE'. Otherwise the constituent tests will be skipped. - * - * @param testContext - * The test (set) context. - */ - @BeforeTest - public void implementsBasicWFS(ITestContext testContext) { - this.wfsMetadata = (Document) testContext.getSuite().getAttribute(SuiteAttribute.TEST_SUBJECT.getName()); - String xpath = String.format("//ows:Constraint[@name='%s']/ows:DefaultValue = 'TRUE'", WFS2.BASIC_WFS); - ETSAssert.assertXPath(xpath, this.wfsMetadata, null); - } + /** + * Builds a DOM Document representing a GetCapabilities request for a complete service + * metadata document. + */ + @BeforeClass + public void buildGetCapabilitiesRequest() { + this.reqEntity = this.docBuilder.newDocument(); + Element docElem = reqEntity.createElementNS(Namespaces.WFS, WFS2.GET_CAPABILITIES); + docElem.setAttribute(WFS2.SERVICE_PARAM, WFS2.SERVICE_TYPE_CODE); + this.reqEntity.appendChild(docElem); + } - /** - * Builds a DOM Document representing a GetCapabilities request for a - * complete service metadata document. - */ - @BeforeClass - public void buildGetCapabilitiesRequest() { - this.reqEntity = this.docBuilder.newDocument(); - Element docElem = reqEntity.createElementNS(Namespaces.WFS, WFS2.GET_CAPABILITIES); - docElem.setAttribute(WFS2.SERVICE_PARAM, WFS2.SERVICE_TYPE_CODE); - this.reqEntity.appendChild(docElem); - } + /** + * Verifies that the content of the service metadata (wfs:WFS_Capabilities) document + * satisfies the requirements for "Basic WFS" conformance. Additional service + * endpoints, service properties (constraints), and filtering options must be + * implemented. The applicable rules are incorporated into the + * {@value #BASIC_WFS_PHASE} phase of the Schematron schema + * {@code wfs-capabilities-2.0.sch}. + * @param binding The ProtocolBinding to use. + * @throws SOAPException If an error occurs while processing a SOAP response message. + * + * @see "ISO 19142:2010, Table 1: Conformance classes" + * @see "ISO 19142:2010, Table 13: Service constraints" + * @see "ISO 19142:2010, cl. A.1.2: Basic WFS" + * @see "ISO 19143:2010, Table 5: Names of conformance class constraints" + */ + @Test(description = "See ISO 19142: Table 1, Table 13, A.1.2", dataProvider = "protocol-binding") + public void describesBasicWFS(ProtocolBinding binding) throws SOAPException { + URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.GET_CAPABILITIES, binding); + Response rsp = wfsClient.submitRequest(new DOMSource(reqEntity), binding, endpoint); + Assert.assertTrue(rsp.hasEntity(), ErrorMessage.get(ErrorMessageKeys.MISSING_XML_ENTITY)); + Document entity = extractBodyAsDocument(rsp); + SchematronValidator validator = ValidationUtils.buildSchematronValidator(SCHEMATRON_METADATA, BASIC_WFS_PHASE); + Result result = validator.validate(new DOMSource(entity, entity.getDocumentURI()), false); + Assert.assertFalse(validator.ruleViolationsDetected(), ErrorMessage.format(ErrorMessageKeys.NOT_SCHEMA_VALID, + validator.getRuleViolationCount(), XMLUtils.resultToString(result))); + } - /** - * Verifies that the content of the service metadata (wfs:WFS_Capabilities) - * document satisfies the requirements for "Basic WFS" conformance. - * Additional service endpoints, service properties (constraints), and - * filtering options must be implemented. The applicable rules are - * incorporated into the {@value #BASIC_WFS_PHASE} phase of the Schematron - * schema {@code wfs-capabilities-2.0.sch}. - * - * @param binding - * The ProtocolBinding to use. - * @throws SOAPException - * If an error occurs while processing a SOAP response message. - * - * @see "ISO 19142:2010, Table 1: Conformance classes" - * @see "ISO 19142:2010, Table 13: Service constraints" - * @see "ISO 19142:2010, cl. A.1.2: Basic WFS" - * @see "ISO 19143:2010, Table 5: Names of conformance class constraints" - */ - @Test(description = "See ISO 19142: Table 1, Table 13, A.1.2", dataProvider = "protocol-binding") - public void describesBasicWFS(ProtocolBinding binding) throws SOAPException { - URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.GET_CAPABILITIES, binding); - Response rsp = wfsClient.submitRequest(new DOMSource(reqEntity), binding, endpoint); - Assert.assertTrue(rsp.hasEntity(), ErrorMessage.get(ErrorMessageKeys.MISSING_XML_ENTITY)); - Document entity = extractBodyAsDocument(rsp); - SchematronValidator validator = ValidationUtils.buildSchematronValidator(SCHEMATRON_METADATA, BASIC_WFS_PHASE); - Result result = validator.validate(new DOMSource(entity, entity.getDocumentURI()), false); - Assert.assertFalse(validator.ruleViolationsDetected(), ErrorMessage.format(ErrorMessageKeys.NOT_SCHEMA_VALID, - validator.getRuleViolationCount(), XMLUtils.resultToString(result))); - } } diff --git a/src/main/java/org/opengis/cite/iso19142/basic/BasicGetFeatureTests.java b/src/main/java/org/opengis/cite/iso19142/basic/BasicGetFeatureTests.java index 17321bdf..1ee899bc 100644 --- a/src/main/java/org/opengis/cite/iso19142/basic/BasicGetFeatureTests.java +++ b/src/main/java/org/opengis/cite/iso19142/basic/BasicGetFeatureTests.java @@ -38,61 +38,55 @@ import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response.Status; - /** - * Tests the response to a GetFeature request that returns a selection of - * features matching specified criteria. The request must include one or more - * stored query (wfs:StoredQuery) or ad hoc query (wfs:Query) expressions. An ad - * hoc query element may contain projection clauses (wfs:PropertyName), a - * selection clause (fes:Filter), or a sorting clause (fes:SortBy). - * + * Tests the response to a GetFeature request that returns a selection of features + * matching specified criteria. The request must include one or more stored query + * (wfs:StoredQuery) or ad hoc query (wfs:Query) expressions. An ad hoc query element may + * contain projection clauses (wfs:PropertyName), a selection clause (fes:Filter), or a + * sorting clause (fes:SortBy). + * * A successful response entity must include a schema reference (using the * xsi:schemaLocation attribute) that is sufficient to validate the response. - * + * * @see "ISO 19142:2010, cl. 11: GetFeature operation" * @see "ISO 19142:2010, cl. 7.8: Use of the schemaLocation attribute" * @see "ISO 19143:2010, Geographic information -- Filter encoding" */ public class BasicGetFeatureTests extends BaseFixture { - private static final QName FEATURE_COLL = new QName(Namespaces.WFS, - WFS2.FEATURE_COLLECTION); + private static final QName FEATURE_COLL = new QName(Namespaces.WFS, WFS2.FEATURE_COLLECTION); + Validator hintsValidator; /** - * Creates a special XML Schema validator that uses schema location hints - * specified in an XML instance document. Beware that this can introduce a - * vulnerability to denial-of-service attacks, even though local copies of - * standard schemas will be used if possible. + * Creates a special XML Schema validator that uses schema location hints specified in + * an XML instance document. Beware that this can introduce a vulnerability to + * denial-of-service attacks, even though local copies of standard schemas will be + * used if possible. */ @BeforeClass public void buildValidator() { - SchemaFactory factory = SchemaFactory - .newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); + SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); try { Schema schema = factory.newSchema(); this.hintsValidator = schema.newValidator(); - LSResourceResolver resolver = ValidationUtils - .createSchemaResolver(Namespaces.XSD); + LSResourceResolver resolver = ValidationUtils.createSchemaResolver(Namespaces.XSD); this.hintsValidator.setResourceResolver(resolver); - } catch (SAXException e) { + } + catch (SAXException e) { // very unlikely to occur with no schema to process - TestSuiteLogger - .log(Level.WARNING, - "Failed to build XML Schema Validator that heeds location hints.", - e); + TestSuiteLogger.log(Level.WARNING, "Failed to build XML Schema Validator that heeds location hints.", e); } } /** - * Builds a DOM Document node representing the entity body for a GetFeature - * request. A minimal XML representation is read from the classpath + * Builds a DOM Document node representing the entity body for a GetFeature request. A + * minimal XML representation is read from the classpath * ("util/GetFeature-Minimal.xml"). */ @BeforeMethod public void buildRequestEntity() { - this.reqEntity = WFSMessage.createRequestEntity("GetFeature-Minimal", - this.wfsVersion); + this.reqEntity = WFSMessage.createRequestEntity("GetFeature-Minimal", this.wfsVersion); this.rspEntity = null; } @@ -105,41 +99,33 @@ public void resetValidator() { } /** - * [{@code Test}] Submits a minimal GetFeature request (without a filter - * predicate) for feature types listed in the WFS the capabilities document. - * The test is run for all supported protocol bindings and feature types. - * The response entity (wfs:FeatureCollection) must be schema-valid and - * contain only instances of the requested type as members. - * - * @param binding - * A supported message binding. - * @param featureType - * A QName representing the qualified name of some feature type. + * [{@code Test}] Submits a minimal GetFeature request (without a filter predicate) + * for feature types listed in the WFS the capabilities document. The test is run for + * all supported protocol bindings and feature types. The response entity + * (wfs:FeatureCollection) must be schema-valid and contain only instances of the + * requested type as members. + * @param binding A supported message binding. + * @param featureType A QName representing the qualified name of some feature type. */ @Test(description = "See ISO 19142: 11.2.2, 11.2.3", dataProvider = "all-protocols-featureTypes") public void getFeaturesByType(ProtocolBinding binding, QName featureType) { WFSMessage.appendSimpleQuery(this.reqEntity, featureType); - URI endpoint = ServiceMetadataUtils.getOperationEndpoint( - this.wfsMetadata, WFS2.GET_FEATURE, binding); - Response rsp = wfsClient.submitRequest(new DOMSource(reqEntity), - binding, endpoint); + URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.GET_FEATURE, binding); + Response rsp = wfsClient.submitRequest(new DOMSource(reqEntity), binding, endpoint); this.rspEntity = extractBodyAsDocument(rsp); - Assert.assertEquals(rsp.getStatus(), - Status.OK.getStatusCode(), + Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - ETSAssert.assertQualifiedName(rspEntity.getDocumentElement(), - FEATURE_COLL); - ETSAssert.assertSchemaValid(this.hintsValidator, new DOMSource( - rspEntity.getDocumentElement(), rspEntity.getDocumentURI())); + ETSAssert.assertQualifiedName(rspEntity.getDocumentElement(), FEATURE_COLL); + ETSAssert.assertSchemaValid(this.hintsValidator, + new DOMSource(rspEntity.getDocumentElement(), rspEntity.getDocumentURI())); } /** - * [{@code Test}] Submits a request for geometry representations in some - * other (non-default) CRS that is supported by the IUT, as indicated by the - * value of the {@literal wfs:Query/@srsName} attribute or srsName query - * parameter. The test is skipped if no alternatives are offered for any - * available feature type. - * + * [{@code Test}] Submits a request for geometry representations in some other + * (non-default) CRS that is supported by the IUT, as indicated by the value of the + * {@literal wfs:Query/@srsName} attribute or srsName query parameter. The test is + * skipped if no alternatives are offered for any available feature type. + * * @see "OGC 09-025r2, 7.9.2.4.4: srsName parameter" */ @Test(description = "See OGC 09-025r2: 7.9.2.4.4") @@ -150,37 +136,32 @@ public void getFeatureInOtherCRS() { do { featureType = itr.next(); FeatureTypeInfo typeInfo = this.featureInfo.get(featureType); - if (typeInfo.isInstantiated() - && typeInfo.getSupportedCRSIdentifiers().size() > 1) { + if (typeInfo.isInstantiated() && typeInfo.getSupportedCRSIdentifiers().size() > 1) { // skip first (default) CRS otherCRSId = typeInfo.getSupportedCRSIdentifiers().get(1); break; } - } while (itr.hasNext()); + } + while (itr.hasNext()); if (null == otherCRSId) { - throw new SkipException( - "No alternative (non-default) CRS supported for any feature type with data."); + throw new SkipException("No alternative (non-default) CRS supported for any feature type with data."); } WFSMessage.appendSimpleQuery(this.reqEntity, featureType); - Element qry = (Element) this.reqEntity.getElementsByTagNameNS( - WFS2.NS_URI, WFS2.QUERY_ELEM).item(0); + Element qry = (Element) this.reqEntity.getElementsByTagNameNS(WFS2.NS_URI, WFS2.QUERY_ELEM).item(0); qry.setAttribute(WFS2.SRSNAME_PARAM, otherCRSId); - Response rsp = wfsClient.submitRequest(this.reqEntity, - ProtocolBinding.ANY); - Assert.assertEquals(rsp.getStatus(), - Status.OK.getStatusCode(), + Response rsp = wfsClient.submitRequest(this.reqEntity, ProtocolBinding.ANY); + Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); this.rspEntity = extractBodyAsDocument(rsp); ETSAssert.assertSpatialReference(this.rspEntity, otherCRSId); } /** - * [{@code Test}] Submits a request for geometry representations in a - * non-existent CRS, as indicated by the value of the - * {@literal wfs:Query/@srsName} attribute or srsName parameter. An - * exception is expected in response, with status code 400 and OGC exception - * code "InvalidParameterValue". - * + * [{@code Test}] Submits a request for geometry representations in a non-existent + * CRS, as indicated by the value of the {@literal wfs:Query/@srsName} attribute or + * srsName parameter. An exception is expected in response, with status code 400 and + * OGC exception code "InvalidParameterValue". + * * @see "OGC 09-025r2, A.2.22.1.4: srsName parameter" */ @Test(description = "See OGC 09-025r2: A.2.22.1.4") @@ -189,43 +170,33 @@ public void getFeatureInUnsupportedCRS() { : "http://www.opengis.net/def/crs/EPSG/0/32690"; QName featureType = getFeatureTypeWithInstanceData(); WFSMessage.appendSimpleQuery(this.reqEntity, featureType); - Element qry = (Element) this.reqEntity.getElementsByTagNameNS( - WFS2.NS_URI, WFS2.QUERY_ELEM).item(0); + Element qry = (Element) this.reqEntity.getElementsByTagNameNS(WFS2.NS_URI, WFS2.QUERY_ELEM).item(0); qry.setAttribute(WFS2.SRSNAME_PARAM, crsId); - Response rsp = wfsClient.submitRequest(this.reqEntity, - ProtocolBinding.ANY); - Assert.assertEquals(rsp.getStatus(), - Status.BAD_REQUEST.getStatusCode(), + Response rsp = wfsClient.submitRequest(this.reqEntity, ProtocolBinding.ANY); + Assert.assertEquals(rsp.getStatus(), Status.BAD_REQUEST.getStatusCode(), ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); this.rspEntity = extractBodyAsDocument(rsp); - ETSAssert.assertExceptionReport(this.rspEntity, - "InvalidParameterValue", "SRSNAME"); + ETSAssert.assertExceptionReport(this.rspEntity, "InvalidParameterValue", "SRSNAME"); } /** - * Sets the availability status of instances of a given feature type - * according to the content of a GetFeature response entity. Availability is - * set to {@code true} if the response entity contains at least one - * instance. - * - * @param featureInfo - * A FeatureTypeInfo object that provides information about a - * feature type. - * @param entity - * A GetFeature response entity (wfs:FeatureCollection). + * Sets the availability status of instances of a given feature type according to the + * content of a GetFeature response entity. Availability is set to {@code true} if the + * response entity contains at least one instance. + * @param featureInfo A FeatureTypeInfo object that provides information about a + * feature type. + * @param entity A GetFeature response entity (wfs:FeatureCollection). */ void setFeatureAvailability(FeatureTypeInfo featureInfo, Document entity) { QName typeName = featureInfo.getTypeName(); - NodeList features = entity.getElementsByTagNameNS( - typeName.getNamespaceURI(), typeName.getLocalPart()); + NodeList features = entity.getElementsByTagNameNS(typeName.getNamespaceURI(), typeName.getLocalPart()); featureInfo.setInstantiated(features.getLength() > 0); } /** * Gets the name of a feature type for which instances exist. - * - * @return The qualified name of a feature type, or null if no - * data are available. + * @return The qualified name of a feature type, or null if no data are + * available. */ QName getFeatureTypeWithInstanceData() { Iterator itr = this.featureTypes.iterator(); @@ -236,7 +207,9 @@ QName getFeatureTypeWithInstanceData() { if (typeInfo.isInstantiated()) { break; } - } while (itr.hasNext()); + } + while (itr.hasNext()); return featureType; } + } diff --git a/src/main/java/org/opengis/cite/iso19142/basic/GetPropertyValueTests.java b/src/main/java/org/opengis/cite/iso19142/basic/GetPropertyValueTests.java index 912a311b..ee7b0aea 100644 --- a/src/main/java/org/opengis/cite/iso19142/basic/GetPropertyValueTests.java +++ b/src/main/java/org/opengis/cite/iso19142/basic/GetPropertyValueTests.java @@ -28,12 +28,11 @@ import jakarta.ws.rs.core.Response; - /** - * Tests the service response to a GetPropertyValue request. The - * GetPropertyValue operation allows the value of a feature property (or part of - * the value of a complex feature property) to be retrieved. - * + * Tests the service response to a GetPropertyValue request. The GetPropertyValue + * operation allows the value of a feature property (or part of the value of a complex + * feature property) to be retrieved. + * * @see "ISO 19142:2010, cl. 10: GetPropertyValue operation" */ public class GetPropertyValueTests extends BaseFixture { @@ -42,94 +41,72 @@ public class GetPropertyValueTests extends BaseFixture { /** * Retrieves the (pre-compiled) WFS schema from the suite fixture. - * - * @param testContext - * The test (group) context. + * @param testContext The test (group) context. */ @BeforeClass public void setupClassFixture(ITestContext testContext) { - this.wfsSchema = (Schema) testContext.getSuite().getAttribute( - SuiteAttribute.WFS_SCHEMA.getName()); - Assert.assertNotNull(this.wfsSchema, - "WFS schema not found in suite fixture."); + this.wfsSchema = (Schema) testContext.getSuite().getAttribute(SuiteAttribute.WFS_SCHEMA.getName()); + Assert.assertNotNull(this.wfsSchema, "WFS schema not found in suite fixture."); } /** - * Builds a DOM Document node representing the entity body for a - * GetPropertyValue request. A minimal XML representation is read from the - * classpath (at util/GetPropertyValue.xml). + * Builds a DOM Document node representing the entity body for a GetPropertyValue + * request. A minimal XML representation is read from the classpath (at + * util/GetPropertyValue.xml). */ @BeforeMethod public void buildRequestEntity() { - this.reqEntity = WFSMessage.createRequestEntity("GetPropertyValue", - this.wfsVersion); + this.reqEntity = WFSMessage.createRequestEntity("GetPropertyValue", this.wfsVersion); } /** * Submits a GetPropertyValue request for a known feature type with - * valueReference="@gml:id". The members in the resulting ValueCollection - * element are expected to contain simple (atomic) text values representing - * the server-assigned object identifiers (xsd:ID). - * - * @param binding - * The ProtocolBinding to use. - * + * valueReference="@gml:id". The members in the resulting ValueCollection element are + * expected to contain simple (atomic) text values representing the server-assigned + * object identifiers (xsd:ID). + * @param binding The ProtocolBinding to use. + * * @see "ISO 19142:2010, cl. 10.2.4.3: GetPropertyValue - valueReference parameter" */ @Test(description = "See ISO 19142: 10.2.4.3", dataProvider = "protocol-binding") public void getProperty_gmlId(ProtocolBinding binding) { setValueReference(reqEntity, "@gml:id"); addQuery(this.reqEntity, this.featureTypes.get(0)); - URI endpoint = ServiceMetadataUtils.getOperationEndpoint( - this.wfsMetadata, WFS2.GET_PROP_VALUE, binding); - Response rsp = wfsClient.submitRequest(new DOMSource(reqEntity), - binding, endpoint); - Assert.assertTrue(rsp.hasEntity(), - ErrorMessage.get(ErrorMessageKeys.MISSING_XML_ENTITY)); + URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.GET_PROP_VALUE, binding); + Response rsp = wfsClient.submitRequest(new DOMSource(reqEntity), binding, endpoint); + Assert.assertTrue(rsp.hasEntity(), ErrorMessage.get(ErrorMessageKeys.MISSING_XML_ENTITY)); Document entity = extractBodyAsDocument(rsp); - ETSAssert.assertQualifiedName(entity.getDocumentElement(), new QName( - Namespaces.WFS, WFS2.VALUE_COLLECTION)); - NodeList members = entity.getElementsByTagNameNS(Namespaces.WFS, - "member"); + ETSAssert.assertQualifiedName(entity.getDocumentElement(), new QName(Namespaces.WFS, WFS2.VALUE_COLLECTION)); + NodeList members = entity.getElementsByTagNameNS(Namespaces.WFS, "member"); for (int i = 0; i < members.getLength(); i++) { String value = members.item(i).getTextContent(); - Assert.assertFalse(value.isEmpty(), "Found empty wfs:member[" + i - + "]"); + Assert.assertFalse(value.isEmpty(), "Found empty wfs:member[" + i + "]"); } } /** - * If the valueReference is an empty string, an ExceptionReport is expected - * to contain the error code "InvalidParameterValue". - * - * @param binding - * The ProtocolBinding to use. - * + * If the valueReference is an empty string, an ExceptionReport is expected to contain + * the error code "InvalidParameterValue". + * @param binding The ProtocolBinding to use. + * * @see "ISO 19142:2010, cl. 10.4: GetPropertyValue - Exceptions" */ @Test(description = "See ISO 19142: 7.5, 10.4", dataProvider = "protocol-binding") public void getProperty_emptyValueRef(ProtocolBinding binding) { setValueReference(reqEntity, ""); addQuery(reqEntity, this.featureTypes.get(0)); - URI endpoint = ServiceMetadataUtils.getOperationEndpoint( - this.wfsMetadata, WFS2.GET_PROP_VALUE, binding); - Response rsp = wfsClient.submitRequest(new DOMSource(reqEntity), - binding, endpoint); - Assert.assertTrue(rsp.hasEntity(), - ErrorMessage.get(ErrorMessageKeys.MISSING_XML_ENTITY)); + URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.GET_PROP_VALUE, binding); + Response rsp = wfsClient.submitRequest(new DOMSource(reqEntity), binding, endpoint); + Assert.assertTrue(rsp.hasEntity(), ErrorMessage.get(ErrorMessageKeys.MISSING_XML_ENTITY)); Document entity = extractBodyAsDocument(rsp); - ETSAssert.assertXPath( - "//ows:Exception/@exceptionCode = 'InvalidParameterValue'", - entity.getDocumentElement(), null); + ETSAssert.assertXPath("//ows:Exception/@exceptionCode = 'InvalidParameterValue'", entity.getDocumentElement(), + null); } /** * Sets the valueReference attribute on the request entity. - * - * @param entity - * The request entity (wfs:GetPropertyValue). - * @param xpath - * A String representing an XPath expression. + * @param entity The request entity (wfs:GetPropertyValue). + * @param xpath A String representing an XPath expression. */ void setValueReference(Document entity, String xpath) { entity.getDocumentElement().setAttribute("valueReference", xpath); @@ -137,11 +114,8 @@ void setValueReference(Document entity, String xpath) { /** * Adds a simple query element to the request entity. - * - * @param request - * The request entity (wfs:GetPropertyValue). - * @param qName - * A QName representing the qualified name of a feature type. + * @param request The request entity (wfs:GetPropertyValue). + * @param qName A QName representing the qualified name of a feature type. */ void addQuery(Document request, QName qName) { Element docElem = request.getDocumentElement(); @@ -149,12 +123,11 @@ void addQuery(Document request, QName qName) { if (null == nsPrefix) { nsPrefix = "ns" + Integer.toString((int) (Math.random() * 100)); } - Element query = request - .createElementNS(Namespaces.WFS, WFS2.QUERY_ELEM); + Element query = request.createElementNS(Namespaces.WFS, WFS2.QUERY_ELEM); query.setPrefix("wfs"); query.setAttribute("typeNames", nsPrefix + ":" + qName.getLocalPart()); docElem.appendChild(query); - docElem.setAttribute(XMLConstants.XMLNS_ATTRIBUTE + ":" + nsPrefix, - qName.getNamespaceURI()); + docElem.setAttribute(XMLConstants.XMLNS_ATTRIBUTE + ":" + nsPrefix, qName.getNamespaceURI()); } + } diff --git a/src/main/java/org/opengis/cite/iso19142/basic/filter/ComparisonOperatorTests.java b/src/main/java/org/opengis/cite/iso19142/basic/filter/ComparisonOperatorTests.java index 9ab84fa0..4b72e00a 100644 --- a/src/main/java/org/opengis/cite/iso19142/basic/filter/ComparisonOperatorTests.java +++ b/src/main/java/org/opengis/cite/iso19142/basic/filter/ComparisonOperatorTests.java @@ -44,10 +44,9 @@ import jakarta.ws.rs.core.Response.Status; import jakarta.xml.bind.DatatypeConverter; - /** - * Tests the response to a GetFeature request that includes a filter predicate - * containing one of the following comparison operators: + * Tests the response to a GetFeature request that includes a filter predicate containing + * one of the following comparison operators: *
      *
    • PropertyIsLessThan
    • *
    • PropertyIsGreaterThan
    • @@ -55,13 +54,13 @@ *
    • PropertyIsGreaterThanOrEqualTo
    • *
    *

    - * These operators compare the value of a simple property against some specified - * value; they can be applied to numeric, temporal, and text data types - * (although lexicographic order may depend on the collation rules). If a - * property has multiple values, the {@code matchAction} parameter affects the - * scope of the comparison ("All", "Any", "One"). + * These operators compare the value of a simple property against some specified value; + * they can be applied to numeric, temporal, and text data types (although lexicographic + * order may depend on the collation rules). If a property has multiple values, the + * {@code matchAction} parameter affects the scope of the comparison ("All", "Any", + * "One"). *

    - * + * *

    * Sources *

    @@ -73,512 +72,476 @@ */ public class ComparisonOperatorTests extends QueryFilterFixture { - private static String MATCH_ALL = "All"; - private static String MATCH_ANY = "Any"; + private static String MATCH_ALL = "All"; + + private static String MATCH_ANY = "Any"; + + /** + * [{@code Test}] Submits a GetFeature request containing a {@code PropertyIsLessThan} + * predicate that applies to some simple (numeric or temporal) feature property. The + * response entity must include only feature instances that satisfy the predicate; if + * multiple values exist, at least one must match (matchAction="Any"). + * @param binding The ProtocolBinding to use for this request. + * @param featureType A QName representing the qualified name of some feature type. + */ + @Test(description = "See ISO 19143: 7.7", dataProvider = "protocol-featureType") + public void propertyIsLessThan_matchAny(ProtocolBinding binding, QName featureType) { + Set dataTypes = getNumericDataTypes(getModel()); + dataTypes.addAll(AppSchemaUtils.getSimpleTemporalDataTypes(getModel())); + Map propRangeMap = findFeaturePropertyValue(getModel(), featureType, dataTypes); + if (propRangeMap.isEmpty()) { + throw new SkipException("No numeric or temporal property values found for " + featureType); + } + Entry propRange = propRangeMap.entrySet().iterator().next(); + String propValue = propRange.getValue()[1]; // use max value + XSElementDeclaration propDecl = propRange.getKey(); + QName propName = new QName(propDecl.getNamespace(), propDecl.getName()); + WFSMessage.appendSimpleQuery(this.reqEntity, featureType); + addComparisonPredicate(this.reqEntity, FES2.LESS_THAN, propName, propValue, true, MATCH_ANY); + Response rsp = wfsClient.submitRequest(reqEntity, binding); + this.rspEntity = extractBodyAsDocument(rsp); + Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + NodeList features = this.rspEntity.getElementsByTagNameNS(featureType.getNamespaceURI(), + featureType.getLocalPart()); + // Add constructor functions for property type (XML Schema datatype) + QName dataType = AppSchemaUtils.getBuiltInDatatype(propDecl); + String propTypeName = dataType.getLocalPart(); + String xpath = String.format("xs:%s(ns1:%s) lt xs:%s('%s')", propTypeName, propName.getLocalPart(), + propTypeName, propValue); + Map nsBindings = new HashMap(); + nsBindings.put(propName.getNamespaceURI(), "ns1"); + for (int i = 0; i < features.getLength(); i++) { + ETSAssert.assertXPath2(xpath, new DOMSource(features.item(i)), nsBindings); + } + } - /** - * [{@code Test}] Submits a GetFeature request containing a - * {@code PropertyIsLessThan} predicate that applies to some simple (numeric - * or temporal) feature property. The response entity must include only - * feature instances that satisfy the predicate; if multiple values exist, - * at least one must match (matchAction="Any"). - * - * @param binding - * The ProtocolBinding to use for this request. - * @param featureType - * A QName representing the qualified name of some feature type. - */ - @Test(description = "See ISO 19143: 7.7", dataProvider = "protocol-featureType") - public void propertyIsLessThan_matchAny(ProtocolBinding binding, QName featureType) { - Set dataTypes = getNumericDataTypes(getModel()); - dataTypes.addAll(AppSchemaUtils.getSimpleTemporalDataTypes(getModel())); - Map propRangeMap = findFeaturePropertyValue(getModel(), featureType, dataTypes); - if (propRangeMap.isEmpty()) { - throw new SkipException("No numeric or temporal property values found for " + featureType); - } - Entry propRange = propRangeMap.entrySet().iterator().next(); - String propValue = propRange.getValue()[1]; // use max value - XSElementDeclaration propDecl = propRange.getKey(); - QName propName = new QName(propDecl.getNamespace(), propDecl.getName()); - WFSMessage.appendSimpleQuery(this.reqEntity, featureType); - addComparisonPredicate(this.reqEntity, FES2.LESS_THAN, propName, propValue, true, MATCH_ANY); - Response rsp = wfsClient.submitRequest(reqEntity, binding); - this.rspEntity = extractBodyAsDocument(rsp); - Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - NodeList features = this.rspEntity.getElementsByTagNameNS(featureType.getNamespaceURI(), - featureType.getLocalPart()); - // Add constructor functions for property type (XML Schema datatype) - QName dataType = AppSchemaUtils.getBuiltInDatatype(propDecl); - String propTypeName = dataType.getLocalPart(); - String xpath = String.format("xs:%s(ns1:%s) lt xs:%s('%s')", propTypeName, propName.getLocalPart(), - propTypeName, propValue); - Map nsBindings = new HashMap(); - nsBindings.put(propName.getNamespaceURI(), "ns1"); - for (int i = 0; i < features.getLength(); i++) { - ETSAssert.assertXPath2(xpath, new DOMSource(features.item(i)), nsBindings); - } - } + /** + * [{@code Test}] Submits a GetFeature request containing a {@code PropertyIsLessThan} + * predicate that applies to some simple (numeric or temporal) feature property. The + * response entity must include only feature instances that satisfy the predicate; if + * multiple values exist, all of them must match (matchAction="All"). + * @param binding The ProtocolBinding to use for this request. + * @param featureType A QName representing the qualified name of some feature type. + */ + @Test(description = "See ISO 19143: 7.7.3.3", dataProvider = "protocol-featureType") + public void propertyIsLessThan_matchAll(ProtocolBinding binding, QName featureType) { + Set dataTypes = getNumericDataTypes(getModel()); + dataTypes.addAll(AppSchemaUtils.getSimpleTemporalDataTypes(getModel())); + Map propRangeMap = findFeaturePropertyValue(getModel(), featureType, dataTypes); + if (propRangeMap.isEmpty()) { + throw new SkipException("No numeric or temporal property values found for " + featureType); + } + Entry propRange = propRangeMap.entrySet().iterator().next(); + String propValue = propRange.getValue()[1]; // use max value + XSElementDeclaration propDecl = propRange.getKey(); + QName propName = new QName(propDecl.getNamespace(), propDecl.getName()); + WFSMessage.appendSimpleQuery(this.reqEntity, featureType); + addComparisonPredicate(this.reqEntity, FES2.LESS_THAN, propName, propValue, true, MATCH_ALL); + Response rsp = wfsClient.submitRequest(reqEntity, binding); + this.rspEntity = extractBodyAsDocument(rsp); + Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + NodeList features = this.rspEntity.getElementsByTagNameNS(featureType.getNamespaceURI(), + featureType.getLocalPart()); + // Add constructor functions for property type (XML Schema datatype) + QName dataType = AppSchemaUtils.getBuiltInDatatype(propDecl); + String propTypeName = dataType.getLocalPart(); + String xpath = String.format("xs:%s(ns1:%s) lt xs:%s('%s')", propTypeName, propName.getLocalPart(), + propTypeName, propValue); + Map nsBindings = new HashMap(); + nsBindings.put(propName.getNamespaceURI(), "ns1"); + for (int i = 0; i < features.getLength(); i++) { + ETSAssert.assertXPath2(xpath, new DOMSource(features.item(i)), nsBindings); + } + } - /** - * [{@code Test}] Submits a GetFeature request containing a - * {@code PropertyIsLessThan} predicate that applies to some simple (numeric - * or temporal) feature property. The response entity must include only - * feature instances that satisfy the predicate; if multiple values exist, - * all of them must match (matchAction="All"). - * - * @param binding - * The ProtocolBinding to use for this request. - * @param featureType - * A QName representing the qualified name of some feature type. - */ - @Test(description = "See ISO 19143: 7.7.3.3", dataProvider = "protocol-featureType") - public void propertyIsLessThan_matchAll(ProtocolBinding binding, QName featureType) { - Set dataTypes = getNumericDataTypes(getModel()); - dataTypes.addAll(AppSchemaUtils.getSimpleTemporalDataTypes(getModel())); - Map propRangeMap = findFeaturePropertyValue(getModel(), featureType, dataTypes); - if (propRangeMap.isEmpty()) { - throw new SkipException("No numeric or temporal property values found for " + featureType); - } - Entry propRange = propRangeMap.entrySet().iterator().next(); - String propValue = propRange.getValue()[1]; // use max value - XSElementDeclaration propDecl = propRange.getKey(); - QName propName = new QName(propDecl.getNamespace(), propDecl.getName()); - WFSMessage.appendSimpleQuery(this.reqEntity, featureType); - addComparisonPredicate(this.reqEntity, FES2.LESS_THAN, propName, propValue, true, MATCH_ALL); - Response rsp = wfsClient.submitRequest(reqEntity, binding); - this.rspEntity = extractBodyAsDocument(rsp); - Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - NodeList features = this.rspEntity.getElementsByTagNameNS(featureType.getNamespaceURI(), - featureType.getLocalPart()); - // Add constructor functions for property type (XML Schema datatype) - QName dataType = AppSchemaUtils.getBuiltInDatatype(propDecl); - String propTypeName = dataType.getLocalPart(); - String xpath = String.format("xs:%s(ns1:%s) lt xs:%s('%s')", propTypeName, propName.getLocalPart(), - propTypeName, propValue); - Map nsBindings = new HashMap(); - nsBindings.put(propName.getNamespaceURI(), "ns1"); - for (int i = 0; i < features.getLength(); i++) { - ETSAssert.assertXPath2(xpath, new DOMSource(features.item(i)), nsBindings); - } - } + /** + * [{@code Test}] Submits a GetFeature request containing a + * {@code PropertyIsGreaterThan} predicate that applies to some simple (numeric or + * temporal) feature property. The response entity must include only feature instances + * that satisfy the predicate; if multiple values exist, at least one must match + * (matchAction="Any"). + * @param binding The ProtocolBinding to use for this request. + * @param featureType A QName representing the qualified name of some feature type. + */ + @Test(description = "See ISO 19143: 7.7.3.1", dataProvider = "protocol-featureType") + public void propertyIsGreaterThan_matchAny(ProtocolBinding binding, QName featureType) { + Set dataTypes = getNumericDataTypes(getModel()); + dataTypes.addAll(AppSchemaUtils.getSimpleTemporalDataTypes(getModel())); + Map propRangeMap = findFeaturePropertyValue(getModel(), featureType, dataTypes); + if (propRangeMap.isEmpty()) { + throw new SkipException("No numeric or temporal property values found for " + featureType); + } + Entry propRange = propRangeMap.entrySet().iterator().next(); + String propValue = propRange.getValue()[0]; // use min value + XSElementDeclaration propDecl = propRange.getKey(); + QName propName = new QName(propDecl.getNamespace(), propDecl.getName()); + WFSMessage.appendSimpleQuery(this.reqEntity, featureType); + addComparisonPredicate(this.reqEntity, FES2.GREATER_THAN, propName, propValue, true, MATCH_ANY); + Response rsp = wfsClient.submitRequest(reqEntity, binding); + this.rspEntity = extractBodyAsDocument(rsp); + Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + NodeList features = this.rspEntity.getElementsByTagNameNS(featureType.getNamespaceURI(), + featureType.getLocalPart()); + // Add constructor functions for property type (XML Schema datatype) + QName dataType = AppSchemaUtils.getBuiltInDatatype(propDecl); + String propTypeName = dataType.getLocalPart(); + String xpath = String.format("xs:%s(ns1:%s) gt xs:%s('%s')", propTypeName, propName.getLocalPart(), + propTypeName, propValue); + Map nsBindings = new HashMap(); + nsBindings.put(propName.getNamespaceURI(), "ns1"); + for (int i = 0; i < features.getLength(); i++) { + ETSAssert.assertXPath2(xpath, new DOMSource(features.item(i)), nsBindings); + } + } - /** - * [{@code Test}] Submits a GetFeature request containing a - * {@code PropertyIsGreaterThan} predicate that applies to some simple - * (numeric or temporal) feature property. The response entity must include - * only feature instances that satisfy the predicate; if multiple values - * exist, at least one must match (matchAction="Any"). - * - * @param binding - * The ProtocolBinding to use for this request. - * @param featureType - * A QName representing the qualified name of some feature type. - */ - @Test(description = "See ISO 19143: 7.7.3.1", dataProvider = "protocol-featureType") - public void propertyIsGreaterThan_matchAny(ProtocolBinding binding, QName featureType) { - Set dataTypes = getNumericDataTypes(getModel()); - dataTypes.addAll(AppSchemaUtils.getSimpleTemporalDataTypes(getModel())); - Map propRangeMap = findFeaturePropertyValue(getModel(), featureType, dataTypes); - if (propRangeMap.isEmpty()) { - throw new SkipException("No numeric or temporal property values found for " + featureType); - } - Entry propRange = propRangeMap.entrySet().iterator().next(); - String propValue = propRange.getValue()[0]; // use min value - XSElementDeclaration propDecl = propRange.getKey(); - QName propName = new QName(propDecl.getNamespace(), propDecl.getName()); - WFSMessage.appendSimpleQuery(this.reqEntity, featureType); - addComparisonPredicate(this.reqEntity, FES2.GREATER_THAN, propName, propValue, true, MATCH_ANY); - Response rsp = wfsClient.submitRequest(reqEntity, binding); - this.rspEntity = extractBodyAsDocument(rsp); - Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - NodeList features = this.rspEntity.getElementsByTagNameNS(featureType.getNamespaceURI(), - featureType.getLocalPart()); - // Add constructor functions for property type (XML Schema datatype) - QName dataType = AppSchemaUtils.getBuiltInDatatype(propDecl); - String propTypeName = dataType.getLocalPart(); - String xpath = String.format("xs:%s(ns1:%s) gt xs:%s('%s')", propTypeName, propName.getLocalPart(), - propTypeName, propValue); - Map nsBindings = new HashMap(); - nsBindings.put(propName.getNamespaceURI(), "ns1"); - for (int i = 0; i < features.getLength(); i++) { - ETSAssert.assertXPath2(xpath, new DOMSource(features.item(i)), nsBindings); - } - } + /** + * [{@code Test}] Submits a GetFeature request containing a + * {@code PropertyIsGreaterThanOrEqualTo} predicate that applies to some simple + * (numeric or temporal) feature property. The response entity must include only + * feature instances that satisfy the predicate; if multiple values exist, at least + * one must match (matchAction="Any"). + * @param binding The ProtocolBinding to use for this request. + * @param featureType A QName representing the qualified name of some feature type. + */ + @Test(description = "See ISO 19143: 7.7.3.1", dataProvider = "protocol-featureType") + public void propertyIsGreaterThanEqualTo_matchAny(ProtocolBinding binding, QName featureType) { + Set dataTypes = getNumericDataTypes(getModel()); + dataTypes.addAll(AppSchemaUtils.getSimpleTemporalDataTypes(getModel())); + Map propRangeMap = findFeaturePropertyValue(getModel(), featureType, dataTypes); + if (propRangeMap.isEmpty()) { + throw new SkipException("No numeric or temporal property values found for " + featureType); + } + Entry propRange = propRangeMap.entrySet().iterator().next(); + String propValue = propRange.getValue()[0]; // use min value + XSElementDeclaration propDecl = propRange.getKey(); + QName propName = new QName(propDecl.getNamespace(), propDecl.getName()); + WFSMessage.appendSimpleQuery(this.reqEntity, featureType); + addComparisonPredicate(this.reqEntity, FES2.GREATER_THAN_OR_EQUAL, propName, propValue, true, MATCH_ANY); + Response rsp = wfsClient.submitRequest(reqEntity, binding); + this.rspEntity = extractBodyAsDocument(rsp); + Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + NodeList features = this.rspEntity.getElementsByTagNameNS(featureType.getNamespaceURI(), + featureType.getLocalPart()); + // Add constructor functions for property type (XML Schema datatype) + QName dataType = AppSchemaUtils.getBuiltInDatatype(propDecl); + String propTypeName = dataType.getLocalPart(); + String xpath = String.format("xs:%s(ns1:%s) ge xs:%s('%s')", propTypeName, propName.getLocalPart(), + propTypeName, propValue); + Map nsBindings = new HashMap(); + nsBindings.put(propName.getNamespaceURI(), "ns1"); + for (int i = 0; i < features.getLength(); i++) { + ETSAssert.assertXPath2(xpath, new DOMSource(features.item(i)), nsBindings); + } + } - /** - * [{@code Test}] Submits a GetFeature request containing a - * {@code PropertyIsGreaterThanOrEqualTo} predicate that applies to some - * simple (numeric or temporal) feature property. The response entity must - * include only feature instances that satisfy the predicate; if multiple - * values exist, at least one must match (matchAction="Any"). - * - * @param binding - * The ProtocolBinding to use for this request. - * @param featureType - * A QName representing the qualified name of some feature type. - */ - @Test(description = "See ISO 19143: 7.7.3.1", dataProvider = "protocol-featureType") - public void propertyIsGreaterThanEqualTo_matchAny(ProtocolBinding binding, QName featureType) { - Set dataTypes = getNumericDataTypes(getModel()); - dataTypes.addAll(AppSchemaUtils.getSimpleTemporalDataTypes(getModel())); - Map propRangeMap = findFeaturePropertyValue(getModel(), featureType, dataTypes); - if (propRangeMap.isEmpty()) { - throw new SkipException("No numeric or temporal property values found for " + featureType); - } - Entry propRange = propRangeMap.entrySet().iterator().next(); - String propValue = propRange.getValue()[0]; // use min value - XSElementDeclaration propDecl = propRange.getKey(); - QName propName = new QName(propDecl.getNamespace(), propDecl.getName()); - WFSMessage.appendSimpleQuery(this.reqEntity, featureType); - addComparisonPredicate(this.reqEntity, FES2.GREATER_THAN_OR_EQUAL, propName, propValue, true, MATCH_ANY); - Response rsp = wfsClient.submitRequest(reqEntity, binding); - this.rspEntity = extractBodyAsDocument(rsp); - Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - NodeList features = this.rspEntity.getElementsByTagNameNS(featureType.getNamespaceURI(), - featureType.getLocalPart()); - // Add constructor functions for property type (XML Schema datatype) - QName dataType = AppSchemaUtils.getBuiltInDatatype(propDecl); - String propTypeName = dataType.getLocalPart(); - String xpath = String.format("xs:%s(ns1:%s) ge xs:%s('%s')", propTypeName, propName.getLocalPart(), - propTypeName, propValue); - Map nsBindings = new HashMap(); - nsBindings.put(propName.getNamespaceURI(), "ns1"); - for (int i = 0; i < features.getLength(); i++) { - ETSAssert.assertXPath2(xpath, new DOMSource(features.item(i)), nsBindings); - } - } + /** + * [{@code Test}] Submits a GetFeature request containing a + * {@code PropertyIsLessThanOrEqualTo} predicate that applies to some simple (numeric + * or temporal) feature property. The response entity must include only feature + * instances that satisfy the predicate; if multiple values exist, at least one must + * match (matchAction="Any"). + * @param binding The ProtocolBinding to use for this request. + * @param featureType A QName representing the qualified name of some feature type. + */ - /** - * [{@code Test}] Submits a GetFeature request containing a - * {@code PropertyIsLessThanOrEqualTo} predicate that applies to some simple - * (numeric or temporal) feature property. The response entity must include - * only feature instances that satisfy the predicate; if multiple values - * exist, at least one must match (matchAction="Any"). - * - * @param binding - * The ProtocolBinding to use for this request. - * @param featureType - * A QName representing the qualified name of some feature type. - */ + @Test(description = "See ISO 19143: 7.7.3.1", dataProvider = "protocol-featureType") + public void propertyIsLessThanEqualTo_matchAny(ProtocolBinding binding, QName featureType) { + Set dataTypes = getNumericDataTypes(getModel()); + dataTypes.addAll(AppSchemaUtils.getSimpleTemporalDataTypes(getModel())); + Map propRangeMap = findFeaturePropertyValue(getModel(), featureType, dataTypes); + if (propRangeMap.isEmpty()) { + throw new SkipException("No numeric or temporal property values found for " + featureType); + } + Entry propRange = propRangeMap.entrySet().iterator().next(); + String propValue = propRange.getValue()[1]; // use max value + XSElementDeclaration propDecl = propRange.getKey(); + QName propName = new QName(propDecl.getNamespace(), propDecl.getName()); + WFSMessage.appendSimpleQuery(this.reqEntity, featureType); + addComparisonPredicate(this.reqEntity, FES2.LESS_THAN_OR_EQUAL, propName, propValue, true, MATCH_ANY); + Response rsp = wfsClient.submitRequest(reqEntity, binding); + this.rspEntity = extractBodyAsDocument(rsp); + Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + NodeList features = this.rspEntity.getElementsByTagNameNS(featureType.getNamespaceURI(), + featureType.getLocalPart()); + // Add constructor functions for property type (XML Schema datatype) + QName dataType = AppSchemaUtils.getBuiltInDatatype(propDecl); + String propTypeName = dataType.getLocalPart(); + String xpath = String.format("xs:%s(ns1:%s) le xs:%s('%s')", propTypeName, propName.getLocalPart(), + propTypeName, propValue); + Map nsBindings = new HashMap(); + nsBindings.put(propName.getNamespaceURI(), "ns1"); + for (int i = 0; i < features.getLength(); i++) { + ETSAssert.assertXPath2(xpath, new DOMSource(features.item(i)), nsBindings); + } + } - @Test(description = "See ISO 19143: 7.7.3.1", dataProvider = "protocol-featureType") - public void propertyIsLessThanEqualTo_matchAny(ProtocolBinding binding, QName featureType) { - Set dataTypes = getNumericDataTypes(getModel()); - dataTypes.addAll(AppSchemaUtils.getSimpleTemporalDataTypes(getModel())); - Map propRangeMap = findFeaturePropertyValue(getModel(), featureType, dataTypes); - if (propRangeMap.isEmpty()) { - throw new SkipException("No numeric or temporal property values found for " + featureType); - } - Entry propRange = propRangeMap.entrySet().iterator().next(); - String propValue = propRange.getValue()[1]; // use max value - XSElementDeclaration propDecl = propRange.getKey(); - QName propName = new QName(propDecl.getNamespace(), propDecl.getName()); - WFSMessage.appendSimpleQuery(this.reqEntity, featureType); - addComparisonPredicate(this.reqEntity, FES2.LESS_THAN_OR_EQUAL, propName, propValue, true, MATCH_ANY); - Response rsp = wfsClient.submitRequest(reqEntity, binding); - this.rspEntity = extractBodyAsDocument(rsp); - Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - NodeList features = this.rspEntity.getElementsByTagNameNS(featureType.getNamespaceURI(), - featureType.getLocalPart()); - // Add constructor functions for property type (XML Schema datatype) - QName dataType = AppSchemaUtils.getBuiltInDatatype(propDecl); - String propTypeName = dataType.getLocalPart(); - String xpath = String.format("xs:%s(ns1:%s) le xs:%s('%s')", propTypeName, propName.getLocalPart(), - propTypeName, propValue); - Map nsBindings = new HashMap(); - nsBindings.put(propName.getNamespaceURI(), "ns1"); - for (int i = 0; i < features.getLength(); i++) { - ETSAssert.assertXPath2(xpath, new DOMSource(features.item(i)), nsBindings); - } - } + /** + * [{@code Test}] Submits a GetFeature request containing a comparison filter + * predicate that refers to an invalid feature property. An exception report is + * expected in response with status code 400 and exception code + * {@code InvalidParameterValue}. + * @param binding The ProtocolBinding to use for this request. + * + * @see "ISO 19143:2010, cl. 8.3: Exceptions" + */ + @Test(description = "See ISO 19143: 8.3", dataProvider = "protocol-binding") + public void invalidPropertyReference(ProtocolBinding binding) { + QName propName = new QName("http://example.org", "undefined", "ex"); + // randomly select a feature type + Random rnd = new Random(); + int index = rnd.nextInt(this.featureTypes.size()); + WFSMessage.appendSimpleQuery(this.reqEntity, this.featureTypes.get(index)); + addComparisonPredicate(this.reqEntity, FES2.LESS_THAN_OR_EQUAL, propName, "1355941270", true, MATCH_ANY); + Response rsp = wfsClient.submitRequest(reqEntity, binding); + this.rspEntity = rsp.readEntity(Document.class); + Assert.assertEquals(rsp.getStatus(), Status.BAD_REQUEST.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + String xpath = "//ows:Exception[@exceptionCode='InvalidParameterValue']"; + ETSAssert.assertXPath(xpath, this.rspEntity, null); + } - /** - * [{@code Test}] Submits a GetFeature request containing a comparison - * filter predicate that refers to an invalid feature property. An exception - * report is expected in response with status code 400 and exception code - * {@code InvalidParameterValue}. - * - * @param binding - * The ProtocolBinding to use for this request. - * - * @see "ISO 19143:2010, cl. 8.3: Exceptions" - */ - @Test(description = "See ISO 19143: 8.3", dataProvider = "protocol-binding") - public void invalidPropertyReference(ProtocolBinding binding) { - QName propName = new QName("http://example.org", "undefined", "ex"); - // randomly select a feature type - Random rnd = new Random(); - int index = rnd.nextInt(this.featureTypes.size()); - WFSMessage.appendSimpleQuery(this.reqEntity, this.featureTypes.get(index)); - addComparisonPredicate(this.reqEntity, FES2.LESS_THAN_OR_EQUAL, propName, "1355941270", true, MATCH_ANY); - Response rsp = wfsClient.submitRequest(reqEntity, binding); - this.rspEntity = rsp.readEntity(Document.class); - Assert.assertEquals(rsp.getStatus(), Status.BAD_REQUEST.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - String xpath = "//ows:Exception[@exceptionCode='InvalidParameterValue']"; - ETSAssert.assertXPath(xpath, this.rspEntity, null); - } + /** + * [{@code Test}] Submits a GetFeature request containing a comparison filter + * predicate that refers to the complex feature property gml:boundedBy (with + * fes:Literal/gml:Envelope as the literal operand). + * + * An exception report is expected in response with exception code + * {@code OperationProcessingFailed} and status code 400 or 403. + * + *

    + * Sources + *

    + *
      + *
    • ISO 19142:2010, cl. 11.4: Exceptions
    • + *
    • ISO 19142:2010, Table 3 - WFS exception codes
    • + *
    + * @param binding The ProtocolBinding to use for this request. + */ + @Test(description = "See ISO 19142: 7.5, 11.4", dataProvider = "protocol-binding") + public void invalidOperand_boundedBy(ProtocolBinding binding) { + QName propName = new QName(Namespaces.GML, "boundedBy", "gml"); + // randomly select a feature type + Random rnd = new Random(); + int index = rnd.nextInt(this.featureTypes.size()); + WFSMessage.appendSimpleQuery(this.reqEntity, this.featureTypes.get(index)); + Document gmlEnv = WFSMessage.createGMLEnvelope(); + addComparisonPredicate(this.reqEntity, FES2.LESS_THAN_OR_EQUAL, propName, gmlEnv, true, MATCH_ANY); + Response rsp = wfsClient.submitRequest(reqEntity, binding); + this.rspEntity = rsp.readEntity(Document.class); + ETSAssert.assertStatusCode(rsp.getStatus(), new int[] { 500, 400, 403 }); + String xpath = "//ows:Exception[@exceptionCode='OperationProcessingFailed']"; + ETSAssert.assertXPath(xpath, this.rspEntity, null); + } - /** - * [{@code Test}] Submits a GetFeature request containing a comparison - * filter predicate that refers to the complex feature property - * gml:boundedBy (with fes:Literal/gml:Envelope as the literal operand). - * - * An exception report is expected in response with exception code - * {@code OperationProcessingFailed} and status code 400 or 403. - * - *

    - * Sources - *

    - *
      - *
    • ISO 19142:2010, cl. 11.4: Exceptions
    • - *
    • ISO 19142:2010, Table 3 - WFS exception codes
    • - *
    - * - * @param binding - * The ProtocolBinding to use for this request. - */ - @Test(description = "See ISO 19142: 7.5, 11.4", dataProvider = "protocol-binding") - public void invalidOperand_boundedBy(ProtocolBinding binding) { - QName propName = new QName(Namespaces.GML, "boundedBy", "gml"); - // randomly select a feature type - Random rnd = new Random(); - int index = rnd.nextInt(this.featureTypes.size()); - WFSMessage.appendSimpleQuery(this.reqEntity, this.featureTypes.get(index)); - Document gmlEnv = WFSMessage.createGMLEnvelope(); - addComparisonPredicate(this.reqEntity, FES2.LESS_THAN_OR_EQUAL, propName, gmlEnv, true, MATCH_ANY); - Response rsp = wfsClient.submitRequest(reqEntity, binding); - this.rspEntity = rsp.readEntity(Document.class); - ETSAssert.assertStatusCode(rsp.getStatus(), new int[] { 500, 400, 403 }); - String xpath = "//ows:Exception[@exceptionCode='OperationProcessingFailed']"; - ETSAssert.assertXPath(xpath, this.rspEntity, null); - } + /** + * Adds a comparison predicate to a GetFeature request entity with the given property + * name and literal value. The predicate is structured as shown in the listing below. + * + *
    +	 * {@code
    +	 * 
    +	 *   
    +	 *     value
    +	 *     tns:featureProperty
    +	 *   
    +	 * 
    +	 * }
    +	 * 
    + * @param request The request entity (/wfs:GetFeature). + * @param operator The name of the comparison operator. + * @param propertyName A QName that specifies the feature property to check. + * @param literalValue The literal object to compare the property value with; it must + * be a String or a DOM Document (in which case the document element is used to + * represent a complex literal). + * @param matchCase A boolean value indicating whether or not the comparison should be + * case-sensitive. + * @param matchAction A String specifying how the predicate should be applied to a + * multi-valued property; the default value is "Any". + */ + void addComparisonPredicate(Document request, String operator, QName propertyName, Object literalValue, + boolean matchCase, String matchAction) { + if (!request.getDocumentElement().getLocalName().equals(WFS2.GET_FEATURE)) { + throw new IllegalArgumentException( + "Not a GetFeature request: " + request.getDocumentElement().getNodeName()); + } + if (null == propertyName) { + throw new IllegalArgumentException("propertyName is required."); + } + Element queryElem = (Element) request.getElementsByTagNameNS(Namespaces.WFS, WFS2.QUERY_ELEM).item(0); + Element filter = request.createElementNS(Namespaces.FES, "Filter"); + queryElem.appendChild(filter); + Element predicate = request.createElementNS(Namespaces.FES, operator); + filter.appendChild(predicate); + String matchActionAttr = (null != matchAction && !matchAction.isEmpty()) ? matchAction : MATCH_ANY; + predicate.setAttribute("matchCase", Boolean.toString(matchCase)); + predicate.setAttribute("matchAction", matchActionAttr); + Element literalElem = request.createElementNS(Namespaces.FES, "Literal"); + if (String.class.isInstance(literalValue)) { + literalElem.setTextContent((String) literalValue); + } + else { + Document literalDoc = (Document) literalValue; + literalElem.appendChild(request.adoptNode(literalDoc.getDocumentElement())); + } + predicate.appendChild(literalElem); + Element valueRef = request.createElementNS(Namespaces.FES, "ValueReference"); + predicate.appendChild(valueRef); + String prefix = (propertyName.getPrefix().length() > 0) ? propertyName.getPrefix() : TNS_PREFIX; + String nsURI = request.lookupNamespaceURI(prefix); + if (null == nsURI) { + valueRef.setAttribute(XMLConstants.XMLNS_ATTRIBUTE + ":" + prefix, propertyName.getNamespaceURI()); + } + valueRef.setTextContent(prefix + ":" + propertyName.getLocalPart()); + } - /** - * Adds a comparison predicate to a GetFeature request entity with the given - * property name and literal value. The predicate is structured as shown in - * the listing below. - * - *
    -     * {@code
    -     * 
    -     *   
    -     *     value
    -     *     tns:featureProperty
    -     *   
    -     * 
    -     * }
    -     * 
    - * - * @param request - * The request entity (/wfs:GetFeature). - * @param operator - * The name of the comparison operator. - * @param propertyName - * A QName that specifies the feature property to check. - * @param literalValue - * The literal object to compare the property value with; it must - * be a String or a DOM Document (in which case the document - * element is used to represent a complex literal). - * @param matchCase - * A boolean value indicating whether or not the comparison - * should be case-sensitive. - * @param matchAction - * A String specifying how the predicate should be applied to a - * multi-valued property; the default value is "Any". - */ - void addComparisonPredicate(Document request, String operator, QName propertyName, Object literalValue, - boolean matchCase, String matchAction) { - if (!request.getDocumentElement().getLocalName().equals(WFS2.GET_FEATURE)) { - throw new IllegalArgumentException( - "Not a GetFeature request: " + request.getDocumentElement().getNodeName()); - } - if (null == propertyName) { - throw new IllegalArgumentException("propertyName is required."); - } - Element queryElem = (Element) request.getElementsByTagNameNS(Namespaces.WFS, WFS2.QUERY_ELEM).item(0); - Element filter = request.createElementNS(Namespaces.FES, "Filter"); - queryElem.appendChild(filter); - Element predicate = request.createElementNS(Namespaces.FES, operator); - filter.appendChild(predicate); - String matchActionAttr = (null != matchAction && !matchAction.isEmpty()) ? matchAction : MATCH_ANY; - predicate.setAttribute("matchCase", Boolean.toString(matchCase)); - predicate.setAttribute("matchAction", matchActionAttr); - Element literalElem = request.createElementNS(Namespaces.FES, "Literal"); - if (String.class.isInstance(literalValue)) { - literalElem.setTextContent((String) literalValue); - } else { - Document literalDoc = (Document) literalValue; - literalElem.appendChild(request.adoptNode(literalDoc.getDocumentElement())); - } - predicate.appendChild(literalElem); - Element valueRef = request.createElementNS(Namespaces.FES, "ValueReference"); - predicate.appendChild(valueRef); - String prefix = (propertyName.getPrefix().length() > 0) ? propertyName.getPrefix() : TNS_PREFIX; - String nsURI = request.lookupNamespaceURI(prefix); - if (null == nsURI) { - valueRef.setAttribute(XMLConstants.XMLNS_ATTRIBUTE + ":" + prefix, propertyName.getNamespaceURI()); - } - valueRef.setTextContent(prefix + ":" + propertyName.getLocalPart()); - } + /** + * Inspects sample data retrieved from the SUT and determines the range of simple + * property values for the specified feature type. The method finds the first feature + * property that (a) conforms to one of the given type definitions, and (b) has at + * least one value in the data sample. + * @param model An XSModel object representing an application schema. + * @param featureType The qualified name of some feature type. + * @param dataTypes A Set of simple data types that possess an interval or ratio scale + * of measurement (e.g. numeric or temporal data types). + * @return A Map containing a single entry where the key is an element declaration and + * the value is a {@code String[]} array containing two String objects representing + * the minimum and maximum values of the property. + */ + Map findFeaturePropertyValue(XSModel model, QName featureType, + Set dataTypes) { + List featureProps = null; + // look for properties of the given data types + for (XSTypeDefinition dataType : dataTypes) { + featureProps = AppSchemaUtils.getFeaturePropertiesByType(model, featureType, dataType); + if (!featureProps.isEmpty()) { + break; + } + } + ListIterator listItr = featureProps.listIterator(featureProps.size()); + XSElementDeclaration prop = null; + String[] valueRange = null; + // start with application-specific properties at end of list + while (listItr.hasPrevious()) { + prop = listItr.previous(); + QName propName = new QName(prop.getNamespace(), prop.getName()); + List valueList = this.dataSampler.getSimplePropertyValues(featureType, propName, null); + if (!valueList.isEmpty()) { + String[] values = new String[valueList.size()]; + for (int i = 0; i < values.length; i++) { + values[i] = valueList.get(i); + } + // use actual datatype to produce valid string representation + QName datatype = AppSchemaUtils.getBuiltInDatatype(prop); + valueRange = calculateRange(values, datatype); + TestSuiteLogger.log(Level.FINE, String.format("Found property values of %s, %s \n %s", featureType, + propName, Arrays.toString(valueRange))); + break; + } + } + Map map = new HashMap(); + if (null != valueRange) { + map.put(prop, valueRange); + } + return map; + } - /** - * Inspects sample data retrieved from the SUT and determines the range of - * simple property values for the specified feature type. The method finds - * the first feature property that (a) conforms to one of the given type - * definitions, and (b) has at least one value in the data sample. - * - * @param model - * An XSModel object representing an application schema. - * @param featureType - * The qualified name of some feature type. - * @param dataTypes - * A Set of simple data types that possess an interval or ratio - * scale of measurement (e.g. numeric or temporal data types). - * @return A Map containing a single entry where the key is an element - * declaration and the value is a {@code String[]} array containing - * two String objects representing the minimum and maximum values of - * the property. - */ - Map findFeaturePropertyValue(XSModel model, QName featureType, - Set dataTypes) { - List featureProps = null; - // look for properties of the given data types - for (XSTypeDefinition dataType : dataTypes) { - featureProps = AppSchemaUtils.getFeaturePropertiesByType(model, featureType, dataType); - if (!featureProps.isEmpty()) { - break; - } - } - ListIterator listItr = featureProps.listIterator(featureProps.size()); - XSElementDeclaration prop = null; - String[] valueRange = null; - // start with application-specific properties at end of list - while (listItr.hasPrevious()) { - prop = listItr.previous(); - QName propName = new QName(prop.getNamespace(), prop.getName()); - List valueList = this.dataSampler.getSimplePropertyValues(featureType, propName, null); - if (!valueList.isEmpty()) { - String[] values = new String[valueList.size()]; - for (int i = 0; i < values.length; i++) { - values[i] = valueList.get(i); - } - // use actual datatype to produce valid string representation - QName datatype = AppSchemaUtils.getBuiltInDatatype(prop); - valueRange = calculateRange(values, datatype); - TestSuiteLogger.log(Level.FINE, String.format("Found property values of %s, %s \n %s", featureType, - propName, Arrays.toString(valueRange))); - break; - } - } - Map map = new HashMap(); - if (null != valueRange) { - map.put(prop, valueRange); - } - return map; - } + /** + * Calculates the range of the given values and returns the minimum and maximum values + * as valid string literals. + * @param values An array of strings representing numeric or temporal values. + * @param datatype The name of the built-in XML Schema datatype to which the values + * must conform. + * @return An array containing the (min, max) values. + */ + String[] calculateRange(String[] values, QName datatype) { + sortValues(values); + Set integerDatatypes = new HashSet(); + // WARNING: some subtypes omitted + Collections.addAll(integerDatatypes, + new String[] { "integer", "nonPositiveInteger", "nonNegativeInteger", "long", "int" }); + if (integerDatatypes.contains(datatype.getLocalPart())) { + for (int i = 0; i < values.length; i++) { + int intValue = Double.valueOf(values[i]).intValue(); + values[i] = Integer.toString(intValue); + } + } + return new String[] { values[0], values[values.length - 1] }; + } - /** - * Calculates the range of the given values and returns the minimum and - * maximum values as valid string literals. - * - * @param values - * An array of strings representing numeric or temporal values. - * @param datatype - * The name of the built-in XML Schema datatype to which the - * values must conform. - * @return An array containing the (min, max) values. - */ - String[] calculateRange(String[] values, QName datatype) { - sortValues(values); - Set integerDatatypes = new HashSet(); - // WARNING: some subtypes omitted - Collections.addAll(integerDatatypes, - new String[] { "integer", "nonPositiveInteger", "nonNegativeInteger", "long", "int" }); - if (integerDatatypes.contains(datatype.getLocalPart())) { - for (int i = 0; i < values.length; i++) { - int intValue = Double.valueOf(values[i]).intValue(); - values[i] = Integer.toString(intValue); - } - } - return new String[] { values[0], values[values.length - 1] }; - } + /** + * Sorts the given array into ascending order, assuming its elements represent either + * numeric (Double) or temporal (Calendar) values. Temporal values are expressed in + * UTC. The corresponding built-in datatypes from XML Schema are: + * + *
      + *
    • xsd:decimal (including xsd:integer and its subtypes)
    • + *
    • xsd:double
    • + *
    • xsd:float
    • + *
    • xsd:dateTime
    • + *
    • xsd:date
    • + *
    + * @param values An array containing String representations of numeric or temporal + * values. + */ + void sortValues(String[] values) { + if ((null == values) || values.length == 0) { + return; + } + Object[] objValues = null; + try { + objValues = new Double[values.length]; + for (int i = 0; i < objValues.length; i++) { + objValues[i] = Double.valueOf(values[i]); + } + Arrays.sort(objValues); + } + catch (NumberFormatException nfe) { + // use (Gregorian)Calendar to sort temporal values + objValues = new Calendar[values.length]; + for (int i = 0; i < objValues.length; i++) { + if (values[i].indexOf('T') > 0) { + objValues[i] = DatatypeConverter.parseDateTime(values[i]); + } + else { + objValues[i] = DatatypeConverter.parseDate(values[i]); + } + } + Arrays.sort(objValues); + } + // Earlier we using ISO_OFFSET_DATE_TIME but offset is appended with seconds due + // to that test is getting failed. + // So we have added a custom datetime formatter pattern(yyyy-MM-dd'T'HH:mm:ssXXX) + // which will ignore seconds from offset. + DateTimeFormatter tmFormatter = (values[0].indexOf('T') > 0) + ? DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX") : DateTimeFormatter.ISO_DATE; + for (int i = 0; i < values.length; i++) { + if (GregorianCalendar.class.isInstance(objValues[i])) { + GregorianCalendar cal = (GregorianCalendar) objValues[i]; + values[i] = tmFormatter.format(cal.toZonedDateTime()); + } + else { + values[i] = DatatypeConverter.printDecimal(new BigDecimal(objValues[i].toString())); + } + } + } - /** - * Sorts the given array into ascending order, assuming its elements - * represent either numeric (Double) or temporal (Calendar) values. Temporal - * values are expressed in UTC. The corresponding built-in datatypes from - * XML Schema are: - * - *
      - *
    • xsd:decimal (including xsd:integer and its subtypes)
    • - *
    • xsd:double
    • - *
    • xsd:float
    • - *
    • xsd:dateTime
    • - *
    • xsd:date
    • - *
    - * - * @param values - * An array containing String representations of numeric or - * temporal values. - */ - void sortValues(String[] values) { - if ((null == values) || values.length == 0) { - return; - } - Object[] objValues = null; - try { - objValues = new Double[values.length]; - for (int i = 0; i < objValues.length; i++) { - objValues[i] = Double.valueOf(values[i]); - } - Arrays.sort(objValues); - } catch (NumberFormatException nfe) { - // use (Gregorian)Calendar to sort temporal values - objValues = new Calendar[values.length]; - for (int i = 0; i < objValues.length; i++) { - if (values[i].indexOf('T') > 0) { - objValues[i] = DatatypeConverter.parseDateTime(values[i]); - } else { - objValues[i] = DatatypeConverter.parseDate(values[i]); - } - } - Arrays.sort(objValues); - } - // Earlier we using ISO_OFFSET_DATE_TIME but offset is appended with seconds due to that test is getting failed. - // So we have added a custom datetime formatter pattern(yyyy-MM-dd'T'HH:mm:ssXXX) which will ignore seconds from offset. - DateTimeFormatter tmFormatter = (values[0].indexOf('T') > 0) ? DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX") - : DateTimeFormatter.ISO_DATE; - for (int i = 0; i < values.length; i++) { - if (GregorianCalendar.class.isInstance(objValues[i])) { - GregorianCalendar cal = (GregorianCalendar) objValues[i]; - values[i] = tmFormatter.format(cal.toZonedDateTime()); - } else { - values[i] = DatatypeConverter.printDecimal(new BigDecimal(objValues[i].toString())); - } - } - } + /** + * Returns a set of primitive numeric data type definitions (xsd:decimal, xsd:double, + * xsd:float). Derived data types are also implicitly included (e.g. xsd:integer). + * @param model An XSModel object representing an application schema. + * @return A Set of simple type definitions corresponding to numeric data types. + */ + Set getNumericDataTypes(XSModel model) { + Set dataTypes = new HashSet(); + dataTypes.add(model.getTypeDefinition("decimal", XMLConstants.W3C_XML_SCHEMA_NS_URI)); + dataTypes.add(model.getTypeDefinition("double", XMLConstants.W3C_XML_SCHEMA_NS_URI)); + dataTypes.add(model.getTypeDefinition("float", XMLConstants.W3C_XML_SCHEMA_NS_URI)); + return dataTypes; + } - /** - * Returns a set of primitive numeric data type definitions (xsd:decimal, - * xsd:double, xsd:float). Derived data types are also implicitly included - * (e.g. xsd:integer). - * - * @param model - * An XSModel object representing an application schema. - * @return A Set of simple type definitions corresponding to numeric data - * types. - */ - Set getNumericDataTypes(XSModel model) { - Set dataTypes = new HashSet(); - dataTypes.add(model.getTypeDefinition("decimal", XMLConstants.W3C_XML_SCHEMA_NS_URI)); - dataTypes.add(model.getTypeDefinition("double", XMLConstants.W3C_XML_SCHEMA_NS_URI)); - dataTypes.add(model.getTypeDefinition("float", XMLConstants.W3C_XML_SCHEMA_NS_URI)); - return dataTypes; - } } diff --git a/src/main/java/org/opengis/cite/iso19142/basic/filter/PropertyIsEqualToOperatorTests.java b/src/main/java/org/opengis/cite/iso19142/basic/filter/PropertyIsEqualToOperatorTests.java index 170ebce9..3d267e60 100644 --- a/src/main/java/org/opengis/cite/iso19142/basic/filter/PropertyIsEqualToOperatorTests.java +++ b/src/main/java/org/opengis/cite/iso19142/basic/filter/PropertyIsEqualToOperatorTests.java @@ -30,16 +30,16 @@ import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response.Status; - /** * Tests the response to a GetFeature request that includes a - * {@code PropertyIs[Not]EqualTo} filter that compares the value of a property - * against some specified value. The comparison may or may not be done in a - * case-sensitive manner. If a property has multiple values, the - * {@code matchAction} parameter affects the scope of the comparison ("All", - * "Any", "One"). - * - *

    Sources

    + * {@code PropertyIs[Not]EqualTo} filter that compares the value of a property against + * some specified value. The comparison may or may not be done in a case-sensitive manner. + * If a property has multiple values, the {@code matchAction} parameter affects the scope + * of the comparison ("All", "Any", "One"). + * + *

    + * Sources + *

    *
      *
    • ISO 19142:2010, cl. A.1.2: Basic WFS
    • *
    • ISO 19143:2010, cl. 7.7: Comparison operators
    • @@ -48,220 +48,179 @@ */ public class PropertyIsEqualToOperatorTests extends QueryFilterFixture { - private static String MATCH_ALL = "All"; - private static String MATCH_ANY = "Any"; + private static String MATCH_ALL = "All"; + + private static String MATCH_ANY = "Any"; + + /** + * [{@code Test}] Submits a GetFeature request containing a {@code PropertyIsEqualTo} + * predicate that applies to some simple feature property. The response entity must + * include only feature instances with matching (case-sensitive) property values; if + * multiple values exist, at least one must match (matchAction="Any"). + * @param binding The ProtocolBinding to use for this request. + * @param featureType A QName representing the qualified name of some feature type. + */ + @Test(description = "See ISO 19143: 7.7.3.2", dataProvider = "protocol-featureType") + public void propertyIsEqualTo_caseSensitive(ProtocolBinding binding, QName featureType) { + WFSMessage.appendSimpleQuery(this.reqEntity, featureType); + Map propValueMap = findMatchingPropertyValue(featureType); + if (propValueMap.isEmpty()) { + throw new SkipException("No simple property values found for feature type " + featureType); + } + Entry propValue = propValueMap.entrySet().iterator().next(); + XSElementDeclaration propDecl = propValue.getKey(); + QName propName = new QName(propDecl.getNamespace(), propDecl.getName()); + addPropertyIsEqualToPredicate(this.reqEntity, propName, propValue.getValue(), true, null, false); + Response rsp = wfsClient.submitRequest(reqEntity, binding); + this.rspEntity = extractBodyAsDocument(rsp); + Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + NodeList features = this.rspEntity.getElementsByTagNameNS(featureType.getNamespaceURI(), + featureType.getLocalPart()); + // Add constructor functions for property type (XML Schema datatype) + QName dataType = AppSchemaUtils.getBuiltInDatatype(propDecl); + String propTypeName = dataType.getLocalPart(); + String xpath = String.format("xs:%s(ns1:%s[1]) = xs:%s('%s')", propTypeName, propName.getLocalPart(), + propTypeName, propValue.getValue()); + Map nsBindings = new HashMap(); + nsBindings.put(propName.getNamespaceURI(), "ns1"); + for (int i = 0; i < features.getLength(); i++) { + ETSAssert.assertXPath2(xpath, new DOMSource(features.item(i)), nsBindings); + } + } - /** - * [{@code Test}] Submits a GetFeature request containing a - * {@code PropertyIsEqualTo} predicate that applies to some simple feature - * property. The response entity must include only feature instances with - * matching (case-sensitive) property values; if multiple values exist, at - * least one must match (matchAction="Any"). - * - * @param binding - * The ProtocolBinding to use for this request. - * @param featureType - * A QName representing the qualified name of some feature type. - */ - @Test(description = "See ISO 19143: 7.7.3.2", dataProvider = "protocol-featureType") - public void propertyIsEqualTo_caseSensitive(ProtocolBinding binding, - QName featureType) { - WFSMessage.appendSimpleQuery(this.reqEntity, featureType); - Map propValueMap = findMatchingPropertyValue(featureType); - if (propValueMap.isEmpty()) { - throw new SkipException( - "No simple property values found for feature type " - + featureType); - } - Entry propValue = propValueMap.entrySet() - .iterator().next(); - XSElementDeclaration propDecl = propValue.getKey(); - QName propName = new QName(propDecl.getNamespace(), propDecl.getName()); - addPropertyIsEqualToPredicate(this.reqEntity, propName, - propValue.getValue(), true, null, false); - Response rsp = wfsClient.submitRequest(reqEntity, binding); - this.rspEntity = extractBodyAsDocument(rsp); - Assert.assertEquals(rsp.getStatus(), - Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - NodeList features = this.rspEntity.getElementsByTagNameNS( - featureType.getNamespaceURI(), featureType.getLocalPart()); - // Add constructor functions for property type (XML Schema datatype) - QName dataType = AppSchemaUtils.getBuiltInDatatype(propDecl); - String propTypeName = dataType.getLocalPart(); - String xpath = String.format("xs:%s(ns1:%s[1]) = xs:%s('%s')", - propTypeName, propName.getLocalPart(), propTypeName, - propValue.getValue()); - Map nsBindings = new HashMap(); - nsBindings.put(propName.getNamespaceURI(), "ns1"); - for (int i = 0; i < features.getLength(); i++) { - ETSAssert.assertXPath2(xpath, new DOMSource(features.item(i)), - nsBindings); - } - } + /** + * [{@code Test}] Submits a GetFeature request containing a + * {@code PropertyIsNotEqualTo} predicate that applies to some simple feature + * property. The response entity must not include any features with a matching + * (case-sensitive) property value; if multiple values exist, all must satisfy the + * predicate (matchAction="All"). + * @param binding The ProtocolBinding to use for this request. + * @param featureType A QName representing the qualified name of some feature type. + */ + @Test(description = "See ISO 19143: 7.7.3.2", dataProvider = "protocol-featureType") + public void propertyIsNotEqualTo_caseSensitive(ProtocolBinding binding, QName featureType) { + WFSMessage.appendSimpleQuery(this.reqEntity, featureType); + Map propValueMap = findMatchingPropertyValue(featureType); + if (propValueMap.isEmpty()) { + throw new SkipException("No simple property values found for feature type " + featureType); + } + Entry propValue = propValueMap.entrySet().iterator().next(); + XSElementDeclaration propDecl = propValue.getKey(); + QName propName = new QName(propDecl.getNamespace(), propDecl.getName()); + addPropertyIsEqualToPredicate(this.reqEntity, propName, propValue.getValue(), true, MATCH_ALL, true); + Response rsp = wfsClient.submitRequest(reqEntity, binding); + this.rspEntity = extractBodyAsDocument(rsp); + Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + NodeList features = this.rspEntity.getElementsByTagNameNS(featureType.getNamespaceURI(), + featureType.getLocalPart()); + // Add constructor functions for property type (XML Schema datatype) + QName dataType = AppSchemaUtils.getBuiltInDatatype(propDecl); + String propTypeName = dataType.getLocalPart(); + // expect no matches even if multiple values exist + String xpath = String.format("not(xs:%s(ns1:%s[1]) = xs:%s('%s'))", propTypeName, propName.getLocalPart(), + propTypeName, propValue.getValue()); + Map nsBindings = new HashMap(); + nsBindings.put(propName.getNamespaceURI(), "ns1"); + for (int i = 0; i < features.getLength(); i++) { + ETSAssert.assertXPath2(xpath, new DOMSource(features.item(i)), nsBindings); + } + } - /** - * [{@code Test}] Submits a GetFeature request containing a - * {@code PropertyIsNotEqualTo} predicate that applies to some simple - * feature property. The response entity must not include any features with - * a matching (case-sensitive) property value; if multiple values exist, all - * must satisfy the predicate (matchAction="All"). - * - * @param binding - * The ProtocolBinding to use for this request. - * @param featureType - * A QName representing the qualified name of some feature type. - */ - @Test(description = "See ISO 19143: 7.7.3.2", dataProvider = "protocol-featureType") - public void propertyIsNotEqualTo_caseSensitive(ProtocolBinding binding, - QName featureType) { - WFSMessage.appendSimpleQuery(this.reqEntity, featureType); - Map propValueMap = findMatchingPropertyValue(featureType); - if (propValueMap.isEmpty()) { - throw new SkipException( - "No simple property values found for feature type " - + featureType); - } - Entry propValue = propValueMap.entrySet() - .iterator().next(); - XSElementDeclaration propDecl = propValue.getKey(); - QName propName = new QName(propDecl.getNamespace(), propDecl.getName()); - addPropertyIsEqualToPredicate(this.reqEntity, propName, - propValue.getValue(), true, MATCH_ALL, true); - Response rsp = wfsClient.submitRequest(reqEntity, binding); - this.rspEntity = extractBodyAsDocument(rsp); - Assert.assertEquals(rsp.getStatus(), - Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - NodeList features = this.rspEntity.getElementsByTagNameNS( - featureType.getNamespaceURI(), featureType.getLocalPart()); - // Add constructor functions for property type (XML Schema datatype) - QName dataType = AppSchemaUtils.getBuiltInDatatype(propDecl); - String propTypeName = dataType.getLocalPart(); - // expect no matches even if multiple values exist - String xpath = String.format("not(xs:%s(ns1:%s[1]) = xs:%s('%s'))", - propTypeName, propName.getLocalPart(), propTypeName, - propValue.getValue()); - Map nsBindings = new HashMap(); - nsBindings.put(propName.getNamespaceURI(), "ns1"); - for (int i = 0; i < features.getLength(); i++) { - ETSAssert.assertXPath2(xpath, new DOMSource(features.item(i)), - nsBindings); - } - } + /** + * Adds a {@code PropertyIsEqualTo} predicate to a GetFeature request entity with the + * given property name and literal value. The predicate is structured as shown in the + * listing below. + * + *
      +	 * {@code
      +	 * 
      +	 *   
      +	 *     value
      +	 *     tns:featureProperty
      +	 *   
      +	 * 
      +	 * }
      +	 * 
      + * @param request The request entity (/wfs:GetFeature). + * @param propertyName A QName that specifies the feature property to check. + * @param value The literal value to match the property value against. + * @param matchCase A boolean value indicating whether or not the comparison should be + * case-sensitive. + * @param matchAction A String specifying how the predicate should be applied to a + * multi-valued property; the default value is "Any". + * @param negate Negates the predicate by using the {@code PropertyIsNotEqualTo} + * predicate instead. + */ + void addPropertyIsEqualToPredicate(Document request, QName propertyName, String value, boolean matchCase, + String matchAction, boolean negate) { + if (!request.getDocumentElement().getLocalName().equals(WFS2.GET_FEATURE)) { + throw new IllegalArgumentException( + "Not a GetFeature request: " + request.getDocumentElement().getNodeName()); + } + if (null == propertyName) { + throw new IllegalArgumentException("propertyName is required."); + } + Element queryElem = (Element) request.getElementsByTagNameNS(Namespaces.WFS, WFS2.QUERY_ELEM).item(0); + Element filter = request.createElementNS(Namespaces.FES, "Filter"); + queryElem.appendChild(filter); + Element predicate; + if (negate) { + predicate = request.createElementNS(Namespaces.FES, "PropertyIsNotEqualTo"); + } + else { + predicate = request.createElementNS(Namespaces.FES, "PropertyIsEqualTo"); + } + filter.appendChild(predicate); + String matchActionAttr = (null != matchAction && !matchAction.isEmpty()) ? matchAction : MATCH_ANY; + predicate.setAttribute("matchCase", Boolean.toString(matchCase)); + predicate.setAttribute("matchAction", matchActionAttr); + Element literal = request.createElementNS(Namespaces.FES, "Literal"); + literal.setTextContent(value); + predicate.appendChild(literal); + Element valueRef = request.createElementNS(Namespaces.FES, "ValueReference"); + predicate.appendChild(valueRef); + String prefix = (propertyName.getPrefix().length() > 0) ? propertyName.getPrefix() : TNS_PREFIX; + String nsURI = request.lookupNamespaceURI(prefix); + if (null == nsURI) { + valueRef.setAttribute(XMLConstants.XMLNS_ATTRIBUTE + ":" + prefix, propertyName.getNamespaceURI()); + } + valueRef.setTextContent(prefix + ":" + propertyName.getLocalPart()); + } - /** - * Adds a {@code PropertyIsEqualTo} predicate to a GetFeature request entity - * with the given property name and literal value. The predicate is - * structured as shown in the listing below. - * - *
      -     * {@code
      -     * 
      -     *   
      -     *     value
      -     *     tns:featureProperty
      -     *   
      -     * 
      -     * }
      -     * 
      - * - * @param request - * The request entity (/wfs:GetFeature). - * @param propertyName - * A QName that specifies the feature property to check. - * @param value - * The literal value to match the property value against. - * @param matchCase - * A boolean value indicating whether or not the comparison - * should be case-sensitive. - * @param matchAction - * A String specifying how the predicate should be applied to a - * multi-valued property; the default value is "Any". - * @param negate - * Negates the predicate by using the - * {@code PropertyIsNotEqualTo} predicate instead. - */ - void addPropertyIsEqualToPredicate(Document request, QName propertyName, - String value, boolean matchCase, String matchAction, boolean negate) { - if (!request.getDocumentElement().getLocalName() - .equals(WFS2.GET_FEATURE)) { - throw new IllegalArgumentException("Not a GetFeature request: " - + request.getDocumentElement().getNodeName()); - } - if (null == propertyName) { - throw new IllegalArgumentException("propertyName is required."); - } - Element queryElem = (Element) request.getElementsByTagNameNS( - Namespaces.WFS, WFS2.QUERY_ELEM).item(0); - Element filter = request.createElementNS(Namespaces.FES, "Filter"); - queryElem.appendChild(filter); - Element predicate; - if (negate) { - predicate = request.createElementNS(Namespaces.FES, - "PropertyIsNotEqualTo"); - } else { - predicate = request.createElementNS(Namespaces.FES, - "PropertyIsEqualTo"); - } - filter.appendChild(predicate); - String matchActionAttr = (null != matchAction && !matchAction.isEmpty()) ? matchAction - : MATCH_ANY; - predicate.setAttribute("matchCase", Boolean.toString(matchCase)); - predicate.setAttribute("matchAction", matchActionAttr); - Element literal = request.createElementNS(Namespaces.FES, "Literal"); - literal.setTextContent(value); - predicate.appendChild(literal); - Element valueRef = request.createElementNS(Namespaces.FES, - "ValueReference"); - predicate.appendChild(valueRef); - String prefix = (propertyName.getPrefix().length() > 0) ? propertyName - .getPrefix() : TNS_PREFIX; - String nsURI = request.lookupNamespaceURI(prefix); - if (null == nsURI) { - valueRef.setAttribute(XMLConstants.XMLNS_ATTRIBUTE + ":" + prefix, - propertyName.getNamespaceURI()); - } - valueRef.setTextContent(prefix + ":" + propertyName.getLocalPart()); - } + /** + * Inspects sample data retrieved from the SUT and finds a value that matches at least + * one simple property for the specified feature type. If the property occurs more + * than once only the first occurrrence is used. + * @param featureType The qualified name of some feature type. + * @return A Map containing a single entry where the key is an element declaration and + * the value is a String representing the property value. + */ + Map findMatchingPropertyValue(QName featureType) { + XSTypeDefinition xsdSimpleType = getModel().getTypeDefinition("anySimpleType", + XMLConstants.W3C_XML_SCHEMA_NS_URI); + List simpleProps = AppSchemaUtils.getFeaturePropertiesByType(getModel(), featureType, + xsdSimpleType); + ListIterator listItr = simpleProps.listIterator(simpleProps.size()); + XSElementDeclaration prop = null; + String propValue = null; + // start with application-specific properties at end of list + while (listItr.hasPrevious()) { + prop = listItr.previous(); + QName propName = new QName(prop.getNamespace(), prop.getName()); + List values = dataSampler.getSimplePropertyValues(featureType, propName, null); + if (!values.isEmpty()) { + // select first value if multiple occurrences + propValue = values.get(0); + break; + } + } + Map map = new HashMap(); + if (null != propValue) { + map.put(prop, propValue); + } + return map; + } - /** - * Inspects sample data retrieved from the SUT and finds a value that - * matches at least one simple property for the specified feature type. If - * the property occurs more than once only the first occurrrence is used. - * - * @param featureType - * The qualified name of some feature type. - * @return A Map containing a single entry where the key is an element - * declaration and the value is a String representing the property - * value. - */ - Map findMatchingPropertyValue( - QName featureType) { - XSTypeDefinition xsdSimpleType = getModel().getTypeDefinition( - "anySimpleType", XMLConstants.W3C_XML_SCHEMA_NS_URI); - List simpleProps = AppSchemaUtils - .getFeaturePropertiesByType(getModel(), featureType, xsdSimpleType); - ListIterator listItr = simpleProps - .listIterator(simpleProps.size()); - XSElementDeclaration prop = null; - String propValue = null; - // start with application-specific properties at end of list - while (listItr.hasPrevious()) { - prop = listItr.previous(); - QName propName = new QName(prop.getNamespace(), prop.getName()); - List values = dataSampler.getSimplePropertyValues( - featureType, propName, null); - if (!values.isEmpty()) { - // select first value if multiple occurrences - propValue = values.get(0); - break; - } - } - Map map = new HashMap(); - if (null != propValue) { - map.put(prop, propValue); - } - return map; - } } diff --git a/src/main/java/org/opengis/cite/iso19142/basic/filter/PropertyIsLikeOperatorTests.java b/src/main/java/org/opengis/cite/iso19142/basic/filter/PropertyIsLikeOperatorTests.java index ef2f2b40..b5b92200 100644 --- a/src/main/java/org/opengis/cite/iso19142/basic/filter/PropertyIsLikeOperatorTests.java +++ b/src/main/java/org/opengis/cite/iso19142/basic/filter/PropertyIsLikeOperatorTests.java @@ -30,14 +30,12 @@ import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response.Status; - /** - * Tests the response to a GetFeature request that includes a - * {@code PropertyIsLike} filter that tests the value of a property using a - * specified pattern--a combination of regular characters and metacharacters. - * The {@code PropertyIsLike} predicate can be regarded as a very simple regular - * expression operator. - * + * Tests the response to a GetFeature request that includes a {@code PropertyIsLike} + * filter that tests the value of a property using a specified pattern--a combination of + * regular characters and metacharacters. The {@code PropertyIsLike} predicate can be + * regarded as a very simple regular expression operator. + * *

      * Sources *

      @@ -50,96 +48,76 @@ public class PropertyIsLikeOperatorTests extends QueryFilterFixture { /** - * [{@code Test}] Submits a GetFeature request containing a - * {@code Not/PropertyIsLike} predicate that applies to some simple feature - * property (of type xsd:string). The response entity must not include any - * feature instances with matching property values. - * - * @param binding - * The ProtocolBinding to use for this request. - * @param featureType - * A QName representing the qualified name of some feature type. + * [{@code Test}] Submits a GetFeature request containing a {@code Not/PropertyIsLike} + * predicate that applies to some simple feature property (of type xsd:string). The + * response entity must not include any feature instances with matching property + * values. + * @param binding The ProtocolBinding to use for this request. + * @param featureType A QName representing the qualified name of some feature type. */ @Test(description = "See ISO 19143: 7.7.3.4, 7.10", dataProvider = "protocol-featureType") public void propertyIsNotLike(ProtocolBinding binding, QName featureType) { WFSMessage.appendSimpleQuery(this.reqEntity, featureType); Map patternMap = generateMatchingStringPattern(featureType); if (patternMap.isEmpty()) { - throw new SkipException( - "No string property values found for feature type " - + featureType); + throw new SkipException("No string property values found for feature type " + featureType); } - Entry propPattern = patternMap.entrySet().iterator() - .next(); - addPropertyIsLikePredicate(this.reqEntity, propPattern.getKey(), - propPattern.getValue(), true); + Entry propPattern = patternMap.entrySet().iterator().next(); + addPropertyIsLikePredicate(this.reqEntity, propPattern.getKey(), propPattern.getValue(), true); Response rsp = wfsClient.submitRequest(reqEntity, binding); this.rspEntity = extractBodyAsDocument(rsp); - Assert.assertEquals(rsp.getStatus(), - Status.OK.getStatusCode(), + Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - NodeList features = this.rspEntity.getElementsByTagNameNS( - featureType.getNamespaceURI(), featureType.getLocalPart()); + NodeList features = this.rspEntity.getElementsByTagNameNS(featureType.getNamespaceURI(), + featureType.getLocalPart()); // convert wildcards in pattern to proper regular expression - String xpath = String.format("not(matches(ns1:%s[1], '%s'))", - propPattern.getKey().getLocalPart(), propPattern.getValue() - .replace("*", ".*")); + String xpath = String.format("not(matches(ns1:%s[1], '%s'))", propPattern.getKey().getLocalPart(), + propPattern.getValue().replace("*", ".*")); Map nsBindings = new HashMap(); nsBindings.put(propPattern.getKey().getNamespaceURI(), "ns1"); for (int i = 0; i < features.getLength(); i++) { - ETSAssert.assertXPath2(xpath, new DOMSource(features.item(i)), - nsBindings); + ETSAssert.assertXPath2(xpath, new DOMSource(features.item(i)), nsBindings); } } /** - * [{@code Test}] Submits a GetFeature request containing a - * {@code PropertyIsLike} predicate that applies to some simple feature - * property (of type xsd:string). The response entity must include only - * features with a property value matching the specified pattern. - * - * @param binding - * The ProtocolBinding to use for this request. - * @param featureType - * A QName representing the qualified name of some feature type. + * [{@code Test}] Submits a GetFeature request containing a {@code PropertyIsLike} + * predicate that applies to some simple feature property (of type xsd:string). The + * response entity must include only features with a property value matching the + * specified pattern. + * @param binding The ProtocolBinding to use for this request. + * @param featureType A QName representing the qualified name of some feature type. */ @Test(description = "See ISO 19143: 7.7.3.4", dataProvider = "protocol-featureType") public void propertyIsLike(ProtocolBinding binding, QName featureType) { WFSMessage.appendSimpleQuery(this.reqEntity, featureType); Map patternMap = generateMatchingStringPattern(featureType); if (patternMap.isEmpty()) { - throw new SkipException( - "No string property values found for feature type " - + featureType); + throw new SkipException("No string property values found for feature type " + featureType); } - Entry propPattern = patternMap.entrySet().iterator() - .next(); - addPropertyIsLikePredicate(this.reqEntity, propPattern.getKey(), - propPattern.getValue(), false); + Entry propPattern = patternMap.entrySet().iterator().next(); + addPropertyIsLikePredicate(this.reqEntity, propPattern.getKey(), propPattern.getValue(), false); Response rsp = wfsClient.submitRequest(reqEntity, binding); this.rspEntity = extractBodyAsDocument(rsp); - Assert.assertEquals(rsp.getStatus(), - Status.OK.getStatusCode(), + Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - NodeList features = this.rspEntity.getElementsByTagNameNS( - featureType.getNamespaceURI(), featureType.getLocalPart()); + NodeList features = this.rspEntity.getElementsByTagNameNS(featureType.getNamespaceURI(), + featureType.getLocalPart()); // convert wildcards in pattern to proper regular expression - String xpath = String.format("matches(ns1:%s[1], '%s')", propPattern - .getKey().getLocalPart(), + String xpath = String.format("matches(ns1:%s[1], '%s')", propPattern.getKey().getLocalPart(), propPattern.getValue().replace("*", ".*")); Map nsBindings = new HashMap(); nsBindings.put(propPattern.getKey().getNamespaceURI(), "ns1"); for (int i = 0; i < features.getLength(); i++) { - ETSAssert.assertXPath2(xpath, new DOMSource(features.item(i)), - nsBindings); + ETSAssert.assertXPath2(xpath, new DOMSource(features.item(i)), nsBindings); } } /** - * Adds a {@code PropertyIsLike} predicate to a GetFeature request entity - * with the given property name and pattern. The metacharacters are shown in - * the following example. - * + * Adds a {@code PropertyIsLike} predicate to a GetFeature request entity with the + * given property name and pattern. The metacharacters are shown in the following + * example. + * *
       	 * {@code
       	 * 
      @@ -150,33 +128,24 @@ public void propertyIsLike(ProtocolBinding binding, QName featureType) {
       	 * 
       	 * }
       	 * 
      - * - * @param request - * The request entity (/wfs:GetFeature). - * @param propertyName - * A QName that specifies the feature property to check. - * @param pattern - * The pattern to match the property value against. - * @param negate - * Negates the predicate by inserting a {@code } operator - * (logical complement). + * @param request The request entity (/wfs:GetFeature). + * @param propertyName A QName that specifies the feature property to check. + * @param pattern The pattern to match the property value against. + * @param negate Negates the predicate by inserting a {@code } operator (logical + * complement). */ - void addPropertyIsLikePredicate(Document request, QName propertyName, - String pattern, boolean negate) { - if (!request.getDocumentElement().getLocalName() - .equals(WFS2.GET_FEATURE)) { - throw new IllegalArgumentException("Not a GetFeature request: " - + request.getDocumentElement().getNodeName()); + void addPropertyIsLikePredicate(Document request, QName propertyName, String pattern, boolean negate) { + if (!request.getDocumentElement().getLocalName().equals(WFS2.GET_FEATURE)) { + throw new IllegalArgumentException( + "Not a GetFeature request: " + request.getDocumentElement().getNodeName()); } if (null == propertyName) { throw new IllegalArgumentException("propertyName is required."); } - Element queryElem = (Element) request.getElementsByTagNameNS( - Namespaces.WFS, WFS2.QUERY_ELEM).item(0); + Element queryElem = (Element) request.getElementsByTagNameNS(Namespaces.WFS, WFS2.QUERY_ELEM).item(0); Element filter = request.createElementNS(Namespaces.FES, "Filter"); queryElem.appendChild(filter); - Element predicate = request.createElementNS(Namespaces.FES, - "PropertyIsLike"); + Element predicate = request.createElementNS(Namespaces.FES, "PropertyIsLike"); predicate.setAttribute("wildCard", "*"); predicate.setAttribute("singleChar", "?"); predicate.setAttribute("escapeChar", "\\"); @@ -184,18 +153,16 @@ void addPropertyIsLikePredicate(Document request, QName propertyName, Element not = request.createElementNS(Namespaces.FES, "Not"); filter.appendChild(not); not.appendChild(predicate); - } else { + } + else { filter.appendChild(predicate); } - Element valueRef = request.createElementNS(Namespaces.FES, - "ValueReference"); + Element valueRef = request.createElementNS(Namespaces.FES, "ValueReference"); predicate.appendChild(valueRef); - String prefix = (propertyName.getPrefix().length() > 0) ? propertyName - .getPrefix() : TNS_PREFIX; + String prefix = (propertyName.getPrefix().length() > 0) ? propertyName.getPrefix() : TNS_PREFIX; String nsURI = request.lookupNamespaceURI(prefix); if (null == nsURI) { - valueRef.setAttribute(XMLConstants.XMLNS_ATTRIBUTE + ":" + prefix, - propertyName.getNamespaceURI()); + valueRef.setAttribute(XMLConstants.XMLNS_ATTRIBUTE + ":" + prefix, propertyName.getNamespaceURI()); } valueRef.setTextContent(prefix + ":" + propertyName.getLocalPart()); Element literal = request.createElementNS(Namespaces.FES, "Literal"); @@ -204,33 +171,27 @@ void addPropertyIsLikePredicate(Document request, QName propertyName, } /** - * Inspects sample data retrieved from the SUT and generates a pattern that - * matches at least one simple property value (of type xsd:string) for the - * specified feature type. If the property occurs more than once only the - * first occurrrence is used. - * + * Inspects sample data retrieved from the SUT and generates a pattern that matches at + * least one simple property value (of type xsd:string) for the specified feature + * type. If the property occurs more than once only the first occurrrence is used. + * * Complex properties are not inspected for constituent string elements. - * - * @param featureType - * The qualified name of some feature type. - * @return A Map containing a single entry where the key is a property name - * and the value is a pattern (with metacharacters). + * @param featureType The qualified name of some feature type. + * @return A Map containing a single entry where the key is a property name and the + * value is a pattern (with metacharacters). */ Map generateMatchingStringPattern(QName featureType) { QName propName = null; String pattern = null; - XSTypeDefinition stringType = getModel().getTypeDefinition("string", - XMLConstants.W3C_XML_SCHEMA_NS_URI); - List strProps = AppSchemaUtils - .getFeaturePropertiesByType(getModel(), featureType, stringType); - ListIterator listItr = strProps - .listIterator(strProps.size()); + XSTypeDefinition stringType = getModel().getTypeDefinition("string", XMLConstants.W3C_XML_SCHEMA_NS_URI); + List strProps = AppSchemaUtils.getFeaturePropertiesByType(getModel(), featureType, + stringType); + ListIterator listItr = strProps.listIterator(strProps.size()); // start with application-specific properties at end of list while (listItr.hasPrevious()) { XSElementDeclaration prop = listItr.previous(); propName = new QName(prop.getNamespace(), prop.getName()); - List values = this.dataSampler.getSimplePropertyValues( - featureType, propName, null); + List values = this.dataSampler.getSimplePropertyValues(featureType, propName, null); if (!values.isEmpty()) { // just use first value and replace first two chars with '*' StringBuilder patternBuilder = new StringBuilder(values.get(0)); @@ -245,4 +206,5 @@ Map generateMatchingStringPattern(QName featureType) { } return map; } + } diff --git a/src/main/java/org/opengis/cite/iso19142/basic/filter/PropertyIsNilOperatorTests.java b/src/main/java/org/opengis/cite/iso19142/basic/filter/PropertyIsNilOperatorTests.java index d4f79deb..21ef9e6f 100644 --- a/src/main/java/org/opengis/cite/iso19142/basic/filter/PropertyIsNilOperatorTests.java +++ b/src/main/java/org/opengis/cite/iso19142/basic/filter/PropertyIsNilOperatorTests.java @@ -27,16 +27,19 @@ import jakarta.ws.rs.core.Response.Status; /** - * Tests the response to a GetFeature request that includes a {@code PropertyIsNil} filter predicate that tests the - * content of a specified property and evaluates if it is nil. It is also possible to check the reason for a missing - * value by matching on the nilReason parameter. While the gml:boundedBy property is nillable, it is ignored; the - * application schema must define at least one nillable feature property. - * - *
      In the GML schema and in GML application schemas, the "nillable" and "nilReason" construction may be - * used on elements representing GML properties (see 7.2.3). This allows properties that are part of the content of - * objects and features in GML and GML application languages to be declared to be mandatory, while still permitting them - * to appear in an instance document with no value. [ISO 19136/OGC 07-036, 8.2.3.2]
      - * + * Tests the response to a GetFeature request that includes a {@code PropertyIsNil} filter + * predicate that tests the content of a specified property and evaluates if it is nil. It + * is also possible to check the reason for a missing value by matching on the nilReason + * parameter. While the gml:boundedBy property is nillable, it is ignored; the application + * schema must define at least one nillable feature property. + * + *
      In the GML schema and in GML application schemas, the "nillable" and + * "nilReason" construction may be used on elements representing GML properties (see + * 7.2.3). This allows properties that are part of the content of objects and features in + * GML and GML application languages to be declared to be mandatory, while still + * permitting them to appear in an instance document with no value. [ISO 19136/OGC 07-036, + * 8.2.3.2]
      + * *

      * Sources *

      @@ -46,111 +49,112 @@ *
    • ISO 19143:2010, cl. A.6: Test cases for standard filter
    • *
    • ISO 19136:2007, cl. 8.2.3.2: Elements declared to be "nillable"
    • *
    - * - * @see OGC 07-036: GML 3.2.1 + * + * @see OGC 07-036: GML 3.2.1 */ public class PropertyIsNilOperatorTests extends QueryFilterFixture { - /** - * [{@code Test}] Submits a GetFeature request containing a {@code PropertyIsNil} predicate designating a nillable - * feature property (one per feature type). The response entity must include only feature instances that include the - * specified property with {@literal @xsi:nil="true"}. - * - *

    - * All Basic WFS implementations must support the Standard Filter conformance - * class defined in OpenGIS Filter Encoding 2.0 Encoding Standard (ISO 19143). - *

    - * - * @param binding - * The ProtocolBinding to use for this request. - * @param featureType - * The FeatureType to use for this test. - */ - @Test(description = "See ISO 19143: 7.7.3.6, A.6", dataProvider = "protocol-featureType") - public void propertyIsNil( ProtocolBinding binding, QName featureType ) { - List nillables = this.dataSampler.getNillableProperties(getModel(), featureType); - if ( nillables.isEmpty() ) { - throw new SkipException( "FeatureType " + featureType + " does not contain at least one nillable property" ); - } - this.reqEntity = WFSMessage.createRequestEntity( GET_FEATURE_MINIMAL, this.wfsVersion ); - WFSMessage.appendSimpleQuery( this.reqEntity, featureType ); - // get last nillable property for this feature type - QName propName = nillables.get( nillables.size() - 1 ); - addPropertyIsNilPredicate( this.reqEntity, propName, null, false ); - Response rsp = wfsClient.submitRequest( reqEntity, binding ); - this.rspEntity = extractBodyAsDocument( rsp ); - Assert.assertEquals( rsp.getStatus(), Status.OK.getStatusCode(), - ErrorMessage.get( ErrorMessageKeys.UNEXPECTED_STATUS ) ); - NodeList features = this.rspEntity.getElementsByTagNameNS(Namespaces.WFS, WFS2.MEMBER); - - String xpath = String.format( "ns1:%s[@xsi:nil='true']", propName.getLocalPart() ); - Map nsBindings = new HashMap(); - nsBindings.put( propName.getNamespaceURI(), "ns1" ); - Node featureNode = null; - for ( int i = 0; i < features.getLength(); i++ ) { - featureNode = XMLUtils.getElementByNamespaceURIandLocalPart(features.item( i ), featureType.getNamespaceURI(), featureType.getLocalPart()); - if(featureNode != null) { - ETSAssert.assertXPath( xpath, featureNode, nsBindings ); - } else { - //TODO - Assert.fail("No node found."); - } - } - } + /** + * [{@code Test}] Submits a GetFeature request containing a {@code PropertyIsNil} + * predicate designating a nillable feature property (one per feature type). The + * response entity must include only feature instances that include the specified + * property with {@literal @xsi:nil="true"}. + * + *

    + * All Basic WFS implementations must support the Standard + * Filter conformance class defined in OpenGIS Filter Encoding 2.0 + * Encoding Standard (ISO 19143). + *

    + * @param binding The ProtocolBinding to use for this request. + * @param featureType The FeatureType to use for this test. + */ + @Test(description = "See ISO 19143: 7.7.3.6, A.6", dataProvider = "protocol-featureType") + public void propertyIsNil(ProtocolBinding binding, QName featureType) { + List nillables = this.dataSampler.getNillableProperties(getModel(), featureType); + if (nillables.isEmpty()) { + throw new SkipException("FeatureType " + featureType + " does not contain at least one nillable property"); + } + this.reqEntity = WFSMessage.createRequestEntity(GET_FEATURE_MINIMAL, this.wfsVersion); + WFSMessage.appendSimpleQuery(this.reqEntity, featureType); + // get last nillable property for this feature type + QName propName = nillables.get(nillables.size() - 1); + addPropertyIsNilPredicate(this.reqEntity, propName, null, false); + Response rsp = wfsClient.submitRequest(reqEntity, binding); + this.rspEntity = extractBodyAsDocument(rsp); + Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + NodeList features = this.rspEntity.getElementsByTagNameNS(Namespaces.WFS, WFS2.MEMBER); + + String xpath = String.format("ns1:%s[@xsi:nil='true']", propName.getLocalPart()); + Map nsBindings = new HashMap(); + nsBindings.put(propName.getNamespaceURI(), "ns1"); + Node featureNode = null; + for (int i = 0; i < features.getLength(); i++) { + featureNode = XMLUtils.getElementByNamespaceURIandLocalPart(features.item(i), featureType.getNamespaceURI(), + featureType.getLocalPart()); + if (featureNode != null) { + ETSAssert.assertXPath(xpath, featureNode, nsBindings); + } + else { + // TODO + Assert.fail("No node found."); + } + } + } + + /** + * Adds a {@code PropertyIsNil} predicate to a GetFeature request entity with the + * given property name. + * + *
    +	 * {@code
    +	 * 
    +	 *   
    +	 *     tns:featureProperty
    +	 *   
    +	 * 
    +	 * }
    +	 * 
    + * @param request The request entity (/wfs:GetFeature). + * @param propertyName A QName that specifies the feature property to check. + * @param nilReason A String that specifies a reason for the missing value; it may be + * a standard value or an absolute URI in accord with the gml:NilReasonType type + * definition. Supply an empty string or null value if the reason does not matter. + * @param negate Negates the predicate by inserting a {@code } operator (logical + * complement). + */ + void addPropertyIsNilPredicate(Document request, QName propertyName, String nilReason, boolean negate) { + if (!request.getDocumentElement().getLocalName().equals(WFS2.GET_FEATURE)) { + throw new IllegalArgumentException( + "Not a GetFeature request: " + request.getDocumentElement().getNodeName()); + } + if (null == propertyName) { + throw new IllegalArgumentException("propertyName is required."); + } + Element queryElem = (Element) request.getElementsByTagNameNS(Namespaces.WFS, WFS2.QUERY_ELEM).item(0); + Element filter = request.createElementNS(Namespaces.FES, "Filter"); + queryElem.appendChild(filter); + Element predicate = request.createElementNS(Namespaces.FES, "PropertyIsNil"); + if (null != nilReason && !nilReason.isEmpty()) { + predicate.setAttribute("nilReason", nilReason); + } + if (negate) { + Element not = request.createElementNS(Namespaces.FES, "Not"); + filter.appendChild(not); + not.appendChild(predicate); + } + else { + filter.appendChild(predicate); + } + Element valueRef = request.createElementNS(Namespaces.FES, "ValueReference"); + predicate.appendChild(valueRef); + String prefix = (propertyName.getPrefix().length() > 0) ? propertyName.getPrefix() : TNS_PREFIX; + String nsURI = request.lookupNamespaceURI(prefix); + if (null == nsURI) { + valueRef.setAttribute(XMLConstants.XMLNS_ATTRIBUTE + ":" + prefix, propertyName.getNamespaceURI()); + } + valueRef.setTextContent(prefix + ":" + propertyName.getLocalPart()); + } - /** - * Adds a {@code PropertyIsNil} predicate to a GetFeature request entity with the given property name. - * - *
    -     * {@code
    -     * 
    -     *   
    -     *     tns:featureProperty
    -     *   
    -     * 
    -     * }
    -     * 
    - * - * @param request - * The request entity (/wfs:GetFeature). - * @param propertyName - * A QName that specifies the feature property to check. - * @param nilReason - * A String that specifies a reason for the missing value; it may be a standard value or an absolute URI - * in accord with the gml:NilReasonType type definition. Supply an empty string or null value if the - * reason does not matter. - * @param negate - * Negates the predicate by inserting a {@code } operator (logical complement). - */ - void addPropertyIsNilPredicate( Document request, QName propertyName, String nilReason, boolean negate ) { - if ( !request.getDocumentElement().getLocalName().equals( WFS2.GET_FEATURE ) ) { - throw new IllegalArgumentException( "Not a GetFeature request: " - + request.getDocumentElement().getNodeName() ); - } - if ( null == propertyName ) { - throw new IllegalArgumentException( "propertyName is required." ); - } - Element queryElem = (Element) request.getElementsByTagNameNS( Namespaces.WFS, WFS2.QUERY_ELEM ).item( 0 ); - Element filter = request.createElementNS( Namespaces.FES, "Filter" ); - queryElem.appendChild( filter ); - Element predicate = request.createElementNS( Namespaces.FES, "PropertyIsNil" ); - if ( null != nilReason && !nilReason.isEmpty() ) { - predicate.setAttribute( "nilReason", nilReason ); - } - if ( negate ) { - Element not = request.createElementNS( Namespaces.FES, "Not" ); - filter.appendChild( not ); - not.appendChild( predicate ); - } else { - filter.appendChild( predicate ); - } - Element valueRef = request.createElementNS( Namespaces.FES, "ValueReference" ); - predicate.appendChild( valueRef ); - String prefix = ( propertyName.getPrefix().length() > 0 ) ? propertyName.getPrefix() : TNS_PREFIX; - String nsURI = request.lookupNamespaceURI( prefix ); - if ( null == nsURI ) { - valueRef.setAttribute( XMLConstants.XMLNS_ATTRIBUTE + ":" + prefix, propertyName.getNamespaceURI() ); - } - valueRef.setTextContent( prefix + ":" + propertyName.getLocalPart() ); - } } diff --git a/src/main/java/org/opengis/cite/iso19142/basic/filter/PropertyIsNullOperatorTests.java b/src/main/java/org/opengis/cite/iso19142/basic/filter/PropertyIsNullOperatorTests.java index 1572ad18..a299ed1f 100644 --- a/src/main/java/org/opengis/cite/iso19142/basic/filter/PropertyIsNullOperatorTests.java +++ b/src/main/java/org/opengis/cite/iso19142/basic/filter/PropertyIsNullOperatorTests.java @@ -25,10 +25,10 @@ import jakarta.ws.rs.core.Response.Status; /** - * Tests the response to a GetFeature request that includes a - * {@code PropertyIsNull} filter predicate; this operator tests for the - * existence of a specified feature property. - * + * Tests the response to a GetFeature request that includes a {@code PropertyIsNull} + * filter predicate; this operator tests for the existence of a specified feature + * property. + * *

    * Sources *

    @@ -41,15 +41,11 @@ public class PropertyIsNullOperatorTests extends QueryFilterFixture { /** - * [{@code Test}] Submits a GetFeature request containing a - * {@code PropertyIsNull} predicate designating the gml:name property. The - * response entity must include only features that lack the specified - * property. - * - * @param binding - * The ProtocolBinding to use for this request. - * @param featureType - * A QName representing the qualified name of some feature type. + * [{@code Test}] Submits a GetFeature request containing a {@code PropertyIsNull} + * predicate designating the gml:name property. The response entity must include only + * features that lack the specified property. + * @param binding The ProtocolBinding to use for this request. + * @param featureType A QName representing the qualified name of some feature type. */ @Test(description = "See ISO 19143: 7.7.3.5", dataProvider = "protocol-featureType") public void gmlNameIsNull(ProtocolBinding binding, QName featureType) { @@ -58,40 +54,32 @@ public void gmlNameIsNull(ProtocolBinding binding, QName featureType) { addPropertyIsNullPredicate(this.reqEntity, gmlName, false); Response rsp = wfsClient.submitRequest(reqEntity, binding); this.rspEntity = extractBodyAsDocument(rsp); - Assert.assertEquals(rsp.getStatus(), - Status.OK.getStatusCode(), + Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); Map nsBindings = new HashMap(); nsBindings.put(featureType.getNamespaceURI(), "tns"); - String xpath = String.format("not(//tns:%s[gml:name])", - featureType.getLocalPart()); + String xpath = String.format("not(//tns:%s[gml:name])", featureType.getLocalPart()); ETSAssert.assertXPath(xpath, this.rspEntity, nsBindings); } /** - * [{@code Test}] Submits a GetFeature request containing a - * {@code Not/PropertyIsNull} predicate designating some feature property - * (the last one in document order). The response entity must include only - * features that include the specified property. - * - * @param binding - * The ProtocolBinding to use for this request. - * @param featureType - * A QName representing the qualified name of some feature type. + * [{@code Test}] Submits a GetFeature request containing a {@code Not/PropertyIsNull} + * predicate designating some feature property (the last one in document order). The + * response entity must include only features that include the specified property. + * @param binding The ProtocolBinding to use for this request. + * @param featureType A QName representing the qualified name of some feature type. */ @Test(description = "See ISO 19143: 7.7.3.5, 7.10", dataProvider = "protocol-featureType") public void propertyIsNotNull(ProtocolBinding binding, QName featureType) { WFSMessage.appendSimpleQuery(this.reqEntity, featureType); - List props = AppSchemaUtils - .getAllFeatureProperties(getModel(), featureType); + List props = AppSchemaUtils.getAllFeatureProperties(getModel(), featureType); // get last property in document order XSElementDeclaration lastProp = props.get(props.size() - 1); QName propName = new QName(lastProp.getNamespace(), lastProp.getName()); addPropertyIsNullPredicate(this.reqEntity, propName, true); Response rsp = wfsClient.submitRequest(reqEntity, binding); this.rspEntity = extractBodyAsDocument(rsp); - Assert.assertEquals(rsp.getStatus(), - Status.OK.getStatusCode(), + Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); Map nsBindings = new HashMap(); nsBindings.put(featureType.getNamespaceURI(), "ns1"); @@ -100,16 +88,15 @@ public void propertyIsNotNull(ProtocolBinding binding, QName featureType) { nsPrefix = "ns2"; nsBindings.put(propName.getNamespaceURI(), nsPrefix); } - String xpath = String.format( - "count(//ns1:%s[%s:%s]) = count(//wfs:member)", - featureType.getLocalPart(), nsPrefix, propName.getLocalPart()); + String xpath = String.format("count(//ns1:%s[%s:%s]) = count(//wfs:member)", featureType.getLocalPart(), + nsPrefix, propName.getLocalPart()); ETSAssert.assertXPath(xpath, this.rspEntity, nsBindings); } /** - * Adds a {@code PropertyIsNull} predicate to a GetFeature request entity - * using the given property name. - * + * Adds a {@code PropertyIsNull} predicate to a GetFeature request entity using the + * given property name. + * *
     	 * {@code
     	 * 
    @@ -119,48 +106,39 @@ public void propertyIsNotNull(ProtocolBinding binding, QName featureType) {
     	 * 
     	 * }
     	 * 
    - * - * @param request - * The request entity (/wfs:GetFeature). - * @param propertyName - * A QName that specifies the feature property to check. - * @param negate - * Negates the predicate by inserting a {@code } operator - * (logical complement). + * @param request The request entity (/wfs:GetFeature). + * @param propertyName A QName that specifies the feature property to check. + * @param negate Negates the predicate by inserting a {@code } operator (logical + * complement). */ - void addPropertyIsNullPredicate(Document request, QName propertyName, - boolean negate) { - if (!request.getDocumentElement().getLocalName() - .equals(WFS2.GET_FEATURE)) { - throw new IllegalArgumentException("Not a GetFeature request: " - + request.getDocumentElement().getNodeName()); + void addPropertyIsNullPredicate(Document request, QName propertyName, boolean negate) { + if (!request.getDocumentElement().getLocalName().equals(WFS2.GET_FEATURE)) { + throw new IllegalArgumentException( + "Not a GetFeature request: " + request.getDocumentElement().getNodeName()); } if (null == propertyName) { throw new IllegalArgumentException("propertyName is required."); } - Element queryElem = (Element) request.getElementsByTagNameNS( - Namespaces.WFS, WFS2.QUERY_ELEM).item(0); + Element queryElem = (Element) request.getElementsByTagNameNS(Namespaces.WFS, WFS2.QUERY_ELEM).item(0); Element filter = request.createElementNS(Namespaces.FES, "Filter"); queryElem.appendChild(filter); - Element predicate = request.createElementNS(Namespaces.FES, - "PropertyIsNull"); + Element predicate = request.createElementNS(Namespaces.FES, "PropertyIsNull"); if (negate) { Element not = request.createElementNS(Namespaces.FES, "Not"); filter.appendChild(not); not.appendChild(predicate); - } else { + } + else { filter.appendChild(predicate); } - Element valueRef = request.createElementNS(Namespaces.FES, - "ValueReference"); + Element valueRef = request.createElementNS(Namespaces.FES, "ValueReference"); predicate.appendChild(valueRef); - String prefix = (propertyName.getPrefix().length() > 0) ? propertyName - .getPrefix() : TNS_PREFIX; + String prefix = (propertyName.getPrefix().length() > 0) ? propertyName.getPrefix() : TNS_PREFIX; String nsURI = request.lookupNamespaceURI(prefix); if (null == nsURI) { - valueRef.setAttribute(XMLConstants.XMLNS_ATTRIBUTE + ":" + prefix, - propertyName.getNamespaceURI()); + valueRef.setAttribute(XMLConstants.XMLNS_ATTRIBUTE + ":" + prefix, propertyName.getNamespaceURI()); } valueRef.setTextContent(prefix + ":" + propertyName.getLocalPart()); } + } diff --git a/src/main/java/org/opengis/cite/iso19142/basic/filter/QueryFilterFixture.java b/src/main/java/org/opengis/cite/iso19142/basic/filter/QueryFilterFixture.java index f7cee054..ce22e6d5 100644 --- a/src/main/java/org/opengis/cite/iso19142/basic/filter/QueryFilterFixture.java +++ b/src/main/java/org/opengis/cite/iso19142/basic/filter/QueryFilterFixture.java @@ -22,89 +22,86 @@ import org.testng.annotations.BeforeMethod; /** - * Provides configuration methods that facilitate the testing of query filters - * by inspecting the application schema and sample data in order to deduce - * appropriate property names and values to include in filter predicates. + * Provides configuration methods that facilitate the testing of query filters by + * inspecting the application schema and sample data in order to deduce appropriate + * property names and values to include in filter predicates. */ public class QueryFilterFixture extends BaseFixture { - /** Acquires and saves sample data. */ - protected DataSampler dataSampler; - protected Map> temporalProperties; + /** Acquires and saves sample data. */ + protected DataSampler dataSampler; - /** - * An XSModel object representing the application schema supported by the - * SUT. - */ - protected XSModel model; - protected final String GET_FEATURE_MINIMAL = "GetFeature-Minimal"; + protected Map> temporalProperties; - public QueryFilterFixture() { - super(); - } + /** + * An XSModel object representing the application schema supported by the SUT. + */ + protected XSModel model; - /** - * Obtains a DataSampler object from the test suite context (the value of - * the {@link SuiteAttribute#SAMPLER SuiteAttribute.SAMPLER attribute}), or - * adds one if it's not found there. - * - * A schema model (XSModel) is also obtained from the test suite context by - * accessing the {@link org.opengis.cite.iso19136.SuiteAttribute#XSMODEL - * xsmodel} attribute. - * - * @param testContext - * The test (set) context. - */ - @BeforeClass() - public void initQueryFilterFixture(ITestContext testContext) { - ISuite suite = testContext.getSuite(); - this.dataSampler = (DataSampler) suite.getAttribute(SuiteAttribute.SAMPLER.getName()); - this.model = (XSModel) suite.getAttribute(org.opengis.cite.iso19136.SuiteAttribute.XSMODEL.getName()); - this.temporalProperties = new HashMap<>(); - } + protected final String GET_FEATURE_MINIMAL = "GetFeature-Minimal"; - /** - * Builds a DOM Document node representing the entity body for a GetFeature - * request. A minimal XML representation is read from the classpath - * ("util/GetFeature-Minimal.xml"). - */ - @BeforeMethod - public void buildRequestEntity() { - this.reqEntity = WFSMessage.createRequestEntity(GET_FEATURE_MINIMAL, this.wfsVersion); - } + public QueryFilterFixture() { + super(); + } + + /** + * Obtains a DataSampler object from the test suite context (the value of the + * {@link SuiteAttribute#SAMPLER SuiteAttribute.SAMPLER attribute}), or adds one if + * it's not found there. + * + * A schema model (XSModel) is also obtained from the test suite context by accessing + * the {@link org.opengis.cite.iso19136.SuiteAttribute#XSMODEL xsmodel} attribute. + * @param testContext The test (set) context. + */ + @BeforeClass() + public void initQueryFilterFixture(ITestContext testContext) { + ISuite suite = testContext.getSuite(); + this.dataSampler = (DataSampler) suite.getAttribute(SuiteAttribute.SAMPLER.getName()); + this.model = (XSModel) suite.getAttribute(org.opengis.cite.iso19136.SuiteAttribute.XSMODEL.getName()); + this.temporalProperties = new HashMap<>(); + } + + /** + * Builds a DOM Document node representing the entity body for a GetFeature request. A + * minimal XML representation is read from the classpath + * ("util/GetFeature-Minimal.xml"). + */ + @BeforeMethod + public void buildRequestEntity() { + this.reqEntity = WFSMessage.createRequestEntity(GET_FEATURE_MINIMAL, this.wfsVersion); + } - /** - * Discard previous response entity. - */ - @BeforeMethod - public void discardResponseEntity() { - this.rspEntity = null; - } + /** + * Discard previous response entity. + */ + @BeforeMethod + public void discardResponseEntity() { + this.rspEntity = null; + } - /** - * Finds the temporal properties defined for the specified feature type. - * - * @param featureType - * The qualified name of a feature type (which corresponds to an - * element declared in an application schema). - * @return A sequence of element declarations representing simple and - * complex temporal properties; the list may be empty. - */ - protected List findTemporalProperties(QName featureType) { - List tmProps = this.temporalProperties.get(featureType); - if (tmProps != null) { - return tmProps; - } - tmProps = AppSchemaUtils.getTemporalFeatureProperties(getModel(), featureType); - this.temporalProperties.put(featureType, tmProps); - TestSuiteLogger.log(Level.FINE, - String.format("Temporal properties for feature type %s: %s", featureType, tmProps)); - return tmProps; - } + /** + * Finds the temporal properties defined for the specified feature type. + * @param featureType The qualified name of a feature type (which corresponds to an + * element declared in an application schema). + * @return A sequence of element declarations representing simple and complex temporal + * properties; the list may be empty. + */ + protected List findTemporalProperties(QName featureType) { + List tmProps = this.temporalProperties.get(featureType); + if (tmProps != null) { + return tmProps; + } + tmProps = AppSchemaUtils.getTemporalFeatureProperties(getModel(), featureType); + this.temporalProperties.put(featureType, tmProps); + TestSuiteLogger.log(Level.FINE, + String.format("Temporal properties for feature type %s: %s", featureType, tmProps)); + return tmProps; + } public XSModel getModel() { - if(model == null) { - Assert.fail("Test cannot be executed as no schema can be found; Please check if DescribeFeatureType returns a valid schema."); + if (model == null) { + Assert.fail( + "Test cannot be executed as no schema can be found; Please check if DescribeFeatureType returns a valid schema."); } return model; } diff --git a/src/main/java/org/opengis/cite/iso19142/basic/filter/ResourceId.java b/src/main/java/org/opengis/cite/iso19142/basic/filter/ResourceId.java index 6c0151ab..bd937730 100644 --- a/src/main/java/org/opengis/cite/iso19142/basic/filter/ResourceId.java +++ b/src/main/java/org/opengis/cite/iso19142/basic/filter/ResourceId.java @@ -11,165 +11,152 @@ import org.w3c.dom.Element; /** - * A resource identifier as defined in ISO 19143 (OGC Filter Encoding 2.0 - * Encoding Standard). It may have additional information about a specific - * version of a resource, and can be used to request specific versions in a - * filter expression. - * + * A resource identifier as defined in ISO 19143 (OGC Filter Encoding 2.0 Encoding + * Standard). It may have additional information about a specific version of a + * resource, and can be used to request specific versions in a filter expression. + * * @see Object - * identifiers + * "http://docs.opengeospatial.org/is/09-026r2/09-026r2.html#71">Object identifiers */ public class ResourceId { - private final String rid; - private String previousRid; - private String version; - private String start; - private String end; - DateTimeFormatter dateTimeFormatter; - - /** - * Constructs a new identifier for the resource version. All members of the - * version chain have a unique rid value. - * - * @param rid - * A String that is a legal XML Schema ID (xsd:ID) value. - */ - public ResourceId(String rid) { - // check rid with regular expression? - this.rid = rid; - this.dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss[.SSS][XXX]"); - } - - /** - * Get the identifier for this resource version. - * - * @return A String that is a legal xsd:ID value. - */ - public String getRid() { - return rid; - } - - /** - * Get the identifier of the previous version. - * - * @return A String that identifies the predecessor, or null if there isn't - * one. - */ - public String getPreviousRid() { - return previousRid; - } - - /** - * Set identifier of the previous version. - * - * @param previousRid - * A valid xsd:ID value. - */ - public void setPreviousRid(String previousRid) { - this.previousRid = previousRid; - } - - /** - * Get the version designation for this resource version. - * - * @return A version designation, or null it no value was set. - */ - public String getVersion() { - return version; - } - - /** - * Set the version designation for this resource version. It must adhere to - * one of the following schemes: - *
      - *
    • A positive integer value (1,2,...);
    • - *
    • A time instant (xsd:dateTime value) indicating when the version was - * created;
    • - *
    • One of the following tokens: "FIRST", "LAST", "PREVIOUS", "NEXT", - * "ALL" (primarily used to select resource versions).
    • - *
    - * - * @param version - * A version designator that adheres to a recognized scheme. - */ - public void setVersion(String version) { - this.version = version; - } - - public String getStart() { - return start; - } - - /** - * Set the starting instant of a temporal interval for selecting resource - * versions. An ending instant must also be specified to define a closed - * interval. - * - * @param startDateTime - * A lexical representation of an xsd:dateTime value. - */ - public void setStart(String startDateTime) { - try { - dateTimeFormatter.parse(startDateTime); - this.start = startDateTime; - } catch (DateTimeParseException e) { - } - } - - public String getEnd() { - return end; - } - - /** - * Set the ending instant of a temporal interval for selecting resource - * versions. - * - * @param endDateTime - * A lexical representation of an xsd:dateTime value. - */ - public void setEnd(String endDateTime) { - try { - dateTimeFormatter.parse(endDateTime); - this.end = endDateTime; - } catch (DateTimeParseException e) { - } - } - - @Override - public String toString() { - StringBuilder str = new StringBuilder(); - str.append("ResourceId [rid=").append(rid); - str.append(", previousRid=").append(previousRid); - str.append(", version=").append(version); - str.append(", start=").append(start); - str.append(", end=").append(end).append(']'); - return str.toString(); - } - - /** - * Creates a DOM Element representing this resource identifier. - * - * @return An empty fes:ResourceId element with the attributes set - * accordingly. - */ - public Element toElement() { - Element resourceId = XMLUtils.createElement(new QName(Namespaces.FES, FES2.RESOURCE_ID)); - resourceId.setAttribute("rid", rid); - if (null != previousRid) { - resourceId.setAttribute("previousRid", previousRid); - } - if (null != version) { - resourceId.setAttribute("version", version); - } - if (null != start) { - resourceId.setAttribute("startDate", start); - } - if (null != end) { - resourceId.setAttribute("endDate", end); - } - return resourceId; - } + private final String rid; + + private String previousRid; + + private String version; + + private String start; + + private String end; + + DateTimeFormatter dateTimeFormatter; + + /** + * Constructs a new identifier for the resource version. All members of the version + * chain have a unique rid value. + * @param rid A String that is a legal XML Schema ID (xsd:ID) value. + */ + public ResourceId(String rid) { + // check rid with regular expression? + this.rid = rid; + this.dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss[.SSS][XXX]"); + } + + /** + * Get the identifier for this resource version. + * @return A String that is a legal xsd:ID value. + */ + public String getRid() { + return rid; + } + + /** + * Get the identifier of the previous version. + * @return A String that identifies the predecessor, or null if there isn't one. + */ + public String getPreviousRid() { + return previousRid; + } + + /** + * Set identifier of the previous version. + * @param previousRid A valid xsd:ID value. + */ + public void setPreviousRid(String previousRid) { + this.previousRid = previousRid; + } + + /** + * Get the version designation for this resource version. + * @return A version designation, or null it no value was set. + */ + public String getVersion() { + return version; + } + + /** + * Set the version designation for this resource version. It must adhere to one of the + * following schemes: + *
      + *
    • A positive integer value (1,2,...);
    • + *
    • A time instant (xsd:dateTime value) indicating when the version was + * created;
    • + *
    • One of the following tokens: "FIRST", "LAST", "PREVIOUS", "NEXT", "ALL" + * (primarily used to select resource versions).
    • + *
    + * @param version A version designator that adheres to a recognized scheme. + */ + public void setVersion(String version) { + this.version = version; + } + + public String getStart() { + return start; + } + + /** + * Set the starting instant of a temporal interval for selecting resource versions. An + * ending instant must also be specified to define a closed interval. + * @param startDateTime A lexical representation of an xsd:dateTime value. + */ + public void setStart(String startDateTime) { + try { + dateTimeFormatter.parse(startDateTime); + this.start = startDateTime; + } + catch (DateTimeParseException e) { + } + } + + public String getEnd() { + return end; + } + + /** + * Set the ending instant of a temporal interval for selecting resource versions. + * @param endDateTime A lexical representation of an xsd:dateTime value. + */ + public void setEnd(String endDateTime) { + try { + dateTimeFormatter.parse(endDateTime); + this.end = endDateTime; + } + catch (DateTimeParseException e) { + } + } + + @Override + public String toString() { + StringBuilder str = new StringBuilder(); + str.append("ResourceId [rid=").append(rid); + str.append(", previousRid=").append(previousRid); + str.append(", version=").append(version); + str.append(", start=").append(start); + str.append(", end=").append(end).append(']'); + return str.toString(); + } + + /** + * Creates a DOM Element representing this resource identifier. + * @return An empty fes:ResourceId element with the attributes set accordingly. + */ + public Element toElement() { + Element resourceId = XMLUtils.createElement(new QName(Namespaces.FES, FES2.RESOURCE_ID)); + resourceId.setAttribute("rid", rid); + if (null != previousRid) { + resourceId.setAttribute("previousRid", previousRid); + } + if (null != version) { + resourceId.setAttribute("version", version); + } + if (null != start) { + resourceId.setAttribute("startDate", start); + } + if (null != end) { + resourceId.setAttribute("endDate", end); + } + return resourceId; + } } diff --git a/src/main/java/org/opengis/cite/iso19142/basic/filter/ResourceIdFilterTests.java b/src/main/java/org/opengis/cite/iso19142/basic/filter/ResourceIdFilterTests.java index cb7d41c5..fc3968b3 100644 --- a/src/main/java/org/opengis/cite/iso19142/basic/filter/ResourceIdFilterTests.java +++ b/src/main/java/org/opengis/cite/iso19142/basic/filter/ResourceIdFilterTests.java @@ -21,13 +21,12 @@ import jakarta.ws.rs.core.Response.Status; /** - * Tests the response to a GetFeature request that includes a ResourceId filter - * predicate, which is used to identify a feature instance (or some version of - * it). The resource identifier maps to the standard gml:id attribute common to - * all features; its value is assigned by the server when the instance is - * created. A feature identifier cannot be reused once it has been assigned, - * even after the feature is deleted. - * + * Tests the response to a GetFeature request that includes a ResourceId filter predicate, + * which is used to identify a feature instance (or some version of it). The resource + * identifier maps to the standard gml:id attribute common to all features; its value is + * assigned by the server when the instance is created. A feature identifier cannot be + * reused once it has been assigned, even after the feature is deleted. + * *

    * Sources *

    @@ -40,81 +39,72 @@ */ public class ResourceIdFilterTests extends QueryFilterFixture { - /** - * [{@code Test}] Submits a GetFeature request containing a ResourceId - * predicate with two resource identifiers. The response entity must include - * only instances of the requested type with matching gml:id attribute - * values. - * - * @param binding - * The ProtocolBinding to use for this request. - * @param featureType - * A QName representing the qualified name of some feature type. - */ - @Test(description = "See ISO 19142: 7.2.2; ISO 19143: 7.11", dataProvider = "protocol-featureType") - public void twoValidFeatureIdentifiers(ProtocolBinding binding, QName featureType) { - WFSMessage.appendSimpleQuery(this.reqEntity, featureType); - Set idSet = this.dataSampler.selectRandomFeatureIdentifiers(featureType, 2); - WFSMessage.addResourceIdPredicate(this.reqEntity, idSet); - Response rsp = wfsClient.submitRequest(reqEntity, binding); - this.rspEntity = extractBodyAsDocument(rsp); - Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - ETSAssert.assertDescendantElementCount(this.rspEntity, new QName(Namespaces.WFS, WFS2.MEMBER), idSet.size()); - } + /** + * [{@code Test}] Submits a GetFeature request containing a ResourceId predicate with + * two resource identifiers. The response entity must include only instances of the + * requested type with matching gml:id attribute values. + * @param binding The ProtocolBinding to use for this request. + * @param featureType A QName representing the qualified name of some feature type. + */ + @Test(description = "See ISO 19142: 7.2.2; ISO 19143: 7.11", dataProvider = "protocol-featureType") + public void twoValidFeatureIdentifiers(ProtocolBinding binding, QName featureType) { + WFSMessage.appendSimpleQuery(this.reqEntity, featureType); + Set idSet = this.dataSampler.selectRandomFeatureIdentifiers(featureType, 2); + WFSMessage.addResourceIdPredicate(this.reqEntity, idSet); + Response rsp = wfsClient.submitRequest(reqEntity, binding); + this.rspEntity = extractBodyAsDocument(rsp); + Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + ETSAssert.assertDescendantElementCount(this.rspEntity, new QName(Namespaces.WFS, WFS2.MEMBER), idSet.size()); + } - /** - * [{@code Test}] Submits a GetFeature request containing a ResourceId - * predicate with an unknown feature identifier. The response entity is - * expected to be a wfs:FeatureCollection that has no matching feature - * member. - * - * @param binding - * The ProtocolBinding to use for this request (e.g. GET, POST). - * @param featureType - * A QName representing the qualified name of some supported - * feature type. - */ - @Test(description = "See ISO 19142: 7.2.2, Table 8", dataProvider = "protocol-featureType") - public void unknownFeatureIdentifier(ProtocolBinding binding, QName featureType) { - WFSMessage.appendSimpleQuery(this.reqEntity, featureType); - Set idSet = new HashSet(); - idSet.add("test-" + UUID.randomUUID()); - WFSMessage.addResourceIdPredicate(this.reqEntity, idSet); - Response rsp = wfsClient.submitRequest(reqEntity, binding); - this.rspEntity = extractBodyAsDocument(rsp); - Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - ETSAssert.assertQualifiedName(this.rspEntity.getDocumentElement(), - new QName(Namespaces.WFS, WFS2.FEATURE_COLLECTION)); - ETSAssert.assertFeatureCount(this.rspEntity, featureType, 0); - } + /** + * [{@code Test}] Submits a GetFeature request containing a ResourceId predicate with + * an unknown feature identifier. The response entity is expected to be a + * wfs:FeatureCollection that has no matching feature member. + * @param binding The ProtocolBinding to use for this request (e.g. GET, POST). + * @param featureType A QName representing the qualified name of some supported + * feature type. + */ + @Test(description = "See ISO 19142: 7.2.2, Table 8", dataProvider = "protocol-featureType") + public void unknownFeatureIdentifier(ProtocolBinding binding, QName featureType) { + WFSMessage.appendSimpleQuery(this.reqEntity, featureType); + Set idSet = new HashSet(); + idSet.add("test-" + UUID.randomUUID()); + WFSMessage.addResourceIdPredicate(this.reqEntity, idSet); + Response rsp = wfsClient.submitRequest(reqEntity, binding); + this.rspEntity = extractBodyAsDocument(rsp); + Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + ETSAssert.assertQualifiedName(this.rspEntity.getDocumentElement(), + new QName(Namespaces.WFS, WFS2.FEATURE_COLLECTION)); + ETSAssert.assertFeatureCount(this.rspEntity, featureType, 0); + } + + /** + * [{@code Test}] If a feature instance identified by the RESOURCEID parameter is not + * of the type specified by the TYPENAMES parameter, the server shall raise an + * InvalidParameterValue exception where the "locator" attribute value shall be set to + * "RESOURCEID". + * @param featureType A QName representing the qualified name of some feature type. + * + * @see "OGC 09-025r1, cl. 7.9.2.4.1: typeNames parameter" + */ + @Test(description = "See ISO 19142: 7.9.2.4.1", dataProvider = "instantiated-feature-types") + public void inconsistentFeatureIdentifierAndType(QName featureType) { + WFSMessage.appendSimpleQuery(this.reqEntity, featureType); + Set idSet = new HashSet(); + String featureId = this.dataSampler.getFeatureIdNotOfType(featureType); + if (null == featureId) { + throw new SkipException("Unable to find id of feature instance that is NOT of type " + featureType); + } + idSet.add(featureId); + WFSMessage.addResourceIdPredicate(this.reqEntity, idSet); + Response rsp = wfsClient.submitRequest(reqEntity, ProtocolBinding.GET); + this.rspEntity = extractBodyAsDocument(rsp); + Assert.assertEquals(rsp.getStatus(), Status.BAD_REQUEST.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + ETSAssert.assertExceptionReport(this.rspEntity, "InvalidParameterValue", "RESOURCEID"); + } - /** - * [{@code Test}] If a feature instance identified by the RESOURCEID - * parameter is not of the type specified by the TYPENAMES parameter, the - * server shall raise an InvalidParameterValue exception where the "locator" - * attribute value shall be set to "RESOURCEID". - * - * @param featureType - * A QName representing the qualified name of some feature type. - * - * @see "OGC 09-025r1, cl. 7.9.2.4.1: typeNames parameter" - */ - @Test(description = "See ISO 19142: 7.9.2.4.1", dataProvider = "instantiated-feature-types") - public void inconsistentFeatureIdentifierAndType(QName featureType) { - WFSMessage.appendSimpleQuery(this.reqEntity, featureType); - Set idSet = new HashSet(); - String featureId = this.dataSampler.getFeatureIdNotOfType(featureType); - if (null == featureId) { - throw new SkipException("Unable to find id of feature instance that is NOT of type " + featureType); - } - idSet.add(featureId); - WFSMessage.addResourceIdPredicate(this.reqEntity, idSet); - Response rsp = wfsClient.submitRequest(reqEntity, ProtocolBinding.GET); - this.rspEntity = extractBodyAsDocument(rsp); - Assert.assertEquals(rsp.getStatus(), Status.BAD_REQUEST.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - ETSAssert.assertExceptionReport(this.rspEntity, "InvalidParameterValue", "RESOURCEID"); - } } diff --git a/src/main/java/org/opengis/cite/iso19142/basic/filter/package-info.java b/src/main/java/org/opengis/cite/iso19142/basic/filter/package-info.java index 3a9079f3..bac0b091 100644 --- a/src/main/java/org/opengis/cite/iso19142/basic/filter/package-info.java +++ b/src/main/java/org/opengis/cite/iso19142/basic/filter/package-info.java @@ -1,8 +1,8 @@ /** - * This package includes tests covering the use of filter predicates in query - * expressions. The following filter capabilities must be implemented at the - * "Basic WFS" conformance level: - * + * This package includes tests covering the use of filter predicates in query expressions. + * The following filter capabilities must be implemented at the "Basic WFS" conformance + * level: + * *
      *
    • ImplementsAdHocQuery
    • *
    • ImplementsResourceId
    • @@ -13,7 +13,9 @@ *
    • ImplementsMinimumXPath
    • *
    * - *

    Sources

    + *

    + * Sources + *

    *
      *
    • ISO 19142:2010, cl. A.1.2: Basic WFS
    • *
    • ISO 19142:2010, Table 1: Conformance classes (FES Conformance Tests)
    • diff --git a/src/main/java/org/opengis/cite/iso19142/basic/filter/spatial/BBOXTests.java b/src/main/java/org/opengis/cite/iso19142/basic/filter/spatial/BBOXTests.java index 09ae996f..f88ff476 100644 --- a/src/main/java/org/opengis/cite/iso19142/basic/filter/spatial/BBOXTests.java +++ b/src/main/java/org/opengis/cite/iso19142/basic/filter/spatial/BBOXTests.java @@ -40,9 +40,8 @@ import jakarta.ws.rs.core.Response.Status; /** - * Tests the response to a GetFeature request that includes a BBOX predicate. - * All conforming "Basic WFS" implementations must support this spatial - * operator. + * Tests the response to a GetFeature request that includes a BBOX predicate. All + * conforming "Basic WFS" implementations must support this spatial operator. * *

      * Sources @@ -54,255 +53,249 @@ */ public class BBOXTests extends QueryFilterFixture { - private static final String XSLT_ENV2POLYGON = "/org/opengis/cite/iso19142/util/bbox2polygon.xsl"; - // avoids rounding issues in the GML encoding - public static final double ENVELOPE_EXPANSION = 0.01; - private XSTypeDefinition gmlGeomBaseType; + private static final String XSLT_ENV2POLYGON = "/org/opengis/cite/iso19142/util/bbox2polygon.xsl"; - /** - * Creates an XSTypeDefinition object representing the - * gml:AbstractGeometryType definition. - */ - @BeforeClass - public void createGeometryBaseType() { - this.gmlGeomBaseType = getModel().getTypeDefinition("AbstractGeometryType", Namespaces.GML); - } + // avoids rounding issues in the GML encoding + public static final double ENVELOPE_EXPANSION = 0.01; - /** - * [{@code Test}] Submits a GetFeature request with a non-specific BBOX - * predicate. If no value reference is specified the predicate is applied to - * all spatial properties. The response entity (wfs:FeatureCollection) must - * be schema-valid and contain only instances of the requested type that - * satisfy the spatial predicate. - * - * @param binding - * The ProtocolBinding to use for this request. - * @param featureType - * A QName representing the qualified name of some feature type. - * - * @see "ISO 19143:2010, 7.8.3.2: BBOX operator" - */ - @Test(description = "See ISO 19143: 7.8.3.2", dataProvider = "protocol-featureType") - public void nonSpecificBBOX(ProtocolBinding binding, QName featureType) { - List geomProps = AppSchemaUtils.getFeaturePropertiesByType(getModel(), featureType, - gmlGeomBaseType); - if (geomProps.isEmpty()) { - throw new SkipException("Feature type has no geometry properties: " + featureType); - } - Envelope extent = this.dataSampler.getSpatialExtent(getModel(), featureType); - Document gmlEnv = null; - try { - gmlEnv = envelopeAsGML(extent); - } catch (Exception e) { - throw new RuntimeException("Could not create envelope for feature type: " + featureType); + private XSTypeDefinition gmlGeomBaseType; + + /** + * Creates an XSTypeDefinition object representing the gml:AbstractGeometryType + * definition. + */ + @BeforeClass + public void createGeometryBaseType() { + this.gmlGeomBaseType = getModel().getTypeDefinition("AbstractGeometryType", Namespaces.GML); + } + + /** + * [{@code Test}] Submits a GetFeature request with a non-specific BBOX predicate. If + * no value reference is specified the predicate is applied to all spatial properties. + * The response entity (wfs:FeatureCollection) must be schema-valid and contain only + * instances of the requested type that satisfy the spatial predicate. + * @param binding The ProtocolBinding to use for this request. + * @param featureType A QName representing the qualified name of some feature type. + * + * @see "ISO 19143:2010, 7.8.3.2: BBOX operator" + */ + @Test(description = "See ISO 19143: 7.8.3.2", dataProvider = "protocol-featureType") + public void nonSpecificBBOX(ProtocolBinding binding, QName featureType) { + List geomProps = AppSchemaUtils.getFeaturePropertiesByType(getModel(), featureType, + gmlGeomBaseType); + if (geomProps.isEmpty()) { + throw new SkipException("Feature type has no geometry properties: " + featureType); + } + Envelope extent = this.dataSampler.getSpatialExtent(getModel(), featureType); + Document gmlEnv = null; + try { + gmlEnv = envelopeAsGML(extent); + } + catch (Exception e) { + throw new RuntimeException("Could not create envelope for feature type: " + featureType); + } + WFSMessage.appendSimpleQuery(this.reqEntity, featureType); + addBBOXPredicate(this.reqEntity, gmlEnv.getDocumentElement(), null); + URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.GET_FEATURE, binding); + Response rsp = wfsClient.submitRequest(new DOMSource(reqEntity), binding, endpoint); + this.rspEntity = extractBodyAsDocument(rsp); + Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + Map nsBindings = new HashMap(); + nsBindings.put(Namespaces.WFS, "wfs"); + NodeList members; + String xpath = "//wfs:member/*"; + try { + members = XMLUtils.evaluateXPath(this.rspEntity, xpath, nsBindings); + } + catch (XPathExpressionException e) { + throw new RuntimeException(e); } - WFSMessage.appendSimpleQuery(this.reqEntity, featureType); - addBBOXPredicate(this.reqEntity, gmlEnv.getDocumentElement(), null); - URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.GET_FEATURE, binding); - Response rsp = wfsClient.submitRequest(new DOMSource(reqEntity), binding, endpoint); - this.rspEntity = extractBodyAsDocument(rsp); - Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - Map nsBindings = new HashMap(); - nsBindings.put(Namespaces.WFS, "wfs"); - NodeList members; - String xpath = "//wfs:member/*"; - try { - members = XMLUtils.evaluateXPath(this.rspEntity, xpath, nsBindings); - } catch (XPathExpressionException e) { - throw new RuntimeException(e); - } - Assert.assertTrue(members.getLength() > 0, ErrorMessage.format(ErrorMessageKeys.XPATH_RESULT, - this.rspEntity.getDocumentElement().getNodeName(), xpath)); - for (int i = 0; i < members.getLength(); i++) { - ETSAssert.assertQualifiedName(members.item(i), featureType); - } - } + Assert.assertTrue(members.getLength() > 0, ErrorMessage.format(ErrorMessageKeys.XPATH_RESULT, + this.rspEntity.getDocumentElement().getNodeName(), xpath)); + for (int i = 0; i < members.getLength(); i++) { + ETSAssert.assertQualifiedName(members.item(i), featureType); + } + } - /** - * [{@code Test}] Submits a GetFeature request with a BBOX predicate - * referring to a valid geometry property. The response shall contain only - * features possessing a geometry value that spatially interacts (i.e. is - * not disjoint) with the given envelope. - * - *

      - * Sources - *

      - *
        - *
      • ISO 19143:2010, cl. A.7: Test cases for minimum spatial filter
      • - *
      • ISO 19143:2010, 7.8.3.2: BBOX operator
      • - *
      - * - * @param binding - * The ProtocolBinding to use for this request. - * @param featureType - * A QName representing the qualified name of some feature type. - * - */ - @Test(description = "See ISO 19143: 7.8.3.2, A.7", dataProvider = "protocol-featureType") - public void bboxWithDefaultExtent(ProtocolBinding binding, QName featureType) { - List geomProps = AppSchemaUtils.getFeaturePropertiesByType(getModel(), featureType, - gmlGeomBaseType); - if (geomProps.isEmpty()) { - throw new SkipException("Feature type has no geometry properties: " + featureType); - } - XSElementDeclaration geomProp = geomProps.get(0); - Element valueRef = WFSMessage.createValueReference(geomProp); - WFSMessage.appendSimpleQuery(this.reqEntity, featureType); - Envelope extent = this.dataSampler.getSpatialExtent(getModel(), featureType); - Document gmlEnv = null; - try { - gmlEnv = envelopeAsGML(extent); - } catch (Exception e) { - throw new RuntimeException("Could not create envelope for feature type: " + featureType); + /** + * [{@code Test}] Submits a GetFeature request with a BBOX predicate referring to a + * valid geometry property. The response shall contain only features possessing a + * geometry value that spatially interacts (i.e. is not disjoint) with the given + * envelope. + * + *

      + * Sources + *

      + *
        + *
      • ISO 19143:2010, cl. A.7: Test cases for minimum spatial filter
      • + *
      • ISO 19143:2010, 7.8.3.2: BBOX operator
      • + *
      + * @param binding The ProtocolBinding to use for this request. + * @param featureType A QName representing the qualified name of some feature type. + * + */ + @Test(description = "See ISO 19143: 7.8.3.2, A.7", dataProvider = "protocol-featureType") + public void bboxWithDefaultExtent(ProtocolBinding binding, QName featureType) { + List geomProps = AppSchemaUtils.getFeaturePropertiesByType(getModel(), featureType, + gmlGeomBaseType); + if (geomProps.isEmpty()) { + throw new SkipException("Feature type has no geometry properties: " + featureType); + } + XSElementDeclaration geomProp = geomProps.get(0); + Element valueRef = WFSMessage.createValueReference(geomProp); + WFSMessage.appendSimpleQuery(this.reqEntity, featureType); + Envelope extent = this.dataSampler.getSpatialExtent(getModel(), featureType); + Document gmlEnv = null; + try { + gmlEnv = envelopeAsGML(extent); + } + catch (Exception e) { + throw new RuntimeException("Could not create envelope for feature type: " + featureType); + } + addBBOXPredicate(this.reqEntity, gmlEnv.getDocumentElement(), valueRef); + Response rsp = wfsClient.submitRequest(reqEntity, binding); + this.rspEntity = extractBodyAsDocument(rsp); + Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + Map nsBindings = new HashMap(); + nsBindings.put(featureType.getNamespaceURI(), "ns1"); + String xpath; + if (!geomProp.getNamespace().equals(featureType.getNamespaceURI())) { + nsBindings.put(geomProp.getNamespace(), "ns2"); + xpath = String.format("//ns1:%s/ns2:%s/*[1]", featureType.getLocalPart(), geomProp.getName()); + } + else { + xpath = String.format("//ns1:%s/ns1:%s/*[1]", featureType.getLocalPart(), geomProp.getName()); } - addBBOXPredicate(this.reqEntity, gmlEnv.getDocumentElement(), valueRef); - Response rsp = wfsClient.submitRequest(reqEntity, binding); - this.rspEntity = extractBodyAsDocument(rsp); - Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - Map nsBindings = new HashMap(); - nsBindings.put(featureType.getNamespaceURI(), "ns1"); - String xpath; - if (!geomProp.getNamespace().equals(featureType.getNamespaceURI())) { - nsBindings.put(geomProp.getNamespace(), "ns2"); - xpath = String.format("//ns1:%s/ns2:%s/*[1]", featureType.getLocalPart(), geomProp.getName()); - } else { - xpath = String.format("//ns1:%s/ns1:%s/*[1]", featureType.getLocalPart(), geomProp.getName()); - } - NodeList geometryNodes; - try { - geometryNodes = XMLUtils.evaluateXPath(this.rspEntity, xpath, nsBindings); - } catch (XPathExpressionException e) { - throw new RuntimeException(e); - } - Assert.assertTrue(geometryNodes.getLength() > 0, ErrorMessage.format(ErrorMessageKeys.XPATH_RESULT, - this.rspEntity.getDocumentElement().getNodeName(), xpath)); - Document gmlPolygon = XMLUtils.transform(new StreamSource(getClass().getResourceAsStream(XSLT_ENV2POLYGON)), - gmlEnv, null); - for (int i = 0; i < geometryNodes.getLength(); i++) { - Element geometry = (Element) geometryNodes.item(i); - if (geometry.getElementsByTagNameNS(Namespaces.GML, "PolygonPatch").getLength() > 0) { - // gml:Surface comprised of one or more PolygonPatch elements - geometry = surfaceToPolygon(geometry); - } - boolean intersects = TopologicalRelationships.isSpatiallyRelated(SpatialOperator.INTERSECTS, - gmlPolygon.getDocumentElement(), geometry); - Assert.assertTrue(intersects, ErrorMessage.format(ErrorMessageKeys.PREDICATE_NOT_SATISFIED, "BBOX", - XMLUtils.writeNodeToString(gmlPolygon), XMLUtils.writeNodeToString(geometry))); - } - } + NodeList geometryNodes; + try { + geometryNodes = XMLUtils.evaluateXPath(this.rspEntity, xpath, nsBindings); + } + catch (XPathExpressionException e) { + throw new RuntimeException(e); + } + Assert.assertTrue(geometryNodes.getLength() > 0, ErrorMessage.format(ErrorMessageKeys.XPATH_RESULT, + this.rspEntity.getDocumentElement().getNodeName(), xpath)); + Document gmlPolygon = XMLUtils.transform(new StreamSource(getClass().getResourceAsStream(XSLT_ENV2POLYGON)), + gmlEnv, null); + for (int i = 0; i < geometryNodes.getLength(); i++) { + Element geometry = (Element) geometryNodes.item(i); + if (geometry.getElementsByTagNameNS(Namespaces.GML, "PolygonPatch").getLength() > 0) { + // gml:Surface comprised of one or more PolygonPatch elements + geometry = surfaceToPolygon(geometry); + } + boolean intersects = TopologicalRelationships.isSpatiallyRelated(SpatialOperator.INTERSECTS, + gmlPolygon.getDocumentElement(), geometry); + Assert.assertTrue(intersects, ErrorMessage.format(ErrorMessageKeys.PREDICATE_NOT_SATISFIED, "BBOX", + XMLUtils.writeNodeToString(gmlPolygon), XMLUtils.writeNodeToString(geometry))); + } + } - /** - * Work around for Extents{@link #envelopeAsGML(Envelope)} only using 2 decimals - * - * @param extent the envelope - * @return the envelope as GML - */ - public Document envelopeAsGML(Envelope extent) { - double[] low = new double[extent.getDimension()]; - double[] high = new double[extent.getDimension()]; - for (int i = 0; i < extent.getDimension(); i++) { - low[i] = extent.getMinimum(i) - ENVELOPE_EXPANSION; - high[i] = extent.getMaximum(i) + ENVELOPE_EXPANSION; - } - GeneralEnvelope expanded = new GeneralEnvelope(low, high); - expanded.setCoordinateReferenceSystem(extent.getCoordinateReferenceSystem()); - return Extents.envelopeAsGML(expanded); - } + /** + * Work around for Extents{@link #envelopeAsGML(Envelope)} only using 2 decimals + * @param extent the envelope + * @return the envelope as GML + */ + public Document envelopeAsGML(Envelope extent) { + double[] low = new double[extent.getDimension()]; + double[] high = new double[extent.getDimension()]; + for (int i = 0; i < extent.getDimension(); i++) { + low[i] = extent.getMinimum(i) - ENVELOPE_EXPANSION; + high[i] = extent.getMaximum(i) + ENVELOPE_EXPANSION; + } + GeneralEnvelope expanded = new GeneralEnvelope(low, high); + expanded.setCoordinateReferenceSystem(extent.getCoordinateReferenceSystem()); + return Extents.envelopeAsGML(expanded); + } - /** - * [{@code Test}] Submits a GetFeature request where the BBOX predicate - * refers to a feature property (gml:description) that is not - * geometry-valued. An exception is expected in response with status code - * 400 and exception code {@code InvalidParameterValue}. - * - * @param featureType - * A QName representing the qualified name of a feature type for - * which instances exist. - * - * @see "ISO 19142:2010, 11.4: GetFeature - Exceptions" - * @see "ISO 19143:2010, 8.3: Exceptions" - */ - @Test(description = "See ISO 19142: 11.4; ISO 19143: 8.3", dataProvider = "instantiated-feature-types") - public void invalidGeometryOperand(QName featureType) { - XSElementDeclaration gmlDesc = getModel().getElementDeclaration("description", Namespaces.GML); - Element valueRef = WFSMessage.createValueReference(gmlDesc); - WFSMessage.appendSimpleQuery(this.reqEntity, featureType); - Envelope extent = featureInfo.get(featureType).getSpatialExtent(); - Document gmlEnv = null; - try { - gmlEnv = envelopeAsGML(extent); - } catch (Exception e) { - throw new RuntimeException("Could not create envelope for feature type: " + featureType); + /** + * [{@code Test}] Submits a GetFeature request where the BBOX predicate refers to a + * feature property (gml:description) that is not geometry-valued. An exception is + * expected in response with status code 400 and exception code + * {@code InvalidParameterValue}. + * @param featureType A QName representing the qualified name of a feature type for + * which instances exist. + * + * @see "ISO 19142:2010, 11.4: GetFeature - Exceptions" + * @see "ISO 19143:2010, 8.3: Exceptions" + */ + @Test(description = "See ISO 19142: 11.4; ISO 19143: 8.3", dataProvider = "instantiated-feature-types") + public void invalidGeometryOperand(QName featureType) { + XSElementDeclaration gmlDesc = getModel().getElementDeclaration("description", Namespaces.GML); + Element valueRef = WFSMessage.createValueReference(gmlDesc); + WFSMessage.appendSimpleQuery(this.reqEntity, featureType); + Envelope extent = featureInfo.get(featureType).getSpatialExtent(); + Document gmlEnv = null; + try { + gmlEnv = envelopeAsGML(extent); + } + catch (Exception e) { + throw new RuntimeException("Could not create envelope for feature type: " + featureType); + } + addBBOXPredicate(this.reqEntity, gmlEnv.getDocumentElement(), valueRef); + Response rsp = wfsClient.submitRequest(reqEntity, ProtocolBinding.ANY); + this.rspEntity = rsp.readEntity(Document.class); + // https://github.com/opengeospatial/ets-wfs20/issues/147 + // Agreed solution to this issue is to also allow OperationProcessingFailed + // (Status Code 403) as valid exception type. + int statusCode = rsp.getStatus(); + if (statusCode == Status.BAD_REQUEST.getStatusCode()) { + String xpath = "//ows:Exception[@exceptionCode='InvalidParameterValue']"; + ETSAssert.assertXPath(xpath, this.rspEntity, null); } - addBBOXPredicate(this.reqEntity, gmlEnv.getDocumentElement(), valueRef); - Response rsp = wfsClient.submitRequest(reqEntity, ProtocolBinding.ANY); - this.rspEntity = rsp.readEntity(Document.class); - //https://github.com/opengeospatial/ets-wfs20/issues/147 - //Agreed solution to this issue is to also allow OperationProcessingFailed (Status Code 403) as valid exception type. - int statusCode = rsp.getStatus(); - if(statusCode == Status.BAD_REQUEST.getStatusCode()) { - String xpath = "//ows:Exception[@exceptionCode='InvalidParameterValue']"; - ETSAssert.assertXPath(xpath, this.rspEntity, null); - } else if(statusCode == Status.FORBIDDEN.getStatusCode()) { - String xpath = "//ows:Exception[@exceptionCode='OperationProcessingFailed']"; - ETSAssert.assertXPath(xpath, this.rspEntity, null); - } else { - Assert.fail(ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - } - } + else if (statusCode == Status.FORBIDDEN.getStatusCode()) { + String xpath = "//ows:Exception[@exceptionCode='OperationProcessingFailed']"; + ETSAssert.assertXPath(xpath, this.rspEntity, null); + } + else { + Assert.fail(ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + } + } + + // bboxWithOtherCRS + // bboxWithUnsupportedCRS - // bboxWithOtherCRS - // bboxWithUnsupportedCRS + /** + * Replaces gml:Surface elements having a single gml:PolygonPatch with a gml:Polygon + * element. Such a simplification facilitates a binding to JTS geometry objects. + * @param geometry A GML geometry collection. + * @return A new DOM Element containing a simplified representation of the original + * geometry collection. + * + * @see "ISO 19125-1: Geographic information -- Simple feature access -- Part 1: + * Common architecture" + */ + Element surfaceToPolygon(Element geometry) { + Document result = XMLUtils.transform(new StreamSource(getClass().getResourceAsStream("surface2polygon.xsl")), + geometry, null); + return result.getDocumentElement(); + } - /** - * Replaces gml:Surface elements having a single gml:PolygonPatch with a - * gml:Polygon element. Such a simplification facilitates a binding to JTS - * geometry objects. - * - * @param geometry - * A GML geometry collection. - * @return A new DOM Element containing a simplified representation of the - * original geometry collection. - * - * @see "ISO 19125-1: Geographic information -- Simple feature access -- - * Part 1: Common architecture" - */ - Element surfaceToPolygon(Element geometry) { - Document result = XMLUtils.transform(new StreamSource(getClass().getResourceAsStream("surface2polygon.xsl")), - geometry, null); - return result.getDocumentElement(); - } + /** + * Adds a BBOX spatial predicate to a GetFeature request entity. If the envelope has + * no spatial reference (srsName) it is assumed to be the default CRS specified in the + * capabilities document. + * @param request The request entity (/wfs:GetFeature). + * @param envelope A DOM Element representing a gml:Envelope. + * @param valueRef An Element (fes:ValueReference) that specifies the spatial property + * to check. If it is {@code null}, the predicate applies to all spatial properties. + */ + void addBBOXPredicate(Document request, Element envelope, Element valueRef) { + if (!request.getDocumentElement().getLocalName().equals(WFS2.GET_FEATURE)) { + throw new IllegalArgumentException( + "Not a GetFeature request: " + request.getDocumentElement().getNodeName()); + } + Element queryElem = (Element) request.getElementsByTagNameNS(Namespaces.WFS, WFS2.QUERY_ELEM).item(0); + Element filter = request.createElementNS(Namespaces.FES, "fes:Filter"); + queryElem.appendChild(filter); + Element bbox = request.createElementNS(Namespaces.FES, "fes:BBOX"); + filter.appendChild(bbox); + if (null != valueRef) { + bbox.appendChild(request.importNode(valueRef, true)); + } + // import envelope node to avoid WRONG_DOCUMENT_ERR + bbox.appendChild(request.importNode(envelope, true)); + } - /** - * Adds a BBOX spatial predicate to a GetFeature request entity. If the - * envelope has no spatial reference (srsName) it is assumed to be the - * default CRS specified in the capabilities document. - * - * @param request - * The request entity (/wfs:GetFeature). - * @param envelope - * A DOM Element representing a gml:Envelope. - * @param valueRef - * An Element (fes:ValueReference) that specifies the spatial - * property to check. If it is {@code null}, the predicate - * applies to all spatial properties. - */ - void addBBOXPredicate(Document request, Element envelope, Element valueRef) { - if (!request.getDocumentElement().getLocalName().equals(WFS2.GET_FEATURE)) { - throw new IllegalArgumentException( - "Not a GetFeature request: " + request.getDocumentElement().getNodeName()); - } - Element queryElem = (Element) request.getElementsByTagNameNS(Namespaces.WFS, WFS2.QUERY_ELEM).item(0); - Element filter = request.createElementNS(Namespaces.FES, "fes:Filter"); - queryElem.appendChild(filter); - Element bbox = request.createElementNS(Namespaces.FES, "fes:BBOX"); - filter.appendChild(bbox); - if (null != valueRef) { - bbox.appendChild(request.importNode(valueRef, true)); - } - // import envelope node to avoid WRONG_DOCUMENT_ERR - bbox.appendChild(request.importNode(envelope, true)); - } } diff --git a/src/main/java/org/opengis/cite/iso19142/basic/filter/spatial/IntersectsTests.java b/src/main/java/org/opengis/cite/iso19142/basic/filter/spatial/IntersectsTests.java index e73805e0..fcafdbb4 100644 --- a/src/main/java/org/opengis/cite/iso19142/basic/filter/spatial/IntersectsTests.java +++ b/src/main/java/org/opengis/cite/iso19142/basic/filter/spatial/IntersectsTests.java @@ -49,311 +49,298 @@ import jakarta.ws.rs.core.Response.Status; /** - * Tests the response to a GetFeature request that includes the spatial - * predicate Intersects. This predicate is the logical complement of - * the Disjoint predicate; that is: - * + * Tests the response to a GetFeature request that includes the spatial predicate + * Intersects. This predicate is the logical complement of the Disjoint + * predicate; that is: + * *
        * a.Intersects(b) ⇔ ! a.Disjoint(b)
        * 
      - * + * * @see "OGC 09-026r2, A.8: Test cases for spatial filter" * @see "ISO 19125-1, 6.1.15.3" */ public class IntersectsTests extends QueryFilterFixture { - public final static String IMPL_SPATIAL_FILTER = "ImplementsSpatialFilter"; - private static final String INTERSECTS_OP = "Intersects"; - private Map> allGeomProperties; - private Set geomOperands; - private static final String XSLT_ENV2POLYGON = "/org/opengis/cite/iso19142/util/bbox2polygon.xsl"; + public final static String IMPL_SPATIAL_FILTER = "ImplementsSpatialFilter"; - /** - * Checks the value of the filter constraint {@value #IMPL_SPATIAL_FILTER} - * in the capabilities document. All tests are skipped if this is not - * "TRUE". - * - * @param testContext - * Information about the test run environment. - */ - @BeforeTest - public void implementsSpatialFilter(ITestContext testContext) { - this.wfsMetadata = (Document) testContext.getSuite().getAttribute(SuiteAttribute.TEST_SUBJECT.getName()); - String xpath = String.format("//fes:Constraint[@name='%s' and (ows:DefaultValue = 'TRUE')]", - IMPL_SPATIAL_FILTER); - NodeList result; - try { - result = XMLUtils.evaluateXPath(this.wfsMetadata, xpath, null); - } catch (XPathExpressionException e) { - throw new AssertionError(e.getMessage()); - } - if (result.getLength() == 0) { - throw new SkipException(ErrorMessage.format(ErrorMessageKeys.NOT_IMPLEMENTED, IMPL_SPATIAL_FILTER)); - } - } + private static final String INTERSECTS_OP = "Intersects"; - /** - * Finds the geometry properties for all feature types recognized by the - * SUT. - */ - @BeforeClass - public void findAllGeometryProperties() { - XSTypeDefinition geomBaseType = getModel().getTypeDefinition("AbstractGeometryType", Namespaces.GML); - this.allGeomProperties = new HashMap<>(); - for (QName featureType : this.featureTypes) { - List geomProps = AppSchemaUtils.getFeaturePropertiesByType(getModel(), featureType, - geomBaseType); - if (!geomProps.isEmpty()) { - this.allGeomProperties.put(featureType, geomProps); - } - } - } + private Map> allGeomProperties; - /** - * Checks if the spatial operator "Intersects" is implemented and which - * geometry operands are supported. If it's not implemented, all tests for - * this operator are skipped. - */ - @BeforeClass - public void implementsIntersectsOp() { - Map> capabilities = ServiceMetadataUtils.getSpatialCapabilities(this.wfsMetadata); - if (!capabilities.containsKey(SpatialOperator.INTERSECTS)) { - throw new SkipException(ErrorMessage.format(ErrorMessageKeys.NOT_IMPLEMENTED, SpatialOperator.INTERSECTS)); - } - this.geomOperands = capabilities.get(SpatialOperator.INTERSECTS); - } + private Set geomOperands; - /** - * [{@code Test}] Submits a GetFeature request containing an Intersects - * predicate with a gml:Polygon operand. The response entity must be - * schema-valid and contain only matching instances. - * - * @param binding - * The ProtocolBinding to use for this request. - * @param featureType - * A QName representing the qualified name of some feature type. - */ - @Test(description = "See OGC 09-026r2, A.8", dataProvider = "protocol-featureType") - public void intersectsPolygon(ProtocolBinding binding, QName featureType) throws XPathExpressionException { - if (!this.allGeomProperties.keySet().contains(featureType)) { - throw new SkipException("Feature type has no geometry properties: " + featureType); - } - QName gmlPolygon = new QName(Namespaces.GML, GML32.POLYGON); - if (!this.geomOperands.contains(gmlPolygon)) { - throw new SkipException("Unsupported geometry operand: " + gmlPolygon); - } - Envelope extent = this.dataSampler.getSpatialExtent(getModel(), featureType); - Document gmlEnv = null; - try { - gmlEnv = Extents.envelopeAsGML(extent); - } catch (Exception e) { - throw new RuntimeException("Could not create envelope for feature type: " + featureType); + private static final String XSLT_ENV2POLYGON = "/org/opengis/cite/iso19142/util/bbox2polygon.xsl"; + + /** + * Checks the value of the filter constraint {@value #IMPL_SPATIAL_FILTER} in the + * capabilities document. All tests are skipped if this is not "TRUE". + * @param testContext Information about the test run environment. + */ + @BeforeTest + public void implementsSpatialFilter(ITestContext testContext) { + this.wfsMetadata = (Document) testContext.getSuite().getAttribute(SuiteAttribute.TEST_SUBJECT.getName()); + String xpath = String.format("//fes:Constraint[@name='%s' and (ows:DefaultValue = 'TRUE')]", + IMPL_SPATIAL_FILTER); + NodeList result; + try { + result = XMLUtils.evaluateXPath(this.wfsMetadata, xpath, null); + } + catch (XPathExpressionException e) { + throw new AssertionError(e.getMessage()); + } + if (result.getLength() == 0) { + throw new SkipException(ErrorMessage.format(ErrorMessageKeys.NOT_IMPLEMENTED, IMPL_SPATIAL_FILTER)); + } + } + + /** + * Finds the geometry properties for all feature types recognized by the SUT. + */ + @BeforeClass + public void findAllGeometryProperties() { + XSTypeDefinition geomBaseType = getModel().getTypeDefinition("AbstractGeometryType", Namespaces.GML); + this.allGeomProperties = new HashMap<>(); + for (QName featureType : this.featureTypes) { + List geomProps = AppSchemaUtils.getFeaturePropertiesByType(getModel(), featureType, + geomBaseType); + if (!geomProps.isEmpty()) { + this.allGeomProperties.put(featureType, geomProps); + } + } + } + + /** + * Checks if the spatial operator "Intersects" is implemented and which geometry + * operands are supported. If it's not implemented, all tests for this operator are + * skipped. + */ + @BeforeClass + public void implementsIntersectsOp() { + Map> capabilities = ServiceMetadataUtils.getSpatialCapabilities(this.wfsMetadata); + if (!capabilities.containsKey(SpatialOperator.INTERSECTS)) { + throw new SkipException(ErrorMessage.format(ErrorMessageKeys.NOT_IMPLEMENTED, SpatialOperator.INTERSECTS)); + } + this.geomOperands = capabilities.get(SpatialOperator.INTERSECTS); + } + + /** + * [{@code Test}] Submits a GetFeature request containing an Intersects predicate with + * a gml:Polygon operand. The response entity must be schema-valid and contain only + * matching instances. + * @param binding The ProtocolBinding to use for this request. + * @param featureType A QName representing the qualified name of some feature type. + */ + @Test(description = "See OGC 09-026r2, A.8", dataProvider = "protocol-featureType") + public void intersectsPolygon(ProtocolBinding binding, QName featureType) throws XPathExpressionException { + if (!this.allGeomProperties.keySet().contains(featureType)) { + throw new SkipException("Feature type has no geometry properties: " + featureType); + } + QName gmlPolygon = new QName(Namespaces.GML, GML32.POLYGON); + if (!this.geomOperands.contains(gmlPolygon)) { + throw new SkipException("Unsupported geometry operand: " + gmlPolygon); + } + Envelope extent = this.dataSampler.getSpatialExtent(getModel(), featureType); + Document gmlEnv = null; + try { + gmlEnv = Extents.envelopeAsGML(extent); + } + catch (Exception e) { + throw new RuntimeException("Could not create envelope for feature type: " + featureType); + } + Element gmlPolygonElem = XMLUtils + .transform(new StreamSource(getClass().getResourceAsStream(XSLT_ENV2POLYGON)), gmlEnv, null) + .getDocumentElement(); + List geomProps = this.allGeomProperties.get(featureType); + Iterator itr = geomProps.iterator(); + XSElementDeclaration geomProperty = null; + XSElementDeclaration value = null; + while (itr.hasNext()) { + geomProperty = itr.next(); + value = AppSchemaUtils.getComplexPropertyValue(geomProperty); + // this also filters out MultiPoint geometry types, see + // https://github.com/opengeospatial/ets-wfs20/issues/236 + if (!value.getName().contains(GML32.POINT)) + break; } - Element gmlPolygonElem = XMLUtils - .transform(new StreamSource(getClass().getResourceAsStream(XSLT_ENV2POLYGON)), gmlEnv, null) - .getDocumentElement(); - List geomProps = this.allGeomProperties.get(featureType); - Iterator itr = geomProps.iterator(); - XSElementDeclaration geomProperty = null; - XSElementDeclaration value = null; - while (itr.hasNext()) { - geomProperty = itr.next(); - value = AppSchemaUtils.getComplexPropertyValue(geomProperty); - // this also filters out MultiPoint geometry types, see https://github.com/opengeospatial/ets-wfs20/issues/236 - if (!value.getName().contains(GML32.POINT)) - break; - } - if(value.getName().contains(GML32.POINT)){ - // ignore point property--unlikely to intersect line - throw new SkipException("Intersects tests are not supported for point geometry types."); - } - this.reqEntity = buildGetFeatureRequest(featureType, INTERSECTS_OP, geomProperty, gmlPolygonElem); - URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.GET_FEATURE, binding); - Response rsp = wfsClient.submitRequest(new DOMSource(reqEntity), binding, endpoint); - this.rspEntity = extractBodyAsDocument(rsp); - Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - NodeList geomNodes = findGeometryPropertyValues(featureType, geomProperty); - if (geomNodes.getLength() == 0) { - TestSuiteLogger.log(Level.INFO, String.format( - "intersectsPolygon: No values found for geometry property %s (in %s)", geomProperty, featureType)); - } - for (int i = 0; i < geomNodes.getLength(); i++) { - Node geom = geomNodes.item(i); - if (GmlUtils.checkForAbstractSurfacePatchTypes(geom)) { - geom = GmlUtils.handleAbstractSurfacePatch(geom); - } - boolean intersects = TopologicalRelationships.isSpatiallyRelated(SpatialOperator.INTERSECTS, gmlPolygonElem, - geom); - Assert.assertTrue(intersects, ErrorMessage.format(ErrorMessageKeys.PREDICATE_NOT_SATISFIED, INTERSECTS_OP, - XMLUtils.writeNodeToString(gmlPolygonElem), XMLUtils.writeNodeToString(geom))); - } - } + if (value.getName().contains(GML32.POINT)) { + // ignore point property--unlikely to intersect line + throw new SkipException("Intersects tests are not supported for point geometry types."); + } + this.reqEntity = buildGetFeatureRequest(featureType, INTERSECTS_OP, geomProperty, gmlPolygonElem); + URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.GET_FEATURE, binding); + Response rsp = wfsClient.submitRequest(new DOMSource(reqEntity), binding, endpoint); + this.rspEntity = extractBodyAsDocument(rsp); + Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + NodeList geomNodes = findGeometryPropertyValues(featureType, geomProperty); + if (geomNodes.getLength() == 0) { + TestSuiteLogger.log(Level.INFO, String.format( + "intersectsPolygon: No values found for geometry property %s (in %s)", geomProperty, featureType)); + } + for (int i = 0; i < geomNodes.getLength(); i++) { + Node geom = geomNodes.item(i); + if (GmlUtils.checkForAbstractSurfacePatchTypes(geom)) { + geom = GmlUtils.handleAbstractSurfacePatch(geom); + } + boolean intersects = TopologicalRelationships.isSpatiallyRelated(SpatialOperator.INTERSECTS, gmlPolygonElem, + geom); + Assert.assertTrue(intersects, ErrorMessage.format(ErrorMessageKeys.PREDICATE_NOT_SATISFIED, INTERSECTS_OP, + XMLUtils.writeNodeToString(gmlPolygonElem), XMLUtils.writeNodeToString(geom))); + } + } - /** - * [{@code Test}] Submits a GetFeature request containing an - * Intersects predicate with a gml:LineString or gml:Curve - * (LineStringSegment) operand. The response entity must be schema-valid and - * contain only matching instances. - * - * @param binding - * The ProtocolBinding to use for this request. - * @param featureType - * A QName representing the qualified name of some feature type. - */ - @Test(description = "See OGC 09-026r2, A.8", dataProvider = "protocol-featureType") - public void intersectsCurve(ProtocolBinding binding, QName featureType) throws XPathExpressionException { - if (!this.allGeomProperties.keySet().contains(featureType)) { - throw new SkipException("Feature type has no geometry properties: " + featureType); - } - QName gmlCurve = new QName(Namespaces.GML, GML32.LINE_STRING); - if (!this.geomOperands.contains(gmlCurve)) { - gmlCurve = new QName(Namespaces.GML, GML32.CURVE); - if (!this.geomOperands.contains(gmlCurve)) { - throw new SkipException("Unsupported geometry operands: gml:LineString, gml:Curve"); - } - } - Envelope extent = this.dataSampler.getSpatialExtent(getModel(), featureType); - Document gmlEnv = null; - try { - gmlEnv = Extents.envelopeAsGML(extent); - } catch (Exception e) { - throw new RuntimeException("Could not create envelope for feature type: " + featureType); + /** + * [{@code Test}] Submits a GetFeature request containing an + * Intersects predicate with a gml:LineString or gml:Curve + * (LineStringSegment) operand. The response entity must be schema-valid and contain + * only matching instances. + * @param binding The ProtocolBinding to use for this request. + * @param featureType A QName representing the qualified name of some feature type. + */ + @Test(description = "See OGC 09-026r2, A.8", dataProvider = "protocol-featureType") + public void intersectsCurve(ProtocolBinding binding, QName featureType) throws XPathExpressionException { + if (!this.allGeomProperties.keySet().contains(featureType)) { + throw new SkipException("Feature type has no geometry properties: " + featureType); + } + QName gmlCurve = new QName(Namespaces.GML, GML32.LINE_STRING); + if (!this.geomOperands.contains(gmlCurve)) { + gmlCurve = new QName(Namespaces.GML, GML32.CURVE); + if (!this.geomOperands.contains(gmlCurve)) { + throw new SkipException("Unsupported geometry operands: gml:LineString, gml:Curve"); + } + } + Envelope extent = this.dataSampler.getSpatialExtent(getModel(), featureType); + Document gmlEnv = null; + try { + gmlEnv = Extents.envelopeAsGML(extent); + } + catch (Exception e) { + throw new RuntimeException("Could not create envelope for feature type: " + featureType); + } + Element gmlCurveElem = XMLUtils + .transform(new StreamSource(getClass().getResourceAsStream("envelopeTocurve.xsl")), gmlEnv, + Collections.singletonMap("curveType", gmlCurve.getLocalPart())) + .getDocumentElement(); + List geomProps = this.allGeomProperties.get(featureType); + Iterator itr = geomProps.iterator(); + XSElementDeclaration geomProperty = null; + XSElementDeclaration value = null; + while (itr.hasNext()) { + geomProperty = itr.next(); + value = AppSchemaUtils.getComplexPropertyValue(geomProperty); + // this also filters out MultiPoint geometry types, see + // https://github.com/opengeospatial/ets-wfs20/issues/236 + if (!value.getName().contains(GML32.POINT)) + break; } - Element gmlCurveElem = XMLUtils - .transform(new StreamSource(getClass().getResourceAsStream("envelopeTocurve.xsl")), gmlEnv, - Collections.singletonMap("curveType", gmlCurve.getLocalPart())) - .getDocumentElement(); - List geomProps = this.allGeomProperties.get(featureType); - Iterator itr = geomProps.iterator(); - XSElementDeclaration geomProperty = null; - XSElementDeclaration value = null; - while (itr.hasNext()) { - geomProperty = itr.next(); - value = AppSchemaUtils.getComplexPropertyValue(geomProperty); - // this also filters out MultiPoint geometry types, see https://github.com/opengeospatial/ets-wfs20/issues/236 - if (!value.getName().contains(GML32.POINT)) - break; - } - if(value.getName().contains(GML32.POINT)){ - // ignore point property--unlikely to intersect line - throw new SkipException("Intersects tests are not supported for point geometry types."); - } + if (value.getName().contains(GML32.POINT)) { + // ignore point property--unlikely to intersect line + throw new SkipException("Intersects tests are not supported for point geometry types."); + } - this.reqEntity = buildGetFeatureRequest(featureType, INTERSECTS_OP, geomProperty, gmlCurveElem); - URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.GET_FEATURE, binding); - Response rsp = wfsClient.submitRequest(new DOMSource(this.reqEntity), binding, endpoint); - this.rspEntity = extractBodyAsDocument(rsp); - Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - NodeList geomNodes = findGeometryPropertyValues(featureType, geomProperty); - if (geomNodes.getLength() == 0) { - TestSuiteLogger.log(Level.INFO, String.format( - "intersectsCurve: No values found for geometry property %s (in %s)", geomProperty, featureType)); - } - for (int i = 0; i < geomNodes.getLength(); i++) { - Node geom = geomNodes.item(i); - if (GmlUtils.checkForAbstractSurfacePatchTypes(geom)) { - geom = GmlUtils.handleAbstractSurfacePatch(geom); - } - boolean intersects = TopologicalRelationships.isSpatiallyRelated(SpatialOperator.INTERSECTS, gmlCurveElem, - geom); - Assert.assertTrue(intersects, ErrorMessage.format(ErrorMessageKeys.PREDICATE_NOT_SATISFIED, INTERSECTS_OP, - XMLUtils.writeNodeToString(gmlCurveElem), XMLUtils.writeNodeToString(geom))); - } - } + this.reqEntity = buildGetFeatureRequest(featureType, INTERSECTS_OP, geomProperty, gmlCurveElem); + URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.GET_FEATURE, binding); + Response rsp = wfsClient.submitRequest(new DOMSource(this.reqEntity), binding, endpoint); + this.rspEntity = extractBodyAsDocument(rsp); + Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + NodeList geomNodes = findGeometryPropertyValues(featureType, geomProperty); + if (geomNodes.getLength() == 0) { + TestSuiteLogger.log(Level.INFO, String.format( + "intersectsCurve: No values found for geometry property %s (in %s)", geomProperty, featureType)); + } + for (int i = 0; i < geomNodes.getLength(); i++) { + Node geom = geomNodes.item(i); + if (GmlUtils.checkForAbstractSurfacePatchTypes(geom)) { + geom = GmlUtils.handleAbstractSurfacePatch(geom); + } + boolean intersects = TopologicalRelationships.isSpatiallyRelated(SpatialOperator.INTERSECTS, gmlCurveElem, + geom); + Assert.assertTrue(intersects, ErrorMessage.format(ErrorMessageKeys.PREDICATE_NOT_SATISFIED, INTERSECTS_OP, + XMLUtils.writeNodeToString(gmlCurveElem), XMLUtils.writeNodeToString(geom))); + } + } - // intersectsCurve (corner points of bbox) - other CRS - // intersectsCircle (corner points of bbox) - default CRS + // intersectsCurve (corner points of bbox) - other CRS + // intersectsCircle (corner points of bbox) - default CRS - /** - * Builds a simple GetFeature request entity that contains a basic filter - * expression. - * - * @param featureType - * The name of the feature type. - * @param operator - * The name of the filter predicate (operator). - * @param propertyDecl - * The declaration of the feature property to be queried. - * @param literal - * An element representing the literal value to match. - * @return A Document containing a request entity with wfs:GetFeature as the - * document element. - */ - Document buildGetFeatureRequest(QName featureType, String operator, XSElementDeclaration propertyDecl, - Element literal) { - Document entity = WFSMessage.createRequestEntity(GET_FEATURE_MINIMAL, this.wfsVersion); - WFSMessage.appendSimpleQuery(entity, featureType); - Element valueRef = WFSMessage.createValueReference(propertyDecl); - addSpatialPredicate(entity, operator, literal, valueRef); - return entity; - } + /** + * Builds a simple GetFeature request entity that contains a basic filter expression. + * @param featureType The name of the feature type. + * @param operator The name of the filter predicate (operator). + * @param propertyDecl The declaration of the feature property to be queried. + * @param literal An element representing the literal value to match. + * @return A Document containing a request entity with wfs:GetFeature as the document + * element. + */ + Document buildGetFeatureRequest(QName featureType, String operator, XSElementDeclaration propertyDecl, + Element literal) { + Document entity = WFSMessage.createRequestEntity(GET_FEATURE_MINIMAL, this.wfsVersion); + WFSMessage.appendSimpleQuery(entity, featureType); + Element valueRef = WFSMessage.createValueReference(propertyDecl); + addSpatialPredicate(entity, operator, literal, valueRef); + return entity; + } - /** - * Adds a spatial predicate to a GetFeature request entity. If the given - * geometry element has no spatial reference (srsName) it is assumed to use - * the default CRS specified in the capabilities document. - * - * @param request - * The request entity (wfs:GetFeature). - * @param spatialOp - * The name of a spatial operator. - * @param gmlGeom - * A DOM Element representing a GML geometry. - * @param valueRef - * An Element (fes:ValueReference) that specifies the spatial - * property to check. If it is {@code null}, the predicate - * applies to all spatial properties. - */ - void addSpatialPredicate(Document request, String spatialOp, Element gmlGeom, Element valueRef) { - if (!request.getDocumentElement().getLocalName().equals(WFS2.GET_FEATURE)) { - throw new IllegalArgumentException( - "Not a GetFeature request: " + request.getDocumentElement().getNodeName()); - } - Element queryElem = (Element) request.getElementsByTagNameNS(Namespaces.WFS, WFS2.QUERY_ELEM).item(0); - if (null == queryElem) { - throw new IllegalArgumentException("No Query element found in GetFeature request entity."); - } - Element filter = request.createElementNS(Namespaces.FES, "fes:Filter"); - queryElem.appendChild(filter); - Element predicate = request.createElementNS(Namespaces.FES, "fes:" + spatialOp); - filter.appendChild(predicate); - if (null != valueRef) { - predicate.appendChild(request.importNode(valueRef, true)); - } - // import geometry element to avoid WRONG_DOCUMENT_ERR - predicate.appendChild(request.importNode(gmlGeom, true)); - } + /** + * Adds a spatial predicate to a GetFeature request entity. If the given geometry + * element has no spatial reference (srsName) it is assumed to use the default CRS + * specified in the capabilities document. + * @param request The request entity (wfs:GetFeature). + * @param spatialOp The name of a spatial operator. + * @param gmlGeom A DOM Element representing a GML geometry. + * @param valueRef An Element (fes:ValueReference) that specifies the spatial property + * to check. If it is {@code null}, the predicate applies to all spatial properties. + */ + void addSpatialPredicate(Document request, String spatialOp, Element gmlGeom, Element valueRef) { + if (!request.getDocumentElement().getLocalName().equals(WFS2.GET_FEATURE)) { + throw new IllegalArgumentException( + "Not a GetFeature request: " + request.getDocumentElement().getNodeName()); + } + Element queryElem = (Element) request.getElementsByTagNameNS(Namespaces.WFS, WFS2.QUERY_ELEM).item(0); + if (null == queryElem) { + throw new IllegalArgumentException("No Query element found in GetFeature request entity."); + } + Element filter = request.createElementNS(Namespaces.FES, "fes:Filter"); + queryElem.appendChild(filter); + Element predicate = request.createElementNS(Namespaces.FES, "fes:" + spatialOp); + filter.appendChild(predicate); + if (null != valueRef) { + predicate.appendChild(request.importNode(valueRef, true)); + } + // import geometry element to avoid WRONG_DOCUMENT_ERR + predicate.appendChild(request.importNode(gmlGeom, true)); + } - private NodeList findGeometryPropertyValues( QName featureType, XSElementDeclaration geomProperty ) - throws XPathExpressionException { - String featureTypeNs = featureType.getNamespaceURI(); - String featureTypePrefix = getFeatureTypePrefix( featureType ); - String geomPropNs = geomProperty.getNamespace(); - String geomPropPrefix = getGeomPropPrefix( featureTypeNs, featureTypePrefix, geomPropNs ); - String xpath = String.format( "//%s:%s/%s:%s/*", featureTypePrefix, featureType.getLocalPart(), geomPropPrefix, - geomProperty.getName() ); - Map nsBindings = new HashMap<>(); - nsBindings.put( featureTypeNs, featureTypePrefix ); - if ( !geomPropPrefix.equals( featureTypePrefix ) ) - nsBindings.put( geomPropNs, geomPropPrefix ); - return XMLUtils.evaluateXPath( this.rspEntity, xpath, nsBindings ); - } + private NodeList findGeometryPropertyValues(QName featureType, XSElementDeclaration geomProperty) + throws XPathExpressionException { + String featureTypeNs = featureType.getNamespaceURI(); + String featureTypePrefix = getFeatureTypePrefix(featureType); + String geomPropNs = geomProperty.getNamespace(); + String geomPropPrefix = getGeomPropPrefix(featureTypeNs, featureTypePrefix, geomPropNs); + String xpath = String.format("//%s:%s/%s:%s/*", featureTypePrefix, featureType.getLocalPart(), geomPropPrefix, + geomProperty.getName()); + Map nsBindings = new HashMap<>(); + nsBindings.put(featureTypeNs, featureTypePrefix); + if (!geomPropPrefix.equals(featureTypePrefix)) + nsBindings.put(geomPropNs, geomPropPrefix); + return XMLUtils.evaluateXPath(this.rspEntity, xpath, nsBindings); + } - private String getFeatureTypePrefix( QName featureType ) { - String prefix = featureType.getPrefix(); - if ( prefix != null && !prefix.isEmpty() ) - return prefix; - return "t1"; - } + private String getFeatureTypePrefix(QName featureType) { + String prefix = featureType.getPrefix(); + if (prefix != null && !prefix.isEmpty()) + return prefix; + return "t1"; + } - private String getGeomPropPrefix( String featureTypeNs, String featureTypePrefix, String geomPropNs ) { - if ( geomPropNs != null && geomPropNs.equals( featureTypeNs ) || ( geomPropNs == null && featureTypeNs == null ) ) - return featureTypePrefix; - return "t2"; - } + private String getGeomPropPrefix(String featureTypeNs, String featureTypePrefix, String geomPropNs) { + if (geomPropNs != null && geomPropNs.equals(featureTypeNs) || (geomPropNs == null && featureTypeNs == null)) + return featureTypePrefix; + return "t2"; + } } diff --git a/src/main/java/org/opengis/cite/iso19142/basic/filter/spatial/package-info.java b/src/main/java/org/opengis/cite/iso19142/basic/filter/spatial/package-info.java index 5578d22b..97b301c7 100644 --- a/src/main/java/org/opengis/cite/iso19142/basic/filter/spatial/package-info.java +++ b/src/main/java/org/opengis/cite/iso19142/basic/filter/spatial/package-info.java @@ -1,9 +1,9 @@ /** - * This package includes tests covering the use of spatial operators in query - * expressions. The BBOX predicate must be implemented at the "Basic WFS" conformance - * level. Support for at least one other spatial operator is required to meet the - * requirements of the Spatial Filter conformance class. - * + * This package includes tests covering the use of spatial operators in query expressions. + * The BBOX predicate must be implemented at the "Basic WFS" conformance level. Support + * for at least one other spatial operator is required to meet the requirements of the + * Spatial Filter conformance class. + * *
        *
      • BBOX (required for "Basic WFS" conformance)
      • *
      • Equals
      • @@ -18,11 +18,14 @@ *
      • DWithin
      • *
      * - *

      Sources

      + *

      + * Sources + *

      *
        *
      • OGC 09-026r2, Table 1: FE conformance classes
      • *
      • OGC 09-026r2, 7.8: Spatial operators
      • - *
      • OGC 06-103r4, 6.1.15.3: Named spatial relationship predicates based on the DE-9IM
      • + *
      • OGC 06-103r4, 6.1.15.3: Named spatial relationship predicates based on the + * DE-9IM
      • *
      */ package org.opengis.cite.iso19142.basic.filter.spatial; \ No newline at end of file diff --git a/src/main/java/org/opengis/cite/iso19142/basic/filter/temporal/AbstractTemporalTest.java b/src/main/java/org/opengis/cite/iso19142/basic/filter/temporal/AbstractTemporalTest.java index 77ddc06f..eaffb2a4 100644 --- a/src/main/java/org/opengis/cite/iso19142/basic/filter/temporal/AbstractTemporalTest.java +++ b/src/main/java/org/opengis/cite/iso19142/basic/filter/temporal/AbstractTemporalTest.java @@ -15,68 +15,69 @@ */ public abstract class AbstractTemporalTest extends QueryFilterFixture { - private static final Logger LOGR = Logger.getLogger( AbstractTemporalTest.class.getPackage().getName() ); - - public TemporalProperty findTemporalProperty( QName featureType ) { - List temporalProperties = findTemporalProperties( featureType ); - if ( temporalProperties.isEmpty() ) { - throw new SkipException( "Feature type has no temporal properties: " + featureType ); - } - - TemporalProperty temporalExtent = null; - - try { - temporalExtent = findTemporalExtent( featureType, temporalProperties ); - } catch (Exception e) { - if(e instanceof SkipException) { - throw e; - } + private static final Logger LOGR = Logger.getLogger(AbstractTemporalTest.class.getPackage().getName()); + + public TemporalProperty findTemporalProperty(QName featureType) { + List temporalProperties = findTemporalProperties(featureType); + if (temporalProperties.isEmpty()) { + throw new SkipException("Feature type has no temporal properties: " + featureType); + } + + TemporalProperty temporalExtent = null; + + try { + temporalExtent = findTemporalExtent(featureType, temporalProperties); + } + catch (Exception e) { + if (e instanceof SkipException) { + throw e; + } + } + + if (temporalExtent == null) + throw new SkipException("Feature type + " + featureType + + " has at least one temporal properties but an extent could not be calculated (e.g. all properties are nill). "); + return temporalExtent; + } + + private TemporalProperty findTemporalExtent(QName featureType, List temporalProperties) { + for (XSElementDeclaration temporalProp : temporalProperties) { + try { + Period temporalExtent = this.dataSampler.getTemporalExtentOfProperty(getModel(), featureType, + temporalProp); + if (temporalExtent != null) + return new TemporalProperty(temporalProp, temporalExtent); + } + catch (Exception e) { + LOGR.warning("Could not calculate the extent of the temporal property " + temporalProp + + " of the feature type " + featureType); + if (e instanceof SkipException) { + throw e; + } + } + } + return null; + } + + class TemporalProperty { + + private XSElementDeclaration property; + + private Period extent; + + public TemporalProperty(XSElementDeclaration property, Period extent) { + this.property = property; + this.extent = extent; } - - if ( temporalExtent == null ) - throw new SkipException( - "Feature type + " - + featureType - + " has at least one temporal properties but an extent could not be calculated (e.g. all properties are nill). " ); - return temporalExtent; - } - - private TemporalProperty findTemporalExtent( QName featureType, List temporalProperties ) { - for ( XSElementDeclaration temporalProp : temporalProperties ) { - try { - Period temporalExtent = this.dataSampler.getTemporalExtentOfProperty(getModel(), featureType, - temporalProp); - if ( temporalExtent != null ) - return new TemporalProperty( temporalProp, temporalExtent ); - } catch ( Exception e ) { - LOGR.warning( "Could not calculate the extent of the temporal property " + temporalProp - + " of the feature type " + featureType ); - if(e instanceof SkipException) { - throw e; - } - } - } - return null; - } - - class TemporalProperty { - - private XSElementDeclaration property; - - private Period extent; - - public TemporalProperty( XSElementDeclaration property, Period extent ) { - this.property = property; - this.extent = extent; - } - - public XSElementDeclaration getProperty() { - return property; - } - - public Period getExtent() { - return extent; - } - } + + public XSElementDeclaration getProperty() { + return property; + } + + public Period getExtent() { + return extent; + } + + } } diff --git a/src/main/java/org/opengis/cite/iso19142/basic/filter/temporal/AfterTests.java b/src/main/java/org/opengis/cite/iso19142/basic/filter/temporal/AfterTests.java index a3338527..998c1628 100644 --- a/src/main/java/org/opengis/cite/iso19142/basic/filter/temporal/AfterTests.java +++ b/src/main/java/org/opengis/cite/iso19142/basic/filter/temporal/AfterTests.java @@ -32,16 +32,14 @@ import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response.Status; - /** - * Tests the response to a GetFeature request that includes the temporal - * predicate After. Either operand may represent an instant or a - * period. - * + * Tests the response to a GetFeature request that includes the temporal predicate + * After. Either operand may represent an instant or a period. + * *

      - * The following figure illustrates the relationship. A solid line denotes a - * temporal property; a dashed line denotes a literal time value that specifies - * the temporal extent of interest. + * The following figure illustrates the relationship. A solid line denotes a temporal + * property; a dashed line denotes a literal time value that specifies the temporal extent + * of interest. *

      * * After relationship @@ -49,135 +47,126 @@ */ public class AfterTests extends AbstractTemporalTest { - private static final String AFTER_OP = "After"; + private static final String AFTER_OP = "After"; + + /** + * Checks if the temporal operator "After" is supported. If not, the relevant tests + * are skipped. + */ + @BeforeClass + public void implementsAfterOperator() { + if (!ServiceMetadataUtils.implementsTemporalOperator(this.wfsMetadata, AFTER_OP)) { + throw new SkipException(ErrorMessage.format(ErrorMessageKeys.NOT_IMPLEMENTED, "After operator")); + } + } - /** - * Checks if the temporal operator "After" is supported. If not, the - * relevant tests are skipped. - */ - @BeforeClass - public void implementsAfterOperator() { - if (!ServiceMetadataUtils.implementsTemporalOperator(this.wfsMetadata, AFTER_OP)) { - throw new SkipException(ErrorMessage.format(ErrorMessageKeys.NOT_IMPLEMENTED, "After operator")); - } - } + /** + * [{@code Test}] Submits a GetFeature request containing the After + * temporal predicate with a literal gml:TimePeriod value. The response entity must + * contain only feature instances having a temporal property value that is after the + * specified period. + * @param binding The ProtocolBinding to use for this request. + * @param featureType A QName representing the qualified name of some feature type. + */ + @Test(description = "See ISO 19143: A.10", dataProvider = "protocol-featureType") + public void afterPeriod(ProtocolBinding binding, QName featureType) { + TemporalProperty temporalProperty = findTemporalProperty(featureType); - /** - * [{@code Test}] Submits a GetFeature request containing the - * After temporal predicate with a literal gml:TimePeriod - * value. The response entity must contain only feature instances having a - * temporal property value that is after the specified period. - * - * @param binding - * The ProtocolBinding to use for this request. - * @param featureType - * A QName representing the qualified name of some feature type. - */ - @Test(description = "See ISO 19143: A.10", dataProvider = "protocol-featureType") - public void afterPeriod(ProtocolBinding binding, QName featureType) { - TemporalProperty temporalProperty = findTemporalProperty( featureType ); + List subIntervals = TemporalUtils.splitInterval(temporalProperty.getExtent(), 3); + Period firstSubInterval = subIntervals.get(0); + Document gmlTimeLiteral = TimeUtils.periodAsGMLSubtractOneDay(firstSubInterval); + WFSMessage.appendSimpleQuery(this.reqEntity, featureType); + Element valueRef = WFSMessage.createValueReference(temporalProperty.getProperty()); + WFSMessage.addTemporalPredicate(this.reqEntity, AFTER_OP, gmlTimeLiteral, valueRef); + Response rsp = wfsClient.getFeature(new DOMSource(this.reqEntity), binding); + this.rspEntity = extractBodyAsDocument(rsp); + Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + List temporalNodes = TemporalQuery.extractTemporalNodes(this.rspEntity, temporalProperty.getProperty(), + getModel()); + assertAfter(temporalNodes, temporalProperty.getProperty(), gmlTimeLiteral); + } - List subIntervals = TemporalUtils.splitInterval(temporalProperty.getExtent(), 3); - Period firstSubInterval = subIntervals.get(0); - Document gmlTimeLiteral = TimeUtils.periodAsGMLSubtractOneDay(firstSubInterval); - WFSMessage.appendSimpleQuery(this.reqEntity, featureType); - Element valueRef = WFSMessage.createValueReference(temporalProperty.getProperty()); - WFSMessage.addTemporalPredicate(this.reqEntity, AFTER_OP, gmlTimeLiteral, valueRef); - Response rsp = wfsClient.getFeature(new DOMSource(this.reqEntity), binding); - this.rspEntity = extractBodyAsDocument(rsp); - Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - List temporalNodes = TemporalQuery.extractTemporalNodes(this.rspEntity, temporalProperty.getProperty(), getModel()); - assertAfter(temporalNodes, temporalProperty.getProperty(), gmlTimeLiteral); - } + /** + * [{@code Test}] Submits a GetFeature request containing the After + * temporal predicate with a literal gml:TimeInstant value. The response entity must + * contain only feature instances having a temporal property value that is after the + * specified (UTC) instant. + * @param binding The ProtocolBinding to use for this request. + * @param featureType A QName representing the qualified name of some feature type. + */ + @Test(description = "See ISO 19143: A.10", dataProvider = "protocol-featureType") + public void afterInstant(ProtocolBinding binding, QName featureType) { + TemporalProperty temporalProperty = findTemporalProperty(featureType); - /** - * [{@code Test}] Submits a GetFeature request containing the - * After temporal predicate with a literal gml:TimeInstant - * value. The response entity must contain only feature instances having a - * temporal property value that is after the specified (UTC) instant. - * - * @param binding - * The ProtocolBinding to use for this request. - * @param featureType - * A QName representing the qualified name of some feature type. - */ - @Test(description = "See ISO 19143: A.10", dataProvider = "protocol-featureType") - public void afterInstant(ProtocolBinding binding, QName featureType) { - TemporalProperty temporalProperty = findTemporalProperty( featureType ); + List subIntervals = TemporalUtils.splitInterval(temporalProperty.getExtent(), 3); + // end of first sub-interval + Instant instant = subIntervals.get(0).getEnding(); + Document gmlTimeLiteral = TimeUtils.instantAsGMLSubtractOneDay(instant, ZoneOffset.UTC); + WFSMessage.appendSimpleQuery(this.reqEntity, featureType); + Element valueRef = WFSMessage.createValueReference(temporalProperty.getProperty()); + WFSMessage.addTemporalPredicate(this.reqEntity, AFTER_OP, gmlTimeLiteral, valueRef); + Response rsp = wfsClient.getFeature(new DOMSource(this.reqEntity), binding); + this.rspEntity = extractBodyAsDocument(rsp); + Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + List temporalNodes = TemporalQuery.extractTemporalNodes(this.rspEntity, temporalProperty.getProperty(), + getModel()); + assertAfter(temporalNodes, temporalProperty.getProperty(), gmlTimeLiteral); + } - List subIntervals = TemporalUtils.splitInterval(temporalProperty.getExtent(), 3); - // end of first sub-interval - Instant instant = subIntervals.get(0).getEnding(); - Document gmlTimeLiteral = TimeUtils.instantAsGMLSubtractOneDay(instant, ZoneOffset.UTC); - WFSMessage.appendSimpleQuery(this.reqEntity, featureType); - Element valueRef = WFSMessage.createValueReference(temporalProperty.getProperty()); - WFSMessage.addTemporalPredicate(this.reqEntity, AFTER_OP, gmlTimeLiteral, valueRef); - Response rsp = wfsClient.getFeature(new DOMSource(this.reqEntity), binding); - this.rspEntity = extractBodyAsDocument(rsp); - Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - List temporalNodes = TemporalQuery.extractTemporalNodes(this.rspEntity, temporalProperty.getProperty(), getModel()); - assertAfter(temporalNodes, temporalProperty.getProperty(), gmlTimeLiteral); - } + /** + * [{@code Test}] Submits a GetFeature request containing the After + * temporal predicate with a literal gml:TimeInstant value that is offset from UTC. + * The response entity must contain only feature instances having a temporal property + * value that is after the specified instant. + * @param binding The ProtocolBinding to use for this request. + * @param featureType A QName representing the qualified name of some feature type. + */ + @Test(description = "See ISO 19143: A.10", dataProvider = "protocol-featureType") + public void afterInstantWithOffset(ProtocolBinding binding, QName featureType) { + TemporalProperty temporalProperty = findTemporalProperty(featureType); - /** - * [{@code Test}] Submits a GetFeature request containing the - * After temporal predicate with a literal gml:TimeInstant - * value that is offset from UTC. The response entity must contain only - * feature instances having a temporal property value that is after the - * specified instant. - * - * @param binding - * The ProtocolBinding to use for this request. - * @param featureType - * A QName representing the qualified name of some feature type. - */ - @Test(description = "See ISO 19143: A.10", dataProvider = "protocol-featureType") - public void afterInstantWithOffset(ProtocolBinding binding, QName featureType) { - TemporalProperty temporalProperty = findTemporalProperty( featureType ); + List subIntervals = TemporalUtils.splitInterval(temporalProperty.getExtent(), 3); + // end of first sub-interval with UTC offset +09:00 (Japan) + Instant instant = subIntervals.get(0).getEnding(); + Document gmlTimeLiteral = TimeUtils.instantAsGMLSubtractOneDay(instant, ZoneOffset.ofHours(9)); + WFSMessage.appendSimpleQuery(this.reqEntity, featureType); + Element valueRef = WFSMessage.createValueReference(temporalProperty.getProperty()); + WFSMessage.addTemporalPredicate(this.reqEntity, AFTER_OP, gmlTimeLiteral, valueRef); + Response rsp = wfsClient.getFeature(new DOMSource(this.reqEntity), binding); + this.rspEntity = extractBodyAsDocument(rsp); + Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + List temporalNodes = TemporalQuery.extractTemporalNodes(this.rspEntity, temporalProperty.getProperty(), + getModel()); + assertAfter(temporalNodes, temporalProperty.getProperty(), gmlTimeLiteral); + } - List subIntervals = TemporalUtils.splitInterval(temporalProperty.getExtent(), 3); - // end of first sub-interval with UTC offset +09:00 (Japan) - Instant instant = subIntervals.get(0).getEnding(); - Document gmlTimeLiteral = TimeUtils.instantAsGMLSubtractOneDay(instant, ZoneOffset.ofHours(9)); - WFSMessage.appendSimpleQuery(this.reqEntity, featureType); - Element valueRef = WFSMessage.createValueReference(temporalProperty.getProperty()); - WFSMessage.addTemporalPredicate(this.reqEntity, AFTER_OP, gmlTimeLiteral, valueRef); - Response rsp = wfsClient.getFeature(new DOMSource(this.reqEntity), binding); - this.rspEntity = extractBodyAsDocument(rsp); - Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - List temporalNodes = TemporalQuery.extractTemporalNodes(this.rspEntity, temporalProperty.getProperty(), getModel()); - assertAfter(temporalNodes, temporalProperty.getProperty(), gmlTimeLiteral); - } + /** + * Asserts that all temporal values in the given list occur after the specified GML + * temporal value. + * @param temporalNodes A list of simple or complex temporal values. + * @param propertyDecl An element declaration for a temporal property. + * @param gmlTimeLiteral A document that contains a GML representation of an instant + * or period. + */ + void assertAfter(List temporalNodes, XSElementDeclaration propertyDecl, Document gmlTimeLiteral) { + Assert.assertFalse(temporalNodes.isEmpty(), + String.format("No temporal values found in results: property is %s.", propertyDecl)); + TemporalGeometricPrimitive t2 = GmlUtils.gmlToTemporalGeometricPrimitive(gmlTimeLiteral.getDocumentElement()); + XSTypeDefinition typeDef = propertyDecl.getTypeDefinition(); + for (Node timeNode : temporalNodes) { + TemporalGeometricPrimitive t1 = null; + if (typeDef.getTypeCategory() == XSTypeDefinition.SIMPLE_TYPE || ((XSComplexTypeDefinition) typeDef) + .getContentType() == XSComplexTypeDefinition.CONTENTTYPE_SIMPLE) { + t1 = TemporalQuery.parseTemporalValue(timeNode.getTextContent(), typeDef); + } + else { + t1 = GmlUtils.gmlToTemporalGeometricPrimitive((Element) timeNode); + } + TemporalUtils.assertTemporalRelation(RelativePosition.AFTER, t1, t2); + } + } - /** - * Asserts that all temporal values in the given list occur after the - * specified GML temporal value. - * - * @param temporalNodes - * A list of simple or complex temporal values. - * @param propertyDecl - * An element declaration for a temporal property. - * @param gmlTimeLiteral - * A document that contains a GML representation of an instant or - * period. - */ - void assertAfter(List temporalNodes, XSElementDeclaration propertyDecl, Document gmlTimeLiteral) { - Assert.assertFalse(temporalNodes.isEmpty(), - String.format("No temporal values found in results: property is %s.", propertyDecl)); - TemporalGeometricPrimitive t2 = GmlUtils.gmlToTemporalGeometricPrimitive(gmlTimeLiteral.getDocumentElement()); - XSTypeDefinition typeDef = propertyDecl.getTypeDefinition(); - for (Node timeNode : temporalNodes) { - TemporalGeometricPrimitive t1 = null; - if ( typeDef.getTypeCategory() == XSTypeDefinition.SIMPLE_TYPE - || ( (XSComplexTypeDefinition) typeDef ).getContentType() == XSComplexTypeDefinition.CONTENTTYPE_SIMPLE ) { - t1 = TemporalQuery.parseTemporalValue(timeNode.getTextContent(), typeDef); - } else { - t1 = GmlUtils.gmlToTemporalGeometricPrimitive((Element) timeNode); - } - TemporalUtils.assertTemporalRelation(RelativePosition.AFTER, t1, t2); - } - } } diff --git a/src/main/java/org/opengis/cite/iso19142/basic/filter/temporal/BeforeTests.java b/src/main/java/org/opengis/cite/iso19142/basic/filter/temporal/BeforeTests.java index ac66fde8..6fee164e 100644 --- a/src/main/java/org/opengis/cite/iso19142/basic/filter/temporal/BeforeTests.java +++ b/src/main/java/org/opengis/cite/iso19142/basic/filter/temporal/BeforeTests.java @@ -31,88 +31,83 @@ import jakarta.ws.rs.core.Response.Status; /** - * Tests the response to a GetFeature request that includes the temporal - * predicate Before. Either operand may represent an instant or a - * period. + * Tests the response to a GetFeature request that includes the temporal predicate + * Before. Either operand may represent an instant or a period. * *

      - * The following figure illustrates the relationship. A solid line denotes a - * temporal property; a dashed line denotes a literal time value that specifies - * the temporal extent of interest. + * The following figure illustrates the relationship. A solid line denotes a temporal + * property; a dashed line denotes a literal time value that specifies the temporal extent + * of interest. *

      * * Before relationship */ public class BeforeTests extends AbstractTemporalTest { - private static final String BEFORE_OP = "Before"; + private static final String BEFORE_OP = "Before"; - /** - * Checks if the temporal operator "Before" is supported. If not, the - * relevant tests are skipped. - */ - @BeforeClass - public void implementsBeforeOperator() { - if (!ServiceMetadataUtils.implementsTemporalOperator(this.wfsMetadata, BEFORE_OP)) { - throw new SkipException(ErrorMessage.format(ErrorMessageKeys.NOT_IMPLEMENTED, "Before operator")); - } - } + /** + * Checks if the temporal operator "Before" is supported. If not, the relevant tests + * are skipped. + */ + @BeforeClass + public void implementsBeforeOperator() { + if (!ServiceMetadataUtils.implementsTemporalOperator(this.wfsMetadata, BEFORE_OP)) { + throw new SkipException(ErrorMessage.format(ErrorMessageKeys.NOT_IMPLEMENTED, "Before operator")); + } + } - /** - * [{@code Test}] Submits a GetFeature request containing the - * Before temporal predicate with a literal gml:TimePeriod - * value. The response entity must contain only feature instances having a - * temporal property value that is before the specified period. - * - * @param binding - * The ProtocolBinding to use for this request. - * @param featureType - * A QName representing the qualified name of some feature type. - */ - @Test(description = "See ISO 19143: A.10", dataProvider = "protocol-featureType") - public void beforePeriod(ProtocolBinding binding, QName featureType) { - TemporalProperty temporalProperty = findTemporalProperty( featureType ); + /** + * [{@code Test}] Submits a GetFeature request containing the Before + * temporal predicate with a literal gml:TimePeriod value. The response entity must + * contain only feature instances having a temporal property value that is before the + * specified period. + * @param binding The ProtocolBinding to use for this request. + * @param featureType A QName representing the qualified name of some feature type. + */ + @Test(description = "See ISO 19143: A.10", dataProvider = "protocol-featureType") + public void beforePeriod(ProtocolBinding binding, QName featureType) { + TemporalProperty temporalProperty = findTemporalProperty(featureType); - List subIntervals = TemporalUtils.splitInterval(temporalProperty.getExtent(), 3); - Period lastSubInterval = subIntervals.get(2); - Document gmlTimeLiteral = TimeUtils.periodAsGMLAddOneDay(lastSubInterval); - WFSMessage.appendSimpleQuery(this.reqEntity, featureType); - Element valueRef = WFSMessage.createValueReference(temporalProperty.getProperty()); - WFSMessage.addTemporalPredicate(this.reqEntity, BEFORE_OP, gmlTimeLiteral, valueRef); - Response rsp = wfsClient.getFeature(new DOMSource(this.reqEntity), binding); - this.rspEntity = extractBodyAsDocument(rsp); - Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - List temporalNodes = TemporalQuery.extractTemporalNodes(this.rspEntity, temporalProperty.getProperty(), getModel()); - assertBefore(temporalNodes, temporalProperty.getProperty(), gmlTimeLiteral); - } + List subIntervals = TemporalUtils.splitInterval(temporalProperty.getExtent(), 3); + Period lastSubInterval = subIntervals.get(2); + Document gmlTimeLiteral = TimeUtils.periodAsGMLAddOneDay(lastSubInterval); + WFSMessage.appendSimpleQuery(this.reqEntity, featureType); + Element valueRef = WFSMessage.createValueReference(temporalProperty.getProperty()); + WFSMessage.addTemporalPredicate(this.reqEntity, BEFORE_OP, gmlTimeLiteral, valueRef); + Response rsp = wfsClient.getFeature(new DOMSource(this.reqEntity), binding); + this.rspEntity = extractBodyAsDocument(rsp); + Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + List temporalNodes = TemporalQuery.extractTemporalNodes(this.rspEntity, temporalProperty.getProperty(), + getModel()); + assertBefore(temporalNodes, temporalProperty.getProperty(), gmlTimeLiteral); + } + + /** + * Asserts that all temporal values in the given list occur before the specified GML + * temporal value. + * @param temporalNodes A list of simple or complex temporal values. + * @param propertyDecl An element declaration for a temporal property. + * @param gmlTimeLiteral A document that contains a GML representation of an instant + * or period. + */ + void assertBefore(List temporalNodes, XSElementDeclaration propertyDecl, Document gmlTimeLiteral) { + Assert.assertFalse(temporalNodes.isEmpty(), + String.format("No temporal values found in results: property is %s.", propertyDecl)); + TemporalGeometricPrimitive t2 = GmlUtils.gmlToTemporalGeometricPrimitive(gmlTimeLiteral.getDocumentElement()); + XSTypeDefinition typeDef = propertyDecl.getTypeDefinition(); + for (Node timeNode : temporalNodes) { + TemporalGeometricPrimitive t1 = null; + if (typeDef.getTypeCategory() == XSTypeDefinition.SIMPLE_TYPE || ((XSComplexTypeDefinition) typeDef) + .getContentType() == XSComplexTypeDefinition.CONTENTTYPE_SIMPLE) { + t1 = TemporalQuery.parseTemporalValue(timeNode.getTextContent(), typeDef); + } + else { + t1 = GmlUtils.gmlToTemporalGeometricPrimitive((Element) timeNode); + } + TemporalUtils.assertTemporalRelation(RelativePosition.BEFORE, t1, t2); + } + } - /** - * Asserts that all temporal values in the given list occur before the - * specified GML temporal value. - * - * @param temporalNodes - * A list of simple or complex temporal values. - * @param propertyDecl - * An element declaration for a temporal property. - * @param gmlTimeLiteral - * A document that contains a GML representation of an instant or - * period. - */ - void assertBefore(List temporalNodes, XSElementDeclaration propertyDecl, Document gmlTimeLiteral) { - Assert.assertFalse(temporalNodes.isEmpty(), - String.format("No temporal values found in results: property is %s.", propertyDecl)); - TemporalGeometricPrimitive t2 = GmlUtils.gmlToTemporalGeometricPrimitive(gmlTimeLiteral.getDocumentElement()); - XSTypeDefinition typeDef = propertyDecl.getTypeDefinition(); - for (Node timeNode : temporalNodes) { - TemporalGeometricPrimitive t1 = null; - if (typeDef.getTypeCategory() == XSTypeDefinition.SIMPLE_TYPE - || ( (XSComplexTypeDefinition) typeDef ).getContentType() == XSComplexTypeDefinition.CONTENTTYPE_SIMPLE) { - t1 = TemporalQuery.parseTemporalValue(timeNode.getTextContent(), typeDef); - } else { - t1 = GmlUtils.gmlToTemporalGeometricPrimitive((Element) timeNode); - } - TemporalUtils.assertTemporalRelation(RelativePosition.BEFORE, t1, t2); - } - } } diff --git a/src/main/java/org/opengis/cite/iso19142/basic/filter/temporal/DuringTests.java b/src/main/java/org/opengis/cite/iso19142/basic/filter/temporal/DuringTests.java index 925e73ca..b38e0778 100644 --- a/src/main/java/org/opengis/cite/iso19142/basic/filter/temporal/DuringTests.java +++ b/src/main/java/org/opengis/cite/iso19142/basic/filter/temporal/DuringTests.java @@ -27,26 +27,26 @@ import jakarta.ws.rs.core.Response.Status; /** - * Tests the response to a GetFeature request that includes the temporal - * predicate During. The relation can be expressed as follows when - * comparing a temporal instant to a temporal period: - * + * Tests the response to a GetFeature request that includes the temporal predicate + * During. The relation can be expressed as follows when comparing a temporal + * instant to a temporal period: + * *
        * self.position > other.begin.position AND self.position < other.end.position
        * 
      - * + * *

      * If both operands are periods then the following must hold: *

      - * + * *
        * self.begin.position > other.begin.position AND self.end.position < other.end.position
        * 
      - * + * *

      - * The following figure illustrates the relationship. A solid line denotes a - * temporal property; a dashed line denotes a literal time value that specifies - * the temporal extent of interest. + * The following figure illustrates the relationship. A solid line denotes a temporal + * property; a dashed line denotes a literal time value that specifies the temporal extent + * of interest. *

      * * During relationship @@ -60,61 +60,55 @@ */ public class DuringTests extends AbstractTemporalTest { - private static final String DURING_OP = "During"; + private static final String DURING_OP = "During"; - /** - * [{@code Test}] Submits a GetFeature request containing a During temporal - * predicate with a gml:TimePeriod operand spanning some time interval. The - * response entity must contain only instances of the requested type that - * satisfy the temporal relation. - * - * @param binding - * The ProtocolBinding to use for this request. - * @param featureType - * A QName representing the qualified name of some feature type. - */ - @Test(description = "See ISO 19143: 7.14.6, A.9", dataProvider = "protocol-featureType") - public void duringPeriod(ProtocolBinding binding, QName featureType) { - TemporalProperty temporalProperty = findTemporalProperty( featureType ); + /** + * [{@code Test}] Submits a GetFeature request containing a During temporal predicate + * with a gml:TimePeriod operand spanning some time interval. The response entity must + * contain only instances of the requested type that satisfy the temporal relation. + * @param binding The ProtocolBinding to use for this request. + * @param featureType A QName representing the qualified name of some feature type. + */ + @Test(description = "See ISO 19143: 7.14.6, A.9", dataProvider = "protocol-featureType") + public void duringPeriod(ProtocolBinding binding, QName featureType) { + TemporalProperty temporalProperty = findTemporalProperty(featureType); - Document gmlTimeLiteral = TimeUtils.periodAsGML(temporalProperty.getExtent()); - WFSMessage.appendSimpleQuery(this.reqEntity, featureType); - Element valueRef = WFSMessage.createValueReference(temporalProperty.getProperty()); - WFSMessage.addTemporalPredicate(this.reqEntity, DURING_OP, gmlTimeLiteral, valueRef); - Response rsp = wfsClient.getFeature(new DOMSource(reqEntity), binding); - this.rspEntity = extractBodyAsDocument(rsp); - Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - List temporalNodes = TemporalQuery.extractTemporalNodes(this.rspEntity, temporalProperty.getProperty(), getModel()); - assertDuring(temporalNodes, temporalProperty.getProperty(), gmlTimeLiteral); - } + Document gmlTimeLiteral = TimeUtils.periodAsGML(temporalProperty.getExtent()); + WFSMessage.appendSimpleQuery(this.reqEntity, featureType); + Element valueRef = WFSMessage.createValueReference(temporalProperty.getProperty()); + WFSMessage.addTemporalPredicate(this.reqEntity, DURING_OP, gmlTimeLiteral, valueRef); + Response rsp = wfsClient.getFeature(new DOMSource(reqEntity), binding); + this.rspEntity = extractBodyAsDocument(rsp); + Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + List temporalNodes = TemporalQuery.extractTemporalNodes(this.rspEntity, temporalProperty.getProperty(), + getModel()); + assertDuring(temporalNodes, temporalProperty.getProperty(), gmlTimeLiteral); + } - /** - * Asserts that all temporal values in the given list occur during the - * specified GML temporal value (gml:TimePeriod). - * - * @param temporalNodes - * A list of simple or complex temporal values. - * @param propertyDecl - * An element declaration for a temporal property. - * @param gmlTimeLiteral - * A document that contains a GML representation of a period. - */ - void assertDuring(List temporalNodes, XSElementDeclaration propertyDecl, Document gmlTimeLiteral) { - Assert.assertFalse(temporalNodes.isEmpty(), - String.format("No temporal values found in results: property is %s.", propertyDecl)); - TemporalGeometricPrimitive t2 = GmlUtils.gmlToTemporalGeometricPrimitive(gmlTimeLiteral.getDocumentElement()); - XSTypeDefinition typeDef = propertyDecl.getTypeDefinition(); - for (Node timeNode : temporalNodes) { - TemporalGeometricPrimitive t1 = null; - if (typeDef.getTypeCategory() == XSTypeDefinition.SIMPLE_TYPE - || ( (XSComplexTypeDefinition) typeDef ).getContentType() == XSComplexTypeDefinition.CONTENTTYPE_SIMPLE) { - t1 = TemporalQuery.parseTemporalValue(timeNode.getTextContent(), typeDef); - } else { - t1 = GmlUtils.gmlToTemporalGeometricPrimitive((Element) timeNode); - } - TemporalUtils.assertTemporalRelation(RelativePosition.DURING, t1, t2); - } - } + /** + * Asserts that all temporal values in the given list occur during the specified GML + * temporal value (gml:TimePeriod). + * @param temporalNodes A list of simple or complex temporal values. + * @param propertyDecl An element declaration for a temporal property. + * @param gmlTimeLiteral A document that contains a GML representation of a period. + */ + void assertDuring(List temporalNodes, XSElementDeclaration propertyDecl, Document gmlTimeLiteral) { + Assert.assertFalse(temporalNodes.isEmpty(), + String.format("No temporal values found in results: property is %s.", propertyDecl)); + TemporalGeometricPrimitive t2 = GmlUtils.gmlToTemporalGeometricPrimitive(gmlTimeLiteral.getDocumentElement()); + XSTypeDefinition typeDef = propertyDecl.getTypeDefinition(); + for (Node timeNode : temporalNodes) { + TemporalGeometricPrimitive t1 = null; + if (typeDef.getTypeCategory() == XSTypeDefinition.SIMPLE_TYPE || ((XSComplexTypeDefinition) typeDef) + .getContentType() == XSComplexTypeDefinition.CONTENTTYPE_SIMPLE) { + t1 = TemporalQuery.parseTemporalValue(timeNode.getTextContent(), typeDef); + } + else { + t1 = GmlUtils.gmlToTemporalGeometricPrimitive((Element) timeNode); + } + TemporalUtils.assertTemporalRelation(RelativePosition.DURING, t1, t2); + } + } } diff --git a/src/main/java/org/opengis/cite/iso19142/basic/filter/temporal/TemporalFilter.java b/src/main/java/org/opengis/cite/iso19142/basic/filter/temporal/TemporalFilter.java index b00a34da..77d7f4a7 100644 --- a/src/main/java/org/opengis/cite/iso19142/basic/filter/temporal/TemporalFilter.java +++ b/src/main/java/org/opengis/cite/iso19142/basic/filter/temporal/TemporalFilter.java @@ -11,11 +11,11 @@ /** * Checks preconditions for running tests to verify that the IUT satisfies the - * requirements of the Minimum Temporal filter conformance - * class. All tests are skipped if any preconditions are not met. The filter - * constraint {@value #IMPL_MIN_TEMPORAL_FILTER} must be set to "TRUE" in the - * capabilities document. The During operator must be implemented. - * + * requirements of the Minimum Temporal filter conformance class. All + * tests are skipped if any preconditions are not met. The filter constraint + * {@value #IMPL_MIN_TEMPORAL_FILTER} must be set to "TRUE" in the capabilities document. + * The During operator must be implemented. + * *
        * {@code
        * 
      @@ -27,28 +27,26 @@
        * 
        * }
        * 
      - * + * * @see ATC - * A.9: Test cases for minimum temporal filter + * "http://docs.opengeospatial.org/is/09-026r2/09-026r2.html#requirement_9">ATC A.9: Test + * cases for minimum temporal filter */ public class TemporalFilter { - public final static String IMPL_MIN_TEMPORAL_FILTER = "ImplementsMinTemporalFilter"; + public final static String IMPL_MIN_TEMPORAL_FILTER = "ImplementsMinTemporalFilter"; + + /** + * This {@literal @BeforeTest} configuration method checks the implementation status + * of the {@value #IMPL_MIN_TEMPORAL_FILTER} conformance class. + * @param testContext Information about the test run environment. + */ + @BeforeTest + public void implementsMinimumTemporalFilter(ITestContext testContext) { + Document wfsMetadata = (Document) testContext.getSuite().getAttribute(SuiteAttribute.TEST_SUBJECT.getName()); + if (!ServiceMetadataUtils.implementsConformanceClass(wfsMetadata, IMPL_MIN_TEMPORAL_FILTER)) { + throw new SkipException(ErrorMessage.format(ErrorMessageKeys.NOT_IMPLEMENTED, IMPL_MIN_TEMPORAL_FILTER)); + } + } - /** - * This {@literal @BeforeTest} configuration method checks the - * implementation status of the {@value #IMPL_MIN_TEMPORAL_FILTER} - * conformance class. - * - * @param testContext - * Information about the test run environment. - */ - @BeforeTest - public void implementsMinimumTemporalFilter(ITestContext testContext) { - Document wfsMetadata = (Document) testContext.getSuite().getAttribute(SuiteAttribute.TEST_SUBJECT.getName()); - if (!ServiceMetadataUtils.implementsConformanceClass(wfsMetadata, IMPL_MIN_TEMPORAL_FILTER)) { - throw new SkipException(ErrorMessage.format(ErrorMessageKeys.NOT_IMPLEMENTED, IMPL_MIN_TEMPORAL_FILTER)); - } - } } diff --git a/src/main/java/org/opengis/cite/iso19142/basic/filter/temporal/TemporalQuery.java b/src/main/java/org/opengis/cite/iso19142/basic/filter/temporal/TemporalQuery.java index 01db8f3f..21258349 100644 --- a/src/main/java/org/opengis/cite/iso19142/basic/filter/temporal/TemporalQuery.java +++ b/src/main/java/org/opengis/cite/iso19142/basic/filter/temporal/TemporalQuery.java @@ -30,97 +30,92 @@ import org.w3c.dom.Node; /** - * Utility methods to facilitate the creation and verification of temporal - * queries. + * Utility methods to facilitate the creation and verification of temporal queries. */ public class TemporalQuery { - /** - * Extracts the values of the specified temporal property from the given XML - * document. - * - * @param rspEntity - * A Document representing a WFS response entity containing - * feature instances. - * @param timeProperty - * An element declaration for a temporal feature property. - * @param model - * A representation of an application schema. - * @return A sequence of (element) nodes that are either (a) simple - * properties with temporal values as text content, or b) complex - * temporal values that can substitute for - * gml:AbstractTimeGeometricPrimitive (e.g. gml:Instant, - * gml:TimePeriod). - */ - public static List extractTemporalNodes(Document rspEntity, XSElementDeclaration timeProperty, - XSModel model) { - List temporalNodes = null; - XSTypeDefinition timePropType = timeProperty.getTypeDefinition(); - if ( timePropType.getTypeCategory() == XSTypeDefinition.SIMPLE_TYPE - || ( (XSComplexTypeDefinition) timePropType ).getContentType() == XSComplexTypeDefinition.CONTENTTYPE_SIMPLE ) { - temporalNodes = WFSMessage.findMatchingElements(rspEntity, timeProperty); - } else { - // elements that substitute for gml:AbstractTimeGeometricPrimitive - XSElementDeclaration gmlAbstractTimePrimitive = model - .getElementDeclaration("AbstractTimeGeometricPrimitive", Namespaces.GML); - List expectedValues = XMLSchemaModelUtils.getElementsByAffiliation(model, - gmlAbstractTimePrimitive); - temporalNodes = WFSMessage.findMatchingElements(rspEntity, - expectedValues.toArray(new XSElementDeclaration[expectedValues.size()])); - } - return temporalNodes; - } + /** + * Extracts the values of the specified temporal property from the given XML document. + * @param rspEntity A Document representing a WFS response entity containing feature + * instances. + * @param timeProperty An element declaration for a temporal feature property. + * @param model A representation of an application schema. + * @return A sequence of (element) nodes that are either (a) simple properties with + * temporal values as text content, or b) complex temporal values that can substitute + * for gml:AbstractTimeGeometricPrimitive (e.g. gml:Instant, gml:TimePeriod). + */ + public static List extractTemporalNodes(Document rspEntity, XSElementDeclaration timeProperty, + XSModel model) { + List temporalNodes = null; + XSTypeDefinition timePropType = timeProperty.getTypeDefinition(); + if (timePropType.getTypeCategory() == XSTypeDefinition.SIMPLE_TYPE || ((XSComplexTypeDefinition) timePropType) + .getContentType() == XSComplexTypeDefinition.CONTENTTYPE_SIMPLE) { + temporalNodes = WFSMessage.findMatchingElements(rspEntity, timeProperty); + } + else { + // elements that substitute for gml:AbstractTimeGeometricPrimitive + XSElementDeclaration gmlAbstractTimePrimitive = model + .getElementDeclaration("AbstractTimeGeometricPrimitive", Namespaces.GML); + List expectedValues = XMLSchemaModelUtils.getElementsByAffiliation(model, + gmlAbstractTimePrimitive); + temporalNodes = WFSMessage.findMatchingElements(rspEntity, + expectedValues.toArray(new XSElementDeclaration[expectedValues.size()])); + } + return temporalNodes; + } - /** - * Creates a primitive temporal object from the given temporal value and - * type definition. - * - * @param value - * A string representation of a temporal value. - * @param typeDefinition - * A simple type definition to which the value conforms - * (xsd:dateTime, xsd:date, xsd:gYear, xsd:gYearMonth). - * @return A TemporalGeometricPrimitive instance (instant or period). - */ - public static TemporalGeometricPrimitive parseTemporalValue(String value, XSTypeDefinition typeDefinition) { - if ( typeDefinition.getTypeCategory() != XSTypeDefinition.SIMPLE_TYPE - && !( ( (XSComplexTypeDefinition) typeDefinition ).getContentType() == XSComplexTypeDefinition.CONTENTTYPE_SIMPLE ) ) { - throw new IllegalArgumentException( "Not a simple type definition: " + typeDefinition.getName() ); - } - TemporalGeometricPrimitive tmPrimitive = null; - TemporalFactory tmFactory = new DefaultTemporalFactory(); - XSSimpleTypeDefinition simpleTypeDefinition; - if ( typeDefinition.getTypeCategory() == XSTypeDefinition.SIMPLE_TYPE ) { - simpleTypeDefinition = XSSimpleTypeDefinition.class.cast( typeDefinition ); - } else - simpleTypeDefinition = ( (XSComplexTypeDefinition) typeDefinition ).getSimpleType(); + /** + * Creates a primitive temporal object from the given temporal value and type + * definition. + * @param value A string representation of a temporal value. + * @param typeDefinition A simple type definition to which the value conforms + * (xsd:dateTime, xsd:date, xsd:gYear, xsd:gYearMonth). + * @return A TemporalGeometricPrimitive instance (instant or period). + */ + public static TemporalGeometricPrimitive parseTemporalValue(String value, XSTypeDefinition typeDefinition) { + if (typeDefinition.getTypeCategory() != XSTypeDefinition.SIMPLE_TYPE + && !(((XSComplexTypeDefinition) typeDefinition) + .getContentType() == XSComplexTypeDefinition.CONTENTTYPE_SIMPLE)) { + throw new IllegalArgumentException("Not a simple type definition: " + typeDefinition.getName()); + } + TemporalGeometricPrimitive tmPrimitive = null; + TemporalFactory tmFactory = new DefaultTemporalFactory(); + XSSimpleTypeDefinition simpleTypeDefinition; + if (typeDefinition.getTypeCategory() == XSTypeDefinition.SIMPLE_TYPE) { + simpleTypeDefinition = XSSimpleTypeDefinition.class.cast(typeDefinition); + } + else + simpleTypeDefinition = ((XSComplexTypeDefinition) typeDefinition).getSimpleType(); + + switch (simpleTypeDefinition.getBuiltInKind()) { + case XSConstants.DATETIME_DT: + DateTimeFormatter xsdDateTimeFormatter = DateTimeFormatter + .ofPattern("yyyy-MM-dd'T'HH:mm:ss[.SSS][XXX]"); + TemporalAccessor tm = xsdDateTimeFormatter.parseBest(value, ZonedDateTime::from, LocalDateTime::from); + if (tm instanceof LocalDateTime) { + throw new SkipException( + "{%s uses date values without timezone, which are currently not supported by this test suite."); + } + ZonedDateTime dateTime = (ZonedDateTime) tm; + tmPrimitive = tmFactory.createInstant(Date.from(dateTime.toInstant())); + break; + case XSConstants.DATE_DT: + ZoneOffset zone = DateTimeFormatter.ISO_DATE.parse(value, TemporalQueries.offset()); + if (null == zone) { + throw new SkipException( + "{%s uses date values without timezone, which are currently not supported by this test suite."); + } + LocalDate date = LocalDate.parse(value, DateTimeFormatter.ISO_DATE); + Temporal startOfDay = date.atStartOfDay(zone); + // date is top-open interval (ends at 23:59:59.999) + Temporal endOfDay = startOfDay.plus(1, ChronoUnit.DAYS).minus(1, ChronoUnit.MILLIS); + tmPrimitive = tmFactory.createPeriod(tmFactory.createInstant(Date.from(Instant.from(startOfDay))), + tmFactory.createInstant(Date.from(Instant.from(endOfDay)))); + break; + default: + throw new IllegalArgumentException("Unsupported datatype: " + typeDefinition.getName()); + } + return tmPrimitive; + } - switch ( simpleTypeDefinition.getBuiltInKind() ) { - case XSConstants.DATETIME_DT: - DateTimeFormatter xsdDateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss[.SSS][XXX]"); - TemporalAccessor tm = xsdDateTimeFormatter.parseBest(value, ZonedDateTime::from, LocalDateTime::from); - if (tm instanceof LocalDateTime) { - throw new SkipException("{%s uses date values without timezone, which are currently not supported by this test suite."); - } - ZonedDateTime dateTime = (ZonedDateTime) tm; - tmPrimitive = tmFactory.createInstant(Date.from(dateTime.toInstant())); - break; - case XSConstants.DATE_DT: - ZoneOffset zone = DateTimeFormatter.ISO_DATE.parse(value, TemporalQueries.offset()); - if (null == zone) { - throw new SkipException("{%s uses date values without timezone, which are currently not supported by this test suite."); - } - LocalDate date = LocalDate.parse(value, DateTimeFormatter.ISO_DATE); - Temporal startOfDay = date.atStartOfDay(zone); - // date is top-open interval (ends at 23:59:59.999) - Temporal endOfDay = startOfDay.plus(1, ChronoUnit.DAYS).minus(1, ChronoUnit.MILLIS); - tmPrimitive = tmFactory.createPeriod( - tmFactory.createInstant(Date.from(Instant.from(startOfDay))), - tmFactory.createInstant(Date.from(Instant.from(endOfDay)))); - break; - default: - throw new IllegalArgumentException("Unsupported datatype: " + typeDefinition.getName()); - } - return tmPrimitive; - } } diff --git a/src/main/java/org/opengis/cite/iso19142/basic/filter/temporal/package-info.java b/src/main/java/org/opengis/cite/iso19142/basic/filter/temporal/package-info.java index 89a09856..32d76231 100644 --- a/src/main/java/org/opengis/cite/iso19142/basic/filter/temporal/package-info.java +++ b/src/main/java/org/opengis/cite/iso19142/basic/filter/temporal/package-info.java @@ -1,8 +1,8 @@ /** - * This package includes tests covering the use of temporal operators in query - * expressions. There are 14 temporal operators defined in the OGC Filter Encoding + * This package includes tests covering the use of temporal operators in query + * expressions. There are 14 temporal operators defined in the OGC Filter Encoding * specification: - * + * *
        *
      • During (required for "Minimum Temporal Filter" conformance)
      • *
      • After
      • @@ -20,11 +20,15 @@ *
      • AnyInteracts
      • *
      * - *

      The conformance class "Minimum Temporal Filter" only requires support for - * the During operator. At least one other operator must be implemented - * to satisfy the requirements for "Temporal Filter" conformance.

      - * - *

      Sources

      + *

      + * The conformance class "Minimum Temporal Filter" only requires support for the + * During operator. At least one other operator must be implemented to + * satisfy the requirements for "Temporal Filter" conformance. + *

      + * + *

      + * Sources + *

      *
        *
      • OGC 09-026r2, Table 1: FE conformance classes
      • *
      • OGC 09-026r2, 7.9: Temporal operators
      • diff --git a/src/main/java/org/opengis/cite/iso19142/basic/package-info.java b/src/main/java/org/opengis/cite/iso19142/basic/package-info.java index 62f4def0..8ca58bf5 100644 --- a/src/main/java/org/opengis/cite/iso19142/basic/package-info.java +++ b/src/main/java/org/opengis/cite/iso19142/basic/package-info.java @@ -1,14 +1,16 @@ /** - * This package contains test classes that assess conformance of the SUT with - * respect to the "Basic WFS" conformance level. It builds upon the "Simple - * WFS" conformance level and adds the following service requests: + * This package contains test classes that assess conformance of the SUT with respect to + * the "Basic WFS" conformance level. It builds upon the "Simple WFS" conformance level + * and adds the following service requests: * *
          *
        • GetFeature with ad hoc query support
        • *
        • GetPropertyValue
        • *
        * - *

        Sources

        + *

        + * Sources + *

        *
          *
        • ISO ISO 19142:2010, cl. 2, Table 1
        • *
        • ISO ISO 19142:2010, cl. A.1.2: Basic WFS
        • diff --git a/src/main/java/org/opengis/cite/iso19142/joins/JoinQueryUtils.java b/src/main/java/org/opengis/cite/iso19142/joins/JoinQueryUtils.java index 0bb138a0..82321c60 100644 --- a/src/main/java/org/opengis/cite/iso19142/joins/JoinQueryUtils.java +++ b/src/main/java/org/opengis/cite/iso19142/joins/JoinQueryUtils.java @@ -19,28 +19,21 @@ public class JoinQueryUtils { /** * Inserts a spatial join query into a GetFeature request entity. - * - * @param req - * A Document node representing a GetFeature request. - * @param operator - * The name of a spatial operator. - * @param properties - * A sequence of (2) FeatureProperty descriptors that identify - * the feature properties in the join condition. + * @param req A Document node representing a GetFeature request. + * @param operator The name of a spatial operator. + * @param properties A sequence of (2) FeatureProperty descriptors that identify the + * feature properties in the join condition. */ - public static void appendSpatialJoinQuery(Document req, String operator, - List properties) { + public static void appendSpatialJoinQuery(Document req, String operator, List properties) { if (!req.getDocumentElement().getLocalName().equals(WFS2.GET_FEATURE)) { - throw new IllegalArgumentException("Not a GetFeature request: " - + req.getDocumentElement().getNodeName()); + throw new IllegalArgumentException("Not a GetFeature request: " + req.getDocumentElement().getNodeName()); } if (null == properties) { throw new NullPointerException("Feature properties are required."); } if (properties.size() != 2) { throw new IllegalArgumentException( - "Two feature properties are required, but received " - + properties.size()); + "Two feature properties are required, but received " + properties.size()); } Element docElem = req.getDocumentElement(); Element qryElem = req.createElementNS(Namespaces.WFS, "wfs:Query"); @@ -49,15 +42,13 @@ public static void appendSpatialJoinQuery(Document req, String operator, for (FeatureProperty property : properties) { QName featureType = property.getFeatureType(); // look for prefix already bound to this namespace URI - String nsPrefix = qryElem.lookupPrefix(featureType - .getNamespaceURI()); + String nsPrefix = qryElem.lookupPrefix(featureType.getNamespaceURI()); if (null == nsPrefix) { nsPrefix = "ns" + Integer.toString((int) (Math.random() * 100)); - qryElem.setAttributeNS(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, - "xmlns:" + nsPrefix, featureType.getNamespaceURI()); + qryElem.setAttributeNS(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, "xmlns:" + nsPrefix, + featureType.getNamespaceURI()); } - typeNames.append(nsPrefix).append(':') - .append(featureType.getLocalPart()).append(' '); + typeNames.append(nsPrefix).append(':').append(featureType.getLocalPart()).append(' '); } qryElem.setAttribute("typeNames", typeNames.toString().trim()); Element filter = req.createElementNS(Namespaces.FES, "Filter"); @@ -70,28 +61,21 @@ public static void appendSpatialJoinQuery(Document req, String operator, /** * Appends a fes:ValueReference element to a filter predicate. - * - * @param predicateElem - * An Element representing a predicate (operator). - * @param featureProperty - * A FeatureProperty descriptor. + * @param predicateElem An Element representing a predicate (operator). + * @param featureProperty A FeatureProperty descriptor. */ - public static void appendValueRefToPredicate(Element predicateElem, - FeatureProperty featureProperty) { - Element valueRef = predicateElem.getOwnerDocument().createElementNS( - Namespaces.FES, "ValueReference"); + public static void appendValueRefToPredicate(Element predicateElem, FeatureProperty featureProperty) { + Element valueRef = predicateElem.getOwnerDocument().createElementNS(Namespaces.FES, "ValueReference"); predicateElem.appendChild(valueRef); XSElementDeclaration propDecl = featureProperty.getDeclaration(); String nsURI = propDecl.getNamespace(); String propPrefix = predicateElem.lookupPrefix(nsURI); if (null == propPrefix) { propPrefix = "ns-" + Integer.toString((int) (Math.random() * 100)); - valueRef.setAttributeNS(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, - "xmlns:" + propPrefix, nsURI); + valueRef.setAttributeNS(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, "xmlns:" + propPrefix, nsURI); } QName featureType = featureProperty.getFeatureType(); - String typePrefix = predicateElem.lookupPrefix(featureType - .getNamespaceURI()); + String typePrefix = predicateElem.lookupPrefix(featureType.getNamespaceURI()); StringBuilder xpath = new StringBuilder(); xpath.append(typePrefix).append(':').append(featureType.getLocalPart()); xpath.append('/').append(propPrefix).append(':'); diff --git a/src/main/java/org/opengis/cite/iso19142/joins/SpatialJoinTests.java b/src/main/java/org/opengis/cite/iso19142/joins/SpatialJoinTests.java index b8eaebae..8347ce58 100644 --- a/src/main/java/org/opengis/cite/iso19142/joins/SpatialJoinTests.java +++ b/src/main/java/org/opengis/cite/iso19142/joins/SpatialJoinTests.java @@ -38,10 +38,9 @@ import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response.Status; - /** - * A spatial join includes a spatial predicate. One or more of the following - * spatial predicates must be supported: + * A spatial join includes a spatial predicate. One or more of the following spatial + * predicates must be supported: *
            *
          • Equals
          • *
          • Disjoint
          • @@ -54,15 +53,15 @@ *
          • Beyond
          • *
          • DWithin
          • *
          - * + * *

          - * A sample GetFeature request entity is shown below, where the "Intersects" - * predicate refers to the geometry properties of two different feature types. + * A sample GetFeature request entity is shown below, where the "Intersects" predicate + * refers to the geometry properties of two different feature types. *

          - * + * *
          - * <wfs:GetFeature version="2.0.0" service="WFS" 
          - *   xmlns:tns="http://example.org/ns1" 
          + * <wfs:GetFeature version="2.0.0" service="WFS"
          + *   xmlns:tns="http://example.org/ns1"
            *   xmlns:wfs="http://www.opengis.net/wfs/2.0"
            *   xmlns:fes="http://www.opengis.net/fes/2.0">
            *   <wfs:Query typeNames="tns:Parks tns:Lakes">
          @@ -87,163 +86,173 @@
            */
           public class SpatialJoinTests extends QueryFilterFixture {
           
          -    public final static String IMPL_SPATIAL_JOINS = "ImplementsSpatialJoins";
          -    private static final Logger LOGR = Logger.getLogger(SpatialJoinTests.class.getPackage().getName());
          -    private Map> surfaceProps;
          -    private Map> curveProps;
          -    private Map> pointProps;
          -    private Map> spatialCapabilities;
          -
          -    /**
          -     * Searches the application schema for geometry properties where the value
          -     * is an instance of the given type.
          -     * 
          -     * @param gmlTypeName
          -     *            The name of a GML geometry type (may be abstract).
          -     * @return A Map containing, for each feature type name (key), a list of
          -     *         matching geometry properties (value).
          -     */
          -    Map> findGeometryProperties(String gmlTypeName) {
          -        Map> geomProps = new HashMap>();
          -        XSTypeDefinition gmlGeomBaseType = getModel().getTypeDefinition(gmlTypeName, Namespaces.GML);
          -        for (QName featureType : this.featureTypes) {
          -            List geomPropsList = AppSchemaUtils.getFeaturePropertiesByType(getModel(), featureType,
          -                    gmlGeomBaseType);
          -            if (!geomPropsList.isEmpty()) {
          -                geomProps.put(featureType, geomPropsList);
          -            }
          -        }
          -        return geomProps;
          -    }
          -
          -    /**
          -     * Checks the value of the service constraint {@value #IMPL_SPATIAL_JOINS}
          -     * in the capabilities document. All tests are skipped if this is not
          -     * "TRUE".
          -     * 
          -     * @param testContext
          -     *            Information about the test run environment.
          -     */
          -    @BeforeTest
          -    public void implementsSpatialJoins(ITestContext testContext) {
          -        this.wfsMetadata = (Document) testContext.getSuite().getAttribute(SuiteAttribute.TEST_SUBJECT.getName());
          -        String xpath = String.format("//ows:Constraint[@name='%s' and (ows:DefaultValue = 'TRUE')]",
          -                IMPL_SPATIAL_JOINS);
          -        NodeList result;
          -        try {
          -            result = XMLUtils.evaluateXPath(this.wfsMetadata, xpath, null);
          -        } catch (XPathExpressionException e) {
          -            throw new AssertionError(e.getMessage());
          -        }
          -        if (result.getLength() == 0) {
          -            throw new SkipException(ErrorMessage.format(ErrorMessageKeys.NOT_IMPLEMENTED,
          -                    ConformanceClass.SPATIAL_JOINS.getConstraintName()));
          -        }
          -    }
          -
          -    /**
          -     * Initializes the test class fixture. Finds surface, curve, and point
          -     * properties defined in the application schema. Properties that use
          -     * primitive types are preferred, but if none are defined then aggregate
          -     * geometry types (Multi*) will be used instead.
          -     */
          -    @BeforeClass
          -    public void initFixture() {
          -        this.spatialCapabilities = ServiceMetadataUtils.getSpatialCapabilities(this.wfsMetadata);
          -        this.surfaceProps = findGeometryProperties("AbstractSurfaceType");
          -        if (this.surfaceProps.isEmpty()) {
          -            this.surfaceProps = findGeometryProperties("MultiSurfaceType");
          -        }
          -        LOGR.info(this.surfaceProps.toString());
          -        this.curveProps = findGeometryProperties("AbstractCurveType");
          -        if (this.curveProps.isEmpty()) {
          -            this.curveProps = findGeometryProperties("MultiCurveType");
          -        }
          -        LOGR.info(this.curveProps.toString());
          -        this.pointProps = findGeometryProperties("PointType");
          -        if (this.pointProps.isEmpty()) {
          -            this.pointProps = findGeometryProperties("MultiPointType");
          -        }
          -        LOGR.info(this.pointProps.toString());
          -    }
          -
          -    /**
          -     * [{@code Test}] Submits a basic join query that includes the
          -     * Intersects operator. A projection clause (wfs:PropertyName)
          -     * is omitted, so the response entity is expected to contain instances of
          -     * both feature types.
          -     */
          -    @Test(description = "See OGC 09-025r2: 7.9.2.5.3, A.1.12")
          -    public void joinWithIntersects() {
          -        if (!this.spatialCapabilities.keySet().contains(SpatialOperator.INTERSECTS)) {
          -            throw new SkipException(ErrorMessage.format(ErrorMessageKeys.NOT_IMPLEMENTED, SpatialOperator.INTERSECTS));
          -        }
          -        List joinProperties = new ArrayList();
          -        if (this.surfaceProps.size() > 1) {
          -            Iterator>> itr = this.surfaceProps.entrySet().iterator();
          -            Entry> entry = itr.next();
          -            joinProperties.add(new FeatureProperty(entry.getKey(), entry.getValue().get(0)));
          -            entry = itr.next();
          -            joinProperties.add(new FeatureProperty(entry.getKey(), entry.getValue().get(0)));
          -        } else if (!this.surfaceProps.isEmpty() && !this.curveProps.isEmpty()) {
          -            // surface property
          -            Iterator>> itrSurfaceProps = this.surfaceProps.entrySet().iterator();
          -            Entry> entrySurfaceProps = itrSurfaceProps.next();
          -            joinProperties.add(new FeatureProperty(entrySurfaceProps.getKey(), entrySurfaceProps.getValue().get(0)));
          -
          -            // curve property
          -            Iterator>> itrCurveProps = this.curveProps.entrySet().iterator();
          -            Entry> entryCurveProps = itrCurveProps.next();
          -            joinProperties.add(new FeatureProperty(entryCurveProps.getKey(), entryCurveProps.getValue().get(0)));
          -        } else if (!this.surfaceProps.isEmpty() && !this.pointProps.isEmpty()) {
          -            // surface property
          -            Iterator>> itrSurfaceProps = this.surfaceProps.entrySet().iterator();
          -            Entry> entrySurfaceProps = itrSurfaceProps.next();
          -            joinProperties.add(new FeatureProperty(entrySurfaceProps.getKey(), entrySurfaceProps.getValue().get(0)));
          -
          -            // point property
          -            Iterator>> itrPointProps = this.pointProps.entrySet().iterator();
          -            Entry> entryPointProps = itrPointProps.next();
          -            joinProperties.add(new FeatureProperty(entryPointProps.getKey(), entryPointProps.getValue().get(0)));
          -        } else if (!this.curveProps.isEmpty() && !this.pointProps.isEmpty()) {
          -            // curve property
          -            Iterator>> itrCurveProps = this.curveProps.entrySet().iterator();
          -            Entry> entryCurveProps = itrCurveProps.next();
          -            joinProperties.add(new FeatureProperty(entryCurveProps.getKey(), entryCurveProps.getValue().get(0)));
          -            
          -            // point property
          -            Iterator>> itrPointProps = this.pointProps.entrySet().iterator();
          -            Entry> entryPointProps = itrPointProps.next();
          -            joinProperties.add(new FeatureProperty(entryPointProps.getKey(), entryPointProps.getValue().get(0)));         
          -        }
          -        else{
          -           throw new SkipException("This test has triggered an unexpected Spatial Join condition. The Spatial Join test will need to be applied manually.");       
          -        } 
          -        JoinQueryUtils.appendSpatialJoinQuery(this.reqEntity, "Intersects", joinProperties);
          -        Response rsp = wfsClient.submitRequest(this.reqEntity, ProtocolBinding.ANY);
          -        this.rspEntity = extractBodyAsDocument(rsp);
          -        Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(),
          -                ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS));
          -        // TODO check entity body: F1 intersects F2
          -    }
          -
          -    // self-join T1 BBOX T1 (gml:boundedBy)??
          -    // self-join T1 INTERSECTS T1
          -    // self-join T1 BEFORE T1
          -    // join T1 BBOX T2 (gml;boundedBy) ??
          -    // join T1 AFTER T2
          -    // join T1 BEFORE T2
          -
          -    public void selfJoinWithIntersects() {
          -        if (!ServiceMetadataUtils.implementsSpatialOperator(this.wfsMetadata, "Intersects")) {
          -            throw new SkipException(ErrorMessage.format(ErrorMessageKeys.NOT_IMPLEMENTED, "Intersects operator"));
          -        }
          -        if (!this.surfaceProps.isEmpty()) {
          -            // TODO
          -        }
          -        if (!this.curveProps.isEmpty()) {
          -            // TODO
          -        }
          -    }
          +	public final static String IMPL_SPATIAL_JOINS = "ImplementsSpatialJoins";
          +
          +	private static final Logger LOGR = Logger.getLogger(SpatialJoinTests.class.getPackage().getName());
          +
          +	private Map> surfaceProps;
          +
          +	private Map> curveProps;
          +
          +	private Map> pointProps;
          +
          +	private Map> spatialCapabilities;
          +
          +	/**
          +	 * Searches the application schema for geometry properties where the value is an
          +	 * instance of the given type.
          +	 * @param gmlTypeName The name of a GML geometry type (may be abstract).
          +	 * @return A Map containing, for each feature type name (key), a list of matching
          +	 * geometry properties (value).
          +	 */
          +	Map> findGeometryProperties(String gmlTypeName) {
          +		Map> geomProps = new HashMap>();
          +		XSTypeDefinition gmlGeomBaseType = getModel().getTypeDefinition(gmlTypeName, Namespaces.GML);
          +		for (QName featureType : this.featureTypes) {
          +			List geomPropsList = AppSchemaUtils.getFeaturePropertiesByType(getModel(),
          +					featureType, gmlGeomBaseType);
          +			if (!geomPropsList.isEmpty()) {
          +				geomProps.put(featureType, geomPropsList);
          +			}
          +		}
          +		return geomProps;
          +	}
          +
          +	/**
          +	 * Checks the value of the service constraint {@value #IMPL_SPATIAL_JOINS} in the
          +	 * capabilities document. All tests are skipped if this is not "TRUE".
          +	 * @param testContext Information about the test run environment.
          +	 */
          +	@BeforeTest
          +	public void implementsSpatialJoins(ITestContext testContext) {
          +		this.wfsMetadata = (Document) testContext.getSuite().getAttribute(SuiteAttribute.TEST_SUBJECT.getName());
          +		String xpath = String.format("//ows:Constraint[@name='%s' and (ows:DefaultValue = 'TRUE')]",
          +				IMPL_SPATIAL_JOINS);
          +		NodeList result;
          +		try {
          +			result = XMLUtils.evaluateXPath(this.wfsMetadata, xpath, null);
          +		}
          +		catch (XPathExpressionException e) {
          +			throw new AssertionError(e.getMessage());
          +		}
          +		if (result.getLength() == 0) {
          +			throw new SkipException(ErrorMessage.format(ErrorMessageKeys.NOT_IMPLEMENTED,
          +					ConformanceClass.SPATIAL_JOINS.getConstraintName()));
          +		}
          +	}
          +
          +	/**
          +	 * Initializes the test class fixture. Finds surface, curve, and point properties
          +	 * defined in the application schema. Properties that use primitive types are
          +	 * preferred, but if none are defined then aggregate geometry types (Multi*) will be
          +	 * used instead.
          +	 */
          +	@BeforeClass
          +	public void initFixture() {
          +		this.spatialCapabilities = ServiceMetadataUtils.getSpatialCapabilities(this.wfsMetadata);
          +		this.surfaceProps = findGeometryProperties("AbstractSurfaceType");
          +		if (this.surfaceProps.isEmpty()) {
          +			this.surfaceProps = findGeometryProperties("MultiSurfaceType");
          +		}
          +		LOGR.info(this.surfaceProps.toString());
          +		this.curveProps = findGeometryProperties("AbstractCurveType");
          +		if (this.curveProps.isEmpty()) {
          +			this.curveProps = findGeometryProperties("MultiCurveType");
          +		}
          +		LOGR.info(this.curveProps.toString());
          +		this.pointProps = findGeometryProperties("PointType");
          +		if (this.pointProps.isEmpty()) {
          +			this.pointProps = findGeometryProperties("MultiPointType");
          +		}
          +		LOGR.info(this.pointProps.toString());
          +	}
          +
          +	/**
          +	 * [{@code Test}] Submits a basic join query that includes the Intersects
          +	 * operator. A projection clause (wfs:PropertyName) is omitted, so the response entity
          +	 * is expected to contain instances of both feature types.
          +	 */
          +	@Test(description = "See OGC 09-025r2: 7.9.2.5.3, A.1.12")
          +	public void joinWithIntersects() {
          +		if (!this.spatialCapabilities.keySet().contains(SpatialOperator.INTERSECTS)) {
          +			throw new SkipException(ErrorMessage.format(ErrorMessageKeys.NOT_IMPLEMENTED, SpatialOperator.INTERSECTS));
          +		}
          +		List joinProperties = new ArrayList();
          +		if (this.surfaceProps.size() > 1) {
          +			Iterator>> itr = this.surfaceProps.entrySet().iterator();
          +			Entry> entry = itr.next();
          +			joinProperties.add(new FeatureProperty(entry.getKey(), entry.getValue().get(0)));
          +			entry = itr.next();
          +			joinProperties.add(new FeatureProperty(entry.getKey(), entry.getValue().get(0)));
          +		}
          +		else if (!this.surfaceProps.isEmpty() && !this.curveProps.isEmpty()) {
          +			// surface property
          +			Iterator>> itrSurfaceProps = this.surfaceProps.entrySet()
          +				.iterator();
          +			Entry> entrySurfaceProps = itrSurfaceProps.next();
          +			joinProperties.add(new FeatureProperty(entrySurfaceProps.getKey(), entrySurfaceProps.getValue().get(0)));
          +
          +			// curve property
          +			Iterator>> itrCurveProps = this.curveProps.entrySet()
          +				.iterator();
          +			Entry> entryCurveProps = itrCurveProps.next();
          +			joinProperties.add(new FeatureProperty(entryCurveProps.getKey(), entryCurveProps.getValue().get(0)));
          +		}
          +		else if (!this.surfaceProps.isEmpty() && !this.pointProps.isEmpty()) {
          +			// surface property
          +			Iterator>> itrSurfaceProps = this.surfaceProps.entrySet()
          +				.iterator();
          +			Entry> entrySurfaceProps = itrSurfaceProps.next();
          +			joinProperties.add(new FeatureProperty(entrySurfaceProps.getKey(), entrySurfaceProps.getValue().get(0)));
          +
          +			// point property
          +			Iterator>> itrPointProps = this.pointProps.entrySet()
          +				.iterator();
          +			Entry> entryPointProps = itrPointProps.next();
          +			joinProperties.add(new FeatureProperty(entryPointProps.getKey(), entryPointProps.getValue().get(0)));
          +		}
          +		else if (!this.curveProps.isEmpty() && !this.pointProps.isEmpty()) {
          +			// curve property
          +			Iterator>> itrCurveProps = this.curveProps.entrySet()
          +				.iterator();
          +			Entry> entryCurveProps = itrCurveProps.next();
          +			joinProperties.add(new FeatureProperty(entryCurveProps.getKey(), entryCurveProps.getValue().get(0)));
          +
          +			// point property
          +			Iterator>> itrPointProps = this.pointProps.entrySet()
          +				.iterator();
          +			Entry> entryPointProps = itrPointProps.next();
          +			joinProperties.add(new FeatureProperty(entryPointProps.getKey(), entryPointProps.getValue().get(0)));
          +		}
          +		else {
          +			throw new SkipException(
          +					"This test has triggered an unexpected Spatial Join condition. The Spatial Join test will need to be applied manually.");
          +		}
          +		JoinQueryUtils.appendSpatialJoinQuery(this.reqEntity, "Intersects", joinProperties);
          +		Response rsp = wfsClient.submitRequest(this.reqEntity, ProtocolBinding.ANY);
          +		this.rspEntity = extractBodyAsDocument(rsp);
          +		Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(),
          +				ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS));
          +		// TODO check entity body: F1 intersects F2
          +	}
          +
          +	// self-join T1 BBOX T1 (gml:boundedBy)??
          +	// self-join T1 INTERSECTS T1
          +	// self-join T1 BEFORE T1
          +	// join T1 BBOX T2 (gml;boundedBy) ??
          +	// join T1 AFTER T2
          +	// join T1 BEFORE T2
          +
          +	public void selfJoinWithIntersects() {
          +		if (!ServiceMetadataUtils.implementsSpatialOperator(this.wfsMetadata, "Intersects")) {
          +			throw new SkipException(ErrorMessage.format(ErrorMessageKeys.NOT_IMPLEMENTED, "Intersects operator"));
          +		}
          +		if (!this.surfaceProps.isEmpty()) {
          +			// TODO
          +		}
          +		if (!this.curveProps.isEmpty()) {
          +			// TODO
          +		}
          +	}
           
           }
          diff --git a/src/main/java/org/opengis/cite/iso19142/joins/TemporalJoinTests.java b/src/main/java/org/opengis/cite/iso19142/joins/TemporalJoinTests.java
          index 954a4e1b..5b63c4b1 100644
          --- a/src/main/java/org/opengis/cite/iso19142/joins/TemporalJoinTests.java
          +++ b/src/main/java/org/opengis/cite/iso19142/joins/TemporalJoinTests.java
          @@ -2,10 +2,11 @@
           
           public class TemporalJoinTests {
           
          -    // self-join T1 DURING T1 (period)
          -    // self-join T1 AFTER T1
          -    // self-join T1 BEFORE T1
          -    // join T1 DURING T2 (period)
          -    // join T1 AFTER T2
          -    // join T1 BEFORE T2
          +	// self-join T1 DURING T1 (period)
          +	// self-join T1 AFTER T1
          +	// self-join T1 BEFORE T1
          +	// join T1 DURING T2 (period)
          +	// join T1 AFTER T2
          +	// join T1 BEFORE T2
          +
           }
          diff --git a/src/main/java/org/opengis/cite/iso19142/joins/package-info.java b/src/main/java/org/opengis/cite/iso19142/joins/package-info.java
          index 3f21fc42..afa625bd 100644
          --- a/src/main/java/org/opengis/cite/iso19142/joins/package-info.java
          +++ b/src/main/java/org/opengis/cite/iso19142/joins/package-info.java
          @@ -1,17 +1,21 @@
           /**
          - * This package includes tests for join queries. Servers that implement join 
          - * queries shall implement an inner join; that is, only features that satisfy 
          - * the join condition(s) shall be included in the result set.
          - * 
          - * 

          Three conformance classes are distinguished; the corresponding service - * constraints are shown in parentheses:

          + * This package includes tests for join queries. Servers that implement join queries shall + * implement an inner join; that is, only features that satisfy the join condition(s) + * shall be included in the result set. + * + *

          + * Three conformance classes are distinguished; the corresponding service constraints are + * shown in parentheses: + *

          *
            *
          • Standard joins (ImplementsStandardJoins)
          • *
          • Spatial joins (ImplementsSpatialJoins)
          • *
          • Temporal joins (ImplementsTemporalJoins)
          • *
          * - *

          Sources

          + *

          + * Sources + *

          *
            *
          • OGC 09-025r2, 7.9.2.5.3: Join processing
          • *
          • OGC 09-025r2, Table 13: Service constraints
          • diff --git a/src/main/java/org/opengis/cite/iso19142/locking/GetFeatureWithLockTests.java b/src/main/java/org/opengis/cite/iso19142/locking/GetFeatureWithLockTests.java index 150856ce..5b31f5d0 100644 --- a/src/main/java/org/opengis/cite/iso19142/locking/GetFeatureWithLockTests.java +++ b/src/main/java/org/opengis/cite/iso19142/locking/GetFeatureWithLockTests.java @@ -24,13 +24,11 @@ import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response.Status; - /** - * Tests the response to a GetFeatureWithLock request that attempts to lock - * feature instances that belong to a set of query results. The lock is active - * until it either expires (default duration is 300 s) or it is released by a - * subsequent transaction. - * + * Tests the response to a GetFeatureWithLock request that attempts to lock feature + * instances that belong to a set of query results. The lock is active until it either + * expires (default duration is 300 s) or it is released by a subsequent transaction. + * *

            * Sources *

            @@ -43,38 +41,34 @@ public class GetFeatureWithLockTests extends LockingFixture { /** - * Checks that the GetFeatureWithLock operation is implemented by the SUT. - * If not, all test methods defined in this class are skipped. - * - * @param testContext - * Supplies details about the test run. + * Checks that the GetFeatureWithLock operation is implemented by the SUT. If not, all + * test methods defined in this class are skipped. + * @param testContext Supplies details about the test run. */ @BeforeClass(alwaysRun = true) public void sutImplementsGetFeatureWithLock(ITestContext testContext) { - String xpath = String.format("//ows:Operation[@name='%s']", - WFS2.GET_FEATURE_WITH_LOCK); - boolean xpathResult = ETSAssert.evaluateXPathToBoolean( xpath, this.wfsMetadata, null ); - if ( !xpathResult ) { - throw new SkipException( "The service does not support " + WFS2.GET_FEATURE_WITH_LOCK - + " operation so tests are skipped." ); + String xpath = String.format("//ows:Operation[@name='%s']", WFS2.GET_FEATURE_WITH_LOCK); + boolean xpathResult = ETSAssert.evaluateXPathToBoolean(xpath, this.wfsMetadata, null); + if (!xpathResult) { + throw new SkipException( + "The service does not support " + WFS2.GET_FEATURE_WITH_LOCK + " operation so tests are skipped."); } } /** - * Builds a DOM Document representing a GetFeatureWithLock request entity. - * It contains default values for all lock-related attributes. + * Builds a DOM Document representing a GetFeatureWithLock request entity. It contains + * default values for all lock-related attributes. */ @BeforeMethod public void buildGetFeatureWithLockRequest() { - this.reqEntity = WFSMessage.createRequestEntity("GetFeatureWithLock", - this.wfsVersion); + this.reqEntity = WFSMessage.createRequestEntity("GetFeatureWithLock", this.wfsVersion); } /** * [{@code Test}] The only valid value for the resultType attribute in a - * GetFeatureWithLock request is "results". Any other value ("hits") shall - * produce an exception report with error code "InvalidParameterValue". - * + * GetFeatureWithLock request is "results". Any other value ("hits") shall produce an + * exception report with error code "InvalidParameterValue". + * * @see "ISO 19142:2010, cl. 13.2.4.3: resultType parameter" */ @Test(description = "See ISO 19142: 13.2.4.3") @@ -82,22 +76,20 @@ public void lockQueryResults_hits() { QName featureType = this.dataSampler.selectFeatureType(); WFSMessage.appendSimpleQuery(this.reqEntity, featureType); this.reqEntity.getDocumentElement().setAttribute("resultType", "hits"); - Response rsp = wfsClient.submitRequest(this.reqEntity, - ProtocolBinding.ANY); + Response rsp = wfsClient.submitRequest(this.reqEntity, ProtocolBinding.ANY); this.rspEntity = rsp.readEntity(Document.class); - Assert.assertEquals(rsp.getStatus(), - Status.BAD_REQUEST.getStatusCode(), + Assert.assertEquals(rsp.getStatus(), Status.BAD_REQUEST.getStatusCode(), ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); String xpath = "//ows:Exception[@exceptionCode = 'InvalidParameterValue']"; ETSAssert.assertXPath(xpath, this.rspEntity.getDocumentElement(), null); } /** - * [{@code Test}] Submits a request to lock all instances of a randomly - * selected feature type for 20 seconds. After this time has elapsed, an - * attempt is made to reset the lock; this LockFeature request should fail - * with a 'LockHasExpired' exception and HTTP status code 403 (Forbidden). - * + * [{@code Test}] Submits a request to lock all instances of a randomly selected + * feature type for 20 seconds. After this time has elapsed, an attempt is made to + * reset the lock; this LockFeature request should fail with a 'LockHasExpired' + * exception and HTTP status code 403 (Forbidden). + * *

            * Sources *

            @@ -111,75 +103,66 @@ public void lockAllQueryResults_20Seconds() { QName featureType = this.dataSampler.selectFeatureType(); WFSMessage.appendSimpleQuery(this.reqEntity, featureType); this.reqEntity.getDocumentElement().setAttribute("expiry", "20"); - Response rsp = wfsClient.submitRequest(this.reqEntity, - ProtocolBinding.ANY); + Response rsp = wfsClient.submitRequest(this.reqEntity, ProtocolBinding.ANY); this.rspEntity = rsp.readEntity(Document.class); - Assert.assertEquals(rsp.getStatus(), - Status.OK.getStatusCode(), + Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - Element featureColl = (Element) this.rspEntity.getElementsByTagNameNS( - Namespaces.WFS, WFS2.FEATURE_COLLECTION).item(0); + Element featureColl = (Element) this.rspEntity.getElementsByTagNameNS(Namespaces.WFS, WFS2.FEATURE_COLLECTION) + .item(0); String lockId = featureColl.getAttribute("lockId"); - Assert.assertFalse(lockId.isEmpty(), ErrorMessage.format( - ErrorMessageKeys.MISSING_INFOSET_ITEM, "@lockId")); + Assert.assertFalse(lockId.isEmpty(), ErrorMessage.format(ErrorMessageKeys.MISSING_INFOSET_ITEM, "@lockId")); locks.add(lockId); try { Thread.sleep(34 * 1000); - } catch (InterruptedException e) { + } + catch (InterruptedException e) { // ignore interrupt should one occur } // try to reset expired lock with LockFeature request - this.reqEntity = WFSMessage.createRequestEntity("LockFeature", - this.wfsVersion); + this.reqEntity = WFSMessage.createRequestEntity("LockFeature", this.wfsVersion); reqEntity.getDocumentElement().setAttribute("lockId", lockId); rsp = wfsClient.submitRequest(reqEntity, ProtocolBinding.ANY); this.rspEntity = rsp.readEntity(Document.class); - Assert.assertEquals(rsp.getStatus(), - Status.FORBIDDEN.getStatusCode(), + Assert.assertEquals(rsp.getStatus(), Status.FORBIDDEN.getStatusCode(), ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); String xpath = "//ows:Exception[@exceptionCode = 'LockHasExpired']"; ETSAssert.assertXPath(xpath, this.rspEntity.getDocumentElement(), null); } /** - * [{@code Test}] Verifies that a feature can be protected by only one lock - * at a time (i.e. a mutual exclusion lock). Two - * GetFeatureWithLock requests are submitted: + * [{@code Test}] Verifies that a feature can be protected by only one lock at a time + * (i.e. a mutual exclusion lock). Two GetFeatureWithLock requests are + * submitted: *
              *
            1. A request to lock a single feature instance for 60 seconds (Q1)
            2. - *
            3. A request to lock SOME of Q1 plus other feature instances (a proper - * superset of the Q1).
            4. + *
            5. A request to lock SOME of Q1 plus other feature instances (a proper superset of + * the Q1).
            6. *
            - * + * *

            - * The last request should succeed. However, the response entity shall not - * contain the feature that was previously locked in Q1. + * The last request should succeed. However, the response entity shall not contain the + * feature that was previously locked in Q1. *

            - * + * * @see "ISO 19142:2010, cl. 13.2.4.2: lockAction parameter" */ @Test(description = "See ISO 19142: 13.2.4.2") public void lockSomeFeatures() { QName featureType = this.dataSampler.selectFeatureType(); - Set featureIdSet = this.dataSampler - .selectRandomFeatureIdentifiers(featureType, 10); + Set featureIdSet = this.dataSampler.selectRandomFeatureIdentifiers(featureType, 10); // Submit Q1 to lock one feature - Set singleton = Collections.singleton(featureIdSet.iterator() - .next()); + Set singleton = Collections.singleton(featureIdSet.iterator().next()); WFSMessage.appendSimpleQuery(this.reqEntity, featureType); WFSMessage.addResourceIdPredicate(this.reqEntity, singleton); this.reqEntity.getDocumentElement().setAttribute("expiry", "60"); - Response rsp = wfsClient.submitRequest(this.reqEntity, - ProtocolBinding.ANY); + Response rsp = wfsClient.submitRequest(this.reqEntity, ProtocolBinding.ANY); this.rspEntity = rsp.readEntity(Document.class); - Assert.assertEquals(rsp.getStatus(), - Status.OK.getStatusCode(), + Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - Element featureColl = (Element) this.rspEntity.getElementsByTagNameNS( - Namespaces.WFS, WFS2.FEATURE_COLLECTION).item(0); + Element featureColl = (Element) this.rspEntity.getElementsByTagNameNS(Namespaces.WFS, WFS2.FEATURE_COLLECTION) + .item(0); String lockId = featureColl.getAttribute("lockId"); - Assert.assertFalse(lockId.isEmpty(), ErrorMessage.format( - ErrorMessageKeys.MISSING_INFOSET_ITEM, + Assert.assertFalse(lockId.isEmpty(), ErrorMessage.format(ErrorMessageKeys.MISSING_INFOSET_ITEM, "@lockId in response to GetFeatureWithLock")); locks.add(lockId); // Submit Q2 to lock all features in set @@ -189,23 +172,18 @@ public void lockSomeFeatures() { this.reqEntity.getDocumentElement().setAttribute("lockAction", "SOME"); rsp = wfsClient.submitRequest(this.reqEntity, ProtocolBinding.ANY); this.rspEntity = rsp.readEntity(Document.class); - Assert.assertEquals(rsp.getStatus(), - Status.OK.getStatusCode(), + Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - featureColl = (Element) this.rspEntity.getElementsByTagNameNS( - Namespaces.WFS, WFS2.FEATURE_COLLECTION).item(0); + featureColl = (Element) this.rspEntity.getElementsByTagNameNS(Namespaces.WFS, WFS2.FEATURE_COLLECTION).item(0); lockId = featureColl.getAttribute("lockId"); - Assert.assertFalse(lockId.isEmpty(), ErrorMessage.format( - ErrorMessageKeys.MISSING_INFOSET_ITEM, + Assert.assertFalse(lockId.isEmpty(), ErrorMessage.format(ErrorMessageKeys.MISSING_INFOSET_ITEM, "@lockId in response to GetFeatureWithLock")); locks.add(lockId); String xpath1 = "/wfs:FeatureCollection/@numberReturned > 1"; - ETSAssert - .assertXPath(xpath1, this.rspEntity.getDocumentElement(), null); + ETSAssert.assertXPath(xpath1, this.rspEntity.getDocumentElement(), null); // response must exclude feature that was previously locked - xpath1 = String.format("not(//*[@gml:id = '%s'])", singleton.iterator() - .next()); - ETSAssert - .assertXPath(xpath1, this.rspEntity.getDocumentElement(), null); + xpath1 = String.format("not(//*[@gml:id = '%s'])", singleton.iterator().next()); + ETSAssert.assertXPath(xpath1, this.rspEntity.getDocumentElement(), null); } + } diff --git a/src/main/java/org/opengis/cite/iso19142/locking/LockFeatureTests.java b/src/main/java/org/opengis/cite/iso19142/locking/LockFeatureTests.java index 059389bb..97f9c79a 100644 --- a/src/main/java/org/opengis/cite/iso19142/locking/LockFeatureTests.java +++ b/src/main/java/org/opengis/cite/iso19142/locking/LockFeatureTests.java @@ -28,11 +28,10 @@ import jakarta.ws.rs.core.Response.Status; /** - * Tests the response to a LockFeature request that attempts to lock feature - * instances identified using one or more query expressions. A lock is active - * until it either expires (default duration is 300 s) or it is released by a - * subsequent transaction. - * + * Tests the response to a LockFeature request that attempts to lock feature instances + * identified using one or more query expressions. A lock is active until it either + * expires (default duration is 300 s) or it is released by a subsequent transaction. + * *

            * Sources *

            @@ -51,15 +50,14 @@ public class LockFeatureTests extends LockingFixture { */ @BeforeMethod public void buildSimpleLockFeatureRequest() { - this.reqEntity = WFSMessage.createRequestEntity("LockFeature", - this.wfsVersion); + this.reqEntity = WFSMessage.createRequestEntity("LockFeature", this.wfsVersion); } /** - * [{@code Test}] Submits a request to lock a feature instance; within this - * interval an attempt to delete the instance without the correct lock - * identifier should fail with exception code {@code MissingParameterValue}. - * + * [{@code Test}] Submits a request to lock a feature instance; within this interval + * an attempt to delete the instance without the correct lock identifier should fail + * with exception code {@code MissingParameterValue}. + * *

            * Sources *

            @@ -71,39 +69,33 @@ public void buildSimpleLockFeatureRequest() { @Test(description = "See ISO 19142: 12.2.4.3, 15.2.3.1.2") public void lockFeatureAndAttemptDelete() { QName featureType = this.dataSampler.selectFeatureType(); - String gmlId = this.dataSampler.selectRandomFeatureIdentifiers( featureType, 1 ).iterator().next(); - WFSMessage.appendStoredQuery(reqEntity, this.storedQueryId, - Collections.singletonMap("id", (Object) gmlId)); + String gmlId = this.dataSampler.selectRandomFeatureIdentifiers(featureType, 1).iterator().next(); + WFSMessage.appendStoredQuery(reqEntity, this.storedQueryId, Collections.singletonMap("id", (Object) gmlId)); reqEntity.getDocumentElement().setAttribute("expiry", "60"); - Response rsp = wfsClient.submitRequest(reqEntity, - ProtocolBinding.ANY); + Response rsp = wfsClient.submitRequest(reqEntity, ProtocolBinding.ANY); this.rspEntity = rsp.readEntity(Document.class); - Assert.assertEquals(rsp.getStatus(), - Status.OK.getStatusCode(), + Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - ETSAssert - .assertXPath("//wfs:LockFeatureResponse", this.rspEntity, null); - Element lockRsp = (Element) this.rspEntity.getElementsByTagNameNS( - Namespaces.WFS, WFS2.LOCK_FEATURE_RSP).item(0); + ETSAssert.assertXPath("//wfs:LockFeatureResponse", this.rspEntity, null); + Element lockRsp = (Element) this.rspEntity.getElementsByTagNameNS(Namespaces.WFS, WFS2.LOCK_FEATURE_RSP) + .item(0); String lockId = lockRsp.getAttribute("lockId"); - Assert.assertFalse(lockId.isEmpty(), ErrorMessage.format( - ErrorMessageKeys.MISSING_INFOSET_ITEM, "@lockId")); + Assert.assertFalse(lockId.isEmpty(), ErrorMessage.format(ErrorMessageKeys.MISSING_INFOSET_ITEM, "@lockId")); locks.add(lockId); - String xpath = String.format( - "//wfs:FeaturesLocked/fes:ResourceId/@rid = '%s'", gmlId); + String xpath = String.format("//wfs:FeaturesLocked/fes:ResourceId/@rid = '%s'", gmlId); ETSAssert.assertXPath(xpath, lockRsp, null); Map idTofeatureType = new HashMap<>(); - idTofeatureType.put( gmlId, featureType ); - Document trxResponse = wfsClient.deleteFeatures( idTofeatureType, ProtocolBinding.ANY); + idTofeatureType.put(gmlId, featureType); + Document trxResponse = wfsClient.deleteFeatures(idTofeatureType, ProtocolBinding.ANY); String xpath2 = "//ows:Exception[@exceptionCode = 'MissingParameterValue']"; ETSAssert.assertXPath(xpath2, trxResponse.getDocumentElement(), null); } /** - * [{@code Test}] A feature instance may be locked by only one lock. An - * attempt to establish another lock should fail with exception code + * [{@code Test}] A feature instance may be locked by only one lock. An attempt to + * establish another lock should fail with exception code * {@code CannotLockAllFeatures} if lockAction = "ALL" (the default value). - * + * *

            * Sources *

            @@ -115,130 +107,113 @@ public void lockFeatureAndAttemptDelete() { @Test(description = "See ISO 19142: 12.2.3, 12.2.5") public void lockFeatureAlreadyLocked() { QName featureType = this.dataSampler.selectFeatureType(); - String gmlId = this.dataSampler.selectRandomFeatureIdentifiers( featureType, 1 ).iterator().next(); - WFSMessage.appendStoredQuery(reqEntity, this.storedQueryId, - Collections.singletonMap("id", (Object) gmlId)); + String gmlId = this.dataSampler.selectRandomFeatureIdentifiers(featureType, 1).iterator().next(); + WFSMessage.appendStoredQuery(reqEntity, this.storedQueryId, Collections.singletonMap("id", (Object) gmlId)); reqEntity.getDocumentElement().setAttribute("expiry", "60"); - Response rsp = wfsClient.submitRequest(reqEntity, - ProtocolBinding.ANY); + Response rsp = wfsClient.submitRequest(reqEntity, ProtocolBinding.ANY); this.rspEntity = rsp.readEntity(Document.class); - Assert.assertEquals(rsp.getStatus(), - Status.OK.getStatusCode(), + Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - Element lockRsp = (Element) this.rspEntity.getElementsByTagNameNS( - Namespaces.WFS, WFS2.LOCK_FEATURE_RSP).item(0); + Element lockRsp = (Element) this.rspEntity.getElementsByTagNameNS(Namespaces.WFS, WFS2.LOCK_FEATURE_RSP) + .item(0); locks.add(lockRsp.getAttribute("lockId")); // try to lock it again (without specifying lockId) reqEntity.getDocumentElement().setAttribute("expiry", "180"); rsp = wfsClient.submitRequest(reqEntity, ProtocolBinding.ANY); this.rspEntity = rsp.readEntity(Document.class); - Assert.assertEquals(rsp.getStatus(), - Status.BAD_REQUEST.getStatusCode(), + Assert.assertEquals(rsp.getStatus(), Status.BAD_REQUEST.getStatusCode(), ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); String xpath = "//ows:Exception[@exceptionCode = 'CannotLockAllFeatures']"; ETSAssert.assertXPath(xpath, this.rspEntity.getDocumentElement(), null); } /** - * [{@code Test}] Locks all feature instances of a given type using default - * values for all locking options. If no data exist for a given feature type - * it is skipped. The response entity must include a lockId attribute and - * the wfs:FeaturesLocked element. The wfs:FeatureNotLocked element must not - * be present. - * + * [{@code Test}] Locks all feature instances of a given type using default values for + * all locking options. If no data exist for a given feature type it is skipped. The + * response entity must include a lockId attribute and the wfs:FeaturesLocked element. + * The wfs:FeatureNotLocked element must not be present. + * *

            * Sources *

            *
              *
            • ISO 19142:2010, cl. 12.3.2: XML-encoding
            • *
            - * - * @param binding - * The ProtocolBinding to use for the request. - * @param featureType - * A QName object denoting the feature type name. + * @param binding The ProtocolBinding to use for the request. + * @param featureType A QName object denoting the feature type name. */ @Test(description = "See ISO 19142: 12.3.2", dataProvider = "protocol-featureType") public void lockAllFeaturesByType(ProtocolBinding binding, QName featureType) { if (!this.featureInfo.get(featureType).isInstantiated()) { - throw new SkipException("No data available for feature type " - + featureType); + throw new SkipException("No data available for feature type " + featureType); } WFSMessage.appendSimpleQuery(this.reqEntity, featureType); - URI endpoint = ServiceMetadataUtils.getOperationEndpoint( - this.wfsMetadata, WFS2.LOCK_FEATURE, binding); - Response rsp = wfsClient.submitRequest(new DOMSource(reqEntity), - binding, endpoint); + URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.LOCK_FEATURE, binding); + Response rsp = wfsClient.submitRequest(new DOMSource(reqEntity), binding, endpoint); this.rspEntity = extractBodyAsDocument(rsp); - Assert.assertEquals(rsp.getStatus(), - Status.OK.getStatusCode(), + Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); Element lockRsp = this.rspEntity.getDocumentElement(); Assert.assertEquals(lockRsp.getLocalName(), WFS2.LOCK_FEATURE_RSP, ErrorMessage.get(ErrorMessageKeys.LOCAL_NAME)); String lockId = lockRsp.getAttribute("lockId"); - Assert.assertFalse(lockId.isEmpty(), ErrorMessage.format( - ErrorMessageKeys.MISSING_INFOSET_ITEM, - "@lockId in " + lockRsp.getNodeName())); + Assert.assertFalse(lockId.isEmpty(), + ErrorMessage.format(ErrorMessageKeys.MISSING_INFOSET_ITEM, "@lockId in " + lockRsp.getNodeName())); locks.add(lockId); ETSAssert.assertXPath("//wfs:FeaturesLocked", lockRsp, null); ETSAssert.assertXPath("not(//wfs:FeaturesNotLocked)", lockRsp, null); } - /** - * [{@code Test}] An attempt to reset a lock with locKId and fes:AbstractQueryExpression - * should produce a service exception with error code "OperationParsingFailed" and HTTP - * status code 400 (Bad Request). - * - *

            - * Note: The WFS 2.0.2 specification specifies this behaviour in detail (12.2.4.2 lockId parameter): - * - * "If both a lockId parameter and one or more fes:AbstractQueryExpression elements are - * included in a LockFeature request then the server shall raise an OperationParsingFailed - * exception (see 7.5).[24]" - *

            - * - *

            - * Sources - *

            - *
              - *
            • 09-025r2, cl. 12.2.4.2: lockId parameter
            • - *
            • 09-025r2, Table D.2
            • - *
            - */ - @Test(description = "See 09-025r2: 12.2.4.2") - public void lockFeatureWithLockIdAndQuery() { - if(!"2.0.2".equals( this.wfsVersion) ){ - throw new SkipException( "Tested only for WFS 2.0.2" ); - } + /** + * [{@code Test}] An attempt to reset a lock with locKId and + * fes:AbstractQueryExpression should produce a service exception with error code + * "OperationParsingFailed" and HTTP status code 400 (Bad Request). + * + *

            + * Note: The WFS 2.0.2 specification specifies this behaviour in + * detail (12.2.4.2 lockId parameter): + * + * "If both a lockId parameter and one or more fes:AbstractQueryExpression elements + * are included in a LockFeature request then the server shall raise an + * OperationParsingFailed exception (see 7.5).[24]" + *

            + * + *

            + * Sources + *

            + *
              + *
            • 09-025r2, cl. 12.2.4.2: lockId parameter
            • + *
            • 09-025r2, Table D.2
            • + *
            + */ + @Test(description = "See 09-025r2: 12.2.4.2") + public void lockFeatureWithLockIdAndQuery() { + if (!"2.0.2".equals(this.wfsVersion)) { + throw new SkipException("Tested only for WFS 2.0.2"); + } QName featureType = this.dataSampler.selectFeatureType(); - WFSMessage.appendSimpleQuery(this.reqEntity, featureType); - this.reqEntity.getDocumentElement().setAttribute("expiry", "10"); - Response rsp = wfsClient.submitRequest(this.reqEntity, - ProtocolBinding.ANY); - this.rspEntity = rsp.readEntity(Document.class); - Assert.assertEquals(rsp.getStatus(), - Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - Element featureColl = (Element) this.rspEntity.getElementsByTagNameNS( - Namespaces.WFS, WFS2.FEATURE_COLLECTION).item(0); - String lockId = featureColl.getAttribute("lockId"); - Assert.assertFalse(lockId.isEmpty(), ErrorMessage.format( - ErrorMessageKeys.MISSING_INFOSET_ITEM, "@lockId")); - locks.add(lockId); + WFSMessage.appendSimpleQuery(this.reqEntity, featureType); + this.reqEntity.getDocumentElement().setAttribute("expiry", "10"); + Response rsp = wfsClient.submitRequest(this.reqEntity, ProtocolBinding.ANY); + this.rspEntity = rsp.readEntity(Document.class); + Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + Element featureColl = (Element) this.rspEntity.getElementsByTagNameNS(Namespaces.WFS, WFS2.FEATURE_COLLECTION) + .item(0); + String lockId = featureColl.getAttribute("lockId"); + Assert.assertFalse(lockId.isEmpty(), ErrorMessage.format(ErrorMessageKeys.MISSING_INFOSET_ITEM, "@lockId")); + locks.add(lockId); - // try to reset expired lock with LockFeature request - this.reqEntity = WFSMessage.createRequestEntity("LockFeature", - this.wfsVersion); - reqEntity.getDocumentElement().setAttribute("lockId", lockId); - WFSMessage.appendSimpleQuery(this.reqEntity, featureType); - rsp = wfsClient.submitRequest(reqEntity, ProtocolBinding.ANY); - this.rspEntity = rsp.readEntity(Document.class); - Assert.assertEquals(rsp.getStatus(), - Status.BAD_REQUEST.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - String xpath = "//ows:Exception[@exceptionCode = 'OperationParsingFailed']"; - ETSAssert.assertXPath(xpath, this.rspEntity.getDocumentElement(), null); - } + // try to reset expired lock with LockFeature request + this.reqEntity = WFSMessage.createRequestEntity("LockFeature", this.wfsVersion); + reqEntity.getDocumentElement().setAttribute("lockId", lockId); + WFSMessage.appendSimpleQuery(this.reqEntity, featureType); + rsp = wfsClient.submitRequest(reqEntity, ProtocolBinding.ANY); + this.rspEntity = rsp.readEntity(Document.class); + Assert.assertEquals(rsp.getStatus(), Status.BAD_REQUEST.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + String xpath = "//ows:Exception[@exceptionCode = 'OperationParsingFailed']"; + ETSAssert.assertXPath(xpath, this.rspEntity.getDocumentElement(), null); + } } diff --git a/src/main/java/org/opengis/cite/iso19142/locking/LockingCapabilitiesTests.java b/src/main/java/org/opengis/cite/iso19142/locking/LockingCapabilitiesTests.java index 347107d6..e39bdbea 100644 --- a/src/main/java/org/opengis/cite/iso19142/locking/LockingCapabilitiesTests.java +++ b/src/main/java/org/opengis/cite/iso19142/locking/LockingCapabilitiesTests.java @@ -23,78 +23,76 @@ import org.w3c.dom.Element; /** - * Verifies that the content of the service description satisfies the - * requirements for "Locking WFS" conformance. - * + * Verifies that the content of the service description satisfies the requirements for + * "Locking WFS" conformance. + * * @see "ISO 19142:2010, cl. 8: GetCapabilities operation" */ public class LockingCapabilitiesTests extends BaseFixture { - static final String LOCKING_WFS_PHASE = "LockingWFSPhase"; - static final String SCHEMATRON_METADATA = "wfs-capabilities-2.0.sch"; + static final String LOCKING_WFS_PHASE = "LockingWFSPhase"; + static final String SCHEMATRON_METADATA = "wfs-capabilities-2.0.sch"; - @BeforeTest - public void checkSuitePreconditions(ITestContext context) { - Object failedPreconditions = context.getSuite().getAttribute(SuiteAttribute.FAILED_PRECONDITIONS.getName()); - if (null != failedPreconditions) { - throw new SkipException("One or more test suite preconditions were not satisfied: " + failedPreconditions); - } - } + @BeforeTest + public void checkSuitePreconditions(ITestContext context) { + Object failedPreconditions = context.getSuite().getAttribute(SuiteAttribute.FAILED_PRECONDITIONS.getName()); + if (null != failedPreconditions) { + throw new SkipException("One or more test suite preconditions were not satisfied: " + failedPreconditions); + } + } - /** - * Confirms that the service constraint - * {@value org.opengis.cite.iso19142.WFS2#LOCKING_WFS} has the value 'TRUE'. - * If not, all tests of locking behavior will be skipped. - * - * @param testContext - * The test (set) context. - */ - @BeforeTest - public void implementsLockingWFS(ITestContext testContext) { - this.wfsMetadata = (Document) testContext.getSuite().getAttribute(SuiteAttribute.TEST_SUBJECT.getName()); - String xpath = String.format("//ows:Constraint[@name='%s']/ows:DefaultValue = 'TRUE'", WFS2.LOCKING_WFS); - boolean xpathResult = ETSAssert.evaluateXPathToBoolean(xpath, this.wfsMetadata, null); - if(!xpathResult){ - throw new SkipException( "The service don't have the " + WFS2.LOCKING_WFS - + " implementation so tests are skipped." ); - } - } + /** + * Confirms that the service constraint + * {@value org.opengis.cite.iso19142.WFS2#LOCKING_WFS} has the value 'TRUE'. If not, + * all tests of locking behavior will be skipped. + * @param testContext The test (set) context. + */ + @BeforeTest + public void implementsLockingWFS(ITestContext testContext) { + this.wfsMetadata = (Document) testContext.getSuite().getAttribute(SuiteAttribute.TEST_SUBJECT.getName()); + String xpath = String.format("//ows:Constraint[@name='%s']/ows:DefaultValue = 'TRUE'", WFS2.LOCKING_WFS); + boolean xpathResult = ETSAssert.evaluateXPathToBoolean(xpath, this.wfsMetadata, null); + if (!xpathResult) { + throw new SkipException( + "The service don't have the " + WFS2.LOCKING_WFS + " implementation so tests are skipped."); + } + } - /** - * Builds a DOM Document representing a GetCapabilities request for a - * complete service metadata document. - */ - @BeforeClass - public void buildGetCapabilitiesRequest() { - this.reqEntity = this.docBuilder.newDocument(); - Element docElem = reqEntity.createElementNS(Namespaces.WFS, WFS2.GET_CAPABILITIES); - docElem.setAttribute(WFS2.SERVICE_PARAM, WFS2.SERVICE_TYPE_CODE); - this.reqEntity.appendChild(docElem); - } + /** + * Builds a DOM Document representing a GetCapabilities request for a complete service + * metadata document. + */ + @BeforeClass + public void buildGetCapabilitiesRequest() { + this.reqEntity = this.docBuilder.newDocument(); + Element docElem = reqEntity.createElementNS(Namespaces.WFS, WFS2.GET_CAPABILITIES); + docElem.setAttribute(WFS2.SERVICE_PARAM, WFS2.SERVICE_TYPE_CODE); + this.reqEntity.appendChild(docElem); + } + + /** + * [{@code Test}] Checks the content of the complete service metadata document for + * additional service endpoints and properties (constraints) that must be present. The + * applicable rules are incorporated into the {@value #LOCKING_WFS_PHASE} phase of the + * Schematron schema {@code wfs-capabilities-2.0.sch}. + * + *

            + * Sources + *

            + *
              + *
            • ISO 19142:2010, Table 1: Conformance classes
            • + *
            • ISO 19142:2010, Table 13: Service constraints
            • + *
            • ISO 19142:2010, cl. A.1.4: Locking WFS
            • + *
            + * + */ + @Test(description = "See ISO 19142: Table 13, A.2.23") + public void capabilitiesDescribesLockingWFS() { + SchematronValidator validator = ValidationUtils.buildSchematronValidator(SCHEMATRON_METADATA, + LOCKING_WFS_PHASE); + Result result = validator.validate(new DOMSource(this.wfsMetadata, this.wfsMetadata.getDocumentURI()), false); + Assert.assertFalse(validator.ruleViolationsDetected(), ErrorMessage.format(ErrorMessageKeys.NOT_SCHEMA_VALID, + validator.getRuleViolationCount(), XMLUtils.resultToString(result))); + } - /** - * [{@code Test}] Checks the content of the complete service metadata - * document for additional service endpoints and properties (constraints) - * that must be present. The applicable rules are incorporated into the - * {@value #LOCKING_WFS_PHASE} phase of the Schematron schema - * {@code wfs-capabilities-2.0.sch}. - * - *

            - * Sources - *

            - *
              - *
            • ISO 19142:2010, Table 1: Conformance classes
            • - *
            • ISO 19142:2010, Table 13: Service constraints
            • - *
            • ISO 19142:2010, cl. A.1.4: Locking WFS
            • - *
            - * - */ - @Test(description = "See ISO 19142: Table 13, A.2.23") - public void capabilitiesDescribesLockingWFS() { - SchematronValidator validator = ValidationUtils.buildSchematronValidator(SCHEMATRON_METADATA, - LOCKING_WFS_PHASE); - Result result = validator.validate(new DOMSource(this.wfsMetadata, this.wfsMetadata.getDocumentURI()), false); - Assert.assertFalse(validator.ruleViolationsDetected(), ErrorMessage.format(ErrorMessageKeys.NOT_SCHEMA_VALID, - validator.getRuleViolationCount(), XMLUtils.resultToString(result))); - } } diff --git a/src/main/java/org/opengis/cite/iso19142/locking/LockingFixture.java b/src/main/java/org/opengis/cite/iso19142/locking/LockingFixture.java index 22294a2b..12a365ce 100644 --- a/src/main/java/org/opengis/cite/iso19142/locking/LockingFixture.java +++ b/src/main/java/org/opengis/cite/iso19142/locking/LockingFixture.java @@ -20,58 +20,59 @@ import jakarta.ws.rs.core.Response.Status; /** - * Provides configuration methods that facilitate the testing of locking - * behavior specified for the "Locking WFS" conformance level. + * Provides configuration methods that facilitate the testing of locking behavior + * specified for the "Locking WFS" conformance level. */ public class LockingFixture extends BaseFixture { - /** List containing lock identifiers */ - protected List locks = new ArrayList(); - /** Acquires and saves sample feature data. */ - protected DataSampler dataSampler; - /** Identifier for GetFeatureById stored query */ - protected String storedQueryId; + /** List containing lock identifiers */ + protected List locks = new ArrayList(); - public LockingFixture() { - super(); - } + /** Acquires and saves sample feature data. */ + protected DataSampler dataSampler; - /** - * Obtains a DataSampler object from the test run context (the value of the - * {@link SuiteAttribute#SAMPLER SuiteAttribute.SAMPLER attribute}). - * - * @param testContext - * The test run context. - */ - @BeforeClass - public void initLockingFixture(ITestContext testContext) { - this.dataSampler = (DataSampler) testContext.getSuite().getAttribute(SuiteAttribute.SAMPLER.getName()); - this.featureInfo = this.dataSampler.getFeatureTypeInfo(); - this.storedQueryId = (this.wfsVersion.equals(WFS2.V2_0_0)) ? WFS2.QRY_GET_FEATURE_BY_ID_URN - : WFS2.QRY_GET_FEATURE_BY_ID; - } + /** Identifier for GetFeatureById stored query */ + protected String storedQueryId; + + public LockingFixture() { + super(); + } + + /** + * Obtains a DataSampler object from the test run context (the value of the + * {@link SuiteAttribute#SAMPLER SuiteAttribute.SAMPLER attribute}). + * @param testContext The test run context. + */ + @BeforeClass + public void initLockingFixture(ITestContext testContext) { + this.dataSampler = (DataSampler) testContext.getSuite().getAttribute(SuiteAttribute.SAMPLER.getName()); + this.featureInfo = this.dataSampler.getFeatureTypeInfo(); + this.storedQueryId = (this.wfsVersion.equals(WFS2.V2_0_0)) ? WFS2.QRY_GET_FEATURE_BY_ID_URN + : WFS2.QRY_GET_FEATURE_BY_ID; + } + + /** + * Releases all locks by submitting empty Transaction requests that include the lockId + * and releaseAction (="ALL") attributes. An unsuccessful request is logged (as a + * {@link Level#WARNING}). + */ + @AfterMethod + public void releaseAllLocks() { + if (locks.isEmpty()) { + return; + } + TestSuiteLogger.log(Level.CONFIG, "releaseAllLocks: " + this.locks); + Document trxEntity = WFSMessage.createRequestEntity("Transaction", this.wfsVersion); + trxEntity.getDocumentElement().setAttribute("releaseAction", "ALL"); + for (String lockId : locks) { + trxEntity.getDocumentElement().setAttribute("lockId", lockId); + Response rsp = this.wfsClient.submitRequest(trxEntity, ProtocolBinding.ANY); + if (rsp.getStatus() != Status.OK.getStatusCode()) { + String entity = rsp.readEntity(String.class); + TestSuiteLogger.log(Level.WARNING, "Failed to release lock " + lockId + "\n" + entity); + } + } + locks.clear(); + } - /** - * Releases all locks by submitting empty Transaction requests that include - * the lockId and releaseAction (="ALL") attributes. An unsuccessful request - * is logged (as a {@link Level#WARNING}). - */ - @AfterMethod - public void releaseAllLocks() { - if (locks.isEmpty()) { - return; - } - TestSuiteLogger.log(Level.CONFIG, "releaseAllLocks: " + this.locks); - Document trxEntity = WFSMessage.createRequestEntity("Transaction", this.wfsVersion); - trxEntity.getDocumentElement().setAttribute("releaseAction", "ALL"); - for (String lockId : locks) { - trxEntity.getDocumentElement().setAttribute("lockId", lockId); - Response rsp = this.wfsClient.submitRequest(trxEntity, ProtocolBinding.ANY); - if (rsp.getStatus() != Status.OK.getStatusCode()) { - String entity = rsp.readEntity(String.class); - TestSuiteLogger.log(Level.WARNING, "Failed to release lock " + lockId + "\n" + entity); - } - } - locks.clear(); - } } diff --git a/src/main/java/org/opengis/cite/iso19142/locking/package-info.java b/src/main/java/org/opengis/cite/iso19142/locking/package-info.java index 189b4a03..968cc9e3 100644 --- a/src/main/java/org/opengis/cite/iso19142/locking/package-info.java +++ b/src/main/java/org/opengis/cite/iso19142/locking/package-info.java @@ -1,17 +1,18 @@ /** - * This package contains tests that assess behavior of the SUT with respect to - * the "Locking WFS" conformance level. It builds upon the "Transaction WFS" - * conformance level and adds support for locking operations that allow - * exclusive access to feature instances for the purpose of modifying or - * deleting them. - * + * This package contains tests that assess behavior of the SUT with respect to the + * "Locking WFS" conformance level. It builds upon the "Transaction WFS" conformance level + * and adds support for locking operations that allow exclusive access to feature + * instances for the purpose of modifying or deleting them. + * * At least one of the following operations must be implemented: *
              *
            • GetFeatureWithLock
            • *
            • LockFeature
            • *
            * - *

            Sources

            + *

            + * Sources + *

            *
              *
            • ISO ISO 19142:2010, cl. 2, Table 1
            • *
            • ISO ISO 19142:2010, cl. A.1.4: Locking WFS
            • diff --git a/src/main/java/org/opengis/cite/iso19142/package-info.java b/src/main/java/org/opengis/cite/iso19142/package-info.java index d2aa6529..24b8c343 100644 --- a/src/main/java/org/opengis/cite/iso19142/package-info.java +++ b/src/main/java/org/opengis/cite/iso19142/package-info.java @@ -1,9 +1,10 @@ /** - * This executable test suite (ETS) validates WFS 2.0 implementations in accord - * with ISO 19136:2007. The root package includes supporting classes of general - * utility such as the main controller, listeners, and reporters. + * This executable test suite (ETS) validates WFS 2.0 implementations in accord with ISO + * 19136:2007. The root package includes supporting classes of general utility such as the + * main controller, listeners, and reporters. * - *

              Subsidiary packages correspond to distinct test groups such as conformance - * classes.

              + *

              + * Subsidiary packages correspond to distinct test groups such as conformance classes. + *

              */ package org.opengis.cite.iso19142; \ No newline at end of file diff --git a/src/main/java/org/opengis/cite/iso19142/paging/PagingTests.java b/src/main/java/org/opengis/cite/iso19142/paging/PagingTests.java index ee2695da..c408e5ea 100644 --- a/src/main/java/org/opengis/cite/iso19142/paging/PagingTests.java +++ b/src/main/java/org/opengis/cite/iso19142/paging/PagingTests.java @@ -38,174 +38,167 @@ import jakarta.ws.rs.core.Response.Status; /** - * Provides test methods that verify the pagination of search results. This - * capability allows a client to iterate over the items in a result set. The - * lifetime of a cached result set is indicated by the value of the operation - * constraint ResponseCacheTimeout (duration in seconds); if this - * constraint is not specified then the response cache never times out. - * + * Provides test methods that verify the pagination of search results. This capability + * allows a client to iterate over the items in a result set. The lifetime of a cached + * result set is indicated by the value of the operation constraint + * ResponseCacheTimeout (duration in seconds); if this constraint is not + * specified then the response cache never times out. + * * @see "OGC 09-025: Table 14" */ public class PagingTests extends BaseFixture { - private int cacheTimeout = 0; - - private DataSampler dataSampler; - - @BeforeClass - public void initDataSample(ITestContext testContext) { - this.dataSampler = (DataSampler) testContext.getSuite().getAttribute( SuiteAttribute.SAMPLER.getName() ); - } - - @BeforeClass - public void getPagingConstraints(ITestContext testContext) { - Object obj = testContext.getAttribute(ResponsePaging.CACHE_TIMEOUT); - if (null != obj) { - this.cacheTimeout = Integer.class.cast(obj); - } - - } - - /** - * [{@code Test}] Submits a GetFeature request with a very small page size - * (count="1") and resultType="hits". The initial response entity shall be - * an empty feature collection with numberReturned = 0. The value of the - * next attribute shall be a URI that refers to the first page - * of results. Furthermore, the previous attribute must not - * appear. - */ - @Test(description = "See OGC 09-025: 7.7.4.2") - public void getFeatureWithHitsOnly() { - int count = 1; - this.reqEntity = WFSMessage.createRequestEntity("GetFeature-Minimal", this.wfsVersion); - this.reqEntity.getDocumentElement().setAttribute("count", "" + count); - this.reqEntity.getDocumentElement().setAttribute("resultType", "hits"); - QName featureType = anyFeatureType(this.featureInfo); - WFSMessage.appendSimpleQuery(this.reqEntity, featureType); - URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.GET_FEATURE, - ProtocolBinding.GET); - Response rsp = wfsClient.submitRequest(new DOMSource(this.reqEntity), ProtocolBinding.GET, endpoint); - this.rspEntity = extractBodyAsDocument(rsp); - ETSAssert.assertQualifiedName(rspEntity.getDocumentElement(), - new QName(Namespaces.WFS, WFS2.FEATURE_COLLECTION)); - String numReturned = this.rspEntity.getDocumentElement().getAttribute("numberReturned"); - assertEquals(Integer.parseInt(numReturned), 0, ErrorMessage.get(ErrorMessageKeys.NUM_RETURNED)); - String prev = this.rspEntity.getDocumentElement().getAttribute("previous"); - assertTrue(prev.isEmpty(), "Unexpected attribute found in response entity: 'previous'."); - String next = this.rspEntity.getDocumentElement().getAttribute("next"); - assertFalse(next.isEmpty(), "Expected attribute not found in response entity: 'next'."); - rsp = retrieveResource(next); - this.rspEntity = extractBodyAsDocument(rsp); - assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - ETSAssert.assertQualifiedName(rspEntity.getDocumentElement(), - new QName(Namespaces.WFS, WFS2.FEATURE_COLLECTION)); - numReturned = this.rspEntity.getDocumentElement().getAttribute("numberReturned"); - assertEquals(Integer.parseInt(numReturned), count, ErrorMessage.get(ErrorMessageKeys.NUM_RETURNED)); - } - - /** - * [{@code Test}] Submits a GetFeature request with count = "4" and - * resultType = "results". The feature identifiers in the first page of - * results are collected and the next page is retrieved. Then the previous - * (first) page is retrieved and the feature identifiers are extracted; they - * must match the known values from the first page. - */ - @Test(description = "See OGC 09-025: 7.7.4.4.1") - public void traverseResultSetInBothDirections() { - QName featureType = featureTypeWithAtLeastTwoFeatures(this.featureInfo); - if(featureType == null) - throw new SkipException( "Could not find appropriate feature type. A feature type with at least two features is required." ); - this.reqEntity = WFSMessage.createRequestEntity("GetFeature-Minimal", this.wfsVersion); - int count = 1; - this.reqEntity.getDocumentElement().setAttribute("count", Integer.toString(count)); - WFSMessage.appendSimpleQuery(this.reqEntity, featureType); - URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.GET_FEATURE, - ProtocolBinding.GET); - Response rsp = wfsClient.submitRequest(new DOMSource(this.reqEntity), ProtocolBinding.GET, endpoint); - this.rspEntity = extractBodyAsDocument(rsp); - ETSAssert.assertQualifiedName(rspEntity.getDocumentElement(), - new QName(Namespaces.WFS, WFS2.FEATURE_COLLECTION)); - ETSAssert.assertFeatureCount(this.rspEntity, featureType, count); - Set initialMembers = WFSMessage.extractFeatureIdentifiers(this.rspEntity, featureType); - // retrieve second page - String next = this.rspEntity.getDocumentElement().getAttribute("next"); - assertFalse(next.isEmpty(), "Expected attribute not found in response entity: 'next'."); - rsp = retrieveResource(next); - this.rspEntity = extractBodyAsDocument(rsp); - // previous should return first page - String prev = this.rspEntity.getDocumentElement().getAttribute("previous"); - assertFalse(prev.isEmpty(), "Expected attribute not found in response entity: 'previous'."); - rsp = retrieveResource(prev); - this.rspEntity = extractBodyAsDocument(rsp); - Set prevMembers = WFSMessage.extractFeatureIdentifiers(this.rspEntity, featureType); - assertTrue(prevMembers.containsAll(initialMembers), - String.format( - "Expected members of previous page to include all members of first page. \nFeature Identifiers: %s", - initialMembers)); - } - - // startIndex - // getLastPage: request last page, NO NEXT - // GetPropertyValue - - /** - * Dereferences the given URI reference and returns the response message. - * - * @param uriRef - * A String denoting an absolute 'http' URI. - * @return A representation of the HTTP response message. - */ - Response retrieveResource(String uriRef) { - Logger.getLogger(getClass().getName()).log(Level.FINE, "Attempting to retrieve XML resource from {0}", uriRef); - URI uri; - try { - uri = new URI(uriRef); - } catch (URISyntaxException e) { - throw new AssertionError(e.getMessage()); - } - WebTarget target = this.wfsClient.getClient().target(uri); - Builder builder = target.request(); - Response rsp = builder.accept(MediaType.APPLICATION_XML_TYPE).buildGet().invoke(Response.class); - return rsp; - } - - /** - * Returns the name of a feature type for which data exist. - * - * @param featureInfo - * Information about feature types gleaned from the SUT. - * @return The qualified name of a feature type. - */ - QName anyFeatureType(Map featureInfo) { - QName qName = null; - for (QName featureType : featureInfo.keySet()) { - if (featureInfo.get(featureType).isInstantiated()) { - qName = featureType; - break; - } - } - return qName; - } - - /** - * Returns the name of a feature type for which at least two features exist. - * - * @param featureInfo - * Information about feature types gleaned from the SUT. - * @return The qualified name of a feature type. - */ - QName featureTypeWithAtLeastTwoFeatures( Map featureInfo ) { - for ( Map.Entry featureTypeNameToInfo : featureInfo.entrySet() ) { - FeatureTypeInfo featureTypeValue = featureTypeNameToInfo.getValue(); - if ( featureTypeValue.isInstantiated() ) { - QName featureTypeName = featureTypeNameToInfo.getKey(); - Set featureId = dataSampler.selectRandomFeatureIdentifiers( featureTypeName, 2 ); - if ( featureId.size() > 1 ) - return featureTypeName; - } - } - return null; - } - + private int cacheTimeout = 0; + + private DataSampler dataSampler; + + @BeforeClass + public void initDataSample(ITestContext testContext) { + this.dataSampler = (DataSampler) testContext.getSuite().getAttribute(SuiteAttribute.SAMPLER.getName()); + } + + @BeforeClass + public void getPagingConstraints(ITestContext testContext) { + Object obj = testContext.getAttribute(ResponsePaging.CACHE_TIMEOUT); + if (null != obj) { + this.cacheTimeout = Integer.class.cast(obj); + } + + } + + /** + * [{@code Test}] Submits a GetFeature request with a very small page size (count="1") + * and resultType="hits". The initial response entity shall be an empty feature + * collection with numberReturned = 0. The value of the next attribute + * shall be a URI that refers to the first page of results. Furthermore, the + * previous attribute must not appear. + */ + @Test(description = "See OGC 09-025: 7.7.4.2") + public void getFeatureWithHitsOnly() { + int count = 1; + this.reqEntity = WFSMessage.createRequestEntity("GetFeature-Minimal", this.wfsVersion); + this.reqEntity.getDocumentElement().setAttribute("count", "" + count); + this.reqEntity.getDocumentElement().setAttribute("resultType", "hits"); + QName featureType = anyFeatureType(this.featureInfo); + WFSMessage.appendSimpleQuery(this.reqEntity, featureType); + URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.GET_FEATURE, + ProtocolBinding.GET); + Response rsp = wfsClient.submitRequest(new DOMSource(this.reqEntity), ProtocolBinding.GET, endpoint); + this.rspEntity = extractBodyAsDocument(rsp); + ETSAssert.assertQualifiedName(rspEntity.getDocumentElement(), + new QName(Namespaces.WFS, WFS2.FEATURE_COLLECTION)); + String numReturned = this.rspEntity.getDocumentElement().getAttribute("numberReturned"); + assertEquals(Integer.parseInt(numReturned), 0, ErrorMessage.get(ErrorMessageKeys.NUM_RETURNED)); + String prev = this.rspEntity.getDocumentElement().getAttribute("previous"); + assertTrue(prev.isEmpty(), "Unexpected attribute found in response entity: 'previous'."); + String next = this.rspEntity.getDocumentElement().getAttribute("next"); + assertFalse(next.isEmpty(), "Expected attribute not found in response entity: 'next'."); + rsp = retrieveResource(next); + this.rspEntity = extractBodyAsDocument(rsp); + assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + ETSAssert.assertQualifiedName(rspEntity.getDocumentElement(), + new QName(Namespaces.WFS, WFS2.FEATURE_COLLECTION)); + numReturned = this.rspEntity.getDocumentElement().getAttribute("numberReturned"); + assertEquals(Integer.parseInt(numReturned), count, ErrorMessage.get(ErrorMessageKeys.NUM_RETURNED)); + } + + /** + * [{@code Test}] Submits a GetFeature request with count = "4" and resultType = + * "results". The feature identifiers in the first page of results are collected and + * the next page is retrieved. Then the previous (first) page is retrieved and the + * feature identifiers are extracted; they must match the known values from the first + * page. + */ + @Test(description = "See OGC 09-025: 7.7.4.4.1") + public void traverseResultSetInBothDirections() { + QName featureType = featureTypeWithAtLeastTwoFeatures(this.featureInfo); + if (featureType == null) + throw new SkipException( + "Could not find appropriate feature type. A feature type with at least two features is required."); + this.reqEntity = WFSMessage.createRequestEntity("GetFeature-Minimal", this.wfsVersion); + int count = 1; + this.reqEntity.getDocumentElement().setAttribute("count", Integer.toString(count)); + WFSMessage.appendSimpleQuery(this.reqEntity, featureType); + URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.GET_FEATURE, + ProtocolBinding.GET); + Response rsp = wfsClient.submitRequest(new DOMSource(this.reqEntity), ProtocolBinding.GET, endpoint); + this.rspEntity = extractBodyAsDocument(rsp); + ETSAssert.assertQualifiedName(rspEntity.getDocumentElement(), + new QName(Namespaces.WFS, WFS2.FEATURE_COLLECTION)); + ETSAssert.assertFeatureCount(this.rspEntity, featureType, count); + Set initialMembers = WFSMessage.extractFeatureIdentifiers(this.rspEntity, featureType); + // retrieve second page + String next = this.rspEntity.getDocumentElement().getAttribute("next"); + assertFalse(next.isEmpty(), "Expected attribute not found in response entity: 'next'."); + rsp = retrieveResource(next); + this.rspEntity = extractBodyAsDocument(rsp); + // previous should return first page + String prev = this.rspEntity.getDocumentElement().getAttribute("previous"); + assertFalse(prev.isEmpty(), "Expected attribute not found in response entity: 'previous'."); + rsp = retrieveResource(prev); + this.rspEntity = extractBodyAsDocument(rsp); + Set prevMembers = WFSMessage.extractFeatureIdentifiers(this.rspEntity, featureType); + assertTrue(prevMembers.containsAll(initialMembers), String.format( + "Expected members of previous page to include all members of first page. \nFeature Identifiers: %s", + initialMembers)); + } + + // startIndex + // getLastPage: request last page, NO NEXT + // GetPropertyValue + + /** + * Dereferences the given URI reference and returns the response message. + * @param uriRef A String denoting an absolute 'http' URI. + * @return A representation of the HTTP response message. + */ + Response retrieveResource(String uriRef) { + Logger.getLogger(getClass().getName()).log(Level.FINE, "Attempting to retrieve XML resource from {0}", uriRef); + URI uri; + try { + uri = new URI(uriRef); + } + catch (URISyntaxException e) { + throw new AssertionError(e.getMessage()); + } + WebTarget target = this.wfsClient.getClient().target(uri); + Builder builder = target.request(); + Response rsp = builder.accept(MediaType.APPLICATION_XML_TYPE).buildGet().invoke(Response.class); + return rsp; + } + + /** + * Returns the name of a feature type for which data exist. + * @param featureInfo Information about feature types gleaned from the SUT. + * @return The qualified name of a feature type. + */ + QName anyFeatureType(Map featureInfo) { + QName qName = null; + for (QName featureType : featureInfo.keySet()) { + if (featureInfo.get(featureType).isInstantiated()) { + qName = featureType; + break; + } + } + return qName; + } + + /** + * Returns the name of a feature type for which at least two features exist. + * @param featureInfo Information about feature types gleaned from the SUT. + * @return The qualified name of a feature type. + */ + QName featureTypeWithAtLeastTwoFeatures(Map featureInfo) { + for (Map.Entry featureTypeNameToInfo : featureInfo.entrySet()) { + FeatureTypeInfo featureTypeValue = featureTypeNameToInfo.getValue(); + if (featureTypeValue.isInstantiated()) { + QName featureTypeName = featureTypeNameToInfo.getKey(); + Set featureId = dataSampler.selectRandomFeatureIdentifiers(featureTypeName, 2); + if (featureId.size() > 1) + return featureTypeName; + } + } + return null; + } + } diff --git a/src/main/java/org/opengis/cite/iso19142/paging/ResponsePaging.java b/src/main/java/org/opengis/cite/iso19142/paging/ResponsePaging.java index 46e1211b..14061cc2 100644 --- a/src/main/java/org/opengis/cite/iso19142/paging/ResponsePaging.java +++ b/src/main/java/org/opengis/cite/iso19142/paging/ResponsePaging.java @@ -14,11 +14,10 @@ /** * Checks preconditions for running tests to verify that the IUT satisfies the - * requirements of the Response paging conformance class. All - * tests are skipped if any preconditions are not met. The service constraint - * {@value #IMPL_RESULT_PAGING} must be set to "TRUE" in the capabilities - * document. - * + * requirements of the Response paging conformance class. All tests are + * skipped if any preconditions are not met. The service constraint + * {@value #IMPL_RESULT_PAGING} must be set to "TRUE" in the capabilities document. + * *
                * {@code
                * 
              @@ -30,55 +29,57 @@
                * 
                * }
                * 
              - * - * An implementation may ensure transactional consistency for response paging, - * as indicated by the value of the operation constraint - * PagingIsTransactionSafe (default: FALSE). This constraint applies to - * GetFeature, GetFeatureWithLock, and GetPropertyValue requests. - * + * + * An implementation may ensure transactional consistency for response paging, as + * indicated by the value of the operation constraint PagingIsTransactionSafe + * (default: FALSE). This constraint applies to GetFeature, GetFeatureWithLock, and + * GetPropertyValue requests. + * * @see ATC - * A.1.10: Response Paging + * "http://docs.opengeospatial.org/is/09-025r2/09-025r2.html#requirement_10">ATC A.1.10: + * Response Paging */ public class ResponsePaging { - public final static String IMPL_RESULT_PAGING = "ImplementsResultPaging"; - public final static String CACHE_TIMEOUT = "ResponseCacheTimeout"; - public final static String PAGING_IS_CONSISTENT = "PagingIsTransactionSafe"; - public final static String COUNT_DEFAULT = "CountDefault"; + public final static String IMPL_RESULT_PAGING = "ImplementsResultPaging"; + + public final static String CACHE_TIMEOUT = "ResponseCacheTimeout"; + + public final static String PAGING_IS_CONSISTENT = "PagingIsTransactionSafe"; + + public final static String COUNT_DEFAULT = "CountDefault"; + + /** + * This {@literal @BeforeTest} configuration method checks the implementation status + * of the {@value #IMPL_RESULT_PAGING} conformance class and looks up the values of + * the relevant operation constraints ({@value ResponsePaging#CACHE_TIMEOUT}, + * {@value #PAGING_IS_CONSISTENT}, and , {@value #COUNT_DEFAULT}). + * @param testContext Information about the test run environment. + * @see "OGC 09-025: Table 14, A.2.20.2" + */ + @BeforeTest + public void implementsResponsePaging(ITestContext testContext) { + Document wfsMetadata = (Document) testContext.getSuite().getAttribute(SuiteAttribute.TEST_SUBJECT.getName()); + if (!ServiceMetadataUtils.implementsConformanceClass(wfsMetadata, IMPL_RESULT_PAGING)) { + throw new SkipException(ErrorMessage.format(ErrorMessageKeys.NOT_IMPLEMENTED, IMPL_RESULT_PAGING)); + } + String pagingIsConsistent = ServiceMetadataUtils.getConstraintValue(wfsMetadata, PAGING_IS_CONSISTENT); + testContext.setAttribute(PAGING_IS_CONSISTENT, Boolean.valueOf(pagingIsConsistent)); + String cacheTimeout = ServiceMetadataUtils.getConstraintValue(wfsMetadata, CACHE_TIMEOUT); + String countDefault = ServiceMetadataUtils.getConstraintValue(wfsMetadata, COUNT_DEFAULT); + try { + if (!cacheTimeout.isEmpty()) { + testContext.setAttribute(CACHE_TIMEOUT, Integer.valueOf(cacheTimeout)); + } + if (!countDefault.isEmpty()) { + testContext.setAttribute(COUNT_DEFAULT, Integer.valueOf(countDefault)); + } + } + catch (NumberFormatException e) { + // cache never times out or page size is unconstrained + TestSuiteLogger.log(Level.WARNING, + String.format("Invalid constraint (expected integer value): %s", e.getMessage())); + } + } - /** - * This {@literal @BeforeTest} configuration method checks the - * implementation status of the {@value #IMPL_RESULT_PAGING} conformance - * class and looks up the values of the relevant operation constraints - * ({@value ResponsePaging#CACHE_TIMEOUT}, {@value #PAGING_IS_CONSISTENT}, - * and , {@value #COUNT_DEFAULT}). - * - * @param testContext - * Information about the test run environment. - * @see "OGC 09-025: Table 14, A.2.20.2" - */ - @BeforeTest - public void implementsResponsePaging(ITestContext testContext) { - Document wfsMetadata = (Document) testContext.getSuite().getAttribute(SuiteAttribute.TEST_SUBJECT.getName()); - if (!ServiceMetadataUtils.implementsConformanceClass(wfsMetadata, IMPL_RESULT_PAGING)) { - throw new SkipException(ErrorMessage.format(ErrorMessageKeys.NOT_IMPLEMENTED, IMPL_RESULT_PAGING)); - } - String pagingIsConsistent = ServiceMetadataUtils.getConstraintValue(wfsMetadata, PAGING_IS_CONSISTENT); - testContext.setAttribute(PAGING_IS_CONSISTENT, Boolean.valueOf(pagingIsConsistent)); - String cacheTimeout = ServiceMetadataUtils.getConstraintValue(wfsMetadata, CACHE_TIMEOUT); - String countDefault = ServiceMetadataUtils.getConstraintValue(wfsMetadata, COUNT_DEFAULT); - try { - if (!cacheTimeout.isEmpty()) { - testContext.setAttribute(CACHE_TIMEOUT, Integer.valueOf(cacheTimeout)); - } - if (!countDefault.isEmpty()) { - testContext.setAttribute(COUNT_DEFAULT, Integer.valueOf(countDefault)); - } - } catch (NumberFormatException e) { - // cache never times out or page size is unconstrained - TestSuiteLogger.log(Level.WARNING, - String.format("Invalid constraint (expected integer value): %s", e.getMessage())); - } - } } diff --git a/src/main/java/org/opengis/cite/iso19142/paging/package-info.java b/src/main/java/org/opengis/cite/iso19142/paging/package-info.java index a6042e27..d424f817 100644 --- a/src/main/java/org/opengis/cite/iso19142/paging/package-info.java +++ b/src/main/java/org/opengis/cite/iso19142/paging/package-info.java @@ -1,18 +1,16 @@ /** - * This package contains tests to verify that the IUT satisfies the requirements - * of the Response paging conformance class. If the WFS service - * constraint ImplementsResultPaging is set to "TRUE" in the - * capabilities document, a client may choose to page through the items in a - * result set. - * + * This package contains tests to verify that the IUT satisfies the requirements of the + * Response paging conformance class. If the WFS service constraint + * ImplementsResultPaging is set to "TRUE" in the capabilities document, a client + * may choose to page through the items in a result set. + * *

              - * A server implementation that caches query results shall advertise for how - * long items can stay in the query cache before they are invalidated; the - * ResponseCacheTimeout constraint in the capabilities document is used - * for this purpose (duration in seconds). A service may also support - * transactional consistency for paging using the - * PagingIsTransactionSafe constraint. If enabled, a result set is not - * affected by any transactions that were completed after the original query was + * A server implementation that caches query results shall advertise for how long items + * can stay in the query cache before they are invalidated; the + * ResponseCacheTimeout constraint in the capabilities document is used for this + * purpose (duration in seconds). A service may also support transactional consistency for + * paging using the PagingIsTransactionSafe constraint. If enabled, a result set + * is not affected by any transactions that were completed after the original query was * submitted. *

              * diff --git a/src/main/java/org/opengis/cite/iso19142/querymgmt/CreateStoredQueryTests.java b/src/main/java/org/opengis/cite/iso19142/querymgmt/CreateStoredQueryTests.java index 6346a737..547679b0 100644 --- a/src/main/java/org/opengis/cite/iso19142/querymgmt/CreateStoredQueryTests.java +++ b/src/main/java/org/opengis/cite/iso19142/querymgmt/CreateStoredQueryTests.java @@ -36,143 +36,141 @@ import jakarta.ws.rs.core.Response.Status; /** - * Provides test methods that verify the creation of stored queries. A - * conforming implementation must support the query language + * Provides test methods that verify the creation of stored queries. A conforming + * implementation must support the query language * {@value org.opengis.cite.iso19142.querymgmt.StoredQueryManagement#LANG_WFS_QUERY}. - * Other query languages may be listed in the capabilities document in the - * context of the CreateStoredQuery request. + * Other query languages may be listed in the capabilities document in the context of the + * CreateStoredQuery request. */ public class CreateStoredQueryTests extends BaseFixture { - public final static String QRY_GET_FEATURE_BY_TYPE = "urn:example:wfs2-query:GetFeatureByTypeName"; - public final static String QRY_GET_FEATURE_BY_NAME = "urn:example:wfs2-query:GetFeatureByName"; - public final static String QRY_INVALID_LANG = "urn:example:wfs2-query:InvalidLang"; - private List createdStoredQueries = new ArrayList<>(2); - private DataSampler dataSampler; - - @BeforeClass() - public void initQueryFilterFixture( ITestContext testContext ) { - ISuite suite = testContext.getSuite(); - this.dataSampler = (DataSampler) suite.getAttribute( SuiteAttribute.SAMPLER.getName() ); - } - - /** - * This configuration method deletes specific stored queries that may - * already be known to the IUT; specifically: - *
                - *
              • {@value #QRY_GET_FEATURE_BY_TYPE}
              • - *
              • {@value #QRY_GET_FEATURE_BY_NAME}
              • - *
              • {@value #QRY_INVALID_LANG}
              • - *
              - * - * The remaining tests are skipped if this fails because a precondition - * cannot be met. - */ - @BeforeClass - public void deleteQueriesAtStart() { - List knownQueries = this.wfsClient.listStoredQueries(); - for (String queryId : new String[] { QRY_GET_FEATURE_BY_TYPE, QRY_GET_FEATURE_BY_NAME, QRY_INVALID_LANG }) { - if (!knownQueries.contains(queryId)) - continue; - int status = this.wfsClient.deleteStoredQuery(queryId); - if (status >= 400) { - throw new SkipException(String.format("[%s] Error dropping stored query: %s (status code was %d)", - getClass().getName(), queryId, status)); - } - } - } - - /** - * This configuration method drops any stored queries that may have been - * created by a test method. If an error occurs a WARNING message is logged. - */ - @AfterClass - public void deleteQueriesAtEnd() { - for (String queryId : this.createdStoredQueries) { - int status = this.wfsClient.deleteStoredQuery(queryId); - if (status >= 400) { - TestSuiteLogger.log(Level.WARNING, - String.format("[%s] Error dropping stored query: %s (status code was %d)", getClass().getName(), - queryId, status)); - } - } - this.createdStoredQueries.clear(); - } - - /** - * [{@code Test}] Submits a CreateStoredQuery request to - * retrieve features by type name. The query identifier is - * {@value #QRY_GET_FEATURE_BY_TYPE}. The response is expected to contain an - * XML entity with "CreateStoredQueryResponse" as the document element. The - * query is then invoked for all feature types for which data exist. - */ - @Test(description = "See OGC 09-025: 14.2, 14.5.2") - public void createGetFeatureByTypeName() { - this.reqEntity = WFSMessage.createRequestEntity(ETS_PKG + "/querymgmt/CreateStoredQuery-GetFeatureByTypeName", - this.wfsVersion); - URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.CREATE_STORED_QRY, - ProtocolBinding.POST); - Response rsp = this.wfsClient.submitRequest(new DOMSource(this.reqEntity), ProtocolBinding.POST, - endpoint); - this.rspEntity = rsp.readEntity(Document.class); - assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - ETSAssert.assertQualifiedName(this.rspEntity.getDocumentElement(), - new QName(WFS2.NS_URI, "CreateStoredQueryResponse")); - this.createdStoredQueries.add(QRY_GET_FEATURE_BY_TYPE); - for (QName featureTypeName : this.featureInfo.keySet()) { - if (!this.featureInfo.get(featureTypeName).isInstantiated()) - continue; - Map params = Collections.singletonMap("typeName", featureTypeName); - Document doc = this.wfsClient.invokeStoredQuery(QRY_GET_FEATURE_BY_TYPE, params); - ETSAssert.assertResultSetNotEmpty(doc, featureTypeName); - } - } - - /** - * [{@code Test}] Submits a CreateStoredQuery request that - * contains a query expressed in an unsupported query language. An exception - * report is expected in response containing the error code - * "InvalidParameterValue". - */ - @Test(description = "See OGC 09-025: 14.2.2.5.3, 14.7") - public void createStoredQueryWithUnsupportedQueryLanguage() { - this.reqEntity = WFSMessage.createRequestEntity(ETS_PKG + "/querymgmt/CreateStoredQuery-GetFeatureByTypeName", - this.wfsVersion); - Element qryDefn = (Element) this.reqEntity.getElementsByTagNameNS(WFS2.NS_URI, "StoredQueryDefinition").item(0); - qryDefn.setAttribute("id", QRY_INVALID_LANG); - Element qryExpr = (Element) this.reqEntity.getElementsByTagNameNS(WFS2.NS_URI, "QueryExpressionText").item(0); - qryExpr.setAttribute("language", "http://qry.example.org"); - URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.CREATE_STORED_QRY, - ProtocolBinding.POST); - Response rsp = this.wfsClient.submitRequest(new DOMSource(this.reqEntity), ProtocolBinding.POST, - endpoint); - this.rspEntity = rsp.readEntity(Document.class); - ETSAssert.assertExceptionReport(this.rspEntity, "InvalidParameterValue", "language"); - } - - /** - * [{@code Test}] Submits a CreateStoredQuery request - * containing a query definition that is identical to an existing one. An - * exception report is expected in response containing the error code - * "DuplicateStoredQueryIdValue". - */ - @Test(description = "See OGC 09-025: Table 3") - public void duplicateQuery() { - URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.CREATE_STORED_QRY, - ProtocolBinding.POST); - this.reqEntity = WFSMessage.createRequestEntity(ETS_PKG + "/querymgmt/CreateStoredQuery-GetFeatureByName", - this.wfsVersion); - WFSMessage.setReturnTypesAndTypeNamesAttribute( this.reqEntity, this.dataSampler.selectFeatureType() ); - Response rsp = this.wfsClient.submitRequest(new DOMSource(this.reqEntity), ProtocolBinding.POST, - endpoint); - this.rspEntity = rsp.readEntity(Document.class); - ETSAssert.assertQualifiedName(this.rspEntity.getDocumentElement(), - new QName(WFS2.NS_URI, "CreateStoredQueryResponse")); - this.createdStoredQueries.add(QRY_GET_FEATURE_BY_NAME); - // resubmit - rsp = this.wfsClient.submitRequest(new DOMSource(this.reqEntity), ProtocolBinding.POST, endpoint); - this.rspEntity = rsp.readEntity(Document.class); - ETSAssert.assertExceptionReport(this.rspEntity, "DuplicateStoredQueryIdValue", QRY_GET_FEATURE_BY_NAME); - } + public final static String QRY_GET_FEATURE_BY_TYPE = "urn:example:wfs2-query:GetFeatureByTypeName"; + + public final static String QRY_GET_FEATURE_BY_NAME = "urn:example:wfs2-query:GetFeatureByName"; + + public final static String QRY_INVALID_LANG = "urn:example:wfs2-query:InvalidLang"; + + private List createdStoredQueries = new ArrayList<>(2); + + private DataSampler dataSampler; + + @BeforeClass() + public void initQueryFilterFixture(ITestContext testContext) { + ISuite suite = testContext.getSuite(); + this.dataSampler = (DataSampler) suite.getAttribute(SuiteAttribute.SAMPLER.getName()); + } + + /** + * This configuration method deletes specific stored queries that may already be known + * to the IUT; specifically: + *
                + *
              • {@value #QRY_GET_FEATURE_BY_TYPE}
              • + *
              • {@value #QRY_GET_FEATURE_BY_NAME}
              • + *
              • {@value #QRY_INVALID_LANG}
              • + *
              + * + * The remaining tests are skipped if this fails because a precondition cannot be met. + */ + @BeforeClass + public void deleteQueriesAtStart() { + List knownQueries = this.wfsClient.listStoredQueries(); + for (String queryId : new String[] { QRY_GET_FEATURE_BY_TYPE, QRY_GET_FEATURE_BY_NAME, QRY_INVALID_LANG }) { + if (!knownQueries.contains(queryId)) + continue; + int status = this.wfsClient.deleteStoredQuery(queryId); + if (status >= 400) { + throw new SkipException(String.format("[%s] Error dropping stored query: %s (status code was %d)", + getClass().getName(), queryId, status)); + } + } + } + + /** + * This configuration method drops any stored queries that may have been created by a + * test method. If an error occurs a WARNING message is logged. + */ + @AfterClass + public void deleteQueriesAtEnd() { + for (String queryId : this.createdStoredQueries) { + int status = this.wfsClient.deleteStoredQuery(queryId); + if (status >= 400) { + TestSuiteLogger.log(Level.WARNING, + String.format("[%s] Error dropping stored query: %s (status code was %d)", getClass().getName(), + queryId, status)); + } + } + this.createdStoredQueries.clear(); + } + + /** + * [{@code Test}] Submits a CreateStoredQuery request to retrieve + * features by type name. The query identifier is {@value #QRY_GET_FEATURE_BY_TYPE}. + * The response is expected to contain an XML entity with "CreateStoredQueryResponse" + * as the document element. The query is then invoked for all feature types for which + * data exist. + */ + @Test(description = "See OGC 09-025: 14.2, 14.5.2") + public void createGetFeatureByTypeName() { + this.reqEntity = WFSMessage.createRequestEntity(ETS_PKG + "/querymgmt/CreateStoredQuery-GetFeatureByTypeName", + this.wfsVersion); + URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.CREATE_STORED_QRY, + ProtocolBinding.POST); + Response rsp = this.wfsClient.submitRequest(new DOMSource(this.reqEntity), ProtocolBinding.POST, endpoint); + this.rspEntity = rsp.readEntity(Document.class); + assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + ETSAssert.assertQualifiedName(this.rspEntity.getDocumentElement(), + new QName(WFS2.NS_URI, "CreateStoredQueryResponse")); + this.createdStoredQueries.add(QRY_GET_FEATURE_BY_TYPE); + for (QName featureTypeName : this.featureInfo.keySet()) { + if (!this.featureInfo.get(featureTypeName).isInstantiated()) + continue; + Map params = Collections.singletonMap("typeName", featureTypeName); + Document doc = this.wfsClient.invokeStoredQuery(QRY_GET_FEATURE_BY_TYPE, params); + ETSAssert.assertResultSetNotEmpty(doc, featureTypeName); + } + } + + /** + * [{@code Test}] Submits a CreateStoredQuery request that contains a + * query expressed in an unsupported query language. An exception report is expected + * in response containing the error code "InvalidParameterValue". + */ + @Test(description = "See OGC 09-025: 14.2.2.5.3, 14.7") + public void createStoredQueryWithUnsupportedQueryLanguage() { + this.reqEntity = WFSMessage.createRequestEntity(ETS_PKG + "/querymgmt/CreateStoredQuery-GetFeatureByTypeName", + this.wfsVersion); + Element qryDefn = (Element) this.reqEntity.getElementsByTagNameNS(WFS2.NS_URI, "StoredQueryDefinition").item(0); + qryDefn.setAttribute("id", QRY_INVALID_LANG); + Element qryExpr = (Element) this.reqEntity.getElementsByTagNameNS(WFS2.NS_URI, "QueryExpressionText").item(0); + qryExpr.setAttribute("language", "http://qry.example.org"); + URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.CREATE_STORED_QRY, + ProtocolBinding.POST); + Response rsp = this.wfsClient.submitRequest(new DOMSource(this.reqEntity), ProtocolBinding.POST, endpoint); + this.rspEntity = rsp.readEntity(Document.class); + ETSAssert.assertExceptionReport(this.rspEntity, "InvalidParameterValue", "language"); + } + + /** + * [{@code Test}] Submits a CreateStoredQuery request containing a query + * definition that is identical to an existing one. An exception report is expected in + * response containing the error code "DuplicateStoredQueryIdValue". + */ + @Test(description = "See OGC 09-025: Table 3") + public void duplicateQuery() { + URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.CREATE_STORED_QRY, + ProtocolBinding.POST); + this.reqEntity = WFSMessage.createRequestEntity(ETS_PKG + "/querymgmt/CreateStoredQuery-GetFeatureByName", + this.wfsVersion); + WFSMessage.setReturnTypesAndTypeNamesAttribute(this.reqEntity, this.dataSampler.selectFeatureType()); + Response rsp = this.wfsClient.submitRequest(new DOMSource(this.reqEntity), ProtocolBinding.POST, endpoint); + this.rspEntity = rsp.readEntity(Document.class); + ETSAssert.assertQualifiedName(this.rspEntity.getDocumentElement(), + new QName(WFS2.NS_URI, "CreateStoredQueryResponse")); + this.createdStoredQueries.add(QRY_GET_FEATURE_BY_NAME); + // resubmit + rsp = this.wfsClient.submitRequest(new DOMSource(this.reqEntity), ProtocolBinding.POST, endpoint); + this.rspEntity = rsp.readEntity(Document.class); + ETSAssert.assertExceptionReport(this.rspEntity, "DuplicateStoredQueryIdValue", QRY_GET_FEATURE_BY_NAME); + } + } diff --git a/src/main/java/org/opengis/cite/iso19142/querymgmt/DropStoredQueryTests.java b/src/main/java/org/opengis/cite/iso19142/querymgmt/DropStoredQueryTests.java index a8b8e3ef..d58bb8ec 100644 --- a/src/main/java/org/opengis/cite/iso19142/querymgmt/DropStoredQueryTests.java +++ b/src/main/java/org/opengis/cite/iso19142/querymgmt/DropStoredQueryTests.java @@ -34,62 +34,57 @@ */ public class DropStoredQueryTests extends BaseFixture { - private DataSampler dataSampler; + private DataSampler dataSampler; - @BeforeClass() - public void initQueryFilterFixture( ITestContext testContext ) { - ISuite suite = testContext.getSuite(); - this.dataSampler = (DataSampler) suite.getAttribute( SuiteAttribute.SAMPLER.getName() ); - } - - /** - * [{@code Test}] Submits a DropStoredQuery request to remove - * an existing stored query. The response is expected to contain an XML - * entity with "DropStoredQueryResponse" as the document element. A - * subsequent attempt to invoke the query should fail with an exception - * report ("InvalidParameterValue"). - */ - @Test(description = "See OGC 09-025: 14.6.2") - public void dropStoredQuery() { - URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.CREATE_STORED_QRY, - ProtocolBinding.POST); - this.reqEntity = WFSMessage.createRequestEntity(ETS_PKG + "/querymgmt/CreateStoredQuery-GetFeatureByName", - this.wfsVersion); - WFSMessage.setReturnTypesAndTypeNamesAttribute( this.reqEntity, this.dataSampler.selectFeatureType() ); - Response rsp = this.wfsClient.submitRequest(new DOMSource(this.reqEntity), ProtocolBinding.POST, - endpoint); - assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - this.reqEntity = WFSMessage.createRequestEntity("DropStoredQuery", this.wfsVersion); - this.reqEntity.getDocumentElement().setAttribute("id", CreateStoredQueryTests.QRY_GET_FEATURE_BY_NAME); - endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.DROP_STORED_QRY, - ProtocolBinding.POST); - rsp = this.wfsClient.submitRequest(new DOMSource(this.reqEntity), ProtocolBinding.POST, endpoint); - this.rspEntity = rsp.readEntity(Document.class); - assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - ETSAssert.assertQualifiedName(this.rspEntity.getDocumentElement(), - new QName(WFS2.NS_URI, "DropStoredQueryResponse")); - Map params = Collections.singletonMap("name", "Irrelevant"); - this.rspEntity = this.wfsClient.invokeStoredQuery(CreateStoredQueryTests.QRY_GET_FEATURE_BY_NAME, params); - ETSAssert.assertExceptionReport(this.rspEntity, "InvalidParameterValue", "id"); - } + @BeforeClass() + public void initQueryFilterFixture(ITestContext testContext) { + ISuite suite = testContext.getSuite(); + this.dataSampler = (DataSampler) suite.getAttribute(SuiteAttribute.SAMPLER.getName()); + } - /** - * [{@code Test}] Submits a DropStoredQuery request that - * identifies a nonexistent query. An exception report is expected in - * response containing the error code "InvalidParameterValue". - */ - @Test(description = "See OGC 09-025: 14.6.1, 14.7") - public void dropNonexistentQuery() { - this.reqEntity = WFSMessage.createRequestEntity("DropStoredQuery", this.wfsVersion); - this.reqEntity.getDocumentElement().setAttribute("id", "urn:uuid:" + UUID.randomUUID().toString()); - URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.DROP_STORED_QRY, - ProtocolBinding.POST); - Response rsp = this.wfsClient.submitRequest(new DOMSource(this.reqEntity), ProtocolBinding.POST, - endpoint); - this.rspEntity = rsp.readEntity(Document.class); - ETSAssert.assertExceptionReport(this.rspEntity, "InvalidParameterValue", "id"); - } + /** + * [{@code Test}] Submits a DropStoredQuery request to remove an existing + * stored query. The response is expected to contain an XML entity with + * "DropStoredQueryResponse" as the document element. A subsequent attempt to invoke + * the query should fail with an exception report ("InvalidParameterValue"). + */ + @Test(description = "See OGC 09-025: 14.6.2") + public void dropStoredQuery() { + URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.CREATE_STORED_QRY, + ProtocolBinding.POST); + this.reqEntity = WFSMessage.createRequestEntity(ETS_PKG + "/querymgmt/CreateStoredQuery-GetFeatureByName", + this.wfsVersion); + WFSMessage.setReturnTypesAndTypeNamesAttribute(this.reqEntity, this.dataSampler.selectFeatureType()); + Response rsp = this.wfsClient.submitRequest(new DOMSource(this.reqEntity), ProtocolBinding.POST, endpoint); + assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + this.reqEntity = WFSMessage.createRequestEntity("DropStoredQuery", this.wfsVersion); + this.reqEntity.getDocumentElement().setAttribute("id", CreateStoredQueryTests.QRY_GET_FEATURE_BY_NAME); + endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.DROP_STORED_QRY, + ProtocolBinding.POST); + rsp = this.wfsClient.submitRequest(new DOMSource(this.reqEntity), ProtocolBinding.POST, endpoint); + this.rspEntity = rsp.readEntity(Document.class); + assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + ETSAssert.assertQualifiedName(this.rspEntity.getDocumentElement(), + new QName(WFS2.NS_URI, "DropStoredQueryResponse")); + Map params = Collections.singletonMap("name", "Irrelevant"); + this.rspEntity = this.wfsClient.invokeStoredQuery(CreateStoredQueryTests.QRY_GET_FEATURE_BY_NAME, params); + ETSAssert.assertExceptionReport(this.rspEntity, "InvalidParameterValue", "id"); + } + + /** + * [{@code Test}] Submits a DropStoredQuery request that identifies a + * nonexistent query. An exception report is expected in response containing the error + * code "InvalidParameterValue". + */ + @Test(description = "See OGC 09-025: 14.6.1, 14.7") + public void dropNonexistentQuery() { + this.reqEntity = WFSMessage.createRequestEntity("DropStoredQuery", this.wfsVersion); + this.reqEntity.getDocumentElement().setAttribute("id", "urn:uuid:" + UUID.randomUUID().toString()); + URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.DROP_STORED_QRY, + ProtocolBinding.POST); + Response rsp = this.wfsClient.submitRequest(new DOMSource(this.reqEntity), ProtocolBinding.POST, endpoint); + this.rspEntity = rsp.readEntity(Document.class); + ETSAssert.assertExceptionReport(this.rspEntity, "InvalidParameterValue", "id"); + } } diff --git a/src/main/java/org/opengis/cite/iso19142/querymgmt/StoredQueryManagement.java b/src/main/java/org/opengis/cite/iso19142/querymgmt/StoredQueryManagement.java index f4dfcfa1..a5b87da9 100644 --- a/src/main/java/org/opengis/cite/iso19142/querymgmt/StoredQueryManagement.java +++ b/src/main/java/org/opengis/cite/iso19142/querymgmt/StoredQueryManagement.java @@ -18,11 +18,10 @@ /** * Checks preconditions for running tests to verify that the IUT satisfies the - * requirements of the Manage stored queries conformance class. - * All tests are skipped if any preconditions are not met. The service - * constraint {@value #MANAGE_STORED_QRY} must be set to "TRUE" in the - * capabilities document. - * + * requirements of the Manage stored queries conformance class. All tests + * are skipped if any preconditions are not met. The service constraint + * {@value #MANAGE_STORED_QRY} must be set to "TRUE" in the capabilities document. + * *
                * {@code
                * 
              @@ -34,51 +33,49 @@
                * 
                * }
                * 
              - * + * * @see ATC - * A.1.15: Manage stored queries + * "http://docs.opengeospatial.org/is/09-025r2/09-025r2.html#requirement_15">ATC A.1.15: + * Manage stored queries */ public class StoredQueryManagement { - public final static String LANG_WFS_QUERY = "urn:ogc:def:queryLanguage:OGC-WFS::WFSQueryExpression"; - public final static String MANAGE_STORED_QRY = "ManageStoredQueries"; + public final static String LANG_WFS_QUERY = "urn:ogc:def:queryLanguage:OGC-WFS::WFSQueryExpression"; - /** - * This {@literal @BeforeTest} configuration method checks the - * implementation status of the {@value #MANAGE_STORED_QRY} conformance - * class. - * - * @param testContext - * Information about the test run. - */ - @BeforeTest - public void implementsManageStoredQueries(ITestContext testContext) { - Document wfsMetadata = (Document) testContext.getSuite().getAttribute(SuiteAttribute.TEST_SUBJECT.getName()); - if (!ServiceMetadataUtils.implementsConformanceClass(wfsMetadata, MANAGE_STORED_QRY)) { - throw new SkipException(ErrorMessage.format(ErrorMessageKeys.NOT_IMPLEMENTED, MANAGE_STORED_QRY)); - } - } + public final static String MANAGE_STORED_QRY = "ManageStoredQueries"; + + /** + * This {@literal @BeforeTest} configuration method checks the implementation status + * of the {@value #MANAGE_STORED_QRY} conformance class. + * @param testContext Information about the test run. + */ + @BeforeTest + public void implementsManageStoredQueries(ITestContext testContext) { + Document wfsMetadata = (Document) testContext.getSuite().getAttribute(SuiteAttribute.TEST_SUBJECT.getName()); + if (!ServiceMetadataUtils.implementsConformanceClass(wfsMetadata, MANAGE_STORED_QRY)) { + throw new SkipException(ErrorMessage.format(ErrorMessageKeys.NOT_IMPLEMENTED, MANAGE_STORED_QRY)); + } + } + + /** + * [{@code Test}] Confirms that the capabilities document advertises support for the + * (stored) query language {@value #LANG_WFS_QUERY} in the context of + * CreateStoredQuery. + * @param testContext Information about the test run. + */ + @Test(description = "See OGC 09-025: Table 12, 14.2.2.5.3") + public void supportedStoredQueryLanguages(ITestContext testContext) { + Document wfsMetadata = (Document) testContext.getSuite().getAttribute(SuiteAttribute.TEST_SUBJECT.getName()); + NodeList result = null; + try { + result = XMLUtils.evaluateXPath(wfsMetadata, + "//ows:Operation[@name='CreateStoredQuery']/ows:Parameter[@name='language']", null); + } + catch (XPathExpressionException e) { // valid expression + } + assertTrue(result.getLength() > 0, "Missing 'language' parameter for CreateStoredQuery."); + assertTrue(result.item(0).getTextContent().trim().contains(LANG_WFS_QUERY), + ErrorMessage.format(ErrorMessageKeys.QRY_LANG_NOT_SUPPORTED, LANG_WFS_QUERY)); + } - /** - * [{@code Test}] Confirms that the capabilities document advertises support - * for the (stored) query language {@value #LANG_WFS_QUERY} in the context - * of CreateStoredQuery. - * - * @param testContext - * Information about the test run. - */ - @Test(description = "See OGC 09-025: Table 12, 14.2.2.5.3") - public void supportedStoredQueryLanguages(ITestContext testContext) { - Document wfsMetadata = (Document) testContext.getSuite().getAttribute(SuiteAttribute.TEST_SUBJECT.getName()); - NodeList result = null; - try { - result = XMLUtils.evaluateXPath(wfsMetadata, - "//ows:Operation[@name='CreateStoredQuery']/ows:Parameter[@name='language']", null); - } catch (XPathExpressionException e) { // valid expression - } - assertTrue(result.getLength() > 0, "Missing 'language' parameter for CreateStoredQuery."); - assertTrue(result.item(0).getTextContent().trim().contains(LANG_WFS_QUERY), - ErrorMessage.format(ErrorMessageKeys.QRY_LANG_NOT_SUPPORTED, LANG_WFS_QUERY)); - } } diff --git a/src/main/java/org/opengis/cite/iso19142/querymgmt/package-info.java b/src/main/java/org/opengis/cite/iso19142/querymgmt/package-info.java index 1f8be85d..fa7f5e2a 100644 --- a/src/main/java/org/opengis/cite/iso19142/querymgmt/package-info.java +++ b/src/main/java/org/opengis/cite/iso19142/querymgmt/package-info.java @@ -1,20 +1,19 @@ /** - * This package contains tests to verify that the IUT satisfies the requirements - * of the Manage stored queries conformance class. If the WFS - * service constraint ManageStoredQueries is set to "TRUE" in the - * capabilities document, it is possible for clients to add and remove stored - * queries. - * + * This package contains tests to verify that the IUT satisfies the requirements of the + * Manage stored queries conformance class. If the WFS service constraint + * ManageStoredQueries is set to "TRUE" in the capabilities document, it is + * possible for clients to add and remove stored queries. + * *

              * All WFS implementations must handle the ListStoredQueries and - * DescribeStoredQueries requests. This conformance class - * introduces two requests for managing stored query definitions: + * DescribeStoredQueries requests. This conformance class introduces two + * requests for managing stored query definitions: *

              *
                *
              • CreateStoredQuery
              • *
              • DropStoredQuery
              • *
              - * + * *

              * Sources *

              diff --git a/src/main/java/org/opengis/cite/iso19142/simple/DescribeFeatureTypeTests.java b/src/main/java/org/opengis/cite/iso19142/simple/DescribeFeatureTypeTests.java index 9134ac4c..cc8e994c 100644 --- a/src/main/java/org/opengis/cite/iso19142/simple/DescribeFeatureTypeTests.java +++ b/src/main/java/org/opengis/cite/iso19142/simple/DescribeFeatureTypeTests.java @@ -37,154 +37,143 @@ import jakarta.ws.rs.core.Response.Status; /** - * Tests the service response to a DescribeFeatureType request. No particular - * HTTP method binding is mandated. A conforming service must be able to provide - * a GML application schema, although alternative schema languages are - * permitted. - * + * Tests the service response to a DescribeFeatureType request. No particular HTTP method + * binding is mandated. A conforming service must be able to provide a GML application + * schema, although alternative schema languages are permitted. + * * @see "ISO 19142:2010, cl. 9: DescribeFeatureType operation" */ public class DescribeFeatureTypeTests extends BaseFixture { - DocumentBuilder docBuilder; + DocumentBuilder docBuilder; - /** - * Builds a DOM Document node representing the request entity - * (/wfs:DescribeFeatureType). - */ - @BeforeClass - public void buildRequestEntity() { - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - factory.setNamespaceAware(true); - try { - this.docBuilder = factory.newDocumentBuilder(); - this.reqEntity = docBuilder.parse(getClass().getResourceAsStream("DescribeFeatureType.xml")); - } catch (Exception e) { - TestSuiteLogger.log(Level.WARNING, "Failed to parse request entity from classpath", e); - } - } + /** + * Builds a DOM Document node representing the request entity + * (/wfs:DescribeFeatureType). + */ + @BeforeClass + public void buildRequestEntity() { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + try { + this.docBuilder = factory.newDocumentBuilder(); + this.reqEntity = docBuilder.parse(getClass().getResourceAsStream("DescribeFeatureType.xml")); + } + catch (Exception e) { + TestSuiteLogger.log(Level.WARNING, "Failed to parse request entity from classpath", e); + } + } - @BeforeMethod - public void clearTypeNames() { - removeAllTypeNames(this.reqEntity); - } + @BeforeMethod + public void clearTypeNames() { + removeAllTypeNames(this.reqEntity); + } - /** - * Removes all child wfs:TypeName elements from the request entity. - * - * @param reqEntity - * The request entity (/wfs:DescribeFeatureType). - */ - void removeAllTypeNames(Document reqEntity) { - Element docElem = reqEntity.getDocumentElement(); - NodeList children = docElem.getChildNodes(); - for (int i = 0; i < children.getLength(); i++) { - docElem.removeChild(children.item(i)); - } - } + /** + * Removes all child wfs:TypeName elements from the request entity. + * @param reqEntity The request entity (/wfs:DescribeFeatureType). + */ + void removeAllTypeNames(Document reqEntity) { + Element docElem = reqEntity.getDocumentElement(); + NodeList children = docElem.getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + docElem.removeChild(children.item(i)); + } + } - /** - * Adds a wfs:TypeName child element to a wfs:DescribeFeatureType entity. A - * suitable namespace binding will be added to the document element if - * necessary. - * - * @param request - * The request entity (wfs:DescribeFeatureType). - * @param qName - * The qualified name of the feature type. - */ - void addFeatureType(Document request, QName qName) { - Element docElem = request.getDocumentElement(); - Element typeName = request.createElementNS(Namespaces.WFS, WFS2.TYPENAME_ELEM); - String nsPrefix = docElem.lookupPrefix(qName.getNamespaceURI()); - if (null == nsPrefix) { - nsPrefix = "ns" + Integer.toString((int) (Math.random() * 100)); - } - typeName.setTextContent(nsPrefix + ":" + qName.getLocalPart()); - typeName.setPrefix("wfs"); - docElem.appendChild(typeName); - docElem.setAttribute(XMLConstants.XMLNS_ATTRIBUTE + ":" + nsPrefix, qName.getNamespaceURI()); - } + /** + * Adds a wfs:TypeName child element to a wfs:DescribeFeatureType entity. A suitable + * namespace binding will be added to the document element if necessary. + * @param request The request entity (wfs:DescribeFeatureType). + * @param qName The qualified name of the feature type. + */ + void addFeatureType(Document request, QName qName) { + Element docElem = request.getDocumentElement(); + Element typeName = request.createElementNS(Namespaces.WFS, WFS2.TYPENAME_ELEM); + String nsPrefix = docElem.lookupPrefix(qName.getNamespaceURI()); + if (null == nsPrefix) { + nsPrefix = "ns" + Integer.toString((int) (Math.random() * 100)); + } + typeName.setTextContent(nsPrefix + ":" + qName.getLocalPart()); + typeName.setPrefix("wfs"); + docElem.appendChild(typeName); + docElem.setAttribute(XMLConstants.XMLNS_ATTRIBUTE + ":" + nsPrefix, qName.getNamespaceURI()); + } - /** - * If the typeNames parameter is omitted, the complete application schema(s) - * supported by the server shall be returned in response. By default, it - * must be a GML application schema (XML Schema). - * - * @param binding - * The ProtocolBinding to use. - * - * @see "ISO 19142:2010, cl. 9.2.4.1: typeNames parameter" - */ - @Test(description = "See ISO 19142: 9.2.4.1", dataProvider = "protocol-binding") - public void describeAllFeatureTypes(ProtocolBinding binding) { - URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.DESCRIBE_FEATURE_TYPE, binding); - Response rsp = wfsClient.submitRequest(new DOMSource(reqEntity), binding, endpoint); - Assert.assertTrue(rsp.hasEntity(), ErrorMessage.get(ErrorMessageKeys.MISSING_XML_ENTITY)); - this.rspEntity = extractBodyAsDocument(rsp); - Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - Element docElem = this.rspEntity.getDocumentElement(); - if (docElem.getLocalName().equals("DescribeFeatureTypeResponse")) { - // special case for SOAP response - Document appSchema = decodeSchema(this.rspEntity); - Assert.assertNotNull(appSchema, "Base64-encoded schema could not be read."); - docElem = appSchema.getDocumentElement(); - } - Assert.assertEquals(docElem.getLocalName(), "schema", "Document element has unexpected [local name]."); - // TODO: compile schema - } + /** + * If the typeNames parameter is omitted, the complete application schema(s) supported + * by the server shall be returned in response. By default, it must be a GML + * application schema (XML Schema). + * @param binding The ProtocolBinding to use. + * + * @see "ISO 19142:2010, cl. 9.2.4.1: typeNames parameter" + */ + @Test(description = "See ISO 19142: 9.2.4.1", dataProvider = "protocol-binding") + public void describeAllFeatureTypes(ProtocolBinding binding) { + URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.DESCRIBE_FEATURE_TYPE, binding); + Response rsp = wfsClient.submitRequest(new DOMSource(reqEntity), binding, endpoint); + Assert.assertTrue(rsp.hasEntity(), ErrorMessage.get(ErrorMessageKeys.MISSING_XML_ENTITY)); + this.rspEntity = extractBodyAsDocument(rsp); + Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + Element docElem = this.rspEntity.getDocumentElement(); + if (docElem.getLocalName().equals("DescribeFeatureTypeResponse")) { + // special case for SOAP response + Document appSchema = decodeSchema(this.rspEntity); + Assert.assertNotNull(appSchema, "Base64-encoded schema could not be read."); + docElem = appSchema.getDocumentElement(); + } + Assert.assertEquals(docElem.getLocalName(), "schema", "Document element has unexpected [local name]."); + // TODO: compile schema + } - /** - * If the typeNames parameter specifies an unknown feature type, the - * resulting exception report must indicate an "InvalidParameterValue" - * error. The set of permissible values for the typeNames parameter is the - * set of feature type names listed in the capabilities document. - * - * @param binding - * The ProtocolBinding to use. - * - * @see "ISO 19142:2010, cl. 8.3.4: FeatureTypeList section" - * @see "ISO 19142:2010, cl. 9.2.4.1: typeNames parameter" - * @see "OGC 06-121r3, cl. 8.3: exceptionCode parameter values" - */ - @Test(description = "See ISO 19142: 8.3.4, 9.2.4.1", dataProvider = "protocol-binding") - public void describeUnknownFeatureType(ProtocolBinding binding) { - addFeatureType(this.reqEntity, new QName("http://example.org", "Unknown1.Type")); - URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.DESCRIBE_FEATURE_TYPE, binding); - Response rsp = wfsClient.submitRequest(new DOMSource(this.reqEntity), binding, endpoint); - Assert.assertTrue(rsp.hasEntity(), ErrorMessage.get(ErrorMessageKeys.MISSING_XML_ENTITY)); - this.rspEntity = extractBodyAsDocument(rsp); - Assert.assertEquals(rsp.getStatus(), Status.BAD_REQUEST.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - SchematronValidator validator = ValidationUtils.buildSchematronValidator("ExceptionReport.sch", - "InvalidParameterValuePhase"); - Result result = validator.validate(new DOMSource(this.rspEntity), false); - Assert.assertFalse(validator.ruleViolationsDetected(), ErrorMessage.format(ErrorMessageKeys.NOT_SCHEMA_VALID, - validator.getRuleViolationCount(), XMLUtils.resultToString(result))); - } + /** + * If the typeNames parameter specifies an unknown feature type, the resulting + * exception report must indicate an "InvalidParameterValue" error. The set of + * permissible values for the typeNames parameter is the set of feature type names + * listed in the capabilities document. + * @param binding The ProtocolBinding to use. + * + * @see "ISO 19142:2010, cl. 8.3.4: FeatureTypeList section" + * @see "ISO 19142:2010, cl. 9.2.4.1: typeNames parameter" + * @see "OGC 06-121r3, cl. 8.3: exceptionCode parameter values" + */ + @Test(description = "See ISO 19142: 8.3.4, 9.2.4.1", dataProvider = "protocol-binding") + public void describeUnknownFeatureType(ProtocolBinding binding) { + addFeatureType(this.reqEntity, new QName("http://example.org", "Unknown1.Type")); + URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.DESCRIBE_FEATURE_TYPE, binding); + Response rsp = wfsClient.submitRequest(new DOMSource(this.reqEntity), binding, endpoint); + Assert.assertTrue(rsp.hasEntity(), ErrorMessage.get(ErrorMessageKeys.MISSING_XML_ENTITY)); + this.rspEntity = extractBodyAsDocument(rsp); + Assert.assertEquals(rsp.getStatus(), Status.BAD_REQUEST.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + SchematronValidator validator = ValidationUtils.buildSchematronValidator("ExceptionReport.sch", + "InvalidParameterValuePhase"); + Result result = validator.validate(new DOMSource(this.rspEntity), false); + Assert.assertFalse(validator.ruleViolationsDetected(), ErrorMessage.format(ErrorMessageKeys.NOT_SCHEMA_VALID, + validator.getRuleViolationCount(), XMLUtils.resultToString(result))); + } + + /** + * The body of a SOAP response contains a DescribeFeatureTypeResponse element. Its + * content is a Base64-encoded application schema. + * @param doc A Document containing a wfs:DescribeFeatureTypeResponse element. + * @return A Document representing an XML Schema. + * + * @see "ISO 19142, D.4.5: Encoding XML Schema in a SOAP Body" + */ + Document decodeSchema(Document doc) { + String base64Schema = doc.getDocumentElement().getTextContent().trim(); + byte[] schema = Base64.getDecoder().decode(base64Schema); + Document appSchema = null; + try { + appSchema = this.docBuilder.parse(new ByteArrayInputStream(schema), doc.getDocumentURI()); + } + catch (SAXException | IOException e) { + TestSuiteLogger.log(Level.WARNING, String.format("Failed to parse decoded schema from %s.\n%s ", + doc.getDocumentURI(), e.getMessage())); + } + return appSchema; + } - /** - * The body of a SOAP response contains a DescribeFeatureTypeResponse - * element. Its content is a Base64-encoded application schema. - * - * @param doc - * A Document containing a wfs:DescribeFeatureTypeResponse - * element. - * @return A Document representing an XML Schema. - * - * @see "ISO 19142, D.4.5: Encoding XML Schema in a SOAP Body" - */ - Document decodeSchema(Document doc) { - String base64Schema = doc.getDocumentElement().getTextContent().trim(); - byte[] schema = Base64.getDecoder().decode(base64Schema); - Document appSchema = null; - try { - appSchema = this.docBuilder.parse(new ByteArrayInputStream(schema), doc.getDocumentURI()); - } catch (SAXException | IOException e) { - TestSuiteLogger.log(Level.WARNING, String.format("Failed to parse decoded schema from %s.\n%s ", - doc.getDocumentURI(), e.getMessage())); - } - return appSchema; - } } diff --git a/src/main/java/org/opengis/cite/iso19142/simple/DescribeStoredQueriesTests.java b/src/main/java/org/opengis/cite/iso19142/simple/DescribeStoredQueriesTests.java index 8621b541..3526ece1 100644 --- a/src/main/java/org/opengis/cite/iso19142/simple/DescribeStoredQueriesTests.java +++ b/src/main/java/org/opengis/cite/iso19142/simple/DescribeStoredQueriesTests.java @@ -33,9 +33,9 @@ import jakarta.ws.rs.core.Response; /** - * Tests the service response to a DescribeStoredQueries request. This operation - * provides a detailed description of each stored query that a server offers. - * + * Tests the service response to a DescribeStoredQueries request. This operation provides + * a detailed description of each stored query that a server offers. + * * @see "ISO 19142:2010, cl. 14.2: Defining stored queries" * @see "ISO 19142:2010, cl. 14.4: DescribeStoredQueries operations [sic]" */ @@ -44,28 +44,23 @@ public class DescribeStoredQueriesTests extends BaseFixture { private Schema wfsSchema; /** - * Retrieves the (pre-compiled) WFS schema from the suite fixture and builds - * a DOM Document node representing the request entity. - * - * @param testContext - * The test (group) context. + * Retrieves the (pre-compiled) WFS schema from the suite fixture and builds a DOM + * Document node representing the request entity. + * @param testContext The test (group) context. */ @BeforeClass public void setupClassFixture(ITestContext testContext) { - this.wfsSchema = (Schema) testContext.getSuite().getAttribute( - SuiteAttribute.WFS_SCHEMA.getName()); - Assert.assertNotNull(this.wfsSchema, - "WFS schema not found in suite fixture."); + this.wfsSchema = (Schema) testContext.getSuite().getAttribute(SuiteAttribute.WFS_SCHEMA.getName()); + Assert.assertNotNull(this.wfsSchema, "WFS schema not found in suite fixture."); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(true); try { DocumentBuilder builder = factory.newDocumentBuilder(); - this.reqEntity = builder.parse(getClass().getResourceAsStream( - "DescribeStoredQueries.xml")); - WFSMessage.updateVersion( this.reqEntity, this.wfsVersion ); - } catch (Exception e) { - TestSuiteLogger.log(Level.WARNING, - "Failed to parse request entity from classpath", e); + this.reqEntity = builder.parse(getClass().getResourceAsStream("DescribeStoredQueries.xml")); + WFSMessage.updateVersion(this.reqEntity, this.wfsVersion); + } + catch (Exception e) { + TestSuiteLogger.log(Level.WARNING, "Failed to parse request entity from classpath", e); } } @@ -75,55 +70,45 @@ public void clearQueryIdentifiers() { } /** - * If no stored query identifiers are supplied in the request then all - * stored queries offered by a server shall be described (one or more). - * - * @param binding - * The ProtocolBinding to use. - * + * If no stored query identifiers are supplied in the request then all stored queries + * offered by a server shall be described (one or more). + * @param binding The ProtocolBinding to use. + * * @see "ISO 19142:2010, cl. 14.4.2: XML encoding" * @see "ISO 19142:2010, Table 21: Keywords for DescribeStoredQueries KVP-encoding" */ @Test(description = "See ISO 19142: 14.4.2, Table 21", dataProvider = "protocol-binding") public void describeAllStoredQueries(ProtocolBinding binding) { - URI endpoint = ServiceMetadataUtils.getOperationEndpoint( - this.wfsMetadata, WFS2.DESC_STORED_QUERIES, binding); - Response rsp = wfsClient.submitRequest(new DOMSource(reqEntity), - binding, endpoint); - Assert.assertTrue(rsp.hasEntity(), - ErrorMessage.get(ErrorMessageKeys.MISSING_XML_ENTITY)); + URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.DESC_STORED_QUERIES, binding); + Response rsp = wfsClient.submitRequest(new DOMSource(reqEntity), binding, endpoint); + Assert.assertTrue(rsp.hasEntity(), ErrorMessage.get(ErrorMessageKeys.MISSING_XML_ENTITY)); this.rspEntity = extractBodyAsDocument(rsp); Validator validator = this.wfsSchema.newValidator(); ValidationErrorHandler errHandler = new ValidationErrorHandler(); validator.setErrorHandler(errHandler); try { - validator.validate(new DOMSource(this.rspEntity, this.rspEntity - .getDocumentURI())); - } catch (Exception ex) { + validator.validate(new DOMSource(this.rspEntity, this.rspEntity.getDocumentURI())); + } + catch (Exception ex) { // unlikely with DOM object and ErrorHandler set - TestSuiteLogger.log(Level.WARNING, - "Failed to validate WFS capabilities document", ex); + TestSuiteLogger.log(Level.WARNING, "Failed to validate WFS capabilities document", ex); } - Assert.assertFalse(errHandler.errorsDetected(), ErrorMessage.format( - ErrorMessageKeys.NOT_SCHEMA_VALID, errHandler.getErrorCount(), - errHandler.toString())); + Assert.assertFalse(errHandler.errorsDetected(), ErrorMessage.format(ErrorMessageKeys.NOT_SCHEMA_VALID, + errHandler.getErrorCount(), errHandler.toString())); String xpath = "count(//wfs:StoredQueryDescription) > 0"; ETSAssert.assertXPath(xpath, this.rspEntity.getDocumentElement(), null); } /** - * A conforming service must implement at least the GetFeatureById stored - * query, which has the identifier - * {@value org.opengis.cite.iso19142.WFS2#QRY_GET_FEATURE_BY_ID}. - * + * A conforming service must implement at least the GetFeatureById stored query, which + * has the identifier {@value org.opengis.cite.iso19142.WFS2#QRY_GET_FEATURE_BY_ID}. + * *

              - * Note: The URN form of the query identifier was - * deprecated in WFS 2.0.2; the 'http' URI is preferred. + * Note: The URN form of the query identifier was deprecated in WFS + * 2.0.2; the 'http' URI is preferred. *

              - * - * @param binding - * The ProtocolBinding to use. - * + * @param binding The ProtocolBinding to use. + * * @see "ISO 19142:2010, cl. 7.9.3.6: GetFeatureById stored query" */ @Test(description = "See ISO 19142: 7.9.3.6", dataProvider = "protocol-binding") @@ -131,24 +116,17 @@ public void describeStoredQuery_GetFeatureById(ProtocolBinding binding) { String queryId = (this.wfsVersion.equals(WFS2.V2_0_0)) ? WFS2.QRY_GET_FEATURE_BY_ID_URN : WFS2.QRY_GET_FEATURE_BY_ID; addQueryIdentifier(this.reqEntity, queryId); - URI endpoint = ServiceMetadataUtils.getOperationEndpoint( - this.wfsMetadata, WFS2.DESC_STORED_QUERIES, binding); - Response rsp = wfsClient.submitRequest(new DOMSource(reqEntity), - binding, endpoint); - Assert.assertTrue(rsp.hasEntity(), - ErrorMessage.get(ErrorMessageKeys.MISSING_XML_ENTITY)); + URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.DESC_STORED_QUERIES, binding); + Response rsp = wfsClient.submitRequest(new DOMSource(reqEntity), binding, endpoint); + Assert.assertTrue(rsp.hasEntity(), ErrorMessage.get(ErrorMessageKeys.MISSING_XML_ENTITY)); this.rspEntity = extractBodyAsDocument(rsp); - String xpath = String.format("//wfs:StoredQueryDescription[@id='%s']", - queryId); + String xpath = String.format("//wfs:StoredQueryDescription[@id='%s']", queryId); ETSAssert.assertXPath(xpath, this.rspEntity.getDocumentElement(), null); } /** * Removes all wfs:StoredQueryId elements from the request entity. - * - * @param reqEntity - * A Document with wfs:DescribeStoredQueries as the document - * element. + * @param reqEntity A Document with wfs:DescribeStoredQueries as the document element. */ void removeAllQueryIdentifiers(Document reqEntity) { Element docElem = reqEntity.getDocumentElement(); @@ -160,18 +138,14 @@ void removeAllQueryIdentifiers(Document reqEntity) { /** * Adds a wfs:StoredQueryId element with the given text value. - * - * @param request - * A Document with wfs:DescribeStoredQueries as the document - * element. - * @param queryId - * A URI value that identifies a stored query. + * @param request A Document with wfs:DescribeStoredQueries as the document element. + * @param queryId A URI value that identifies a stored query. */ void addQueryIdentifier(Document request, String queryId) { Element docElem = reqEntity.getDocumentElement(); - Element storedQueryId = request.createElementNS(Namespaces.WFS, - WFS2.STORED_QRY_ID_ELEM); + Element storedQueryId = request.createElementNS(Namespaces.WFS, WFS2.STORED_QRY_ID_ELEM); storedQueryId.setTextContent(queryId); docElem.appendChild(storedQueryId); } + } diff --git a/src/main/java/org/opengis/cite/iso19142/simple/ListStoredQueriesTests.java b/src/main/java/org/opengis/cite/iso19142/simple/ListStoredQueriesTests.java index 2fde75d0..be236cb7 100644 --- a/src/main/java/org/opengis/cite/iso19142/simple/ListStoredQueriesTests.java +++ b/src/main/java/org/opengis/cite/iso19142/simple/ListStoredQueriesTests.java @@ -27,10 +27,10 @@ import jakarta.ws.rs.core.Response; /** - * Tests the service response to a ListStoredQueries request. A conforming - * service must implement at least the GetFeatureById stored query, which has - * the identifier {@value org.opengis.cite.iso19142.WFS2#QRY_GET_FEATURE_BY_ID}. - * + * Tests the service response to a ListStoredQueries request. A conforming service must + * implement at least the GetFeatureById stored query, which has the identifier + * {@value org.opengis.cite.iso19142.WFS2#QRY_GET_FEATURE_BY_ID}. + * * @see "ISO 19142:2010, cl. 14.3: ListStoredQueries operation" * @see "ISO 19142:2010, cl. 7.9.3.6: GetFeatureById stored query" */ @@ -39,66 +39,55 @@ public class ListStoredQueriesTests extends BaseFixture { private Schema wfsSchema; /** - * Retrieves the (pre-compiled) WFS schema from the suite fixture and builds - * the XML request entity. - * - * @param testContext - * The test (group) context. + * Retrieves the (pre-compiled) WFS schema from the suite fixture and builds the XML + * request entity. + * @param testContext The test (group) context. */ @BeforeClass public void setupClassFixture(ITestContext testContext) { - this.wfsSchema = (Schema) testContext.getSuite().getAttribute( - SuiteAttribute.WFS_SCHEMA.getName()); - Assert.assertNotNull(this.wfsSchema, - "WFS schema not found in suite fixture."); + this.wfsSchema = (Schema) testContext.getSuite().getAttribute(SuiteAttribute.WFS_SCHEMA.getName()); + Assert.assertNotNull(this.wfsSchema, "WFS schema not found in suite fixture."); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(true); try { DocumentBuilder builder = factory.newDocumentBuilder(); - this.reqEntity = builder.parse(getClass().getResourceAsStream( - "ListStoredQueries.xml")); - } catch (Exception e) { - TestSuiteLogger.log(Level.WARNING, - "Failed to parse request entity from classpath", e); + this.reqEntity = builder.parse(getClass().getResourceAsStream("ListStoredQueries.xml")); + } + catch (Exception e) { + TestSuiteLogger.log(Level.WARNING, "Failed to parse request entity from classpath", e); } } /** - * The response to a ListStoredQueries request must include an XML entity - * having wfs:ListStoredQueriesResponse as the document element. The - * document must (a) be schema valid, and (b) contain one or more - * wfs:StoredQuery elements, including the mandatory GetFeatureById query. - * - * @param binding - * The ProtocolBinding to use. - * + * The response to a ListStoredQueries request must include an XML entity having + * wfs:ListStoredQueriesResponse as the document element. The document must (a) be + * schema valid, and (b) contain one or more wfs:StoredQuery elements, including the + * mandatory GetFeatureById query. + * @param binding The ProtocolBinding to use. + * * @see "ISO 19142:2010, cl. 14.3.4: Response" */ @Test(description = "See ISO 19142: 14.3.4", dataProvider = "protocol-binding") public void listStoredQueries(ProtocolBinding binding) { - URI endpoint = ServiceMetadataUtils.getOperationEndpoint( - this.wfsMetadata, WFS2.LIST_STORED_QUERIES, binding); - Response rsp = wfsClient.submitRequest(new DOMSource( - this.reqEntity), binding, endpoint); - Assert.assertTrue(rsp.hasEntity(), - ErrorMessage.get(ErrorMessageKeys.MISSING_XML_ENTITY)); + URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.LIST_STORED_QUERIES, binding); + Response rsp = wfsClient.submitRequest(new DOMSource(this.reqEntity), binding, endpoint); + Assert.assertTrue(rsp.hasEntity(), ErrorMessage.get(ErrorMessageKeys.MISSING_XML_ENTITY)); this.rspEntity = extractBodyAsDocument(rsp); Validator validator = this.wfsSchema.newValidator(); ValidationErrorHandler errHandler = new ValidationErrorHandler(); validator.setErrorHandler(errHandler); try { - validator.validate(new DOMSource(this.rspEntity, this.rspEntity - .getDocumentURI())); - } catch (Exception ex) { + validator.validate(new DOMSource(this.rspEntity, this.rspEntity.getDocumentURI())); + } + catch (Exception ex) { // unlikely with DOM object and ErrorHandler set - TestSuiteLogger.log(Level.WARNING, - "Failed to validate WFS capabilities document", ex); + TestSuiteLogger.log(Level.WARNING, "Failed to validate WFS capabilities document", ex); } - Assert.assertFalse(errHandler.errorsDetected(), ErrorMessage.format( - ErrorMessageKeys.NOT_SCHEMA_VALID, errHandler.getErrorCount(), - errHandler.toString())); - String xpath = String.format("//wfs:StoredQuery[@id='%s' or @id='%s']", - WFS2.QRY_GET_FEATURE_BY_ID, WFS2.QRY_GET_FEATURE_BY_ID_URN); + Assert.assertFalse(errHandler.errorsDetected(), ErrorMessage.format(ErrorMessageKeys.NOT_SCHEMA_VALID, + errHandler.getErrorCount(), errHandler.toString())); + String xpath = String.format("//wfs:StoredQuery[@id='%s' or @id='%s']", WFS2.QRY_GET_FEATURE_BY_ID, + WFS2.QRY_GET_FEATURE_BY_ID_URN); ETSAssert.assertXPath(xpath, this.rspEntity.getDocumentElement(), null); } + } diff --git a/src/main/java/org/opengis/cite/iso19142/simple/ServiceMetadataTests.java b/src/main/java/org/opengis/cite/iso19142/simple/ServiceMetadataTests.java index 2cbe552b..4cf974aa 100644 --- a/src/main/java/org/opengis/cite/iso19142/simple/ServiceMetadataTests.java +++ b/src/main/java/org/opengis/cite/iso19142/simple/ServiceMetadataTests.java @@ -19,55 +19,54 @@ import org.testng.annotations.Test; /** - * Contains tests regarding service metadata resources, especially the content - * of the WFS capabilities document. The capabilities document must be - * well-formed and schema-valid. + * Contains tests regarding service metadata resources, especially the content of the WFS + * capabilities document. The capabilities document must be well-formed and schema-valid. */ public class ServiceMetadataTests extends BaseFixture { - static final String ROOT_PKG = "/org/opengis/cite/iso19142/"; - static final String SIMPLE_WFS_PHASE = "SimpleWFSPhase"; - private Schema wfsSchema; + static final String ROOT_PKG = "/org/opengis/cite/iso19142/"; + static final String SIMPLE_WFS_PHASE = "SimpleWFSPhase"; - /** - * Obtains the WFS 2.0 message schema from the test context. - * - * @param testContext - * The test (group) context. - */ - @BeforeClass(alwaysRun = true) - public void obtainWFSSchema(ITestContext testContext) { - this.wfsSchema = (Schema) testContext.getSuite().getAttribute(SuiteAttribute.WFS_SCHEMA.getName()); - Assert.assertNotNull(this.wfsSchema, "WFS schema not found in suite fixture."); - } + private Schema wfsSchema; - /** - * Verifies that the WFS capabilities document is valid with respect to the - * official {@code wfs.xsd} schema. - * - * @see "ISO 19142:2010, cl. 8.3.2: GetCapabilities - Response" - */ - @Test(description = "See ISO 19142: 8.3.2") - public void capabilitiesDocIsXmlSchemaValid() { - Validator validator = this.wfsSchema.newValidator(); - ETSAssert.assertSchemaValid(validator, new DOMSource(this.wfsMetadata, this.wfsMetadata.getDocumentURI())); - } + /** + * Obtains the WFS 2.0 message schema from the test context. + * @param testContext The test (group) context. + */ + @BeforeClass(alwaysRun = true) + public void obtainWFSSchema(ITestContext testContext) { + this.wfsSchema = (Schema) testContext.getSuite().getAttribute(SuiteAttribute.WFS_SCHEMA.getName()); + Assert.assertNotNull(this.wfsSchema, "WFS schema not found in suite fixture."); + } + + /** + * Verifies that the WFS capabilities document is valid with respect to the official + * {@code wfs.xsd} schema. + * + * @see "ISO 19142:2010, cl. 8.3.2: GetCapabilities - Response" + */ + @Test(description = "See ISO 19142: 8.3.2") + public void capabilitiesDocIsXmlSchemaValid() { + Validator validator = this.wfsSchema.newValidator(); + ETSAssert.assertSchemaValid(validator, new DOMSource(this.wfsMetadata, this.wfsMetadata.getDocumentURI())); + } + + /** + * Checks that the content of the WFS capabilities document reflects the + * {@code Simple WFS} conformance class. The applicable rules are incorporated into + * the {@value #SIMPLE_WFS_PHASE} phase of the Schematron schema + * {@code wfs-capabilities-2.0.sch}. + * + * @see "ISO 19142:2010, cl. A.1.1: Simple WFS" + * @see "ISO 19142:2010, cl. A.2.23: Declaring conformance" + */ + @Test(description = "See ISO 19142: A.1.1, A.2.23") + public void capabilitiesDocCorrespondsToWfsSimple() { + SchematronValidator validator = ValidationUtils.buildSchematronValidator("wfs-capabilities-2.0.sch", + SIMPLE_WFS_PHASE); + Result result = validator.validate(new DOMSource(this.wfsMetadata, this.wfsMetadata.getDocumentURI()), false); + Assert.assertFalse(validator.ruleViolationsDetected(), ErrorMessage.format(ErrorMessageKeys.NOT_SCHEMA_VALID, + validator.getRuleViolationCount(), XMLUtils.resultToString(result))); + } - /** - * Checks that the content of the WFS capabilities document reflects the - * {@code Simple WFS} conformance class. The applicable rules are - * incorporated into the {@value #SIMPLE_WFS_PHASE} phase of the Schematron - * schema {@code wfs-capabilities-2.0.sch}. - * - * @see "ISO 19142:2010, cl. A.1.1: Simple WFS" - * @see "ISO 19142:2010, cl. A.2.23: Declaring conformance" - */ - @Test(description = "See ISO 19142: A.1.1, A.2.23") - public void capabilitiesDocCorrespondsToWfsSimple() { - SchematronValidator validator = ValidationUtils.buildSchematronValidator("wfs-capabilities-2.0.sch", - SIMPLE_WFS_PHASE); - Result result = validator.validate(new DOMSource(this.wfsMetadata, this.wfsMetadata.getDocumentURI()), false); - Assert.assertFalse(validator.ruleViolationsDetected(), ErrorMessage.format(ErrorMessageKeys.NOT_SCHEMA_VALID, - validator.getRuleViolationCount(), XMLUtils.resultToString(result))); - } } diff --git a/src/main/java/org/opengis/cite/iso19142/simple/SimpleCapabilitiesTests.java b/src/main/java/org/opengis/cite/iso19142/simple/SimpleCapabilitiesTests.java index cca9f8a5..e033300b 100644 --- a/src/main/java/org/opengis/cite/iso19142/simple/SimpleCapabilitiesTests.java +++ b/src/main/java/org/opengis/cite/iso19142/simple/SimpleCapabilitiesTests.java @@ -34,122 +34,120 @@ import jakarta.ws.rs.core.Response; /** - * Tests the service response to a GetCapabilities request for "Simple WFS" - * conformance. The HTTP GET method must be supported by all conforming - * implementations. - * + * Tests the service response to a GetCapabilities request for "Simple WFS" conformance. + * The HTTP GET method must be supported by all conforming implementations. + * * @see "ISO 19142:2010, cl. 8: GetCapabilities operation" */ public class SimpleCapabilitiesTests extends BaseFixture { - private URI reqEndpointUsingGET; - private Client client; + private URI reqEndpointUsingGET; - @BeforeTest - public void checkSuitePreconditions(ITestContext context) { - Object failedPreconditions = context.getSuite().getAttribute(SuiteAttribute.FAILED_PRECONDITIONS.getName()); - if (null != failedPreconditions) { - throw new SkipException("One or more test suite preconditions were not satisfied: " + failedPreconditions); - } - } + private Client client; - /** - * Extracts the GET request endpoint from the capabilities document. - * - * @param testContext - * The test (set) context. - */ - @BeforeClass - public void extractEndpoint(ITestContext testContext) { - this.client = ClientBuilder.newClient(); - this.reqEndpointUsingGET = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.GET_CAPABILITIES, - ProtocolBinding.GET); - } + @BeforeTest + public void checkSuitePreconditions(ITestContext context) { + Object failedPreconditions = context.getSuite().getAttribute(SuiteAttribute.FAILED_PRECONDITIONS.getName()); + if (null != failedPreconditions) { + throw new SkipException("One or more test suite preconditions were not satisfied: " + failedPreconditions); + } + } - /** - * A GET request that omits a mandatory query parameter must produce a - * response with status code 400 (Bad Request) and an exception report - * containing the exception code {@code MissingParameterValue}. - * - * @see "ISO 19142:2010, cl. 7.5: Exception reporting" - * @see "OGC 06-121r3, cl. 8: Exception reports" - * @see "OGC 06-121r3, cl. A.4.1.5: HTTP response status code" - */ - @Test(description = "See ISO 19142: 7.5") - public void getCapabilities_missingServiceParam() { - WebTarget target = client.target(reqEndpointUsingGET); - target = target.queryParam(WFS2.REQUEST_PARAM, WFS2.GET_CAPABILITIES); - Response rsp = target.request(MediaType.APPLICATION_XML).get(); - Assert.assertEquals(rsp.getStatus(), Response.Status.BAD_REQUEST.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - Assert.assertTrue(rsp.hasEntity(), ErrorMessage.get(ErrorMessageKeys.MISSING_XML_ENTITY)); - Object entity = rsp.getEntity(); - if (entity instanceof InputStream) { - try { - this.rspEntity = docBuilder.parse((InputStream) entity); - } catch (SAXException | IOException e) { - throw new AssertionError(e.getMessage()); - } - } - SchematronValidator validator = ValidationUtils.buildSchematronValidator("ExceptionReport.sch", - "MissingParameterValuePhase"); - Result result = validator.validate(new DOMSource(this.rspEntity)); - Assert.assertFalse(validator.ruleViolationsDetected(), ErrorMessage.format(ErrorMessageKeys.NOT_SCHEMA_VALID, - validator.getRuleViolationCount(), XMLUtils.resultToString(result))); - } + /** + * Extracts the GET request endpoint from the capabilities document. + * @param testContext The test (set) context. + */ + @BeforeClass + public void extractEndpoint(ITestContext testContext) { + this.client = ClientBuilder.newClient(); + this.reqEndpointUsingGET = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.GET_CAPABILITIES, + ProtocolBinding.GET); + } - /** - * A minimally valid GetCapabilities request must produce a complete - * representation of the service capabilities document. The content of the - * document must correspond to the "Simple WFS" conformance level. - * - * @see "OGC 06-121r3, cl. 7.2: GetCapabilities request" - */ - @Test(description = "See ISO 19142: 7.2") - public void getFullCapabilities() { - WebTarget target = client.target(reqEndpointUsingGET); - target = target.queryParam(WFS2.REQUEST_PARAM, WFS2.GET_CAPABILITIES); - target = target.queryParam(WFS2.SERVICE_PARAM, WFS2.SERVICE_TYPE_CODE); - Response rsp = target.request(MediaType.APPLICATION_XML).get(); - Assert.assertEquals(rsp.getStatus(), Response.Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - Object entity = rsp.getEntity(); - if (entity instanceof InputStream) { - try { - this.rspEntity = docBuilder.parse((InputStream) entity); - } catch (SAXException | IOException e) { - throw new AssertionError(e.getMessage()); - } - } - Assert.assertNotNull(this.rspEntity, ErrorMessage.get(ErrorMessageKeys.MISSING_XML_ENTITY)); - SchematronValidator validator = ValidationUtils.buildSchematronValidator("wfs-capabilities-2.0.sch", - "SimpleWFSPhase"); - Result result = validator.validate(new DOMSource(this.rspEntity, this.rspEntity.getDocumentURI()), false); - Assert.assertFalse(validator.ruleViolationsDetected(), ErrorMessage.format(ErrorMessageKeys.NOT_SCHEMA_VALID, - validator.getRuleViolationCount(), XMLUtils.resultToString(result))); - } + /** + * A GET request that omits a mandatory query parameter must produce a response with + * status code 400 (Bad Request) and an exception report containing the exception code + * {@code MissingParameterValue}. + * + * @see "ISO 19142:2010, cl. 7.5: Exception reporting" + * @see "OGC 06-121r3, cl. 8: Exception reports" + * @see "OGC 06-121r3, cl. A.4.1.5: HTTP response status code" + */ + @Test(description = "See ISO 19142: 7.5") + public void getCapabilities_missingServiceParam() { + WebTarget target = client.target(reqEndpointUsingGET); + target = target.queryParam(WFS2.REQUEST_PARAM, WFS2.GET_CAPABILITIES); + Response rsp = target.request(MediaType.APPLICATION_XML).get(); + Assert.assertEquals(rsp.getStatus(), Response.Status.BAD_REQUEST.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + Assert.assertTrue(rsp.hasEntity(), ErrorMessage.get(ErrorMessageKeys.MISSING_XML_ENTITY)); + Object entity = rsp.getEntity(); + if (entity instanceof InputStream) { + try { + this.rspEntity = docBuilder.parse((InputStream) entity); + } + catch (SAXException | IOException e) { + throw new AssertionError(e.getMessage()); + } + } + SchematronValidator validator = ValidationUtils.buildSchematronValidator("ExceptionReport.sch", + "MissingParameterValuePhase"); + Result result = validator.validate(new DOMSource(this.rspEntity)); + Assert.assertFalse(validator.ruleViolationsDetected(), ErrorMessage.format(ErrorMessageKeys.NOT_SCHEMA_VALID, + validator.getRuleViolationCount(), XMLUtils.resultToString(result))); + } + + /** + * A minimally valid GetCapabilities request must produce a complete representation of + * the service capabilities document. The content of the document must correspond to + * the "Simple WFS" conformance level. + * + * @see "OGC 06-121r3, cl. 7.2: GetCapabilities request" + */ + @Test(description = "See ISO 19142: 7.2") + public void getFullCapabilities() { + WebTarget target = client.target(reqEndpointUsingGET); + target = target.queryParam(WFS2.REQUEST_PARAM, WFS2.GET_CAPABILITIES); + target = target.queryParam(WFS2.SERVICE_PARAM, WFS2.SERVICE_TYPE_CODE); + Response rsp = target.request(MediaType.APPLICATION_XML).get(); + Assert.assertEquals(rsp.getStatus(), Response.Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + Object entity = rsp.getEntity(); + if (entity instanceof InputStream) { + try { + this.rspEntity = docBuilder.parse((InputStream) entity); + } + catch (SAXException | IOException e) { + throw new AssertionError(e.getMessage()); + } + } + Assert.assertNotNull(this.rspEntity, ErrorMessage.get(ErrorMessageKeys.MISSING_XML_ENTITY)); + SchematronValidator validator = ValidationUtils.buildSchematronValidator("wfs-capabilities-2.0.sch", + "SimpleWFSPhase"); + Result result = validator.validate(new DOMSource(this.rspEntity, this.rspEntity.getDocumentURI()), false); + Assert.assertFalse(validator.ruleViolationsDetected(), ErrorMessage.format(ErrorMessageKeys.NOT_SCHEMA_VALID, + validator.getRuleViolationCount(), XMLUtils.resultToString(result))); + } + + /** + * Acceptable versions of the capabilities document are specified in order of + * preference (10.0.0, 2.0.0, 1.1.0). The response document must conform to the first + * version number that the SUT supports. All WFS implementations must be able to + * perform rudimentary version negotiation in this manner. + * @param binding The ProtocolBinding to use. + * + * @see "OGC 06-121r3, cl. 7.2: GetCapabilities request" + * @see "OGC 06-121r3, cl. 7.3.2: Version negotiation" + */ + @Test(description = "See ISO 19142: 7.2, 7.3.2", dataProvider = "protocol-binding") + public void getCapabilities_acceptVersions(ProtocolBinding binding) { + InputStream entityStream = getClass().getResourceAsStream("getCapabilities_acceptVersions.xml"); + URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.GET_CAPABILITIES, binding); + Response rsp = wfsClient.submitRequest(new StreamSource(entityStream), binding, endpoint); + Assert.assertTrue(rsp.hasEntity(), ErrorMessage.get(ErrorMessageKeys.MISSING_XML_ENTITY)); + this.rspEntity = extractBodyAsDocument(rsp); + String xpath = "/wfs:WFS_Capabilities/@version = '2.0.0'"; + ETSAssert.assertXPath(xpath, this.rspEntity.getDocumentElement(), null); + } - /** - * Acceptable versions of the capabilities document are specified in order - * of preference (10.0.0, 2.0.0, 1.1.0). The response document must conform - * to the first version number that the SUT supports. All WFS - * implementations must be able to perform rudimentary version negotiation - * in this manner. - * - * @param binding - * The ProtocolBinding to use. - * - * @see "OGC 06-121r3, cl. 7.2: GetCapabilities request" - * @see "OGC 06-121r3, cl. 7.3.2: Version negotiation" - */ - @Test(description = "See ISO 19142: 7.2, 7.3.2", dataProvider = "protocol-binding") - public void getCapabilities_acceptVersions(ProtocolBinding binding) { - InputStream entityStream = getClass().getResourceAsStream("getCapabilities_acceptVersions.xml"); - URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.GET_CAPABILITIES, binding); - Response rsp = wfsClient.submitRequest(new StreamSource(entityStream), binding, endpoint); - Assert.assertTrue(rsp.hasEntity(), ErrorMessage.get(ErrorMessageKeys.MISSING_XML_ENTITY)); - this.rspEntity = extractBodyAsDocument(rsp); - String xpath = "/wfs:WFS_Capabilities/@version = '2.0.0'"; - ETSAssert.assertXPath(xpath, this.rspEntity.getDocumentElement(), null); - } } diff --git a/src/main/java/org/opengis/cite/iso19142/simple/StoredQueryTests.java b/src/main/java/org/opengis/cite/iso19142/simple/StoredQueryTests.java index 2d0d15cd..58fe44fc 100644 --- a/src/main/java/org/opengis/cite/iso19142/simple/StoredQueryTests.java +++ b/src/main/java/org/opengis/cite/iso19142/simple/StoredQueryTests.java @@ -32,10 +32,9 @@ import jakarta.ws.rs.core.Response.Status; /** - * Tests the service response to a GetFeature request that invokes a stored - * query. A WFS implementation is required to support stored queries at this - * conformance level. - * + * Tests the service response to a GetFeature request that invokes a stored query. A WFS + * implementation is required to support stored queries at this conformance level. + * *

              * Sources *

              @@ -44,141 +43,139 @@ *
            • ISO 19142:2010, cl. 11: GetFeature operation
            • *
            • ISO 19142:2010, cl. 7.9.3: Stored query expression
            • *
            - * + * */ public class StoredQueryTests extends BaseFixture { - private Schema wfsSchema; - private String queryId; - private DataSampler dataSampler; - - /** - * Initializes the test class fixture. The (pre-compiled) WFS schema is retrieved from the test run context. - * - * @param testContext - * The test run context. - */ - @BeforeClass - public void initClassFixture(ITestContext testContext) { - this.wfsSchema = (Schema) testContext.getSuite().getAttribute(SuiteAttribute.WFS_SCHEMA.getName()); - Assert.assertNotNull(this.wfsSchema, "WFS schema not found in suite fixture."); - this.queryId = (this.wfsVersion.equals(WFS2.V2_0_0)) ? WFS2.QRY_GET_FEATURE_BY_ID_URN - : WFS2.QRY_GET_FEATURE_BY_ID; - this.dataSampler = (DataSampler) testContext.getSuite().getAttribute(SuiteAttribute.SAMPLER.getName()); - } - - /** - * Builds a DOM Document representing a GetFeature request entity that - * contains no query expressions. - */ - @BeforeMethod - public void buildGetFeatureRequestEntity() { - this.reqEntity = WFSMessage.createRequestEntity("GetFeature", this.wfsVersion); - } - - /** - * [{@code Test}] If no stored query matches the given identifier then an - * exception report with exception code "InvalidParameterValue" is expected. - * - * @param binding - * The ProtocolBinding to use. - * - * @see "ISO 19142:2010, cl. 7.9.3.4: Stored query identifier" - */ - @Test(description = "See ISO 19142: 7.9.3.4", dataProvider = "protocol-binding") - public void unknownStoredQuery(ProtocolBinding binding) { - WFSMessage.appendStoredQuery(this.reqEntity, "http://docbook.org/ns/docbook", - Collections.emptyMap()); - URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.GET_FEATURE, binding); - Response rsp = wfsClient.submitRequest(new DOMSource(this.reqEntity), binding, endpoint); - Assert.assertTrue(rsp.hasEntity(), ErrorMessage.get(ErrorMessageKeys.MISSING_XML_ENTITY)); - this.rspEntity = extractBodyAsDocument(rsp); - SchematronValidator validator = ValidationUtils.buildSchematronValidator("ExceptionReport.sch", - "InvalidParameterValuePhase"); - Result invalidaParamValueResult = validator.validate(new DOMSource(this.rspEntity), false); - boolean ruleViolated = validator.ruleViolationsDetected(); - - SchematronValidator operationParsingvalidator = ValidationUtils.buildSchematronValidator("ExceptionReport.sch", "OperationParsingFailedPhase"); - Result operationParsingResult = operationParsingvalidator.validate(new DOMSource(this.rspEntity), false); - - if (!ruleViolated) { - //Assertion for InvalidParameterException - Assert.assertFalse(ruleViolated, ErrorMessage.format(ErrorMessageKeys.NOT_SCHEMA_VALID, validator.getRuleViolationCount(), - XMLUtils.resultToString(invalidaParamValueResult))); - } else if(!operationParsingvalidator.ruleViolationsDetected()) { - //Assertion for OperationParsingFailed - Assert.assertFalse(operationParsingvalidator.ruleViolationsDetected(), ErrorMessage.format(ErrorMessageKeys.NOT_SCHEMA_VALID, operationParsingvalidator.getRuleViolationCount(), - XMLUtils.resultToString(operationParsingResult))); - } else { - //Assertion fail if both exception code not found. - Assert.fail("Required exception code not found in response. Test expects either InvalidParameterException or OperationParsingFailed exception code."); - } - } - - /** - * [{@code Test}] Invoking the {@code GetFeatureById} query with an - * {@code id} parameter value that does not match any feature should produce - * an error response with status code 404 (Not Found). The corresponding OGC - * exception code in the response entity, if present, must be "NotFound". - * - * In the WFS 2.0.0 specification, clause 11.4 stipulates that "In the event - * that a web feature service encounters an error processing a GetFeature - * request, it shall raise an OperationProcessingFailed exception as - * described in 7.5." In Table D.2 this exception code is mapped to status - * code 403 for some reason. - * - * @param binding - * The ProtocolBinding to use. - * - * @see "OGC 09-026r2, cl. 11.3.5: GetFeatureById response" - * @see "OGC 09-026r1, cl. 7.9.3.6: GetFeatureById stored query" - */ - @Test(description = "See ISO 19142: 7.9.3.6, 11.4", dataProvider = "protocol-binding") - public void invokeGetFeatureByIdWithUnknownID(ProtocolBinding binding) { - String id = "uuid-" + UUID.randomUUID().toString(); - WFSMessage.appendStoredQuery(this.reqEntity, this.queryId, Collections.singletonMap("id", (Object) id)); - URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.GET_FEATURE, binding); - Response rsp = wfsClient.submitRequest(new DOMSource(this.reqEntity), binding, endpoint); - this.rspEntity = extractBodyAsDocument(rsp); - int statusCode = rsp.getStatus(); - if (this.wfsVersion.equals("2.0.0")) { - Assert.assertTrue( - statusCode == Status.NOT_FOUND.getStatusCode() - || statusCode == Status.FORBIDDEN.getStatusCode(), - "Expected status code 404 or 403. Received: " + statusCode); - } else { - Assert.assertTrue( - statusCode == Status.INTERNAL_SERVER_ERROR.getStatusCode() - || statusCode == Status.FORBIDDEN.getStatusCode() - || statusCode == Status.NOT_FOUND.getStatusCode(), - ErrorMessageKeys.UNEXPECTED_STATUS); - } - } - - /** - * [{@code Test}] Invoking the {@code GetFeatureById} query with a known - * feature identifier shall produce the matching feature representation - * (@gml:id) as the response entity. If there is no matching feature, an - * error response with a status code 404 (Not Found) is expected. - * - * @param binding - * The ProtocolBinding to use. - * - * @see "ISO 19142:2010, cl. 7.9.3.6: GetFeatureById stored query" - */ - @Test(description = "See ISO 19142: 7.9.3.6", dataProvider = "protocol-binding") - public void invokeGetFeatureById( ProtocolBinding binding ) { - String featureIdToRequest = this.dataSampler.getFeatureId(); - Assert.assertTrue( featureIdToRequest != null && !featureIdToRequest.isEmpty(), - ErrorMessage.get( ErrorMessageKeys.FID_NOT_FOUND ) ); - WFSMessage.appendStoredQuery( this.reqEntity, this.queryId, Collections.singletonMap( "id", featureIdToRequest ) ); - URI endpoint = ServiceMetadataUtils.getOperationEndpoint( this.wfsMetadata, WFS2.GET_FEATURE, binding ); - Response rsp = wfsClient.submitRequest( new DOMSource( this.reqEntity ), binding, endpoint ); - Assert.assertTrue( rsp.hasEntity(), ErrorMessage.get( ErrorMessageKeys.MISSING_XML_ENTITY ) ); - this.rspEntity = extractBodyAsDocument( rsp ); - Element feature = this.rspEntity.getDocumentElement(); - Assert.assertEquals( feature.getAttributeNS( Namespaces.GML, "id" ), featureIdToRequest, - ErrorMessage.get( ErrorMessageKeys.UNEXPECTED_ID ) ); - } + private Schema wfsSchema; + + private String queryId; + + private DataSampler dataSampler; + + /** + * Initializes the test class fixture. The (pre-compiled) WFS schema is retrieved from + * the test run context. + * @param testContext The test run context. + */ + @BeforeClass + public void initClassFixture(ITestContext testContext) { + this.wfsSchema = (Schema) testContext.getSuite().getAttribute(SuiteAttribute.WFS_SCHEMA.getName()); + Assert.assertNotNull(this.wfsSchema, "WFS schema not found in suite fixture."); + this.queryId = (this.wfsVersion.equals(WFS2.V2_0_0)) ? WFS2.QRY_GET_FEATURE_BY_ID_URN + : WFS2.QRY_GET_FEATURE_BY_ID; + this.dataSampler = (DataSampler) testContext.getSuite().getAttribute(SuiteAttribute.SAMPLER.getName()); + } + + /** + * Builds a DOM Document representing a GetFeature request entity that contains no + * query expressions. + */ + @BeforeMethod + public void buildGetFeatureRequestEntity() { + this.reqEntity = WFSMessage.createRequestEntity("GetFeature", this.wfsVersion); + } + + /** + * [{@code Test}] If no stored query matches the given identifier then an exception + * report with exception code "InvalidParameterValue" is expected. + * @param binding The ProtocolBinding to use. + * + * @see "ISO 19142:2010, cl. 7.9.3.4: Stored query identifier" + */ + @Test(description = "See ISO 19142: 7.9.3.4", dataProvider = "protocol-binding") + public void unknownStoredQuery(ProtocolBinding binding) { + WFSMessage.appendStoredQuery(this.reqEntity, "http://docbook.org/ns/docbook", + Collections.emptyMap()); + URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.GET_FEATURE, binding); + Response rsp = wfsClient.submitRequest(new DOMSource(this.reqEntity), binding, endpoint); + Assert.assertTrue(rsp.hasEntity(), ErrorMessage.get(ErrorMessageKeys.MISSING_XML_ENTITY)); + this.rspEntity = extractBodyAsDocument(rsp); + SchematronValidator validator = ValidationUtils.buildSchematronValidator("ExceptionReport.sch", + "InvalidParameterValuePhase"); + Result invalidaParamValueResult = validator.validate(new DOMSource(this.rspEntity), false); + boolean ruleViolated = validator.ruleViolationsDetected(); + + SchematronValidator operationParsingvalidator = ValidationUtils.buildSchematronValidator("ExceptionReport.sch", + "OperationParsingFailedPhase"); + Result operationParsingResult = operationParsingvalidator.validate(new DOMSource(this.rspEntity), false); + + if (!ruleViolated) { + // Assertion for InvalidParameterException + Assert.assertFalse(ruleViolated, ErrorMessage.format(ErrorMessageKeys.NOT_SCHEMA_VALID, + validator.getRuleViolationCount(), XMLUtils.resultToString(invalidaParamValueResult))); + } + else if (!operationParsingvalidator.ruleViolationsDetected()) { + // Assertion for OperationParsingFailed + Assert.assertFalse(operationParsingvalidator.ruleViolationsDetected(), + ErrorMessage.format(ErrorMessageKeys.NOT_SCHEMA_VALID, + operationParsingvalidator.getRuleViolationCount(), + XMLUtils.resultToString(operationParsingResult))); + } + else { + // Assertion fail if both exception code not found. + Assert.fail( + "Required exception code not found in response. Test expects either InvalidParameterException or OperationParsingFailed exception code."); + } + } + + /** + * [{@code Test}] Invoking the {@code GetFeatureById} query with an {@code id} + * parameter value that does not match any feature should produce an error response + * with status code 404 (Not Found). The corresponding OGC exception code in the + * response entity, if present, must be "NotFound". + * + * In the WFS 2.0.0 specification, clause 11.4 stipulates that "In the event that a + * web feature service encounters an error processing a GetFeature request, it shall + * raise an OperationProcessingFailed exception as described in 7.5." In Table D.2 + * this exception code is mapped to status code 403 for some reason. + * @param binding The ProtocolBinding to use. + * + * @see "OGC 09-026r2, cl. 11.3.5: GetFeatureById response" + * @see "OGC 09-026r1, cl. 7.9.3.6: GetFeatureById stored query" + */ + @Test(description = "See ISO 19142: 7.9.3.6, 11.4", dataProvider = "protocol-binding") + public void invokeGetFeatureByIdWithUnknownID(ProtocolBinding binding) { + String id = "uuid-" + UUID.randomUUID().toString(); + WFSMessage.appendStoredQuery(this.reqEntity, this.queryId, Collections.singletonMap("id", (Object) id)); + URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.GET_FEATURE, binding); + Response rsp = wfsClient.submitRequest(new DOMSource(this.reqEntity), binding, endpoint); + this.rspEntity = extractBodyAsDocument(rsp); + int statusCode = rsp.getStatus(); + if (this.wfsVersion.equals("2.0.0")) { + Assert.assertTrue( + statusCode == Status.NOT_FOUND.getStatusCode() || statusCode == Status.FORBIDDEN.getStatusCode(), + "Expected status code 404 or 403. Received: " + statusCode); + } + else { + Assert.assertTrue(statusCode == Status.INTERNAL_SERVER_ERROR.getStatusCode() + || statusCode == Status.FORBIDDEN.getStatusCode() || statusCode == Status.NOT_FOUND.getStatusCode(), + ErrorMessageKeys.UNEXPECTED_STATUS); + } + } + + /** + * [{@code Test}] Invoking the {@code GetFeatureById} query with a known feature + * identifier shall produce the matching feature representation (@gml:id) as the + * response entity. If there is no matching feature, an error response with a status + * code 404 (Not Found) is expected. + * @param binding The ProtocolBinding to use. + * + * @see "ISO 19142:2010, cl. 7.9.3.6: GetFeatureById stored query" + */ + @Test(description = "See ISO 19142: 7.9.3.6", dataProvider = "protocol-binding") + public void invokeGetFeatureById(ProtocolBinding binding) { + String featureIdToRequest = this.dataSampler.getFeatureId(); + Assert.assertTrue(featureIdToRequest != null && !featureIdToRequest.isEmpty(), + ErrorMessage.get(ErrorMessageKeys.FID_NOT_FOUND)); + WFSMessage.appendStoredQuery(this.reqEntity, this.queryId, Collections.singletonMap("id", featureIdToRequest)); + URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.GET_FEATURE, binding); + Response rsp = wfsClient.submitRequest(new DOMSource(this.reqEntity), binding, endpoint); + Assert.assertTrue(rsp.hasEntity(), ErrorMessage.get(ErrorMessageKeys.MISSING_XML_ENTITY)); + this.rspEntity = extractBodyAsDocument(rsp); + Element feature = this.rspEntity.getDocumentElement(); + Assert.assertEquals(feature.getAttributeNS(Namespaces.GML, "id"), featureIdToRequest, + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_ID)); + } } diff --git a/src/main/java/org/opengis/cite/iso19142/simple/package-info.java b/src/main/java/org/opengis/cite/iso19142/simple/package-info.java index 331c17bd..4491646b 100644 --- a/src/main/java/org/opengis/cite/iso19142/simple/package-info.java +++ b/src/main/java/org/opengis/cite/iso19142/simple/package-info.java @@ -1,13 +1,13 @@ /** - * This package contains test classes that assess conformance of the SUT with - * respect to the "Simple WFS" conformance class, which is the lowest level - * of WFS 2.0 conformance. The following service requests must be supported: + * This package contains test classes that assess conformance of the SUT with respect to + * the "Simple WFS" conformance class, which is the lowest level of WFS 2.0 conformance. + * The following service requests must be supported: * *
              - *
            • GetCapabilities
            • + *
            • GetCapabilities
            • *
            • DescribeFeatureType
            • - *
            • ListStoredQueries
            • - *
            • DescribeStoredQueries
            • + *
            • ListStoredQueries
            • + *
            • DescribeStoredQueries
            • *
            • GetFeature with stored query support
            • *
            * @@ -15,4 +15,3 @@ * @see "ISO ISO 19142:2010, cl. A.1.1: Simple WFS" */ package org.opengis.cite.iso19142.simple; - diff --git a/src/main/java/org/opengis/cite/iso19142/transaction/DeleteTests.java b/src/main/java/org/opengis/cite/iso19142/transaction/DeleteTests.java index 1098b5e3..262fbfc0 100644 --- a/src/main/java/org/opengis/cite/iso19142/transaction/DeleteTests.java +++ b/src/main/java/org/opengis/cite/iso19142/transaction/DeleteTests.java @@ -23,80 +23,67 @@ import org.w3c.dom.NodeList; /** - * Tests the response to a Transaction request that includes one or more delete - * actions. - * + * Tests the response to a Transaction request that includes one or more delete actions. + * * @see "ISO 19142:2010, cl. 15.2.7: Delete action" */ public class DeleteTests extends TransactionFixture { - /** List containing original representations of deleted features */ - private List deletedFeatures = new ArrayList(); + /** List containing original representations of deleted features */ + private List deletedFeatures = new ArrayList(); - /** - * Restores the WFS data store to its previous state by replacing all - * deleted features with their previous representations. - */ - @AfterClass - public void restoreDeletedFeatures() { - if (deletedFeatures.isEmpty()) { - return; - } - Document rspEntity = this.wfsClient.insert(deletedFeatures, - ProtocolBinding.ANY); - String xpath = String.format("//wfs:totalInserted = '%d'", - deletedFeatures.size()); - Boolean result; - try { - result = (Boolean) XMLUtils.evaluateXPath(rspEntity, xpath, null, - XPathConstants.BOOLEAN); - } catch (XPathExpressionException xpe) { - throw new RuntimeException(xpe); - } - if (!result) { - String msg = String.format( - "%s: Failed to insert deleted features.\n%s", getClass() - .getName(), XMLUtils.writeNodeToString(rspEntity)); - TestSuiteLogger.log(Level.WARNING, msg); - } - } + /** + * Restores the WFS data store to its previous state by replacing all deleted features + * with their previous representations. + */ + @AfterClass + public void restoreDeletedFeatures() { + if (deletedFeatures.isEmpty()) { + return; + } + Document rspEntity = this.wfsClient.insert(deletedFeatures, ProtocolBinding.ANY); + String xpath = String.format("//wfs:totalInserted = '%d'", deletedFeatures.size()); + Boolean result; + try { + result = (Boolean) XMLUtils.evaluateXPath(rspEntity, xpath, null, XPathConstants.BOOLEAN); + } + catch (XPathExpressionException xpe) { + throw new RuntimeException(xpe); + } + if (!result) { + String msg = String.format("%s: Failed to insert deleted features.\n%s", getClass().getName(), + XMLUtils.writeNodeToString(rspEntity)); + TestSuiteLogger.log(Level.WARNING, msg); + } + } + + /** + * [{@code Test}] Submits a Transaction request to delete an existing feature + * instance. The test is run for all supported transaction request bindings and + * feature types. The response entity (wfs:TransactionResponse) must be schema-valid + * and the wfs:TransactionSummary element shall report the correct number of deleted + * features (totalDeleted). + * @param binding A supported message binding (POST preferred over SOAP). + * @param featureType A QName representing the qualified name of some feature type. + * + * @see "ISO 19142:2010, cl. 15.3.3: TransactionSummary element" + */ + @Test(description = "See ISO 19142: 15.2.7", dataProvider = "binding+availFeatureType") + public void deleteFeature(ProtocolBinding binding, QName featureType) { + Document doc = wfsClient.getFeatureByType(featureType, 10, null); + NodeList features = doc.getElementsByTagNameNS(featureType.getNamespaceURI(), featureType.getLocalPart()); + // randomly select an available feature to delete + Random random = new Random(); + Element originalFeature = (Element) features.item(random.nextInt(features.getLength())); + String gmlId = originalFeature.getAttributeNS(Namespaces.GML, "id"); + Map featuresToDelete = new HashMap(); + featuresToDelete.put(gmlId, new QName(originalFeature.getNamespaceURI(), originalFeature.getLocalName())); + this.rspEntity = wfsClient.deleteFeatures(featuresToDelete, binding); + ETSAssert.assertXPath("//wfs:TransactionResponse", this.rspEntity, null); + String xpath = String.format("//wfs:totalDeleted = '%d'", featuresToDelete.size()); + ETSAssert.assertXPath(xpath, this.rspEntity, null); + deletedFeatures.add(originalFeature); + ETSAssert.assertFeatureAvailability(gmlId, false, wfsClient); + } - /** - * [{@code Test}] Submits a Transaction request to delete an existing - * feature instance. The test is run for all supported transaction request - * bindings and feature types. The response entity (wfs:TransactionResponse) - * must be schema-valid and the wfs:TransactionSummary element shall report - * the correct number of deleted features (totalDeleted). - * - * @param binding - * A supported message binding (POST preferred over SOAP). - * @param featureType - * A QName representing the qualified name of some feature type. - * - * @see "ISO 19142:2010, cl. 15.3.3: TransactionSummary element" - */ - @Test(description = "See ISO 19142: 15.2.7", dataProvider = "binding+availFeatureType") - public void deleteFeature(ProtocolBinding binding, QName featureType) { - Document doc = wfsClient.getFeatureByType(featureType, 10, null); - NodeList features = doc.getElementsByTagNameNS( - featureType.getNamespaceURI(), featureType.getLocalPart()); - // randomly select an available feature to delete - Random random = new Random(); - Element originalFeature = (Element) features.item(random - .nextInt(features.getLength())); - String gmlId = originalFeature.getAttributeNS(Namespaces.GML, "id"); - Map featuresToDelete = new HashMap(); - featuresToDelete.put( - gmlId, - new QName(originalFeature.getNamespaceURI(), originalFeature - .getLocalName())); - this.rspEntity = wfsClient.deleteFeatures(featuresToDelete, binding); - ETSAssert - .assertXPath("//wfs:TransactionResponse", this.rspEntity, null); - String xpath = String.format("//wfs:totalDeleted = '%d'", - featuresToDelete.size()); - ETSAssert.assertXPath(xpath, this.rspEntity, null); - deletedFeatures.add(originalFeature); - ETSAssert.assertFeatureAvailability(gmlId, false, wfsClient); - } } diff --git a/src/main/java/org/opengis/cite/iso19142/transaction/InsertTests.java b/src/main/java/org/opengis/cite/iso19142/transaction/InsertTests.java index 6b22608a..29fdc480 100644 --- a/src/main/java/org/opengis/cite/iso19142/transaction/InsertTests.java +++ b/src/main/java/org/opengis/cite/iso19142/transaction/InsertTests.java @@ -36,191 +36,179 @@ import jakarta.ws.rs.core.Response.Status; /** - * Tests the response to a Transaction request that includes one or more insert - * actions. - * + * Tests the response to a Transaction request that includes one or more insert actions. + * * @see "ISO 19142:2010, cl. 15.2.4: Insert action" */ public class InsertTests extends TransactionFixture { - private Map createdFeatures = new HashMap(); + private Map createdFeatures = new HashMap(); - /** - * Restores the WFS data store to its previous state by deleting all - * features that were successfully inserted by test methods in this class. - */ - @AfterClass - public void deleteInsertedFeatures() { - if (createdFeatures.isEmpty()) { - return; - } - Document rspEntity = this.wfsClient.deleteFeatures(createdFeatures, ProtocolBinding.ANY); - String xpath = String.format("//wfs:totalDeleted = '%d'", createdFeatures.size()); - Boolean result; - try { - result = (Boolean) XMLUtils.evaluateXPath(rspEntity, xpath, null, XPathConstants.BOOLEAN); - } catch (XPathExpressionException xpe) { - throw new RuntimeException(xpe); - } - if (!result) { - String msg = String.format("%s: Failed to remove all new features:\n %s \n%s", getClass().getName(), - this.createdFeatures, XMLUtils.writeNodeToString(rspEntity)); - TestSuiteLogger.log(Level.WARNING, msg); - } - this.createdFeatures.clear(); - } + /** + * Restores the WFS data store to its previous state by deleting all features that + * were successfully inserted by test methods in this class. + */ + @AfterClass + public void deleteInsertedFeatures() { + if (createdFeatures.isEmpty()) { + return; + } + Document rspEntity = this.wfsClient.deleteFeatures(createdFeatures, ProtocolBinding.ANY); + String xpath = String.format("//wfs:totalDeleted = '%d'", createdFeatures.size()); + Boolean result; + try { + result = (Boolean) XMLUtils.evaluateXPath(rspEntity, xpath, null, XPathConstants.BOOLEAN); + } + catch (XPathExpressionException xpe) { + throw new RuntimeException(xpe); + } + if (!result) { + String msg = String.format("%s: Failed to remove all new features:\n %s \n%s", getClass().getName(), + this.createdFeatures, XMLUtils.writeNodeToString(rspEntity)); + TestSuiteLogger.log(Level.WARNING, msg); + } + this.createdFeatures.clear(); + } - /** - * [{@code Test}] Submits a Transaction request to insert a feature instance - * of a type supported by the SUT. The test is run for all supported - * Transaction request bindings and feature types. The response entity - * (wfs:TransactionResponse) must be schema-valid and contain the - * wfs:InsertResults element. - * - * @param binding - * A supported message binding. - * @param featureType - * A QName representing the qualified name of some feature type. - * - * @see "ISO 19142:2010, cl. 15.3.3: TransactionSummary element" - * @see "ISO 19142:2010, cl. 15.3.4: InsertResults element" - */ - @Test(description = "See ISO 19142: 15.2.4, 15.3.4", dataProvider = "binding+availFeatureType") - public void insertSupportedFeature(ProtocolBinding binding, QName featureType) { - Node feature = createFeatureInstance(featureType); - WFSMessage.addInsertStatement(this.reqEntity, feature); - URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.TRANSACTION, binding); - Response rsp = this.wfsClient.submitRequest(new DOMSource(this.reqEntity), binding, endpoint); - this.rspEntity = rsp.readEntity(Document.class); - Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - ETSAssert.assertXPath("//wfs:TransactionResponse/wfs:InsertResults", this.rspEntity, null); - List newFeatureIDs = extractFeatureIdentifiers(this.rspEntity, WFS2.Transaction.INSERT); - String rid = newFeatureIDs.get(0).getRid(); - createdFeatures.put(rid, featureType); - ETSAssert.assertFeatureAvailability(rid, true, this.wfsClient); - } + /** + * [{@code Test}] Submits a Transaction request to insert a feature instance of a type + * supported by the SUT. The test is run for all supported Transaction request + * bindings and feature types. The response entity (wfs:TransactionResponse) must be + * schema-valid and contain the wfs:InsertResults element. + * @param binding A supported message binding. + * @param featureType A QName representing the qualified name of some feature type. + * + * @see "ISO 19142:2010, cl. 15.3.3: TransactionSummary element" + * @see "ISO 19142:2010, cl. 15.3.4: InsertResults element" + */ + @Test(description = "See ISO 19142: 15.2.4, 15.3.4", dataProvider = "binding+availFeatureType") + public void insertSupportedFeature(ProtocolBinding binding, QName featureType) { + Node feature = createFeatureInstance(featureType); + WFSMessage.addInsertStatement(this.reqEntity, feature); + URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.TRANSACTION, binding); + Response rsp = this.wfsClient.submitRequest(new DOMSource(this.reqEntity), binding, endpoint); + this.rspEntity = rsp.readEntity(Document.class); + Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + ETSAssert.assertXPath("//wfs:TransactionResponse/wfs:InsertResults", this.rspEntity, null); + List newFeatureIDs = extractFeatureIdentifiers(this.rspEntity, WFS2.Transaction.INSERT); + String rid = newFeatureIDs.get(0).getRid(); + createdFeatures.put(rid, featureType); + ETSAssert.assertFeatureAvailability(rid, true, this.wfsClient); + } - /** - * [{@code Test}] Submits a Transaction request to insert a feature instance - * of a type not recognized by the SUT. An ExceptionReport (with status code - * 400) containing the exception code {@code InvalidValue} is expected in - * response. - * - * @see "ISO 19142:2010, Table 3: WFS exception codes" - */ - @Test(description = "See ISO 19142: 7.5, 15.4") - public void insertInvalidFeature() { - try { - this.reqEntity = docBuilder.parse(getClass().getResourceAsStream("InsertUnrecognizedFeature.xml")); - } catch (Exception e) { - throw new RuntimeException("Failed to parse InsertUnrecognizedFeature.xml from classpath", e); - } - ProtocolBinding binding = wfsClient.getAnyTransactionBinding(); - URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.TRANSACTION, binding); - Response rsp = wfsClient.submitRequest(new DOMSource(this.reqEntity), binding, endpoint); - Assert.assertEquals(rsp.getStatus(), Status.BAD_REQUEST.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - this.rspEntity = rsp.readEntity(Document.class); - String xpath = "//ows:Exception[@exceptionCode = 'InvalidValue']"; - ETSAssert.assertXPath(xpath, this.rspEntity.getDocumentElement(), null); - } + /** + * [{@code Test}] Submits a Transaction request to insert a feature instance of a type + * not recognized by the SUT. An ExceptionReport (with status code 400) containing the + * exception code {@code InvalidValue} is expected in response. + * + * @see "ISO 19142:2010, Table 3: WFS exception codes" + */ + @Test(description = "See ISO 19142: 7.5, 15.4") + public void insertInvalidFeature() { + try { + this.reqEntity = docBuilder.parse(getClass().getResourceAsStream("InsertUnrecognizedFeature.xml")); + } + catch (Exception e) { + throw new RuntimeException("Failed to parse InsertUnrecognizedFeature.xml from classpath", e); + } + ProtocolBinding binding = wfsClient.getAnyTransactionBinding(); + URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.TRANSACTION, binding); + Response rsp = wfsClient.submitRequest(new DOMSource(this.reqEntity), binding, endpoint); + Assert.assertEquals(rsp.getStatus(), Status.BAD_REQUEST.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + this.rspEntity = rsp.readEntity(Document.class); + String xpath = "//ows:Exception[@exceptionCode = 'InvalidValue']"; + ETSAssert.assertXPath(xpath, this.rspEntity.getDocumentElement(), null); + } - /** - * Extracts a list of resource identifiers for features that were affected - * by a successful transaction request. The identifiers are found using this - * XPath expression: "//{wfs:ActionResultsType}/wfs:Feature/fes:ResourceId", - * where {@literal wfs:ActionResultsType} denotes an element of this type. - * - * @param entity - * A Document representing a successful transaction response - * entity (wfs:TransactionResponse). - * @param action - * The transaction results of interest. - * @return A List containing zero or more feature identifiers. - */ - public static List extractFeatureIdentifiers(Document entity, WFS2.Transaction action) { - List idList = new ArrayList<>(); - String xpath = String.format("//wfs:%sResults/wfs:Feature/fes:ResourceId", action.toString()); - Map nsBindings = new HashMap(); - nsBindings.put(Namespaces.WFS, "wfs"); - nsBindings.put(Namespaces.FES, "fes"); - try { - NodeList idNodes = XMLUtils.evaluateXPath(entity, xpath, nsBindings); - for (int i = 0; i < idNodes.getLength(); i++) { - Element idElem = (Element) idNodes.item(i); - ResourceId id = new ResourceId(idElem.getAttribute("rid")); - if (!idElem.getAttribute("previousRid").isEmpty()) { - id.setPreviousRid(idElem.getAttribute("previousRid")); - } - if (!idElem.getAttribute("version").isEmpty()) { - id.setVersion(idElem.getAttribute("version")); - } - idList.add(id); - } - } catch (XPathExpressionException e) { - throw new RuntimeException(e); - } - return idList; - } + /** + * Extracts a list of resource identifiers for features that were affected by a + * successful transaction request. The identifiers are found using this XPath + * expression: "//{wfs:ActionResultsType}/wfs:Feature/fes:ResourceId", where + * {@literal wfs:ActionResultsType} denotes an element of this type. + * @param entity A Document representing a successful transaction response entity + * (wfs:TransactionResponse). + * @param action The transaction results of interest. + * @return A List containing zero or more feature identifiers. + */ + public static List extractFeatureIdentifiers(Document entity, WFS2.Transaction action) { + List idList = new ArrayList<>(); + String xpath = String.format("//wfs:%sResults/wfs:Feature/fes:ResourceId", action.toString()); + Map nsBindings = new HashMap(); + nsBindings.put(Namespaces.WFS, "wfs"); + nsBindings.put(Namespaces.FES, "fes"); + try { + NodeList idNodes = XMLUtils.evaluateXPath(entity, xpath, nsBindings); + for (int i = 0; i < idNodes.getLength(); i++) { + Element idElem = (Element) idNodes.item(i); + ResourceId id = new ResourceId(idElem.getAttribute("rid")); + if (!idElem.getAttribute("previousRid").isEmpty()) { + id.setPreviousRid(idElem.getAttribute("previousRid")); + } + if (!idElem.getAttribute("version").isEmpty()) { + id.setVersion(idElem.getAttribute("version")); + } + idList.add(id); + } + } + catch (XPathExpressionException e) { + throw new RuntimeException(e); + } + return idList; + } - /** - * Creates a new representation of a feature instance. First, an attempt is - * made to retrieve a feature representation from the SUT; if this fails or - * no instances exist then one is generated using an application schema - * (Note: this facility is not yet implemented). The gml:id attribute is - * modified and a new gml:identifier is added. - * - * @param featureType - * A QName representing the qualified name of some feature type. - * @return A Node (Element) node representing a feature instance. - */ - Node createFeatureInstance(QName featureType) { - Document entity = wfsClient.getFeatureByType(featureType, 1, null); - NodeList features = entity.getElementsByTagNameNS(featureType.getNamespaceURI(), featureType.getLocalPart()); - if (features.getLength() == 0) { - // TODO: try generating minimal instance from schema - throw new NullPointerException("Unable to obtain feature instance of type " + featureType); - } - Element feature = (Element) features.item(0); - feature.setAttributeNS(Namespaces.GML, "gml:id", "id-" + System.currentTimeMillis()); - insertRandomIdentifier(feature); - return feature.cloneNode(true); - } + /** + * Creates a new representation of a feature instance. First, an attempt is made to + * retrieve a feature representation from the SUT; if this fails or no instances exist + * then one is generated using an application schema (Note: this facility is not yet + * implemented). The gml:id attribute is modified and a new gml:identifier is added. + * @param featureType A QName representing the qualified name of some feature type. + * @return A Node (Element) node representing a feature instance. + */ + Node createFeatureInstance(QName featureType) { + Document entity = wfsClient.getFeatureByType(featureType, 1, null); + NodeList features = entity.getElementsByTagNameNS(featureType.getNamespaceURI(), featureType.getLocalPart()); + if (features.getLength() == 0) { + // TODO: try generating minimal instance from schema + throw new NullPointerException("Unable to obtain feature instance of type " + featureType); + } + Element feature = (Element) features.item(0); + feature.setAttributeNS(Namespaces.GML, "gml:id", "id-" + System.currentTimeMillis()); + insertRandomIdentifier(feature); + return feature.cloneNode(true); + } - /** - * Inserts a user-assigned gml:identifier element having a random UUID - * value. An existing identifier is replaced. - * - * @param feature - * An Element node representing a GML feature. - * @return The UUID that was assigned to the feature instance. - */ - public static UUID insertRandomIdentifier(Element feature) { - QName propName = new QName(Namespaces.GML, "identifier"); - Element identifier = XMLUtils.createElement(propName); - identifier.setAttribute("codeSpace", "http://cite.opengeospatial.org/"); - UUID uuid = UUID.randomUUID(); - identifier.setTextContent(uuid.toString()); - WFSMessage.insertGMLProperty(feature, identifier); - return uuid; - } + /** + * Inserts a user-assigned gml:identifier element having a random UUID value. An + * existing identifier is replaced. + * @param feature An Element node representing a GML feature. + * @return The UUID that was assigned to the feature instance. + */ + public static UUID insertRandomIdentifier(Element feature) { + QName propName = new QName(Namespaces.GML, "identifier"); + Element identifier = XMLUtils.createElement(propName); + identifier.setAttribute("codeSpace", "http://cite.opengeospatial.org/"); + UUID uuid = UUID.randomUUID(); + identifier.setTextContent(uuid.toString()); + WFSMessage.insertGMLProperty(feature, identifier); + return uuid; + } + + /** + * Adds a gml:name element to the given feature representation. The first name will be + * replaced if one or more are already present. + * @param feature An Element node representing a GML feature. + * @return The name that was assigned to the feature instance. + */ + public static String addRandomName(Element feature) { + QName gmlName = new QName(Namespaces.GML, "name"); + Element name = XMLUtils.createElement(gmlName); + name.setAttribute("codeSpace", "http://cite.opengeospatial.org/"); + String value = UUID.randomUUID().toString().replaceAll("-", ""); + name.setTextContent(value.toString()); + WFSMessage.insertGMLProperty(feature, name); + return value; + } - /** - * Adds a gml:name element to the given feature representation. The first - * name will be replaced if one or more are already present. - * - * @param feature - * An Element node representing a GML feature. - * @return The name that was assigned to the feature instance. - */ - public static String addRandomName(Element feature) { - QName gmlName = new QName(Namespaces.GML, "name"); - Element name = XMLUtils.createElement(gmlName); - name.setAttribute("codeSpace", "http://cite.opengeospatial.org/"); - String value = UUID.randomUUID().toString().replaceAll("-", ""); - name.setTextContent(value.toString()); - WFSMessage.insertGMLProperty(feature, name); - return value; - } } diff --git a/src/main/java/org/opengis/cite/iso19142/transaction/ReplaceTests.java b/src/main/java/org/opengis/cite/iso19142/transaction/ReplaceTests.java index b8ed8654..5fb54a94 100644 --- a/src/main/java/org/opengis/cite/iso19142/transaction/ReplaceTests.java +++ b/src/main/java/org/opengis/cite/iso19142/transaction/ReplaceTests.java @@ -33,130 +33,124 @@ import jakarta.ws.rs.core.Response; - /** - * Tests the response to a Transaction request that includes one or more replace - * actions. If the WFS supports feature versioning, then the wfs:ReplaceResults - * element must be present (to convey the new identifiers); however, this is not - * currently checked. - * + * Tests the response to a Transaction request that includes one or more replace actions. + * If the WFS supports feature versioning, then the wfs:ReplaceResults element must be + * present (to convey the new identifiers); however, this is not currently checked. + * * @see "ISO 19142:2010, cl. 15.2.6: Replace action" */ public class ReplaceTests extends TransactionFixture { - /** Identifier for the collection of replacement property values. */ - public static final String REPL_PROPS = "replProps"; - /** List containing original representations of modified features */ - private List originalFeatures = new ArrayList(); + /** Identifier for the collection of replacement property values. */ + public static final String REPL_PROPS = "replProps"; + + /** List containing original representations of modified features */ + private List originalFeatures = new ArrayList(); + + /** + * Restores the WFS data store to its previous state by replacing all modified + * features with their original representations. + */ + @AfterClass + public void restoreModifiedFeatures() { + if (originalFeatures.isEmpty()) { + return; + } + Document req = WFSMessage.createRequestEntity(WFS2.TRANSACTION, this.wfsVersion); + WFSMessage.addReplaceStatements(req, originalFeatures); + Response rsp = wfsClient.submitRequest(req, ProtocolBinding.ANY); + Document rspEntity = rsp.readEntity(Document.class); + String expr = String.format("//wfs:totalReplaced = '%d'", originalFeatures.size()); + Boolean result; + try { + result = (Boolean) XMLUtils.evaluateXPath(rspEntity, expr, null, XPathConstants.BOOLEAN); + } + catch (XPathExpressionException xpe) { + throw new RuntimeException(xpe); + } + if (!result) { + String msg = String.format("%s: Failed to replace modified features.\n%s", getClass().getName(), + XMLUtils.writeNodeToString(rspEntity)); + TestSuiteLogger.log(Level.WARNING, msg); + } + } - /** - * Restores the WFS data store to its previous state by replacing all - * modified features with their original representations. - */ - @AfterClass - public void restoreModifiedFeatures() { - if (originalFeatures.isEmpty()) { - return; - } - Document req = WFSMessage.createRequestEntity(WFS2.TRANSACTION, this.wfsVersion); - WFSMessage.addReplaceStatements(req, originalFeatures); - Response rsp = wfsClient.submitRequest(req, ProtocolBinding.ANY); - Document rspEntity = rsp.readEntity(Document.class); - String expr = String.format("//wfs:totalReplaced = '%d'", originalFeatures.size()); - Boolean result; - try { - result = (Boolean) XMLUtils.evaluateXPath(rspEntity, expr, null, XPathConstants.BOOLEAN); - } catch (XPathExpressionException xpe) { - throw new RuntimeException(xpe); - } - if (!result) { - String msg = String.format("%s: Failed to replace modified features.\n%s", getClass().getName(), - XMLUtils.writeNodeToString(rspEntity)); - TestSuiteLogger.log(Level.WARNING, msg); - } - } + /** + * [{@code Test}] Submits a Transaction request to replace an existing feature + * instance. The test is run for all supported protocol bindings (except HTTP GET) and + * feature types. The response entity (wfs:TransactionResponse) must be schema-valid. + * + * @see "ISO 19142:2010, cl. 15.3.3: TransactionSummary element" + * @see "ISO 19142:2010, cl. 15.3.6: ReplaceResults element" + * @param binding A supported transaction request binding. + * @param featureType A QName representing the qualified name of some feature type. + * + */ + @Test(description = "See ISO 19142: 15.3.3, 15.3.6", dataProvider = "binding+availFeatureType") + public void replaceFeature(ProtocolBinding binding, QName featureType) { + Document doc = wfsClient.getFeatureByType(featureType, 1, null); + NodeList features = doc.getElementsByTagNameNS(featureType.getNamespaceURI(), featureType.getLocalPart()); + Element originalFeature = (Element) features.item(0); + Element replacement = createReplacementFeature(originalFeature); + List replacements = Arrays.asList(replacement); + WFSMessage.addReplaceStatements(this.reqEntity, replacements); + Response rsp = wfsClient.submitRequest(this.reqEntity, binding); + this.rspEntity = rsp.readEntity(Document.class); + String xpath = String.format("//wfs:totalReplaced = '%d'", replacements.size()); + ETSAssert.assertXPath(xpath, this.rspEntity, null); + originalFeatures.add(originalFeature); + // feature versioning may be enabled, so get fes:ResourceId/@rid + Element resourceId = (Element) this.rspEntity.getElementsByTagNameNS(FES2.NS, FES2.RESOURCE_ID).item(0); + Assert.assertNotNull(resourceId, ErrorMessage.format(ErrorMessageKeys.MISSING_INFOSET_ITEM, FES2.RESOURCE_ID)); + String gmlId = resourceId.getAttribute("rid"); + @SuppressWarnings("unchecked") + Map replProps = (Map) replacement + .getUserData(REPL_PROPS); + this.rspEntity = wfsClient.invokeStoredQuery(WFS2.QRY_GET_FEATURE_BY_ID, Collections.singletonMap("id", gmlId)); + Element feature = this.rspEntity.getDocumentElement(); + ETSAssert.assertQualifiedName(feature, featureType); + ETSAssert.assertSimpleProperties(feature, replProps, null); + } - /** - * [{@code Test}] Submits a Transaction request to replace an existing - * feature instance. The test is run for all supported protocol bindings - * (except HTTP GET) and feature types. The response entity - * (wfs:TransactionResponse) must be schema-valid. - * - * @see "ISO 19142:2010, cl. 15.3.3: TransactionSummary element" - * @see "ISO 19142:2010, cl. 15.3.6: ReplaceResults element" - * - * @param binding - * A supported transaction request binding. - * @param featureType - * A QName representing the qualified name of some feature type. - * - */ - @Test(description = "See ISO 19142: 15.3.3, 15.3.6", dataProvider = "binding+availFeatureType") - public void replaceFeature(ProtocolBinding binding, QName featureType) { - Document doc = wfsClient.getFeatureByType(featureType, 1, null); - NodeList features = doc.getElementsByTagNameNS(featureType.getNamespaceURI(), featureType.getLocalPart()); - Element originalFeature = (Element) features.item(0); - Element replacement = createReplacementFeature(originalFeature); - List replacements = Arrays.asList(replacement); - WFSMessage.addReplaceStatements(this.reqEntity, replacements); - Response rsp = wfsClient.submitRequest(this.reqEntity, binding); - this.rspEntity = rsp.readEntity(Document.class); - String xpath = String.format("//wfs:totalReplaced = '%d'", replacements.size()); - ETSAssert.assertXPath(xpath, this.rspEntity, null); - originalFeatures.add(originalFeature); - // feature versioning may be enabled, so get fes:ResourceId/@rid - Element resourceId = (Element) this.rspEntity.getElementsByTagNameNS(FES2.NS, FES2.RESOURCE_ID).item(0); - Assert.assertNotNull(resourceId, ErrorMessage.format(ErrorMessageKeys.MISSING_INFOSET_ITEM, FES2.RESOURCE_ID)); - String gmlId = resourceId.getAttribute("rid"); - @SuppressWarnings("unchecked") - Map replProps = (Map) replacement - .getUserData(REPL_PROPS); - this.rspEntity = wfsClient.invokeStoredQuery(WFS2.QRY_GET_FEATURE_BY_ID, Collections.singletonMap("id", gmlId)); - Element feature = this.rspEntity.getDocumentElement(); - ETSAssert.assertQualifiedName(feature, featureType); - ETSAssert.assertSimpleProperties(feature, replProps, null); - } + /** + * Creates a new feature representation by cloning and modifying the original + * instance. The following standard GML properties are added (or replaced if already + * present): + * + *
              + *
            • gml:description ("Lorem ipsum dolor sit amet.")
            • + *
            • gml:identifier (randomly generated UUID value)
            • + *
            + * + *

            + * The resulting Element node will have associated user data with the key value + * {@link #REPL_PROPS}. The user data are represented by a + * {@literal Map} object containing a collection of + * property-value pairs, where each key is an element declaration denoting a property + * and the corresponding value is the replacement value. + *

            + * @param originalFeature The original feature instance (unmodified). + * @return A new Element node representing the modified feature. + */ + Element createReplacementFeature(Element originalFeature) { + Element replacement = (Element) originalFeature.cloneNode(true); + Map replProps = new HashMap<>(); + QName propName = new QName(Namespaces.GML, "identifier"); + Element identifier = XMLUtils.createElement(propName); + identifier.setAttribute("codeSpace", "http://cite.opengeospatial.org/"); + String idValue = UUID.randomUUID().toString(); + identifier.setTextContent(idValue); + WFSMessage.insertGMLProperty(replacement, identifier); + replProps.put(this.model.getElementDeclaration("identifier", Namespaces.GML), idValue); + propName = new QName(Namespaces.GML, "description"); + Element desc = XMLUtils.createElement(propName); + String description = "Lorem ipsum dolor sit amet."; + desc.setTextContent(description); + WFSMessage.insertGMLProperty(replacement, desc); + replProps.put(this.model.getElementDeclaration("description", Namespaces.GML), description); + replacement.setUserData(REPL_PROPS, replProps, null); + return replacement; + } - /** - * Creates a new feature representation by cloning and modifying the - * original instance. The following standard GML properties are added (or - * replaced if already present): - * - *
              - *
            • gml:description ("Lorem ipsum dolor sit amet.")
            • - *
            • gml:identifier (randomly generated UUID value)
            • - *
            - * - *

            - * The resulting Element node will have associated user data with the key - * value {@link #REPL_PROPS}. The user data are represented by a - * {@literal Map} object containing a - * collection of property-value pairs, where each key is an element - * declaration denoting a property and the corresponding value is the - * replacement value. - *

            - * - * @param originalFeature - * The original feature instance (unmodified). - * @return A new Element node representing the modified feature. - */ - Element createReplacementFeature(Element originalFeature) { - Element replacement = (Element) originalFeature.cloneNode(true); - Map replProps = new HashMap<>(); - QName propName = new QName(Namespaces.GML, "identifier"); - Element identifier = XMLUtils.createElement(propName); - identifier.setAttribute("codeSpace", "http://cite.opengeospatial.org/"); - String idValue = UUID.randomUUID().toString(); - identifier.setTextContent(idValue); - WFSMessage.insertGMLProperty(replacement, identifier); - replProps.put(this.model.getElementDeclaration("identifier", Namespaces.GML), idValue); - propName = new QName(Namespaces.GML, "description"); - Element desc = XMLUtils.createElement(propName); - String description = "Lorem ipsum dolor sit amet."; - desc.setTextContent(description); - WFSMessage.insertGMLProperty(replacement, desc); - replProps.put(this.model.getElementDeclaration("description", Namespaces.GML), description); - replacement.setUserData(REPL_PROPS, replProps, null); - return replacement; - } } diff --git a/src/main/java/org/opengis/cite/iso19142/transaction/TransactionCapabilitiesTests.java b/src/main/java/org/opengis/cite/iso19142/transaction/TransactionCapabilitiesTests.java index 07019fd0..0183668e 100644 --- a/src/main/java/org/opengis/cite/iso19142/transaction/TransactionCapabilitiesTests.java +++ b/src/main/java/org/opengis/cite/iso19142/transaction/TransactionCapabilitiesTests.java @@ -23,74 +23,72 @@ import org.w3c.dom.Element; /** - * Tests the service response to a GetCapabilities request for "Transactional - * WFS" conformance. - * + * Tests the service response to a GetCapabilities request for "Transactional WFS" + * conformance. + * * @see "ISO 19142:2010, cl. 8: GetCapabilities operation" */ public class TransactionCapabilitiesTests extends BaseFixture { - static final String TRX_WFS_PHASE = "TransactionalWFSPhase"; - static final String SCHEMATRON_METADATA = "wfs-capabilities-2.0.sch"; + static final String TRX_WFS_PHASE = "TransactionalWFSPhase"; + static final String SCHEMATRON_METADATA = "wfs-capabilities-2.0.sch"; - @BeforeTest - public void checkSuitePreconditions(ITestContext context) { - Object failedPreconditions = context.getSuite().getAttribute(SuiteAttribute.FAILED_PRECONDITIONS.getName()); - if (null != failedPreconditions) { - throw new SkipException("One or more test suite preconditions were not satisfied: " + failedPreconditions); - } - } + @BeforeTest + public void checkSuitePreconditions(ITestContext context) { + Object failedPreconditions = context.getSuite().getAttribute(SuiteAttribute.FAILED_PRECONDITIONS.getName()); + if (null != failedPreconditions) { + throw new SkipException("One or more test suite preconditions were not satisfied: " + failedPreconditions); + } + } - /** - * Run the tests for the "Transactional WFS" conformance class only if the - * service constraint {@value org.opengis.cite.iso19142.WFS2#TRX_WFS} has - * the value 'TRUE'. Otherwise they will be skipped. - * - * @param testContext - * The test (set) context. - */ - @BeforeTest - public void implementsTransactionalWFS(ITestContext testContext) { - this.wfsMetadata = (Document) testContext.getSuite().getAttribute(SuiteAttribute.TEST_SUBJECT.getName()); - String xpath = String.format("//ows:Constraint[@name='%s']/ows:DefaultValue = 'TRUE'", WFS2.TRX_WFS); - ETSAssert.assertXPath(xpath, this.wfsMetadata, null); - } + /** + * Run the tests for the "Transactional WFS" conformance class only if the service + * constraint {@value org.opengis.cite.iso19142.WFS2#TRX_WFS} has the value 'TRUE'. + * Otherwise they will be skipped. + * @param testContext The test (set) context. + */ + @BeforeTest + public void implementsTransactionalWFS(ITestContext testContext) { + this.wfsMetadata = (Document) testContext.getSuite().getAttribute(SuiteAttribute.TEST_SUBJECT.getName()); + String xpath = String.format("//ows:Constraint[@name='%s']/ows:DefaultValue = 'TRUE'", WFS2.TRX_WFS); + ETSAssert.assertXPath(xpath, this.wfsMetadata, null); + } - /** - * Builds a DOM Document representing a GetCapabilities request for a - * complete service metadata document. - */ - @BeforeClass - public void buildGetCapabilitiesRequest() { - this.reqEntity = this.docBuilder.newDocument(); - Element docElem = this.reqEntity.createElementNS(Namespaces.WFS, WFS2.GET_CAPABILITIES); - docElem.setAttribute(WFS2.SERVICE_PARAM, WFS2.SERVICE_TYPE_CODE); - this.reqEntity.appendChild(docElem); - } + /** + * Builds a DOM Document representing a GetCapabilities request for a complete service + * metadata document. + */ + @BeforeClass + public void buildGetCapabilitiesRequest() { + this.reqEntity = this.docBuilder.newDocument(); + Element docElem = this.reqEntity.createElementNS(Namespaces.WFS, WFS2.GET_CAPABILITIES); + docElem.setAttribute(WFS2.SERVICE_PARAM, WFS2.SERVICE_TYPE_CODE); + this.reqEntity.appendChild(docElem); + } + + /** + * [{@code Test}] Verifies that the content of the complete service metadata document + * (wfs:WFS_Capabilities) satisfies the requirements for "Transactional WFS" + * conformance. Additional service endpoints and properties (constraints) must be + * implemented. The applicable rules are incorporated into the {@value #TRX_WFS_PHASE} + * phase of the Schematron schema {@code wfs-capabilities-2.0.sch}. + * + *

            + * Sources + *

            + *
              + *
            • ISO 19142:2010, Table 1: Conformance classes
            • + *
            • ISO 19142:2010, Table 13: Service constraints
            • + *
            • ISO 19142:2010, cl. A.1.3: Transactional WFS
            • + *
            + * + */ + @Test(description = "See ISO 19142: Table 13, A.2.23") + public void capabilitiesDescribesTransactionalWFS() { + SchematronValidator validator = ValidationUtils.buildSchematronValidator(SCHEMATRON_METADATA, TRX_WFS_PHASE); + Result result = validator.validate(new DOMSource(this.wfsMetadata, this.wfsMetadata.getDocumentURI()), false); + Assert.assertFalse(validator.ruleViolationsDetected(), ErrorMessage.format(ErrorMessageKeys.NOT_SCHEMA_VALID, + validator.getRuleViolationCount(), XMLUtils.resultToString(result))); + } - /** - * [{@code Test}] Verifies that the content of the complete service metadata - * document (wfs:WFS_Capabilities) satisfies the requirements for - * "Transactional WFS" conformance. Additional service endpoints and - * properties (constraints) must be implemented. The applicable rules are - * incorporated into the {@value #TRX_WFS_PHASE} phase of the Schematron - * schema {@code wfs-capabilities-2.0.sch}. - * - *

            - * Sources - *

            - *
              - *
            • ISO 19142:2010, Table 1: Conformance classes
            • - *
            • ISO 19142:2010, Table 13: Service constraints
            • - *
            • ISO 19142:2010, cl. A.1.3: Transactional WFS
            • - *
            - * - */ - @Test(description = "See ISO 19142: Table 13, A.2.23") - public void capabilitiesDescribesTransactionalWFS() { - SchematronValidator validator = ValidationUtils.buildSchematronValidator(SCHEMATRON_METADATA, TRX_WFS_PHASE); - Result result = validator.validate(new DOMSource(this.wfsMetadata, this.wfsMetadata.getDocumentURI()), false); - Assert.assertFalse(validator.ruleViolationsDetected(), ErrorMessage.format(ErrorMessageKeys.NOT_SCHEMA_VALID, - validator.getRuleViolationCount(), XMLUtils.resultToString(result))); - } } diff --git a/src/main/java/org/opengis/cite/iso19142/transaction/TransactionFixture.java b/src/main/java/org/opengis/cite/iso19142/transaction/TransactionFixture.java index 56d6ba97..fe682827 100644 --- a/src/main/java/org/opengis/cite/iso19142/transaction/TransactionFixture.java +++ b/src/main/java/org/opengis/cite/iso19142/transaction/TransactionFixture.java @@ -25,89 +25,82 @@ import org.w3c.dom.Document; /** - * Provides configuration methods that facilitate testing of transaction - * capabilities. + * Provides configuration methods that facilitate testing of transaction capabilities. */ public class TransactionFixture extends BaseFixture { - protected DataSampler dataSampler; - /** - * An XSModel object representing the application schema supported by the - * SUT. - */ - protected XSModel model; + protected DataSampler dataSampler; - public TransactionFixture() { - super(); - } + /** + * An XSModel object representing the application schema supported by the SUT. + */ + protected XSModel model; - /** - * This is intended only to facilitate unit testing. - * - * @param model - * A representation of an application schema. - */ - void setModel(XSModel model) { - this.model = model; - } + public TransactionFixture() { + super(); + } - /** - * Obtains a DataSampler object from the test run context (the value of the - * {@link SuiteAttribute#SAMPLER SuiteAttribute.SAMPLER attribute}). A - * schema model (XSModel) is also obtained from the test context by - * accessing the {@link org.opengis.cite.iso19136.SuiteAttribute#XSMODEL - * xsmodel} attribute. - * - * @param testContext - * The test run context. - */ - @BeforeClass(alwaysRun = true) - public void initTransactionFixture(ITestContext testContext) { - ISuite suite = testContext.getSuite(); - this.dataSampler = (DataSampler) suite.getAttribute(SuiteAttribute.SAMPLER.getName()); - this.model = (XSModel) suite.getAttribute(org.opengis.cite.iso19136.SuiteAttribute.XSMODEL.getName()); - } + /** + * This is intended only to facilitate unit testing. + * @param model A representation of an application schema. + */ + void setModel(XSModel model) { + this.model = model; + } - /** - * Builds a DOM Document representing a Transaction request entity. - */ - @BeforeMethod - public void buildTransactionRequest() { - this.reqEntity = WFSMessage.createRequestEntity(WFS2.TRANSACTION, this.wfsVersion); - } + /** + * Obtains a DataSampler object from the test run context (the value of the + * {@link SuiteAttribute#SAMPLER SuiteAttribute.SAMPLER attribute}). A schema model + * (XSModel) is also obtained from the test context by accessing the + * {@link org.opengis.cite.iso19136.SuiteAttribute#XSMODEL xsmodel} attribute. + * @param testContext The test run context. + */ + @BeforeClass(alwaysRun = true) + public void initTransactionFixture(ITestContext testContext) { + ISuite suite = testContext.getSuite(); + this.dataSampler = (DataSampler) suite.getAttribute(SuiteAttribute.SAMPLER.getName()); + this.model = (XSModel) suite.getAttribute(org.opengis.cite.iso19136.SuiteAttribute.XSMODEL.getName()); + } + + /** + * Builds a DOM Document representing a Transaction request entity. + */ + @BeforeMethod + public void buildTransactionRequest() { + this.reqEntity = WFSMessage.createRequestEntity(WFS2.TRANSACTION, this.wfsVersion); + } + + /** + * A DataProvider that supplies a collection of parameter tuples where each tuple has + * two elements: + *
              + *
            1. ProtocolBinding - a supported transaction request binding
            2. + *
            3. QName - the name of a feature type for which data are available
            4. + *
            + * @param testContext The ITestContext object for the test run. + * @return {@literal Iterator} An iterator over a collection of parameter + * tuples. + */ + @DataProvider(name = "binding+availFeatureType") + public Iterator trxTestParameters(ITestContext testContext) { + ISuite suite = testContext.getSuite(); + Document wfsMetadata = (Document) suite.getAttribute(SuiteAttribute.TEST_SUBJECT.getName()); + if (null == wfsMetadata) { + throw new NullPointerException("Service description not found in ITestContext"); + } + Set trxBindings = ServiceMetadataUtils.getOperationBindings(wfsMetadata, WFS2.TRANSACTION); + DataSampler sampler = (DataSampler) suite.getAttribute(SuiteAttribute.SAMPLER.getName()); + Map featureInfo = sampler.getFeatureTypeInfo(); + List paramList = new ArrayList(); + for (ProtocolBinding binding : trxBindings) { + for (FeatureTypeInfo typeInfo : featureInfo.values()) { + if (typeInfo.isInstantiated()) { + Object[] tuple = { binding, typeInfo.getTypeName() }; + paramList.add(tuple); + } + } + } + return paramList.iterator(); + } - /** - * A DataProvider that supplies a collection of parameter tuples where each - * tuple has two elements: - *
              - *
            1. ProtocolBinding - a supported transaction request binding
            2. - *
            3. QName - the name of a feature type for which data are available
            4. - *
            - * - * @param testContext - * The ITestContext object for the test run. - * @return {@literal Iterator} An iterator over a collection of - * parameter tuples. - */ - @DataProvider(name = "binding+availFeatureType") - public Iterator trxTestParameters(ITestContext testContext) { - ISuite suite = testContext.getSuite(); - Document wfsMetadata = (Document) suite.getAttribute(SuiteAttribute.TEST_SUBJECT.getName()); - if (null == wfsMetadata) { - throw new NullPointerException("Service description not found in ITestContext"); - } - Set trxBindings = ServiceMetadataUtils.getOperationBindings(wfsMetadata, WFS2.TRANSACTION); - DataSampler sampler = (DataSampler) suite.getAttribute(SuiteAttribute.SAMPLER.getName()); - Map featureInfo = sampler.getFeatureTypeInfo(); - List paramList = new ArrayList(); - for (ProtocolBinding binding : trxBindings) { - for (FeatureTypeInfo typeInfo : featureInfo.values()) { - if (typeInfo.isInstantiated()) { - Object[] tuple = { binding, typeInfo.getTypeName() }; - paramList.add(tuple); - } - } - } - return paramList.iterator(); - } } diff --git a/src/main/java/org/opengis/cite/iso19142/transaction/Update.java b/src/main/java/org/opengis/cite/iso19142/transaction/Update.java index 99454683..b46dd08c 100644 --- a/src/main/java/org/opengis/cite/iso19142/transaction/Update.java +++ b/src/main/java/org/opengis/cite/iso19142/transaction/Update.java @@ -47,249 +47,240 @@ import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response.Status; - /** - * Tests the response to a Transaction request that includes one or more update - * actions. If the WFS supports feature versioning, then the wfs:UpdateResults - * element must be present (to convey the new identifiers); however, this is not - * currently checked. - * + * Tests the response to a Transaction request that includes one or more update actions. + * If the WFS supports feature versioning, then the wfs:UpdateResults element must be + * present (to convey the new identifiers); however, this is not currently checked. + * * @see "ISO 19142:2010, cl. 15.2.5: Update action" */ public class Update extends TransactionFixture { - /** List containing original representations of modified features */ - private List modifiedFeatures = new ArrayList(); + /** List containing original representations of modified features */ + private List modifiedFeatures = new ArrayList(); + + /** + * Attempts to restore the WFS data store to its previous state by replacing all + * modified features with their original representations. If this does not succeed a + * log message (WARNING) is written to the test suite logger. + */ + @AfterClass + public void restoreModifiedFeatures() { + if (modifiedFeatures.isEmpty()) { + return; + } + Document req = WFSMessage.createRequestEntity(WFS2.TRANSACTION, this.wfsVersion); + WFSMessage.addReplaceStatements(req, modifiedFeatures); + Response rsp = wfsClient.submitRequest(req, ProtocolBinding.ANY); + Document rspEntity = rsp.readEntity(Document.class); + String expr = String.format("//wfs:totalReplaced = '%d'", modifiedFeatures.size()); + Boolean result; + try { + result = (Boolean) XMLUtils.evaluateXPath(rspEntity, expr, null, XPathConstants.BOOLEAN); + } + catch (XPathExpressionException xpe) { + throw new RuntimeException(xpe); + } + if (!result) { + String msg = String.format("%s: Failed to replace modified features.\n%s", getClass().getName(), + XMLUtils.writeNodeToString(rspEntity)); + TestSuiteLogger.log(Level.WARNING, msg); + } + } - /** - * Attempts to restore the WFS data store to its previous state by replacing - * all modified features with their original representations. If this does - * not succeed a log message (WARNING) is written to the test suite logger. - */ - @AfterClass - public void restoreModifiedFeatures() { - if (modifiedFeatures.isEmpty()) { - return; - } - Document req = WFSMessage.createRequestEntity(WFS2.TRANSACTION, this.wfsVersion); - WFSMessage.addReplaceStatements(req, modifiedFeatures); - Response rsp = wfsClient.submitRequest(req, ProtocolBinding.ANY); - Document rspEntity = rsp.readEntity(Document.class); - String expr = String.format("//wfs:totalReplaced = '%d'", modifiedFeatures.size()); - Boolean result; - try { - result = (Boolean) XMLUtils.evaluateXPath(rspEntity, expr, null, XPathConstants.BOOLEAN); - } catch (XPathExpressionException xpe) { - throw new RuntimeException(xpe); - } - if (!result) { - String msg = String.format("%s: Failed to replace modified features.\n%s", getClass().getName(), - XMLUtils.writeNodeToString(rspEntity)); - TestSuiteLogger.log(Level.WARNING, msg); - } - } + /** + * [{@code Test}] Submits a Transaction request to update the first name (gml:name[1]) + * of an existing feature instance. The test is run for all supported transaction + * bindings and available feature types. The response entity (wfs:TransactionResponse) + * must be schema-valid. + * + *

            + * Sources + *

            + *
              + *
            • ISO 19142:2010, cl. 15.3.3: TransactionSummary element
            • + *
            • ISO 19142:2010, cl. 15.3.5: UpdateResults element
            • + *
            + * @param binding A supported transaction request binding. + * @param featureType A QName representing the name of some feature type for which + * data are available. + */ + @Test(description = "See ISO 19142: 15.3.3, 15.3.5", dataProvider = "binding+availFeatureType") + public void updateGMLName(ProtocolBinding binding, QName featureType) { + Document doc = wfsClient.getFeatureByType(featureType, 1, null); + NodeList features = doc.getElementsByTagNameNS(featureType.getNamespaceURI(), featureType.getLocalPart()); + Element originalFeature = (Element) features.item(0); + String gmlId = originalFeature.getAttributeNS(Namespaces.GML, "id"); + Map properties = new HashMap(); + String newName = "Pellentesque Arcu Lorem"; + properties.put("gml:name[1]", newName); + this.rspEntity = wfsClient.updateFeature(this.reqEntity, gmlId, featureType, properties, binding); + if (this.rspEntity.getDocumentElement().getLocalName().equals(WFS2.TRANSACTION_RSP)) { + modifiedFeatures.add(originalFeature); + } + this.rspEntity = wfsClient.invokeStoredQuery(WFS2.QRY_GET_FEATURE_BY_ID, Collections.singletonMap("id", gmlId)); + Element feature = this.rspEntity.getDocumentElement(); + ETSAssert.assertQualifiedName(feature, featureType); + XSElementDeclaration gmlName = this.model.getElementDeclaration("name", Namespaces.GML); + ETSAssert.assertSimpleProperties(feature, Collections.singletonMap(gmlName, newName), null); + } - /** - * [{@code Test}] Submits a Transaction request to update the first name - * (gml:name[1]) of an existing feature instance. The test is run for all - * supported transaction bindings and available feature types. The response - * entity (wfs:TransactionResponse) must be schema-valid. - * - *

            - * Sources - *

            - *
              - *
            • ISO 19142:2010, cl. 15.3.3: TransactionSummary element
            • - *
            • ISO 19142:2010, cl. 15.3.5: UpdateResults element
            • - *
            - * - * @param binding - * A supported transaction request binding. - * @param featureType - * A QName representing the name of some feature type for which - * data are available. - */ - @Test(description = "See ISO 19142: 15.3.3, 15.3.5", dataProvider = "binding+availFeatureType") - public void updateGMLName(ProtocolBinding binding, QName featureType) { - Document doc = wfsClient.getFeatureByType(featureType, 1, null); - NodeList features = doc.getElementsByTagNameNS(featureType.getNamespaceURI(), featureType.getLocalPart()); - Element originalFeature = (Element) features.item(0); - String gmlId = originalFeature.getAttributeNS(Namespaces.GML, "id"); - Map properties = new HashMap(); - String newName = "Pellentesque Arcu Lorem"; - properties.put("gml:name[1]", newName); - this.rspEntity = wfsClient.updateFeature(this.reqEntity, gmlId, featureType, properties, binding); - if (this.rspEntity.getDocumentElement().getLocalName().equals(WFS2.TRANSACTION_RSP)) { - modifiedFeatures.add(originalFeature); - } - this.rspEntity = wfsClient.invokeStoredQuery(WFS2.QRY_GET_FEATURE_BY_ID, Collections.singletonMap("id", gmlId)); - Element feature = this.rspEntity.getDocumentElement(); - ETSAssert.assertQualifiedName(feature, featureType); - XSElementDeclaration gmlName = this.model.getElementDeclaration("name", Namespaces.GML); - ETSAssert.assertSimpleProperties(feature, Collections.singletonMap(gmlName, newName), null); - } + /** + * [{@code Test}] Submits a Transaction request to update a property (gml:boundedBy) + * with an invalid value (kml:Point). An ExceptionReport (with status code 400) + * containing the exception code {@code InvalidValue} is expected in response. + * + *

            + * Sources + *

            + *
              + *
            • ISO 19142:2010, 15.2.5.2.1: Property element
            • + *
            • ISO 19142:2010, Table 3: WFS exception codes
            • + *
            + * + */ + @Test(description = "See ISO 19142: 7.5, 15.2.5.2.1") + public void updateBoundedByWithKMLPoint() { + try { + this.reqEntity = docBuilder.parse(getClass().getResourceAsStream("UpdateInvalidFeatureProperty.xml")); + } + catch (Exception e) { + throw new RuntimeException("Failed to parse XML resource from classpath", e); + } + Element update = (Element) this.reqEntity.getElementsByTagNameNS(Namespaces.WFS, WFS2.UPDATE).item(0); + WFSMessage.setTypeName(update, featureTypes.get(0)); + ProtocolBinding binding = wfsClient.getAnyTransactionBinding(); + URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.TRANSACTION, binding); + Response rsp = wfsClient.submitRequest(new DOMSource(this.reqEntity), binding, endpoint); + this.rspEntity = rsp.readEntity(Document.class); + Assert.assertEquals(rsp.getStatus(), Status.BAD_REQUEST.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + String xpath = "//ows:Exception[@exceptionCode = 'InvalidValue']"; + ETSAssert.assertXPath(xpath, this.rspEntity.getDocumentElement(), null); + } - /** - * [{@code Test}] Submits a Transaction request to update a property - * (gml:boundedBy) with an invalid value (kml:Point). An ExceptionReport - * (with status code 400) containing the exception code {@code InvalidValue} - * is expected in response. - * - *

            - * Sources - *

            - *
              - *
            • ISO 19142:2010, 15.2.5.2.1: Property element
            • - *
            • ISO 19142:2010, Table 3: WFS exception codes
            • - *
            - * - */ - @Test(description = "See ISO 19142: 7.5, 15.2.5.2.1") - public void updateBoundedByWithKMLPoint() { - try { - this.reqEntity = docBuilder.parse(getClass().getResourceAsStream("UpdateInvalidFeatureProperty.xml")); - } catch (Exception e) { - throw new RuntimeException("Failed to parse XML resource from classpath", e); - } - Element update = (Element) this.reqEntity.getElementsByTagNameNS(Namespaces.WFS, WFS2.UPDATE).item(0); - WFSMessage.setTypeName(update, featureTypes.get(0)); - ProtocolBinding binding = wfsClient.getAnyTransactionBinding(); - URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.TRANSACTION, binding); - Response rsp = wfsClient.submitRequest(new DOMSource(this.reqEntity), binding, endpoint); - this.rspEntity = rsp.readEntity(Document.class); - Assert.assertEquals(rsp.getStatus(), Status.BAD_REQUEST.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - String xpath = "//ows:Exception[@exceptionCode = 'InvalidValue']"; - ETSAssert.assertXPath(xpath, this.rspEntity.getDocumentElement(), null); - } + /** + * [{@code Test}] Submits a request to update a simple property value, one that is + * based on a built-in XML Schema datatype (including enumerated types). The test is + * run for all supported transaction bindings and available feature types. + * + *

            + * Sources + *

            + *
              + *
            • ISO 19142:2010, cl. 15.3.3: TransactionSummary element
            • + *
            • ISO 19142:2010, cl. 15.3.5: UpdateResults element
            • + *
            + * @param binding A supported message binding. + * @param featureType A QName representing the name of some feature type for which + * data are available. + */ + @Test(description = "See ISO 19142: 15.3.3, 15.3.5", dataProvider = "binding+availFeatureType") + public void updateSimplePropertyValue(ProtocolBinding binding, QName featureType) { + List simpleProps = AppSchemaUtils.getSimpleFeatureProperties(this.model, featureType); + // position iterator at end of list so previous() returns last item + ListIterator propItr = simpleProps.listIterator(simpleProps.size()); + XSElementDeclaration prop = propItr.previous(); + Set identifiers = this.dataSampler.selectRandomFeatureIdentifiers(featureType, 1); + String featureId = identifiers.iterator().next(); + QName propName = new QName(prop.getNamespace(), prop.getName(), "tns"); + List propValues = this.dataSampler.getSimplePropertyValues(featureType, propName, featureId); + String newVal = newPropertyValue(prop, propValues); + WFSMessage.addNamespaceBinding(this.reqEntity, propName); + Map properties = new HashMap(); + properties.put(propName.getPrefix() + ":" + propName.getLocalPart() + "[1]", newVal); + this.rspEntity = wfsClient.updateFeature(this.reqEntity, featureId, featureType, properties, binding); + if (this.rspEntity.getDocumentElement().getLocalName().equals(WFS2.TRANSACTION_RSP)) { + modifiedFeatures.add(this.dataSampler.getFeatureById(featureId)); + } + this.rspEntity = wfsClient.invokeStoredQuery(WFS2.QRY_GET_FEATURE_BY_ID, + Collections.singletonMap("id", featureId)); + Element feature = this.rspEntity.getDocumentElement(); + ETSAssert.assertQualifiedName(feature, featureType); + ETSAssert.assertSimpleProperties(feature, Collections.singletonMap(prop, newVal), + Collections.singletonMap(propName.getNamespaceURI(), propName.getPrefix())); + } - /** - * [{@code Test}] Submits a request to update a simple property value, one - * that is based on a built-in XML Schema datatype (including enumerated - * types). The test is run for all supported transaction bindings and - * available feature types. - * - *

            - * Sources - *

            - *
              - *
            • ISO 19142:2010, cl. 15.3.3: TransactionSummary element
            • - *
            • ISO 19142:2010, cl. 15.3.5: UpdateResults element
            • - *
            - * - * @param binding - * A supported message binding. - * @param featureType - * A QName representing the name of some feature type for which - * data are available. - */ - @Test(description = "See ISO 19142: 15.3.3, 15.3.5", dataProvider = "binding+availFeatureType") - public void updateSimplePropertyValue(ProtocolBinding binding, QName featureType) { - List simpleProps = AppSchemaUtils.getSimpleFeatureProperties(this.model, featureType); - // position iterator at end of list so previous() returns last item - ListIterator propItr = simpleProps.listIterator(simpleProps.size()); - XSElementDeclaration prop = propItr.previous(); - Set identifiers = this.dataSampler.selectRandomFeatureIdentifiers(featureType, 1); - String featureId = identifiers.iterator().next(); - QName propName = new QName(prop.getNamespace(), prop.getName(), "tns"); - List propValues = this.dataSampler.getSimplePropertyValues(featureType, propName, featureId); - String newVal = newPropertyValue(prop, propValues); - WFSMessage.addNamespaceBinding(this.reqEntity, propName); - Map properties = new HashMap(); - properties.put(propName.getPrefix() + ":" + propName.getLocalPart() + "[1]", newVal); - this.rspEntity = wfsClient.updateFeature(this.reqEntity, featureId, featureType, properties, binding); - if (this.rspEntity.getDocumentElement().getLocalName().equals(WFS2.TRANSACTION_RSP)) { - modifiedFeatures.add(this.dataSampler.getFeatureById(featureId)); - } - this.rspEntity = wfsClient.invokeStoredQuery(WFS2.QRY_GET_FEATURE_BY_ID, - Collections.singletonMap("id", featureId)); - Element feature = this.rspEntity.getDocumentElement(); - ETSAssert.assertQualifiedName(feature, featureType); - ETSAssert.assertSimpleProperties(feature, Collections.singletonMap(prop, newVal), - Collections.singletonMap(propName.getNamespaceURI(), propName.getPrefix())); - } + /** + * Returns a new property value that conforms to the applicable simple type + * definition. If the type contains an enumeration facet, the new value will be one + * that does not occur in the original feature instance. + * + *

            + * The following XML Schema data types are recognized: + *

            + *
              + *
            • xsd:string
            • + *
            • xsd:dateTime
            • + *
            • xsd:date
            • + *
            • xsd:float
            • + *
            • xsd:double
            • + *
            • xsd:decimal
            • + *
            • xsd:integer
            • + *
            • xsd:anyURI
            • + *
            • xsd:boolean
            • + *
            + * @param prop A property (element) declaration. + * @param propValues A List of known property values (may be empty). + * @return A String representing the new property value. + */ + String newPropertyValue(XSElementDeclaration prop, List propValues) { + XSSimpleTypeDefinition propType; + if (prop.getTypeDefinition().getTypeCategory() == XSTypeDefinition.SIMPLE_TYPE) { + propType = (XSSimpleTypeDefinition) prop.getTypeDefinition(); + } + else { + XSComplexTypeDefinition complexType = (XSComplexTypeDefinition) prop.getTypeDefinition(); + propType = complexType.getSimpleType(); + } + // empty StringList if not an enumerated data type + StringList enums = propType.getLexicalEnumeration(); + for (int i = 0; i < enums.getLength(); i++) { + if (!propValues.contains(enums.item(i))) { + return enums.item(i); + } + } + String newValue = null; + switch (propType.getBuiltInKind()) { + case XSConstants.STRING_DT: + newValue = "TEST_VALUE"; + break; + case XSConstants.DATETIME_DT: + ZonedDateTime utcNow = ZonedDateTime.now(ZoneId.of("Z")); + newValue = utcNow.format(DateTimeFormatter.ISO_INSTANT); + break; + case XSConstants.DATE_DT: + LocalDate localDate = LocalDate.now(ZoneId.of("Z")); + newValue = localDate.toString(); + break; + case XSConstants.FLOAT_DT: + Float fVal = Float.valueOf(propValues.get(0)); + newValue = Float.toString(fVal / 2); + break; + case XSConstants.DOUBLE_DT: + Double dVal = Double.valueOf(propValues.get(0)); + newValue = Double.toString(dVal / 2); + break; + case XSConstants.DECIMAL_DT: + BigDecimal decimalVal = new BigDecimal(propValues.get(0)); + newValue = decimalVal.movePointLeft(1).toString(); + break; + case XSConstants.INTEGER_DT: + int intVal = Integer.parseInt(propValues.get(0)); + newValue = Integer.toString(intVal + 1); + break; + case XSConstants.ANYURI_DT: + newValue = "http://example.org/test"; + break; + case XSConstants.BOOLEAN_DT: + String boolVal = propValues.get(0); + boolean oldValue = (boolVal.equals("true") || boolVal.equals("1")) ? true : false; + newValue = Boolean.valueOf(!oldValue).toString(); + break; + default: + newValue = "UNSUPPORTED_DATATYPE"; + } + return newValue; + } - /** - * Returns a new property value that conforms to the applicable simple type - * definition. If the type contains an enumeration facet, the new value will - * be one that does not occur in the original feature instance. - * - *

            - * The following XML Schema data types are recognized: - *

            - *
              - *
            • xsd:string
            • - *
            • xsd:dateTime
            • - *
            • xsd:date
            • - *
            • xsd:float
            • - *
            • xsd:double
            • - *
            • xsd:decimal
            • - *
            • xsd:integer
            • - *
            • xsd:anyURI
            • - *
            • xsd:boolean
            • - *
            - * - * @param prop - * A property (element) declaration. - * @param propValues - * A List of known property values (may be empty). - * @return A String representing the new property value. - */ - String newPropertyValue(XSElementDeclaration prop, List propValues) { - XSSimpleTypeDefinition propType; - if (prop.getTypeDefinition().getTypeCategory() == XSTypeDefinition.SIMPLE_TYPE) { - propType = (XSSimpleTypeDefinition) prop.getTypeDefinition(); - } else { - XSComplexTypeDefinition complexType = (XSComplexTypeDefinition) prop.getTypeDefinition(); - propType = complexType.getSimpleType(); - } - // empty StringList if not an enumerated data type - StringList enums = propType.getLexicalEnumeration(); - for (int i = 0; i < enums.getLength(); i++) { - if (!propValues.contains(enums.item(i))) { - return enums.item(i); - } - } - String newValue = null; - switch (propType.getBuiltInKind()) { - case XSConstants.STRING_DT: - newValue = "TEST_VALUE"; - break; - case XSConstants.DATETIME_DT: - ZonedDateTime utcNow = ZonedDateTime.now(ZoneId.of("Z")); - newValue = utcNow.format(DateTimeFormatter.ISO_INSTANT); - break; - case XSConstants.DATE_DT: - LocalDate localDate = LocalDate.now(ZoneId.of("Z")); - newValue = localDate.toString(); - break; - case XSConstants.FLOAT_DT: - Float fVal = Float.valueOf(propValues.get(0)); - newValue = Float.toString(fVal / 2); - break; - case XSConstants.DOUBLE_DT: - Double dVal = Double.valueOf(propValues.get(0)); - newValue = Double.toString(dVal / 2); - break; - case XSConstants.DECIMAL_DT: - BigDecimal decimalVal = new BigDecimal(propValues.get(0)); - newValue = decimalVal.movePointLeft(1).toString(); - break; - case XSConstants.INTEGER_DT: - int intVal = Integer.parseInt(propValues.get(0)); - newValue = Integer.toString(intVal + 1); - break; - case XSConstants.ANYURI_DT: - newValue = "http://example.org/test"; - break; - case XSConstants.BOOLEAN_DT: - String boolVal = propValues.get(0); - boolean oldValue = (boolVal.equals("true") || boolVal.equals("1")) ? true : false; - newValue = Boolean.valueOf(!oldValue).toString(); - break; - default: - newValue = "UNSUPPORTED_DATATYPE"; - } - return newValue; - } } diff --git a/src/main/java/org/opengis/cite/iso19142/transaction/package-info.java b/src/main/java/org/opengis/cite/iso19142/transaction/package-info.java index 596d9710..7350cd08 100644 --- a/src/main/java/org/opengis/cite/iso19142/transaction/package-info.java +++ b/src/main/java/org/opengis/cite/iso19142/transaction/package-info.java @@ -1,13 +1,15 @@ /** - * This package contains test classes that assess behavior of the SUT with - * respect to the "Transactional WFS" conformance level. It builds upon the - * "Basic WFS" conformance level and adds the following service requests: + * This package contains test classes that assess behavior of the SUT with respect to the + * "Transactional WFS" conformance level. It builds upon the "Basic WFS" conformance level + * and adds the following service requests: * *
              *
            • Transaction
            • *
            * - *

            Sources

            + *

            + * Sources + *

            *
              *
            • ISO ISO 19142:2010, cl. 2, Table 1
            • *
            • ISO ISO 19142:2010, cl. A.1.3: Transactional WFS
            • diff --git a/src/main/java/org/opengis/cite/iso19142/util/AppSchemaUtils.java b/src/main/java/org/opengis/cite/iso19142/util/AppSchemaUtils.java index aaaaa525..0536e1a7 100644 --- a/src/main/java/org/opengis/cite/iso19142/util/AppSchemaUtils.java +++ b/src/main/java/org/opengis/cite/iso19142/util/AppSchemaUtils.java @@ -21,324 +21,299 @@ import org.opengis.cite.iso19142.Namespaces; /** - * Utility methods for accessing and analyzing components of GML application - * schemas. - * + * Utility methods for accessing and analyzing components of GML application schemas. + * */ public class AppSchemaUtils { - /** - * Produces a list of feature properties where the property value has a type - * derived from the given (simple or complex) type definition. - * - * @param model - * An XSModel object representing an application schema. - * @param featureTypeName - * A qualified feature type name. - * @param typeDef - * A (simple or complex) type definition that characterizes the - * property value domain. - * @return A {@literal List} containing matching - * feature properties; the list may be empty. - */ - public static List getFeaturePropertiesByType(XSModel model, QName featureTypeName, - XSTypeDefinition typeDef) { - XSElementDeclaration elemDecl = model.getElementDeclaration(featureTypeName.getLocalPart(), - featureTypeName.getNamespaceURI()); - XSComplexTypeDefinition featureTypeDef = (XSComplexTypeDefinition) elemDecl.getTypeDefinition(); - List featureProps = XMLSchemaModelUtils - .getAllElementsInParticle(featureTypeDef.getParticle()); - removeDeprecatedGMLElements(featureProps, model); - List props = new ArrayList(); - // set bit mask to indicate acceptable derivation mechanisms - short extendOrRestrict = XSConstants.DERIVATION_EXTENSION | XSConstants.DERIVATION_RESTRICTION; - for (XSElementDeclaration featureProp : featureProps) { - XSTypeDefinition propType = featureProp.getTypeDefinition(); - switch (propType.getTypeCategory()) { - case XSTypeDefinition.SIMPLE_TYPE: - if ((typeDef.getTypeCategory() == XSTypeDefinition.SIMPLE_TYPE) - && propType.derivedFromType(typeDef, extendOrRestrict)) { - props.add(featureProp); - } - break; - case XSTypeDefinition.COMPLEX_TYPE: - if (typeDef.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE) { - // check type of child element(s) - XSComplexTypeDefinition complexPropType = (XSComplexTypeDefinition) propType; - List propValues = XMLSchemaModelUtils - .getAllElementsInParticle(complexPropType.getParticle()); - for (XSElementDeclaration propValue : propValues) { - if (propValue.getTypeDefinition().derivedFromType(typeDef, extendOrRestrict)) { - props.add(featureProp); - } - } - } else { - // complex type may derive from simple type - if (propType.derivedFromType(typeDef, extendOrRestrict)) { - props.add(featureProp); - } - } - break; - } - } - if (TestSuiteLogger.isLoggable(Level.FINER)) { - TestSuiteLogger.log(Level.FINER, - new StringBuilder("In feature type defn ").append(featureTypeDef.getName()) - .append(", found properties with value of type ").append(typeDef.getName()).append("\n") - .append(props).toString()); - } - return props; - } + /** + * Produces a list of feature properties where the property value has a type derived + * from the given (simple or complex) type definition. + * @param model An XSModel object representing an application schema. + * @param featureTypeName A qualified feature type name. + * @param typeDef A (simple or complex) type definition that characterizes the + * property value domain. + * @return A {@literal List} containing matching feature + * properties; the list may be empty. + */ + public static List getFeaturePropertiesByType(XSModel model, QName featureTypeName, + XSTypeDefinition typeDef) { + XSElementDeclaration elemDecl = model.getElementDeclaration(featureTypeName.getLocalPart(), + featureTypeName.getNamespaceURI()); + XSComplexTypeDefinition featureTypeDef = (XSComplexTypeDefinition) elemDecl.getTypeDefinition(); + List featureProps = XMLSchemaModelUtils + .getAllElementsInParticle(featureTypeDef.getParticle()); + removeDeprecatedGMLElements(featureProps, model); + List props = new ArrayList(); + // set bit mask to indicate acceptable derivation mechanisms + short extendOrRestrict = XSConstants.DERIVATION_EXTENSION | XSConstants.DERIVATION_RESTRICTION; + for (XSElementDeclaration featureProp : featureProps) { + XSTypeDefinition propType = featureProp.getTypeDefinition(); + switch (propType.getTypeCategory()) { + case XSTypeDefinition.SIMPLE_TYPE: + if ((typeDef.getTypeCategory() == XSTypeDefinition.SIMPLE_TYPE) + && propType.derivedFromType(typeDef, extendOrRestrict)) { + props.add(featureProp); + } + break; + case XSTypeDefinition.COMPLEX_TYPE: + if (typeDef.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE) { + // check type of child element(s) + XSComplexTypeDefinition complexPropType = (XSComplexTypeDefinition) propType; + List propValues = XMLSchemaModelUtils + .getAllElementsInParticle(complexPropType.getParticle()); + for (XSElementDeclaration propValue : propValues) { + if (propValue.getTypeDefinition().derivedFromType(typeDef, extendOrRestrict)) { + props.add(featureProp); + } + } + } + else { + // complex type may derive from simple type + if (propType.derivedFromType(typeDef, extendOrRestrict)) { + props.add(featureProp); + } + } + break; + } + } + if (TestSuiteLogger.isLoggable(Level.FINER)) { + TestSuiteLogger.log(Level.FINER, + new StringBuilder("In feature type defn ").append(featureTypeDef.getName()) + .append(", found properties with value of type ") + .append(typeDef.getName()) + .append("\n") + .append(props) + .toString()); + } + return props; + } - /** - * Produces a list of nillable properties for the specified feature type. - * The absence of such a property value is explicitly indicated by setting - * the attribute xsi:nil="true". - * - * @param model - * An XSModel object representing an application schema. - * @param featureTypeName - * A qualified feature type name. - * @return A {@literal List} containing elements that - * may have nil values. - */ - public static List getNillableProperties(XSModel model, QName featureTypeName) { - List featureProps = getAllFeatureProperties(model, featureTypeName); - Iterator itr = featureProps.iterator(); - while (itr.hasNext()) { - XSElementDeclaration prop = itr.next(); - if (!prop.getNillable()) { - itr.remove(); - } - } - return featureProps; - } + /** + * Produces a list of nillable properties for the specified feature type. The absence + * of such a property value is explicitly indicated by setting the attribute + * xsi:nil="true". + * @param model An XSModel object representing an application schema. + * @param featureTypeName A qualified feature type name. + * @return A {@literal List} containing elements that may have + * nil values. + */ + public static List getNillableProperties(XSModel model, QName featureTypeName) { + List featureProps = getAllFeatureProperties(model, featureTypeName); + Iterator itr = featureProps.iterator(); + while (itr.hasNext()) { + XSElementDeclaration prop = itr.next(); + if (!prop.getNillable()) { + itr.remove(); + } + } + return featureProps; + } - /** - * Produces a list of all properties for a specified feature type. - * - * @param model - * An XSModel object representing an application schema. - * @param featureTypeName - * A qualified feature type name. - * @return A {@literal List} containing one or more - * element declarations defining feature properties. - */ - public static List getAllFeatureProperties(XSModel model, QName featureTypeName) { - XSElementDeclaration elemDecl = model.getElementDeclaration(featureTypeName.getLocalPart(), - featureTypeName.getNamespaceURI()); - XSComplexTypeDefinition featureTypeDef = (XSComplexTypeDefinition) elemDecl.getTypeDefinition(); - return XMLSchemaModelUtils.getAllElementsInParticle(featureTypeDef.getParticle()); - } + /** + * Produces a list of all properties for a specified feature type. + * @param model An XSModel object representing an application schema. + * @param featureTypeName A qualified feature type name. + * @return A {@literal List} containing one or more element + * declarations defining feature properties. + */ + public static List getAllFeatureProperties(XSModel model, QName featureTypeName) { + XSElementDeclaration elemDecl = model.getElementDeclaration(featureTypeName.getLocalPart(), + featureTypeName.getNamespaceURI()); + XSComplexTypeDefinition featureTypeDef = (XSComplexTypeDefinition) elemDecl.getTypeDefinition(); + return XMLSchemaModelUtils.getAllElementsInParticle(featureTypeDef.getParticle()); + } - /** - * Produces a list of properties for a specified feature type that have - * either (a) a simple type definition or (b) a complex type definition with - * a simple content model. The standard GML properties are ignored. - * - * @param model - * An XSModel object representing an application schema. - * @param featureTypeName - * A qualified feature type name. - * @return A {@literal List} containing one or more - * element declarations defining properties with a simple content - * model. - */ - public static List getSimpleFeatureProperties(XSModel model, QName featureTypeName) { - XSElementDeclaration elemDecl = model.getElementDeclaration(featureTypeName.getLocalPart(), - featureTypeName.getNamespaceURI()); - XSComplexTypeDefinition featureTypeDef = (XSComplexTypeDefinition) elemDecl.getTypeDefinition(); - List props = XMLSchemaModelUtils.getAllElementsInParticle(featureTypeDef.getParticle()); - Iterator propsItr = props.iterator(); - while (propsItr.hasNext()) { - XSElementDeclaration prop = propsItr.next(); - if (prop.getNamespace().equals(Namespaces.GML)) { - propsItr.remove(); - continue; - } - if (prop.getTypeDefinition().getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE) { - XSComplexTypeDefinition typeDef = (XSComplexTypeDefinition) prop.getTypeDefinition(); - if (null == typeDef.getSimpleType()) { - propsItr.remove(); - } - } - } - return props; - } + /** + * Produces a list of properties for a specified feature type that have either (a) a + * simple type definition or (b) a complex type definition with a simple content + * model. The standard GML properties are ignored. + * @param model An XSModel object representing an application schema. + * @param featureTypeName A qualified feature type name. + * @return A {@literal List} containing one or more element + * declarations defining properties with a simple content model. + */ + public static List getSimpleFeatureProperties(XSModel model, QName featureTypeName) { + XSElementDeclaration elemDecl = model.getElementDeclaration(featureTypeName.getLocalPart(), + featureTypeName.getNamespaceURI()); + XSComplexTypeDefinition featureTypeDef = (XSComplexTypeDefinition) elemDecl.getTypeDefinition(); + List props = XMLSchemaModelUtils.getAllElementsInParticle(featureTypeDef.getParticle()); + Iterator propsItr = props.iterator(); + while (propsItr.hasNext()) { + XSElementDeclaration prop = propsItr.next(); + if (prop.getNamespace().equals(Namespaces.GML)) { + propsItr.remove(); + continue; + } + if (prop.getTypeDefinition().getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE) { + XSComplexTypeDefinition typeDef = (XSComplexTypeDefinition) prop.getTypeDefinition(); + if (null == typeDef.getSimpleType()) { + propsItr.remove(); + } + } + } + return props; + } - /** - * Produces a list of all required properties for a given feature type. - * - * @param model - * An XSModel object representing an application schema. - * @param featureTypeName - * A qualified feature type name. - * @return A {@literal List} defining zero or more - * elements which must occur in a valid instance. - */ - public static List getRequiredProperties(XSModel model, QName featureTypeName) { - XSElementDeclaration elemDecl = model.getElementDeclaration(featureTypeName.getLocalPart(), - featureTypeName.getNamespaceURI()); - XSComplexTypeDefinition typeDef = (XSComplexTypeDefinition) elemDecl.getTypeDefinition(); - List particles = XMLSchemaModelUtils.getAllElementParticles(typeDef.getParticle()); - List requiredElems = new ArrayList(); - for (XSParticle particle : particles) { - if (particle.getMinOccurs() > 0) { - XSElementDeclaration elem = (XSElementDeclaration) particle.getTerm(); - requiredElems.add(elem); - } - } - return requiredElems; - } + /** + * Produces a list of all required properties for a given feature type. + * @param model An XSModel object representing an application schema. + * @param featureTypeName A qualified feature type name. + * @return A {@literal List} defining zero or more elements + * which must occur in a valid instance. + */ + public static List getRequiredProperties(XSModel model, QName featureTypeName) { + XSElementDeclaration elemDecl = model.getElementDeclaration(featureTypeName.getLocalPart(), + featureTypeName.getNamespaceURI()); + XSComplexTypeDefinition typeDef = (XSComplexTypeDefinition) elemDecl.getTypeDefinition(); + List particles = XMLSchemaModelUtils.getAllElementParticles(typeDef.getParticle()); + List requiredElems = new ArrayList(); + for (XSParticle particle : particles) { + if (particle.getMinOccurs() > 0) { + XSElementDeclaration elem = (XSElementDeclaration) particle.getTerm(); + requiredElems.add(elem); + } + } + return requiredElems; + } - /** - * Removes deprecated GML feature properties from a given list of element - * declarations. The currently deprecated elements are listed below. - * - *
                - *
              • gml:metaDataProperty
              • - *
              • gml:location
              • - *
              - * - * @param elemDecls - * A List of XSElementDeclaration objects. - * @param model - * An XSModel object representing a GML application schema. - */ - public static void removeDeprecatedGMLElements(List elemDecls, XSModel model) { - XSElementDeclaration elemDecl = model.getElementDeclaration("location", Namespaces.GML); - elemDecls.remove(elemDecl); - elemDecl = model.getElementDeclaration("metaDataProperty", Namespaces.GML); - elemDecls.remove(elemDecl); - } + /** + * Removes deprecated GML feature properties from a given list of element + * declarations. The currently deprecated elements are listed below. + * + *
                + *
              • gml:metaDataProperty
              • + *
              • gml:location
              • + *
              + * @param elemDecls A List of XSElementDeclaration objects. + * @param model An XSModel object representing a GML application schema. + */ + public static void removeDeprecatedGMLElements(List elemDecls, XSModel model) { + XSElementDeclaration elemDecl = model.getElementDeclaration("location", Namespaces.GML); + elemDecls.remove(elemDecl); + elemDecl = model.getElementDeclaration("metaDataProperty", Namespaces.GML); + elemDecls.remove(elemDecl); + } - /** - * Determines the built-in XML Schema datatype from which the given element - * declaration is derived. The corresponding type definition may be either a - * simple type or a complex type with a simple content model. - * - * @param propDecl - * An element declaration (simple content). - * @return A QName specifying an XML Schema datatype. - */ - public static QName getBuiltInDatatype(XSElementDeclaration propDecl) { - XSTypeDefinition typeDef = propDecl.getTypeDefinition(); - int builtInType; - if (typeDef.getTypeCategory() == XSTypeDefinition.SIMPLE_TYPE) { - XSSimpleTypeDefinition simpleType = (XSSimpleTypeDefinition) typeDef; - builtInType = simpleType.getBuiltInKind(); - } else { - XSComplexTypeDefinition complexType = (XSComplexTypeDefinition) typeDef; - builtInType = complexType.getSimpleType().getBuiltInKind(); - } - QName datatype = null; - switch (builtInType) { - case XSConstants.DOUBLE_DT: - datatype = new QName(XMLConstants.W3C_XML_SCHEMA_NS_URI, "double"); - break; - case XSConstants.FLOAT_DT: - datatype = new QName(XMLConstants.W3C_XML_SCHEMA_NS_URI, "float"); - break; - case XSConstants.DECIMAL_DT: - datatype = new QName(XMLConstants.W3C_XML_SCHEMA_NS_URI, "decimal"); - break; - case XSConstants.INTEGER_DT: - case XSConstants.BYTE_DT: - case XSConstants.UNSIGNEDBYTE_DT: - case XSConstants.INT_DT: - case XSConstants.UNSIGNEDINT_DT: - case XSConstants.LONG_DT: - case XSConstants.UNSIGNEDLONG_DT: - case XSConstants.NEGATIVEINTEGER_DT: - case XSConstants.POSITIVEINTEGER_DT: - case XSConstants.NONNEGATIVEINTEGER_DT: - case XSConstants.NONPOSITIVEINTEGER_DT: - case XSConstants.SHORT_DT: - case XSConstants.UNSIGNEDSHORT_DT: - datatype = new QName(XMLConstants.W3C_XML_SCHEMA_NS_URI, "integer"); - break; - case XSConstants.STRING_DT: - datatype = new QName(XMLConstants.W3C_XML_SCHEMA_NS_URI, "string"); - break; - case XSConstants.DATETIME_DT: - datatype = new QName(XMLConstants.W3C_XML_SCHEMA_NS_URI, "dateTime"); - break; - case XSConstants.DATE_DT: - datatype = new QName(XMLConstants.W3C_XML_SCHEMA_NS_URI, "date"); - break; - case XSConstants.BOOLEAN_DT: - datatype = new QName(XMLConstants.W3C_XML_SCHEMA_NS_URI, "boolean"); - break; - default: - datatype = new QName(XMLConstants.W3C_XML_SCHEMA_NS_URI, "string"); - } - return datatype; - } + /** + * Determines the built-in XML Schema datatype from which the given element + * declaration is derived. The corresponding type definition may be either a simple + * type or a complex type with a simple content model. + * @param propDecl An element declaration (simple content). + * @return A QName specifying an XML Schema datatype. + */ + public static QName getBuiltInDatatype(XSElementDeclaration propDecl) { + XSTypeDefinition typeDef = propDecl.getTypeDefinition(); + int builtInType; + if (typeDef.getTypeCategory() == XSTypeDefinition.SIMPLE_TYPE) { + XSSimpleTypeDefinition simpleType = (XSSimpleTypeDefinition) typeDef; + builtInType = simpleType.getBuiltInKind(); + } + else { + XSComplexTypeDefinition complexType = (XSComplexTypeDefinition) typeDef; + builtInType = complexType.getSimpleType().getBuiltInKind(); + } + QName datatype = null; + switch (builtInType) { + case XSConstants.DOUBLE_DT: + datatype = new QName(XMLConstants.W3C_XML_SCHEMA_NS_URI, "double"); + break; + case XSConstants.FLOAT_DT: + datatype = new QName(XMLConstants.W3C_XML_SCHEMA_NS_URI, "float"); + break; + case XSConstants.DECIMAL_DT: + datatype = new QName(XMLConstants.W3C_XML_SCHEMA_NS_URI, "decimal"); + break; + case XSConstants.INTEGER_DT: + case XSConstants.BYTE_DT: + case XSConstants.UNSIGNEDBYTE_DT: + case XSConstants.INT_DT: + case XSConstants.UNSIGNEDINT_DT: + case XSConstants.LONG_DT: + case XSConstants.UNSIGNEDLONG_DT: + case XSConstants.NEGATIVEINTEGER_DT: + case XSConstants.POSITIVEINTEGER_DT: + case XSConstants.NONNEGATIVEINTEGER_DT: + case XSConstants.NONPOSITIVEINTEGER_DT: + case XSConstants.SHORT_DT: + case XSConstants.UNSIGNEDSHORT_DT: + datatype = new QName(XMLConstants.W3C_XML_SCHEMA_NS_URI, "integer"); + break; + case XSConstants.STRING_DT: + datatype = new QName(XMLConstants.W3C_XML_SCHEMA_NS_URI, "string"); + break; + case XSConstants.DATETIME_DT: + datatype = new QName(XMLConstants.W3C_XML_SCHEMA_NS_URI, "dateTime"); + break; + case XSConstants.DATE_DT: + datatype = new QName(XMLConstants.W3C_XML_SCHEMA_NS_URI, "date"); + break; + case XSConstants.BOOLEAN_DT: + datatype = new QName(XMLConstants.W3C_XML_SCHEMA_NS_URI, "boolean"); + break; + default: + datatype = new QName(XMLConstants.W3C_XML_SCHEMA_NS_URI, "string"); + } + return datatype; + } - /** - * Returns a set of primitive, non-recurring temporal data type definitions, - * including: - * - *
                - *
              • xsd:dateTime ("yyyy-MM-dd'T'HH:mm:ssZ")
              • - *
              • xsd:date ("yyyy-MM-ddZ")
              • - *
              • xsd:gYearMonth ("yyyy-MM")
              • - *
              • xsd:gYear ("yyyy")
              • - *
              - * - * @param model - * An XSModel object representing an application schema. - * @return A Set of simple type definitions corresponding to temporal data - * types. - */ - public static Set getSimpleTemporalDataTypes(XSModel model) { - Set dataTypes = new HashSet(); - dataTypes.add(model.getTypeDefinition("dateTime", XMLConstants.W3C_XML_SCHEMA_NS_URI)); - dataTypes.add(model.getTypeDefinition("date", XMLConstants.W3C_XML_SCHEMA_NS_URI)); - dataTypes.add(model.getTypeDefinition("gYearMonth", XMLConstants.W3C_XML_SCHEMA_NS_URI)); - dataTypes.add(model.getTypeDefinition("gYear", XMLConstants.W3C_XML_SCHEMA_NS_URI)); - return dataTypes; - } + /** + * Returns a set of primitive, non-recurring temporal data type definitions, + * including: + * + *
                + *
              • xsd:dateTime ("yyyy-MM-dd'T'HH:mm:ssZ")
              • + *
              • xsd:date ("yyyy-MM-ddZ")
              • + *
              • xsd:gYearMonth ("yyyy-MM")
              • + *
              • xsd:gYear ("yyyy")
              • + *
              + * @param model An XSModel object representing an application schema. + * @return A Set of simple type definitions corresponding to temporal data types. + */ + public static Set getSimpleTemporalDataTypes(XSModel model) { + Set dataTypes = new HashSet(); + dataTypes.add(model.getTypeDefinition("dateTime", XMLConstants.W3C_XML_SCHEMA_NS_URI)); + dataTypes.add(model.getTypeDefinition("date", XMLConstants.W3C_XML_SCHEMA_NS_URI)); + dataTypes.add(model.getTypeDefinition("gYearMonth", XMLConstants.W3C_XML_SCHEMA_NS_URI)); + dataTypes.add(model.getTypeDefinition("gYear", XMLConstants.W3C_XML_SCHEMA_NS_URI)); + return dataTypes; + } - /** - * Returns the expected value of the given (complex) property declaration. - * - * @param propertyDecl - * An element declaration for some feature property. - * @return An element declaration corresponding to the expected child - * element, or null if the property has a simple content model. - */ - public static XSElementDeclaration getComplexPropertyValue(XSElementDeclaration propertyDecl) { - XSTypeDefinition typeDef = propertyDecl.getTypeDefinition(); - XSElementDeclaration value = null; - if (typeDef.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE) { - XSComplexTypeDefinition complexTypeDef = (XSComplexTypeDefinition) typeDef; - List values = XMLSchemaModelUtils - .getAllElementsInParticle(complexTypeDef.getParticle()); - // should be only one in accord with GML object-property model - value = values.get(0); - } - return value; - } + /** + * Returns the expected value of the given (complex) property declaration. + * @param propertyDecl An element declaration for some feature property. + * @return An element declaration corresponding to the expected child element, or null + * if the property has a simple content model. + */ + public static XSElementDeclaration getComplexPropertyValue(XSElementDeclaration propertyDecl) { + XSTypeDefinition typeDef = propertyDecl.getTypeDefinition(); + XSElementDeclaration value = null; + if (typeDef.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE) { + XSComplexTypeDefinition complexTypeDef = (XSComplexTypeDefinition) typeDef; + List values = XMLSchemaModelUtils + .getAllElementsInParticle(complexTypeDef.getParticle()); + // should be only one in accord with GML object-property model + value = values.get(0); + } + return value; + } + + /** + * Finds all simple and complex temporal properties defined for a particular feature + * type. + * @param model A representation of a GML application schema. + * @param featureType The qualified name of a feature type. + * @return A list of element declarations corresponding to properties that have + * temporal values; it may be empty. + */ + public static List getTemporalFeatureProperties(XSModel model, QName featureType) { + List tmProps = getFeaturePropertiesByType(model, featureType, + model.getTypeDefinition("AbstractTimeGeometricPrimitiveType", Namespaces.GML)); + // also look for simple temporal types + for (XSTypeDefinition dataType : getSimpleTemporalDataTypes(model)) { + tmProps.addAll(AppSchemaUtils.getFeaturePropertiesByType(model, featureType, dataType)); + } + return tmProps; + } - /** - * Finds all simple and complex temporal properties defined for a particular - * feature type. - * - * @param model - * A representation of a GML application schema. - * @param featureType - * The qualified name of a feature type. - * @return A list of element declarations corresponding to properties that - * have temporal values; it may be empty. - */ - public static List getTemporalFeatureProperties(XSModel model, QName featureType) { - List tmProps = getFeaturePropertiesByType(model, featureType, - model.getTypeDefinition("AbstractTimeGeometricPrimitiveType", Namespaces.GML)); - // also look for simple temporal types - for (XSTypeDefinition dataType : getSimpleTemporalDataTypes(model)) { - tmProps.addAll(AppSchemaUtils.getFeaturePropertiesByType(model, featureType, dataType)); - } - return tmProps; - } } diff --git a/src/main/java/org/opengis/cite/iso19142/util/DataSampler.java b/src/main/java/org/opengis/cite/iso19142/util/DataSampler.java index f5a657d3..0373a29d 100644 --- a/src/main/java/org/opengis/cite/iso19142/util/DataSampler.java +++ b/src/main/java/org/opengis/cite/iso19142/util/DataSampler.java @@ -63,590 +63,580 @@ import net.sf.saxon.s9api.XdmValue; /** - * Obtains samples of the feature data available from the WFS under test. - * Instances of all feature types advertised in the service description are - * requested, but data need not exist for every type. + * Obtains samples of the feature data available from the WFS under test. Instances of all + * feature types advertised in the service description are requested, but data need not + * exist for every type. */ public class DataSampler { - private static final Logger LOGR = Logger.getLogger(DataSampler.class.getPackage().getName()); - public static final QName BOUNDED_BY = new QName( GML, "boundedBy" ); - private int maxFeatures = 25; - private Document serviceDescription; - private Map featureInfo; - private Map spatialExtents; - private Map temporalPropertyExtents; - private final Map> nillableProperties = new HashMap<>(); - private DocumentBuilder documentBuilder; - - /** - * Constructs a new DataSampler for a particular WFS implementation. - * - * @param wfsCapabilities - * A DOM Document representing the service metadata - * (/wfs:WFS_Capabilities). - */ - public DataSampler(Document wfsCapabilities) { - if (null == wfsCapabilities - || !wfsCapabilities.getDocumentElement().getLocalName().equals(WFS2.WFS_CAPABILITIES)) { - throw new IllegalArgumentException("Did not supply a WFS capabilities document"); - } - this.serviceDescription = wfsCapabilities; - this.featureInfo = ServiceMetadataUtils.extractFeatureTypeInfo(wfsCapabilities); - if (this.featureInfo.isEmpty()) { - throw new RuntimeException("No feature type info available."); - } - this.spatialExtents = new HashMap<>(); - this.temporalPropertyExtents = new HashMap<>(); - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - - try { - documentBuilder = dbf.newDocumentBuilder(); - } catch (ParserConfigurationException e) { - throw new RuntimeException(e.getMessage()); - } - - LOGR.config("Created DataSampler - GetCapabilities (GET) endpoint is " + ServiceMetadataUtils - .getOperationEndpoint(wfsCapabilities, WFS2.GET_CAPABILITIES, ProtocolBinding.GET)); - } - - /** - * Sets the maximum number of features to include in the response entity. - * - * @param maxFeatures - * An integer value > 0 (the default value is 25). - */ - public void setMaxFeatures(int maxFeatures) { - if (maxFeatures > 0) { - this.maxFeatures = maxFeatures; - } - } - - /** - * Returns a set of identifiers for available feature instances of a given - * type. The identifiers are randomly selected from the sample data. - * - * @param featureType - * A QName representing the qualified name of some feature type. - * @param numId - * The desired number of identifiers. - * @return A Set containing zero or more feature identifiers. - */ - public Set selectRandomFeatureIdentifiers(QName featureType, int numId) { - FeatureTypeInfo typeInfo = featureInfo.get(featureType); - File dataFile = typeInfo.getSampleData(); - Set idSet = new HashSet(); - if (null == dataFile || !dataFile.exists()) { - return idSet; - } - String xpath = "//wfs:member/*/@gml:id"; - Map nsBindings = new HashMap(); - nsBindings.put(Namespaces.GML, "gml"); - nsBindings.put(Namespaces.WFS, "wfs"); - XdmValue result = null; - try { - result = XMLUtils.evaluateXPath2(new StreamSource(dataFile), xpath, nsBindings); - } catch (SaxonApiException e) { - LOGR.log(Level.WARNING, String.format("Failed to extract feature identifiers from data file at %s", - dataFile.getAbsolutePath())); - } - int sampleSize = result.size(); - numId = (numId > sampleSize) ? sampleSize : numId; - Set randomSet = new HashSet<>(); - Random random = new Random(); - while (randomSet.size() < numId) { - int randomInt = random.nextInt(sampleSize); - randomSet.add(randomInt); - } - for (int randomInt : randomSet) { - String featureIdentifier = result.itemAt(randomInt).getStringValue(); - if (idSet.contains(featureIdentifier)) - throw new IllegalArgumentException("Feature id " + featureIdentifier + " exists multiple times in Feature Type " + featureType.toString() + ". This is not allowed according to GML 3.2.1 specification (OGC 07-036; chapter 7.2.4.5)."); - idSet.add(featureIdentifier); - } - return idSet; - } - - /** - * Returns a list containing the values (in document order) of the specified - * feature property in the sample data set. The property value is converted - * to a string as if the XPath string() function were applied. - * - * @param featureType - * A QName representing the qualified name of some feature type. - * @param propName - * The name of the property. - * @param featureId - * A feature identifer (gml:id); if {@code null} or empty the - * evaluation context includes all members of the collection. - * @return A List containing simple property values; the list is empty if no - * values are found. - */ - public List getSimplePropertyValues(QName featureType, QName propName, String featureId) { - FeatureTypeInfo typeInfo = featureInfo.get(featureType); - File dataFile = typeInfo.getSampleData(); - List values = new ArrayList(); - if (null == dataFile || !dataFile.exists()) { - return values; - } - Map nsBindings = new HashMap(); - nsBindings.put(Namespaces.WFS, "wfs"); - nsBindings.put(Namespaces.GML, "gml"); - nsBindings.put(Namespaces.XSI, "xsi"); - nsBindings.put(featureType.getNamespaceURI(), "ns1"); - StringBuilder xpath = new StringBuilder("//wfs:member/ns1:"); - xpath.append(featureType.getLocalPart()); - if (null != featureId && !featureId.isEmpty()) { - xpath.append("[@gml:id='").append(featureId).append("']"); - } - if (!propName.getNamespaceURI().equals(featureType.getNamespaceURI())) { - xpath.append("/ns2:"); - nsBindings.put(propName.getNamespaceURI(), "ns2"); - } else { - xpath.append("/ns1:"); - } - xpath.append(propName.getLocalPart()); - // ignore nil property values - xpath.append("[not(@xsi:nil)]"); - XdmValue result = null; - try { - result = XMLUtils.evaluateXPath2(new StreamSource(dataFile), xpath.toString(), nsBindings); - } catch (SaxonApiException e) { - LOGR.log(Level.WARNING, String.format("Failed to evaluate XPath expression %s against data at %s\n%s\n", - xpath, dataFile.getAbsolutePath(), nsBindings) + e.getMessage()); - } - if (null != result) { - for (XdmItem item : result) { - values.add(item.getStringValue()); - } - } - if (LOGR.isLoggable(Level.FINE)) { - LOGR.log(Level.FINE, "[{0}] Evaluating xpath {1}\n {2}", - new Object[] { this.getClass().getName(), xpath, values }); - } - return values; - } - - /** - * Deletes all saved data files. - * - * @return {@code true} if all data files were deleted; {@code false} - * otherwise (see warnings in log file for details). - */ - public boolean deleteData() { - boolean allDeleted = true; - for (QName typeName : featureInfo.keySet()) { - File file = featureInfo.get(typeName).getSampleData(); - if ((file != null) && file.exists()) { - if (!file.delete()) { - allDeleted = false; - LOGR.log(Level.WARNING, "Failed to delete sample data file at " + file); - } - } - } - return allDeleted; - } - - /** - * Attempts to acquire instances of all feature types supported by the WFS - * using all supported GetFeature message bindings (request encodings). The - * feature representations are saved in a temporary file. If no data exist - * for a given feature type, {@link FeatureTypeInfo#isInstantiated()} - * returns {@code false}. - */ - public void acquireFeatureData() { - WFSClient wfsClient = new WFSClient(this.serviceDescription); - Set getFeatureBindings = ServiceMetadataUtils.getOperationBindings(serviceDescription, - WFS2.GET_FEATURE); - if(getFeatureBindings.isEmpty()) - throw new IllegalArgumentException( "No bindings available for GetFeature request." ); - for (Map.Entry entry : featureInfo.entrySet()) { - QName typeName = entry.getKey(); - FeatureTypeInfo featureTypeInfo = entry.getValue(); - acquireFeatureData( wfsClient, getFeatureBindings, typeName, featureTypeInfo ); - } - LOGR.log(Level.INFO, featureInfo.toString()); - } - - /** - * Returns a Map containing information about the feature types supported by - * the WFS. - * - * @return A Map where the keys are QName objects representing the names of - * feature types advertised in the capabilities document. - */ - public Map getFeatureTypeInfo() { - return featureInfo; - } - - /** - * Returns a feature instance from the sample data. - * - * @param id - * The feature identifier (@gml:id). - * @return An Element representing a feature instance, or null if no - * matching feature is found. - */ - public Element getFeatureById(String id) { - XPath xpath = XPathFactory.newInstance().newXPath(); - xpath.setNamespaceContext(NamespaceBindings.withStandardBindings()); - String expr = String.format("//wfs:member/*[@gml:id='%s']", id); - Element feature = null; - for (FeatureTypeInfo featureInfo : this.featureInfo.values()) { - if (!featureInfo.isInstantiated()) - continue; - File dataFile = featureInfo.getSampleData(); - NodeList result = null; - try { - result = (NodeList) xpath.evaluate(expr, new InputSource(new FileInputStream(dataFile)), - XPathConstants.NODESET); - } catch (XPathExpressionException | FileNotFoundException e) { - LOGR.log(Level.WARNING, String.format("Failed to evaluate XPath %s against data file at %s", expr, - dataFile.getAbsolutePath())); - } - if (result.getLength() > 0) { - feature = (Element) result.item(0); - break; - } - } - return feature; - } - - /** - * Returns the identifier (gml:id attribute value) for an existing feature instance. - * - * @param featureType - * The qualified name of a the feature type the feature should not be an instance from. - * @return A feature identifier, or null if one cannot be found. - */ - public String getFeatureIdNotOfType( QName featureType ) { - return getFeatureId( featureTypeInfo -> featureTypeInfo.getTypeName().equals( featureType ) ); - } - - /** - * Returns the identifier (gml:id attribute value) for an existing feature instance. - * - * @return A feature identifier, or null if one cannot be found. - */ - public String getFeatureId() { - return getFeatureId( featureTypeInfo -> false ); - } - - private String getFeatureId( Function skip ) { - String expr = "(//wfs:member/*/@gml:id)[1]"; - Map nsBindings = new HashMap<>(); - nsBindings.put( Namespaces.GML, "gml" ); - nsBindings.put( Namespaces.WFS, "wfs" ); - for ( FeatureTypeInfo featureTypeInfo : featureInfo.values() ) { - if ( !skip.apply( featureTypeInfo ) && featureTypeInfo.isInstantiated() ) { - File dataFile = featureTypeInfo.getSampleData(); - try { - XdmValue result = XMLUtils.evaluateXPath2( new StreamSource( dataFile ), expr, nsBindings ); - if ( result.size() > 0 ) { - String featureId = result.itemAt( 0 ).getStringValue(); - if ( featureId != null && !featureId.isEmpty() ) - return featureId; - } - } catch ( SaxonApiException e ) { - LOGR.log( Level.WARNING, - String.format( "Failed to evaluate XPath %s against data file at %s", expr, - dataFile.getAbsolutePath() ) ); - } - } - } - return null; - } - - /** - * Determines the spatial extent of the feature instances in the sample - * data. If a feature type defines more than one geometry property, the - * envelope is calculated using the first non-empty property. - * - * @param model - * A model representing the supported GML application schema. - * @param featureType - * The name of the feature type. - * @return An Envelope, or null if one cannot be created or the feature type - * has no geometry properties defined. - */ - public Envelope getSpatialExtent(XSModel model, QName featureType) { - Envelope envelope = this.spatialExtents.get(featureType); - if (null != envelope) { - return envelope; - } - List geomProps = AppSchemaUtils.getFeaturePropertiesByType(model, featureType, - model.getTypeDefinition("AbstractGeometryType", Namespaces.GML)); - if (geomProps.isEmpty()) { - return null; - } - Iterator itr = geomProps.iterator(); - NamespaceBindings nsBindings = NamespaceBindings.withStandardBindings(); - XPathFactory factory = XPathFactory.newInstance(); - NodeList geomNodes = null; - File dataFile = this.featureInfo.get(featureType).getSampleData(); - do { - XSElementDeclaration geomProp = itr.next(); - nsBindings.addNamespaceBinding(geomProp.getNamespace(), "ns1"); - String expr = String.format("//ns1:%s/*[1]", geomProp.getName()); - XPath xpath = factory.newXPath(); - xpath.setNamespaceContext(nsBindings); - try { - geomNodes = (NodeList) xpath.evaluate(expr, new InputSource(new FileInputStream(dataFile)), - XPathConstants.NODESET); - } catch (XPathExpressionException | FileNotFoundException e) { - LOGR.log(Level.WARNING, String.format("Failed to evaluate XPath %s against data file at %s.\n %s", expr, - dataFile.getAbsolutePath(), e.getMessage())); - } - if (null != geomNodes && geomNodes.getLength() > 0) { - break; - } - } while (itr.hasNext()); - if (null != geomNodes && geomNodes.getLength() > 0) { - try { - envelope = Extents.calculateEnvelopeUsingSingleGeometry(geomNodes); - } catch (JAXBException e) { - LOGR.log(Level.WARNING, - String.format("Failed to create envelope from geometry nodes.", e.getMessage())); - } - } - this.spatialExtents.put(featureType, envelope); - return envelope; - } - - /** - * Determines the temporal extent of all instances of the specified feature - * property in the sample data. The temporal extent is extend by 1 day and 1 hour in the beginning and the end. - * - * @param model - * A model representing the relevant GML application schema. - * @param featureType - * The name of the feature type. - * @param tmPropDecl - * A declaration of a temporal property. - * @return A Period, or null if the property does not occur or has no - * values. - */ - public Period getTemporalExtentOfProperty(XSModel model, QName featureType, XSElementDeclaration tmPropDecl) { - FeatureProperty tmProp; - try { - tmProp = new FeatureProperty( featureType, tmPropDecl ); - } catch ( Exception e ) { - throw new IllegalArgumentException( "Property " + tmPropDecl + " is not suitable as temporal property.", e ); - } - Period period = this.temporalPropertyExtents.get(tmProp); - if (null != period) { - return period; - } - File dataFile = this.featureInfo.get(featureType).getSampleData(); - Document data; - try { - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - factory.setNamespaceAware(true); - data = factory.newDocumentBuilder().parse(dataFile); - } catch (SAXException | IOException | ParserConfigurationException e) { - throw new RuntimeException( - String.format("Failed to parse data file at %s.\n %s", dataFile.getAbsolutePath(), e.getMessage())); - } - TreeSet tmSet = new TreeSet<>(new TemporalComparator()); - NodeList propNodes = data.getElementsByTagNameNS(tmPropDecl.getNamespace(), tmPropDecl.getName()); - for (int i = 0; i < propNodes.getLength(); i++) { - TemporalGeometricPrimitive tVal; - XSTypeDefinition propType = tmPropDecl.getTypeDefinition(); - Element propElem = (Element) propNodes.item(i); - try { - if ( propType.getTypeCategory() == XSTypeDefinition.SIMPLE_TYPE - || ( (XSComplexTypeDefinition) propType ).getContentType() == XSComplexTypeDefinition.CONTENTTYPE_SIMPLE ) { - tVal = TemporalQuery.parseTemporalValue( propElem.getTextContent(), propType ); - } else { - Element propValue = (Element) propElem.getElementsByTagName("*").item(0); - tVal = GmlUtils.gmlToTemporalGeometricPrimitive(propValue); - } - tmSet.add(tVal); - } catch (RuntimeException re) { - LOGR.log(Level.WARNING, re.getMessage()); - if(re instanceof SkipException) { - String message = re.getMessage(); - message = String.format(message, featureType.toString()); - throw new SkipException(message); - } - continue; - } - } - if ( period != null ) { - TemporalUtils.add( period.getEnding(), 2, ChronoUnit.DAYS ); - TemporalUtils.add( period.getBeginning(), -2, ChronoUnit.DAYS ); - } - period = TemporalUtils.temporalExtent(tmSet); - this.temporalPropertyExtents.put(tmProp, period); - return period; - } - - /** - * Determines a property which is nillable and contains nilled properties for the specified feature type in the - * sample data. - * - * @param model - * A model representing the relevant GML application schema, never null. - * @param featureType - * The name of the feature type, never null. - * @return A list of property names which are nillable and contains nilled instances, empty if such a property does - * not occur or has no values. - */ - public List getNillableProperties( XSModel model, QName featureType ) { - if ( nillableProperties.containsKey( featureType ) ) - return nillableProperties.get( featureType ); - List nillableProperties = new ArrayList<>(); - LOGR.fine( "Checking feature type for nillable properties: " + featureType ); - List nillableProps = AppSchemaUtils.getNillableProperties( model, featureType ); - LOGR.fine( nillableProps.toString() ); - FeatureTypeInfo typeInfo = getFeatureTypeInfo().get( featureType ); - - if ( typeInfo.isInstantiated() ) { - for ( XSElementDeclaration elementDeclaration : nillableProps ) { - QName propName = new QName( elementDeclaration.getNamespace(), elementDeclaration.getName() ); - // ignore nillable gml:boundedBy property - if ( !BOUNDED_BY.equals( propName ) && nillablePropertyContainsNilledProperties( typeInfo, propName ) ) { - nillableProperties.add( propName ); - } - - } - } - LOGR.fine( "Nillable properties:\n" + nillableProps ); - this.nillableProperties.put( featureType, nillableProperties ); - return nillableProperties; - } - - private boolean nillablePropertyContainsNilledProperties( FeatureTypeInfo typeInfo, QName propertyName ) { - LOGR.fine( "Checking property " + propertyName + " for nilled properties." ); - File dataFile = typeInfo.getSampleData(); - Document data; - try { - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - factory.setNamespaceAware( true ); - data = factory.newDocumentBuilder().parse( dataFile ); - } catch ( SAXException | IOException | ParserConfigurationException e ) { - throw new RuntimeException( String.format( "Failed to parse data file at %s.\n %s", - dataFile.getAbsolutePath(), e.getMessage() ) ); - } - NodeList propNodes = data.getElementsByTagNameNS( propertyName.getNamespaceURI(), propertyName.getLocalPart() ); - for ( int i = 0; i < propNodes.getLength(); i++ ) { - Element propElem = (Element) propNodes.item( i ); - String nilValue = propElem.getAttributeNS( Namespaces.XSI, "nil" ); - if ( "true".equals( nilValue ) ) - return true; - } - LOGR.fine( "Property " + propertyName + " does not have nilled properties." ); - return false; - } - - /** - * Randomly selects a feature instance from the sample data obtained from - * the IUT. - * - * @return An Element node representing a feature instance. - */ - public Element randomlySelectFeatureInstance() { - Element feature = null; - for (Entry entry : this.featureInfo.entrySet()) { - if (!entry.getValue().isInstantiated()) - continue; - Set idSet = selectRandomFeatureIdentifiers(entry.getKey(), 1); - feature = getFeatureById(idSet.iterator().next()); - } - return feature; - } - - /** - * Sort alphabetically and selects first feature type name for which - * instances are available in the SUT. - * - * @return A QName object denoting the name of a feature type, or - * {@code null} if no data exist in the SUT. - */ - public QName selectFeatureType() { - List availableTypes = new ArrayList(); - List featureName = new ArrayList(); - for (FeatureTypeInfo typeInfo : this.featureInfo.values()) { - if (typeInfo.isInstantiated()) { - availableTypes.add(typeInfo); - featureName.add(typeInfo.getTypeName().getLocalPart()); - } - } - if (availableTypes.isEmpty()) { - return null; - } - Collections.sort(featureName); - Optional availableType = availableTypes.stream() - .filter(x -> x.getTypeName().getLocalPart().equalsIgnoreCase(featureName.get(0))).findFirst(); - return availableType.get().getTypeName(); - } - - /** - * Evaluates the given XPath expression against all sample data sets. The - * first non-empty result is returned. - * - * @param expr - * An XPath 2.0 expression. - * @param nsBindings - * A collection of namespace bindings required to evaluate the - * XPath expression, where each entry maps a namespace URI (key) - * to a prefix (value). - * @return An XdmValue object containing a sequence of zero or more matching - * items. - */ - public XdmValue evaluateXPathAgainstSampleData(String expr, Map nsBindings) { - XdmValue results = null; - for (Entry entry : this.featureInfo.entrySet()) { - if (!entry.getValue().isInstantiated()) - continue; - File dataFile = entry.getValue().getSampleData(); - try { - results = XMLUtils.evaluateXPath2(new StreamSource(dataFile), expr, nsBindings); - if (results.size() > 0) { - break; - } - } catch (SaxonApiException e) { - LOGR.log(Level.WARNING, e.getMessage()); - } - } - return results; - } - - private void acquireFeatureData( WFSClient wfsClient, Set getFeatureBindings, QName typeName, - FeatureTypeInfo featureTypeInfo ) { - for (ProtocolBinding binding : getFeatureBindings) { - try { - Document rspEntity = wfsClient.getFeatureByType( typeName, maxFeatures, binding); - NodeList features = rspEntity.getElementsByTagNameNS( typeName.getNamespaceURI(), - typeName.getLocalPart()); - boolean hasFeatures = features.getLength() > 0; - if (hasFeatures) { - saveFeatureDataFile( featureTypeInfo, typeName, rspEntity ); - return; - } - } catch (RuntimeException re) { - StringBuilder err = new StringBuilder(); - err.append(String.format("Failed to read XML response entity using %s method for feature type %s.", - binding, typeName)); - err.append(" \n").append(re.getMessage()); - LOGR.log( Level.WARNING, err.toString(), re); - } - } - } - - private void saveFeatureDataFile( FeatureTypeInfo featureTypeInfo, QName typeName, Document rspEntity ) { - try { - File file = File.createTempFile( typeName.getLocalPart() + "-", ".xml"); - FileOutputStream fos = new FileOutputStream( file); - XMLUtils.writeNode( rspEntity, fos); - LOGR.log( Level.CONFIG, - this.getClass().getName() + " - wrote feature data to " + file.getAbsolutePath()); - featureTypeInfo.setSampleData(file); - fos.close(); - featureTypeInfo.setInstantiated(true); - } catch (Exception e) { - LOGR.log(Level.WARNING, "Failed to save feature data.", e); - } - } + private static final Logger LOGR = Logger.getLogger(DataSampler.class.getPackage().getName()); + + public static final QName BOUNDED_BY = new QName(GML, "boundedBy"); + + private int maxFeatures = 25; + + private Document serviceDescription; + + private Map featureInfo; + + private Map spatialExtents; + + private Map temporalPropertyExtents; + + private final Map> nillableProperties = new HashMap<>(); + + private DocumentBuilder documentBuilder; + + /** + * Constructs a new DataSampler for a particular WFS implementation. + * @param wfsCapabilities A DOM Document representing the service metadata + * (/wfs:WFS_Capabilities). + */ + public DataSampler(Document wfsCapabilities) { + if (null == wfsCapabilities + || !wfsCapabilities.getDocumentElement().getLocalName().equals(WFS2.WFS_CAPABILITIES)) { + throw new IllegalArgumentException("Did not supply a WFS capabilities document"); + } + this.serviceDescription = wfsCapabilities; + this.featureInfo = ServiceMetadataUtils.extractFeatureTypeInfo(wfsCapabilities); + if (this.featureInfo.isEmpty()) { + throw new RuntimeException("No feature type info available."); + } + this.spatialExtents = new HashMap<>(); + this.temporalPropertyExtents = new HashMap<>(); + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + + try { + documentBuilder = dbf.newDocumentBuilder(); + } + catch (ParserConfigurationException e) { + throw new RuntimeException(e.getMessage()); + } + + LOGR.config("Created DataSampler - GetCapabilities (GET) endpoint is " + ServiceMetadataUtils + .getOperationEndpoint(wfsCapabilities, WFS2.GET_CAPABILITIES, ProtocolBinding.GET)); + } + + /** + * Sets the maximum number of features to include in the response entity. + * @param maxFeatures An integer value > 0 (the default value is 25). + */ + public void setMaxFeatures(int maxFeatures) { + if (maxFeatures > 0) { + this.maxFeatures = maxFeatures; + } + } + + /** + * Returns a set of identifiers for available feature instances of a given type. The + * identifiers are randomly selected from the sample data. + * @param featureType A QName representing the qualified name of some feature type. + * @param numId The desired number of identifiers. + * @return A Set containing zero or more feature identifiers. + */ + public Set selectRandomFeatureIdentifiers(QName featureType, int numId) { + FeatureTypeInfo typeInfo = featureInfo.get(featureType); + File dataFile = typeInfo.getSampleData(); + Set idSet = new HashSet(); + if (null == dataFile || !dataFile.exists()) { + return idSet; + } + String xpath = "//wfs:member/*/@gml:id"; + Map nsBindings = new HashMap(); + nsBindings.put(Namespaces.GML, "gml"); + nsBindings.put(Namespaces.WFS, "wfs"); + XdmValue result = null; + try { + result = XMLUtils.evaluateXPath2(new StreamSource(dataFile), xpath, nsBindings); + } + catch (SaxonApiException e) { + LOGR.log(Level.WARNING, String.format("Failed to extract feature identifiers from data file at %s", + dataFile.getAbsolutePath())); + } + int sampleSize = result.size(); + numId = (numId > sampleSize) ? sampleSize : numId; + Set randomSet = new HashSet<>(); + Random random = new Random(); + while (randomSet.size() < numId) { + int randomInt = random.nextInt(sampleSize); + randomSet.add(randomInt); + } + for (int randomInt : randomSet) { + String featureIdentifier = result.itemAt(randomInt).getStringValue(); + if (idSet.contains(featureIdentifier)) + throw new IllegalArgumentException("Feature id " + featureIdentifier + + " exists multiple times in Feature Type " + featureType.toString() + + ". This is not allowed according to GML 3.2.1 specification (OGC 07-036; chapter 7.2.4.5)."); + idSet.add(featureIdentifier); + } + return idSet; + } + + /** + * Returns a list containing the values (in document order) of the specified feature + * property in the sample data set. The property value is converted to a string as if + * the XPath string() function were applied. + * @param featureType A QName representing the qualified name of some feature type. + * @param propName The name of the property. + * @param featureId A feature identifer (gml:id); if {@code null} or empty the + * evaluation context includes all members of the collection. + * @return A List containing simple property values; the list is empty if no values + * are found. + */ + public List getSimplePropertyValues(QName featureType, QName propName, String featureId) { + FeatureTypeInfo typeInfo = featureInfo.get(featureType); + File dataFile = typeInfo.getSampleData(); + List values = new ArrayList(); + if (null == dataFile || !dataFile.exists()) { + return values; + } + Map nsBindings = new HashMap(); + nsBindings.put(Namespaces.WFS, "wfs"); + nsBindings.put(Namespaces.GML, "gml"); + nsBindings.put(Namespaces.XSI, "xsi"); + nsBindings.put(featureType.getNamespaceURI(), "ns1"); + StringBuilder xpath = new StringBuilder("//wfs:member/ns1:"); + xpath.append(featureType.getLocalPart()); + if (null != featureId && !featureId.isEmpty()) { + xpath.append("[@gml:id='").append(featureId).append("']"); + } + if (!propName.getNamespaceURI().equals(featureType.getNamespaceURI())) { + xpath.append("/ns2:"); + nsBindings.put(propName.getNamespaceURI(), "ns2"); + } + else { + xpath.append("/ns1:"); + } + xpath.append(propName.getLocalPart()); + // ignore nil property values + xpath.append("[not(@xsi:nil)]"); + XdmValue result = null; + try { + result = XMLUtils.evaluateXPath2(new StreamSource(dataFile), xpath.toString(), nsBindings); + } + catch (SaxonApiException e) { + LOGR.log(Level.WARNING, String.format("Failed to evaluate XPath expression %s against data at %s\n%s\n", + xpath, dataFile.getAbsolutePath(), nsBindings) + e.getMessage()); + } + if (null != result) { + for (XdmItem item : result) { + values.add(item.getStringValue()); + } + } + if (LOGR.isLoggable(Level.FINE)) { + LOGR.log(Level.FINE, "[{0}] Evaluating xpath {1}\n {2}", + new Object[] { this.getClass().getName(), xpath, values }); + } + return values; + } + + /** + * Deletes all saved data files. + * @return {@code true} if all data files were deleted; {@code false} otherwise (see + * warnings in log file for details). + */ + public boolean deleteData() { + boolean allDeleted = true; + for (QName typeName : featureInfo.keySet()) { + File file = featureInfo.get(typeName).getSampleData(); + if ((file != null) && file.exists()) { + if (!file.delete()) { + allDeleted = false; + LOGR.log(Level.WARNING, "Failed to delete sample data file at " + file); + } + } + } + return allDeleted; + } + + /** + * Attempts to acquire instances of all feature types supported by the WFS using all + * supported GetFeature message bindings (request encodings). The feature + * representations are saved in a temporary file. If no data exist for a given feature + * type, {@link FeatureTypeInfo#isInstantiated()} returns {@code false}. + */ + public void acquireFeatureData() { + WFSClient wfsClient = new WFSClient(this.serviceDescription); + Set getFeatureBindings = ServiceMetadataUtils.getOperationBindings(serviceDescription, + WFS2.GET_FEATURE); + if (getFeatureBindings.isEmpty()) + throw new IllegalArgumentException("No bindings available for GetFeature request."); + for (Map.Entry entry : featureInfo.entrySet()) { + QName typeName = entry.getKey(); + FeatureTypeInfo featureTypeInfo = entry.getValue(); + acquireFeatureData(wfsClient, getFeatureBindings, typeName, featureTypeInfo); + } + LOGR.log(Level.INFO, featureInfo.toString()); + } + + /** + * Returns a Map containing information about the feature types supported by the WFS. + * @return A Map where the keys are QName objects representing the names of feature + * types advertised in the capabilities document. + */ + public Map getFeatureTypeInfo() { + return featureInfo; + } + + /** + * Returns a feature instance from the sample data. + * @param id The feature identifier (@gml:id). + * @return An Element representing a feature instance, or null if no matching feature + * is found. + */ + public Element getFeatureById(String id) { + XPath xpath = XPathFactory.newInstance().newXPath(); + xpath.setNamespaceContext(NamespaceBindings.withStandardBindings()); + String expr = String.format("//wfs:member/*[@gml:id='%s']", id); + Element feature = null; + for (FeatureTypeInfo featureInfo : this.featureInfo.values()) { + if (!featureInfo.isInstantiated()) + continue; + File dataFile = featureInfo.getSampleData(); + NodeList result = null; + try { + result = (NodeList) xpath.evaluate(expr, new InputSource(new FileInputStream(dataFile)), + XPathConstants.NODESET); + } + catch (XPathExpressionException | FileNotFoundException e) { + LOGR.log(Level.WARNING, String.format("Failed to evaluate XPath %s against data file at %s", expr, + dataFile.getAbsolutePath())); + } + if (result.getLength() > 0) { + feature = (Element) result.item(0); + break; + } + } + return feature; + } + + /** + * Returns the identifier (gml:id attribute value) for an existing feature instance. + * @param featureType The qualified name of a the feature type the feature should not + * be an instance from. + * @return A feature identifier, or null if one cannot be found. + */ + public String getFeatureIdNotOfType(QName featureType) { + return getFeatureId(featureTypeInfo -> featureTypeInfo.getTypeName().equals(featureType)); + } + + /** + * Returns the identifier (gml:id attribute value) for an existing feature instance. + * @return A feature identifier, or null if one cannot be found. + */ + public String getFeatureId() { + return getFeatureId(featureTypeInfo -> false); + } + + private String getFeatureId(Function skip) { + String expr = "(//wfs:member/*/@gml:id)[1]"; + Map nsBindings = new HashMap<>(); + nsBindings.put(Namespaces.GML, "gml"); + nsBindings.put(Namespaces.WFS, "wfs"); + for (FeatureTypeInfo featureTypeInfo : featureInfo.values()) { + if (!skip.apply(featureTypeInfo) && featureTypeInfo.isInstantiated()) { + File dataFile = featureTypeInfo.getSampleData(); + try { + XdmValue result = XMLUtils.evaluateXPath2(new StreamSource(dataFile), expr, nsBindings); + if (result.size() > 0) { + String featureId = result.itemAt(0).getStringValue(); + if (featureId != null && !featureId.isEmpty()) + return featureId; + } + } + catch (SaxonApiException e) { + LOGR.log(Level.WARNING, String.format("Failed to evaluate XPath %s against data file at %s", expr, + dataFile.getAbsolutePath())); + } + } + } + return null; + } + + /** + * Determines the spatial extent of the feature instances in the sample data. If a + * feature type defines more than one geometry property, the envelope is calculated + * using the first non-empty property. + * @param model A model representing the supported GML application schema. + * @param featureType The name of the feature type. + * @return An Envelope, or null if one cannot be created or the feature type has no + * geometry properties defined. + */ + public Envelope getSpatialExtent(XSModel model, QName featureType) { + Envelope envelope = this.spatialExtents.get(featureType); + if (null != envelope) { + return envelope; + } + List geomProps = AppSchemaUtils.getFeaturePropertiesByType(model, featureType, + model.getTypeDefinition("AbstractGeometryType", Namespaces.GML)); + if (geomProps.isEmpty()) { + return null; + } + Iterator itr = geomProps.iterator(); + NamespaceBindings nsBindings = NamespaceBindings.withStandardBindings(); + XPathFactory factory = XPathFactory.newInstance(); + NodeList geomNodes = null; + File dataFile = this.featureInfo.get(featureType).getSampleData(); + do { + XSElementDeclaration geomProp = itr.next(); + nsBindings.addNamespaceBinding(geomProp.getNamespace(), "ns1"); + String expr = String.format("//ns1:%s/*[1]", geomProp.getName()); + XPath xpath = factory.newXPath(); + xpath.setNamespaceContext(nsBindings); + try { + geomNodes = (NodeList) xpath.evaluate(expr, new InputSource(new FileInputStream(dataFile)), + XPathConstants.NODESET); + } + catch (XPathExpressionException | FileNotFoundException e) { + LOGR.log(Level.WARNING, String.format("Failed to evaluate XPath %s against data file at %s.\n %s", expr, + dataFile.getAbsolutePath(), e.getMessage())); + } + if (null != geomNodes && geomNodes.getLength() > 0) { + break; + } + } + while (itr.hasNext()); + if (null != geomNodes && geomNodes.getLength() > 0) { + try { + envelope = Extents.calculateEnvelopeUsingSingleGeometry(geomNodes); + } + catch (JAXBException e) { + LOGR.log(Level.WARNING, + String.format("Failed to create envelope from geometry nodes.", e.getMessage())); + } + } + this.spatialExtents.put(featureType, envelope); + return envelope; + } + + /** + * Determines the temporal extent of all instances of the specified feature property + * in the sample data. The temporal extent is extend by 1 day and 1 hour in the + * beginning and the end. + * @param model A model representing the relevant GML application schema. + * @param featureType The name of the feature type. + * @param tmPropDecl A declaration of a temporal property. + * @return A Period, or null if the property does not occur or has no values. + */ + public Period getTemporalExtentOfProperty(XSModel model, QName featureType, XSElementDeclaration tmPropDecl) { + FeatureProperty tmProp; + try { + tmProp = new FeatureProperty(featureType, tmPropDecl); + } + catch (Exception e) { + throw new IllegalArgumentException("Property " + tmPropDecl + " is not suitable as temporal property.", e); + } + Period period = this.temporalPropertyExtents.get(tmProp); + if (null != period) { + return period; + } + File dataFile = this.featureInfo.get(featureType).getSampleData(); + Document data; + try { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + data = factory.newDocumentBuilder().parse(dataFile); + } + catch (SAXException | IOException | ParserConfigurationException e) { + throw new RuntimeException( + String.format("Failed to parse data file at %s.\n %s", dataFile.getAbsolutePath(), e.getMessage())); + } + TreeSet tmSet = new TreeSet<>(new TemporalComparator()); + NodeList propNodes = data.getElementsByTagNameNS(tmPropDecl.getNamespace(), tmPropDecl.getName()); + for (int i = 0; i < propNodes.getLength(); i++) { + TemporalGeometricPrimitive tVal; + XSTypeDefinition propType = tmPropDecl.getTypeDefinition(); + Element propElem = (Element) propNodes.item(i); + try { + if (propType.getTypeCategory() == XSTypeDefinition.SIMPLE_TYPE || ((XSComplexTypeDefinition) propType) + .getContentType() == XSComplexTypeDefinition.CONTENTTYPE_SIMPLE) { + tVal = TemporalQuery.parseTemporalValue(propElem.getTextContent(), propType); + } + else { + Element propValue = (Element) propElem.getElementsByTagName("*").item(0); + tVal = GmlUtils.gmlToTemporalGeometricPrimitive(propValue); + } + tmSet.add(tVal); + } + catch (RuntimeException re) { + LOGR.log(Level.WARNING, re.getMessage()); + if (re instanceof SkipException) { + String message = re.getMessage(); + message = String.format(message, featureType.toString()); + throw new SkipException(message); + } + continue; + } + } + if (period != null) { + TemporalUtils.add(period.getEnding(), 2, ChronoUnit.DAYS); + TemporalUtils.add(period.getBeginning(), -2, ChronoUnit.DAYS); + } + period = TemporalUtils.temporalExtent(tmSet); + this.temporalPropertyExtents.put(tmProp, period); + return period; + } + + /** + * Determines a property which is nillable and contains nilled properties for the + * specified feature type in the sample data. + * @param model A model representing the relevant GML application schema, never + * null. + * @param featureType The name of the feature type, never null. + * @return A list of property names which are nillable and contains nilled instances, + * empty if such a property does not occur or has no values. + */ + public List getNillableProperties(XSModel model, QName featureType) { + if (nillableProperties.containsKey(featureType)) + return nillableProperties.get(featureType); + List nillableProperties = new ArrayList<>(); + LOGR.fine("Checking feature type for nillable properties: " + featureType); + List nillableProps = AppSchemaUtils.getNillableProperties(model, featureType); + LOGR.fine(nillableProps.toString()); + FeatureTypeInfo typeInfo = getFeatureTypeInfo().get(featureType); + + if (typeInfo.isInstantiated()) { + for (XSElementDeclaration elementDeclaration : nillableProps) { + QName propName = new QName(elementDeclaration.getNamespace(), elementDeclaration.getName()); + // ignore nillable gml:boundedBy property + if (!BOUNDED_BY.equals(propName) && nillablePropertyContainsNilledProperties(typeInfo, propName)) { + nillableProperties.add(propName); + } + + } + } + LOGR.fine("Nillable properties:\n" + nillableProps); + this.nillableProperties.put(featureType, nillableProperties); + return nillableProperties; + } + + private boolean nillablePropertyContainsNilledProperties(FeatureTypeInfo typeInfo, QName propertyName) { + LOGR.fine("Checking property " + propertyName + " for nilled properties."); + File dataFile = typeInfo.getSampleData(); + Document data; + try { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + data = factory.newDocumentBuilder().parse(dataFile); + } + catch (SAXException | IOException | ParserConfigurationException e) { + throw new RuntimeException( + String.format("Failed to parse data file at %s.\n %s", dataFile.getAbsolutePath(), e.getMessage())); + } + NodeList propNodes = data.getElementsByTagNameNS(propertyName.getNamespaceURI(), propertyName.getLocalPart()); + for (int i = 0; i < propNodes.getLength(); i++) { + Element propElem = (Element) propNodes.item(i); + String nilValue = propElem.getAttributeNS(Namespaces.XSI, "nil"); + if ("true".equals(nilValue)) + return true; + } + LOGR.fine("Property " + propertyName + " does not have nilled properties."); + return false; + } + + /** + * Randomly selects a feature instance from the sample data obtained from the IUT. + * @return An Element node representing a feature instance. + */ + public Element randomlySelectFeatureInstance() { + Element feature = null; + for (Entry entry : this.featureInfo.entrySet()) { + if (!entry.getValue().isInstantiated()) + continue; + Set idSet = selectRandomFeatureIdentifiers(entry.getKey(), 1); + feature = getFeatureById(idSet.iterator().next()); + } + return feature; + } + + /** + * Sort alphabetically and selects first feature type name for which instances are + * available in the SUT. + * @return A QName object denoting the name of a feature type, or {@code null} if no + * data exist in the SUT. + */ + public QName selectFeatureType() { + List availableTypes = new ArrayList(); + List featureName = new ArrayList(); + for (FeatureTypeInfo typeInfo : this.featureInfo.values()) { + if (typeInfo.isInstantiated()) { + availableTypes.add(typeInfo); + featureName.add(typeInfo.getTypeName().getLocalPart()); + } + } + if (availableTypes.isEmpty()) { + return null; + } + Collections.sort(featureName); + Optional availableType = availableTypes.stream() + .filter(x -> x.getTypeName().getLocalPart().equalsIgnoreCase(featureName.get(0))) + .findFirst(); + return availableType.get().getTypeName(); + } + + /** + * Evaluates the given XPath expression against all sample data sets. The first + * non-empty result is returned. + * @param expr An XPath 2.0 expression. + * @param nsBindings A collection of namespace bindings required to evaluate the XPath + * expression, where each entry maps a namespace URI (key) to a prefix (value). + * @return An XdmValue object containing a sequence of zero or more matching items. + */ + public XdmValue evaluateXPathAgainstSampleData(String expr, Map nsBindings) { + XdmValue results = null; + for (Entry entry : this.featureInfo.entrySet()) { + if (!entry.getValue().isInstantiated()) + continue; + File dataFile = entry.getValue().getSampleData(); + try { + results = XMLUtils.evaluateXPath2(new StreamSource(dataFile), expr, nsBindings); + if (results.size() > 0) { + break; + } + } + catch (SaxonApiException e) { + LOGR.log(Level.WARNING, e.getMessage()); + } + } + return results; + } + + private void acquireFeatureData(WFSClient wfsClient, Set getFeatureBindings, QName typeName, + FeatureTypeInfo featureTypeInfo) { + for (ProtocolBinding binding : getFeatureBindings) { + try { + Document rspEntity = wfsClient.getFeatureByType(typeName, maxFeatures, binding); + NodeList features = rspEntity.getElementsByTagNameNS(typeName.getNamespaceURI(), + typeName.getLocalPart()); + boolean hasFeatures = features.getLength() > 0; + if (hasFeatures) { + saveFeatureDataFile(featureTypeInfo, typeName, rspEntity); + return; + } + } + catch (RuntimeException re) { + StringBuilder err = new StringBuilder(); + err.append(String.format("Failed to read XML response entity using %s method for feature type %s.", + binding, typeName)); + err.append(" \n").append(re.getMessage()); + LOGR.log(Level.WARNING, err.toString(), re); + } + } + } + + private void saveFeatureDataFile(FeatureTypeInfo featureTypeInfo, QName typeName, Document rspEntity) { + try { + File file = File.createTempFile(typeName.getLocalPart() + "-", ".xml"); + FileOutputStream fos = new FileOutputStream(file); + XMLUtils.writeNode(rspEntity, fos); + LOGR.log(Level.CONFIG, this.getClass().getName() + " - wrote feature data to " + file.getAbsolutePath()); + featureTypeInfo.setSampleData(file); + fos.close(); + featureTypeInfo.setInstantiated(true); + } + catch (Exception e) { + LOGR.log(Level.WARNING, "Failed to save feature data.", e); + } + } } diff --git a/src/main/java/org/opengis/cite/iso19142/util/FeatureProperty.java b/src/main/java/org/opengis/cite/iso19142/util/FeatureProperty.java index 3a2afc9a..d90beec1 100644 --- a/src/main/java/org/opengis/cite/iso19142/util/FeatureProperty.java +++ b/src/main/java/org/opengis/cite/iso19142/util/FeatureProperty.java @@ -10,143 +10,139 @@ import org.opengis.cite.iso19136.util.XMLSchemaModelUtils; /** - * An immutable description of a feature property. The property type may be - * simple (e.g. a string) or complex (e.g. a GML geometry element). + * An immutable description of a feature property. The property type may be simple (e.g. a + * string) or complex (e.g. a GML geometry element). */ final public class FeatureProperty { - final private QName name; - final private QName featureType; - final private QName valueType; - final private XSElementDeclaration declaration; - - /** - * Constructor specifying the feature type and property declaration. The - * property name and value type are derived from the element declaration. - * - * @param featureType - * A QName specifying the feature type to which the property - * belongs. - * @param declaration - * A schema component representing an element declaration for the - * property. - */ - public FeatureProperty(QName featureType, XSElementDeclaration declaration) { - this.featureType = featureType; - this.declaration = declaration; - this.name = new QName(declaration.getNamespace(), declaration.getName()); - this.valueType = getTypeName(declaration); - } - - /** - * Gets the qualified name of the feature property. - * - * @return A QName object. - */ - public QName getName() { - return name; - } - - /** - * Gets the qualified name of the feature type to which this property - * belongs. - * - * @return A QName object. - */ - public QName getFeatureType() { - return featureType; - } - - /** - * Gets the qualified name of the property value type. This is either the - * name of a simple datatype (e.g. xsd:decimal) or the name of an acceptable - * child element (e.g. gml:Point). - * - * @return A QName object. - */ - public QName getValueType() { - return valueType; - } - - /** - * Gets the element declaration for this feature property. - * - * @return A schema component representing an element declaration from an - * XML Schema. - */ - public XSElementDeclaration getDeclaration() { - return declaration; - } - - /** - * Returns the qualified name of the property value type. If the property - * has a complex type, this is the type name of the expected value. Since - * the use of a choice compositor is very unconventional in this context (an - * abstract element is generally preferred), only one element is assumed to - * appear as an allowed value of a complex type. - * - * @param elementDecl - * A schema component representing an element declaration. - * @return A QName denoting the name of a simple or complex type. - */ - QName getTypeName( XSElementDeclaration elementDecl ) { - XSTypeDefinition typeDef = elementDecl.getTypeDefinition(); - if ( typeDef.getTypeCategory() == XSTypeDefinition.SIMPLE_TYPE ) - return new QName( typeDef.getNamespace(), typeDef.getName() ); - - XSComplexTypeDefinition complexTypeDef = (XSComplexTypeDefinition) typeDef; - if ( complexTypeDef.getContentType() == XSComplexTypeDefinition.CONTENTTYPE_SIMPLE ) - return new QName( elementDecl.getNamespace(), elementDecl.getName() ); - - List allElementsInParticle = XMLSchemaModelUtils.getAllElementsInParticle( complexTypeDef.getParticle() ); - XSElementDeclaration elemDecl = allElementsInParticle.get( 0 ); - return new QName( elemDecl.getNamespace(), elemDecl.getName() ); - } - - @Override - public String toString() { - StringBuilder str = new StringBuilder(); - str.append(getFeatureType()).append('/'); - str.append(getName()).append('/'); - str.append(getValueType()); - return str.toString(); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((featureType == null) ? 0 : featureType.hashCode()); - result = prime * result + ((name == null) ? 0 : name.hashCode()); - result = prime * result + ((valueType == null) ? 0 : valueType.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - FeatureProperty other = (FeatureProperty) obj; - if (featureType == null) { - if (other.featureType != null) - return false; - } else if (!featureType.equals(other.featureType)) - return false; - if (name == null) { - if (other.name != null) - return false; - } else if (!name.equals(other.name)) - return false; - if (valueType == null) { - if (other.valueType != null) - return false; - } else if (!valueType.equals(other.valueType)) - return false; - return true; - } + final private QName name; + + final private QName featureType; + + final private QName valueType; + + final private XSElementDeclaration declaration; + + /** + * Constructor specifying the feature type and property declaration. The property name + * and value type are derived from the element declaration. + * @param featureType A QName specifying the feature type to which the property + * belongs. + * @param declaration A schema component representing an element declaration for the + * property. + */ + public FeatureProperty(QName featureType, XSElementDeclaration declaration) { + this.featureType = featureType; + this.declaration = declaration; + this.name = new QName(declaration.getNamespace(), declaration.getName()); + this.valueType = getTypeName(declaration); + } + + /** + * Gets the qualified name of the feature property. + * @return A QName object. + */ + public QName getName() { + return name; + } + + /** + * Gets the qualified name of the feature type to which this property belongs. + * @return A QName object. + */ + public QName getFeatureType() { + return featureType; + } + + /** + * Gets the qualified name of the property value type. This is either the name of a + * simple datatype (e.g. xsd:decimal) or the name of an acceptable child element (e.g. + * gml:Point). + * @return A QName object. + */ + public QName getValueType() { + return valueType; + } + + /** + * Gets the element declaration for this feature property. + * @return A schema component representing an element declaration from an XML Schema. + */ + public XSElementDeclaration getDeclaration() { + return declaration; + } + + /** + * Returns the qualified name of the property value type. If the property has a + * complex type, this is the type name of the expected value. Since the use of a + * choice compositor is very unconventional in this context (an abstract element is + * generally preferred), only one element is assumed to appear as an allowed value of + * a complex type. + * @param elementDecl A schema component representing an element declaration. + * @return A QName denoting the name of a simple or complex type. + */ + QName getTypeName(XSElementDeclaration elementDecl) { + XSTypeDefinition typeDef = elementDecl.getTypeDefinition(); + if (typeDef.getTypeCategory() == XSTypeDefinition.SIMPLE_TYPE) + return new QName(typeDef.getNamespace(), typeDef.getName()); + + XSComplexTypeDefinition complexTypeDef = (XSComplexTypeDefinition) typeDef; + if (complexTypeDef.getContentType() == XSComplexTypeDefinition.CONTENTTYPE_SIMPLE) + return new QName(elementDecl.getNamespace(), elementDecl.getName()); + + List allElementsInParticle = XMLSchemaModelUtils + .getAllElementsInParticle(complexTypeDef.getParticle()); + XSElementDeclaration elemDecl = allElementsInParticle.get(0); + return new QName(elemDecl.getNamespace(), elemDecl.getName()); + } + + @Override + public String toString() { + StringBuilder str = new StringBuilder(); + str.append(getFeatureType()).append('/'); + str.append(getName()).append('/'); + str.append(getValueType()); + return str.toString(); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((featureType == null) ? 0 : featureType.hashCode()); + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + ((valueType == null) ? 0 : valueType.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + FeatureProperty other = (FeatureProperty) obj; + if (featureType == null) { + if (other.featureType != null) + return false; + } + else if (!featureType.equals(other.featureType)) + return false; + if (name == null) { + if (other.name != null) + return false; + } + else if (!name.equals(other.name)) + return false; + if (valueType == null) { + if (other.valueType != null) + return false; + } + else if (!valueType.equals(other.valueType)) + return false; + return true; + } } diff --git a/src/main/java/org/opengis/cite/iso19142/util/NamespaceBindings.java b/src/main/java/org/opengis/cite/iso19142/util/NamespaceBindings.java index 22462b52..932115ba 100644 --- a/src/main/java/org/opengis/cite/iso19142/util/NamespaceBindings.java +++ b/src/main/java/org/opengis/cite/iso19142/util/NamespaceBindings.java @@ -12,107 +12,97 @@ import org.opengis.cite.iso19142.Namespaces; /** - * Provides namespace bindings for evaluating XPath 1.0 expressions using the - * JAXP XPath API. A namespace name (URI) may be bound to only one prefix. + * Provides namespace bindings for evaluating XPath 1.0 expressions using the JAXP XPath + * API. A namespace name (URI) may be bound to only one prefix. */ public class NamespaceBindings implements NamespaceContext { - private Map bindings = new HashMap(); + private Map bindings = new HashMap(); - @Override - public String getNamespaceURI(String prefix) { - String nsName = null; - for (Map.Entry binding : bindings.entrySet()) { - if (binding.getValue().equals(prefix)) { - nsName = binding.getKey(); - break; - } - } - return nsName; - } + @Override + public String getNamespaceURI(String prefix) { + String nsName = null; + for (Map.Entry binding : bindings.entrySet()) { + if (binding.getValue().equals(prefix)) { + nsName = binding.getKey(); + break; + } + } + return nsName; + } - @Override - public String getPrefix(String namespaceURI) { - return bindings.get(namespaceURI); - } + @Override + public String getPrefix(String namespaceURI) { + return bindings.get(namespaceURI); + } - @Override - public Iterator getPrefixes(String namespaceURI) { - return Arrays.asList(getPrefix(namespaceURI)).iterator(); - } + @Override + public Iterator getPrefixes(String namespaceURI) { + return Arrays.asList(getPrefix(namespaceURI)).iterator(); + } - /** - * Adds a namespace binding that associates a namespace name with a prefix. - * If a binding for a given namespace name already exists it will be - * replaced. - * - * @param namespaceURI - * A String denoting a namespace name (an absolute URI value). - * @param prefix - * A prefix associated with the namespace name. - */ - public void addNamespaceBinding(String namespaceURI, String prefix) { - bindings.put(namespaceURI, prefix); - } + /** + * Adds a namespace binding that associates a namespace name with a prefix. If a + * binding for a given namespace name already exists it will be replaced. + * @param namespaceURI A String denoting a namespace name (an absolute URI value). + * @param prefix A prefix associated with the namespace name. + */ + public void addNamespaceBinding(String namespaceURI, String prefix) { + bindings.put(namespaceURI, prefix); + } - /** - * Adds all of the supplied namespace bindings to the existing set of - * entries. - * - * @param nsBindings - * A Map containing a collection of namespace bindings where the - * key is an absolute URI specifying the namespace name and the - * value denotes the associated prefix. - */ - public void addAllBindings(Map nsBindings) { - if (null != nsBindings) - bindings.putAll(nsBindings); - } + /** + * Adds all of the supplied namespace bindings to the existing set of entries. + * @param nsBindings A Map containing a collection of namespace bindings where the key + * is an absolute URI specifying the namespace name and the value denotes the + * associated prefix. + */ + public void addAllBindings(Map nsBindings) { + if (null != nsBindings) + bindings.putAll(nsBindings); + } - /** - * Returns an unmodifiable view of the declared namespace bindings. - * - * @return An immutable Map containing zero or more namespace bindings where - * the key is an absolute URI specifying the namespace name and the - * value is the associated prefix. - */ - public Map getAllBindings() { - return Collections.unmodifiableMap(this.bindings); - } + /** + * Returns an unmodifiable view of the declared namespace bindings. + * @return An immutable Map containing zero or more namespace bindings where the key + * is an absolute URI specifying the namespace name and the value is the associated + * prefix. + */ + public Map getAllBindings() { + return Collections.unmodifiableMap(this.bindings); + } - /** - * Creates a NamespaceBindings object that declares the following namespace - * bindings: - * - *
                - *
              • wfs: {@value org.opengis.cite.iso19142.Namespaces#WFS}
              • - *
              • fes: {@value org.opengis.cite.iso19142.Namespaces#FES}
              • - *
              • ows: {@value org.opengis.cite.iso19142.Namespaces#OWS}
              • - *
              • xlink: {@value org.opengis.cite.iso19142.Namespaces#XLINK}
              • - *
              • gml: {@value org.opengis.cite.iso19142.Namespaces#GML}
              • - *
              • soap: {@value org.opengis.cite.iso19142.Namespaces#SOAP_ENV}
              • - *
              • soap11: {@value org.opengis.cite.iso19142.Namespaces#SOAP11}
              • - *
              • xsi: {@value javax.xml.XMLConstants#W3C_XML_SCHEMA_INSTANCE_NS_URI}
              • - *
              - * - * @return A NamespaceBindings object. - */ - public static NamespaceBindings withStandardBindings() { - NamespaceBindings nsBindings = new NamespaceBindings(); - nsBindings.addNamespaceBinding(Namespaces.WFS, "wfs"); - nsBindings.addNamespaceBinding(Namespaces.FES, "fes"); - nsBindings.addNamespaceBinding(Namespaces.OWS, "ows"); - nsBindings.addNamespaceBinding(Namespaces.XLINK, "xlink"); - nsBindings.addNamespaceBinding(Namespaces.GML, "gml"); - nsBindings.addNamespaceBinding(Namespaces.SOAP_ENV, "soap"); - nsBindings.addNamespaceBinding(Namespaces.SOAP11, "soap11"); - nsBindings.addNamespaceBinding( - XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "xsi"); - return nsBindings; - } + /** + * Creates a NamespaceBindings object that declares the following namespace bindings: + * + *
                + *
              • wfs: {@value org.opengis.cite.iso19142.Namespaces#WFS}
              • + *
              • fes: {@value org.opengis.cite.iso19142.Namespaces#FES}
              • + *
              • ows: {@value org.opengis.cite.iso19142.Namespaces#OWS}
              • + *
              • xlink: {@value org.opengis.cite.iso19142.Namespaces#XLINK}
              • + *
              • gml: {@value org.opengis.cite.iso19142.Namespaces#GML}
              • + *
              • soap: {@value org.opengis.cite.iso19142.Namespaces#SOAP_ENV}
              • + *
              • soap11: {@value org.opengis.cite.iso19142.Namespaces#SOAP11}
              • + *
              • xsi: {@value javax.xml.XMLConstants#W3C_XML_SCHEMA_INSTANCE_NS_URI}
              • + *
              + * @return A NamespaceBindings object. + */ + public static NamespaceBindings withStandardBindings() { + NamespaceBindings nsBindings = new NamespaceBindings(); + nsBindings.addNamespaceBinding(Namespaces.WFS, "wfs"); + nsBindings.addNamespaceBinding(Namespaces.FES, "fes"); + nsBindings.addNamespaceBinding(Namespaces.OWS, "ows"); + nsBindings.addNamespaceBinding(Namespaces.XLINK, "xlink"); + nsBindings.addNamespaceBinding(Namespaces.GML, "gml"); + nsBindings.addNamespaceBinding(Namespaces.SOAP_ENV, "soap"); + nsBindings.addNamespaceBinding(Namespaces.SOAP11, "soap11"); + nsBindings.addNamespaceBinding(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "xsi"); + return nsBindings; + } + + @Override + public String toString() { + return "NamespaceBindings:\n" + bindings; + } - @Override - public String toString() { - return "NamespaceBindings:\n" + bindings; - } } diff --git a/src/main/java/org/opengis/cite/iso19142/util/Randomizer.java b/src/main/java/org/opengis/cite/iso19142/util/Randomizer.java index 305ffa13..2c747cff 100644 --- a/src/main/java/org/opengis/cite/iso19142/util/Randomizer.java +++ b/src/main/java/org/opengis/cite/iso19142/util/Randomizer.java @@ -7,35 +7,37 @@ */ public class Randomizer { - private static final int MIN_CODE_POINT = 65; - private static final int MAX_CODE_POINT = 122; - private static final int WORD_LENGTH = 10; + private static final int MIN_CODE_POINT = 65; + + private static final int MAX_CODE_POINT = 122; + + private static final int WORD_LENGTH = 10; + + /** + * Generates a sequence of of (space-separated) words, each of which contains a random + * sequence of letter characters in the range [A-Za-z]. + * @param numWords The number of words in the sequence (1..10). + * @return A String consisting of one or more words. + */ + public static String generateWords(int numWords) { + if (numWords < 1 || numWords > 10) { + numWords = 1; + } + StringBuilder words = new StringBuilder(); + ThreadLocalRandom rnd = ThreadLocalRandom.current(); + for (int i = 0; i < numWords; i++) { + int charPos = 0; + do { + int codePoint = rnd.nextInt(MIN_CODE_POINT, MAX_CODE_POINT + 1); + if (codePoint < 91 || codePoint > 96) { + words.append(Character.toChars(codePoint)); + charPos++; + } + } + while (charPos < WORD_LENGTH); + words.append(' '); + } + return words.toString().trim(); + } - /** - * Generates a sequence of of (space-separated) words, each of which - * contains a random sequence of letter characters in the range [A-Za-z]. - * - * @param numWords - * The number of words in the sequence (1..10). - * @return A String consisting of one or more words. - */ - public static String generateWords(int numWords) { - if (numWords < 1 || numWords > 10) { - numWords = 1; - } - StringBuilder words = new StringBuilder(); - ThreadLocalRandom rnd = ThreadLocalRandom.current(); - for (int i = 0; i < numWords; i++) { - int charPos = 0; - do { - int codePoint = rnd.nextInt(MIN_CODE_POINT, MAX_CODE_POINT + 1); - if (codePoint < 91 || codePoint > 96) { - words.append(Character.toChars(codePoint)); - charPos++; - } - } while (charPos < WORD_LENGTH); - words.append(' '); - } - return words.toString().trim(); - } } diff --git a/src/main/java/org/opengis/cite/iso19142/util/SOAPMessageConsumer.java b/src/main/java/org/opengis/cite/iso19142/util/SOAPMessageConsumer.java index 5029805f..9c336894 100644 --- a/src/main/java/org/opengis/cite/iso19142/util/SOAPMessageConsumer.java +++ b/src/main/java/org/opengis/cite/iso19142/util/SOAPMessageConsumer.java @@ -19,41 +19,39 @@ import jakarta.xml.soap.SOAPPart; /** - * A W3C SOAP message consumer that converts an input stream to a - * {@link SOAPMessage} object. It is suitable for reading SOAP 1.2 entities - * (media type "application/soap+xml"). + * A W3C SOAP message consumer that converts an input stream to a {@link SOAPMessage} + * object. It is suitable for reading SOAP 1.2 entities (media type + * "application/soap+xml"). * - * @see RFC 3902: - * The "application/soap+xml" media type + * @see RFC 3902: The + * "application/soap+xml" media type */ @Consumes("application/soap+xml") public class SOAPMessageConsumer implements MessageBodyReader { - private static final Logger LOGR = Logger - .getLogger(SOAPMessageConsumer.class.getPackage().getName()); - - @Override - public boolean isReadable(Class type, Type genericType, - Annotation[] annotations, MediaType mediaType) { - return type.isAssignableFrom(SOAPMessage.class); - } - - @Override - public SOAPMessage readFrom(Class type, Type genericType, - Annotation[] annotations, MediaType mediaType, - MultivaluedMap httpHeaders, InputStream entityStream) - throws IOException, WebApplicationException { - SOAPMessage message = null; - try { - MessageFactory messageFactory = MessageFactory.newInstance(); - message = messageFactory.createMessage(); - SOAPPart soapPart = message.getSOAPPart(); - StreamSource messageSource = new StreamSource(entityStream); - soapPart.setContent(messageSource); - } catch (SOAPException se) { - LOGR.warning("Unable to create SOAPMessage.\n" + se.getMessage()); - } - return message; - } + private static final Logger LOGR = Logger.getLogger(SOAPMessageConsumer.class.getPackage().getName()); + + @Override + public boolean isReadable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { + return type.isAssignableFrom(SOAPMessage.class); + } + + @Override + public SOAPMessage readFrom(Class type, Type genericType, Annotation[] annotations, + MediaType mediaType, MultivaluedMap httpHeaders, InputStream entityStream) + throws IOException, WebApplicationException { + SOAPMessage message = null; + try { + MessageFactory messageFactory = MessageFactory.newInstance(); + message = messageFactory.createMessage(); + SOAPPart soapPart = message.getSOAPPart(); + StreamSource messageSource = new StreamSource(entityStream); + soapPart.setContent(messageSource); + } + catch (SOAPException se) { + LOGR.warning("Unable to create SOAPMessage.\n" + se.getMessage()); + } + return message; + } } diff --git a/src/main/java/org/opengis/cite/iso19142/util/ServiceMetadataUtils.java b/src/main/java/org/opengis/cite/iso19142/util/ServiceMetadataUtils.java index de82aa13..d531d7f8 100644 --- a/src/main/java/org/opengis/cite/iso19142/util/ServiceMetadataUtils.java +++ b/src/main/java/org/opengis/cite/iso19142/util/ServiceMetadataUtils.java @@ -38,524 +38,497 @@ */ public class ServiceMetadataUtils { - private static final Logger LOGR = Logger.getLogger(ServiceMetadataUtils.class.getPackage().getName()); + 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"; - } + /** + * 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; + /** + * 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; + // 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; - } + 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; - } + /** + * 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 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; - } + /** + * 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); - } + /** + * 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); + } - /** - * 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; - } + if (null != prefix) { + return new QName(nsName, localPart, prefix); + } + return new QName(nsName, localPart); + } - /** - * 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; - } + /** + * 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; + } - /** - * 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; - } + /** + * 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; + } - /** - * 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; - } + /** + * 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; + } - /** - * 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; - } + /** + * 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; + } - /** - * 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; - } + /** + * 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; + } - /** - * 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; - } + /** + * 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 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; - } + /** + * 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; + } - /** - * 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; - } } diff --git a/src/main/java/org/opengis/cite/iso19142/util/TestSuiteLogger.java b/src/main/java/org/opengis/cite/iso19142/util/TestSuiteLogger.java index 5f2f8c68..81077b93 100644 --- a/src/main/java/org/opengis/cite/iso19142/util/TestSuiteLogger.java +++ b/src/main/java/org/opengis/cite/iso19142/util/TestSuiteLogger.java @@ -4,79 +4,65 @@ import java.util.logging.Logger; /** - * Logging utility class that provides simple access to the JDK Logging API. Set - * the "java.util.logging.config.file" system property to specify the location - * of the desired logging configuration file. A sample configuration file is - * available at {@code src/main/config/logging.properties}. - * + * Logging utility class that provides simple access to the JDK Logging API. Set the + * "java.util.logging.config.file" system property to specify the location of the desired + * logging configuration file. A sample configuration file is available at + * {@code src/main/config/logging.properties}. + * * @see java.util.logging.LogManager LogManager */ public class TestSuiteLogger { - private static final Logger LOGR = Logger.getLogger(TestSuiteLogger.class - .getPackage().getName()); + private static final Logger LOGR = Logger.getLogger(TestSuiteLogger.class.getPackage().getName()); - /** - * Logs a message at the specified logging level with the given message - * parameters. - * - * @param level - * The logging {@link Level level}. - * @param message - * A String representing the content of the log message. - * @param params - * An array of message parameters. - */ - public static void log(Level level, String message, Object[] params) { - if (LOGR.isLoggable(level)) { - LOGR.log(level, message, params); - } - } + /** + * Logs a message at the specified logging level with the given message parameters. + * @param level The logging {@link Level level}. + * @param message A String representing the content of the log message. + * @param params An array of message parameters. + */ + public static void log(Level level, String message, Object[] params) { + if (LOGR.isLoggable(level)) { + LOGR.log(level, message, params); + } + } - /** - * Logs a message at the specified logging level with the given Exception - * object that represents a noteworthy error condition. - * - * @param level - * The logging {@link Level level}. - * @param message - * A String representing the content of the log message. - * @param except - * An object that indicates an exceptional situation. - */ - public static void log(Level level, String message, Exception except) { - if (LOGR.isLoggable(level)) { - LOGR.log(level, message, except); - } - } + /** + * Logs a message at the specified logging level with the given Exception object that + * represents a noteworthy error condition. + * @param level The logging {@link Level level}. + * @param message A String representing the content of the log message. + * @param except An object that indicates an exceptional situation. + */ + public static void log(Level level, String message, Exception except) { + if (LOGR.isLoggable(level)) { + LOGR.log(level, message, except); + } + } - /** - * Logs a simple message at the specified logging level. - * - * @param level - * The logging {@link Level level}. - * @param message - * A String representing the content of the log message. - */ - public static void log(Level level, String message) { - if (LOGR.isLoggable(level)) { - LOGR.log(level, message); - } - } + /** + * Logs a simple message at the specified logging level. + * @param level The logging {@link Level level}. + * @param message A String representing the content of the log message. + */ + public static void log(Level level, String message) { + if (LOGR.isLoggable(level)) { + LOGR.log(level, message); + } + } - /** - * Indicates if the logger is enabled at a given logging level. Message - * levels lower than this value will be discarded. - * - * @param level - * The logging {@link Level level}. - * @return true if the logger is currently enabled for this logging level; - * false otherwise. - */ - public static boolean isLoggable(Level level) { - return LOGR.isLoggable(level); - } + /** + * Indicates if the logger is enabled at a given logging level. Message levels lower + * than this value will be discarded. + * @param level The logging {@link Level level}. + * @return true if the logger is currently enabled for this logging level; false + * otherwise. + */ + public static boolean isLoggable(Level level) { + return LOGR.isLoggable(level); + } + + private TestSuiteLogger() { + } - private TestSuiteLogger() { - } } diff --git a/src/main/java/org/opengis/cite/iso19142/util/TimeUtils.java b/src/main/java/org/opengis/cite/iso19142/util/TimeUtils.java index 489cf4b8..33c7eab4 100644 --- a/src/main/java/org/opengis/cite/iso19142/util/TimeUtils.java +++ b/src/main/java/org/opengis/cite/iso19142/util/TimeUtils.java @@ -20,177 +20,161 @@ import org.xml.sax.SAXException; /** - * Provides various utility methods for working with representations of temporal - * values. + * Provides various utility methods for working with representations of temporal values. */ public class TimeUtils { - private static final DocumentBuilder DOC_BUILDER = initDocBuilder(); - - private static DocumentBuilder initDocBuilder() { - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - factory.setNamespaceAware(true); - DocumentBuilder builder = null; - try { - builder = factory.newDocumentBuilder(); - } catch (ParserConfigurationException e) { - TestSuiteLogger.log(Level.WARNING, "TimeUtils: Failed to create DocumentBuilder", e); - } - return builder; - } - - /** - * Builds a GML representation of a time interval delimited by the given - * time instants. The temporal reference system is ISO 8601 (UTC). - * - * @param startDateTime - * The starting instant. - * @param endDateTime - * The ending instant. - * @return A Document with gml:TimePeriod as the document element, or null - * if it cannot be created. - */ - public static Document intervalAsGML(ZonedDateTime startDateTime, ZonedDateTime endDateTime) { - Document gmlTimePeriod; - try { - gmlTimePeriod = DOC_BUILDER.parse(TimeUtils.class.getResourceAsStream("TimePeriod.xml")); - } catch (SAXException | IOException e) { - return null; - } - String beginPosition = startDateTime.format(DateTimeFormatter.ISO_INSTANT); - gmlTimePeriod.getElementsByTagNameNS(Namespaces.GML, "beginPosition").item(0).setTextContent(beginPosition); - String endPosition = endDateTime.format(DateTimeFormatter.ISO_INSTANT); - gmlTimePeriod.getElementsByTagNameNS(Namespaces.GML, "endPosition").item(0).setTextContent(endPosition); - return gmlTimePeriod; - } - - /** - * Builds a GML representation of the given time period. - * - * @param period - * A Period representing a temporal interval (UTC). - * @return A Document with gml:TimePeriod as the document element. - */ - public static Document periodAsGML(Period period) { - return periodAsGML(period, null); - } - - /** - * Builds a GML representation of the given time period. - * - * @param period - * A Period representing a temporal interval (UTC). - * @param offset - * A time-zone offset from UTC ('Z' if null). - * @return A Document with gml:TimePeriod as the document element. - */ - public static Document periodAsGML(Period period, ZoneOffset offset) { - if (null == offset) { - offset = ZoneOffset.UTC; - } - DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME; - String startOfPeriod = period.getBeginning().getDate().toInstant().atOffset(offset).toString(); - ZonedDateTime startDateTime = ZonedDateTime.parse(startOfPeriod, dateTimeFormatter); - String endOfPeriod = period.getEnding().getDate().toInstant().atOffset(offset).toString(); - ZonedDateTime endDateTime = ZonedDateTime.parse(endOfPeriod, dateTimeFormatter); - return intervalAsGML(startDateTime, endDateTime); - } - - /** - * Builds a GML representation of the given time period. - * - * @param period - * A Period representing a temporal interval (UTC). - * @return A Document with gml:TimePeriod as the document element. - * Subtracts one day from beginning and end, see https://github.com/opengeospatial/ets-wfs20/issues/199. - */ - public static Document periodAsGMLSubtractOneDay(Period period) { - DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXX"); - String startOfPeriod = period.getBeginning().getDate().toInstant().toString(); - ZonedDateTime startDateTime = ZonedDateTime.parse(startOfPeriod, dateTimeFormatter); - startDateTime = startDateTime.minus(1, ChronoUnit.DAYS); - String endOfPeriod = period.getEnding().getDate().toInstant().toString(); - ZonedDateTime endDateTime = ZonedDateTime.parse(endOfPeriod, dateTimeFormatter); - endDateTime = endDateTime.minus(1, ChronoUnit.DAYS); - return intervalAsGML(startDateTime, endDateTime); - } - - /** - * Builds a GML representation of the given time period. - * - * @param period - * A Period representing a temporal interval (UTC). - * @return A Document with gml:TimePeriod as the document element. - * Adds one day to beginning and end, see https://github.com/opengeospatial/ets-wfs20/issues/226. - */ + private static final DocumentBuilder DOC_BUILDER = initDocBuilder(); + + private static DocumentBuilder initDocBuilder() { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + DocumentBuilder builder = null; + try { + builder = factory.newDocumentBuilder(); + } + catch (ParserConfigurationException e) { + TestSuiteLogger.log(Level.WARNING, "TimeUtils: Failed to create DocumentBuilder", e); + } + return builder; + } + + /** + * Builds a GML representation of a time interval delimited by the given time + * instants. The temporal reference system is ISO 8601 (UTC). + * @param startDateTime The starting instant. + * @param endDateTime The ending instant. + * @return A Document with gml:TimePeriod as the document element, or null if it + * cannot be created. + */ + public static Document intervalAsGML(ZonedDateTime startDateTime, ZonedDateTime endDateTime) { + Document gmlTimePeriod; + try { + gmlTimePeriod = DOC_BUILDER.parse(TimeUtils.class.getResourceAsStream("TimePeriod.xml")); + } + catch (SAXException | IOException e) { + return null; + } + String beginPosition = startDateTime.format(DateTimeFormatter.ISO_INSTANT); + gmlTimePeriod.getElementsByTagNameNS(Namespaces.GML, "beginPosition").item(0).setTextContent(beginPosition); + String endPosition = endDateTime.format(DateTimeFormatter.ISO_INSTANT); + gmlTimePeriod.getElementsByTagNameNS(Namespaces.GML, "endPosition").item(0).setTextContent(endPosition); + return gmlTimePeriod; + } + + /** + * Builds a GML representation of the given time period. + * @param period A Period representing a temporal interval (UTC). + * @return A Document with gml:TimePeriod as the document element. + */ + public static Document periodAsGML(Period period) { + return periodAsGML(period, null); + } + + /** + * Builds a GML representation of the given time period. + * @param period A Period representing a temporal interval (UTC). + * @param offset A time-zone offset from UTC ('Z' if null). + * @return A Document with gml:TimePeriod as the document element. + */ + public static Document periodAsGML(Period period, ZoneOffset offset) { + if (null == offset) { + offset = ZoneOffset.UTC; + } + DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME; + String startOfPeriod = period.getBeginning().getDate().toInstant().atOffset(offset).toString(); + ZonedDateTime startDateTime = ZonedDateTime.parse(startOfPeriod, dateTimeFormatter); + String endOfPeriod = period.getEnding().getDate().toInstant().atOffset(offset).toString(); + ZonedDateTime endDateTime = ZonedDateTime.parse(endOfPeriod, dateTimeFormatter); + return intervalAsGML(startDateTime, endDateTime); + } + + /** + * Builds a GML representation of the given time period. + * @param period A Period representing a temporal interval (UTC). + * @return A Document with gml:TimePeriod as the document element. Subtracts one day + * from beginning and end, see https://github.com/opengeospatial/ets-wfs20/issues/199. + */ + public static Document periodAsGMLSubtractOneDay(Period period) { + DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXX"); + String startOfPeriod = period.getBeginning().getDate().toInstant().toString(); + ZonedDateTime startDateTime = ZonedDateTime.parse(startOfPeriod, dateTimeFormatter); + startDateTime = startDateTime.minus(1, ChronoUnit.DAYS); + String endOfPeriod = period.getEnding().getDate().toInstant().toString(); + ZonedDateTime endDateTime = ZonedDateTime.parse(endOfPeriod, dateTimeFormatter); + endDateTime = endDateTime.minus(1, ChronoUnit.DAYS); + return intervalAsGML(startDateTime, endDateTime); + } + + /** + * Builds a GML representation of the given time period. + * @param period A Period representing a temporal interval (UTC). + * @return A Document with gml:TimePeriod as the document element. Adds one day to + * beginning and end, see https://github.com/opengeospatial/ets-wfs20/issues/226. + */ public static Document periodAsGMLAddOneDay(Period period) { - DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXX"); - String startOfPeriod = period.getBeginning().getDate().toInstant().toString(); - ZonedDateTime startDateTime = ZonedDateTime.parse(startOfPeriod, dateTimeFormatter); - startDateTime = startDateTime.plus(1, ChronoUnit.DAYS); - String endOfPeriod = period.getEnding().getDate().toInstant().toString(); - ZonedDateTime endDateTime = ZonedDateTime.parse(endOfPeriod, dateTimeFormatter); - endDateTime = endDateTime.plus(1, ChronoUnit.DAYS); - return intervalAsGML(startDateTime, endDateTime); + DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXX"); + String startOfPeriod = period.getBeginning().getDate().toInstant().toString(); + ZonedDateTime startDateTime = ZonedDateTime.parse(startOfPeriod, dateTimeFormatter); + startDateTime = startDateTime.plus(1, ChronoUnit.DAYS); + String endOfPeriod = period.getEnding().getDate().toInstant().toString(); + ZonedDateTime endDateTime = ZonedDateTime.parse(endOfPeriod, dateTimeFormatter); + endDateTime = endDateTime.plus(1, ChronoUnit.DAYS); + return intervalAsGML(startDateTime, endDateTime); + } + + /** + * Builds a GML representation of a time instant with the specified time-zone offset. + * @param instant An instant representing a position in time. + * @param offset A time-zone offset from UTC ('Z' if null). + * @return A Document with gml:TimeInstant as the document element. + */ + public static Document instantAsGML(org.opengis.temporal.Instant instant, ZoneOffset offset) { + if (null == offset) { + offset = ZoneOffset.UTC; + } + Document gmlTimeInstant; + try { + gmlTimeInstant = DOC_BUILDER.parse(TimeUtils.class.getResourceAsStream("TimeInstant.xml")); + } + catch (SAXException | IOException e) { + return null; + } + OffsetDateTime tPos = instant.getDate().toInstant().atOffset(offset); + String timePositionValue = tPos.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME); + Node timePosition = gmlTimeInstant.getElementsByTagNameNS(Namespaces.GML, "timePosition").item(0); + timePosition.setTextContent(timePositionValue); + return gmlTimeInstant; + } + + /** + * Builds a GML representation of a time instant with the specified time-zone offset. + * @param instant An instant representing a position in time. + * @param offset A time-zone offset from UTC ('Z' if null). + * @return A Document with gml:TimeInstant as the document element. Subtracts one day + * from the instant, see https://github.com/opengeospatial/ets-wfs20/issues/199. + */ + public static Document instantAsGMLSubtractOneDay(org.opengis.temporal.Instant instant, ZoneOffset offset) { + if (null == offset) { + offset = ZoneOffset.UTC; + } + Document gmlTimeInstant; + try { + gmlTimeInstant = DOC_BUILDER.parse(TimeUtils.class.getResourceAsStream("TimeInstant.xml")); + } + catch (SAXException | IOException e) { + return null; + } + OffsetDateTime tPos = ogcInstantToJavaInstantSubtractOneDay(instant).atOffset(offset); + String timePositionValue = tPos.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME); + Node timePosition = gmlTimeInstant.getElementsByTagNameNS(Namespaces.GML, "timePosition").item(0); + timePosition.setTextContent(timePositionValue); + return gmlTimeInstant; + } + + private static Instant ogcInstantToJavaInstantSubtractOneDay(org.opengis.temporal.Instant instant) { + return instant.getDate().toInstant().minus(1, ChronoUnit.DAYS); } - /** - * Builds a GML representation of a time instant with the specified - * time-zone offset. - * - * @param instant - * An instant representing a position in time. - * @param offset - * A time-zone offset from UTC ('Z' if null). - * @return A Document with gml:TimeInstant as the document element. - */ - public static Document instantAsGML(org.opengis.temporal.Instant instant, ZoneOffset offset) { - if (null == offset) { - offset = ZoneOffset.UTC; - } - Document gmlTimeInstant; - try { - gmlTimeInstant = DOC_BUILDER.parse(TimeUtils.class.getResourceAsStream("TimeInstant.xml")); - } catch (SAXException | IOException e) { - return null; - } - OffsetDateTime tPos = instant.getDate().toInstant().atOffset(offset); - String timePositionValue = tPos.format( DateTimeFormatter.ISO_OFFSET_DATE_TIME ); - Node timePosition = gmlTimeInstant.getElementsByTagNameNS( Namespaces.GML, "timePosition" ).item( 0 ); - timePosition.setTextContent( timePositionValue ); - return gmlTimeInstant; - } - - /** - * Builds a GML representation of a time instant with the specified - * time-zone offset. - * - * @param instant - * An instant representing a position in time. - * @param offset - * A time-zone offset from UTC ('Z' if null). - * @return A Document with gml:TimeInstant as the document element. - * Subtracts one day from the instant, see https://github.com/opengeospatial/ets-wfs20/issues/199. - */ - public static Document instantAsGMLSubtractOneDay(org.opengis.temporal.Instant instant, ZoneOffset offset) { - if (null == offset) { - offset = ZoneOffset.UTC; - } - Document gmlTimeInstant; - try { - gmlTimeInstant = DOC_BUILDER.parse(TimeUtils.class.getResourceAsStream("TimeInstant.xml")); - } catch (SAXException | IOException e) { - return null; - } - OffsetDateTime tPos = ogcInstantToJavaInstantSubtractOneDay(instant).atOffset(offset); - String timePositionValue = tPos.format( DateTimeFormatter.ISO_OFFSET_DATE_TIME ); - Node timePosition = gmlTimeInstant.getElementsByTagNameNS( Namespaces.GML, "timePosition" ).item( 0 ); - timePosition.setTextContent( timePositionValue ); - return gmlTimeInstant; - } - - private static Instant ogcInstantToJavaInstantSubtractOneDay(org.opengis.temporal.Instant instant) { - return instant.getDate().toInstant().minus(1, ChronoUnit.DAYS); - } } diff --git a/src/main/java/org/opengis/cite/iso19142/util/URIUtils.java b/src/main/java/org/opengis/cite/iso19142/util/URIUtils.java index b44215ca..6ba8db32 100644 --- a/src/main/java/org/opengis/cite/iso19142/util/URIUtils.java +++ b/src/main/java/org/opengis/cite/iso19142/util/URIUtils.java @@ -21,117 +21,99 @@ import jakarta.ws.rs.core.Response; /** - * Provides a collection of utility methods for manipulating or resolving URI - * references. + * Provides a collection of utility methods for manipulating or resolving URI references. */ public class URIUtils { - private static final String FIXUP_BASE_URI = "http://apache.org/xml/features/xinclude/fixup-base-uris"; + private static final String FIXUP_BASE_URI = "http://apache.org/xml/features/xinclude/fixup-base-uris"; - /** - * Parses the content of the given URI as an XML document and returns a new - * DOM Document object. Entity reference nodes will not be expanded. XML - * inclusions (xi:include elements) will be processed if present. - * - * @param uriRef - * An absolute URI specifying the location of an XML resource. - * @return A DOM Document node representing an XML resource. - * @throws SAXException - * If the resource cannot be parsed. - * @throws IOException - * If the resource is not accessible. - */ - public static Document resolveURIAsDocument(URI uriRef) - throws SAXException, IOException { - if ((null == uriRef) || !uriRef.isAbsolute()) { - throw new IllegalArgumentException( - "Absolute URI is required, but received " + uriRef); - } - DocumentBuilderFactory docFactory = DocumentBuilderFactory - .newInstance(); - docFactory.setNamespaceAware(true); - docFactory.setExpandEntityReferences(false); - docFactory.setXIncludeAware(true); - Document doc = null; - try { - // XInclude processor will not add xml:base attributes - docFactory.setFeature(FIXUP_BASE_URI, false); - DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); - doc = docBuilder.parse(uriRef.toString()); - } catch (ParserConfigurationException x) { - TestSuiteLogger.log(Level.WARNING, - "Failed to create DocumentBuilder." + x); - } - if (null != doc) { - doc.setDocumentURI(uriRef.toString()); - } - return doc; - } + /** + * Parses the content of the given URI as an XML document and returns a new DOM + * Document object. Entity reference nodes will not be expanded. XML inclusions + * (xi:include elements) will be processed if present. + * @param uriRef An absolute URI specifying the location of an XML resource. + * @return A DOM Document node representing an XML resource. + * @throws SAXException If the resource cannot be parsed. + * @throws IOException If the resource is not accessible. + */ + public static Document resolveURIAsDocument(URI uriRef) throws SAXException, IOException { + if ((null == uriRef) || !uriRef.isAbsolute()) { + throw new IllegalArgumentException("Absolute URI is required, but received " + uriRef); + } + DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); + docFactory.setNamespaceAware(true); + docFactory.setExpandEntityReferences(false); + docFactory.setXIncludeAware(true); + Document doc = null; + try { + // XInclude processor will not add xml:base attributes + docFactory.setFeature(FIXUP_BASE_URI, false); + DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); + doc = docBuilder.parse(uriRef.toString()); + } + catch (ParserConfigurationException x) { + TestSuiteLogger.log(Level.WARNING, "Failed to create DocumentBuilder." + x); + } + if (null != doc) { + doc.setDocumentURI(uriRef.toString()); + } + return doc; + } - /** - * Resolves the given URI and stores the resulting resource representation - * in a local file. The file will be located in the default temporary file - * directory. - * - * @param uriRef - * An absolute URI specifying the location of some resource. - * @return A File containing the content of the resource; it may be empty if - * resolution failed for any reason. - * @throws IOException - * If an IO error occurs. - */ - public static File resolveURIAsFile(URI uriRef) throws IOException { - if ((null == uriRef) || !uriRef.isAbsolute()) { - throw new IllegalArgumentException( - "Absolute URI is required, but received " + uriRef); - } - if (uriRef.getScheme().equalsIgnoreCase("file")) { - return new File(uriRef); - } - Client client = ClientBuilder.newClient(); - WebTarget target = client.target(uriRef); - Response rsp = target.request().buildGet().invoke(); - File destFile = File.createTempFile("entity-", ".xml"); - if (rsp.hasEntity()) { - Object entityObject = rsp.getEntity(); - if(!(entityObject instanceof InputStream)) { - return null; - } - InputStream is = (InputStream) entityObject; - OutputStream os = new FileOutputStream(destFile); - byte[] buffer = new byte[8 * 1024]; - int bytesRead; - while ((bytesRead = is.read(buffer)) != -1) { - os.write(buffer, 0, bytesRead); - } - is.close(); - os.flush(); - os.close(); - } - TestSuiteLogger.log(Level.FINE, "Wrote " + destFile.length() - + " bytes to file at " + destFile.getAbsolutePath()); - return destFile; - } + /** + * Resolves the given URI and stores the resulting resource representation in a local + * file. The file will be located in the default temporary file directory. + * @param uriRef An absolute URI specifying the location of some resource. + * @return A File containing the content of the resource; it may be empty if + * resolution failed for any reason. + * @throws IOException If an IO error occurs. + */ + public static File resolveURIAsFile(URI uriRef) throws IOException { + if ((null == uriRef) || !uriRef.isAbsolute()) { + throw new IllegalArgumentException("Absolute URI is required, but received " + uriRef); + } + if (uriRef.getScheme().equalsIgnoreCase("file")) { + return new File(uriRef); + } + Client client = ClientBuilder.newClient(); + WebTarget target = client.target(uriRef); + Response rsp = target.request().buildGet().invoke(); + File destFile = File.createTempFile("entity-", ".xml"); + if (rsp.hasEntity()) { + Object entityObject = rsp.getEntity(); + if (!(entityObject instanceof InputStream)) { + return null; + } + InputStream is = (InputStream) entityObject; + OutputStream os = new FileOutputStream(destFile); + byte[] buffer = new byte[8 * 1024]; + int bytesRead; + while ((bytesRead = is.read(buffer)) != -1) { + os.write(buffer, 0, bytesRead); + } + is.close(); + os.flush(); + os.close(); + } + TestSuiteLogger.log(Level.FINE, + "Wrote " + destFile.length() + " bytes to file at " + destFile.getAbsolutePath()); + return destFile; + } + + /** + * Constructs an absolute URI value from a relative reference and a base URI. + * @param baseURI The base URI; it must contain at least the scheme component. + * @param path A URI reference (path) that is relative to the given base URI. + * @return A String representing the resulting URI value. + * + * @see RFC 3986, 5.2 + */ + public static String resolveRelativeURI(String baseURI, String path) { + URI uri = URI.create(baseURI); + if (null == uri.getScheme()) { + throw new IllegalArgumentException("Base URI has no scheme component: " + baseURI); + } + return uri.resolve(path).toString(); + } - /** - * Constructs an absolute URI value from a relative reference and a base - * URI. - * - * @param baseURI - * The base URI; it must contain at least the scheme component. - * @param path - * A URI reference (path) that is relative to the given base URI. - * @return A String representing the resulting URI value. - * - * @see RFC 3986, - * 5.2 - */ - public static String resolveRelativeURI(String baseURI, String path) { - URI uri = URI.create(baseURI); - if (null == uri.getScheme()) { - throw new IllegalArgumentException( - "Base URI has no scheme component: " + baseURI); - } - return uri.resolve(path).toString(); - } } diff --git a/src/main/java/org/opengis/cite/iso19142/util/ValidationUtils.java b/src/main/java/org/opengis/cite/iso19142/util/ValidationUtils.java index 76fd13f0..0dcd167f 100644 --- a/src/main/java/org/opengis/cite/iso19142/util/ValidationUtils.java +++ b/src/main/java/org/opengis/cite/iso19142/util/ValidationUtils.java @@ -25,167 +25,144 @@ import org.xml.sax.SAXException; /** - * A utility class that provides convenience methods to support schema - * validation. + * A utility class that provides convenience methods to support schema validation. */ public class ValidationUtils { - private static final XMLCatalogResolver SCH_RESOLVER = initCatalogResolver(); + private static final XMLCatalogResolver SCH_RESOLVER = initCatalogResolver(); - static final String ROOT_PKG = "/org/opengis/cite/iso19142/"; + static final String ROOT_PKG = "/org/opengis/cite/iso19142/"; - private static XMLCatalogResolver initCatalogResolver() { - return (XMLCatalogResolver) createSchemaResolver(Namespaces.SCH); - } + private static XMLCatalogResolver initCatalogResolver() { + return (XMLCatalogResolver) createSchemaResolver(Namespaces.SCH); + } - /** - * Creates a resource resolver suitable for locating schemas using an entity - * catalog. In effect, local copies of standard schemas are returned instead - * of retrieving them from external repositories. - * - * @param schemaLanguage - * A URI that identifies a schema language by namespace name. - * @return A {@code LSResourceResolver} object that is configured to use an - * OASIS entity catalog. - */ - public static LSResourceResolver createSchemaResolver(URI schemaLanguage) { - String catalogFileName; - if (schemaLanguage.equals(Namespaces.XSD)) { - catalogFileName = "schema-catalog.xml"; - } else { - catalogFileName = "schematron-catalog.xml"; - } - URL catalogURL = ValidationUtils.class.getResource(ROOT_PKG - + catalogFileName); - XMLCatalogResolver resolver = new XMLCatalogResolver(); - resolver.setCatalogList(new String[] { catalogURL.toString() }); - return resolver; - } + /** + * Creates a resource resolver suitable for locating schemas using an entity catalog. + * In effect, local copies of standard schemas are returned instead of retrieving them + * from external repositories. + * @param schemaLanguage A URI that identifies a schema language by namespace name. + * @return A {@code LSResourceResolver} object that is configured to use an OASIS + * entity catalog. + */ + public static LSResourceResolver createSchemaResolver(URI schemaLanguage) { + String catalogFileName; + if (schemaLanguage.equals(Namespaces.XSD)) { + catalogFileName = "schema-catalog.xml"; + } + else { + catalogFileName = "schematron-catalog.xml"; + } + URL catalogURL = ValidationUtils.class.getResource(ROOT_PKG + catalogFileName); + XMLCatalogResolver resolver = new XMLCatalogResolver(); + resolver.setCatalogList(new String[] { catalogURL.toString() }); + return resolver; + } - /** - * Constructs a SchematronValidator that will check an XML resource against - * the rules defined in a Schematron schema. An attempt is made to resolve - * the schema reference using an entity catalog; if this fails the reference - * is used as given. - * - * @param schemaRef - * A reference to a Schematron schema; this is expected to be a - * relative or absolute URI value, possibly matching the system - * identifier for some entry in an entity catalog. - * @param phase - * The name of the phase to invoke. - * @return A SchematronValidator instance, or {@code null} if the validator - * cannot be constructed (e.g. invalid schema reference or phase - * name). - */ - public static SchematronValidator buildSchematronValidator( - String schemaRef, String phase) { - Source source = null; - try { - String catalogRef = SCH_RESOLVER - .resolveSystem(schemaRef.toString()); - if (null != catalogRef) { - source = new StreamSource(URI.create(catalogRef).toString()); - } else { - source = new StreamSource(schemaRef); - } - } catch (IOException x) { - TestSuiteLogger.log(Level.WARNING, - "Error reading Schematron schema catalog.", x); - } - SchematronValidator validator = null; - try { - validator = new SchematronValidator(source, phase); - } catch (Exception e) { - TestSuiteLogger.log(Level.WARNING, - "Error creating Schematron validator.", e); - } - return validator; - } + /** + * Constructs a SchematronValidator that will check an XML resource against the rules + * defined in a Schematron schema. An attempt is made to resolve the schema reference + * using an entity catalog; if this fails the reference is used as given. + * @param schemaRef A reference to a Schematron schema; this is expected to be a + * relative or absolute URI value, possibly matching the system identifier for some + * entry in an entity catalog. + * @param phase The name of the phase to invoke. + * @return A SchematronValidator instance, or {@code null} if the validator cannot be + * constructed (e.g. invalid schema reference or phase name). + */ + public static SchematronValidator buildSchematronValidator(String schemaRef, String phase) { + Source source = null; + try { + String catalogRef = SCH_RESOLVER.resolveSystem(schemaRef.toString()); + if (null != catalogRef) { + source = new StreamSource(URI.create(catalogRef).toString()); + } + else { + source = new StreamSource(schemaRef); + } + } + catch (IOException x) { + TestSuiteLogger.log(Level.WARNING, "Error reading Schematron schema catalog.", x); + } + SchematronValidator validator = null; + try { + validator = new SchematronValidator(source, phase); + } + catch (Exception e) { + TestSuiteLogger.log(Level.WARNING, "Error creating Schematron validator.", e); + } + return validator; + } - /** - * Extracts an XML Schema reference from a source XML document. The - * resulting URI value refers to the schema whose target namespace matches - * the namespace of the document element. - * - * @param source - * The source instance to read from; its base URI (systemId) - * should be set. The document element is expected to include the - * standard xsi:schemaLocation attribute. - * @param baseURI - * An alternative base URI to use if the source does not have a - * system identifier set or if its system id is a {@code file} - * URI. This will usually be the URI used to retrieve the - * resource; it may be null. - * @return An absolute URI reference specifying the location of an XML - * Schema resource, or {@code null} if no reference is found. - * @throws XMLStreamException - * If an error occurs while reading the source instance. - */ - public static URI extractSchemaReference(Source source, String baseURI) - throws XMLStreamException { - XMLInputFactory factory = XMLInputFactory.newInstance(); - XMLEventReader reader = factory.createXMLEventReader(source); - // advance to document element - StartElement docElem = reader.nextTag().asStartElement(); - QName qName = docElem.getName(); - String namespace = qName.getNamespaceURI(); - Attribute schemaLoc = docElem.getAttributeByName(new QName( - XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "schemaLocation")); - String[] uriValues = new String[] {}; - if (null != schemaLoc) { - uriValues = schemaLoc.getValue().split("\\s"); - } - URI schemaURI = null; - // one or more pairs of [namespace name] [schema location] - for (int i = 0; i < uriValues.length; i += 2) { - if (uriValues[i].equals(namespace)) { - if (!URI.create(uriValues[i + 1]).isAbsolute() - && (null != source.getSystemId())) { - String schemaRef = URIUtils.resolveRelativeURI( - source.getSystemId(), uriValues[i + 1]); - if (schemaRef.startsWith("file") - && !new File(schemaRef).exists() - && (null != baseURI)) { - schemaRef = URIUtils.resolveRelativeURI(baseURI, - uriValues[i + 1]); - } - schemaURI = URI.create(schemaRef); - } else { - schemaURI = URI.create(uriValues[i + 1]); - } - break; - } - } - return schemaURI; - } + /** + * Extracts an XML Schema reference from a source XML document. The resulting URI + * value refers to the schema whose target namespace matches the namespace of the + * document element. + * @param source The source instance to read from; its base URI (systemId) should be + * set. The document element is expected to include the standard xsi:schemaLocation + * attribute. + * @param baseURI An alternative base URI to use if the source does not have a system + * identifier set or if its system id is a {@code file} URI. This will usually be the + * URI used to retrieve the resource; it may be null. + * @return An absolute URI reference specifying the location of an XML Schema + * resource, or {@code null} if no reference is found. + * @throws XMLStreamException If an error occurs while reading the source instance. + */ + public static URI extractSchemaReference(Source source, String baseURI) throws XMLStreamException { + XMLInputFactory factory = XMLInputFactory.newInstance(); + XMLEventReader reader = factory.createXMLEventReader(source); + // advance to document element + StartElement docElem = reader.nextTag().asStartElement(); + QName qName = docElem.getName(); + String namespace = qName.getNamespaceURI(); + Attribute schemaLoc = docElem + .getAttributeByName(new QName(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "schemaLocation")); + String[] uriValues = new String[] {}; + if (null != schemaLoc) { + uriValues = schemaLoc.getValue().split("\\s"); + } + URI schemaURI = null; + // one or more pairs of [namespace name] [schema location] + for (int i = 0; i < uriValues.length; i += 2) { + if (uriValues[i].equals(namespace)) { + if (!URI.create(uriValues[i + 1]).isAbsolute() && (null != source.getSystemId())) { + String schemaRef = URIUtils.resolveRelativeURI(source.getSystemId(), uriValues[i + 1]); + if (schemaRef.startsWith("file") && !new File(schemaRef).exists() && (null != baseURI)) { + schemaRef = URIUtils.resolveRelativeURI(baseURI, uriValues[i + 1]); + } + schemaURI = URI.create(schemaRef); + } + else { + schemaURI = URI.create(uriValues[i + 1]); + } + break; + } + } + return schemaURI; + } - /** - * Creates a single Schema object representing the complete set of XML - * Schema constraints that apply to WFS 2.0 message entities. - * - * @return An immutable Schema object, or null if one cannot be - * constructed. - * - * @see XML Schema for WFS 2.0 - */ - public static Schema createWFSSchema() { - URL entityCatalog = ValidationUtils.class.getResource(ROOT_PKG - + "schema-catalog.xml"); - XmlSchemaCompiler xsdCompiler = new XmlSchemaCompiler(entityCatalog); - Schema wfsSchema = null; - try { - URL schemaURL = ValidationUtils.class.getResource(ROOT_PKG - + "xsd/opengis/wfs/2.0/wfs.xsd"); - Source xsdSource = new StreamSource(schemaURL.toString()); - wfsSchema = xsdCompiler - .compileXmlSchema(new Source[] { xsdSource }); - } catch (SAXException e) { - TestSuiteLogger.log(Level.WARNING, - "Failed to create WFS Schema object.", e); - } - return wfsSchema; - } + /** + * Creates a single Schema object representing the complete set of XML Schema + * constraints that apply to WFS 2.0 message entities. + * @return An immutable Schema object, or null if one cannot be + * constructed. + * + * @see XML + * Schema for WFS 2.0 + */ + public static Schema createWFSSchema() { + URL entityCatalog = ValidationUtils.class.getResource(ROOT_PKG + "schema-catalog.xml"); + XmlSchemaCompiler xsdCompiler = new XmlSchemaCompiler(entityCatalog); + Schema wfsSchema = null; + try { + URL schemaURL = ValidationUtils.class.getResource(ROOT_PKG + "xsd/opengis/wfs/2.0/wfs.xsd"); + Source xsdSource = new StreamSource(schemaURL.toString()); + wfsSchema = xsdCompiler.compileXmlSchema(new Source[] { xsdSource }); + } + catch (SAXException e) { + TestSuiteLogger.log(Level.WARNING, "Failed to create WFS Schema object.", e); + } + return wfsSchema; + } } diff --git a/src/main/java/org/opengis/cite/iso19142/util/WFSClient.java b/src/main/java/org/opengis/cite/iso19142/util/WFSClient.java index c6c0b3da..a031ed5a 100644 --- a/src/main/java/org/opengis/cite/iso19142/util/WFSClient.java +++ b/src/main/java/org/opengis/cite/iso19142/util/WFSClient.java @@ -39,548 +39,491 @@ import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.UriBuilder; - /** * A WFS 2.0 client component. */ public class WFSClient { - private static final Logger LOGR = Logger.getLogger(WFSClient.class.getPackage().getName()); - protected Client client; - /** A Document that describes the service under test. */ - protected Document wfsMetadata; - /** The set of message bindings broadly implemented by the SUT. */ - protected Set globalBindings; - /** The list of feature types recognized by the SUT. */ - protected List featureTypes; - /** The WFS version supported by the IUT. */ - private String wfsVersion; - - /** - * Default client constructor. The client is configured to consume SOAP - * message entities. The request and response may be logged to a default JDK - * logger (in the namespace "com.sun.jersey.api.client"). TODO - */ - public WFSClient() { - ClientConfig config = new ClientConfig(); - config.register(new LoggingFeature(LOGR, Level.ALL, LoggingFeature.Verbosity.PAYLOAD_ANY, 5000)); - this.client = ClientBuilder.newClient(config); - this.client.register(new SOAPMessageConsumer()); - } - - /** - * Constructs a client that is aware of the capabilities of a WFS - * implementation. - * - * @param wfsMetadata - * A service description (e.g. WFS capabilities document). - */ - public WFSClient(Document wfsMetadata) { - this(); - String docElemName = wfsMetadata.getDocumentElement().getLocalName(); - if (!docElemName.equals(WFS2.WFS_CAPABILITIES)) { - throw new IllegalArgumentException("Not a WFS service description: " + docElemName); - } - this.wfsMetadata = wfsMetadata; - this.wfsVersion = wfsMetadata.getDocumentElement().getAttribute("version"); - this.featureTypes = ServiceMetadataUtils.getFeatureTypes(wfsMetadata); - this.globalBindings = ServiceMetadataUtils.getGlobalBindings(wfsMetadata); - } - - /** - * Returns the WFS service description set for this client. - * - * @return A WFS capabilities document (wfs:WFS_Capabilities). - */ - public Document getServiceDescription() { - return wfsMetadata; - } - - /** - * Returns the underlying JAX-RS client. - * - * @return A JAX-RS client component. - */ - public Client getClient() { - return client; - } - - /** - * Sets the service description obtained using the given InputStream. The - * standard description is an XML representation of a WFS capabilities - * document. - * - * @param srvMetadata - * An InputStream supplying the service metadata. - * @throws SAXException - * If any I/O errors occur. - * @throws IOException - * If any parsing errors occur. - */ - public void setServiceDescription(InputStream srvMetadata) throws SAXException, IOException { - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - factory.setNamespaceAware(true); - try { - DocumentBuilder docBuilder = factory.newDocumentBuilder(); - Document doc = docBuilder.parse(srvMetadata); - if (doc.getDocumentElement().getLocalName().equals(WFS2.WFS_CAPABILITIES)) - this.wfsMetadata = docBuilder.parse(srvMetadata); - } catch (ParserConfigurationException e) { - LOGR.log(Level.WARNING, e.getMessage()); - } - } - - /** - * Invokes a stored query using any supported protocol binding (request - * encoding). - * - * @param queryId - * A stored query identifier. - * @param params - * A collection of query parameters distinguished by name (may be - * empty, e.g. {@literal Collections..emptyMap()} - * ). - * @return A Document representing the XML response entity, or {@code null} - * if the response doesn't contain one. - */ - public Document invokeStoredQuery(String queryId, Map params) { - if (this.wfsVersion.equals(WFS2.V2_0_0) && queryId.equals(WFS2.QRY_GET_FEATURE_BY_ID)) { - // use deprecated URN identifier in WFS 2.0.0 - queryId = WFS2.QRY_GET_FEATURE_BY_ID_URN; - } - Document req = WFSMessage.createRequestEntity("GetFeature", this.wfsVersion); - WFSMessage.appendStoredQuery(req, queryId, params); - ProtocolBinding binding = globalBindings.iterator().next(); - return retrieveXMLResponseEntity(req, binding); - } - - /** - * Retrieves feature representations by type name. - * - * @param typeName - * A QName denoting the feature type. - * @param count - * The maximum number of features to fetch (> 0). If count - * < 1, the default value (10) applies. - * @param binding - * The ProtocolBinding to use for this request; if {@code null} a - * global binding will be used. - * @return A Document representing the XML response entity, or {@code null} - * if the response doesn't contain one. - */ - public Document getFeatureByType(QName typeName, int count, ProtocolBinding binding) { - if (null == binding) { - binding = globalBindings.iterator().next(); - } - Document req = WFSMessage.createRequestEntity("GetFeature", this.wfsVersion); - if (count > 0) { - req.getDocumentElement().setAttribute("count", Integer.toString(count)); - } - WFSMessage.appendSimpleQuery(req, typeName); - return retrieveXMLResponseEntity(req, binding); - } - - /** - * Submits a GetFeature request. - * - * @param reqEntity - * A Source representing the content of the request entity; if - * necessary it will be transformed into the query component of a - * GET request. - * @param binding - * The HTTP method binding to use. - * @return A client response context. - */ - public Response getFeature(Source reqEntity, ProtocolBinding binding) { - URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.GET_FEATURE, binding); - return submitRequest(reqEntity, binding, endpoint); - } - - /** - * Submits a request to delete a collection of features specified by - * identifier and type name. - * - * @param features - * A Map containing entries that specify a feature by identifier - * (gml:id attribute value) and type name (QName). - * @param binding - * The ProtocolBinding to use. - * @return A Document representing the XML response entity, or {@code null} - * if the response doesn't contain one. - */ - public Document deleteFeatures(Map features, ProtocolBinding binding) { - Document req = WFSMessage.createRequestEntity(WFS2.TRANSACTION, this.wfsVersion); - for (Map.Entry entry : features.entrySet()) { - QName typeName = entry.getValue(); - Element delete = req.createElementNS(Namespaces.WFS, "Delete"); - delete.setPrefix("wfs"); - delete.setAttribute(XMLConstants.XMLNS_ATTRIBUTE + ":tns", typeName.getNamespaceURI()); - delete.setAttribute("typeName", "tns:" + typeName.getLocalPart()); - req.getDocumentElement().appendChild(delete); - Element filter = req.createElementNS(Namespaces.FES, "Filter"); - delete.appendChild(filter); - Element resourceId = req.createElementNS(Namespaces.FES, "ResourceId"); - resourceId.setAttribute("rid", entry.getKey()); - filter.appendChild(resourceId); - } - if (TestSuiteLogger.isLoggable(Level.FINE)) { - TestSuiteLogger.log(Level.FINE, XMLUtils.writeNodeToString(req)); - } - return executeTransaction(req, binding); - } - - /** - * Submits a request to delete a feature. A wfs:Delete element is appended - * to the given transaction request entity. - * - * @param reqEntity - * A WFS transaction request entity (wfs:Transaction). - * @param id - * A feature identifier. - * @param typeName - * The type name of the feature. - * @return A representation of the HTTP response. - */ - public Response deleteFeature(Document reqEntity, String id, QName typeName) { - Element delete = reqEntity.createElementNS(Namespaces.WFS, "Delete"); - delete.setPrefix("wfs"); - delete.setAttribute(XMLConstants.XMLNS_ATTRIBUTE + ":tns", typeName.getNamespaceURI()); - delete.setAttribute("typeName", "tns:" + typeName.getLocalPart()); - reqEntity.getDocumentElement().appendChild(delete); - Element filter = reqEntity.createElementNS(Namespaces.FES, "Filter"); - delete.appendChild(filter); - Element resourceId = reqEntity.createElementNS(Namespaces.FES, "ResourceId"); - resourceId.setAttribute("rid", id); - filter.appendChild(resourceId); - return submitRequest(reqEntity, ProtocolBinding.POST); - } - - /** - * Submits a request to retrieve one or more feature versions as specified - * by the given resource identifier. - * - * @param rid - * A resource identifier that selects members of the version - * chain to which this identified version belongs. - * @param typeName - * The name of a feature type. - * @return The (JAX-RS) client response message. - */ - public Response GetFeatureVersion(ResourceId rid, QName typeName) { - Document req = WFSMessage.createRequestEntity("GetFeature-Minimal", this.wfsVersion); - Element qry = WFSMessage.appendSimpleQuery(req, typeName); - Element filter = req.createElementNS(Namespaces.FES, "Filter"); - filter.appendChild(req.importNode(rid.toElement(), true)); - qry.appendChild(filter); - return submitRequest(req, ProtocolBinding.ANY); - } - - /** - * Submits a request to insert a collection of GML feature instances. - * - * @param features - * A {@literal List} containing one or more feature - * representations. - * @param binding - * The ProtocolBinding to use. - * @return A Document representing the XML response entity, or {@code null} - * if the response doesn't contain one. - */ - public Document insert(List features, ProtocolBinding binding) { - if (features.isEmpty()) { - throw new IllegalArgumentException("No features instances to insert."); - } - Document req = WFSMessage.createRequestEntity(WFS2.TRANSACTION, this.wfsVersion); - Element insert = req.createElementNS(Namespaces.WFS, "Insert"); - insert.setPrefix("wfs"); - req.getDocumentElement().appendChild(insert); - for (Element feature : features) { - insert.appendChild(req.importNode(feature, true)); - } - if (TestSuiteLogger.isLoggable(Level.FINE)) { - TestSuiteLogger.log(Level.FINE, XMLUtils.writeNodeToString(req)); - } - return executeTransaction(req, binding); - } - - /** - * Submits a request to update a feature using the POST protocol binding. - * - * @param id - * A feature identifier. - * @param featureType - * The qualified name of the feature type. - * @param properties - * A Map containing the feature properties to be updated. - * @return A Document representing the XML response entity. - * - * @see #updateFeature(Document, String, QName, Map, ProtocolBinding) - */ - public Document updateFeature(String id, QName featureType, Map properties) { - Document req = WFSMessage.createRequestEntity(WFS2.TRANSACTION, this.wfsVersion); - return updateFeature(req, id, featureType, properties, ProtocolBinding.POST); - } - - /** - * Submits a request to update a feature. - * - * @param req - * An empty wfs:Transaction request entity. - * @param id - * The GML identifier of the feature to be updated (gml:id - * attribute). - * @param featureType - * The type of the feature instance. - * @param properties - * A Map containing the feature properties to be updated - * (replaced). Each entry consists of a value reference (an XPath - * expression) and a value object. The value may be a Node - * representing a complex property value; otherwise it is treated - * as a simple value by calling the object's toString() method. - * @param binding - * The ProtocolBinding to use. - * @return A Document representing the XML response entity, or {@code null} - * if the response doesn't contain one. - */ - public Document updateFeature(Document req, String id, QName featureType, Map properties, - ProtocolBinding binding) { - Element update = req.createElementNS(Namespaces.WFS, "Update"); - update.setPrefix("wfs"); - update.setAttribute("handle", "Update"); - req.getDocumentElement().appendChild(update); - WFSMessage.setTypeName(update, featureType); - for (Map.Entry property : properties.entrySet()) { - Element prop = req.createElementNS(Namespaces.WFS, "Property"); - prop.setPrefix("wfs"); - Element valueRef = req.createElementNS(Namespaces.WFS, "ValueReference"); - valueRef.setTextContent(property.getKey()); - valueRef.setPrefix("wfs"); - prop.appendChild(valueRef); - Element value = req.createElementNS(Namespaces.WFS, "Value"); - value.setPrefix("wfs"); - if (Node.class.isInstance(property.getValue())) { - value.appendChild((Node) property.getValue()); - } else { - value.setTextContent(property.getValue().toString()); - } - prop.appendChild(value); - update.appendChild(prop); - } - Element filter = WFSMessage.newResourceIdFilter(id); - update.appendChild(req.adoptNode(filter)); - if (TestSuiteLogger.isLoggable(Level.FINE)) { - TestSuiteLogger.log(Level.FINE, XMLUtils.writeNodeToString(req)); - } - return executeTransaction(req, binding); - } - - /** - * Submits an HTTP request message. For GET requests the XML request entity - * is serialized to its corresponding KVP string format and added to the - * query component of the Request-URI. For SOAP requests that adhere to the - * "Request-Response" message exchange pattern, the outbound message entity - * is a SOAP envelope containing the standard XML request in the body. - * - * @param entity - * An XML representation of the request entity. - * @param binding - * The {@link ProtocolBinding} to use. - * @param endpoint - * The service endpoint. - * @return A Response object representing the response message. - */ - public Response submitRequest(Source entity, ProtocolBinding binding, URI endpoint) { - - WebTarget target = client.target(endpoint); - target = target.queryParam(WFS2.REQUEST_PARAM, WFS2.GET_CAPABILITIES); - Builder builder = target.request(); - LOGR.log(Level.FINE, String.format("Submitting %s request to URI %s", binding, target.getUri())); - Response response = null; - switch (binding) { - case GET: - String queryString = WFSMessage.transformEntityToKVP(entity); - URI requestURI = UriBuilder.fromUri(target.getUri()).replaceQuery(queryString).build(); - LOGR.log(Level.FINE, String.format("Request URI: %s", requestURI)); - target = client.target(requestURI); - builder = target.request(); - response = builder.accept(MediaType.APPLICATION_XML_TYPE).buildGet().invoke(); - break; - case POST: - response = builder.accept(MediaType.APPLICATION_XML_TYPE).buildPost(Entity.entity(entity, MediaType.APPLICATION_XML_TYPE)).invoke(); - break; - case SOAP: - Document soapEnv = WFSMessage.wrapEntityInSOAPEnvelope( entity, determineSoapVersion() ); - response = builder.accept(MediaType.valueOf(WFS2.APPLICATION_SOAP)).buildPost(Entity.entity(new DOMSource(soapEnv), MediaType.valueOf(WFS2.APPLICATION_SOAP))).invoke(); - break; - default: - throw new IllegalArgumentException("Unsupported message binding: " + binding); - } - return response; - } - - /** - * Submits a request using the specified message binding and the content of - * the given XML request entity. - * - * @param reqEntity - * A DOM Document representing the content of the request - * message. - * @param binding - * The ProtocolBinding to use; may be {@link ProtocolBinding#ANY} - * if any supported binding can be used. - * @return A Response object representing the response message. - */ - public Response submitRequest(Document reqEntity, ProtocolBinding binding) { - String requestName = reqEntity.getDocumentElement().getLocalName(); - Map endpoints = ServiceMetadataUtils.getRequestEndpoints(this.wfsMetadata, requestName); - if (null == endpoints) { - throw new IllegalArgumentException("No HTTP method bindings found for " + requestName); - } - if ((null == binding) || binding.equals(ProtocolBinding.ANY)) { - String methodName = endpoints.keySet().iterator().next(); - binding = Enum.valueOf(ProtocolBinding.class, methodName); - } - // SOAP Request-Response MEP bound to HTTP POST - String httpMethod = (binding == ProtocolBinding.SOAP) ? ProtocolBinding.POST.name() : binding.name(); - return submitRequest(new DOMSource(reqEntity), binding, endpoints.get(httpMethod)); - } - - /** - * Retrieves a complete representation of the capabilities document from the - * WFS implementation described by the service metadata. The - * acceptVersions parameter is omitted, so the response shall - * reflect the latest version supported by the SUT. - * - * @return A Document containing the response to a GetCapabilities request, - * or {@code null} if one could not be obtained. - */ - public Document getCapabilities() { - if (null == this.wfsMetadata) { - throw new IllegalStateException("Service description is unavailable."); - } - URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.GET_CAPABILITIES, - ProtocolBinding.GET); - WebTarget target = client.target(endpoint); - target = target.queryParam(WFS2.REQUEST_PARAM, WFS2.GET_CAPABILITIES); - target = target.queryParam(WFS2.SERVICE_PARAM, WFS2.SERVICE_TYPE_CODE); - Builder builder = target.request(); - return builder.buildGet().invoke(Document.class); - } - - /** - * Returns a protocol binding suitable for transaction requests. Any binding - * advertised in the service capabilities document is returned. - * - * @return A supported ProtocolBinding instance (POST or SOAP). - */ - public ProtocolBinding getAnyTransactionBinding() { - Set trxBindings = ServiceMetadataUtils.getOperationBindings(this.wfsMetadata, - WFS2.TRANSACTION); - return trxBindings.iterator().next(); - } - - /** - * Executes a WFS transaction. - * - * @param request - * A Document node representing a wfs:Transaction request entity. - * @param binding - * The ProtocolBinding to use - * @return A Document node representing the response entity. - */ - Document executeTransaction(Document request, ProtocolBinding binding) { - if (binding == ProtocolBinding.ANY) { - binding = getAnyTransactionBinding(); - } - URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.TRANSACTION, binding); - if (null == endpoint.getScheme()) { - throw new IllegalArgumentException("No Transaction endpoint found for binding " + binding); - } - LOGR.log(Level.FINE, String.format("Submitting request entity to URI %s \n%s", endpoint, - XMLUtils.writeNodeToString(request))); - Response rsp = submitRequest(new DOMSource(request), binding, endpoint); - Document entity = null; - if (rsp.hasEntity()) { - entity = rsp.readEntity(Document.class); - } - return entity; - } - - /** - * Submits the given request entity and returns the response entity as a DOM - * Document. - * - * @param request - * An XML representation of the request entity; the actual - * request depends on the message binding in use. - * @param binding - * The ProtocolBinding to use (GET, POST, or SOAP). - * @return A DOM Document containing the response entity, or {@code null} if - * the request failed or the message body could not be parsed. - */ - Document retrieveXMLResponseEntity(Document request, ProtocolBinding binding) { - if (LOGR.isLoggable(Level.FINE)) { - LOGR.fine("Request entity:\n" + XMLUtils.writeNodeToString(request)); - } - URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, - request.getDocumentElement().getLocalName(), binding); - Response rsp = submitRequest(new DOMSource(request), binding, endpoint); - Document rspEntity = null; - if (rsp.hasEntity()) { - MediaType mediaType = rsp.getMediaType(); - if (!mediaType.getSubtype().endsWith("xml")) { - throw new RuntimeException("Did not receive an XML entity: " + mediaType); - } - rspEntity = rsp.readEntity(Document.class); - if (LOGR.isLoggable(Level.FINE)) { - LOGR.fine("Response entity:\n" + XMLUtils.writeNodeToString(rspEntity)); - } - } - return rspEntity; - } - - /** - * Submits a request to delete a stored query. - * - * @param queryId - * A URI value that identifies the query to be dropped. - * @return The HTTP status code. - */ - public int deleteStoredQuery(String queryId) { - Document req = WFSMessage.createRequestEntity("DropStoredQuery", this.wfsVersion); - req.getDocumentElement().setAttribute("id", queryId); - ProtocolBinding binding = ProtocolBinding.POST; - URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, - req.getDocumentElement().getLocalName(), binding); - Response rsp = submitRequest(new DOMSource(req), binding, endpoint); - return rsp.getStatus(); - } - - /** - * Requests a list of stored queries. - * - * @return A list of query identifiers. - */ - public List listStoredQueries() { - Document req = WFSMessage.createRequestEntity(WFS2.LIST_STORED_QUERIES, this.wfsVersion); - // use any supported HTTP method - ProtocolBinding binding = ServiceMetadataUtils.getOperationBindings(wfsMetadata, WFS2.LIST_STORED_QUERIES) - .iterator().next(); - URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.LIST_STORED_QUERIES, binding); - Response rsp = submitRequest(new DOMSource(req), binding, endpoint); - Document rspEntity = rsp.readEntity(Document.class); - NodeList qryList = rspEntity.getElementsByTagNameNS(Namespaces.WFS, "StoredQuery"); - List idList = new ArrayList<>(); - for (int i = 0; i < qryList.getLength(); i++) { - idList.add(Element.class.cast(qryList.item(i)).getAttribute("id")); - } - return idList; - } - - private String determineSoapVersion() { - if ( "2.0.2".equals( this.wfsVersion ) ) - return WFS2.SOAP_VERSION_1_2; - return WFS2.SOAP_VERSION; - } + private static final Logger LOGR = Logger.getLogger(WFSClient.class.getPackage().getName()); + + protected Client client; + + /** A Document that describes the service under test. */ + protected Document wfsMetadata; + + /** The set of message bindings broadly implemented by the SUT. */ + protected Set globalBindings; + + /** The list of feature types recognized by the SUT. */ + protected List featureTypes; + + /** The WFS version supported by the IUT. */ + private String wfsVersion; + + /** + * Default client constructor. The client is configured to consume SOAP message + * entities. The request and response may be logged to a default JDK logger (in the + * namespace "com.sun.jersey.api.client"). TODO + */ + public WFSClient() { + ClientConfig config = new ClientConfig(); + config.register(new LoggingFeature(LOGR, Level.ALL, LoggingFeature.Verbosity.PAYLOAD_ANY, 5000)); + this.client = ClientBuilder.newClient(config); + this.client.register(new SOAPMessageConsumer()); + } + + /** + * Constructs a client that is aware of the capabilities of a WFS implementation. + * @param wfsMetadata A service description (e.g. WFS capabilities document). + */ + public WFSClient(Document wfsMetadata) { + this(); + String docElemName = wfsMetadata.getDocumentElement().getLocalName(); + if (!docElemName.equals(WFS2.WFS_CAPABILITIES)) { + throw new IllegalArgumentException("Not a WFS service description: " + docElemName); + } + this.wfsMetadata = wfsMetadata; + this.wfsVersion = wfsMetadata.getDocumentElement().getAttribute("version"); + this.featureTypes = ServiceMetadataUtils.getFeatureTypes(wfsMetadata); + this.globalBindings = ServiceMetadataUtils.getGlobalBindings(wfsMetadata); + } + + /** + * Returns the WFS service description set for this client. + * @return A WFS capabilities document (wfs:WFS_Capabilities). + */ + public Document getServiceDescription() { + return wfsMetadata; + } + + /** + * Returns the underlying JAX-RS client. + * @return A JAX-RS client component. + */ + public Client getClient() { + return client; + } + + /** + * Sets the service description obtained using the given InputStream. The standard + * description is an XML representation of a WFS capabilities document. + * @param srvMetadata An InputStream supplying the service metadata. + * @throws SAXException If any I/O errors occur. + * @throws IOException If any parsing errors occur. + */ + public void setServiceDescription(InputStream srvMetadata) throws SAXException, IOException { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + try { + DocumentBuilder docBuilder = factory.newDocumentBuilder(); + Document doc = docBuilder.parse(srvMetadata); + if (doc.getDocumentElement().getLocalName().equals(WFS2.WFS_CAPABILITIES)) + this.wfsMetadata = docBuilder.parse(srvMetadata); + } + catch (ParserConfigurationException e) { + LOGR.log(Level.WARNING, e.getMessage()); + } + } + + /** + * Invokes a stored query using any supported protocol binding (request encoding). + * @param queryId A stored query identifier. + * @param params A collection of query parameters distinguished by name (may be empty, + * e.g. {@literal Collections..emptyMap()} ). + * @return A Document representing the XML response entity, or {@code null} if the + * response doesn't contain one. + */ + public Document invokeStoredQuery(String queryId, Map params) { + if (this.wfsVersion.equals(WFS2.V2_0_0) && queryId.equals(WFS2.QRY_GET_FEATURE_BY_ID)) { + // use deprecated URN identifier in WFS 2.0.0 + queryId = WFS2.QRY_GET_FEATURE_BY_ID_URN; + } + Document req = WFSMessage.createRequestEntity("GetFeature", this.wfsVersion); + WFSMessage.appendStoredQuery(req, queryId, params); + ProtocolBinding binding = globalBindings.iterator().next(); + return retrieveXMLResponseEntity(req, binding); + } + + /** + * Retrieves feature representations by type name. + * @param typeName A QName denoting the feature type. + * @param count The maximum number of features to fetch (> 0). If count < 1, the + * default value (10) applies. + * @param binding The ProtocolBinding to use for this request; if {@code null} a + * global binding will be used. + * @return A Document representing the XML response entity, or {@code null} if the + * response doesn't contain one. + */ + public Document getFeatureByType(QName typeName, int count, ProtocolBinding binding) { + if (null == binding) { + binding = globalBindings.iterator().next(); + } + Document req = WFSMessage.createRequestEntity("GetFeature", this.wfsVersion); + if (count > 0) { + req.getDocumentElement().setAttribute("count", Integer.toString(count)); + } + WFSMessage.appendSimpleQuery(req, typeName); + return retrieveXMLResponseEntity(req, binding); + } + + /** + * Submits a GetFeature request. + * @param reqEntity A Source representing the content of the request entity; if + * necessary it will be transformed into the query component of a GET request. + * @param binding The HTTP method binding to use. + * @return A client response context. + */ + public Response getFeature(Source reqEntity, ProtocolBinding binding) { + URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.GET_FEATURE, binding); + return submitRequest(reqEntity, binding, endpoint); + } + + /** + * Submits a request to delete a collection of features specified by identifier and + * type name. + * @param features A Map containing entries that specify a feature by identifier + * (gml:id attribute value) and type name (QName). + * @param binding The ProtocolBinding to use. + * @return A Document representing the XML response entity, or {@code null} if the + * response doesn't contain one. + */ + public Document deleteFeatures(Map features, ProtocolBinding binding) { + Document req = WFSMessage.createRequestEntity(WFS2.TRANSACTION, this.wfsVersion); + for (Map.Entry entry : features.entrySet()) { + QName typeName = entry.getValue(); + Element delete = req.createElementNS(Namespaces.WFS, "Delete"); + delete.setPrefix("wfs"); + delete.setAttribute(XMLConstants.XMLNS_ATTRIBUTE + ":tns", typeName.getNamespaceURI()); + delete.setAttribute("typeName", "tns:" + typeName.getLocalPart()); + req.getDocumentElement().appendChild(delete); + Element filter = req.createElementNS(Namespaces.FES, "Filter"); + delete.appendChild(filter); + Element resourceId = req.createElementNS(Namespaces.FES, "ResourceId"); + resourceId.setAttribute("rid", entry.getKey()); + filter.appendChild(resourceId); + } + if (TestSuiteLogger.isLoggable(Level.FINE)) { + TestSuiteLogger.log(Level.FINE, XMLUtils.writeNodeToString(req)); + } + return executeTransaction(req, binding); + } + + /** + * Submits a request to delete a feature. A wfs:Delete element is appended to the + * given transaction request entity. + * @param reqEntity A WFS transaction request entity (wfs:Transaction). + * @param id A feature identifier. + * @param typeName The type name of the feature. + * @return A representation of the HTTP response. + */ + public Response deleteFeature(Document reqEntity, String id, QName typeName) { + Element delete = reqEntity.createElementNS(Namespaces.WFS, "Delete"); + delete.setPrefix("wfs"); + delete.setAttribute(XMLConstants.XMLNS_ATTRIBUTE + ":tns", typeName.getNamespaceURI()); + delete.setAttribute("typeName", "tns:" + typeName.getLocalPart()); + reqEntity.getDocumentElement().appendChild(delete); + Element filter = reqEntity.createElementNS(Namespaces.FES, "Filter"); + delete.appendChild(filter); + Element resourceId = reqEntity.createElementNS(Namespaces.FES, "ResourceId"); + resourceId.setAttribute("rid", id); + filter.appendChild(resourceId); + return submitRequest(reqEntity, ProtocolBinding.POST); + } + + /** + * Submits a request to retrieve one or more feature versions as specified by the + * given resource identifier. + * @param rid A resource identifier that selects members of the version chain to which + * this identified version belongs. + * @param typeName The name of a feature type. + * @return The (JAX-RS) client response message. + */ + public Response GetFeatureVersion(ResourceId rid, QName typeName) { + Document req = WFSMessage.createRequestEntity("GetFeature-Minimal", this.wfsVersion); + Element qry = WFSMessage.appendSimpleQuery(req, typeName); + Element filter = req.createElementNS(Namespaces.FES, "Filter"); + filter.appendChild(req.importNode(rid.toElement(), true)); + qry.appendChild(filter); + return submitRequest(req, ProtocolBinding.ANY); + } + + /** + * Submits a request to insert a collection of GML feature instances. + * @param features A {@literal List} containing one or more feature + * representations. + * @param binding The ProtocolBinding to use. + * @return A Document representing the XML response entity, or {@code null} if the + * response doesn't contain one. + */ + public Document insert(List features, ProtocolBinding binding) { + if (features.isEmpty()) { + throw new IllegalArgumentException("No features instances to insert."); + } + Document req = WFSMessage.createRequestEntity(WFS2.TRANSACTION, this.wfsVersion); + Element insert = req.createElementNS(Namespaces.WFS, "Insert"); + insert.setPrefix("wfs"); + req.getDocumentElement().appendChild(insert); + for (Element feature : features) { + insert.appendChild(req.importNode(feature, true)); + } + if (TestSuiteLogger.isLoggable(Level.FINE)) { + TestSuiteLogger.log(Level.FINE, XMLUtils.writeNodeToString(req)); + } + return executeTransaction(req, binding); + } + + /** + * Submits a request to update a feature using the POST protocol binding. + * @param id A feature identifier. + * @param featureType The qualified name of the feature type. + * @param properties A Map containing the feature properties to be updated. + * @return A Document representing the XML response entity. + * + * @see #updateFeature(Document, String, QName, Map, ProtocolBinding) + */ + public Document updateFeature(String id, QName featureType, Map properties) { + Document req = WFSMessage.createRequestEntity(WFS2.TRANSACTION, this.wfsVersion); + return updateFeature(req, id, featureType, properties, ProtocolBinding.POST); + } + + /** + * Submits a request to update a feature. + * @param req An empty wfs:Transaction request entity. + * @param id The GML identifier of the feature to be updated (gml:id attribute). + * @param featureType The type of the feature instance. + * @param properties A Map containing the feature properties to be updated (replaced). + * Each entry consists of a value reference (an XPath expression) and a value object. + * The value may be a Node representing a complex property value; otherwise it is + * treated as a simple value by calling the object's toString() method. + * @param binding The ProtocolBinding to use. + * @return A Document representing the XML response entity, or {@code null} if the + * response doesn't contain one. + */ + public Document updateFeature(Document req, String id, QName featureType, Map properties, + ProtocolBinding binding) { + Element update = req.createElementNS(Namespaces.WFS, "Update"); + update.setPrefix("wfs"); + update.setAttribute("handle", "Update"); + req.getDocumentElement().appendChild(update); + WFSMessage.setTypeName(update, featureType); + for (Map.Entry property : properties.entrySet()) { + Element prop = req.createElementNS(Namespaces.WFS, "Property"); + prop.setPrefix("wfs"); + Element valueRef = req.createElementNS(Namespaces.WFS, "ValueReference"); + valueRef.setTextContent(property.getKey()); + valueRef.setPrefix("wfs"); + prop.appendChild(valueRef); + Element value = req.createElementNS(Namespaces.WFS, "Value"); + value.setPrefix("wfs"); + if (Node.class.isInstance(property.getValue())) { + value.appendChild((Node) property.getValue()); + } + else { + value.setTextContent(property.getValue().toString()); + } + prop.appendChild(value); + update.appendChild(prop); + } + Element filter = WFSMessage.newResourceIdFilter(id); + update.appendChild(req.adoptNode(filter)); + if (TestSuiteLogger.isLoggable(Level.FINE)) { + TestSuiteLogger.log(Level.FINE, XMLUtils.writeNodeToString(req)); + } + return executeTransaction(req, binding); + } + + /** + * Submits an HTTP request message. For GET requests the XML request entity is + * serialized to its corresponding KVP string format and added to the query component + * of the Request-URI. For SOAP requests that adhere to the "Request-Response" message + * exchange pattern, the outbound message entity is a SOAP envelope containing the + * standard XML request in the body. + * @param entity An XML representation of the request entity. + * @param binding The {@link ProtocolBinding} to use. + * @param endpoint The service endpoint. + * @return A Response object representing the response message. + */ + public Response submitRequest(Source entity, ProtocolBinding binding, URI endpoint) { + + WebTarget target = client.target(endpoint); + target = target.queryParam(WFS2.REQUEST_PARAM, WFS2.GET_CAPABILITIES); + Builder builder = target.request(); + LOGR.log(Level.FINE, String.format("Submitting %s request to URI %s", binding, target.getUri())); + Response response = null; + switch (binding) { + case GET: + String queryString = WFSMessage.transformEntityToKVP(entity); + URI requestURI = UriBuilder.fromUri(target.getUri()).replaceQuery(queryString).build(); + LOGR.log(Level.FINE, String.format("Request URI: %s", requestURI)); + target = client.target(requestURI); + builder = target.request(); + response = builder.accept(MediaType.APPLICATION_XML_TYPE).buildGet().invoke(); + break; + case POST: + response = builder.accept(MediaType.APPLICATION_XML_TYPE) + .buildPost(Entity.entity(entity, MediaType.APPLICATION_XML_TYPE)) + .invoke(); + break; + case SOAP: + Document soapEnv = WFSMessage.wrapEntityInSOAPEnvelope(entity, determineSoapVersion()); + response = builder.accept(MediaType.valueOf(WFS2.APPLICATION_SOAP)) + .buildPost(Entity.entity(new DOMSource(soapEnv), MediaType.valueOf(WFS2.APPLICATION_SOAP))) + .invoke(); + break; + default: + throw new IllegalArgumentException("Unsupported message binding: " + binding); + } + return response; + } + + /** + * Submits a request using the specified message binding and the content of the given + * XML request entity. + * @param reqEntity A DOM Document representing the content of the request message. + * @param binding The ProtocolBinding to use; may be {@link ProtocolBinding#ANY} if + * any supported binding can be used. + * @return A Response object representing the response message. + */ + public Response submitRequest(Document reqEntity, ProtocolBinding binding) { + String requestName = reqEntity.getDocumentElement().getLocalName(); + Map endpoints = ServiceMetadataUtils.getRequestEndpoints(this.wfsMetadata, requestName); + if (null == endpoints) { + throw new IllegalArgumentException("No HTTP method bindings found for " + requestName); + } + if ((null == binding) || binding.equals(ProtocolBinding.ANY)) { + String methodName = endpoints.keySet().iterator().next(); + binding = Enum.valueOf(ProtocolBinding.class, methodName); + } + // SOAP Request-Response MEP bound to HTTP POST + String httpMethod = (binding == ProtocolBinding.SOAP) ? ProtocolBinding.POST.name() : binding.name(); + return submitRequest(new DOMSource(reqEntity), binding, endpoints.get(httpMethod)); + } + + /** + * Retrieves a complete representation of the capabilities document from the WFS + * implementation described by the service metadata. The acceptVersions + * parameter is omitted, so the response shall reflect the latest version supported by + * the SUT. + * @return A Document containing the response to a GetCapabilities request, or + * {@code null} if one could not be obtained. + */ + public Document getCapabilities() { + if (null == this.wfsMetadata) { + throw new IllegalStateException("Service description is unavailable."); + } + URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.GET_CAPABILITIES, + ProtocolBinding.GET); + WebTarget target = client.target(endpoint); + target = target.queryParam(WFS2.REQUEST_PARAM, WFS2.GET_CAPABILITIES); + target = target.queryParam(WFS2.SERVICE_PARAM, WFS2.SERVICE_TYPE_CODE); + Builder builder = target.request(); + return builder.buildGet().invoke(Document.class); + } + + /** + * Returns a protocol binding suitable for transaction requests. Any binding + * advertised in the service capabilities document is returned. + * @return A supported ProtocolBinding instance (POST or SOAP). + */ + public ProtocolBinding getAnyTransactionBinding() { + Set trxBindings = ServiceMetadataUtils.getOperationBindings(this.wfsMetadata, + WFS2.TRANSACTION); + return trxBindings.iterator().next(); + } + + /** + * Executes a WFS transaction. + * @param request A Document node representing a wfs:Transaction request entity. + * @param binding The ProtocolBinding to use + * @return A Document node representing the response entity. + */ + Document executeTransaction(Document request, ProtocolBinding binding) { + if (binding == ProtocolBinding.ANY) { + binding = getAnyTransactionBinding(); + } + URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.TRANSACTION, binding); + if (null == endpoint.getScheme()) { + throw new IllegalArgumentException("No Transaction endpoint found for binding " + binding); + } + LOGR.log(Level.FINE, String.format("Submitting request entity to URI %s \n%s", endpoint, + XMLUtils.writeNodeToString(request))); + Response rsp = submitRequest(new DOMSource(request), binding, endpoint); + Document entity = null; + if (rsp.hasEntity()) { + entity = rsp.readEntity(Document.class); + } + return entity; + } + + /** + * Submits the given request entity and returns the response entity as a DOM Document. + * @param request An XML representation of the request entity; the actual request + * depends on the message binding in use. + * @param binding The ProtocolBinding to use (GET, POST, or SOAP). + * @return A DOM Document containing the response entity, or {@code null} if the + * request failed or the message body could not be parsed. + */ + Document retrieveXMLResponseEntity(Document request, ProtocolBinding binding) { + if (LOGR.isLoggable(Level.FINE)) { + LOGR.fine("Request entity:\n" + XMLUtils.writeNodeToString(request)); + } + URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, + request.getDocumentElement().getLocalName(), binding); + Response rsp = submitRequest(new DOMSource(request), binding, endpoint); + Document rspEntity = null; + if (rsp.hasEntity()) { + MediaType mediaType = rsp.getMediaType(); + if (!mediaType.getSubtype().endsWith("xml")) { + throw new RuntimeException("Did not receive an XML entity: " + mediaType); + } + rspEntity = rsp.readEntity(Document.class); + if (LOGR.isLoggable(Level.FINE)) { + LOGR.fine("Response entity:\n" + XMLUtils.writeNodeToString(rspEntity)); + } + } + return rspEntity; + } + + /** + * Submits a request to delete a stored query. + * @param queryId A URI value that identifies the query to be dropped. + * @return The HTTP status code. + */ + public int deleteStoredQuery(String queryId) { + Document req = WFSMessage.createRequestEntity("DropStoredQuery", this.wfsVersion); + req.getDocumentElement().setAttribute("id", queryId); + ProtocolBinding binding = ProtocolBinding.POST; + URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, + req.getDocumentElement().getLocalName(), binding); + Response rsp = submitRequest(new DOMSource(req), binding, endpoint); + return rsp.getStatus(); + } + + /** + * Requests a list of stored queries. + * @return A list of query identifiers. + */ + public List listStoredQueries() { + Document req = WFSMessage.createRequestEntity(WFS2.LIST_STORED_QUERIES, this.wfsVersion); + // use any supported HTTP method + ProtocolBinding binding = ServiceMetadataUtils.getOperationBindings(wfsMetadata, WFS2.LIST_STORED_QUERIES) + .iterator() + .next(); + URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.LIST_STORED_QUERIES, binding); + Response rsp = submitRequest(new DOMSource(req), binding, endpoint); + Document rspEntity = rsp.readEntity(Document.class); + NodeList qryList = rspEntity.getElementsByTagNameNS(Namespaces.WFS, "StoredQuery"); + List idList = new ArrayList<>(); + for (int i = 0; i < qryList.getLength(); i++) { + idList.add(Element.class.cast(qryList.item(i)).getAttribute("id")); + } + return idList; + } + + private String determineSoapVersion() { + if ("2.0.2".equals(this.wfsVersion)) + return WFS2.SOAP_VERSION_1_2; + return WFS2.SOAP_VERSION; + } } diff --git a/src/main/java/org/opengis/cite/iso19142/util/WFSMessage.java b/src/main/java/org/opengis/cite/iso19142/util/WFSMessage.java index 98b9037d..5de0fb71 100644 --- a/src/main/java/org/opengis/cite/iso19142/util/WFSMessage.java +++ b/src/main/java/org/opengis/cite/iso19142/util/WFSMessage.java @@ -34,554 +34,504 @@ import org.w3c.dom.NodeList; /** - * Provides various utility methods for constructing, accessing, or manipulating - * the content of WFS request and response messages. + * Provides various utility methods for constructing, accessing, or manipulating the + * content of WFS request and response messages. */ public class WFSMessage { - private static final Logger LOGR = Logger.getLogger(WFSMessage.class.getPackage().getName()); - private static final String TNS_PREFIX = "tns"; - private static final DocumentBuilder BUILDER = initDocBuilder(); - - private static DocumentBuilder initDocBuilder() { - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - factory.setNamespaceAware(true); - DocumentBuilder builder = null; - try { - builder = factory.newDocumentBuilder(); - } catch (ParserConfigurationException e) { - TestSuiteLogger.log(Level.WARNING, "Failed to create parser", e); - } - return builder; - } - - /** - * Transforms the XML representation of a WFS request entity to its - * corresponding KVP serialization format. - * - * @param xmlSource - * A Source representing the XML request entity. - * @return A String containing the resulting query component. - */ - public static String transformEntityToKVP(Source xmlSource) { - Source xsltSource = new StreamSource(WFSMessage.class.getResourceAsStream("xml2kvp.xsl")); - TransformerFactory factory = TransformerFactory.newInstance(); - StringWriter writer = new StringWriter(); - try { - Transformer transformer = factory.newTransformer(xsltSource); - transformer.transform(xmlSource, new StreamResult(writer)); - } catch (Exception e) { - TestSuiteLogger.log(Level.WARNING, "Failed to generate KVP result from Source " + xmlSource.getSystemId(), - e); - } - return writer.toString(); - } - - /** - * Wraps the given XML request entity in the body of a SOAP envelope. - * - * @param xmlSource - * The Source providing the XML request entity. - * @param version - * The version of the SOAP protocol (either "1.1" or "1.2"); if - * not specified the latest version is assumed. - * @return A DOM Document node representing a SOAP request message. - */ - public static Document wrapEntityInSOAPEnvelope( Source xmlSource, String version ) { - if ( "1.1".equals( version ) ) - return wrapEntityInSOAPEnvelopeWithNS( xmlSource, Namespaces.SOAP11 ); - return wrapEntityInSOAPEnvelopeWithNS( xmlSource, Namespaces.SOAP_ENV ); - } - - /** - * Adds a simple wfs:Query element (without a filter) to the given request - * entity. The typeNames attribute value is set using the supplied QName - * objects. Namespace bindings are added if necessary. - * - * @param doc - * A Document representing a WFS request entity that accepts - * wfs:Query elements as children of the document element - * (GetFeature, GetPropertyValue, GetFeatureWithLock, - * LockFeature). - * @param qNames - * A sequence of QName objects representing (qualified) feature - * type names recognized by the IUT. - * @return The Element representing the query expression (wfs:Query); it - * will be empty. - */ - public static Element appendSimpleQuery(Document doc, QName... qNames) { - Element docElement = doc.getDocumentElement(); - Element newQuery = doc.createElementNS(Namespaces.WFS, "wfs:Query"); - StringBuilder typeNames = new StringBuilder(); - for (QName qName : qNames) { - // look for prefix already bound to this namespace URI - String nsPrefix = docElement.lookupPrefix(qName.getNamespaceURI()); - if (null == nsPrefix) { - nsPrefix = "ns" + Integer.toString((int) (Math.random() * 100)); - newQuery.setAttributeNS(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, "xmlns:" + nsPrefix, - qName.getNamespaceURI()); - } - typeNames.append(nsPrefix).append(':').append(qName.getLocalPart()).append(' '); - } - newQuery.setAttribute("typeNames", typeNames.toString().trim()); - docElement.appendChild(newQuery); - return newQuery; - } - - /** - * Adds a wfs:StoredQuery element to the given request entity. - * - * @param doc - * A Document representing a WFS request entity that accepts - * wfs:StoredQuery elements as children of the document element - * (GetFeature, GetPropertyValue, GetFeatureWithLock, - * LockFeature). - * @param queryId - * A URI that identifies the stored query to invoke. - * @param params - * A Map containing query parameters (may be empty, e.g. - * {@literal Collections..emptyMap()}). A - * parameter name is associated with an Object (String or QName) - * representing its value. - */ - public static void appendStoredQuery(Document doc, String queryId, Map params) { - Element docElement = doc.getDocumentElement(); - Element newQuery = doc.createElementNS(Namespaces.WFS, "wfs:StoredQuery"); - newQuery.setAttribute("id", queryId); - docElement.appendChild(newQuery); - for (Map.Entry entry : params.entrySet()) { - Element param = doc.createElementNS(Namespaces.WFS, WFS2.PARAM_ELEM); - param.setPrefix("wfs"); - param.setAttribute("name", entry.getKey()); - newQuery.appendChild(param); - Object value = entry.getValue(); - if (QName.class.isInstance(value)) { - QName qName = QName.class.cast(value); - String prefix = (qName.getPrefix().isEmpty()) ? "tns" : qName.getPrefix(); - param.setAttributeNS(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, "xmlns:" + prefix, qName.getNamespaceURI()); - param.setTextContent(prefix + ":" + qName.getLocalPart()); - } else { - param.setTextContent(value.toString()); - } - } - } - - /** - * Creates an XML request entity of the specified request type. - * - * @param reqResource - * The name of a classpath resource containing an XML request - * entity. - * @param wfsVersion - * A WFS version identifier ("2.0.0" if not specified). - * @return A Document representing a WFS request entity. - */ - public static Document createRequestEntity(String reqResource, String wfsVersion) { - String resourceName = reqResource + ".xml"; - Document doc = null; - try { - doc = BUILDER.parse(WFSMessage.class.getResourceAsStream(resourceName)); - } catch (Exception e) { - TestSuiteLogger.log(Level.WARNING, "Failed to parse request entity from classpath: " + resourceName, e); - } - updateVersion( doc , wfsVersion); - return doc; - } - - /** - * Sets the @version attribute in the root element of the doc to the specified version - * - * @param doc - * the doc to update. never null - * @param wfsVersion - * A WFS version identifier ("2.0.0" if not specified). - */ - public static void updateVersion( Document doc, String wfsVersion ) { - Attr verAttr = doc.getDocumentElement().getAttributeNode( "version" ); - if ( null != verAttr && null != wfsVersion && !wfsVersion.isEmpty() ) { - doc.getDocumentElement().getAttributeNode( "version" ).setValue( wfsVersion ); - } - } - - /** - * Sets the value of the typeName attribute on an action element - * (wfs:Update, wfs:Delete) contained in a Transaction request entity. - * - * @param elem - * An action element in a transaction request. - * @param qName - * The qualified name of a feature type. - */ - public static void setTypeName(Element elem, QName qName) { - List actions = Arrays.asList(WFS2.UPDATE, WFS2.DELETE); - if (!actions.contains(elem.getLocalName())) { - return; - } - StringBuilder typeNames = new StringBuilder(); - // look for prefix already bound to this namespace URI - String nsPrefix = elem.lookupPrefix(qName.getNamespaceURI()); - if (null == nsPrefix) { // check document element - nsPrefix = elem.getOwnerDocument().lookupPrefix(qName.getNamespaceURI()); - } - if (null == nsPrefix) { - nsPrefix = "ns" + Integer.toString((int) (Math.random() * 100)); - elem.setAttributeNS(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, "xmlns:" + nsPrefix, qName.getNamespaceURI()); - } - typeNames.append(nsPrefix).append(':').append(qName.getLocalPart()); - elem.setAttribute("typeName", typeNames.toString()); - } - - /** - * Builds a filter predicate containing a fes:ResourceId element that - * identifies the feature instance to be modified. - * - * @param id - * A String denoting a GML object identifier (gml:id). - * @return An Element node (fes:Filter). - */ - public static Element newResourceIdFilter(String id) { - Element filter = XMLUtils.createElement(new QName(Namespaces.FES, "Filter", "fes")); - Element resourceId = XMLUtils.createElement(new QName(Namespaces.FES, "ResourceId", "fes")); - resourceId.setAttribute("rid", id); - filter.appendChild(filter.getOwnerDocument().adoptNode(resourceId)); - return filter; - } - - /** - * Inserts a standard GML property into a given feature instance. If the - * property node already exists it is replaced. - * - * @param feature - * An Element node representing a GML feature - * @param gmlProperty - * An Element node representing a standard (non-deprecated) GML - * feature property. - */ - public static void insertGMLProperty(Element feature, Element gmlProperty) { - Document doc = feature.getOwnerDocument(); - QName gmlPropName = new QName(gmlProperty.getNamespaceURI(), gmlProperty.getLocalName()); - NodeList existing = feature.getElementsByTagNameNS(gmlPropName.getNamespaceURI(), gmlPropName.getLocalPart()); - if (existing.getLength() > 0) { - Node oldProp = existing.item(0); - gmlProperty.setPrefix(oldProp.getPrefix()); - feature.replaceChild(doc.adoptNode(gmlProperty), oldProp); - return; - } - // Create map associating GML property with collection of following - // siblings to help determine insertion point (before nextSibling). - Map> followingSiblingsMap = new HashMap>(); - followingSiblingsMap.put(new QName(Namespaces.GML, "description"), - Arrays.asList("descriptionReference", "identifier", "name", "boundedBy", "location")); - followingSiblingsMap.put(new QName(Namespaces.GML, "identifier"), - Arrays.asList("name", "boundedBy", "location")); - followingSiblingsMap.put(new QName(Namespaces.GML, "name"), Arrays.asList("boundedBy", "location")); - if (!followingSiblingsMap.containsKey(gmlPropName)) - return; // ignore deprecated properties - List followingSibs = followingSiblingsMap.get(gmlPropName); - NodeList properties = feature.getChildNodes(); - Node nextSibling = null; - for (int i = 0; i < properties.getLength(); i++) { - Node property = properties.item(i); - if (property.getNodeType() != Node.ELEMENT_NODE) - continue; - String nsURI = property.getNamespaceURI(); - String propName = property.getLocalName(); - // check if application-defined prop or a following GML prop - if (!nsURI.equals(Namespaces.GML) || (followingSibs.contains(propName) && nsURI.equals(Namespaces.GML))) { - nextSibling = property; - break; - } - } - if (nextSibling.getNamespaceURI().equals(Namespaces.GML)) { - gmlProperty.setPrefix(nextSibling.getPrefix()); - } else { - gmlProperty.setPrefix("gml"); - } - feature.insertBefore(doc.adoptNode(gmlProperty), nextSibling); - } - - /** - * Creates an Element node (fes:ValueReference) containing an XPath - * expression derived from a property element declaration. - * - * @param propertyElem - * An element declaration that defines some feature property. - * @return An Element containing an XPath expression and an appropriate - * namespace binding. - */ - public static Element createValueReference(XSElementDeclaration propertyElem) { - Element valueRef = XMLUtils.createElement(new QName(Namespaces.FES, "ValueReference", "fes")); - valueRef.setAttributeNS(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, "xmlns:" + TNS_PREFIX, - propertyElem.getNamespace()); - valueRef.setTextContent(TNS_PREFIX + ":" + propertyElem.getName()); - return valueRef; - } - - /** - * Creates a GML envelope covering the area of use for the "WGS 84" CRS - * (srsName="urn:ogc:def:crs:EPSG::4326"). - * - * @return A Document containing gml:Envelope as the document element. - */ - public static Document createGMLEnvelope() { - Document doc; - try { - doc = BUILDER.parse(WFSMessage.class.getResourceAsStream("Envelope.xml")); - } catch (Exception e) { - throw new RuntimeException(e); - } - return doc; - } - - /** - * Adds a namespace binding to the document element. - * - * @param doc - * A Document representing a request entity. - * @param qName - * A QName containing a namespace URI and prefix; the local part - * is ignored. - */ - public static void addNamespaceBinding(Document doc, QName qName) { - Element docElem = doc.getDocumentElement(); - docElem.setAttributeNS(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, "xmlns:" + qName.getPrefix(), - qName.getNamespaceURI()); - } - - /** - * Adds a sequence of wfs:Replace statements to the given transaction - * request entity. - * - * @param trxRequest - * A Document node representing a wfs:Transaction request entity. - * @param replacements - * A List containing replacement feature representations (as - * GML). - */ - public static void addReplaceStatements(Document trxRequest, List replacements) { - Element docElem = trxRequest.getDocumentElement(); - if (!docElem.getLocalName().equals(WFS2.TRANSACTION)) { - throw new IllegalArgumentException("Document node is not a Transaction request: " + docElem.getNodeName()); - } - for (Element feature : replacements) { - Element replace = trxRequest.createElementNS(Namespaces.WFS, "Replace"); - replace.setPrefix("wfs"); - replace.appendChild(trxRequest.importNode(feature, true)); - Element filter = WFSMessage.newResourceIdFilter(feature.getAttributeNS(Namespaces.GML, "id")); - replace.appendChild(trxRequest.adoptNode(filter)); - docElem.appendChild(replace); - } - if (TestSuiteLogger.isLoggable(Level.FINE)) { - TestSuiteLogger.log(Level.FINE, XMLUtils.writeNodeToString(trxRequest)); - } - } - - /** - * Appends a wfs:Insert element to the document element in the given request - * entity. The wfs:Insert element contains the supplied feature instance. - * - * @param request - * A Document node representing a wfs:Transaction request entity. - * @param feature - * A Node representing a GML feature instance. - */ - public static void addInsertStatement(Document request, Node feature) { - Element docElem = request.getDocumentElement(); - if (!docElem.getLocalName().equals(WFS2.TRANSACTION)) { - throw new IllegalArgumentException("Document node is not a Transaction request: " + docElem.getNodeName()); - } - Element insert = request.createElementNS(Namespaces.WFS, "Insert"); - docElem.appendChild(insert); - insert.appendChild(request.importNode(feature, true)); - } - - /** - * Adds a ResourceId predicate to a GetFeature (or GetFeatureWithLock) - * request entity that contains a simple query expression without a filter. - * The identifiers should match features of the indicated type. - * - * @param request - * The request entity (/wfs:GetFeature/[wfs:Query]). - * @param idSet - * A {@literal Set} of feature identifiers that conform - * to the xsd:ID datatype. - */ - public static void addResourceIdPredicate(Document request, Set idSet) { - if (idSet.isEmpty()) - return; - if (!request.getDocumentElement().getLocalName().startsWith(WFS2.GET_FEATURE)) { - throw new IllegalArgumentException( - "Expected a GetFeature(WithLock) request: " + request.getDocumentElement().getNodeName()); - } - NodeList queryList = request.getElementsByTagNameNS(Namespaces.WFS, WFS2.QUERY_ELEM); - if (queryList.getLength() == 0) { - throw new IllegalArgumentException( - "No wfs:Query element found in request: " + request.getDocumentElement().getNodeName()); - } - Element filter = request.createElementNS(Namespaces.FES, "Filter"); - queryList.item(0).appendChild(filter); - for (String id : idSet) { - Element resourceId = request.createElementNS(Namespaces.FES, "ResourceId"); - resourceId.setAttribute("rid", id); - filter.appendChild(resourceId); - } - } - - /** - * Checks the given list of objects for the presence of a - * {@link ProtocolBinding#GET} object. - * - * @param testParams - * A list of objects representing test method parameters. - * @return true if a {@literal ProtocolBinding#GET} object was found; false - * otherwise. - */ - public static boolean containsGetProtocolBinding(Object[] testParams) { - if (null == testParams || testParams.length == 0) { - return false; - } - boolean foundGetBinding = false; - for (Object param : testParams) { - if (ProtocolBinding.class.isInstance(param) - && ProtocolBinding.class.cast(param).equals(ProtocolBinding.GET)) { - foundGetBinding = true; - break; - } - } - return foundGetBinding; - } - - /** - * Finds elements in a DOM Document that correspond to the given collection - * of element declarations. - * - * @param doc - * A Document node containing an XML entity. - * @param elemDeclarations - * A collection of element declarations. - * @return A list of matching element nodes (it may be empty). - */ - public static List findMatchingElements(Document doc, XSElementDeclaration... elemDeclarations) { - LOGR.log(Level.FINE, String.format("In %s, find %s", doc.getDocumentElement().getNodeName(), - Arrays.toString(elemDeclarations))); - List nodes = new ArrayList<>(); - for (XSElementDeclaration decl : elemDeclarations) { - NodeList matches = doc.getElementsByTagNameNS(decl.getNamespace(), decl.getName()); - LOGR.log(Level.FINE, String.format("Found %d instances of %s", matches.getLength(), decl)); - for (int i = 0; i < matches.getLength(); i++) { - nodes.add(matches.item(i)); - } - } - return nodes; - } - - /** - * Returns the set of feature identifiers found in the given WFS response - * entity. - * - * @param doc - * A WFS response entity that may contain feature instances. - * @param featureType - * The feature type of interest. - * @return A set of feature identifiers (gml:id attribute values); it may be - * empty. - */ - public static Set extractFeatureIdentifiers(Document doc, QName featureType) { - Set idSet = new HashSet<>(); - if (null == featureType) { // alternative: use XPath if null - throw new IllegalArgumentException("featureType is null"); - } - NodeList features = doc.getElementsByTagNameNS(featureType.getNamespaceURI(), featureType.getLocalPart()); - for (int i = 0; i < features.getLength(); i++) { - Element feature = (Element) features.item(i); - idSet.add(feature.getAttributeNS(Namespaces.GML, "id")); - } - return idSet; - } - - /** - * Adds a temporal predicate to a GetFeature request entity. If the given - * temporal element has no temporal reference (frame) it is assumed to use - * the default frame (ISO 8601). - * - * @param request - * The request entity (wfs:GetFeature). - * @param temporalOp - * The name of a spatial operator. - * @param gmlTime - * A Document containing a GML temporal primitive. - * @param valueRef - * An Element (fes:ValueReference) that specifies the temporal - * property to check. If it is {@code null}, the predicate - * applies to all temporal properties. - */ - public static void addTemporalPredicate(Document request, String temporalOp, Document gmlTime, Element valueRef) { - if (!request.getDocumentElement().getLocalName().equals(WFS2.GET_FEATURE)) { - throw new IllegalArgumentException( - "Not a GetFeature request: " + request.getDocumentElement().getNodeName()); - } - Element queryElem = (Element) request.getElementsByTagNameNS(Namespaces.WFS, WFS2.QUERY_ELEM).item(0); - if (null == queryElem) { - throw new IllegalArgumentException("No Query element found in GetFeature request entity."); - } - Element filter = request.createElementNS(Namespaces.FES, "fes:Filter"); - queryElem.appendChild(filter); - Element predicate = request.createElementNS(Namespaces.FES, "fes:" + temporalOp); - filter.appendChild(predicate); - if (null != valueRef) { - predicate.appendChild(request.importNode(valueRef, true)); - } - // import temporal element to avoid WRONG_DOCUMENT_ERR - predicate.appendChild(request.importNode(gmlTime.getDocumentElement(), true)); - } - - /** - * Sets the attribute CreateStoredQuery/StoredQueryDefinition/QueryExpressionText/Query/@typeNames to the passed - * feature type name. - * - * @param request - * request to modify, never null - * @param featureTypeName - * name to set, never null - */ - public static void setReturnTypesAndTypeNamesAttribute( Document request, QName featureTypeName ) { - if ( !request.getDocumentElement().getLocalName().equals( WFS2.CREATE_STORED_QRY ) ) { - throw new IllegalArgumentException( "Not a CreateStoredQuery request: " - + request.getDocumentElement().getNodeName() ); - } - Element storedQueryDefinition = (Element) request.getElementsByTagNameNS( Namespaces.WFS, - "StoredQueryDefinition" ).item( 0 ); - Element queryExpressionText = (Element) storedQueryDefinition.getElementsByTagNameNS( Namespaces.WFS, - "QueryExpressionText" ).item( 0 ); - Element queryElem = (Element) queryExpressionText.getElementsByTagNameNS( Namespaces.WFS, WFS2.QUERY_ELEM ).item( 0 ); - - String prefix = "ns" + Integer.toString( (int) ( Math.random() * 100 ) ); - String typeNamesAttributeValue = prefix + ":" + featureTypeName.getLocalPart(); - queryExpressionText.setAttribute( "xmlns:" + prefix, featureTypeName.getNamespaceURI() ); - - queryElem.setAttribute( "typeNames", typeNamesAttributeValue ); - queryExpressionText.setAttribute( "returnFeatureTypes", typeNamesAttributeValue ); - } private static Document wrapEntityInSOAPEnvelopeWithNS( Source xmlSource, String soapNS ) { - Document soapDoc = BUILDER.newDocument(); - Element soapEnv = soapDoc.createElementNS( soapNS, "soap:Envelope" ); - soapDoc.appendChild( soapEnv ); - Element soapBody = soapDoc.createElementNS( soapNS, "soap:Body" ); - soapEnv.appendChild( soapBody ); - appendContent( xmlSource, soapDoc, soapBody ); - return soapDoc; - } - - private static void appendContent( Source xmlSource, Document soapDoc, Element soapBody ) { - try { - TransformerFactory tFactory = TransformerFactory.newInstance(); - Transformer idTransformer = tFactory.newTransformer(); - Document wfsReq = BUILDER.newDocument(); - idTransformer.transform( xmlSource, new DOMResult( wfsReq ) ); - soapBody.appendChild( soapDoc.importNode( wfsReq.getDocumentElement(), true ) ); - } catch ( Exception e ) { - TestSuiteLogger.log( Level.WARNING, - "Failed to create SOAP envelope from Source " + xmlSource.getSystemId(), e ); - } - } + private static final Logger LOGR = Logger.getLogger(WFSMessage.class.getPackage().getName()); + + private static final String TNS_PREFIX = "tns"; + + private static final DocumentBuilder BUILDER = initDocBuilder(); + + private static DocumentBuilder initDocBuilder() { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + DocumentBuilder builder = null; + try { + builder = factory.newDocumentBuilder(); + } + catch (ParserConfigurationException e) { + TestSuiteLogger.log(Level.WARNING, "Failed to create parser", e); + } + return builder; + } + + /** + * Transforms the XML representation of a WFS request entity to its corresponding KVP + * serialization format. + * @param xmlSource A Source representing the XML request entity. + * @return A String containing the resulting query component. + */ + public static String transformEntityToKVP(Source xmlSource) { + Source xsltSource = new StreamSource(WFSMessage.class.getResourceAsStream("xml2kvp.xsl")); + TransformerFactory factory = TransformerFactory.newInstance(); + StringWriter writer = new StringWriter(); + try { + Transformer transformer = factory.newTransformer(xsltSource); + transformer.transform(xmlSource, new StreamResult(writer)); + } + catch (Exception e) { + TestSuiteLogger.log(Level.WARNING, "Failed to generate KVP result from Source " + xmlSource.getSystemId(), + e); + } + return writer.toString(); + } + + /** + * Wraps the given XML request entity in the body of a SOAP envelope. + * @param xmlSource The Source providing the XML request entity. + * @param version The version of the SOAP protocol (either "1.1" or "1.2"); if not + * specified the latest version is assumed. + * @return A DOM Document node representing a SOAP request message. + */ + public static Document wrapEntityInSOAPEnvelope(Source xmlSource, String version) { + if ("1.1".equals(version)) + return wrapEntityInSOAPEnvelopeWithNS(xmlSource, Namespaces.SOAP11); + return wrapEntityInSOAPEnvelopeWithNS(xmlSource, Namespaces.SOAP_ENV); + } + + /** + * Adds a simple wfs:Query element (without a filter) to the given request entity. The + * typeNames attribute value is set using the supplied QName objects. Namespace + * bindings are added if necessary. + * @param doc A Document representing a WFS request entity that accepts wfs:Query + * elements as children of the document element (GetFeature, GetPropertyValue, + * GetFeatureWithLock, LockFeature). + * @param qNames A sequence of QName objects representing (qualified) feature type + * names recognized by the IUT. + * @return The Element representing the query expression (wfs:Query); it will be + * empty. + */ + public static Element appendSimpleQuery(Document doc, QName... qNames) { + Element docElement = doc.getDocumentElement(); + Element newQuery = doc.createElementNS(Namespaces.WFS, "wfs:Query"); + StringBuilder typeNames = new StringBuilder(); + for (QName qName : qNames) { + // look for prefix already bound to this namespace URI + String nsPrefix = docElement.lookupPrefix(qName.getNamespaceURI()); + if (null == nsPrefix) { + nsPrefix = "ns" + Integer.toString((int) (Math.random() * 100)); + newQuery.setAttributeNS(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, "xmlns:" + nsPrefix, + qName.getNamespaceURI()); + } + typeNames.append(nsPrefix).append(':').append(qName.getLocalPart()).append(' '); + } + newQuery.setAttribute("typeNames", typeNames.toString().trim()); + docElement.appendChild(newQuery); + return newQuery; + } + + /** + * Adds a wfs:StoredQuery element to the given request entity. + * @param doc A Document representing a WFS request entity that accepts + * wfs:StoredQuery elements as children of the document element (GetFeature, + * GetPropertyValue, GetFeatureWithLock, LockFeature). + * @param queryId A URI that identifies the stored query to invoke. + * @param params A Map containing query parameters (may be empty, e.g. + * {@literal Collections..emptyMap()}). A parameter name is associated + * with an Object (String or QName) representing its value. + */ + public static void appendStoredQuery(Document doc, String queryId, Map params) { + Element docElement = doc.getDocumentElement(); + Element newQuery = doc.createElementNS(Namespaces.WFS, "wfs:StoredQuery"); + newQuery.setAttribute("id", queryId); + docElement.appendChild(newQuery); + for (Map.Entry entry : params.entrySet()) { + Element param = doc.createElementNS(Namespaces.WFS, WFS2.PARAM_ELEM); + param.setPrefix("wfs"); + param.setAttribute("name", entry.getKey()); + newQuery.appendChild(param); + Object value = entry.getValue(); + if (QName.class.isInstance(value)) { + QName qName = QName.class.cast(value); + String prefix = (qName.getPrefix().isEmpty()) ? "tns" : qName.getPrefix(); + param.setAttributeNS(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, "xmlns:" + prefix, qName.getNamespaceURI()); + param.setTextContent(prefix + ":" + qName.getLocalPart()); + } + else { + param.setTextContent(value.toString()); + } + } + } + + /** + * Creates an XML request entity of the specified request type. + * @param reqResource The name of a classpath resource containing an XML request + * entity. + * @param wfsVersion A WFS version identifier ("2.0.0" if not specified). + * @return A Document representing a WFS request entity. + */ + public static Document createRequestEntity(String reqResource, String wfsVersion) { + String resourceName = reqResource + ".xml"; + Document doc = null; + try { + doc = BUILDER.parse(WFSMessage.class.getResourceAsStream(resourceName)); + } + catch (Exception e) { + TestSuiteLogger.log(Level.WARNING, "Failed to parse request entity from classpath: " + resourceName, e); + } + updateVersion(doc, wfsVersion); + return doc; + } + + /** + * Sets the @version attribute in the root element of the doc to the specified version + * @param doc the doc to update. never null + * @param wfsVersion A WFS version identifier ("2.0.0" if not specified). + */ + public static void updateVersion(Document doc, String wfsVersion) { + Attr verAttr = doc.getDocumentElement().getAttributeNode("version"); + if (null != verAttr && null != wfsVersion && !wfsVersion.isEmpty()) { + doc.getDocumentElement().getAttributeNode("version").setValue(wfsVersion); + } + } + + /** + * Sets the value of the typeName attribute on an action element (wfs:Update, + * wfs:Delete) contained in a Transaction request entity. + * @param elem An action element in a transaction request. + * @param qName The qualified name of a feature type. + */ + public static void setTypeName(Element elem, QName qName) { + List actions = Arrays.asList(WFS2.UPDATE, WFS2.DELETE); + if (!actions.contains(elem.getLocalName())) { + return; + } + StringBuilder typeNames = new StringBuilder(); + // look for prefix already bound to this namespace URI + String nsPrefix = elem.lookupPrefix(qName.getNamespaceURI()); + if (null == nsPrefix) { // check document element + nsPrefix = elem.getOwnerDocument().lookupPrefix(qName.getNamespaceURI()); + } + if (null == nsPrefix) { + nsPrefix = "ns" + Integer.toString((int) (Math.random() * 100)); + elem.setAttributeNS(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, "xmlns:" + nsPrefix, qName.getNamespaceURI()); + } + typeNames.append(nsPrefix).append(':').append(qName.getLocalPart()); + elem.setAttribute("typeName", typeNames.toString()); + } + + /** + * Builds a filter predicate containing a fes:ResourceId element that identifies the + * feature instance to be modified. + * @param id A String denoting a GML object identifier (gml:id). + * @return An Element node (fes:Filter). + */ + public static Element newResourceIdFilter(String id) { + Element filter = XMLUtils.createElement(new QName(Namespaces.FES, "Filter", "fes")); + Element resourceId = XMLUtils.createElement(new QName(Namespaces.FES, "ResourceId", "fes")); + resourceId.setAttribute("rid", id); + filter.appendChild(filter.getOwnerDocument().adoptNode(resourceId)); + return filter; + } + + /** + * Inserts a standard GML property into a given feature instance. If the property node + * already exists it is replaced. + * @param feature An Element node representing a GML feature + * @param gmlProperty An Element node representing a standard (non-deprecated) GML + * feature property. + */ + public static void insertGMLProperty(Element feature, Element gmlProperty) { + Document doc = feature.getOwnerDocument(); + QName gmlPropName = new QName(gmlProperty.getNamespaceURI(), gmlProperty.getLocalName()); + NodeList existing = feature.getElementsByTagNameNS(gmlPropName.getNamespaceURI(), gmlPropName.getLocalPart()); + if (existing.getLength() > 0) { + Node oldProp = existing.item(0); + gmlProperty.setPrefix(oldProp.getPrefix()); + feature.replaceChild(doc.adoptNode(gmlProperty), oldProp); + return; + } + // Create map associating GML property with collection of following + // siblings to help determine insertion point (before nextSibling). + Map> followingSiblingsMap = new HashMap>(); + followingSiblingsMap.put(new QName(Namespaces.GML, "description"), + Arrays.asList("descriptionReference", "identifier", "name", "boundedBy", "location")); + followingSiblingsMap.put(new QName(Namespaces.GML, "identifier"), + Arrays.asList("name", "boundedBy", "location")); + followingSiblingsMap.put(new QName(Namespaces.GML, "name"), Arrays.asList("boundedBy", "location")); + if (!followingSiblingsMap.containsKey(gmlPropName)) + return; // ignore deprecated properties + List followingSibs = followingSiblingsMap.get(gmlPropName); + NodeList properties = feature.getChildNodes(); + Node nextSibling = null; + for (int i = 0; i < properties.getLength(); i++) { + Node property = properties.item(i); + if (property.getNodeType() != Node.ELEMENT_NODE) + continue; + String nsURI = property.getNamespaceURI(); + String propName = property.getLocalName(); + // check if application-defined prop or a following GML prop + if (!nsURI.equals(Namespaces.GML) || (followingSibs.contains(propName) && nsURI.equals(Namespaces.GML))) { + nextSibling = property; + break; + } + } + if (nextSibling.getNamespaceURI().equals(Namespaces.GML)) { + gmlProperty.setPrefix(nextSibling.getPrefix()); + } + else { + gmlProperty.setPrefix("gml"); + } + feature.insertBefore(doc.adoptNode(gmlProperty), nextSibling); + } + + /** + * Creates an Element node (fes:ValueReference) containing an XPath expression derived + * from a property element declaration. + * @param propertyElem An element declaration that defines some feature property. + * @return An Element containing an XPath expression and an appropriate namespace + * binding. + */ + public static Element createValueReference(XSElementDeclaration propertyElem) { + Element valueRef = XMLUtils.createElement(new QName(Namespaces.FES, "ValueReference", "fes")); + valueRef.setAttributeNS(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, "xmlns:" + TNS_PREFIX, + propertyElem.getNamespace()); + valueRef.setTextContent(TNS_PREFIX + ":" + propertyElem.getName()); + return valueRef; + } + + /** + * Creates a GML envelope covering the area of use for the "WGS 84" CRS + * (srsName="urn:ogc:def:crs:EPSG::4326"). + * @return A Document containing gml:Envelope as the document element. + */ + public static Document createGMLEnvelope() { + Document doc; + try { + doc = BUILDER.parse(WFSMessage.class.getResourceAsStream("Envelope.xml")); + } + catch (Exception e) { + throw new RuntimeException(e); + } + return doc; + } + + /** + * Adds a namespace binding to the document element. + * @param doc A Document representing a request entity. + * @param qName A QName containing a namespace URI and prefix; the local part is + * ignored. + */ + public static void addNamespaceBinding(Document doc, QName qName) { + Element docElem = doc.getDocumentElement(); + docElem.setAttributeNS(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, "xmlns:" + qName.getPrefix(), + qName.getNamespaceURI()); + } + + /** + * Adds a sequence of wfs:Replace statements to the given transaction request entity. + * @param trxRequest A Document node representing a wfs:Transaction request entity. + * @param replacements A List containing replacement feature representations (as GML). + */ + public static void addReplaceStatements(Document trxRequest, List replacements) { + Element docElem = trxRequest.getDocumentElement(); + if (!docElem.getLocalName().equals(WFS2.TRANSACTION)) { + throw new IllegalArgumentException("Document node is not a Transaction request: " + docElem.getNodeName()); + } + for (Element feature : replacements) { + Element replace = trxRequest.createElementNS(Namespaces.WFS, "Replace"); + replace.setPrefix("wfs"); + replace.appendChild(trxRequest.importNode(feature, true)); + Element filter = WFSMessage.newResourceIdFilter(feature.getAttributeNS(Namespaces.GML, "id")); + replace.appendChild(trxRequest.adoptNode(filter)); + docElem.appendChild(replace); + } + if (TestSuiteLogger.isLoggable(Level.FINE)) { + TestSuiteLogger.log(Level.FINE, XMLUtils.writeNodeToString(trxRequest)); + } + } + + /** + * Appends a wfs:Insert element to the document element in the given request entity. + * The wfs:Insert element contains the supplied feature instance. + * @param request A Document node representing a wfs:Transaction request entity. + * @param feature A Node representing a GML feature instance. + */ + public static void addInsertStatement(Document request, Node feature) { + Element docElem = request.getDocumentElement(); + if (!docElem.getLocalName().equals(WFS2.TRANSACTION)) { + throw new IllegalArgumentException("Document node is not a Transaction request: " + docElem.getNodeName()); + } + Element insert = request.createElementNS(Namespaces.WFS, "Insert"); + docElem.appendChild(insert); + insert.appendChild(request.importNode(feature, true)); + } + + /** + * Adds a ResourceId predicate to a GetFeature (or GetFeatureWithLock) request entity + * that contains a simple query expression without a filter. The identifiers should + * match features of the indicated type. + * @param request The request entity (/wfs:GetFeature/[wfs:Query]). + * @param idSet A {@literal Set} of feature identifiers that conform to the + * xsd:ID datatype. + */ + public static void addResourceIdPredicate(Document request, Set idSet) { + if (idSet.isEmpty()) + return; + if (!request.getDocumentElement().getLocalName().startsWith(WFS2.GET_FEATURE)) { + throw new IllegalArgumentException( + "Expected a GetFeature(WithLock) request: " + request.getDocumentElement().getNodeName()); + } + NodeList queryList = request.getElementsByTagNameNS(Namespaces.WFS, WFS2.QUERY_ELEM); + if (queryList.getLength() == 0) { + throw new IllegalArgumentException( + "No wfs:Query element found in request: " + request.getDocumentElement().getNodeName()); + } + Element filter = request.createElementNS(Namespaces.FES, "Filter"); + queryList.item(0).appendChild(filter); + for (String id : idSet) { + Element resourceId = request.createElementNS(Namespaces.FES, "ResourceId"); + resourceId.setAttribute("rid", id); + filter.appendChild(resourceId); + } + } + + /** + * Checks the given list of objects for the presence of a {@link ProtocolBinding#GET} + * object. + * @param testParams A list of objects representing test method parameters. + * @return true if a {@literal ProtocolBinding#GET} object was found; false otherwise. + */ + public static boolean containsGetProtocolBinding(Object[] testParams) { + if (null == testParams || testParams.length == 0) { + return false; + } + boolean foundGetBinding = false; + for (Object param : testParams) { + if (ProtocolBinding.class.isInstance(param) + && ProtocolBinding.class.cast(param).equals(ProtocolBinding.GET)) { + foundGetBinding = true; + break; + } + } + return foundGetBinding; + } + + /** + * Finds elements in a DOM Document that correspond to the given collection of element + * declarations. + * @param doc A Document node containing an XML entity. + * @param elemDeclarations A collection of element declarations. + * @return A list of matching element nodes (it may be empty). + */ + public static List findMatchingElements(Document doc, XSElementDeclaration... elemDeclarations) { + LOGR.log(Level.FINE, String.format("In %s, find %s", doc.getDocumentElement().getNodeName(), + Arrays.toString(elemDeclarations))); + List nodes = new ArrayList<>(); + for (XSElementDeclaration decl : elemDeclarations) { + NodeList matches = doc.getElementsByTagNameNS(decl.getNamespace(), decl.getName()); + LOGR.log(Level.FINE, String.format("Found %d instances of %s", matches.getLength(), decl)); + for (int i = 0; i < matches.getLength(); i++) { + nodes.add(matches.item(i)); + } + } + return nodes; + } + + /** + * Returns the set of feature identifiers found in the given WFS response entity. + * @param doc A WFS response entity that may contain feature instances. + * @param featureType The feature type of interest. + * @return A set of feature identifiers (gml:id attribute values); it may be empty. + */ + public static Set extractFeatureIdentifiers(Document doc, QName featureType) { + Set idSet = new HashSet<>(); + if (null == featureType) { // alternative: use XPath if null + throw new IllegalArgumentException("featureType is null"); + } + NodeList features = doc.getElementsByTagNameNS(featureType.getNamespaceURI(), featureType.getLocalPart()); + for (int i = 0; i < features.getLength(); i++) { + Element feature = (Element) features.item(i); + idSet.add(feature.getAttributeNS(Namespaces.GML, "id")); + } + return idSet; + } + + /** + * Adds a temporal predicate to a GetFeature request entity. If the given temporal + * element has no temporal reference (frame) it is assumed to use the default frame + * (ISO 8601). + * @param request The request entity (wfs:GetFeature). + * @param temporalOp The name of a spatial operator. + * @param gmlTime A Document containing a GML temporal primitive. + * @param valueRef An Element (fes:ValueReference) that specifies the temporal + * property to check. If it is {@code null}, the predicate applies to all temporal + * properties. + */ + public static void addTemporalPredicate(Document request, String temporalOp, Document gmlTime, Element valueRef) { + if (!request.getDocumentElement().getLocalName().equals(WFS2.GET_FEATURE)) { + throw new IllegalArgumentException( + "Not a GetFeature request: " + request.getDocumentElement().getNodeName()); + } + Element queryElem = (Element) request.getElementsByTagNameNS(Namespaces.WFS, WFS2.QUERY_ELEM).item(0); + if (null == queryElem) { + throw new IllegalArgumentException("No Query element found in GetFeature request entity."); + } + Element filter = request.createElementNS(Namespaces.FES, "fes:Filter"); + queryElem.appendChild(filter); + Element predicate = request.createElementNS(Namespaces.FES, "fes:" + temporalOp); + filter.appendChild(predicate); + if (null != valueRef) { + predicate.appendChild(request.importNode(valueRef, true)); + } + // import temporal element to avoid WRONG_DOCUMENT_ERR + predicate.appendChild(request.importNode(gmlTime.getDocumentElement(), true)); + } + + /** + * Sets the attribute + * CreateStoredQuery/StoredQueryDefinition/QueryExpressionText/Query/@typeNames to the + * passed feature type name. + * @param request request to modify, never null + * @param featureTypeName name to set, never null + */ + public static void setReturnTypesAndTypeNamesAttribute(Document request, QName featureTypeName) { + if (!request.getDocumentElement().getLocalName().equals(WFS2.CREATE_STORED_QRY)) { + throw new IllegalArgumentException( + "Not a CreateStoredQuery request: " + request.getDocumentElement().getNodeName()); + } + Element storedQueryDefinition = (Element) request + .getElementsByTagNameNS(Namespaces.WFS, "StoredQueryDefinition") + .item(0); + Element queryExpressionText = (Element) storedQueryDefinition + .getElementsByTagNameNS(Namespaces.WFS, "QueryExpressionText") + .item(0); + Element queryElem = (Element) queryExpressionText.getElementsByTagNameNS(Namespaces.WFS, WFS2.QUERY_ELEM) + .item(0); + + String prefix = "ns" + Integer.toString((int) (Math.random() * 100)); + String typeNamesAttributeValue = prefix + ":" + featureTypeName.getLocalPart(); + queryExpressionText.setAttribute("xmlns:" + prefix, featureTypeName.getNamespaceURI()); + + queryElem.setAttribute("typeNames", typeNamesAttributeValue); + queryExpressionText.setAttribute("returnFeatureTypes", typeNamesAttributeValue); + } + + private static Document wrapEntityInSOAPEnvelopeWithNS(Source xmlSource, String soapNS) { + Document soapDoc = BUILDER.newDocument(); + Element soapEnv = soapDoc.createElementNS(soapNS, "soap:Envelope"); + soapDoc.appendChild(soapEnv); + Element soapBody = soapDoc.createElementNS(soapNS, "soap:Body"); + soapEnv.appendChild(soapBody); + appendContent(xmlSource, soapDoc, soapBody); + return soapDoc; + } + + private static void appendContent(Source xmlSource, Document soapDoc, Element soapBody) { + try { + TransformerFactory tFactory = TransformerFactory.newInstance(); + Transformer idTransformer = tFactory.newTransformer(); + Document wfsReq = BUILDER.newDocument(); + idTransformer.transform(xmlSource, new DOMResult(wfsReq)); + soapBody.appendChild(soapDoc.importNode(wfsReq.getDocumentElement(), true)); + } + catch (Exception e) { + TestSuiteLogger.log(Level.WARNING, "Failed to create SOAP envelope from Source " + xmlSource.getSystemId(), + e); + } + } } diff --git a/src/main/java/org/opengis/cite/iso19142/util/XMLUtils.java b/src/main/java/org/opengis/cite/iso19142/util/XMLUtils.java index 65a93769..092a690b 100644 --- a/src/main/java/org/opengis/cite/iso19142/util/XMLUtils.java +++ b/src/main/java/org/opengis/cite/iso19142/util/XMLUtils.java @@ -58,397 +58,367 @@ import net.sf.saxon.s9api.XsltTransformer; /** - * Provides various utility methods for accessing or manipulating XML - * representations. + * Provides various utility methods for accessing or manipulating XML representations. */ public class XMLUtils { - private static final Logger LOGR = Logger.getLogger(XMLUtils.class.getPackage().getName()); - private static final XMLInputFactory STAX_FACTORY = initXMLInputFactory(); - private static final XPathFactory XPATH_FACTORY = initXPathFactory(); + private static final Logger LOGR = Logger.getLogger(XMLUtils.class.getPackage().getName()); - private static XMLInputFactory initXMLInputFactory() { - XMLInputFactory factory = XMLInputFactory.newInstance(); - factory.setProperty(XMLInputFactory.IS_COALESCING, Boolean.TRUE); - return factory; - } + private static final XMLInputFactory STAX_FACTORY = initXMLInputFactory(); - private static XPathFactory initXPathFactory() { - XPathFactory factory = XPathFactory.newInstance(); - return factory; - } + private static final XPathFactory XPATH_FACTORY = initXPathFactory(); - /** - * Writes the content of a DOM node to a String. An XML declaration is - * omitted. - * - * @param node - * The node to be serialized. - * @return A String representing the content of the given node. - */ - public static String writeNodeToString(Node node) { - if (null == node) { - throw new IllegalArgumentException("Supplied node is null."); - } - StringWriter writer = null; - try { - Transformer idTransformer = TransformerFactory.newInstance().newTransformer(); - Properties outProps = new Properties(); - outProps.setProperty(OutputKeys.ENCODING, "UTF-8"); - outProps.setProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); - outProps.setProperty(OutputKeys.INDENT, "yes"); - idTransformer.setOutputProperties(outProps); - writer = new StringWriter(); - idTransformer.transform(new DOMSource(node), new StreamResult(writer)); - } catch (TransformerException ex) { - LOGR.log(Level.WARNING, "Failed to serialize DOM node: " + node.getNodeName(), ex); - } - return writer.toString(); - } + private static XMLInputFactory initXMLInputFactory() { + XMLInputFactory factory = XMLInputFactory.newInstance(); + factory.setProperty(XMLInputFactory.IS_COALESCING, Boolean.TRUE); + return factory; + } - /** - * Writes the result of a transformation to a String. An XML declaration is - * always omitted. - * - * @param result - * An object (DOMResult or StreamResult) that holds the result of - * a transformation, which may be XML or plain text. - * @return A String representing the content of the result; it may be empty - * if the content could not be read. - */ - public static String resultToString(Result result) { - if (null == result) { - throw new IllegalArgumentException("Result is null."); - } - StringWriter writer = new StringWriter(); - if (result instanceof DOMResult) { - Node node = DOMResult.class.cast(result).getNode(); - Properties outProps = new Properties(); - outProps.setProperty(OutputKeys.ENCODING, "UTF-8"); - outProps.setProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); - outProps.setProperty(OutputKeys.INDENT, "yes"); - Transformer idTransformer; - try { - idTransformer = TransformerFactory.newInstance().newTransformer(); - idTransformer.setOutputProperties(outProps); - idTransformer.transform(new DOMSource(node), new StreamResult(writer)); - } catch (TransformerFactoryConfigurationError | TransformerException e) { - LOGR.warning(e.getMessage()); - } - } else if (result instanceof StreamResult) { - StreamResult streamResult = StreamResult.class.cast(result); - OutputStream os = streamResult.getOutputStream(); - if (null != os) { - writer.write(os.toString()); // probably ByteArrayOutputStream - } else { // try system id or writer - Path path = Paths.get(URI.create(streamResult.getSystemId())); - try { - byte[] data = Files.readAllBytes(path); - writer.write(new String(data)); - } catch (IOException e) { - LOGR.warning(e.getMessage()); - } - } - } else { - throw new IllegalArgumentException("Unsupported Result type:" + result.getClass()); - } - return writer.toString(); - } + private static XPathFactory initXPathFactory() { + XPathFactory factory = XPathFactory.newInstance(); + return factory; + } - /** - * Writes the content of a DOM node to a byte stream. An XML declaration is - * omitted. - * - * @param node - * The node to be serialized. - * @param outputStream - * The destination OutputStream reference. - */ - public static void writeNode(Node node, OutputStream outputStream) { - if (null == node) { - throw new IllegalArgumentException("Supplied node is null."); - } - try { - Transformer idTransformer = TransformerFactory.newInstance().newTransformer(); - Properties outProps = new Properties(); - outProps.setProperty(OutputKeys.METHOD, "xml"); - outProps.setProperty(OutputKeys.ENCODING, "UTF-8"); - outProps.setProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); - outProps.setProperty(OutputKeys.INDENT, "yes"); - idTransformer.setOutputProperties(outProps); - idTransformer.transform(new DOMSource(node), new StreamResult(outputStream)); - } catch (TransformerException ex) { - String nodeName = (node.getNodeType() == Node.DOCUMENT_NODE) - ? Document.class.cast(node).getDocumentElement().getNodeName() : node.getNodeName(); - LOGR.log(Level.WARNING, "Failed to serialize DOM node: " + nodeName, ex); - } - } + /** + * Writes the content of a DOM node to a String. An XML declaration is omitted. + * @param node The node to be serialized. + * @return A String representing the content of the given node. + */ + public static String writeNodeToString(Node node) { + if (null == node) { + throw new IllegalArgumentException("Supplied node is null."); + } + StringWriter writer = null; + try { + Transformer idTransformer = TransformerFactory.newInstance().newTransformer(); + Properties outProps = new Properties(); + outProps.setProperty(OutputKeys.ENCODING, "UTF-8"); + outProps.setProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); + outProps.setProperty(OutputKeys.INDENT, "yes"); + idTransformer.setOutputProperties(outProps); + writer = new StringWriter(); + idTransformer.transform(new DOMSource(node), new StreamResult(writer)); + } + catch (TransformerException ex) { + LOGR.log(Level.WARNING, "Failed to serialize DOM node: " + node.getNodeName(), ex); + } + return writer.toString(); + } - /** - * Evaluates an XPath 1.0 expression using the given context and returns the - * result as a node set. - * - * @param context - * The context node. - * @param expr - * An XPath expression. - * @param namespaceBindings - * A collection of namespace bindings for the XPath expression, - * where each entry maps a namespace URI (key) to a prefix - * (value). Standard bindings do not need to be declared (see - * {@link NamespaceBindings#withStandardBindings()}. - * @return A NodeList containing nodes that satisfy the expression (it may - * be empty). - * @throws XPathExpressionException - * If the expression cannot be evaluated for any reason. - */ - public static NodeList evaluateXPath(Node context, String expr, Map namespaceBindings) - throws XPathExpressionException { - Object result = evaluateXPath(context, expr, namespaceBindings, XPathConstants.NODESET); - if (!NodeList.class.isInstance(result)) { - throw new XPathExpressionException("Expression does not evaluate to a NodeList: " + expr); - } - return (NodeList) result; - } + /** + * Writes the result of a transformation to a String. An XML declaration is always + * omitted. + * @param result An object (DOMResult or StreamResult) that holds the result of a + * transformation, which may be XML or plain text. + * @return A String representing the content of the result; it may be empty if the + * content could not be read. + */ + public static String resultToString(Result result) { + if (null == result) { + throw new IllegalArgumentException("Result is null."); + } + StringWriter writer = new StringWriter(); + if (result instanceof DOMResult) { + Node node = DOMResult.class.cast(result).getNode(); + Properties outProps = new Properties(); + outProps.setProperty(OutputKeys.ENCODING, "UTF-8"); + outProps.setProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); + outProps.setProperty(OutputKeys.INDENT, "yes"); + Transformer idTransformer; + try { + idTransformer = TransformerFactory.newInstance().newTransformer(); + idTransformer.setOutputProperties(outProps); + idTransformer.transform(new DOMSource(node), new StreamResult(writer)); + } + catch (TransformerFactoryConfigurationError | TransformerException e) { + LOGR.warning(e.getMessage()); + } + } + else if (result instanceof StreamResult) { + StreamResult streamResult = StreamResult.class.cast(result); + OutputStream os = streamResult.getOutputStream(); + if (null != os) { + writer.write(os.toString()); // probably ByteArrayOutputStream + } + else { // try system id or writer + Path path = Paths.get(URI.create(streamResult.getSystemId())); + try { + byte[] data = Files.readAllBytes(path); + writer.write(new String(data)); + } + catch (IOException e) { + LOGR.warning(e.getMessage()); + } + } + } + else { + throw new IllegalArgumentException("Unsupported Result type:" + result.getClass()); + } + return writer.toString(); + } - /** - * Evaluates an XPath expression using the given context and returns the - * result as the specified type. - * - *

              - * Note: The Saxon implementation supports XPath 2.0 - * expressions when using the JAXP XPath APIs (the default implementation - * will throw an exception). - *

              - * - * @param context - * The context node. - * @param expr - * An XPath expression. - * @param namespaceBindings - * A collection of namespace bindings for the XPath expression, - * where each entry maps a namespace URI (key) to a prefix - * (value). Standard bindings do not need to be declared (see - * {@link NamespaceBindings#withStandardBindings()}. - * @param returnType - * The desired return type (as declared in {@link XPathConstants} - * ). - * @return The result converted to the desired returnType. - * @throws XPathExpressionException - * If the expression cannot be evaluated for any reason. - */ - public static Object evaluateXPath(Node context, String expr, Map namespaceBindings, - QName returnType) throws XPathExpressionException { - NamespaceBindings bindings = NamespaceBindings.withStandardBindings(); - bindings.addAllBindings(namespaceBindings); - XPathFactory factory = XPATH_FACTORY; - // WARNING: If context node is Saxon NodeOverNodeInfo, the factory must - // use the same Configuration object to avoid IllegalArgumentException - XPath xpath = factory.newXPath(); - xpath.setNamespaceContext(bindings); - Object result = xpath.evaluate(expr, context, returnType); - return result; - } + /** + * Writes the content of a DOM node to a byte stream. An XML declaration is omitted. + * @param node The node to be serialized. + * @param outputStream The destination OutputStream reference. + */ + public static void writeNode(Node node, OutputStream outputStream) { + if (null == node) { + throw new IllegalArgumentException("Supplied node is null."); + } + try { + Transformer idTransformer = TransformerFactory.newInstance().newTransformer(); + Properties outProps = new Properties(); + outProps.setProperty(OutputKeys.METHOD, "xml"); + outProps.setProperty(OutputKeys.ENCODING, "UTF-8"); + outProps.setProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); + outProps.setProperty(OutputKeys.INDENT, "yes"); + idTransformer.setOutputProperties(outProps); + idTransformer.transform(new DOMSource(node), new StreamResult(outputStream)); + } + catch (TransformerException ex) { + String nodeName = (node.getNodeType() == Node.DOCUMENT_NODE) + ? Document.class.cast(node).getDocumentElement().getNodeName() : node.getNodeName(); + LOGR.log(Level.WARNING, "Failed to serialize DOM node: " + nodeName, ex); + } + } - /** - * Evaluates an XPath 2.0 expression using the Saxon s9api interfaces. - * - * @param xmlSource - * The XML Source. - * @param expr - * The XPath expression to be evaluated. - * @param nsBindings - * A collection of namespace bindings required to evaluate the - * XPath expression, where each entry maps a namespace URI (key) - * to a prefix (value); this may be {@code null} if not needed. A - * namespace binding for "gml" may be omitted. - * @return An XdmValue object representing a value in the XDM data model; - * this is a sequence of zero or more items, where each item is - * either an atomic value or a node. - * @throws SaxonApiException - * If an error occurs while evaluating the expression; this - * always wraps some other underlying exception. - */ - public static XdmValue evaluateXPath2(Source xmlSource, String expr, Map nsBindings) - throws SaxonApiException { - Processor proc = new Processor(false); - XPathCompiler compiler = proc.newXPathCompiler(); - compiler.declareNamespace("gml", Namespaces.GML); - if (null != nsBindings) { - for (String nsURI : nsBindings.keySet()) { - compiler.declareNamespace(nsBindings.get(nsURI), nsURI); - } - } - XPathSelector xpath = compiler.compile(expr).load(); - DocumentBuilder builder = proc.newDocumentBuilder(); - XdmNode node = null; - if (DOMSource.class.isInstance(xmlSource)) { - DOMSource domSource = (DOMSource) xmlSource; - node = builder.wrap(domSource.getNode()); - } else { - node = builder.build(xmlSource); - } - xpath.setContextItem(node); - return xpath.evaluate(); - } + /** + * Evaluates an XPath 1.0 expression using the given context and returns the result as + * a node set. + * @param context The context node. + * @param expr An XPath expression. + * @param namespaceBindings A collection of namespace bindings for the XPath + * expression, where each entry maps a namespace URI (key) to a prefix (value). + * Standard bindings do not need to be declared (see + * {@link NamespaceBindings#withStandardBindings()}. + * @return A NodeList containing nodes that satisfy the expression (it may be empty). + * @throws XPathExpressionException If the expression cannot be evaluated for any + * reason. + */ + public static NodeList evaluateXPath(Node context, String expr, Map namespaceBindings) + throws XPathExpressionException { + Object result = evaluateXPath(context, expr, namespaceBindings, XPathConstants.NODESET); + if (!NodeList.class.isInstance(result)) { + throw new XPathExpressionException("Expression does not evaluate to a NodeList: " + expr); + } + return (NodeList) result; + } - /** - * Creates a new Element having the specified qualified name. The element - * must be {@link Document#adoptNode(Node) adopted} when inserted into - * another Document. - * - * @param qName - * A QName object. - * @return An Element node (with a Document owner but no parent). - */ - public static Element createElement(QName qName) { - Document doc = null; - try { - doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); - } catch (ParserConfigurationException e) { - throw new RuntimeException(e); - } - StringBuilder nodeName = new StringBuilder(); - if (qName.getPrefix().isEmpty()) { - nodeName.append(qName.getLocalPart()); - } else { - nodeName.append(qName.getPrefix()).append(':').append(qName.getLocalPart()); - } - Element elem = doc.createElementNS(qName.getNamespaceURI(), nodeName.toString()); - return elem; - } + /** + * Evaluates an XPath expression using the given context and returns the result as the + * specified type. + * + *

              + * Note: The Saxon implementation supports XPath 2.0 expressions when + * using the JAXP XPath APIs (the default implementation will throw an exception). + *

              + * @param context The context node. + * @param expr An XPath expression. + * @param namespaceBindings A collection of namespace bindings for the XPath + * expression, where each entry maps a namespace URI (key) to a prefix (value). + * Standard bindings do not need to be declared (see + * {@link NamespaceBindings#withStandardBindings()}. + * @param returnType The desired return type (as declared in {@link XPathConstants} ). + * @return The result converted to the desired returnType. + * @throws XPathExpressionException If the expression cannot be evaluated for any + * reason. + */ + public static Object evaluateXPath(Node context, String expr, Map namespaceBindings, + QName returnType) throws XPathExpressionException { + NamespaceBindings bindings = NamespaceBindings.withStandardBindings(); + bindings.addAllBindings(namespaceBindings); + XPathFactory factory = XPATH_FACTORY; + // WARNING: If context node is Saxon NodeOverNodeInfo, the factory must + // use the same Configuration object to avoid IllegalArgumentException + XPath xpath = factory.newXPath(); + xpath.setNamespaceContext(bindings); + Object result = xpath.evaluate(expr, context, returnType); + return result; + } - /** - * Returns a List of all descendant Element nodes having the specified - * [namespace name] property. The elements are listed in document order. - * - * @param node - * The node to search from. - * @param namespaceURI - * An absolute URI denoting a namespace name. - * @return A List containing elements in the specified namespace; the list - * is empty if there are no elements in the namespace. - */ - public static List getElementsByNamespaceURI(Node node, String namespaceURI) { - List list = new ArrayList(); - NodeList children = node.getChildNodes(); - for (int i = 0; i < children.getLength(); i++) { - Node child = children.item(i); - if (child.getNodeType() != Node.ELEMENT_NODE) - continue; - if (child.getNamespaceURI().equals(namespaceURI)) - list.add((Element) child); - } - return list; - } + /** + * Evaluates an XPath 2.0 expression using the Saxon s9api interfaces. + * @param xmlSource The XML Source. + * @param expr The XPath expression to be evaluated. + * @param nsBindings A collection of namespace bindings required to evaluate the XPath + * expression, where each entry maps a namespace URI (key) to a prefix (value); this + * may be {@code null} if not needed. A namespace binding for "gml" may be omitted. + * @return An XdmValue object representing a value in the XDM data model; this is a + * sequence of zero or more items, where each item is either an atomic value or a + * node. + * @throws SaxonApiException If an error occurs while evaluating the expression; this + * always wraps some other underlying exception. + */ + public static XdmValue evaluateXPath2(Source xmlSource, String expr, Map nsBindings) + throws SaxonApiException { + Processor proc = new Processor(false); + XPathCompiler compiler = proc.newXPathCompiler(); + compiler.declareNamespace("gml", Namespaces.GML); + if (null != nsBindings) { + for (String nsURI : nsBindings.keySet()) { + compiler.declareNamespace(nsBindings.get(nsURI), nsURI); + } + } + XPathSelector xpath = compiler.compile(expr).load(); + DocumentBuilder builder = proc.newDocumentBuilder(); + XdmNode node = null; + if (DOMSource.class.isInstance(xmlSource)) { + DOMSource domSource = (DOMSource) xmlSource; + node = builder.wrap(domSource.getNode()); + } + else { + node = builder.build(xmlSource); + } + xpath.setContextItem(node); + return xpath.evaluate(); + } - /** - * Returns a descendant Element node having the specified - * [namespace name] and [local part] property. - * - * @param node - * The node to search from. - * @param namespaceURI - * An absolute URI denoting a namespace name. - * @param localPart - * The local part. - * @return The matching Node with the specified namespace and local part; null, if no matching node is found. - */ - public static Node getElementByNamespaceURIandLocalPart(Node node, String namespaceURI, String localPart) { - NodeList children = node.getChildNodes(); - for (int i = 0; i < children.getLength(); i++) { - Node child = children.item(i); - if (child.getNodeType() != Node.ELEMENT_NODE) - continue; - if (child.getNamespaceURI().equals(namespaceURI) && child.getLocalName().equals(localPart)) { - return child; - } - } - return null; - } + /** + * Creates a new Element having the specified qualified name. The element must be + * {@link Document#adoptNode(Node) adopted} when inserted into another Document. + * @param qName A QName object. + * @return An Element node (with a Document owner but no parent). + */ + public static Element createElement(QName qName) { + Document doc = null; + try { + doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); + } + catch (ParserConfigurationException e) { + throw new RuntimeException(e); + } + StringBuilder nodeName = new StringBuilder(); + if (qName.getPrefix().isEmpty()) { + nodeName.append(qName.getLocalPart()); + } + else { + nodeName.append(qName.getPrefix()).append(':').append(qName.getLocalPart()); + } + Element elem = doc.createElementNS(qName.getNamespaceURI(), nodeName.toString()); + return elem; + } - /** - * Transforms the content of a DOM Node using a specified XSLT stylesheet. - * - * @param xslt - * A Source object representing a stylesheet (XSLT 1.0 or 2.0). - * @param source - * A Node representing the XML source. If it is an Element node - * it will be imported into a new DOM Document. - * @param params - * A Map containing global stylesheet parameters (name-value - * pairs). - * @return A DOM Document containing the result of the transformation. - */ - public static Document transform(Source xslt, Node source, Map params) { - Document sourceDoc = null; - Document resultDoc = null; - if (null == params) - params = Collections.emptyMap(); - try { - resultDoc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); - if (source.getNodeType() == Node.DOCUMENT_NODE) { - sourceDoc = (Document) source; - } else { - sourceDoc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); - sourceDoc.appendChild(sourceDoc.importNode(source, true)); - } - } catch (ParserConfigurationException pce) { - throw new RuntimeException(pce); - } - Processor processor = new Processor(false); - XsltCompiler compiler = processor.newXsltCompiler(); - try { - XsltExecutable exec = compiler.compile(xslt); - XsltTransformer transformer = exec.load(); - transformer.setSource(new DOMSource(sourceDoc)); - transformer.setDestination(new DOMDestination(resultDoc)); - for (Map.Entry entry : params.entrySet()) { - net.sf.saxon.s9api.QName paramName = new net.sf.saxon.s9api.QName(entry.getKey()); - XdmValue paramValue = new XdmAtomicValue(entry.getValue()); - transformer.setParameter(paramName, paramValue); - } - transformer.transform(); - } catch (SaxonApiException e) { - throw new RuntimeException(e); - } - return resultDoc; - } + /** + * Returns a List of all descendant Element nodes having the specified [namespace + * name] property. The elements are listed in document order. + * @param node The node to search from. + * @param namespaceURI An absolute URI denoting a namespace name. + * @return A List containing elements in the specified namespace; the list is empty if + * there are no elements in the namespace. + */ + public static List getElementsByNamespaceURI(Node node, String namespaceURI) { + List list = new ArrayList(); + NodeList children = node.getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + Node child = children.item(i); + if (child.getNodeType() != Node.ELEMENT_NODE) + continue; + if (child.getNamespaceURI().equals(namespaceURI)) + list.add((Element) child); + } + return list; + } - /** - * Expands character entity ({@literal &name;}) and numeric references ( - * {@literal &#xhhhh;} or {@literal &dddd;}) that occur within a given - * string value. It may be necessary to do this before processing an XPath - * expression. - * - * @param value - * A string representing text content. - * @return A string with all included references expanded. - */ - public static String expandReferencesInText(String value) { - StringBuilder wrapper = new StringBuilder(""); - wrapper.append(value).append(""); - Reader reader = new StringReader(wrapper.toString()); - String str = null; - try { - XMLStreamReader xsr = STAX_FACTORY.createXMLStreamReader(reader); - xsr.nextTag(); // document element - str = xsr.getElementText(); - } catch (XMLStreamException xse) { - LOGR.log(Level.WARNING, xse.getMessage(), xse); - } - return str; - } + /** + * Returns a descendant Element node having the specified [namespace name] and [local + * part] property. + * @param node The node to search from. + * @param namespaceURI An absolute URI denoting a namespace name. + * @param localPart The local part. + * @return The matching Node with the specified namespace and local part; null, if no + * matching node is found. + */ + public static Node getElementByNamespaceURIandLocalPart(Node node, String namespaceURI, String localPart) { + NodeList children = node.getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + Node child = children.item(i); + if (child.getNodeType() != Node.ELEMENT_NODE) + continue; + if (child.getNamespaceURI().equals(namespaceURI) && child.getLocalName().equals(localPart)) { + return child; + } + } + return null; + } + + /** + * Transforms the content of a DOM Node using a specified XSLT stylesheet. + * @param xslt A Source object representing a stylesheet (XSLT 1.0 or 2.0). + * @param source A Node representing the XML source. If it is an Element node it will + * be imported into a new DOM Document. + * @param params A Map containing global stylesheet parameters (name-value pairs). + * @return A DOM Document containing the result of the transformation. + */ + public static Document transform(Source xslt, Node source, Map params) { + Document sourceDoc = null; + Document resultDoc = null; + if (null == params) + params = Collections.emptyMap(); + try { + resultDoc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); + if (source.getNodeType() == Node.DOCUMENT_NODE) { + sourceDoc = (Document) source; + } + else { + sourceDoc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); + sourceDoc.appendChild(sourceDoc.importNode(source, true)); + } + } + catch (ParserConfigurationException pce) { + throw new RuntimeException(pce); + } + Processor processor = new Processor(false); + XsltCompiler compiler = processor.newXsltCompiler(); + try { + XsltExecutable exec = compiler.compile(xslt); + XsltTransformer transformer = exec.load(); + transformer.setSource(new DOMSource(sourceDoc)); + transformer.setDestination(new DOMDestination(resultDoc)); + for (Map.Entry entry : params.entrySet()) { + net.sf.saxon.s9api.QName paramName = new net.sf.saxon.s9api.QName(entry.getKey()); + XdmValue paramValue = new XdmAtomicValue(entry.getValue()); + transformer.setParameter(paramName, paramValue); + } + transformer.transform(); + } + catch (SaxonApiException e) { + throw new RuntimeException(e); + } + return resultDoc; + } + + /** + * Expands character entity ({@literal &name;}) and numeric references ( + * {@literal &#xhhhh;} or {@literal &dddd;}) that occur within a given string value. + * It may be necessary to do this before processing an XPath expression. + * @param value A string representing text content. + * @return A string with all included references expanded. + */ + public static String expandReferencesInText(String value) { + StringBuilder wrapper = new StringBuilder(""); + wrapper.append(value).append(""); + Reader reader = new StringReader(wrapper.toString()); + String str = null; + try { + XMLStreamReader xsr = STAX_FACTORY.createXMLStreamReader(reader); + xsr.nextTag(); // document element + str = xsr.getElementText(); + } + catch (XMLStreamException xse) { + LOGR.log(Level.WARNING, xse.getMessage(), xse); + } + return str; + } + + /** + * Returns the qualified name of a DOM node. + * @param node A DOM node. + * @return A QName representing a qualified name. + */ + public static QName getQName(Node node) { + String localName = (null == node.getLocalName()) ? "" : node.getLocalName(); + return new QName(node.getNamespaceURI(), localName); + } - /** - * Returns the qualified name of a DOM node. - * - * @param node - * A DOM node. - * @return A QName representing a qualified name. - */ - public static QName getQName(Node node) { - String localName = (null == node.getLocalName()) ? "" : node.getLocalName(); - return new QName(node.getNamespaceURI(), localName); - } } diff --git a/src/main/java/org/opengis/cite/iso19142/util/package-info.java b/src/main/java/org/opengis/cite/iso19142/util/package-info.java index 5d1f4092..408c5861 100644 --- a/src/main/java/org/opengis/cite/iso19142/util/package-info.java +++ b/src/main/java/org/opengis/cite/iso19142/util/package-info.java @@ -2,4 +2,3 @@ * This package includes miscellaneous utility classes to support testing. */ package org.opengis.cite.iso19142.util; - diff --git a/src/main/java/org/opengis/cite/iso19142/versioning/FeatureVersioning.java b/src/main/java/org/opengis/cite/iso19142/versioning/FeatureVersioning.java index b39842d7..8324af8b 100644 --- a/src/main/java/org/opengis/cite/iso19142/versioning/FeatureVersioning.java +++ b/src/main/java/org/opengis/cite/iso19142/versioning/FeatureVersioning.java @@ -11,11 +11,11 @@ /** * Checks preconditions for running tests to verify that the IUT satisfies the - * requirements of the Feature versions conformance class. All - * tests are skipped if any preconditions are not met. The implementation - * constraints {@value #IMPL_FEATURE_VERSIONING} and {@value #IMPL_VERSION_NAV} - * (FES) must be set to "TRUE" in the capabilities document. - * + * requirements of the Feature versions conformance class. All tests are + * skipped if any preconditions are not met. The implementation constraints + * {@value #IMPL_FEATURE_VERSIONING} and {@value #IMPL_VERSION_NAV} (FES) must be set to + * "TRUE" in the capabilities document. + * *
                * {@code
                * 
              @@ -27,35 +27,35 @@
                * 
                * }
                * 
              - * + * * @see ATC - * A.1.14: Feature versions + * "http://docs.opengeospatial.org/is/09-025r2/09-025r2.html#requirement_14">ATC A.1.14: + * Feature versions */ public class FeatureVersioning { - public final static String IMPL_FEATURE_VERSIONING = "ImplementsFeatureVersioning"; - public final static String IMPL_VERSION_NAV = "ImplementsVersionNav"; + public final static String IMPL_FEATURE_VERSIONING = "ImplementsFeatureVersioning"; + + public final static String IMPL_VERSION_NAV = "ImplementsVersionNav"; + + /** + * This {@literal @BeforeTest} configuration method checks the implementation status + * of the {@value #IMPL_FEATURE_VERSIONING} and {@value #IMPL_VERSION_NAV} (FES) + * conformance classes. + * @param testContext Information about the test run environment. + */ + @BeforeTest + public void implementsFeatureVersioning(ITestContext testContext) { + Document wfsMetadata = (Document) testContext.getSuite().getAttribute(SuiteAttribute.TEST_SUBJECT.getName()); + if (!ServiceMetadataUtils.implementsConformanceClass(wfsMetadata, IMPL_FEATURE_VERSIONING)) { + throw new SkipException(ErrorMessage.format(ErrorMessageKeys.NOT_IMPLEMENTED, IMPL_FEATURE_VERSIONING)); + } + if (!ServiceMetadataUtils.implementsConformanceClass(wfsMetadata, IMPL_FEATURE_VERSIONING)) { + throw new SkipException(ErrorMessage.format(ErrorMessageKeys.NOT_IMPLEMENTED, IMPL_FEATURE_VERSIONING)); + } + if (!ServiceMetadataUtils.implementsConformanceClass(wfsMetadata, IMPL_VERSION_NAV)) { + throw new SkipException(ErrorMessage.format(ErrorMessageKeys.NOT_IMPLEMENTED, IMPL_VERSION_NAV)); + } + } - /** - * This {@literal @BeforeTest} configuration method checks the - * implementation status of the {@value #IMPL_FEATURE_VERSIONING} and - * {@value #IMPL_VERSION_NAV} (FES) conformance classes. - * - * @param testContext - * Information about the test run environment. - */ - @BeforeTest - public void implementsFeatureVersioning(ITestContext testContext) { - Document wfsMetadata = (Document) testContext.getSuite().getAttribute(SuiteAttribute.TEST_SUBJECT.getName()); - if (!ServiceMetadataUtils.implementsConformanceClass(wfsMetadata, IMPL_FEATURE_VERSIONING)) { - throw new SkipException(ErrorMessage.format(ErrorMessageKeys.NOT_IMPLEMENTED, IMPL_FEATURE_VERSIONING)); - } - if (!ServiceMetadataUtils.implementsConformanceClass(wfsMetadata, IMPL_FEATURE_VERSIONING)) { - throw new SkipException(ErrorMessage.format(ErrorMessageKeys.NOT_IMPLEMENTED, IMPL_FEATURE_VERSIONING)); - } - if (!ServiceMetadataUtils.implementsConformanceClass(wfsMetadata, IMPL_VERSION_NAV)) { - throw new SkipException(ErrorMessage.format(ErrorMessageKeys.NOT_IMPLEMENTED, IMPL_VERSION_NAV)); - } - } } diff --git a/src/main/java/org/opengis/cite/iso19142/versioning/VersioningTests.java b/src/main/java/org/opengis/cite/iso19142/versioning/VersioningTests.java index 5852c268..6fdb191d 100644 --- a/src/main/java/org/opengis/cite/iso19142/versioning/VersioningTests.java +++ b/src/main/java/org/opengis/cite/iso19142/versioning/VersioningTests.java @@ -43,227 +43,222 @@ import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response.Status; - /** - * Provides test methods that verify the behavior of the IUT with respect to - * creating and navigating feature versions. + * Provides test methods that verify the behavior of the IUT with respect to creating and + * navigating feature versions. */ public class VersioningTests extends BaseFixture { - private DataSampler dataSampler; - private Map modifiedFeatures = new HashMap(); + private DataSampler dataSampler; + + private Map modifiedFeatures = new HashMap(); + + @BeforeClass + public void getDataSamplerFromContext(ITestContext testContext) { + Object obj = testContext.getSuite().getAttribute(SuiteAttribute.SAMPLER.getName()); + if (null != obj) { + this.dataSampler = DataSampler.class.cast(obj); + } + } - @BeforeClass - public void getDataSamplerFromContext(ITestContext testContext) { - Object obj = testContext.getSuite().getAttribute(SuiteAttribute.SAMPLER.getName()); - if (null != obj) { - this.dataSampler = DataSampler.class.cast(obj); - } - } + @AfterClass + public void deleteModifiedFeatures() { + if (modifiedFeatures.isEmpty()) { + return; + } + Document rspEntity = this.wfsClient.deleteFeatures(this.modifiedFeatures, ProtocolBinding.ANY); + Element totalDeleted = (Element) rspEntity.getElementsByTagNameNS(Namespaces.WFS, WFS2.TOTAL_DEL).item(0); + if (null == totalDeleted || Integer.parseInt(totalDeleted.getTextContent().trim()) != modifiedFeatures.size()) { + Logger.getLogger(getClass().getName()) + .log(Level.WARNING, String.format("Failed to delete all new features: %s \n%s", this.modifiedFeatures, + XMLUtils.writeNodeToString(rspEntity))); + } + this.modifiedFeatures.clear(); + } - @AfterClass - public void deleteModifiedFeatures() { - if (modifiedFeatures.isEmpty()) { - return; - } - Document rspEntity = this.wfsClient.deleteFeatures(this.modifiedFeatures, ProtocolBinding.ANY); - Element totalDeleted = (Element) rspEntity.getElementsByTagNameNS(Namespaces.WFS, WFS2.TOTAL_DEL).item(0); - if (null == totalDeleted || Integer.parseInt(totalDeleted.getTextContent().trim()) != modifiedFeatures.size()) { - Logger.getLogger(getClass().getName()).log(Level.WARNING, - String.format("Failed to delete all new features: %s \n%s", this.modifiedFeatures, - XMLUtils.writeNodeToString(rspEntity))); - } - this.modifiedFeatures.clear(); - } + /** + * [{@code Test}] Submits a request to insert a feature. The response is expected to + * contain a single fes:ResourceId element with version="1" and the + * previousRid attribute NOT set. A subsequent query to retrieve the LAST + * version shall reveal that its state is "valid". + */ + @Test(description = "See OGC 09-025: 11.3.3.2, 15.3.4; OGC 09-026: 7.11.2") + public void firstVersionHasNoPredecessor() { + this.reqEntity = WFSMessage.createRequestEntity(WFS2.TRANSACTION, this.wfsVersion); + Element feature = this.dataSampler.randomlySelectFeatureInstance(); + QName typeName = new QName(feature.getNamespaceURI(), feature.getLocalName()); + feature.setAttributeNS(Namespaces.GML, "id", "id-" + System.currentTimeMillis()); + InsertTests.insertRandomIdentifier(feature); + WFSMessage.addInsertStatement(this.reqEntity, feature); + URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.TRANSACTION, + ProtocolBinding.POST); + Response rsp = this.wfsClient.submitRequest(new DOMSource(this.reqEntity), ProtocolBinding.POST, endpoint); + this.rspEntity = rsp.readEntity(Document.class); + Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + List newFeatureIDs = InsertTests.extractFeatureIdentifiers(this.rspEntity, WFS2.Transaction.INSERT); + assertEquals(newFeatureIDs.size(), 1, + "Unexpected number of fes:ResourceId elements in response entity (InsertResults)."); + ResourceId id = newFeatureIDs.get(0); + this.modifiedFeatures.put(id.getRid(), typeName); + assertNotNull(id.getVersion(), String.format("ResourceId is missing version designator (rid=%s)", id.getRid())); + assertEquals(id.getVersion(), "1", "Unexpected designator for first version."); + assertNull(id.getPreviousRid(), String.format("New feature has previousRid (rid=%s)", id.getRid())); + ResourceId qryId = new ResourceId(id.getRid()); + // get LAST version and check that state attribute is "valid" + qryId.setVersion(FES2.VersionAction.LAST.name()); + rsp = this.wfsClient.GetFeatureVersion(qryId, typeName); + this.rspEntity = rsp.readEntity(Document.class); + int numReturned = Integer.parseInt(this.rspEntity.getDocumentElement().getAttribute("numberReturned")); + assertEquals(numReturned, 1, ErrorMessage.get(ErrorMessageKeys.NUM_RETURNED)); + feature = (Element) this.rspEntity.getElementsByTagNameNS(typeName.getNamespaceURI(), typeName.getLocalPart()) + .item(0); + Element member = (Element) feature.getParentNode(); + assertEquals(member.getAttribute("state"), WFS2.VersionState.VALID.toString(), + ErrorMessage.get(ErrorMessageKeys.VERSION_STATE)); + } - /** - * [{@code Test}] Submits a request to insert a feature. The response is - * expected to contain a single fes:ResourceId element with - * version="1" and the previousRid attribute NOT - * set. A subsequent query to retrieve the LAST version shall reveal that - * its state is "valid". - */ - @Test(description = "See OGC 09-025: 11.3.3.2, 15.3.4; OGC 09-026: 7.11.2") - public void firstVersionHasNoPredecessor() { - this.reqEntity = WFSMessage.createRequestEntity(WFS2.TRANSACTION, this.wfsVersion); - Element feature = this.dataSampler.randomlySelectFeatureInstance(); - QName typeName = new QName(feature.getNamespaceURI(), feature.getLocalName()); - feature.setAttributeNS(Namespaces.GML, "id", "id-" + System.currentTimeMillis()); - InsertTests.insertRandomIdentifier(feature); - WFSMessage.addInsertStatement(this.reqEntity, feature); - URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.TRANSACTION, - ProtocolBinding.POST); - Response rsp = this.wfsClient.submitRequest(new DOMSource(this.reqEntity), ProtocolBinding.POST, - endpoint); - this.rspEntity = rsp.readEntity(Document.class); - Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - List newFeatureIDs = InsertTests.extractFeatureIdentifiers(this.rspEntity, WFS2.Transaction.INSERT); - assertEquals(newFeatureIDs.size(), 1, - "Unexpected number of fes:ResourceId elements in response entity (InsertResults)."); - ResourceId id = newFeatureIDs.get(0); - this.modifiedFeatures.put(id.getRid(), typeName); - assertNotNull(id.getVersion(), String.format("ResourceId is missing version designator (rid=%s)", id.getRid())); - assertEquals(id.getVersion(), "1", "Unexpected designator for first version."); - assertNull(id.getPreviousRid(), String.format("New feature has previousRid (rid=%s)", id.getRid())); - ResourceId qryId = new ResourceId(id.getRid()); - // get LAST version and check that state attribute is "valid" - qryId.setVersion(FES2.VersionAction.LAST.name()); - rsp = this.wfsClient.GetFeatureVersion(qryId, typeName); - this.rspEntity = rsp.readEntity(Document.class); - int numReturned = Integer.parseInt(this.rspEntity.getDocumentElement().getAttribute("numberReturned")); - assertEquals(numReturned, 1, ErrorMessage.get(ErrorMessageKeys.NUM_RETURNED)); - feature = (Element) this.rspEntity.getElementsByTagNameNS(typeName.getNamespaceURI(), typeName.getLocalPart()) - .item(0); - Element member = (Element) feature.getParentNode(); - assertEquals(member.getAttribute("state"), WFS2.VersionState.VALID.toString(), - ErrorMessage.get(ErrorMessageKeys.VERSION_STATE)); - } + /** + * [{@code Test}] Submits a request to update a feature property (gml:name). The + * response is expected to contain a single fes:ResourceId element with the + * previousRid attribute set. A subsequent query to retrieve the PREVIOUS + * version shall reveal that its state is "superseded". + */ + @Test(description = "See OGC 09-025: 15.3.5") + public void updatedVersionHasSupersededPredecessor() { + this.reqEntity = WFSMessage.createRequestEntity(WFS2.TRANSACTION, this.wfsVersion); + Element feature = this.dataSampler.randomlySelectFeatureInstance(); + QName typeName = new QName(feature.getNamespaceURI(), feature.getLocalName()); + String oldId = feature.getAttributeNS(Namespaces.GML, "id"); + Map properties = new HashMap(); + String newName = Randomizer.generateWords(2); + properties.put("gml:name[1]", newName); + this.rspEntity = wfsClient.updateFeature(this.reqEntity, oldId, typeName, properties, ProtocolBinding.POST); + List updatedIDs = InsertTests.extractFeatureIdentifiers(this.rspEntity, WFS2.Transaction.UPDATE); + assertEquals(updatedIDs.size(), 1, + "Unexpected number of fes:ResourceId elements in response entity (UpdateResults)."); + ResourceId id = updatedIDs.get(0); + this.modifiedFeatures.put(id.getRid(), typeName); + assertNotNull(id.getPreviousRid(), + String.format("Updated feature is missing previousRid (rid=%s)", id.getRid())); + // get PREVIOUS version and check that state attribute is "superseded" + ResourceId qryId = new ResourceId(id.getPreviousRid()); + Response rsp = this.wfsClient.GetFeatureVersion(qryId, typeName); + this.rspEntity = rsp.readEntity(Document.class); + int numReturned = Integer.parseInt(this.rspEntity.getDocumentElement().getAttribute("numberReturned")); + assertEquals(numReturned, 1, ErrorMessage.get(ErrorMessageKeys.NUM_RETURNED)); + feature = (Element) this.rspEntity.getElementsByTagNameNS(typeName.getNamespaceURI(), typeName.getLocalPart()) + .item(0); + Element member = (Element) feature.getParentNode(); + assertEquals(member.getAttribute("state"), WFS2.VersionState.SUPERSEDED.toString(), + ErrorMessage.get(ErrorMessageKeys.VERSION_STATE)); + } - /** - * [{@code Test}] Submits a request to update a feature property (gml:name). - * The response is expected to contain a single fes:ResourceId element with - * the previousRid attribute set. A subsequent query to - * retrieve the PREVIOUS version shall reveal that its state is - * "superseded". - */ - @Test(description = "See OGC 09-025: 15.3.5") - public void updatedVersionHasSupersededPredecessor() { - this.reqEntity = WFSMessage.createRequestEntity(WFS2.TRANSACTION, this.wfsVersion); - Element feature = this.dataSampler.randomlySelectFeatureInstance(); - QName typeName = new QName(feature.getNamespaceURI(), feature.getLocalName()); - String oldId = feature.getAttributeNS(Namespaces.GML, "id"); - Map properties = new HashMap(); - String newName = Randomizer.generateWords(2); - properties.put("gml:name[1]", newName); - this.rspEntity = wfsClient.updateFeature(this.reqEntity, oldId, typeName, properties, ProtocolBinding.POST); - List updatedIDs = InsertTests.extractFeatureIdentifiers(this.rspEntity, WFS2.Transaction.UPDATE); - assertEquals(updatedIDs.size(), 1, - "Unexpected number of fes:ResourceId elements in response entity (UpdateResults)."); - ResourceId id = updatedIDs.get(0); - this.modifiedFeatures.put(id.getRid(), typeName); - assertNotNull(id.getPreviousRid(), - String.format("Updated feature is missing previousRid (rid=%s)", id.getRid())); - // get PREVIOUS version and check that state attribute is "superseded" - ResourceId qryId = new ResourceId(id.getPreviousRid()); - Response rsp = this.wfsClient.GetFeatureVersion(qryId, typeName); - this.rspEntity = rsp.readEntity(Document.class); - int numReturned = Integer.parseInt(this.rspEntity.getDocumentElement().getAttribute("numberReturned")); - assertEquals(numReturned, 1, ErrorMessage.get(ErrorMessageKeys.NUM_RETURNED)); - feature = (Element) this.rspEntity.getElementsByTagNameNS(typeName.getNamespaceURI(), typeName.getLocalPart()) - .item(0); - Element member = (Element) feature.getParentNode(); - assertEquals(member.getAttribute("state"), WFS2.VersionState.SUPERSEDED.toString(), - ErrorMessage.get(ErrorMessageKeys.VERSION_STATE)); - } + /** + * [{@code Test}] Submits a request to update a superseded version (whose state is NOT + * "valid"). An exception report is expected in response; it must contain the error + * code OperationProcessingFailed and refer to the update handle. + * + * TODO: Enable this when expected behavior is clarified. + */ + @Test(description = "See OGC 09-025: Table 3, 15.4", enabled = false) + public void updateSupersededVersion() { + this.reqEntity = WFSMessage.createRequestEntity(WFS2.TRANSACTION, this.wfsVersion); + Element feature = this.dataSampler.randomlySelectFeatureInstance(); + QName typeName = new QName(feature.getNamespaceURI(), feature.getLocalName()); + String oldId = feature.getAttributeNS(Namespaces.GML, "id"); + Map properties = new HashMap(); + String newName = Randomizer.generateWords(2); + properties.put("gml:name[1]", newName); + this.rspEntity = this.wfsClient.updateFeature(this.reqEntity, oldId, typeName, properties, + ProtocolBinding.POST); + List updatedIDs = InsertTests.extractFeatureIdentifiers(this.rspEntity, WFS2.Transaction.UPDATE); + assertEquals(updatedIDs.size(), 1, + "Unexpected number of fes:ResourceId elements in response entity (UpdateResults)."); + ResourceId id = updatedIDs.get(0); + this.modifiedFeatures.put(id.getRid(), typeName); + assertFalse(id.getRid().equals(id.getPreviousRid()), + String.format("The rid and previousRid values should not match. ", id)); + // resubmit update request against previous revision + this.rspEntity = this.wfsClient.updateFeature(this.reqEntity, oldId, typeName, properties, + ProtocolBinding.POST); + ETSAssert.assertExceptionReport(this.rspEntity, "OperationProcessingFailed", "Update"); + } - /** - * [{@code Test}] Submits a request to update a superseded version (whose - * state is NOT "valid"). An exception report is expected in response; it - * must contain the error code OperationProcessingFailed and - * refer to the update handle. - * - * TODO: Enable this when expected behavior is clarified. - */ - @Test(description = "See OGC 09-025: Table 3, 15.4", enabled = false) - public void updateSupersededVersion() { - this.reqEntity = WFSMessage.createRequestEntity(WFS2.TRANSACTION, this.wfsVersion); - Element feature = this.dataSampler.randomlySelectFeatureInstance(); - QName typeName = new QName(feature.getNamespaceURI(), feature.getLocalName()); - String oldId = feature.getAttributeNS(Namespaces.GML, "id"); - Map properties = new HashMap(); - String newName = Randomizer.generateWords(2); - properties.put("gml:name[1]", newName); - this.rspEntity = this.wfsClient.updateFeature(this.reqEntity, oldId, typeName, properties, - ProtocolBinding.POST); - List updatedIDs = InsertTests.extractFeatureIdentifiers(this.rspEntity, WFS2.Transaction.UPDATE); - assertEquals(updatedIDs.size(), 1, - "Unexpected number of fes:ResourceId elements in response entity (UpdateResults)."); - ResourceId id = updatedIDs.get(0); - this.modifiedFeatures.put(id.getRid(), typeName); - assertFalse(id.getRid().equals(id.getPreviousRid()), - String.format("The rid and previousRid values should not match. ", id)); - // resubmit update request against previous revision - this.rspEntity = this.wfsClient.updateFeature(this.reqEntity, oldId, typeName, properties, - ProtocolBinding.POST); - ETSAssert.assertExceptionReport(this.rspEntity, "OperationProcessingFailed", "Update"); - } + /** + * [{@code Test}] Submits a request to replace a feature version. The response is + * expected to contain a single fes:ResourceId element with the + * previousRid attribute set. A subsequent query to retrieve the NEXT + * version shall produce an empty response. + */ + @Test(description = "See OGC 09-025: 15.3.6") + public void replacementVersionHasNoSuccessor() { + this.reqEntity = WFSMessage.createRequestEntity(WFS2.TRANSACTION, this.wfsVersion); + Element feature = this.dataSampler.randomlySelectFeatureInstance(); + QName typeName = new QName(feature.getNamespaceURI(), feature.getLocalName()); + InsertTests.insertRandomIdentifier(feature); + InsertTests.addRandomName(feature); + WFSMessage.addReplaceStatements(this.reqEntity, Collections.singletonList(feature)); + URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.TRANSACTION, + ProtocolBinding.POST); + Response rsp = this.wfsClient.submitRequest(new DOMSource(this.reqEntity), ProtocolBinding.POST, endpoint); + this.rspEntity = rsp.readEntity(Document.class); + Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + List newFeatureIDs = InsertTests.extractFeatureIdentifiers(this.rspEntity, + WFS2.Transaction.REPLACE); + assertEquals(newFeatureIDs.size(), 1, + "Unexpected number of fes:ResourceId elements in response entity (ReplaceResults)."); + ResourceId id = newFeatureIDs.get(0); + this.modifiedFeatures.put(id.getRid(), typeName); + assertNotNull(id.getPreviousRid(), + String.format("Replacement feature is missing previousRid (rid=%s)", id.getRid())); + // get NEXT version and check that it doesn't exist + ResourceId qryId = new ResourceId(id.getRid()); + qryId.setVersion(FES2.VersionAction.NEXT.name()); + rsp = this.wfsClient.GetFeatureVersion(qryId, typeName); + this.rspEntity = rsp.readEntity(Document.class); + int numMatched = Integer.parseInt(this.rspEntity.getDocumentElement().getAttribute("numberMatched")); + assertEquals(numMatched, 0, ErrorMessage.get(ErrorMessageKeys.NUM_MATCHED)); + } - /** - * [{@code Test}] Submits a request to replace a feature version. The - * response is expected to contain a single fes:ResourceId element with the - * previousRid attribute set. A subsequent query to retrieve - * the NEXT version shall produce an empty response. - */ - @Test(description = "See OGC 09-025: 15.3.6") - public void replacementVersionHasNoSuccessor() { - this.reqEntity = WFSMessage.createRequestEntity(WFS2.TRANSACTION, this.wfsVersion); - Element feature = this.dataSampler.randomlySelectFeatureInstance(); - QName typeName = new QName(feature.getNamespaceURI(), feature.getLocalName()); - InsertTests.insertRandomIdentifier(feature); - InsertTests.addRandomName(feature); - WFSMessage.addReplaceStatements(this.reqEntity, Collections.singletonList(feature)); - URI endpoint = ServiceMetadataUtils.getOperationEndpoint(this.wfsMetadata, WFS2.TRANSACTION, - ProtocolBinding.POST); - Response rsp = this.wfsClient.submitRequest(new DOMSource(this.reqEntity), ProtocolBinding.POST, - endpoint); - this.rspEntity = rsp.readEntity(Document.class); - Assert.assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - List newFeatureIDs = InsertTests.extractFeatureIdentifiers(this.rspEntity, - WFS2.Transaction.REPLACE); - assertEquals(newFeatureIDs.size(), 1, - "Unexpected number of fes:ResourceId elements in response entity (ReplaceResults)."); - ResourceId id = newFeatureIDs.get(0); - this.modifiedFeatures.put(id.getRid(), typeName); - assertNotNull(id.getPreviousRid(), - String.format("Replacement feature is missing previousRid (rid=%s)", id.getRid())); - // get NEXT version and check that it doesn't exist - ResourceId qryId = new ResourceId(id.getRid()); - qryId.setVersion(FES2.VersionAction.NEXT.name()); - rsp = this.wfsClient.GetFeatureVersion(qryId, typeName); - this.rspEntity = rsp.readEntity(Document.class); - int numMatched = Integer.parseInt(this.rspEntity.getDocumentElement().getAttribute("numberMatched")); - assertEquals(numMatched, 0, ErrorMessage.get(ErrorMessageKeys.NUM_MATCHED)); - } + /** + * [{@code Test}] Submits a request to delete a feature. The response is expected to + * report totalDeleted = 1. A subsequent query to retrieve the LAST version shall + * reveal that its state is "retired". + */ + @Test(description = "See OGC 09-025: 15.2.7.1") + public void deletedFeatureIsRetired() { + this.reqEntity = WFSMessage.createRequestEntity(WFS2.TRANSACTION, this.wfsVersion); + Element feature = this.dataSampler.randomlySelectFeatureInstance(); + String gmlId = feature.getAttributeNS(Namespaces.GML, "id"); + QName typeName = new QName(feature.getNamespaceURI(), feature.getLocalName()); + Response rsp = wfsClient.deleteFeature(this.reqEntity, gmlId, typeName); + this.rspEntity = rsp.readEntity(Document.class); + assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + int totalDeleted = Integer + .parseInt(this.rspEntity.getElementsByTagNameNS(Namespaces.WFS, WFS2.TOTAL_DEL).item(0).getTextContent()); + assertEquals(totalDeleted, 1, ErrorMessage.format(ErrorMessageKeys.UNEXPECTED_VALUE, WFS2.TOTAL_DEL)); + // get LAST version and check that state attribute is "superseded" + ResourceId qryId = new ResourceId(gmlId); + qryId.setVersion(FES2.VersionAction.LAST.name()); + rsp = this.wfsClient.GetFeatureVersion(qryId, typeName); + this.rspEntity = rsp.readEntity(Document.class); + int numReturned = Integer.parseInt(this.rspEntity.getDocumentElement().getAttribute("numberReturned")); + assertEquals(numReturned, 1, ErrorMessage.get(ErrorMessageKeys.NUM_RETURNED)); + feature = (Element) this.rspEntity.getElementsByTagNameNS(typeName.getNamespaceURI(), typeName.getLocalPart()) + .item(0); + Element member = (Element) feature.getParentNode(); + assertEquals(member.getAttribute("state"), WFS2.VersionState.RETIRED.toString(), + ErrorMessage.get(ErrorMessageKeys.VERSION_STATE)); + // attempt to restore deleted feature + Document doc = this.wfsClient.insert(Collections.singletonList(feature), ProtocolBinding.POST); + int totalInserted = Integer + .parseInt(doc.getElementsByTagNameNS(Namespaces.WFS, WFS2.TOTAL_INS).item(0).getTextContent()); + if (totalInserted != 1) { + Logger.getLogger(getClass().getName()) + .log(Level.WARNING, String.format("Failed to restore deleted feature with id = %s", gmlId)); + } + } - /** - * [{@code Test}] Submits a request to delete a feature. The response is - * expected to report totalDeleted = 1. A subsequent query to retrieve the - * LAST version shall reveal that its state is "retired". - */ - @Test(description = "See OGC 09-025: 15.2.7.1") - public void deletedFeatureIsRetired() { - this.reqEntity = WFSMessage.createRequestEntity(WFS2.TRANSACTION, this.wfsVersion); - Element feature = this.dataSampler.randomlySelectFeatureInstance(); - String gmlId = feature.getAttributeNS(Namespaces.GML, "id"); - QName typeName = new QName(feature.getNamespaceURI(), feature.getLocalName()); - Response rsp = wfsClient.deleteFeature(this.reqEntity, gmlId, typeName); - this.rspEntity = rsp.readEntity(Document.class); - assertEquals(rsp.getStatus(), Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - int totalDeleted = Integer.parseInt( - this.rspEntity.getElementsByTagNameNS(Namespaces.WFS, WFS2.TOTAL_DEL).item(0).getTextContent()); - assertEquals(totalDeleted, 1, ErrorMessage.format(ErrorMessageKeys.UNEXPECTED_VALUE, WFS2.TOTAL_DEL)); - // get LAST version and check that state attribute is "superseded" - ResourceId qryId = new ResourceId(gmlId); - qryId.setVersion(FES2.VersionAction.LAST.name()); - rsp = this.wfsClient.GetFeatureVersion(qryId, typeName); - this.rspEntity = rsp.readEntity(Document.class); - int numReturned = Integer.parseInt(this.rspEntity.getDocumentElement().getAttribute("numberReturned")); - assertEquals(numReturned, 1, ErrorMessage.get(ErrorMessageKeys.NUM_RETURNED)); - feature = (Element) this.rspEntity.getElementsByTagNameNS(typeName.getNamespaceURI(), typeName.getLocalPart()) - .item(0); - Element member = (Element) feature.getParentNode(); - assertEquals(member.getAttribute("state"), WFS2.VersionState.RETIRED.toString(), - ErrorMessage.get(ErrorMessageKeys.VERSION_STATE)); - // attempt to restore deleted feature - Document doc = this.wfsClient.insert(Collections.singletonList(feature), ProtocolBinding.POST); - int totalInserted = Integer - .parseInt(doc.getElementsByTagNameNS(Namespaces.WFS, WFS2.TOTAL_INS).item(0).getTextContent()); - if (totalInserted != 1) { - Logger.getLogger(getClass().getName()).log(Level.WARNING, - String.format("Failed to restore deleted feature with id = %s", gmlId)); - } - } } diff --git a/src/main/java/org/opengis/cite/iso19142/versioning/package-info.java b/src/main/java/org/opengis/cite/iso19142/versioning/package-info.java index ee2a9a94..bc2e8e3c 100644 --- a/src/main/java/org/opengis/cite/iso19142/versioning/package-info.java +++ b/src/main/java/org/opengis/cite/iso19142/versioning/package-info.java @@ -1,18 +1,16 @@ /** - * This package contains tests to verify that the IUT satisfies the requirements - * of the Feature versions conformance class. If the WFS - * service constraint ImplementsFeatureVersioning is set to "TRUE" in - * the capabilities document, a client may choose to navigate versions of - * feature instances. - * + * This package contains tests to verify that the IUT satisfies the requirements of the + * Feature versions conformance class. If the WFS service constraint + * ImplementsFeatureVersioning is set to "TRUE" in the capabilities document, a + * client may choose to navigate versions of feature instances. + * *

              - * A server implementation that supports versioning shall maintain version - * information about each feature instance, but exactly how this is accomplished - * is not specified. The ResourceId operator may be used to query feature - * versions; the filter constraint ImplementsVersionNav must be set to - * "TRUE" in the capabilities document. + * A server implementation that supports versioning shall maintain version information + * about each feature instance, but exactly how this is accomplished is not specified. The + * ResourceId operator may be used to query feature versions; the filter constraint + * ImplementsVersionNav must be set to "TRUE" in the capabilities document. *

              - * + * *

              * Sources *

              diff --git a/src/test/java/org/opengis/cite/iso19142/CommonTestFixture.java b/src/test/java/org/opengis/cite/iso19142/CommonTestFixture.java index 4f5aaea3..b5a204b6 100644 --- a/src/test/java/org/opengis/cite/iso19142/CommonTestFixture.java +++ b/src/test/java/org/opengis/cite/iso19142/CommonTestFixture.java @@ -13,30 +13,33 @@ import org.w3c.dom.ls.LSSerializer; public class CommonTestFixture { - protected static DocumentBuilder BUILDER; - - @Rule - public ExpectedException thrown = ExpectedException.none(); - - @BeforeClass - public static void initCommonTestFixture() throws ParserConfigurationException { - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(true); - BUILDER = dbf.newDocumentBuilder(); - - } - - protected String writeNodeToString(Node node) { - DOMImplementationRegistry registry; - try { - registry = DOMImplementationRegistry.newInstance(); - } catch (Exception e) { - throw new RuntimeException(e); - } - DOMImplementationLS impl = (DOMImplementationLS) registry.getDOMImplementation("LS"); - LSSerializer serializer = impl.createLSSerializer(); - serializer.getDomConfig().setParameter("xml-declaration", false); - serializer.getDomConfig().setParameter("format-pretty-print", true); - return serializer.writeToString(node); - } + + protected static DocumentBuilder BUILDER; + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @BeforeClass + public static void initCommonTestFixture() throws ParserConfigurationException { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + BUILDER = dbf.newDocumentBuilder(); + + } + + protected String writeNodeToString(Node node) { + DOMImplementationRegistry registry; + try { + registry = DOMImplementationRegistry.newInstance(); + } + catch (Exception e) { + throw new RuntimeException(e); + } + DOMImplementationLS impl = (DOMImplementationLS) registry.getDOMImplementation("LS"); + LSSerializer serializer = impl.createLSSerializer(); + serializer.getDomConfig().setParameter("xml-declaration", false); + serializer.getDomConfig().setParameter("format-pretty-print", true); + return serializer.writeToString(node); + } + } diff --git a/src/test/java/org/opengis/cite/iso19142/VerifyETSAssert.java b/src/test/java/org/opengis/cite/iso19142/VerifyETSAssert.java index e7df5bbd..bd65ef7c 100644 --- a/src/test/java/org/opengis/cite/iso19142/VerifyETSAssert.java +++ b/src/test/java/org/opengis/cite/iso19142/VerifyETSAssert.java @@ -31,88 +31,91 @@ public class VerifyETSAssert extends CommonTestFixture { - private static final String WADL_NS = "http://wadl.dev.java.net/2009/02"; - private static final String EX_NS = "http://example.org/ns1"; - private static XSModel model; - private static SchemaFactory factory; - @Rule - public ExpectedException thrown = ExpectedException.none(); - - public VerifyETSAssert() { - } - - @BeforeClass - public static void setUpClass() throws ParserConfigurationException, SAXException { - factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); - URL entityCatalog = VerifyAppSchemaUtils.class.getResource("/schema-catalog.xml"); - XmlSchemaCompiler xsdCompiler = new XmlSchemaCompiler(entityCatalog); - InputStream xis = VerifyAppSchemaUtils.class.getResourceAsStream("/xsd/simple.xsd"); - Schema schema = xsdCompiler.compileXmlSchema(new StreamSource(xis)); - model = XSModelBuilder.buildXMLSchemaModel(schema, EX_NS); - } - - @Test - public void validateUsingSchemaHints_expect2Errors() throws SAXException { - thrown.expect(AssertionError.class); - thrown.expectMessage("2 schema validation error(s) detected"); - URL url = this.getClass().getResource("/Gamma.xml"); - Schema schema = factory.newSchema(); - Validator validator = schema.newValidator(); - ETSAssert.assertSchemaValid(validator, new StreamSource(url.toString())); - } - - @Test - public void assertXPathWithNamespaceBindings() throws SAXException, IOException { - Document doc = BUILDER.parse(this.getClass().getResourceAsStream("/capabilities-simple.xml")); - Map nsBindings = new HashMap(); - nsBindings.put(WADL_NS, "ns1"); - String xpath = "//ns1:resources"; - ETSAssert.assertXPath(xpath, doc, nsBindings); - } - - @Test - public void assertXPath_expectFalse() throws SAXException, IOException { - thrown.expect(AssertionError.class); - thrown.expectMessage("Unexpected result evaluating XPath expression"); - Document doc = BUILDER.parse(this.getClass().getResourceAsStream("/capabilities-simple.xml")); - // using built-in namespace binding - String xpath = "//ows:OperationsMetadata/ows:Constraint[@name='SOAPEncoding']/ows:DefaultValue = 'TRUE'"; - ETSAssert.assertXPath(xpath, doc, null); - } - - @Test - public void evaluateXPathToBoolean() - throws SAXException, IOException { - Document doc = BUILDER.parse( this.getClass().getResourceAsStream( "/capabilities-simple.xml" ) ); - Map nsBindings = new HashMap<>(); - nsBindings.put( WADL_NS, "ns1" ); - String xpath = "//ns1:resources"; - boolean result = ETSAssert.evaluateXPathToBoolean( xpath, doc, nsBindings ); - assertThat( result, CoreMatchers.is( true ) ); - } - - @Test - public void evaluateXPathToBoolean_expectFalse() - throws SAXException, IOException { - Document doc = BUILDER.parse( this.getClass().getResourceAsStream( "/capabilities-simple.xml" ) ); - Map nsBindings = new HashMap<>(); - nsBindings.put( WADL_NS, "ns1" ); - String xpath = "//ns1:unknown"; - boolean result = ETSAssert.evaluateXPathToBoolean( xpath, doc, nsBindings ); - assertThat( result, CoreMatchers.is( false ) ); - } - - @Test - public void assertStatusCodeMatches() { - ETSAssert.assertStatusCode(400, new int[] { 500, 403, 400 }); - } - - @Test - public void assertDateTimeProperty() throws SAXException, IOException { - Document gml = BUILDER.parse(this.getClass().getResourceAsStream("/wfs/SimpleFeature-SF01.xml")); - XSElementDeclaration propDecl = model.getElementDeclaration("dateTimeProperty", EX_NS); - Map expectedValues = Collections.singletonMap(propDecl, "2016-07-21T14:47:51Z"); - ETSAssert.assertSimpleProperties(gml.getDocumentElement(), expectedValues, - Collections.singletonMap(EX_NS, "tns")); - } + private static final String WADL_NS = "http://wadl.dev.java.net/2009/02"; + + private static final String EX_NS = "http://example.org/ns1"; + + private static XSModel model; + + private static SchemaFactory factory; + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + public VerifyETSAssert() { + } + + @BeforeClass + public static void setUpClass() throws ParserConfigurationException, SAXException { + factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); + URL entityCatalog = VerifyAppSchemaUtils.class.getResource("/schema-catalog.xml"); + XmlSchemaCompiler xsdCompiler = new XmlSchemaCompiler(entityCatalog); + InputStream xis = VerifyAppSchemaUtils.class.getResourceAsStream("/xsd/simple.xsd"); + Schema schema = xsdCompiler.compileXmlSchema(new StreamSource(xis)); + model = XSModelBuilder.buildXMLSchemaModel(schema, EX_NS); + } + + @Test + public void validateUsingSchemaHints_expect2Errors() throws SAXException { + thrown.expect(AssertionError.class); + thrown.expectMessage("2 schema validation error(s) detected"); + URL url = this.getClass().getResource("/Gamma.xml"); + Schema schema = factory.newSchema(); + Validator validator = schema.newValidator(); + ETSAssert.assertSchemaValid(validator, new StreamSource(url.toString())); + } + + @Test + public void assertXPathWithNamespaceBindings() throws SAXException, IOException { + Document doc = BUILDER.parse(this.getClass().getResourceAsStream("/capabilities-simple.xml")); + Map nsBindings = new HashMap(); + nsBindings.put(WADL_NS, "ns1"); + String xpath = "//ns1:resources"; + ETSAssert.assertXPath(xpath, doc, nsBindings); + } + + @Test + public void assertXPath_expectFalse() throws SAXException, IOException { + thrown.expect(AssertionError.class); + thrown.expectMessage("Unexpected result evaluating XPath expression"); + Document doc = BUILDER.parse(this.getClass().getResourceAsStream("/capabilities-simple.xml")); + // using built-in namespace binding + String xpath = "//ows:OperationsMetadata/ows:Constraint[@name='SOAPEncoding']/ows:DefaultValue = 'TRUE'"; + ETSAssert.assertXPath(xpath, doc, null); + } + + @Test + public void evaluateXPathToBoolean() throws SAXException, IOException { + Document doc = BUILDER.parse(this.getClass().getResourceAsStream("/capabilities-simple.xml")); + Map nsBindings = new HashMap<>(); + nsBindings.put(WADL_NS, "ns1"); + String xpath = "//ns1:resources"; + boolean result = ETSAssert.evaluateXPathToBoolean(xpath, doc, nsBindings); + assertThat(result, CoreMatchers.is(true)); + } + + @Test + public void evaluateXPathToBoolean_expectFalse() throws SAXException, IOException { + Document doc = BUILDER.parse(this.getClass().getResourceAsStream("/capabilities-simple.xml")); + Map nsBindings = new HashMap<>(); + nsBindings.put(WADL_NS, "ns1"); + String xpath = "//ns1:unknown"; + boolean result = ETSAssert.evaluateXPathToBoolean(xpath, doc, nsBindings); + assertThat(result, CoreMatchers.is(false)); + } + + @Test + public void assertStatusCodeMatches() { + ETSAssert.assertStatusCode(400, new int[] { 500, 403, 400 }); + } + + @Test + public void assertDateTimeProperty() throws SAXException, IOException { + Document gml = BUILDER.parse(this.getClass().getResourceAsStream("/wfs/SimpleFeature-SF01.xml")); + XSElementDeclaration propDecl = model.getElementDeclaration("dateTimeProperty", EX_NS); + Map expectedValues = Collections.singletonMap(propDecl, "2016-07-21T14:47:51Z"); + ETSAssert.assertSimpleProperties(gml.getDocumentElement(), expectedValues, + Collections.singletonMap(EX_NS, "tns")); + } + } diff --git a/src/test/java/org/opengis/cite/iso19142/VerifyFeatureTypeInfo.java b/src/test/java/org/opengis/cite/iso19142/VerifyFeatureTypeInfo.java index ab54947d..a205bf59 100644 --- a/src/test/java/org/opengis/cite/iso19142/VerifyFeatureTypeInfo.java +++ b/src/test/java/org/opengis/cite/iso19142/VerifyFeatureTypeInfo.java @@ -44,10 +44,8 @@ public void getDefaultExtent_epsg4326() throws FactoryException { Envelope envelope = iut.getSpatialExtent(); Assert.assertNotNull("Default extent is null.", envelope); String lowerCoordAsWKT = envelope.getLowerCorner().toString(); - String lowerCoord = lowerCoordAsWKT.substring( - lowerCoordAsWKT.indexOf('(') + 1, lowerCoordAsWKT.indexOf(')')); - Assert.assertEquals("Unexpected coordinates of lower corner.", - "-90 -180", lowerCoord); + String lowerCoord = lowerCoordAsWKT.substring(lowerCoordAsWKT.indexOf('(') + 1, lowerCoordAsWKT.indexOf(')')); + Assert.assertEquals("Unexpected coordinates of lower corner.", "-90 -180", lowerCoord); } @Test @@ -58,7 +56,7 @@ public void getDefaultExtent_epsg26910() throws FactoryException { Envelope envelope = iut.getSpatialExtent(); Assert.assertNotNull("Default extent is null.", envelope); DirectPosition pos = envelope.getLowerCorner(); - Assert.assertTrue("Expected easting of lower corner > 200000 ", - pos.getOrdinate(0) > 200000); + Assert.assertTrue("Expected easting of lower corner > 200000 ", pos.getOrdinate(0) > 200000); } + } diff --git a/src/test/java/org/opengis/cite/iso19142/VerifySuiteFixtureListener.java b/src/test/java/org/opengis/cite/iso19142/VerifySuiteFixtureListener.java index 8e020d43..f53553ec 100644 --- a/src/test/java/org/opengis/cite/iso19142/VerifySuiteFixtureListener.java +++ b/src/test/java/org/opengis/cite/iso19142/VerifySuiteFixtureListener.java @@ -22,49 +22,49 @@ public class VerifySuiteFixtureListener { - private static XmlSuite xmlSuite; - private static ISuite suite; + private static XmlSuite xmlSuite; - public VerifySuiteFixtureListener() { - } + private static ISuite suite; - @BeforeClass - public static void setUpClass() { - xmlSuite = mock(XmlSuite.class); - suite = mock(ISuite.class); - when(suite.getXmlSuite()).thenReturn(xmlSuite); - } + public VerifySuiteFixtureListener() { + } - @AfterClass - public static void tearDownClass() { - } + @BeforeClass + public static void setUpClass() { + xmlSuite = mock(XmlSuite.class); + suite = mock(ISuite.class); + when(suite.getXmlSuite()).thenReturn(xmlSuite); + } - @Before - public void setUp() { - } + @AfterClass + public static void tearDownClass() { + } - @After - public void tearDown() { - } + @Before + public void setUp() { + } - @Test(expected = IllegalArgumentException.class) - public void noSuiteParameters() { - Map params = new HashMap(); - when(xmlSuite.getParameters()).thenReturn(params); - SuiteFixtureListener iut = new SuiteFixtureListener(); - iut.onStart(suite); - } + @After + public void tearDown() { + } + + @Test(expected = IllegalArgumentException.class) + public void noSuiteParameters() { + Map params = new HashMap(); + when(xmlSuite.getParameters()).thenReturn(params); + SuiteFixtureListener iut = new SuiteFixtureListener(); + iut.onStart(suite); + } + + @Test + public void processWFSParameter() throws URISyntaxException { + URL url = this.getClass().getResource("/capabilities-simple.xml"); + Map params = new HashMap(); + params.put(TestRunArg.WFS.toString(), url.toURI().toString()); + when(xmlSuite.getParameters()).thenReturn(params); + SuiteFixtureListener iut = new SuiteFixtureListener(); + iut.onStart(suite); + verify(suite).setAttribute(eq(SuiteAttribute.TEST_SUBJECT.getName()), isA(Document.class)); + } - @Test - public void processWFSParameter() throws URISyntaxException { - URL url = this.getClass().getResource("/capabilities-simple.xml"); - Map params = new HashMap(); - params.put(TestRunArg.WFS.toString(), url.toURI().toString()); - when(xmlSuite.getParameters()).thenReturn(params); - SuiteFixtureListener iut = new SuiteFixtureListener(); - iut.onStart(suite); - verify(suite).setAttribute( - eq(SuiteAttribute.TEST_SUBJECT.getName()), - isA(Document.class)); - } } diff --git a/src/test/java/org/opengis/cite/iso19142/VerifyTestNGController.java b/src/test/java/org/opengis/cite/iso19142/VerifyTestNGController.java index d788f9d0..e4a87dee 100644 --- a/src/test/java/org/opengis/cite/iso19142/VerifyTestNGController.java +++ b/src/test/java/org/opengis/cite/iso19142/VerifyTestNGController.java @@ -26,37 +26,39 @@ */ public class VerifyTestNGController { - private static DocumentBuilder docBuilder; - @Rule - public ExpectedException thrown = ExpectedException.none(); - - @BeforeClass - public static void initParser() throws ParserConfigurationException { - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(true); - dbf.setValidating(false); - dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); - docBuilder = dbf.newDocumentBuilder(); - } - - @Test - public void etsCode() throws Exception { - TestNGController controller = new TestNGController(); - String etsCode = controller.getCode(); - assertEquals("Unexpected ETS code.", "wfs20", etsCode); - } - - @Test - public void missingArgument() throws URISyntaxException, IOException, SAXException { - thrown.expect(IllegalArgumentException.class); - thrown.expectMessage("'iut' or 'wfs' must be present"); - Properties testRunProps = new Properties(); - URL sut = getClass().getResource("/wfs/capabilities-acme.xml"); - testRunProps.setProperty("sut", sut.toURI().toString()); - ByteArrayOutputStream outStream = new ByteArrayOutputStream(1024); - testRunProps.storeToXML(outStream, "Integration test"); - Document testRunArgs = docBuilder.parse(new ByteArrayInputStream(outStream.toByteArray())); - TestNGController controller = new TestNGController(); - controller.validateTestRunArgs(testRunArgs); - } + private static DocumentBuilder docBuilder; + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @BeforeClass + public static void initParser() throws ParserConfigurationException { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + dbf.setValidating(false); + dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + docBuilder = dbf.newDocumentBuilder(); + } + + @Test + public void etsCode() throws Exception { + TestNGController controller = new TestNGController(); + String etsCode = controller.getCode(); + assertEquals("Unexpected ETS code.", "wfs20", etsCode); + } + + @Test + public void missingArgument() throws URISyntaxException, IOException, SAXException { + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("'iut' or 'wfs' must be present"); + Properties testRunProps = new Properties(); + URL sut = getClass().getResource("/wfs/capabilities-acme.xml"); + testRunProps.setProperty("sut", sut.toURI().toString()); + ByteArrayOutputStream outStream = new ByteArrayOutputStream(1024); + testRunProps.storeToXML(outStream, "Integration test"); + Document testRunArgs = docBuilder.parse(new ByteArrayInputStream(outStream.toByteArray())); + TestNGController controller = new TestNGController(); + controller.validateTestRunArgs(testRunArgs); + } + } diff --git a/src/test/java/org/opengis/cite/iso19142/basic/VerifyBasicGetFeatureTests.java b/src/test/java/org/opengis/cite/iso19142/basic/VerifyBasicGetFeatureTests.java index 956c017d..1d43b96a 100644 --- a/src/test/java/org/opengis/cite/iso19142/basic/VerifyBasicGetFeatureTests.java +++ b/src/test/java/org/opengis/cite/iso19142/basic/VerifyBasicGetFeatureTests.java @@ -21,32 +21,34 @@ */ public class VerifyBasicGetFeatureTests { - private static ITestContext testContext; - private static ISuite suite; - - public VerifyBasicGetFeatureTests() { - } - - @BeforeClass - public static void setUpClass() throws Exception { - testContext = mock(ITestContext.class); - suite = mock(ISuite.class); - when(testContext.getSuite()).thenReturn(suite); - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(true); - dbf.newDocumentBuilder(); - } - - @Test - public void buildAndResetValidator() { - BasicGetFeatureTests iut = new BasicGetFeatureTests(); - iut.buildValidator(); - Validator validator = iut.hintsValidator; - LSResourceResolver resolver = validator.getResourceResolver(); - assertNotNull("Resolver is null.", resolver); - iut.resetValidator(); - LSInput resource = resolver.resolveResource(Namespaces.XSD.toString(), - Namespaces.WFS, null, WFS2.SCHEMA_URI, null); - assertNotNull("WFS2 schema resource is null.", resource); - } + private static ITestContext testContext; + + private static ISuite suite; + + public VerifyBasicGetFeatureTests() { + } + + @BeforeClass + public static void setUpClass() throws Exception { + testContext = mock(ITestContext.class); + suite = mock(ISuite.class); + when(testContext.getSuite()).thenReturn(suite); + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + dbf.newDocumentBuilder(); + } + + @Test + public void buildAndResetValidator() { + BasicGetFeatureTests iut = new BasicGetFeatureTests(); + iut.buildValidator(); + Validator validator = iut.hintsValidator; + LSResourceResolver resolver = validator.getResourceResolver(); + assertNotNull("Resolver is null.", resolver); + iut.resetValidator(); + LSInput resource = resolver.resolveResource(Namespaces.XSD.toString(), Namespaces.WFS, null, WFS2.SCHEMA_URI, + null); + assertNotNull("WFS2 schema resource is null.", resource); + } + } diff --git a/src/test/java/org/opengis/cite/iso19142/basic/VerifyGetPropertyValueTests.java b/src/test/java/org/opengis/cite/iso19142/basic/VerifyGetPropertyValueTests.java index 3d56b8f5..398249c9 100644 --- a/src/test/java/org/opengis/cite/iso19142/basic/VerifyGetPropertyValueTests.java +++ b/src/test/java/org/opengis/cite/iso19142/basic/VerifyGetPropertyValueTests.java @@ -27,7 +27,9 @@ public class VerifyGetPropertyValueTests { private static ITestContext testContext; + private static ISuite suite; + private static DocumentBuilder docBuilder; public VerifyGetPropertyValueTests() { @@ -53,16 +55,12 @@ public void tearDown() { @Test public void addTwoQueries() throws SAXException, IOException { - Document doc = docBuilder.parse(this.getClass().getResourceAsStream( - "/GetPropertyValue.xml")); + Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/GetPropertyValue.xml")); GetPropertyValueTests iut = new GetPropertyValueTests(); - iut.addQuery(doc, new QName("http://cite.opengeospatial.org/gmlsf", - "PrimitiveGeoFeature")); - iut.addQuery(doc, new QName( - "http://www.opengis.net/citygml/building/2.0", "Building")); - NodeList qryElems = doc.getElementsByTagNameNS(Namespaces.WFS, - WFS2.QUERY_ELEM); - Assert.assertEquals("Unexpected number of wfs:Query elements.", 3, - qryElems.getLength()); + iut.addQuery(doc, new QName("http://cite.opengeospatial.org/gmlsf", "PrimitiveGeoFeature")); + iut.addQuery(doc, new QName("http://www.opengis.net/citygml/building/2.0", "Building")); + NodeList qryElems = doc.getElementsByTagNameNS(Namespaces.WFS, WFS2.QUERY_ELEM); + Assert.assertEquals("Unexpected number of wfs:Query elements.", 3, qryElems.getLength()); } + } diff --git a/src/test/java/org/opengis/cite/iso19142/basic/filter/VerifyComparisonOperatorTests.java b/src/test/java/org/opengis/cite/iso19142/basic/filter/VerifyComparisonOperatorTests.java index 3cd6d75f..4b85f6ef 100644 --- a/src/test/java/org/opengis/cite/iso19142/basic/filter/VerifyComparisonOperatorTests.java +++ b/src/test/java/org/opengis/cite/iso19142/basic/filter/VerifyComparisonOperatorTests.java @@ -54,125 +54,133 @@ */ public class VerifyComparisonOperatorTests { - private static final String NS1 = "http://example.org/ns1"; - private static ITestContext testContext; - private static ISuite suite; - private static XSModel model; - private static DataSampler dataSampler; - private static DocumentBuilder docBuilder; - - public VerifyComparisonOperatorTests() { - } - - @BeforeClass - public static void prepareMockTestContext() throws ParserConfigurationException { - testContext = mock(ITestContext.class); - suite = mock(ISuite.class); - when(testContext.getSuite()).thenReturn(suite); - dataSampler = mock(DataSampler.class); - when(suite.getAttribute(SuiteAttribute.SAMPLER.getName())).thenReturn(dataSampler); - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(true); - docBuilder = dbf.newDocumentBuilder(); - } - - @BeforeClass - public static void buildSchemaModel() throws SAXException { - URL entityCatalog = VerifyAppSchemaUtils.class.getResource("/schema-catalog.xml"); - XmlSchemaCompiler xsdCompiler = new XmlSchemaCompiler(entityCatalog); - InputStream xis = VerifyAppSchemaUtils.class.getResourceAsStream("/xsd/simple.xsd"); - Schema schema = xsdCompiler.compileXmlSchema(new StreamSource(xis)); - model = XSModelBuilder.buildXMLSchemaModel(schema, NS1); - } - - @Test - public void findNumericPropertyOfSimpleFeature() { - QName featureType = new QName( NS1, "SimpleFeature" ); - when( suite.getAttribute( org.opengis.cite.iso19136.SuiteAttribute.XSMODEL.getName() ) ).thenReturn( model ); - - List integerValueList = Arrays.asList( "600", "100", "200" ); - QName integerPropName = new QName( NS1, "intProperty2" ); - when( dataSampler.getSimplePropertyValues( eq( featureType ), eq( integerPropName ), any() ) ).thenReturn( integerValueList ); - - List doubleValueList = Arrays.asList( "600.68", "100.47", "200.54" ); - QName doublePropName = new QName( NS1, "measurand" ); - when( dataSampler.getSimplePropertyValues( eq( featureType ), eq( doublePropName ), any() ) ).thenReturn( doubleValueList ); - - ComparisonOperatorTests iut = new ComparisonOperatorTests(); - iut.initQueryFilterFixture( testContext ); - Set dataTypes = iut.getNumericDataTypes( model ); - Map prop = iut.findFeaturePropertyValue( model, featureType, dataTypes ); - assertFalse( "Expected to find numeric property for SimpleFeature.", prop.isEmpty() ); - Entry propRange = prop.entrySet().iterator().next(); - String[] range = propRange.getValue(); - assertThat( "Unexpected maximum value.", range[range.length - 1], anyOf( is( "600" ), is( "600.68" ) ) ); - } - - @Test - public void addPropertyIsGreaterThanPredicate() throws SAXException, IOException { - Document reqEntity = docBuilder - .parse(this.getClass().getResourceAsStream("/GetFeature/GetFeature-Minimal.xml")); - QName featureType = new QName(NS1, "SimpleFeature"); - WFSMessage.appendSimpleQuery(reqEntity, featureType); - QName propName = new QName(NS1, "decimalProperty"); - String literalValue = "122.6"; - ComparisonOperatorTests iut = new ComparisonOperatorTests(); - iut.addComparisonPredicate(reqEntity, FES2.GREATER_THAN, propName, literalValue, true, null); - Element predicate = (Element) reqEntity.getElementsByTagNameNS(Namespaces.FES, FES2.GREATER_THAN).item(0); - assertEquals("Unexpected Literal value.", literalValue, predicate.getFirstChild().getTextContent()); - } - - @Test - public void sortNumericValues() { - String[] values = new String[] { "0.8", "1.314E+1", "-100.5" }; - ComparisonOperatorTests iut = new ComparisonOperatorTests(); - iut.sortValues(values); - assertEquals("Unexpected values[0].", "-100.5", values[0]); - } - - @Test - public void sortDateTimeValues() { - String[] values = new String[] { "2012-12-12T17:00:00+04:00", "2012-12-12T10:00:00-08:00", - "2012-12-12T17:00:00Z" }; - ComparisonOperatorTests iut = new ComparisonOperatorTests(); - iut.sortValues(values); - String latestValue = values[values.length - 1]; - ZonedDateTime latest = ZonedDateTime.parse(latestValue, DateTimeFormatter.ISO_OFFSET_DATE_TIME); - ZonedDateTime expected = ZonedDateTime.parse("2012-12-12T18:00:00Z", DateTimeFormatter.ISO_OFFSET_DATE_TIME); - assertTrue("Expected latest to be 2012-12-12T18:00:00Z", latest.isEqual(expected)); - } - - @Test - public void sortDateValues() { - String[] values = new String[] { "2011-12-31Z", "2011-12-01Z", "2011-12-10Z" }; - ComparisonOperatorTests iut = new ComparisonOperatorTests(); - iut.sortValues(values); - String latestValue = values[values.length - 1]; - assertEquals("2011-12-31Z", latestValue); - } - - @Test - public void calculateIntegerRange() { - String[] values = new String[] { "9", "-2", "7" }; - ComparisonOperatorTests iut = new ComparisonOperatorTests(); - String[] range = iut.calculateRange(values, new QName(XMLConstants.W3C_XML_SCHEMA_NS_URI, "integer")); - assertEquals("Unexpected min value.", -2, Integer.parseInt(range[0])); - } - - @Test - public void calculateDecimalRange() { - String[] values = new String[] { "6919880000", "3571970000", "964773000", "45401800" }; - ComparisonOperatorTests iut = new ComparisonOperatorTests(); - String[] range = iut.calculateRange(values, new QName(XMLConstants.W3C_XML_SCHEMA_NS_URI, "decimal")); - assertEquals("Unexpected min value.", "45401800", range[0]); - assertEquals("Unexpected max value.", 6919880000d, Double.parseDouble(range[1]), 0); - } - - @Test - public void sortNumericValuesWithExponents() { - String[] values = new String[] { "0.8", "1.20528E5", "1.20528E3" }; - ComparisonOperatorTests iut = new ComparisonOperatorTests(); - iut.sortValues(values); - assertEquals("Unexpected max value.", 120528, Double.parseDouble(values[values.length - 1]), 0); - } + private static final String NS1 = "http://example.org/ns1"; + + private static ITestContext testContext; + + private static ISuite suite; + + private static XSModel model; + + private static DataSampler dataSampler; + + private static DocumentBuilder docBuilder; + + public VerifyComparisonOperatorTests() { + } + + @BeforeClass + public static void prepareMockTestContext() throws ParserConfigurationException { + testContext = mock(ITestContext.class); + suite = mock(ISuite.class); + when(testContext.getSuite()).thenReturn(suite); + dataSampler = mock(DataSampler.class); + when(suite.getAttribute(SuiteAttribute.SAMPLER.getName())).thenReturn(dataSampler); + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + docBuilder = dbf.newDocumentBuilder(); + } + + @BeforeClass + public static void buildSchemaModel() throws SAXException { + URL entityCatalog = VerifyAppSchemaUtils.class.getResource("/schema-catalog.xml"); + XmlSchemaCompiler xsdCompiler = new XmlSchemaCompiler(entityCatalog); + InputStream xis = VerifyAppSchemaUtils.class.getResourceAsStream("/xsd/simple.xsd"); + Schema schema = xsdCompiler.compileXmlSchema(new StreamSource(xis)); + model = XSModelBuilder.buildXMLSchemaModel(schema, NS1); + } + + @Test + public void findNumericPropertyOfSimpleFeature() { + QName featureType = new QName(NS1, "SimpleFeature"); + when(suite.getAttribute(org.opengis.cite.iso19136.SuiteAttribute.XSMODEL.getName())).thenReturn(model); + + List integerValueList = Arrays.asList("600", "100", "200"); + QName integerPropName = new QName(NS1, "intProperty2"); + when(dataSampler.getSimplePropertyValues(eq(featureType), eq(integerPropName), any())) + .thenReturn(integerValueList); + + List doubleValueList = Arrays.asList("600.68", "100.47", "200.54"); + QName doublePropName = new QName(NS1, "measurand"); + when(dataSampler.getSimplePropertyValues(eq(featureType), eq(doublePropName), any())) + .thenReturn(doubleValueList); + + ComparisonOperatorTests iut = new ComparisonOperatorTests(); + iut.initQueryFilterFixture(testContext); + Set dataTypes = iut.getNumericDataTypes(model); + Map prop = iut.findFeaturePropertyValue(model, featureType, dataTypes); + assertFalse("Expected to find numeric property for SimpleFeature.", prop.isEmpty()); + Entry propRange = prop.entrySet().iterator().next(); + String[] range = propRange.getValue(); + assertThat("Unexpected maximum value.", range[range.length - 1], anyOf(is("600"), is("600.68"))); + } + + @Test + public void addPropertyIsGreaterThanPredicate() throws SAXException, IOException { + Document reqEntity = docBuilder + .parse(this.getClass().getResourceAsStream("/GetFeature/GetFeature-Minimal.xml")); + QName featureType = new QName(NS1, "SimpleFeature"); + WFSMessage.appendSimpleQuery(reqEntity, featureType); + QName propName = new QName(NS1, "decimalProperty"); + String literalValue = "122.6"; + ComparisonOperatorTests iut = new ComparisonOperatorTests(); + iut.addComparisonPredicate(reqEntity, FES2.GREATER_THAN, propName, literalValue, true, null); + Element predicate = (Element) reqEntity.getElementsByTagNameNS(Namespaces.FES, FES2.GREATER_THAN).item(0); + assertEquals("Unexpected Literal value.", literalValue, predicate.getFirstChild().getTextContent()); + } + + @Test + public void sortNumericValues() { + String[] values = new String[] { "0.8", "1.314E+1", "-100.5" }; + ComparisonOperatorTests iut = new ComparisonOperatorTests(); + iut.sortValues(values); + assertEquals("Unexpected values[0].", "-100.5", values[0]); + } + + @Test + public void sortDateTimeValues() { + String[] values = new String[] { "2012-12-12T17:00:00+04:00", "2012-12-12T10:00:00-08:00", + "2012-12-12T17:00:00Z" }; + ComparisonOperatorTests iut = new ComparisonOperatorTests(); + iut.sortValues(values); + String latestValue = values[values.length - 1]; + ZonedDateTime latest = ZonedDateTime.parse(latestValue, DateTimeFormatter.ISO_OFFSET_DATE_TIME); + ZonedDateTime expected = ZonedDateTime.parse("2012-12-12T18:00:00Z", DateTimeFormatter.ISO_OFFSET_DATE_TIME); + assertTrue("Expected latest to be 2012-12-12T18:00:00Z", latest.isEqual(expected)); + } + + @Test + public void sortDateValues() { + String[] values = new String[] { "2011-12-31Z", "2011-12-01Z", "2011-12-10Z" }; + ComparisonOperatorTests iut = new ComparisonOperatorTests(); + iut.sortValues(values); + String latestValue = values[values.length - 1]; + assertEquals("2011-12-31Z", latestValue); + } + + @Test + public void calculateIntegerRange() { + String[] values = new String[] { "9", "-2", "7" }; + ComparisonOperatorTests iut = new ComparisonOperatorTests(); + String[] range = iut.calculateRange(values, new QName(XMLConstants.W3C_XML_SCHEMA_NS_URI, "integer")); + assertEquals("Unexpected min value.", -2, Integer.parseInt(range[0])); + } + + @Test + public void calculateDecimalRange() { + String[] values = new String[] { "6919880000", "3571970000", "964773000", "45401800" }; + ComparisonOperatorTests iut = new ComparisonOperatorTests(); + String[] range = iut.calculateRange(values, new QName(XMLConstants.W3C_XML_SCHEMA_NS_URI, "decimal")); + assertEquals("Unexpected min value.", "45401800", range[0]); + assertEquals("Unexpected max value.", 6919880000d, Double.parseDouble(range[1]), 0); + } + + @Test + public void sortNumericValuesWithExponents() { + String[] values = new String[] { "0.8", "1.20528E5", "1.20528E3" }; + ComparisonOperatorTests iut = new ComparisonOperatorTests(); + iut.sortValues(values); + assertEquals("Unexpected max value.", 120528, Double.parseDouble(values[values.length - 1]), 0); + } + } diff --git a/src/test/java/org/opengis/cite/iso19142/basic/filter/VerifyPropertyIsLikeOperatorTests.java b/src/test/java/org/opengis/cite/iso19142/basic/filter/VerifyPropertyIsLikeOperatorTests.java index 7c7ea7dc..b92be5b3 100644 --- a/src/test/java/org/opengis/cite/iso19142/basic/filter/VerifyPropertyIsLikeOperatorTests.java +++ b/src/test/java/org/opengis/cite/iso19142/basic/filter/VerifyPropertyIsLikeOperatorTests.java @@ -34,9 +34,13 @@ public class VerifyPropertyIsLikeOperatorTests { private static final String NS1 = "http://example.org/ns1"; + private static ITestContext testContext; + private static ISuite suite; + private static XSModel model; + private static DataSampler dataSampler; public VerifyPropertyIsLikeOperatorTests() { @@ -48,68 +52,46 @@ public static void prepareMockTestContext() { suite = mock(ISuite.class); when(testContext.getSuite()).thenReturn(suite); dataSampler = mock(DataSampler.class); - when(suite.getAttribute(SuiteAttribute.SAMPLER.getName())).thenReturn( - dataSampler); + when(suite.getAttribute(SuiteAttribute.SAMPLER.getName())).thenReturn(dataSampler); } @BeforeClass public static void buildSchemaModel() throws SAXException { - URL entityCatalog = VerifyAppSchemaUtils.class - .getResource("/schema-catalog.xml"); + URL entityCatalog = VerifyAppSchemaUtils.class.getResource("/schema-catalog.xml"); XmlSchemaCompiler xsdCompiler = new XmlSchemaCompiler(entityCatalog); - InputStream xis = VerifyAppSchemaUtils.class - .getResourceAsStream("/xsd/simple.xsd"); + InputStream xis = VerifyAppSchemaUtils.class.getResourceAsStream("/xsd/simple.xsd"); Schema schema = xsdCompiler.compileXmlSchema(new StreamSource(xis)); model = XSModelBuilder.buildXMLSchemaModel(schema, NS1); } @Test public void generateStringPatternForLocationNameInSimpleFeature() { - List valueList = Arrays.asList("Haida Gwaii", - "Queen Charlotte Islands"); - when( - suite.getAttribute(org.opengis.cite.iso19136.SuiteAttribute.XSMODEL - .getName())).thenReturn(model); - when( - dataSampler.getSimplePropertyValues(any(QName.class), - any(QName.class), any())) - .thenReturn(valueList); + List valueList = Arrays.asList("Haida Gwaii", "Queen Charlotte Islands"); + when(suite.getAttribute(org.opengis.cite.iso19136.SuiteAttribute.XSMODEL.getName())).thenReturn(model); + when(dataSampler.getSimplePropertyValues(any(QName.class), any(QName.class), any())).thenReturn(valueList); QName featureType = new QName(NS1, "SimpleFeature"); PropertyIsLikeOperatorTests iut = new PropertyIsLikeOperatorTests(); iut.initQueryFilterFixture(testContext); - Map prop = iut - .generateMatchingStringPattern(featureType); - Assert.assertFalse( - "Expected to find string property value pattern for SimpleFeature.", - prop.isEmpty()); + Map prop = iut.generateMatchingStringPattern(featureType); + Assert.assertFalse("Expected to find string property value pattern for SimpleFeature.", prop.isEmpty()); Entry entry = prop.entrySet().iterator().next(); - Assert.assertEquals("Unexpected property name", "locationName", entry - .getKey().getLocalPart()); - Assert.assertEquals("Unexpected pattern.", "*ida Gwaii", - entry.getValue()); + Assert.assertEquals("Unexpected property name", "locationName", entry.getKey().getLocalPart()); + Assert.assertEquals("Unexpected pattern.", "*ida Gwaii", entry.getValue()); } @Test public void generateStringPatternForLocationCodeInComplexFeature() { List valueList = Arrays.asList("CA-BC", "CA-AB"); - when( - suite.getAttribute(org.opengis.cite.iso19136.SuiteAttribute.XSMODEL - .getName())).thenReturn(model); - when( - dataSampler.getSimplePropertyValues(any(QName.class), - any(QName.class), any())) - .thenReturn(valueList); + when(suite.getAttribute(org.opengis.cite.iso19136.SuiteAttribute.XSMODEL.getName())).thenReturn(model); + when(dataSampler.getSimplePropertyValues(any(QName.class), any(QName.class), any())).thenReturn(valueList); QName featureType = new QName(NS1, "ComplexFeature"); PropertyIsLikeOperatorTests iut = new PropertyIsLikeOperatorTests(); iut.initQueryFilterFixture(testContext); - Map prop = iut - .generateMatchingStringPattern(featureType); - Assert.assertFalse( - "Expected to find string property value pattern for ComplexFeature.", - prop.isEmpty()); + Map prop = iut.generateMatchingStringPattern(featureType); + Assert.assertFalse("Expected to find string property value pattern for ComplexFeature.", prop.isEmpty()); Entry entry = prop.entrySet().iterator().next(); - Assert.assertEquals("Unexpected property name", "locationCode", entry - .getKey().getLocalPart()); + Assert.assertEquals("Unexpected property name", "locationCode", entry.getKey().getLocalPart()); Assert.assertEquals("Unexpected pattern.", "*-BC", entry.getValue()); } + } diff --git a/src/test/java/org/opengis/cite/iso19142/basic/filter/VerifyResourceId.java b/src/test/java/org/opengis/cite/iso19142/basic/filter/VerifyResourceId.java index 90f338c9..ec99db10 100644 --- a/src/test/java/org/opengis/cite/iso19142/basic/filter/VerifyResourceId.java +++ b/src/test/java/org/opengis/cite/iso19142/basic/filter/VerifyResourceId.java @@ -22,47 +22,51 @@ public class VerifyResourceId { - @Test - public void createWithValidTemporalInterval() { - ResourceId id = new ResourceId("id-01"); - id.setEnd("2016-09-30T11:31:00-07:00"); - id.setStart("2016-07-01T00:00:00-07:00"); - assertNull(id.getPreviousRid()); - assertEquals("2016-07-01T00:00:00-07:00", id.getStart()); - } + @Test + public void createWithValidTemporalInterval() { + ResourceId id = new ResourceId("id-01"); + id.setEnd("2016-09-30T11:31:00-07:00"); + id.setStart("2016-07-01T00:00:00-07:00"); + assertNull(id.getPreviousRid()); + assertEquals("2016-07-01T00:00:00-07:00", id.getStart()); + } - @Test - public void createWithInvalidTimestamps() { - ResourceId id = new ResourceId("id-01"); - id.setStart("2016-07-01"); - id.setEnd("2016-09-30"); - assertNull(id.getStart()); - } + @Test + public void createWithInvalidTimestamps() { + ResourceId id = new ResourceId("id-01"); + id.setStart("2016-07-01"); + id.setEnd("2016-09-30"); + assertNull(id.getStart()); + } - @Test - public void toElement() { - ResourceId id = new ResourceId("id-02"); - id.setVersion("1"); - Element idElem = id.toElement(); - assertTrue(idElem.getNamespaceURI().equals(Namespaces.FES)); - assertTrue("Found @previousRid", idElem.getAttribute("previousRid").isEmpty()); - } + @Test + public void toElement() { + ResourceId id = new ResourceId("id-02"); + id.setVersion("1"); + Element idElem = id.toElement(); + assertTrue(idElem.getNamespaceURI().equals(Namespaces.FES)); + assertTrue("Found @previousRid", idElem.getAttribute("previousRid").isEmpty()); + } - @Test - public void assertElementCount() { - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(true); - DocumentBuilder docBuilder = null; - try { + @Test + public void assertElementCount() { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + DocumentBuilder docBuilder = null; + try { docBuilder = dbf.newDocumentBuilder(); - } catch (ParserConfigurationException e) { + } + catch (ParserConfigurationException e) { fail("Could not set up DocumentBuilder."); } - try { - Document doc = docBuilder.parse(getClass().getClassLoader().getResourceAsStream("GetFeature/FeatureCollection-River.xml")); + try { + Document doc = docBuilder + .parse(getClass().getClassLoader().getResourceAsStream("GetFeature/FeatureCollection-River.xml")); ETSAssert.assertDescendantElementCount(doc, new QName(Namespaces.WFS, WFS2.MEMBER), 4); - } catch (SAXException | IOException e) { + } + catch (SAXException | IOException e) { fail("Could not parse feature collection."); } - } + } + } diff --git a/src/test/java/org/opengis/cite/iso19142/basic/filter/spatial/VerifyBBOXTests.java b/src/test/java/org/opengis/cite/iso19142/basic/filter/spatial/VerifyBBOXTests.java index 5112da58..ca01a99c 100644 --- a/src/test/java/org/opengis/cite/iso19142/basic/filter/spatial/VerifyBBOXTests.java +++ b/src/test/java/org/opengis/cite/iso19142/basic/filter/spatial/VerifyBBOXTests.java @@ -30,7 +30,9 @@ public class VerifyBBOXTests extends CommonTestFixture { private static final String NS1 = "http://example.org/ns1"; + private static ITestContext testContext; + private static ISuite suite; public VerifyBBOXTests() { @@ -45,36 +47,26 @@ public static void initClass() throws Exception { @Test public void addBBOX() throws SAXException, IOException { - Document req = BUILDER.parse(this.getClass().getResourceAsStream( - "/GetFeature/GetFeature-Minimal.xml")); + Document req = BUILDER.parse(this.getClass().getResourceAsStream("/GetFeature/GetFeature-Minimal.xml")); WFSMessage.appendSimpleQuery(req, new QName(NS1, "Type1")); - Document env = BUILDER.parse(this.getClass().getResourceAsStream( - "/Envelope.xml")); + Document env = BUILDER.parse(this.getClass().getResourceAsStream("/Envelope.xml")); BBOXTests iut = new BBOXTests(); iut.addBBOXPredicate(req, env.getDocumentElement(), null); - NodeList envElems = req.getElementsByTagNameNS(Namespaces.GML, - "Envelope"); - Assert.assertEquals("Unexpected number of gml:Envelope elements.", 1, - envElems.getLength()); + NodeList envElems = req.getElementsByTagNameNS(Namespaces.GML, "Envelope"); + Assert.assertEquals("Unexpected number of gml:Envelope elements.", 1, envElems.getLength()); } @Test public void addBBOXWithValueReference() throws SAXException, IOException { - Document req = BUILDER.parse(this.getClass().getResourceAsStream( - "/GetFeature/GetFeature-Minimal.xml")); + Document req = BUILDER.parse(this.getClass().getResourceAsStream("/GetFeature/GetFeature-Minimal.xml")); WFSMessage.appendSimpleQuery(req, new QName(NS1, "Type1")); - Document env = BUILDER.parse(this.getClass().getResourceAsStream( - "/Envelope.xml")); + Document env = BUILDER.parse(this.getClass().getResourceAsStream("/Envelope.xml")); BBOXTests iut = new BBOXTests(); - Element valueRef = XMLUtils.createElement(new QName(Namespaces.FES, - "ValueReference", "fes")); + Element valueRef = XMLUtils.createElement(new QName(Namespaces.FES, "ValueReference", "fes")); valueRef.setTextContent("tns:geom"); iut.addBBOXPredicate(req, env.getDocumentElement(), valueRef); - Node node = req - .getElementsByTagNameNS(Namespaces.FES, "ValueReference").item( - 0); - Assert.assertEquals("Unexpected fes:ValueReference.", "tns:geom", - node.getTextContent()); + Node node = req.getElementsByTagNameNS(Namespaces.FES, "ValueReference").item(0); + Assert.assertEquals("Unexpected fes:ValueReference.", "tns:geom", node.getTextContent()); } @Test @@ -83,8 +75,9 @@ public void bboxExpansion() { BBOXTests iut = new BBOXTests(); Document document = iut.envelopeAsGML(envelope); Node nodeLower = document.getElementsByTagNameNS(Namespaces.GML, "lowerCorner").item(0); - Assert.assertEquals("Unexpected LowerCorner", "-0.01 -0.01",nodeLower.getTextContent()); + Assert.assertEquals("Unexpected LowerCorner", "-0.01 -0.01", nodeLower.getTextContent()); Node nodeUpper = document.getElementsByTagNameNS(Namespaces.GML, "upperCorner").item(0); Assert.assertEquals("Unexpected LowerCorner", "1.01 1.01", nodeUpper.getTextContent()); } + } diff --git a/src/test/java/org/opengis/cite/iso19142/basic/filter/spatial/VerifyIntersectsTests.java b/src/test/java/org/opengis/cite/iso19142/basic/filter/spatial/VerifyIntersectsTests.java index 17acbe34..eca5a117 100644 --- a/src/test/java/org/opengis/cite/iso19142/basic/filter/spatial/VerifyIntersectsTests.java +++ b/src/test/java/org/opengis/cite/iso19142/basic/filter/spatial/VerifyIntersectsTests.java @@ -28,38 +28,41 @@ public class VerifyIntersectsTests { - private static final String NS1 = "http://example.org/ns1"; - private static DocumentBuilder docBuilder; - private static XSModel model; + private static final String NS1 = "http://example.org/ns1"; - @BeforeClass - public static void initParser() throws ParserConfigurationException { - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(true); - dbf.setValidating(false); - dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); - docBuilder = dbf.newDocumentBuilder(); - } + private static DocumentBuilder docBuilder; - @BeforeClass - public static void buildSchemaModel() throws SAXException { - URL entityCatalog = VerifyAppSchemaUtils.class.getResource("/schema-catalog.xml"); - XmlSchemaCompiler xsdCompiler = new XmlSchemaCompiler(entityCatalog); - InputStream xis = VerifyAppSchemaUtils.class.getResourceAsStream("/xsd/simple.xsd"); - Schema schema = xsdCompiler.compileXmlSchema(new StreamSource(xis)); - model = XSModelBuilder.buildXMLSchemaModel(schema, NS1); - } + private static XSModel model; + + @BeforeClass + public static void initParser() throws ParserConfigurationException { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + dbf.setValidating(false); + dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + docBuilder = dbf.newDocumentBuilder(); + } + + @BeforeClass + public static void buildSchemaModel() throws SAXException { + URL entityCatalog = VerifyAppSchemaUtils.class.getResource("/schema-catalog.xml"); + XmlSchemaCompiler xsdCompiler = new XmlSchemaCompiler(entityCatalog); + InputStream xis = VerifyAppSchemaUtils.class.getResourceAsStream("/xsd/simple.xsd"); + Schema schema = xsdCompiler.compileXmlSchema(new StreamSource(xis)); + model = XSModelBuilder.buildXMLSchemaModel(schema, NS1); + } + + @Test + public void addIntersectsPredicate() throws SAXException, IOException { + Document reqEntity = WFSMessage.createRequestEntity("GetFeature-Minimal", "2.0.2"); + WFSMessage.appendSimpleQuery(reqEntity, new QName(NS1, "SimpleFeature")); + Element gmlGeom = docBuilder.parse(getClass().getResourceAsStream("Polygon-01.xml")).getDocumentElement(); + Element valueRef = WFSMessage.createValueReference(model.getElementDeclaration("lineProperty", NS1)); + IntersectsTests iut = new IntersectsTests(); + iut.addSpatialPredicate(reqEntity, "Intersects", gmlGeom, valueRef); + Node predicate = reqEntity.getElementsByTagNameNS(Namespaces.FES, "Intersects").item(0); + assertNotNull(predicate); + assertEquals("Unexpected number of operands", 2, predicate.getChildNodes().getLength()); + } - @Test - public void addIntersectsPredicate() throws SAXException, IOException { - Document reqEntity = WFSMessage.createRequestEntity("GetFeature-Minimal", "2.0.2"); - WFSMessage.appendSimpleQuery(reqEntity, new QName(NS1, "SimpleFeature")); - Element gmlGeom = docBuilder.parse(getClass().getResourceAsStream("Polygon-01.xml")).getDocumentElement(); - Element valueRef = WFSMessage.createValueReference(model.getElementDeclaration("lineProperty", NS1)); - IntersectsTests iut = new IntersectsTests(); - iut.addSpatialPredicate(reqEntity, "Intersects", gmlGeom, valueRef); - Node predicate = reqEntity.getElementsByTagNameNS(Namespaces.FES, "Intersects").item(0); - assertNotNull(predicate); - assertEquals("Unexpected number of operands", 2, predicate.getChildNodes().getLength()); - } } diff --git a/src/test/java/org/opengis/cite/iso19142/basic/filter/temporal/VerifyDuringTests.java b/src/test/java/org/opengis/cite/iso19142/basic/filter/temporal/VerifyDuringTests.java index e2a65e98..ec3c4576 100644 --- a/src/test/java/org/opengis/cite/iso19142/basic/filter/temporal/VerifyDuringTests.java +++ b/src/test/java/org/opengis/cite/iso19142/basic/filter/temporal/VerifyDuringTests.java @@ -15,16 +15,17 @@ public class VerifyDuringTests { - private static final String NS1 = "http://example.org/ns1"; - private static XSModel model; - - @BeforeClass - public static void buildSchemaModel() throws SAXException { - URL entityCatalog = VerifyAppSchemaUtils.class.getResource("/schema-catalog.xml"); - XmlSchemaCompiler xsdCompiler = new XmlSchemaCompiler(entityCatalog); - InputStream xis = VerifyAppSchemaUtils.class.getResourceAsStream("/xsd/simple.xsd"); - Schema schema = xsdCompiler.compileXmlSchema(new StreamSource(xis)); - model = XSModelBuilder.buildXMLSchemaModel(schema, NS1); - } + private static final String NS1 = "http://example.org/ns1"; + + private static XSModel model; + + @BeforeClass + public static void buildSchemaModel() throws SAXException { + URL entityCatalog = VerifyAppSchemaUtils.class.getResource("/schema-catalog.xml"); + XmlSchemaCompiler xsdCompiler = new XmlSchemaCompiler(entityCatalog); + InputStream xis = VerifyAppSchemaUtils.class.getResourceAsStream("/xsd/simple.xsd"); + Schema schema = xsdCompiler.compileXmlSchema(new StreamSource(xis)); + model = XSModelBuilder.buildXMLSchemaModel(schema, NS1); + } } diff --git a/src/test/java/org/opengis/cite/iso19142/basic/filter/temporal/VerifyTemporalFilter.java b/src/test/java/org/opengis/cite/iso19142/basic/filter/temporal/VerifyTemporalFilter.java index 894b9619..28ae652d 100644 --- a/src/test/java/org/opengis/cite/iso19142/basic/filter/temporal/VerifyTemporalFilter.java +++ b/src/test/java/org/opengis/cite/iso19142/basic/filter/temporal/VerifyTemporalFilter.java @@ -21,38 +21,41 @@ public class VerifyTemporalFilter { - private static ITestContext testContext; - private static ISuite suite; - private static DocumentBuilder docBuilder; - @Rule - public ExpectedException thrown = ExpectedException.none(); - - @BeforeClass - public static void setUpClass() throws Exception { - testContext = mock(ITestContext.class); - suite = mock(ISuite.class); - when(testContext.getSuite()).thenReturn(suite); - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(true); - docBuilder = dbf.newDocumentBuilder(); - } - - @Test - public void implementsMinTemporalFilter() throws SAXException, IOException { - Document doc = docBuilder.parse(getClass().getResourceAsStream("/wfs/capabilities-acme.xml")); - when(suite.getAttribute(SuiteAttribute.TEST_SUBJECT.getName())).thenReturn(doc); - TemporalFilter iut = new TemporalFilter(); - iut.implementsMinimumTemporalFilter(testContext); - } - - @Test - public void doesNotImplementMinTemporalFilter() throws SAXException, IOException { - thrown.expect(SkipException.class); - thrown.expectMessage("Capability not implemented"); - Document doc = docBuilder.parse(getClass().getResourceAsStream("/capabilities-simple.xml")); - when(suite.getAttribute(SuiteAttribute.TEST_SUBJECT.getName())).thenReturn(doc); - TemporalFilter iut = new TemporalFilter(); - iut.implementsMinimumTemporalFilter(testContext); - } + private static ITestContext testContext; + + private static ISuite suite; + + private static DocumentBuilder docBuilder; + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @BeforeClass + public static void setUpClass() throws Exception { + testContext = mock(ITestContext.class); + suite = mock(ISuite.class); + when(testContext.getSuite()).thenReturn(suite); + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + docBuilder = dbf.newDocumentBuilder(); + } + + @Test + public void implementsMinTemporalFilter() throws SAXException, IOException { + Document doc = docBuilder.parse(getClass().getResourceAsStream("/wfs/capabilities-acme.xml")); + when(suite.getAttribute(SuiteAttribute.TEST_SUBJECT.getName())).thenReturn(doc); + TemporalFilter iut = new TemporalFilter(); + iut.implementsMinimumTemporalFilter(testContext); + } + + @Test + public void doesNotImplementMinTemporalFilter() throws SAXException, IOException { + thrown.expect(SkipException.class); + thrown.expectMessage("Capability not implemented"); + Document doc = docBuilder.parse(getClass().getResourceAsStream("/capabilities-simple.xml")); + when(suite.getAttribute(SuiteAttribute.TEST_SUBJECT.getName())).thenReturn(doc); + TemporalFilter iut = new TemporalFilter(); + iut.implementsMinimumTemporalFilter(testContext); + } } diff --git a/src/test/java/org/opengis/cite/iso19142/basic/filter/temporal/VerifyTemporalQuery.java b/src/test/java/org/opengis/cite/iso19142/basic/filter/temporal/VerifyTemporalQuery.java index a688e839..523bbded 100644 --- a/src/test/java/org/opengis/cite/iso19142/basic/filter/temporal/VerifyTemporalQuery.java +++ b/src/test/java/org/opengis/cite/iso19142/basic/filter/temporal/VerifyTemporalQuery.java @@ -33,64 +33,69 @@ public class VerifyTemporalQuery extends CommonTestFixture { - private static final String EX_NS = "http://example.org/ns1"; - private static XSModel model; - @Rule - public ExpectedException thrown = ExpectedException.none(); + private static final String EX_NS = "http://example.org/ns1"; - @BeforeClass - public static void initFixture() throws Exception { - URL entityCatalog = VerifyTemporalQuery.class.getResource("/schema-catalog.xml"); - XmlSchemaCompiler xsdCompiler = new XmlSchemaCompiler(entityCatalog); - InputStream xis = VerifyTemporalQuery.class.getResourceAsStream("/xsd/simple.xsd"); - Schema schema = xsdCompiler.compileXmlSchema(new StreamSource(xis)); - model = XSModelBuilder.buildXMLSchemaModel(schema, EX_NS); - } + private static XSModel model; - @Test - public void parseDateTime() { - XSTypeDefinition typeDef = model.getTypeDefinition("dateTime", XMLConstants.W3C_XML_SCHEMA_NS_URI); - TemporalGeometricPrimitive result = TemporalQuery.parseTemporalValue("2016-05-15T12:00:00Z", typeDef); - assertTrue("Expected result: " + Instant.class.getName(), Instant.class.isInstance(result)); - Instant instant = Instant.class.cast(result); - assertTrue(instant.getDate().toInstant().toString().startsWith("2016-05")); - } + @Rule + public ExpectedException thrown = ExpectedException.none(); - @Test - public void parseDate() { - XSTypeDefinition typeDef = model.getTypeDefinition("date", XMLConstants.W3C_XML_SCHEMA_NS_URI); - TemporalGeometricPrimitive result = TemporalQuery.parseTemporalValue("2016-05-15Z", typeDef); - assertTrue("Expected result: " + Period.class.getName(), Period.class.isInstance(result)); - Period period = Period.class.cast(result); - assertTrue("Expected duration: PT23H59M59S", period.length().toString().equals("PT23H59M59S")); - } + @BeforeClass + public static void initFixture() throws Exception { + URL entityCatalog = VerifyTemporalQuery.class.getResource("/schema-catalog.xml"); + XmlSchemaCompiler xsdCompiler = new XmlSchemaCompiler(entityCatalog); + InputStream xis = VerifyTemporalQuery.class.getResourceAsStream("/xsd/simple.xsd"); + Schema schema = xsdCompiler.compileXmlSchema(new StreamSource(xis)); + model = XSModelBuilder.buildXMLSchemaModel(schema, EX_NS); + } - @Test(expected = SkipException.class) - public void parseDateWithoutTimezone() { - XSTypeDefinition typeDef = model.getTypeDefinition("date", XMLConstants.W3C_XML_SCHEMA_NS_URI); - TemporalQuery.parseTemporalValue("2016-05-15", typeDef); - } + @Test + public void parseDateTime() { + XSTypeDefinition typeDef = model.getTypeDefinition("dateTime", XMLConstants.W3C_XML_SCHEMA_NS_URI); + TemporalGeometricPrimitive result = TemporalQuery.parseTemporalValue("2016-05-15T12:00:00Z", typeDef); + assertTrue("Expected result: " + Instant.class.getName(), Instant.class.isInstance(result)); + Instant instant = Instant.class.cast(result); + assertTrue(instant.getDate().toInstant().toString().startsWith("2016-05")); + } - @Test - public void parseDateWithOffset() { - XSTypeDefinition typeDef = model.getTypeDefinition("date", XMLConstants.W3C_XML_SCHEMA_NS_URI); - TemporalGeometricPrimitive result = TemporalQuery.parseTemporalValue("2016-05-15-04:00", typeDef); - assertTrue("Expected result: " + Period.class.getName(), Period.class.isInstance(result)); - Period period = Period.class.cast(result); - DateTimeFormatter xsdDateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSX"); - ZonedDateTime actualStart = ZonedDateTime - .parse(period.getBeginning().getDate().toInstant().atOffset(java.time.ZoneOffset.of("+02:00")) - .format(xsdDateTimeFormatter)); - ZonedDateTime expectedStart = ZonedDateTime.parse("2016-05-15T00:00:00-04:00"); - assertTrue(actualStart.isEqual(expectedStart)); - } + @Test + public void parseDate() { + XSTypeDefinition typeDef = model.getTypeDefinition("date", XMLConstants.W3C_XML_SCHEMA_NS_URI); + TemporalGeometricPrimitive result = TemporalQuery.parseTemporalValue("2016-05-15Z", typeDef); + assertTrue("Expected result: " + Period.class.getName(), Period.class.isInstance(result)); + Period period = Period.class.cast(result); + assertTrue("Expected duration: PT23H59M59S", period.length().toString().equals("PT23H59M59S")); + } + + @Test(expected = SkipException.class) + public void parseDateWithoutTimezone() { + XSTypeDefinition typeDef = model.getTypeDefinition("date", XMLConstants.W3C_XML_SCHEMA_NS_URI); + TemporalQuery.parseTemporalValue("2016-05-15", typeDef); + } + + @Test + public void parseDateWithOffset() { + XSTypeDefinition typeDef = model.getTypeDefinition("date", XMLConstants.W3C_XML_SCHEMA_NS_URI); + TemporalGeometricPrimitive result = TemporalQuery.parseTemporalValue("2016-05-15-04:00", typeDef); + assertTrue("Expected result: " + Period.class.getName(), Period.class.isInstance(result)); + Period period = Period.class.cast(result); + DateTimeFormatter xsdDateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSX"); + ZonedDateTime actualStart = ZonedDateTime.parse(period.getBeginning() + .getDate() + .toInstant() + .atOffset(java.time.ZoneOffset.of("+02:00")) + .format(xsdDateTimeFormatter)); + ZonedDateTime expectedStart = ZonedDateTime.parse("2016-05-15T00:00:00-04:00"); + assertTrue(actualStart.isEqual(expectedStart)); + } + + @Test + public void extractValidTimeValues() throws SAXException, IOException { + Document rspEntity = BUILDER.parse(getClass().getResourceAsStream("/wfs/FeatureCollection-ComplexFeature.xml")); + XSElementDeclaration validTimeProp = model.getElementDeclaration("validTime", EX_NS); + List tmValues = TemporalQuery.extractTemporalNodes(rspEntity, validTimeProp, model); + assertEquals("Unexpected number of temporal values.", 2, tmValues.size()); + assertEquals("First node has unexpected name.", "TimePeriod", tmValues.get(0).getLocalName()); + } - @Test - public void extractValidTimeValues() throws SAXException, IOException { - Document rspEntity = BUILDER.parse(getClass().getResourceAsStream("/wfs/FeatureCollection-ComplexFeature.xml")); - XSElementDeclaration validTimeProp = model.getElementDeclaration("validTime", EX_NS); - List tmValues = TemporalQuery.extractTemporalNodes(rspEntity, validTimeProp, model); - assertEquals("Unexpected number of temporal values.", 2, tmValues.size()); - assertEquals("First node has unexpected name.", "TimePeriod", tmValues.get(0).getLocalName()); - } } diff --git a/src/test/java/org/opengis/cite/iso19142/joins/VerifyJoinQueryUtils.java b/src/test/java/org/opengis/cite/iso19142/joins/VerifyJoinQueryUtils.java index 4d5ea85f..9a28ab9f 100644 --- a/src/test/java/org/opengis/cite/iso19142/joins/VerifyJoinQueryUtils.java +++ b/src/test/java/org/opengis/cite/iso19142/joins/VerifyJoinQueryUtils.java @@ -30,18 +30,17 @@ public class VerifyJoinQueryUtils extends CommonTestFixture { private static XSModel model; + private static final String NS1 = "http://example.org/ns1"; + private static final String FES = Namespaces.FES; @BeforeClass public static void buildSchemaModel() throws SAXException { - URL entityCatalog = VerifyAppSchemaUtils.class - .getResource("/schema-catalog.xml"); + URL entityCatalog = VerifyAppSchemaUtils.class.getResource("/schema-catalog.xml"); XmlSchemaCompiler xsdCompiler = new XmlSchemaCompiler(entityCatalog); - InputStream inStream = VerifyAppSchemaUtils.class - .getResourceAsStream("/xsd/simple.xsd"); - Schema schema = xsdCompiler - .compileXmlSchema(new StreamSource(inStream)); + InputStream inStream = VerifyAppSchemaUtils.class.getResourceAsStream("/xsd/simple.xsd"); + Schema schema = xsdCompiler.compileXmlSchema(new StreamSource(inStream)); model = XSModelBuilder.buildXMLSchemaModel(schema, NS1); } @@ -49,34 +48,27 @@ public static void buildSchemaModel() throws SAXException { public void noFeatureProperties() { thrown.expect(NullPointerException.class); thrown.expectMessage("Feature properties are required"); - Document reqEntity = WFSMessage.createRequestEntity( - "GetFeature-Minimal", "2.0.0"); + Document reqEntity = WFSMessage.createRequestEntity("GetFeature-Minimal", "2.0.0"); List geomProps = null; - JoinQueryUtils.appendSpatialJoinQuery(reqEntity, "Intersects", - geomProps); + JoinQueryUtils.appendSpatialJoinQuery(reqEntity, "Intersects", geomProps); } @Test public void buildIntersectsQuery() { - Document reqEntity = WFSMessage.createRequestEntity( - "GetFeature-Minimal", "2.0.0"); + Document reqEntity = WFSMessage.createRequestEntity("GetFeature-Minimal", "2.0.0"); List geomProps = new ArrayList(); - geomProps.add(new FeatureProperty(new QName(NS1, "SimpleFeature"), - model.getElementDeclaration("lineProperty", NS1))); + geomProps.add( + new FeatureProperty(new QName(NS1, "SimpleFeature"), model.getElementDeclaration("lineProperty", NS1))); geomProps.add(new FeatureProperty(new QName(NS1, "ComplexFeature"), model.getElementDeclaration("multiGeomProperty", NS1))); - JoinQueryUtils.appendSpatialJoinQuery(reqEntity, "Intersects", - geomProps); - NodeList valueRefs = reqEntity.getElementsByTagNameNS(FES, - "ValueReference"); - assertEquals("Unexpected number of fes:ValueReference elements.", 2, - valueRefs.getLength()); + JoinQueryUtils.appendSpatialJoinQuery(reqEntity, "Intersects", geomProps); + NodeList valueRefs = reqEntity.getElementsByTagNameNS(FES, "ValueReference"); + assertEquals("Unexpected number of fes:ValueReference elements.", 2, valueRefs.getLength()); Node valueRef = valueRefs.item(0); String xpath = valueRef.getTextContent(); String propName = xpath.substring(xpath.lastIndexOf('/') + 1); - assertEquals("Unexpected local name", "lineProperty", - propName.split(":")[1]); - assertEquals("Unexpected namespace name", NS1, - valueRef.lookupNamespaceURI(propName.split(":")[0])); + assertEquals("Unexpected local name", "lineProperty", propName.split(":")[1]); + assertEquals("Unexpected namespace name", NS1, valueRef.lookupNamespaceURI(propName.split(":")[0])); } + } diff --git a/src/test/java/org/opengis/cite/iso19142/simple/VerifyDescribeFeatureTypeTests.java b/src/test/java/org/opengis/cite/iso19142/simple/VerifyDescribeFeatureTypeTests.java index 5ebf6b82..ba8b2884 100644 --- a/src/test/java/org/opengis/cite/iso19142/simple/VerifyDescribeFeatureTypeTests.java +++ b/src/test/java/org/opengis/cite/iso19142/simple/VerifyDescribeFeatureTypeTests.java @@ -26,50 +26,53 @@ */ public class VerifyDescribeFeatureTypeTests { - private static ITestContext testContext; - private static ISuite suite; - private static DocumentBuilder docBuilder; + private static ITestContext testContext; - public VerifyDescribeFeatureTypeTests() { - } + private static ISuite suite; - @BeforeClass - public static void setUpClass() throws Exception { - testContext = mock(ITestContext.class); - suite = mock(ISuite.class); - when(testContext.getSuite()).thenReturn(suite); - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(true); - docBuilder = dbf.newDocumentBuilder(); - } + private static DocumentBuilder docBuilder; - @Before - public void setUp() { - } + public VerifyDescribeFeatureTypeTests() { + } - @After - public void tearDown() { - } + @BeforeClass + public static void setUpClass() throws Exception { + testContext = mock(ITestContext.class); + suite = mock(ISuite.class); + when(testContext.getSuite()).thenReturn(suite); + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + docBuilder = dbf.newDocumentBuilder(); + } - @Test - public void addOneFeatureType() throws SAXException, IOException { - Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/DescribeFeatureType-Empty.xml")); - DescribeFeatureTypeTests iut = new DescribeFeatureTypeTests(); - iut.addFeatureType(doc, new QName("http://example.org", "Unknown1.Type")); - Element typeName = (Element) doc.getElementsByTagNameNS(Namespaces.WFS, WFS2.TYPENAME_ELEM).item(0); - String[] qName = typeName.getTextContent().split(":"); - assertEquals("Qualified name should be 'prefix:localPart'.", 2, qName.length); - assertEquals("Unexpected type name.", "Unknown1.Type", qName[1]); - } + @Before + public void setUp() { + } + + @After + public void tearDown() { + } + + @Test + public void addOneFeatureType() throws SAXException, IOException { + Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/DescribeFeatureType-Empty.xml")); + DescribeFeatureTypeTests iut = new DescribeFeatureTypeTests(); + iut.addFeatureType(doc, new QName("http://example.org", "Unknown1.Type")); + Element typeName = (Element) doc.getElementsByTagNameNS(Namespaces.WFS, WFS2.TYPENAME_ELEM).item(0); + String[] qName = typeName.getTextContent().split(":"); + assertEquals("Qualified name should be 'prefix:localPart'.", 2, qName.length); + assertEquals("Unexpected type name.", "Unknown1.Type", qName[1]); + } + + @Test + public void decodeAppSchema() throws SAXException, IOException { + Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/DescribeFeatureTypeResponse-Base64.xml")); + DescribeFeatureTypeTests iut = new DescribeFeatureTypeTests(); + iut.buildRequestEntity(); + Document schema = iut.decodeSchema(doc); + assertNotNull("Failed to parse decoded schema.", schema); + assertEquals("Unexpected namespace name", "http://www.w3.org/2001/XMLSchema", + schema.getDocumentElement().getNamespaceURI()); + } - @Test - public void decodeAppSchema() throws SAXException, IOException { - Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/DescribeFeatureTypeResponse-Base64.xml")); - DescribeFeatureTypeTests iut = new DescribeFeatureTypeTests(); - iut.buildRequestEntity(); - Document schema = iut.decodeSchema(doc); - assertNotNull("Failed to parse decoded schema.", schema); - assertEquals("Unexpected namespace name", "http://www.w3.org/2001/XMLSchema", - schema.getDocumentElement().getNamespaceURI()); - } } diff --git a/src/test/java/org/opengis/cite/iso19142/simple/VerifyServiceMetadataTests.java b/src/test/java/org/opengis/cite/iso19142/simple/VerifyServiceMetadataTests.java index 6b3e0225..80d32617 100644 --- a/src/test/java/org/opengis/cite/iso19142/simple/VerifyServiceMetadataTests.java +++ b/src/test/java/org/opengis/cite/iso19142/simple/VerifyServiceMetadataTests.java @@ -27,9 +27,13 @@ public class VerifyServiceMetadataTests { @Rule public ExpectedException thrown = ExpectedException.none(); + private static ITestContext testContext; + private static ISuite suite; + private static DocumentBuilder docBuilder; + private static Schema wfsSchema; public VerifyServiceMetadataTests() { @@ -41,20 +45,16 @@ public static void setUpClass() throws Exception { suite = mock(ISuite.class); when(testContext.getSuite()).thenReturn(suite); wfsSchema = ValidationUtils.createWFSSchema(); - when(suite.getAttribute(SuiteAttribute.WFS_SCHEMA.getName())) - .thenReturn(wfsSchema); + when(suite.getAttribute(SuiteAttribute.WFS_SCHEMA.getName())).thenReturn(wfsSchema); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); docBuilder = dbf.newDocumentBuilder(); } @Test - public void validateEmptyCapabilitiesDoc_valid() throws SAXException, - IOException { - Document doc = docBuilder.parse(this.getClass().getResourceAsStream( - "/empty-wfs-capabilities.xml")); - when(suite.getAttribute(SuiteAttribute.TEST_SUBJECT.getName())) - .thenReturn(doc); + public void validateEmptyCapabilitiesDoc_valid() throws SAXException, IOException { + Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/empty-wfs-capabilities.xml")); + when(suite.getAttribute(SuiteAttribute.TEST_SUBJECT.getName())).thenReturn(doc); ServiceMetadataTests iut = new ServiceMetadataTests(); iut.initBaseFixture(testContext); iut.obtainWFSSchema(testContext); @@ -65,11 +65,10 @@ public void validateEmptyCapabilitiesDoc_valid() throws SAXException, public void atomFeed() throws SAXException, IOException { thrown.expect(IllegalArgumentException.class); thrown.expectMessage("Not a WFS service description"); - Document doc = docBuilder.parse(this.getClass().getResourceAsStream( - "/atom-feed.xml")); - when(suite.getAttribute(SuiteAttribute.TEST_SUBJECT.getName())) - .thenReturn(doc); + Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/atom-feed.xml")); + when(suite.getAttribute(SuiteAttribute.TEST_SUBJECT.getName())).thenReturn(doc); ServiceMetadataTests iut = new ServiceMetadataTests(); iut.initBaseFixture(testContext); } + } diff --git a/src/test/java/org/opengis/cite/iso19142/simple/VerifyStoredQueryTests.java b/src/test/java/org/opengis/cite/iso19142/simple/VerifyStoredQueryTests.java index db53abd8..97c04ee5 100644 --- a/src/test/java/org/opengis/cite/iso19142/simple/VerifyStoredQueryTests.java +++ b/src/test/java/org/opengis/cite/iso19142/simple/VerifyStoredQueryTests.java @@ -15,28 +15,29 @@ */ public class VerifyStoredQueryTests { - private static ITestContext testContext; - private static ISuite suite; - - public VerifyStoredQueryTests() { - } - - @BeforeClass - public static void setUpClass() throws Exception { - testContext = mock(ITestContext.class); - suite = mock(ISuite.class); - when(testContext.getSuite()).thenReturn(suite); - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(true); - dbf.newDocumentBuilder(); - } - - @Before - public void setUp() { - } - - @After - public void tearDown() { - } + private static ITestContext testContext; + + private static ISuite suite; + + public VerifyStoredQueryTests() { + } + + @BeforeClass + public static void setUpClass() throws Exception { + testContext = mock(ITestContext.class); + suite = mock(ISuite.class); + when(testContext.getSuite()).thenReturn(suite); + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + dbf.newDocumentBuilder(); + } + + @Before + public void setUp() { + } + + @After + public void tearDown() { + } } diff --git a/src/test/java/org/opengis/cite/iso19142/transaction/VerifyInsertTests.java b/src/test/java/org/opengis/cite/iso19142/transaction/VerifyInsertTests.java index 76976adf..ea4fd18a 100644 --- a/src/test/java/org/opengis/cite/iso19142/transaction/VerifyInsertTests.java +++ b/src/test/java/org/opengis/cite/iso19142/transaction/VerifyInsertTests.java @@ -22,27 +22,30 @@ public class VerifyInsertTests { - private static ITestContext testContext; - private static ISuite suite; - private static DocumentBuilder docBuilder; - - @BeforeClass - public static void initFixture() throws ParserConfigurationException { - testContext = mock(ITestContext.class); - suite = mock(ISuite.class); - when(testContext.getSuite()).thenReturn(suite); - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(true); - docBuilder = dbf.newDocumentBuilder(); - } - - @Test - public void replaceFirstName() throws SAXException, IOException { - Document doc = docBuilder.parse(getClass().getResourceAsStream("/Alpha-1.xml")); - String name = InsertTests.addRandomName(doc.getDocumentElement()); - assertFalse("No name value", name.isEmpty()); - NodeList nameList = doc.getElementsByTagNameNS(Namespaces.GML, "name"); - assertEquals("Unexpected number of names", 2, nameList.getLength()); - assertEquals("Unexpected name", name, nameList.item(0).getTextContent()); - } + private static ITestContext testContext; + + private static ISuite suite; + + private static DocumentBuilder docBuilder; + + @BeforeClass + public static void initFixture() throws ParserConfigurationException { + testContext = mock(ITestContext.class); + suite = mock(ISuite.class); + when(testContext.getSuite()).thenReturn(suite); + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + docBuilder = dbf.newDocumentBuilder(); + } + + @Test + public void replaceFirstName() throws SAXException, IOException { + Document doc = docBuilder.parse(getClass().getResourceAsStream("/Alpha-1.xml")); + String name = InsertTests.addRandomName(doc.getDocumentElement()); + assertFalse("No name value", name.isEmpty()); + NodeList nameList = doc.getElementsByTagNameNS(Namespaces.GML, "name"); + assertEquals("Unexpected number of names", 2, nameList.getLength()); + assertEquals("Unexpected name", name, nameList.item(0).getTextContent()); + } + } diff --git a/src/test/java/org/opengis/cite/iso19142/transaction/VerifyReplaceTests.java b/src/test/java/org/opengis/cite/iso19142/transaction/VerifyReplaceTests.java index 3b8b8f2a..a682ec34 100644 --- a/src/test/java/org/opengis/cite/iso19142/transaction/VerifyReplaceTests.java +++ b/src/test/java/org/opengis/cite/iso19142/transaction/VerifyReplaceTests.java @@ -26,38 +26,41 @@ public class VerifyReplaceTests { - private static final String NS1 = "http://example.org/ns1"; - private static DocumentBuilder docBuilder; - private static XSModel model; - - @BeforeClass - public static void initFixture() throws ParserConfigurationException, SAXException { - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(true); - docBuilder = dbf.newDocumentBuilder(); - URL entityCatalog = VerifyUpdate.class.getResource("/schema-catalog.xml"); - XmlSchemaCompiler xsdCompiler = new XmlSchemaCompiler(entityCatalog); - InputStream xis = VerifyUpdate.class.getResourceAsStream("/xsd/simple.xsd"); - Schema schema = xsdCompiler.compileXmlSchema(new StreamSource(xis)); - model = XSModelBuilder.buildXMLSchemaModel(schema, NS1); - } - - @Test - public void createReplacementFeature() throws SAXException, IOException { - Document featureDoc = docBuilder.parse(getClass().getResourceAsStream("/Feature-River.xml")); - Element original = featureDoc.getDocumentElement(); - ReplaceTests iut = new ReplaceTests(); - iut.setModel(model); - Element repl = iut.createReplacementFeature(original); - NodeList idNodes = repl.getElementsByTagNameNS(GML32.NS_NAME, "identifier"); - assertEquals("Unexpected number of gml:identifier elements.", 1, idNodes.getLength()); - Element id = (Element) idNodes.item(0); - assertEquals("Unexpected value for @codeSpace.", "http://cite.opengeospatial.org/", - id.getAttribute("codeSpace")); - Object userData = repl.getUserData(ReplaceTests.REPL_PROPS); - assertNotNull(userData); - @SuppressWarnings("unchecked") - Map props = (Map) userData; - assertEquals("Unexpected number of Map entries in user data", 2, props.size()); - } + private static final String NS1 = "http://example.org/ns1"; + + private static DocumentBuilder docBuilder; + + private static XSModel model; + + @BeforeClass + public static void initFixture() throws ParserConfigurationException, SAXException { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + docBuilder = dbf.newDocumentBuilder(); + URL entityCatalog = VerifyUpdate.class.getResource("/schema-catalog.xml"); + XmlSchemaCompiler xsdCompiler = new XmlSchemaCompiler(entityCatalog); + InputStream xis = VerifyUpdate.class.getResourceAsStream("/xsd/simple.xsd"); + Schema schema = xsdCompiler.compileXmlSchema(new StreamSource(xis)); + model = XSModelBuilder.buildXMLSchemaModel(schema, NS1); + } + + @Test + public void createReplacementFeature() throws SAXException, IOException { + Document featureDoc = docBuilder.parse(getClass().getResourceAsStream("/Feature-River.xml")); + Element original = featureDoc.getDocumentElement(); + ReplaceTests iut = new ReplaceTests(); + iut.setModel(model); + Element repl = iut.createReplacementFeature(original); + NodeList idNodes = repl.getElementsByTagNameNS(GML32.NS_NAME, "identifier"); + assertEquals("Unexpected number of gml:identifier elements.", 1, idNodes.getLength()); + Element id = (Element) idNodes.item(0); + assertEquals("Unexpected value for @codeSpace.", "http://cite.opengeospatial.org/", + id.getAttribute("codeSpace")); + Object userData = repl.getUserData(ReplaceTests.REPL_PROPS); + assertNotNull(userData); + @SuppressWarnings("unchecked") + Map props = (Map) userData; + assertEquals("Unexpected number of Map entries in user data", 2, props.size()); + } + } diff --git a/src/test/java/org/opengis/cite/iso19142/transaction/VerifyUpdate.java b/src/test/java/org/opengis/cite/iso19142/transaction/VerifyUpdate.java index 4d36c6e9..726bf9f5 100644 --- a/src/test/java/org/opengis/cite/iso19142/transaction/VerifyUpdate.java +++ b/src/test/java/org/opengis/cite/iso19142/transaction/VerifyUpdate.java @@ -36,71 +36,76 @@ */ public class VerifyUpdate { - private static final String NS1 = "http://example.org/ns1"; - private static ITestContext testContext; - private static ISuite suite; - private static XSModel model; - private static DataSampler dataSampler; - - @BeforeClass - public static void mockTestContext() throws ParserConfigurationException { - testContext = mock(ITestContext.class); - suite = mock(ISuite.class); - when(testContext.getSuite()).thenReturn(suite); - dataSampler = mock(DataSampler.class); - when(suite.getAttribute(SuiteAttribute.SAMPLER.getName())).thenReturn(dataSampler); - } - - @BeforeClass - public static void buildSchemaModel() throws SAXException { - URL entityCatalog = VerifyUpdate.class.getResource("/schema-catalog.xml"); - XmlSchemaCompiler xsdCompiler = new XmlSchemaCompiler(entityCatalog); - InputStream xis = VerifyUpdate.class.getResourceAsStream("/xsd/simple.xsd"); - Schema schema = xsdCompiler.compileXmlSchema(new StreamSource(xis)); - model = XSModelBuilder.buildXMLSchemaModel(schema, NS1); - } - - @Test - public void createNewStringEnumValue() throws SAXException, IOException { - QName featureType = new QName(NS1, "ComplexFeature"); - List simpleProps = AppSchemaUtils.getSimpleFeatureProperties(model, featureType); - List propValues = new ArrayList(); - propValues.add("CA-AB"); - Update iut = new Update(); - String newVal = iut.newPropertyValue(simpleProps.get(simpleProps.size() - 1), propValues); - assertEquals("CA-BC", newVal); - } - - @Test - public void createNewDoubleValue() throws SAXException, IOException { - QName featureType = new QName(NS1, "ComplexFeature"); - List simpleProps = AppSchemaUtils.getSimpleFeatureProperties(model, featureType); - List propValues = new ArrayList(); - propValues.add("48.2"); - Update iut = new Update(); - String newVal = iut.newPropertyValue(simpleProps.get(0), propValues); - assertEquals(24.1, Double.parseDouble(newVal), 0.00001); - } - - @Test - public void createNewDecimalValue() throws SAXException, IOException { - QName featureType = new QName(NS1, "SimpleFeature"); - List simpleProps = AppSchemaUtils.getSimpleFeatureProperties(model, featureType); - List propValues = new ArrayList(); - propValues.add("49.25"); - Update iut = new Update(); - String newVal = iut.newPropertyValue(simpleProps.get(5), propValues); - assertEquals(4.925, Double.parseDouble(newVal), 0.0001); - } - - @Test - public void createNewDateValue() throws SAXException, IOException { - QName featureType = new QName(NS1, "SimpleFeature"); - List simpleProps = AppSchemaUtils.getSimpleFeatureProperties(model, featureType); - List propValues = new ArrayList(); - propValues.add("2010-01-01"); - Update iut = new Update(); - String newVal = iut.newPropertyValue(simpleProps.get(4), propValues); - assertEquals(LocalDate.now(ZoneId.of("Z")), LocalDate.parse(newVal)); - } + private static final String NS1 = "http://example.org/ns1"; + + private static ITestContext testContext; + + private static ISuite suite; + + private static XSModel model; + + private static DataSampler dataSampler; + + @BeforeClass + public static void mockTestContext() throws ParserConfigurationException { + testContext = mock(ITestContext.class); + suite = mock(ISuite.class); + when(testContext.getSuite()).thenReturn(suite); + dataSampler = mock(DataSampler.class); + when(suite.getAttribute(SuiteAttribute.SAMPLER.getName())).thenReturn(dataSampler); + } + + @BeforeClass + public static void buildSchemaModel() throws SAXException { + URL entityCatalog = VerifyUpdate.class.getResource("/schema-catalog.xml"); + XmlSchemaCompiler xsdCompiler = new XmlSchemaCompiler(entityCatalog); + InputStream xis = VerifyUpdate.class.getResourceAsStream("/xsd/simple.xsd"); + Schema schema = xsdCompiler.compileXmlSchema(new StreamSource(xis)); + model = XSModelBuilder.buildXMLSchemaModel(schema, NS1); + } + + @Test + public void createNewStringEnumValue() throws SAXException, IOException { + QName featureType = new QName(NS1, "ComplexFeature"); + List simpleProps = AppSchemaUtils.getSimpleFeatureProperties(model, featureType); + List propValues = new ArrayList(); + propValues.add("CA-AB"); + Update iut = new Update(); + String newVal = iut.newPropertyValue(simpleProps.get(simpleProps.size() - 1), propValues); + assertEquals("CA-BC", newVal); + } + + @Test + public void createNewDoubleValue() throws SAXException, IOException { + QName featureType = new QName(NS1, "ComplexFeature"); + List simpleProps = AppSchemaUtils.getSimpleFeatureProperties(model, featureType); + List propValues = new ArrayList(); + propValues.add("48.2"); + Update iut = new Update(); + String newVal = iut.newPropertyValue(simpleProps.get(0), propValues); + assertEquals(24.1, Double.parseDouble(newVal), 0.00001); + } + + @Test + public void createNewDecimalValue() throws SAXException, IOException { + QName featureType = new QName(NS1, "SimpleFeature"); + List simpleProps = AppSchemaUtils.getSimpleFeatureProperties(model, featureType); + List propValues = new ArrayList(); + propValues.add("49.25"); + Update iut = new Update(); + String newVal = iut.newPropertyValue(simpleProps.get(5), propValues); + assertEquals(4.925, Double.parseDouble(newVal), 0.0001); + } + + @Test + public void createNewDateValue() throws SAXException, IOException { + QName featureType = new QName(NS1, "SimpleFeature"); + List simpleProps = AppSchemaUtils.getSimpleFeatureProperties(model, featureType); + List propValues = new ArrayList(); + propValues.add("2010-01-01"); + Update iut = new Update(); + String newVal = iut.newPropertyValue(simpleProps.get(4), propValues); + assertEquals(LocalDate.now(ZoneId.of("Z")), LocalDate.parse(newVal)); + } + } diff --git a/src/test/java/org/opengis/cite/iso19142/util/VerifyAppSchemaUtils.java b/src/test/java/org/opengis/cite/iso19142/util/VerifyAppSchemaUtils.java index 91e8e893..961b65c2 100644 --- a/src/test/java/org/opengis/cite/iso19142/util/VerifyAppSchemaUtils.java +++ b/src/test/java/org/opengis/cite/iso19142/util/VerifyAppSchemaUtils.java @@ -27,167 +27,169 @@ */ public class VerifyAppSchemaUtils { - private static final String EX_NS = "http://example.org/ns1"; - private static final QName DOUBLE_DT = new QName(XMLConstants.W3C_XML_SCHEMA_NS_URI, "double"); - private static final QName TM_PERIOD_PROP = new QName(GML32.NS_NAME, "TimePeriodPropertyType"); - private static XSModel model; - - public VerifyAppSchemaUtils() { - } - - @BeforeClass - public static void setUpClass() throws Exception { - URL entityCatalog = VerifyAppSchemaUtils.class.getResource("/schema-catalog.xml"); - XmlSchemaCompiler xsdCompiler = new XmlSchemaCompiler(entityCatalog); - InputStream xis = VerifyAppSchemaUtils.class.getResourceAsStream("/xsd/simple.xsd"); - Schema schema = xsdCompiler.compileXmlSchema(new StreamSource(xis)); - model = XSModelBuilder.buildXMLSchemaModel(schema, EX_NS); - } - - @Test - public void findSimpleProperties_doubleType() { - QName featureTypeName = new QName(EX_NS, "SimpleFeature"); - XSTypeDefinition xsdDoubleType = model.getTypeDefinition("double", XMLConstants.W3C_XML_SCHEMA_NS_URI); - List props = AppSchemaUtils.getFeaturePropertiesByType(model, featureTypeName, - xsdDoubleType); - assertEquals("Unexpected number of xsd:double properties.", 1, props.size()); - assertEquals("Unexpected property name.", "measurand", props.get(0).getName()); - } - - @Test - public void findSimpleProperties_decimalType() { - QName featureTypeName = new QName(EX_NS, "SimpleFeature"); - XSTypeDefinition xsdDoubleType = model.getTypeDefinition("decimal", XMLConstants.W3C_XML_SCHEMA_NS_URI); - List props = AppSchemaUtils.getFeaturePropertiesByType(model, featureTypeName, - xsdDoubleType); - assertEquals("Unexpected number of xsd:decimal properties.", 14, props.size()); - } - - @Test - public void findGeometryProperties_curveType() { - QName featureTypeName = new QName(EX_NS, "SimpleFeature"); - XSTypeDefinition gmlCurveType = model.getTypeDefinition("AbstractCurveType", Namespaces.GML); - List props = AppSchemaUtils.getFeaturePropertiesByType(model, featureTypeName, - gmlCurveType); - assertEquals("Unexpected number of curve properties.", 1, props.size()); - assertEquals("Unexpected property name.", "lineProperty", props.get(0).getName()); - } - - @Test - public void findAllGeometryProperties() { - QName featureTypeName = new QName(EX_NS, "SimpleFeature"); - XSTypeDefinition gmlGeomType = model.getTypeDefinition("AbstractGeometryType", Namespaces.GML); - List props = AppSchemaUtils.getFeaturePropertiesByType(model, featureTypeName, - gmlGeomType); - assertEquals("Unexpected number of geometry properties.", 3, props.size()); - } - - @Test - public void findBooleanProperty_none() { - QName featureTypeName = new QName(EX_NS, "SimpleFeature"); - XSTypeDefinition xsdBooleanType = model.getTypeDefinition("boolean", XMLConstants.W3C_XML_SCHEMA_NS_URI); - List props = AppSchemaUtils.getFeaturePropertiesByType(model, featureTypeName, - xsdBooleanType); - assertTrue("Expected empty property list.", props.isEmpty()); - } - - @Test - public void findAllProperties_doubleType() { - QName featureTypeName = new QName(EX_NS, "ComplexFeature"); - XSTypeDefinition doubleType = model.getTypeDefinition("double", XMLConstants.W3C_XML_SCHEMA_NS_URI); - List props = AppSchemaUtils.getFeaturePropertiesByType(model, featureTypeName, - doubleType); - assertEquals("Unexpected number of properties derived from xsd:double.", 1, props.size()); - assertEquals("Unexpected property name.", "observation", props.get(0).getName()); - } - - @Test - public void findFeatureAssociations() { - QName featureTypeName = new QName(EX_NS, "ComplexFeature"); - XSTypeDefinition featureType = model.getTypeDefinition("AbstractFeatureType", Namespaces.GML); - List props = AppSchemaUtils.getFeaturePropertiesByType(model, featureTypeName, - featureType); - assertEquals("Unexpected number of feature associations.", 1, props.size()); - assertEquals("Unexpected property name.", "simpleFeature", props.get(0).getName()); - } - - @Test - public void findNillableFeatureProperties() { - QName featureTypeName = new QName(EX_NS, "ComplexFeature"); - List props = AppSchemaUtils.getNillableProperties(model, featureTypeName); - assertEquals("Unexpected number of nillable properties.", 2, props.size()); - } - - @Test - public void findAllProperties_ComplexFeature() { - QName featureTypeName = new QName(EX_NS, "ComplexFeature"); - List props = AppSchemaUtils.getAllFeatureProperties(model, featureTypeName); - assertEquals("Found unexpected number of feature properties.", 15, props.size()); - XSElementDeclaration elem = props.get(0); - assertEquals("Unexpected name for first property in list.", "metaDataProperty", elem.getName()); - } - - @Test - public void findRequiredProperties_ComplexFeature() { - QName featureTypeName = new QName(EX_NS, "ComplexFeature"); - List props = AppSchemaUtils.getRequiredProperties(model, featureTypeName); - assertEquals("Found unexpected number of required feature properties.", 5, props.size()); - XSElementDeclaration elem = props.get(props.size() - 1); - assertEquals("Unexpected name for last property in list.", "validTime", elem.getName()); - } - - @Test - public void getBuiltInTypeOfObservationProperty() { - QName featureTypeName = new QName(EX_NS, "ComplexFeature"); - List simpleProps = AppSchemaUtils.getSimpleFeatureProperties(model, featureTypeName); - XSElementDeclaration obsProp = simpleProps.get(0); - QName qName = AppSchemaUtils.getBuiltInDatatype(obsProp); - assertEquals("Unexpected datatype.", DOUBLE_DT, qName); - } - - @Test - public void getTypeOfValidTimeProperty() { - QName featureTypeName = new QName(EX_NS, "ComplexFeature"); - List tmProps = AppSchemaUtils.getTemporalFeatureProperties(model, featureTypeName); - assertFalse("No temporal properties found for " + featureTypeName, tmProps.isEmpty()); - XSElementDeclaration timeProp = tmProps.get(0); - XSTypeDefinition typeDefn = timeProp.getTypeDefinition(); - QName qName = new QName(typeDefn.getNamespace(), typeDefn.getName()); - assertEquals("Unexpected property type.", TM_PERIOD_PROP, qName); - } - - @Test - public void getValueOfMultiGeomProperty() { - XSElementDeclaration multiGeomProperty = model.getElementDeclaration("multiGeomProperty", EX_NS); - XSElementDeclaration value = AppSchemaUtils.getComplexPropertyValue(multiGeomProperty); - assertEquals("Unexpected namespace", GML32.NS_NAME, value.getNamespace()); - assertEquals("Unexpected name.", "AbstractGeometricAggregate", value.getName()); - } - - @Test - public void findSimpleProperties_integerType() { - String[] integerProperties = {"byteProperty", "intProperty2", "longProperty", - "negativeIntegerProperty", "nonNegativeIntegerProperty", - "nonPositiveIntegerProperty", "positiveIntegerProperty", "shortProperty", - "unsignedLongProperty", "unsignedIntProperty", "unsingedShortProperty", - "unsignedByteProperty"}; - for (String property : integerProperties) { - XSElementDeclaration declaration = getPropertyByLocalName(property); - assertNotNull("Could not find property " + property, declaration); - QName qName = AppSchemaUtils.getBuiltInDatatype(declaration); - assertEquals("integer", qName.getLocalPart()); - } - } - - private XSElementDeclaration getPropertyByLocalName(String name) { - QName featureTypeName = new QName(EX_NS, "SimpleFeature"); - List simpleProps = AppSchemaUtils.getSimpleFeatureProperties(model, featureTypeName); - for (XSElementDeclaration simpleProp : simpleProps) { - if (name.equals(simpleProp.getName())) { - return simpleProp; - } - } - - return null; - } + private static final String EX_NS = "http://example.org/ns1"; + + private static final QName DOUBLE_DT = new QName(XMLConstants.W3C_XML_SCHEMA_NS_URI, "double"); + + private static final QName TM_PERIOD_PROP = new QName(GML32.NS_NAME, "TimePeriodPropertyType"); + + private static XSModel model; + + public VerifyAppSchemaUtils() { + } + + @BeforeClass + public static void setUpClass() throws Exception { + URL entityCatalog = VerifyAppSchemaUtils.class.getResource("/schema-catalog.xml"); + XmlSchemaCompiler xsdCompiler = new XmlSchemaCompiler(entityCatalog); + InputStream xis = VerifyAppSchemaUtils.class.getResourceAsStream("/xsd/simple.xsd"); + Schema schema = xsdCompiler.compileXmlSchema(new StreamSource(xis)); + model = XSModelBuilder.buildXMLSchemaModel(schema, EX_NS); + } + + @Test + public void findSimpleProperties_doubleType() { + QName featureTypeName = new QName(EX_NS, "SimpleFeature"); + XSTypeDefinition xsdDoubleType = model.getTypeDefinition("double", XMLConstants.W3C_XML_SCHEMA_NS_URI); + List props = AppSchemaUtils.getFeaturePropertiesByType(model, featureTypeName, + xsdDoubleType); + assertEquals("Unexpected number of xsd:double properties.", 1, props.size()); + assertEquals("Unexpected property name.", "measurand", props.get(0).getName()); + } + + @Test + public void findSimpleProperties_decimalType() { + QName featureTypeName = new QName(EX_NS, "SimpleFeature"); + XSTypeDefinition xsdDoubleType = model.getTypeDefinition("decimal", XMLConstants.W3C_XML_SCHEMA_NS_URI); + List props = AppSchemaUtils.getFeaturePropertiesByType(model, featureTypeName, + xsdDoubleType); + assertEquals("Unexpected number of xsd:decimal properties.", 14, props.size()); + } + + @Test + public void findGeometryProperties_curveType() { + QName featureTypeName = new QName(EX_NS, "SimpleFeature"); + XSTypeDefinition gmlCurveType = model.getTypeDefinition("AbstractCurveType", Namespaces.GML); + List props = AppSchemaUtils.getFeaturePropertiesByType(model, featureTypeName, + gmlCurveType); + assertEquals("Unexpected number of curve properties.", 1, props.size()); + assertEquals("Unexpected property name.", "lineProperty", props.get(0).getName()); + } + + @Test + public void findAllGeometryProperties() { + QName featureTypeName = new QName(EX_NS, "SimpleFeature"); + XSTypeDefinition gmlGeomType = model.getTypeDefinition("AbstractGeometryType", Namespaces.GML); + List props = AppSchemaUtils.getFeaturePropertiesByType(model, featureTypeName, + gmlGeomType); + assertEquals("Unexpected number of geometry properties.", 3, props.size()); + } + + @Test + public void findBooleanProperty_none() { + QName featureTypeName = new QName(EX_NS, "SimpleFeature"); + XSTypeDefinition xsdBooleanType = model.getTypeDefinition("boolean", XMLConstants.W3C_XML_SCHEMA_NS_URI); + List props = AppSchemaUtils.getFeaturePropertiesByType(model, featureTypeName, + xsdBooleanType); + assertTrue("Expected empty property list.", props.isEmpty()); + } + + @Test + public void findAllProperties_doubleType() { + QName featureTypeName = new QName(EX_NS, "ComplexFeature"); + XSTypeDefinition doubleType = model.getTypeDefinition("double", XMLConstants.W3C_XML_SCHEMA_NS_URI); + List props = AppSchemaUtils.getFeaturePropertiesByType(model, featureTypeName, + doubleType); + assertEquals("Unexpected number of properties derived from xsd:double.", 1, props.size()); + assertEquals("Unexpected property name.", "observation", props.get(0).getName()); + } + + @Test + public void findFeatureAssociations() { + QName featureTypeName = new QName(EX_NS, "ComplexFeature"); + XSTypeDefinition featureType = model.getTypeDefinition("AbstractFeatureType", Namespaces.GML); + List props = AppSchemaUtils.getFeaturePropertiesByType(model, featureTypeName, + featureType); + assertEquals("Unexpected number of feature associations.", 1, props.size()); + assertEquals("Unexpected property name.", "simpleFeature", props.get(0).getName()); + } + + @Test + public void findNillableFeatureProperties() { + QName featureTypeName = new QName(EX_NS, "ComplexFeature"); + List props = AppSchemaUtils.getNillableProperties(model, featureTypeName); + assertEquals("Unexpected number of nillable properties.", 2, props.size()); + } + + @Test + public void findAllProperties_ComplexFeature() { + QName featureTypeName = new QName(EX_NS, "ComplexFeature"); + List props = AppSchemaUtils.getAllFeatureProperties(model, featureTypeName); + assertEquals("Found unexpected number of feature properties.", 15, props.size()); + XSElementDeclaration elem = props.get(0); + assertEquals("Unexpected name for first property in list.", "metaDataProperty", elem.getName()); + } + + @Test + public void findRequiredProperties_ComplexFeature() { + QName featureTypeName = new QName(EX_NS, "ComplexFeature"); + List props = AppSchemaUtils.getRequiredProperties(model, featureTypeName); + assertEquals("Found unexpected number of required feature properties.", 5, props.size()); + XSElementDeclaration elem = props.get(props.size() - 1); + assertEquals("Unexpected name for last property in list.", "validTime", elem.getName()); + } + + @Test + public void getBuiltInTypeOfObservationProperty() { + QName featureTypeName = new QName(EX_NS, "ComplexFeature"); + List simpleProps = AppSchemaUtils.getSimpleFeatureProperties(model, featureTypeName); + XSElementDeclaration obsProp = simpleProps.get(0); + QName qName = AppSchemaUtils.getBuiltInDatatype(obsProp); + assertEquals("Unexpected datatype.", DOUBLE_DT, qName); + } + + @Test + public void getTypeOfValidTimeProperty() { + QName featureTypeName = new QName(EX_NS, "ComplexFeature"); + List tmProps = AppSchemaUtils.getTemporalFeatureProperties(model, featureTypeName); + assertFalse("No temporal properties found for " + featureTypeName, tmProps.isEmpty()); + XSElementDeclaration timeProp = tmProps.get(0); + XSTypeDefinition typeDefn = timeProp.getTypeDefinition(); + QName qName = new QName(typeDefn.getNamespace(), typeDefn.getName()); + assertEquals("Unexpected property type.", TM_PERIOD_PROP, qName); + } + + @Test + public void getValueOfMultiGeomProperty() { + XSElementDeclaration multiGeomProperty = model.getElementDeclaration("multiGeomProperty", EX_NS); + XSElementDeclaration value = AppSchemaUtils.getComplexPropertyValue(multiGeomProperty); + assertEquals("Unexpected namespace", GML32.NS_NAME, value.getNamespace()); + assertEquals("Unexpected name.", "AbstractGeometricAggregate", value.getName()); + } + + @Test + public void findSimpleProperties_integerType() { + String[] integerProperties = { "byteProperty", "intProperty2", "longProperty", "negativeIntegerProperty", + "nonNegativeIntegerProperty", "nonPositiveIntegerProperty", "positiveIntegerProperty", "shortProperty", + "unsignedLongProperty", "unsignedIntProperty", "unsingedShortProperty", "unsignedByteProperty" }; + for (String property : integerProperties) { + XSElementDeclaration declaration = getPropertyByLocalName(property); + assertNotNull("Could not find property " + property, declaration); + QName qName = AppSchemaUtils.getBuiltInDatatype(declaration); + assertEquals("integer", qName.getLocalPart()); + } + } + + private XSElementDeclaration getPropertyByLocalName(String name) { + QName featureTypeName = new QName(EX_NS, "SimpleFeature"); + List simpleProps = AppSchemaUtils.getSimpleFeatureProperties(model, featureTypeName); + for (XSElementDeclaration simpleProp : simpleProps) { + if (name.equals(simpleProp.getName())) { + return simpleProp; + } + } + + return null; + } + } diff --git a/src/test/java/org/opengis/cite/iso19142/util/VerifyDataSampler.java b/src/test/java/org/opengis/cite/iso19142/util/VerifyDataSampler.java index 4b17504f..749c7eef 100644 --- a/src/test/java/org/opengis/cite/iso19142/util/VerifyDataSampler.java +++ b/src/test/java/org/opengis/cite/iso19142/util/VerifyDataSampler.java @@ -37,127 +37,129 @@ public class VerifyDataSampler { - private static final String TNS = "http://example.org/ns1"; - private static XSModel model; - private static DocumentBuilder docBuilder; - - @BeforeClass - public static void createBuilder() throws Exception { - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(true); - docBuilder = dbf.newDocumentBuilder(); - } - - @BeforeClass - public static void createModel() throws Exception { - URL entityCatalog = VerifyAppSchemaUtils.class.getResource("/schema-catalog.xml"); - XmlSchemaCompiler xsdCompiler = new XmlSchemaCompiler(entityCatalog); - InputStream xis = VerifyAppSchemaUtils.class.getResourceAsStream("/xsd/simple.xsd"); - Schema schema = xsdCompiler.compileXmlSchema(new StreamSource(xis)); - model = XSModelBuilder.buildXMLSchemaModel(schema, TNS); - } - - @Test - public void initDataSampler() throws SAXException, IOException { - Document capabilitiesDoc = docBuilder.parse(getClass().getResourceAsStream("/wfs/capabilities-acme.xml")); - DataSampler iut = new DataSampler(capabilitiesDoc); - Set featureTypes = iut.getFeatureTypeInfo().keySet(); - assertEquals("Unexpected number of feature types.", featureTypes.size(), 2); - QName simpleFeature = new QName(TNS, "SimpleFeature"); - assertTrue("Expected type: " + simpleFeature, featureTypes.contains(simpleFeature)); - } - - @Test - public void getSpatialExtentOfSimpleFeature() throws URISyntaxException, SAXException, IOException { - Document capabilitiesDoc = docBuilder.parse(getClass().getResourceAsStream("/wfs/capabilities-acme.xml")); - QName simpleFeature = new QName(TNS, "SimpleFeature"); - DataSampler iut = new DataSampler(capabilitiesDoc); - setSampleData(iut, simpleFeature, "/wfs/FeatureCollection-SimpleFeature.xml" ); - Envelope bbox = iut.getSpatialExtent(model, simpleFeature); - assertNotNull("Envelope is null.", bbox); - DirectPosition upperCorner = bbox.getUpperCorner(); - assertEquals("Unexpected ordinate[0] for upper corner.", 51.92, upperCorner.getOrdinate(0), 0.005); - assertEquals("Unexpected ordinate[1] for upper corner.", 8.541, upperCorner.getOrdinate(1), 0.005); - } - - @Test - public void getTemporalExtentOfSimpleFeatures() throws URISyntaxException, SAXException, IOException { - Document capabilitiesDoc = docBuilder.parse(getClass().getResourceAsStream("/wfs/capabilities-acme.xml")); - QName simpleFeature = new QName(TNS, "SimpleFeature"); - List tmProps = AppSchemaUtils.getTemporalFeatureProperties(model, simpleFeature); - XSElementDeclaration tmProp = tmProps.stream().filter(decl -> decl.getName().equals("dateTimeProperty")) - .findAny().orElse(null); - DataSampler iut = new DataSampler( capabilitiesDoc ); - setSampleData(iut, simpleFeature, "/wfs/FeatureCollection-SimpleFeature.xml" ); - Period period = iut.getTemporalExtentOfProperty(model, simpleFeature, tmProp); - assertNotNull("Period is null.", period); - assertTrue("Expected duration P8M", period.length().toString().startsWith("P8M")); - } - - @Test - public void getTemporalExtentOfComplexFeatures() throws URISyntaxException, SAXException, IOException { - Document capabilitiesDoc = docBuilder.parse(getClass().getResourceAsStream("/wfs/capabilities-acme.xml")); - QName featureType = new QName(TNS, "ComplexFeature"); - List tmProps = AppSchemaUtils.getTemporalFeatureProperties(model, featureType); - XSElementDeclaration tmProp = tmProps.stream().filter(decl -> decl.getName().equals("validTime")).findAny() - .orElse(null); - DataSampler iut = new DataSampler(capabilitiesDoc); - setSampleData(iut, featureType, "/wfs/FeatureCollection-ComplexFeature.xml" ); - Period period = iut.getTemporalExtentOfProperty(model, featureType, tmProp); - assertNotNull("Period is null.", period); - assertTrue("Expected duration P11M", period.length().toString().startsWith("P11M")); - } - - @Test - public void testSelectRandomFeatureType() - throws Exception { - Document capabilitiesDoc = docBuilder.parse( getClass().getResourceAsStream( "/wfs/capabilities-acme.xml" ) ); - QName featureType = new QName( TNS, "ComplexFeature" ); - DataSampler iut = new DataSampler( capabilitiesDoc ); - - QName selectedFeatureTypeBeforeInstantiaed = iut.selectFeatureType(); - assertThat( selectedFeatureTypeBeforeInstantiaed, nullValue()); - - FeatureTypeInfo typeInfo = iut.getFeatureTypeInfo().get(featureType); - typeInfo.setInstantiated(true); - - QName selectedFeatureType = iut.selectFeatureType(); - assertThat( selectedFeatureType, is( featureType ) ); - } - - @Test - public void testGetFeatureId() - throws Exception { - Document capabilitiesDoc = docBuilder.parse( getClass().getResourceAsStream( "/wfs/capabilities-acme.xml" ) ); - QName simpleFt = new QName( TNS, "SimpleFeature" ); - QName complexFt = new QName( TNS, "ComplexFeature" ); - DataSampler iut = new DataSampler( capabilitiesDoc ); - setSampleData( iut, simpleFt, "/wfs/FeatureCollection-SimpleFeature.xml" ); - setSampleData( iut, complexFt, "/wfs/FeatureCollection-ComplexFeature.xml" ); - String id = iut.getFeatureId(); - assertThat( id, anyOf( is( "CF01" ), is( "SF-01" ) ) ); - } - - @Test - public void testGetFeatureId_matchFalse() - throws Exception { - Document capabilitiesDoc = docBuilder.parse( getClass().getResourceAsStream( "/wfs/capabilities-acme.xml" ) ); - QName simpleFt = new QName( TNS, "SimpleFeature" ); - QName complexFt = new QName( TNS, "ComplexFeature" ); - DataSampler iut = new DataSampler( capabilitiesDoc ); - setSampleData( iut, simpleFt, "/wfs/FeatureCollection-SimpleFeature.xml" ); - setSampleData( iut, complexFt, "/wfs/FeatureCollection-ComplexFeature.xml" ); - String id = iut.getFeatureIdNotOfType( simpleFt ); - assertThat( id, is( "CF01" ) ); - } - - private void setSampleData( DataSampler iut, QName featureType, String resource ) - throws URISyntaxException { - URL dataURL = getClass().getResource( resource ); - File dataFile = new File( dataURL.toURI() ); - FeatureTypeInfo typeInfo = iut.getFeatureTypeInfo().get( featureType ); - typeInfo.setInstantiated( true ); - typeInfo.setSampleData( dataFile ); - } + private static final String TNS = "http://example.org/ns1"; + + private static XSModel model; + + private static DocumentBuilder docBuilder; + + @BeforeClass + public static void createBuilder() throws Exception { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + docBuilder = dbf.newDocumentBuilder(); + } + + @BeforeClass + public static void createModel() throws Exception { + URL entityCatalog = VerifyAppSchemaUtils.class.getResource("/schema-catalog.xml"); + XmlSchemaCompiler xsdCompiler = new XmlSchemaCompiler(entityCatalog); + InputStream xis = VerifyAppSchemaUtils.class.getResourceAsStream("/xsd/simple.xsd"); + Schema schema = xsdCompiler.compileXmlSchema(new StreamSource(xis)); + model = XSModelBuilder.buildXMLSchemaModel(schema, TNS); + } + + @Test + public void initDataSampler() throws SAXException, IOException { + Document capabilitiesDoc = docBuilder.parse(getClass().getResourceAsStream("/wfs/capabilities-acme.xml")); + DataSampler iut = new DataSampler(capabilitiesDoc); + Set featureTypes = iut.getFeatureTypeInfo().keySet(); + assertEquals("Unexpected number of feature types.", featureTypes.size(), 2); + QName simpleFeature = new QName(TNS, "SimpleFeature"); + assertTrue("Expected type: " + simpleFeature, featureTypes.contains(simpleFeature)); + } + + @Test + public void getSpatialExtentOfSimpleFeature() throws URISyntaxException, SAXException, IOException { + Document capabilitiesDoc = docBuilder.parse(getClass().getResourceAsStream("/wfs/capabilities-acme.xml")); + QName simpleFeature = new QName(TNS, "SimpleFeature"); + DataSampler iut = new DataSampler(capabilitiesDoc); + setSampleData(iut, simpleFeature, "/wfs/FeatureCollection-SimpleFeature.xml"); + Envelope bbox = iut.getSpatialExtent(model, simpleFeature); + assertNotNull("Envelope is null.", bbox); + DirectPosition upperCorner = bbox.getUpperCorner(); + assertEquals("Unexpected ordinate[0] for upper corner.", 51.92, upperCorner.getOrdinate(0), 0.005); + assertEquals("Unexpected ordinate[1] for upper corner.", 8.541, upperCorner.getOrdinate(1), 0.005); + } + + @Test + public void getTemporalExtentOfSimpleFeatures() throws URISyntaxException, SAXException, IOException { + Document capabilitiesDoc = docBuilder.parse(getClass().getResourceAsStream("/wfs/capabilities-acme.xml")); + QName simpleFeature = new QName(TNS, "SimpleFeature"); + List tmProps = AppSchemaUtils.getTemporalFeatureProperties(model, simpleFeature); + XSElementDeclaration tmProp = tmProps.stream() + .filter(decl -> decl.getName().equals("dateTimeProperty")) + .findAny() + .orElse(null); + DataSampler iut = new DataSampler(capabilitiesDoc); + setSampleData(iut, simpleFeature, "/wfs/FeatureCollection-SimpleFeature.xml"); + Period period = iut.getTemporalExtentOfProperty(model, simpleFeature, tmProp); + assertNotNull("Period is null.", period); + assertTrue("Expected duration P8M", period.length().toString().startsWith("P8M")); + } + + @Test + public void getTemporalExtentOfComplexFeatures() throws URISyntaxException, SAXException, IOException { + Document capabilitiesDoc = docBuilder.parse(getClass().getResourceAsStream("/wfs/capabilities-acme.xml")); + QName featureType = new QName(TNS, "ComplexFeature"); + List tmProps = AppSchemaUtils.getTemporalFeatureProperties(model, featureType); + XSElementDeclaration tmProp = tmProps.stream() + .filter(decl -> decl.getName().equals("validTime")) + .findAny() + .orElse(null); + DataSampler iut = new DataSampler(capabilitiesDoc); + setSampleData(iut, featureType, "/wfs/FeatureCollection-ComplexFeature.xml"); + Period period = iut.getTemporalExtentOfProperty(model, featureType, tmProp); + assertNotNull("Period is null.", period); + assertTrue("Expected duration P11M", period.length().toString().startsWith("P11M")); + } + + @Test + public void testSelectRandomFeatureType() throws Exception { + Document capabilitiesDoc = docBuilder.parse(getClass().getResourceAsStream("/wfs/capabilities-acme.xml")); + QName featureType = new QName(TNS, "ComplexFeature"); + DataSampler iut = new DataSampler(capabilitiesDoc); + + QName selectedFeatureTypeBeforeInstantiaed = iut.selectFeatureType(); + assertThat(selectedFeatureTypeBeforeInstantiaed, nullValue()); + + FeatureTypeInfo typeInfo = iut.getFeatureTypeInfo().get(featureType); + typeInfo.setInstantiated(true); + + QName selectedFeatureType = iut.selectFeatureType(); + assertThat(selectedFeatureType, is(featureType)); + } + + @Test + public void testGetFeatureId() throws Exception { + Document capabilitiesDoc = docBuilder.parse(getClass().getResourceAsStream("/wfs/capabilities-acme.xml")); + QName simpleFt = new QName(TNS, "SimpleFeature"); + QName complexFt = new QName(TNS, "ComplexFeature"); + DataSampler iut = new DataSampler(capabilitiesDoc); + setSampleData(iut, simpleFt, "/wfs/FeatureCollection-SimpleFeature.xml"); + setSampleData(iut, complexFt, "/wfs/FeatureCollection-ComplexFeature.xml"); + String id = iut.getFeatureId(); + assertThat(id, anyOf(is("CF01"), is("SF-01"))); + } + + @Test + public void testGetFeatureId_matchFalse() throws Exception { + Document capabilitiesDoc = docBuilder.parse(getClass().getResourceAsStream("/wfs/capabilities-acme.xml")); + QName simpleFt = new QName(TNS, "SimpleFeature"); + QName complexFt = new QName(TNS, "ComplexFeature"); + DataSampler iut = new DataSampler(capabilitiesDoc); + setSampleData(iut, simpleFt, "/wfs/FeatureCollection-SimpleFeature.xml"); + setSampleData(iut, complexFt, "/wfs/FeatureCollection-ComplexFeature.xml"); + String id = iut.getFeatureIdNotOfType(simpleFt); + assertThat(id, is("CF01")); + } + + private void setSampleData(DataSampler iut, QName featureType, String resource) throws URISyntaxException { + URL dataURL = getClass().getResource(resource); + File dataFile = new File(dataURL.toURI()); + FeatureTypeInfo typeInfo = iut.getFeatureTypeInfo().get(featureType); + typeInfo.setInstantiated(true); + typeInfo.setSampleData(dataFile); + } } diff --git a/src/test/java/org/opengis/cite/iso19142/util/VerifyFeatureProperty.java b/src/test/java/org/opengis/cite/iso19142/util/VerifyFeatureProperty.java index e879eb3e..8d171945 100644 --- a/src/test/java/org/opengis/cite/iso19142/util/VerifyFeatureProperty.java +++ b/src/test/java/org/opengis/cite/iso19142/util/VerifyFeatureProperty.java @@ -22,33 +22,35 @@ public class VerifyFeatureProperty { - private static final String EX_NS = "http://example.org/ns1"; - private static XSModel model; - - @BeforeClass - public static void setUpClass() throws Exception { - URL entityCatalog = VerifyAppSchemaUtils.class.getResource("/schema-catalog.xml"); - XmlSchemaCompiler xsdCompiler = new XmlSchemaCompiler(entityCatalog); - InputStream xis = VerifyAppSchemaUtils.class.getResourceAsStream("/xsd/simple.xsd"); - Schema schema = xsdCompiler.compileXmlSchema(new StreamSource(xis)); - model = XSModelBuilder.buildXMLSchemaModel(schema, EX_NS); - } - - @Test - public void curveProperty() { - QName featureTypeName = new QName(EX_NS, "SimpleFeature"); - XSTypeDefinition typeDef = model.getTypeDefinition("AbstractCurveType", Namespaces.GML); - List props = AppSchemaUtils.getFeaturePropertiesByType(model, featureTypeName, typeDef); - FeatureProperty prop = new FeatureProperty(featureTypeName, props.get(0)); - assertEquals(new QName(Namespaces.GML, "LineString"), prop.getValueType()); - } - - @Test - public void decimalProperty() { - QName featureTypeName = new QName(EX_NS, "SimpleFeature"); - XSTypeDefinition typeDef = model.getTypeDefinition("decimal", XMLConstants.W3C_XML_SCHEMA_NS_URI); - List props = AppSchemaUtils.getFeaturePropertiesByType(model, featureTypeName, typeDef); - FeatureProperty prop = new FeatureProperty(featureTypeName, props.get(0)); - assertEquals(new QName(Namespaces.XSD.toString(), "integer"), prop.getValueType()); - } + private static final String EX_NS = "http://example.org/ns1"; + + private static XSModel model; + + @BeforeClass + public static void setUpClass() throws Exception { + URL entityCatalog = VerifyAppSchemaUtils.class.getResource("/schema-catalog.xml"); + XmlSchemaCompiler xsdCompiler = new XmlSchemaCompiler(entityCatalog); + InputStream xis = VerifyAppSchemaUtils.class.getResourceAsStream("/xsd/simple.xsd"); + Schema schema = xsdCompiler.compileXmlSchema(new StreamSource(xis)); + model = XSModelBuilder.buildXMLSchemaModel(schema, EX_NS); + } + + @Test + public void curveProperty() { + QName featureTypeName = new QName(EX_NS, "SimpleFeature"); + XSTypeDefinition typeDef = model.getTypeDefinition("AbstractCurveType", Namespaces.GML); + List props = AppSchemaUtils.getFeaturePropertiesByType(model, featureTypeName, typeDef); + FeatureProperty prop = new FeatureProperty(featureTypeName, props.get(0)); + assertEquals(new QName(Namespaces.GML, "LineString"), prop.getValueType()); + } + + @Test + public void decimalProperty() { + QName featureTypeName = new QName(EX_NS, "SimpleFeature"); + XSTypeDefinition typeDef = model.getTypeDefinition("decimal", XMLConstants.W3C_XML_SCHEMA_NS_URI); + List props = AppSchemaUtils.getFeaturePropertiesByType(model, featureTypeName, typeDef); + FeatureProperty prop = new FeatureProperty(featureTypeName, props.get(0)); + assertEquals(new QName(Namespaces.XSD.toString(), "integer"), prop.getValueType()); + } + } diff --git a/src/test/java/org/opengis/cite/iso19142/util/VerifyRandomizer.java b/src/test/java/org/opengis/cite/iso19142/util/VerifyRandomizer.java index d656eb37..353bb7f1 100644 --- a/src/test/java/org/opengis/cite/iso19142/util/VerifyRandomizer.java +++ b/src/test/java/org/opengis/cite/iso19142/util/VerifyRandomizer.java @@ -6,22 +6,23 @@ public class VerifyRandomizer { - @Test - public void generate2Words() { - String words = Randomizer.generateWords(2); - assertEquals("Unexpected number of words", 2, words.split("\\s").length); - } + @Test + public void generate2Words() { + String words = Randomizer.generateWords(2); + assertEquals("Unexpected number of words", 2, words.split("\\s").length); + } - @Test - public void generate10Words() { - String words = Randomizer.generateWords(10); - System.out.println(words); - assertEquals("Unexpected number of words", 10, words.split("\\s").length); - } + @Test + public void generate10Words() { + String words = Randomizer.generateWords(10); + System.out.println(words); + assertEquals("Unexpected number of words", 10, words.split("\\s").length); + } + + @Test + public void generate11Words() { + String words = Randomizer.generateWords(11); + assertEquals("Unexpected number of words", 1, words.split("\\s").length); + } - @Test - public void generate11Words() { - String words = Randomizer.generateWords(11); - assertEquals("Unexpected number of words", 1, words.split("\\s").length); - } } diff --git a/src/test/java/org/opengis/cite/iso19142/util/VerifyServiceMetadataUtils.java b/src/test/java/org/opengis/cite/iso19142/util/VerifyServiceMetadataUtils.java index 1c65fe80..0cb65824 100644 --- a/src/test/java/org/opengis/cite/iso19142/util/VerifyServiceMetadataUtils.java +++ b/src/test/java/org/opengis/cite/iso19142/util/VerifyServiceMetadataUtils.java @@ -30,131 +30,129 @@ */ public class VerifyServiceMetadataUtils { - private static DocumentBuilder docBuilder; - private static final String TNS = "http://example.org/tns"; - - public VerifyServiceMetadataUtils() { - } - - @BeforeClass - public static void setUpClass() throws Exception { - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(true); - docBuilder = dbf.newDocumentBuilder(); - } - - @Test - public void findDescribeFeatureTypeUsingGET() throws SAXException, IOException { - File xmlFile = new File("src/test/resources/capabilities-simple.xml"); - URI endpoint = ServiceMetadataUtils.getOperationEndpoint(docBuilder.parse(xmlFile), WFS2.DESCRIBE_FEATURE_TYPE, - ProtocolBinding.GET); - assertEquals("Unexpected endpoint for DescribeFeatureType(GET)", "http://localhost/wfs2", endpoint.toString()); - } - - @Test - public void findDescribeFeatureTypeUsingPOST_notFound() throws SAXException, IOException { - File xmlFile = new File("src/test/resources/capabilities-simple.xml"); - URI endpoint = ServiceMetadataUtils.getOperationEndpoint(docBuilder.parse(xmlFile), WFS2.DESCRIBE_FEATURE_TYPE, - ProtocolBinding.POST); - assertEquals("Expected empty URI reference.", URI.create(""), endpoint); - } - - @Test - public void findDescribeFeatureTypeUsingGETPreserveQuery() - throws SAXException, IOException { - File xmlFile = new File( "src/test/resources/capabilities-querystring.xml" ); - URI endpoint = ServiceMetadataUtils.getOperationEndpoint( docBuilder.parse( xmlFile ), - WFS2.DESCRIBE_FEATURE_TYPE, ProtocolBinding.GET ); - assertEquals( "Unexpected endpoint for DescribeFeatureType(GET)", "http://localhost/wfs2?param=abc&", - endpoint.toString() ); - } - - @Test - public void findDescribeFeatureTypeUsingGETRemoveTrailingQuestionMark() - throws SAXException, IOException { - File xmlFile = new File( "src/test/resources/capabilities-querystring.xml" ); - URI endpoint = ServiceMetadataUtils.getOperationEndpoint( docBuilder.parse( xmlFile ), - WFS2.LIST_STORED_QUERIES, ProtocolBinding.GET ); - assertEquals( "Unexpected endpoint for DescribeFeatureType(GET)", "http://localhost/wfs2", - endpoint.toString() ); - } - - @Test - public void getFeatureTypeList() throws SAXException, IOException { - File xmlFile = new File("src/test/resources/capabilities-simple.xml"); - List typeNames = ServiceMetadataUtils.getFeatureTypes(docBuilder.parse(xmlFile)); - assertEquals("Unexpected size of type name list.", 1, typeNames.size()); - QName typeName = typeNames.get(0); - assertEquals("Feature type has unexpected [namespace name].", "http://example.org/ns1", - typeName.getNamespaceURI()); - assertEquals("Feature type has unexpected [local name].", "Alpha", typeName.getLocalPart()); - } - - @Test - public void acquireFeatureTypeInfo() throws SAXException, IOException { - File xmlFile = new File("src/test/resources/capabilities-simple.xml"); - Map typeInfo = ServiceMetadataUtils.extractFeatureTypeInfo(docBuilder.parse(xmlFile)); - assertEquals("Unexpected size of type info collection.", 1, typeInfo.size()); - QName qName = new QName("http://example.org/ns1", "Alpha"); - assertEquals("Unexpected default CRS.", "urn:ogc:def:crs:EPSG::4326", typeInfo.get(qName).getDefaultCRS()); - Document gmlEnv = Extents.envelopeAsGML(typeInfo.get(qName).getSpatialExtent()); - assertEquals("Unexpected [local name] for extent.", "Envelope", gmlEnv.getDocumentElement().getLocalName()); - } - - @Test - public void getRequestEndpoints_getCapabilities() throws SAXException, IOException { - File xmlFile = new File("src/test/resources/capabilities-simple.xml"); - Map endpoints = ServiceMetadataUtils.getRequestEndpoints(docBuilder.parse(xmlFile), - "GetCapabilities"); - assertEquals("Unexpected number of endpoints.", 2, endpoints.size()); - assertEquals("Unexpected GET endpoint.", "http://localhost/wfs2/capabilities", endpoints.get("GET").toString()); - } - - @Test - public void getOperationBindings_getFeature() throws SAXException, IOException { - File xmlFile = new File("src/test/resources/capabilities-simple.xml"); - Set bindings = ServiceMetadataUtils.getOperationBindings(docBuilder.parse(xmlFile), - WFS2.GET_FEATURE); - assertEquals("Unexpected number of GetFeature bindings (request encodings).", 2, bindings.size()); - } - - @Test - public void getConformanceClaims() throws SAXException, IOException { - File xmlFile = new File("src/test/resources/capabilities-simple.xml"); - Set claims = ServiceMetadataUtils.getConformanceClaims(docBuilder.parse(xmlFile)); - assertEquals("Unexpected number of conformance claims", 3, claims.size()); - assertTrue("Expected 'Simple WFS' conformance claim", claims.contains(ConformanceClass.SIMPLE_WFS)); - } - - @Test - public void buildQName_unprefixedWithoutDefaultBinding() { - Document doc = docBuilder.newDocument(); - doc.appendChild(doc.createElementNS(TNS, "tns:alpha")); - doc.getDocumentElement().setTextContent("localPart"); - QName qName = ServiceMetadataUtils.buildQName(doc.getDocumentElement()); - assertEquals("Unexpected local name", "localPart", qName.getLocalPart()); - assertEquals("Unexpected namespace name", XMLConstants.NULL_NS_URI, qName.getNamespaceURI()); - } - - @Test - public void buildQName_prefixed() { - Document doc = docBuilder.newDocument(); - doc.appendChild(doc.createElementNS(TNS, "tns:alpha")); - doc.getDocumentElement().setTextContent("tns:localPart"); - QName qName = ServiceMetadataUtils.buildQName(doc.getDocumentElement()); - assertEquals("Unexpected local name", "localPart", qName.getLocalPart()); - assertEquals("Unexpected namespace name", TNS, qName.getNamespaceURI()); - } - - @Test - public void simpleSpatialCapabilities() throws SAXException, IOException { - Document wfsDescr = docBuilder.parse(getClass().getResourceAsStream("/capabilities-simple.xml")); - Map> capabilities = ServiceMetadataUtils.getSpatialCapabilities(wfsDescr); - Set operators = capabilities.keySet(); - assertEquals("Unexpected number of spatial operators.", 3, operators.size()); - assertTrue("Expected INTERSECTS in set.", operators.contains(SpatialOperator.INTERSECTS)); - Set geomOperands = capabilities.get(SpatialOperator.INTERSECTS); - assertEquals("Unexpected number of geometry operands (INTERSECTS).", 6, geomOperands.size()); - } + private static DocumentBuilder docBuilder; + + private static final String TNS = "http://example.org/tns"; + + public VerifyServiceMetadataUtils() { + } + + @BeforeClass + public static void setUpClass() throws Exception { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + docBuilder = dbf.newDocumentBuilder(); + } + + @Test + public void findDescribeFeatureTypeUsingGET() throws SAXException, IOException { + File xmlFile = new File("src/test/resources/capabilities-simple.xml"); + URI endpoint = ServiceMetadataUtils.getOperationEndpoint(docBuilder.parse(xmlFile), WFS2.DESCRIBE_FEATURE_TYPE, + ProtocolBinding.GET); + assertEquals("Unexpected endpoint for DescribeFeatureType(GET)", "http://localhost/wfs2", endpoint.toString()); + } + + @Test + public void findDescribeFeatureTypeUsingPOST_notFound() throws SAXException, IOException { + File xmlFile = new File("src/test/resources/capabilities-simple.xml"); + URI endpoint = ServiceMetadataUtils.getOperationEndpoint(docBuilder.parse(xmlFile), WFS2.DESCRIBE_FEATURE_TYPE, + ProtocolBinding.POST); + assertEquals("Expected empty URI reference.", URI.create(""), endpoint); + } + + @Test + public void findDescribeFeatureTypeUsingGETPreserveQuery() throws SAXException, IOException { + File xmlFile = new File("src/test/resources/capabilities-querystring.xml"); + URI endpoint = ServiceMetadataUtils.getOperationEndpoint(docBuilder.parse(xmlFile), WFS2.DESCRIBE_FEATURE_TYPE, + ProtocolBinding.GET); + assertEquals("Unexpected endpoint for DescribeFeatureType(GET)", "http://localhost/wfs2?param=abc&", + endpoint.toString()); + } + + @Test + public void findDescribeFeatureTypeUsingGETRemoveTrailingQuestionMark() throws SAXException, IOException { + File xmlFile = new File("src/test/resources/capabilities-querystring.xml"); + URI endpoint = ServiceMetadataUtils.getOperationEndpoint(docBuilder.parse(xmlFile), WFS2.LIST_STORED_QUERIES, + ProtocolBinding.GET); + assertEquals("Unexpected endpoint for DescribeFeatureType(GET)", "http://localhost/wfs2", endpoint.toString()); + } + + @Test + public void getFeatureTypeList() throws SAXException, IOException { + File xmlFile = new File("src/test/resources/capabilities-simple.xml"); + List typeNames = ServiceMetadataUtils.getFeatureTypes(docBuilder.parse(xmlFile)); + assertEquals("Unexpected size of type name list.", 1, typeNames.size()); + QName typeName = typeNames.get(0); + assertEquals("Feature type has unexpected [namespace name].", "http://example.org/ns1", + typeName.getNamespaceURI()); + assertEquals("Feature type has unexpected [local name].", "Alpha", typeName.getLocalPart()); + } + + @Test + public void acquireFeatureTypeInfo() throws SAXException, IOException { + File xmlFile = new File("src/test/resources/capabilities-simple.xml"); + Map typeInfo = ServiceMetadataUtils.extractFeatureTypeInfo(docBuilder.parse(xmlFile)); + assertEquals("Unexpected size of type info collection.", 1, typeInfo.size()); + QName qName = new QName("http://example.org/ns1", "Alpha"); + assertEquals("Unexpected default CRS.", "urn:ogc:def:crs:EPSG::4326", typeInfo.get(qName).getDefaultCRS()); + Document gmlEnv = Extents.envelopeAsGML(typeInfo.get(qName).getSpatialExtent()); + assertEquals("Unexpected [local name] for extent.", "Envelope", gmlEnv.getDocumentElement().getLocalName()); + } + + @Test + public void getRequestEndpoints_getCapabilities() throws SAXException, IOException { + File xmlFile = new File("src/test/resources/capabilities-simple.xml"); + Map endpoints = ServiceMetadataUtils.getRequestEndpoints(docBuilder.parse(xmlFile), + "GetCapabilities"); + assertEquals("Unexpected number of endpoints.", 2, endpoints.size()); + assertEquals("Unexpected GET endpoint.", "http://localhost/wfs2/capabilities", endpoints.get("GET").toString()); + } + + @Test + public void getOperationBindings_getFeature() throws SAXException, IOException { + File xmlFile = new File("src/test/resources/capabilities-simple.xml"); + Set bindings = ServiceMetadataUtils.getOperationBindings(docBuilder.parse(xmlFile), + WFS2.GET_FEATURE); + assertEquals("Unexpected number of GetFeature bindings (request encodings).", 2, bindings.size()); + } + + @Test + public void getConformanceClaims() throws SAXException, IOException { + File xmlFile = new File("src/test/resources/capabilities-simple.xml"); + Set claims = ServiceMetadataUtils.getConformanceClaims(docBuilder.parse(xmlFile)); + assertEquals("Unexpected number of conformance claims", 3, claims.size()); + assertTrue("Expected 'Simple WFS' conformance claim", claims.contains(ConformanceClass.SIMPLE_WFS)); + } + + @Test + public void buildQName_unprefixedWithoutDefaultBinding() { + Document doc = docBuilder.newDocument(); + doc.appendChild(doc.createElementNS(TNS, "tns:alpha")); + doc.getDocumentElement().setTextContent("localPart"); + QName qName = ServiceMetadataUtils.buildQName(doc.getDocumentElement()); + assertEquals("Unexpected local name", "localPart", qName.getLocalPart()); + assertEquals("Unexpected namespace name", XMLConstants.NULL_NS_URI, qName.getNamespaceURI()); + } + + @Test + public void buildQName_prefixed() { + Document doc = docBuilder.newDocument(); + doc.appendChild(doc.createElementNS(TNS, "tns:alpha")); + doc.getDocumentElement().setTextContent("tns:localPart"); + QName qName = ServiceMetadataUtils.buildQName(doc.getDocumentElement()); + assertEquals("Unexpected local name", "localPart", qName.getLocalPart()); + assertEquals("Unexpected namespace name", TNS, qName.getNamespaceURI()); + } + + @Test + public void simpleSpatialCapabilities() throws SAXException, IOException { + Document wfsDescr = docBuilder.parse(getClass().getResourceAsStream("/capabilities-simple.xml")); + Map> capabilities = ServiceMetadataUtils.getSpatialCapabilities(wfsDescr); + Set operators = capabilities.keySet(); + assertEquals("Unexpected number of spatial operators.", 3, operators.size()); + assertTrue("Expected INTERSECTS in set.", operators.contains(SpatialOperator.INTERSECTS)); + Set geomOperands = capabilities.get(SpatialOperator.INTERSECTS); + assertEquals("Unexpected number of geometry operands (INTERSECTS).", 6, geomOperands.size()); + } } diff --git a/src/test/java/org/opengis/cite/iso19142/util/VerifyTimeUtils.java b/src/test/java/org/opengis/cite/iso19142/util/VerifyTimeUtils.java index c6a378b6..d4fdfca3 100644 --- a/src/test/java/org/opengis/cite/iso19142/util/VerifyTimeUtils.java +++ b/src/test/java/org/opengis/cite/iso19142/util/VerifyTimeUtils.java @@ -22,75 +22,75 @@ public class VerifyTimeUtils { - private static TemporalFactory tmFactory; - @Rule - public ExpectedException thrown = ExpectedException.none(); + private static TemporalFactory tmFactory; - @BeforeClass - public static void initFixture() throws Exception { - tmFactory = new DefaultTemporalFactory(); - } + @Rule + public ExpectedException thrown = ExpectedException.none(); - @Test - public void periodInUTCAsGML() { - ZonedDateTime t1 = ZonedDateTime.of(2016, 05, 3, 10, 15, 30, 0, ZoneId.of("Z")); - Instant startPeriod = tmFactory.createInstant(Date.from(t1.minusMonths(1).toInstant())); - Instant endPeriod = tmFactory.createInstant(Date.from(t1.plusMonths(1).toInstant())); - Period period = tmFactory.createPeriod(startPeriod, endPeriod); - Document doc = TimeUtils.periodAsGML(period, null); - Node endPosition = doc.getElementsByTagNameNS(Namespaces.GML, "endPosition").item(0); - assertTrue("Expected end date 2016-06-03", endPosition.getTextContent().startsWith("2016-06-03")); - } + @BeforeClass + public static void initFixture() throws Exception { + tmFactory = new DefaultTemporalFactory(); + } - @Test - public void periodWithOffsetAsGML() { - ZonedDateTime t1 = ZonedDateTime.of(2016, 05, 3, 10, 15, 30, 0, ZoneOffset.of("-07:00")); - Instant startPeriod = tmFactory.createInstant(Date.from(t1.minusMonths(1).toInstant())); - Instant endPeriod = tmFactory.createInstant(Date.from(t1.plusMonths(1).toInstant())); - Period period = tmFactory.createPeriod(startPeriod, endPeriod); - Document doc = TimeUtils.periodAsGML(period, ZoneOffset.of("-07:00")); - Node beginPosition = doc.getElementsByTagNameNS(Namespaces.GML, "beginPosition").item(0); - assertTrue("Expected begin time 17:15:30Z", beginPosition.getTextContent().endsWith("17:15:30Z")); - } + @Test + public void periodInUTCAsGML() { + ZonedDateTime t1 = ZonedDateTime.of(2016, 05, 3, 10, 15, 30, 0, ZoneId.of("Z")); + Instant startPeriod = tmFactory.createInstant(Date.from(t1.minusMonths(1).toInstant())); + Instant endPeriod = tmFactory.createInstant(Date.from(t1.plusMonths(1).toInstant())); + Period period = tmFactory.createPeriod(startPeriod, endPeriod); + Document doc = TimeUtils.periodAsGML(period, null); + Node endPosition = doc.getElementsByTagNameNS(Namespaces.GML, "endPosition").item(0); + assertTrue("Expected end date 2016-06-03", endPosition.getTextContent().startsWith("2016-06-03")); + } - @Test - public void instantInUTCAsGML() { - ZonedDateTime t1 = ZonedDateTime.of(2016, 05, 3, 10, 15, 30, 0, ZoneId.of("Z")); - Instant instant = tmFactory.createInstant(Date.from(t1.minusMonths(1).toInstant())); - Document doc = TimeUtils.instantAsGML(instant, ZoneOffset.UTC); - Node timePosition = doc.getElementsByTagNameNS(Namespaces.GML, "timePosition").item(0); - assertEquals("Unexpected date-time", timePosition.getTextContent().trim(), "2016-04-03T10:15:30Z"); - } + @Test + public void periodWithOffsetAsGML() { + ZonedDateTime t1 = ZonedDateTime.of(2016, 05, 3, 10, 15, 30, 0, ZoneOffset.of("-07:00")); + Instant startPeriod = tmFactory.createInstant(Date.from(t1.minusMonths(1).toInstant())); + Instant endPeriod = tmFactory.createInstant(Date.from(t1.plusMonths(1).toInstant())); + Period period = tmFactory.createPeriod(startPeriod, endPeriod); + Document doc = TimeUtils.periodAsGML(period, ZoneOffset.of("-07:00")); + Node beginPosition = doc.getElementsByTagNameNS(Namespaces.GML, "beginPosition").item(0); + assertTrue("Expected begin time 17:15:30Z", beginPosition.getTextContent().endsWith("17:15:30Z")); + } - @Test - public void instantWithOffsetAsGML() { - ZonedDateTime t1 = ZonedDateTime.of(2016, 05, 3, 10, 15, 30, 0, ZoneOffset.of("-07:00")); - Instant instant = tmFactory.createInstant(Date.from(t1.toInstant())); - Document doc = TimeUtils.instantAsGML(instant, ZoneOffset.of("-0700")); - Node timePosition = doc.getElementsByTagNameNS(Namespaces.GML, "timePosition").item(0); - assertEquals("Unexpected date-time", timePosition.getTextContent().trim(), "2016-05-03T10:15:30-07:00"); - } + @Test + public void instantInUTCAsGML() { + ZonedDateTime t1 = ZonedDateTime.of(2016, 05, 3, 10, 15, 30, 0, ZoneId.of("Z")); + Instant instant = tmFactory.createInstant(Date.from(t1.minusMonths(1).toInstant())); + Document doc = TimeUtils.instantAsGML(instant, ZoneOffset.UTC); + Node timePosition = doc.getElementsByTagNameNS(Namespaces.GML, "timePosition").item(0); + assertEquals("Unexpected date-time", timePosition.getTextContent().trim(), "2016-04-03T10:15:30Z"); + } - @Test - public void instantDateTimeWithoutSecond() { - ZonedDateTime t1 = ZonedDateTime.of(2016, 05, 3, 0, 0, 0, 0, ZoneId.of("Z")); - Instant instant = tmFactory.createInstant(Date.from(t1.minusMonths(1).toInstant())); - Document doc = TimeUtils.instantAsGML(instant, ZoneOffset.UTC); - Node timePosition = doc.getElementsByTagNameNS(Namespaces.GML, "timePosition").item(0); - assertEquals("Unexpected date-time", timePosition.getTextContent().trim(), "2016-04-03T00:00:00Z"); - } + @Test + public void instantWithOffsetAsGML() { + ZonedDateTime t1 = ZonedDateTime.of(2016, 05, 3, 10, 15, 30, 0, ZoneOffset.of("-07:00")); + Instant instant = tmFactory.createInstant(Date.from(t1.toInstant())); + Document doc = TimeUtils.instantAsGML(instant, ZoneOffset.of("-0700")); + Node timePosition = doc.getElementsByTagNameNS(Namespaces.GML, "timePosition").item(0); + assertEquals("Unexpected date-time", timePosition.getTextContent().trim(), "2016-05-03T10:15:30-07:00"); + } + @Test + public void instantDateTimeWithoutSecond() { + ZonedDateTime t1 = ZonedDateTime.of(2016, 05, 3, 0, 0, 0, 0, ZoneId.of("Z")); + Instant instant = tmFactory.createInstant(Date.from(t1.minusMonths(1).toInstant())); + Document doc = TimeUtils.instantAsGML(instant, ZoneOffset.UTC); + Node timePosition = doc.getElementsByTagNameNS(Namespaces.GML, "timePosition").item(0); + assertEquals("Unexpected date-time", timePosition.getTextContent().trim(), "2016-04-03T00:00:00Z"); + } - @Test - public void intervalAsGML() { - ZonedDateTime t1 = ZonedDateTime.of(2016, 05, 3, 10, 20, 30, 0, ZoneId.of("Z")); - ZonedDateTime t2 = ZonedDateTime.of(2017, 05, 3, 0, 0, 0, 0, ZoneId.of("Z")); - Document doc = TimeUtils.intervalAsGML( t1, t2 ); - Node beginPosition = doc.getElementsByTagNameNS(Namespaces.GML, "beginPosition").item(0); - Node endPosition = doc.getElementsByTagNameNS(Namespaces.GML, "endPosition").item(0); + @Test + public void intervalAsGML() { + ZonedDateTime t1 = ZonedDateTime.of(2016, 05, 3, 10, 20, 30, 0, ZoneId.of("Z")); + ZonedDateTime t2 = ZonedDateTime.of(2017, 05, 3, 0, 0, 0, 0, ZoneId.of("Z")); + Document doc = TimeUtils.intervalAsGML(t1, t2); + Node beginPosition = doc.getElementsByTagNameNS(Namespaces.GML, "beginPosition").item(0); + Node endPosition = doc.getElementsByTagNameNS(Namespaces.GML, "endPosition").item(0); - assertEquals("Unexpected date-time", beginPosition.getTextContent().trim(), "2016-05-03T10:20:30Z"); - assertEquals("Unexpected date-time", endPosition.getTextContent().trim(), "2017-05-03T00:00:00Z"); - } + assertEquals("Unexpected date-time", beginPosition.getTextContent().trim(), "2016-05-03T10:20:30Z"); + assertEquals("Unexpected date-time", endPosition.getTextContent().trim(), "2017-05-03T00:00:00Z"); + } } diff --git a/src/test/java/org/opengis/cite/iso19142/util/VerifyURIUtils.java b/src/test/java/org/opengis/cite/iso19142/util/VerifyURIUtils.java index 6e9a6558..05c5d928 100644 --- a/src/test/java/org/opengis/cite/iso19142/util/VerifyURIUtils.java +++ b/src/test/java/org/opengis/cite/iso19142/util/VerifyURIUtils.java @@ -19,65 +19,60 @@ */ public class VerifyURIUtils { - public VerifyURIUtils() { - } + public VerifyURIUtils() { + } - @BeforeClass - public static void setUpClass() { - } + @BeforeClass + public static void setUpClass() { + } - @Ignore - @Test - // comment out @Ignore to run test (requires network connection) - public void resolveHttpUriAsDocument() throws SAXException, IOException { - URI uriRef = URI.create("http://www.w3schools.com/xml/note.xml"); - Document doc = URIUtils.resolveURIAsDocument(uriRef); - Assert.assertNotNull(doc); - Assert.assertEquals("Document element has unexpected [local name].", - "note", doc.getDocumentElement().getLocalName()); - } + @Ignore + @Test + // comment out @Ignore to run test (requires network connection) + public void resolveHttpUriAsDocument() throws SAXException, IOException { + URI uriRef = URI.create("http://www.w3schools.com/xml/note.xml"); + Document doc = URIUtils.resolveURIAsDocument(uriRef); + Assert.assertNotNull(doc); + Assert.assertEquals("Document element has unexpected [local name].", "note", + doc.getDocumentElement().getLocalName()); + } - @Ignore - @Test - // comment out @Ignore to run test (requires network connection) - public void resolveHttpUriAsFile() throws SAXException, IOException { - URI uriRef = URI.create("http://www.w3schools.com/xml/note.xml"); - File file = URIUtils.resolveURIAsFile(uriRef); - Assert.assertNotNull(file); - Assert.assertTrue("File should not be empty", file.length() > 0); - } + @Ignore + @Test + // comment out @Ignore to run test (requires network connection) + public void resolveHttpUriAsFile() throws SAXException, IOException { + URI uriRef = URI.create("http://www.w3schools.com/xml/note.xml"); + File file = URIUtils.resolveURIAsFile(uriRef); + Assert.assertNotNull(file); + Assert.assertTrue("File should not be empty", file.length() > 0); + } - @Test - public void resolveClasspathResource() throws SAXException, IOException, - URISyntaxException { - URL url = this.getClass().getResource("/atom-feed.xml"); - Document doc = URIUtils.resolveURIAsDocument(url.toURI()); - Assert.assertNotNull(doc); - Assert.assertEquals("Document element has unexpected [local name].", - "feed", doc.getDocumentElement().getLocalName()); - } + @Test + public void resolveClasspathResource() throws SAXException, IOException, URISyntaxException { + URL url = this.getClass().getResource("/atom-feed.xml"); + Document doc = URIUtils.resolveURIAsDocument(url.toURI()); + Assert.assertNotNull(doc); + Assert.assertEquals("Document element has unexpected [local name].", "feed", + doc.getDocumentElement().getLocalName()); + } - @Test - public void resolveFileRefWithXInclude() throws SAXException, IOException, - URISyntaxException { - File file = new File("src/test/resources/Alpha-xinclude.xml"); - Document doc = URIUtils.resolveURIAsDocument(file.toURI()); - Assert.assertNotNull(doc); - Assert.assertEquals("Document element has unexpected [local name].", - "Alpha", doc.getDocumentElement().getLocalName()); - NodeList nodes = doc.getDocumentElement().getElementsByTagNameNS( - "http://www.example.net/gamma", "Gamma"); - Assert.assertEquals( - "Expected element {http://www.example.net/gamma}Gamma", 1, - nodes.getLength()); - } + @Test + public void resolveFileRefWithXInclude() throws SAXException, IOException, URISyntaxException { + File file = new File("src/test/resources/Alpha-xinclude.xml"); + Document doc = URIUtils.resolveURIAsDocument(file.toURI()); + Assert.assertNotNull(doc); + Assert.assertEquals("Document element has unexpected [local name].", "Alpha", + doc.getDocumentElement().getLocalName()); + NodeList nodes = doc.getDocumentElement().getElementsByTagNameNS("http://www.example.net/gamma", "Gamma"); + Assert.assertEquals("Expected element {http://www.example.net/gamma}Gamma", 1, nodes.getLength()); + } + + @Test(expected = IllegalArgumentException.class) + public void resolveMissingClasspathResource() throws SAXException, URISyntaxException, IOException { + URL url = this.getClass().getResource("/alpha.xml"); + URI uri = (null != url) ? url.toURI() : null; + Document doc = URIUtils.resolveURIAsDocument(uri); + Assert.assertNull(doc); + } - @Test(expected = IllegalArgumentException.class) - public void resolveMissingClasspathResource() throws SAXException, - URISyntaxException, IOException { - URL url = this.getClass().getResource("/alpha.xml"); - URI uri = (null != url) ? url.toURI() : null; - Document doc = URIUtils.resolveURIAsDocument(uri); - Assert.assertNull(doc); - } } diff --git a/src/test/java/org/opengis/cite/iso19142/util/VerifyValidationUtils.java b/src/test/java/org/opengis/cite/iso19142/util/VerifyValidationUtils.java index d13a80a5..e349e95a 100644 --- a/src/test/java/org/opengis/cite/iso19142/util/VerifyValidationUtils.java +++ b/src/test/java/org/opengis/cite/iso19142/util/VerifyValidationUtils.java @@ -22,41 +22,37 @@ */ public class VerifyValidationUtils { - public VerifyValidationUtils() { - } - - @Test - public void testBuildSchematronValidator() { - String schemaRef = "http://schemas.opengis.net/gml/3.2.1/SchematronConstraints.xml"; - String phase = ""; - SchematronValidator result = ValidationUtils.buildSchematronValidator( - schemaRef, phase); - assertNotNull(result); - } - - @Test - public void extractRelativeSchemaReference() throws FileNotFoundException, - XMLStreamException { - File xmlFile = new File("src/test/resources/Alpha-1.xml"); - URI xsdRef = ValidationUtils.extractSchemaReference(new StreamSource( - xmlFile), null); - assertTrue("Expected schema reference */xsd/alpha.xsd", xsdRef - .toString().endsWith("/xsd/alpha.xsd")); - } - - @Test - public void compileWFSSchema() { - Schema schema = ValidationUtils.createWFSSchema(); - assertNotNull(schema); - } - - @Test - public void createXMLSchemaResolver() { - LSResourceResolver resolver = ValidationUtils - .createSchemaResolver(Namespaces.XSD); - assertNotNull(resolver); - LSInput resource = resolver.resolveResource(Namespaces.XSD.toString(), - Namespaces.WFS, null, WFS2.SCHEMA_URI, null); - assertNotNull("Failed to resolve WFS2 schema resource.", resource); - } + public VerifyValidationUtils() { + } + + @Test + public void testBuildSchematronValidator() { + String schemaRef = "http://schemas.opengis.net/gml/3.2.1/SchematronConstraints.xml"; + String phase = ""; + SchematronValidator result = ValidationUtils.buildSchematronValidator(schemaRef, phase); + assertNotNull(result); + } + + @Test + public void extractRelativeSchemaReference() throws FileNotFoundException, XMLStreamException { + File xmlFile = new File("src/test/resources/Alpha-1.xml"); + URI xsdRef = ValidationUtils.extractSchemaReference(new StreamSource(xmlFile), null); + assertTrue("Expected schema reference */xsd/alpha.xsd", xsdRef.toString().endsWith("/xsd/alpha.xsd")); + } + + @Test + public void compileWFSSchema() { + Schema schema = ValidationUtils.createWFSSchema(); + assertNotNull(schema); + } + + @Test + public void createXMLSchemaResolver() { + LSResourceResolver resolver = ValidationUtils.createSchemaResolver(Namespaces.XSD); + assertNotNull(resolver); + LSInput resource = resolver.resolveResource(Namespaces.XSD.toString(), Namespaces.WFS, null, WFS2.SCHEMA_URI, + null); + assertNotNull("Failed to resolve WFS2 schema resource.", resource); + } + } diff --git a/src/test/java/org/opengis/cite/iso19142/util/VerifyWFSMessage.java b/src/test/java/org/opengis/cite/iso19142/util/VerifyWFSMessage.java index 5d306e3a..f55207bf 100644 --- a/src/test/java/org/opengis/cite/iso19142/util/VerifyWFSMessage.java +++ b/src/test/java/org/opengis/cite/iso19142/util/VerifyWFSMessage.java @@ -34,229 +34,231 @@ */ public class VerifyWFSMessage { - private static final String NS1 = "http://example.org/ns1"; - private static final String NS2 = "http://example.org/ns2"; - private static DocumentBuilder docBuilder; + private static final String NS1 = "http://example.org/ns1"; - public VerifyWFSMessage() { - } + private static final String NS2 = "http://example.org/ns2"; - @BeforeClass - public static void setUpClass() throws ParserConfigurationException { - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(true); - docBuilder = dbf.newDocumentBuilder(); - } + private static DocumentBuilder docBuilder; - @Test - public void transformGetCapabilitiesToKVP() { - InputStream inStream = getClass().getResourceAsStream("/GetCapabilities-AcceptSections.xml"); - String kvp = WFSMessage.transformEntityToKVP(new StreamSource(inStream)); - assertTrue("Expected result to contain 'acceptversions=2.0.0,1.1.0'", - kvp.contains("acceptversions=2.0.0,1.1.0")); - assertTrue("Expected result to contain 'sections=ServiceIdentification'", - kvp.contains("sections=ServiceIdentification")); - } + public VerifyWFSMessage() { + } - @Test - public void transformGetFeatureBBOXToKVP() { - InputStream inStream = getClass().getResourceAsStream("/GetFeature/GetFeature-BBOX.xml"); - String kvp = WFSMessage.transformEntityToKVP(new StreamSource(inStream)); - // expect to be percent-encoded - assertTrue("Expected result to contain '%3Cfes%3ABBOX%3E'", kvp.contains("%3Cfes%3ABBOX%3E")); - } + @BeforeClass + public static void setUpClass() throws ParserConfigurationException { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + docBuilder = dbf.newDocumentBuilder(); + } - @Test - public void transformStoredQueryToKVP() { - InputStream inStream = getClass().getResourceAsStream("/GetFeature/GetFeatureById.xml"); - String kvp = WFSMessage.transformEntityToKVP(new StreamSource(inStream)); - assertTrue("Expected result to contain 'storedquery_id=urn:ogc:def:query:OGC-WFS::GetFeatureById'", - kvp.contains("storedquery_id=urn:ogc:def:query:OGC-WFS::GetFeatureById")); - assertTrue("Expected result to contain 'id=id-1'", kvp.contains("id=id-1")); - assertTrue("Expected result to contain 'count=10'", kvp.contains("count=10")); - } + @Test + public void transformGetCapabilitiesToKVP() { + InputStream inStream = getClass().getResourceAsStream("/GetCapabilities-AcceptSections.xml"); + String kvp = WFSMessage.transformEntityToKVP(new StreamSource(inStream)); + assertTrue("Expected result to contain 'acceptversions=2.0.0,1.1.0'", + kvp.contains("acceptversions=2.0.0,1.1.0")); + assertTrue("Expected result to contain 'sections=ServiceIdentification'", + kvp.contains("sections=ServiceIdentification")); + } - @Test - public void transformGetFeatureQuery2TypesToKVP() { - InputStream inStream = getClass().getResourceAsStream("/GetFeature/GetFeature-Query2Types.xml"); - String kvp = WFSMessage.transformEntityToKVP(new StreamSource(inStream)); - assertTrue("Expected result to contain 'typenames=tns:PrimitiveGeoFeature,tns:AggregateGeoFeature'", - kvp.contains("typenames=tns:PrimitiveGeoFeature,tns:AggregateGeoFeature")); - assertTrue("Expected result to contain 'xmlns(tns,http://cite.opengeospatial.org/gmlsf)'", - kvp.contains("xmlns(tns,http://cite.opengeospatial.org/gmlsf)")); - } + @Test + public void transformGetFeatureBBOXToKVP() { + InputStream inStream = getClass().getResourceAsStream("/GetFeature/GetFeature-BBOX.xml"); + String kvp = WFSMessage.transformEntityToKVP(new StreamSource(inStream)); + // expect to be percent-encoded + assertTrue("Expected result to contain '%3Cfes%3ABBOX%3E'", kvp.contains("%3Cfes%3ABBOX%3E")); + } - @Test - public void transformDescribeFeatureTypeToKVP() { - InputStream inStream = getClass().getResourceAsStream("/DescribeFeatureType.xml"); - String kvp = WFSMessage.transformEntityToKVP(new StreamSource(inStream)); - assertTrue("Expected result to contain 'version=2.0.0'", kvp.contains("version=2.0.0")); - assertTrue("Expected result to contain 'typename=tns:ComplexGeoFeature,tns:AggregateGeoFeature'", - kvp.contains("typename=tns:ComplexGeoFeature,tns:AggregateGeoFeature")); - assertTrue("Expected result to contain 'xmlns(tns,http://cite.opengeospatial.org/gmlsf)'", - kvp.contains("xmlns(tns,http://cite.opengeospatial.org/gmlsf)")); - } + @Test + public void transformStoredQueryToKVP() { + InputStream inStream = getClass().getResourceAsStream("/GetFeature/GetFeatureById.xml"); + String kvp = WFSMessage.transformEntityToKVP(new StreamSource(inStream)); + assertTrue("Expected result to contain 'storedquery_id=urn:ogc:def:query:OGC-WFS::GetFeatureById'", + kvp.contains("storedquery_id=urn:ogc:def:query:OGC-WFS::GetFeatureById")); + assertTrue("Expected result to contain 'id=id-1'", kvp.contains("id=id-1")); + assertTrue("Expected result to contain 'count=10'", kvp.contains("count=10")); + } - @Test - public void transformDescribeStoredQueriesToKVP() { - InputStream inStream = getClass().getResourceAsStream("/DescribeStoredQueries.xml"); - String kvp = WFSMessage.transformEntityToKVP(new StreamSource(inStream)); - assertTrue("Expected result to contain 'request=DescribeStoredQueries'", - kvp.contains("request=DescribeStoredQueries")); - assertTrue( - "Expected result to contain 'storedquery_id=urn:ogc:def:query:OGC-WFS::GetFeatureById,urn:ogc:def:query:OGC-WFS::GetFeatureByType'", - kvp.contains( - "storedquery_id=urn:ogc:def:query:OGC-WFS::GetFeatureById,urn:ogc:def:query:OGC-WFS::GetFeatureByType")); - } + @Test + public void transformGetFeatureQuery2TypesToKVP() { + InputStream inStream = getClass().getResourceAsStream("/GetFeature/GetFeature-Query2Types.xml"); + String kvp = WFSMessage.transformEntityToKVP(new StreamSource(inStream)); + assertTrue("Expected result to contain 'typenames=tns:PrimitiveGeoFeature,tns:AggregateGeoFeature'", + kvp.contains("typenames=tns:PrimitiveGeoFeature,tns:AggregateGeoFeature")); + assertTrue("Expected result to contain 'xmlns(tns,http://cite.opengeospatial.org/gmlsf)'", + kvp.contains("xmlns(tns,http://cite.opengeospatial.org/gmlsf)")); + } - @Test - public void wrapGetFeatureRequestInSOAP11Envelope() { - InputStream inStream = getClass().getResourceAsStream("/GetFeature/GetFeature-Query2Types.xml"); - Document soapDoc = WFSMessage.wrapEntityInSOAPEnvelope(new StreamSource(inStream), WFS2.SOAP_VERSION); - assertEquals("Document element has unexpected namespace.", Namespaces.SOAP11, - soapDoc.getDocumentElement().getNamespaceURI()); - NodeList nodes = soapDoc.getElementsByTagNameNS(Namespaces.WFS, WFS2.GET_FEATURE); - assertEquals("Unexpected number of wfs:GetFeature nodes.", 1, nodes.getLength()); - } + @Test + public void transformDescribeFeatureTypeToKVP() { + InputStream inStream = getClass().getResourceAsStream("/DescribeFeatureType.xml"); + String kvp = WFSMessage.transformEntityToKVP(new StreamSource(inStream)); + assertTrue("Expected result to contain 'version=2.0.0'", kvp.contains("version=2.0.0")); + assertTrue("Expected result to contain 'typename=tns:ComplexGeoFeature,tns:AggregateGeoFeature'", + kvp.contains("typename=tns:ComplexGeoFeature,tns:AggregateGeoFeature")); + assertTrue("Expected result to contain 'xmlns(tns,http://cite.opengeospatial.org/gmlsf)'", + kvp.contains("xmlns(tns,http://cite.opengeospatial.org/gmlsf)")); + } - @Test - public void wrapRequestInSOAP12Envelope() { - InputStream inStream = getClass().getResourceAsStream("/GetFeature/GetFeature-Query2Types.xml"); - Document soapDoc = WFSMessage.wrapEntityInSOAPEnvelope(new StreamSource(inStream), null); - assertEquals("Document element has unexpected namespace.", Namespaces.SOAP_ENV, - soapDoc.getDocumentElement().getNamespaceURI()); - } + @Test + public void transformDescribeStoredQueriesToKVP() { + InputStream inStream = getClass().getResourceAsStream("/DescribeStoredQueries.xml"); + String kvp = WFSMessage.transformEntityToKVP(new StreamSource(inStream)); + assertTrue("Expected result to contain 'request=DescribeStoredQueries'", + kvp.contains("request=DescribeStoredQueries")); + assertTrue( + "Expected result to contain 'storedquery_id=urn:ogc:def:query:OGC-WFS::GetFeatureById,urn:ogc:def:query:OGC-WFS::GetFeatureByType'", + kvp.contains( + "storedquery_id=urn:ogc:def:query:OGC-WFS::GetFeatureById,urn:ogc:def:query:OGC-WFS::GetFeatureByType")); + } - @Test - public void setStoredQueryParameterAsString() throws SAXException, IOException { - Document doc = WFSMessage.createRequestEntity("GetFeature", null); - WFSMessage.appendStoredQuery(doc, "q1", Collections.singletonMap("p1", (Object) "v1")); - Element param = (Element) doc.getElementsByTagNameNS(Namespaces.WFS, WFS2.PARAM_ELEM).item(0); - assertEquals("Unexpected parameter name.", "p1", param.getAttribute("name")); - assertEquals("Unexpected parameter value.", "v1", param.getTextContent()); - } + @Test + public void wrapGetFeatureRequestInSOAP11Envelope() { + InputStream inStream = getClass().getResourceAsStream("/GetFeature/GetFeature-Query2Types.xml"); + Document soapDoc = WFSMessage.wrapEntityInSOAPEnvelope(new StreamSource(inStream), WFS2.SOAP_VERSION); + assertEquals("Document element has unexpected namespace.", Namespaces.SOAP11, + soapDoc.getDocumentElement().getNamespaceURI()); + NodeList nodes = soapDoc.getElementsByTagNameNS(Namespaces.WFS, WFS2.GET_FEATURE); + assertEquals("Unexpected number of wfs:GetFeature nodes.", 1, nodes.getLength()); + } - @Test - public void createResourceIdFilter() { - String identifier = "alpha"; - Element result = WFSMessage.newResourceIdFilter(identifier); - assertEquals("Unexpected element name.", "Filter", result.getLocalName()); - Element resourceId = (Element) result.getElementsByTagNameNS(Namespaces.FES, "ResourceId").item(0); - assertEquals("Unexpected rid value.", identifier, resourceId.getAttribute("rid")); - } + @Test + public void wrapRequestInSOAP12Envelope() { + InputStream inStream = getClass().getResourceAsStream("/GetFeature/GetFeature-Query2Types.xml"); + Document soapDoc = WFSMessage.wrapEntityInSOAPEnvelope(new StreamSource(inStream), null); + assertEquals("Document element has unexpected namespace.", Namespaces.SOAP_ENV, + soapDoc.getDocumentElement().getNamespaceURI()); + } - @Test - public void insertGMLIdentifier_featureWithoutGMLProps() throws SAXException, IOException { - Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/Gamma.xml")); - QName propName = new QName(Namespaces.GML, "identifier"); - Element identifier = XMLUtils.createElement(propName); - identifier.setAttribute("codeSpace", "http://cite.opengeospatial.org/"); - String uuid = UUID.randomUUID().toString(); - identifier.setTextContent(uuid); - WFSMessage.insertGMLProperty(doc.getDocumentElement(), identifier); - Element gmlIdentifier = (Element) doc.getElementsByTagNameNS(Namespaces.GML, "identifier").item(0); - assertEquals("Unexpected gml:identifier value.", uuid, gmlIdentifier.getTextContent()); - } + @Test + public void setStoredQueryParameterAsString() throws SAXException, IOException { + Document doc = WFSMessage.createRequestEntity("GetFeature", null); + WFSMessage.appendStoredQuery(doc, "q1", Collections.singletonMap("p1", (Object) "v1")); + Element param = (Element) doc.getElementsByTagNameNS(Namespaces.WFS, WFS2.PARAM_ELEM).item(0); + assertEquals("Unexpected parameter name.", "p1", param.getAttribute("name")); + assertEquals("Unexpected parameter value.", "v1", param.getTextContent()); + } - @Test - public void insertGMLIdentifier_featureWithGMLProps() throws SAXException, IOException { - Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/Alpha-1.xml")); - QName propName = new QName(Namespaces.GML, "identifier"); - Element identifier = XMLUtils.createElement(propName); - identifier.setAttribute("codeSpace", "http://cite.opengeospatial.org/"); - String uuid = UUID.randomUUID().toString(); - identifier.setTextContent(uuid); - WFSMessage.insertGMLProperty(doc.getDocumentElement(), identifier); - Element gmlIdentifier = (Element) doc.getElementsByTagNameNS(Namespaces.GML, "identifier").item(0); - assertEquals("Unexpected gml:identifier value.", uuid, gmlIdentifier.getTextContent()); - assertEquals("Unexpected name of next sibling.", "name", gmlIdentifier.getNextSibling().getLocalName()); - } + @Test + public void createResourceIdFilter() { + String identifier = "alpha"; + Element result = WFSMessage.newResourceIdFilter(identifier); + assertEquals("Unexpected element name.", "Filter", result.getLocalName()); + Element resourceId = (Element) result.getElementsByTagNameNS(Namespaces.FES, "ResourceId").item(0); + assertEquals("Unexpected rid value.", identifier, resourceId.getAttribute("rid")); + } - @Test - public void replaceGmlName() throws SAXException, IOException { - Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/Alpha-1.xml")); - QName propName = new QName(Namespaces.GML, "name"); - Element name = XMLUtils.createElement(propName); - name.setAttribute("codeSpace", "http://cite.opengeospatial.org/"); - String newName = "New name"; - name.setTextContent(newName); - WFSMessage.insertGMLProperty(doc.getDocumentElement(), name); - Element gmlName = (Element) doc.getElementsByTagNameNS(Namespaces.GML, "name").item(0); - assertEquals("Unexpected gml:name value.", newName, gmlName.getTextContent()); - } + @Test + public void insertGMLIdentifier_featureWithoutGMLProps() throws SAXException, IOException { + Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/Gamma.xml")); + QName propName = new QName(Namespaces.GML, "identifier"); + Element identifier = XMLUtils.createElement(propName); + identifier.setAttribute("codeSpace", "http://cite.opengeospatial.org/"); + String uuid = UUID.randomUUID().toString(); + identifier.setTextContent(uuid); + WFSMessage.insertGMLProperty(doc.getDocumentElement(), identifier); + Element gmlIdentifier = (Element) doc.getElementsByTagNameNS(Namespaces.GML, "identifier").item(0); + assertEquals("Unexpected gml:identifier value.", uuid, gmlIdentifier.getTextContent()); + } - @Test - public void append2QueryElementsToGetFeature() throws SAXException, IOException { - Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/GetFeature/GetFeature-Minimal.xml")); - QName typeName1 = new QName(NS1, "Type1"); - WFSMessage.appendSimpleQuery(doc, typeName1); - QName typeName2 = new QName(NS2, "Type2"); - WFSMessage.appendSimpleQuery(doc, typeName2); - NodeList queries = doc.getElementsByTagNameNS(Namespaces.WFS, WFS2.QUERY_ELEM); - assertEquals("Unexpected number of wfs:Query elements.", 2, queries.getLength()); - Element query2 = (Element) queries.item(1); - assertTrue("Expected Query[2]/@typeNames to end with " + typeName2.getLocalPart(), - query2.getAttribute("typeNames").endsWith(typeName2.getLocalPart())); - } + @Test + public void insertGMLIdentifier_featureWithGMLProps() throws SAXException, IOException { + Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/Alpha-1.xml")); + QName propName = new QName(Namespaces.GML, "identifier"); + Element identifier = XMLUtils.createElement(propName); + identifier.setAttribute("codeSpace", "http://cite.opengeospatial.org/"); + String uuid = UUID.randomUUID().toString(); + identifier.setTextContent(uuid); + WFSMessage.insertGMLProperty(doc.getDocumentElement(), identifier); + Element gmlIdentifier = (Element) doc.getElementsByTagNameNS(Namespaces.GML, "identifier").item(0); + assertEquals("Unexpected gml:identifier value.", uuid, gmlIdentifier.getTextContent()); + assertEquals("Unexpected name of next sibling.", "name", gmlIdentifier.getNextSibling().getLocalName()); + } - @Test - public void appendQueryToLockFeatureRequest() throws SAXException, IOException { - Document req = docBuilder.parse(this.getClass().getResourceAsStream("/LockFeature-Empty.xml")); - QName typeName = new QName(NS1, "River"); - WFSMessage.appendSimpleQuery(req, typeName); - NodeList queries = req.getElementsByTagNameNS(Namespaces.WFS, WFS2.QUERY_ELEM); - assertEquals("Unexpected number of wfs:Query elements.", 1, queries.getLength()); - Element query = (Element) queries.item(0); - assertTrue("Expected Query/@typeNames to end with " + typeName.getLocalPart(), - query.getAttribute("typeNames").endsWith(typeName.getLocalPart())); - } + @Test + public void replaceGmlName() throws SAXException, IOException { + Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/Alpha-1.xml")); + QName propName = new QName(Namespaces.GML, "name"); + Element name = XMLUtils.createElement(propName); + name.setAttribute("codeSpace", "http://cite.opengeospatial.org/"); + String newName = "New name"; + name.setTextContent(newName); + WFSMessage.insertGMLProperty(doc.getDocumentElement(), name); + Element gmlName = (Element) doc.getElementsByTagNameNS(Namespaces.GML, "name").item(0); + assertEquals("Unexpected gml:name value.", newName, gmlName.getTextContent()); + } - @Test - public void addResourceIdPredicateToGetFeatureWithLock() throws SAXException, IOException { - Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/GetFeature/GetFeatureWithLock.xml")); - String id = "id001"; - Set idSet = Collections.singleton(id); - WFSMessage.addResourceIdPredicate(doc, idSet); - NodeList filters = doc.getElementsByTagNameNS(Namespaces.FES, "Filter"); - assertEquals("Unexpected number of filters.", 1, filters.getLength()); - Element predicate = (Element) filters.item(0).getChildNodes().item(0); - assertEquals("Unexpected resource id.", id, predicate.getAttribute("rid")); - } + @Test + public void append2QueryElementsToGetFeature() throws SAXException, IOException { + Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/GetFeature/GetFeature-Minimal.xml")); + QName typeName1 = new QName(NS1, "Type1"); + WFSMessage.appendSimpleQuery(doc, typeName1); + QName typeName2 = new QName(NS2, "Type2"); + WFSMessage.appendSimpleQuery(doc, typeName2); + NodeList queries = doc.getElementsByTagNameNS(Namespaces.WFS, WFS2.QUERY_ELEM); + assertEquals("Unexpected number of wfs:Query elements.", 2, queries.getLength()); + Element query2 = (Element) queries.item(1); + assertTrue("Expected Query[2]/@typeNames to end with " + typeName2.getLocalPart(), + query2.getAttribute("typeNames").endsWith(typeName2.getLocalPart())); + } - @Test - public void addDuringPredicate() { - XSElementDeclaration propertyElem = mock(XSElementDeclaration.class); - when(propertyElem.getNamespace()).thenReturn(NS1); - when(propertyElem.getNamespace()).thenReturn("dateTimeProperty"); - Document reqEntity = WFSMessage.createRequestEntity("GetFeature-Minimal", "2.0.2"); - WFSMessage.appendSimpleQuery(reqEntity, new QName(NS1, "SimpleFeature")); - ZonedDateTime endTime = ZonedDateTime.now(ZoneId.of("Z")); - ZonedDateTime startTime = endTime.minusYears(5); - Document gmlTime = TimeUtils.intervalAsGML(startTime, endTime); - Element valueRef = WFSMessage.createValueReference(propertyElem); - WFSMessage.addTemporalPredicate(reqEntity, "During", gmlTime, valueRef); - Node predicate = reqEntity.getElementsByTagNameNS(Namespaces.FES, "During").item(0); - assertNotNull(predicate); - assertEquals("Unexpected number of operands", 2, predicate.getChildNodes().getLength()); - } + @Test + public void appendQueryToLockFeatureRequest() throws SAXException, IOException { + Document req = docBuilder.parse(this.getClass().getResourceAsStream("/LockFeature-Empty.xml")); + QName typeName = new QName(NS1, "River"); + WFSMessage.appendSimpleQuery(req, typeName); + NodeList queries = req.getElementsByTagNameNS(Namespaces.WFS, WFS2.QUERY_ELEM); + assertEquals("Unexpected number of wfs:Query elements.", 1, queries.getLength()); + Element query = (Element) queries.item(0); + assertTrue("Expected Query/@typeNames to end with " + typeName.getLocalPart(), + query.getAttribute("typeNames").endsWith(typeName.getLocalPart())); + } - @Test - public void setReturnTypesAndTypeNamesAttribute() - throws SAXException, IOException { - Document doc = docBuilder.parse( this.getClass().getResourceAsStream( "/org/opengis/cite/iso19142/querymgmt/CreateStoredQuery-GetFeatureByTypeName.xml" ) ); - QName featureType = new QName( "abc", "http://test.org" ); - WFSMessage.setReturnTypesAndTypeNamesAttribute( doc, featureType ); - NodeList queryExpressionText = doc.getElementsByTagNameNS( Namespaces.WFS, "QueryExpressionText" ); - assertEquals( "Unexpected number of filters.", 1, queryExpressionText.getLength() ); - Element queryExpressionTextElement = (Element) queryExpressionText.item( 0 ); - assertThat( "Unexpected returnFeatureTypes.", queryExpressionTextElement.getAttribute( "returnFeatureTypes" ), - containsString( featureType.getLocalPart() ) ); + @Test + public void addResourceIdPredicateToGetFeatureWithLock() throws SAXException, IOException { + Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/GetFeature/GetFeatureWithLock.xml")); + String id = "id001"; + Set idSet = Collections.singleton(id); + WFSMessage.addResourceIdPredicate(doc, idSet); + NodeList filters = doc.getElementsByTagNameNS(Namespaces.FES, "Filter"); + assertEquals("Unexpected number of filters.", 1, filters.getLength()); + Element predicate = (Element) filters.item(0).getChildNodes().item(0); + assertEquals("Unexpected resource id.", id, predicate.getAttribute("rid")); + } + + @Test + public void addDuringPredicate() { + XSElementDeclaration propertyElem = mock(XSElementDeclaration.class); + when(propertyElem.getNamespace()).thenReturn(NS1); + when(propertyElem.getNamespace()).thenReturn("dateTimeProperty"); + Document reqEntity = WFSMessage.createRequestEntity("GetFeature-Minimal", "2.0.2"); + WFSMessage.appendSimpleQuery(reqEntity, new QName(NS1, "SimpleFeature")); + ZonedDateTime endTime = ZonedDateTime.now(ZoneId.of("Z")); + ZonedDateTime startTime = endTime.minusYears(5); + Document gmlTime = TimeUtils.intervalAsGML(startTime, endTime); + Element valueRef = WFSMessage.createValueReference(propertyElem); + WFSMessage.addTemporalPredicate(reqEntity, "During", gmlTime, valueRef); + Node predicate = reqEntity.getElementsByTagNameNS(Namespaces.FES, "During").item(0); + assertNotNull(predicate); + assertEquals("Unexpected number of operands", 2, predicate.getChildNodes().getLength()); + } + + @Test + public void setReturnTypesAndTypeNamesAttribute() throws SAXException, IOException { + Document doc = docBuilder.parse(this.getClass() + .getResourceAsStream("/org/opengis/cite/iso19142/querymgmt/CreateStoredQuery-GetFeatureByTypeName.xml")); + QName featureType = new QName("abc", "http://test.org"); + WFSMessage.setReturnTypesAndTypeNamesAttribute(doc, featureType); + NodeList queryExpressionText = doc.getElementsByTagNameNS(Namespaces.WFS, "QueryExpressionText"); + assertEquals("Unexpected number of filters.", 1, queryExpressionText.getLength()); + Element queryExpressionTextElement = (Element) queryExpressionText.item(0); + assertThat("Unexpected returnFeatureTypes.", queryExpressionTextElement.getAttribute("returnFeatureTypes"), + containsString(featureType.getLocalPart())); + + NodeList query = doc.getElementsByTagNameNS(Namespaces.WFS, "Query"); + assertEquals("Unexpected number of filters.", 1, queryExpressionText.getLength()); + Element queryElement = (Element) query.item(0); + assertThat("Unexpected typeNames.", queryElement.getAttribute("typeNames"), + containsString(featureType.getLocalPart())); + } - NodeList query = doc.getElementsByTagNameNS( Namespaces.WFS, "Query" ); - assertEquals( "Unexpected number of filters.", 1, queryExpressionText.getLength() ); - Element queryElement = (Element) query.item( 0 ); - assertThat( "Unexpected typeNames.", queryElement.getAttribute( "typeNames" ), - containsString( featureType.getLocalPart() ) ); - } - } diff --git a/src/test/java/org/opengis/cite/iso19142/util/VerifyXMLUtils.java b/src/test/java/org/opengis/cite/iso19142/util/VerifyXMLUtils.java index 7ebdee37..a0c9fc91 100644 --- a/src/test/java/org/opengis/cite/iso19142/util/VerifyXMLUtils.java +++ b/src/test/java/org/opengis/cite/iso19142/util/VerifyXMLUtils.java @@ -40,140 +40,143 @@ */ public class VerifyXMLUtils { - private static final String ATOM_NS = "http://www.w3.org/2005/Atom"; - private static final String EX_NS = "http://example.org/ns1"; - private static DocumentBuilder docBuilder; - - public VerifyXMLUtils() { - } - - @BeforeClass - public static void setUpClass() throws Exception { - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(true); - docBuilder = dbf.newDocumentBuilder(); - } - - @Test - public void writeDocToString() throws SAXException, IOException { - Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/atom-feed.xml")); - String content = XMLUtils.writeNodeToString(doc); - assertTrue("String should start with ' nsBindings = new HashMap(); - nsBindings.put(ATOM_NS, "tns"); - nsBindings.put(EX_NS, "ns1"); - NodeList results = XMLUtils.evaluateXPath(doc, expr, nsBindings); - assertTrue("Expected 1 node in results.", results.getLength() == 1); - assertEquals("author", results.item(0).getLocalName()); - } - - @Test - public void evaluateXPathExpression_noMatch() throws XPathExpressionException, SAXException, IOException { - Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/atom-feed.xml")); - String expr = "/tns:feed/tns:author[ns1:blog]"; - Map nsBindings = new HashMap(); - nsBindings.put(ATOM_NS, "tns"); - nsBindings.put(EX_NS, "ns1"); - NodeList results = XMLUtils.evaluateXPath(doc, expr, nsBindings); - assertTrue("Expected empty results.", results.getLength() == 0); - } - - @Test(expected = XPathExpressionException.class) - public void evaluateXPathExpression_booleanResult() throws XPathExpressionException, SAXException, IOException { - Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/atom-feed.xml")); - String expr = "count(//tns:entry) > 0"; - Map nsBindings = new HashMap(); - nsBindings.put(ATOM_NS, "tns"); - NodeList results = XMLUtils.evaluateXPath(doc, expr, nsBindings); - assertNull(results); - } - - @Test - public void createElement_Alpha() { - QName qName = new QName("http://example.org", "Alpha"); - Element elem = XMLUtils.createElement(qName); - assertEquals("Alpha", elem.getLocalName()); - assertNull(elem.getParentNode()); - assertNotNull(elem.getOwnerDocument()); - } - - @Test - public void transformGMLEnvelopeToPolygon() throws SAXException, IOException { - Document source = docBuilder.parse(this.getClass().getResourceAsStream("/Envelope.xml")); - Source xslt = new StreamSource(this.getClass().getResourceAsStream("/xslt/bbox2polygon.xsl")); - Document result = XMLUtils.transform(xslt, source, null); - assertEquals("Document element has unexpected [local name].", "Polygon", - result.getDocumentElement().getLocalName()); - String posList = result.getElementsByTagNameNS(Namespaces.GML, "posList").item(0).getTextContent(); - assertTrue("Expected gml:posList to end with coord '49.0 -124.0'", posList.endsWith("49.0 -124.0")); - } - - @Test - public void evaluateXPath2ExpressionAgainstDocument() - throws SAXException, IOException, SaxonApiException, XPathException { - Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/atom-feed.xml")); - String expr = "matches(//tns:entry/tns:title, '.*Robots')"; - Map nsBindings = new HashMap(); - nsBindings.put(ATOM_NS, "tns"); - XdmValue result = XMLUtils.evaluateXPath2(new DOMSource(doc), expr, nsBindings); - assertTrue("Expected non-empty result.", result.size() > 0); - assertEquals("Result has unexpected string value.", "true", result.getUnderlyingValue().getStringValue()); - } - - @Test - public void evaluateXPath2ExpressionAgainstElement() - throws SAXException, IOException, SaxonApiException, XPathException { - Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/atom-feed.xml")); - Node entry = doc.getElementsByTagNameNS(ATOM_NS, "entry").item(0); - String expr = "matches(tns:title, '.*Robots')"; - Map nsBindings = new HashMap(); - nsBindings.put(ATOM_NS, "tns"); - XdmValue result = XMLUtils.evaluateXPath2(new DOMSource(entry), expr, nsBindings); - assertTrue("Expected non-empty result.", result.size() > 0); - assertEquals("Result has unexpected string value.", "true", result.getUnderlyingValue().getStringValue()); - } - - @Test - public void writeNodeToString_Latin1Char() throws SAXException, IOException { - Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/atom-feed.xml")); - Node author = doc.getElementsByTagNameNS(ATOM_NS, "author").item(0); - String result = XMLUtils.writeNodeToString(author); - assertTrue("Expected result to contain 'Montréal'", result.contains("Montréal")); - } - - @Test - public void expandCharacterEntity() { - String text = "Ce n'est pas"; - String result = XMLUtils.expandReferencesInText(text); - assertTrue("Expected result to contain an apostrophe (')", result.contains("'")); - } - - @Test - public void expandNumericCharacterReference() { - String text = "Montréal"; - String result = XMLUtils.expandReferencesInText(text); - assertEquals("Expected result to contain character é (U+00E9)", "Montréal", result); - } - - @Test - public void domResultToString() throws SAXException, IOException { - Document svrl = docBuilder.parse(this.getClass().getResourceAsStream("/sch/result.xml")); - Result result = new DOMResult(svrl.getDocumentElement()); - String strResult = XMLUtils.resultToString(result); - assertTrue("Expected 'successful-report' in result", strResult.contains("successful-report")); - } - - @Test - public void streamResultToString() throws SAXException, IOException, URISyntaxException { - URL url = getClass().getResource("/sch/result.txt"); - Result result = new StreamResult(url.toURI().toString()); - String strResult = XMLUtils.resultToString(result); - assertTrue("Expected 'Error' in result", strResult.contains("Error")); - } + private static final String ATOM_NS = "http://www.w3.org/2005/Atom"; + + private static final String EX_NS = "http://example.org/ns1"; + + private static DocumentBuilder docBuilder; + + public VerifyXMLUtils() { + } + + @BeforeClass + public static void setUpClass() throws Exception { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + docBuilder = dbf.newDocumentBuilder(); + } + + @Test + public void writeDocToString() throws SAXException, IOException { + Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/atom-feed.xml")); + String content = XMLUtils.writeNodeToString(doc); + assertTrue("String should start with ' nsBindings = new HashMap(); + nsBindings.put(ATOM_NS, "tns"); + nsBindings.put(EX_NS, "ns1"); + NodeList results = XMLUtils.evaluateXPath(doc, expr, nsBindings); + assertTrue("Expected 1 node in results.", results.getLength() == 1); + assertEquals("author", results.item(0).getLocalName()); + } + + @Test + public void evaluateXPathExpression_noMatch() throws XPathExpressionException, SAXException, IOException { + Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/atom-feed.xml")); + String expr = "/tns:feed/tns:author[ns1:blog]"; + Map nsBindings = new HashMap(); + nsBindings.put(ATOM_NS, "tns"); + nsBindings.put(EX_NS, "ns1"); + NodeList results = XMLUtils.evaluateXPath(doc, expr, nsBindings); + assertTrue("Expected empty results.", results.getLength() == 0); + } + + @Test(expected = XPathExpressionException.class) + public void evaluateXPathExpression_booleanResult() throws XPathExpressionException, SAXException, IOException { + Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/atom-feed.xml")); + String expr = "count(//tns:entry) > 0"; + Map nsBindings = new HashMap(); + nsBindings.put(ATOM_NS, "tns"); + NodeList results = XMLUtils.evaluateXPath(doc, expr, nsBindings); + assertNull(results); + } + + @Test + public void createElement_Alpha() { + QName qName = new QName("http://example.org", "Alpha"); + Element elem = XMLUtils.createElement(qName); + assertEquals("Alpha", elem.getLocalName()); + assertNull(elem.getParentNode()); + assertNotNull(elem.getOwnerDocument()); + } + + @Test + public void transformGMLEnvelopeToPolygon() throws SAXException, IOException { + Document source = docBuilder.parse(this.getClass().getResourceAsStream("/Envelope.xml")); + Source xslt = new StreamSource(this.getClass().getResourceAsStream("/xslt/bbox2polygon.xsl")); + Document result = XMLUtils.transform(xslt, source, null); + assertEquals("Document element has unexpected [local name].", "Polygon", + result.getDocumentElement().getLocalName()); + String posList = result.getElementsByTagNameNS(Namespaces.GML, "posList").item(0).getTextContent(); + assertTrue("Expected gml:posList to end with coord '49.0 -124.0'", posList.endsWith("49.0 -124.0")); + } + + @Test + public void evaluateXPath2ExpressionAgainstDocument() + throws SAXException, IOException, SaxonApiException, XPathException { + Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/atom-feed.xml")); + String expr = "matches(//tns:entry/tns:title, '.*Robots')"; + Map nsBindings = new HashMap(); + nsBindings.put(ATOM_NS, "tns"); + XdmValue result = XMLUtils.evaluateXPath2(new DOMSource(doc), expr, nsBindings); + assertTrue("Expected non-empty result.", result.size() > 0); + assertEquals("Result has unexpected string value.", "true", result.getUnderlyingValue().getStringValue()); + } + + @Test + public void evaluateXPath2ExpressionAgainstElement() + throws SAXException, IOException, SaxonApiException, XPathException { + Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/atom-feed.xml")); + Node entry = doc.getElementsByTagNameNS(ATOM_NS, "entry").item(0); + String expr = "matches(tns:title, '.*Robots')"; + Map nsBindings = new HashMap(); + nsBindings.put(ATOM_NS, "tns"); + XdmValue result = XMLUtils.evaluateXPath2(new DOMSource(entry), expr, nsBindings); + assertTrue("Expected non-empty result.", result.size() > 0); + assertEquals("Result has unexpected string value.", "true", result.getUnderlyingValue().getStringValue()); + } + + @Test + public void writeNodeToString_Latin1Char() throws SAXException, IOException { + Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/atom-feed.xml")); + Node author = doc.getElementsByTagNameNS(ATOM_NS, "author").item(0); + String result = XMLUtils.writeNodeToString(author); + assertTrue("Expected result to contain 'Montréal'", result.contains("Montréal")); + } + + @Test + public void expandCharacterEntity() { + String text = "Ce n'est pas"; + String result = XMLUtils.expandReferencesInText(text); + assertTrue("Expected result to contain an apostrophe (')", result.contains("'")); + } + + @Test + public void expandNumericCharacterReference() { + String text = "Montréal"; + String result = XMLUtils.expandReferencesInText(text); + assertEquals("Expected result to contain character é (U+00E9)", "Montréal", result); + } + + @Test + public void domResultToString() throws SAXException, IOException { + Document svrl = docBuilder.parse(this.getClass().getResourceAsStream("/sch/result.xml")); + Result result = new DOMResult(svrl.getDocumentElement()); + String strResult = XMLUtils.resultToString(result); + assertTrue("Expected 'successful-report' in result", strResult.contains("successful-report")); + } + + @Test + public void streamResultToString() throws SAXException, IOException, URISyntaxException { + URL url = getClass().getResource("/sch/result.txt"); + Result result = new StreamResult(url.toURI().toString()); + String strResult = XMLUtils.resultToString(result); + assertTrue("Expected 'Error' in result", strResult.contains("Error")); + } + }