001 // $HeadURL: svn+ssh://mschneider@svn.wald.intevation.org/deegree/base/trunk/src/org/deegree/ogcwebservices/wfs/capabilities/WFSCapabilitiesDocument.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.capabilities; 037 038 import java.io.IOException; 039 import java.net.MalformedURLException; 040 import java.net.URI; 041 import java.net.URL; 042 import java.security.InvalidParameterException; 043 import java.util.ArrayList; 044 import java.util.List; 045 046 import org.deegree.datatypes.QualifiedName; 047 import org.deegree.framework.util.StringTools; 048 import org.deegree.framework.xml.XMLParsingException; 049 import org.deegree.framework.xml.XMLTools; 050 import org.deegree.model.filterencoding.capabilities.FilterCapabilities; 051 import org.deegree.model.filterencoding.capabilities.FilterCapabilities100Fragment; 052 import org.deegree.model.metadata.iso19115.Keywords; 053 import org.deegree.model.spatialschema.Envelope; 054 import org.deegree.model.spatialschema.GeometryFactory; 055 import org.deegree.ogcwebservices.getcapabilities.DCPType; 056 import org.deegree.ogcwebservices.getcapabilities.HTTP; 057 import org.deegree.ogcwebservices.getcapabilities.InvalidCapabilitiesException; 058 import org.deegree.ogcwebservices.getcapabilities.MetadataURL; 059 import org.deegree.ogcwebservices.getcapabilities.OGCCapabilities; 060 import org.deegree.ogcwebservices.getcapabilities.OGCCapabilitiesDocument; 061 import org.deegree.ogcwebservices.getcapabilities.Operation; 062 import org.deegree.ogcwebservices.getcapabilities.OperationsMetadata; 063 import org.deegree.ogcwebservices.getcapabilities.ServiceIdentification; 064 import org.deegree.ogcwebservices.getcapabilities.ServiceProvider; 065 import org.deegree.owscommon.OWSDomainType; 066 import org.w3c.dom.Document; 067 import org.w3c.dom.Element; 068 import org.xml.sax.SAXException; 069 070 /** 071 * Represents a capabilities document for an OGC WFS 1.0.0 compliant web service. 072 * <p> 073 * NOTE: The parsing methods produces beans that are designed to match the WFS 1.1.0 specification. Needs testing! The 074 * following things are still TBD: 075 * <ul> 076 * <li>Respect value of wfs:Service/wfs:OnlineResource element. Should possible be stored in a {@link ServiceProvider} 077 * bean.</li> 078 * <li>Respect value of wfs:Capability/wfs:DescribeFeatureType/wfs:SchemaDescriptionLanguage element. Should possibly be 079 * stored as an {@link OWSDomainType} bean in the corresponding {@link Operation} object.</li> 080 * <li>Respect value of wfs:Capability/wfs:GetFeature/wfs:ResultFormat element. Should possibly be stored as an 081 * {@link OWSDomainType} bean in the corresponding {@link Operation} object.</li> 082 * <li>Respect value of wfs:Capability/wfs:GetFeatureWithLock/wfs:ResultFormat element. Should possibly be stored as an 083 * {@link OWSDomainType} bean in the corresponding {@link Operation} object.</li> 084 * </ul> 085 * 086 * @author <a href="mailto:mschneider@lat-lon.de">Markus Schneider </a> 087 * @author last edited by: $Author: rbezema $ 088 * 089 * @version $Revision: 11377 $, $Date: 2008-04-23 07:55:34 +0000 (Mi, 23 Apr 2008) $ 090 */ 091 public class WFSCapabilitiesDocument_1_0_0 extends OGCCapabilitiesDocument { 092 093 private static final long serialVersionUID = 4538469826043112486L; 094 095 private static final String[] VALID_TYPES = { "TDC211", "FGDC" }; 096 097 private static final String[] VALID_FORMATS = { "XML", "SGML", "TXT" }; 098 099 /** 100 * Creates a skeleton capabilities document that contains the mandatory elements only. 101 * 102 * @throws IOException 103 * @throws SAXException 104 */ 105 public void createEmptyDocument() 106 throws IOException, SAXException { 107 // set up the root document. 108 Document doc = XMLTools.create(); 109 Element root = doc.createElementNS( "http://www.opengis.net/wfs", "wfs:WFS_Capabilities" ); 110 doc.importNode( root, false ); 111 112 setRootElement( root ); 113 root.setAttribute( "version", "1.0.0" ); 114 root.setAttribute( "updateSequence", "0" ); 115 } 116 117 /** 118 * Creates a class representation of the document. 119 * 120 * @return class representation of the capabilities document 121 */ 122 @Override 123 public OGCCapabilities parseCapabilities() 124 throws InvalidCapabilitiesException { 125 126 WFSCapabilities wfsCapabilities = null; 127 try { 128 129 wfsCapabilities = new WFSCapabilities( parseVersion(), parseUpdateSequence(), getService(), null, 130 getCapability(), getFeatureTypeList(), null, null, null, 131 getFilterCapabilities() ); 132 } catch ( XMLParsingException e ) { 133 throw new InvalidCapabilitiesException( e.getMessage() + "\n" + StringTools.stackTraceToString( e ) ); 134 } 135 136 return wfsCapabilities; 137 } 138 139 /** 140 * Returns the class representation for the <code>Service</code> section of the document. 141 * 142 * @return class representation for the <code>Service</code> section 143 * @throws XMLParsingException 144 */ 145 public ServiceIdentification getService() 146 throws XMLParsingException { 147 148 Element element = XMLTools.getRequiredElement( getRootElement(), "wfs:Service", nsContext ); 149 150 String name = XMLTools.getRequiredNodeAsString( element, "wfs:Name/text()", nsContext ); 151 String title = XMLTools.getRequiredNodeAsString( element, "wfs:Title/text()", nsContext ); 152 String abstract_ = XMLTools.getNodeAsString( element, "wfs:Abstract/text()", nsContext, null ); 153 154 String keywordsValue = XMLTools.getNodeAsString( element, "wfs:Keywords/text()", nsContext, null ); 155 Keywords[] keywords = null; 156 if ( keywordsValue != null ) { 157 keywords = new Keywords[] { new Keywords( new String[] { keywordsValue } ) }; 158 } 159 160 String[] serviceTypeVersions = new String[] { "1.0.0" }; 161 162 // String onlineResource = XMLTools.getRequiredNodeAsString( element, "wfs:OnlineResource/text()", nsContext ); 163 164 // 'Fees' element (optional) 165 String fees = XMLTools.getNodeAsString( element, "wfs:Fees/text()", nsContext, null ); 166 167 return new ServiceIdentification( name, null, serviceTypeVersions, title, abstract_, keywords, fees, null ); 168 } 169 170 /** 171 * Creates an object representation of the <code>wfs:Capability</code> section. 172 * 173 * @return object representation of the <code>wfs:Capability</code> section 174 * @throws XMLParsingException 175 */ 176 public OperationsMetadata getCapability() 177 throws XMLParsingException { 178 179 Element requestElement = XMLTools.getRequiredElement( getRootElement(), "wfs:Capability/wfs:Request", nsContext ); 180 181 // wfs:GetCapabilities element 182 Operation getCapabilities = null; 183 Element getCapabilitiesElement = XMLTools.getElement( requestElement, "wfs:GetCapabilities", nsContext ); 184 if ( getCapabilitiesElement != null ) { 185 List<Element> dcpTypeElements = XMLTools.getElements( getCapabilitiesElement, "wfs:DCPType", nsContext ); 186 DCPType[] dcpTypes = new DCPType[dcpTypeElements.size()]; 187 for ( int i = 0; i < dcpTypes.length; i++ ) { 188 Element dcpTypeElement = dcpTypeElements.get( i ); 189 dcpTypes[i] = getDCPTypeType( dcpTypeElement ); 190 } 191 getCapabilities = new Operation( "GetCapabilities", dcpTypes ); 192 } 193 194 // wfs:DescribeFeatureType element 195 Operation describeFeatureType = null; 196 Element describeFeatureTypeElement = XMLTools.getElement( requestElement, "wfs:DescribeFeatureType", nsContext ); 197 if ( describeFeatureTypeElement != null ) { 198 List<Element> dcpTypeElements = XMLTools.getElements( describeFeatureTypeElement, "wfs:DCPType", nsContext ); 199 DCPType[] dcpTypes = new DCPType[dcpTypeElements.size()]; 200 for ( int i = 0; i < dcpTypes.length; i++ ) { 201 Element dcpTypeElement = dcpTypeElements.get( i ); 202 dcpTypes[i] = getDCPTypeType( dcpTypeElement ); 203 } 204 205 // TODO evaluate SchemaDescriptionLanguage element 206 207 describeFeatureType = new Operation( "DescribeFeatureType", dcpTypes ); 208 } 209 210 // wfs:GetFeature element 211 Operation getFeature = null; 212 Element getFeatureElement = XMLTools.getElement( requestElement, "wfs:GetFeature", nsContext ); 213 if ( getFeatureElement != null ) { 214 List<Element> dcpTypeElements = XMLTools.getElements( getFeatureElement, "wfs:DCPType", nsContext ); 215 DCPType[] dcpTypes = new DCPType[dcpTypeElements.size()]; 216 for ( int i = 0; i < dcpTypes.length; i++ ) { 217 Element dcpTypeElement = dcpTypeElements.get( i ); 218 dcpTypes[i] = getDCPTypeType( dcpTypeElement ); 219 } 220 221 // TODO evaluate ResultFormat element 222 223 getFeature = new Operation( "GetFeature", dcpTypes ); 224 } 225 226 // wfs:GetFeature element 227 Operation getFeatureWithLock = null; 228 Element getFeatureWithLockElement = XMLTools.getElement( requestElement, "wfs:GetFeatureWithLock", nsContext ); 229 if ( getFeatureWithLockElement != null ) { 230 List<Element> dcpTypeElements = XMLTools.getElements( getFeatureWithLockElement, "wfs:DCPType", nsContext ); 231 DCPType[] dcpTypes = new DCPType[dcpTypeElements.size()]; 232 for ( int i = 0; i < dcpTypes.length; i++ ) { 233 Element dcpTypeElement = dcpTypeElements.get( i ); 234 dcpTypes[i] = getDCPTypeType( dcpTypeElement ); 235 } 236 237 // TODO evaluate ResultFormat element 238 239 getFeatureWithLock = new Operation( "GetFeatureWithLock", dcpTypes ); 240 } 241 242 // wfs:LockFeature element 243 Operation lockFeature = null; 244 Element lockFeatureElement = XMLTools.getElement( requestElement, "wfs:LockFeature", nsContext ); 245 if ( lockFeatureElement != null ) { 246 List<Element> dcpTypeElements = XMLTools.getElements( lockFeatureElement, "wfs:DCPType", nsContext ); 247 DCPType[] dcpTypes = new DCPType[dcpTypeElements.size()]; 248 for ( int i = 0; i < dcpTypes.length; i++ ) { 249 Element dcpTypeElement = dcpTypeElements.get( i ); 250 dcpTypes[i] = getDCPTypeType( dcpTypeElement ); 251 } 252 lockFeature = new Operation( "LockFeature", dcpTypes ); 253 } 254 255 // wfs:Transaction element 256 Operation transaction = null; 257 Element transactionElement = XMLTools.getElement( requestElement, "wfs:Transaction", nsContext ); 258 if ( transactionElement != null ) { 259 List<Element> dcpTypeElements = XMLTools.getElements( transactionElement, "wfs:DCPType", nsContext ); 260 DCPType[] dcpTypes = new DCPType[dcpTypeElements.size()]; 261 for ( int i = 0; i < dcpTypes.length; i++ ) { 262 Element dcpTypeElement = dcpTypeElements.get( i ); 263 dcpTypes[i] = getDCPTypeType( dcpTypeElement ); 264 } 265 transaction = new Operation( "Transaction", dcpTypes ); 266 } 267 268 return new WFSOperationsMetadata( getCapabilities, describeFeatureType, getFeature, getFeatureWithLock, null, 269 lockFeature, transaction, null, null ); 270 } 271 272 /** 273 * Creates an object representation of the given <code>wfs:DCPTypeType</code> element. 274 * 275 * @return object representation of the given <code>wfs:DCPTypeType</code> element. 276 * @throws XMLParsingException 277 */ 278 private DCPType getDCPTypeType( Element element ) 279 throws XMLParsingException { 280 281 Element httpElement = XMLTools.getRequiredElement( element, "wfs:HTTP", nsContext ); 282 String[] gets = XMLTools.getNodesAsStrings( httpElement, "wfs:Get/@onlineResource", nsContext ); 283 URL[] getURLs = new URL[gets.length]; 284 for ( int j = 0; j < gets.length; j++ ) { 285 try { 286 getURLs[j] = new URL( gets[j] ); 287 } catch ( MalformedURLException e ) { 288 throw new XMLParsingException( "OnlineResource '" + gets[j] + "' is not a valid URL." ); 289 } 290 } 291 String[] posts = XMLTools.getNodesAsStrings( httpElement, "wfs:Post/@onlineResource", nsContext ); 292 URL[] postURLs = new URL[posts.length]; 293 for ( int j = 0; j < posts.length; j++ ) { 294 try { 295 postURLs[j] = new URL( posts[j] ); 296 } catch ( MalformedURLException e ) { 297 throw new XMLParsingException( "OnlineResource '" + posts[j] + "' is not a valid URL." ); 298 } 299 } 300 return new DCPType( new HTTP( getURLs, postURLs ) ); 301 } 302 303 /** 304 * Returns the object representation of the <code>wfs:FeatureTypeList</code>- section. 305 * 306 * @return object representation of the <code>wfs:FeatureTypeList</code> section, may be empty (if missing) 307 * @throws XMLParsingException 308 */ 309 public FeatureTypeList getFeatureTypeList() 310 throws XMLParsingException { 311 312 List<WFSFeatureType> wfsFeatureTypes = new ArrayList<WFSFeatureType>(); 313 314 FeatureTypeList featureTypeList = new FeatureTypeList( 315 new org.deegree.ogcwebservices.wfs.capabilities.Operation[0], 316 wfsFeatureTypes ); 317 318 Element element = (Element) XMLTools.getNode( getRootElement(), "wfs:FeatureTypeList", nsContext ); 319 if ( element != null ) { 320 org.deegree.ogcwebservices.wfs.capabilities.Operation[] globalOperations = null; 321 Element operationsTypeElement = (Element) XMLTools.getNode( element, "wfs:Operations", nsContext ); 322 if ( operationsTypeElement != null ) { 323 globalOperations = getOperationsType( operationsTypeElement ); 324 } 325 List<Element> featureTypeElementList = XMLTools.getElements( element, "wfs:FeatureType", nsContext ); 326 if ( featureTypeElementList.size() < 1 ) { 327 throw new XMLParsingException( 328 "A wfs:FeatureTypeListType must contain at least one wfs:FeatureType-element." ); 329 } 330 for ( int i = 0; i < featureTypeElementList.size(); i++ ) { 331 WFSFeatureType wfsFT = getFeatureTypeType( featureTypeElementList.get( i ) ); 332 wfsFeatureTypes.add( wfsFT ); 333 } 334 335 featureTypeList = new FeatureTypeList( globalOperations, wfsFeatureTypes ); 336 } 337 338 return featureTypeList; 339 } 340 341 /** 342 * Returns the object representation for an element of type <code>wfs:FeatureTypeType</code>. 343 * 344 * @param element 345 * @return object representation for the element of type <code>wfs:OperationsType</code> 346 * @throws XMLParsingException 347 */ 348 public WFSFeatureType getFeatureTypeType( Element element ) 349 throws XMLParsingException { 350 351 QualifiedName name = parseQualifiedName( XMLTools.getRequiredNode( element, "wfs:Name/text()", nsContext ) ); 352 String title = XMLTools.getNodeAsString( element, "wfs:Title/text()", nsContext, null ); 353 String abstract_ = XMLTools.getNodeAsString( element, "wfs:Abstract/text()", nsContext, null ); 354 355 String keywordsValue = XMLTools.getNodeAsString( element, "wfs:Keywords/text()", nsContext, null ); 356 Keywords[] keywords = null; 357 if ( keywordsValue != null ) { 358 keywords = new Keywords[] { new Keywords( new String[] { keywordsValue } ) }; 359 } 360 361 URI defaultSrs = XMLTools.getRequiredNodeAsURI( element, "wfs:SRS", nsContext ); 362 363 org.deegree.ogcwebservices.wfs.capabilities.Operation[] operations = null; 364 Element operationsTypeElement = (Element) XMLTools.getNode( element, "wfs:Operations", nsContext ); 365 if ( operationsTypeElement != null ) { 366 operations = getOperationsType( operationsTypeElement ); 367 } 368 369 List<Element> latLongBoundingBoxElements = XMLTools.getElements( element, "wfs:LatLongBoundingBox", nsContext ); 370 Envelope[] latLongBoundingBoxes = new Envelope[latLongBoundingBoxElements.size()]; 371 for ( int i = 0; i < latLongBoundingBoxes.length; i++ ) { 372 latLongBoundingBoxes[i] = getLatLongBoundingBoxType( latLongBoundingBoxElements.get( i ) ); 373 } 374 375 List<Element> metadataURLElementList = XMLTools.getElements( element, "wfs:MetadataURL", nsContext ); 376 MetadataURL[] metadataUrls = new MetadataURL[metadataURLElementList.size()]; 377 for ( int i = 0; i < metadataUrls.length; i++ ) { 378 metadataUrls[i] = getMetadataURL( metadataURLElementList.get( i ) ); 379 } 380 381 return new WFSFeatureType( name, title, abstract_, keywords, defaultSrs, null, operations, null, 382 latLongBoundingBoxes, metadataUrls ); 383 } 384 385 /** 386 * Creates an <code>Envelope</code> object from the given element of type <code>wfs:LatLongBoundingBoxType</code>. 387 * 388 * @param element 389 * @return corresponsing <code>Envelope</code> object 390 * @throws XMLParsingException 391 */ 392 private Envelope getLatLongBoundingBoxType( Element element ) 393 throws XMLParsingException { 394 double minX = XMLTools.getRequiredNodeAsDouble( element, "@minx", nsContext ); 395 double minY = XMLTools.getRequiredNodeAsDouble( element, "@miny", nsContext ); 396 double maxX = XMLTools.getRequiredNodeAsDouble( element, "@maxx", nsContext ); 397 double maxY = XMLTools.getRequiredNodeAsDouble( element, "@maxy", nsContext ); 398 return GeometryFactory.createEnvelope( minX, minY, maxX, maxY, null ); 399 } 400 401 /** 402 * Returns the object representation for an element node of type <code>wfs:MetadataURLType</code>. 403 * 404 * TODO: Schema says base type is String, not URL! 405 * 406 * @param element 407 * @return object representation for the element of type <code>wfs:MetadataURLType</code> 408 * @throws XMLParsingException 409 */ 410 public MetadataURL getMetadataURL( Element element ) 411 throws XMLParsingException { 412 413 String type = XMLTools.getRequiredNodeAsString( element, "@type", nsContext, VALID_TYPES ); 414 String format = XMLTools.getRequiredNodeAsString( element, "@format", nsContext, VALID_FORMATS ); 415 String url = XMLTools.getRequiredNodeAsString( element, "text()", nsContext ); 416 URL onlineResource; 417 try { 418 onlineResource = new URL( url ); 419 } catch ( MalformedURLException e ) { 420 throw new XMLParsingException( "A wfs:MetadataURLType must contain a valid URL: " + e.getMessage() ); 421 } 422 423 return new MetadataURL( type, format, onlineResource ); 424 } 425 426 /** 427 * Returns the object representation for an element node of type <code>wfs:OperationsType</code>. 428 * 429 * @param element 430 * @return object representation for the element of type <code>wfs:OperationsType</code> 431 * @throws XMLParsingException 432 */ 433 public org.deegree.ogcwebservices.wfs.capabilities.Operation[] getOperationsType( Element element ) 434 throws XMLParsingException { 435 436 String[] operationCodes = XMLTools.getNodesAsStrings( element, "wfs:Operation/text()", nsContext ); 437 org.deegree.ogcwebservices.wfs.capabilities.Operation[] operations = new org.deegree.ogcwebservices.wfs.capabilities.Operation[operationCodes.length]; 438 for ( int i = 0; i < operations.length; i++ ) { 439 if ( org.deegree.ogcwebservices.wfs.capabilities.Operation.GET_GML_OBJECT.equals( operationCodes[i] ) ) { 440 String msg = ( "Invalid WFS capabilities document. WFS 1.0.0 does not specify operation '" 441 + org.deegree.ogcwebservices.wfs.capabilities.Operation.GET_GML_OBJECT + ".'" ); 442 throw new XMLParsingException( msg ); 443 } 444 try { 445 operations[i] = new org.deegree.ogcwebservices.wfs.capabilities.Operation( operationCodes[i] ); 446 } catch ( InvalidParameterException e ) { 447 throw new XMLParsingException( e.getMessage() ); 448 } 449 } 450 451 return operations; 452 } 453 454 /** 455 * Returns the object representation of the <code>Filter_Capabilities</code> section of the document. 456 * 457 * @return class representation of the <code>Filter_Capabilities</code> section 458 * @throws XMLParsingException 459 */ 460 public FilterCapabilities getFilterCapabilities() 461 throws XMLParsingException { 462 463 FilterCapabilities filterCapabilities = null; 464 Element filterCapabilitiesElement = (Element) XMLTools.getNode( getRootElement(), "ogc:Filter_Capabilities", 465 nsContext ); 466 if ( filterCapabilitiesElement != null ) { 467 filterCapabilities = new FilterCapabilities100Fragment( filterCapabilitiesElement, getSystemId() ).parseFilterCapabilities(); 468 } 469 return filterCapabilities; 470 } 471 }