    001    //$HeadURL: svn+ssh://developername@svn.wald.intevation.org/deegree/base/trunk/src/org/deegree/ogcwebservices/wms/capabilities/WMSCapabilitiesDocument.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.wms.capabilities;
    037
    038    import java.io.IOException;
    039    import java.net.MalformedURLException;
    040    import java.net.URI;
    041    import java.net.URISyntaxException;
    042    import java.net.URL;
    043    import java.util.ArrayList;
    044    import java.util.Arrays;
    045    import java.util.Date;
    046    import java.util.List;
    047
    048    import org.deegree.datatypes.Code;
    049    import org.deegree.datatypes.QualifiedName;
    050    import org.deegree.datatypes.values.TypedLiteral;
    051    import org.deegree.framework.log.ILogger;
    052    import org.deegree.framework.log.LoggerFactory;
    053    import org.deegree.framework.util.StringTools;
    054    import org.deegree.framework.xml.XMLParsingException;
    055    import org.deegree.framework.xml.XMLTools;
    056    import org.deegree.i18n.Messages;
    057    import org.deegree.model.crs.CRSFactory;
    058    import org.deegree.model.crs.CoordinateSystem;
    059    import org.deegree.model.crs.UnknownCRSException;
    060    import org.deegree.model.metadata.iso19115.Constraints;
    061    import org.deegree.model.metadata.iso19115.Keywords;
    062    import org.deegree.model.metadata.iso19115.Linkage;
    063    import org.deegree.model.metadata.iso19115.OnlineResource;
    064    import org.deegree.model.spatialschema.Envelope;
    065    import org.deegree.model.spatialschema.GeometryFactory;
    066    import org.deegree.model.spatialschema.Position;
    067    import org.deegree.ogcwebservices.getcapabilities.InvalidCapabilitiesException;
    068    import org.deegree.ogcwebservices.getcapabilities.OGCCapabilities;
    069    import org.deegree.owscommon_new.DCP;
    070    import org.deegree.owscommon_new.DomainType;
    071    import org.deegree.owscommon_new.HTTP;
    072    import org.deegree.owscommon_new.Operation;
    073    import org.deegree.owscommon_new.OperationsMetadata;
    074    import org.deegree.owscommon_new.Parameter;
    075    import org.deegree.owscommon_new.ServiceIdentification;
    076    import org.deegree.owscommon_new.ServiceProvider;
    077    import org.w3c.dom.Element;
    078    import org.w3c.dom.Node;
    079    import org.xml.sax.SAXException;
    080
    081    /**
    082     * <code>WMSCapabilitiesDocument</code> is the parser class for WMS capabilities documents that
    083     * uses the new OWS common classes to encapsulate the data.
    084     *
    085     * @author <a href="mailto:schmitz@lat-lon.de">Andreas Schmitz</a>
    086     * @author last edited by: $Author: aschmitz $
    087     *
    088     * @version 2.0, $Revision: 8220 $, $Date: 2007-09-28 15:26:08 +0200 (Fr, 28 Sep 2007) $
    089     *
    090     * @since 2.0
    091     */
    092
    093    public class WMSCapabilitiesDocument_1_0_0 extends WMSCapabilitiesDocument {
    094
    095        private static final long serialVersionUID = 4689978960047737035L;
    096
    097        private static final String XML_TEMPLATE = "WMSCapabilitiesTemplate.xml";
    098
    099        private static final ILogger LOG = LoggerFactory.getLogger( WMSCapabilitiesDocument_1_0_0.class );
    100
    101        /**
    102         * Creates a skeleton capabilities document that contains the mandatory elements only.
    103         *
    104         * @throws IOException
    105         * @throws SAXException
    106         */
    107        @Override
    108        public void createEmptyDocument()
    109                                throws IOException, SAXException {
    110
    111            URL url = WMSCapabilitiesDocument_1_0_0.class.getResource( XML_TEMPLATE );
    112            if ( url == null ) {
    113                throw new IOException( "The resource '" + XML_TEMPLATE + " could not be found." );
    114            }
    115            load( url );
    116        }
    117
    118        /**
    119         *
    120         * @param elem
    121         * @return array of supported exception formats
    122         * @throws XMLParsingException
    123         */
    124        @Override
    125        protected List<String> parseExceptionFormats( Element elem )
    126                                throws XMLParsingException {
    127            List<Node> nodes = XMLTools.getRequiredNodes( elem, "Format", nsContext );
    128            String[] formats = new String[nodes.size()];
    129            for ( int i = 0; i < formats.length; i++ ) {
    130                formats[i] = nodes.get( i ).getLocalName();
    131            }
    132            return Arrays.asList( formats );
    133        }
    134
    135        /**
    136         * Creates a class representation of the document.
    137         *
    138         * @return class representation of the configuration document
    139         * @throws InvalidCapabilitiesException
    140         */
    141        @Override
    142        public OGCCapabilities parseCapabilities()
    143                                throws InvalidCapabilitiesException {
    144            ServiceIdentification serviceIdentification = null;
    145            ServiceProvider serviceProvider = null;
    146            OperationsMetadata metadata = null;
    147            Layer layer = null;
    148            List<String> exceptions;
    149
    150            String updateSeq = parseUpdateSequence();
    151            try {
    152                serviceIdentification = parseServiceIdentification();
    153                metadata = parseOperationsMetadata();
    154
    155                Element exceptionElement = XMLTools.getRequiredElement( getRootElement(), "Capability/Exception", nsContext );
    156                exceptions = parseExceptionFormats( exceptionElement );
    157
    158                Element layerElem = XMLTools.getRequiredElement( getRootElement(), "./Capability/Layer", nsContext );
    159                layer = parseLayers( layerElem, null, null );
    160            } catch ( XMLParsingException e ) {
    161                LOG.logError( e.getMessage(), e );
    162                throw new InvalidCapabilitiesException( e.getMessage() );
    163            } catch ( UnknownCRSException e ) {
    164                LOG.logError( e.getMessage(), e );
    165                throw new InvalidCapabilitiesException( e.getMessage() );
    166            }
    167
    168            return new WMSCapabilities_1_0_0( updateSeq, serviceIdentification, serviceProvider, metadata, layer,
    169                                              exceptions );
    170
    171        }
    172
    173        /**
    174         * returns the services indentification read from the WMS capabilities service section
    175         *
    176         * @throws XMLParsingException
    177         */
    178        @Override
    179        protected ServiceIdentification parseServiceIdentification()
    180                                throws XMLParsingException {
    181            String name = XMLTools.getNodeAsString( getRootElement(), "./Service/Name", nsContext, null );
    182            String title = XMLTools.getNodeAsString( getRootElement(), "./Service/Title", nsContext, name );
    183            String serviceAbstract = XMLTools.getNodeAsString( getRootElement(), "./Service/Abstract", nsContext, null );
    184
    185            String tmp = XMLTools.getNodeAsString( getRootElement(), "./Service/Keywords", nsContext, "" );
    186            String[] kw = StringTools.toArray( tmp, " ", false );
    187
    188            Keywords[] keywordArray = new Keywords[] { new Keywords( kw ) };
    189            List<Keywords> keywords = Arrays.asList( keywordArray );
    190
    191            String fees = XMLTools.getNodeAsString( getRootElement(), "./Service/Fees", nsContext, null );
    192
    193            List<Constraints> accessConstraints = new ArrayList<Constraints>();
    194
    195            String constraints = XMLTools.getNodeAsString( getRootElement(), "./Service/AccessConstraints", nsContext, null );
    196
    197            if ( constraints != null ) {
    198                List<String> limits = new ArrayList<String>();
    199                limits.add( constraints );
    200                accessConstraints.add( new Constraints( fees, null, null, null, limits, null, null, null ) );
    201            }
    202
    203            List<String> versions = new ArrayList<String>();
    204            versions.add( "1.0.0" );
    205
    206            return new ServiceIdentification( new Code( "OGC:WMS" ), versions, title, null,
    207                                              new Date( System.currentTimeMillis() ), title, serviceAbstract, keywords,
    208                                              accessConstraints );
    209
    210        }
    211
    212        /**
    213         * returns the services capabilitiy read from the WMS capabilities file
    214         *
    215         * @return the operations metadata
    216         * @throws XMLParsingException
    217         */
    218        @Override
    219        protected OperationsMetadata parseOperationsMetadata()
    220                                throws XMLParsingException {
    221
    222            Node opNode = XMLTools.getRequiredNode( getRootElement(), "./Capability/Request/Capabilities", nsContext );
    223
    224            Operation getCapa = parseOperation( opNode );
    225
    226            opNode = XMLTools.getRequiredNode( getRootElement(), "./Capability/Request/Map", nsContext );
    227
    228            Operation getMap = parseOperation( opNode );
    229
    230            Operation getFI = null;
    231            opNode = XMLTools.getNode( getRootElement(), "./Capability/Request/FeatureInfo", nsContext );
    232            if ( opNode != null ) {
    233                getFI = parseOperation( opNode );
    234            }
    235
    236            List<Operation> operations = new ArrayList<Operation>();
    237            if ( getCapa != null )
    238                operations.add( getCapa );
    239            if ( getMap != null )
    240                operations.add( getMap );
    241            if ( getFI != null )
    242                operations.add( getFI );
    243
    244            return new OperationsMetadata( null, null, operations, null );
    245
    246        }
    247
    248        /**
    249         * Creates an <tt>Operation</tt>-instance according to the contents of the DOM-subtree
    250         * starting at the given <tt>Node</tt>.
    251         * <p>
    252         * Notice: operation to be parsed must be operations in sense of WMS 1.0.0 - 1.3.0 and not as
    253         * defined in OWSCommons. But the method will return an OWSCommon Operation which encapsulates
    254         * parsed WMS operation
    255         * <p>
    256         *
    257         * @param node
    258         *            the <tt>Element</tt> that describes an <tt>Operation</tt>
    259         * @throws XMLParsingException
    260         *             if a syntactic or semantic error in the DOM-subtree is encountered
    261         * @return the constructed <tt>Operation</tt>-instance
    262         */
    263        @Override
    264        protected Operation parseOperation( Node node )
    265                                throws XMLParsingException {
    266            // use node name as name of the Operation to be defined
    267            String name = node.getNodeName();
    268            if ( name.equals( "Capabilities" ) ) {
    269                name = "GetCapabilities";
    270            } else if ( name.equals( "Map" ) ) {
    271                name = "GetMap";
    272            } else if ( name.equals( "FeatureInfo" ) ) {
    273                name = "GetFeatureInfo";
    274            }
    275
    276            List<Node> nodes = XMLTools.getRequiredNodes( node, "./Format", nsContext );
    277
    278            List<TypedLiteral> values = new ArrayList<TypedLiteral>();
    279
    280            URI stringURI = null;
    281            try {
    282                stringURI = new URI( null, "String", null );
    283            } catch ( URISyntaxException e ) {
    284                // cannot happen, why do I have to catch this?
    285            }
    286
    287            for ( Node str : nodes )
    288                values.add( new TypedLiteral( str.getLocalName(), stringURI ) );
    289
    290            DomainType owsDomainType = new DomainType( false, true, null, 0, new QualifiedName( "Format" ), values, null,
    291                                                       null, false, null, false, null, null, null, null );
    292            List<Parameter> parameters = new ArrayList<Parameter>();
    293            parameters.add( owsDomainType );
    294
    295            List<Element> nl = XMLTools.getRequiredElements( node, "./DCPType", nsContext );
    296            List<DCP> dcps = new ArrayList<DCP>();
    297
    298            for ( Element element : nl ) {
    299                dcps.add( parseDCP( element ) );
    300            }
    301
    302            return new Operation( new QualifiedName( name ), dcps, parameters, null, null, null );
    303        }
    304
    305        /**
    306         * Parses a DCPType element. Does not override the method defined in the base class any more.
    307         *
    308         * @param element
    309         * @return created <code>DCPType</code>
    310         * @throws XMLParsingException
    311         * @see org.deegree.ogcwebservices.getcapabilities.OGCStandardCapabilities
    312         */
    313        @Override
    314        protected DCP parseDCP( Element element )
    315                                throws XMLParsingException {
    316
    317            List<HTTP.Type> types = new ArrayList<HTTP.Type>();
    318            List<OnlineResource> links = new ArrayList<OnlineResource>();
    319
    320            Element elem = XMLTools.getRequiredElement( element, "HTTP", nsContext );
    321            String s = null;
    322            try {
    323                List<Node> nl = XMLTools.getNodes( elem, "Get", nsContext );
    324                for ( int i = 0; i < nl.size(); i++ ) {
    325                    s = XMLTools.getNodeAsString( nl.get( i ), "./@onlineResource", nsContext, null );
    326                    types.add( HTTP.Type.Get );
    327                    links.add( new OnlineResource( new Linkage( new URL( s ) ) ) );
    328                }
    329            } catch ( Exception e ) {
    330                LOG.logError( e.getMessage(), e );
    331                throw new XMLParsingException( Messages.getMessage( "WMS_DCPGET", s ) );
    332            }
    333            try {
    334                List<Node> nl = XMLTools.getNodes( elem, "Post", nsContext );
    335
    336                for ( int i = 0; i < nl.size(); i++ ) {
    337                    s = XMLTools.getNodeAsString( nl.get( i ), "./@onlineResource", nsContext, null );
    338                    types.add( HTTP.Type.Post );
    339                    links.add( new OnlineResource( new Linkage( new URL( s ) ) ) );
    340                }
    341
    342            } catch ( MalformedURLException e ) {
    343                throw new XMLParsingException( Messages.getMessage( "WMS_DCPPOST", s ) );
    344            }
    345            return new HTTP( links, null, types );
    346
    347        }
    348
    349        /**
    350         * returns the layers offered by the WMS
    351         *
    352         * @return the layer
    353         * @throws XMLParsingException
    354         * @throws UnknownCRSException
    355         */
    356        @Override
    357        protected Layer parseLayers( Element layerElem, Layer parent, ScaleHint scaleHint )
    358                                throws XMLParsingException, UnknownCRSException {
    359
    360            boolean queryable = XMLTools.getNodeAsBoolean( layerElem, "./@queryable", nsContext, false );
    361
    362            int cascaded = 0;
    363            boolean opaque = false;
    364            boolean noSubsets = false;
    365            int fixedWidth = 0;
    366            int fixedHeight = 0;
    367            String name = XMLTools.getNodeAsString( layerElem, "./Name", nsContext, null );
    368            String title = XMLTools.getRequiredNodeAsString( layerElem, "./Title", nsContext );
    369            String layerAbstract = XMLTools.getNodeAsString( layerElem, "./Abstract", nsContext, null );
    370            String[] keywords = XMLTools.getNodesAsStrings( layerElem, "./Keywords", nsContext );
    371            String[] srs = XMLTools.getNodesAsStrings( layerElem, "./SRS", nsContext );
    372
    373            List<Element> nl = XMLTools.getElements( layerElem, "./BoundingBox", nsContext );
    374            // TODO
    375            // substitue with Envelope
    376            LayerBoundingBox[] bboxes = null;
    377            if ( nl.size() == 0 && parent != null ) {
    378                // inherit BoundingBoxes from parent layer
    379                bboxes = parent.getBoundingBoxes();
    380            } else {
    381                bboxes = parseLayerBoundingBoxes( nl );
    382            }
    383
    384            Element llBox = XMLTools.getElement( layerElem, "./LatLonBoundingBox", nsContext );
    385            Envelope llBoundingBox = null;
    386
    387            if ( llBox == null && parent != null ) {
    388                // inherit LatLonBoundingBox parent layer
    389                llBoundingBox = parent.getLatLonBoundingBox();
    390            } else if ( llBox != null ) {
    391                llBoundingBox = parseLatLonBoundingBox( llBox );
    392            } else {
    393                llBoundingBox = GeometryFactory.createEnvelope( -180, -90, 180, 90, CRSFactory.create( "EPSG:4326" ) );
    394            }
    395
    396            DataURL[] dataURLs = parseDataURL( layerElem );
    397
    398            Style[] styles = parseStyles( layerElem );
    399
    400            scaleHint = parseScaleHint( layerElem, scaleHint );
    401
    402            Layer layer = new Layer( queryable, cascaded, opaque, noSubsets, fixedWidth, fixedHeight, name, title,
    403                                     layerAbstract, llBoundingBox, null, scaleHint, keywords, srs, bboxes, null, null,
    404                                     null, null, null, dataURLs, null, styles, null, null, parent );
    405
    406            // get Child layers
    407            nl = XMLTools.getElements( layerElem, "./Layer", nsContext );
    408            Layer[] layers = new Layer[nl.size()];
    409            for ( int i = 0; i < layers.length; i++ ) {
    410                layers[i] = parseLayers( nl.get( i ), layer, scaleHint );
    411            }
    412
    413            // set child layers
    414            layer.setLayer( layers );
    415
    416            return layer;
    417        }
    418
    419        /**
    420         *
    421         * @param layerElem
    422         * @return the URLs
    423         * @throws XMLParsingException
    424         */
    425        @Override
    426        protected DataURL[] parseDataURL( Element layerElem )
    427                                throws XMLParsingException {
    428
    429            List<Node> nl = XMLTools.getNodes( layerElem, "./DataURL", nsContext );
    430            DataURL[] dataURL = new DataURL[nl.size()];
    431            for ( int i = 0; i < dataURL.length; i++ ) {
    432                URL url;
    433                try {
    434                    url = new URL( XMLTools.getStringValue( nl.get( i ) ) );
    435                } catch ( MalformedURLException e ) {
    436                    throw new XMLParsingException( XMLTools.getStringValue( nl.get( i ) ) + " is not an URL" );
    437                }
    438                dataURL[i] = new DataURL( null, url );
    439
    440            }
    441
    442            return dataURL;
    443        }
    444
    445        /**
    446         *
    447         * @param layerElem
    448         * @return the styles
    449         * @throws XMLParsingException
    450         */
    451        @Override
    452        protected Style[] parseStyles( Element layerElem )
    453                                throws XMLParsingException {
    454
    455            List<Node> nl = XMLTools.getNodes( layerElem, "./Style", nsContext );
    456            Style[] styles = new Style[nl.size()];
    457            for ( int i = 0; i < styles.length; i++ ) {
    458                String name = XMLTools.getRequiredNodeAsString( nl.get( i ), "./Name", nsContext );
    459
    460                if ( name == null ) {
    461                    throw new XMLParsingException( Messages.getMessage( "WMS_STYLENAME" ) );
    462                }
    463                String title = XMLTools.getNodeAsString( nl.get( i ), "./Title", nsContext, null );
    464                if ( title == null ) {
    465                    throw new XMLParsingException( Messages.getMessage( "WMS_STYLETITLE" ) );
    466                }
    467                String styleAbstract = XMLTools.getNodeAsString( nl.get( i ), "./Abstract", nsContext, null );
    468                StyleURL styleURL = parseStyleURL( nl.get( i ) );
    469
    470                styles[i] = new Style( name, title, styleAbstract, null, null, styleURL, null );
    471            }
    472
    473            return styles;
    474        }
    475
    476        /**
    477         *
    478         * @param node
    479         * @return the URL
    480         * @throws XMLParsingException
    481         */
    482        @Override
    483        protected StyleURL parseStyleURL( Node node )
    484                                throws XMLParsingException {
    485
    486            StyleURL styleURL = null;
    487            Node styleNode = XMLTools.getNode( node, "./StyleURL", nsContext );
    488
    489            if ( styleNode != null ) {
    490                URL url;
    491                try {
    492                    url = new URL( XMLTools.getStringValue( styleNode ) );
    493                } catch ( MalformedURLException e ) {
    494                    throw new XMLParsingException( XMLTools.getStringValue( styleNode ) + " is not an URL" );
    495                }
    496                styleURL = new StyleURL( null, url );
    497            }
    498
    499            return styleURL;
    500        }
    501
    502        /**
    503         *
    504         * @param layerElem
    505         * @param scaleHint
    506         *            the default scale hint
    507         * @return the scale hint
    508         * @throws XMLParsingException
    509         */
    510        @Override
    511        protected ScaleHint parseScaleHint( Element layerElem, ScaleHint scaleHint )
    512                                throws XMLParsingException {
    513
    514            Node scNode = XMLTools.getNode( layerElem, "./ScaleHint", nsContext );
    515            if ( scNode != null ) {
    516                double mn = XMLTools.getNodeAsDouble( scNode, "./@min", nsContext, 0 );
    517                double mx = XMLTools.getNodeAsDouble( scNode, "./@max", nsContext, Double.MAX_VALUE );
    518                scaleHint = new ScaleHint( mn, mx );
    519            }
    520
    521            if ( scaleHint == null ) {
    522                // set default value to avoid NullPointerException
    523                // when accessing a layers scalehint
    524                scaleHint = new ScaleHint( 0, Double.MAX_VALUE );
    525            }
    526
    527            return scaleHint;
    528        }
    529
    530        /**
    531         *
    532         * @param nl
    533         * @return the bboxes
    534         * @throws XMLParsingException
    535         */
    536        @Override
    537        protected LayerBoundingBox[] parseLayerBoundingBoxes( List<Element> nl )
    538                                throws XMLParsingException {
    539
    540            LayerBoundingBox[] llBoxes = new LayerBoundingBox[nl.size()];
    541            for ( int i = 0; i < llBoxes.length; i++ ) {
    542                double minx = XMLTools.getRequiredNodeAsDouble( nl.get( i ), "./@minx", nsContext );
    543                double maxx = XMLTools.getRequiredNodeAsDouble( nl.get( i ), "./@maxx", nsContext );
    544                double miny = XMLTools.getRequiredNodeAsDouble( nl.get( i ), "./@miny", nsContext );
    545                double maxy = XMLTools.getRequiredNodeAsDouble( nl.get( i ), "./@maxy", nsContext );
    546                String srs = XMLTools.getRequiredNodeAsString( nl.get( i ), "./@SRS", nsContext );
    547                Position min = GeometryFactory.createPosition( minx, miny );
    548                Position max = GeometryFactory.createPosition( maxx, maxy );
    549                llBoxes[i] = new LayerBoundingBox( min, max, srs, -1, -1 );
    550            }
    551
    552            return llBoxes;
    553        }
    554
    555        /**
    556         *
    557         * @param llBox
    558         * @return the envelope
    559         * @throws XMLParsingException
    560         * @throws UnknownCRSException
    561         */
    562        @Override
    563        protected Envelope parseLatLonBoundingBox( Element llBox )
    564                                throws XMLParsingException, UnknownCRSException {
    565
    566            double minx = XMLTools.getRequiredNodeAsDouble( llBox, "./@minx", nsContext );
    567            double maxx = XMLTools.getRequiredNodeAsDouble( llBox, "./@maxx", nsContext );
    568            double miny = XMLTools.getRequiredNodeAsDouble( llBox, "./@miny", nsContext );
    569            double maxy = XMLTools.getRequiredNodeAsDouble( llBox, "./@maxy", nsContext );
    570            CoordinateSystem crs = CRSFactory.create( "EPSG:4326" );
    571
    572            Envelope env = GeometryFactory.createEnvelope( minx, miny, maxx, maxy, crs );
    573
    574            return env;
    575        }
    576
    577    }