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 }