001 //$HeadURL: svn+ssh://jwilden@svn.wald.intevation.org/deegree/base/branches/2.5_testing/src/org/deegree/ogcwebservices/wfs/operation/GetFeature.java $
002 /*----------------------------------------------------------------------------
003 This file is part of deegree, http://deegree.org/
004 Copyright (C) 2001-2009 by:
005 Department of Geography, University of Bonn
006 and
007 lat/lon GmbH
008
009 This library is free software; you can redistribute it and/or modify it under
010 the terms of the GNU Lesser General Public License as published by the Free
011 Software Foundation; either version 2.1 of the License, or (at your option)
012 any later version.
013 This library is distributed in the hope that it will be useful, but WITHOUT
014 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
015 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
016 details.
017 You should have received a copy of the GNU Lesser General Public License
018 along with this library; if not, write to the Free Software Foundation, Inc.,
019 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020
021 Contact information:
022
023 lat/lon GmbH
024 Aennchenstr. 19, 53177 Bonn
025 Germany
026 http://lat-lon.de/
027
028 Department of Geography, University of Bonn
029 Prof. Dr. Klaus Greve
030 Postfach 1147, 53001 Bonn
031 Germany
032 http://www.geographie.uni-bonn.de/deegree/
033
034 e-mail: info@deegree.org
035 ----------------------------------------------------------------------------*/
036 package org.deegree.ogcwebservices.wfs.operation;
037
038 import java.net.URI;
039 import java.util.ArrayList;
040 import java.util.HashMap;
041 import java.util.List;
042 import java.util.Map;
043
044 import org.deegree.datatypes.QualifiedName;
045 import org.deegree.framework.log.ILogger;
046 import org.deegree.framework.log.LoggerFactory;
047 import org.deegree.framework.util.KVP2Map;
048 import org.deegree.framework.xml.NamespaceContext;
049 import org.deegree.framework.xml.XMLParsingException;
050 import org.deegree.i18n.Messages;
051 import org.deegree.model.filterencoding.FeatureFilter;
052 import org.deegree.model.filterencoding.FeatureId;
053 import org.deegree.model.filterencoding.Filter;
054 import org.deegree.ogcbase.PropertyPath;
055 import org.deegree.ogcbase.PropertyPathFactory;
056 import org.deegree.ogcbase.PropertyPathStep;
057 import org.deegree.ogcbase.SortProperty;
058 import org.deegree.ogcwebservices.InconsistentRequestException;
059 import org.deegree.ogcwebservices.InvalidParameterValueException;
060 import org.deegree.ogcwebservices.MissingParameterValueException;
061 import org.deegree.ogcwebservices.OGCWebServiceException;
062 import org.deegree.ogcwebservices.wfs.WFService;
063 import org.w3c.dom.Element;
064
065 /**
066 * Represents a <code>GetFeature</code> request to a web feature service.
067 * <p>
068 * The GetFeature operation allows the retrieval of features from a web feature service. A GetFeature request is
069 * processed by a WFS and when the value of the outputFormat attribute is set to text/gml; subtype=gml/3.1.1, a GML
070 * instance document, containing the result set, is returned to the client.
071 *
072 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth </a>
073 * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider</a>
074 * @author last edited by: $Author: mschneider $
075 *
076 * @version $Revision: 18195 $, $Date: 2009-06-18 17:55:39 +0200 (Do, 18 Jun 2009) $
077 */
078 public class GetFeature extends AbstractWFSRequest {
079
080 private static final ILogger LOG = LoggerFactory.getLogger( GetFeature.class );
081
082 private static final long serialVersionUID = 8885456550385433051L;
083
084 /** Serialized java object format (deegree specific extension) * */
085 public static final String FORMAT_FEATURECOLLECTION = "FEATURECOLLECTION";
086
087 /**
088 * Known result types.
089 */
090 public static enum RESULT_TYPE {
091
092 /** A full response should be generated. */
093 RESULTS,
094
095 /** Only a count of the number of features should be returned. */
096 HITS
097 }
098
099 protected RESULT_TYPE resultType = RESULT_TYPE.RESULTS;
100
101 protected String outputFormat;
102
103 protected int maxFeatures;
104
105 private int traverseXLinkDepth;
106
107 private int traverseXLinkExpiry;
108
109 protected List<Query> queries;
110
111 // deegree specific extension, default: 1 (start at first feature)
112 protected int startPosition;
113
114 /**
115 * Creates a new <code>GetFeature</code> instance.
116 *
117 * @param version
118 * request version
119 * @param id
120 * id of the request
121 * @param handle
122 * @param resultType
123 * desired result type (results | hits)
124 * @param outputFormat
125 * requested result format
126 * @param maxFeatures
127 * @param startPosition
128 * deegree specific parameter defining where to start considering features
129 * @param traverseXLinkDepth
130 * indicates the depth to which nested property XLink linking element locator attribute (href) XLinks are
131 * traversed and resolved if possible (not implemented yet, use -1 as default)
132 * @param traverseXLinkExpiry
133 * indicates how long a Web Feature Service should wait to receive a response to a nested GetGmlObject
134 * request (not implemented yet, use 0 as default)
135 * @param queries
136 * @param vendorSpecificParam
137 */
138 GetFeature( String version, String id, String handle, RESULT_TYPE resultType, String outputFormat, int maxFeatures,
139 int startPosition, int traverseXLinkDepth, int traverseXLinkExpiry, Query[] queries,
140 Map<String, String> vendorSpecificParam ) {
141 super( version, id, handle, vendorSpecificParam );
142 this.setQueries( queries );
143 this.outputFormat = outputFormat;
144 this.maxFeatures = maxFeatures;
145 this.startPosition = startPosition;
146 this.resultType = resultType;
147 this.traverseXLinkDepth = traverseXLinkDepth;
148 this.traverseXLinkExpiry = traverseXLinkExpiry;
149 }
150
151 /**
152 * Creates an uninitialized {@link GetFeature} instance.
153 */
154 protected GetFeature() {
155 super( null, null, null, null );
156 }
157
158 /**
159 * Creates a new <code>GetFeature</code> instance from the given parameters.
160 *
161 * @param version
162 * request version
163 * @param id
164 * id of the request
165 * @param resultType
166 * desired result type (results | hits)
167 * @param outputFormat
168 * requested result format
169 * @param handle
170 * @param maxFeatures
171 * default = -1 (all features)
172 * @param startPosition
173 * default = 0 (starting at the first feature)
174 * @param traverseXLinkDepth
175 * @param traverseXLinkExpiry
176 * @param queries
177 * a set of Query objects that describes the query to perform
178 * @return new <code>GetFeature</code> request
179 */
180 public static GetFeature create( String version, String id, RESULT_TYPE resultType, String outputFormat,
181 String handle, int maxFeatures, int startPosition, int traverseXLinkDepth,
182 int traverseXLinkExpiry, Query[] queries ) {
183 return new GetFeature( version, id, handle, resultType, outputFormat, maxFeatures, startPosition,
184 traverseXLinkDepth, traverseXLinkExpiry, queries, null );
185 }
186
187 /**
188 * Creates a new <code>GetFeature</code> instance from a document that contains the DOM representation of the
189 * request.
190 *
191 * @param id
192 * of the request
193 * @param root
194 * element that contains the DOM representation of the request
195 * @return new <code>GetFeature</code> request
196 * @throws OGCWebServiceException
197 */
198 public static GetFeature create( String id, Element root )
199 throws OGCWebServiceException {
200 GetFeatureDocument doc = new GetFeatureDocument();
201 doc.setRootElement( root );
202 GetFeature request;
203 try {
204 request = doc.parse( id );
205 } catch ( InvalidParameterValueException e ) {
206 throw e;
207 } catch ( XMLParsingException e ) {
208 // have to wrap it, to set exception code
209 throw new InvalidParameterValueException( e.getMessage(), e );
210 } catch ( Exception e ) {
211 LOG.logError( e.getMessage(), e );
212 throw new OGCWebServiceException( "GetFeature", e.getMessage() );
213 }
214 return request;
215 }
216
217 /**
218 * Creates a new <code>GetFeature</code> instance from the given key-value pair encoded request.
219 *
220 * @param id
221 * request identifier
222 * @param request
223 * @return new <code>GetFeature</code> request
224 * @throws InvalidParameterValueException
225 * @throws InconsistentRequestException
226 * @throws MissingParameterValueException
227 */
228 public static GetFeature create( String id, String request )
229 throws InconsistentRequestException, InvalidParameterValueException,
230 MissingParameterValueException {
231 Map<String, String> map = KVP2Map.toMap( request );
232 map.put( "ID", id );
233 return create( map );
234 }
235
236 /**
237 * Creates a new <code>GetFeature</code> request from the given map.
238 *
239 * @param kvp
240 * key-value pairs, keys have to be uppercase
241 * @return new <code>GetFeature</code> request
242 * @throws InvalidParameterValueException
243 * @throws InconsistentRequestException
244 * @throws MissingParameterValueException
245 */
246 public static GetFeature create( Map<String, String> kvp )
247 throws InconsistentRequestException, InvalidParameterValueException,
248 MissingParameterValueException {
249
250 // SERVICE
251 checkServiceParameter( kvp );
252
253 // ID (deegree specific)
254 String id = kvp.get( "ID" );
255
256 // VERSION
257 String version = checkVersionParameter( kvp );
258
259 boolean is100 = version.equals( "1.0.0" );
260
261 // OUTPUTFORMAT
262 String outputFormat = getParam( "OUTPUTFORMAT", kvp, is100 ? FORMAT_GML2_WFS100 : FORMAT_GML3 );
263
264 // RESULTTYPE
265 RESULT_TYPE resultType = RESULT_TYPE.RESULTS;
266 String resultTypeString = kvp.get( "RESULTTYPE" );
267 if ( "hits".equals( resultTypeString ) ) {
268 resultType = RESULT_TYPE.HITS;
269 }
270
271 // FEATUREVERSION
272 String featureVersion = kvp.get( "FEATUREVERSION" );
273
274 // MAXFEATURES
275 String maxFeaturesString = kvp.get( "MAXFEATURES" );
276 // -1: fetch all features
277 int maxFeatures = -1;
278 if ( maxFeaturesString != null ) {
279 try {
280 maxFeatures = Integer.parseInt( maxFeaturesString );
281 if ( maxFeatures < 1 ) {
282 throw new NumberFormatException();
283 }
284 } catch ( NumberFormatException e ) {
285 LOG.logError( e.getMessage(), e );
286 String msg = Messages.getMessage( "WFS_PARAMETER_INVALID_INT", maxFeaturesString, "MAXFEATURES" );
287 throw new InvalidParameterValueException( msg );
288 }
289 }
290
291 // STARTPOSITION (deegree specific)
292 String startPosString = getParam( "STARTPOSITION", kvp, "1" );
293 int startPosition = 1;
294 try {
295 startPosition = Integer.parseInt( startPosString );
296 if ( startPosition < 1 ) {
297 throw new NumberFormatException();
298 }
299 } catch ( NumberFormatException e ) {
300 LOG.logError( e.getMessage(), e );
301 String msg = Messages.getMessage( "WFS_PARAMETER_INVALID_INT", startPosString, "STARTPOSITION" );
302 throw new InvalidParameterValueException( msg );
303 }
304
305 // SRSNAME
306 String srsName = kvp.get( "SRSNAME" );
307
308 // SORTBY
309 SortProperty[] sortProperties = SortProperty.create( kvp.get( "SORTBY" ),
310 extractNamespaceParameter( kvp ).getNamespaceMap() );
311
312 // TRAVERSEXLINKDEPTH
313 int traverseXLinkDepth = -1;
314
315 // TRAVERSEXLINKEXPIRY
316 int traverseXLinkExpiry = -1;
317
318 Map<QualifiedName, Filter> filterMap = null;
319
320 // TYPENAME
321 QualifiedName[] typeNames = extractTypeNames( kvp );
322 if ( typeNames.length == 0 ) {
323 // check if FEATUREID is present
324 String featureId = kvp.get( "FEATUREID" );
325 if ( featureId != null ) {
326 // no TYPENAME parameter -> request needs to be augmented later (with configuration)
327 return new AugmentableGetFeature( version, id, null, resultType, outputFormat, maxFeatures,
328 startPosition, traverseXLinkDepth, traverseXLinkExpiry, new Query[0],
329 kvp );
330 }
331 String msg = Messages.getMessage( "WFS_TYPENAME+FID_PARAMS_MISSING" );
332 throw new InvalidParameterValueException( msg );
333 }
334
335 // check if FEATUREID is present
336 String featureId = kvp.get( "FEATUREID" );
337 if ( featureId != null ) {
338 String[] featureIds = featureId.split( "," );
339 if ( typeNames.length != 1 && featureIds.length != typeNames.length ) {
340 String msg = Messages.getMessage( "WFS_TYPENAME+FID_COUNT_MISMATCH", typeNames.length,
341 featureIds.length );
342 throw new InvalidParameterValueException( msg );
343 } else if ( typeNames.length == 1 ) {
344 // build one filter
345 ArrayList<FeatureId> fids = new ArrayList<FeatureId>( featureIds.length );
346 for ( String fid : featureIds ) {
347 fids.add( new FeatureId( fid ) );
348 }
349 Filter filter = new FeatureFilter( fids );
350 filterMap = new HashMap<QualifiedName, Filter>();
351 filterMap.put( typeNames[0], filter );
352 } else {
353 throw new InvalidParameterValueException(
354 "Usage of FEATUREID with multiple TYPENAME values is not supported yet." );
355 }
356 }
357
358 // BBOX
359 Filter bboxFilter = extractBBOXFilter( kvp );
360
361 // FILTER (mutually exclusive with FEATUREID or BBOX, prequisite: TYPENAME)
362 if ( filterMap != null || bboxFilter != null ) {
363 if ( kvp.containsKey( "FILTER" ) ) {
364 String msg = Messages.getMessage( "WFS_GET_FEATURE_FEATUREID_BBOX_AND_FILTER" );
365 throw new InvalidParameterValueException( msg );
366 }
367 } else {
368 filterMap = extractFilters( kvp, typeNames );
369 }
370
371 // PROPERTYNAME
372 Map<QualifiedName, PropertyPath[]> propertyNameMap = extractPropNames( kvp, typeNames );
373
374 // build a Query instance for each requested feature type (later also for each featureid...)
375 Query[] queries = new Query[typeNames.length];
376 for ( int i = 0; i < queries.length; i++ ) {
377 QualifiedName ftName = typeNames[i];
378 PropertyPath[] properties = propertyNameMap.get( ftName );
379 Filter filter;
380 if ( filterMap != null ) {
381 filter = filterMap.get( ftName );
382 } else {
383 filter = bboxFilter;
384 }
385 QualifiedName[] ftNames = new QualifiedName[] { ftName };
386 queries[i] = new Query( properties, null, sortProperties, null, featureVersion, ftNames, null, srsName,
387 filter, resultType, maxFeatures, startPosition );
388 }
389
390 // build a GetFeature request that contains all queries
391 GetFeature request = new GetFeature( version, id, null, resultType, outputFormat, maxFeatures, startPosition,
392 traverseXLinkDepth, traverseXLinkExpiry, queries, kvp );
393 return request;
394 }
395
396 /**
397 * Extracts the PROPERTYNAME parameter and assigns them to the requested type names.
398 *
399 * @param kvp
400 * @param typeNames
401 * @return map with the assignments of type names to property names
402 * @throws InvalidParameterValueException
403 */
404 protected static Map<QualifiedName, PropertyPath[]> extractPropNames( Map<String, String> kvp,
405 QualifiedName[] typeNames )
406 throws InvalidParameterValueException {
407 Map<QualifiedName, PropertyPath[]> propMap = new HashMap<QualifiedName, PropertyPath[]>();
408 String propNameString = kvp.get( "PROPERTYNAME" );
409 if ( propNameString != null ) {
410 String[] propNameLists = propNameString.split( "\\)" );
411 if ( propNameLists.length != typeNames.length ) {
412 String msg = Messages.getMessage( "WFS_PROPNAME_PARAM_WRONG_COUNT",
413 Integer.toString( propNameLists.length ),
414 Integer.toString( typeNames.length ) );
415 throw new InvalidParameterValueException( msg );
416 }
417 NamespaceContext nsContext = extractNamespaceParameter( kvp );
418 for ( int i = 0; i < propNameLists.length; i++ ) {
419 String propNameList = propNameLists[i];
420 if ( propNameList.startsWith( "(" ) ) {
421 propNameList = propNameList.substring( 1 );
422 }
423 String[] propNames = propNameList.split( "," );
424 PropertyPath[] paths = new PropertyPath[propNames.length];
425 for ( int j = 0; j < propNames.length; j++ ) {
426 PropertyPath path = transformToPropertyPath( propNames[j], nsContext );
427 paths[j] = ( path );
428 }
429 propMap.put( typeNames[i], paths );
430 }
431 }
432 return propMap;
433 }
434
435 /**
436 * Transforms the given property name to a (qualified) <code>PropertyPath</code> object by using the specified
437 * namespace bindings.
438 *
439 * @param propName
440 * @param nsContext
441 * @return (qualified) <code>PropertyPath</code> object
442 * @throws InvalidParameterValueException
443 */
444 private static PropertyPath transformToPropertyPath( String propName, NamespaceContext nsContext )
445 throws InvalidParameterValueException {
446 String[] steps = propName.split( "/" );
447 List<PropertyPathStep> propertyPathSteps = new ArrayList<PropertyPathStep>( steps.length );
448
449 for ( int i = 0; i < steps.length; i++ ) {
450 PropertyPathStep propertyStep = null;
451 QualifiedName propertyName = null;
452 String step = steps[i];
453 boolean isAttribute = false;
454 boolean isIndexed = false;
455 int selectedIndex = -1;
456
457 // check if step begins with '@' -> must be the final step then
458 if ( step.startsWith( "@" ) ) {
459 if ( i != steps.length - 1 ) {
460 String msg = "PropertyName '" + propName + "' is illegal: the attribute specifier may only "
461 + "be used for the final step.";
462 throw new InvalidParameterValueException( msg );
463 }
464 step = step.substring( 1 );
465 isAttribute = true;
466 }
467
468 // check if the step ends with brackets ([...])
469 if ( step.endsWith( "]" ) ) {
470 if ( isAttribute ) {
471 String msg = "PropertyName '" + propName
472 + "' is illegal: if the attribute specifier ('@') is used, "
473 + "index selection ('[...']) is not possible.";
474 throw new InvalidParameterValueException( msg );
475 }
476 int bracketPos = step.indexOf( '[' );
477 if ( bracketPos < 0 ) {
478 String msg = "PropertyName '" + propName + "' is illegal. No opening brackets found for step '"
479 + step + "'.";
480 throw new InvalidParameterValueException( msg );
481 }
482 try {
483 selectedIndex = Integer.parseInt( step.substring( bracketPos + 1, step.length() - 1 ) );
484 } catch ( NumberFormatException e ) {
485 LOG.logError( e.getMessage(), e );
486 String msg = "PropertyName '" + propName + "' is illegal. Specified index '"
487 + step.substring( bracketPos + 1, step.length() - 1 ) + "' is not a number.";
488 throw new InvalidParameterValueException( msg );
489 }
490 step = step.substring( 0, bracketPos );
491 isIndexed = true;
492 }
493
494 // determine namespace prefix and binding (if any)
495 int colonPos = step.indexOf( ':' );
496 String prefix = "";
497 String localName = step;
498 if ( colonPos > 0 ) {
499 prefix = step.substring( 0, colonPos );
500 localName = step.substring( colonPos + 1 );
501 }
502 URI nsURI = nsContext.getURI( prefix );
503 propertyName = new QualifiedName( prefix, localName, nsURI );
504
505 if ( isAttribute ) {
506 propertyStep = PropertyPathFactory.createAttributePropertyPathStep( propertyName );
507 } else if ( isIndexed ) {
508 propertyStep = PropertyPathFactory.createPropertyPathStep( propertyName, selectedIndex );
509 } else {
510 propertyStep = PropertyPathFactory.createPropertyPathStep( propertyName );
511 }
512 propertyPathSteps.add( propertyStep );
513 }
514 return PropertyPathFactory.createPropertyPath( propertyPathSteps );
515 }
516
517 /**
518 * Returns the output format.
519 * <p>
520 * The outputFormat attribute defines the format to use to generate the result set. Vendor specific formats,
521 * declared in the capabilities document are possible. The WFS-specs implies GML as default output format.
522 *
523 * @return the output format.
524 */
525 public String getOutputFormat() {
526 return this.outputFormat;
527 }
528
529 /**
530 * The query defines which feature type to query, what properties to retrieve and what constraints (spatial and
531 * non-spatial) to apply to those properties.
532 * <p>
533 * only used for xml-coded requests
534 *
535 * @return contained queries
536 */
537 public Query[] getQuery() {
538 return queries.toArray( new Query[queries.size()] );
539 }
540
541 /**
542 * sets the <Query>
543 *
544 * @param query
545 */
546 public void setQueries( Query[] query ) {
547 if ( query != null ) {
548 this.queries = new ArrayList<Query>( query.length );
549 for ( int i = 0; i < query.length; i++ ) {
550 this.queries.add( query[i] );
551 }
552 } else {
553 this.queries = new ArrayList<Query>();
554 }
555 }
556
557 /**
558 * The optional maxFeatures attribute can be used to limit the number of features that a GetFeature request
559 * retrieves. Once the maxFeatures limit is reached, the result set is truncated at that point. If not limit is set
560 * -1 will be returned.
561 *
562 * @return number of feature to fetch, -1 if no limit is set
563 */
564 public int getMaxFeatures() {
565 return maxFeatures;
566 }
567
568 /**
569 * @see #getMaxFeatures()
570 * @param max
571 */
572 public void setMaxFeatures( int max ) {
573 this.maxFeatures = max;
574 for ( int i = 0; i < queries.size(); i++ ) {
575 queries.get( i ).setMaxFeatures( max );
576 }
577 }
578
579 /**
580 * The startPosition parameter identifies the first result set entry to be returned specified the default is the
581 * first record. If not startposition is set 0 will be returned
582 *
583 * @return the first result set entry to be returned
584 */
585 public int getStartPosition() {
586 return startPosition;
587 }
588
589 /**
590 * Returns the desired result type of the GetFeature operation. Possible values are 'results' and 'hits'.
591 *
592 * @return the desired result type
593 */
594 public RESULT_TYPE getResultType() {
595 return this.resultType;
596 }
597
598 /**
599 * The optional traverseXLinkDepth attribute indicates the depth to which nested property XLink linking element
600 * locator attribute (href) XLinks in all properties of the selected feature(s) are traversed and resolved if
601 * possible. A value of "1" indicates that one linking element locator attribute (href) XLink will be traversed and
602 * the referenced element returned if possible, but nested property XLink linking element locator attribute (href)
603 * XLinks in the returned element are not traversed. A value of "*" indicates that all nested property XLink linking
604 * element locator attribute (href) XLinks will be traversed and the referenced elements returned if possible. The
605 * range of valid values for this attribute consists of positive integers plus "*".
606 *
607 * @return the depth to which nested property XLinks are traversed and resolved
608 */
609 public int getTraverseXLinkDepth() {
610 return traverseXLinkDepth;
611 }
612
613 /**
614 * The traverseXLinkExpiry attribute is specified in minutes. It indicates how long a Web Feature Service should
615 * wait to receive a response to a nested GetGmlObject request. If no traverseXLinkExpiry attribute is present for a
616 * GetGmlObject request, the WFS wait time is implementation dependent.
617 *
618 * @return how long to wait to receive a response to a nested GetGmlObject request
619 */
620 public int getTraverseXLinkExpiry() {
621 return traverseXLinkExpiry;
622 }
623
624 /**
625 * Adds missing namespaces in the names of requested feature types.
626 * <p>
627 * If the {@link QualifiedName} of a requested type has a null namespace, the first qualified feature type name of
628 * the given {@link WFService} with the same local name is used instead.
629 * <p>
630 * Note: The method changes this request (the feature type names) and should only be called by the
631 * <code>WFSHandler</code> class.
632 *
633 * @param wfs
634 * {@link WFService} instance that is used for the lookup of proper (qualified) feature type names
635 */
636 public void guessMissingTypeNameNamespaces( WFService wfs ) {
637 for ( Query query : queries ) {
638 query.guessMissingTypeNameNamespace( wfs );
639 }
640 }
641
642 /**
643 * Adds missing namespaces to requested feature type names, property names, filter properties and sort properties.
644 * <p>
645 * Note: The method changes the request and should only be called by the <code>WFSHandler</code> class.
646 *
647 * @param wfs
648 * {@link WFService} instance that is used for the lookup of proper (qualified) feature and property
649 * names
650 */
651 public void guessAllMissingNamespaces( WFService wfs ) {
652 for ( Query query : queries ) {
653 query.guessAllMissingNamespaces( wfs );
654 }
655 }
656
657 @Override
658 public String toString() {
659 String ret = null;
660 ret = "WFSGetFeatureRequest: { \n ";
661 ret += "outputFormat = " + outputFormat + "\n";
662 ret += ( "handle = " + getHandle() + "\n" );
663 ret += ( "query = " + queries + "\n" );
664 ret += "}\n";
665 return ret;
666 }
667 }