org.geotools.data.Query Maven / Gradle / Ivy
Show all versions of gt-main Show documentation
/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2008-2015, Open Source Geospatial Foundation (OSGeo)
*
* 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;
* version 2.1 of the License.
*
* 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.
*/
package org.geotools.data;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.util.factory.Hints;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory;
import org.opengis.filter.expression.PropertyName;
import org.opengis.filter.identity.ResourceId;
import org.opengis.filter.identity.Version;
import org.opengis.filter.sort.SortBy;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
/**
* Encapsulates a request for data, typically as:
*
*
* Query query = ...
* myFeatureSource.getFeatures(query);
*
*
* The query class is based on the Web Feature Server specification and offers a few interesting
* capabilities such as the ability to sort results and use a filter (similar to the WHERE clause in
* SQL).
*
* Additional capabilities:
*
*
* - {@linkplain #setMaxFeatures(int)} and {@linkplain #setStartIndex(Integer)} can be used
* implement 'paging' through the data source's content. This is useful if, for example, the
* FeatureSource has an upper limit on the number of features it can return in a single
* request or you are working with limited memory.
*
- {@linkplain #setHandle(String)} can be used to give the query a mnemonic name which will
* appear in error reporing and logs.
*
- {@linkplain #setCoordinateSystem(CoordinateReferenceSystem)} is used to to specify the
* coordinate system that retrieved features will be "forced" into. This is often used to
* correct a feature source when the application and the data format have different ideas
* about the coordinate system (for example, the "axis order" issue).
*
- {@linkplain #setCoordinateSystemReproject(CoordinateReferenceSystem)} is used to ask for
* the retrieved features to be reproejcted.
*
*
* Vendor specific:
*
*
* - {@linkplain setHints(Hints)} is used to specify venfor specific capabilities provided by a
* feature source implementation.
*
*
* Joins:
*
* The Query class supports the concepts of joins in that a query can result in a join of the
* feature type to other feature types in the same datastore. For example, the following would be a
* spatial join that selected the country that contain a particular city.
*
*
* Query query = new Query("countries");
* Join join = new Join("cities", CQL.toFilter("CONTAINS(geometry, b.geometry)"));
* join.setAlias("b");
* join.setFilter(CQL.toFilter("CITY_NAME = 'Canmore'"))
* query.getJoins().add(join);
*
*
* Example:
*
*
* Filter filter = CQL.toFilter("NAME like '%land'");
* Query query = new Query( "countries", filter );
*
* FeatureCollection features = featureSource.getFeatures( query );
*
*
* @author Chris Holmes
* @version $Id$
*/
public class Query {
/**
* When specifying properties to select, setting this hint flag true tells the datastore to
* include mandatory properties (i.e. properties with minOccurs >= 1) in the end result,
* irrespective of whether they are not included in the list of properties.
*
* Datastores may implement adding all mandatory properties to the end result when this flag
* is set to true. For example:
*
*
Object includeProps = query.getHints().get(Query.INCLUDE_MANDATORY_PROPS); if
* (includeProps instanceof Boolean && ((Boolean)includeProps).booleanValue()) {
* query.setProperties (DataUtilities.addMandatoryProperties(type, query.getProperties())); }
*/
public static Hints.Key INCLUDE_MANDATORY_PROPS = new Hints.Key(Boolean.class);
/**
* Constant (actually null) used to represent no namespace restrictions on the returned result,
* should be considered ANY_URI
*/
public static final URI NO_NAMESPACE = null;
/** So getMaxFeatures does not return null we use a very large number. */
public static final int DEFAULT_MAX = Integer.MAX_VALUE;
/**
* Implements a query that will fetch all features from a datasource. This query should retrieve
* all properties, with no maxFeatures, no filtering, and the default featureType.
*/
public static final Query ALL = new ALLQuery();
/**
* Implements a query that will fetch all the FeatureIDs from a datasource. This query should
* retrieve no properties, with no maxFeatures, no filtering, and the a featureType with no
* attribtues.
*/
public static final Query FIDS = new FIDSQuery();
/**
* A constant (empty String array) that can be used with {@linkplain
* #setPropertyNames(String[])} to indicate that no properties are to be retrieved.
*
*
Note the query will still return a result - limited to FeatureIDs.
*/
public static final String[] NO_NAMES = new String[0];
/**
* A constant (value {@code null}) that can be used with {@linkplain
* #setPropertyNames(String[])} to indicate that all properties are to be retrieved.
*/
public static final String[] ALL_NAMES = null;
/**
* A constant (empty String array) that can be used with {@linkplain
* #setProperties(Collection)} to indicate that no properties are to be retrieved.
*
* Note the query will still return a result - limited to FeatureIDs.
*/
public static final List NO_PROPERTIES = Collections.emptyList();
/**
* A constant (value {@code null}) that can be used with {@linkplain
* #setProperties(Collection)} to indicate that all properties are to be
* retrieved.
*/
public static final List ALL_PROPERTIES = null;
/** The properties to fetch */
protected List properties;
/** The maximum numbers of features to fetch */
protected int maxFeatures = Query.DEFAULT_MAX;
/** The index of the first feature to process */
protected Integer startIndex = null;
/** The filter to constrain the request. */
protected Filter filter = Filter.INCLUDE;
/** The typeName to get */
protected String typeName;
/** The optional alias for type name */
protected String alias;
/** The namespace to get */
protected URI namespace = Query.NO_NAMESPACE;
/** The handle associated with this query. */
protected String handle;
/** Coordinate System associated with this query */
protected CoordinateReferenceSystem coordinateSystem;
/** Reprojection associated associated with this query */
protected CoordinateReferenceSystem coordinateSystemReproject;
/** Sorting for the query */
protected SortBy[] sortBy;
/** The version according to WFS 1.0 and 1.1 specs */
protected String version;
/** The hints to be used during query execution */
protected Hints hints;
/** join clauses for this query */
protected List joins = new ArrayList();
/**
* Default constructor. Use setter methods to configure the Query before use (the default Query
* will retrieve all features).
*/
public Query() {
// no arg
}
/**
* Constructor.
*
* @param typeName the name of the featureType to retrieve
*/
public Query(String typeName) {
this(typeName, Filter.INCLUDE);
}
/**
* Constructor.
*
* @param typeName the name of the featureType to retrieve.
* @param filter the OGC filter to constrain the request.
*/
public Query(String typeName, Filter filter) {
this(typeName, filter, Query.ALL_NAMES);
}
/**
* Constructor.
*
* @param typeName the name of the featureType to retrieve.
* @param filter the OGC filter to constrain the request.
* @param properties an array of the properties to fetch.
*/
public Query(String typeName, Filter filter, String[] properties) {
this(typeName, null, filter, Query.DEFAULT_MAX, properties, null);
}
/**
* Constructor.
*
* @param typeName the name of the featureType to retrieve.
* @param filter the OGC filter to constrain the request.
* @param properties a list of the properties to fetch.
*/
public Query(String typeName, Filter filter, List properties) {
this(typeName, null, filter, Query.DEFAULT_MAX, properties, null);
}
/**
* Constructor.
*
* @param typeName the name of the featureType to retrieve.
* @param filter the OGC filter to constrain the request.
* @param maxFeatures the maximum number of features to be returned.
* @param propNames an array of the properties to fetch.
* @param handle the name to associate with this query.
*/
public Query(
String typeName, Filter filter, int maxFeatures, String[] propNames, String handle) {
this(typeName, null, filter, maxFeatures, propNames, handle);
}
/**
* Constructor.
*
* @param typeName the name of the featureType to retrieve.
* @param filter the OGC filter to constrain the request.
* @param maxFeatures the maximum number of features to be returned.
* @param properties a list of the properties to fetch.
* @param handle the name to associate with this query.
*/
public Query(
String typeName,
Filter filter,
int maxFeatures,
List properties,
String handle) {
this(typeName, null, filter, maxFeatures, properties, handle);
}
/**
* Constructor.
*
* @param typeName the name of the featureType to retrieve.
* @param namespace Namespace for provided typeName, or null if unspecified
* @param filter the OGC filter to constrain the request.
* @param maxFeatures the maximum number of features to be returned.
* @param propNames an array of the properties to fetch.
* @param handle the name to associate with the query.
*/
public Query(
String typeName,
URI namespace,
Filter filter,
int maxFeatures,
String[] propNames,
String handle) {
this.typeName = typeName;
this.filter = filter;
this.namespace = namespace;
this.maxFeatures = maxFeatures;
this.handle = handle;
setPropertyNames(propNames);
}
/**
* Constructor.
*
* @param typeName the name of the featureType to retrieve.
* @param namespace Namespace for provided typeName, or null if unspecified
* @param filter the OGC filter to constrain the request.
* @param maxFeatures the maximum number of features to be returned.
* @param properties a list of the property names to fetch.
* @param handle the name to associate with the query.
*/
public Query(
String typeName,
URI namespace,
Filter filter,
int maxFeatures,
List properties,
String handle) {
this.typeName = typeName;
this.filter = filter;
this.namespace = namespace;
this.maxFeatures = maxFeatures;
this.handle = handle;
this.properties = properties == null ? null : new ArrayList(properties);
}
/**
* Copy contructor.
*
* @param query the query to copy
*/
public Query(Query query) {
this(
query.getTypeName(),
query.getNamespace(),
query.getFilter(),
query.getMaxFeatures(),
query.getProperties(),
query.getHandle());
this.sortBy = query.getSortBy();
this.coordinateSystem = query.getCoordinateSystem();
this.coordinateSystemReproject = query.getCoordinateSystemReproject();
this.version = query.getVersion();
this.hints = query.getHints();
this.startIndex = query.getStartIndex();
this.alias = query.getAlias();
this.joins = new ArrayList();
for (Join j : query.getJoins()) {
this.joins.add(new Join(j));
}
}
/**
* Get the names of the properties that this Query will retrieve as part of the returned
* {@linkplain org.geotools.feature.FeatureCollection}.
*
* @return the attributes to be used in the returned FeatureCollection.
* @see #retrieveAllProperties()
* @task REVISIT: make a FidProperties object, instead of an array size 0. I think Query.FIDS
* fills this role to some degree. Query.FIDS.equals( filter ) would meet this need?
*/
public String[] getPropertyNames() {
if (properties == null) {
return null;
}
String[] propertyNames = new String[properties.size()];
for (int i = 0; i < properties.size(); i++) {
PropertyName propertyName = properties.get(i);
if (propertyName != null) {
String xpath = propertyName.getPropertyName();
propertyNames[i] = xpath;
}
}
return propertyNames;
}
/**
* Set the names of the properties that this Query should retrieve as part of the returned
* {@linkplain org.geotools.feature.FeatureCollection}. As well as an array of names, the
* following constants can be used:
*
*
* - {@linkplain #ALL_NAMES} to retrieve all properties.
*
- {@linkplain #NO_NAMES} to indicate no properties are required, just feature IDs.
*
*
* The available properties can be determined with {@linkplain FeatureSource#getSchema()}. If
* properties that are not part of the source's schema are requested an exception will be
* thrown.
*
* @param propNames the names of the properties to retrieve or one of {@linkplain #ALL_NAMES} or
* {@linkplain #NO_NAMES}.
*/
public void setPropertyNames(String[] propNames) {
if (propNames == null) {
properties = ALL_PROPERTIES;
return;
}
final FilterFactory ff = CommonFactoryFinder.getFilterFactory(null);
properties = new ArrayList(propNames.length);
for (int i = 0; i < propNames.length; i++) {
String xpath = propNames[i];
if (xpath != null) {
properties.add(ff.property(xpath));
}
}
}
/**
* Get the names of the properties that this Query will retrieve values for as part of the
* returned {@linkplain org.geotools.feature.FeatureCollection}.
*
* @return the xpath expressions to be used in the returned FeatureCollection.
* @see #retrieveAllProperties()
*/
public List getProperties() {
if (properties == ALL_PROPERTIES) {
return ALL_PROPERTIES;
}
return Collections.unmodifiableList(properties);
}
/**
* Set the names of the properties that this Query should retrieve as part of the returned
* {@linkplain org.geotools.feature.FeatureCollection}. As well as an array of names, the
* following constants can be used:
*
*
* - {@linkplain #ALL_PROPERTIES} to retrieve all properties.
*
- {@linkplain #NO_PROPERTIES} to indicate no properties are required, just feature IDs.
*
*
* The available properties can be determined with {@linkplain FeatureSource#getSchema()}. If
* properties that are not part of the source's schema are requested an exception will be
* thrown.
*
* @param propNames the names of the properties to retrieve or one of {@linkplain
* #ALL_PROPERTIES} or {@linkplain #NO_PROPERTIES}.
*/
public void setProperties(List propNames) {
this.properties =
propNames == ALL_PROPERTIES
? ALL_PROPERTIES
: new ArrayList(propNames);
}
/**
* Set the names of the properties that this Query should retrieve as part of the returned
* {@linkplain org.geotools.feature.FeatureCollection}.
*
* The available properties can be determined with {@linkplain FeatureSource#getSchema()}. If
* properties that are not part of the source's schema are requested an exception will be
* thrown.
*
* @param propNames the names of the properties to retrieve or {@linkplain #ALL_NAMES}; an empty
* List can be passed in to indicate that only feature IDs should be retrieved
* @task REVISIT: This syntax is really obscure. Consider having an fid or featureID
* propertyName that datasource implementors look for instead of looking to see if the list
* size is 0.
*/
public void setPropertyNames(List propNames) {
if (propNames == null) {
this.properties = ALL_PROPERTIES;
return;
}
final FilterFactory ff = CommonFactoryFinder.getFilterFactory(null);
properties = new ArrayList(propNames.size());
for (int i = 0; i < propNames.size(); i++) {
String xpath = propNames.get(i);
if (xpath != null) {
properties.add(ff.property(xpath));
}
}
}
/**
* Convenience method to determine if the query should retrieve all properties defined in the
* schema of the feature data source. This is equivalent to testing if {@linkplain
* #getPropertyNames()} returns {@linkplain #ALL_NAMES}.
*
* @return true if all properties will be retrieved by this Query; false otherwise
*/
public boolean retrieveAllProperties() {
return properties == null;
}
/**
* Get the maximum number of features that will be retrieved by this Query.
*
* Note: This is the only method that is not directly out of the Query element in the WFS
* specification. It is instead a part of a GetFeature request, which can hold one or more
* queries. But each of those in turn will need a maxFeatures, so it is needed here.
*
*
If the value returned here is max integer then the number of features should not be
* limited.
*
* @return the maximum number of features that will be retrieved by this query
*/
public int getMaxFeatures() {
return this.maxFeatures;
}
/**
* Check if this query allows an unlimited number of features to be returned.
*
*
*
* @return true maxFeatures is less then zero, or equal to Integer.MAX_VALUE.
*/
public boolean isMaxFeaturesUnlimited() {
return maxFeatures < 0 || maxFeatures == Integer.MAX_VALUE;
}
/**
* Sets the maximum number of features that should be retrieved by this query. The default is to
* retrieve all features.
*
* @param maxFeatures the maximum number of features to retrieve
*/
public void setMaxFeatures(int maxFeatures) {
this.maxFeatures = maxFeatures;
}
/**
* Get the index of the first feature to retrieve.
*
* @return the index of the first feature to retrieve or {@code null} if no start index is
* defined.
*/
public Integer getStartIndex() {
return this.startIndex;
}
/**
* Set the index of the first feature to retrieve. This can be used in conjuction with
* {@linkplain #setMaxFeatures(int) } to 'page' through a feature data source.
*
* @param startIndex index of the first feature to retrieve or {@code null} to indicate no start
* index
* @throws IllegalArgumentException if startIndex is less than 0
*/
public void setStartIndex(Integer startIndex) {
if (startIndex != null && startIndex.intValue() < 0) {
throw new IllegalArgumentException(
"startIndex shall be a positive integer: " + startIndex);
}
this.startIndex = startIndex;
}
/**
* Gets the filter used to define constraints on the features that will be retrieved by this
* Query.
*
* @return The filter that defines constraints on the query.
*/
public Filter getFilter() {
return this.filter;
}
/**
* Sets the filter to constrain the features that will be retrieved by this Query. If no filter
* is set all features will be retrieved (taking into account any bounds set via {@linkplain
* #setMaxFeatures(int) } and {@linkplain #setStartIndex(java.lang.Integer) }).
*
*
The default is {@linkplain Filter#INCLUDE}.
*
* @param filter the OGC filter which features must pass through to be retrieved by this Query.
*/
public void setFilter(Filter filter) {
this.filter = filter;
}
/**
* Get the name of the feature type to be queried.
*
* @return the name of the feature type to be returned with this query.
*/
public String getTypeName() {
return this.typeName;
}
/**
* Sets the name of the feature type to be queried. If no typename is specified, then the data
* source's default type will be used. When working with sources such as shapefiles that only
* support one feature type this method can be ignored.
*
* @param typeName the name of the featureType to retrieve.
*/
public void setTypeName(String typeName) {
this.typeName = typeName;
}
/**
* An alias substitutable for {@link #getTypeName()}.
*
*
This value is typically used in a join query in which the join filter requires
* disambiguation due to property name overlaps between joined types.
*
* @since 8.0
*/
public String getAlias() {
return alias;
}
/**
* Sets the type name alias.
*
* @since 8.0
* @see #getAlias()
*/
public void setAlias(String alias) {
this.alias = alias;
}
/**
* Get the namespace of the feature type to be queried.
*
* @return the gml namespace of the feature type to be returned with this query
*/
public URI getNamespace() {
return namespace;
}
/** Set the namespace of the feature type to be queried. */
public void setNamespace(URI namespace) {
this.namespace = namespace;
}
/**
* Get the handle (mnemonic name) that will be associated with this Query. The handle is used in
* logging and error reporting.
*
* @return the name to refer to this query.
*/
public String getHandle() {
return this.handle;
}
/**
* Set the handle (mnemonic name) that will be associated with this Query. The handle is used in
* logging and error reporting.
*
* @param handle the name to refer to this query.
*/
public void setHandle(String handle) {
this.handle = handle;
}
/**
* Defines version or version range requested.
*
*
* From WFS Spec: The version attribute is included in order to
* accommodate systems that support feature versioning. A value of {@linkplain #ALL}
* indicates that all versions of a feature should be fetched. Otherwise
* an integer, n, can be specified to return the n th version of a
* feature. The version numbers start at '1' which is the oldest version.
* If a version value larger than the largest version is specified then
* the latest version is return. The default action shall be for the query
* to return the latest version. Systems that do not support versioning
* can ignore the parameter and return the only version that they have.
*
* GeoTools employs the following options:
*
* - {@link #setVersion(Date)}: "date: dow mon dd hh:mm:ss zzz yyyy"
* - {@link #setVersion(int)}: "index"
* - {@link #setVersion(org.opengis.filter.identity.Version.Action)): "PREVIOUS", "LAST", "NEXT", "FIRST", "ALL"
* - {@link #setVersion(Date, Date): "start: dow mon dd hh:mm:ss zzz yyyy end: dow mon dd hh:mm:ss zzz yyyy"
*
* @return the version of the feature to return, or null
for LAST.
*/
public String getVersion() {
return version;
}
public void setVersion(int index) {
this.version = String.valueOf(index);
}
public void setVersion(Date date) {
this.version = date == null ? null : "date:" + date;
}
public void setVersion(Version.Action action) {
this.version = action == null ? null : action.name();
}
public void setVersion(Date startTime, Date endTime) {
if (startTime == null || endTime == null) {
this.version = null;
} else {
this.version = "start:" + startTime + " end:" + endTime;
}
}
public void setVersion(ResourceId history) {
if (history.getStartTime() != null && history.getEndTime() != null) {
setVersion(history.getStartTime(), history.getEndTime());
} else if (history.getVersion() != null) {
Version ver = history.getVersion();
if (ver.isVersionAction()) {
setVersion(ver.getVersionAction());
} else if (ver.isDateTime()) {
setVersion(ver.getDateTime());
} else if (ver.isIndex()) {
setVersion(ver.getIndex());
}
}
}
/**
* Set the version of features to retrieve where this is supported by the data source being
* queried.
*
* @see #getVersion() getVersion() for explanation
* @since 2.4
*/
public void setVersion(String version) {
this.version = version;
}
/**
* Get the coordinate system to use as an override for features retrieved by this Query.
*
* @return The coordinate system to be returned for Features from this Query (override the set
* coordinate system).
*/
public CoordinateReferenceSystem getCoordinateSystem() {
return coordinateSystem;
}
/**
* Provide an override coordinate system to apply to features retrieved by this Query.
*
* This denotes a request to temporarily override the coordinate system contained in
* the feature data source being queried. The same coordinate values will be used, but the
* features retrieved will appear in this Coordinate System.
*
*
This change is not persistent and only applies to the features returned by this Query. If
* used in conjunction with {@link #getCoordinateSystemReproject()} the reprojection will occur
* from {@link #getCoordinateSystem()} to {@link #getCoordinateSystemReproject()}.
*
* @param system the coordinate system to apply to features retrieved by this Query
*/
public void setCoordinateSystem(CoordinateReferenceSystem system) {
coordinateSystem = system;
}
/**
* If reprojection has been requested, this returns the coordinate system that features
* retrieved by this Query will be reprojected into.
*
* @return the coordinate system that features will be reprojected into (if set)
* @see #setCoordinateSystemReproject( CoordinateReferenceSystem )
*/
public CoordinateReferenceSystem getCoordinateSystemReproject() {
return coordinateSystemReproject;
}
/**
* Request that features retrieved by this Query be reprojected into the given coordinate
* system.
*
*
If used in conjunction with {@link #setCoordinateSystem(CoordinateReferenceSystem)} the
* reprojection will occur from the overridden coordinate system to the system specified here.
*/
public void setCoordinateSystemReproject(CoordinateReferenceSystem system) {
coordinateSystemReproject = system;
}
/**
* SortBy results according to indicated property and order.
*
*
SortBy is part of the Filter 1.1 specification, it is referenced by WFS1.1 and Catalog
* 2.0.x specifications and is used to organize results. The SortBy's are ment to be applied in
* order:
*
*
* - SortBy( year, ascending )
*
- SortBy( month, decsending )
*
*
* Would produce something like:
*
*
* [year=2002 month=4],[year=2002 month=3],[year=2002 month=2],
* [year=2002 month=1],[year=2003 month=12],[year=2002 month=4],
*
*
* SortBy should be considered at the same level of abstraction as Filter, and like Filter
* you may sort using properties not listed in getPropertyNames.
*
*
At a technical level the interface SortBy2 is used to indicate the additional requirements
* of a GeoTools implementation. The pure WFS 1.1 specification itself is limited to SortBy.
*
* @return SortBy array or order of application
*/
public SortBy[] getSortBy() {
return sortBy;
}
/** Sets the sort by information. */
public void setSortBy(SortBy[] sortBy) {
this.sortBy = sortBy;
}
/**
* Get hints that have been set to control the query execution.
*
* @return hints that are set (may be empty)
* @see #setHints(Hints) setHints(Hints) for more explanation
*/
public Hints getHints() {
if (hints == null) {
hints = new Hints();
}
return hints;
}
/**
* Set hints to control the query execution.
*
*
Hints can control such things as:
*
*
* - the GeometryFactory to be used
*
- a generalization distance to be applied
*
- the fetch size to be used in JDBC queries
*
*
* The set of hints supported can be found by calling {@linkplain
* org.geotools.data.FeatureSource#getSupportedHints() }.
*
* Note: Data sources may ignore hints (depending on their values) and no mechanism currently
* exists to discover which hints where actually used during the query's execution.
*
* @see Hints#FEATURE_DETACHED
* @see Hints#JTS_GEOMETRY_FACTORY
* @see Hints#JTS_COORDINATE_SEQUENCE_FACTORY
* @see Hints#JTS_PRECISION_MODEL
* @see Hints#JTS_SRID
* @see Hints#GEOMETRY_DISTANCE
* @see Hints#FEATURE_2D
* @param hints the hints to apply
*/
public void setHints(Hints hints) {
this.hints = hints;
}
/**
* Hashcode based on all parameters other than the handle.
*
* @return hascode for this Query
*/
@Override
public int hashCode() {
String[] n = getPropertyNames();
return ((n == null) ? (-1) : ((n.length == 0) ? 0 : (n.length | n[0].hashCode())))
| getMaxFeatures()
| ((getFilter() == null) ? 0 : getFilter().hashCode())
| ((getTypeName() == null) ? 0 : getTypeName().hashCode())
| ((getVersion() == null) ? 0 : getVersion().hashCode())
| ((getCoordinateSystem() == null) ? 0 : getCoordinateSystem().hashCode())
| ((getCoordinateSystemReproject() == null)
? 0
: getCoordinateSystemReproject().hashCode())
| getStartIndex();
}
/**
* Equality based on all query parameters other than the handle.
*
* @param obj Other object to compare against
* @return true if this Query matches the other object; false otherwise
*/
@Override
public boolean equals(Object obj) {
if ((obj == null) || !(obj instanceof Query)) {
return false;
}
if (this == obj) return true;
Query other = (Query) obj;
return Arrays.equals(getPropertyNames(), other.getPropertyNames())
&& (retrieveAllProperties() == other.retrieveAllProperties())
&& (getMaxFeatures() == other.getMaxFeatures())
&& ((getFilter() == null)
? (other.getFilter() == null)
: getFilter().equals(other.getFilter()))
&& ((getTypeName() == null)
? (other.getTypeName() == null)
: getTypeName().equals(other.getTypeName()))
&& ((getVersion() == null)
? (other.getVersion() == null)
: getVersion().equals(other.getVersion()))
&& ((getCoordinateSystem() == null)
? (other.getCoordinateSystem() == null)
: getCoordinateSystem().equals(other.getCoordinateSystem()))
&& ((getCoordinateSystemReproject() == null)
? (other.getCoordinateSystemReproject() == null)
: getCoordinateSystemReproject()
.equals(other.getCoordinateSystemReproject()))
&& isSortEquals(other)
&& Objects.equals(getStartIndex(), other.getStartIndex())
&& (getHints() == null
? (other.getHints() == null)
: getHints().equals(other.getHints()));
}
/** Compares the sortby by their effect (null and empty arrays are considered the same) */
private boolean isSortEquals(Query other) {
if (this.sortBy == null || this.sortBy.length == 0) {
return other.getSortBy() == null || other.getSortBy().length == 0;
}
return Arrays.equals(getSortBy(), other.getSortBy());
}
/**
* Return a string representation of this Query.
*
* @return a string representation of this Query
*/
@Override
public String toString() {
StringBuffer returnString = new StringBuffer("Query:");
if (handle != null) {
returnString.append(" [" + handle + "]");
}
returnString.append("\n feature type: " + typeName);
if (filter != null) {
returnString.append("\n filter: " + filter.toString());
}
returnString.append("\n [properties: ");
if ((properties == null) || (properties.size() == 0)) {
returnString.append(" ALL ]");
} else {
for (int i = 0; i < properties.size(); i++) {
returnString.append(properties.get(i));
if (i < (properties.size() - 1)) {
returnString.append(", ");
}
}
returnString.append("]");
}
if (sortBy != null && sortBy.length > 0) {
returnString.append("\n [sort by: ");
for (int i = 0; i < sortBy.length; i++) {
SortBy sb = sortBy[i];
if (sb == SortBy.NATURAL_ORDER) {
returnString.append("NATURAL");
} else if (sb == SortBy.REVERSE_ORDER) {
returnString.append("REVERSE");
} else {
returnString.append(sb.getPropertyName().getPropertyName());
returnString.append(" ");
returnString.append(sb.getSortOrder().name());
}
if (i < (sortBy.length - 1)) {
returnString.append(", ");
}
}
returnString.append("]");
}
return returnString.toString();
}
/**
* The list of joins for this query.
*
*
Each {@link Join} object specifies a feature type to join to. The join may only reference
* a feature type from within the same datastore.
*
* @see Join
*/
public List getJoins() {
return joins;
}
}