/*
* Geotools2 - OpenSource mapping toolkit
* http://geotools.org
* (C) 2002, Geotools Project Managment Committee (PMC)
*
* 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 org.geotools.filter.Filter;
import org.opengis.sc.CoordinateReferenceSystem;
import java.util.Arrays;
import java.util.List;
/**
* The query object is used by the {@link DataSource#getFeatures()} method of
* the DataSource interface, to encapsulate a request. It defines which
* feature type to query, what properties to retrieve and what constraints
* (spatial and non-spatial) to apply to those properties. It is designed to
* closesly match a WFS Query element of a getFeatures
request.
* The only difference is the addition of the maxFeatures element, which
* allows more control over the features selected. It allows a full
* getFeatures
request to properly control how many features it
* gets from each query, instead of requesting and discarding when the max is
* reached.
*
* @author Chris Holmes
*/
public class DefaultQuery implements Query {
/** The properties to fetch */
private String[] properties;
/** The maximum numbers of features to fetch */
private int maxFeatures = Query.DEFAULT_MAX;
/** The filter to constrain the request. */
private Filter filter = Filter.NONE;
/** The typeName to get */
private String typeName;
/** The handle associated with this query. */
private String handle;
/** Coordinate System associated with this query */
private CoordinateReferenceSystem coordinateSystem;
/** Reprojection associated associated with this query */
private CoordinateReferenceSystem coordinateSystemReproject;
/**
* No argument constructor.
*/
public DefaultQuery() {
}
/**
* Query with typeName.
*
*
* @param typeName the name of the featureType to retrieve */ public DefaultQuery( String typeName ){ this( typeName, Filter.NONE ); } /** * Constructor that sets the filter. ** Using a Query with only a filter, and no typeName, almost always * results in an error. *
** Please use DefaulQuery( typeName, filter ) *
* @param filter the OGC filter to constrain the request. * @deprecated Please use DefaultQuery( typeName, filter ) instead. */ public DefaultQuery(Filter filter) { this.filter = filter; } /** * Constructor with typeName and filter. Note that current datasource * implementations only have one type per datasource, so the typeName * field will likely be ignored. * * @param typeName the name of the featureType to retrieve. * @param filter the OGC filter to constrain the request. */ public DefaultQuery(String typeName, Filter filter) { this(filter); this.typeName = typeName; } /** * Constructor that sets the filter and properties * * @param filter the OGC filter to constrain the request. * @param properties an array of the properties to fetch. * * @deprecated Use a constructor with the typeName, as datastores depend on * it. */ public DefaultQuery(Filter filter, String[] properties) { this(filter); this.properties = properties; } /** * Constructor that sets all fields. * * @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 the query. */ public DefaultQuery(String typeName, Filter filter, int maxFeatures, String[] propNames, String handle) { this(typeName, filter); this.properties = propNames; this.maxFeatures = maxFeatures; this.handle = handle; } /** * The property names is used to specify the attributes that should be * selected for the return feature collection. If the property array is * null, then the datasource should return all available properties, its * full schema. If an array of specified then the full schema should be * used (all property names). The property names can be determined with a * getSchema call from the DataSource interface. * ** This replaces our funky setSchema method of retrieving select * properties. I think it makes it easier to understand how to get * certain properties out of the datasource, instead of having users get * the schema and then compose a new schema using the attributes that * they want. The old way was also giving me problems because I couldn't * have multiple object reuse the same datasource object, since some other * object could come along and change its schema, and would then return * the wrong properties. *
* * * * @return the property names to be used in the returned FeatureCollection. */ public String[] getPropertyNames() { return properties; } /** * Sets the properties to retrieve from the db. If the boolean to load all * properties is set to true then the AttributeTypes that are not in the * database's schema will just be filled with null values. * * @param propNames The names of attributes to load from the datasouce. */ public void setPropertyNames(String[] propNames) { this.properties = propNames; } /** * Sets the proper attributeTypes constructed from a schema and a list of * propertyNames. * * @param propNames the names of the properties to check against the * schema. If null then all attributes will be returned. If a List * of size 0 is used then only the featureIDs should be used. * * @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 = null; return; } String[] stringArr = new String[propNames.size()]; this.properties = (String[]) propNames.toArray(stringArr); } /** * Convenience method to determine if the query should use the full schema * (all properties) of the data source for the features returned. This * method is equivalent to if (query.getProperties() == null), but allows * for more clarity on the part of datasource implementors, so they do not * need to examine and use null values. All Query implementations should * return true for this function if getProperties returns null. * * @return if all datasource attributes should be included in the schema * of the returned FeatureCollection. */ public boolean retrieveAllProperties() { return properties == null; } /** * The optional maxFeatures can be used to limit the number of features * that a query request retrieves. If no maxFeatures is specified then * all features should be returned. * *
* This is the only method that is not directly out of the Query element in
* the WFS spec. It is instead a part of a getFeatures
* request, which can hold one or more queries. But each of those in turn
* will need a maxFeatures, so it is needed here.
*
* All our datasources now assume one feature type per datasource, but it * doesn't seem like we should constrain ourselves to that. This field * will allow us to create a postgis datasource that can make use of the * whole db, specifying with each request which type to get. *
* ** Geotools currently limits datasources to a one to one relationship * between featureType and datasource, so datasources can ignore this * field of the query for now. *
* ** Update for DataStore *
** The DataStore API does not assume one feature type per datastore. * It currently makes use of this field to to specify with each request * what type to get. *
* @return the name of the feature type to be returned with this query. * * @task REVISIT: the transaction methods do not work with different * typenames, so that will have to be resolved before typenames are * used. Perhaps a MultiType interface? We will also need to * rethink the getSchema, and probably schemas in general, as they * could be more than one FeatureType in datasources that support * multiple types. If users wish to use more than one type in a * datasource whose backend naturally supports multiple types (like * postgis) they should just construct a datasource for each type, * generally on different connections so as to avoid * getFeature/multi-transaction confusion. */ public String getTypeName() { return this.typeName; } /** * Sets the typename. * * @param typeName the name of the featureType to retrieve. */ public void setTypeName(String typeName) { this.typeName = typeName; } /** * The handle attribute is included to allow a client to associate a * mnemonic name to the Query request. The purpose of the handle attribute * is to provide an error handling mechanism for locating a statement * that might fail. * * @return the mnemonic name of the query request. */ public String getHandle() { return this.handle; } /** * Sets a mnemonic name for the query request. * * @param handle the name to refer to this query. */ public void setHandle(String handle) { this.handle = handle; } /** * From WFS Spec: The version attribute is included in order to * accommodate systems that support feature versioning. A value of 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. * ** This will not be used for awhile, but at some future point geotools * should support feature versioning. Obviously none do now, nor are any * close to supporting it, so perhaps we should just wait and see. And of * course we'd need the corresponding supportsFeatureVersioning in the * datasource metadata object. *
* * @return the version of the feature to return. * * @throws UnsupportedOperationException if a user attempts to use this * method - no versioning supported yet. */ public String getVersion() throws UnsupportedOperationException { throw new UnsupportedOperationException("No feature versioning yet"); } /** * Hashcode based on propertyName, maxFeatures and filter. * * @return hascode for filter */ 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()) | ((getCoordianteSystem() == null) ? 0 : getCoordianteSystem().hashCode()) | ((getCoordianteSystemReproject() == null) ? 0 : getCoordianteSystemReproject().hashCode()); } /** * Equality based on propertyNames, maxFeatures, filter, typeName and * version. * ** Changing the handle does not change the meaning of the Query. *
* * @param obj Other object to compare against * * @returntrue
if obj
matches this filter
*/
public boolean equals(Object obj) {
if ((obj == null) || !(obj instanceof Query)) {
return false;
}
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()))
&& ((getCoordianteSystem() == null) ? (other.getCoordianteSystem() == null)
: getCoordianteSystem().equals(other.getCoordianteSystem()))
&& ((getCoordianteSystemReproject() == null) ? (other.getCoordianteSystemReproject() == null)
: getCoordianteSystemReproject().equals(other.getCoordianteSystemReproject()))
;
}
/**
* Over ride of toString
*
* @return a string representation of this query object.
*/
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.length == 0)) {
return returnString + " ALL ]";
} else {
for (int i = 0; i < properties.length; i++) {
returnString.append(properties[i]);
if (i < (properties.length - 1)) {
returnString.append(", ");
}
}
returnString.append("]");
return returnString.toString();
}
}
/**
* getCoordinateSystem purpose.
* * Description ... *
* @return */ public CoordinateReferenceSystem getCoordianteSystem() { return coordinateSystem; } /** * getCoordinateSystemReproject purpose. ** Description ... *
* @return */ public CoordinateReferenceSystem getCoordianteSystemReproject() { return coordinateSystemReproject; } /** * setCoordinateSystem purpose. ** Description ... *
* @param system */ public void setCoordianteSystem(CoordinateReferenceSystem system) { coordinateSystem = system; } /** * setCoordinateSystemReproject purpose. ** Description ... *
* @param system */ public void setCoordianteSystemReproject(CoordinateReferenceSystem system) { coordinateSystemReproject = system; } }