/* (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.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
import java.io.IOException;
import org.geoserver.catalog.FeatureTypeInfo;
import org.geotools.data.FeatureSource;
import org.geotools.data.complex.AppSchemaDataAccess;
import org.geotools.data.complex.FeatureTypeMapping;
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.Filter;
import org.opengis.filter.PropertyIsEqualTo;
import org.w3c.dom.Document;
/**
* WFS GetFeature to test polymorphism in Geoserver app-schema.
*
* @author Rini Angreani, CSIRO Earth Science and Resource Engineering
*/
public class PolymorphismWfsTest extends AbstractAppSchemaTestSupport {
@Override
protected PolymorphismMockData createTestData() {
return new PolymorphismMockData();
}
@Test
public void testPolymorphism() {
Document doc = getAsDOM("wfs?request=GetFeature&version=1.1.0&typename=ex:PolymorphicFeature");
LOGGER
.info("WFS GetFeature&typename=ex:PolymorphicFeature response:\n"
+ prettyString(doc));
assertXpathCount(6, "//ex:PolymorphicFeature", doc);
// check contents per attribute (each attribute has a different use case)
checkPolymorphicFeatureChaining(doc);
checkPolymorphismOnly(doc);
checkFeatureChainingOnly(doc);
checkXlinkHrefValues(doc);
checkAnyType(doc);
}
/**
* Test filtering polymorphism with feature chaining set up works. Also tests filtering when
* mappingName is used as linkElement.
*/
@Test
public void testFirstValueFilters() {
// <AttributeMapping>
// <!-- Test feature chaining and polymorphism -->
// <targetAttribute>ex:firstValue</targetAttribute>
// <sourceExpression>
// <OCQL>VALUE_ID</OCQL>
// <linkElement>
// Recode(CLASS_TEXT, 'numeric', 'NumericType', 'literal', toXlinkHref(strConcat('urn:value::', VALUE_ID)))
// </linkElement>
// <linkField>FEATURE_LINK</linkField>
// </sourceExpression>
// <isMultiple>true</isMultiple>
// </AttributeMapping>
String xml = //
"<wfs:GetFeature " //
+ "service=\"WFS\" " //
+ "version=\"1.1.0\" " //
+ "xmlns:cdf=\"http://www.opengis.net/cite/data\" " //
+ "xmlns:ogc=\"http://www.opengis.net/ogc\" " //
+ "xmlns:wfs=\"http://www.opengis.net/wfs\" " //
+ "xmlns:gml=\"http://www.opengis.net/gml\" " //
+ "xmlns:ex=\"http://example.com\" " //
+ "xmlns:gsml=\""
+ AbstractAppSchemaMockData.GSML_URI
+ "\" " //
+ ">" //
+ " <wfs:Query typeName=\"ex:PolymorphicFeature\">" //
+ " <ogc:Filter>" //
+ " <ogc:PropertyIsEqualTo>" //
+ " <ogc:PropertyName>ex:firstValue/gsml:CGI_NumericValue/gsml:principalValue</ogc:PropertyName>" //
+ " <ogc:Literal>1.0</ogc:Literal>" //
+ " </ogc:PropertyIsEqualTo>" //
+ " </ogc:Filter>" //
+ " </wfs:Query> " //
+ "</wfs:GetFeature>";
Document doc = postAsDOM("wfs", xml);
LOGGER.info("WFS filter GetFeature response:\n" + prettyString(doc));
assertXpathEvaluatesTo("1", "/wfs:FeatureCollection/@numberOfFeatures", doc);
assertXpathCount(1, "//ex:PolymorphicFeature", doc);
// f1
assertXpathEvaluatesTo("f1", "//ex:PolymorphicFeature/@gml:id", doc);
assertXpathEvaluatesTo(
"1.0",
"//ex:PolymorphicFeature[@gml:id='f1']/ex:firstValue/gsml:CGI_NumericValue/gsml:principalValue",
doc);
assertXpathEvaluatesTo(
"m",
"//ex:PolymorphicFeature[@gml:id='f1']/ex:firstValue/gsml:CGI_NumericValue/gsml:principalValue/@uom",
doc);
}
/**
* Test filtering polymorphism with no feature chaining works. Also tests filtering when
* mappingName is used as linkElement.
*/
@Test
public void testSecondValueFilters() {
// <AttributeMapping>
// <!-- Test polymorphism with no feature chaining i.e. no linkField -->
// <targetAttribute>ex:secondValue</targetAttribute>
// <sourceExpression>
// <linkElement>
// if_then_else(isNull(CLASS_TEXT), Expression.Nil,
// if_then_else(equalTo(CLASS_TEXT, 'numeric'), 'NumericType', 'TermValue2'))
// </linkElement>
// </sourceExpression>
// </AttributeMapping>
String xml = //
"<wfs:GetFeature " //
+ "service=\"WFS\" " //
+ "version=\"1.1.0\" " //
+ "xmlns:cdf=\"http://www.opengis.net/cite/data\" " //
+ "xmlns:ogc=\"http://www.opengis.net/ogc\" " //
+ "xmlns:wfs=\"http://www.opengis.net/wfs\" " //
+ "xmlns:gml=\"http://www.opengis.net/gml\" " //
+ "xmlns:ex=\"http://example.com\" " //
+ "xmlns:gsml=\""
+ AbstractAppSchemaMockData.GSML_URI
+ "\" " //
+ ">" //
+ " <wfs:Query typeName=\"ex:PolymorphicFeature\">" //
+ " <ogc:Filter>" //
+ " <ogc:PropertyIsEqualTo>" //
+ " <ogc:PropertyName>ex:secondValue/gsml:CGI_NumericValue/gsml:principalValue/@uom</ogc:PropertyName>" //
+ " <ogc:Literal>m</ogc:Literal>" //
+ " </ogc:PropertyIsEqualTo>" //
+ " </ogc:Filter>" //
+ " </wfs:Query> " //
+ "</wfs:GetFeature>";
Document doc = postAsDOM("wfs", xml);
LOGGER.info("WFS filter GetFeature response:\n" + prettyString(doc));
assertXpathEvaluatesTo("2", "/wfs:FeatureCollection/@numberOfFeatures", doc);
assertXpathCount(2, "//ex:PolymorphicFeature", doc);
// f1
assertXpathEvaluatesTo("f1", "(//ex:PolymorphicFeature)[1]/@gml:id", doc);
assertXpathEvaluatesTo(
"1.0",
"//ex:PolymorphicFeature[@gml:id='f1']/ex:secondValue/gsml:CGI_NumericValue/gsml:principalValue",
doc);
assertXpathEvaluatesTo(
"m",
"//ex:PolymorphicFeature[@gml:id='f1']/ex:secondValue/gsml:CGI_NumericValue/gsml:principalValue/@uom",
doc);
// f3
assertXpathEvaluatesTo("f3", "(//ex:PolymorphicFeature)[2]/@gml:id", doc);
assertXpathEvaluatesTo(
"0.0",
"//ex:PolymorphicFeature[@gml:id='f3']/ex:secondValue/gsml:CGI_NumericValue/gsml:principalValue",
doc);
assertXpathEvaluatesTo(
"m",
"//ex:PolymorphicFeature[@gml:id='f3']/ex:secondValue/gsml:CGI_NumericValue/gsml:principalValue/@uom",
doc);
}
/**
* Tests filtering mapping of any type works.
*/
@Test
public void testAnyTypeFilters() {
// <AttributeMapping>
// <!-- Test polymorphism with anyType -->
// <targetAttribute>ex:anyValue</targetAttribute>
// <sourceExpression>
// <linkElement>
// Recode(CLASS_TEXT, Expression.Nil, toXlinkHref('urn:ogc:def:nil:OGC::missing'),
// 'numeric', toXlinkHref(strConcat('urn:numeric-value::', NUMERIC_VALUE)),
// 'literal', 'TermValue2')
// </linkElement>
// </sourceExpression>
// </AttributeMapping>
String xml = //
"<wfs:GetFeature " //
+ "service=\"WFS\" " //
+ "version=\"1.1.0\" " //
+ "xmlns:cdf=\"http://www.opengis.net/cite/data\" " //
+ "xmlns:ogc=\"http://www.opengis.net/ogc\" " //
+ "xmlns:wfs=\"http://www.opengis.net/wfs\" " //
+ "xmlns:gml=\"http://www.opengis.net/gml\" " //
+ "xmlns:ex=\"http://example.com\" " //
+ "xmlns:gsml=\""
+ AbstractAppSchemaMockData.GSML_URI
+ "\" " //
+ ">" //
+ " <wfs:Query typeName=\"ex:PolymorphicFeature\">" //
+ " <ogc:Filter>" //
+ " <ogc:PropertyIsEqualTo>" //
+ " <ogc:PropertyName>ex:anyValue/gsml:CGI_TermValue/gsml:value</ogc:PropertyName>" //
+ " <ogc:Literal>0</ogc:Literal>" //
+ " </ogc:PropertyIsEqualTo>" //
+ " </ogc:Filter>" //
+ " </wfs:Query> " //
+ "</wfs:GetFeature>";
Document doc = postAsDOM("wfs", xml);
LOGGER.info("WFS filter GetFeature response:\n" + prettyString(doc));
assertXpathEvaluatesTo("2", "/wfs:FeatureCollection/@numberOfFeatures", doc);
assertXpathCount(2, "//ex:PolymorphicFeature", doc);
// f2
assertXpathEvaluatesTo("f2", "(//ex:PolymorphicFeature)[1]/@gml:id", doc);
assertXpathEvaluatesTo("0",
"//ex:PolymorphicFeature[@gml:id='f2']/ex:anyValue/gsml:CGI_TermValue/gsml:value",
doc);
// f5
assertXpathEvaluatesTo("f5", "(//ex:PolymorphicFeature)[2]/@gml:id", doc);
assertXpathEvaluatesTo("0",
"//ex:PolymorphicFeature[@gml:id='f5']/ex:anyValue/gsml:CGI_TermValue/gsml:value",
doc);
}
/**
* Tests filtering feature chaining where it's linked by mappingName.
*/
@Test
public void testMappingNameFilters() {
// <AttributeMapping>
// <!-- Test polymorphism using normal feature chaining with no conditions -->
// <targetAttribute>ex:thirdValue</targetAttribute>
// <sourceExpression>
// <OCQL>VALUE_ID</OCQL>
// <linkElement>gsml:CGI_NumericValue</linkElement>
// <linkField>FEATURE_LINK</linkField>
// </sourceExpression>
// </AttributeMapping>
// <AttributeMapping>
// <!-- See above -->
// <targetAttribute>ex:thirdValue</targetAttribute>
// <sourceExpression>
// <OCQL>VALUE_ID</OCQL>
// <linkElement>gsml:CGI_TermValue</linkElement>
// <linkField>FEATURE_LINK</linkField>
// </sourceExpression>
// </AttributeMapping>
String xml = //
"<wfs:GetFeature " //
+ "service=\"WFS\" " //
+ "version=\"1.1.0\" " //
+ "xmlns:cdf=\"http://www.opengis.net/cite/data\" " //
+ "xmlns:ogc=\"http://www.opengis.net/ogc\" " //
+ "xmlns:wfs=\"http://www.opengis.net/wfs\" " //
+ "xmlns:gml=\"http://www.opengis.net/gml\" " //
+ "xmlns:ex=\"http://example.com\" " //
+ "xmlns:gsml=\""
+ AbstractAppSchemaMockData.GSML_URI
+ "\" " //
+ ">" //
+ " <wfs:Query typeName=\"ex:PolymorphicFeature\">" //
+ " <ogc:Filter>" //
+ " <ogc:PropertyIsEqualTo>" //
+ " <ogc:PropertyName>ex:thirdValue/gsml:CGI_NumericValue/gsml:principalValue</ogc:PropertyName>" //
+ " <ogc:Literal>1.0</ogc:Literal>" //
+ " </ogc:PropertyIsEqualTo>" //
+ " </ogc:Filter>" //
+ " </wfs:Query> " //
+ "</wfs:GetFeature>";
Document doc = postAsDOM("wfs", xml);
LOGGER.info("WFS filter GetFeature response:\n" + prettyString(doc));
assertXpathEvaluatesTo("2", "/wfs:FeatureCollection/@numberOfFeatures", doc);
assertXpathCount(2, "//ex:PolymorphicFeature", doc);
// f1
assertXpathEvaluatesTo("f1", "(//ex:PolymorphicFeature)[1]/@gml:id", doc);
assertXpathEvaluatesTo(
"1.0",
"//ex:PolymorphicFeature[@gml:id='f1']/ex:thirdValue/gsml:CGI_NumericValue/gsml:principalValue",
doc);
assertXpathEvaluatesTo(
"m",
"//ex:PolymorphicFeature[@gml:id='f1']/ex:thirdValue/gsml:CGI_NumericValue/gsml:principalValue/@uom",
doc);
// f4
assertXpathEvaluatesTo("f4", "(//ex:PolymorphicFeature)[2]/@gml:id", doc);
assertXpathEvaluatesTo(
"1.0",
"//ex:PolymorphicFeature[@gml:id='f4']/ex:thirdValue/gsml:CGI_NumericValue/gsml:principalValue",
doc);
assertXpathEvaluatesTo(
"m",
"//ex:PolymorphicFeature[@gml:id='f4']/ex:thirdValue/gsml:CGI_NumericValue/gsml:principalValue/@uom",
doc);
}
/**
* This is to test that polymorphism with feature chaining works.
*/
private void checkPolymorphicFeatureChaining(Document doc) {
// f1: make sure only 1 gsml:CGI_NumericValue is encoded
assertXpathCount(1, "//ex:PolymorphicFeature[@gml:id='f1']/ex:firstValue", doc);
assertXpathCount(1,
"//ex:PolymorphicFeature[@gml:id='f1']/ex:firstValue/gsml:CGI_NumericValue", doc);
assertXpathCount(0,
"//ex:PolymorphicFeature[@gml:id='f1']/ex:firstValue/gsml:CGI_TermValue", doc);
assertXpathCount(0,
"//ex:PolymorphicFeature[@gml:id='f1']/ex:firstValue/gsml:CGI_TermValue", doc);
assertXpathEvaluatesTo(
"1.0",
"//ex:PolymorphicFeature[@gml:id='f1']/ex:firstValue/gsml:CGI_NumericValue/gsml:principalValue",
doc);
assertXpathEvaluatesTo(
"m",
"//ex:PolymorphicFeature[@gml:id='f1']/ex:firstValue/gsml:CGI_NumericValue/gsml:principalValue/@uom",
doc);
// f2: make sure only 1 xlink:href to gsml:CGI_TermValue is encoded
assertXpathCount(1, "//ex:PolymorphicFeature[@gml:id='f2']/ex:firstValue", doc);
assertXpathEvaluatesTo("urn:value::x",
"//ex:PolymorphicFeature[@gml:id='f2']/ex:firstValue/@xlink:href", doc);
assertXpathCount(0,
"//ex:PolymorphicFeature[@gml:id='f2']/ex:firstValue/gsml:CGI_NumericValue", doc);
// f3: make sure firstValue is null
assertXpathCount(0, "//ex:PolymorphicFeature[@gml:id='f3']/ex:firstValue", doc);
// f4: make sure firstValue is null
assertXpathCount(0, "//ex:PolymorphicFeature[@gml:id='f4']/ex:firstValue", doc);
// f5: make sure only 1 xlink:href to gsml:CGI_TermValue is encoded
assertXpathCount(1, "//ex:PolymorphicFeature[@gml:id='f5']/ex:firstValue", doc);
assertXpathEvaluatesTo("urn:value::y",
"//ex:PolymorphicFeature[@gml:id='f5']/ex:firstValue/@xlink:href", doc);
assertXpathCount(0,
"//ex:PolymorphicFeature[@gml:id='f5']/ex:firstValue/gsml:CGI_NumericValue", doc);
// f6: make sure firstValue is null
assertXpathCount(0, "//ex:PolymorphicFeature[@gml:id='f6']/ex:firstValue", doc);
}
/**
* This is to test that polymorphism with no feature chaining works.
*/
private void checkPolymorphismOnly(Document doc) {
// f1: make sure only 1 gsml:CGI_NumericValue is encoded
assertXpathCount(1, "//ex:PolymorphicFeature[@gml:id='f1']/ex:secondValue", doc);
assertXpathCount(1,
"//ex:PolymorphicFeature[@gml:id='f1']/ex:secondValue/gsml:CGI_NumericValue", doc);
assertXpathCount(0,
"//ex:PolymorphicFeature[@gml:id='f1']/ex:secondValue/gsml:CGI_TermValue", doc);
assertXpathEvaluatesTo(
"1.0",
"//ex:PolymorphicFeature[@gml:id='f1']/ex:secondValue/gsml:CGI_NumericValue/gsml:principalValue",
doc);
assertXpathEvaluatesTo(
"m",
"//ex:PolymorphicFeature[@gml:id='f1']/ex:secondValue/gsml:CGI_NumericValue/gsml:principalValue/@uom",
doc);
// f2: make sure only 1 gsml:CGI_TermValue is encoded
assertXpathCount(1, "//ex:PolymorphicFeature[@gml:id='f2']/ex:secondValue", doc);
assertXpathCount(1,
"//ex:PolymorphicFeature[@gml:id='f2']/ex:secondValue/gsml:CGI_TermValue", doc);
assertXpathCount(0,
"//ex:PolymorphicFeature[@gml:id='f2']/ex:secondValue/gsml:CGI_NumericValue", doc);
assertXpathEvaluatesTo(
"0",
"//ex:PolymorphicFeature[@gml:id='f2']/ex:secondValue/gsml:CGI_TermValue/gsml:value",
doc);
// test GEOT-3304
assertXpathEvaluatesTo(
"approximate",
"//ex:PolymorphicFeature[@gml:id='f2']/ex:secondValue/gsml:CGI_TermValue/@gsml:qualifier",
doc);
// f3: make sure only 1 gsml:CGI_NumericValue is encoded
assertXpathCount(1, "//ex:PolymorphicFeature[@gml:id='f3']/ex:secondValue", doc);
assertXpathCount(1,
"//ex:PolymorphicFeature[@gml:id='f3']/ex:secondValue/gsml:CGI_NumericValue", doc);
assertXpathCount(0,
"//ex:PolymorphicFeature[@gml:id='f3']/ex:secondValue/gsml:CGI_TermValue", doc);
assertXpathEvaluatesTo(
"0.0",
"//ex:PolymorphicFeature[@gml:id='f3']/ex:secondValue/gsml:CGI_NumericValue/gsml:principalValue",
doc);
assertXpathEvaluatesTo(
"m",
"//ex:PolymorphicFeature[@gml:id='f3']/ex:secondValue/gsml:CGI_NumericValue/gsml:principalValue/@uom",
doc);
// f4: make sure nothing is encoded
assertXpathCount(0, "//ex:PolymorphicFeature[@gml:id='f4']/ex:secondValue", doc);
// f5: make sure only 1 gsml:CGI_TermValue is encoded
assertXpathCount(1, "//ex:PolymorphicFeature[@gml:id='f5']/ex:secondValue", doc);
assertXpathCount(1,
"//ex:PolymorphicFeature[@gml:id='f5']/ex:secondValue/gsml:CGI_TermValue", doc);
assertXpathCount(0,
"//ex:PolymorphicFeature[@gml:id='f5']/ex:secondValue/gsml:CGI_NumericValue", doc);
assertXpathEvaluatesTo(
"0",
"//ex:PolymorphicFeature[@gml:id='f5']/ex:secondValue/gsml:CGI_TermValue/gsml:value",
doc);
// test GEOT-3304
assertXpathEvaluatesTo(
"approximate",
"//ex:PolymorphicFeature[@gml:id='f5']/ex:secondValue/gsml:CGI_TermValue/@gsml:qualifier",
doc);
// f6: make sure only 1 gsml:CGI_TermValue is encoded
assertXpathCount(1, "//ex:PolymorphicFeature[@gml:id='f6']/ex:secondValue", doc);
assertXpathCount(1,
"//ex:PolymorphicFeature[@gml:id='f6']/ex:secondValue/gsml:CGI_TermValue", doc);
assertXpathCount(0,
"//ex:PolymorphicFeature[@gml:id='f6']/ex:secondValue/gsml:CGI_NumericValue", doc);
assertXpathEvaluatesTo(
"1000",
"//ex:PolymorphicFeature[@gml:id='f6']/ex:secondValue/gsml:CGI_TermValue/gsml:value",
doc);
// test GEOT-3304
assertXpathEvaluatesTo(
"approximate",
"//ex:PolymorphicFeature[@gml:id='f6']/ex:secondValue/gsml:CGI_TermValue/@gsml:qualifier",
doc);
}
/**
* This is to test that polymorphism can be achieved with feature chaining alone.
*/
private void checkFeatureChainingOnly(Document doc) {
// f1: make sure only 1 gsml:CGI_NumericValue is encoded
assertXpathCount(1, "//ex:PolymorphicFeature[@gml:id='f1']/ex:thirdValue", doc);
assertXpathCount(1,
"//ex:PolymorphicFeature[@gml:id='f1']/ex:thirdValue/gsml:CGI_NumericValue", doc);
assertXpathCount(0,
"//ex:PolymorphicFeature[@gml:id='f1']/ex:thirdValue/gsml:CGI_TermValue", doc);
assertXpathEvaluatesTo(
"1.0",
"//ex:PolymorphicFeature[@gml:id='f1']/ex:thirdValue/gsml:CGI_NumericValue/gsml:principalValue",
doc);
assertXpathEvaluatesTo(
"m",
"//ex:PolymorphicFeature[@gml:id='f1']/ex:thirdValue/gsml:CGI_NumericValue/gsml:principalValue/@uom",
doc);
// f2: make sure only 1 gsml:CGI_TermValue is encoded
assertXpathCount(1, "//ex:PolymorphicFeature[@gml:id='f2']/ex:thirdValue", doc);
assertXpathCount(1,
"//ex:PolymorphicFeature[@gml:id='f2']/ex:thirdValue/gsml:CGI_TermValue", doc);
assertXpathCount(0,
"//ex:PolymorphicFeature[@gml:id='f2']/ex:thirdValue/gsml:CGI_NumericValue", doc);
assertXpathEvaluatesTo(
"x",
"//ex:PolymorphicFeature[@gml:id='f2']/ex:thirdValue/gsml:CGI_TermValue/gsml:value",
doc);
assertXpathEvaluatesTo(
"some:uri",
"//ex:PolymorphicFeature[@gml:id='f2']/ex:thirdValue/gsml:CGI_TermValue/gsml:value/@codeSpace",
doc);
// f3: make sure only 1 gsml:CGI_TermValue is encoded
assertXpathCount(1, "//ex:PolymorphicFeature[@gml:id='f3']/ex:thirdValue", doc);
assertXpathCount(1,
"//ex:PolymorphicFeature[@gml:id='f3']/ex:thirdValue/gsml:CGI_TermValue", doc);
assertXpathCount(0,
"//ex:PolymorphicFeature[@gml:id='f3']/ex:thirdValue/gsml:CGI_NumericValue", doc);
assertXpathEvaluatesTo(
"y",
"//ex:PolymorphicFeature[@gml:id='f3']/ex:thirdValue/gsml:CGI_TermValue/gsml:value",
doc);
assertXpathEvaluatesTo(
"some:uri",
"//ex:PolymorphicFeature[@gml:id='f3']/ex:thirdValue/gsml:CGI_TermValue/gsml:value/@codeSpace",
doc);
// f4: make sure only 1 gsml:CGI_NumericValue is encoded
assertXpathCount(1, "//ex:PolymorphicFeature[@gml:id='f4']/ex:thirdValue", doc);
assertXpathCount(1,
"//ex:PolymorphicFeature[@gml:id='f4']/ex:thirdValue/gsml:CGI_NumericValue", doc);
assertXpathCount(0,
"//ex:PolymorphicFeature[@gml:id='f4']/ex:thirdValue/gsml:CGI_TermValue", doc);
assertXpathEvaluatesTo(
"1.0",
"//ex:PolymorphicFeature[@gml:id='f4']/ex:thirdValue/gsml:CGI_NumericValue/gsml:principalValue",
doc);
assertXpathEvaluatesTo(
"m",
"//ex:PolymorphicFeature[@gml:id='f4']/ex:thirdValue/gsml:CGI_NumericValue/gsml:principalValue/@uom",
doc);
// f5: make sure only 1 gsml:CGI_TermValue is encoded
assertXpathCount(1, "//ex:PolymorphicFeature[@gml:id='f5']/ex:thirdValue", doc);
assertXpathCount(1,
"//ex:PolymorphicFeature[@gml:id='f5']/ex:thirdValue/gsml:CGI_TermValue", doc);
assertXpathCount(0,
"//ex:PolymorphicFeature[@gml:id='f5']/ex:thirdValue/gsml:CGI_NumericValue", doc);
assertXpathEvaluatesTo(
"y",
"//ex:PolymorphicFeature[@gml:id='f5']/ex:thirdValue/gsml:CGI_TermValue/gsml:value",
doc);
assertXpathEvaluatesTo(
"some:uri",
"//ex:PolymorphicFeature[@gml:id='f5']/ex:thirdValue/gsml:CGI_TermValue/gsml:value/@codeSpace",
doc);
// f6: make sure only 1 gsml:CGI_NumericValue is encoded
assertXpathCount(1, "//ex:PolymorphicFeature[@gml:id='f6']/ex:thirdValue", doc);
assertXpathCount(1,
"//ex:PolymorphicFeature[@gml:id='f6']/ex:thirdValue/gsml:CGI_NumericValue", doc);
assertXpathCount(0,
"//ex:PolymorphicFeature[@gml:id='f6']/ex:thirdValue/gsml:CGI_TermValue", doc);
assertXpathEvaluatesTo(
"1000.0",
"//ex:PolymorphicFeature[@gml:id='f6']/ex:thirdValue/gsml:CGI_NumericValue/gsml:principalValue",
doc);
assertXpathEvaluatesTo(
"m",
"//ex:PolymorphicFeature[@gml:id='f6']/ex:thirdValue/gsml:CGI_NumericValue/gsml:principalValue/@uom",
doc);
}
/**
* This is to test referential polymorphism, mixed with sub-type polymorphism.
*/
private void checkXlinkHrefValues(Document doc) {
// f1: make sure only null reference is encoded
assertXpathCount(1, "//ex:PolymorphicFeature[@gml:id='f1']/ex:fourthValue", doc);
assertXpathCount(0,
"//ex:PolymorphicFeature[@gml:id='f1']/ex:fourthValue/ex:firstParentFeature", doc);
assertXpathEvaluatesTo(
"urn:ogc:def:nil:OGC::missing",
"//ex:PolymorphicFeature[@gml:id='f1']/ex:fourthValue/@xlink:href",
doc);
assertXpathCount(1, "//ex:PolymorphicFeature[@gml:id='f1']/ex:fifthValue", doc);
assertXpathCount(0,
"//ex:PolymorphicFeature[@gml:id='f1']/ex:fifthValue/ex:firstParentFeature", doc);
assertXpathEvaluatesTo(
"urn:ogc:def:nil:OGC::missing",
"//ex:PolymorphicFeature[@gml:id='f1']/ex:fifthValue/@xlink:href",
doc);
// f2: make sure only 1 null reference is encoded
assertXpathCount(1, "//ex:PolymorphicFeature[@gml:id='f2']/ex:fourthValue", doc);
assertXpathCount(0,
"//ex:PolymorphicFeature[@gml:id='f2']/ex:fourthValue/ex:firstParentFeature", doc);
assertXpathEvaluatesTo(
"urn:ogc:def:nil:OGC::missing",
"//ex:PolymorphicFeature[@gml:id='f2']/ex:fourthValue/@xlink:href",
doc);
assertXpathCount(1, "//ex:PolymorphicFeature[@gml:id='f2']/ex:fifthValue", doc);
assertXpathCount(0,
"//ex:PolymorphicFeature[@gml:id='f2']/ex:fifthValue/ex:firstParentFeature", doc);
assertXpathEvaluatesTo(
"urn:ogc:def:nil:OGC::missing",
"//ex:PolymorphicFeature[@gml:id='f2']/ex:fifthValue/@xlink:href",
doc);
// f3: make sure only 1 null reference is encoded
assertXpathCount(1, "//ex:PolymorphicFeature[@gml:id='f3']/ex:fourthValue", doc);
assertXpathCount(0,
"//ex:PolymorphicFeature[@gml:id='f3']/ex:fourthValue/ex:firstParentFeature", doc);
assertXpathEvaluatesTo(
"urn:ogc:def:nil:OGC::missing",
"//ex:PolymorphicFeature[@gml:id='f3']/ex:fourthValue/@xlink:href",
doc);
assertXpathCount(1, "//ex:PolymorphicFeature[@gml:id='f3']/ex:fifthValue", doc);
assertXpathCount(0,
"//ex:PolymorphicFeature[@gml:id='f3']/ex:fifthValue/ex:firstParentFeature", doc);
assertXpathEvaluatesTo(
"urn:ogc:def:nil:OGC::missing",
"//ex:PolymorphicFeature[@gml:id='f3']/ex:fifthValue/@xlink:href",
doc);
// f4: make sure only 1 null reference is encoded
assertXpathCount(1, "//ex:PolymorphicFeature[@gml:id='f4']/ex:fourthValue", doc);
assertXpathCount(0,
"//ex:PolymorphicFeature[@gml:id='f4']/ex:fourthValue/ex:firstParentFeature", doc);
assertXpathEvaluatesTo(
"urn:ogc:def:nil:OGC::missing",
"//ex:PolymorphicFeature[@gml:id='f4']/ex:fourthValue/@xlink:href",
doc);
assertXpathCount(1, "//ex:PolymorphicFeature[@gml:id='f4']/ex:fifthValue", doc);
assertXpathCount(0,
"//ex:PolymorphicFeature[@gml:id='f4']/ex:fifthValue/ex:firstParentFeature", doc);
assertXpathEvaluatesTo(
"urn:ogc:def:nil:OGC::missing",
"//ex:PolymorphicFeature[@gml:id='f4']/ex:fifthValue/@xlink:href",
doc);
// f5: make sure only 1 null reference is encoded
assertXpathCount(1, "//ex:PolymorphicFeature[@gml:id='f5']/ex:fourthValue", doc);
assertXpathCount(0,
"//ex:PolymorphicFeature[@gml:id='f5']/ex:fourthValue/ex:firstParentFeature", doc);
assertXpathEvaluatesTo(
"urn:ogc:def:nil:OGC::missing",
"//ex:PolymorphicFeature[@gml:id='f5']/ex:fourthValue/@xlink:href",
doc);
assertXpathCount(1, "//ex:PolymorphicFeature[@gml:id='f5']/ex:fifthValue", doc);
assertXpathCount(0,
"//ex:PolymorphicFeature[@gml:id='f5']/ex:fifthValue/ex:firstParentFeature", doc);
assertXpathEvaluatesTo(
"urn:ogc:def:nil:OGC::missing",
"//ex:PolymorphicFeature[@gml:id='f5']/ex:fifthValue/@xlink:href",
doc);
// f6: make sure only 1 gsml:CGI_NumericValue is encoded
assertXpathCount(1, "//ex:PolymorphicFeature[@gml:id='f6']/ex:fourthValue", doc);
assertXpathCount(1,
"//ex:PolymorphicFeature[@gml:id='f6']/ex:fourthValue/gsml:CGI_NumericValue", doc);
assertXpathCount(0,
"//ex:PolymorphicFeature[@gml:id='f6']/ex:fourthValue/gsml:CGI_TermValue", doc);
assertXpathEvaluatesTo(
"1000.0",
"//ex:PolymorphicFeature[@gml:id='f6']/ex:fourthValue/gsml:CGI_NumericValue/gsml:principalValue",
doc);
assertXpathEvaluatesTo(
"m",
"//ex:PolymorphicFeature[@gml:id='f6']/ex:fourthValue/gsml:CGI_NumericValue/gsml:principalValue/@uom",
doc);
// GEOT:4417
assertXpathCount(1, "//ex:PolymorphicFeature[@gml:id='f6']/ex:fifthValue", doc);
assertXpathCount(1,
"//ex:PolymorphicFeature[@gml:id='f6']/ex:fifthValue/gsml:CGI_NumericValue", doc);
assertXpathCount(0,
"//ex:PolymorphicFeature[@gml:id='f6']/ex:fifthValue/gsml:CGI_TermValue", doc);
assertXpathEvaluatesTo(
"1000.0",
"//ex:PolymorphicFeature[@gml:id='f6']/ex:fifthValue/gsml:CGI_NumericValue/gsml:principalValue",
doc);
assertXpathEvaluatesTo(
"m",
"//ex:PolymorphicFeature[@gml:id='f6']/ex:fifthValue/gsml:CGI_NumericValue/gsml:principalValue/@uom",
doc);
}
/**
* This is to test that conditional polymorphism works with xs:anyType.
*/
private void checkAnyType(Document doc) {
// f1: make sure only 1 ex:AnyValue is encoded
assertXpathCount(1, "//ex:PolymorphicFeature[@gml:id='f1']/ex:anyValue", doc);
assertXpathCount(0, "//ex:PolymorphicFeature[@gml:id='f1']/ex:anyValue/gsml:CGI_TermValue",
doc);
assertXpathCount(0,
"//ex:PolymorphicFeature[@gml:id='f1']/ex:anyValue/gsml:CGI_NumericValue", doc);
assertXpathEvaluatesTo(
"urn:numeric-value::1",
"//ex:PolymorphicFeature[@gml:id='f1']/ex:anyValue/@xlink:href",
doc);
// f2: make sure only 1 ex:anyValue is encoded
assertXpathCount(1, "//ex:PolymorphicFeature[@gml:id='f2']/ex:anyValue", doc);
assertXpathCount(1, "//ex:PolymorphicFeature[@gml:id='f2']/ex:anyValue/gsml:CGI_TermValue",
doc);
assertXpathCount(0,
"//ex:PolymorphicFeature[@gml:id='f2']/ex:anyValue/gsml:CGI_NumericValue", doc);
assertXpathEvaluatesTo("0",
"//ex:PolymorphicFeature[@gml:id='f2']/ex:anyValue/gsml:CGI_TermValue/gsml:value",
doc);
// test GEOT-3304
assertXpathEvaluatesTo("approximate",
"//ex:PolymorphicFeature[@gml:id='f2']/ex:anyValue/gsml:CGI_TermValue/@gsml:qualifier",
doc);
// f3: make sure only 1 ex:anyValue is encoded
assertXpathCount(1, "//ex:PolymorphicFeature[@gml:id='f3']/ex:anyValue", doc);
assertXpathCount(0, "//ex:PolymorphicFeature[@gml:id='f3']/ex:anyValue/gsml:CGI_TermValue",
doc);
assertXpathCount(0,
"//ex:PolymorphicFeature[@gml:id='f3']/ex:anyValue/gsml:CGI_NumericValue", doc);
assertXpathEvaluatesTo(
"urn:numeric-value::0",
"//ex:PolymorphicFeature[@gml:id='f3']/ex:anyValue/@xlink:href",
doc);
// f4: make sure there's only null reference encoded
assertXpathCount(1, "//ex:PolymorphicFeature[@gml:id='f4']/ex:anyValue", doc);
assertXpathCount(0, "//ex:PolymorphicFeature[@gml:id='f4']/ex:anyValue/gsml:CGI_TermValue",
doc);
assertXpathCount(0,
"//ex:PolymorphicFeature[@gml:id='f4']/ex:anyValue/gsml:CGI_NumericValue", doc);
assertXpathEvaluatesTo("urn:ogc:def:nil:OGC::missing",
"//ex:PolymorphicFeature[@gml:id='f4']/ex:anyValue/@xlink:href",
doc);
// f5: make sure only 1 ex:anyValue is encoded
assertXpathCount(1, "//ex:PolymorphicFeature[@gml:id='f5']/ex:anyValue", doc);
assertXpathCount(1, "//ex:PolymorphicFeature[@gml:id='f5']/ex:anyValue/gsml:CGI_TermValue",
doc);
assertXpathCount(0,
"//ex:PolymorphicFeature[@gml:id='f5']/ex:anyValue/gsml:CGI_NumericValue", doc);
assertXpathEvaluatesTo("0",
"//ex:PolymorphicFeature[@gml:id='f5']/ex:anyValue/gsml:CGI_TermValue/gsml:value",
doc);
// test GEOT-3304
assertXpathEvaluatesTo("approximate",
"//ex:PolymorphicFeature[@gml:id='f5']/ex:anyValue/gsml:CGI_TermValue/@gsml:qualifier",
doc);
// f6: make sure nothing is encoded
assertXpathCount(0, "//ex:PolymorphicFeature[@gml:id='f6']/ex:anyValue", doc);
}
@Test
public void testNestedFilterEncoding() throws FilterToSQLException, IOException {
FeatureTypeInfo ftInfo = getCatalog().getFeatureTypeByName("ex", "PolymorphicFeature");
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();
FilterFactoryImplNamespaceAware ff = new FilterFactoryImplNamespaceAware();
ff.setNamepaceContext(rootMapping.getNamespaces());
/*
* test equals filter on first nested attribute
*/
PropertyIsEqualTo equalsFirstValue = ff.equals(
ff.property("ex:firstValue/gsml:CGI_NumericValue/gsml:principalValue"),
ff.literal(1.0));
// Filter involving conditional polymorphism --> CANNOT be encoded
ComplexFilterSplitter splitterFirstValueFilter = new ComplexFilterSplitter(
store.getFilterCapabilities(), rootMapping);
splitterFirstValueFilter.visit(equalsFirstValue, null);
Filter preFilter = splitterFirstValueFilter.getFilterPre();
Filter postFilter = splitterFirstValueFilter.getFilterPost();
assertEquals(Filter.INCLUDE, preFilter);
assertEquals(equalsFirstValue, postFilter);
// filter must be "unrolled" (i.e. reverse mapped) first
Filter unrolled = AppSchemaDataAccess.unrollFilter(equalsFirstValue, rootMapping);
// Filter is nested
assertTrue(NestedFilterToSQL.isNestedFilter(unrolled));
assertContainsFeatures(fs.getFeatures(equalsFirstValue), "f1");
/*
* test equals filter on second nested attribute
*/
PropertyIsEqualTo equalsSecondValue = ff.equals(
ff.property("ex:secondValue/gsml:CGI_NumericValue/gsml:principalValue/@uom"),
ff.literal("m"));
// Filter involving conditional polymorphism --> CANNOT be encoded
ComplexFilterSplitter splitterSecondValue = new ComplexFilterSplitter(
store.getFilterCapabilities(), rootMapping);
splitterSecondValue.visit(equalsSecondValue, null);
preFilter = splitterSecondValue.getFilterPre();
postFilter = splitterSecondValue.getFilterPost();
assertEquals(Filter.INCLUDE, preFilter);
assertEquals(equalsSecondValue, postFilter);
// filter must be "unrolled" (i.e. reverse mapped) first
unrolled = AppSchemaDataAccess.unrollFilter(equalsSecondValue, rootMapping);
// Filter is nested
assertTrue(NestedFilterToSQL.isNestedFilter(unrolled));
assertContainsFeatures(fs.getFeatures(equalsSecondValue), "f1", "f3");
/*
* test equals filter on third nested attribute
*/
PropertyIsEqualTo equalsThirdValue = ff.equals(
ff.property("ex:thirdValue/gsml:CGI_NumericValue/gsml:principalValue"),
ff.literal(1.0));
// Filter involving non-conditional polymorphism --> can be encoded, because target attribute mapping can be unequivocally determined a-priori
ComplexFilterSplitter splitterThirdValueFilter = new ComplexFilterSplitter(
store.getFilterCapabilities(), rootMapping);
splitterThirdValueFilter.visit(equalsThirdValue, null);
preFilter = splitterThirdValueFilter.getFilterPre();
postFilter = splitterThirdValueFilter.getFilterPost();
assertEquals(equalsThirdValue, preFilter);
assertEquals(Filter.INCLUDE, postFilter);
// filter must be "unrolled" (i.e. reverse mapped) first
unrolled = AppSchemaDataAccess.unrollFilter(equalsThirdValue, rootMapping);
// Filter is nested
assertTrue(NestedFilterToSQL.isNestedFilter(unrolled));
NestedFilterToSQL nestedFilterEncoder = createNestedFilterEncoder(rootMapping);
String encodedFilter = nestedFilterEncoder.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"."POLYMORPHICFEATURE" "chain_link_1"
// WHERE "chain_link_1"."NUMERIC_VALUE" = 1.0 AND
// "appschematest"."POLYMORPHICFEATURE"."VALUE_ID" = "chain_link_1"."ID"
assertTrue(encodedFilter.contains("EXISTS"));
assertContainsFeatures(fs.getFeatures(equalsThirdValue), "f1", "f4");
}
}