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 }