From f2075f6d1d39e047a69d66d35150c5391feacf4c Mon Sep 17 00:00:00 2001 From: Lyn Elisa Goltz Date: Thu, 26 May 2016 12:16:37 +0200 Subject: [PATCH 01/25] #3475 - implemented parsing of a ValueReference as second parameter in Intersects operator --- .../deegree/filter/spatial/Intersects.java | 43 ++++++++++++++-- .../filter/xml/Filter200XMLDecoder.java | 51 +++++++++++-------- .../filter/xml/Filter200XMLDecoderTest.java | 17 +++++++ .../xml/v200/intersectsWithSpatialJoin.xml | 9 ++++ 4 files changed, 97 insertions(+), 23 deletions(-) create mode 100644 deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/intersectsWithSpatialJoin.xml diff --git a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Intersects.java b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Intersects.java index 067e0668a7..f75590ca0d 100644 --- a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Intersects.java +++ b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Intersects.java @@ -41,6 +41,7 @@ import org.deegree.filter.Expression; import org.deegree.filter.FilterEvaluationException; import org.deegree.filter.XPathEvaluator; +import org.deegree.filter.expression.ValueReference; import org.deegree.geometry.Envelope; import org.deegree.geometry.Geometry; import org.slf4j.Logger; @@ -60,15 +61,38 @@ public class Intersects extends SpatialOperator { private final Geometry geometry; + private final ValueReference valueReference; + /** + * Instantiates a {@link Intersects} operator with geometry as second parameter. + * * @param propName * may actually be null (deegree extension to cope with features that have only hidden * geometry props) * @param geometry + * never null */ public Intersects( Expression propName, Geometry geometry ) { + this( propName, geometry, null ); + } + + /** + * Instantiates a {@link Intersects} operator with value reference as second parameter. + * + * @param propName + * may actually be null (deegree extension to cope with features that have only hidden + * geometry props) + * @param valueReference + * never null + */ + public Intersects( Expression propName, ValueReference valueReference ) { + this( propName, null, valueReference ); + } + + private Intersects( Expression propName, Geometry geometry, ValueReference valueReference ) { super( propName ); this.geometry = geometry; + this.valueReference = valueReference; } @Override @@ -125,22 +149,35 @@ public boolean evaluate( T obj, XPathEvaluator xpathEvaluator ) } /** - * @return the geometry + * @return the second parameter, null if it is a value reference */ public Geometry getGeometry() { return geometry; } + /** + * @return the second parameter, null if it is a geometry + */ + public ValueReference getValueReference() { + return valueReference; + } + @Override public String toString( String indent ) { String s = indent + "-Intersects\n"; s += indent + propName + "\n"; - s += indent + geometry; + if ( geometry != null ) + s += indent + geometry; + if ( valueReference != null ) + s += indent + valueReference; return s; } @Override public Object[] getParams() { + if ( valueReference != null ) + return new Object[] { propName, valueReference }; return new Object[] { propName, geometry }; } -} + +} \ No newline at end of file diff --git a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/xml/Filter200XMLDecoder.java b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/xml/Filter200XMLDecoder.java index 1e6a02c167..06eed8e361 100644 --- a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/xml/Filter200XMLDecoder.java +++ b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/xml/Filter200XMLDecoder.java @@ -341,7 +341,8 @@ public static Filter parse( XMLStreamReader xmlStream ) *

*

    *
  • Precondition: cursor must point at the START_ELEMENT event (<fes:expression>)
  • - *
  • Postcondition: cursor points at the corresponding END_ELEMENT event (</fes:expression>)
  • + *
  • Postcondition: cursor points at the corresponding END_ELEMENT event (</fes:expression>) + *
  • *
*

* @@ -434,7 +435,8 @@ public static Function parseFunction( XMLStreamReader xmlStream ) *

*

    *
  • Precondition: cursor must point at the START_ELEMENT event (<fes:expression>)
  • - *
  • Postcondition: cursor points at the corresponding END_ELEMENT event (</fes:expression>)
  • + *
  • Postcondition: cursor points at the corresponding END_ELEMENT event (</fes:expression>) + *
  • *
*

* @@ -500,8 +502,7 @@ public static ComparisonOperator parseComparisonOperator( XMLStreamReader xmlStr // check if element name is a valid comparison operator element ComparisonOperator.SubType type = elementNameToComparisonOperatorType.get( xmlStream.getName() ); if ( type == null ) { - String msg = Messages.getMessage( "FILTER_PARSER_UNEXPECTED_ELEMENT", - xmlStream.getName(), + String msg = Messages.getMessage( "FILTER_PARSER_UNEXPECTED_ELEMENT", xmlStream.getName(), elemNames( ComparisonOperator.SubType.class, comparisonOperatorTypeToElementName ) ); throw new XMLParsingException( xmlStream, msg ); @@ -557,7 +558,8 @@ public static ComparisonOperator parseComparisonOperator( XMLStreamReader xmlStr *

*

    *
  • Precondition: cursor must point at the START_ELEMENT event (<fes:temporalOps>)
  • - *
  • Postcondition: cursor points at the corresponding END_ELEMENT event (</fes:temporalOps>)
  • + *
  • Postcondition: cursor points at the corresponding END_ELEMENT event (</fes:temporalOps>) + *
  • *
*

* @@ -624,8 +626,7 @@ public static TemporalOperator parseTemporalOperator( final XMLStreamReader xmlS private static TemporalOperator.SubType checkTemporalOperatorName( final XMLStreamReader xmlStream ) { TemporalOperator.SubType type = elementNameToTemporalOperatorType.get( xmlStream.getName() ); if ( type == null ) { - String msg = Messages.getMessage( "FILTER_PARSER_UNEXPECTED_ELEMENT", - xmlStream.getName(), + String msg = Messages.getMessage( "FILTER_PARSER_UNEXPECTED_ELEMENT", xmlStream.getName(), elemNames( TemporalOperator.SubType.class, temporalOperatorTypeToElementName ) ); throw new XMLParsingException( xmlStream, msg ); @@ -643,7 +644,8 @@ private static Operator parseOperator( XMLStreamReader xmlStream ) String expectedList = elemNames( LogicalOperator.SubType.class, logicalOperatorTypeToElementName ) + ", " + elemNames( SpatialOperator.SubType.class, spatialOperatorTypeToElementName ) + ", " + elemNames( ComparisonOperator.SubType.class, comparisonOperatorTypeToElementName ) - + "," + elemNames( TemporalOperator.SubType.class, temporalOperatorTypeToElementName ); + + "," + + elemNames( TemporalOperator.SubType.class, temporalOperatorTypeToElementName ); String msg = Messages.getMessage( "FILTER_PARSER_UNEXPECTED_ELEMENT", xmlStream.getName(), expectedList ); throw new XMLParsingException( xmlStream, msg ); } @@ -783,7 +785,7 @@ private static Literal parseLiteral( XMLStreamReader xmlStream ) while ( xmlStream.next() != END_ELEMENT ) { int eventType = xmlStream.getEventType(); if ( eventType == START_ELEMENT ) { - checkIfCurrentStartElementIsGmlGeometry( xmlStream ); + checkRequiredGmlGeometry( xmlStream ); children.add( parseElement( xmlStream ) ); } else if ( eventType == CHARACTERS || eventType == CDATA ) { children.add( new PrimitiveValue( xmlStream.getText() ) ); @@ -944,8 +946,7 @@ private static LogicalOperator parseLogicalOperator( XMLStreamReader xmlStream ) // check if element name is a valid logical operator element LogicalOperator.SubType type = elementNameToLogicalOperatorType.get( xmlStream.getName() ); if ( type == null ) { - String msg = Messages.getMessage( "FILTER_PARSER_UNEXPECTED_ELEMENT", - xmlStream.getName(), + String msg = Messages.getMessage( "FILTER_PARSER_UNEXPECTED_ELEMENT", xmlStream.getName(), elemNames( LogicalOperator.SubType.class, logicalOperatorTypeToElementName ) ); throw new XMLParsingException( xmlStream, msg ); @@ -996,8 +997,7 @@ private static SpatialOperator parseSpatialOperator( XMLStreamReader xmlStream ) // check if element name is a valid spatial operator element name SpatialOperator.SubType type = elementNameToSpatialOperatorType.get( xmlStream.getName() ); if ( type == null ) { - String msg = Messages.getMessage( "FILTER_PARSER_UNEXPECTED_ELEMENT", - xmlStream.getName(), + String msg = Messages.getMessage( "FILTER_PARSER_UNEXPECTED_ELEMENT", xmlStream.getName(), elemNames( SpatialOperator.SubType.class, spatialOperatorTypeToElementName ) ); throw new XMLParsingException( xmlStream, msg ); @@ -1043,9 +1043,15 @@ private static SpatialOperator parseSpatialOperator( XMLStreamReader xmlStream ) param1 = parseExpression( xmlStream ); nextElement( xmlStream ); } - // (must be 'gml:Geometry') - Geometry param2 = parseGeomOrEnvelope( xmlStream ); - spatialOperator = new Intersects( param1, param2 ); + if ( isCurrentStartElementIsGmlGeometry( xmlStream ) ) { + // (is 'gml:Geometry') + Geometry param2 = parseGeomOrEnvelope( xmlStream ); + spatialOperator = new Intersects( param1, param2 ); + } else { + // (is 'fes:ValueReference') + ValueReference param2 = parseValueReference( xmlStream, false ); + spatialOperator = new Intersects( param1, param2 ); + } break; } case CONTAINS: { @@ -1171,15 +1177,20 @@ private static Geometry parseGeomOrEnvelope( XMLStreamReader xmlStream ) } } - private static void checkIfCurrentStartElementIsGmlGeometry( XMLStreamReader xmlStream ) + private static void checkRequiredGmlGeometry( XMLStreamReader xmlStream ) + throws XMLStreamException { + if ( isCurrentStartElementIsGmlGeometry( xmlStream ) ) + throw new XMLParsingException( xmlStream, "Geometry elements as Literal child are not supported!" ); + } + + private static boolean isCurrentStartElementIsGmlGeometry( XMLStreamReader xmlStream ) throws XMLStreamException { String ns = xmlStream.getNamespaceURI(); if ( CommonNamespaces.GMLNS.equals( ns ) || CommonNamespaces.GML3_2_NS.equals( ns ) ) { GMLGeometryReader gmlReader = GMLGeometryVersionHelper.getGeometryReader( xmlStream.getName(), xmlStream ); - boolean isGeometryElement = gmlReader.isGeometryOrEnvelopeElement( xmlStream ); - if ( isGeometryElement ) - throw new XMLParsingException( xmlStream, "Geometry elements as Literal child are not supported!" ); + return gmlReader.isGeometryOrEnvelopeElement( xmlStream ); } + return false; } /** diff --git a/deegree-core/deegree-core-base/src/test/java/org/deegree/filter/xml/Filter200XMLDecoderTest.java b/deegree-core/deegree-core-base/src/test/java/org/deegree/filter/xml/Filter200XMLDecoderTest.java index ba7c95a58f..59dc610882 100644 --- a/deegree-core/deegree-core-base/src/test/java/org/deegree/filter/xml/Filter200XMLDecoderTest.java +++ b/deegree-core/deegree-core-base/src/test/java/org/deegree/filter/xml/Filter200XMLDecoderTest.java @@ -35,7 +35,9 @@ ----------------------------------------------------------------------------*/ package org.deegree.filter.xml; +import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; import java.io.File; import java.io.IOException; @@ -66,10 +68,12 @@ import org.deegree.filter.logical.And; import org.deegree.filter.logical.Not; import org.deegree.filter.spatial.Disjoint; +import org.deegree.filter.spatial.Intersects; import org.deegree.filter.spatial.Overlaps; import org.deegree.geometry.Envelope; import org.deegree.geometry.primitive.Polygon; import org.deegree.workspace.standard.DefaultWorkspace; +import org.hamcrest.CoreMatchers; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -328,6 +332,19 @@ public void parseOGCExample14() } } + @Test + public void parseIntersectsWithSpatialJoin() + throws XMLStreamException, FactoryConfigurationError, IOException { + InputStream filterAsStream = this.getClass().getResourceAsStream( "v200/intersectsWithSpatialJoin.xml" ); + XMLStreamReader xmlStream = XMLInputFactory.newInstance().createXMLStreamReader( filterAsStream ); + XMLStreamUtils.skipStartDocument( xmlStream ); + Intersects intersects = (Intersects) ( (OperatorFilter) Filter200XMLDecoder.parse( xmlStream ) ).getOperator(); + + assertThat( ( (ValueReference) intersects.getParam1() ).getAsText(), is( "app:ft1/geometry" ) ); + assertThat( intersects.getGeometry(), is( CoreMatchers.nullValue() ) ); + assertThat( intersects.getValueReference().getAsText(), is( "app:ft2/geometry" ) ); + } + @Test(expected = XMLParsingException.class) public void parsePropertyIsLessThanOrEuqlToWithLiteralContainingUnexpectedGeometry() throws Exception { diff --git a/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/intersectsWithSpatialJoin.xml b/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/intersectsWithSpatialJoin.xml new file mode 100644 index 0000000000..b9f3157ad6 --- /dev/null +++ b/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/intersectsWithSpatialJoin.xml @@ -0,0 +1,9 @@ + + + + app:ft1/geometry + app:ft2/geometry + + \ No newline at end of file From 9d06eec160de7d38612e72d01b53661bac51cb44 Mon Sep 17 00:00:00 2001 From: Lyn Elisa Goltz Date: Thu, 26 May 2016 13:28:32 +0200 Subject: [PATCH 02/25] #3475 - implememted parsing of second paramater as PropertyName for 1.1.0 --- .../filter/xml/Filter110XMLDecoder.java | 27 ++++++++++++++++--- .../filter/xml/Filter110XMLDecoderTest.java | 19 ++++++++++++- .../xml/v110/intersectsWithSpatialJoin.xml | 8 ++++++ 3 files changed, 49 insertions(+), 5 deletions(-) create mode 100644 deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v110/intersectsWithSpatialJoin.xml diff --git a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/xml/Filter110XMLDecoder.java b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/xml/Filter110XMLDecoder.java index 11df2adf1a..1db80338ec 100644 --- a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/xml/Filter110XMLDecoder.java +++ b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/xml/Filter110XMLDecoder.java @@ -66,6 +66,7 @@ import org.deegree.commons.tom.primitive.PrimitiveValue; import org.deegree.commons.uom.Measure; import org.deegree.commons.utils.ArrayUtils; +import org.deegree.commons.xml.CommonNamespaces; import org.deegree.commons.xml.NamespaceBindings; import org.deegree.commons.xml.XMLParsingException; import org.deegree.commons.xml.XPathUtils; @@ -122,6 +123,8 @@ import org.deegree.gml.GMLInputFactory; import org.deegree.gml.GMLStreamReader; import org.deegree.gml.geometry.GML3GeometryReader; +import org.deegree.gml.geometry.GMLGeometryReader; +import org.deegree.gml.geometry.GMLGeometryVersionHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -880,9 +883,14 @@ private static SpatialOperator parseSpatialOperator( XMLStreamReader xmlStream ) // first parameter: 'ogc:PropertyName' (cannot be empty) ValueReference param1 = parsePropertyName( xmlStream, false ); nextElement( xmlStream ); - // second parameter: 'gml:_Geometry' or 'gml:Envelope' - Geometry param2 = geomParser.parseGeometryOrEnvelope( wrapper ); - spatialOperator = new Intersects( param1, param2 ); + if ( isCurrentStartElementIsGmlGeometry( xmlStream ) ) { + // second parameter: 'gml:_Geometry' or 'gml:Envelope' + Geometry param2 = geomParser.parseGeometryOrEnvelope( wrapper ); + spatialOperator = new Intersects( param1, param2 ); + } else { + ValueReference param2 = parsePropertyName( xmlStream, false ); + spatialOperator = new Intersects( param1, param2 ); + } break; } case CONTAINS: { @@ -991,4 +999,15 @@ private static String elemNames( Class> enumClass, Map + + + app:ft1/app:geom + app:ft2/app:geom + + From a970a7ae27b1c1219de9cc77353c91b7a31ba5f4 Mon Sep 17 00:00:00 2001 From: Lyn Elisa Goltz Date: Thu, 26 May 2016 13:29:56 +0200 Subject: [PATCH 03/25] #3475 - enhanced filter encoder to export second PropertyName/ValueReference --- .../java/org/deegree/filter/xml/Filter110XMLEncoder.java | 7 +++++++ .../java/org/deegree/filter/xml/Filter200XMLEncoder.java | 7 ++++++- .../org/deegree/filter/xml/Filter110XMLEncoderTest.java | 9 +++++++++ .../filter/xml/Filter200XMLEncoderParameterizedTest.java | 1 + 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/xml/Filter110XMLEncoder.java b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/xml/Filter110XMLEncoder.java index 53c04ffdfd..5a50ac9abf 100644 --- a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/xml/Filter110XMLEncoder.java +++ b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/xml/Filter110XMLEncoder.java @@ -386,6 +386,7 @@ private static void export( SpatialOperator operator, XMLStreamWriter writer ) ValueReference propertyName = null; Geometry geometry = null; + ValueReference secondParam = null; Measure distance = null; switch ( operator.getSubType() ) { @@ -423,6 +424,7 @@ private static void export( SpatialOperator operator, XMLStreamWriter writer ) case INTERSECTS: propertyName = ( (Intersects) operator ).getPropName(); geometry = ( (Intersects) operator ).getGeometry(); + secondParam = ( (Intersects) operator ).getValueReference(); break; case OVERLAPS: propertyName = ( (Overlaps) operator ).getPropName(); @@ -441,12 +443,17 @@ private static void export( SpatialOperator operator, XMLStreamWriter writer ) // exporting the comparable geometry property export( propertyName, writer ); + // serializing the geometry if ( geometry != null ) { gmlWriter.setOutputCrs( geometry.getCoordinateSystem() ); gmlWriter.write( geometry ); } + // or value reference + if ( secondParam != null ) + export( secondParam, writer ); + if ( distance != null ) { // in case of Beyond- and DWithin-operators export their distance variable QName distanceElementName = new QName( CommonNamespaces.OGCNS, "Distance" ); writer.writeStartElement( distanceElementName.getNamespaceURI(), distanceElementName.getLocalPart() ); diff --git a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/xml/Filter200XMLEncoder.java b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/xml/Filter200XMLEncoder.java index 296045c594..0c47c9cdec 100644 --- a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/xml/Filter200XMLEncoder.java +++ b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/xml/Filter200XMLEncoder.java @@ -276,6 +276,7 @@ private static void export( SpatialOperator operator, XMLStreamWriter writer ) writer.writeStartElement( elementName.getNamespaceURI(), elementName.getLocalPart() ); Geometry geometry = null; + ValueReference secondParam = null; Measure distance = null; switch ( operator.getSubType() ) { case BBOX: @@ -303,6 +304,7 @@ private static void export( SpatialOperator operator, XMLStreamWriter writer ) break; case INTERSECTS: geometry = ( (Intersects) operator ).getGeometry(); + secondParam = ( (Intersects) operator ).getValueReference(); break; case OVERLAPS: geometry = ( (Overlaps) operator ).getGeometry(); @@ -320,7 +322,10 @@ private static void export( SpatialOperator operator, XMLStreamWriter writer ) GMLStreamWriter gmlWriter = createGml32StreamWriter( writer ); export( operator.getParam1(), writer ); - exportGeometry( geometry, gmlWriter ); + if ( secondParam != null ) + export( secondParam, writer ); + if ( geometry != null ) + exportGeometry( geometry, gmlWriter ); exportDistance( distance, writer ); writer.writeEndElement(); } diff --git a/deegree-core/deegree-core-base/src/test/java/org/deegree/filter/xml/Filter110XMLEncoderTest.java b/deegree-core/deegree-core-base/src/test/java/org/deegree/filter/xml/Filter110XMLEncoderTest.java index e5bbd6d81f..9d885bc39f 100644 --- a/deegree-core/deegree-core-base/src/test/java/org/deegree/filter/xml/Filter110XMLEncoderTest.java +++ b/deegree-core/deegree-core-base/src/test/java/org/deegree/filter/xml/Filter110XMLEncoderTest.java @@ -265,4 +265,13 @@ public void parseWithinFilter() Assert.assertNotNull( filter ); } + + @Test + public void parseIntersectsWithSpatialJoin() + throws XMLStreamException, FactoryConfigurationError, IOException, UnknownCRSException, + TransformationException { + Filter filter = testImportExportImport( "intersectsWithSpatialJoin.xml" ); + Assert.assertNotNull( filter ); + } + } diff --git a/deegree-core/deegree-core-base/src/test/java/org/deegree/filter/xml/Filter200XMLEncoderParameterizedTest.java b/deegree-core/deegree-core-base/src/test/java/org/deegree/filter/xml/Filter200XMLEncoderParameterizedTest.java index 0b62fa904b..9658f3a168 100644 --- a/deegree-core/deegree-core-base/src/test/java/org/deegree/filter/xml/Filter200XMLEncoderParameterizedTest.java +++ b/deegree-core/deegree-core-base/src/test/java/org/deegree/filter/xml/Filter200XMLEncoderParameterizedTest.java @@ -93,6 +93,7 @@ public static List data() asString( "v200/aixm_custom_geometry_property.xml" ) } ); filterTests.add( new Object[] { "aixm_timeinstant_begin.xml", asString( "v200/aixm_timeinstant_begin.xml" ) } ); filterTests.add( new Object[] { "temporal/tequals.xml", asString( "v200/temporal/tequals.xml" ) } ); + filterTests.add( new Object[] { "intersectsWithSpatialJoin.xml", asString( "v200/intersectsWithSpatialJoin.xml" ) } ); return filterTests; } From 066a53beb276a0622db3c245eaf0278757b9bd02 Mon Sep 17 00:00:00 2001 From: Lyn Elisa Goltz Date: Thu, 26 May 2016 13:30:30 +0200 Subject: [PATCH 04/25] #3475 - avoid possible exceptions if geometry is null --- .../src/main/java/org/deegree/filter/Filters.java | 3 ++- .../src/main/java/org/deegree/filter/spatial/Intersects.java | 2 ++ .../services/csw/exporthandling/AdhocQueryAnalyzer.java | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/Filters.java b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/Filters.java index 33bcc281b4..139666642a 100644 --- a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/Filters.java +++ b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/Filters.java @@ -204,7 +204,8 @@ private static BBOX extractBBox( SpatialOperator oper ) { case EQUALS: return new BBOX( ( (Equals) oper ).getParam1(), ( (Equals) oper ).getGeometry().getEnvelope() ); case INTERSECTS: - return new BBOX( ( (Intersects) oper ).getParam1(), ( (Intersects) oper ).getGeometry().getEnvelope() ); + if ( ( (Intersects) oper ).getGeometry() != null ) + return new BBOX( ( (Intersects) oper ).getParam1(), ( (Intersects) oper ).getGeometry().getEnvelope() ); case OVERLAPS: return new BBOX( ( (Overlaps) oper ).getParam1(), ( (Overlaps) oper ).getGeometry().getEnvelope() ); case WITHIN: diff --git a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Intersects.java b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Intersects.java index f75590ca0d..0bf9a62707 100644 --- a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Intersects.java +++ b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Intersects.java @@ -98,6 +98,8 @@ private Intersects( Expression propName, Geometry geometry, ValueReference value @Override public boolean evaluate( T obj, XPathEvaluator xpathEvaluator ) throws FilterEvaluationException { + if ( geometry == null ) + return false; Expression param1 = getParam1(); if ( param1 != null ) { diff --git a/deegree-services/deegree-services-csw/src/main/java/org/deegree/services/csw/exporthandling/AdhocQueryAnalyzer.java b/deegree-services/deegree-services-csw/src/main/java/org/deegree/services/csw/exporthandling/AdhocQueryAnalyzer.java index 35aa39614b..8e8e0c365d 100644 --- a/deegree-services/deegree-services-csw/src/main/java/org/deegree/services/csw/exporthandling/AdhocQueryAnalyzer.java +++ b/deegree-services/deegree-services-csw/src/main/java/org/deegree/services/csw/exporthandling/AdhocQueryAnalyzer.java @@ -304,6 +304,8 @@ private Operator copy( Operator op, Map values ) { return new Equals( copy( equals.getPropName() ), equals.getGeometry() ); case INTERSECTS: Intersects intersects = (Intersects) op; + if ( intersects.getValueReference() != null ) + return new Intersects( copy( intersects.getPropName() ), intersects.getValueReference() ); return new Intersects( copy( intersects.getPropName() ), intersects.getGeometry() ); case OVERLAPS: Overlaps overlaps = (Overlaps) op; From 1951d32b08f09d44e0a9d17c73c15e8d2272a467 Mon Sep 17 00:00:00 2001 From: Lyn Elisa Goltz Date: Mon, 30 May 2016 07:20:52 +0200 Subject: [PATCH 05/25] #3476 - removed check if features requested from different feature stores --- .../services/wfs/query/QueryAnalyzer.java | 23 +++++-------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/deegree-services/deegree-services-wfs/src/main/java/org/deegree/services/wfs/query/QueryAnalyzer.java b/deegree-services/deegree-services-wfs/src/main/java/org/deegree/services/wfs/query/QueryAnalyzer.java index ca2f06a881..c66693702c 100644 --- a/deegree-services/deegree-services-wfs/src/main/java/org/deegree/services/wfs/query/QueryAnalyzer.java +++ b/deegree-services/deegree-services-wfs/src/main/java/org/deegree/services/wfs/query/QueryAnalyzer.java @@ -363,7 +363,6 @@ private Query validateQuery( org.deegree.protocol.wfs.query.Query wfsQuery ) // requalify query typenames and keep track of them TypeName[] wfsTypeNames = ( (AdHocQuery) wfsQuery ).getTypeNames(); TypeName[] typeNames = new TypeName[wfsTypeNames.length]; - FeatureStore commonFs = null; for ( int i = 0; i < wfsTypeNames.length; i++ ) { String alias = wfsTypeNames[i].getAlias(); FeatureType ft = service.lookupFeatureType( wfsTypeNames[i].getFeatureTypeName() ); @@ -372,15 +371,6 @@ private Query validateQuery( org.deegree.protocol.wfs.query.Query wfsQuery ) + "' is not served by this WFS."; throw new OWSException( msg, INVALID_PARAMETER_VALUE, "typeName" ); } - FeatureStore fs = service.getStore( ft.getName() ); - if ( commonFs != null ) { - if ( fs != commonFs ) { - String msg = "Requested join of feature types from different feature stores. This is not supported."; - throw new OWSException( msg, INVALID_PARAMETER_VALUE, "typeName" ); - } - } else { - commonFs = fs; - } requestedFts.add( ft ); QName ftName = ft.getName(); typeNames[i] = new TypeName( ftName, alias ); @@ -422,7 +412,8 @@ private Query validateQuery( org.deegree.protocol.wfs.query.Query wfsQuery ) } } if ( checkAreaOfUse ) { - validateGeometryConstraint( ( (BBoxQuery) wfsQuery ).getBBox(), ( (AdHocQuery) wfsQuery ).getSrsName() ); + validateGeometryConstraint( ( (BBoxQuery) wfsQuery ).getBBox(), + ( (AdHocQuery) wfsQuery ).getSrsName() ); } Envelope bbox = bboxQuery.getBBox(); @@ -502,8 +493,7 @@ private void validatePropertyName( ValueReference propName, TypeName[] typeNames */ private boolean isPrefixedAndBound( ValueReference propName ) { QName name = propName.getAsQName(); - return !name.getPrefix().equals( DEFAULT_NS_PREFIX ) - && !name.getNamespaceURI().equals( "" ); + return !name.getPrefix().equals( DEFAULT_NS_PREFIX ) && !name.getNamespaceURI().equals( "" ); } /** @@ -558,7 +548,8 @@ private QName getPropertyNameAsQName( ValueReference propName ) { String s = propName.getAsText(); int colonIdx = s.indexOf( ':' ); if ( !s.contains( "/" ) && colonIdx != -1 ) { - if ( Character.isLetterOrDigit( s.charAt( 0 ) ) && Character.isLetterOrDigit( s.charAt( s.length() - 1 ) ) ) { + if ( Character.isLetterOrDigit( s.charAt( 0 ) ) + && Character.isLetterOrDigit( s.charAt( s.length() - 1 ) ) ) { String prefix = s.substring( 0, colonIdx ); String localName = s.substring( colonIdx + 1, s.length() ); String nsUri = null; @@ -596,9 +587,7 @@ private void validateGeometryConstraint( Geometry geom, ICRS queriedCrs ) domainOfValidity = transform( domainOfValidity, bbox.getCoordinateSystem() ); if ( !bbox.isWithin( domainOfValidity ) ) { String msg = "Invalid geometry constraint in filter. The envelope of the geometry is not within the domain of validity ('" - + domainOfValidity - + "') of its CRS ('" - + bbox.getCoordinateSystem().getAlias() + + domainOfValidity + "') of its CRS ('" + bbox.getCoordinateSystem().getAlias() + "')."; throw new OWSException( msg, INVALID_PARAMETER_VALUE, "filter" ); } From a26a9dab918b0d130e69ccadf3eb12afef2262b4 Mon Sep 17 00:00:00 2001 From: Lyn Elisa Goltz Date: Mon, 30 May 2016 07:32:21 +0200 Subject: [PATCH 06/25] #3475 - fixed ClassCastException --- .../src/main/java/org/deegree/filter/Filters.java | 1 + 1 file changed, 1 insertion(+) diff --git a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/Filters.java b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/Filters.java index 139666642a..297615fe31 100644 --- a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/Filters.java +++ b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/Filters.java @@ -206,6 +206,7 @@ private static BBOX extractBBox( SpatialOperator oper ) { case INTERSECTS: if ( ( (Intersects) oper ).getGeometry() != null ) return new BBOX( ( (Intersects) oper ).getParam1(), ( (Intersects) oper ).getGeometry().getEnvelope() ); + return null; case OVERLAPS: return new BBOX( ( (Overlaps) oper ).getParam1(), ( (Overlaps) oper ).getGeometry().getEnvelope() ); case WITHIN: From a796a40733327c75963f3962941507af6a1492b2 Mon Sep 17 00:00:00 2001 From: Lyn Elisa Goltz Date: Mon, 30 May 2016 11:06:14 +0200 Subject: [PATCH 07/25] #3467 - enhanced SQLFeatureStore and FeatureBuilderRelational to allow multiple FeatureTypes --- .../filter/CombinedPropertyNameMapper.java | 100 +++++++++ .../sqldialect/filter/TableAliasManager.java | 22 ++ .../persistence/sql/SQLFeatureStore.java | 106 ++++++--- .../sql/rules/FeatureBuilderRelational.java | 201 +++++++++++------- .../persistence/sql/xpath/MappedXPath.java | 2 +- 5 files changed, 323 insertions(+), 108 deletions(-) create mode 100644 deegree-core/deegree-core-sqldialect/deegree-sqldialect-commons/src/main/java/org/deegree/sqldialect/filter/CombinedPropertyNameMapper.java diff --git a/deegree-core/deegree-core-sqldialect/deegree-sqldialect-commons/src/main/java/org/deegree/sqldialect/filter/CombinedPropertyNameMapper.java b/deegree-core/deegree-core-sqldialect/deegree-sqldialect-commons/src/main/java/org/deegree/sqldialect/filter/CombinedPropertyNameMapper.java new file mode 100644 index 0000000000..fb065c367b --- /dev/null +++ b/deegree-core/deegree-core-sqldialect/deegree-sqldialect-commons/src/main/java/org/deegree/sqldialect/filter/CombinedPropertyNameMapper.java @@ -0,0 +1,100 @@ +//$HeadURL$ +/*---------------------------------------------------------------------------- + This file is part of deegree, http://deegree.org/ + Copyright (C) 2001-2015 by: + - Department of Geography, University of Bonn - + and + - lat/lon GmbH - + + This library is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at your option) + any later version. + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + details. + You should have received a copy of the GNU Lesser General Public License + along with this library; if not, write to the Free Software Foundation, Inc., + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Contact information: + + lat/lon GmbH + Aennchenstr. 19, 53177 Bonn + Germany + http://lat-lon.de/ + + Department of Geography, University of Bonn + Prof. Dr. Klaus Greve + Postfach 1147, 53001 Bonn + Germany + http://www.geographie.uni-bonn.de/deegree/ + + e-mail: info@deegree.org +----------------------------------------------------------------------------*/ +package org.deegree.sqldialect.filter; + +import java.util.ArrayList; +import java.util.List; + +import org.deegree.filter.FilterEvaluationException; +import org.deegree.filter.expression.ValueReference; + +/** + * Combines {@link PropertyNameMapper}s. + * + * @author Lyn Goltz + */ +public class CombinedPropertyNameMapper implements PropertyNameMapper { + + private final List propertyNameMappers; + + /** + * Instantiates an {@link CombinedPropertyNameMapper} without propertyNameMappers. + */ + public CombinedPropertyNameMapper() { + this( new ArrayList() ); + } + + /** + * Instantiates an {@link CombinedPropertyNameMapper} with a list of propertyNameMappers. + * + * @param propertyNameMappers + * never null + */ + public CombinedPropertyNameMapper( List propertyNameMappers ) { + this.propertyNameMappers = propertyNameMappers; + } + + /** + * @param mapperToAdd + * added to the list of combined propertyNameMappers, never null + */ + public void addPropertyNameMapper( PropertyNameMapper mapperToAdd ) { + propertyNameMappers.add( mapperToAdd ); + } + + @Override + public PropertyNameMapping getMapping( ValueReference propName, TableAliasManager aliasManager ) + throws FilterEvaluationException, UnmappableException { + for ( PropertyNameMapper propertyNameMapper : propertyNameMappers ) { + PropertyNameMapping mapping = propertyNameMapper.getMapping( propName, aliasManager ); + if ( mapping != null ) + return mapping; + } + return null; + } + + @Override + public PropertyNameMapping getSpatialMapping( ValueReference propName, TableAliasManager aliasManager ) + throws FilterEvaluationException, UnmappableException { + for ( PropertyNameMapper propertyNameMapper : propertyNameMappers ) { + PropertyNameMapping mapping = propertyNameMapper.getSpatialMapping( propName, aliasManager ); + if ( mapping != null ) + return mapping; + } + return null; + } + +} \ No newline at end of file diff --git a/deegree-core/deegree-core-sqldialect/deegree-sqldialect-commons/src/main/java/org/deegree/sqldialect/filter/TableAliasManager.java b/deegree-core/deegree-core-sqldialect/deegree-sqldialect-commons/src/main/java/org/deegree/sqldialect/filter/TableAliasManager.java index bebd7216e5..8ef557b443 100644 --- a/deegree-core/deegree-core-sqldialect/deegree-sqldialect-commons/src/main/java/org/deegree/sqldialect/filter/TableAliasManager.java +++ b/deegree-core/deegree-core-sqldialect/deegree-sqldialect-commons/src/main/java/org/deegree/sqldialect/filter/TableAliasManager.java @@ -35,6 +35,10 @@ ----------------------------------------------------------------------------*/ package org.deegree.sqldialect.filter; +import java.util.HashMap; +import java.util.Map; + +import org.deegree.commons.jdbc.TableName; import org.deegree.filter.expression.ValueReference; /** @@ -49,6 +53,8 @@ */ public class TableAliasManager { + private final Map aliases = new HashMap(); + private final String rootTableAlias; private int currentIdx = 1; @@ -61,14 +67,30 @@ public TableAliasManager() { } /** + * Deprecated: Use #getTableAlias(TableName) instead. + * * Returns the table alias for the root table. * * @return the table alias for the root table, never null */ + @Deprecated public String getRootTableAlias() { return rootTableAlias; } + /** + * Returns the table alias for the passed {@link TableName}. + * + * @param tableName + * to retrieve the alias for, never null + * @return the table alias of the passed {@link TableName}, never null + */ + public String getTableAlias( TableName tableName ) { + if ( !aliases.containsKey( tableName ) ) + aliases.put( tableName, generateNew() ); + return aliases.get( tableName ); + } + /** * Returns a new unique table alias. * diff --git a/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/SQLFeatureStore.java b/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/SQLFeatureStore.java index 6ca03faabf..001435b3a8 100644 --- a/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/SQLFeatureStore.java +++ b/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/SQLFeatureStore.java @@ -35,6 +35,7 @@ ----------------------------------------------------------------------------*/ package org.deegree.feature.persistence.sql; +import static java.util.Collections.singletonList; import static org.deegree.commons.xml.CommonNamespaces.OGCNS; import static org.deegree.commons.xml.CommonNamespaces.XLNNS; import static org.deegree.commons.xml.CommonNamespaces.XSINS; @@ -48,6 +49,7 @@ import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; @@ -125,8 +127,10 @@ import org.deegree.geometry.Envelope; import org.deegree.geometry.Geometry; import org.deegree.geometry.GeometryTransformer; +import org.deegree.protocol.wfs.getfeature.TypeName; import org.deegree.sqldialect.SQLDialect; import org.deegree.sqldialect.filter.AbstractWhereBuilder; +import org.deegree.sqldialect.filter.CombinedPropertyNameMapper; import org.deegree.sqldialect.filter.DBField; import org.deegree.sqldialect.filter.Join; import org.deegree.sqldialect.filter.MappingExpression; @@ -220,10 +224,10 @@ public SQLFeatureStore( SQLFeatureStoreJAXB config, URL configURL, SQLDialect di this.jdbcConnId = config.getJDBCConnId().getValue(); this.allowInMemoryFiltering = config.getDisablePostFiltering() == null; fetchSize = config.getJDBCConnId().getFetchSize() != null ? config.getJDBCConnId().getFetchSize().intValue() - : DEFAULT_FETCH_SIZE; + : DEFAULT_FETCH_SIZE; LOG.debug( "Fetch size: " + fetchSize ); readAutoCommit = config.getJDBCConnId().isReadAutoCommit() != null ? config.getJDBCConnId().isReadAutoCommit() - : !dialect.requiresTransactionForCursorMode(); + : !dialect.requiresTransactionForCursorMode(); LOG.debug( "Read auto commit: " + readAutoCommit ); if ( config.getFeatureCache() != null ) { @@ -272,8 +276,8 @@ private void initConverter( Mapping particleMapping ) { if ( fm.getValueFtName() != null ) { valueFt = schema.getFeatureType( fm.getValueFtName() ); } - ParticleConverter converter = new FeatureParticleConverter( fkColumn, hrefColumn, getResolver(), - valueFt, schema ); + ParticleConverter converter = new FeatureParticleConverter( fkColumn, hrefColumn, getResolver(), valueFt, + schema ); particleMappingToConverter.put( particleMapping, converter ); } else if ( particleMapping instanceof CompoundMapping ) { CompoundMapping cm = (CompoundMapping) particleMapping; @@ -572,7 +576,8 @@ public FeatureStoreTransaction acquireTransaction() ta = new SQLFeatureStoreTransaction( this, conn, getSchema(), inspectors ); transaction.set( ta ); } catch ( SQLException e ) { - throw new FeatureStoreException( "Unable to acquire JDBC connection for transaction: " + e.getMessage(), e ); + throw new FeatureStoreException( "Unable to acquire JDBC connection for transaction: " + e.getMessage(), + e ); } return ta; } @@ -676,7 +681,7 @@ private int queryHitsByOperatorFilter( Query query, QName ftName, OperatorFilter if ( wb.getPostFilter() != null ) { LOG.debug( "Filter not fully mappable to WHERE clause. Need to iterate over all features to determine count." ); - hits = queryByOperatorFilter( query, ftName, filter ).count(); + hits = queryByOperatorFilter( query, singletonList( ftName ), filter ).count(); } else { StringBuilder sql = new StringBuilder( "SELECT " ); if ( wb.getWhere() == null ) { @@ -878,22 +883,16 @@ short getFtId( QName ftName ) { public FeatureInputStream query( Query query ) throws FeatureStoreException, FilterEvaluationException { - if ( query.getTypeNames() == null || query.getTypeNames().length > 1 ) { - String msg = "Join queries between multiple feature types are not by SQLFeatureStore (yet)."; + if ( query.getTypeNames() == null ) { + String msg = "Requestes TypeNames are not available, this ist not supported."; throw new UnsupportedOperationException( msg ); } FeatureInputStream result = null; Filter filter = query.getFilter(); - if ( query.getTypeNames().length == 1 && ( filter == null || filter instanceof OperatorFilter ) ) { - QName ftName = query.getTypeNames()[0].getFeatureTypeName(); - FeatureType ft = getSchema().getFeatureType( ftName ); - if ( ft == null ) { - String msg = "Feature store is not configured to serve feature type '" + ftName + "'."; - throw new FeatureStoreException( msg ); - } - result = queryByOperatorFilter( query, ftName, (OperatorFilter) filter ); + if ( filter == null || filter instanceof OperatorFilter ) { + result = queryByOperatorFilter( query, (OperatorFilter) filter ); } else { // must be an id filter based query if ( query.getFilter() == null || !( query.getFilter() instanceof IdFilter ) ) { @@ -1033,8 +1032,7 @@ private FeatureInputStream queryByIdFilterRelational( IdFilter filter, SortPrope } if ( ftNameToIdAnalysis.size() != 1 ) { - throw new FeatureStoreException( - "Currently, only relational id queries are supported that target single feature types." ); + throw new FeatureStoreException( "Currently, only relational id queries are supported that target single feature types." ); } QName ftName = ftNameToIdAnalysis.keySet().iterator().next(); @@ -1270,18 +1268,37 @@ private FeatureInputStream queryByOperatorFilterBlob( Query query, QName ftName, if ( query.getSortProperties().length > 0 ) { LOG.debug( "Applying in-memory post-sorting." ); - result = new MemoryFeatureInputStream( Features.sortFc( result.toCollection(), query.getSortProperties() ) ); + result = new MemoryFeatureInputStream( Features.sortFc( result.toCollection(), + query.getSortProperties() ) ); } return result; } - private FeatureInputStream queryByOperatorFilter( Query query, QName ftName, OperatorFilter filter ) + private FeatureInputStream queryByOperatorFilter( Query query, OperatorFilter filter ) throws FeatureStoreException { + List ftNames = new ArrayList(); + for ( TypeName typeName : query.getTypeNames() ) { + QName ftName = typeName.getFeatureTypeName(); + FeatureType ft = getSchema().getFeatureType( ftName ); + if ( ft == null ) { + String msg = "Feature store is not configured to serve feature type '" + ftName + "'."; + throw new FeatureStoreException( msg ); + } + ftNames.add( ftName ); + } + return queryByOperatorFilter( query, ftNames, filter ); + } + private FeatureInputStream queryByOperatorFilter( Query query, List ftNames, OperatorFilter filter ) + throws FeatureStoreException { LOG.debug( "Performing query by operator filter" ); if ( getSchema().getBlobMapping() != null ) { - return queryByOperatorFilterBlob( query, ftName, filter ); + if ( ftNames.size() > 1 ) { + String msg = "Join queries between multiple feature types in blob mode are not by SQLFeatureStore (yet)."; + throw new FeatureStoreException( msg ); + } + return queryByOperatorFilterBlob( query, ftNames.get( 0 ), filter ); } AbstractWhereBuilder wb = null; @@ -1290,22 +1307,26 @@ private FeatureInputStream queryByOperatorFilter( Query query, QName ftName, Ope PreparedStatement stmt = null; ResultSet rs = null; - FeatureType ft = getSchema().getFeatureType( ftName ); - FeatureTypeMapping ftMapping = getMapping( ftName ); - if ( ftMapping == null ) { - String msg = "Cannot perform query on feature type '" + ftName + "'. Feature type is not mapped."; - throw new FeatureStoreException( msg ); + Map featureTypeAndMappings = new HashMap(); + for ( QName ftName : ftNames ) { + FeatureType ft = getSchema().getFeatureType( ftName ); + FeatureTypeMapping ftMapping = getMapping( ftName ); + if ( ftMapping == null ) { + String msg = "Cannot perform query on feature type '" + ftNames + "'. Feature type is not mapped."; + throw new FeatureStoreException( msg ); + } + featureTypeAndMappings.put( ft, ftMapping ); } try { conn = getConnection(); - wb = getWhereBuilder( ft, filter, query.getSortProperties(), conn ); - String ftTableAlias = wb.getAliasManager().getRootTableAlias(); + wb = getWhereBuilder( featureTypeAndMappings.values(), filter, query.getSortProperties(), conn ); + TableAliasManager aliasManager = wb.getAliasManager(); LOG.debug( "WHERE clause: " + wb.getWhere() ); LOG.debug( "ORDER BY clause: " + wb.getOrderBy() ); - FeatureBuilder builder = new FeatureBuilderRelational( this, ft, ftMapping, conn, ftTableAlias, + FeatureBuilder builder = new FeatureBuilderRelational( this, featureTypeAndMappings, conn, aliasManager, nullEscalation ); List columns = builder.getInitialSelectList(); @@ -1320,9 +1341,16 @@ private FeatureInputStream queryByOperatorFilter( Query query, QName ftName, Ope sql.append( " FROM " ); // pure relational query - sql.append( ftMapping.getFtTable() ); - sql.append( ' ' ); - sql.append( ftTableAlias ); + boolean isFirst = true; + for ( FeatureTypeMapping ftMapping : featureTypeAndMappings.values() ) { + if ( !isFirst ) + sql.append( ", " ); + TableName ftTable = ftMapping.getFtTable(); + sql.append( ftTable ); + sql.append( ' ' ); + sql.append( aliasManager.getTableAlias( ftTable ) ); + isFirst = false; + } for ( PropertyNameMapping mappedPropName : wb.getMappedPropertyNames() ) { for ( Join join : mappedPropName.getJoins() ) { @@ -1465,11 +1493,23 @@ private short[] getQueriedFeatureTypeIds( Query[] queries ) { private AbstractWhereBuilder getWhereBuilder( FeatureType ft, OperatorFilter filter, SortProperty[] sortCrit, Connection conn ) - throws FilterEvaluationException, UnmappableException { + throws FilterEvaluationException, + UnmappableException { PropertyNameMapper mapper = new SQLPropertyNameMapper( this, getMapping( ft.getName() ) ); return dialect.getWhereBuilder( mapper, filter, sortCrit, allowInMemoryFiltering ); } + private AbstractWhereBuilder getWhereBuilder( Collection ftMappings, OperatorFilter filter, + SortProperty[] sortCrit, Connection conn ) + throws FilterEvaluationException, + UnmappableException { + CombinedPropertyNameMapper mapper = new CombinedPropertyNameMapper(); + for ( FeatureTypeMapping ftMapping : ftMappings ) { + mapper.addPropertyNameMapper( new SQLPropertyNameMapper( this, ftMapping ) ); + } + return dialect.getWhereBuilder( mapper, filter, sortCrit, allowInMemoryFiltering ); + } + private AbstractWhereBuilder getWhereBuilderBlob( OperatorFilter filter, Connection conn ) throws FilterEvaluationException, UnmappableException { final String undefinedSrid = dialect.getUndefinedSrid(); diff --git a/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/rules/FeatureBuilderRelational.java b/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/rules/FeatureBuilderRelational.java index d00dd12c08..c60bacaaad 100644 --- a/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/rules/FeatureBuilderRelational.java +++ b/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/rules/FeatureBuilderRelational.java @@ -54,6 +54,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import javax.xml.namespace.QName; import javax.xml.stream.XMLInputFactory; @@ -85,6 +86,7 @@ import org.deegree.feature.persistence.sql.SQLFeatureStore; import org.deegree.feature.persistence.sql.expressions.TableJoin; import org.deegree.feature.property.GenericProperty; +import org.deegree.feature.types.AppSchema; import org.deegree.feature.types.AppSchemaGeometryHierarchy; import org.deegree.feature.types.FeatureType; import org.deegree.feature.types.property.ObjectPropertyType; @@ -104,6 +106,7 @@ import org.deegree.gml.schema.GMLSchemaInfoSet; import org.deegree.sqldialect.filter.DBField; import org.deegree.sqldialect.filter.MappingExpression; +import org.deegree.sqldialect.filter.TableAliasManager; import org.deegree.time.TimeObject; import org.jaxen.expr.Expr; import org.jaxen.expr.LocationPath; @@ -130,12 +133,12 @@ public class FeatureBuilderRelational implements FeatureBuilder { private final SQLFeatureStore fs; - private final FeatureType ft; - - private final FeatureTypeMapping ftMapping; + private final Map featureTypeAndMappings; private final Connection conn; + private final TableAliasManager tableAliasManager; + private final String tableAlias; private final NamespaceBindings nsBindings; @@ -157,16 +160,44 @@ public class FeatureBuilderRelational implements FeatureBuilder { * feature type mapping, must not be null * @param conn * JDBC connection (used for performing subsequent SELECTs), must not be null - * @param escalationPolicy + * @param ftTableAlias + * the alias of the feature table, must not be null + * @param nullEscalation * the void escalation policy, must not be null */ + @Deprecated public FeatureBuilderRelational( SQLFeatureStore fs, FeatureType ft, FeatureTypeMapping ftMapping, Connection conn, String ftTableAlias, boolean nullEscalation ) { + this( fs, Collections.singletonMap( ft, ftMapping ), conn, null, ftTableAlias, nullEscalation ); + } + + /** + * Creates a new {@link FeatureBuilderRelational} instance. + * + * @param fs + * feature store, must not be null + * @param featureTypeAndMappings + * feature types and their mappings, must not be null and empty + * @param conn + * JDBC connection (used for performing subsequent SELECTs), must not be null + * @param tableAliasManager + * the manager of the table aliases, must not be null + * @param nullEscalation + * the void escalation policy, must not be null + */ + public FeatureBuilderRelational( SQLFeatureStore fs, Map featureTypeAndMappings, + Connection conn, TableAliasManager tableAliasManager, boolean nullEscalation ) { + this( fs, featureTypeAndMappings, conn, tableAliasManager, null, nullEscalation ); + } + + private FeatureBuilderRelational( SQLFeatureStore fs, Map featureTypeAndMappings, + Connection conn, TableAliasManager tableAliasManager, String tableAlias, + boolean nullEscalation ) { this.fs = fs; - this.ft = ft; - this.ftMapping = ftMapping; + this.featureTypeAndMappings = featureTypeAndMappings; this.conn = conn; - this.tableAlias = ftTableAlias; + this.tableAliasManager = tableAliasManager; + this.tableAlias = tableAlias; this.nullEscalation = nullEscalation; this.nsBindings = new NamespaceBindings(); for ( String prefix : fs.getNamespaceContext().keySet() ) { @@ -182,11 +213,14 @@ public FeatureBuilderRelational( SQLFeatureStore fs, FeatureType ft, FeatureType @Override public List getInitialSelectList() { - for ( Pair fidColumn : ftMapping.getFidMapping().getColumns() ) { - addColumn( qualifiedSqlExprToRsIdx, tableAlias + "." + fidColumn.first.getName() ); - } - for ( Mapping mapping : ftMapping.getMappings() ) { - addSelectColumns( mapping, qualifiedSqlExprToRsIdx, true ); + for ( FeatureTypeMapping ftMapping : featureTypeAndMappings.values() ) { + String alias = detectTableAlias( ftMapping ); + for ( Pair fidColumn : ftMapping.getFidMapping().getColumns() ) { + addColumn( qualifiedSqlExprToRsIdx, alias + "." + fidColumn.first.getName() ); + } + for ( Mapping mapping : ftMapping.getMappings() ) { + addSelectColumns( mapping, qualifiedSqlExprToRsIdx, alias, true ); + } } LOG.debug( "Initial select columns: " + qualifiedSqlExprToRsIdx ); return new ArrayList( qualifiedSqlExprToRsIdx.keySet() ); @@ -198,13 +232,14 @@ private void addColumn( LinkedHashMap colToRsIdx, String column } } - private LinkedHashMap getSubsequentSelectColumns( Mapping mapping ) { + private LinkedHashMap getSubsequentSelectColumns( Mapping mapping, String tableAlias ) { LinkedHashMap colToRsIdx = new LinkedHashMap(); - addSelectColumns( mapping, colToRsIdx, false ); + addSelectColumns( mapping, colToRsIdx, tableAlias, false ); return colToRsIdx; } - private void addSelectColumns( Mapping mapping, LinkedHashMap colToRsIdx, boolean initial ) { + private void addSelectColumns( Mapping mapping, LinkedHashMap colToRsIdx, String tableAlias, + boolean initial ) { List jc = mapping.getJoinedTable(); if ( jc != null && initial ) { if ( mapping instanceof FeatureMapping ) { @@ -242,7 +277,7 @@ private void addSelectColumns( Mapping mapping, LinkedHashMap c } else if ( mapping instanceof CompoundMapping ) { CompoundMapping cm = (CompoundMapping) mapping; for ( Mapping particle : cm.getParticles() ) { - addSelectColumns( particle, colToRsIdx, true ); + addSelectColumns( particle, colToRsIdx, tableAlias, true ); } } else if ( mapping instanceof SqlExpressionMapping ) { // nothing to do @@ -258,38 +293,44 @@ public Feature buildFeature( ResultSet rs ) Feature feature = null; try { - String gmlId = ftMapping.getFidMapping().getPrefix(); - List> fidColumns = ftMapping.getFidMapping().getColumns(); - gmlId += rs.getObject( qualifiedSqlExprToRsIdx.get( tableAlias + "." + fidColumns.get( 0 ).first ) ); - for ( int i = 1; i < fidColumns.size(); i++ ) { - gmlId += ftMapping.getFidMapping().getDelimiter() - + rs.getObject( qualifiedSqlExprToRsIdx.get( tableAlias + "." + fidColumns.get( i ).first ) ); - } - if ( fs.getCache() != null ) { - feature = (Feature) fs.getCache().get( gmlId ); - } - if ( feature == null ) { - LOG.debug( "Recreating feature '" + gmlId + "' from db (relational mode)." ); - List props = new ArrayList(); - for ( Mapping mapping : ftMapping.getMappings() ) { - ValueReference propName = mapping.getPath(); - QName childEl = getChildElementStepAsQName( propName ); - if ( childEl != null ) { - PropertyType pt = ft.getPropertyDeclaration( childEl ); - String idPrefix = gmlId + "_" + toIdPrefix( propName ); - addProperties( props, pt, mapping, rs, idPrefix ); - } else { - LOG.warn( "Omitting mapping '" + mapping - + "'. Only single child element steps (optionally with number predicate)" - + " are currently supported." ); - } + for ( Entry featureTypeAndMapping : featureTypeAndMappings.entrySet() ) { + FeatureType ft = featureTypeAndMapping.getKey(); + FeatureTypeMapping ftMapping = featureTypeAndMapping.getValue(); + String tableAlias = detectTableAlias( ftMapping ); + String gmlId = ftMapping.getFidMapping().getPrefix(); + List> fidColumns = ftMapping.getFidMapping().getColumns(); + gmlId += rs.getObject( qualifiedSqlExprToRsIdx.get( tableAlias + "." + fidColumns.get( 0 ).first ) ); + for ( int i = 1; i < fidColumns.size(); i++ ) { + gmlId += ftMapping.getFidMapping().getDelimiter() + + rs.getObject( qualifiedSqlExprToRsIdx.get( tableAlias + "." + + fidColumns.get( i ).first ) ); } - feature = ft.newFeature( gmlId, props, null ); if ( fs.getCache() != null ) { - fs.getCache().add( feature ); + feature = (Feature) fs.getCache().get( gmlId ); + } + if ( feature == null ) { + LOG.debug( "Recreating feature '" + gmlId + "' from db (relational mode)." ); + List props = new ArrayList(); + for ( Mapping mapping : ftMapping.getMappings() ) { + ValueReference propName = mapping.getPath(); + QName childEl = getChildElementStepAsQName( propName ); + if ( childEl != null ) { + PropertyType pt = ft.getPropertyDeclaration( childEl ); + String idPrefix = gmlId + "_" + toIdPrefix( propName ); + addProperties( ft, props, pt, mapping, rs, tableAlias, idPrefix ); + } else { + LOG.warn( "Omitting mapping '" + mapping + + "'. Only single child element steps (optionally with number predicate)" + + " are currently supported." ); + } + } + feature = ft.newFeature( gmlId, props, null ); + if ( fs.getCache() != null ) { + fs.getCache().add( feature ); + } + } else { + LOG.debug( "Cache hit." ); } - } else { - LOG.debug( "Cache hit." ); } } catch ( Throwable t ) { LOG.error( t.getMessage(), t ); @@ -308,10 +349,11 @@ private String toIdPrefix( ValueReference propName ) { return s; } - private void addProperties( List props, PropertyType pt, Mapping propMapping, ResultSet rs, - String idPrefix ) - throws SQLException { - List particles = buildParticles( propMapping, rs, qualifiedSqlExprToRsIdx, idPrefix ); + private void addProperties( FeatureType ft, List props, PropertyType pt, Mapping propMapping, + ResultSet rs, String tableAlias, String idPrefix ) + throws SQLException { + List particles = buildParticles( propMapping, rs, qualifiedSqlExprToRsIdx, tableAlias, + idPrefix ); if ( particles.isEmpty() && pt.getMinOccurs() > 0 ) { if ( pt.isNillable() ) { Map attrs = Collections.singletonMap( new QName( CommonNamespaces.XSINS, "nil" ), @@ -326,7 +368,7 @@ private void addProperties( List props, PropertyType pt, Mapping propM for ( final TypedObjectNode particle : particles ) { if ( particle instanceof GenericXMLElement ) { if ( pt instanceof ObjectPropertyType && particle instanceof TimeObject ) { - props.add( recreatePropertyFromGml( pt, (GenericXMLElement) particle ) ); + props.add( recreatePropertyFromGml( ft, pt, (GenericXMLElement) particle ) ); } else { GenericXMLElement xmlEl = (GenericXMLElement) particle; props.add( new GenericProperty( pt, xmlEl.getName(), null, xmlEl.getAttributes(), @@ -365,12 +407,14 @@ private void addProperties( List props, PropertyType pt, Mapping propM // } // } - private Property recreatePropertyFromGml( final PropertyType pt, final GenericXMLElement particle ) { + private Property recreatePropertyFromGml( FeatureType ft, final PropertyType pt, + final GenericXMLElement particle ) { try { - final GMLSchemaInfoSet gmlSchema = ft.getSchema().getGMLSchema(); + AppSchema schema = ft.getSchema(); + final GMLSchemaInfoSet gmlSchema = schema.getGMLSchema(); final ByteArrayOutputStream bos = new ByteArrayOutputStream(); final XMLStreamWriter xmlWriter = XMLOutputFactory.newInstance().createXMLStreamWriter( bos ); - final GMLVersion version = ft.getSchema().getGMLSchema().getVersion(); + final GMLVersion version = gmlSchema.getVersion(); final GMLStreamWriter gmlWriter = GMLOutputFactory.createGMLStreamWriter( version, xmlWriter ); gmlWriter.setNamespaceBindings( gmlSchema.getNamespacePrefixes() ); final GmlXlinkOptions resolveState = new GmlXlinkOptions(); @@ -381,10 +425,9 @@ private Property recreatePropertyFromGml( final PropertyType pt, final GenericXM final InputStream is = new ByteArrayInputStream( bos.toByteArray() ); final XMLStreamReader xmlReader = XMLInputFactory.newInstance().createXMLStreamReader( is ); final GMLStreamReader gmlReader = GMLInputFactory.createGMLStreamReader( version, xmlReader ); - gmlReader.setApplicationSchema( ft.getSchema() ); + gmlReader.setApplicationSchema( schema ); gmlReader.setLaxMode( true ); - final Property property = gmlReader.getFeatureReader().parseProperty( new XMLStreamReaderWrapper( - xmlReader, + final Property property = gmlReader.getFeatureReader().parseProperty( new XMLStreamReaderWrapper( xmlReader, null ), pt, null ); return property; @@ -395,19 +438,22 @@ private Property recreatePropertyFromGml( final PropertyType pt, final GenericXM } private List buildParticles( Mapping mapping, ResultSet rs, - LinkedHashMap colToRsIdx, String idPrefix ) - throws SQLException { + LinkedHashMap colToRsIdx, String tableAlias, + String idPrefix ) + throws SQLException { if ( !( mapping instanceof FeatureMapping ) && mapping.getJoinedTable() != null ) { List values = new ArrayList(); ResultSet rs2 = null; try { Pair> p = getJoinedResultSet( mapping.getJoinedTable().get( 0 ), - mapping, rs, colToRsIdx ); + mapping, rs, colToRsIdx, + tableAlias ); rs2 = p.first; int i = 0; while ( rs2.next() ) { - TypedObjectNode particle = buildParticle( mapping, rs2, p.second, idPrefix + "_" + ( i++ ) ); + TypedObjectNode particle = buildParticle( mapping, rs2, p.second, tableAlias, + idPrefix + "_" + ( i++ ) ); if ( particle != null ) { values.add( particle ); } @@ -420,7 +466,7 @@ private List buildParticles( Mapping mapping, ResultSet rs, } return values; } - TypedObjectNode particle = buildParticle( mapping, rs, colToRsIdx, idPrefix ); + TypedObjectNode particle = buildParticle( mapping, rs, colToRsIdx, tableAlias, idPrefix ); if ( particle != null ) { return Collections.singletonList( particle ); } @@ -428,8 +474,8 @@ private List buildParticles( Mapping mapping, ResultSet rs, } private TypedObjectNode buildParticle( Mapping mapping, ResultSet rs, LinkedHashMap colToRsIdx, - String idPrefix ) - throws SQLException { + String tableAlias, String idPrefix ) + throws SQLException { LOG.debug( "Trying to build particle with path {}.", mapping.getPath() ); @@ -472,7 +518,8 @@ private TypedObjectNode buildParticle( Mapping mapping, ResultSet rs, LinkedHash for ( Mapping particleMapping : cm.getParticles() ) { // TODO idPrefix - List particleValues = buildParticles( particleMapping, rs, colToRsIdx, idPrefix ); + List particleValues = buildParticles( particleMapping, rs, colToRsIdx, tableAlias, + idPrefix ); if ( !particleMapping.isVoidable() ) { boolean found = false; @@ -535,9 +582,7 @@ private TypedObjectNode buildParticle( Mapping mapping, ResultSet rs, LinkedHash if ( particleValue instanceof PrimitiveValue ) { // TODO XSElementDeclaration childType = null; - GenericXMLElement child = new GenericXMLElement( - name, - childType, + GenericXMLElement child = new GenericXMLElement( name, childType, Collections. emptyMap(), Collections.singletonList( particleValue ) ); children.add( child ); @@ -636,7 +681,8 @@ private TypedObjectNode unwrapCustomGeometry( GenericXMLElement particle ) { } else if ( child instanceof GenericXMLElement ) { GenericXMLElement xmlEl = (GenericXMLElement) child; PropertyType pt = ot.getPropertyDeclaration( xmlEl.getName() ); - props.add( new GenericProperty( pt, xmlEl.getName(), null, xmlEl.getAttributes(), xmlEl.getChildren() ) ); + props.add( new GenericProperty( pt, xmlEl.getName(), null, xmlEl.getAttributes(), + xmlEl.getChildren() ) ); } else { LOG.warn( "Unhandled particle: " + child ); } @@ -654,7 +700,8 @@ private TypedObjectNode unwrapCustomGeometry( GenericXMLElement particle ) { List patches = new ArrayList(); patches.add( geomFac.createPolygonPatch( p.getExteriorRing(), p.getInteriorRings() ) ); geom = geomFac.createSurface( geom.getId(), patches, geom.getCoordinateSystem() ); - } else if ( hierarchy.getCurveSubstitutions().contains( particle.getName() ) && geom instanceof LineString ) { + } else if ( hierarchy.getCurveSubstitutions().contains( particle.getName() ) + && geom instanceof LineString ) { // constructed as LineString, but needs to become a Curve LineString p = (LineString) geom; GeometryFactory geomFac = new GeometryFactory(); @@ -668,6 +715,12 @@ private TypedObjectNode unwrapCustomGeometry( GenericXMLElement particle ) { return geom; } + private String detectTableAlias( FeatureTypeMapping ftMapping ) { + if ( tableAliasManager != null ) + return tableAliasManager.getTableAlias( ftMapping.getFtTable() ); + return tableAlias; + } + private QName getName( ValueReference path ) { if ( path.getAsQName() != null ) { return path.getAsQName(); @@ -685,13 +738,13 @@ private QName getName( ValueReference path ) { return null; } - private Pair> getJoinedResultSet( TableJoin jc, - Mapping mapping, + private Pair> getJoinedResultSet( TableJoin jc, Mapping mapping, ResultSet rs, - LinkedHashMap colToRsIdx ) - throws SQLException { + LinkedHashMap colToRsIdx, + String tableAlias ) + throws SQLException { - LinkedHashMap rsToIdx = getSubsequentSelectColumns( mapping ); + LinkedHashMap rsToIdx = getSubsequentSelectColumns( mapping, tableAlias ); StringBuilder sql = new StringBuilder( "SELECT " ); boolean first = true; diff --git a/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/xpath/MappedXPath.java b/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/xpath/MappedXPath.java index 04022deef9..fd78554080 100644 --- a/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/xpath/MappedXPath.java +++ b/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/xpath/MappedXPath.java @@ -131,7 +131,7 @@ public MappedXPath( SQLFeatureStore fs, FeatureTypeMapping ftMapping, ValueRefer } currentTable = ftMapping.getFtTable().toString(); - currentTableAlias = aliasManager.getRootTableAlias(); + currentTableAlias = aliasManager.getTableAlias( ftMapping.getFtTable() ); map( ftMapping.getMappings(), steps ); } From 6c32dd3be707affdd50fa071de99cfca20e81a41 Mon Sep 17 00:00:00 2001 From: Lyn Elisa Goltz Date: Mon, 30 May 2016 11:09:22 +0200 Subject: [PATCH 08/25] #3476 - enhanced intersects request to support a second value reference instead of geometry --- .../postgis/PostGISWhereBuilder.java | 33 ++++++++++++++----- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/deegree-core/deegree-core-sqldialect/deegree-sqldialect-postgis/src/main/java/org/deegree/sqldialect/postgis/PostGISWhereBuilder.java b/deegree-core/deegree-core-sqldialect/deegree-sqldialect-postgis/src/main/java/org/deegree/sqldialect/postgis/PostGISWhereBuilder.java index f48aafbb2f..8a6f08e540 100644 --- a/deegree-core/deegree-core-sqldialect/deegree-sqldialect-postgis/src/main/java/org/deegree/sqldialect/postgis/PostGISWhereBuilder.java +++ b/deegree-core/deegree-core-sqldialect/deegree-sqldialect-postgis/src/main/java/org/deegree/sqldialect/postgis/PostGISWhereBuilder.java @@ -112,7 +112,7 @@ public class PostGISWhereBuilder extends AbstractWhereBuilder { */ public PostGISWhereBuilder( PostGISDialect dialect, PropertyNameMapper mapper, OperatorFilter filter, SortProperty[] sortCrit, boolean allowPartialMappings, boolean useLegacyPredicates ) - throws FilterEvaluationException, UnmappableException { + throws FilterEvaluationException, UnmappableException { super( dialect, mapper, filter, sortCrit ); this.useLegacyPredicates = useLegacyPredicates; build( allowPartialMappings ); @@ -156,8 +156,7 @@ protected String getStringValueFromFunction( Expression pattern ) appendParamsFromFunction( function, params ); TypedObjectNode value = evaluateFunction( function, params ); if ( !( value instanceof PrimitiveValue ) ) { - throw new UnsupportedOperationException( - "SQL IsLike request with a function evaluating to a non-primitive value is not supported!" ); + throw new UnsupportedOperationException( "SQL IsLike request with a function evaluating to a non-primitive value is not supported!" ); } String valueAsString = ( (PrimitiveValue) value ).getAsText(); return valueAsString; @@ -204,11 +203,7 @@ protected SQLOperation toProtoSQL( SpatialOperator op ) SQLOperationBuilder builder = new SQLOperationBuilder( BOOLEAN ); SQLExpression propNameExpr = toProtoSQLSpatial( op.getPropName() ); - if ( !propNameExpr.isSpatial() ) { - String msg = "Cannot evaluate spatial operator on database. Targeted property name '" + op.getPropName() - + "' does not denote a spatial column."; - throw new FilterEvaluationException( msg ); - } + checkIfExpressionIsSpatial( propNameExpr, op.getPropName() ); ICRS storageCRS = propNameExpr.getCRS(); int srid = propNameExpr.getSRID() != null ? Integer.parseInt( propNameExpr.getSRID() ) : -1; @@ -337,7 +332,7 @@ protected SQLOperation toProtoSQL( SpatialOperator op ) } builder.add( propNameExpr ); builder.add( "," ); - builder.add( toProtoSQL( intersects.getGeometry(), storageCRS, srid ) ); + builder.add( toProtoSqlSecondParameter( intersects, storageCRS, srid ) ); builder.add( ")" ); break; } @@ -399,4 +394,24 @@ private SQLExpression toProtoSQL( Geometry geom, ICRS targetCRS, int srid ) throws FilterEvaluationException { return new SQLArgument( geom, new PostGISGeometryConverter( null, targetCRS, "" + srid, useLegacyPredicates ) ); } + + private SQLExpression toProtoSqlSecondParameter( Intersects intersects, ICRS storageCRS, int srid ) + throws FilterEvaluationException, UnmappableException { + if ( intersects.getValueReference() != null ) { + SQLExpression sqlExpression = toProtoSQLSpatial( intersects.getValueReference() ); + checkIfExpressionIsSpatial( sqlExpression, intersects.getValueReference() ); + return sqlExpression; + } + return toProtoSQL( intersects.getGeometry(), storageCRS, srid ); + } + + private void checkIfExpressionIsSpatial( SQLExpression sqlExpression, ValueReference propName ) + throws FilterEvaluationException { + if ( !sqlExpression.isSpatial() ) { + String msg = "Cannot evaluate spatial operator on database. Targeted property name '" + propName + + "' does not denote a spatial column."; + throw new FilterEvaluationException( msg ); + } + } + } \ No newline at end of file From b67dbbcfe13232d345cbb795e641e7352e136f6c Mon Sep 17 00:00:00 2001 From: Lyn Elisa Goltz Date: Mon, 30 May 2016 11:48:23 +0200 Subject: [PATCH 09/25] #3467 - fixed GetRecords with hits --- .../org/deegree/feature/persistence/sql/SQLFeatureStore.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/SQLFeatureStore.java b/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/SQLFeatureStore.java index 001435b3a8..3fc421b6cf 100644 --- a/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/SQLFeatureStore.java +++ b/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/SQLFeatureStore.java @@ -690,7 +690,7 @@ private int queryHitsByOperatorFilter( Query query, QName ftName, OperatorFilter } else { sql.append( "COUNT(*) FROM (SELECT DISTINCT " ); - String ftTableAlias = wb.getAliasManager().getRootTableAlias(); + String ftTableAlias = wb.getAliasManager().getTableAlias( ftMapping.getFtTable() ); FIDMapping fidMapping = ftMapping.getFidMapping(); List> fidCols = fidMapping.getColumns(); From fd05a29116c1b11962ce7d22dae05b8d4179cdc5 Mon Sep 17 00:00:00 2001 From: Lyn Elisa Goltz Date: Mon, 30 May 2016 14:23:34 +0200 Subject: [PATCH 10/25] #3477 - store spatial join result as FeatureTuple --- .../org/deegree/feature/FeatureTuple.java | 172 ++++++++++++++++++ .../sql/rules/FeatureBuilderRelational.java | 13 +- 2 files changed, 181 insertions(+), 4 deletions(-) create mode 100644 deegree-core/deegree-core-base/src/main/java/org/deegree/feature/FeatureTuple.java diff --git a/deegree-core/deegree-core-base/src/main/java/org/deegree/feature/FeatureTuple.java b/deegree-core/deegree-core-base/src/main/java/org/deegree/feature/FeatureTuple.java new file mode 100644 index 0000000000..6b3353913f --- /dev/null +++ b/deegree-core/deegree-core-base/src/main/java/org/deegree/feature/FeatureTuple.java @@ -0,0 +1,172 @@ +//$HeadURL$ +/*---------------------------------------------------------------------------- + This file is part of deegree, http://deegree.org/ + Copyright (C) 2001-2015 by: + - Department of Geography, University of Bonn - + and + - lat/lon GmbH - + + This library is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at your option) + any later version. + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + details. + You should have received a copy of the GNU Lesser General Public License + along with this library; if not, write to the Free Software Foundation, Inc., + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Contact information: + + lat/lon GmbH + Aennchenstr. 19, 53177 Bonn + Germany + http://lat-lon.de/ + + Department of Geography, University of Bonn + Prof. Dr. Klaus Greve + Postfach 1147, 53001 Bonn + Germany + http://www.geographie.uni-bonn.de/deegree/ + + e-mail: info@deegree.org +----------------------------------------------------------------------------*/ +package org.deegree.feature; + +import java.util.List; + +import javax.xml.namespace.QName; + +import org.deegree.commons.tom.TypedObjectNode; +import org.deegree.commons.tom.gml.property.Property; +import org.deegree.feature.property.ExtraProps; +import org.deegree.feature.types.FeatureType; +import org.deegree.geometry.Envelope; + +/** + * Encapsulates a tuple of features as described in WFS 2.0 as result set for joins. + * + * @author Lyn Goltz + */ +public class FeatureTuple implements Feature { + + private final List features; + + private String id; + + private Envelope envelope; + + private boolean envelopeCalculated = false; + + /** + * @param features + * list of features part of this tuple, never null + */ + public FeatureTuple( List features ) { + this.features = features; + this.id = createId( features ); + } + + /** + * @return all features part of this tuple, never null + */ + public List getTupleFeatures() { + return features; + } + + @Override + public String getId() { + return id; + } + + @Override + public List getProperties() { + throw new UnsupportedOperationException( "Not implemented yet." ); + } + + @Override + public List getProperties( QName propName ) { + throw new UnsupportedOperationException( "Not implemented yet." ); + } + + @Override + public void setId( String id ) { + this.id = id; + } + + @Override + public QName getName() { + throw new UnsupportedOperationException( "Not implemented yet." ); + } + + @Override + public FeatureType getType() { + throw new UnsupportedOperationException( "Not implemented yet." ); + } + + @Override + public List getGeometryProperties() { + throw new UnsupportedOperationException( "Not implemented yet." ); + } + + @Override + public Envelope getEnvelope() { + if ( !envelopeCalculated ) { + envelope = calcEnvelope(); + } + return envelope; + } + + @Override + public void setEnvelope( Envelope env ) { + this.envelope = env; + envelopeCalculated = true; + } + + @Override + public Envelope calcEnvelope() { + Envelope fcBBox = null; + for ( Feature feature : this.features ) { + Envelope memberBBox = feature.getEnvelope(); + if ( memberBBox != null ) { + if ( fcBBox != null ) { + fcBBox = fcBBox.merge( memberBBox ); + } else { + fcBBox = memberBBox; + } + } + } + return fcBBox; + } + + @Override + public void setPropertyValue( QName propName, int occurence, TypedObjectNode value ) { + throw new UnsupportedOperationException( "Not implemented yet." ); + } + + @Override + public void setProperties( List props ) + throws IllegalArgumentException { + throw new UnsupportedOperationException( "Not implemented yet." ); + } + + @Override + public ExtraProps getExtraProperties() { + throw new UnsupportedOperationException( "Not implemented yet." ); + } + + @Override + public void setExtraProperties( ExtraProps extraProps ) { + throw new UnsupportedOperationException( "Not implemented yet." ); + } + + private String createId( List features ) { + String id = "tupel_"; + for ( Feature feature : features ) + id += feature.getId(); + return id; + } + +} \ No newline at end of file diff --git a/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/rules/FeatureBuilderRelational.java b/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/rules/FeatureBuilderRelational.java index c60bacaaad..4ca0f3a1bc 100644 --- a/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/rules/FeatureBuilderRelational.java +++ b/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/rules/FeatureBuilderRelational.java @@ -81,6 +81,7 @@ import org.deegree.commons.xml.NamespaceBindings; import org.deegree.commons.xml.stax.XMLStreamReaderWrapper; import org.deegree.feature.Feature; +import org.deegree.feature.FeatureTuple; import org.deegree.feature.persistence.sql.FeatureBuilder; import org.deegree.feature.persistence.sql.FeatureTypeMapping; import org.deegree.feature.persistence.sql.SQLFeatureStore; @@ -290,10 +291,10 @@ private void addSelectColumns( Mapping mapping, LinkedHashMap c @Override public Feature buildFeature( ResultSet rs ) throws SQLException { - - Feature feature = null; + List features = new ArrayList(); try { for ( Entry featureTypeAndMapping : featureTypeAndMappings.entrySet() ) { + Feature feature = null; FeatureType ft = featureTypeAndMapping.getKey(); FeatureTypeMapping ftMapping = featureTypeAndMapping.getValue(); String tableAlias = detectTableAlias( ftMapping ); @@ -324,7 +325,7 @@ public Feature buildFeature( ResultSet rs ) + " are currently supported." ); } } - feature = ft.newFeature( gmlId, props, null ); + features.add( ft.newFeature( gmlId, props, null ) ); if ( fs.getCache() != null ) { fs.getCache().add( feature ); } @@ -336,7 +337,11 @@ public Feature buildFeature( ResultSet rs ) LOG.error( t.getMessage(), t ); throw new SQLException( t.getMessage(), t ); } - return feature; + if ( features.size() == 0 ) + return null; + if ( features.size() == 1 ) + return features.get( 0 ); + return new FeatureTuple( features ); } private String toIdPrefix( ValueReference propName ) { From 42616f852ffc34ef679e359e72d98fbc594a11e0 Mon Sep 17 00:00:00 2001 From: Lyn Elisa Goltz Date: Mon, 30 May 2016 14:26:18 +0200 Subject: [PATCH 11/25] #3478 - write join result as wfs:Tuple --- .../request/AbstractGmlRequestHandler.java | 41 ++++++++++++++++--- .../gml/request/GmlGetFeatureHandler.java | 8 ++-- 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/deegree-services/deegree-services-wfs/src/main/java/org/deegree/services/wfs/format/gml/request/AbstractGmlRequestHandler.java b/deegree-services/deegree-services-wfs/src/main/java/org/deegree/services/wfs/format/gml/request/AbstractGmlRequestHandler.java index dc7857ed49..129f406ef0 100644 --- a/deegree-services/deegree-services-wfs/src/main/java/org/deegree/services/wfs/format/gml/request/AbstractGmlRequestHandler.java +++ b/deegree-services/deegree-services-wfs/src/main/java/org/deegree/services/wfs/format/gml/request/AbstractGmlRequestHandler.java @@ -83,6 +83,7 @@ import org.deegree.cs.exceptions.TransformationException; import org.deegree.cs.exceptions.UnknownCRSException; import org.deegree.feature.Feature; +import org.deegree.feature.FeatureTuple; import org.deegree.feature.persistence.FeatureStore; import org.deegree.feature.persistence.FeatureStoreException; import org.deegree.feature.persistence.query.Query; @@ -150,7 +151,8 @@ protected GMLObject retrieveObject( String id ) protected void writeAdditionalObjects( GMLStreamWriter gmlStream, WfsXlinkStrategy additionalObjects, QName featureMemberEl, Version requestVersion ) - throws XMLStreamException, UnknownCRSException, TransformationException { + throws XMLStreamException, UnknownCRSException, + TransformationException, OWSException { Collection> nextLevelObjects = additionalObjects.getAdditionalRefs(); XMLStreamWriter xmlStream = gmlStream.getXMLStream(); @@ -166,7 +168,7 @@ protected void writeAdditionalObjects( GMLStreamWriter gmlStream, WfsXlinkStrate writeAdditionalObjectsStart( xmlStream, requestVersion ); wroteStartSection = true; } - writeMemberFeature( feature, gmlStream, xmlStream, resolveState, featureMemberEl ); + writeMemberFeature( feature, gmlStream, xmlStream, resolveState, featureMemberEl, requestVersion ); } } nextLevelObjects = additionalObjects.getAdditionalRefs(); @@ -208,9 +210,19 @@ private boolean isResolvable( GMLReference ref ) { } protected void writeMemberFeature( Feature member, GMLStreamWriter gmlStream, XMLStreamWriter xmlStream, - GmlXlinkOptions resolveState, QName featureMemberEl ) - throws XMLStreamException, UnknownCRSException, TransformationException { + GmlXlinkOptions resolveState, QName featureMemberEl, Version requestVersion ) + throws XMLStreamException, UnknownCRSException, + TransformationException, OWSException { + if ( member instanceof FeatureTuple ) + writeFeatureTuple( (FeatureTuple) member, gmlStream, xmlStream, resolveState, featureMemberEl, requestVersion ); + else + writeFeature( member, gmlStream, xmlStream, resolveState, featureMemberEl ); + } + private void writeFeature( Feature member, GMLStreamWriter gmlStream, XMLStreamWriter xmlStream, + GmlXlinkOptions resolveState, QName featureMemberEl ) + throws XMLStreamException, UnknownCRSException, + TransformationException { if ( gmlStream.getReferenceResolveStrategy().isObjectExported( member.getId() ) ) { xmlStream.writeEmptyElement( featureMemberEl.getNamespaceURI(), featureMemberEl.getLocalPart() ); if ( xmlStream.getPrefix( XLNNS ) == null ) { @@ -225,6 +237,21 @@ protected void writeMemberFeature( Feature member, GMLStreamWriter gmlStream, XM } } + protected void writeFeatureTuple( FeatureTuple featureTuple, GMLStreamWriter gmlStream, XMLStreamWriter xmlStream, + GmlXlinkOptions resolveState, QName featureMemberEl, Version requestVersion ) + throws XMLStreamException, UnknownCRSException, + TransformationException, OWSException { + if ( !VERSION_200.equals( requestVersion ) ) + throw new OWSException( "Tuples are not supported in WFS " + requestVersion, NO_APPLICABLE_CODE ); + xmlStream.writeStartElement( featureMemberEl.getNamespaceURI(), featureMemberEl.getLocalPart() ); + xmlStream.writeStartElement( featureMemberEl.getNamespaceURI(), "Tuple" ); + for ( Feature feature : featureTuple.getTupleFeatures() ) { + writeMemberFeature( feature, gmlStream, xmlStream, resolveState, featureMemberEl, requestVersion ); + } + xmlStream.writeEndElement(); + xmlStream.writeEndElement(); + } + /** * Returns an URL template for requesting individual objects (feature or geometries) from the server by the object's * id. @@ -264,7 +291,8 @@ protected String getObjectXlinkTemplate( Version version, GMLVersion gmlVersion + URLEncoder.encode( gmlVersion.getMimeType(), "UTF-8" ) + "&STOREDQUERY_ID=" + QUERY_ID_GET_FEATURE_BY_ID + "&ID={}#{}"; } else { - throw new UnsupportedOperationException( Messages.getMessage( "WFS_BACKREFERENCE_UNSUPPORTED", version ) ); + throw new UnsupportedOperationException( Messages.getMessage( "WFS_BACKREFERENCE_UNSUPPORTED", + version ) ); } } catch ( UnsupportedEncodingException e ) { // should never happen (UTF-8 is known) @@ -323,7 +351,8 @@ private String getSchemaLocationForWfs200( Collection requestedFts return schemaLocation + " " + getSchemaLocationPartForFeatureTypes( VERSION_200, gmlVersion, requestedFts ); } - private String getCustomSchemaLocationForWfs100Or110( Version requestVersion, Collection requestedFts ) { + private String getCustomSchemaLocationForWfs100Or110( Version requestVersion, + Collection requestedFts ) { String schemaLocation = ""; if ( options.getSchemaLocation() != null ) { schemaLocation = options.getSchemaLocation(); diff --git a/deegree-services/deegree-services-wfs/src/main/java/org/deegree/services/wfs/format/gml/request/GmlGetFeatureHandler.java b/deegree-services/deegree-services-wfs/src/main/java/org/deegree/services/wfs/format/gml/request/GmlGetFeatureHandler.java index 35c09df4bf..7465a40deb 100644 --- a/deegree-services/deegree-services-wfs/src/main/java/org/deegree/services/wfs/format/gml/request/GmlGetFeatureHandler.java +++ b/deegree-services/deegree-services-wfs/src/main/java/org/deegree/services/wfs/format/gml/request/GmlGetFeatureHandler.java @@ -461,7 +461,7 @@ private void writeFeatureMembersStream( Version wfsVersion, GMLStreamWriter gmlS GMLVersion outputFormat, int maxFeatures, int startIndex, QName featureMemberEl, Lock lock ) throws XMLStreamException, UnknownCRSException, TransformationException, - FeatureStoreException, FilterEvaluationException, FactoryConfigurationError { + FeatureStoreException, FilterEvaluationException, FactoryConfigurationError, OWSException { XMLStreamWriter xmlStream = gmlStream.getXMLStream(); @@ -500,7 +500,7 @@ private void writeFeatureMembersStream( Version wfsVersion, GMLStreamWriter gmlS if ( featuresSkipped < startIndex ) { featuresSkipped++; } else { - writeMemberFeature( member, gmlStream, xmlStream, resolveState, featureMemberEl ); + writeMemberFeature( member, gmlStream, xmlStream, resolveState, featureMemberEl, wfsVersion ); featuresAdded++; } } @@ -515,7 +515,7 @@ private void writeFeatureMembersCached( Version wfsVersion, GMLStreamWriter gmlS GMLVersion outputFormat, int maxFeatures, int startIndex, QName featureMemberEl, Lock lock, ResponsePagingUris responsePagingUris ) throws XMLStreamException, UnknownCRSException, TransformationException, - FeatureStoreException, FilterEvaluationException, FactoryConfigurationError { + FeatureStoreException, FilterEvaluationException, FactoryConfigurationError, OWSException { FeatureCollection allFeatures = new GenericFeatureCollection(); Set fids = new HashSet(); @@ -568,7 +568,7 @@ private void writeFeatureMembersCached( Version wfsVersion, GMLStreamWriter gmlS // retrieve and write result features GmlXlinkOptions resolveState = gmlStream.getReferenceResolveStrategy().getResolveOptions(); for ( Feature member : allFeatures ) { - writeMemberFeature( member, gmlStream, xmlStream, resolveState, featureMemberEl ); + writeMemberFeature( member, gmlStream, xmlStream, resolveState, featureMemberEl, wfsVersion ); } } From bd5b9500dca6258521907c5b314387ddfae69afe Mon Sep 17 00:00:00 2001 From: Lyn Elisa Goltz Date: Mon, 30 May 2016 15:39:15 +0200 Subject: [PATCH 12/25] #3479 - set constraint ImplementsSpatialJoins to TRUE --- .../java/org/deegree/services/wfs/GetCapabilitiesHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deegree-services/deegree-services-wfs/src/main/java/org/deegree/services/wfs/GetCapabilitiesHandler.java b/deegree-services/deegree-services-wfs/src/main/java/org/deegree/services/wfs/GetCapabilitiesHandler.java index b79e5f609c..f218897e59 100644 --- a/deegree-services/deegree-services-wfs/src/main/java/org/deegree/services/wfs/GetCapabilitiesHandler.java +++ b/deegree-services/deegree-services-wfs/src/main/java/org/deegree/services/wfs/GetCapabilitiesHandler.java @@ -913,7 +913,7 @@ void export200() } else constraints.add( new Domain( "ImplementsResultPaging", "FALSE" ) ); constraints.add( new Domain( "ImplementsStandardJoins", "FALSE" ) ); - constraints.add( new Domain( "ImplementsSpatialJoins", "FALSE" ) ); + constraints.add( new Domain( "ImplementsSpatialJoins", "TRUE" ) ); constraints.add( new Domain( "ImplementsTemporalJoins", "FALSE" ) ); constraints.add( new Domain( "ImplementsFeatureVersioning", "FALSE" ) ); constraints.add( new Domain( "ManageStoredQueries", "FALSE" ) ); From 54644475e60846b0aac9c49359ef4eebf8a81c87 Mon Sep 17 00:00:00 2001 From: Lyn Elisa Goltz Date: Mon, 30 May 2016 16:17:38 +0200 Subject: [PATCH 13/25] #3481 - allow join queries for resultType=hits --- .../persistence/sql/SQLFeatureStore.java | 133 ++++++++++-------- 1 file changed, 77 insertions(+), 56 deletions(-) diff --git a/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/SQLFeatureStore.java b/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/SQLFeatureStore.java index 3fc421b6cf..f8d95b22e9 100644 --- a/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/SQLFeatureStore.java +++ b/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/SQLFeatureStore.java @@ -35,7 +35,6 @@ ----------------------------------------------------------------------------*/ package org.deegree.feature.persistence.sql; -import static java.util.Collections.singletonList; import static org.deegree.commons.xml.CommonNamespaces.OGCNS; import static org.deegree.commons.xml.CommonNamespaces.XLNNS; import static org.deegree.commons.xml.CommonNamespaces.XSINS; @@ -625,22 +624,16 @@ public void destroy() { public int queryHits( Query query ) throws FeatureStoreException, FilterEvaluationException { - if ( query.getTypeNames() == null || query.getTypeNames().length > 1 ) { - String msg = "Join queries between multiple feature types are not supported by the SQLFeatureStore implementation (yet)."; + if ( query.getTypeNames() == null ) { + String msg = "Requestes TypeNames are not available, this ist not supported."; throw new UnsupportedOperationException( msg ); } Filter filter = query.getFilter(); int hits = 0; - if ( query.getTypeNames().length == 1 && ( filter == null || filter instanceof OperatorFilter ) ) { - QName ftName = query.getTypeNames()[0].getFeatureTypeName(); - FeatureType ft = getSchema().getFeatureType( ftName ); - if ( ft == null ) { - String msg = "Feature type '" + ftName + "' is not served by this feature store."; - throw new FeatureStoreException( msg ); - } - hits = queryHitsByOperatorFilter( query, ftName, (OperatorFilter) filter ); + if ( filter == null || filter instanceof OperatorFilter ) { + hits = queryHitsByOperatorFilter( query, (OperatorFilter) filter ); } else { // must be an id filter based query if ( query.getFilter() == null || !( query.getFilter() instanceof IdFilter ) ) { @@ -653,21 +646,26 @@ public int queryHits( Query query ) return hits; } - private int queryHitsByOperatorFilter( Query query, QName ftName, OperatorFilter filter ) + private int queryHitsByOperatorFilter( Query query, OperatorFilter filter ) + throws FeatureStoreException { + List ftNames = collectFeatureTypesNames( query ); + return queryHitsByOperatorFilter( query, ftNames, filter ); + } + + private int queryHitsByOperatorFilter( Query query, List ftNames, OperatorFilter filter ) throws FeatureStoreException { LOG.debug( "Performing hits query by operator filter" ); if ( getSchema().getBlobMapping() != null ) { - return queryHitsByOperatorFilterBlob( query, ftName, filter ); + if ( ftNames.size() > 1 ) { + String msg = "Join queries between multiple feature types in blob mode are not by SQLFeatureStore (yet)."; + throw new FeatureStoreException( msg ); + } + return queryHitsByOperatorFilterBlob( query, ftNames.get( 0 ), filter ); } - FeatureType ft = getSchema().getFeatureType( ftName ); - FeatureTypeMapping ftMapping = getMapping( ftName ); - if ( ftMapping == null ) { - String msg = "Cannot perform query on feature type '" + ftName + "'. Feature type is not mapped."; - throw new FeatureStoreException( msg ); - } + Map featureTypeAndMappings = collectFeatureTypesAndMappings( ftNames ); int hits = 0; @@ -677,40 +675,51 @@ private int queryHitsByOperatorFilter( Query query, QName ftName, OperatorFilter try { conn = getConnection(); - AbstractWhereBuilder wb = getWhereBuilder( ft, filter, query.getSortProperties(), conn ); + AbstractWhereBuilder wb = getWhereBuilder( featureTypeAndMappings.values(), filter, + query.getSortProperties(), conn ); if ( wb.getPostFilter() != null ) { LOG.debug( "Filter not fully mappable to WHERE clause. Need to iterate over all features to determine count." ); - hits = queryByOperatorFilter( query, singletonList( ftName ), filter ).count(); + hits = queryByOperatorFilter( query, ftNames, filter ).count(); } else { StringBuilder sql = new StringBuilder( "SELECT " ); if ( wb.getWhere() == null ) { sql.append( "COUNT(*) FROM " ); - sql.append( ftMapping.getFtTable() ); + // TODO: check the assumption that only one feature type is requested if the where clause is null... + sql.append( featureTypeAndMappings.values().iterator().next().getFtTable() ); } else { sql.append( "COUNT(*) FROM (SELECT DISTINCT " ); - String ftTableAlias = wb.getAliasManager().getTableAlias( ftMapping.getFtTable() ); - - FIDMapping fidMapping = ftMapping.getFidMapping(); - List> fidCols = fidMapping.getColumns(); boolean first = true; - for ( Pair fidCol : fidCols ) { - if ( !first ) { - sql.append( "," ); - } else { - first = false; + for ( FeatureTypeMapping ftMapping : featureTypeAndMappings.values() ) { + String ftTableAlias = wb.getAliasManager().getTableAlias( ftMapping.getFtTable() ); + + FIDMapping fidMapping = ftMapping.getFidMapping(); + List> fidCols = fidMapping.getColumns(); + for ( Pair fidCol : fidCols ) { + if ( !first ) { + sql.append( "," ); + } else { + first = false; + } + sql.append( ftTableAlias ).append( '.' ).append( fidCol.first ); } - sql.append( ftTableAlias ).append( '.' ).append( fidCol.first ); } sql.append( " FROM " ); // pure relational query - sql.append( ftMapping.getFtTable() ); - sql.append( ' ' ); - sql.append( ftTableAlias ); - + boolean firstTable = true; + for ( FeatureTypeMapping ftMapping : featureTypeAndMappings.values() ) { + TableName ftTable = ftMapping.getFtTable(); + String ftTableAlias = wb.getAliasManager().getTableAlias( ftTable ); + if ( !firstTable ) + sql.append( ", " ); + sql.append( ftTable ); + sql.append( ' ' ); + sql.append( ftTableAlias ); + firstTable = false; + } for ( PropertyNameMapping mappedPropName : wb.getMappedPropertyNames() ) { for ( Join join : mappedPropName.getJoins() ) { sql.append( " LEFT OUTER JOIN " ); @@ -1276,16 +1285,7 @@ private FeatureInputStream queryByOperatorFilterBlob( Query query, QName ftName, private FeatureInputStream queryByOperatorFilter( Query query, OperatorFilter filter ) throws FeatureStoreException { - List ftNames = new ArrayList(); - for ( TypeName typeName : query.getTypeNames() ) { - QName ftName = typeName.getFeatureTypeName(); - FeatureType ft = getSchema().getFeatureType( ftName ); - if ( ft == null ) { - String msg = "Feature store is not configured to serve feature type '" + ftName + "'."; - throw new FeatureStoreException( msg ); - } - ftNames.add( ftName ); - } + List ftNames = collectFeatureTypesNames( query ); return queryByOperatorFilter( query, ftNames, filter ); } @@ -1307,16 +1307,7 @@ private FeatureInputStream queryByOperatorFilter( Query query, List ftNam PreparedStatement stmt = null; ResultSet rs = null; - Map featureTypeAndMappings = new HashMap(); - for ( QName ftName : ftNames ) { - FeatureType ft = getSchema().getFeatureType( ftName ); - FeatureTypeMapping ftMapping = getMapping( ftName ); - if ( ftMapping == null ) { - String msg = "Cannot perform query on feature type '" + ftNames + "'. Feature type is not mapped."; - throw new FeatureStoreException( msg ); - } - featureTypeAndMappings.put( ft, ftMapping ); - } + Map featureTypeAndMappings = collectFeatureTypesAndMappings( ftNames ); try { conn = getConnection(); @@ -1510,6 +1501,36 @@ private AbstractWhereBuilder getWhereBuilder( Collection ftM return dialect.getWhereBuilder( mapper, filter, sortCrit, allowInMemoryFiltering ); } + private List collectFeatureTypesNames( Query query ) + throws FeatureStoreException { + List ftNames = new ArrayList(); + for ( TypeName typeName : query.getTypeNames() ) { + QName ftName = typeName.getFeatureTypeName(); + FeatureType ft = getSchema().getFeatureType( ftName ); + if ( ft == null ) { + String msg = "Feature store is not configured to serve feature type '" + ftName + "'."; + throw new FeatureStoreException( msg ); + } + ftNames.add( ftName ); + } + return ftNames; + } + + private Map collectFeatureTypesAndMappings( List ftNames ) + throws FeatureStoreException { + Map featureTypeAndMappings = new HashMap(); + for ( QName ftName : ftNames ) { + FeatureType ft = getSchema().getFeatureType( ftName ); + FeatureTypeMapping ftMapping = getMapping( ftName ); + if ( ftMapping == null ) { + String msg = "Cannot perform query on feature type '" + ftNames + "'. Feature type is not mapped."; + throw new FeatureStoreException( msg ); + } + featureTypeAndMappings.put( ft, ftMapping ); + } + return featureTypeAndMappings; + } + private AbstractWhereBuilder getWhereBuilderBlob( OperatorFilter filter, Connection conn ) throws FilterEvaluationException, UnmappableException { final String undefinedSrid = dialect.getUndefinedSrid(); From 20b6cb4c99577f85d1c7f916fb3e038bcd5585a3 Mon Sep 17 00:00:00 2001 From: Lyn Elisa Goltz Date: Tue, 31 May 2016 09:35:52 +0200 Subject: [PATCH 14/25] #3482 - implemented export of tuples for WFS 1.1.0/ GML 3.2 --- .../request/AbstractGmlRequestHandler.java | 48 +++++++++++++++---- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/deegree-services/deegree-services-wfs/src/main/java/org/deegree/services/wfs/format/gml/request/AbstractGmlRequestHandler.java b/deegree-services/deegree-services-wfs/src/main/java/org/deegree/services/wfs/format/gml/request/AbstractGmlRequestHandler.java index 129f406ef0..27cb39074c 100644 --- a/deegree-services/deegree-services-wfs/src/main/java/org/deegree/services/wfs/format/gml/request/AbstractGmlRequestHandler.java +++ b/deegree-services/deegree-services-wfs/src/main/java/org/deegree/services/wfs/format/gml/request/AbstractGmlRequestHandler.java @@ -214,7 +214,8 @@ protected void writeMemberFeature( Feature member, GMLStreamWriter gmlStream, XM throws XMLStreamException, UnknownCRSException, TransformationException, OWSException { if ( member instanceof FeatureTuple ) - writeFeatureTuple( (FeatureTuple) member, gmlStream, xmlStream, resolveState, featureMemberEl, requestVersion ); + writeFeatureTuple( (FeatureTuple) member, gmlStream, xmlStream, resolveState, featureMemberEl, + requestVersion ); else writeFeature( member, gmlStream, xmlStream, resolveState, featureMemberEl ); } @@ -241,15 +242,14 @@ protected void writeFeatureTuple( FeatureTuple featureTuple, GMLStreamWriter gml GmlXlinkOptions resolveState, QName featureMemberEl, Version requestVersion ) throws XMLStreamException, UnknownCRSException, TransformationException, OWSException { - if ( !VERSION_200.equals( requestVersion ) ) - throw new OWSException( "Tuples are not supported in WFS " + requestVersion, NO_APPLICABLE_CODE ); - xmlStream.writeStartElement( featureMemberEl.getNamespaceURI(), featureMemberEl.getLocalPart() ); - xmlStream.writeStartElement( featureMemberEl.getNamespaceURI(), "Tuple" ); - for ( Feature feature : featureTuple.getTupleFeatures() ) { - writeMemberFeature( feature, gmlStream, xmlStream, resolveState, featureMemberEl, requestVersion ); - } - xmlStream.writeEndElement(); - xmlStream.writeEndElement(); + if ( VERSION_110.equals( requestVersion ) && GML_32.equals( gmlStream.getVersion() ) ) + exportTupleWFS110_GML32( featureTuple, gmlStream, xmlStream, resolveState, featureMemberEl, + requestVersion ); + else if ( VERSION_200.equals( requestVersion ) ) + exportTupleWFS200( featureTuple, gmlStream, xmlStream, resolveState, featureMemberEl, requestVersion ); + else + throw new OWSException( "Export of tuples is not supported for WFS " + requestVersion + " / GML " + + gmlStream.getVersion(), NO_APPLICABLE_CODE ); } /** @@ -549,4 +549,32 @@ protected String getSchemaLocation( Version version, GMLVersion gmlVersion, QNam return ns + " " + baseUrl.toString(); } + private void exportTupleWFS110_GML32( FeatureTuple featureTuple, GMLStreamWriter gmlStream, + XMLStreamWriter xmlStream, GmlXlinkOptions resolveState, + QName featureMemberEl, Version requestVersion ) + throws XMLStreamException, UnknownCRSException, + TransformationException, OWSException { + xmlStream.writeStartElement( featureMemberEl.getNamespaceURI(), "featureMembers" ); + xmlStream.writeStartElement( featureMemberEl.getNamespaceURI(), "FeatureCollection" ); + xmlStream.writeAttribute( featureMemberEl.getNamespaceURI(), "id", featureTuple.getId() ); + for ( Feature feature : featureTuple.getTupleFeatures() ) { + writeMemberFeature( feature, gmlStream, xmlStream, resolveState, featureMemberEl, requestVersion ); + } + xmlStream.writeEndElement(); + xmlStream.writeEndElement(); + } + + private void exportTupleWFS200( FeatureTuple featureTuple, GMLStreamWriter gmlStream, XMLStreamWriter xmlStream, + GmlXlinkOptions resolveState, QName featureMemberEl, Version requestVersion ) + throws XMLStreamException, UnknownCRSException, + TransformationException, OWSException { + xmlStream.writeStartElement( featureMemberEl.getNamespaceURI(), featureMemberEl.getLocalPart() ); + xmlStream.writeStartElement( featureMemberEl.getNamespaceURI(), "Tuple" ); + for ( Feature feature : featureTuple.getTupleFeatures() ) { + writeMemberFeature( feature, gmlStream, xmlStream, resolveState, featureMemberEl, requestVersion ); + } + xmlStream.writeEndElement(); + xmlStream.writeEndElement(); + } + } From 52e708d9c2040556d23063695d354c677829f0d4 Mon Sep 17 00:00:00 2001 From: Dirk Stenger Date: Wed, 1 Jun 2016 13:11:09 +0200 Subject: [PATCH 15/25] #3476 - Reenabled check if features are requested from different feature stores --- .../services/wfs/query/QueryAnalyzer.java | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/deegree-services/deegree-services-wfs/src/main/java/org/deegree/services/wfs/query/QueryAnalyzer.java b/deegree-services/deegree-services-wfs/src/main/java/org/deegree/services/wfs/query/QueryAnalyzer.java index c66693702c..a984cd3fff 100644 --- a/deegree-services/deegree-services-wfs/src/main/java/org/deegree/services/wfs/query/QueryAnalyzer.java +++ b/deegree-services/deegree-services-wfs/src/main/java/org/deegree/services/wfs/query/QueryAnalyzer.java @@ -363,6 +363,7 @@ private Query validateQuery( org.deegree.protocol.wfs.query.Query wfsQuery ) // requalify query typenames and keep track of them TypeName[] wfsTypeNames = ( (AdHocQuery) wfsQuery ).getTypeNames(); TypeName[] typeNames = new TypeName[wfsTypeNames.length]; + FeatureStore commonFs = null; for ( int i = 0; i < wfsTypeNames.length; i++ ) { String alias = wfsTypeNames[i].getAlias(); FeatureType ft = service.lookupFeatureType( wfsTypeNames[i].getFeatureTypeName() ); @@ -371,6 +372,15 @@ private Query validateQuery( org.deegree.protocol.wfs.query.Query wfsQuery ) + "' is not served by this WFS."; throw new OWSException( msg, INVALID_PARAMETER_VALUE, "typeName" ); } + FeatureStore fs = service.getStore( ft.getName() ); + if ( commonFs != null ) { + if ( fs != commonFs ) { + String msg = "Requested join of feature types from different feature stores. This is not supported."; + throw new OWSException( msg, INVALID_PARAMETER_VALUE, "typeName" ); + } + } else { + commonFs = fs; + } requestedFts.add( ft ); QName ftName = ft.getName(); typeNames[i] = new TypeName( ftName, alias ); @@ -412,8 +422,7 @@ private Query validateQuery( org.deegree.protocol.wfs.query.Query wfsQuery ) } } if ( checkAreaOfUse ) { - validateGeometryConstraint( ( (BBoxQuery) wfsQuery ).getBBox(), - ( (AdHocQuery) wfsQuery ).getSrsName() ); + validateGeometryConstraint( ( (BBoxQuery) wfsQuery ).getBBox(), ( (AdHocQuery) wfsQuery ).getSrsName() ); } Envelope bbox = bboxQuery.getBBox(); @@ -487,13 +496,14 @@ private void validatePropertyName( ValueReference propName, TypeName[] typeNames /** * Returns whether the propName has to be considered for re-qualification. - * + * * @param propName * @return */ private boolean isPrefixedAndBound( ValueReference propName ) { QName name = propName.getAsQName(); - return !name.getPrefix().equals( DEFAULT_NS_PREFIX ) && !name.getNamespaceURI().equals( "" ); + return !name.getPrefix().equals( DEFAULT_NS_PREFIX ) + && !name.getNamespaceURI().equals( "" ); } /** @@ -502,7 +512,7 @@ private boolean isPrefixedAndBound( ValueReference propName ) { *

* This types of propertynames especially occurs in WFS 1.0.0 requests. *

- * + * * @param propName * property name to be repaired, must be "simple", i.e. contain only of a QName * @param typeName @@ -548,8 +558,7 @@ private QName getPropertyNameAsQName( ValueReference propName ) { String s = propName.getAsText(); int colonIdx = s.indexOf( ':' ); if ( !s.contains( "/" ) && colonIdx != -1 ) { - if ( Character.isLetterOrDigit( s.charAt( 0 ) ) - && Character.isLetterOrDigit( s.charAt( s.length() - 1 ) ) ) { + if ( Character.isLetterOrDigit( s.charAt( 0 ) ) && Character.isLetterOrDigit( s.charAt( s.length() - 1 ) ) ) { String prefix = s.substring( 0, colonIdx ); String localName = s.substring( colonIdx + 1, s.length() ); String nsUri = null; @@ -587,7 +596,9 @@ private void validateGeometryConstraint( Geometry geom, ICRS queriedCrs ) domainOfValidity = transform( domainOfValidity, bbox.getCoordinateSystem() ); if ( !bbox.isWithin( domainOfValidity ) ) { String msg = "Invalid geometry constraint in filter. The envelope of the geometry is not within the domain of validity ('" - + domainOfValidity + "') of its CRS ('" + bbox.getCoordinateSystem().getAlias() + + domainOfValidity + + "') of its CRS ('" + + bbox.getCoordinateSystem().getAlias() + "')."; throw new OWSException( msg, INVALID_PARAMETER_VALUE, "filter" ); } From ab07d4c9217c23d25acb91c258d9722b858050e6 Mon Sep 17 00:00:00 2001 From: Lyn Elisa Goltz Date: Thu, 23 Jun 2016 07:13:36 +0200 Subject: [PATCH 16/25] #3609 - refactoring: moved valueReference property from Intersects to suberclass SpatialOperator --- .../deegree/filter/spatial/Intersects.java | 11 +------ .../filter/spatial/SpatialOperator.java | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Intersects.java b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Intersects.java index 0bf9a62707..c1c1de3f10 100644 --- a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Intersects.java +++ b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Intersects.java @@ -61,8 +61,6 @@ public class Intersects extends SpatialOperator { private final Geometry geometry; - private final ValueReference valueReference; - /** * Instantiates a {@link Intersects} operator with geometry as second parameter. * @@ -90,9 +88,8 @@ public Intersects( Expression propName, ValueReference valueReference ) { } private Intersects( Expression propName, Geometry geometry, ValueReference valueReference ) { - super( propName ); + super( propName, valueReference ); this.geometry = geometry; - this.valueReference = valueReference; } @Override @@ -157,12 +154,6 @@ public Geometry getGeometry() { return geometry; } - /** - * @return the second parameter, null if it is a geometry - */ - public ValueReference getValueReference() { - return valueReference; - } @Override public String toString( String indent ) { diff --git a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/SpatialOperator.java b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/SpatialOperator.java index e7ee100839..037940d623 100644 --- a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/SpatialOperator.java +++ b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/SpatialOperator.java @@ -68,8 +68,31 @@ public abstract class SpatialOperator implements Operator { protected final Expression propName; + protected final ValueReference valueReference; + + /** + * Instantiates a {@link SpatialOperator} without second parameter (may be stored in the implementation). + * + * @param param1 + * may actually be null (deegree extension to cope with features that have only hidden + * geometry props) + */ protected SpatialOperator( Expression param1 ) { + this( param1, null ); + } + + /** + * Instantiates a {@link Intersects} operator with value reference as second parameter. + * + * @param param1 + * may actually be null (deegree extension to cope with features that have only hidden + * geometry props) + * @param valueReference + * never null + */ + protected SpatialOperator( Expression param1, ValueReference valueReference ) { this.propName = param1; + this.valueReference = valueReference; } /** @@ -130,6 +153,13 @@ public Expression getParam1() { return propName; } + /** + * @return the second parameter, null if it is a geometry + */ + public ValueReference getValueReference() { + return valueReference; + } + /** * Returns the name of the spatial property to be considered. * From 5d85cf95fe2853f38b348b7b1dff266f9002129e Mon Sep 17 00:00:00 2001 From: Lyn Elisa Goltz Date: Thu, 23 Jun 2016 08:00:31 +0200 Subject: [PATCH 17/25] #3609 - refactoring: moved geometry to superclass SpatialOperator --- .../java/org/deegree/filter/spatial/BBOX.java | 51 +++++++++++++++---- .../org/deegree/filter/spatial/Beyond.java | 43 +++++++++++----- .../org/deegree/filter/spatial/Contains.java | 44 ++++++++++------ .../org/deegree/filter/spatial/Crosses.java | 46 ++++++++++------- .../org/deegree/filter/spatial/DWithin.java | 51 ++++++++++++------- .../org/deegree/filter/spatial/Disjoint.java | 46 ++++++++++------- .../org/deegree/filter/spatial/Equals.java | 46 ++++++++++------- .../deegree/filter/spatial/Intersects.java | 49 +++++++----------- .../org/deegree/filter/spatial/Overlaps.java | 46 ++++++++++------- .../filter/spatial/SpatialOperator.java | 36 +++++++++---- .../org/deegree/filter/spatial/Touches.java | 46 ++++++++++------- .../org/deegree/filter/spatial/Within.java | 43 +++++++++++----- 12 files changed, 349 insertions(+), 198 deletions(-) diff --git a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/BBOX.java b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/BBOX.java index aa6b8daf53..439b79239f 100644 --- a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/BBOX.java +++ b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/BBOX.java @@ -68,8 +68,6 @@ public class BBOX extends SpatialOperator { private static final Logger LOG = LoggerFactory.getLogger( BBOX.class ); - private final Envelope param2; - private final boolean allowFalsePositives; /** @@ -94,6 +92,18 @@ public BBOX( Expression param1, Envelope param2 ) { this( param1, param2, false ); } + /** + * Creates a new {@link BBOX} instance which uses the specified geometry property and bounding box. + * + * @param param1 + * geometry to compare to, can be null (use default geometry) + * @param param2 + * bounding box argument for intersection testing, never null + */ + public BBOX( Expression param1, ValueReference param2 ) { + this( param1, param2, false ); + } + /** * Creates a new {@link BBOX} instance which uses the specified geometry property and bounding box. * @@ -105,8 +115,22 @@ public BBOX( Expression param1, Envelope param2 ) { * set to true, if false positives are acceptable (may enable faster index-only checks) */ public BBOX( final Expression param1, final Envelope param2, final boolean allowFalsePositives ) { - super( param1 ); - this.param2 = param2; + super( param1, param2 ); + this.allowFalsePositives = allowFalsePositives; + } + + /** + * Creates a new {@link BBOX} instance which uses the specified geometry property and bounding box. + * + * @param param1 + * geometry to compare to, can be null (use default geometry) + * @param param2 + * bounding box argument for intersection testing, never null + * @param allowFalsePositives + * set to true, if false positives are acceptable (may enable faster index-only checks) + */ + public BBOX( final Expression param1, final ValueReference param2, final boolean allowFalsePositives ) { + super( param1, param2 ); this.allowFalsePositives = allowFalsePositives; } @@ -119,7 +143,7 @@ public BBOX( final Expression param1, final Envelope param2, final boolean allow */ @Override public ValueReference getPropName() { - return (ValueReference) propName; + return (ValueReference) param1; } /** @@ -128,7 +152,7 @@ public ValueReference getPropName() { * @return the envelope, never null */ public Envelope getBoundingBox() { - return param2; + return (Envelope) param2AsGeometry; } /** @@ -149,7 +173,7 @@ public boolean evaluate( T obj, XPathEvaluator xpathEvaluator ) for ( TypedObjectNode paramValue : param1.evaluate( obj, xpathEvaluator ) ) { Geometry param1Value = checkGeometryOrNull( paramValue ); if ( param1Value != null ) { - Envelope transformedBBox = (Envelope) getCompatibleGeometry( param1Value, param2 ); + Envelope transformedBBox = (Envelope) getCompatibleGeometry( param1Value, getBoundingBox() ); return transformedBBox.intersects( param1Value ); } } @@ -158,7 +182,7 @@ public boolean evaluate( T obj, XPathEvaluator xpathEvaluator ) Feature f = (Feature) obj; Envelope env = f.getEnvelope(); if ( env != null ) { - Envelope transformedBBox = (Envelope) getCompatibleGeometry( env, param2 ); + Envelope transformedBBox = (Envelope) getCompatibleGeometry( env, getBoundingBox() ); return transformedBBox.intersects( env ); } } else { @@ -170,13 +194,18 @@ public boolean evaluate( T obj, XPathEvaluator xpathEvaluator ) @Override public String toString( String indent ) { String s = indent + "-BBOX\n"; - s += indent + propName + "\n"; - s += indent + param2; + s += indent + param1 + "\n"; + if ( param2AsGeometry != null ) + s += indent + param2AsGeometry; + if ( param2AsValueReference != null ) + s += indent + param2AsValueReference; return s; } @Override public Object[] getParams() { - return new Object[] { propName, param2 }; + if ( param2AsValueReference != null ) + return new Object[] { param1, param2AsValueReference }; + return new Object[] { param1, param2AsGeometry }; } } \ No newline at end of file diff --git a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Beyond.java b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Beyond.java index 9c4fc5a3ab..a7ec2b3cd5 100644 --- a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Beyond.java +++ b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Beyond.java @@ -40,6 +40,7 @@ import org.deegree.filter.Expression; import org.deegree.filter.FilterEvaluationException; import org.deegree.filter.XPathEvaluator; +import org.deegree.filter.expression.ValueReference; import org.deegree.geometry.Geometry; /** @@ -52,21 +53,32 @@ */ public class Beyond extends SpatialOperator { - private final Geometry geometry; - private final Measure distance; - public Beyond( Expression propName, Geometry geometry, Measure distance ) { - super( propName ); - this.geometry = geometry; + /** + * @param param1 + * geometry to compare to, can be null (use default geometry) + * @param param2 + * geometry argument for testing, never null + * @param distance + * distance, never null + */ + public Beyond( Expression param1, Geometry param2, Measure distance ) { + super( param1, param2 ); this.distance = distance; } /** - * @return the geometry + * @param param1 + * geometry to compare to, can be null (use default geometry) + * @param param2 + * value reference argument for testing, never null + * @param distance + * distance, never null */ - public Geometry getGeometry() { - return geometry; + public Beyond( Expression param1, ValueReference param2, Measure distance ) { + super( param1, param2 ); + this.distance = distance; } /** @@ -79,10 +91,10 @@ public Measure getDistance() { @Override public boolean evaluate( T obj, XPathEvaluator xpathEvaluator ) throws FilterEvaluationException { - for ( TypedObjectNode param1Value : propName.evaluate( obj, xpathEvaluator ) ) { + for ( TypedObjectNode param1Value : param1.evaluate( obj, xpathEvaluator ) ) { Geometry geom = checkGeometryOrNull( param1Value ); if ( geom != null ) { - Geometry transformedLiteral = getCompatibleGeometry( geom, geometry ); + Geometry transformedLiteral = getCompatibleGeometry( geom, param2AsGeometry ); // TODO what about the units of the distance when transforming? return geom.isBeyond( transformedLiteral, distance ); } @@ -92,14 +104,19 @@ public boolean evaluate( T obj, XPathEvaluator xpathEvaluator ) public String toString( String indent ) { String s = indent + "-Beyond\n"; - s += indent + propName + "\n"; - s += indent + geometry + "\n"; + s += indent + param1 + "\n"; + if ( param2AsGeometry != null ) + s += indent + param2AsGeometry + "\n"; + if ( param2AsValueReference != null ) + s += indent + param2AsValueReference + "\n"; s += indent + distance; return s; } @Override public Object[] getParams() { - return new Object[] { propName, geometry }; + if ( param2AsValueReference != null ) + return new Object[] { param1, param2AsValueReference }; + return new Object[] { param1, param2AsGeometry }; } } diff --git a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Contains.java b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Contains.java index f36b15cb1a..9e102383bc 100644 --- a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Contains.java +++ b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Contains.java @@ -39,6 +39,7 @@ import org.deegree.filter.Expression; import org.deegree.filter.FilterEvaluationException; import org.deegree.filter.XPathEvaluator; +import org.deegree.filter.expression.ValueReference; import org.deegree.geometry.Geometry; /** @@ -51,42 +52,53 @@ */ public class Contains extends SpatialOperator { - private final Geometry geometry; - + /** + * @param param1 + * geometry to compare to, can be null (use default geometry) + * @param param2 + * geometry argument for testing, never null + */ public Contains( Expression param1, Geometry param2 ) { - super( param1 ); - this.geometry = param2; + super( param1, param2 ); + } + + /** + * @param param1 + * geometry to compare to, can be null (use default geometry) + * @param param2 + * value reference argument for testing, never null + */ + public Contains( Expression param1, ValueReference param2 ) { + super( param1, param2 ); } @Override public boolean evaluate( T obj, XPathEvaluator xpathEvaluator ) throws FilterEvaluationException { - for ( TypedObjectNode paramValue : propName.evaluate( obj, xpathEvaluator ) ) { + for ( TypedObjectNode paramValue : param1.evaluate( obj, xpathEvaluator ) ) { Geometry geom = checkGeometryOrNull( paramValue ); if ( geom != null ) { - Geometry transformedLiteral = getCompatibleGeometry( geom, geometry ); + Geometry transformedLiteral = getCompatibleGeometry( geom, param2AsGeometry ); return geom.contains( transformedLiteral ); } } return false; } - /** - * @return the geometry - */ - public Geometry getGeometry() { - return geometry; - } - public String toString( String indent ) { String s = indent + "-Contains\n"; - s += indent + propName + "\n"; - s += indent + geometry; + s += indent + param1 + "\n"; + if ( param2AsGeometry != null ) + s += indent + param2AsGeometry; + if ( param2AsValueReference != null ) + s += indent + param2AsValueReference; return s; } @Override public Object[] getParams() { - return new Object[] { propName, geometry }; + if ( param2AsValueReference != null ) + return new Object[] { param1, param2AsValueReference }; + return new Object[] { param1, param2AsGeometry }; } } \ No newline at end of file diff --git a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Crosses.java b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Crosses.java index a198ab15dc..aa1d39ff83 100644 --- a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Crosses.java +++ b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Crosses.java @@ -39,10 +39,11 @@ import org.deegree.filter.Expression; import org.deegree.filter.FilterEvaluationException; import org.deegree.filter.XPathEvaluator; +import org.deegree.filter.expression.ValueReference; import org.deegree.geometry.Geometry; /** - * TODO add documentation here + * If a geometry is spatially crossing in an other geometry. * * @author Markus Schneider * @author last edited by: $Author:$ @@ -51,43 +52,54 @@ */ public class Crosses extends SpatialOperator { - private final Geometry geometry; + /** + * @param param1 + * geometry to compare to, can be null (use default geometry) + * @param param2 + * geometry argument for testing, never null + */ + public Crosses( Expression param1, Geometry param2 ) { + super( param1, param2 ); + } - public Crosses( Expression propName, Geometry geometry ) { - super( propName ); - this.geometry = geometry; + /** + * @param param1 + * geometry to compare to, can be null (use default geometry) + * @param param2 + * value reference argument for testing, never null + */ + public Crosses( Expression param1, ValueReference param2 ) { + super( param1, param2 ); } @Override public boolean evaluate( T obj, XPathEvaluator xpathEvaluator ) throws FilterEvaluationException { - for ( TypedObjectNode paramValue : propName.evaluate( obj, xpathEvaluator ) ) { + for ( TypedObjectNode paramValue : param1.evaluate( obj, xpathEvaluator ) ) { Geometry geom = checkGeometryOrNull( paramValue ); if ( geom != null ) { - Geometry transformedLiteral = getCompatibleGeometry( geom, geometry ); + Geometry transformedLiteral = getCompatibleGeometry( geom, param2AsGeometry ); return geom.crosses( transformedLiteral ); } } return false; } - /** - * @return the geometry - */ - public Geometry getGeometry() { - return geometry; - } - @Override public String toString( String indent ) { String s = indent + "-Within\n"; - s += indent + propName + "\n"; - s += indent + geometry; + s += indent + param1 + "\n"; + if ( param2AsGeometry != null ) + s += indent + param2AsGeometry; + if ( param2AsValueReference != null ) + s += indent + param2AsValueReference; return s; } @Override public Object[] getParams() { - return new Object[] { propName, geometry }; + if ( param2AsValueReference != null ) + return new Object[] { param1, param2AsValueReference }; + return new Object[] { param1, param2AsGeometry }; } } diff --git a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/DWithin.java b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/DWithin.java index 61123e283b..0d2a16377b 100644 --- a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/DWithin.java +++ b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/DWithin.java @@ -40,6 +40,7 @@ import org.deegree.filter.Expression; import org.deegree.filter.FilterEvaluationException; import org.deegree.filter.XPathEvaluator; +import org.deegree.filter.expression.ValueReference; import org.deegree.geometry.Geometry; /** @@ -52,23 +53,41 @@ */ public class DWithin extends SpatialOperator { - private final Geometry geometry; - private final Measure distance; - public DWithin( Expression propName, Geometry geometry, Measure distance ) { - super( propName ); - this.geometry = geometry; + /** + * @param param1 + * geometry to compare to, can be null (use default geometry) + * @param param2 + * geometry argument for testing, never null + * @param distance + * never null + */ + public DWithin( Expression param1, Geometry param2, Measure distance ) { + super( param1, param2 ); + this.distance = distance; + } + + /** + * @param param1 + * geometry to compare to, can be null (use default geometry) + * @param param2 + * value reference argument for testing, never null + * @param distance + * never null + */ + public DWithin( Expression param1, ValueReference param2, Measure distance ) { + super( param1, param2 ); this.distance = distance; } @Override public boolean evaluate( T obj, XPathEvaluator xpathEvaluator ) throws FilterEvaluationException { - for ( TypedObjectNode param1Value : propName.evaluate( obj, xpathEvaluator ) ) { + for ( TypedObjectNode param1Value : param1.evaluate( obj, xpathEvaluator ) ) { Geometry geom = checkGeometryOrNull( param1Value ); if ( geom != null ) { - Geometry transformedLiteral = getCompatibleGeometry( geom, geometry ); + Geometry transformedLiteral = getCompatibleGeometry( geom, param2AsGeometry ); // TODO what about the units of the distance when transforming? return geom.isWithinDistance( transformedLiteral, distance ); } @@ -76,13 +95,6 @@ public boolean evaluate( T obj, XPathEvaluator xpathEvaluator ) return false; } - /** - * @return the geometry - */ - public Geometry getGeometry() { - return geometry; - } - /** * @return the distance */ @@ -92,14 +104,19 @@ public Measure getDistance() { public String toString( String indent ) { String s = indent + "-DWithin\n"; - s += indent + propName + "\n"; - s += indent + geometry + "\n"; + s += indent + param1 + "\n"; + if ( param2AsGeometry != null ) + s += indent + param2AsGeometry + "\n"; + if ( param2AsValueReference != null ) + s += indent + param2AsValueReference + "\n"; s += indent + distance; return s; } @Override public Object[] getParams() { - return new Object[] { propName, geometry }; + if ( param2AsValueReference != null ) + return new Object[] { param1, param2AsValueReference }; + return new Object[] { param1, param2AsGeometry }; } } diff --git a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Disjoint.java b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Disjoint.java index 91f1702c07..a194402d64 100644 --- a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Disjoint.java +++ b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Disjoint.java @@ -39,10 +39,11 @@ import org.deegree.filter.Expression; import org.deegree.filter.FilterEvaluationException; import org.deegree.filter.XPathEvaluator; +import org.deegree.filter.expression.ValueReference; import org.deegree.geometry.Geometry; /** - * TODO add documentation here + * If a geometry spatially disjoins an other geometry. * * @author Markus Schneider * @author last edited by: $Author:$ @@ -51,43 +52,54 @@ */ public class Disjoint extends SpatialOperator { - private final Geometry geometry; + /** + * @param param1 + * geometry to compare to, can be null (use default geometry) + * @param param2 + * geometry argument for testing, never null + */ + public Disjoint( Expression param1, Geometry param2 ) { + super( param1, param2 ); + } - public Disjoint( Expression propName, Geometry geometry ) { - super( propName ); - this.geometry = geometry; + /** + * @param param1 + * geometry to compare to, can be null (use default geometry) + * @param param2 + * value reference argument for testing, never null + */ + public Disjoint( Expression param1, ValueReference param2 ) { + super( param1, param2 ); } @Override public boolean evaluate( T obj, XPathEvaluator xpathEvaluator ) throws FilterEvaluationException { - for ( TypedObjectNode paramValue : propName.evaluate( obj, xpathEvaluator ) ) { + for ( TypedObjectNode paramValue : param1.evaluate( obj, xpathEvaluator ) ) { Geometry geom = checkGeometryOrNull( paramValue ); if ( geom != null ) { - Geometry transformedLiteral = getCompatibleGeometry( geom, geometry ); + Geometry transformedLiteral = getCompatibleGeometry( geom, param2AsGeometry ); return geom.isDisjoint( transformedLiteral ); } } return false; } - /** - * @return the geometry - */ - public Geometry getGeometry() { - return geometry; - } - @Override public String toString( String indent ) { String s = indent + "-Disjoint\n"; - s += indent + propName + "\n"; - s += indent + geometry; + s += indent + param1 + "\n"; + if ( param2AsGeometry != null ) + s += indent + param2AsGeometry; + if ( param2AsValueReference != null ) + s += indent + param2AsValueReference; return s; } @Override public Object[] getParams() { - return new Object[] { propName, geometry }; + if ( param2AsValueReference != null ) + return new Object[] { param1, param2AsValueReference }; + return new Object[] { param1, param2AsGeometry }; } } diff --git a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Equals.java b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Equals.java index 727554b1cf..a313544e98 100644 --- a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Equals.java +++ b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Equals.java @@ -39,10 +39,11 @@ import org.deegree.filter.Expression; import org.deegree.filter.FilterEvaluationException; import org.deegree.filter.XPathEvaluator; +import org.deegree.filter.expression.ValueReference; import org.deegree.geometry.Geometry; /** - * TODO add documentation here + * If a geometry is spatially equal to an other geometry. * * @author Markus Schneider * @author last edited by: $Author:$ @@ -51,43 +52,54 @@ */ public class Equals extends SpatialOperator { - private final Geometry geometry; + /** + * @param param1 + * geometry to compare to, can be null (use default geometry) + * @param param2 + * geometry argument for testing, never null + */ + public Equals( Expression param1, Geometry param2 ) { + super( param1, param2 ); + } - public Equals( Expression propName, Geometry geometry ) { - super( propName ); - this.geometry = geometry; + /** + * @param param1 + * geometry to compare to, can be null (use default geometry) + * @param param2 + * value reference argument for testing, never null + */ + public Equals( Expression param1, ValueReference param2 ) { + super( param1, param2 ); } @Override public boolean evaluate( T obj, XPathEvaluator xpathEvaluator ) throws FilterEvaluationException { - for ( TypedObjectNode paramValue : propName.evaluate( obj, xpathEvaluator ) ) { + for ( TypedObjectNode paramValue : param1.evaluate( obj, xpathEvaluator ) ) { Geometry geom = checkGeometryOrNull( paramValue ); if ( geom != null ) { - Geometry transformedLiteral = getCompatibleGeometry( geom, geometry ); + Geometry transformedLiteral = getCompatibleGeometry( geom, param2AsGeometry ); return geom.equals( transformedLiteral ); } } return false; } - /** - * @return the geometry - */ - public Geometry getGeometry() { - return geometry; - } - @Override public String toString( String indent ) { String s = indent + "-Equals\n"; - s += indent + propName + "\n"; - s += indent + geometry; + s += indent + param1 + "\n"; + if ( param2AsGeometry != null ) + s += indent + param2AsGeometry; + if ( param2AsValueReference != null ) + s += indent + param2AsValueReference; return s; } @Override public Object[] getParams() { - return new Object[] { propName, geometry }; + if ( param2AsValueReference != null ) + return new Object[] { param1, param2AsValueReference }; + return new Object[] { param1, param2AsGeometry }; } } diff --git a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Intersects.java b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Intersects.java index c1c1de3f10..6e9e2a9f88 100644 --- a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Intersects.java +++ b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Intersects.java @@ -59,8 +59,6 @@ public class Intersects extends SpatialOperator { private static final Logger LOG = LoggerFactory.getLogger( Intersects.class ); - private final Geometry geometry; - /** * Instantiates a {@link Intersects} operator with geometry as second parameter. * @@ -71,7 +69,7 @@ public class Intersects extends SpatialOperator { * never null */ public Intersects( Expression propName, Geometry geometry ) { - this( propName, geometry, null ); + super( propName, geometry ); } /** @@ -84,18 +82,13 @@ public Intersects( Expression propName, Geometry geometry ) { * never null */ public Intersects( Expression propName, ValueReference valueReference ) { - this( propName, null, valueReference ); - } - - private Intersects( Expression propName, Geometry geometry, ValueReference valueReference ) { super( propName, valueReference ); - this.geometry = geometry; } @Override public boolean evaluate( T obj, XPathEvaluator xpathEvaluator ) throws FilterEvaluationException { - if ( geometry == null ) + if ( param2AsGeometry == null ) return false; Expression param1 = getParam1(); @@ -103,7 +96,7 @@ public boolean evaluate( T obj, XPathEvaluator xpathEvaluator ) for ( TypedObjectNode paramValue : param1.evaluate( obj, xpathEvaluator ) ) { Geometry param1Value = checkGeometryOrNull( paramValue ); if ( param1Value != null ) { - Geometry transformedGeom = getCompatibleGeometry( param1Value, geometry ); + Geometry transformedGeom = getCompatibleGeometry( param1Value, param2AsGeometry ); return transformedGeom.intersects( param1Value ); } } @@ -115,8 +108,8 @@ public boolean evaluate( T obj, XPathEvaluator xpathEvaluator ) if ( prop.getValue() instanceof Geometry ) { foundGeom = true; Geometry geom = (Geometry) prop.getValue(); - Geometry transformedGeom = getCompatibleGeometry( geometry, geom ); - if ( transformedGeom.intersects( geometry ) ) { + Geometry transformedGeom = getCompatibleGeometry( param2AsGeometry, geom ); + if ( transformedGeom.intersects( param2AsGeometry ) ) { return true; } } @@ -124,8 +117,8 @@ public boolean evaluate( T obj, XPathEvaluator xpathEvaluator ) if ( !foundGeom ) { Envelope env = f.getEnvelope(); if ( env != null ) { - Geometry g = getCompatibleGeometry( geometry, env ); - if ( g.intersects( geometry ) ) { + Geometry g = getCompatibleGeometry( param2AsGeometry, env ); + if ( g.intersects( param2AsGeometry ) ) { return true; } } @@ -134,8 +127,8 @@ public boolean evaluate( T obj, XPathEvaluator xpathEvaluator ) for ( Property prop : f.getExtraProperties().getProperties() ) { if ( prop.getValue() instanceof Geometry ) { Geometry geom = (Geometry) prop.getValue(); - Geometry transformedGeom = getCompatibleGeometry( geometry, geom ); - if ( transformedGeom.intersects( geometry ) ) { + Geometry transformedGeom = getCompatibleGeometry( param2AsGeometry, geom ); + if ( transformedGeom.intersects( param2AsGeometry ) ) { return true; } } @@ -147,30 +140,22 @@ public boolean evaluate( T obj, XPathEvaluator xpathEvaluator ) return false; } - /** - * @return the second parameter, null if it is a value reference - */ - public Geometry getGeometry() { - return geometry; - } - - @Override public String toString( String indent ) { String s = indent + "-Intersects\n"; - s += indent + propName + "\n"; - if ( geometry != null ) - s += indent + geometry; - if ( valueReference != null ) - s += indent + valueReference; + s += indent + param1 + "\n"; + if ( param2AsGeometry != null ) + s += indent + param2AsGeometry; + if ( param2AsValueReference != null ) + s += indent + param2AsValueReference; return s; } @Override public Object[] getParams() { - if ( valueReference != null ) - return new Object[] { propName, valueReference }; - return new Object[] { propName, geometry }; + if ( param2AsValueReference != null ) + return new Object[] { param1, param2AsValueReference }; + return new Object[] { param1, param2AsGeometry }; } } \ No newline at end of file diff --git a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Overlaps.java b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Overlaps.java index d2c29fd681..64b9e2ede8 100644 --- a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Overlaps.java +++ b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Overlaps.java @@ -39,10 +39,11 @@ import org.deegree.filter.Expression; import org.deegree.filter.FilterEvaluationException; import org.deegree.filter.XPathEvaluator; +import org.deegree.filter.expression.ValueReference; import org.deegree.geometry.Geometry; /** - * TODO add documentation here + * If a geometry is spatially overlaps an other geometry. * * @author Markus Schneider * @author last edited by: $Author:$ @@ -51,43 +52,54 @@ */ public class Overlaps extends SpatialOperator { - private final Geometry geometry; + /** + * @param param1 + * geometry to compare to, can be null (use default geometry) + * @param param2 + * geometry argument for testing, never null + */ + public Overlaps( Expression param1, Geometry param2 ) { + super( param1, param2 ); + } - public Overlaps( Expression propName, Geometry geometry ) { - super( propName ); - this.geometry = geometry; + /** + * @param param1 + * geometry to compare to, can be null (use default geometry) + * @param param2 + * value reference argument for testing, never null + */ + public Overlaps( Expression param1, ValueReference param2 ) { + super( param1, param2 ); } @Override public boolean evaluate( T obj, XPathEvaluator xpathEvaluator ) throws FilterEvaluationException { - for ( TypedObjectNode paramValue : propName.evaluate( obj, xpathEvaluator ) ) { + for ( TypedObjectNode paramValue : param1.evaluate( obj, xpathEvaluator ) ) { Geometry geom = checkGeometryOrNull( paramValue ); if ( geom != null ) { - Geometry transformedLiteral = getCompatibleGeometry( geom, geometry ); + Geometry transformedLiteral = getCompatibleGeometry( geom, param2AsGeometry ); return geom.overlaps( transformedLiteral ); } } return false; } - /** - * @return the geometry - */ - public Geometry getGeometry() { - return geometry; - } - @Override public String toString( String indent ) { String s = indent + "-Overlaps\n"; - s += indent + propName + "\n"; - s += indent + geometry; + s += indent + param1 + "\n"; + if ( param2AsGeometry != null ) + s += indent + param2AsGeometry; + if ( param2AsValueReference != null ) + s += indent + param2AsValueReference; return s; } @Override public Object[] getParams() { - return new Object[] { propName, geometry }; + if ( param2AsValueReference != null ) + return new Object[] { param1, param2AsValueReference }; + return new Object[] { param1, param2AsGeometry }; } } diff --git a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/SpatialOperator.java b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/SpatialOperator.java index 037940d623..451d933779 100644 --- a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/SpatialOperator.java +++ b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/SpatialOperator.java @@ -66,9 +66,11 @@ public abstract class SpatialOperator implements Operator { private final Map srsNameToTransformedGeometry = new HashMap(); - protected final Expression propName; + protected final Expression param1; - protected final ValueReference valueReference; + protected final ValueReference param2AsValueReference; + + protected final Geometry param2AsGeometry; /** * Instantiates a {@link SpatialOperator} without second parameter (may be stored in the implementation). @@ -76,9 +78,11 @@ public abstract class SpatialOperator implements Operator { * @param param1 * may actually be null (deegree extension to cope with features that have only hidden * geometry props) + * @param geometry + * second parameter, never null */ - protected SpatialOperator( Expression param1 ) { - this( param1, null ); + protected SpatialOperator( Expression param1, Geometry geometry ) { + this( param1, geometry, null ); } /** @@ -88,11 +92,16 @@ protected SpatialOperator( Expression param1 ) { * may actually be null (deegree extension to cope with features that have only hidden * geometry props) * @param valueReference - * never null + * second parameter,never null */ protected SpatialOperator( Expression param1, ValueReference valueReference ) { - this.propName = param1; - this.valueReference = valueReference; + this( param1, null, valueReference ); + } + + private SpatialOperator( Expression param1, Geometry geometry, ValueReference valueReference ) { + this.param1 = param1; + this.param2AsGeometry = geometry; + this.param2AsValueReference = valueReference; } /** @@ -150,14 +159,21 @@ public SubType getSubType() { * @return the first spatial parameter, may be null (target default geometry property of object) */ public Expression getParam1() { - return propName; + return param1; } /** * @return the second parameter, null if it is a geometry */ public ValueReference getValueReference() { - return valueReference; + return param2AsValueReference; + } + + /** + * @return the second parameter, null if it is a value reference + */ + public Geometry getGeometry() { + return param2AsGeometry; } /** @@ -167,7 +183,7 @@ public ValueReference getValueReference() { * @deprecated use {@link #getParam1()} instead */ public ValueReference getPropName() { - return (ValueReference) propName; + return (ValueReference) param1; } /** diff --git a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Touches.java b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Touches.java index 4a2ac458ee..7f2e70a723 100644 --- a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Touches.java +++ b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Touches.java @@ -39,10 +39,11 @@ import org.deegree.filter.Expression; import org.deegree.filter.FilterEvaluationException; import org.deegree.filter.XPathEvaluator; +import org.deegree.filter.expression.ValueReference; import org.deegree.geometry.Geometry; /** - * TODO add documentation here + * If a geometry is spatially crossing in an other geometry. * * @author Markus Schneider * @author last edited by: $Author:$ @@ -51,43 +52,54 @@ */ public class Touches extends SpatialOperator { - private final Geometry geometry; + /** + * @param param1 + * geometry to compare to, can be null (use default geometry) + * @param param2 + * geometry argument for testing, never null + */ + public Touches( Expression param1, Geometry param2 ) { + super( param1, param2 ); + } - public Touches( Expression propName, Geometry geometry ) { - super( propName ); - this.geometry = geometry; + /** + * @param param1 + * geometry to compare to, can be null (use default geometry) + * @param param2 + * value reference argument for testing, never null + */ + public Touches( Expression param1, ValueReference param2 ) { + super( param1, param2 ); } @Override public boolean evaluate( T obj, XPathEvaluator xpathEvaluator ) throws FilterEvaluationException { - for ( TypedObjectNode paramValue : propName.evaluate( obj, xpathEvaluator ) ) { + for ( TypedObjectNode paramValue : param1.evaluate( obj, xpathEvaluator ) ) { Geometry geom = checkGeometryOrNull( paramValue ); if ( geom != null ) { - Geometry transformedLiteral = getCompatibleGeometry( geom, geometry ); + Geometry transformedLiteral = getCompatibleGeometry( geom, param2AsGeometry ); return geom.touches( transformedLiteral ); } } return false; } - /** - * @return the geometry - */ - public Geometry getGeometry() { - return geometry; - } - @Override public String toString( String indent ) { String s = indent + "-Touches\n"; - s += indent + propName + "\n"; - s += indent + geometry; + s += indent + param1 + "\n"; + if ( param2AsGeometry != null ) + s += indent + param2AsGeometry; + if ( param2AsValueReference != null ) + s += indent + param2AsValueReference; return s; } @Override public Object[] getParams() { - return new Object[] { propName, geometry }; + if ( param2AsValueReference != null ) + return new Object[] { param1, param2AsValueReference }; + return new Object[] { param1, param2AsGeometry }; } } diff --git a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Within.java b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Within.java index 57f1de4231..d0d6a285e3 100644 --- a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Within.java +++ b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/spatial/Within.java @@ -39,10 +39,11 @@ import org.deegree.filter.Expression; import org.deegree.filter.FilterEvaluationException; import org.deegree.filter.XPathEvaluator; +import org.deegree.filter.expression.ValueReference; import org.deegree.geometry.Geometry; /** - * TODO add documentation here + * If a geometry is spatially within an other geometry. * * @author Markus Schneider * @author last edited by: $Author:$ @@ -51,19 +52,32 @@ */ public class Within extends SpatialOperator { - private final Geometry geometry; - + /** + * @param param1 + * geometry to compare to, can be null (use default geometry) + * @param param2 + * geometry argument for testing, never null + */ public Within( Expression param1, Geometry param2 ) { - super( param1 ); - this.geometry = param2; + super( param1, param2 ); + } + + /** + * @param param1 + * geometry to compare to, can be null (use default geometry) + * @param param2 + * value reference argument for testing, never null + */ + public Within( Expression param1, ValueReference param2 ) { + super( param1, param2 ); } public boolean evaluate( T obj, XPathEvaluator xpathEvaluator ) throws FilterEvaluationException { - for ( TypedObjectNode paramValue : propName.evaluate( obj, xpathEvaluator ) ) { + for ( TypedObjectNode paramValue : param1.evaluate( obj, xpathEvaluator ) ) { Geometry geom = checkGeometryOrNull( paramValue ); if ( geom != null ) { - Geometry transformedLiteral = getCompatibleGeometry( geom, geometry ); + Geometry transformedLiteral = getCompatibleGeometry( geom, param2AsGeometry ); return geom.isWithin( transformedLiteral ); } } @@ -73,17 +87,18 @@ public boolean evaluate( T obj, XPathEvaluator xpathEvaluator ) @Override public String toString( String indent ) { String s = indent + "-Within\n"; - s += indent + propName + "\n"; - s += indent + geometry; + s += indent + param1 + "\n"; + if ( param2AsGeometry != null ) + s += indent + param2AsGeometry; + if ( param2AsValueReference != null ) + s += indent + param2AsValueReference; return s; } - public Geometry getGeometry() { - return geometry; - } - @Override public Object[] getParams() { - return new Object[] { propName, geometry }; + if ( param2AsValueReference != null ) + return new Object[] { param1, param2AsValueReference }; + return new Object[] { param1, param2AsGeometry }; } } \ No newline at end of file From 3e89b4fc38ee77d18d685200e5702b2743985664 Mon Sep 17 00:00:00 2001 From: Lyn Elisa Goltz Date: Thu, 23 Jun 2016 09:27:45 +0200 Subject: [PATCH 18/25] #3609 - implemented encoding and decoding of filter with spatial join for 2.0.0 --- .../filter/xml/Filter200XMLDecoder.java | 152 +++++++++++++----- .../filter/xml/Filter200XMLEncoder.java | 32 +--- .../filter/xml/Filter200XMLDecoderTest.java | 127 ++++++++++++++- .../Filter200XMLEncoderParameterizedTest.java | 10 ++ .../filter/xml/v200/bboxWithSpatialJoin.xml | 9 ++ .../filter/xml/v200/beyondWithSpatialJoin.xml | 10 ++ .../xml/v200/containsWithSpatialJoin.xml | 9 ++ .../xml/v200/crossesWithSpatialJoin.xml | 9 ++ .../xml/v200/disjointWithSpatialJoin.xml | 9 ++ .../xml/v200/dwithinWithSpatialJoin.xml | 10 ++ .../filter/xml/v200/equalsWithSpatialJoin.xml | 9 ++ .../xml/v200/overlapsWithSpatialJoin.xml | 9 ++ .../xml/v200/touchesWithSpatialJoin.xml | 9 ++ .../filter/xml/v200/withinWithSpatialJoin.xml | 9 ++ 14 files changed, 341 insertions(+), 72 deletions(-) create mode 100644 deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/bboxWithSpatialJoin.xml create mode 100644 deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/beyondWithSpatialJoin.xml create mode 100644 deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/containsWithSpatialJoin.xml create mode 100644 deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/crossesWithSpatialJoin.xml create mode 100644 deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/disjointWithSpatialJoin.xml create mode 100644 deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/dwithinWithSpatialJoin.xml create mode 100644 deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/equalsWithSpatialJoin.xml create mode 100644 deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/overlapsWithSpatialJoin.xml create mode 100644 deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/touchesWithSpatialJoin.xml create mode 100644 deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/withinWithSpatialJoin.xml diff --git a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/xml/Filter200XMLDecoder.java b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/xml/Filter200XMLDecoder.java index 06eed8e361..bf59378ffc 100644 --- a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/xml/Filter200XMLDecoder.java +++ b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/xml/Filter200XMLDecoder.java @@ -1013,9 +1013,15 @@ private static SpatialOperator parseSpatialOperator( XMLStreamReader xmlStream ) param1 = parseExpression( xmlStream ); nextElement( xmlStream ); } - // (must be 'gml:Envelope'/'gml:Box') - Geometry param2 = parseGeomOrEnvelope( xmlStream ); - spatialOperator = new BBOX( param1, (Envelope) param2 ); + if ( isCurrentStartElementIsGmlGeometry( xmlStream ) ) { + // (is 'gml:Envelope'/'gml:Box') + Geometry param2 = parseGeomOrEnvelope( xmlStream ); + spatialOperator = new BBOX( param1, (Envelope) param2 ); + } else { + // (is 'fes:ValueReference') + ValueReference param2 = parseValueReference( xmlStream, false ); + spatialOperator = new BBOX( param1, param2 ); + } break; } case BEYOND: { @@ -1025,15 +1031,21 @@ private static SpatialOperator parseSpatialOperator( XMLStreamReader xmlStream ) param1 = parseExpression( xmlStream ); nextElement( xmlStream ); } - // (must be 'gml:Geometry') - Geometry param2 = parseGeomOrEnvelope( xmlStream ); - // third parameter: 'fes:Distance' - nextElement( xmlStream ); - xmlStream.require( START_ELEMENT, FES_NS, "Distance" ); - String distanceUnits = getRequiredAttributeValue( xmlStream, "uom" ); - String distanceValue = xmlStream.getElementText(); - Measure distance = new Measure( distanceValue, distanceUnits ); - spatialOperator = new Beyond( param1, param2, distance ); + if ( isCurrentStartElementIsGmlGeometry( xmlStream ) ) { + // (is 'gml:Geometry') + Geometry param2 = parseGeomOrEnvelope( xmlStream ); + // third parameter: 'fes:Distance' + nextElement( xmlStream ); + Measure distance = parseDistance( xmlStream ); + spatialOperator = new Beyond( param1, param2, distance ); + } else { + // (is 'fes:ValueReference') + ValueReference param2 = parseValueReference( xmlStream, false ); + // third parameter: 'fes:Distance' + nextElement( xmlStream ); + Measure distance = parseDistance( xmlStream ); + spatialOperator = new Beyond( param1, param2, distance ); + } break; } case INTERSECTS: { @@ -1061,9 +1073,15 @@ private static SpatialOperator parseSpatialOperator( XMLStreamReader xmlStream ) param1 = parseExpression( xmlStream ); nextElement( xmlStream ); } - // (must be 'gml:Geometry') - Geometry param2 = parseGeomOrEnvelope( xmlStream ); - spatialOperator = new Contains( param1, param2 ); + if ( isCurrentStartElementIsGmlGeometry( xmlStream ) ) { + // (is 'gml:Geometry') + Geometry param2 = parseGeomOrEnvelope( xmlStream ); + spatialOperator = new Contains( param1, param2 ); + } else { + // (is 'fes:ValueReference') + ValueReference param2 = parseValueReference( xmlStream, false ); + spatialOperator = new Contains( param1, param2 ); + } break; } case CROSSES: { @@ -1073,9 +1091,15 @@ private static SpatialOperator parseSpatialOperator( XMLStreamReader xmlStream ) param1 = parseExpression( xmlStream ); nextElement( xmlStream ); } - // (must be 'gml:Geometry') - Geometry param2 = parseGeomOrEnvelope( xmlStream ); - spatialOperator = new Crosses( param1, param2 ); + if ( isCurrentStartElementIsGmlGeometry( xmlStream ) ) { + // (is 'gml:Geometry') + Geometry param2 = parseGeomOrEnvelope( xmlStream ); + spatialOperator = new Crosses( param1, param2 ); + } else { + // (is 'fes:ValueReference') + ValueReference param2 = parseValueReference( xmlStream, false ); + spatialOperator = new Crosses( param1, param2 ); + } break; } case DISJOINT: { @@ -1085,9 +1109,15 @@ private static SpatialOperator parseSpatialOperator( XMLStreamReader xmlStream ) param1 = parseExpression( xmlStream ); nextElement( xmlStream ); } - // (must be 'gml:Geometry') - Geometry param2 = parseGeomOrEnvelope( xmlStream ); - spatialOperator = new Disjoint( param1, param2 ); + if ( isCurrentStartElementIsGmlGeometry( xmlStream ) ) { + // (is 'gml:Geometry') + Geometry param2 = parseGeomOrEnvelope( xmlStream ); + spatialOperator = new Disjoint( param1, param2 ); + } else { + // (is 'fes:ValueReference') + ValueReference param2 = parseValueReference( xmlStream, false ); + spatialOperator = new Disjoint( param1, param2 ); + } break; } case DWITHIN: { @@ -1097,15 +1127,21 @@ private static SpatialOperator parseSpatialOperator( XMLStreamReader xmlStream ) param1 = parseExpression( xmlStream ); nextElement( xmlStream ); } - // (must be 'gml:Geometry') - Geometry param2 = parseGeomOrEnvelope( xmlStream ); - // third parameter: 'fes:Distance' - nextElement( xmlStream ); - xmlStream.require( START_ELEMENT, FES_NS, "Distance" ); - String distanceUnits = getRequiredAttributeValue( xmlStream, "uom" ); - String distanceValue = xmlStream.getElementText(); - Measure distance = new Measure( distanceValue, distanceUnits ); - spatialOperator = new DWithin( param1, param2, distance ); + if ( isCurrentStartElementIsGmlGeometry( xmlStream ) ) { + // (is 'gml:Geometry') + Geometry param2 = parseGeomOrEnvelope( xmlStream ); + // third parameter: 'fes:Distance' + nextElement( xmlStream ); + Measure distance = parseDistance( xmlStream ); + spatialOperator = new DWithin( param1, param2, distance ); + } else { + // (is 'fes:ValueReference') + ValueReference param2 = parseValueReference( xmlStream, false ); + // third parameter: 'fes:Distance' + nextElement( xmlStream ); + Measure distance = parseDistance( xmlStream ); + spatialOperator = new DWithin( param1, param2, distance ); + } break; } case EQUALS: { @@ -1115,9 +1151,15 @@ private static SpatialOperator parseSpatialOperator( XMLStreamReader xmlStream ) param1 = parseExpression( xmlStream ); nextElement( xmlStream ); } - // (must be 'gml:Geometry') - Geometry param2 = parseGeomOrEnvelope( xmlStream ); - spatialOperator = new Equals( param1, param2 ); + if ( isCurrentStartElementIsGmlGeometry( xmlStream ) ) { + // (is 'gml:Geometry') + Geometry param2 = parseGeomOrEnvelope( xmlStream ); + spatialOperator = new Equals( param1, param2 ); + } else { + // (is 'fes:ValueReference') + ValueReference param2 = parseValueReference( xmlStream, false ); + spatialOperator = new Equals( param1, param2 ); + } break; } case OVERLAPS: { @@ -1127,9 +1169,15 @@ private static SpatialOperator parseSpatialOperator( XMLStreamReader xmlStream ) param1 = parseExpression( xmlStream ); nextElement( xmlStream ); } - // (must be 'gml:Geometry') - Geometry param2 = parseGeomOrEnvelope( xmlStream ); - spatialOperator = new Overlaps( param1, param2 ); + if ( isCurrentStartElementIsGmlGeometry( xmlStream ) ) { + // (is 'gml:Geometry') + Geometry param2 = parseGeomOrEnvelope( xmlStream ); + spatialOperator = new Overlaps( param1, param2 ); + } else { + // (is 'fes:ValueReference') + ValueReference param2 = parseValueReference( xmlStream, false ); + spatialOperator = new Overlaps( param1, param2 ); + } break; } case TOUCHES: { @@ -1139,9 +1187,15 @@ private static SpatialOperator parseSpatialOperator( XMLStreamReader xmlStream ) param1 = parseExpression( xmlStream ); nextElement( xmlStream ); } - // (must be 'gml:Geometry') - Geometry param2 = parseGeomOrEnvelope( xmlStream ); - spatialOperator = new Touches( param1, param2 ); + if ( isCurrentStartElementIsGmlGeometry( xmlStream ) ) { + // (is 'gml:Geometry') + Geometry param2 = parseGeomOrEnvelope( xmlStream ); + spatialOperator = new Touches( param1, param2 ); + } else { + // (is 'fes:ValueReference') + ValueReference param2 = parseValueReference( xmlStream, false ); + spatialOperator = new Touches( param1, param2 ); + } break; } case WITHIN: { @@ -1151,9 +1205,15 @@ private static SpatialOperator parseSpatialOperator( XMLStreamReader xmlStream ) param1 = parseExpression( xmlStream ); nextElement( xmlStream ); } - // (must be 'gml:Geometry') - Geometry param2 = parseGeomOrEnvelope( xmlStream ); - spatialOperator = new Within( param1, param2 ); + if ( isCurrentStartElementIsGmlGeometry( xmlStream ) ) { + // (is 'gml:Geometry') + Geometry param2 = parseGeomOrEnvelope( xmlStream ); + spatialOperator = new Within( param1, param2 ); + } else { + // (is 'fes:ValueReference') + ValueReference param2 = parseValueReference( xmlStream, false ); + spatialOperator = new Within( param1, param2 ); + } break; } } @@ -1177,6 +1237,14 @@ private static Geometry parseGeomOrEnvelope( XMLStreamReader xmlStream ) } } + private static Measure parseDistance( XMLStreamReader xmlStream ) + throws XMLStreamException { + xmlStream.require( START_ELEMENT, FES_NS, "Distance" ); + String distanceUnits = getRequiredAttributeValue( xmlStream, "uom" ); + String distanceValue = xmlStream.getElementText(); + return new Measure( distanceValue, distanceUnits ); + } + private static void checkRequiredGmlGeometry( XMLStreamReader xmlStream ) throws XMLStreamException { if ( isCurrentStartElementIsGmlGeometry( xmlStream ) ) diff --git a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/xml/Filter200XMLEncoder.java b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/xml/Filter200XMLEncoder.java index 0c47c9cdec..0cf3879712 100644 --- a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/xml/Filter200XMLEncoder.java +++ b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/xml/Filter200XMLEncoder.java @@ -275,45 +275,25 @@ private static void export( SpatialOperator operator, XMLStreamWriter writer ) + " is not supported yet!" ); writer.writeStartElement( elementName.getNamespaceURI(), elementName.getLocalPart() ); - Geometry geometry = null; - ValueReference secondParam = null; + Geometry geometry = operator.getGeometry(); + ValueReference secondParam = operator.getValueReference(); Measure distance = null; switch ( operator.getSubType() ) { - case BBOX: - geometry = ( (BBOX) operator ).getBoundingBox(); - break; case BEYOND: - geometry = ( (Beyond) operator ).getGeometry(); distance = ( (Beyond) operator ).getDistance(); break; - case CONTAINS: - geometry = ( (Contains) operator ).getGeometry(); - break; - case CROSSES: - geometry = ( (Crosses) operator ).getGeometry(); - break; - case DISJOINT: - geometry = ( (Disjoint) operator ).getGeometry(); - break; case DWITHIN: - geometry = ( (DWithin) operator ).getGeometry(); distance = ( (DWithin) operator ).getDistance(); break; + case BBOX: + case CONTAINS: + case CROSSES: + case DISJOINT: case EQUALS: - geometry = ( (Equals) operator ).getGeometry(); - break; case INTERSECTS: - geometry = ( (Intersects) operator ).getGeometry(); - secondParam = ( (Intersects) operator ).getValueReference(); - break; case OVERLAPS: - geometry = ( (Overlaps) operator ).getGeometry(); - break; case TOUCHES: - geometry = ( (Touches) operator ).getGeometry(); - break; case WITHIN: - geometry = ( (Within) operator ).getGeometry(); break; default: throw new IllegalArgumentException( "Encoding of spatial operator subtype " + operator.getSubType() diff --git a/deegree-core/deegree-core-base/src/test/java/org/deegree/filter/xml/Filter200XMLDecoderTest.java b/deegree-core/deegree-core-base/src/test/java/org/deegree/filter/xml/Filter200XMLDecoderTest.java index 59dc610882..a78fcff771 100644 --- a/deegree-core/deegree-core-base/src/test/java/org/deegree/filter/xml/Filter200XMLDecoderTest.java +++ b/deegree-core/deegree-core-base/src/test/java/org/deegree/filter/xml/Filter200XMLDecoderTest.java @@ -57,6 +57,7 @@ import org.deegree.commons.xml.stax.XMLStreamUtils; import org.deegree.filter.Filter; import org.deegree.filter.IdFilter; +import org.deegree.filter.Operator; import org.deegree.filter.OperatorFilter; import org.deegree.filter.comparison.PropertyIsBetween; import org.deegree.filter.comparison.PropertyIsEqualTo; @@ -67,9 +68,17 @@ import org.deegree.filter.expression.ValueReference; import org.deegree.filter.logical.And; import org.deegree.filter.logical.Not; +import org.deegree.filter.spatial.BBOX; +import org.deegree.filter.spatial.Beyond; +import org.deegree.filter.spatial.Contains; +import org.deegree.filter.spatial.Crosses; +import org.deegree.filter.spatial.DWithin; import org.deegree.filter.spatial.Disjoint; +import org.deegree.filter.spatial.Equals; import org.deegree.filter.spatial.Intersects; import org.deegree.filter.spatial.Overlaps; +import org.deegree.filter.spatial.Touches; +import org.deegree.filter.spatial.Within; import org.deegree.geometry.Envelope; import org.deegree.geometry.primitive.Polygon; import org.deegree.workspace.standard.DefaultWorkspace; @@ -332,19 +341,119 @@ public void parseOGCExample14() } } + @Test + public void parseBBoxWithSpatialJoin() + throws XMLStreamException, FactoryConfigurationError, IOException { + BBOX bbox = (BBOX) parseFilter( "v200/bboxWithSpatialJoin.xml" ); + + assertThat( ( (ValueReference) bbox.getParam1() ).getAsText(), is( "app:ft1/geometry" ) ); + assertThat( bbox.getGeometry(), is( CoreMatchers.nullValue() ) ); + assertThat( bbox.getBoundingBox(), is( CoreMatchers.nullValue() ) ); + assertThat( bbox.getValueReference().getAsText(), is( "app:ft2/geometry" ) ); + } + + @Test + public void parseBeyondWithSpatialJoin() + throws XMLStreamException, FactoryConfigurationError, IOException { + Beyond beyond = (Beyond) parseFilter( "v200/beyondWithSpatialJoin.xml" ); + + assertThat( ( (ValueReference) beyond.getParam1() ).getAsText(), is( "app:ft1/geometry" ) ); + assertThat( beyond.getGeometry(), is( CoreMatchers.nullValue() ) ); + assertThat( beyond.getValueReference().getAsText(), is( "app:ft2/geometry" ) ); + assertThat( beyond.getDistance().getValueAsDouble(), is( 10d ) ); + } + + @Test + public void parseContainsWithSpatialJoin() + throws XMLStreamException, FactoryConfigurationError, IOException { + Contains contains = (Contains) parseFilter( "v200/containsWithSpatialJoin.xml" ); + + assertThat( ( (ValueReference) contains.getParam1() ).getAsText(), is( "app:ft1/geometry" ) ); + assertThat( contains.getGeometry(), is( CoreMatchers.nullValue() ) ); + assertThat( contains.getValueReference().getAsText(), is( "app:ft2/geometry" ) ); + } + + @Test + public void parseCrossesWithSpatialJoin() + throws XMLStreamException, FactoryConfigurationError, IOException { + Crosses crosses = (Crosses) parseFilter( "v200/crossesWithSpatialJoin.xml" ); + + assertThat( ( (ValueReference) crosses.getParam1() ).getAsText(), is( "app:ft1/geometry" ) ); + assertThat( crosses.getGeometry(), is( CoreMatchers.nullValue() ) ); + assertThat( crosses.getValueReference().getAsText(), is( "app:ft2/geometry" ) ); + } + + @Test + public void parseDisjointWithSpatialJoin() + throws XMLStreamException, FactoryConfigurationError, IOException { + Disjoint disjoint = (Disjoint) parseFilter( "v200/disjointWithSpatialJoin.xml" ); + + assertThat( ( (ValueReference) disjoint.getParam1() ).getAsText(), is( "app:ft1/geometry" ) ); + assertThat( disjoint.getGeometry(), is( CoreMatchers.nullValue() ) ); + assertThat( disjoint.getValueReference().getAsText(), is( "app:ft2/geometry" ) ); + } + + @Test + public void parseDWithinWithSpatialJoin() + throws XMLStreamException, FactoryConfigurationError, IOException { + DWithin dwithin = (DWithin) parseFilter( "v200/dwithinWithSpatialJoin.xml" ); + + assertThat( ( (ValueReference) dwithin.getParam1() ).getAsText(), is( "app:ft1/geometry" ) ); + assertThat( dwithin.getGeometry(), is( CoreMatchers.nullValue() ) ); + assertThat( dwithin.getValueReference().getAsText(), is( "app:ft2/geometry" ) ); + assertThat( dwithin.getDistance().getValueAsDouble(), is( 10d ) ); + } + + @Test + public void parseEqualsWithSpatialJoin() + throws XMLStreamException, FactoryConfigurationError, IOException { + Equals equals = (Equals) parseFilter( "v200/equalsWithSpatialJoin.xml" ); + + assertThat( ( (ValueReference) equals.getParam1() ).getAsText(), is( "app:ft1/geometry" ) ); + assertThat( equals.getGeometry(), is( CoreMatchers.nullValue() ) ); + assertThat( equals.getValueReference().getAsText(), is( "app:ft2/geometry" ) ); + } + @Test public void parseIntersectsWithSpatialJoin() throws XMLStreamException, FactoryConfigurationError, IOException { - InputStream filterAsStream = this.getClass().getResourceAsStream( "v200/intersectsWithSpatialJoin.xml" ); - XMLStreamReader xmlStream = XMLInputFactory.newInstance().createXMLStreamReader( filterAsStream ); - XMLStreamUtils.skipStartDocument( xmlStream ); - Intersects intersects = (Intersects) ( (OperatorFilter) Filter200XMLDecoder.parse( xmlStream ) ).getOperator(); + Intersects intersects = (Intersects) parseFilter( "v200/intersectsWithSpatialJoin.xml" ); assertThat( ( (ValueReference) intersects.getParam1() ).getAsText(), is( "app:ft1/geometry" ) ); assertThat( intersects.getGeometry(), is( CoreMatchers.nullValue() ) ); assertThat( intersects.getValueReference().getAsText(), is( "app:ft2/geometry" ) ); } + @Test + public void parseOverlapsWithSpatialJoin() + throws XMLStreamException, FactoryConfigurationError, IOException { + Overlaps overlaps = (Overlaps) parseFilter( "v200/overlapsWithSpatialJoin.xml" ); + + assertThat( ( (ValueReference) overlaps.getParam1() ).getAsText(), is( "app:ft1/geometry" ) ); + assertThat( overlaps.getGeometry(), is( CoreMatchers.nullValue() ) ); + assertThat( overlaps.getValueReference().getAsText(), is( "app:ft2/geometry" ) ); + } + + @Test + public void parseTouchesWithSpatialJoin() + throws XMLStreamException, FactoryConfigurationError, IOException { + Touches touches = (Touches) parseFilter( "v200/touchesWithSpatialJoin.xml" ); + + assertThat( ( (ValueReference) touches.getParam1() ).getAsText(), is( "app:ft1/geometry" ) ); + assertThat( touches.getGeometry(), is( CoreMatchers.nullValue() ) ); + assertThat( touches.getValueReference().getAsText(), is( "app:ft2/geometry" ) ); + } + + @Test + public void parseWithinWithSpatialJoin() + throws XMLStreamException, FactoryConfigurationError, IOException { + Within within = (Within) parseFilter( "v200/withinWithSpatialJoin.xml" ); + + assertThat( ( (ValueReference) within.getParam1() ).getAsText(), is( "app:ft1/geometry" ) ); + assertThat( within.getGeometry(), is( CoreMatchers.nullValue() ) ); + assertThat( within.getValueReference().getAsText(), is( "app:ft2/geometry" ) ); + } + @Test(expected = XMLParsingException.class) public void parsePropertyIsLessThanOrEuqlToWithLiteralContainingUnexpectedGeometry() throws Exception { @@ -419,4 +528,14 @@ private URL buildUrl( String name, String version ) { } return null; } + + private Operator parseFilter( String resource ) + throws XMLStreamException, FactoryConfigurationError { + InputStream filterAsStream = this.getClass().getResourceAsStream( resource ); + XMLStreamReader xmlStream = XMLInputFactory.newInstance().createXMLStreamReader( filterAsStream ); + XMLStreamUtils.skipStartDocument( xmlStream ); + OperatorFilter parsedFilter = (OperatorFilter) Filter200XMLDecoder.parse( xmlStream ); + return parsedFilter.getOperator(); + } + } diff --git a/deegree-core/deegree-core-base/src/test/java/org/deegree/filter/xml/Filter200XMLEncoderParameterizedTest.java b/deegree-core/deegree-core-base/src/test/java/org/deegree/filter/xml/Filter200XMLEncoderParameterizedTest.java index 9658f3a168..998126b9f0 100644 --- a/deegree-core/deegree-core-base/src/test/java/org/deegree/filter/xml/Filter200XMLEncoderParameterizedTest.java +++ b/deegree-core/deegree-core-base/src/test/java/org/deegree/filter/xml/Filter200XMLEncoderParameterizedTest.java @@ -93,7 +93,17 @@ public static List data() asString( "v200/aixm_custom_geometry_property.xml" ) } ); filterTests.add( new Object[] { "aixm_timeinstant_begin.xml", asString( "v200/aixm_timeinstant_begin.xml" ) } ); filterTests.add( new Object[] { "temporal/tequals.xml", asString( "v200/temporal/tequals.xml" ) } ); + filterTests.add( new Object[] { "bboxWithSpatialJoin.xml", asString( "v200/bboxWithSpatialJoin.xml" ) } ); +// filterTests.add( new Object[] { "beyondWithSpatialJoin.xml", asString( "v200/beyondWithSpatialJoin.xml" ) } ); + filterTests.add( new Object[] { "containsWithSpatialJoin.xml", asString( "v200/containsWithSpatialJoin.xml" ) } ); + filterTests.add( new Object[] { "crossesWithSpatialJoin.xml", asString( "v200/crossesWithSpatialJoin.xml" ) } ); + filterTests.add( new Object[] { "disjointWithSpatialJoin.xml", asString( "v200/disjointWithSpatialJoin.xml" ) } ); +// filterTests.add( new Object[] { "dwithinWithSpatialJoin.xml", asString( "v200/dwithinWithSpatialJoin.xml" ) } ); + filterTests.add( new Object[] { "equalsWithSpatialJoin.xml", asString( "v200/equalsWithSpatialJoin.xml" ) } ); filterTests.add( new Object[] { "intersectsWithSpatialJoin.xml", asString( "v200/intersectsWithSpatialJoin.xml" ) } ); + filterTests.add( new Object[] { "overlapsWithSpatialJoin.xml", asString( "v200/overlapsWithSpatialJoin.xml" ) } ); + filterTests.add( new Object[] { "touchesWithSpatialJoin.xml", asString( "v200/touchesWithSpatialJoin.xml" ) } ); + filterTests.add( new Object[] { "withinWithSpatialJoin.xml", asString( "v200/withinWithSpatialJoin.xml" ) } ); return filterTests; } diff --git a/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/bboxWithSpatialJoin.xml b/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/bboxWithSpatialJoin.xml new file mode 100644 index 0000000000..c442210138 --- /dev/null +++ b/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/bboxWithSpatialJoin.xml @@ -0,0 +1,9 @@ + + + + app:ft1/geometry + app:ft2/geometry + + \ No newline at end of file diff --git a/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/beyondWithSpatialJoin.xml b/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/beyondWithSpatialJoin.xml new file mode 100644 index 0000000000..3e1f46c1f9 --- /dev/null +++ b/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/beyondWithSpatialJoin.xml @@ -0,0 +1,10 @@ + + + + app:ft1/geometry + app:ft2/geometry + 10 + + \ No newline at end of file diff --git a/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/containsWithSpatialJoin.xml b/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/containsWithSpatialJoin.xml new file mode 100644 index 0000000000..15b89bdf85 --- /dev/null +++ b/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/containsWithSpatialJoin.xml @@ -0,0 +1,9 @@ + + + + app:ft1/geometry + app:ft2/geometry + + \ No newline at end of file diff --git a/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/crossesWithSpatialJoin.xml b/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/crossesWithSpatialJoin.xml new file mode 100644 index 0000000000..026addc177 --- /dev/null +++ b/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/crossesWithSpatialJoin.xml @@ -0,0 +1,9 @@ + + + + app:ft1/geometry + app:ft2/geometry + + \ No newline at end of file diff --git a/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/disjointWithSpatialJoin.xml b/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/disjointWithSpatialJoin.xml new file mode 100644 index 0000000000..ef2d0a1781 --- /dev/null +++ b/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/disjointWithSpatialJoin.xml @@ -0,0 +1,9 @@ + + + + app:ft1/geometry + app:ft2/geometry + + \ No newline at end of file diff --git a/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/dwithinWithSpatialJoin.xml b/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/dwithinWithSpatialJoin.xml new file mode 100644 index 0000000000..c25bf3aa04 --- /dev/null +++ b/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/dwithinWithSpatialJoin.xml @@ -0,0 +1,10 @@ + + + + app:ft1/geometry + app:ft2/geometry + 10 + + \ No newline at end of file diff --git a/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/equalsWithSpatialJoin.xml b/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/equalsWithSpatialJoin.xml new file mode 100644 index 0000000000..89ec096387 --- /dev/null +++ b/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/equalsWithSpatialJoin.xml @@ -0,0 +1,9 @@ + + + + app:ft1/geometry + app:ft2/geometry + + \ No newline at end of file diff --git a/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/overlapsWithSpatialJoin.xml b/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/overlapsWithSpatialJoin.xml new file mode 100644 index 0000000000..eeb3ea3abf --- /dev/null +++ b/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/overlapsWithSpatialJoin.xml @@ -0,0 +1,9 @@ + + + + app:ft1/geometry + app:ft2/geometry + + \ No newline at end of file diff --git a/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/touchesWithSpatialJoin.xml b/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/touchesWithSpatialJoin.xml new file mode 100644 index 0000000000..a1324b74e6 --- /dev/null +++ b/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/touchesWithSpatialJoin.xml @@ -0,0 +1,9 @@ + + + + app:ft1/geometry + app:ft2/geometry + + \ No newline at end of file diff --git a/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/withinWithSpatialJoin.xml b/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/withinWithSpatialJoin.xml new file mode 100644 index 0000000000..132e96fb14 --- /dev/null +++ b/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v200/withinWithSpatialJoin.xml @@ -0,0 +1,9 @@ + + + + app:ft1/geometry + app:ft2/geometry + + \ No newline at end of file From 4232c959559c3a2ff1a84a8ec8734defff26f067 Mon Sep 17 00:00:00 2001 From: Lyn Elisa Goltz Date: Thu, 23 Jun 2016 09:34:05 +0200 Subject: [PATCH 19/25] #3609 - fixed bug in decoding of distance --- .../main/java/org/deegree/filter/xml/Filter200XMLEncoder.java | 2 +- .../filter/xml/Filter200XMLEncoderParameterizedTest.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/xml/Filter200XMLEncoder.java b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/xml/Filter200XMLEncoder.java index 0cf3879712..6437a0ce07 100644 --- a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/xml/Filter200XMLEncoder.java +++ b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/xml/Filter200XMLEncoder.java @@ -521,7 +521,7 @@ private static void exportDistance( Measure distance, XMLStreamWriter writer ) if ( distance != null ) { // in case of Beyond- and DWithin-operators export their distance variable QName distanceElementName = new QName( CommonNamespaces.FES_20_NS, "Distance" ); writer.writeStartElement( distanceElementName.getNamespaceURI(), distanceElementName.getLocalPart() ); - writer.writeAttribute( "units", distance.getUomUri() ); + writer.writeAttribute( "uom", distance.getUomUri() ); writer.writeCharacters( distance.getValue().toString() ); writer.writeEndElement(); } diff --git a/deegree-core/deegree-core-base/src/test/java/org/deegree/filter/xml/Filter200XMLEncoderParameterizedTest.java b/deegree-core/deegree-core-base/src/test/java/org/deegree/filter/xml/Filter200XMLEncoderParameterizedTest.java index 998126b9f0..84d72e1da5 100644 --- a/deegree-core/deegree-core-base/src/test/java/org/deegree/filter/xml/Filter200XMLEncoderParameterizedTest.java +++ b/deegree-core/deegree-core-base/src/test/java/org/deegree/filter/xml/Filter200XMLEncoderParameterizedTest.java @@ -94,11 +94,11 @@ public static List data() filterTests.add( new Object[] { "aixm_timeinstant_begin.xml", asString( "v200/aixm_timeinstant_begin.xml" ) } ); filterTests.add( new Object[] { "temporal/tequals.xml", asString( "v200/temporal/tequals.xml" ) } ); filterTests.add( new Object[] { "bboxWithSpatialJoin.xml", asString( "v200/bboxWithSpatialJoin.xml" ) } ); -// filterTests.add( new Object[] { "beyondWithSpatialJoin.xml", asString( "v200/beyondWithSpatialJoin.xml" ) } ); + filterTests.add( new Object[] { "beyondWithSpatialJoin.xml", asString( "v200/beyondWithSpatialJoin.xml" ) } ); filterTests.add( new Object[] { "containsWithSpatialJoin.xml", asString( "v200/containsWithSpatialJoin.xml" ) } ); filterTests.add( new Object[] { "crossesWithSpatialJoin.xml", asString( "v200/crossesWithSpatialJoin.xml" ) } ); filterTests.add( new Object[] { "disjointWithSpatialJoin.xml", asString( "v200/disjointWithSpatialJoin.xml" ) } ); -// filterTests.add( new Object[] { "dwithinWithSpatialJoin.xml", asString( "v200/dwithinWithSpatialJoin.xml" ) } ); + filterTests.add( new Object[] { "dwithinWithSpatialJoin.xml", asString( "v200/dwithinWithSpatialJoin.xml" ) } ); filterTests.add( new Object[] { "equalsWithSpatialJoin.xml", asString( "v200/equalsWithSpatialJoin.xml" ) } ); filterTests.add( new Object[] { "intersectsWithSpatialJoin.xml", asString( "v200/intersectsWithSpatialJoin.xml" ) } ); filterTests.add( new Object[] { "overlapsWithSpatialJoin.xml", asString( "v200/overlapsWithSpatialJoin.xml" ) } ); From 376eb840b4e097f3ec89f19dc1fad0533c202d3d Mon Sep 17 00:00:00 2001 From: Lyn Elisa Goltz Date: Thu, 23 Jun 2016 10:20:25 +0200 Subject: [PATCH 20/25] #3609 - implemented implemented encoding and decoding of filter with spatial joins for 1.1.0 --- .../filter/xml/Filter110XMLDecoder.java | 99 ++++++++++++++----- .../filter/xml/Filter110XMLEncoder.java | 48 +-------- .../filter/xml/Filter110XMLDecoderTest.java | 96 ++++++++++++++++++ .../filter/xml/Filter110XMLEncoderTest.java | 64 ++++++++++++ .../filter/xml/v110/bboxWithSpatialJoin.xml | 8 ++ .../xml/v110/containsWithSpatialJoin.xml | 8 ++ .../xml/v110/crossesWithSpatialJoin.xml | 8 ++ .../xml/v110/disjointWithSpatialJoin.xml | 8 ++ .../filter/xml/v110/equalsWithSpatialJoin.xml | 8 ++ .../xml/v110/overlapsWithSpatialJoin.xml | 8 ++ .../xml/v110/touchesWithSpatialJoin.xml | 8 ++ .../filter/xml/v110/withinWithSpatialJoin.xml | 8 ++ 12 files changed, 301 insertions(+), 70 deletions(-) create mode 100644 deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v110/bboxWithSpatialJoin.xml create mode 100644 deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v110/containsWithSpatialJoin.xml create mode 100644 deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v110/crossesWithSpatialJoin.xml create mode 100644 deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v110/disjointWithSpatialJoin.xml create mode 100644 deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v110/equalsWithSpatialJoin.xml create mode 100644 deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v110/overlapsWithSpatialJoin.xml create mode 100644 deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v110/touchesWithSpatialJoin.xml create mode 100644 deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v110/withinWithSpatialJoin.xml diff --git a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/xml/Filter110XMLDecoder.java b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/xml/Filter110XMLDecoder.java index 1db80338ec..42f11f5dc8 100644 --- a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/xml/Filter110XMLDecoder.java +++ b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/xml/Filter110XMLDecoder.java @@ -857,10 +857,16 @@ private static SpatialOperator parseSpatialOperator( XMLStreamReader xmlStream ) param1 = parsePropertyName( xmlStream, true ); nextElement( xmlStream ); } - // second parameter: 'gml:Envelope' - xmlStream.require( START_ELEMENT, GML_NS, "Envelope" ); - Envelope param2 = geomParser.parseEnvelope( wrapper ); - spatialOperator = new BBOX( param1, param2 ); + if ( isCurrentStartElementIsGmlGeometry( xmlStream ) ) { + // second parameter: 'gml:Envelope' + xmlStream.require( START_ELEMENT, GML_NS, "Envelope" ); + Envelope param2 = geomParser.parseEnvelope( wrapper ); + spatialOperator = new BBOX( param1, param2 ); + } else { + // second parameter: 'ogc:PropertyName' + ValueReference param2 = parsePropertyName( xmlStream, false ); + spatialOperator = new BBOX( param1, param2 ); + } break; } case BEYOND: { @@ -888,6 +894,7 @@ private static SpatialOperator parseSpatialOperator( XMLStreamReader xmlStream ) Geometry param2 = geomParser.parseGeometryOrEnvelope( wrapper ); spatialOperator = new Intersects( param1, param2 ); } else { + // second parameter: 'ogc:PropertyName' ValueReference param2 = parsePropertyName( xmlStream, false ); spatialOperator = new Intersects( param1, param2 ); } @@ -897,27 +904,45 @@ private static SpatialOperator parseSpatialOperator( XMLStreamReader xmlStream ) // first parameter: 'ogc:PropertyName' (cannot be empty) ValueReference param1 = parsePropertyName( xmlStream, false ); nextElement( xmlStream ); - // second parameter: 'gml:_Geometry' or 'gml:Envelope' - Geometry param2 = geomParser.parseGeometryOrEnvelope( wrapper ); - spatialOperator = new Contains( param1, param2 ); + if ( isCurrentStartElementIsGmlGeometry( xmlStream ) ) { + // second parameter: 'gml:_Geometry' or 'gml:Envelope' + Geometry param2 = geomParser.parseGeometryOrEnvelope( wrapper ); + spatialOperator = new Contains( param1, param2 ); + } else { + // second parameter: 'ogc:PropertyName' + ValueReference param2 = parsePropertyName( xmlStream, false ); + spatialOperator = new Contains( param1, param2 ); + } break; } case CROSSES: { // first parameter: 'ogc:PropertyName' (cannot be empty) ValueReference param1 = parsePropertyName( xmlStream, false ); nextElement( xmlStream ); - // second parameter: 'gml:_Geometry' or 'gml:Envelope' - Geometry param2 = geomParser.parseGeometryOrEnvelope( wrapper ); - spatialOperator = new Crosses( param1, param2 ); + if ( isCurrentStartElementIsGmlGeometry( xmlStream ) ) { + // second parameter: 'gml:_Geometry' or 'gml:Envelope' + Geometry param2 = geomParser.parseGeometryOrEnvelope( wrapper ); + spatialOperator = new Crosses( param1, param2 ); + } else { + // second parameter: 'ogc:PropertyName' + ValueReference param2 = parsePropertyName( xmlStream, false ); + spatialOperator = new Crosses( param1, param2 ); + } break; } case DISJOINT: { // first parameter: 'ogc:PropertyName' (cannot be empty) ValueReference param1 = parsePropertyName( xmlStream, false ); nextElement( xmlStream ); - // second parameter: 'gml:_Geometry' or 'gml:Envelope' - Geometry param2 = geomParser.parseGeometryOrEnvelope( wrapper ); - spatialOperator = new Disjoint( param1, param2 ); + if ( isCurrentStartElementIsGmlGeometry( xmlStream ) ) { + // second parameter: 'gml:_Geometry' or 'gml:Envelope' + Geometry param2 = geomParser.parseGeometryOrEnvelope( wrapper ); + spatialOperator = new Disjoint( param1, param2 ); + } else { + // second parameter: 'ogc:PropertyName' + ValueReference param2 = parsePropertyName( xmlStream, false ); + spatialOperator = new Disjoint( param1, param2 ); + } break; } case DWITHIN: { @@ -940,36 +965,60 @@ private static SpatialOperator parseSpatialOperator( XMLStreamReader xmlStream ) // first parameter: 'ogc:PropertyName' (cannot be empty) ValueReference param1 = parsePropertyName( xmlStream, false ); nextElement( xmlStream ); - // second parameter: 'gml:_Geometry' or 'gml:Envelope' - Geometry param2 = geomParser.parseGeometryOrEnvelope( wrapper ); - spatialOperator = new Equals( param1, param2 ); + if ( isCurrentStartElementIsGmlGeometry( xmlStream ) ) { + // second parameter: 'gml:_Geometry' or 'gml:Envelope' + Geometry param2 = geomParser.parseGeometryOrEnvelope( wrapper ); + spatialOperator = new Equals( param1, param2 ); + } else { + // second parameter: 'ogc:PropertyName' + ValueReference param2 = parsePropertyName( xmlStream, false ); + spatialOperator = new Equals( param1, param2 ); + } break; } case OVERLAPS: { // first parameter: 'ogc:PropertyName' (cannot be empty) ValueReference param1 = parsePropertyName( xmlStream, false ); nextElement( xmlStream ); - // second parameter: 'gml:_Geometry' or 'gml:Envelope' - Geometry param2 = geomParser.parseGeometryOrEnvelope( wrapper ); - spatialOperator = new Overlaps( param1, param2 ); + if ( isCurrentStartElementIsGmlGeometry( xmlStream ) ) { + // second parameter: 'gml:_Geometry' or 'gml:Envelope' + Geometry param2 = geomParser.parseGeometryOrEnvelope( wrapper ); + spatialOperator = new Overlaps( param1, param2 ); + } else { + // second parameter: 'ogc:PropertyName' + ValueReference param2 = parsePropertyName( xmlStream, false ); + spatialOperator = new Overlaps( param1, param2 ); + } break; } case TOUCHES: { // first parameter: 'ogc:PropertyName' (cannot be empty) ValueReference param1 = parsePropertyName( xmlStream, false ); nextElement( xmlStream ); - // second parameter: 'gml:_Geometry' or 'gml:Envelope' - Geometry param2 = geomParser.parseGeometryOrEnvelope( wrapper ); - spatialOperator = new Touches( param1, param2 ); + if ( isCurrentStartElementIsGmlGeometry( xmlStream ) ) { + // second parameter: 'gml:_Geometry' or 'gml:Envelope' + Geometry param2 = geomParser.parseGeometryOrEnvelope( wrapper ); + spatialOperator = new Touches( param1, param2 ); + } else { + // second parameter: 'ogc:PropertyName' + ValueReference param2 = parsePropertyName( xmlStream, false ); + spatialOperator = new Touches( param1, param2 ); + } break; } case WITHIN: { // first parameter: 'ogc:PropertyName' (cannot be empty) ValueReference param1 = parsePropertyName( xmlStream, false ); nextElement( xmlStream ); - // second parameter: 'gml:_Geometry' or 'gml:Envelope' - Geometry param2 = geomParser.parseGeometryOrEnvelope( wrapper ); - spatialOperator = new Within( param1, param2 ); + if ( isCurrentStartElementIsGmlGeometry( xmlStream ) ) { + // second parameter: 'gml:_Geometry' or 'gml:Envelope' + Geometry param2 = geomParser.parseGeometryOrEnvelope( wrapper ); + spatialOperator = new Within( param1, param2 ); + } else { + // second parameter: 'ogc:PropertyName' + ValueReference param2 = parsePropertyName( xmlStream, false ); + spatialOperator = new Within( param1, param2 ); + } } } } catch ( UnknownCRSException e ) { diff --git a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/xml/Filter110XMLEncoder.java b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/xml/Filter110XMLEncoder.java index 5a50ac9abf..606cd3dbfc 100644 --- a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/xml/Filter110XMLEncoder.java +++ b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/xml/Filter110XMLEncoder.java @@ -384,60 +384,18 @@ private static void export( SpatialOperator operator, XMLStreamWriter writer ) // gmlWriter.setLocalXLinkTemplate( "#{}" ); // gmlWriter.setXLinkDepth( 0 ); - ValueReference propertyName = null; - Geometry geometry = null; - ValueReference secondParam = null; + ValueReference propertyName = (ValueReference) operator.getParam1(); + Geometry geometry = operator.getGeometry(); + ValueReference secondParam = operator.getValueReference(); Measure distance = null; switch ( operator.getSubType() ) { - - case BBOX: - propertyName = ( (BBOX) operator ).getPropName(); - geometry = ( (BBOX) operator ).getBoundingBox(); - break; case BEYOND: - propertyName = ( (Beyond) operator ).getPropName(); - geometry = ( (Beyond) operator ).getGeometry(); distance = ( (Beyond) operator ).getDistance(); break; - case CONTAINS: - propertyName = ( (Contains) operator ).getPropName(); - geometry = ( (Contains) operator ).getGeometry(); - break; - case CROSSES: - propertyName = ( (Crosses) operator ).getPropName(); - geometry = ( (Crosses) operator ).getGeometry(); - break; - case DISJOINT: - propertyName = ( (Disjoint) operator ).getPropName(); - geometry = ( (Disjoint) operator ).getGeometry(); - break; case DWITHIN: - propertyName = ( (DWithin) operator ).getPropName(); - geometry = ( (DWithin) operator ).getGeometry(); distance = ( (DWithin) operator ).getDistance(); break; - case EQUALS: - propertyName = ( (Equals) operator ).getPropName(); - geometry = ( (Equals) operator ).getGeometry(); - break; - case INTERSECTS: - propertyName = ( (Intersects) operator ).getPropName(); - geometry = ( (Intersects) operator ).getGeometry(); - secondParam = ( (Intersects) operator ).getValueReference(); - break; - case OVERLAPS: - propertyName = ( (Overlaps) operator ).getPropName(); - geometry = ( (Overlaps) operator ).getGeometry(); - break; - case TOUCHES: - propertyName = ( (Touches) operator ).getPropName(); - geometry = ( (Touches) operator ).getGeometry(); - break; - case WITHIN: - propertyName = ( (Within) operator ).getPropName(); - geometry = ( (Within) operator ).getGeometry(); - break; } // exporting the comparable geometry property diff --git a/deegree-core/deegree-core-base/src/test/java/org/deegree/filter/xml/Filter110XMLDecoderTest.java b/deegree-core/deegree-core-base/src/test/java/org/deegree/filter/xml/Filter110XMLDecoderTest.java index 9f19b8f36b..e5180d0897 100644 --- a/deegree-core/deegree-core-base/src/test/java/org/deegree/filter/xml/Filter110XMLDecoderTest.java +++ b/deegree-core/deegree-core-base/src/test/java/org/deegree/filter/xml/Filter110XMLDecoderTest.java @@ -61,7 +61,15 @@ import org.deegree.filter.expression.ValueReference; import org.deegree.filter.logical.And; import org.deegree.filter.logical.LogicalOperator; +import org.deegree.filter.spatial.BBOX; +import org.deegree.filter.spatial.Contains; +import org.deegree.filter.spatial.Crosses; +import org.deegree.filter.spatial.Disjoint; +import org.deegree.filter.spatial.Equals; import org.deegree.filter.spatial.Intersects; +import org.deegree.filter.spatial.Overlaps; +import org.deegree.filter.spatial.Touches; +import org.deegree.filter.spatial.Within; import org.deegree.junit.XMLAssert; import org.deegree.junit.XMLMemoryStreamWriter; import org.deegree.workspace.standard.DefaultWorkspace; @@ -249,6 +257,61 @@ public void parseWithinFilter() } + @Test + public void parseBBoxWithSpatialJoin() + throws XMLStreamException, FactoryConfigurationError, IOException { + Filter filter = parse( "bboxWithSpatialJoin.xml" ); + BBOX bbox = (BBOX) ( (OperatorFilter) filter ).getOperator(); + + assertThat( ( (ValueReference) bbox.getParam1() ).getAsText(), is( "app:ft1/app:geom" ) ); + assertThat( bbox.getGeometry(), is( CoreMatchers.nullValue() ) ); + assertThat( bbox.getValueReference().getAsText(), is( "app:ft2/app:geom" ) ); + } + + @Test + public void parseContainsWithSpatialJoin() + throws XMLStreamException, FactoryConfigurationError, IOException { + Filter filter = parse( "containsWithSpatialJoin.xml" ); + Contains contains = (Contains) ( (OperatorFilter) filter ).getOperator(); + + assertThat( ( (ValueReference) contains.getParam1() ).getAsText(), is( "app:ft1/app:geom" ) ); + assertThat( contains.getGeometry(), is( CoreMatchers.nullValue() ) ); + assertThat( contains.getValueReference().getAsText(), is( "app:ft2/app:geom" ) ); + } + + @Test + public void parseCrossesWithSpatialJoin() + throws XMLStreamException, FactoryConfigurationError, IOException { + Filter filter = parse( "crossesWithSpatialJoin.xml" ); + Crosses crosses = (Crosses) ( (OperatorFilter) filter ).getOperator(); + + assertThat( ( (ValueReference) crosses.getParam1() ).getAsText(), is( "app:ft1/app:geom" ) ); + assertThat( crosses.getGeometry(), is( CoreMatchers.nullValue() ) ); + assertThat( crosses.getValueReference().getAsText(), is( "app:ft2/app:geom" ) ); + } + + @Test + public void parseDisjointWithSpatialJoin() + throws XMLStreamException, FactoryConfigurationError, IOException { + Filter filter = parse( "disjointWithSpatialJoin.xml" ); + Disjoint disjoint = (Disjoint) ( (OperatorFilter) filter ).getOperator(); + + assertThat( ( (ValueReference) disjoint.getParam1() ).getAsText(), is( "app:ft1/app:geom" ) ); + assertThat( disjoint.getGeometry(), is( CoreMatchers.nullValue() ) ); + assertThat( disjoint.getValueReference().getAsText(), is( "app:ft2/app:geom" ) ); + } + + @Test + public void parseEqualsWithSpatialJoin() + throws XMLStreamException, FactoryConfigurationError, IOException { + Filter filter = parse( "equalsWithSpatialJoin.xml" ); + Equals equals = (Equals) ( (OperatorFilter) filter ).getOperator(); + + assertThat( ( (ValueReference) equals.getParam1() ).getAsText(), is( "app:ft1/app:geom" ) ); + assertThat( equals.getGeometry(), is( CoreMatchers.nullValue() ) ); + assertThat( equals.getValueReference().getAsText(), is( "app:ft2/app:geom" ) ); + } + @Test public void parseIntersectsWithSpatialJoin() throws XMLStreamException, FactoryConfigurationError, IOException { @@ -260,4 +323,37 @@ public void parseIntersectsWithSpatialJoin() assertThat( intersects.getValueReference().getAsText(), is( "app:ft2/app:geom" ) ); } + @Test + public void parseOverlapsWithSpatialJoin() + throws XMLStreamException, FactoryConfigurationError, IOException { + Filter filter = parse( "overlapsWithSpatialJoin.xml" ); + Overlaps overlaps = (Overlaps) ( (OperatorFilter) filter ).getOperator(); + + assertThat( ( (ValueReference) overlaps.getParam1() ).getAsText(), is( "app:ft1/app:geom" ) ); + assertThat( overlaps.getGeometry(), is( CoreMatchers.nullValue() ) ); + assertThat( overlaps.getValueReference().getAsText(), is( "app:ft2/app:geom" ) ); + } + + @Test + public void parseTouchesWithSpatialJoin() + throws XMLStreamException, FactoryConfigurationError, IOException { + Filter filter = parse( "touchesWithSpatialJoin.xml" ); + Touches touches = (Touches) ( (OperatorFilter) filter ).getOperator(); + + assertThat( ( (ValueReference) touches.getParam1() ).getAsText(), is( "app:ft1/app:geom" ) ); + assertThat( touches.getGeometry(), is( CoreMatchers.nullValue() ) ); + assertThat( touches.getValueReference().getAsText(), is( "app:ft2/app:geom" ) ); + } + + @Test + public void parseWithinWithSpatialJoin() + throws XMLStreamException, FactoryConfigurationError, IOException { + Filter filter = parse( "withinWithSpatialJoin.xml" ); + Within within = (Within) ( (OperatorFilter) filter ).getOperator(); + + assertThat( ( (ValueReference) within.getParam1() ).getAsText(), is( "app:ft1/app:geom" ) ); + assertThat( within.getGeometry(), is( CoreMatchers.nullValue() ) ); + assertThat( within.getValueReference().getAsText(), is( "app:ft2/app:geom" ) ); + } + } \ No newline at end of file diff --git a/deegree-core/deegree-core-base/src/test/java/org/deegree/filter/xml/Filter110XMLEncoderTest.java b/deegree-core/deegree-core-base/src/test/java/org/deegree/filter/xml/Filter110XMLEncoderTest.java index 9d885bc39f..a5afd8e9f3 100644 --- a/deegree-core/deegree-core-base/src/test/java/org/deegree/filter/xml/Filter110XMLEncoderTest.java +++ b/deegree-core/deegree-core-base/src/test/java/org/deegree/filter/xml/Filter110XMLEncoderTest.java @@ -266,6 +266,46 @@ public void parseWithinFilter() } + @Test + public void parseBBoxWithSpatialJoin() + throws XMLStreamException, FactoryConfigurationError, IOException, UnknownCRSException, + TransformationException { + Filter filter = testImportExportImport( "bboxWithSpatialJoin.xml" ); + Assert.assertNotNull( filter ); + } + + @Test + public void parseContainsWithSpatialJoin() + throws XMLStreamException, FactoryConfigurationError, IOException, UnknownCRSException, + TransformationException { + Filter filter = testImportExportImport( "containsWithSpatialJoin.xml" ); + Assert.assertNotNull( filter ); + } + + @Test + public void parseCrossesWithSpatialJoin() + throws XMLStreamException, FactoryConfigurationError, IOException, UnknownCRSException, + TransformationException { + Filter filter = testImportExportImport( "crossesWithSpatialJoin.xml" ); + Assert.assertNotNull( filter ); + } + + @Test + public void parseDisjointWithSpatialJoin() + throws XMLStreamException, FactoryConfigurationError, IOException, UnknownCRSException, + TransformationException { + Filter filter = testImportExportImport( "disjointWithSpatialJoin.xml" ); + Assert.assertNotNull( filter ); + } + + @Test + public void parseEqualsWithSpatialJoin() + throws XMLStreamException, FactoryConfigurationError, IOException, UnknownCRSException, + TransformationException { + Filter filter = testImportExportImport( "equalsWithSpatialJoin.xml" ); + Assert.assertNotNull( filter ); + } + @Test public void parseIntersectsWithSpatialJoin() throws XMLStreamException, FactoryConfigurationError, IOException, UnknownCRSException, @@ -274,4 +314,28 @@ public void parseIntersectsWithSpatialJoin() Assert.assertNotNull( filter ); } + @Test + public void parseOverlapsWithSpatialJoin() + throws XMLStreamException, FactoryConfigurationError, IOException, UnknownCRSException, + TransformationException { + Filter filter = testImportExportImport( "overlapsWithSpatialJoin.xml" ); + Assert.assertNotNull( filter ); + } + + @Test + public void parseTouchesWithSpatialJoin() + throws XMLStreamException, FactoryConfigurationError, IOException, UnknownCRSException, + TransformationException { + Filter filter = testImportExportImport( "touchesWithSpatialJoin.xml" ); + Assert.assertNotNull( filter ); + } + + @Test + public void parseWithinWithSpatialJoin() + throws XMLStreamException, FactoryConfigurationError, IOException, UnknownCRSException, + TransformationException { + Filter filter = testImportExportImport( "withinWithSpatialJoin.xml" ); + Assert.assertNotNull( filter ); + } + } diff --git a/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v110/bboxWithSpatialJoin.xml b/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v110/bboxWithSpatialJoin.xml new file mode 100644 index 0000000000..04ddcfa9b9 --- /dev/null +++ b/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v110/bboxWithSpatialJoin.xml @@ -0,0 +1,8 @@ + + + + app:ft1/app:geom + app:ft2/app:geom + + diff --git a/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v110/containsWithSpatialJoin.xml b/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v110/containsWithSpatialJoin.xml new file mode 100644 index 0000000000..e0c07c4d11 --- /dev/null +++ b/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v110/containsWithSpatialJoin.xml @@ -0,0 +1,8 @@ + + + + app:ft1/app:geom + app:ft2/app:geom + + diff --git a/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v110/crossesWithSpatialJoin.xml b/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v110/crossesWithSpatialJoin.xml new file mode 100644 index 0000000000..8abef019f6 --- /dev/null +++ b/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v110/crossesWithSpatialJoin.xml @@ -0,0 +1,8 @@ + + + + app:ft1/app:geom + app:ft2/app:geom + + diff --git a/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v110/disjointWithSpatialJoin.xml b/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v110/disjointWithSpatialJoin.xml new file mode 100644 index 0000000000..b3eaf67a9e --- /dev/null +++ b/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v110/disjointWithSpatialJoin.xml @@ -0,0 +1,8 @@ + + + + app:ft1/app:geom + app:ft2/app:geom + + diff --git a/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v110/equalsWithSpatialJoin.xml b/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v110/equalsWithSpatialJoin.xml new file mode 100644 index 0000000000..87f6cad3bd --- /dev/null +++ b/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v110/equalsWithSpatialJoin.xml @@ -0,0 +1,8 @@ + + + + app:ft1/app:geom + app:ft2/app:geom + + diff --git a/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v110/overlapsWithSpatialJoin.xml b/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v110/overlapsWithSpatialJoin.xml new file mode 100644 index 0000000000..fb547753c5 --- /dev/null +++ b/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v110/overlapsWithSpatialJoin.xml @@ -0,0 +1,8 @@ + + + + app:ft1/app:geom + app:ft2/app:geom + + diff --git a/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v110/touchesWithSpatialJoin.xml b/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v110/touchesWithSpatialJoin.xml new file mode 100644 index 0000000000..63f01ca4c5 --- /dev/null +++ b/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v110/touchesWithSpatialJoin.xml @@ -0,0 +1,8 @@ + + + + app:ft1/app:geom + app:ft2/app:geom + + diff --git a/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v110/withinWithSpatialJoin.xml b/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v110/withinWithSpatialJoin.xml new file mode 100644 index 0000000000..9f9eb9be19 --- /dev/null +++ b/deegree-core/deegree-core-base/src/test/resources/org/deegree/filter/xml/v110/withinWithSpatialJoin.xml @@ -0,0 +1,8 @@ + + + + app:ft1/app:geom + app:ft2/app:geom + + \ No newline at end of file From 83fe18dbd4edee1dee5fb8b1ced45213d43f42f7 Mon Sep 17 00:00:00 2001 From: Lyn Elisa Goltz Date: Thu, 23 Jun 2016 10:48:11 +0200 Subject: [PATCH 21/25] #3610 - implemented support of spatial joins in PostGISWhereBuilder --- .../postgis/PostGISWhereBuilder.java | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/deegree-core/deegree-core-sqldialect/deegree-sqldialect-postgis/src/main/java/org/deegree/sqldialect/postgis/PostGISWhereBuilder.java b/deegree-core/deegree-core-sqldialect/deegree-sqldialect-postgis/src/main/java/org/deegree/sqldialect/postgis/PostGISWhereBuilder.java index 8a6f08e540..67082c1e35 100644 --- a/deegree-core/deegree-core-sqldialect/deegree-sqldialect-postgis/src/main/java/org/deegree/sqldialect/postgis/PostGISWhereBuilder.java +++ b/deegree-core/deegree-core-sqldialect/deegree-sqldialect-postgis/src/main/java/org/deegree/sqldialect/postgis/PostGISWhereBuilder.java @@ -216,7 +216,7 @@ protected SQLOperation toProtoSQL( SpatialOperator op ) } builder.add( propNameExpr ); builder.add( " && " ); - builder.add( toProtoSQL( bbox.getBoundingBox(), storageCRS, srid ) ); + builder.add( toProtoSqlSecondParameter( bbox, storageCRS, srid ) ); if ( !bbox.getAllowFalsePositives() ) { builder.add( " AND " ); if ( useLegacyPredicates ) { @@ -226,7 +226,7 @@ protected SQLOperation toProtoSQL( SpatialOperator op ) } builder.add( propNameExpr ); builder.add( "," ); - builder.add( toProtoSQL( bbox.getBoundingBox(), storageCRS, srid ) ); + builder.add( toProtoSqlSecondParameter( bbox, storageCRS, srid ) ); builder.add( "))" ); } break; @@ -240,7 +240,7 @@ protected SQLOperation toProtoSQL( SpatialOperator op ) } builder.add( propNameExpr ); builder.add( "," ); - builder.add( toProtoSQL( beyond.getGeometry(), storageCRS, srid ) ); + builder.add( toProtoSqlSecondParameter( beyond, storageCRS, srid ) ); builder.add( "," ); // TODO uom handling PrimitiveType pt = new PrimitiveType( DECIMAL ); @@ -260,7 +260,7 @@ protected SQLOperation toProtoSQL( SpatialOperator op ) } builder.add( propNameExpr ); builder.add( "," ); - builder.add( toProtoSQL( contains.getGeometry(), storageCRS, srid ) ); + builder.add( toProtoSqlSecondParameter( contains, storageCRS, srid ) ); builder.add( ")" ); break; } @@ -273,7 +273,7 @@ protected SQLOperation toProtoSQL( SpatialOperator op ) } builder.add( propNameExpr ); builder.add( "," ); - builder.add( toProtoSQL( crosses.getGeometry(), storageCRS, srid ) ); + builder.add( toProtoSqlSecondParameter( crosses, storageCRS, srid ) ); builder.add( ")" ); break; } @@ -286,7 +286,7 @@ protected SQLOperation toProtoSQL( SpatialOperator op ) } builder.add( propNameExpr ); builder.add( "," ); - builder.add( toProtoSQL( disjoint.getGeometry(), storageCRS, srid ) ); + builder.add( toProtoSqlSecondParameter( disjoint, storageCRS, srid ) ); builder.add( ")" ); break; } @@ -299,7 +299,7 @@ protected SQLOperation toProtoSQL( SpatialOperator op ) } builder.add( propNameExpr ); builder.add( "," ); - builder.add( toProtoSQL( dWithin.getGeometry(), storageCRS, srid ) ); + builder.add( toProtoSqlSecondParameter( dWithin, storageCRS, srid ) ); builder.add( "," ); // TODO uom handling PrimitiveType pt = new PrimitiveType( DECIMAL ); @@ -319,7 +319,7 @@ protected SQLOperation toProtoSQL( SpatialOperator op ) } builder.add( propNameExpr ); builder.add( "," ); - builder.add( toProtoSQL( equals.getGeometry(), storageCRS, srid ) ); + builder.add( toProtoSqlSecondParameter( equals, storageCRS, srid ) ); builder.add( ")" ); break; } @@ -345,7 +345,7 @@ protected SQLOperation toProtoSQL( SpatialOperator op ) } builder.add( propNameExpr ); builder.add( "," ); - builder.add( toProtoSQL( overlaps.getGeometry(), storageCRS, srid ) ); + builder.add( toProtoSqlSecondParameter( overlaps, storageCRS, srid ) ); builder.add( ")" ); break; } @@ -358,7 +358,7 @@ protected SQLOperation toProtoSQL( SpatialOperator op ) } builder.add( propNameExpr ); builder.add( "," ); - builder.add( toProtoSQL( touches.getGeometry(), storageCRS, srid ) ); + builder.add( toProtoSqlSecondParameter( touches, storageCRS, srid ) ); builder.add( ")" ); break; } @@ -371,7 +371,7 @@ protected SQLOperation toProtoSQL( SpatialOperator op ) } builder.add( propNameExpr ); builder.add( "," ); - builder.add( toProtoSQL( within.getGeometry(), storageCRS, srid ) ); + builder.add( toProtoSqlSecondParameter( within, storageCRS, srid ) ); builder.add( ")" ); break; } @@ -395,14 +395,14 @@ private SQLExpression toProtoSQL( Geometry geom, ICRS targetCRS, int srid ) return new SQLArgument( geom, new PostGISGeometryConverter( null, targetCRS, "" + srid, useLegacyPredicates ) ); } - private SQLExpression toProtoSqlSecondParameter( Intersects intersects, ICRS storageCRS, int srid ) + private SQLExpression toProtoSqlSecondParameter( SpatialOperator spatialOperator, ICRS storageCRS, int srid ) throws FilterEvaluationException, UnmappableException { - if ( intersects.getValueReference() != null ) { - SQLExpression sqlExpression = toProtoSQLSpatial( intersects.getValueReference() ); - checkIfExpressionIsSpatial( sqlExpression, intersects.getValueReference() ); + if ( spatialOperator.getValueReference() != null ) { + SQLExpression sqlExpression = toProtoSQLSpatial( spatialOperator.getValueReference() ); + checkIfExpressionIsSpatial( sqlExpression, spatialOperator.getValueReference() ); return sqlExpression; } - return toProtoSQL( intersects.getGeometry(), storageCRS, srid ); + return toProtoSQL( spatialOperator.getGeometry(), storageCRS, srid ); } private void checkIfExpressionIsSpatial( SQLExpression sqlExpression, ValueReference propName ) From 68a4d2bdd260b6bb3e2e63633afaea5720b5e313 Mon Sep 17 00:00:00 2001 From: Lyn Elisa Goltz Date: Thu, 23 Jun 2016 11:38:57 +0200 Subject: [PATCH 22/25] #3609 - fixed expected geometry property --- .../main/java/org/deegree/filter/Filters.java | 56 ++++++++++------ .../exporthandling/AdhocQueryAnalyzer.java | 67 +++++++++++++------ 2 files changed, 80 insertions(+), 43 deletions(-) diff --git a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/Filters.java b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/Filters.java index 297615fe31..436247d558 100644 --- a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/Filters.java +++ b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/Filters.java @@ -123,7 +123,8 @@ public class Filters { *

* The returned {@link Envelope} is determined by the following strategy: *

    - *
  • If the filter is an {@link OperatorFilter}, it is attempted to extract an {@link BBOX} constraint from it.
  • + *
  • If the filter is an {@link OperatorFilter}, it is attempted to extract an {@link BBOX} constraint from it. + *
  • *
  • If no {@link BBOX} constraint can be extracted from the filter (not presented or nested in Or or * Not expressions, null is returned.
  • *
@@ -190,27 +191,37 @@ private static BBOX extractBBox( SpatialOperator oper ) { SubType type = oper.getSubType(); switch ( type ) { case BBOX: - return (BBOX) oper; + if ( ( (BBOX) oper ).getGeometry() != null ) + return (BBOX) oper; + return null; case CONTAINS: // Oracle does not like zero-extent bboxes - if ( !( ( (Contains) oper ).getGeometry() instanceof Point ) ) + if ( ( (Contains) oper ).getGeometry() != null && !( ( (Contains) oper ).getGeometry() instanceof Point ) ) return new BBOX( ( (Contains) oper ).getParam1(), ( (Contains) oper ).getGeometry().getEnvelope() ); return null; case CROSSES: - return new BBOX( ( (Crosses) oper ).getParam1(), ( (Crosses) oper ).getGeometry().getEnvelope() ); + if ( ( (Crosses) oper ).getGeometry() != null ) + return new BBOX( ( (Crosses) oper ).getParam1(), ( (Crosses) oper ).getGeometry().getEnvelope() ); + return null; case DWITHIN: // TOOD use enlarged bbox return null; case EQUALS: - return new BBOX( ( (Equals) oper ).getParam1(), ( (Equals) oper ).getGeometry().getEnvelope() ); + if ( ( (Equals) oper ).getGeometry() != null ) + return new BBOX( ( (Equals) oper ).getParam1(), ( (Equals) oper ).getGeometry().getEnvelope() ); + return null; case INTERSECTS: if ( ( (Intersects) oper ).getGeometry() != null ) return new BBOX( ( (Intersects) oper ).getParam1(), ( (Intersects) oper ).getGeometry().getEnvelope() ); return null; case OVERLAPS: - return new BBOX( ( (Overlaps) oper ).getParam1(), ( (Overlaps) oper ).getGeometry().getEnvelope() ); + if ( ( (Overlaps) oper ).getGeometry() != null ) + return new BBOX( ( (Overlaps) oper ).getParam1(), ( (Overlaps) oper ).getGeometry().getEnvelope() ); + return null; case WITHIN: - return new BBOX( ( (Within) oper ).getParam1(), ( (Within) oper ).getGeometry().getEnvelope() ); + if ( ( (Within) oper ).getGeometry() != null ) + return new BBOX( ( (Within) oper ).getParam1(), ( (Within) oper ).getGeometry().getEnvelope() ); + return null; default: { return null; } @@ -452,33 +463,33 @@ public static Expression repair( Expression e, Map bindings, Set< switch ( e.getType() ) { case ADD: Add a = (Add) e; - return new Add( repair( a.getParameter1(), bindings, validNames ), repair( a.getParameter2(), bindings, - validNames ) ); + return new Add( repair( a.getParameter1(), bindings, validNames ), + repair( a.getParameter2(), bindings, validNames ) ); case CUSTOM: return e; case DIV: Div d = (Div) e; - return new Div( repair( d.getParameter1(), bindings, validNames ), repair( d.getParameter2(), bindings, - validNames ) ); + return new Div( repair( d.getParameter1(), bindings, validNames ), + repair( d.getParameter2(), bindings, validNames ) ); case FUNCTION: // workaround seems to produce errors, so function expressions are not fixed now return e; - // Function f = (Function) e; - // List ps = new ArrayList(); - // for ( Expression ex : f.getParameters() ) { - // ps.add( repair( ex, bindings, validNames ) ); - // } - // return new Function( f.getName(), ps ); + // Function f = (Function) e; + // List ps = new ArrayList(); + // for ( Expression ex : f.getParameters() ) { + // ps.add( repair( ex, bindings, validNames ) ); + // } + // return new Function( f.getName(), ps ); case LITERAL: return e; case MUL: Mul m = (Mul) e; - return new Mul( repair( m.getParameter1(), bindings, validNames ), repair( m.getParameter2(), bindings, - validNames ) ); + return new Mul( repair( m.getParameter1(), bindings, validNames ), + repair( m.getParameter2(), bindings, validNames ) ); case SUB: Sub s = (Sub) e; - return new Sub( repair( s.getParameter1(), bindings, validNames ), repair( s.getParameter2(), bindings, - validNames ) ); + return new Sub( repair( s.getParameter1(), bindings, validNames ), + repair( s.getParameter2(), bindings, validNames ) ); case VALUE_REFERENCE: ValueReference vr = (ValueReference) e; QName name = vr.getAsQName(); @@ -496,7 +507,8 @@ public static Expression repair( Expression e, Map bindings, Set< return e; } - private static ComparisonOperator repair( ComparisonOperator o, Map bindings, Set validNames ) { + private static ComparisonOperator repair( ComparisonOperator o, Map bindings, + Set validNames ) { Expression[] exs = o.getParams(); for ( int i = 0; i < exs.length; ++i ) { exs[i] = repair( exs[i], bindings, validNames ); diff --git a/deegree-services/deegree-services-csw/src/main/java/org/deegree/services/csw/exporthandling/AdhocQueryAnalyzer.java b/deegree-services/deegree-services-csw/src/main/java/org/deegree/services/csw/exporthandling/AdhocQueryAnalyzer.java index 8e8e0c365d..4e5f76440c 100644 --- a/deegree-services/deegree-services-csw/src/main/java/org/deegree/services/csw/exporthandling/AdhocQueryAnalyzer.java +++ b/deegree-services/deegree-services-csw/src/main/java/org/deegree/services/csw/exporthandling/AdhocQueryAnalyzer.java @@ -172,9 +172,8 @@ public AdhocQueryAnalyzer( AdhocQuery adhocQuery, int startPosition, int maxReco private AdhocQuery getStoredQuery( String id, MetadataStore queryStore ) throws MetadataStoreException { MetadataResultSet recordById; - recordById = queryStore.getRecordById( Collections.singletonList( id ), new QName[] { new QName( RIM_NS, - "AdhocQuery", - "rim" ) } ); + recordById = queryStore.getRecordById( Collections.singletonList( id ), + new QName[] { new QName( RIM_NS, "AdhocQuery", "rim" ) } ); recordById.next(); Object storedQuery = recordById.getRecord(); if ( storedQuery == null || !( storedQuery instanceof AdhocQuery ) ) { @@ -280,42 +279,64 @@ private Operator copy( Operator op, Map values ) { switch ( ( (SpatialOperator) op ).getSubType() ) { case BBOX: BBOX bbox = (BBOX) op; + if ( bbox.getValueReference() != null ) + return new BBOX( copy( bbox.getParam1() ), bbox.getValueReference() ); Envelope env = bbox.getBoundingBox(); Envelope newEnv = gf.createEnvelope( env.getMin().get0(), env.getMin().get1(), env.getMax().get0(), env.getMax().get0(), env.getCoordinateSystem() ); - return new BBOX( copy( bbox.getPropName() ), newEnv ); + return new BBOX( copy( bbox.getParam1() ), newEnv ); case BEYOND: Beyond beyond = (Beyond) op; - return new Beyond( copy( beyond.getPropName() ), beyond.getGeometry(), copy( beyond.getDistance() ) ); + if ( beyond.getValueReference() != null ) + return new Beyond( copy( beyond.getParam1() ), beyond.getValueReference(), + copy( beyond.getDistance() ) ); + return new Beyond( copy( beyond.getParam1() ), beyond.getGeometry(), copy( beyond.getDistance() ) ); case CONTAINS: Contains contains = (Contains) op; - return new Contains( copy( contains.getPropName() ), contains.getGeometry() ); + if ( contains.getValueReference() != null ) + return new Contains( copy( contains.getParam1() ), contains.getValueReference() ); + return new Contains( copy( contains.getParam1() ), contains.getGeometry() ); case CROSSES: Crosses crosses = (Crosses) op; - return new Crosses( copy( crosses.getPropName() ), crosses.getGeometry() ); + if ( crosses.getValueReference() != null ) + return new Crosses( copy( crosses.getParam1() ), crosses.getValueReference() ); + return new Crosses( copy( crosses.getParam1() ), crosses.getGeometry() ); case DISJOINT: Disjoint disjoint = (Disjoint) op; - return new Disjoint( copy( disjoint.getPropName() ), disjoint.getGeometry() ); + if ( disjoint.getValueReference() != null ) + return new Disjoint( copy( disjoint.getParam1() ), disjoint.getValueReference() ); + return new Disjoint( copy( disjoint.getParam1() ), disjoint.getGeometry() ); case DWITHIN: DWithin dwithin = (DWithin) op; - return new DWithin( copy( dwithin.getPropName() ), dwithin.getGeometry(), copy( dwithin.getDistance() ) ); + if ( dwithin.getValueReference() != null ) + return new DWithin( copy( dwithin.getParam1() ), dwithin.getValueReference(), + copy( dwithin.getDistance() ) ); + return new DWithin( copy( dwithin.getParam1() ), dwithin.getGeometry(), copy( dwithin.getDistance() ) ); case EQUALS: Equals equals = (Equals) op; - return new Equals( copy( equals.getPropName() ), equals.getGeometry() ); + if ( equals.getValueReference() != null ) + return new Equals( copy( equals.getParam1() ), equals.getValueReference() ); + return new Equals( copy( equals.getParam1() ), equals.getGeometry() ); case INTERSECTS: Intersects intersects = (Intersects) op; if ( intersects.getValueReference() != null ) - return new Intersects( copy( intersects.getPropName() ), intersects.getValueReference() ); - return new Intersects( copy( intersects.getPropName() ), intersects.getGeometry() ); + return new Intersects( copy( intersects.getParam1() ), intersects.getValueReference() ); + return new Intersects( copy( intersects.getParam1() ), intersects.getGeometry() ); case OVERLAPS: Overlaps overlaps = (Overlaps) op; - return new Overlaps( copy( overlaps.getPropName() ), overlaps.getGeometry() ); + if ( overlaps.getValueReference() != null ) + return new Overlaps( copy( overlaps.getParam1() ), overlaps.getValueReference() ); + return new Overlaps( copy( overlaps.getParam1() ), overlaps.getGeometry() ); case TOUCHES: Touches touches = (Touches) op; - return new Touches( copy( touches.getPropName() ), touches.getGeometry() ); + if ( touches.getValueReference() != null ) + return new Touches( copy( touches.getParam1() ), touches.getValueReference() ); + return new Touches( copy( touches.getParam1() ), touches.getGeometry() ); case WITHIN: Within within = (Within) op; - return new Within( copy( within.getPropName() ), within.getGeometry() ); + if ( within.getValueReference() != null ) + return new Within( copy( within.getParam1() ), within.getValueReference() ); + return new Within( copy( within.getParam1() ), within.getGeometry() ); } break; } @@ -329,8 +350,8 @@ private Literal copy( Literal literal, Map values ) { String text = pv.getAsText(); if ( pv.getType().getBaseType().equals( BaseType.STRING ) && text.startsWith( "$" ) ) { String newValue = values.get( text.substring( 1 ) ); - return new Literal( - new PrimitiveValue( newValue, new PrimitiveType( BaseType.STRING ) ), + return new Literal( new PrimitiveValue( newValue, + new PrimitiveType( BaseType.STRING ) ), null ); } } @@ -342,10 +363,12 @@ private Expression copyExpression( Expression expr, Map values ) switch ( expr.getType() ) { case ADD: Add add = (Add) expr; - return new Add( copyExpression( add.getParameter1(), values ), copyExpression( add.getParameter2(), values ) ); + return new Add( copyExpression( add.getParameter1(), values ), + copyExpression( add.getParameter2(), values ) ); case DIV: Div div = (Div) expr; - return new Div( copyExpression( div.getParameter1(), values ), copyExpression( div.getParameter2(), values ) ); + return new Div( copyExpression( div.getParameter1(), values ), + copyExpression( div.getParameter2(), values ) ); case CUSTOM: // TODO break; @@ -360,12 +383,14 @@ private Expression copyExpression( Expression expr, Map values ) return copy( (Literal) expr, values ); case MUL: Mul mul = (Mul) expr; - return new Mul( copyExpression( mul.getParameter1(), values ), copyExpression( mul.getParameter2(), values ) ); + return new Mul( copyExpression( mul.getParameter1(), values ), + copyExpression( mul.getParameter2(), values ) ); case VALUE_REFERENCE: return copy( (ValueReference) expr ); case SUB: Sub sub = (Sub) expr; - return new Sub( copyExpression( sub.getParameter1(), values ), copyExpression( sub.getParameter2(), values ) ); + return new Sub( copyExpression( sub.getParameter1(), values ), + copyExpression( sub.getParameter2(), values ) ); } return newExpr; } From facd2eb0938bf1c6e2e65d488d9d51ad6c0b1b7a Mon Sep 17 00:00:00 2001 From: Lyn Elisa Goltz Date: Thu, 23 Jun 2016 16:34:44 +0200 Subject: [PATCH 23/25] #3616 - enhanced SQLPropertyNameMapper for support of multiple FeatureTypeMappings --- .../filter/CombinedPropertyNameMapper.java | 100 ------------- .../deegree-featurestore-sql/pom.xml | 4 + .../persistence/sql/SQLFeatureStore.java | 18 +-- .../sql/SQLPropertyNameMapper.java | 66 ++++++++- .../sql/xpath/MappableNameStep.java | 4 +- .../persistence/sql/xpath/MappableStep.java | 4 +- .../sql/SQLPropertyNameMapperTest.java | 134 ++++++++++++++++++ 7 files changed, 209 insertions(+), 121 deletions(-) delete mode 100644 deegree-core/deegree-core-sqldialect/deegree-sqldialect-commons/src/main/java/org/deegree/sqldialect/filter/CombinedPropertyNameMapper.java create mode 100644 deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/test/java/org/deegree/feature/persistence/sql/SQLPropertyNameMapperTest.java diff --git a/deegree-core/deegree-core-sqldialect/deegree-sqldialect-commons/src/main/java/org/deegree/sqldialect/filter/CombinedPropertyNameMapper.java b/deegree-core/deegree-core-sqldialect/deegree-sqldialect-commons/src/main/java/org/deegree/sqldialect/filter/CombinedPropertyNameMapper.java deleted file mode 100644 index fb065c367b..0000000000 --- a/deegree-core/deegree-core-sqldialect/deegree-sqldialect-commons/src/main/java/org/deegree/sqldialect/filter/CombinedPropertyNameMapper.java +++ /dev/null @@ -1,100 +0,0 @@ -//$HeadURL$ -/*---------------------------------------------------------------------------- - This file is part of deegree, http://deegree.org/ - Copyright (C) 2001-2015 by: - - Department of Geography, University of Bonn - - and - - lat/lon GmbH - - - This library is free software; you can redistribute it and/or modify it under - the terms of the GNU Lesser General Public License as published by the Free - Software Foundation; either version 2.1 of the License, or (at your option) - any later version. - This library is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - details. - You should have received a copy of the GNU Lesser General Public License - along with this library; if not, write to the Free Software Foundation, Inc., - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - Contact information: - - lat/lon GmbH - Aennchenstr. 19, 53177 Bonn - Germany - http://lat-lon.de/ - - Department of Geography, University of Bonn - Prof. Dr. Klaus Greve - Postfach 1147, 53001 Bonn - Germany - http://www.geographie.uni-bonn.de/deegree/ - - e-mail: info@deegree.org -----------------------------------------------------------------------------*/ -package org.deegree.sqldialect.filter; - -import java.util.ArrayList; -import java.util.List; - -import org.deegree.filter.FilterEvaluationException; -import org.deegree.filter.expression.ValueReference; - -/** - * Combines {@link PropertyNameMapper}s. - * - * @author Lyn Goltz - */ -public class CombinedPropertyNameMapper implements PropertyNameMapper { - - private final List propertyNameMappers; - - /** - * Instantiates an {@link CombinedPropertyNameMapper} without propertyNameMappers. - */ - public CombinedPropertyNameMapper() { - this( new ArrayList() ); - } - - /** - * Instantiates an {@link CombinedPropertyNameMapper} with a list of propertyNameMappers. - * - * @param propertyNameMappers - * never null - */ - public CombinedPropertyNameMapper( List propertyNameMappers ) { - this.propertyNameMappers = propertyNameMappers; - } - - /** - * @param mapperToAdd - * added to the list of combined propertyNameMappers, never null - */ - public void addPropertyNameMapper( PropertyNameMapper mapperToAdd ) { - propertyNameMappers.add( mapperToAdd ); - } - - @Override - public PropertyNameMapping getMapping( ValueReference propName, TableAliasManager aliasManager ) - throws FilterEvaluationException, UnmappableException { - for ( PropertyNameMapper propertyNameMapper : propertyNameMappers ) { - PropertyNameMapping mapping = propertyNameMapper.getMapping( propName, aliasManager ); - if ( mapping != null ) - return mapping; - } - return null; - } - - @Override - public PropertyNameMapping getSpatialMapping( ValueReference propName, TableAliasManager aliasManager ) - throws FilterEvaluationException, UnmappableException { - for ( PropertyNameMapper propertyNameMapper : propertyNameMappers ) { - PropertyNameMapping mapping = propertyNameMapper.getSpatialMapping( propName, aliasManager ); - if ( mapping != null ) - return mapping; - } - return null; - } - -} \ No newline at end of file diff --git a/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/pom.xml b/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/pom.xml index 078f0edfa8..11df6bf11f 100644 --- a/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/pom.xml +++ b/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/pom.xml @@ -63,6 +63,10 @@ junit junit + + org.mockito + mockito-core + org.antlr antlr-runtime diff --git a/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/SQLFeatureStore.java b/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/SQLFeatureStore.java index f8d95b22e9..33daafa356 100644 --- a/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/SQLFeatureStore.java +++ b/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/SQLFeatureStore.java @@ -129,7 +129,6 @@ import org.deegree.protocol.wfs.getfeature.TypeName; import org.deegree.sqldialect.SQLDialect; import org.deegree.sqldialect.filter.AbstractWhereBuilder; -import org.deegree.sqldialect.filter.CombinedPropertyNameMapper; import org.deegree.sqldialect.filter.DBField; import org.deegree.sqldialect.filter.Join; import org.deegree.sqldialect.filter.MappingExpression; @@ -1482,25 +1481,18 @@ private short[] getQueriedFeatureTypeIds( Query[] queries ) { return ftId; } - private AbstractWhereBuilder getWhereBuilder( FeatureType ft, OperatorFilter filter, SortProperty[] sortCrit, - Connection conn ) - throws FilterEvaluationException, - UnmappableException { - PropertyNameMapper mapper = new SQLPropertyNameMapper( this, getMapping( ft.getName() ) ); - return dialect.getWhereBuilder( mapper, filter, sortCrit, allowInMemoryFiltering ); - } - private AbstractWhereBuilder getWhereBuilder( Collection ftMappings, OperatorFilter filter, SortProperty[] sortCrit, Connection conn ) throws FilterEvaluationException, UnmappableException { - CombinedPropertyNameMapper mapper = new CombinedPropertyNameMapper(); - for ( FeatureTypeMapping ftMapping : ftMappings ) { - mapper.addPropertyNameMapper( new SQLPropertyNameMapper( this, ftMapping ) ); - } + PropertyNameMapper mapper = createPropertyNameMapper( ftMappings ); return dialect.getWhereBuilder( mapper, filter, sortCrit, allowInMemoryFiltering ); } + private PropertyNameMapper createPropertyNameMapper( Collection ftMappings ) { + return new SQLPropertyNameMapper( this, ftMappings ); + } + private List collectFeatureTypesNames( Query query ) throws FeatureStoreException { List ftNames = new ArrayList(); diff --git a/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/SQLPropertyNameMapper.java b/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/SQLPropertyNameMapper.java index bf027d5ff1..2a6939543e 100644 --- a/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/SQLPropertyNameMapper.java +++ b/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/SQLPropertyNameMapper.java @@ -35,6 +35,11 @@ ----------------------------------------------------------------------------*/ package org.deegree.feature.persistence.sql; +import java.util.Collection; +import java.util.List; + +import org.deegree.feature.persistence.sql.xpath.MappableNameStep; +import org.deegree.feature.persistence.sql.xpath.MappableStep; import org.deegree.feature.persistence.sql.xpath.MappedXPath; import org.deegree.filter.FilterEvaluationException; import org.deegree.filter.expression.ValueReference; @@ -57,20 +62,73 @@ public class SQLPropertyNameMapper implements PropertyNameMapper { private final FeatureTypeMapping ftMapping; + private final Collection ftMappings; + + /** + * @param fs + * the associated feature {@link SQLFeatureStore}, never null + * @param ftMapping + * the {@link FeatureTypeMapping}, never null + */ public SQLPropertyNameMapper( SQLFeatureStore fs, FeatureTypeMapping ftMapping ) { + this( fs, ftMapping, null ); + } + + /** + * @param fs + * the associated feature {@link SQLFeatureStore}, never null + * @param ftMappings + * a list of the {@link FeatureTypeMapping}s, never null or empty + */ + public SQLPropertyNameMapper( SQLFeatureStore fs, Collection ftMappings ) { + this( fs, null, ftMappings ); + } + + private SQLPropertyNameMapper( SQLFeatureStore fs, FeatureTypeMapping ftMapping, + Collection ftMappings ) { this.fs = fs; - this.ftMapping = ftMapping; + if ( ftMapping != null && ( ftMappings == null || ftMappings.isEmpty() ) ) { + this.ftMapping = ftMapping; + this.ftMappings = null; + } else if ( ftMapping == null && ( ftMappings != null && ftMappings.size() == 1 ) ) { + this.ftMapping = ftMappings.iterator().next(); + this.ftMappings = null; + } else if ( ftMapping == null && ( ftMappings != null && ftMappings.size() > 1 ) ) { + this.ftMapping = null; + this.ftMappings = ftMappings; + } else { + throw new IllegalArgumentException( "At least one feature type mapping is required." ); + } } @Override public PropertyNameMapping getMapping( ValueReference propName, TableAliasManager aliasManager ) throws FilterEvaluationException, UnmappableException { - return new MappedXPath( fs, ftMapping, propName, aliasManager, false ).getPropertyNameMapping(); + if ( ftMapping != null || propName == null || propName.getAsText().isEmpty() ) + return new MappedXPath( fs, ftMapping, propName, aliasManager, false ).getPropertyNameMapping(); + FeatureTypeMapping correspondingFtMapping = findCorrespondingMapping( propName ); + return new MappedXPath( fs, correspondingFtMapping, propName, aliasManager, false ).getPropertyNameMapping(); + } @Override public PropertyNameMapping getSpatialMapping( ValueReference propName, TableAliasManager aliasManager ) throws FilterEvaluationException, UnmappableException { - return new MappedXPath( fs, ftMapping, propName, aliasManager, true ).getPropertyNameMapping(); + if ( ftMapping != null || propName == null || propName.getAsText().isEmpty() ) + return new MappedXPath( fs, ftMapping, propName, aliasManager, true ).getPropertyNameMapping(); + FeatureTypeMapping correspondingFtMapping = findCorrespondingMapping( propName ); + return new MappedXPath( fs, correspondingFtMapping, propName, aliasManager, true ).getPropertyNameMapping(); } -} + + private FeatureTypeMapping findCorrespondingMapping( ValueReference propName ) + throws UnmappableException { + List steps = MappableStep.extractSteps( propName ); + if ( steps.size() > 1 && steps.get( 0 ) instanceof MappableNameStep ) + for ( FeatureTypeMapping ftMapping : ftMappings ) { + if ( ftMapping.getFeatureType().equals( ( (MappableNameStep) steps.get( 0 ) ).getNodeName() ) ) + return ftMapping; + } + throw new UnmappableException( "Could not parse mapping " + propName ); + } + +} \ No newline at end of file diff --git a/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/xpath/MappableNameStep.java b/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/xpath/MappableNameStep.java index aa8deab7cc..458e568568 100644 --- a/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/xpath/MappableNameStep.java +++ b/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/xpath/MappableNameStep.java @@ -45,7 +45,7 @@ * * @version $Revision$, $Date$ */ -abstract class MappableNameStep extends MappableStep { +public abstract class MappableNameStep extends MappableStep { private final QName nodeName; @@ -53,7 +53,7 @@ protected MappableNameStep( QName nodeName ) { this.nodeName = nodeName; } - QName getNodeName() { + public QName getNodeName() { return nodeName; } } \ No newline at end of file diff --git a/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/xpath/MappableStep.java b/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/xpath/MappableStep.java index c651ca2d27..45798d3c00 100644 --- a/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/xpath/MappableStep.java +++ b/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/xpath/MappableStep.java @@ -57,7 +57,7 @@ * * @version $Revision$, $Date$ */ -abstract class MappableStep { +public abstract class MappableStep { /** * Checks and extracts the steps from the given {@link ValueReference}. @@ -68,7 +68,7 @@ abstract class MappableStep { * @throws UnmappableException * if unsupported expressions / axes / predicates are encountered */ - static List extractSteps( ValueReference propName ) + public static List extractSteps( ValueReference propName ) throws UnmappableException { List steps = new ArrayList(); diff --git a/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/test/java/org/deegree/feature/persistence/sql/SQLPropertyNameMapperTest.java b/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/test/java/org/deegree/feature/persistence/sql/SQLPropertyNameMapperTest.java new file mode 100644 index 0000000000..7f9fb2b208 --- /dev/null +++ b/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/test/java/org/deegree/feature/persistence/sql/SQLPropertyNameMapperTest.java @@ -0,0 +1,134 @@ +//$HeadURL$ +/*---------------------------------------------------------------------------- + This file is part of deegree, http://deegree.org/ + Copyright (C) 2001-2015 by: + - Department of Geography, University of Bonn - + and + - lat/lon GmbH - + + This library is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at your option) + any later version. + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + details. + You should have received a copy of the GNU Lesser General Public License + along with this library; if not, write to the Free Software Foundation, Inc., + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Contact information: + + lat/lon GmbH + Aennchenstr. 19, 53177 Bonn + Germany + http://lat-lon.de/ + + Department of Geography, University of Bonn + Prof. Dr. Klaus Greve + Postfach 1147, 53001 Bonn + Germany + http://www.geographie.uni-bonn.de/deegree/ + + e-mail: info@deegree.org +----------------------------------------------------------------------------*/ +package org.deegree.feature.persistence.sql; + +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.List; + +import javax.xml.namespace.QName; + +import org.deegree.commons.jdbc.TableName; +import org.deegree.commons.xml.CommonNamespaces; +import org.deegree.commons.xml.NamespaceBindings; +import org.deegree.feature.persistence.sql.rules.GeometryMapping; +import org.deegree.feature.persistence.sql.rules.Mapping; +import org.deegree.feature.types.property.GeometryPropertyType.GeometryType; +import org.deegree.filter.expression.ValueReference; +import org.deegree.sqldialect.filter.DBField; +import org.deegree.sqldialect.filter.MappingExpression; +import org.deegree.sqldialect.filter.PropertyNameMapping; +import org.deegree.sqldialect.filter.TableAliasManager; +import org.junit.Test; + +/** + * @author Lyn Goltz + */ +public class SQLPropertyNameMapperTest { + + @Test(expected = IllegalArgumentException.class) + public void testConstructorFtMapping_Null() + throws Exception { + FeatureTypeMapping ftMapping = null; + new SQLPropertyNameMapper( mockFeatureStore(), ftMapping ); + } + + @Test(expected = IllegalArgumentException.class) + public void testConstructorFtMappingList_Null() + throws Exception { + List ftMapping = null; + new SQLPropertyNameMapper( mockFeatureStore(), ftMapping ); + } + + @Test(expected = IllegalArgumentException.class) + public void testConstructorFtMappingList_Empty() + throws Exception { + List ftMapping = new ArrayList(); + new SQLPropertyNameMapper( mockFeatureStore(), ftMapping ); + } + + @Test + public void testGetSpatialMapping() + throws Exception { + ValueReference propName = new ValueReference( "app:ftType2/app:geometry", nsContext() ); + List ftMapping = createFeatureTypeMappings( propName ); + SQLPropertyNameMapper mapper = new SQLPropertyNameMapper( mockFeatureStore(), ftMapping ); + PropertyNameMapping spatialMapping = mapper.getSpatialMapping( propName, mockAliasManager() ); + + assertThat( spatialMapping, notNullValue() ); + } + + private List createFeatureTypeMappings( ValueReference valueReference ) { + List ftMapping = new ArrayList(); + ftMapping.add( mockFeatureTypeMapping( "ftType1", "http://www.deegree.org/app", valueReference ) ); + ftMapping.add( mockFeatureTypeMapping( "ftType2", "http://www.deegree.org/app", valueReference ) ); + return ftMapping; + } + + private SQLFeatureStore mockFeatureStore() { + return mock( SQLFeatureStore.class ); + } + + private FeatureTypeMapping mockFeatureTypeMapping( String featureTypeName, String featureTypeNamespace, + ValueReference valueReference ) { + QName featureType = new QName( featureTypeNamespace, featureTypeName, "app" ); + FeatureTypeMapping mockedFtMapping = mock( FeatureTypeMapping.class ); + when( mockedFtMapping.getFtTable() ).thenReturn( new TableName( "table" ) ); + when( mockedFtMapping.getFeatureType() ).thenReturn( featureType ); + List mappings = new ArrayList(); + MappingExpression mappingExpression = new DBField( "column" ); + Mapping mapping = new GeometryMapping( valueReference, true, mappingExpression, GeometryType.GEOMETRY, null, + null ); + mappings.add( mapping ); + when( mockedFtMapping.getMappings() ).thenReturn( mappings ); + return mockedFtMapping; + } + + private TableAliasManager mockAliasManager() { + return mock( TableAliasManager.class ); + } + + private NamespaceBindings nsContext() { + NamespaceBindings nsContext = CommonNamespaces.getNamespaceContext(); + nsContext.addNamespace( "app", "http://www.deegree.org/app" ); + return nsContext; + } + +} \ No newline at end of file From 8d802fd1bae43ca1d04bce152e35ae6bedf44fea Mon Sep 17 00:00:00 2001 From: Lyn Elisa Goltz Date: Mon, 27 Jun 2016 07:24:28 +0200 Subject: [PATCH 24/25] removed unused imports --- .../java/org/deegree/filter/xml/Filter200XMLEncoder.java | 9 --------- 1 file changed, 9 deletions(-) diff --git a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/xml/Filter200XMLEncoder.java b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/xml/Filter200XMLEncoder.java index 6437a0ce07..b89a29efbc 100644 --- a/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/xml/Filter200XMLEncoder.java +++ b/deegree-core/deegree-core-base/src/main/java/org/deegree/filter/xml/Filter200XMLEncoder.java @@ -82,18 +82,9 @@ import org.deegree.filter.logical.LogicalOperator; import org.deegree.filter.logical.Not; import org.deegree.filter.logical.Or; -import org.deegree.filter.spatial.BBOX; import org.deegree.filter.spatial.Beyond; -import org.deegree.filter.spatial.Contains; -import org.deegree.filter.spatial.Crosses; import org.deegree.filter.spatial.DWithin; -import org.deegree.filter.spatial.Disjoint; -import org.deegree.filter.spatial.Equals; -import org.deegree.filter.spatial.Intersects; -import org.deegree.filter.spatial.Overlaps; import org.deegree.filter.spatial.SpatialOperator; -import org.deegree.filter.spatial.Touches; -import org.deegree.filter.spatial.Within; import org.deegree.filter.temporal.TemporalOperator; import org.deegree.geometry.Geometry; import org.deegree.geometry.io.DecimalCoordinateFormatter; From 4035b93f92461a85973f7a09d9ccf70d9604d621 Mon Sep 17 00:00:00 2001 From: Lyn Elisa Goltz Date: Mon, 27 Jun 2016 07:25:13 +0200 Subject: [PATCH 25/25] #3622 - find best matching ftMapping --- .../sql/SQLPropertyNameMapper.java | 29 +++++++++++++++---- .../sql/SQLPropertyNameMapperTest.java | 23 +++++++++++++++ 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/SQLPropertyNameMapper.java b/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/SQLPropertyNameMapper.java index 2a6939543e..f0ce02f4be 100644 --- a/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/SQLPropertyNameMapper.java +++ b/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/SQLPropertyNameMapper.java @@ -35,9 +35,13 @@ ----------------------------------------------------------------------------*/ package org.deegree.feature.persistence.sql; +import java.util.ArrayList; import java.util.Collection; import java.util.List; +import javax.xml.namespace.QName; + +import org.deegree.commons.utils.QNameUtils; import org.deegree.feature.persistence.sql.xpath.MappableNameStep; import org.deegree.feature.persistence.sql.xpath.MappableStep; import org.deegree.feature.persistence.sql.xpath.MappedXPath; @@ -123,12 +127,27 @@ public PropertyNameMapping getSpatialMapping( ValueReference propName, TableAlia private FeatureTypeMapping findCorrespondingMapping( ValueReference propName ) throws UnmappableException { List steps = MappableStep.extractSteps( propName ); - if ( steps.size() > 1 && steps.get( 0 ) instanceof MappableNameStep ) - for ( FeatureTypeMapping ftMapping : ftMappings ) { - if ( ftMapping.getFeatureType().equals( ( (MappableNameStep) steps.get( 0 ) ).getNodeName() ) ) - return ftMapping; - } + if ( steps.size() > 1 && steps.get( 0 ) instanceof MappableNameStep ) { + QName nodeName = ( (MappableNameStep) steps.get( 0 ) ).getNodeName(); + FeatureTypeMapping ftMapping = findBestMatchingFtMapping( nodeName ); + if ( ftMapping != null ) + return ftMapping; + } throw new UnmappableException( "Could not parse mapping " + propName ); } + private FeatureTypeMapping findBestMatchingFtMapping( QName nodeName ) { + List ftNames = new ArrayList(); + for ( FeatureTypeMapping ftMapping : ftMappings ) { + ftNames.add( ftMapping.getFeatureType() ); + } + + QName bestMatch = QNameUtils.findBestMatch( nodeName, ftNames ); + for ( FeatureTypeMapping ftMapping : ftMappings ) { + if ( ftMapping.getFeatureType().equals( bestMatch ) ) + return ftMapping; + } + return null; + } + } \ No newline at end of file diff --git a/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/test/java/org/deegree/feature/persistence/sql/SQLPropertyNameMapperTest.java b/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/test/java/org/deegree/feature/persistence/sql/SQLPropertyNameMapperTest.java index 7f9fb2b208..aaaae09c4d 100644 --- a/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/test/java/org/deegree/feature/persistence/sql/SQLPropertyNameMapperTest.java +++ b/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/test/java/org/deegree/feature/persistence/sql/SQLPropertyNameMapperTest.java @@ -95,6 +95,29 @@ public void testGetSpatialMapping() assertThat( spatialMapping, notNullValue() ); } + @Test + public void testGetSpatialMapping_withMissingNamespaceBinding() + throws Exception { + ValueReference propName = new ValueReference( "app:ftType2/app:geometry", + CommonNamespaces.getNamespaceContext() ); + List ftMapping = createFeatureTypeMappings( propName ); + SQLPropertyNameMapper mapper = new SQLPropertyNameMapper( mockFeatureStore(), ftMapping ); + PropertyNameMapping spatialMapping = mapper.getSpatialMapping( propName, mockAliasManager() ); + + assertThat( spatialMapping, notNullValue() ); + } + + @Test + public void testGetSpatialMapping_withMissingNamespaceBindingAndPrefix() + throws Exception { + ValueReference propName = new ValueReference( "ftType2/geometry", CommonNamespaces.getNamespaceContext() ); + List ftMapping = createFeatureTypeMappings( propName ); + SQLPropertyNameMapper mapper = new SQLPropertyNameMapper( mockFeatureStore(), ftMapping ); + PropertyNameMapping spatialMapping = mapper.getSpatialMapping( propName, mockAliasManager() ); + + assertThat( spatialMapping, notNullValue() ); + } + private List createFeatureTypeMappings( ValueReference valueReference ) { List ftMapping = new ArrayList(); ftMapping.add( mockFeatureTypeMapping( "ftType1", "http://www.deegree.org/app", valueReference ) );