/* * 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. *

* * @return the max features the getFeature call should return. */ public int getMaxFeatures() { return this.maxFeatures; } /** * Sets the max features to retrieved by this query. * * @param maxFeatures the maximum number of features the getFeature call * should return. */ public void setMaxFeatures(int maxFeatures) { this.maxFeatures = maxFeatures; } /** * The Filter can be used to define constraints on a query. If no Filter * is present then the query is unconstrained and all feature instances * should be retrieved. * * @return The filter that defines constraints on the query. */ public Filter getFilter() { return this.filter; } /** * Sets the filter to constrain the query. * * @param filter the OGC filter to limit the datasource getFeatures * request. */ public void setFilter(Filter filter) { this.filter = filter; } /** * The typeName attribute is used to indicate the name of the feature type * to be queried. * *

* 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 * * @return true 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; } }