001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2003-2006, GeoTools Project Managment Committee (PMC)
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation;
009: * version 2.1 of the License.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: */
016: package org.geotools.data;
017:
018: import java.net.URI;
019: import java.util.Arrays;
020: import java.util.Collections;
021: import java.util.List;
022:
023: import org.geotools.factory.Hints;
024: import org.opengis.filter.Filter;
025: import org.opengis.filter.sort.SortBy;
026: import org.opengis.referencing.crs.CoordinateReferenceSystem;
027:
028: /**
029: * The query object is used by the {@link FeatureSource#getFeatures()} method of
030: * the DataSource interface, to encapsulate a request. It defines which
031: * feature type to query, what properties to retrieve and what constraints
032: * (spatial and non-spatial) to apply to those properties. It is designed to
033: * closesly match a WFS Query element of a <code>getFeatures</code> request.
034: * The only difference is the addition of the maxFeatures element, which
035: * allows more control over the features selected. It allows a full
036: * <code>getFeatures</code> request to properly control how many features it
037: * gets from each query, instead of requesting and discarding when the max is
038: * reached.
039: *
040: * @author Chris Holmes
041: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/main/src/main/java/org/geotools/data/DefaultQuery.java $
042: */
043: public class DefaultQuery implements Query {
044: /** The properties to fetch */
045: private String[] properties;
046:
047: /** The maximum numbers of features to fetch */
048: private int maxFeatures = Query.DEFAULT_MAX;
049:
050: /** The filter to constrain the request. */
051: private Filter filter = Filter.INCLUDE;
052:
053: /** The typeName to get */
054: private String typeName;
055:
056: /** The namespace to get */
057: private URI namespace = Query.NO_NAMESPACE;
058:
059: /** The handle associated with this query. */
060: private String handle;
061:
062: /** Coordinate System associated with this query */
063: private CoordinateReferenceSystem coordinateSystem;
064:
065: /** Reprojection associated associated with this query */
066: private CoordinateReferenceSystem coordinateSystemReproject;
067:
068: /** Sorting for the query */
069: private SortBy[] sortBy;
070:
071: /** The version according to WFS 1.0 and 1.1 specs */
072: private String version;
073:
074: /** The hints to be used during query execution */
075: private Hints hints;
076:
077: /**
078: /**
079: * No argument constructor.
080: */
081: public DefaultQuery() {
082: // no arg
083: }
084:
085: /**
086: * Query with typeName.
087: * <p>
088: * </p>
089: * @param typeName the name of the featureType to retrieve
090: */
091: public DefaultQuery(String typeName) {
092: this (typeName, Filter.INCLUDE);
093: }
094:
095: /**
096: * Constructor with typeName and filter. Note that current datasource
097: * implementations only have one type per datasource, so the typeName
098: * field will likely be ignored.
099: *
100: * @param typeName the name of the featureType to retrieve.
101: * @param filter the OGC filter to constrain the request.
102: */
103: public DefaultQuery(String typeName, Filter filter) {
104: this (typeName, filter, Query.ALL_NAMES);
105: }
106:
107: /**
108: * Constructor that sets the filter and properties
109: * @param typeName
110: *
111: * @param filter the OGC filter to constrain the request.
112: * @param properties an array of the properties to fetch.
113: */
114: public DefaultQuery(String typeName, Filter filter,
115: String[] properties) {
116: this (typeName, null, filter, Query.DEFAULT_MAX, properties,
117: null);
118: }
119:
120: /**
121: * Constructor that sets all fields.
122: *
123: * @param typeName the name of the featureType to retrieve.
124: * @param filter the OGC filter to constrain the request.
125: * @param maxFeatures the maximum number of features to be returned.
126: * @param propNames an array of the properties to fetch.
127: * @param handle the name to associate with the query.
128: */
129: public DefaultQuery(String typeName, Filter filter,
130: int maxFeatures, String[] propNames, String handle) {
131: this (typeName, null, filter, maxFeatures, propNames, handle);
132: }
133:
134: /**
135: * Constructor that sets all fields.
136: *
137: * @param typeName the name of the featureType to retrieve.
138: * @param namespace Namespace for provided typeName, or null if unspecified
139: * @param filter the OGC filter to constrain the request.
140: * @param maxFeatures the maximum number of features to be returned.
141: * @param propNames an array of the properties to fetch.
142: * @param handle the name to associate with the query.
143: */
144: public DefaultQuery(String typeName, URI namespace, Filter filter,
145: int maxFeatures, String[] propNames, String handle) {
146: this .typeName = typeName;
147: this .filter = filter;
148: this .namespace = namespace;
149: this .properties = propNames;
150: this .maxFeatures = maxFeatures;
151: this .handle = handle;
152: }
153:
154: /**
155: * Copy contructor, clones the state of a generic Query into a DefaultQuery
156: * @param query
157: */
158: public DefaultQuery(Query query) {
159: this (query.getTypeName(), query.getNamespace(), query
160: .getFilter(), query.getMaxFeatures(), query
161: .getPropertyNames(), query.getHandle());
162: this .sortBy = query.getSortBy();
163: this .coordinateSystem = query.getCoordinateSystem();
164: this .coordinateSystemReproject = query
165: .getCoordinateSystemReproject();
166: this .version = query.getVersion();
167: this .hints = query.getHints();
168: }
169:
170: /**
171: * The property names is used to specify the attributes that should be
172: * selected for the return feature collection. If the property array is
173: * null, then the datasource should return all available properties, its
174: * full schema. If an array of specified then the full schema should be
175: * used (all property names). The property names can be determined with a
176: * getSchema call from the DataSource interface.
177: *
178: * <p>
179: * This replaces our funky setSchema method of retrieving select
180: * properties. I think it makes it easier to understand how to get
181: * certain properties out of the datasource, instead of having users get
182: * the schema and then compose a new schema using the attributes that
183: * they want. The old way was also giving me problems because I couldn't
184: * have multiple object reuse the same datasource object, since some other
185: * object could come along and change its schema, and would then return
186: * the wrong properties.
187: * </p>
188: *
189: * <p></p>
190: *
191: * @return the property names to be used in the returned FeatureCollection.
192: */
193: public String[] getPropertyNames() {
194: return properties;
195: }
196:
197: /**
198: * Sets the properties to retrieve from the db. If the boolean to load all
199: * properties is set to true then the AttributeTypes that are not in the
200: * database's schema will just be filled with null values.
201: *
202: * @param propNames The names of attributes to load from the datasouce.
203: */
204: public void setPropertyNames(String[] propNames) {
205: this .properties = propNames;
206: }
207:
208: /**
209: * Sets the proper attributeTypes constructed from a schema and a list of
210: * propertyNames.
211: *
212: * @param propNames the names of the properties to check against the
213: * schema. If null then all attributes will be returned. If a List
214: * of size 0 is used then only the featureIDs should be used.
215: *
216: * @task REVISIT: This syntax is really obscure. Consider having an fid or
217: * featureID propertyName that datasource implementors look for
218: * instead of looking to see if the list size is 0.
219: */
220: public void setPropertyNames(List propNames) {
221: if (propNames == null) {
222: this .properties = null;
223:
224: return;
225: }
226:
227: String[] stringArr = new String[propNames.size()];
228: this .properties = (String[]) propNames.toArray(stringArr);
229: }
230:
231: /**
232: * Convenience method to determine if the query should use the full schema
233: * (all properties) of the data source for the features returned. This
234: * method is equivalent to if (query.getProperties() == null), but allows
235: * for more clarity on the part of datasource implementors, so they do not
236: * need to examine and use null values. All Query implementations should
237: * return true for this function if getProperties returns null.
238: *
239: * @return if all datasource attributes should be included in the schema
240: * of the returned FeatureCollection.
241: */
242: public boolean retrieveAllProperties() {
243: return properties == null;
244: }
245:
246: /**
247: * The optional maxFeatures can be used to limit the number of features
248: * that a query request retrieves. If no maxFeatures is specified then
249: * all features should be returned.
250: *
251: * <p>
252: * This is the only method that is not directly out of the Query element in
253: * the WFS spec. It is instead a part of a <code>getFeatures</code>
254: * request, which can hold one or more queries. But each of those in turn
255: * will need a maxFeatures, so it is needed here.
256: * </p>
257: *
258: * @return the max features the getFeature call should return.
259: */
260: public int getMaxFeatures() {
261: return this .maxFeatures;
262: }
263:
264: /**
265: * Sets the max features to retrieved by this query.
266: *
267: * @param maxFeatures the maximum number of features the getFeature call
268: * should return.
269: */
270: public void setMaxFeatures(int maxFeatures) {
271: this .maxFeatures = maxFeatures;
272: }
273:
274: /**
275: * The Filter can be used to define constraints on a query. If no Filter
276: * is present then the query is unconstrained and all feature instances
277: * should be retrieved.
278: *
279: * @return The filter that defines constraints on the query.
280: */
281: public Filter getFilter() {
282: return this .filter;
283: }
284:
285: /**
286: * Sets the filter to constrain the query.
287: *
288: * @param filter the OGC filter to limit the datasource getFeatures
289: * request.
290: */
291: public void setFilter(Filter filter) {
292: this .filter = filter;
293: }
294:
295: /**
296: * The typeName attribute is used to indicate the name of the feature type
297: * to be queried.
298: *
299: * <p>
300: * The DataStore API does not assume one feature type per datastore.
301: * It currently makes use of this field to to specify with each request
302: * what type to get.
303: * </p>
304: * @return the name of the feature type to be returned with this query.
305: */
306: public String getTypeName() {
307: return this .typeName;
308: }
309:
310: /* (non-Javadoc)
311: * @see org.geotools.data.Query#getNamespace()
312: */
313: public URI getNamespace() {
314: return namespace;
315: }
316:
317: /**
318: * Sets the typename.
319: *
320: * @param typeName the name of the featureType to retrieve.
321: */
322: public void setTypeName(String typeName) {
323: this .typeName = typeName;
324: }
325:
326: /**
327: * The handle attribute is included to allow a client to associate a
328: * mnemonic name to the Query request. The purpose of the handle attribute
329: * is to provide an error handling mechanism for locating a statement
330: * that might fail.
331: *
332: * @return the mnemonic name of the query request.
333: */
334: public String getHandle() {
335: return this .handle;
336: }
337:
338: /**
339: * Sets a mnemonic name for the query request.
340: *
341: * @param handle the name to refer to this query.
342: */
343: public void setHandle(String handle) {
344: this .handle = handle;
345: }
346:
347: /**
348: * From WFS Spec: The version attribute is included in order to
349: * accommodate systems that support feature versioning. A value of ALL
350: * indicates that all versions of a feature should be fetched. Otherwise
351: * an integer, n, can be specified to return the n th version of a
352: * feature. The version numbers start at '1' which is the oldest version.
353: * If a version value larger than the largest version is specified then
354: * the latest version is return. The default action shall be for the query
355: * to return the latest version. Systems that do not support versioning
356: * can ignore the parameter and return the only version that they have.
357: *
358: * <p>
359: * This is ready for use, it will be up to data store implementors to
360: * support it.
361: * </p>
362: *
363: * @return the version of the feature to return, or null for latest.
364: */
365: public String getVersion() {
366: return version;
367: }
368:
369: /**
370: * @see #getVersion()
371: * @param version
372: * @since 2.4
373: */
374: public void setVersion(String version) {
375: this .version = version;
376: }
377:
378: /**
379: * Hashcode based on propertyName, maxFeatures and filter.
380: *
381: * @return hascode for filter
382: */
383: public int hashCode() {
384: String[] n = getPropertyNames();
385:
386: return ((n == null) ? (-1) : ((n.length == 0) ? 0
387: : (n.length | n[0].hashCode())))
388: | getMaxFeatures()
389: | ((getFilter() == null) ? 0 : getFilter().hashCode())
390: | ((getTypeName() == null) ? 0 : getTypeName()
391: .hashCode())
392: | ((getVersion() == null) ? 0 : getVersion().hashCode())
393: | ((getCoordinateSystem() == null) ? 0
394: : getCoordinateSystem().hashCode())
395: | ((getCoordinateSystemReproject() == null) ? 0
396: : getCoordinateSystemReproject().hashCode());
397: }
398:
399: /**
400: * Equality based on propertyNames, maxFeatures, filter, typeName and
401: * version.
402: *
403: * <p>
404: * Changing the handle does not change the meaning of the Query.
405: * </p>
406: *
407: * @param obj Other object to compare against
408: *
409: * @return <code>true</code> if <code>obj</code> matches this filter
410: */
411: public boolean equals(Object obj) {
412: if ((obj == null) || !(obj instanceof Query)) {
413: return false;
414: }
415: if (this == obj)
416: return true;
417: Query other = (Query) obj;
418:
419: return Arrays.equals(getPropertyNames(), other
420: .getPropertyNames())
421: && (retrieveAllProperties() == other
422: .retrieveAllProperties())
423: && (getMaxFeatures() == other.getMaxFeatures())
424: && ((getFilter() == null) ? (other.getFilter() == null)
425: : getFilter().equals(other.getFilter()))
426: && ((getTypeName() == null) ? (other.getTypeName() == null)
427: : getTypeName().equals(other.getTypeName()))
428: && ((getVersion() == null) ? (other.getVersion() == null)
429: : getVersion().equals(other.getVersion()))
430: && ((getCoordinateSystem() == null) ? (other
431: .getCoordinateSystem() == null)
432: : getCoordinateSystem().equals(
433: other.getCoordinateSystem()))
434: && ((getCoordinateSystemReproject() == null) ? (other
435: .getCoordinateSystemReproject() == null)
436: : getCoordinateSystemReproject().equals(
437: other.getCoordinateSystemReproject()));
438: }
439:
440: /**
441: * Over ride of toString
442: *
443: * @return a string representation of this query object.
444: */
445: public String toString() {
446: StringBuffer returnString = new StringBuffer("Query:");
447:
448: if (handle != null) {
449: returnString.append(" [" + handle + "]");
450: }
451:
452: returnString.append("\n feature type: " + typeName);
453:
454: if (filter != null) {
455: returnString.append("\n filter: " + filter.toString());
456: }
457:
458: returnString.append("\n [properties: ");
459:
460: if ((properties == null) || (properties.length == 0)) {
461: returnString.append(" ALL ]");
462: } else {
463: for (int i = 0; i < properties.length; i++) {
464: returnString.append(properties[i]);
465:
466: if (i < (properties.length - 1)) {
467: returnString.append(", ");
468: }
469: }
470:
471: returnString.append("]");
472: }
473:
474: if (sortBy != null && sortBy.length > 0) {
475: returnString.append("\n [sort by: ");
476: for (int i = 0; i < sortBy.length; i++) {
477: returnString.append(sortBy[i].getPropertyName()
478: .getPropertyName());
479: returnString.append(" ");
480: returnString.append(sortBy[i].getSortOrder().name());
481:
482: if (i < (sortBy.length - 1)) {
483: returnString.append(", ");
484: }
485: }
486:
487: returnString.append("]");
488: }
489:
490: return returnString.toString();
491: }
492:
493: /**
494: * getCoordinateSystem purpose.
495: * <p>
496: * Description ...
497: * </p>
498: */
499: public CoordinateReferenceSystem getCoordinateSystem() {
500: return coordinateSystem;
501: }
502:
503: /**
504: * getCoordinateSystemReproject purpose.
505: * <p>
506: * Description ...
507: * </p>
508: */
509: public CoordinateReferenceSystem getCoordinateSystemReproject() {
510: return coordinateSystemReproject;
511: }
512:
513: /**
514: * setCoordinateSystem purpose.
515: * <p>
516: * Description ...
517: * </p>
518: * @param system
519: */
520: public void setCoordinateSystem(CoordinateReferenceSystem system) {
521: coordinateSystem = system;
522: }
523:
524: /**
525: * setCoordinateSystemReproject purpose.
526: * <p>
527: * Description ...
528: * </p>
529: * @param system
530: */
531: public void setCoordinateSystemReproject(
532: CoordinateReferenceSystem system) {
533: coordinateSystemReproject = system;
534: }
535:
536: /**
537: * Retrive list of SortBy information.
538: * <p>
539: * Note we are using SortBy2, to be standards complient
540: * you may limit yourself to to SortBy information.
541: * </p>
542: */
543: public SortBy[] getSortBy() {
544: return sortBy;
545: }
546:
547: /**
548: * Sets the sort by information.
549: *
550: */
551: public void setSortBy(SortBy[] sortBy) {
552: this .sortBy = sortBy;
553: }
554:
555: public Hints getHints() {
556: if (hints == null)
557: hints = new Hints(Collections.EMPTY_MAP);
558: return hints;
559: }
560:
561: /**
562: * Sets the query hints
563: * @param hints
564: */
565: public void setHints(Hints hints) {
566: this.hints = hints;
567: }
568:
569: }
|