001    // $HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/ogcwebservices/wfs/capabilities/WFSCapabilitiesDocument.java $
002    /*----------------    FILE HEADER  ------------------------------------------
003    
004     This file is part of deegree.
005     Copyright (C) 2001-2008 by:
006     EXSE, Department of Geography, University of Bonn
007     http://www.giub.uni-bonn.de/deegree/
008     lat/lon GmbH
009     http://www.lat-lon.de
010    
011     This library is free software; you can redistribute it and/or
012     modify it under the terms of the GNU Lesser General Public
013     License as published by the Free Software Foundation; either
014     version 2.1 of the License, or (at your option) any later version.
015    
016     This library is distributed in the hope that it will be useful,
017     but WITHOUT ANY WARRANTY; without even the implied warranty of
018     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
019     Lesser General Public License for more details.
020    
021     You should have received a copy of the GNU Lesser General Public
022     License along with this library; if not, write to the Free Software
023     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
024    
025     Contact:
026    
027     Andreas Poth
028     lat/lon GmbH
029     Aennchenstr. 19
030     53115 Bonn
031     Germany
032     E-Mail: poth@lat-lon.de
033    
034     Prof. Dr. Klaus Greve
035     Department of Geography
036     University of Bonn
037     Meckenheimer Allee 166
038     53115 Bonn
039     Germany
040     E-Mail: greve@giub.uni-bonn.de
041     
042     ---------------------------------------------------------------------------*/
043    package org.deegree.ogcwebservices.wfs.capabilities;
044    
045    import java.io.IOException;
046    import java.net.MalformedURLException;
047    import java.net.URI;
048    import java.net.URISyntaxException;
049    import java.net.URL;
050    import java.security.InvalidParameterException;
051    import java.util.ArrayList;
052    import java.util.HashMap;
053    import java.util.List;
054    import java.util.Map;
055    
056    import org.deegree.datatypes.Code;
057    import org.deegree.datatypes.QualifiedName;
058    import org.deegree.framework.log.ILogger;
059    import org.deegree.framework.log.LoggerFactory;
060    import org.deegree.framework.util.StringTools;
061    import org.deegree.framework.xml.XMLParsingException;
062    import org.deegree.framework.xml.XMLTools;
063    import org.deegree.model.filterencoding.capabilities.FilterCapabilities;
064    import org.deegree.model.filterencoding.capabilities.FilterCapabilities110Fragment;
065    import org.deegree.model.metadata.iso19115.Keywords;
066    import org.deegree.model.spatialschema.Envelope;
067    import org.deegree.ogcbase.CommonNamespaces;
068    import org.deegree.ogcwebservices.getcapabilities.InvalidCapabilitiesException;
069    import org.deegree.ogcwebservices.getcapabilities.MetadataURL;
070    import org.deegree.ogcwebservices.getcapabilities.OGCCapabilities;
071    import org.deegree.ogcwebservices.getcapabilities.Operation;
072    import org.deegree.ogcwebservices.getcapabilities.OperationsMetadata;
073    import org.deegree.ogcwebservices.getcapabilities.ServiceIdentification;
074    import org.deegree.owscommon.OWSCommonCapabilitiesDocument;
075    import org.deegree.owscommon.OWSDomainType;
076    import org.w3c.dom.Document;
077    import org.w3c.dom.Element;
078    import org.w3c.dom.Node;
079    import org.xml.sax.SAXException;
080    
081    /**
082     * Represents a capabilities document for an OGC WFS 1.1.0 compliant web service.
083     * 
084     * @author <a href="mailto:mschneider@lat-lon.de">Markus Schneider </a>
085     * @author last edited by: $Author: apoth $
086     * 
087     * @version $Revision: 9345 $, $Date: 2007-12-27 17:22:25 +0100 (Do, 27 Dez 2007) $
088     */
089    public class WFSCapabilitiesDocument extends OWSCommonCapabilitiesDocument {
090    
091        private static final long serialVersionUID = 6664839532969382269L;
092        private static ILogger LOG = LoggerFactory.getLogger( WFSCapabilitiesDocument.class );
093    
094        /**
095         * The "FeatureTypeList" string.
096         */
097        public final static String FEATURE_TYPE_LIST_NAME = "FeatureTypeList";
098    
099        /**
100         * The "ServesGMLObjectTypeList" string.
101         */
102        public final static String SERVES_GML_OBJECT_TYPE_LIST_NAME = "ServesGMLObjectTypeList";
103    
104        /**
105         * The "SupportsGMLObjectTypeList" string.
106         */
107        public final static String SUPPORTS_GML_OBJECT_TYPE_LIST_NAME = "SupportsGMLObjectTypeList";
108    
109        /**
110         * The "FilterCapabilities" string.
111         */
112        public final static String FILTER_CAPABILITIES_NAME = "FilterCapabilities";
113    
114        protected static final URI WFSNS = CommonNamespaces.WFSNS;
115        private static final String PRE_WFS = CommonNamespaces.WFS_PREFIX +":";
116        
117        private static final String PRE_OWS = CommonNamespaces.OWS_PREFIX +":";
118    
119        protected static final URI OGCNS = CommonNamespaces.OGCNS;
120    
121        protected static final URI DEEGREEWFSNS = CommonNamespaces.DEEGREEWFS;
122    
123        private static final String XML_TEMPLATE = "WFSCapabilitiesTemplate.xml";
124    
125        private static final String[] VALID_TYPES = { "TDC211", "FGDC", "19115", "19139" };
126    
127        private static final String[] VALID_FORMATS = { "text/xml", "text/html", "text/sgml",
128                                                       "text/plain" };
129    
130    
131        /**
132         * Creates a skeleton capabilities document that contains the mandatory elements only.
133         * 
134         * @throws IOException
135         * @throws SAXException
136         */
137        public void createEmptyDocument() throws IOException,
138                                         SAXException {
139            URL url = WFSCapabilitiesDocument.class.getResource( XML_TEMPLATE );
140            if ( url == null ) {
141                throw new IOException( "The resource '" + XML_TEMPLATE + " could not be found." );
142            }
143            load( url );
144        }
145        
146        /**
147         * Creates an emptyDocument with given version, and an updateSequence of "0" without reading the skeleton document.
148         * @param version if 
149         * 
150         */
151        public void createEmptyDocument(String version) {
152            //      set up the root document.
153            Document doc = XMLTools.create();
154            Element root = doc.createElementNS( "http://www.opengis.net/wfs", PRE_WFS + "WFS_Capabilities" );
155            doc.importNode( root, false );
156    
157            setRootElement( root );
158            root.setAttribute( "version", version );
159            root.setAttribute( "updateSequence", "0" );
160        }
161    
162        /**
163         * Creates a class representation of the document.
164         * 
165         * @return class representation of the configuration document
166         */
167        @Override
168        public OGCCapabilities parseCapabilities() throws InvalidCapabilitiesException {
169    
170            WFSCapabilities wfsCapabilities = null;
171            try {
172    
173                wfsCapabilities = new WFSCapabilities( parseVersion(), parseUpdateSequence(),
174                                                       getServiceIdentification(),
175                                                       getServiceProvider(), getOperationsMetadata(),
176                                                       getFeatureTypeList(),
177                                                       getServesGMLObjectTypeList(),
178                                                       getSupportsGMLObjectTypeList(), null,
179                                                       getFilterCapabilities() );
180            } catch ( XMLParsingException e ) {
181                throw new InvalidCapabilitiesException( e.getMessage() + "\n"
182                                                        + StringTools.stackTraceToString( e ) );
183            }
184    
185            return wfsCapabilities;
186        }
187    
188        /**
189         * Returns the class representation for the <code>ServiceIdentification</code> section of the
190         * document. <p> NOTE: this method is overridden, because the WFS 1.1.0 requires the OWS 1.0.0
191         * version of the element
192         * 
193         * @return class representation for the <code>ServiceIdentification</code> section
194         * @throws XMLParsingException
195         */
196        @Override
197        public ServiceIdentification getServiceIdentification() throws XMLParsingException {
198    
199    //        Element element = XMLTools.getRequiredChildElement( "ServiceIdentification", OWSNS,
200    //                                                            getRootElement() );
201          Element element = XMLTools.getRequiredElement( getRootElement(),  PRE_OWS+"ServiceIdentification", nsContext );
202    
203            // 'ServiceType' element (mandatory)
204    //        Element serviceTypeElement = XMLTools.getRequiredChildElement( "ServiceType", OWSNS,
205    //                                                                       element );
206            Element serviceTypeElement = XMLTools.getRequiredElement( element, PRE_OWS+"ServiceType", nsContext);
207            Code serviceType = null;
208            try {
209                String codeSpace = XMLTools.getAttrValue( serviceTypeElement, OWSNS, "codeSpace", null );
210                URI uri = codeSpace != null ? new URI( codeSpace ) : null;
211                serviceType = new Code( XMLTools.getStringValue( serviceTypeElement ), uri );
212            } catch ( URISyntaxException e ) {
213                throw new XMLParsingException( "Given value '"
214                                               + XMLTools.getAttrValue( serviceTypeElement, OWSNS,
215                                                                        "codeSpace", null )
216                                               + "' in attribute 'codeSpace' of element 'ServiceType' "
217                                               + "(namespace: '" + OWSNS + "') is not a valid URI." );
218            }
219    
220            // 'ServiceTypeVersion' elements (mandatory)
221            String[] serviceTypeVersions = XMLTools.getRequiredNodeAsStrings( element,
222                                                                              "ows:ServiceTypeVersion",
223                                                                              nsContext, ",;" );
224            if ( serviceTypeVersions.length == 0 ) {
225                String msg = "No version specified in 'ows:ServiceTypeVersion' element.";
226                throw new XMLParsingException( msg );
227            }
228    
229            // 'Fees' element (optional)
230            String fees = XMLTools.getStringValue( "Fees", OWSNS, element, null );
231    
232            // 'AccessConstraints' elements (optional)
233            String accessConstraints[] = XMLTools.getNodesAsStrings( element, "ows:AccessConstraints",
234                                                                     nsContext );
235    
236            String title = XMLTools.getNodeAsString( element, "ows:Title", nsContext, null);
237            String abs = XMLTools.getNodeAsString( element, "ows:Abstract", nsContext, null);
238            
239            Keywords[] kws = getKeywords( XMLTools.getElements( element, "ows:Keywords", nsContext ) );
240            
241            ServiceIdentification serviceIdentification = new ServiceIdentification(
242                                                                                     serviceType,
243                                                                                     serviceTypeVersions,
244                                                                                     title, abs, kws,
245                                                                                     fees,
246                                                                                     accessConstraints );
247            return serviceIdentification;
248        }
249    
250        /**
251         * Creates an object representation of the <code>ows:OperationsMetadata</code> section.
252         * 
253         * @return object representation of the <code>ows:OperationsMetadata</code> section
254         * @throws XMLParsingException
255         */
256        public OperationsMetadata getOperationsMetadata() throws XMLParsingException {
257    
258            List<Node> operationElementList = XMLTools.getNodes( getRootElement(),
259                                                              "ows:OperationsMetadata/ows:Operation",
260                                                              nsContext );
261    
262            // build HashMap of 'ows:Operation'-elements for easier access
263            Map<String, Node> operations = new HashMap<String, Node>();
264            for ( int i = 0; i < operationElementList.size(); i++ ) {
265                operations.put( XMLTools.getRequiredNodeAsString(  operationElementList.get( i ),
266                                                                  "@name", nsContext ),
267                                operationElementList.get( i ) );
268            }
269    
270            Operation getCapabilities = getOperation( OperationsMetadata.GET_CAPABILITIES_NAME, true,
271                                                      operations );
272            Operation describeFeatureType = getOperation(
273                                                          WFSOperationsMetadata.DESCRIBE_FEATURETYPE_NAME,
274                                                          true, operations );
275            Operation getFeature = getOperation( WFSOperationsMetadata.GET_FEATURE_NAME, false,
276                                                 operations );
277            Operation getFeatureWithLock = getOperation(
278                                                         WFSOperationsMetadata.GET_FEATURE_WITH_LOCK_NAME,
279                                                         false, operations );
280            Operation getGMLObject = getOperation( WFSOperationsMetadata.GET_GML_OBJECT_NAME, false,
281                                                   operations );
282            Operation lockFeature = getOperation( WFSOperationsMetadata.LOCK_FEATURE_NAME, false,
283                                                  operations );
284            Operation transaction = getOperation( WFSOperationsMetadata.TRANSACTION_NAME, false,
285                                                  operations );
286    
287            List parameterElementList = XMLTools.getNodes( getRootElement(),
288                                                           "ows:OperationsMetadata/ows:Parameter",
289                                                           nsContext );
290            OWSDomainType[] parameters = new OWSDomainType[parameterElementList.size()];
291            for ( int i = 0; i < parameters.length; i++ ) {
292                parameters[i] = getOWSDomainType( null, (Element) parameterElementList.get( i ) );
293            }
294    
295            List constraintElementList = XMLTools.getNodes( getRootElement(),
296                                                            "ows:OperationsMetadata/ows:Constraint",
297                                                            nsContext );
298            OWSDomainType[] constraints = new OWSDomainType[constraintElementList.size()];
299            for ( int i = 0; i < constraints.length; i++ ) {
300                constraints[i] = getOWSDomainType( null, (Element) constraintElementList.get( i ) );
301            }
302            WFSOperationsMetadata metadata = new WFSOperationsMetadata( getCapabilities,
303                                                                        describeFeatureType,
304                                                                        getFeature, getFeatureWithLock,
305                                                                        getGMLObject, lockFeature,
306                                                                        transaction, parameters,
307                                                                        constraints );
308    
309            return metadata;
310        }
311    
312        /**
313         * Returns the object representation for the <code>wfs:FeatureTypeList</code>- section.
314         * 
315         * @return object representation of the <code>wfs:FeatureTypeList</code> section, may be empty
316         *         (if missing)
317         * @throws XMLParsingException
318         */
319        public FeatureTypeList getFeatureTypeList() throws XMLParsingException {
320    
321            List<WFSFeatureType> wfsFeatureTypes = new ArrayList<WFSFeatureType>();
322    
323            FeatureTypeList featureTypeList = new FeatureTypeList(
324                                                                   new org.deegree.ogcwebservices.wfs.capabilities.Operation[0],
325                                                                   wfsFeatureTypes );
326    
327            Element element = (Element) XMLTools.getNode( getRootElement(), "wfs:FeatureTypeList",
328                                                          nsContext );
329            if ( element != null ) {
330                org.deegree.ogcwebservices.wfs.capabilities.Operation[] globalOperations = null;
331                Element operationsTypeElement = (Element) XMLTools.getNode( element, "wfs:Operations",
332                                                                            nsContext );
333                if ( operationsTypeElement != null ) {
334                    globalOperations = getOperationsType( operationsTypeElement );
335                }
336                List featureTypeElementList = XMLTools.getNodes( element, "wfs:FeatureType", nsContext );
337                // TODO Check this.
338                // if ( featureTypeElementList.getLength() < 1 ) {
339                // throw new XMLParsingException(
340                // "A wfs:FeatureTypeListType must contain at least one wfs:FeatureType-element." );
341                // }
342                for ( int i = 0; i < featureTypeElementList.size(); i++ ) {
343                    WFSFeatureType wfsFT = getFeatureTypeType( (Element) featureTypeElementList.get( i ) );
344                    wfsFeatureTypes.add( wfsFT );
345                }
346    
347                featureTypeList = new FeatureTypeList( globalOperations, wfsFeatureTypes );
348            }
349    
350            return featureTypeList;
351        }
352    
353        /**
354         * Returns the object representation for the <code>wfs:ServesGMLObjectTypeList</code>-
355         * section.
356         * 
357         * @return object representation of the <code>wfs:ServesGMLObjectTypeList</code> section, null
358         *         if the section does not exist
359         * @throws XMLParsingException
360         */
361        public GMLObject[] getServesGMLObjectTypeList() throws XMLParsingException {
362    
363            GMLObject[] gmlObjectTypes = null;
364            Element element = (Element) XMLTools.getNode( getRootElement(),
365                                                          "wfs:ServesGMLObjectTypeList", nsContext );
366            if ( element != null ) {
367                List nodeList = XMLTools.getRequiredNodes( element, "wfs:GMLObjectType", nsContext );
368                gmlObjectTypes = new GMLObject[nodeList.size()];
369                for ( int i = 0; i < gmlObjectTypes.length; i++ ) {
370                    gmlObjectTypes[i] = getGMLObjectType( (Element) nodeList.get( i ) );
371                }
372            }
373    
374            return gmlObjectTypes;
375        }
376    
377        /**
378         * Returns the object representation for the <code>wfs:SupportsGMLObjectTypeList</code>-
379         * section.
380         * 
381         * @return object representation of the <code>wfs:SupportsGMLObjectTypeList</code> section,
382         *         null if the section does not exist
383         * @throws XMLParsingException
384         */
385        public GMLObject[] getSupportsGMLObjectTypeList() throws XMLParsingException {
386    
387            GMLObject[] gmlObjectTypes = null;
388            Element element = (Element) XMLTools.getNode( getRootElement(),
389                                                          "wfs:SupportsGMLObjectTypeList", nsContext );
390            if ( element != null ) {
391                List nodeList = XMLTools.getRequiredNodes( element, "wfs:GMLObjectType", nsContext );
392                gmlObjectTypes = new GMLObject[nodeList.size()];
393                for ( int i = 0; i < gmlObjectTypes.length; i++ ) {
394                    gmlObjectTypes[i] = getGMLObjectType( (Element) nodeList.get( i ) );
395                }
396            }
397    
398            return gmlObjectTypes;
399        }
400    
401        /**
402         * Returns the object representation for an element of type <code>wfs:GMLObjectType</code>.
403         * 
404         * @param element
405         * @return object representation of the element of type <code>wfs:GMLObjectType</code>
406         * @throws XMLParsingException
407         */
408        public GMLObject getGMLObjectType( Element element ) throws XMLParsingException {
409            QualifiedName name = parseQualifiedName( XMLTools.getRequiredNode( element,
410                                                                               "wfs:Name/text()",
411                                                                               nsContext ) );
412            String title = XMLTools.getNodeAsString( element, "wfs:Title/text()", nsContext, null );
413            String abstract_ = XMLTools.getNodeAsString( element, "wfs:Abstract/text()", nsContext,
414                                                         null );
415            Keywords[] keywords = getKeywords( XMLTools.getNodes( element, "ows:Keywords", nsContext ) );
416            List formatElementList = XMLTools.getNodes( element, "wfs:OutputFormats/wfs:Format",
417                                                        nsContext );
418            FormatType[] outputFormats = new FormatType[formatElementList.size()];
419            for ( int i = 0; i < outputFormats.length; i++ ) {
420                outputFormats[i] = getFormatType( (Element) formatElementList.get( i ) );
421            }
422            return new GMLObject( name, title, abstract_, keywords, outputFormats );
423        }
424    
425        /**
426         * Returns the object representation for an element of type <code>wfs:FeatureTypeType</code>.
427         * 
428         * @param element
429         * @return object representation for the element of type <code>wfs:OperationsType</code>
430         * @throws XMLParsingException
431         */
432        public WFSFeatureType getFeatureTypeType( Element element ) throws XMLParsingException {
433    
434            QualifiedName name = parseQualifiedName( XMLTools.getRequiredNode( element,
435                                                                               "wfs:Name/text()",
436                                                                               nsContext ) );
437            String title = XMLTools.getRequiredNodeAsString( element, "wfs:Title/text()", nsContext );
438            String abstract_ = XMLTools.getNodeAsString( element, "wfs:Abstract/text()", nsContext,
439                                                         null );
440            Keywords[] keywords = getKeywords( XMLTools.getNodes( element, "ows:Keywords", nsContext ) );
441    
442            URI defaultSrs = null;
443            URI[] otherSrs = null;
444            Node noSrsElement = XMLTools.getNode( element, "wfs:NoSRS", nsContext );
445            if ( noSrsElement == null ) {
446                defaultSrs = XMLTools.getNodeAsURI( element, "wfs:DefaultSRS/text()", nsContext, null );
447                if ( defaultSrs == null ) {
448                    String msg = "A 'wfs:FeatureType' element must always contain a 'wfs:NoSRS' "
449                                 + "element  or a 'wfs:DefaultSRS' element";
450                    throw new XMLParsingException( msg );
451                }
452                otherSrs = XMLTools.getNodesAsURIs( element, "wfs:OtherSRS/text()", nsContext );
453            }
454    
455            org.deegree.ogcwebservices.wfs.capabilities.Operation[] operations = null;
456            Element operationsTypeElement = (Element) XMLTools.getNode( element, "wfs:Operations",
457                                                                        nsContext );
458            if ( operationsTypeElement != null ) {
459                operations = getOperationsType( operationsTypeElement );
460            }
461            List formatElementList = XMLTools.getNodes( element, "wfs:OutputFormats/wfs:Format",
462                                                        nsContext );
463            FormatType[] formats = new FormatType[formatElementList.size()];
464            for ( int i = 0; i < formats.length; i++ ) {
465                formats[i] = getFormatType( (Element) formatElementList.get( i ) );
466            }
467            List wgs84BoundingBoxElements = XMLTools.getNodes( element, "ows:WGS84BoundingBox",
468                                                               nsContext );
469            if ( wgs84BoundingBoxElements.size() < 1 ) {
470                throw new XMLParsingException( "A 'wfs:FeatureTypeType' must contain at least one "
471                                               + "'ows:WGS84BoundingBox'-element." );
472            }
473            Envelope[] wgs84BoundingBoxes = new Envelope[wgs84BoundingBoxElements.size()];
474            for ( int i = 0; i < wgs84BoundingBoxes.length; i++ ) {
475                wgs84BoundingBoxes[i] = getWGS84BoundingBoxType( (Element) wgs84BoundingBoxElements.get( i ) );
476            }
477            List metadataURLElementList = XMLTools.getNodes( element, "wfs:MetadataURL", nsContext );
478            MetadataURL[] metadataUrls = new MetadataURL[metadataURLElementList.size()];
479            for ( int i = 0; i < metadataUrls.length; i++ ) {
480                metadataUrls[i] = getMetadataURL( (Element) metadataURLElementList.get( i ) );
481            }
482            WFSFeatureType featureType = new WFSFeatureType( name, title, abstract_, keywords,
483                                                             defaultSrs, otherSrs, operations, formats,
484                                                             wgs84BoundingBoxes, metadataUrls );
485    
486            return featureType;
487        }
488    
489        /**
490         * Returns the object representation for an <code>wfs:OutputFormat</code> -element.
491         * 
492         * @param element
493         * @return object representation for the element
494         * @throws XMLParsingException
495         */
496        public FormatType getFormatType( Element element ) throws XMLParsingException {
497    
498            String[] tmp = new String[3];
499            URI[] uris = new URI[3];
500            tmp[0] = XMLTools.getNodeAsString( element, "@deegreewfs:inFilter", nsContext,
501                                                        null );
502            tmp[1] = XMLTools.getNodeAsString( element, "@deegreewfs:outFilter", nsContext,
503                                               null );
504            tmp[2] = XMLTools.getNodeAsString( element, "@deegreewfs:schemaLocation",
505                                               nsContext, null );
506            for ( int i = 0; i < tmp.length; i++ ) {
507               try {
508                   if ( tmp[i] != null && !"".equals( tmp[i].trim() )) {
509                       if (  !( tmp[i].toLowerCase().startsWith( "file:/" ) ) ) {
510                           tmp[i] = this.resolve( tmp[i] ).toExternalForm();
511                           LOG.logDebug( "Found format "+ (( i == 0)?"inFilter":((i==1)?"outFilter":"schemaLocation")) +" at location: " + tmp[i] );
512                       }
513                       uris[i] = new URI( tmp[i] );
514                   }               
515                } catch ( MalformedURLException e ) {
516                    throw new XMLParsingException( "Could not resolve relative path:" + tmp[i] );
517                } catch ( URISyntaxException e ) {
518                    throw new XMLParsingException( "Not a valid URI:" + tmp[i] );
519                }
520            }
521            
522            String value = XMLTools.getRequiredNodeAsString( element, "text()", nsContext );
523            
524            return new FormatType( uris[0], uris[1], uris[2], value );
525        }
526    
527        /**
528         * Returns the object representation for an element node of type
529         * <code>wfs:MetadataURLType</code>.
530         * 
531         * TODO: Schema says base type is String, not URL!
532         * 
533         * @param element
534         * @return object representation for the element of type <code>wfs:MetadataURLType</code>
535         * @throws XMLParsingException
536         */
537        public MetadataURL getMetadataURL( Element element ) throws XMLParsingException {
538    
539            String type = XMLTools.getRequiredNodeAsString( element, "@type", nsContext, VALID_TYPES );
540            String format = XMLTools.getRequiredNodeAsString( element, "@format", nsContext,
541                                                              VALID_FORMATS );
542            String url = XMLTools.getRequiredNodeAsString( element, "text()", nsContext );
543            URL onlineResource;
544            try {
545                onlineResource = new URL( url );
546            } catch ( MalformedURLException e ) {
547                throw new XMLParsingException( "A wfs:MetadataURLType must contain a valid URL: "
548                                               + e.getMessage() );
549            }
550    
551            return new MetadataURL( type, format, onlineResource );
552        }
553    
554        /**
555         * Returns the object representation for an element node of type <code>wfs:OperationsType</code>.
556         * 
557         * @param element
558         * @return object representation for the element of type <code>wfs:OperationsType</code>
559         * @throws XMLParsingException
560         */
561        public org.deegree.ogcwebservices.wfs.capabilities.Operation[] getOperationsType(
562                                                                                         Element element ) throws XMLParsingException {
563    
564            String[] operationCodes = XMLTools.getNodesAsStrings( element, "wfs:Operation/text()",
565                                                                  nsContext );
566            org.deegree.ogcwebservices.wfs.capabilities.Operation[] operations = new org.deegree.ogcwebservices.wfs.capabilities.Operation[operationCodes.length];
567            for ( int i = 0; i < operations.length; i++ ) {
568                try {
569                    operations[i] = new org.deegree.ogcwebservices.wfs.capabilities.Operation(
570                                                                                               operationCodes[i] );
571                } catch ( InvalidParameterException e ) {
572                    throw new XMLParsingException( e.getMessage() );
573                }
574            }
575    
576            return operations;
577        }
578    
579        /**
580         * Returns the object representation for the <code>Filter_Capabilities</code> section of the
581         * document.
582         * 
583         * @return class representation for the <code>Filter_Capabilities</code> section
584         * @throws XMLParsingException
585         */
586        public FilterCapabilities getFilterCapabilities() throws XMLParsingException {
587    
588            FilterCapabilities filterCapabilities = null;
589            Element filterCapabilitiesElement = (Element) XMLTools.getNode( getRootElement(),
590                                                                            "ogc:Filter_Capabilities",
591                                                                            nsContext );
592            if ( filterCapabilitiesElement != null ) {
593                filterCapabilities = new FilterCapabilities110Fragment( filterCapabilitiesElement,
594                                                                        getSystemId() ).parseFilterCapabilities();
595            }
596            return filterCapabilities;
597        }
598    }