    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    }