/* (c) 2014 Open Source Geospatial Foundation - all rights reserved
* (c) 2001 - 2013 OpenPlans
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.test;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import net.sf.json.JSON;
import net.sf.json.JSONObject;
import org.geoserver.catalog.FeatureTypeInfo;
import org.geoserver.util.IOUtils;
import org.geoserver.wfs.WFSInfo;
import org.geoserver.wfs.xml.v1_1_0.WFS;
import org.geotools.data.DataUtilities;
import org.geotools.data.FeatureSource;
import org.geotools.data.complex.AppSchemaDataAccess;
import org.geotools.data.complex.AppSchemaDataAccessRegistry;
import org.geotools.data.complex.FeatureTypeMapping;
import org.geotools.data.complex.config.AppSchemaDataAccessConfigurator;
import org.geotools.data.complex.filter.ComplexFilterSplitter;
import org.geotools.data.jdbc.FilterToSQLException;
import org.geotools.filter.FilterFactoryImplNamespaceAware;
import org.geotools.jdbc.JDBCDataStore;
import org.geotools.jdbc.NestedFilterToSQL;
import org.geotools.util.NullProgressListener;
import org.junit.Test;
import org.opengis.filter.And;
import org.opengis.filter.Filter;
import org.opengis.filter.PropertyIsLike;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
/**
* WFS GetFeature to test integration of {@link AppSchemaDataAccess} with GeoServer.
*
* @author Ben Caradoc-Davies, CSIRO Earth Science and Resource Engineering
* @author Rini Angreani, CSIRO Earth Science and Resource Engineering
* @author Xiangtan Lin, CSIRO Information Management and Technology
*/
public class FeatureChainingWfsTest extends AbstractAppSchemaTestSupport {
@Override
protected FeatureChainingMockData createTestData() {
return new FeatureChainingMockData();
}
public static final String GETFEATURE_ATTRIBUTES = "service=\"WFS\" " //
+ "version=\"1.1.0\" " //
+ "xmlns:ogc=\"http://www.opengis.net/ogc\" " //
+ "xmlns:wfs=\"http://www.opengis.net/wfs\" " //
+ "xmlns:gml=\"http://www.opengis.net/gml\" " //
+ "xmlns:gsml=\"" + AbstractAppSchemaMockData.GSML_URI
+ "\" " //
+ "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " //
+ "xsi:schemaLocation=\"" //
+ "http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.1.0/wfs.xsd " //
+ AbstractAppSchemaMockData.GSML_URI
+ " "
+ AbstractAppSchemaMockData.GSML_SCHEMA_LOCATION_URL //
+ "\""; // end of schemaLocation
/**
* Return the root of the test fixture data directory.
*/
private File getDataDir() {
return getTestData().getDataDirectoryRoot();
}
/**
* Return first ex schema file.
*/
private File getExSchemaOne() {
return findFile("featureTypes/ex_FirstParentFeature/simpleContent.xsd", getDataDir());
}
/**
* Return first ex schema location.
*/
private String getExSchemaOneLocation() {
return DataUtilities.fileToURL(getExSchemaOne()).toString();
}
/**
* Return second ex schema file.
*/
private File getExSchemaTwo() {
return findFile("featureTypes/ex_SecondParentFeature/simpleContent.xsd", getDataDir());
}
/**
* Return second ex schema location.
*/
private String getExSchemaTwoLocation() {
return DataUtilities.fileToURL(getExSchemaTwo()).toString();
}
/**
* Return third ex schema file.
*/
private File getExSchemaThree() {
return findFile("featureTypes/ex_ParentFeature/NonValidNestedGML.xsd", getDataDir());
}
/**
* Return third ex schema location.
*/
private String getExSchemaThreeLocation() {
return DataUtilities.fileToURL(getExSchemaThree()).toString();
}
/**
* Test that ex schemas are found and the files exist.
*/
@Test
public void testExSchemas() {
assertNotNull(getExSchemaOne());
assertTrue(getExSchemaOne().exists());
assertNotNull(getExSchemaTwo());
assertTrue(getExSchemaTwo().exists());
}
/**
* Test whether GetCapabilities returns wfs:WFS_Capabilities.
*/
@Test
public void testGetCapabilities() {
Document doc = getAsDOM("wfs?request=GetCapabilities&version=1.1.0");
LOGGER.info("WFS GetCapabilities response:\n" + prettyString(doc));
assertEquals("wfs:WFS_Capabilities", doc.getDocumentElement().getNodeName());
// check wfs schema location is canonical
String schemaLocation = evaluate("wfs:WFS_Capabilities/@xsi:schemaLocation", doc);
String location = "http://www.opengis.net/wfs " + WFS.CANONICAL_SCHEMA_LOCATION;
assertEquals(location , schemaLocation);
// make sure non-feature types don't appear in FeatureTypeList
assertXpathCount(6, "//wfs:FeatureType", doc);
ArrayList<String> featureTypeNames = new ArrayList<String>(6);
featureTypeNames.add(evaluate("//wfs:FeatureType[1]/wfs:Name", doc));
featureTypeNames.add(evaluate("//wfs:FeatureType[2]/wfs:Name", doc));
featureTypeNames.add(evaluate("//wfs:FeatureType[3]/wfs:Name", doc));
featureTypeNames.add(evaluate("//wfs:FeatureType[4]/wfs:Name", doc));
featureTypeNames.add(evaluate("//wfs:FeatureType[5]/wfs:Name", doc));
featureTypeNames.add(evaluate("//wfs:FeatureType[6]/wfs:Name", doc));
// Mapped Feture
assertTrue(featureTypeNames.contains("gsml:MappedFeature"));
// Geologic Unit
assertTrue(featureTypeNames.contains("gsml:GeologicUnit"));
// FirstParentFeature
assertTrue(featureTypeNames.contains("ex:FirstParentFeature"));
// SecondParentFeature
assertTrue(featureTypeNames.contains("ex:SecondParentFeature"));
// ParentFeature
assertTrue(featureTypeNames.contains("ex:ParentFeature"));
// om:Observation
assertTrue(featureTypeNames.contains("om:Observation"));
}
/**
* Test DescribeFeatureType response for gsml:MappedFeature.
*/
@Test
public void testDescribeFeatureTypeMappedFeature() {
Document doc = getAsDOM("wfs?request=DescribeFeatureType&version=1.1.0&typename=gsml:MappedFeature");
LOGGER.info("WFS DescribeFeatureType, typename=gsml:MappedFeature response:\n"
+ prettyString(doc));
assertEquals("xsd:schema", doc.getDocumentElement().getNodeName());
// check target name space is encoded and is correct
assertXpathEvaluatesTo(AbstractAppSchemaMockData.GSML_URI, "//@targetNamespace", doc);
// make sure the content is only relevant include
assertXpathCount(1, "//xsd:include", doc);
// no import to GML since it's already imported inside the included schema
// otherwise it's invalid to import twice
assertXpathCount(0, "//xsd:import", doc);
// GSML schemaLocation
assertXpathEvaluatesTo(AbstractAppSchemaMockData.GSML_SCHEMA_LOCATION_URL,
"//xsd:include/@schemaLocation", doc);
// nothing else
assertXpathCount(0, "//xsd:complexType", doc);
assertXpathCount(0, "//xsd:element", doc);
}
/**
* Test DescribeFeatureType response for gsml:GeologicUnit.
*/
@Test
public void testDescribeFeatureTypeGeologicUnit() {
Document doc = getAsDOM("wfs?request=DescribeFeatureType&version=1.1.0&typename=gsml:GeologicUnit");
LOGGER.info("WFS DescribeFeatureType, typename=gsml:GeologicUnit response:\n"
+ prettyString(doc));
assertEquals("xsd:schema", doc.getDocumentElement().getNodeName());
assertXpathEvaluatesTo(AbstractAppSchemaMockData.GSML_URI, "//@targetNamespace", doc);
assertXpathCount(1, "//xsd:include", doc);
assertXpathCount(0, "//xsd:import", doc);
// GSML schemaLocation
assertXpathEvaluatesTo(AbstractAppSchemaMockData.GSML_SCHEMA_LOCATION_URL,
"//xsd:include/@schemaLocation", doc);
// nothing else
assertXpathCount(0, "//xsd:complexType", doc);
assertXpathCount(0, "//xsd:element", doc);
}
/**
* Test DescribeFeatureType response for ex:FirstParentFeature and ex:SecondParentFeature, which
* have two schemas in the same namespace.
*/
@Test
public void testDescribeFeatureTypeTwoSchemasSameNamespace() {
Document doc = getAsDOM("wfs?request=DescribeFeatureType&version=1.1.0&typeName=ex:FirstParentFeature,ex:SecondParentFeature");
LOGGER.info("WFS DescribeFeatureType, typename=ex:FirstParentFeature,"
+ "ex:SecondParentFeature response:\n" + prettyString(doc));
assertXpathEvaluatesTo(FeatureChainingMockData.EX_URI, "//@targetNamespace", doc);
assertXpathCount(1, "//xsd:include", doc);
assertXpathCount(0, "//xsd:import", doc);
// EX include
String schemaLocation = evaluate("//xsd:include/@schemaLocation", doc);
if (!schemaLocation.equals(getExSchemaOneLocation())) {
assertEquals(getExSchemaTwoLocation(), schemaLocation);
}
// nothing else
assertXpathCount(0, "//xsd:complexType", doc);
assertXpathCount(0, "//xsd:element", doc);
}
/**
* Test DescribeFeatureType response for om:Observation, which has 2 schemaUris specified in the
* mapping file. Both must appear.
*/
@Test
public void testDescribeFeatureTypeObservation() {
Document doc = getAsDOM("wfs?request=DescribeFeatureType&version=1.1.0&typename=om:Observation");
LOGGER.info("WFS DescribeFeatureType, typename=om:Observation response:\n"
+ prettyString(doc));
assertEquals("xsd:schema", doc.getDocumentElement().getNodeName());
assertXpathEvaluatesTo(FeatureChainingMockData.OM_URI, "//@targetNamespace", doc);
assertXpathCount(1, "//xsd:include", doc);
assertXpathCount(1, "//xsd:import", doc);
// GSML schemaLocation as xsd:import because the namespace is different
assertXpathEvaluatesTo(AbstractAppSchemaMockData.GSML_URI, "//xsd:import/@namespace", doc);
assertXpathEvaluatesTo(AbstractAppSchemaMockData.GSML_SCHEMA_LOCATION_URL,
"//xsd:import/@schemaLocation", doc);
// OM schemaLocation as xsd:include
assertXpathEvaluatesTo(FeatureChainingMockData.OM_SCHEMA_LOCATION_URL,
"//xsd:include/@schemaLocation", doc);
// nothing else
assertXpathCount(0, "//xsd:complexType", doc);
assertXpathCount(0, "//xsd:element", doc);
}
/**
* Test DescribeFeatureType response for mixed namespaces.
*/
@Test
public void testDescribeFeatureTypeMixedNamespaces() {
Document doc = getAsDOM("wfs?request=DescribeFeatureType&version=1.1.0&typeName=gsml:MappedFeature,ex:FirstParentFeature");
LOGGER.info("WFS DescribeFeatureType, typename=gsml:MappedFeature,ex:FirstParentFeature response:\n"
+ prettyString(doc));
checkGsmlExDescribeFeatureType(doc);
}
/**
* Test DescribeFeatureType response for many types.
*/
@Test
public void testDescribeFeatureTypeManyTypes() {
Document doc = getAsDOM("wfs?request=DescribeFeatureType&version=1.1.0&typeName=gsml:MappedFeature,gsml:GeologicUnit,ex:FirstParentFeature,ex:SecondParentFeature");
LOGGER.info("WFS DescribeFeatureType, typename=gsml:MappedFeature,gsml:GeologicUnit,ex:FirstParentFeature,ex:SecondParentFeature response:\n"
+ prettyString(doc));
checkGsmlExDescribeFeatureType(doc);
}
private void checkGsmlExDescribeFeatureType(Document doc) {
assertEquals("xsd:schema", doc.getDocumentElement().getNodeName());
assertXpathEvaluatesTo(FeatureChainingMockData.GSML_URI, "//@targetNamespace", doc);
assertXpathCount(1, "//xsd:include", doc);
assertXpathCount(1, "//xsd:import", doc);
// gsml schemaLocation as xsd:include
assertXpathEvaluatesTo(FeatureChainingMockData.GSML_SCHEMA_LOCATION_URL,
"//xsd:include/@schemaLocation", doc);
// ex schemaLocation as xsd:import because the namespace is different
// Only one ex schema can be imported.
// The other ex schema is ignored (a warning is logged).
// The reason for this is that the effect of importing multiple schemas with
// the same target namespace is undefined, so FeatureTypeSchemaBuilder does
// not do it.
assertXpathEvaluatesTo(FeatureChainingMockData.EX_URI, "//xsd:import/@namespace", doc);
String schemaLocation = evaluate("//xsd:import/@schemaLocation", doc);
assertTrue(schemaLocation.equals(getExSchemaOneLocation())
|| schemaLocation.equals(getExSchemaTwoLocation()));
// nothing else
assertXpathCount(0, "//xsd:complexType", doc);
assertXpathCount(0, "//xsd:element", doc);
}
/**
* Test DescribeFeatureType response when no types are specified.
*/
@Test
public void testDescribeFeatureTypeNoTypes() {
Document doc = getAsDOM("wfs?request=DescribeFeatureType&version=1.1.0");
LOGGER.info("WFS DescribeFeatureType response:\n" + prettyString(doc));
assertEquals("xsd:schema", doc.getDocumentElement().getNodeName());
String targetNamespace = evaluate("//@targetNamespace", doc);
assertFalse(targetNamespace.isEmpty());
int numberOfImports = getMatchingNodes("//xsd:import", doc).getLength();
int numberOfIncludes = getMatchingNodes("//xsd:include", doc).getLength();
ArrayList<String> namespaces = new ArrayList<String>();
namespaces.add(AbstractAppSchemaMockData.GSML_URI);
namespaces.add(FeatureChainingMockData.OM_URI);
namespaces.add(FeatureChainingMockData.EX_URI);
// targetNamespace depends on load order which is platform dependent
if (targetNamespace.equals(FeatureChainingMockData.EX_URI)) {
assertEquals(2, numberOfImports);
assertEquals(3, numberOfIncludes);
@SuppressWarnings("serial")
Set<String> expectedExSchemaLocations = new HashSet<String>() {
{
add(getExSchemaOneLocation());
add(getExSchemaTwoLocation());
add(getExSchemaThreeLocation());
}
};
// ensure expected schemaLocations are distinct
assertEquals(numberOfIncludes, expectedExSchemaLocations.size());
// check that found schemaLocations are as expected
Set<String> foundExSchemaLocations = new HashSet<String>();
for (int i = 1; i <= numberOfIncludes; i++) {
foundExSchemaLocations
.add(evaluate("//xsd:include[" + i + "]/@schemaLocation", doc));
}
assertEquals(expectedExSchemaLocations, foundExSchemaLocations);
// ensure that this namespace is not used for imports in later asserts
namespaces.remove(FeatureChainingMockData.EX_URI);
} else {
// If the targetNamespace is not the ex namespace, only one ex schema can be imported.
// The other ex schema is ignored (a warning is logged).
// The reason for this is that the effect of importing multiple schemas with
// the same target namespace is undefined, so FeatureTypeSchemaBuilder does
// not do it.
assertEquals(2, numberOfImports);
assertEquals(1, numberOfIncludes);
String schemaLocation = "//xsd:include[" + 1 + "]/@schemaLocation";
if (targetNamespace.equals(AbstractAppSchemaMockData.GSML_URI)) {
// gsml include
assertXpathEvaluatesTo(AbstractAppSchemaMockData.GSML_SCHEMA_LOCATION_URL,
schemaLocation, doc);
namespaces.remove(AbstractAppSchemaMockData.GSML_URI);
} else {
// om include
assertEquals(FeatureChainingMockData.OM_URI, targetNamespace);
assertXpathEvaluatesTo(FeatureChainingMockData.OM_SCHEMA_LOCATION_URL,
schemaLocation, doc);
namespaces.remove(FeatureChainingMockData.OM_URI);
}
}
// order is unimportant, and could change, so we don't test the order
for (int i = 1; i <= numberOfImports; i++) {
String namespace = evaluate("//xsd:import[" + i + "]/@namespace", doc);
String schemaLocation = "//xsd:import[" + i + "]/@schemaLocation";
if (namespace.equals(AbstractAppSchemaMockData.GSML_URI)) {
// gsml import
assertXpathEvaluatesTo(AbstractAppSchemaMockData.GSML_SCHEMA_LOCATION_URL,
schemaLocation, doc);
namespaces.remove(AbstractAppSchemaMockData.GSML_URI);
} else if (namespace.equals(FeatureChainingMockData.EX_URI)) {
// ex import
String loc = evaluate(schemaLocation, doc);
assertTrue(loc.equals(getExSchemaOneLocation())
|| loc.equals(getExSchemaTwoLocation())
|| loc.equals(getExSchemaThreeLocation()));
namespaces.remove(FeatureChainingMockData.EX_URI);
} else {
// om import
assertEquals(FeatureChainingMockData.OM_URI, namespace);
assertXpathEvaluatesTo(FeatureChainingMockData.OM_SCHEMA_LOCATION_URL,
schemaLocation, doc);
namespaces.remove(FeatureChainingMockData.OM_URI);
}
}
// ensure there are no unexpected references
assertTrue(namespaces.isEmpty());
// nothing else
assertXpathCount(0, "//xsd:complexType", doc);
assertXpathCount(0, "//xsd:element", doc);
}
/**
* Test whether GetFeature returns wfs:FeatureCollection.
*/
@Test
public void testGetFeatureGML() {
Document doc = getAsDOM("wfs?request=GetFeature&version=1.1.0&typename=gsml:MappedFeature");
LOGGER.info("WFS GetFeature&typename=gsml:MappedFeature response:\n" + prettyString(doc));
assertEquals("wfs:FeatureCollection", doc.getDocumentElement().getNodeName());
// non-feature type should return nothing/exception
doc = getAsDOM("wfs?request=GetFeature&typename=gsml:CompositionPart");
LOGGER.info("WFS GetFeature&typename=gsml:CompositionPart response, exception expected:\n"
+ prettyString(doc));
assertEquals("ows:ExceptionReport", doc.getDocumentElement().getNodeName());
}
@Test
public void testGetFeatureJSON() throws Exception {
testJsonRequest("gsml:MappedFeature", "/test-data/MappedFeature.json");
}
@Test
public void testGetFeatureValid() {
String path = "wfs?request=GetFeature&version=1.1.0&typename=gsml:MappedFeature";
String newline = System.getProperty("line.separator");
Document doc = getAsDOM(path);
LOGGER.info("Response for " + path + " :" + newline + prettyString(doc));
validateGet(path);
}
/**
* GeologicUnit mapping has mappingName specified, to override targetElementName when feature
* chained to MappedFeature. This is to test that querying GeologicUnit as top level feature
* still works, when its real type name is specified in the query.
*/
@Test
public void testGetFeatureWithMappingName() {
Document doc = getAsDOM("wfs?request=GetFeature&version=1.1.0&typename=gsml:GeologicUnit");
LOGGER.info("WFS GetFeature&typename=gsml:GeologicUnit response:\n" + prettyString(doc));
assertEquals("wfs:FeatureCollection", doc.getDocumentElement().getNodeName());
assertXpathEvaluatesTo("3", "/wfs:FeatureCollection/@numberOfFeatures", doc);
assertXpathCount(3, "//gsml:GeologicUnit", doc);
}
/**
* Test nesting features of complex types with simple content. Previously the nested features
* attributes weren't encoded, so this is to ensure that this works. This also tests that a
* feature type can have multiple FEATURE_LINK to be referred by different types.
*/
@Test
public void testComplexTypeWithSimpleContentGML() {
Document doc = getAsDOM("wfs?request=GetFeature&version=1.1.0&typename=ex:FirstParentFeature");
LOGGER
.info("WFS GetFeature&typename=ex:FirstParentFeature response:\n"
+ prettyString(doc));
assertXpathCount(5, "//ex:FirstParentFeature", doc);
// cc.1
assertXpathCount(2, "//ex:FirstParentFeature[@gml:id='cc.1']/ex:nestedFeature", doc);
assertXpathEvaluatesTo(
"string_one",
"//ex:FirstParentFeature[@gml:id='cc.1']/ex:nestedFeature[1]/ex:SimpleContent/ex:someAttribute",
doc);
assertXpathEvaluatesTo(
"string_two",
"//ex:FirstParentFeature[@gml:id='cc.1']/ex:nestedFeature[2]/ex:SimpleContent/ex:someAttribute",
doc);
assertXpathCount(
0,
"//ex:FirstParentFeature[@gml:id='cc.1']/ex:nestedFeature[2]/ex:SimpleContent/FEATURE_LINK",
doc);
// cc.2
assertXpathCount(0, "//ex:FirstParentFeature[@gml:id='cc.2']/ex:nestedFeature", doc);
doc = getAsDOM("wfs?request=GetFeature&version=1.1.0&typename=ex:SecondParentFeature");
LOGGER.info("WFS GetFeature&typename=ex:SecondParentFeature response:\n"
+ prettyString(doc));
assertXpathCount(5, "//ex:SecondParentFeature", doc);
// cc.1
assertXpathCount(0, "//ex:SecondParentFeature[@gml:id='cc.1']/ex:nestedFeature", doc);
// cc.2
assertXpathCount(3, "//ex:SecondParentFeature[@gml:id='cc.2']/ex:nestedFeature", doc);
assertXpathEvaluatesTo(
"string_one",
"//ex:SecondParentFeature[@gml:id='cc.2']/ex:nestedFeature[1]/ex:SimpleContent/ex:someAttribute",
doc);
assertXpathEvaluatesTo(
"string_two",
"//ex:SecondParentFeature[@gml:id='cc.2']/ex:nestedFeature[2]/ex:SimpleContent/ex:someAttribute",
doc);
assertXpathEvaluatesTo(
"string_three",
"//ex:SecondParentFeature[@gml:id='cc.2']/ex:nestedFeature[3]/ex:SimpleContent/ex:someAttribute",
doc);
}
@Test
public void testComplexTypeWithSimpleContentJSON() throws Exception {
testJsonRequest("ex:FirstParentFeature", "/test-data/FirstParentFeature.json");
}
/**
* Test content of GetFeature response.
*/
@Test
public void testGetFeatureContent() throws Exception {
Document doc = getAsDOM("wfs?request=GetFeature&version=1.1.0&typename=gsml:MappedFeature");
LOGGER.info("WFS GetFeature&typename=gsml:MappedFeature response:\n" + prettyString(doc));
assertXpathEvaluatesTo("4", "/wfs:FeatureCollection/@numberOfFeatures", doc);
assertXpathCount(4, "//gsml:MappedFeature", doc);
checkSchemaLocation(doc);
// mf1
{
String id = "mf1";
assertXpathEvaluatesTo(id, "(//gsml:MappedFeature)[1]/@gml:id", doc);
checkMf1Content(id, doc);
}
// mf2
{
String id = "mf2";
assertXpathEvaluatesTo(id, "(//gsml:MappedFeature)[2]/@gml:id", doc);
checkMf2Content(id, doc);
}
// mf3
{
String id = "mf3";
assertXpathEvaluatesTo(id, "(//gsml:MappedFeature)[3]/@gml:id", doc);
checkMf3Content(id, doc);
}
// mf4
{
String id = "mf4";
assertXpathEvaluatesTo(id, "(//gsml:MappedFeature)[4]/@gml:id", doc);
checkMf4Content(id, doc);
}
// check for duplicate gml:id
assertXpathCount(1, "//gsml:GeologicUnit[@gml:id='gu.25678']", doc);
}
/**
* Check schema location
* @param doc
*/
private void checkSchemaLocation(Document doc) {
String schemaLocation = evaluate("/wfs:FeatureCollection/@xsi:schemaLocation", doc);
String gsmlLocation = AbstractAppSchemaMockData.GSML_URI + " "
+ AbstractAppSchemaMockData.GSML_SCHEMA_LOCATION_URL;
String wfsLocation = org.geoserver.wfs.xml.v1_1_0.WFS.NAMESPACE + " "
+ org.geoserver.wfs.xml.v1_1_0.WFS.CANONICAL_SCHEMA_LOCATION;
if (schemaLocation.startsWith(AbstractAppSchemaMockData.GSML_URI)) {
// GSML schema location was encoded first
assertEquals(gsmlLocation + " " + wfsLocation, schemaLocation);
} else {
// WFS schema location was encoded first
assertEquals(wfsLocation + " " + gsmlLocation, schemaLocation);
}
}
/**
* Check mf1 content are encoded correctly
* @param id
* @param doc
*/
private void checkMf1Content(String id, Document doc) {
assertXpathEvaluatesTo("GUNTHORPE FORMATION", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gml:name", doc);
// positionalAccuracy
assertXpathEvaluatesTo("200.0", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:positionalAccuracy/gsml:CGI_NumericValue/gsml:principalValue", doc);
assertXpathEvaluatesTo("urn:ogc:def:uom:UCUM:m", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:positionalAccuracy/gsml:CGI_NumericValue/gsml:principalValue/@uom",
doc);
// shape
assertXpathEvaluatesTo("urn:x-ogc:def:crs:EPSG:4326", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:shape/gml:Polygon/@srsName", doc);
assertXpathEvaluatesTo("52.5 -1.2 52.6 -1.2 52.6 -1.1 52.5 -1.1 52.5 -1.2",
"//gsml:MappedFeature[@gml:id='" + id + "']/gsml:shape//gml:posList", doc);
// specification gu.25699
assertXpathEvaluatesTo("gu.25699", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/@gml:id", doc);
// description
assertXpathEvaluatesTo("Olivine basalt, tuff, microgabbro, minor sedimentary rocks",
"//gsml:MappedFeature[@gml:id='" + id + "']/gsml:specification"
+ "/gsml:GeologicUnit/gml:description", doc);
// name
assertXpathCount(2, "//gsml:MappedFeature[@gml:id='" + id + "']/gsml:specification"
+ "/gsml:GeologicUnit/gml:name", doc);
assertXpathEvaluatesTo("Yaugher Volcanic Group", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification"
+ "/gsml:GeologicUnit/gml:name[@codeSpace='urn:ietf:rfc:2141']", doc);
// multi-valued leaf attributes that are feature chained come in random order
// when joining is used
List<String> names = new ArrayList<String>();
names.add("Yaugher Volcanic Group");
names.add("-Py");
String name = evaluate("//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/gml:name[1]", doc);
assertTrue(names.contains(name));
names.remove(name);
name = evaluate("//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/gml:name[2]", doc);
assertTrue(names.contains(name));
names.remove(name);
assertTrue(names.isEmpty());
// feature link shouldn't appear as it's not in the schema
assertXpathCount(0, "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/FEATURE_LINK", doc);
// occurrence
assertXpathCount(1, "//gsml:MappedFeature[@gml:id='" + id + "']/gsml:specification"
+ "/gsml:GeologicUnit/gsml:occurrence", doc);
assertXpathEvaluatesTo("", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification" + "/gsml:GeologicUnit/gsml:occurrence[1]", doc);
assertXpathEvaluatesTo("urn:cgi:feature:MappedFeature:mf1",
"//gsml:MappedFeature[@gml:id='" + id + "']/gsml:specification"
+ "/gsml:GeologicUnit/gsml:occurrence/@xlink:href", doc);
// exposureColor
assertXpathCount(1, "//gsml:MappedFeature[@gml:id='" + id + "']/gsml:specification"
+ "/gsml:GeologicUnit/gsml:exposureColor", doc);
assertXpathEvaluatesTo("Blue", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/gsml:exposureColor"
+ "/gsml:CGI_TermValue/gsml:value", doc);
assertXpathEvaluatesTo(
"some:uri",
"//gsml:MappedFeature[@gml:id='"
+ id
+ "']/gsml:specification/gsml:GeologicUnit/gsml:exposureColor/gsml:CGI_TermValue/gsml:value/@codeSpace",
doc);
// feature link shouldn't appear as it's not in the schema
assertXpathCount(0, "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/gsml:exposureColor"
+ "/gsml:CGI_TermValue/FEATURE_LINK", doc);
// outcropCharacter
assertXpathCount(1, "//gsml:MappedFeature[@gml:id='" + id + "']/gsml:specification"
+ "/gsml:GeologicUnit/gsml:outcropCharacter", doc);
assertXpathEvaluatesTo("x", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/gsml:outcropCharacter"
+ "/gsml:CGI_TermValue/gsml:value", doc);
// feature link shouldn't appear as it's not in the schema
assertXpathCount(0, "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/gsml:outcropCharacter"
+ "/gsml:CGI_TermValue/FEATURE_LINK", doc);
// composition
assertXpathCount(1, "//gsml:MappedFeature[@gml:id='" + id + "']/gsml:specification"
+ "/gsml:GeologicUnit/gsml:composition", doc);
assertXpathEvaluatesTo("nonexistent", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/gsml:composition"
+ "/gsml:CompositionPart/gsml:proportion/gsml:CGI_TermValue/gsml:value", doc);
assertXpathEvaluatesTo("fictitious component", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/gsml:composition"
+ "/gsml:CompositionPart/gsml:role", doc);
// feature link shouldn't appear as it's not in the schema
assertXpathCount(0, "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/gsml:composition"
+ "/gsml:CompositionPart/gsml:role/FEATURE_LINK", doc);
// lithology
assertXpathCount(1, "//gsml:MappedFeature[@gml:id='" + id + "']/gsml:specification"
+ "/gsml:GeologicUnit/gsml:composition/gsml:CompositionPart/gsml:lithology",
doc);
// feature link shouldn't appear as it's not in the schema
assertXpathCount(0, "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/gsml:composition"
+ "/gsml:CompositionPart/gsml:lithology/FEATURE_LINK", doc);
}
/**
* Check mf2 content are encoded correctly
* @param id
* @param doc
*/
private void checkMf2Content(String id, Document doc) {
assertXpathEvaluatesTo("MERCIA MUDSTONE GROUP", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gml:name", doc);
// positionalAccuracy
assertXpathEvaluatesTo("100.0", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:positionalAccuracy/gsml:CGI_NumericValue/gsml:principalValue", doc);
assertXpathEvaluatesTo("urn:ogc:def:uom:UCUM:m", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:positionalAccuracy/gsml:CGI_NumericValue/gsml:principalValue/@uom",
doc);
// shape
assertXpathEvaluatesTo("urn:x-ogc:def:crs:EPSG:4326", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:shape/gml:Polygon/@srsName", doc);
assertXpathEvaluatesTo("52.5 -1.3 52.6 -1.3 52.6 -1.2 52.5 -1.2 52.5 -1.3",
"//gsml:MappedFeature[@gml:id='" + id + "']/gsml:shape//gml:posList", doc);
// gu.25678
assertXpathEvaluatesTo("gu.25678", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/@gml:id", doc);
// name
assertXpathCount(3, "//gsml:MappedFeature[@gml:id='" + id + "']/gsml:specification"
+ "/gsml:GeologicUnit/gml:name", doc);
// multi-valued leaf attributes that are feature chained come in random order
// when joining is used
HashMap<String, String> names = new HashMap<String, String>();
names.put("Yaugher Volcanic Group 1", "urn:ietf:rfc:2141");
names.put("Yaugher Volcanic Group 2", "urn:ietf:rfc:2141");
names.put("-Py", "");
String name = evaluate("//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/gml:name[1]", doc);
assertTrue(names.containsKey(name));
assertXpathEvaluatesTo(names.get(name), "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/gml:name[1]/@codeSpace", doc);
names.remove(name);
name = evaluate("//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/gml:name[2]", doc);
assertTrue(names.containsKey(name));
assertXpathEvaluatesTo(names.get(name), "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/gml:name[2]/@codeSpace", doc);
names.remove(name);
name = evaluate("//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/gml:name[3]", doc);
assertTrue(names.containsKey(name));
assertXpathEvaluatesTo(names.get(name), "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/gml:name[3]/@codeSpace", doc);
names.remove(name);
assertTrue(names.isEmpty());
assertXpathCount(0, "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/FEATURE_LINK", doc);
// occurrence
assertXpathCount(2, "//gsml:MappedFeature[@gml:id='" + id + "']/gsml:specification"
+ "/gsml:GeologicUnit/gsml:occurrence", doc);
assertXpathEvaluatesTo("", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification" + "/gsml:GeologicUnit/gsml:occurrence[1]", doc);
assertXpathEvaluatesTo("urn:cgi:feature:MappedFeature:mf2",
"//gsml:MappedFeature[@gml:id='" + id + "']/gsml:specification"
+ "/gsml:GeologicUnit/gsml:occurrence[1]/@xlink:href", doc);
assertXpathEvaluatesTo("", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification" + "/gsml:GeologicUnit/gsml:occurrence[2]", doc);
assertXpathEvaluatesTo("urn:cgi:feature:MappedFeature:mf3",
"//gsml:MappedFeature[@gml:id='" + id + "']/gsml:specification"
+ "/gsml:GeologicUnit/gsml:occurrence[2]/@xlink:href", doc);
// description
assertXpathEvaluatesTo("Olivine basalt, tuff, microgabbro, minor sedimentary rocks",
"//gsml:MappedFeature[@gml:id='" + id + "']/gsml:specification"
+ "/gsml:GeologicUnit/gml:description", doc);
// exposureColor
assertXpathCount(2, "//gsml:MappedFeature[@gml:id='" + id + "']/gsml:specification"
+ "/gsml:GeologicUnit/gsml:exposureColor", doc);
assertXpathEvaluatesTo("Blue", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/gsml:exposureColor[1]"
+ "/gsml:CGI_TermValue/gsml:value", doc);
assertXpathEvaluatesTo(
"some:uri",
"//gsml:MappedFeature[@gml:id='"
+ id
+ "']/gsml:specification/gsml:GeologicUnit/gsml:exposureColor[1]/gsml:CGI_TermValue/gsml:value/@codeSpace",
doc);
assertXpathEvaluatesTo("Yellow", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/gsml:exposureColor[2]"
+ "/gsml:CGI_TermValue/gsml:value", doc);
assertXpathEvaluatesTo(
"some:uri",
"//gsml:MappedFeature[@gml:id='"
+ id
+ "']/gsml:specification/gsml:GeologicUnit/gsml:exposureColor[2]/gsml:CGI_TermValue/gsml:value/@codeSpace",
doc);
assertXpathCount(0, "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/gsml:exposureColor"
+ "/gsml:CGI_TermValue/FEATURE_LINK", doc);
// outcropCharacter
assertXpathCount(2, "//gsml:MappedFeature[@gml:id='" + id + "']/gsml:specification"
+ "/gsml:GeologicUnit/gsml:outcropCharacter", doc);
assertXpathEvaluatesTo("x", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/gsml:outcropCharacter[1]"
+ "/gsml:CGI_TermValue/gsml:value", doc);
assertXpathEvaluatesTo("y", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/gsml:outcropCharacter[2]"
+ "/gsml:CGI_TermValue/gsml:value", doc);
assertXpathCount(0, "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/gsml:outcropCharacter"
+ "/gsml:CGI_TermValue/FEATURE_LINK", doc);
// composition
assertXpathCount(2, "//gsml:MappedFeature[@gml:id='" + id + "']/gsml:specification"
+ "/gsml:GeologicUnit/gsml:composition", doc);
assertXpathEvaluatesTo("significant", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/gsml:composition[1]"
+ "/gsml:CompositionPart/gsml:proportion/gsml:CGI_TermValue/gsml:value", doc);
assertXpathEvaluatesTo("interbedded component", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification"
+ "/gsml:GeologicUnit[@gml:id='gu.25678']/gsml:composition[1]"
+ "/gsml:CompositionPart/gsml:role", doc);
assertXpathCount(0, "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/gsml:composition[1]"
+ "/gsml:CompositionPart/gsml:role/FEATURE_LINK", doc);
assertXpathEvaluatesTo("minor", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/gsml:composition[2]"
+ "/gsml:CompositionPart/gsml:proportion/gsml:CGI_TermValue/gsml:value", doc);
assertXpathEvaluatesTo("interbedded component", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/gsml:composition[2]"
+ "/gsml:CompositionPart/gsml:role", doc);
assertXpathCount(0, "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/gsml:composition[2]"
+ "/gsml:CompositionPart/gsml:role/FEATURE_LINK", doc);
// lithology
assertXpathCount(2, "//gsml:MappedFeature[@gml:id='" + id + "']/gsml:specification"
+ "/gsml:GeologicUnit/gsml:composition/gsml:CompositionPart/gsml:lithology",
doc);
assertXpathCount(0, "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/gsml:composition"
+ "/gsml:CompositionPart/gsml:lithology/FEATURE_LINK", doc);
}
/**
* Check mf3 content are encoded correctly
* @param id
* @param doc
*/
private void checkMf3Content(String id, Document doc) {
assertXpathEvaluatesTo("CLIFTON FORMATION", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gml:name", doc);
// positionalAccuracy
assertXpathEvaluatesTo("150.0", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:positionalAccuracy/gsml:CGI_NumericValue/gsml:principalValue", doc);
assertXpathEvaluatesTo("urn:ogc:def:uom:UCUM:m", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:positionalAccuracy/gsml:CGI_NumericValue/gsml:principalValue/@uom",
doc);
// shape
assertXpathEvaluatesTo("urn:x-ogc:def:crs:EPSG:4326", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:shape/gml:Polygon/@srsName", doc);
assertXpathEvaluatesTo("52.5 -1.2 52.6 -1.2 52.6 -1.1 52.5 -1.1 52.5 -1.2",
"//gsml:MappedFeature[@gml:id='" + id + "']/gsml:shape//gml:posList", doc);
// gu.25678
assertXpathEvaluatesTo("#gu.25678", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/@xlink:href", doc);
// make sure nothing else is encoded
assertXpathCount(0, "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit", doc);
}
/**
* Check mf4 content are encoded correctly
* @param id
* @param doc
*/
private void checkMf4Content(String id, Document doc) {
assertXpathEvaluatesTo("MURRADUC BASALT", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gml:name", doc);
// positionalAccuracy
assertXpathEvaluatesTo("120.0", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:positionalAccuracy/gsml:CGI_NumericValue/gsml:principalValue", doc);
assertXpathEvaluatesTo("urn:ogc:def:uom:UCUM:m", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:positionalAccuracy/gsml:CGI_NumericValue/gsml:principalValue/@uom",
doc);
// shape
assertXpathEvaluatesTo("urn:x-ogc:def:crs:EPSG:4326", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:shape/gml:Polygon/@srsName", doc);
assertXpathEvaluatesTo("52.5 -1.3 52.6 -1.3 52.6 -1.2 52.5 -1.2 52.5 -1.3",
"//gsml:MappedFeature[@gml:id='" + id + "']/gsml:shape//gml:posList", doc);
// gu.25682
assertXpathEvaluatesTo("gu.25682", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/@gml:id", doc);
// description
assertXpathEvaluatesTo("Olivine basalt", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/gml:description", doc);
// name
assertXpathCount(2, "//gsml:MappedFeature[@gml:id='" + id + "']/gsml:specification"
+ "/gsml:GeologicUnit/gml:name", doc);
assertXpathEvaluatesTo("New Group", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification"
+ "/gsml:GeologicUnit/gml:name[@codeSpace='urn:ietf:rfc:2141']", doc);
List<String> names = new ArrayList<String>();
names.add("New Group");
names.add("-Xy");
String name = evaluate("//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/gml:name[1]", doc);
assertTrue(names.contains(name));
names.remove(name);
name = evaluate("//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/gml:name[2]", doc);
assertTrue(names.contains(name));
names.remove(name);
assertTrue(names.isEmpty());
assertXpathCount(0, "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/FEATURE_LINK", doc);
// occurrence
assertXpathCount(1, "//gsml:MappedFeature[@gml:id='" + id + "']/gsml:specification"
+ "/gsml:GeologicUnit/gsml:occurrence", doc);
assertXpathEvaluatesTo("", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification" + "/gsml:GeologicUnit/gsml:occurrence[1]", doc);
assertXpathEvaluatesTo("urn:cgi:feature:MappedFeature:mf4",
"//gsml:MappedFeature[@gml:id='" + id + "']/gsml:specification"
+ "/gsml:GeologicUnit/gsml:occurrence/@xlink:href", doc);
// exposureColor
assertXpathCount(1, "//gsml:MappedFeature[@gml:id='" + id + "']/gsml:specification"
+ "/gsml:GeologicUnit/gsml:exposureColor", doc);
assertXpathEvaluatesTo(
"some:uri",
"//gsml:MappedFeature[@gml:id='"
+ id
+ "']/gsml:specification/gsml:GeologicUnit/gsml:exposureColor/gsml:CGI_TermValue/gsml:value/@codeSpace",
doc);
assertXpathEvaluatesTo("Red", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/gsml:exposureColor"
+ "/gsml:CGI_TermValue/gsml:value", doc);
assertXpathCount(0, "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/gsml:exposureColor"
+ "/gsml:CGI_TermValue/FEATURE_LINK", doc);
// outcropCharacter
assertXpathCount(1, "//gsml:MappedFeature[@gml:id='" + id + "']/gsml:specification"
+ "/gsml:GeologicUnit/gsml:outcropCharacter", doc);
assertXpathEvaluatesTo("z", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/gsml:outcropCharacter"
+ "/gsml:CGI_TermValue/gsml:value", doc);
assertXpathCount(0, "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/gsml:outcropCharacter"
+ "/gsml:CGI_TermValue/FEATURE_LINK", doc);
// composition
assertXpathCount(1, "//gsml:MappedFeature[@gml:id='" + id + "']/gsml:specification"
+ "/gsml:GeologicUnit/gsml:composition", doc);
assertXpathEvaluatesTo("significant", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/gsml:composition"
+ "/gsml:CompositionPart/gsml:proportion/gsml:CGI_TermValue/gsml:value", doc);
assertXpathEvaluatesTo("interbedded component", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/gsml:composition"
+ "/gsml:CompositionPart/gsml:role", doc);
assertXpathCount(0, "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/gsml:composition"
+ "/gsml:CompositionPart/gsml:role/FEATURE_LINK", doc);
// lithology
assertXpathCount(2, "//gsml:MappedFeature[@gml:id='" + id + "']/gsml:specification"
+ "/gsml:GeologicUnit/gsml:composition/gsml:CompositionPart/gsml:lithology",
doc);
// lithology:1
assertXpathEvaluatesTo("cc.1", "//gsml:MappedFeature[@gml:id='" + id + "']/gsml:specification"
+ "/gsml:GeologicUnit/gsml:composition/gsml:CompositionPart/gsml:lithology[1]"
+ "/gsml:ControlledConcept/@gml:id", doc);
assertXpathCount(3, "//gsml:MappedFeature[@gml:id='" + id + "']/gsml:specification"
+ "/gsml:GeologicUnit/gsml:composition/gsml:CompositionPart/gsml:lithology[1]"
+ "/gsml:ControlledConcept/gml:name", doc);
// Order depends on what the database returns in case of joining queries
names = new ArrayList<String>();
names.add("name_a");
names.add("name_b");
names.add("name_c");
name = evaluate("//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit"
+ "/gsml:composition/gsml:CompositionPart/gsml:lithology[1]"
+ "/gsml:ControlledConcept/gml:name[1]", doc);
assertTrue(names.contains(name));
names.remove(name);
name = evaluate("//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit"
+ "/gsml:composition/gsml:CompositionPart/gsml:lithology[1]"
+ "/gsml:ControlledConcept/gml:name[2]", doc);
assertTrue(names.contains(name));
names.remove(name);
name = evaluate("//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit"
+ "/gsml:composition/gsml:CompositionPart/gsml:lithology[1]"
+ "/gsml:ControlledConcept/gml:name[3]", doc);
assertTrue(names.contains(name));
names.remove(name);
assertXpathCount(0, "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/gsml:composition"
+ "/gsml:CompositionPart/gsml:lithology[1]/FEATURE_LINK", doc);
// lithology:2
assertXpathEvaluatesTo("cc.2", "//gsml:MappedFeature[@gml:id='" + id + "']/gsml:specification"
+ "/gsml:GeologicUnit/gsml:composition/gsml:CompositionPart/gsml:lithology[2]/"
+ "/gsml:ControlledConcept/@gml:id", doc);
assertXpathCount(1, "//gsml:MappedFeature[@gml:id='" + id + "']/gsml:specification"
+ "/gsml:GeologicUnit/gsml:composition/gsml:CompositionPart/gsml:lithology[2]"
+ "/gsml:ControlledConcept/gml:name", doc);
assertXpathEvaluatesTo("name_2", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit"
+ "/gsml:composition/gsml:CompositionPart/gsml:lithology[2]"
+ "/gsml:ControlledConcept/gml:name", doc);
assertXpathCount(0, "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/gsml:composition"
+ "/gsml:CompositionPart/gsml:lithology[2]/FEATURE_LINK", doc);
}
/**
* Implementation for tests expected to get mf4 only.
*
* @param xml
*/
private void checkGetMf4Only(String xml) {
Document doc = postAsDOM("wfs", xml);
LOGGER.info("WFS filter GetFeature response:\n" + prettyString(doc));
assertEquals("wfs:FeatureCollection", doc.getDocumentElement().getNodeName());
assertXpathEvaluatesTo("1", "/wfs:FeatureCollection/@numberOfFeatures", doc);
assertXpathCount(1, "//gsml:MappedFeature", doc);
// mf4
{
String id = "mf4";
assertXpathEvaluatesTo(id, "//gsml:MappedFeature[1]/@gml:id", doc);
assertXpathEvaluatesTo("MURRADUC BASALT", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gml:name", doc);
// gu.25682
assertXpathEvaluatesTo("gu.25682", "//gsml:MappedFeature[@gml:id='" + id
+ "']/gsml:specification/gsml:GeologicUnit/@gml:id", doc);
}
}
/**
* Test if we can get mf4 by its name.
*/
@Test
public void testGetFeaturePropertyFilter() {
String xml = //
"<wfs:GetFeature " //
+ GETFEATURE_ATTRIBUTES //
+ ">" //
+ " <wfs:Query typeName=\"gsml:MappedFeature\">" //
+ " <ogc:Filter>" //
+ " <ogc:PropertyIsEqualTo>" //
+ " <ogc:PropertyName>gml:name</ogc:PropertyName>" //
+ " <ogc:Literal>MURRADUC BASALT</ogc:Literal>" //
+ " </ogc:PropertyIsEqualTo>" //
+ " </ogc:Filter>" //
+ " </wfs:Query> " //
+ "</wfs:GetFeature>";
validate(xml);
checkGetMf4Only(xml);
}
/**
* Test if we can get mf4 with a FeatureId fid filter.
*/
@Test
public void testGetFeatureWithFeatureIdFilter() {
String xml = //
"<wfs:GetFeature " //
+ GETFEATURE_ATTRIBUTES //
+ ">" //
+ " <wfs:Query typeName=\"gsml:MappedFeature\">" //
+ " <ogc:Filter>" //
+ " <ogc:FeatureId fid=\"mf4\"/>" //
+ " </ogc:Filter>" //
+ " </wfs:Query> " //
+ "</wfs:GetFeature>";
validate(xml);
checkGetMf4Only(xml);
}
/**
* Test if we can get mf4 with a GmlObjectId gml:id filter.
*/
@Test
public void testGetFeatureWithGmlObjectIdFilter() {
String xml = //
"<wfs:GetFeature " //
+ GETFEATURE_ATTRIBUTES //
+ ">" //
+ " <wfs:Query typeName=\"gsml:MappedFeature\">" //
+ " <ogc:Filter>" //
+ " <ogc:GmlObjectId gml:id=\"mf4\"/>" //
+ " </ogc:Filter>" //
+ " </wfs:Query> " //
+ "</wfs:GetFeature>";
validate(xml);
checkGetMf4Only(xml);
}
/**
* Test anyType as complex attributes, and placeholder type (e.g AnyOrReference) which contains
* <any/> element.
*/
@Test
public void testAnyTypeAndAnyElementGML() {
final String OBSERVATION_ID_PREFIX = "observation:";
Document doc = getAsDOM("wfs?request=GetFeature&version=1.1.0&typename=om:Observation");
LOGGER.info("WFS GetFeature&typename=om:Observation response:\n" + prettyString(doc));
assertXpathEvaluatesTo("4", "/wfs:FeatureCollection/@numberOfFeatures", doc);
assertXpathCount(4, "//om:Observation", doc);
String id = "mf1";
assertXpathEvaluatesTo(OBSERVATION_ID_PREFIX + id, "(//om:Observation)[1]/@gml:id", doc);
// om:metadata
assertXpathEvaluatesTo("651.0",
"(//om:Observation)[1]/om:metadata/gsml:CGI_NumericValue/gsml:principalValue", doc);
// om:resultQuality
Node resultQuality = doc.getElementsByTagName("om:resultQuality").item(0);
Node geologicUnit = resultQuality.getFirstChild();
assertEquals("gu.25699", geologicUnit.getAttributes().getNamedItem("gml:id").getNodeValue());
// om:result
assertXpathEvaluatesTo("", "(//om:Observation)[1]/om:result/text()", doc);
assertXpathEvaluatesTo(id, "(//om:Observation)[1]/om:result/gsml:MappedFeature/@gml:id",
doc);
id = "mf2";
assertXpathEvaluatesTo(OBSERVATION_ID_PREFIX + id, "(//om:Observation)[2]/@gml:id", doc);
// om:metadata
assertXpathEvaluatesTo("269.0",
"(//om:Observation)[2]/om:metadata/gsml:CGI_NumericValue/gsml:principalValue", doc);
// om:resultQuality
resultQuality = doc.getElementsByTagName("om:resultQuality").item(1);
geologicUnit = resultQuality.getFirstChild();
assertEquals("gu.25678", geologicUnit.getAttributes().getNamedItem("gml:id").getNodeValue());
// om:result
assertXpathEvaluatesTo("", "(//om:Observation)[2]/om:result/text()", doc);
assertXpathEvaluatesTo(id, "(//om:Observation)[2]/om:result/gsml:MappedFeature/@gml:id",
doc);
id = "mf3";
assertXpathEvaluatesTo(OBSERVATION_ID_PREFIX + id, "(//om:Observation)[3]/@gml:id", doc);
// om:metadata
assertXpathEvaluatesTo("123.0",
"(//om:Observation)[3]/om:metadata/gsml:CGI_NumericValue/gsml:principalValue", doc);
// om:resultQuality
resultQuality = doc.getElementsByTagName("om:resultQuality").item(2);
assertEquals("#gu.25678", resultQuality.getAttributes().getNamedItem("xlink:href")
.getNodeValue());
// om:result
assertXpathEvaluatesTo("", "(//om:Observation)[3]/om:result/text()", doc);
assertXpathEvaluatesTo(id, "(//om:Observation)[3]/om:result/gsml:MappedFeature/@gml:id",
doc);
id = "mf4";
assertXpathEvaluatesTo(OBSERVATION_ID_PREFIX + id, "(//om:Observation)[4]/@gml:id", doc);
// om:metadata
assertXpathEvaluatesTo("456.0",
"(//om:Observation)[4]/om:metadata/gsml:CGI_NumericValue/gsml:principalValue", doc);
// om:resultQuality
resultQuality = doc.getElementsByTagName("om:resultQuality").item(3);
geologicUnit = resultQuality.getFirstChild();
assertEquals("gu.25682", geologicUnit.getAttributes().getNamedItem("gml:id").getNodeValue());
// om:result
assertXpathEvaluatesTo("", "(//om:Observation)[4]/om:result/text()", doc);
assertXpathEvaluatesTo(id, "(//om:Observation)[4]/om:result/gsml:MappedFeature/@gml:id",
doc);
}
@Test
public void testAnyTypeAndAnyElementJSON() throws Exception {
testJsonRequest("om:Observation", "/test-data/Observation.json");
}
/**
* Making sure attributes that are encoded as xlink:href can still be queried in filters.
*/
@Test
public void testFilteringXlinkHref() {
String xml = //
"<wfs:GetFeature " //
+ GETFEATURE_ATTRIBUTES //
+ ">" //
+ " <wfs:Query typeName=\"gsml:MappedFeature\">" //
+ " <ogc:Filter>" //
+ " <ogc:PropertyIsEqualTo>" //
+ " <ogc:PropertyName>gsml:specification/gsml:GeologicUnit/gml:name</ogc:PropertyName>" //
+ " <ogc:Literal>Yaugher Volcanic Group</ogc:Literal>" //
+ " </ogc:PropertyIsEqualTo>" //
+ " </ogc:Filter>" //
+ " </wfs:Query> " //
+ "</wfs:GetFeature>";
validate(xml);
Document doc = postAsDOM("wfs", xml);
LOGGER.info("WFS filter GetFeature response:\n" + prettyString(doc));
assertEquals("wfs:FeatureCollection", doc.getDocumentElement().getNodeName());
// there should be 1:
// - mf1/gu.25699
assertXpathEvaluatesTo("1", "/wfs:FeatureCollection/@numberOfFeatures", doc);
assertXpathCount(1, "//gsml:MappedFeature", doc);
assertXpathEvaluatesTo("mf1", "//gsml:MappedFeature/@gml:id", doc);
//TODO: This test case no longer serves its purpose. It should be changed to below, but it fails at the moment.
// String xml = //
// "<wfs:GetFeature " //
// + GETFEATURE_ATTRIBUTES //
// + ">" //
// + " <wfs:Query typeName=\"gsml:MappedFeature\">" //
// + " <ogc:Filter>" //
// + " <ogc:PropertyIsEqualTo>" //
// + " <ogc:PropertyName>gsml:specification/gsml:GeologicUnit/gml:name</ogc:PropertyName>" //
// + " <ogc:Literal>Yaugher Volcanic Group 2</ogc:Literal>" //
// + " </ogc:PropertyIsEqualTo>" //
// + " </ogc:Filter>" //
// + " </wfs:Query> " //
// + "</wfs:GetFeature>";
// validate(xml);
// Document doc = postAsDOM("wfs", xml);
// LOGGER.info("WFS filter GetFeature response:\n" + prettyString(doc));
// assertEquals("wfs:FeatureCollection", doc.getDocumentElement().getNodeName());
// // there should be 2:
// // - mf2/gu.25678
// // - mf3/gu.25678
// assertXpathEvaluatesTo("2", "/wfs:FeatureCollection/@numberOfFeatures", doc);
// assertXpathCount(2, "//gsml:MappedFeature", doc);
// assertXpathEvaluatesTo("mf2", "//gsml:MappedFeature[1]/@gml:id", doc);
// assertXpathEvaluatesTo("mf3", "//gsml:MappedFeature[2]/@gml:id", doc);
}
/**
* Making sure multi-valued attributes in nested features can be queried from the top level. (GEOT-3156)
*/
@Test
public void testFilteringNestedMultiValuedAttribute() {
// PropertyIsEqual
String xml =
"<wfs:GetFeature "
+ GETFEATURE_ATTRIBUTES
+ ">"
+ " <wfs:Query typeName=\"gsml:MappedFeature\">"
+ " <ogc:Filter>"
+ " <ogc:PropertyIsEqualTo>"
+ " <ogc:Literal>Yaugher Volcanic Group 2</ogc:Literal>"
+ " <ogc:PropertyName>gsml:specification/gsml:GeologicUnit/gml:name</ogc:PropertyName>"
+ " </ogc:PropertyIsEqualTo>"
+ " </ogc:Filter>"
+ " </wfs:Query> "
+ "</wfs:GetFeature>";
validate(xml);
Document doc = postAsDOM("wfs", xml);
LOGGER.info("WFS filter GetFeature response:\n" + prettyString(doc));
assertEquals("wfs:FeatureCollection", doc.getDocumentElement().getNodeName());
// there should be 2:
// - mf2/gu.25678
// - mf3/gu.25678
assertXpathEvaluatesTo("2", "/wfs:FeatureCollection/@numberOfFeatures", doc);
assertXpathCount(2, "//gsml:MappedFeature", doc);
assertXpathEvaluatesTo("mf2", "(//gsml:MappedFeature)[1]/@gml:id", doc);
assertXpathEvaluatesTo("mf3", "(//gsml:MappedFeature)[2]/@gml:id", doc);
// PropertyIsLike
xml = //
"<wfs:GetFeature "
+ GETFEATURE_ATTRIBUTES
+ ">"
+ " <wfs:Query typeName=\"gsml:MappedFeature\">"
+ " <ogc:Filter>"
+ " <ogc:PropertyIsLike wildCard=\"*\" singleChar=\"#\" escapeChar=\"!\">"
+ " <ogc:PropertyName>gsml:specification/gsml:GeologicUnit/gml:name</ogc:PropertyName>"
+ " <ogc:Literal>Yaugher Volcanic Group*</ogc:Literal>"
+ " </ogc:PropertyIsLike>"
+ " </ogc:Filter>"
+ " </wfs:Query> "
+ "</wfs:GetFeature>";
validate(xml);
doc = postAsDOM("wfs", xml);
LOGGER.info("WFS filter GetFeature response:\n" + prettyString(doc));
assertEquals("wfs:FeatureCollection", doc.getDocumentElement().getNodeName());
// there should be 3:
// - mf1/gu.25699
// - mf2/gu.25678
// - mf3/gu.25678
assertXpathEvaluatesTo("3", "/wfs:FeatureCollection/@numberOfFeatures", doc);
assertXpathCount(3, "//gsml:MappedFeature", doc);
assertXpathEvaluatesTo("mf1", "(//gsml:MappedFeature)[1]/@gml:id", doc);
assertXpathEvaluatesTo("mf2", "(//gsml:MappedFeature)[2]/@gml:id", doc);
assertXpathEvaluatesTo("mf3", "(//gsml:MappedFeature)[3]/@gml:id", doc);
}
/**
* Similar to above test case but using AND as a wrapper for 2 filters involving nested
* attributes.
*/
@Test
public void testFilterAnd() {
String xml = "<wfs:GetFeature "
+ GETFEATURE_ATTRIBUTES
+ ">"
+ "<wfs:Query typeName=\"gsml:MappedFeature\">"
+ " <ogc:Filter>"
+ " <ogc:And>"
+ " <ogc:PropertyIsEqualTo>"
+ " <ogc:Literal>significant</ogc:Literal>"
+ " <ogc:PropertyName>gsml:specification/gsml:GeologicUnit/gsml:composition/gsml:CompositionPart/gsml:proportion/gsml:CGI_TermValue/gsml:value</ogc:PropertyName>"
+ " </ogc:PropertyIsEqualTo>"
+ " <ogc:PropertyIsEqualTo>"
+ " <ogc:Literal>New Group</ogc:Literal>"
+ " <ogc:PropertyName>gsml:specification/gsml:GeologicUnit/gml:name</ogc:PropertyName>"
+ " </ogc:PropertyIsEqualTo>"
+ " </ogc:And>"
+ " </ogc:Filter>"
+ "</wfs:Query> "
+ "</wfs:GetFeature>";
validate(xml);
Document doc = postAsDOM("wfs", xml);
LOGGER.info("WFS filter GetFeature response:\n" + prettyString(doc));
assertEquals("wfs:FeatureCollection", doc.getDocumentElement().getNodeName());
assertXpathEvaluatesTo("1", "/wfs:FeatureCollection/@numberOfFeatures", doc);
assertXpathCount(1, "//gsml:MappedFeature", doc);
assertXpathEvaluatesTo("mf4", "//gsml:MappedFeature/@gml:id", doc);
}
/**
* Test that denormalized data reports the correct number of features
*/
@Test
public void testDenormalisedFeaturesCount() {
Document doc = getAsDOM("wfs?request=GetFeature&version=1.1.0&typename=gsml:GeologicUnit&maxFeatures=3");
LOGGER.info("WFS GetFeature&typename=gsml:GeologicUnit&maxFeatures=3 response:\n"
+ prettyString(doc));
assertXpathCount(3, "//gsml:GeologicUnit", doc);
// check that we get features we're expecting
String id = "gu.25678";
assertXpathEvaluatesTo(id, "(//gsml:GeologicUnit)[1]/@gml:id", doc);
id = "gu.25682";
assertXpathEvaluatesTo(id, "(//gsml:GeologicUnit)[2]/@gml:id", doc);
id = "gu.25699";
assertXpathEvaluatesTo(id, "(//gsml:GeologicUnit)[3]/@gml:id", doc);
}
/**
* Test FeatureCollection is encoded with multiple featureMember elements
*/
@Test
public void testEncodeFeatureMember() throws Exception {
// change fixture settings (must restore this at end)
WFSInfo wfs = getGeoServer().getService(WFSInfo.class);
boolean encodeFeatureMember = wfs.isEncodeFeatureMember();
wfs.setEncodeFeatureMember(true);
getGeoServer().save(wfs);
Document doc = getAsDOM("wfs?request=GetFeature&version=1.1.0&typename=gsml:MappedFeature,gsml:GeologicUnit");
LOGGER.info("WFS GetFeature&typename=gsml:MappedFeature,gsml:GeologicUnit response:\n"
+ prettyString(doc));
checkSchemaLocation(doc);
assertXpathEvaluatesTo("7", "/wfs:FeatureCollection/@numberOfFeatures", doc);
assertXpathCount(4, "//gsml:MappedFeature", doc);
assertEquals(7, doc.getElementsByTagName("gml:featureMember").getLength());
assertEquals(0, doc.getElementsByTagName("gml:featureMembers").getLength());
// mf1
{
String id = "mf1";
checkMf1Content(id, doc);
}
// mf2
{
String id = "mf2";
checkMf2Content(id, doc);
}
// mf3
{
String id = "mf3";
checkMf3Content(id, doc);
}
// mf4
{
String id = "mf4";
checkMf4Content(id, doc);
}
// check for duplicate gml:id
assertXpathCount(1, "//gsml:GeologicUnit[@gml:id='gu.25699']", doc);
assertXpathCount(1, "//gsml:GeologicUnit[@gml:id='gu.25678']", doc);
assertXpathCount(1, "//gsml:GeologicUnit[@gml:id='gu.25682']", doc);
// test for xlink:href is encoded within featureMember
assertXpathCount(1, "//gml:featureMember[@xlink:href='#gu.25699']", doc);
assertXpathCount(1, "//gml:featureMember[@xlink:href='#gu.25678']", doc);
assertXpathCount(1, "//gml:featureMember[@xlink:href='#gu.25682']", doc);
// restore fixture settings
wfs = getGeoServer().getService(WFSInfo.class);
wfs.setEncodeFeatureMember(encodeFeatureMember);
getGeoServer().save(wfs);
}
/**
* Test FeatureCollection is encoded with one featureMembers element
*/
@Test
public void testEncodeFeatureMembersGML() throws Exception {
// change fixture settings (must restore this at end)
WFSInfo wfs = getGeoServer().getService(WFSInfo.class);
boolean encodeFeatureMember = wfs.isEncodeFeatureMember();
wfs.setEncodeFeatureMember(false);
getGeoServer().save(wfs);
Document doc = getAsDOM("wfs?request=GetFeature&version=1.1.0&typename=gsml:MappedFeature,gsml:GeologicUnit");
LOGGER.info("WFS GetFeature&typename=gsml:MappedFeature,gsml:GeologicUnit response:\n"
+ prettyString(doc));
checkSchemaLocation(doc);
assertXpathEvaluatesTo("7", "/wfs:FeatureCollection/@numberOfFeatures", doc);
assertXpathCount(4, "//gsml:MappedFeature", doc);
assertEquals(1, doc.getElementsByTagName("gml:featureMembers").getLength());
assertEquals(0, doc.getElementsByTagName("gml:featureMember").getLength());
// mf1
{
String id = "mf1";
checkMf1Content(id, doc);
}
// mf2
{
String id = "mf2";
checkMf2Content(id, doc);
}
// mf3
{
String id = "mf3";
checkMf3Content(id, doc);
}
// mf4
{
String id = "mf4";
checkMf4Content(id, doc);
}
// check for duplicate gml:id
assertXpathCount(1, "//gsml:GeologicUnit[@gml:id='gu.25699']", doc);
assertXpathCount(1, "//gsml:GeologicUnit[@gml:id='gu.25678']", doc);
assertXpathCount(1, "//gsml:GeologicUnit[@gml:id='gu.25682']", doc);
// check for xlink:href if encoded within GeologicUnit itself
// note that this can never be schema-valid, but the best that the
// encoder can do when configured to use featureMembers.
assertXpathCount(1, "//gsml:GeologicUnit[@xlink:href='#gu.25699']", doc);
assertXpathCount(1, "//gsml:GeologicUnit[@xlink:href='#gu.25678']", doc);
assertXpathCount(1, "//gsml:GeologicUnit[@xlink:href='#gu.25682']", doc);
// restore fixture settings
wfs = getGeoServer().getService(WFSInfo.class);
wfs.setEncodeFeatureMember(encodeFeatureMember);
getGeoServer().save(wfs);
}
@Test
public void testEncodeFeatureMembersJSON() throws Exception {
testJsonRequest("gsml:MappedFeature,gsml:GeologicUnit", "/test-data/MultipleCollections.json");
}
@Test
public void testNestedFilterEncoding() throws FilterToSQLException, IOException {
FeatureTypeInfo ftInfo = getCatalog().getFeatureTypeByName("gsml", "MappedFeature");
FeatureSource fs = ftInfo.getFeatureSource(new NullProgressListener(), null);
AppSchemaDataAccess da = (AppSchemaDataAccess) fs.getDataStore();
FeatureTypeMapping rootMapping = da.getMappingByNameOrElement(ftInfo.getQualifiedName());
// make sure nested filters encoding is enabled, otherwise skip test
assumeTrue(shouldTestNestedFiltersEncoding(rootMapping));
JDBCDataStore store = (JDBCDataStore) rootMapping.getSource().getDataStore();
NestedFilterToSQL nestedFilterToSQL = createNestedFilterEncoder(rootMapping);
FilterFactoryImplNamespaceAware ff = new FilterFactoryImplNamespaceAware();
ff.setNamepaceContext(rootMapping.getNamespaces());
/*
* test combined filters on nested attributes
*/
And and = ff
.and(ff.equals(ff.property("gsml:specification/gsml:GeologicUnit/gml:name"),
ff.literal("New Group")),
ff.equals(
ff.property("gsml:specification/gsml:GeologicUnit/gsml:composition/gsml:CompositionPart/gsml:proportion/gsml:CGI_TermValue/gsml:value"),
ff.literal("significant")));
// Each filter involves a single nested attribute --> can be encoded
ComplexFilterSplitter splitter = new ComplexFilterSplitter(store.getFilterCapabilities(),
rootMapping);
splitter.visit(and, null);
Filter preFilter = splitter.getFilterPre();
Filter postFilter = splitter.getFilterPost();
assertEquals(and, preFilter);
assertEquals(Filter.INCLUDE, postFilter);
// filter must be "unrolled" (i.e. reverse mapped) first
Filter unrolled = AppSchemaDataAccess.unrollFilter(and, rootMapping);
// Filter is nested
assertTrue(NestedFilterToSQL.isNestedFilter(unrolled));
String encodedFilter = nestedFilterToSQL.encodeToString(unrolled);
assertTrue(encodedFilter.matches("^\\(EXISTS.*AND EXISTS.*\\)$"));
assertContainsFeatures(fs.getFeatures(and), "mf4");
/*
* test like filter on nested attribute
*/
PropertyIsLike like = ff.like(ff.property("gsml:specification/gsml:GeologicUnit/gml:description"), "*sedimentary*");
// Filter involving single nested attribute --> can be encoded
ComplexFilterSplitter splitterLike = new ComplexFilterSplitter(store.getFilterCapabilities(),
rootMapping);
splitterLike.visit(like, null);
preFilter = splitterLike.getFilterPre();
postFilter = splitterLike.getFilterPost();
assertEquals(like, preFilter);
assertEquals(Filter.INCLUDE, postFilter);
// filter must be "unrolled" (i.e. reverse mapped) first
unrolled = AppSchemaDataAccess.unrollFilter(like, rootMapping);
// Filter is nested
assertTrue(NestedFilterToSQL.isNestedFilter(unrolled));
encodedFilter = nestedFilterToSQL.encodeToString(unrolled);
// this is the generated query in PostGIS, but the test limits to check the presence of the
// EXISTS keyword, as the actual SQL is dependent on the underlying database
// EXISTS (SELECT "chain_link_1"."PKEY"
// FROM "appschematest"."GEOLOGICUNIT" "chain_link_1"
// WHERE UPPER("chain_link_1"."TEXTDESCRIPTION") LIKE '%SEDIMENTARY%' AND
// "appschematest"."MAPPEDFEATUREPROPERTYFILE"."GEOLOGIC_UNIT_ID" = "chain_link_1"."GML_ID")
assertTrue(encodedFilter.contains("EXISTS"));
assertContainsFeatures(fs.getFeatures(like), "mf1", "mf2", "mf3");
}
@Test
public void testNestedFilterEncodingDisabled() throws IOException, FilterToSQLException {
// disable nested filters encoding during the test
AppSchemaDataAccessRegistry.getAppSchemaProperties().setProperty(
AppSchemaDataAccessConfigurator.PROPERTY_ENCODE_NESTED_FILTERS, "false");
try {
assertFalse(AppSchemaDataAccessConfigurator.shouldEncodeNestedFilters());
FeatureTypeInfo ftInfo = getCatalog().getFeatureTypeByName("gsml", "MappedFeature");
FeatureSource fs = ftInfo.getFeatureSource(new NullProgressListener(), null);
AppSchemaDataAccess da = (AppSchemaDataAccess) fs.getDataStore();
FeatureTypeMapping rootMapping = da.getMappingByNameOrElement(ftInfo.getQualifiedName());
// skip test if it's not running against a database
assumeTrue(rootMapping.getSource().getDataStore() instanceof JDBCDataStore);
JDBCDataStore store = (JDBCDataStore) rootMapping.getSource().getDataStore();
NestedFilterToSQL nestedFilterToSQL = createNestedFilterEncoder(rootMapping);
FilterFactoryImplNamespaceAware ff = new FilterFactoryImplNamespaceAware();
ff.setNamepaceContext(rootMapping.getNamespaces());
/*
* test the same like filter tested in method testNestedFilterEncoding
*/
PropertyIsLike like = ff.like(ff.property("gsml:specification/gsml:GeologicUnit/gml:description"), "*sedimentary*");
// Encoding of filters involving nested attributes is disabled --> CANNOT be encoded
ComplexFilterSplitter splitterLike = new ComplexFilterSplitter(store.getFilterCapabilities(),
rootMapping);
splitterLike.visit(like, null);
Filter preFilter = splitterLike.getFilterPre();
Filter postFilter = splitterLike.getFilterPost();
assertEquals(Filter.INCLUDE, preFilter);
assertEquals(like, postFilter);
// filter must be "unrolled" (i.e. reverse mapped) first
Filter unrolled = AppSchemaDataAccess.unrollFilter(like, rootMapping);
// Filter is nested
assertTrue(NestedFilterToSQL.isNestedFilter(unrolled));
assertContainsFeatures(fs.getFeatures(like), "mf1", "mf2", "mf3");
} finally {
// reset default
AppSchemaDataAccessRegistry.getAppSchemaProperties().setProperty(
AppSchemaDataAccessConfigurator.PROPERTY_ENCODE_NESTED_FILTERS, "true");
}
}
/**
* Test the encoding of nested complex features that are mapped to GML complex types
* that don't respect the GML object-property model.
*/
@Test
public void testNonValidNestedGML() throws Exception {
// get the complex features encoded in GML
Document result = getAsDOM("wfs?request=GetFeature&version=1.1.0&typename=ex:ParentFeature");
// checking that we have the correct number of elements
assertXpathCount(3, "//ex:ParentFeature", result);
assertXpathCount(9, "//ex:ParentFeature/ex:nestedFeature", result);
assertXpathCount(9, "//ex:ParentFeature/ex:nestedFeature/ex:nestedValue", result);
// checking the content of the first feature
assertXpathCount(1, "//ex:ParentFeature[@gml:id='sc.1']/ex:parentValue[text()='string_one']", result);
assertXpathCount(1, "//ex:ParentFeature[@gml:id='sc.1']/ex:nestedFeature/ex:nestedValue[text()='1GRAV']", result);
assertXpathCount(1, "//ex:ParentFeature[@gml:id='sc.1']/ex:nestedFeature/ex:nestedValue[text()='1TILL']", result);
assertXpathCount(1, "//ex:ParentFeature[@gml:id='sc.1']/ex:nestedFeature/ex:nestedValue[text()='6ALLU']", result);
// checking the content of the second feature
assertXpathCount(1, "//ex:ParentFeature[@gml:id='sc.2']/ex:parentValue[text()='string_two']", result);
assertXpathCount(1, "//ex:ParentFeature[@gml:id='sc.2']/ex:nestedFeature/ex:nestedValue[text()='1GRAV']", result);
assertXpathCount(1, "//ex:ParentFeature[@gml:id='sc.2']/ex:nestedFeature/ex:nestedValue[text()='1TILL']", result);
assertXpathCount(1, "//ex:ParentFeature[@gml:id='sc.2']/ex:nestedFeature/ex:nestedValue[text()='6ALLU']", result);
// checking the content of the third feature
assertXpathCount(1, "//ex:ParentFeature[@gml:id='sc.3']/ex:parentValue[text()='string_three']", result);
assertXpathCount(1, "//ex:ParentFeature[@gml:id='sc.3']/ex:nestedFeature/ex:nestedValue[text()='1GRAV']", result);
assertXpathCount(1, "//ex:ParentFeature[@gml:id='sc.3']/ex:nestedFeature/ex:nestedValue[text()='1TILL']", result);
assertXpathCount(1, "//ex:ParentFeature[@gml:id='sc.3']/ex:nestedFeature/ex:nestedValue[text()='6ALLU']", result);
}
@Test
public void testNonValidNestedJSON() throws Exception {
testJsonRequest("ex:ParentFeature", "/test-data/ParentFeature.json");
}
private void testJsonRequest(String featureType, String expectResultPath) throws Exception {
// get the complex features encoded in JSON
String request = String.format(
"wfs?request=GetFeature&version=1.1.0&typename=%s&outputFormat=application/json", featureType);
JSON result = getAsJSON(request);
// check that we got a valida JSON object
assertThat(result, instanceOf(JSONObject.class));
JSONObject resultJson = (JSONObject) result;
// check the returned JSON againts the expected result
JSONObject expectedJson = readJsonObject(expectResultPath);
assertThat(resultJson, is(expectedJson));
}
/**
* Helper method that just reads a JSON object from a resource file.
*/
private JSONObject readJsonObject(String resourcePath) throws Exception {
// read the JSON file content
InputStream input = this.getClass().getResourceAsStream(resourcePath);
assertThat(input, notNullValue());
ByteArrayOutputStream output = new ByteArrayOutputStream();
IOUtils.copy(input, output);
// parse the JSON file
String jsonText = new String(output.toByteArray());
return JSONObject.fromObject(jsonText);
}
}