    001    // $HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.4_testing/src/org/deegree/owscommon/OWSCommonCapabilitiesDocument.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.owscommon;
    037
    038    import java.net.MalformedURLException;
    039    import java.net.URI;
    040    import java.net.URISyntaxException;
    041    import java.net.URL;
    042    import java.util.List;
    043    import java.util.Map;
    044
    045    import org.deegree.datatypes.Code;
    046    import org.deegree.datatypes.xlink.SimpleLink;
    047    import org.deegree.framework.util.StringTools;
    048    import org.deegree.framework.xml.ElementList;
    049    import org.deegree.framework.xml.XMLParsingException;
    050    import org.deegree.framework.xml.XMLTools;
    051    import org.deegree.model.metadata.iso19115.Address;
    052    import org.deegree.model.metadata.iso19115.ContactInfo;
    053    import org.deegree.model.metadata.iso19115.Keywords;
    054    import org.deegree.model.metadata.iso19115.OnlineResource;
    055    import org.deegree.model.metadata.iso19115.Phone;
    056    import org.deegree.model.metadata.iso19115.TypeCode;
    057    import org.deegree.model.spatialschema.Envelope;
    058    import org.deegree.model.spatialschema.GeometryFactory;
    059    import org.deegree.ogcbase.CommonNamespaces;
    060    import org.deegree.ogcwebservices.getcapabilities.DCPType;
    061    import org.deegree.ogcwebservices.getcapabilities.HTTP;
    062    import org.deegree.ogcwebservices.getcapabilities.OGCCapabilitiesDocument;
    063    import org.deegree.ogcwebservices.getcapabilities.Operation;
    064    import org.deegree.ogcwebservices.getcapabilities.Protocol;
    065    import org.deegree.ogcwebservices.getcapabilities.ServiceIdentification;
    066    import org.deegree.ogcwebservices.getcapabilities.ServiceProvider;
    067    import org.w3c.dom.Element;
    068    import org.w3c.dom.Node;
    069
    070    /**
    071     * Represents a configuration document for an OGC-Webservice according to the
    072     * <code>OWS Common Implementation Specification 0.3</code>.
    073     * <p>
    074     * It consists of the following elements: <table border="1">
    075     * <tr>
    076     * <th>Name</th>
    077     * <th>Function</th>
    078     * </tr>
    079     * <tr>
    080     * <td>ServiceIdentification</td>
    081     * <td>corresponds to and expands the SV_ServiceIdentification class in ISO 19119</td>
    082     * </tr>
    083     * <tr>
    084     * <td>ServiceProvider</td>
    085     * <td>corresponds to and expands the SV_ServiceProvider class in ISO 19119 </td>
    086     * </tr>
    087     * <tr>
    088     * <td>OperationsMetadata</td>
    089     * <td>contains set of Operation elements that each corresponds to and expand the
    090     * SV_OperationsMetadata class in ISO 19119</td>
    091     * </tr>
    092     * <tr>
    093     * <td>Contents</td>
    094     * <td>whenever relevant, contains set of elements that each corresponds to the
    095     * MD_DataIdentification class in ISO 19119 and 19115</td>
    096     * </tr>
    097     * </table>
    098     *
    099     * @author <a href="mailto:mschneider@lat-lon.de">Markus Schneider </a>
    100     * @author last edited by: $Author: mschneider $
    101     *
    102     * @version $Revision: 18195 $, $Date: 2009-06-18 17:55:39 +0200 (Do, 18. Jun 2009) $
    103     */
    104    public abstract class OWSCommonCapabilitiesDocument extends OGCCapabilitiesDocument {
    105
    106        /**
    107         *
    108         */
    109        public final static String ALL_NAME = "All";
    110
    111        /**
    112         *
    113         */
    114        public final static String SERVICE_IDENTIFICATION_NAME = "ServiceIdentification";
    115
    116        /**
    117         *
    118         */
    119        public final static String SERVICE_PROVIDER_NAME = "ServiceProvider";
    120
    121        /**
    122         *
    123         */
    124        public final static String OPERATIONS_METADATA_NAME = "OperationsMetadata";
    125
    126        /**
    127         *
    128         */
    129        public final static String CONTENTS_NAME = "Contents";
    130
    131        protected static final URI OWSNS = CommonNamespaces.OWSNS;
    132
    133        protected static final URI OGCNS = CommonNamespaces.OGCNS;
    134
    135        /**
    136         * Returns the class representation for the <code>ServiceProvider</code> section of the
    137         * document.
    138         *
    139         * @return class representation for the <code>ServiceProvider</code> section
    140         * @throws XMLParsingException
    141         */
    142        public ServiceProvider getServiceProvider()
    143                                throws XMLParsingException {
    144
    145            Element element = XMLTools.getRequiredChildElement( "ServiceProvider", OWSNS, getRootElement() );
    146
    147            // 'ProviderName' element (optional, default value: 'deegree')
    148            String providerName = XMLTools.getStringValue( "ProviderName", OWSNS, element, "deegree" );
    149
    150            // 'ProviderSite' element (optional)
    151            Element providerSiteElement = XMLTools.getChildElement( "ProviderSite", OWSNS, element );
    152            SimpleLink providerSite = null;
    153            if ( providerSiteElement != null ) {
    154                providerSite = parseSimpleLink( providerSiteElement );
    155            }
    156
    157            // 'ServiceContact' element (mandatory)
    158            Element serviceContactElement = XMLTools.getRequiredChildElement( "ServiceContact", OWSNS, element );
    159
    160            // 'IndividualName' element (optional)
    161            String individualName = XMLTools.getStringValue( "IndividualName", OWSNS, serviceContactElement, null );
    162
    163            // 'PositionName' element (optional)
    164            String positionName = XMLTools.getStringValue( "PositionName", OWSNS, serviceContactElement, null );
    165
    166            // 'ContactInfo' element (optional)
    167            ContactInfo contactInfo = null;
    168            Element contactInfoElement = XMLTools.getChildElement( "ContactInfo", OWSNS, serviceContactElement );
    169            if ( contactInfoElement != null ) {
    170                contactInfo = getContactInfo( contactInfoElement );
    171            }
    172            TypeCode role = null;
    173            Element roleElement = (Element) XMLTools.getNode( serviceContactElement, "ows:Role", nsContext );
    174            if ( roleElement != null ) {
    175                role = getCodeType( roleElement );
    176            }
    177            ServiceProvider serviceProvider = new ServiceProvider( providerName, providerSite, individualName,
    178                                                                   positionName, contactInfo, role );
    179
    180            return serviceProvider;
    181        }
    182
    183        /**
    184         * Returns the class representation for the <code>ServiceIdentification</code> section of the
    185         * document.
    186         *
    187         * @return class representation for the <code>ServiceIdentification</code> section
    188         * @throws XMLParsingException
    189         */
    190        public ServiceIdentification getServiceIdentification()
    191                                throws XMLParsingException {
    192
    193            Element element = XMLTools.getRequiredChildElement( "ServiceIdentification", OWSNS, getRootElement() );
    194
    195            // 'ServiceType' element (mandatory)
    196            Element serviceTypeElement = XMLTools.getRequiredChildElement( "ServiceType", OWSNS, element );
    197            Code serviceType = null;
    198            try {
    199                String codeSpace = XMLTools.getAttrValue( serviceTypeElement, OWSNS, "codeSpace", null );
    200                URI uri = codeSpace != null ? new URI( codeSpace ) : null;
    201                serviceType = new Code( XMLTools.getStringValue( serviceTypeElement ), uri );
    202            } catch ( URISyntaxException e ) {
    203                throw new XMLParsingException( "Given value '"
    204                                               + XMLTools.getAttrValue( serviceTypeElement, OWSNS, "codeSpace", null )
    205                                               + "' in attribute 'codeSpace' of element 'ServiceType' " + "(namespace: '"
    206                                               + OWSNS + "') is not a valid URI." );
    207            }
    208
    209            // 'ServiceTypeVersion' elements (mandatory)
    210            String[] serviceTypeVersions = XMLTools.getRequiredNodeAsStrings( element, "ows:ServiceTypeVersion", nsContext,
    211                                                                              ",;" );
    212
    213            // 'Title' element (mandatory)
    214            String title = XMLTools.getRequiredStringValue( "Title", OWSNS, element );
    215
    216            // 'Abstract' element (optional)
    217            String serviceAbstract = XMLTools.getRequiredStringValue( "Abstract", OWSNS, element );
    218
    219            // 'Keywords' elements (optional)
    220            List<Element> keywordsList = XMLTools.getElements( element, "ows:Keywords", nsContext );
    221            Keywords[] keywords = getKeywords( keywordsList );
    222
    223            // 'Fees' element (optional)
    224            String fees = XMLTools.getStringValue( "Fees", OWSNS, element, null );
    225
    226            // 'AccessConstraints' elements (optional)
    227            String accessConstraints[] = XMLTools.getNodesAsStrings( element, "ows:AccessConstraints", nsContext );
    228
    229            ServiceIdentification serviceIdentification = new ServiceIdentification( serviceType, serviceTypeVersions,
    230                                                                                     title, serviceAbstract, keywords,
    231                                                                                     fees, accessConstraints );
    232
    233            return serviceIdentification;
    234        }
    235
    236        /**
    237         * Creates a <code>Keywords</code> instance from the given element of type
    238         * <code>ows:KeywordsType</code>.
    239         *
    240         * NOTE: This method is redefined here (it is already defined in <code>OGCDocument</code>),
    241         * because the spelling of the first letter ('K') changed in the OWS Common Implementation
    242         * Specification 0.2 from lowercase to uppercase.
    243         *
    244         * @param element
    245         * @return created <code>Keywords</code>
    246         * @throws XMLParsingException
    247         */
    248        protected Keywords getKeywords( Element element )
    249                                throws XMLParsingException {
    250            TypeCode codeType = null;
    251            Element codeTypeElement = (Element) XMLTools.getNode( element, "ows:Type", nsContext );
    252            if ( codeTypeElement != null ) {
    253                codeType = getCodeType( codeTypeElement );
    254            }
    255            Keywords keywords = new Keywords( XMLTools.getNodesAsStrings( element, "ows:Keyword/text()", nsContext ), null,
    256                                              codeType );
    257            return keywords;
    258        }
    259
    260        /**
    261         * Creates an array of <code> Keywords </code> instances from the passed list of elements of
    262         * type <code> ows:KeywordsType </code>.
    263         *
    264         * This may appear to be pretty superfluous (as one <code> ows:KeywordsType
    265         * </code> can hold
    266         * several elements of type <code> ows:Keyword
    267         * </code>.
    268         *
    269         * @param nl
    270         *            may be null
    271         * @return created array of <code> Keywords </code>, null if <code>NodeList</code> constains
    272         *         zero elements
    273         * @throws XMLParsingException
    274         */
    275        public Keywords[] getKeywords( List nl )
    276                                throws XMLParsingException {
    277            Keywords[] kws = null;
    278            if ( nl.size() > 0 ) {
    279                kws = new Keywords[nl.size()];
    280                for ( int i = 0; i < kws.length; i++ ) {
    281                    kws[i] = getKeywords( (Element) nl.get( i ) );
    282                }
    283            }
    284            return kws;
    285        }
    286
    287        /**
    288         * Creates a <code>DCPType</code> object from the passed <code>DCP</code> element.
    289         * <p>
    290         * NOTE: Currently the <code>OnlineResources</code> included in the <code>DCPType</code> are
    291         * just stored as simple <code>URLs</code> (not as <code>OnLineResource</code> instances)!
    292         * <p>
    293         * NOTE: In an <code>OGCStandardCapabilitiesDocument</code> the <code>XLinks</code> (the
    294         * <code>URLs</code>) are stored in separate elements (<code>OnlineResource</code>), in
    295         * an <code>OGCCommonCapabilitiesDocument</code> they are the
    296         * <code>Get<code>/<code>Post</code> elements themselves.
    297         *
    298         * @param element
    299         * @return created <code>DCPType</code>
    300         * @throws XMLParsingException
    301         * @see org.deegree.ogcwebservices.getcapabilities.OGCStandardCapabilities
    302         */
    303        protected DCPType getDCP( Element element )
    304                                throws XMLParsingException {
    305
    306            DCPType dcpType = null;
    307            try {
    308                Element elem = (Element) XMLTools.getRequiredNode( element, "ows:HTTP", nsContext );
    309                List<Node> nl = XMLTools.getNodes( elem, "ows:Get", nsContext );
    310
    311                URL[] get = new URL[nl.size()];
    312                for ( int i = 0; i < get.length; i++ ) {
    313                    String s = XMLTools.getNodeAsString( nl.get( i ), "./@xlink:href", nsContext, null );
    314                    if ( s == null ) {
    315                        s = XMLTools.getRequiredNodeAsString( nl.get( i ), "./ows:OnlineResource/@xlink:href", nsContext );
    316                    }
    317                    get[i] = new URL( s );
    318                }
    319                nl = XMLTools.getNodes( elem, "ows:Post", nsContext );
    320
    321                URL[] post = new URL[nl.size()];
    322                for ( int i = 0; i < post.length; i++ ) {
    323                    String s = XMLTools.getNodeAsString( nl.get( i ), "./@xlink:href", nsContext, null );
    324                    if ( s == null ) {
    325                        s = XMLTools.getRequiredNodeAsString( nl.get( i ), "./ows:OnlineResource/@xlink:href", nsContext );
    326                    }
    327                    post[i] = new URL( s );
    328                }
    329                Protocol protocol = new HTTP( get, post );
    330                dcpType = new DCPType( protocol );
    331            } catch ( MalformedURLException e ) {
    332                throw new XMLParsingException( "Couldn't parse DCPType onlineresource URL about: "
    333                                               + StringTools.stackTraceToString( e ) );
    334            }
    335
    336            return dcpType;
    337        }
    338
    339        /**
    340         * Creates an array of <code>DCPType</code> objects from the passed element list.
    341         * <p>
    342         * NOTE: Currently the <code>OnlineResources</code> included in the <code>DCPType</code> are
    343         * just stored as simple <code>URLs</code> (not as <code>OnLineResource</code> instances)!
    344         *
    345         * @param el
    346         * @return array of <code>DCPType</code>
    347         * @throws XMLParsingException
    348         */
    349        protected DCPType[] getDCPs( List<Element> el )
    350                                throws XMLParsingException {
    351
    352            DCPType[] dcpTypes = new DCPType[el.size()];
    353            for ( int i = 0; i < dcpTypes.length; i++ ) {
    354                dcpTypes[i] = getDCP( el.get( i ) );
    355            }
    356
    357            return dcpTypes;
    358        }
    359
    360        /**
    361         * Creates a class representation of an <code>ows:Operation</code>- element.
    362         *
    363         * @param name
    364         * @param isMandatory
    365         * @param operations
    366         * @return operation
    367         * @throws XMLParsingException
    368         */
    369        protected Operation getOperation( String name, boolean isMandatory, Map operations )
    370                                throws XMLParsingException {
    371
    372            Operation operation = null;
    373            Element operationElement = (Element) operations.get( name );
    374            if ( operationElement == null ) {
    375                if ( isMandatory ) {
    376                    throw new XMLParsingException( "Mandatory operation '" + name
    377                                                   + "' not defined in 'OperationsMetadata'-section." );
    378                }
    379            } else {
    380                // "ows:Parameter"-elements
    381                ElementList parameterElements = XMLTools.getChildElements( "Parameter", OWSNS, operationElement );
    382                OWSDomainType[] parameters = new OWSDomainType[parameterElements.getLength()];
    383                for ( int i = 0; i < parameters.length; i++ ) {
    384                    parameters[i] = getOWSDomainType( name, parameterElements.item( i ) );
    385                }
    386                DCPType[] dcps = getDCPs( XMLTools.getRequiredElements( operationElement, "ows:DCP", nsContext ) );
    387                operation = new Operation( name, dcps, parameters );
    388
    389            }
    390
    391            return operation;
    392        }
    393
    394        /**
    395         *
    396         * @param root
    397         * @return constraints as array of OWSDomainType
    398         * @throws XMLParsingException
    399         */
    400        protected OWSDomainType[] getContraints( Element root )
    401                                throws XMLParsingException {
    402
    403            OWSDomainType[] contraints = null;
    404            // "ows:Contraint"-elements
    405            ElementList contraintElements = XMLTools.getChildElements( "Constraint", OWSNS, root );
    406            contraints = new OWSDomainType[contraintElements.getLength()];
    407            for ( int i = 0; i < contraints.length; i++ ) {
    408                contraints[i] = getOWSDomainType( null, contraintElements.item( i ) );
    409            }
    410
    411            return contraints;
    412        }
    413
    414        /**
    415         * Creates a class representation of an element of type <code>ows:DomainType</code>.
    416         *
    417         * @param element
    418         * @return domainType
    419         * @throws XMLParsingException
    420         */
    421        protected OWSDomainType getOWSDomainType( String opname, Element element )
    422                                throws XMLParsingException {
    423
    424            // "name"-attribute
    425            String name = XMLTools.getRequiredNodeAsString( element, "@name", nsContext );
    426
    427            // "ows:Value"-elements
    428            String[] values = XMLTools.getNodesAsStrings( element, "ows:Value/text()", nsContext );
    429            if ( values.length < 1 ) {
    430                throw new XMLParsingException( "At least one 'ows:Value'-element must be defined in each "
    431                                               + "element of type 'ows:DomainType'." );
    432            }
    433
    434            // TODO: "ows:Metadata"-elements
    435            OWSDomainType domainType = new OWSDomainType( name, values, null );
    436
    437            return domainType;
    438        }
    439
    440        /**
    441         * Creates a class representation of an element of type <code>ows:CodeType</code>.
    442         *
    443         * @param element
    444         *            an ows:CodeType element
    445         * @return the TypeCode (which is defined as something like a dictionary, thesaurus etc.)
    446         * @throws XMLParsingException
    447         */
    448        protected TypeCode getCodeType( Element element )
    449                                throws XMLParsingException {
    450
    451            String code = XMLTools.getRequiredNodeAsString( element, "text()", nsContext );
    452
    453            URI codeSpace = null;
    454            String codeSpaceString = XMLTools.getNodeAsString( element, "@codeSpace", nsContext, null );
    455            if ( codeSpaceString != null ) {
    456                try {
    457                    codeSpace = new URI( codeSpaceString );
    458                } catch ( URISyntaxException e ) {
    459                    throw new XMLParsingException( "'" + codeSpaceString + "' does not denote a valid URI in: "
    460                                                   + e.getMessage() );
    461                }
    462            }
    463            return new TypeCode( code, codeSpace );
    464        }
    465
    466        /**
    467         * Creates a <code>ContactInfo</code> object from the given element of type
    468         * <code>ows:ContactInfoType</code>.
    469         *
    470         * @param element
    471         * @return ContactInfo
    472         * @throws XMLParsingException
    473         */
    474        private ContactInfo getContactInfo( Element element )
    475                                throws XMLParsingException {
    476
    477            // 'Phone' element (optional)
    478            Phone phone = null;
    479            Element phoneElement = XMLTools.getChildElement( "Phone", OWSNS, element );
    480            if ( phoneElement != null ) {
    481                phone = parsePhone( phoneElement, OWSNS );
    482            }
    483
    484            // 'Address' element (optional)
    485            Address address = null;
    486            Element addressElement = XMLTools.getChildElement( "Address", OWSNS, element );
    487            if ( addressElement != null ) {
    488                address = parseAddress( addressElement, OWSNS );
    489            }
    490
    491            // 'OnlineResource' element (optional)
    492            OnlineResource onlineResource = null;
    493            Element onlineResourceElement = XMLTools.getChildElement( "OnlineResource", OWSNS, element );
    494            if ( onlineResourceElement != null ) {
    495                onlineResource = parseOnLineResource( onlineResourceElement );
    496            }
    497
    498            String hoursOfService = XMLTools.getNodeAsString( element, "ows:HoursOfService/text()", nsContext, null );
    499            String contactInstructions = XMLTools.getNodeAsString( element, "ows:ContactInstructions/text()", nsContext,
    500                                                                   null );
    501
    502            return new ContactInfo( address, contactInstructions, hoursOfService, onlineResource, phone );
    503        }
    504
    505        /**
    506         * Creates an <code>Envelope</code> object from the given element of type
    507         * <code>ows:WGS84BoundingBoxType</code>.
    508         *
    509         * @param element
    510         * @return an <code>Envelope</code> object
    511         * @throws XMLParsingException
    512         */
    513        protected Envelope getWGS84BoundingBoxType( Element element )
    514                                throws XMLParsingException {
    515            double[] lowerCorner = XMLTools.getRequiredNodeAsDoubles( element, "ows:LowerCorner/text()", nsContext, " " );
    516            if ( lowerCorner.length != 2 ) {
    517                throw new XMLParsingException( "Element 'ows:LowerCorner' must contain exactly two double values." );
    518            }
    519            double[] upperCorner = XMLTools.getRequiredNodeAsDoubles( element, "ows:UpperCorner/text()", nsContext, " " );
    520            if ( upperCorner.length != 2 ) {
    521                throw new XMLParsingException( "Element 'ows:UpperCorner' must contain exactly two double values." );
    522            }
    523            return GeometryFactory.createEnvelope( lowerCorner[0], lowerCorner[1], upperCorner[0], upperCorner[1], null );
    524        }
    525    }