org.codice.ddf.spatial.ogc.csw.catalog.endpoint.CswQueryFactoryTest.java Source code

Java tutorial

Introduction

Here is the source code for org.codice.ddf.spatial.ogc.csw.catalog.endpoint.CswQueryFactoryTest.java

Source

/**
 * Copyright (c) Codice Foundation
 * <p>
 * This 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 3 of the
 * License, or any later version.
 * <p>
 * This program 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. A copy of the GNU Lesser General Public License
 * is distributed along with this program and can be found at
 * <http://www.gnu.org/licenses/lgpl.html>.
 */
package org.codice.ddf.spatial.ogc.csw.catalog.endpoint;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.anyOf;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
import static org.mockito.Mockito.mock;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.xml.bind.JAXBElement;
import javax.xml.namespace.QName;

import org.apache.commons.lang.StringUtils;
import org.codice.ddf.spatial.ogc.csw.catalog.common.CswConstants;
import org.codice.ddf.spatial.ogc.csw.catalog.common.CswException;
import org.codice.ddf.spatial.ogc.csw.catalog.common.CswRecordMetacardType;
import org.codice.ddf.spatial.ogc.csw.catalog.common.GetRecordsRequest;
import org.geotools.filter.AttributeExpressionImpl;
import org.geotools.filter.LiteralExpressionImpl;
import org.geotools.styling.UomOgcMapping;
import org.junit.Ignore;
import org.junit.Test;
import org.opengis.filter.Filter;
import org.opengis.filter.Or;
import org.opengis.filter.PropertyIsLike;
import org.opengis.filter.sort.SortBy;
import org.opengis.filter.sort.SortOrder;
import org.opengis.filter.spatial.Beyond;
import org.opengis.filter.spatial.BinarySpatialOperator;
import org.opengis.filter.spatial.Contains;
import org.opengis.filter.spatial.Crosses;
import org.opengis.filter.spatial.DWithin;
import org.opengis.filter.spatial.Disjoint;
import org.opengis.filter.spatial.DistanceBufferOperator;
import org.opengis.filter.spatial.Equals;
import org.opengis.filter.spatial.Intersects;
import org.opengis.filter.spatial.Overlaps;
import org.opengis.filter.spatial.Touches;
import org.opengis.filter.spatial.Within;
import org.opengis.filter.temporal.After;
import org.opengis.filter.temporal.Before;
import org.opengis.filter.temporal.BegunBy;
import org.opengis.filter.temporal.BinaryTemporalOperator;
import org.opengis.filter.temporal.During;
import org.opengis.filter.temporal.TEquals;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.Polygon;
import com.vividsolutions.jts.io.ParseException;
import com.vividsolutions.jts.io.WKTReader;

import ddf.catalog.data.Metacard;
import ddf.catalog.federation.FederationException;
import ddf.catalog.filter.FilterAdapter;
import ddf.catalog.filter.FilterBuilder;
import ddf.catalog.filter.delegate.TagsFilterDelegate;
import ddf.catalog.filter.proxy.adapter.GeotoolsFilterAdapterImpl;
import ddf.catalog.filter.proxy.builder.GeotoolsFilterBuilder;
import ddf.catalog.operation.QueryRequest;
import ddf.catalog.operation.impl.QueryImpl;
import ddf.catalog.source.IngestException;
import ddf.catalog.source.SourceUnavailableException;
import ddf.catalog.source.UnsupportedQueryException;
import net.opengis.cat.csw.v_2_0_2.DistributedSearchType;
import net.opengis.cat.csw.v_2_0_2.GetRecordsType;
import net.opengis.cat.csw.v_2_0_2.QueryConstraintType;
import net.opengis.cat.csw.v_2_0_2.QueryType;
import net.opengis.cat.csw.v_2_0_2.ResultType;
import net.opengis.filter.v_1_1_0.BinaryComparisonOpType;
import net.opengis.filter.v_1_1_0.BinarySpatialOpType;
import net.opengis.filter.v_1_1_0.DistanceBufferType;
import net.opengis.filter.v_1_1_0.DistanceType;
import net.opengis.filter.v_1_1_0.FilterType;
import net.opengis.filter.v_1_1_0.LiteralType;
import net.opengis.filter.v_1_1_0.ObjectFactory;
import net.opengis.filter.v_1_1_0.PropertyNameType;
import net.opengis.filter.v_1_1_0.SortByType;
import net.opengis.filter.v_1_1_0.SortPropertyType;
import net.opengis.gml.v_3_1_1.AbstractGeometryType;
import net.opengis.gml.v_3_1_1.AbstractRingPropertyType;
import net.opengis.gml.v_3_1_1.CoordType;
import net.opengis.gml.v_3_1_1.LinearRingType;
import net.opengis.gml.v_3_1_1.PolygonType;

public class CswQueryFactoryTest {

    private static final String VALID_TYPES = "csw:Record,csw:Record";

    private static final String VALID_TYPE = "Record";

    private static final String VALID_PREFIX = "csw";

    private static final String CONTEXTUAL_TEST_ATTRIBUTE = "csw:title";

    private static final String SPATIAL_TEST_ATTRIBUTE = "location";

    private static final String CQL_FRAMEWORK_TEST_ATTRIBUTE = "title";

    private static final String TITLE_TEST_ATTRIBUTE = "dc:title";

    private static final String CQL_CONTEXTUAL_PATTERN = "some title";

    private static final String POLYGON_STR = "POLYGON((10 10, 10 25, 40 25, 40 10, 10 10))";

    private static final double REL_GEO_DISTANCE = 100;

    private static final String REL_GEO_UNITS = "kilometers";

    private static final double EXPECTED_GEO_DISTANCE = REL_GEO_DISTANCE * 1000;

    private static final String CQL_CONTEXTUAL_LIKE_QUERY = CONTEXTUAL_TEST_ATTRIBUTE + " Like '"
            + CQL_CONTEXTUAL_PATTERN + "'";

    private static final String CQL_FEDERATED_QUERY = "\"source-id\" = 'source1' AND " + CQL_CONTEXTUAL_LIKE_QUERY;

    private static final String CQL_SPATIAL_EQUALS_QUERY = "equals(" + SPATIAL_TEST_ATTRIBUTE + ", " + POLYGON_STR
            + ")";

    private static final String CQL_SPATIAL_DISJOINT_QUERY = "disjoint(" + SPATIAL_TEST_ATTRIBUTE + ", "
            + POLYGON_STR + ")";

    private static final String CQL_SPATIAL_INTERSECTS_QUERY = "intersects(" + SPATIAL_TEST_ATTRIBUTE + ", "
            + POLYGON_STR + ")";

    private static final String CQL_SPATIAL_TOUCHES_QUERY = "touches(" + SPATIAL_TEST_ATTRIBUTE + ", " + POLYGON_STR
            + ")";

    private static final String CQL_SPATIAL_CROSSES_QUERY = "crosses(" + SPATIAL_TEST_ATTRIBUTE + ", " + POLYGON_STR
            + ")";

    private static final String CQL_SPATIAL_WITHIN_QUERY = "within(" + SPATIAL_TEST_ATTRIBUTE + ", " + POLYGON_STR
            + ")";

    private static final String CQL_SPATIAL_CONTAINS_QUERY = "contains(" + SPATIAL_TEST_ATTRIBUTE + ", "
            + POLYGON_STR + ")";

    private static final String CQL_SPATIAL_OVERLAPS_QUERY = "overlaps(" + SPATIAL_TEST_ATTRIBUTE + ", "
            + POLYGON_STR + ")";

    private static final String CQL_SPATIAL_DWITHIN_QUERY = "dwithin(" + SPATIAL_TEST_ATTRIBUTE + ", " + POLYGON_STR
            + ", " + REL_GEO_DISTANCE + ", " + REL_GEO_UNITS + ")";

    private static final String CQL_SPATIAL_BEYOND_QUERY = "beyond(" + SPATIAL_TEST_ATTRIBUTE + ", " + POLYGON_STR
            + ", " + REL_GEO_DISTANCE + ", " + REL_GEO_UNITS + ")";

    private static final String TIMESTAMP = "2009-12-04T12:00:00Z";

    private static final String DURATION = "P40D";

    private static final String CQL_BEFORE = "before";

    private static final String CQL_AFTER = "after";

    private static final String CQL_DURING = "during";

    private static final String CQL_BEFORE_OR_DURING = "before or during";

    private static final String CQL_DURING_OR_AFTER = "during OR after";

    private static CswQueryFactory queryFactory;

    private static FilterBuilder filterBuilder = mock(FilterBuilder.class);

    private static Geometry polygon;

    private static net.opengis.gml.v_3_1_1.ObjectFactory gmlObjectFactory;

    private static ObjectFactory filterObjectFactory;

    private static QName cswQnameOutPutSchema = new QName(CswConstants.CSW_OUTPUT_SCHEMA);

    @org.junit.Before
    public void setUp() throws URISyntaxException, SourceUnavailableException, UnsupportedQueryException,
            FederationException, ParseException, IngestException {
        filterBuilder = new GeotoolsFilterBuilder();
        FilterAdapter filterAdapter = new GeotoolsFilterAdapterImpl();
        queryFactory = new CswQueryFactory(filterBuilder, filterAdapter);
        polygon = new WKTReader().read(POLYGON_STR);
        gmlObjectFactory = new net.opengis.gml.v_3_1_1.ObjectFactory();
        filterObjectFactory = new ObjectFactory();
    }

    @SuppressWarnings("unchecked")
    @Test
    public void testPostGetRecordsDistributedSearchNotSet()
            throws CswException, UnsupportedQueryException, SourceUnavailableException, FederationException {
        GetRecordsType grr = createDefaultPostRecordsRequest();

        QueryRequest queryRequest = queryFactory.getQuery(grr);
        assertThat(queryRequest.isEnterprise(), is(false));
        assertThat(queryRequest.getSourceIds(), anyOf(nullValue(), empty()));
    }

    @SuppressWarnings("unchecked")
    @Test
    public void testPostGetRecordsDistributedSearchSetToOne()
            throws CswException, UnsupportedQueryException, SourceUnavailableException, FederationException {
        GetRecordsType grr = createDefaultPostRecordsRequest();

        DistributedSearchType distributedSearch = new DistributedSearchType();
        distributedSearch.setHopCount(BigInteger.ONE);

        grr.setDistributedSearch(distributedSearch);

        QueryRequest queryRequest = queryFactory.getQuery(grr);
        assertThat(queryRequest.isEnterprise(), is(false));
        assertThat(queryRequest.getSourceIds(), anyOf(nullValue(), empty()));
    }

    @SuppressWarnings("unchecked")
    @Test
    public void testPostGetRecordsDistributedSearchSetToTen()
            throws CswException, UnsupportedQueryException, SourceUnavailableException, FederationException {
        GetRecordsType grr = createDefaultPostRecordsRequest();

        DistributedSearchType distributedSearch = new DistributedSearchType();
        distributedSearch.setHopCount(BigInteger.TEN);

        grr.setDistributedSearch(distributedSearch);

        QueryRequest queryRequest = queryFactory.getQuery(grr);
        assertThat(queryRequest.isEnterprise(), is(true));
        assertThat(queryRequest.getSourceIds(), anyOf(nullValue(), empty()));
    }

    @SuppressWarnings("unchecked")
    @Test
    public void testPostGetRecordsDistributedSearchSpecificSources()
            throws CswException, UnsupportedQueryException, SourceUnavailableException, FederationException {
        GetRecordsType grr = createDefaultPostRecordsRequest();

        DistributedSearchType distributedSearch = new DistributedSearchType();
        distributedSearch.setHopCount(BigInteger.TEN);

        grr.setDistributedSearch(distributedSearch);

        QueryType query = new QueryType();
        List<QName> typeNames = new ArrayList<QName>();
        typeNames.add(new QName(CswConstants.CSW_OUTPUT_SCHEMA, VALID_TYPE, VALID_PREFIX));

        query.setTypeNames(typeNames);
        QueryConstraintType constraint = new QueryConstraintType();

        constraint.setCqlText(CQL_FEDERATED_QUERY);

        query.setConstraint(constraint);

        JAXBElement<QueryType> jaxbQuery = new JAXBElement<QueryType>(cswQnameOutPutSchema, QueryType.class, query);
        grr.setAbstractQuery(jaxbQuery);

        QueryRequest queryRequest = queryFactory.getQuery(grr);
        assertThat(queryRequest.isEnterprise(), is(false));
        assertThat(queryRequest.getSourceIds(), contains("source1"));
    }

    @Test
    public void testPostGetRecordsContextualCQLQuery()
            throws CswException, UnsupportedQueryException, SourceUnavailableException, FederationException {
        GetRecordsType grr = createDefaultPostRecordsRequest();

        QueryType query = new QueryType();
        List<QName> typeNames = new ArrayList<QName>();
        typeNames.add(new QName(CswConstants.CSW_OUTPUT_SCHEMA, VALID_TYPE, VALID_PREFIX));
        query.setTypeNames(typeNames);
        QueryConstraintType constraint = new QueryConstraintType();
        constraint.setCqlText(CQL_CONTEXTUAL_LIKE_QUERY);

        query.setConstraint(constraint);
        JAXBElement<QueryType> jaxbQuery = new JAXBElement<QueryType>(cswQnameOutPutSchema, QueryType.class, query);

        grr.setAbstractQuery(jaxbQuery);

        QueryRequest queryRequest = queryFactory.getQuery(grr);
        QueryImpl frameworkQuery = (QueryImpl) queryRequest.getQuery();
        assertThat(frameworkQuery.getFilter(), instanceOf(PropertyIsLike.class));
        PropertyIsLike like = (PropertyIsLike) frameworkQuery.getFilter();
        assertThat(like.getLiteral(), is(CQL_CONTEXTUAL_PATTERN));
        assertThat(((AttributeExpressionImpl) like.getExpression()).getPropertyName(),
                is(CQL_FRAMEWORK_TEST_ATTRIBUTE));
    }

    @Test
    public void testPostGetRecordsValidSort()
            throws CswException, UnsupportedQueryException, SourceUnavailableException, FederationException {
        GetRecordsType grr = createDefaultPostRecordsRequest();

        grr.setResultType(ResultType.RESULTS);
        QueryType query = new QueryType();

        SortByType incomingSort = new SortByType();
        SortPropertyType propType = new SortPropertyType();
        PropertyNameType propName = new PropertyNameType();
        propName.setContent(Arrays.asList((Object) TITLE_TEST_ATTRIBUTE));
        propType.setPropertyName(propName);
        incomingSort.getSortProperty().add(propType);
        query.setSortBy(incomingSort);

        JAXBElement<QueryType> jaxbQuery = new JAXBElement<QueryType>(cswQnameOutPutSchema, QueryType.class, query);

        grr.setAbstractQuery(jaxbQuery);

        QueryRequest queryRequest = queryFactory.getQuery(grr);

        SortBy resultSort = queryRequest.getQuery().getSortBy();

        assertThat(resultSort.getPropertyName().getPropertyName(), is(CQL_FRAMEWORK_TEST_ATTRIBUTE));
        assertThat(resultSort.getSortOrder(), is(SortOrder.ASCENDING));
    }

    @Test
    public void testPostGetRecordsSpatialEqualsCQLQuery()
            throws CswException, UnsupportedQueryException, SourceUnavailableException, FederationException {
        cqlSpatialQuery(Equals.class, CQL_SPATIAL_EQUALS_QUERY);
    }

    @Test
    public void testPostGetRecordsSpatialDisjointCQLQuery()
            throws CswException, UnsupportedQueryException, SourceUnavailableException, FederationException {
        cqlSpatialQuery(Disjoint.class, CQL_SPATIAL_DISJOINT_QUERY);
    }

    @Test
    public void testPostGetRecordsSpatialIntersectsCQLQuery()
            throws CswException, UnsupportedQueryException, SourceUnavailableException, FederationException {
        cqlSpatialQuery(Intersects.class, CQL_SPATIAL_INTERSECTS_QUERY);
    }

    @Test
    public void testPostGetRecordsSpatialTouchesCQLQuery()
            throws CswException, UnsupportedQueryException, SourceUnavailableException, FederationException {
        cqlSpatialQuery(Touches.class, CQL_SPATIAL_TOUCHES_QUERY);
    }

    @Test
    public void testPostGetRecordsSpatialCrossesCQLQuery()
            throws CswException, UnsupportedQueryException, SourceUnavailableException, FederationException {
        cqlSpatialQuery(Crosses.class, CQL_SPATIAL_CROSSES_QUERY);
    }

    @Test
    public void testPostGetRecordsSpatialWithinCQLQuery()
            throws CswException, UnsupportedQueryException, SourceUnavailableException, FederationException {
        cqlSpatialQuery(Within.class, CQL_SPATIAL_WITHIN_QUERY);
    }

    @Test
    public void testPostGetRecordsSpatialContainsCQLQuery()
            throws CswException, UnsupportedQueryException, SourceUnavailableException, FederationException {
        cqlSpatialQuery(Contains.class, CQL_SPATIAL_CONTAINS_QUERY);
    }

    @Test
    public void testPostGetRecordsSpatialOverlapsCQLQuery()
            throws CswException, UnsupportedQueryException, SourceUnavailableException, FederationException {
        cqlSpatialQuery(Overlaps.class, CQL_SPATIAL_OVERLAPS_QUERY);
    }

    @Test
    public void testPostGetRecordsSpatialDWithinCQLQuery()
            throws CswException, UnsupportedQueryException, SourceUnavailableException, FederationException {
        cqlSpatialRelativeQuery(DWithin.class, CQL_SPATIAL_DWITHIN_QUERY);
    }

    @Test
    public void testPostGetRecordsSpatialBeyondCQLQuery()
            throws CswException, UnsupportedQueryException, SourceUnavailableException, FederationException {
        cqlSpatialRelativeQuery(Beyond.class, CQL_SPATIAL_BEYOND_QUERY);
    }

    @Test
    public void testPostGetRecordsSpatialEqualsOgcFilter()
            throws CswException, UnsupportedQueryException, SourceUnavailableException, FederationException {
        BinarySpatialOpType op = createBinarySpatialOpType();

        ogcSpatialQuery(Equals.class, filterObjectFactory.createEquals(op));
    }

    @Test
    public void testPostGetRecordsSpatialDisjointOgcFilter()
            throws CswException, UnsupportedQueryException, SourceUnavailableException, FederationException {
        BinarySpatialOpType op = createBinarySpatialOpType();
        ogcSpatialQuery(Disjoint.class, filterObjectFactory.createDisjoint(op));
    }

    @Test
    public void testPostGetRecordsSpatialIntersectsOgcFilter()
            throws CswException, UnsupportedQueryException, SourceUnavailableException, FederationException {
        BinarySpatialOpType op = createBinarySpatialOpType();
        ogcSpatialQuery(Intersects.class, filterObjectFactory.createIntersects(op));
    }

    @Test
    public void testPostGetRecordsSpatialTouchesOgcFilter()
            throws CswException, UnsupportedQueryException, SourceUnavailableException, FederationException {
        BinarySpatialOpType op = createBinarySpatialOpType();
        ogcSpatialQuery(Touches.class, filterObjectFactory.createTouches(op));
    }

    @Test
    public void testPostGetRecordsSpatialCrossesOgcFilter()
            throws CswException, UnsupportedQueryException, SourceUnavailableException, FederationException {
        BinarySpatialOpType op = createBinarySpatialOpType();
        ogcSpatialQuery(Crosses.class, filterObjectFactory.createCrosses(op));
    }

    @Test
    public void testPostGetRecordsSpatialWithinOgcFilter()
            throws CswException, UnsupportedQueryException, SourceUnavailableException, FederationException {
        BinarySpatialOpType op = createBinarySpatialOpType();
        ogcSpatialQuery(Within.class, filterObjectFactory.createWithin(op));
    }

    @Test
    public void testPostGetRecordsSpatialContainsOgcFilter()
            throws CswException, UnsupportedQueryException, SourceUnavailableException, FederationException {
        BinarySpatialOpType op = createBinarySpatialOpType();
        ogcSpatialQuery(Contains.class, filterObjectFactory.createContains(op));
    }

    @Test
    public void testPostGetRecordsSpatialOverlapsOgcFilter()
            throws CswException, UnsupportedQueryException, SourceUnavailableException, FederationException {
        BinarySpatialOpType op = createBinarySpatialOpType();
        ogcSpatialQuery(Overlaps.class, filterObjectFactory.createOverlaps(op));
    }

    @Test
    public void testPostGetRecordsSpatialDWithinOgcFilter()
            throws CswException, UnsupportedQueryException, SourceUnavailableException, FederationException {
        DistanceBufferType op = createDistanceBufferType();
        ogcSpatialRelativeQuery(DWithin.class, filterObjectFactory.createDWithin(op));
    }

    @Test
    public void testPostGetRecordsSpatialBeyondOgcFilter()
            throws CswException, UnsupportedQueryException, SourceUnavailableException, FederationException {
        DistanceBufferType op = createDistanceBufferType();
        ogcSpatialRelativeQuery(Beyond.class, filterObjectFactory.createBeyond(op));
    }

    @Test
    public void testGetGetRecordsSpatialDWithinOgcFilter()
            throws CswException, UnsupportedQueryException, SourceUnavailableException, FederationException {
        String constraint = createDistanceBufferQuery("DWithin");
        ogcSpatialRelativeQuery(DWithin.class, constraint);
    }

    @Test
    public void testGetGetRecordsSpatialBeyondOgcFilter()
            throws CswException, UnsupportedQueryException, SourceUnavailableException, FederationException {
        String constraint = createDistanceBufferQuery("Beyond");
        ogcSpatialRelativeQuery(Beyond.class, constraint);
    }

    @Test
    public void testPostGetRecordsTemporalPropertyIsLessOgcFilter()
            throws CswException, UnsupportedQueryException, SourceUnavailableException, FederationException {
        BinaryComparisonOpType op = createTemporalBinaryComparisonOpType(CswConstants.CSW_CREATED, TIMESTAMP);
        ogcTemporalQuery(Metacard.CREATED, filterObjectFactory.createPropertyIsLessThan(op), Before.class);
    }

    @Ignore("TODO: the functions this test tests has been augmented to play well with the limited capabilities of the Solr provider.  "
            + "These tests and the functions they test should be reenabled and refactored after DDF-311 is addressed")
    @Test
    public void testPostGetRecordsTemporalPropertyIsLessOrEqualOgcFilter()
            throws CswException, UnsupportedQueryException, SourceUnavailableException, FederationException {
        BinaryComparisonOpType op = createTemporalBinaryComparisonOpType(CswConstants.CSW_CREATED, TIMESTAMP);
        ogcOrdTemporalQuery(Metacard.CREATED, filterObjectFactory.createPropertyIsLessThanOrEqualTo(op),
                BegunBy.class, TEquals.class);
    }

    @Ignore("TODO: the functions this test tests has been augmented to play well with the limited capabilities of the Solr provider.  "
            + "These tests and the functions they test should be reenabled and refactored after DDF-311 is addressed")
    @Test
    public void testPostGetRecordsTemporalPropertyIsGreaterOgcFilter()
            throws CswException, UnsupportedQueryException, SourceUnavailableException, FederationException {
        BinaryComparisonOpType op = createTemporalBinaryComparisonOpType(CswConstants.CSW_CREATED, TIMESTAMP);
        ogcTemporalQuery(Metacard.CREATED, filterObjectFactory.createPropertyIsGreaterThan(op), After.class);
    }

    @Ignore("TODO: the functions this test tests has been augmented to play well with the limited capabilities of the Solr provider.  "
            + "These tests and the functions they test should be reenabled and refactored after DDF-311 is addressed")
    @Test
    public void testPostGetRecordsTemporalPropertyIsGreaterOrEqualOgcFilter()
            throws CswException, UnsupportedQueryException, SourceUnavailableException, FederationException {
        BinaryComparisonOpType op = createTemporalBinaryComparisonOpType(CswConstants.CSW_CREATED, TIMESTAMP);
        ogcOrdTemporalQuery(Metacard.CREATED, filterObjectFactory.createPropertyIsGreaterThanOrEqualTo(op),
                After.class, TEquals.class);
    }

    @SuppressWarnings("unchecked")
    @Test
    public void testPostGetRecordsTemporalBeforeCQLQuery()
            throws CswException, UnsupportedQueryException, SourceUnavailableException, FederationException {

        String[] cqlTextValues = new String[] { CswConstants.CSW_CREATED, CQL_BEFORE, TIMESTAMP };
        String cqlText = StringUtils.join(cqlTextValues, " ");
        cqlTemporalQuery(Metacard.CREATED, cqlText, new Class[] { Before.class });
    }

    @SuppressWarnings("unchecked")
    @Test
    public void testPostGetRecordsTemporalAfterCQLQuery()
            throws CswException, UnsupportedQueryException, SourceUnavailableException, FederationException {

        String[] cqlTextValues = new String[] { CswRecordMetacardType.CSW_ISSUED, CQL_AFTER, TIMESTAMP };
        String cqlText = StringUtils.join(cqlTextValues, " ");
        cqlTemporalQuery(Metacard.MODIFIED, cqlText, new Class[] { After.class });
    }

    @SuppressWarnings("unchecked")
    @Test
    public void testPostGetRecordsTemporalDuringCQLQuery()
            throws CswException, UnsupportedQueryException, SourceUnavailableException, FederationException {

        String[] cqlTextValues = new String[] { CswRecordMetacardType.CSW_DATE_ACCEPTED, CQL_DURING, TIMESTAMP, "/",
                DURATION };
        String cqlText = StringUtils.join(cqlTextValues, " ");
        cqlTemporalQuery(Metacard.EFFECTIVE, cqlText, new Class[] { During.class });
    }

    @SuppressWarnings("unchecked")
    @Test
    public void testPostGetRecordsTemporalBeforeOrDuringCQLQuery()
            throws CswException, UnsupportedQueryException, SourceUnavailableException, FederationException {

        String[] cqlTextValues = new String[] { CswRecordMetacardType.CSW_DATE, CQL_BEFORE_OR_DURING, TIMESTAMP,
                "/", DURATION };
        String cqlText = StringUtils.join(cqlTextValues, " ");
        cqlTemporalQuery(Metacard.MODIFIED, cqlText, new Class[] { Before.class, During.class });
    }

    @SuppressWarnings("unchecked")
    @Test
    public void testPostGetRecordsTemporalAfterOrDuringCQLQuery()
            throws CswException, UnsupportedQueryException, SourceUnavailableException, FederationException {

        String[] cqlTextValues = new String[] { CswRecordMetacardType.CSW_VALID, CQL_DURING_OR_AFTER, TIMESTAMP,
                "/", DURATION };
        String cqlText = StringUtils.join(cqlTextValues, " ");
        cqlTemporalQuery(Metacard.EXPIRATION, cqlText, new Class[] { During.class, After.class });
    }

    @Test
    public void testQueryTags() throws Exception {
        queryFactory.setSchemaToTagsMapping(new String[] { CswConstants.CSW_OUTPUT_SCHEMA + "=myTag" });

        List<String> ids = new ArrayList<>();
        ids.add("someId");

        FilterAdapter adapter = new GeotoolsFilterAdapterImpl();

        assertThat(adapter.adapt(queryFactory
                .updateQueryRequestTags(queryFactory.getQueryById(ids), CswConstants.CSW_OUTPUT_SCHEMA).getQuery(),
                new TagsFilterDelegate("myTag")), is(true));
    }

    /**
     * Runs a binary Spatial CQL Query, verifying that the right filter class is generated based on CQL
     *
     * @param clz Class of filter to generate
     * @param cql CQL Query String
     * @throws UnsupportedQueryException
     * @throws SourceUnavailableException
     * @throws FederationException
     * @throws CswException
     */
    private <N extends BinarySpatialOperator> void cqlSpatialQuery(Class<N> clz, String cql)
            throws UnsupportedQueryException, SourceUnavailableException, FederationException, CswException {
        GetRecordsType grr = createDefaultPostRecordsRequest();

        QueryType query = new QueryType();
        List<QName> typeNames = new ArrayList<QName>();
        typeNames.add(new QName(CswConstants.CSW_OUTPUT_SCHEMA, VALID_TYPE, VALID_PREFIX));
        query.setTypeNames(typeNames);
        QueryConstraintType constraint = new QueryConstraintType();
        constraint.setCqlText(cql);

        query.setConstraint(constraint);
        JAXBElement<QueryType> jaxbQuery = new JAXBElement<QueryType>(cswQnameOutPutSchema, QueryType.class, query);

        grr.setAbstractQuery(jaxbQuery);

        QueryImpl frameworkQuery = (QueryImpl) queryFactory.getQuery(grr).getQuery();
        assertThat(frameworkQuery.getFilter(), instanceOf(clz));
        @SuppressWarnings("unchecked")
        N spatial = (N) frameworkQuery.getFilter();
        assertThat((Polygon) ((LiteralExpressionImpl) spatial.getExpression2()).getValue(), is(polygon));

        assertThat(((AttributeExpressionImpl) spatial.getExpression1()).getPropertyName(),
                is(SPATIAL_TEST_ATTRIBUTE));
    }

    /**
     * Runs a relative spatial CQL Query, verifying that the right filter class is generated based on CQL
     *
     * @param clz Class of filter to generate
     * @param cql CQL Query String
     * @throws UnsupportedQueryException
     * @throws SourceUnavailableException
     * @throws FederationException
     * @throws CswException
     */
    private <N extends DistanceBufferOperator> void cqlSpatialRelativeQuery(Class<N> clz, String cql)
            throws UnsupportedQueryException, SourceUnavailableException, FederationException, CswException {
        GetRecordsType grr = createDefaultPostRecordsRequest();

        QueryType query = new QueryType();
        List<QName> typeNames = new ArrayList<QName>();
        typeNames.add(new QName(CswConstants.CSW_OUTPUT_SCHEMA, VALID_TYPE, VALID_PREFIX));
        query.setTypeNames(typeNames);
        QueryConstraintType constraint = new QueryConstraintType();
        constraint.setCqlText(cql);

        query.setConstraint(constraint);
        JAXBElement<QueryType> jaxbQuery = new JAXBElement<QueryType>(cswQnameOutPutSchema, QueryType.class, query);

        grr.setAbstractQuery(jaxbQuery);

        QueryImpl frameworkQuery = (QueryImpl) queryFactory.getQuery(grr).getQuery();
        assertThat(frameworkQuery.getFilter(), instanceOf(clz));
        @SuppressWarnings("unchecked")
        N spatial = (N) frameworkQuery.getFilter();
        assertThat((Polygon) ((LiteralExpressionImpl) spatial.getExpression2()).getValue(), is(polygon));

        assertThat(((AttributeExpressionImpl) spatial.getExpression1()).getPropertyName(),
                is(SPATIAL_TEST_ATTRIBUTE));

        assertThat(spatial.getDistanceUnits(), is(UomOgcMapping.METRE.name()));
        assertThat(spatial.getDistance(), is(EXPECTED_GEO_DISTANCE));
    }

    private BinaryComparisonOpType createTemporalBinaryComparisonOpType(String attr, String comparison) {
        BinaryComparisonOpType comparisonOp = new BinaryComparisonOpType();

        PropertyNameType propName = new PropertyNameType();
        propName.getContent().add(attr);

        comparisonOp.getExpression().add(filterObjectFactory.createPropertyName(propName));

        LiteralType literal = new LiteralType();
        literal.getContent().add(comparison);

        comparisonOp.getExpression().add(filterObjectFactory.createLiteral(literal));
        return comparisonOp;
    }

    private BinarySpatialOpType createBinarySpatialOpType() {
        BinarySpatialOpType binarySpatialOps = new BinarySpatialOpType();

        PropertyNameType propName = new PropertyNameType();
        propName.getContent().add(SPATIAL_TEST_ATTRIBUTE);
        binarySpatialOps.setPropertyName(propName);

        binarySpatialOps.setGeometry(createPolygon());
        return binarySpatialOps;
    }

    private String createDistanceBufferQuery(String comparison) {
        String query = "      <ogc:Filter xmlns:ogc=\"http://www.opengis.net/ogc\" xmlns:gml=\"http://www.opengis.net/gml\">"
                + "        <ogc:" + comparison + ">" + "          <ogc:PropertyName>" + SPATIAL_TEST_ATTRIBUTE
                + "</ogc:PropertyName>" + "          <gml:Polygon gml:id=\"Pl001\">" + "            <gml:exterior>"
                + "              <gml:LinearRing>" + "                <gml:pos>10 10</gml:pos>"
                + "                <gml:pos>10 25</gml:pos>" + "                <gml:pos>40 25</gml:pos>"
                + "                <gml:pos>40 10</gml:pos>" + "                <gml:pos>10 10</gml:pos>"
                + "              </gml:LinearRing>" + "            </gml:exterior>" + "          </gml:Polygon>"
                + "          <ogc:Distance units=\"" + REL_GEO_UNITS + "\">" + REL_GEO_DISTANCE + "</ogc:Distance>"
                + "        </ogc:" + comparison + ">" + "      </ogc:Filter>";

        return query;
    }

    private DistanceBufferType createDistanceBufferType() {
        DistanceBufferType distanceBuffer = new DistanceBufferType();

        PropertyNameType propName = new PropertyNameType();
        propName.getContent().add(SPATIAL_TEST_ATTRIBUTE);
        distanceBuffer.setPropertyName(propName);

        DistanceType distance = filterObjectFactory.createDistanceType();
        distance.setUnits(REL_GEO_UNITS);
        distance.setContent(Double.toString(REL_GEO_DISTANCE));

        distanceBuffer.setDistance(distance);
        distanceBuffer.setGeometry(createPolygon());
        return distanceBuffer;
    }

    private JAXBElement<AbstractGeometryType> createPolygon() {
        PolygonType localPolygon = new PolygonType();

        LinearRingType ring = new LinearRingType();
        for (Coordinate coordinate : polygon.getCoordinates()) {
            CoordType coord = new CoordType();
            coord.setX(BigDecimal.valueOf(coordinate.x));
            coord.setY(BigDecimal.valueOf(coordinate.y));
            if (!Double.isNaN(coordinate.z)) {
                coord.setZ(BigDecimal.valueOf(coordinate.z));
            }
            ring.getCoord().add(coord);
        }
        AbstractRingPropertyType abstractRing = new AbstractRingPropertyType();
        abstractRing.setRing(gmlObjectFactory.createLinearRing(ring));
        localPolygon.setExterior(gmlObjectFactory.createExterior(abstractRing));

        JAXBElement<AbstractGeometryType> agt = new JAXBElement<AbstractGeometryType>(
                new QName("http://www.opengis.net/gml", "Polygon"), AbstractGeometryType.class, null, localPolygon);
        return agt;
    }

    /**
     * Runs a binary Spatial OGC Query, verifying that the right filter class is generated based on OGC Filter
     *
     * @param constraint The OGC Filter Constraint as an XML string
     * @throws UnsupportedQueryException
     * @throws SourceUnavailableException
     * @throws FederationException
     * @throws CswException
     */
    private <N extends DistanceBufferOperator> void ogcSpatialRelativeQuery(Class<N> clz, String constraint)
            throws UnsupportedQueryException, SourceUnavailableException, FederationException, CswException {
        GetRecordsRequest grr = createDefaultGetRecordsRequest();

        grr.setConstraintLanguage("FILTER");
        grr.setConstraint(constraint);

        QueryImpl frameworkQuery = (QueryImpl) queryFactory.getQuery(grr.get202RecordsType()).getQuery();
        assertThat(frameworkQuery.getFilter(), instanceOf(clz));
        @SuppressWarnings("unchecked")
        N spatial = (N) frameworkQuery.getFilter();
        assertThat((Polygon) ((LiteralExpressionImpl) spatial.getExpression2()).getValue(), is(polygon));

        assertThat(((AttributeExpressionImpl) spatial.getExpression1()).getPropertyName(),
                is(SPATIAL_TEST_ATTRIBUTE));
    }

    /**
     * Runs a binary Spatial OGC Query, verifying that the right filter class is generated based on OGC Filter
     *
     * @param spatialOps BinarySpatialOps query
     * @throws UnsupportedQueryException
     * @throws SourceUnavailableException
     * @throws FederationException
     * @throws CswException
     */
    private <N extends DistanceBufferOperator> void ogcSpatialRelativeQuery(Class<N> clz,
            JAXBElement<DistanceBufferType> spatialOps)
            throws UnsupportedQueryException, SourceUnavailableException, FederationException, CswException {
        GetRecordsType grr = createDefaultPostRecordsRequest();

        QueryType query = new QueryType();
        List<QName> typeNames = new ArrayList<QName>();
        typeNames.add(new QName(CswConstants.CSW_OUTPUT_SCHEMA, VALID_TYPE, VALID_PREFIX));
        query.setTypeNames(typeNames);
        QueryConstraintType constraint = new QueryConstraintType();
        FilterType filter = new FilterType();
        filter.setSpatialOps(spatialOps);

        constraint.setFilter(filter);

        query.setConstraint(constraint);
        JAXBElement<QueryType> jaxbQuery = new JAXBElement<QueryType>(cswQnameOutPutSchema, QueryType.class, query);

        grr.setAbstractQuery(jaxbQuery);

        QueryImpl frameworkQuery = (QueryImpl) queryFactory.getQuery(grr).getQuery();
        assertThat(frameworkQuery.getFilter(), instanceOf(clz));
        @SuppressWarnings("unchecked")
        N spatial = (N) frameworkQuery.getFilter();
        assertThat((Polygon) ((LiteralExpressionImpl) spatial.getExpression2()).getValue(), is(polygon));

        assertThat(((AttributeExpressionImpl) spatial.getExpression1()).getPropertyName(),
                is(SPATIAL_TEST_ATTRIBUTE));
    }

    /**
     * Runs a binary Spatial OGC Query, verifying that the right filter class is generated based on OGC Filter
     *
     * @param spatialOps BinarySpatialOps query
     * @throws UnsupportedQueryException
     * @throws SourceUnavailableException
     * @throws FederationException
     * @throws CswException
     */
    private <N extends BinarySpatialOperator> void ogcSpatialQuery(Class<N> clz,
            JAXBElement<BinarySpatialOpType> spatialOps)
            throws UnsupportedQueryException, SourceUnavailableException, FederationException, CswException {
        GetRecordsType grr = createDefaultPostRecordsRequest();

        QueryType query = new QueryType();
        List<QName> typeNames = new ArrayList<QName>();
        typeNames.add(new QName(CswConstants.CSW_OUTPUT_SCHEMA, VALID_TYPE, VALID_PREFIX));
        query.setTypeNames(typeNames);
        QueryConstraintType constraint = new QueryConstraintType();
        FilterType filter = new FilterType();
        filter.setSpatialOps(spatialOps);

        constraint.setFilter(filter);

        query.setConstraint(constraint);
        JAXBElement<QueryType> jaxbQuery = new JAXBElement<QueryType>(cswQnameOutPutSchema, QueryType.class, query);

        grr.setAbstractQuery(jaxbQuery);

        QueryImpl frameworkQuery = (QueryImpl) queryFactory.getQuery(grr).getQuery();
        assertThat(frameworkQuery.getFilter(), instanceOf(clz));
        @SuppressWarnings("unchecked")
        N spatial = (N) frameworkQuery.getFilter();
        assertThat((Polygon) ((LiteralExpressionImpl) spatial.getExpression2()).getValue(), is(polygon));

        assertThat(((AttributeExpressionImpl) spatial.getExpression1()).getPropertyName(),
                is(SPATIAL_TEST_ATTRIBUTE));
    }

    /**
     * Runs a binary Temporal OGC Query, verifying that the right filter class is generated based on
     * OGC Filter
     *
     * @param expectedAttr Exprected Mapped Attribute
     * @param temporalOps  The Temporal query, in terms of a binary comparison
     * @param clz          the Expected Class result
     * @throws UnsupportedQueryException
     * @throws SourceUnavailableException
     * @throws FederationException
     * @throws CswException
     */
    @SuppressWarnings("unchecked")
    private <N extends BinaryTemporalOperator> void ogcTemporalQuery(String expectedAttr,
            JAXBElement<BinaryComparisonOpType> temporalOps, Class<N> clz)
            throws UnsupportedQueryException, SourceUnavailableException, FederationException, CswException {
        Filter filter = generateTemporalFilter(temporalOps);

        assertThat(filter, instanceOf(clz));

        N temporal = (N) filter;
        assertThat(((AttributeExpressionImpl) temporal.getExpression1()).getPropertyName(), is(expectedAttr));
    }

    /**
     * Runs an Or'd query of multiple binary Temporal OGC Query, verifying that the right filter
     * class is generated based on OGC Filter
     *
     * @param expectedAttr Exprected Mapped Attribute
     * @param temporalOps  The Temporal query, in terms of a binary comparison
     * @param clzzes       the Expected Class result
     * @throws UnsupportedQueryException
     * @throws SourceUnavailableException
     * @throws FederationException
     * @throws CswException
     */
    @SuppressWarnings("unchecked")
    private void ogcOrdTemporalQuery(String expectedAttr, JAXBElement<BinaryComparisonOpType> temporalOps,
            Class<? extends BinaryTemporalOperator>... clzzes)
            throws UnsupportedQueryException, SourceUnavailableException, FederationException, CswException {
        Filter filter = generateTemporalFilter(temporalOps);

        assertThat(filter, instanceOf(Or.class));

        Or ordTemporal = (Or) filter;

        List<Filter> temporalFilters = ordTemporal.getChildren();

        List<Class<? extends BinaryTemporalOperator>> classes = new ArrayList<Class<? extends BinaryTemporalOperator>>();

        for (Filter temporal : temporalFilters) {
            assertThat(temporal, instanceOf(BinaryTemporalOperator.class));
            classes.add((Class<? extends BinaryTemporalOperator>) temporal.getClass());
        }
    }

    private Filter generateTemporalFilter(JAXBElement<BinaryComparisonOpType> temporalOps)
            throws UnsupportedQueryException, SourceUnavailableException, FederationException, CswException {
        GetRecordsType grr = createDefaultPostRecordsRequest();

        QueryType query = new QueryType();
        List<QName> typeNames = new ArrayList<QName>();
        typeNames.add(new QName(CswConstants.CSW_OUTPUT_SCHEMA, VALID_TYPE, VALID_PREFIX));
        query.setTypeNames(typeNames);
        QueryConstraintType constraint = new QueryConstraintType();
        FilterType filter = new FilterType();
        filter.setComparisonOps(temporalOps);

        constraint.setFilter(filter);

        query.setConstraint(constraint);
        JAXBElement<QueryType> jaxbQuery = new JAXBElement<QueryType>(cswQnameOutPutSchema, QueryType.class, query);

        grr.setAbstractQuery(jaxbQuery);

        QueryImpl frameworkQuery = (QueryImpl) queryFactory.getQuery(grr).getQuery();
        return frameworkQuery.getFilter();
    }

    @SuppressWarnings("unchecked")
    private <N extends BinaryTemporalOperator> void cqlTemporalQuery(String expectedAttr,
            String cqlSpatialDwithinQuery, Class<N>[] classes)
            throws UnsupportedQueryException, SourceUnavailableException, FederationException, CswException {
        GetRecordsType grr = createDefaultPostRecordsRequest();

        QueryType query = new QueryType();
        List<QName> typeNames = new ArrayList<QName>();
        typeNames.add(new QName(CswConstants.CSW_OUTPUT_SCHEMA, VALID_TYPE, VALID_PREFIX));
        query.setTypeNames(typeNames);
        QueryConstraintType constraint = new QueryConstraintType();
        constraint.setCqlText(cqlSpatialDwithinQuery);

        query.setConstraint(constraint);
        JAXBElement<QueryType> jaxbQuery = new JAXBElement<QueryType>(cswQnameOutPutSchema, QueryType.class, query);

        grr.setAbstractQuery(jaxbQuery);

        QueryImpl frameworkQuery = (QueryImpl) queryFactory.getQuery(grr).getQuery();
        N temporal = null;
        if (classes.length > 1) {
            assertThat(frameworkQuery.getFilter(), instanceOf(Or.class));
            int i = 0;
            for (Filter filter : ((Or) frameworkQuery.getFilter()).getChildren()) {
                assertThat(filter, instanceOf(classes[i++]));
                temporal = (N) filter;
            }
        } else {
            assertThat(frameworkQuery.getFilter(), instanceOf(classes[0]));
            temporal = (N) frameworkQuery.getFilter();
        }
        assertThat(((AttributeExpressionImpl) temporal.getExpression1()).getPropertyName(), is(expectedAttr));
    }

    /**
     * Creates default GetRecordsRequest GET request, with no sections specified
     *
     * @return Vanilla valid GetRecordsRequest object
     */
    private GetRecordsRequest createDefaultGetRecordsRequest() {
        GetRecordsRequest grr = new GetRecordsRequest();
        grr.setService(CswConstants.CSW);
        grr.setVersion(CswConstants.VERSION_2_0_2);
        grr.setRequest(CswConstants.GET_RECORDS);
        grr.setNamespace(CswConstants.XMLNS_DEFINITION_PREFIX + CswConstants.CSW_NAMESPACE_PREFIX
                + CswConstants.EQUALS + CswConstants.CSW_OUTPUT_SCHEMA + CswConstants.XMLNS_DEFINITION_POSTFIX
                + CswConstants.COMMA

                + CswConstants.XMLNS_DEFINITION_PREFIX + CswConstants.OGC_NAMESPACE_PREFIX + CswConstants.EQUALS
                + CswConstants.OGC_SCHEMA + CswConstants.XMLNS_DEFINITION_POSTFIX + CswConstants.COMMA

                + CswConstants.XMLNS_DEFINITION_PREFIX + CswConstants.GML_NAMESPACE_PREFIX + CswConstants.EQUALS
                + CswConstants.GML_SCHEMA + CswConstants.XMLNS_DEFINITION_POSTFIX + CswConstants.COMMA);

        grr.setOutputSchema(CswConstants.CSW_OUTPUT_SCHEMA);
        grr.setOutputFormat(CswConstants.OUTPUT_FORMAT_XML);
        grr.setTypeNames(VALID_TYPES);
        return grr;
    }

    /**
     * Creates default GetRecordsType POST request, with no sections specified
     *
     * @return Vanilla valid GetRecordsType object
     */
    private GetRecordsType createDefaultPostRecordsRequest() {
        GetRecordsType grr = new GetRecordsType();

        grr.setOutputFormat(CswConstants.OUTPUT_FORMAT_XML);
        grr.setOutputSchema(CswConstants.CSW_OUTPUT_SCHEMA);

        QueryType query = new QueryType();
        List<QName> typeNames = new ArrayList<QName>();
        typeNames.add(new QName(CswConstants.CSW_OUTPUT_SCHEMA, VALID_TYPE, VALID_PREFIX));

        query.setTypeNames(typeNames);

        JAXBElement<QueryType> jaxbQuery = new JAXBElement<QueryType>(cswQnameOutPutSchema, QueryType.class, query);
        grr.setAbstractQuery(jaxbQuery);
        return grr;
    }

}