    001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.4_testing/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.Address;
    061    import org.deegree.model.metadata.iso19115.CitedResponsibleParty;
    062    import org.deegree.model.metadata.iso19115.Constraints;
    063    import org.deegree.model.metadata.iso19115.ContactInfo;
    064    import org.deegree.model.metadata.iso19115.Keywords;
    065    import org.deegree.model.metadata.iso19115.Linkage;
    066    import org.deegree.model.metadata.iso19115.OnlineResource;
    067    import org.deegree.model.metadata.iso19115.Phone;
    068    import org.deegree.model.spatialschema.Envelope;
    069    import org.deegree.model.spatialschema.GeometryFactory;
    070    import org.deegree.model.spatialschema.Position;
    071    import org.deegree.ogcwebservices.getcapabilities.InvalidCapabilitiesException;
    072    import org.deegree.ogcwebservices.getcapabilities.MetadataURL;
    073    import org.deegree.ogcwebservices.getcapabilities.OGCCapabilities;
    074    import org.deegree.ogcwebservices.getcapabilities.OGCCapabilitiesDocument;
    075    import org.deegree.owscommon_new.DCP;
    076    import org.deegree.owscommon_new.DomainType;
    077    import org.deegree.owscommon_new.HTTP;
    078    import org.deegree.owscommon_new.Operation;
    079    import org.deegree.owscommon_new.OperationsMetadata;
    080    import org.deegree.owscommon_new.Parameter;
    081    import org.deegree.owscommon_new.ServiceIdentification;
    082    import org.deegree.owscommon_new.ServiceProvider;
    083    import org.w3c.dom.Element;
    084    import org.w3c.dom.Node;
    085    import org.xml.sax.SAXException;
    086
    087    /**
    088     * <code>WMSCapabilitiesDocument</code> is the parser class for WMS capabilities documents that uses the new OWS common
    089     * classes to encapsulate the data.
    090     *
    091     * @author <a href="mailto:schmitz@lat-lon.de">Andreas Schmitz</a>
    092     * @author last edited by: $Author: mschneider $
    093     *
    094     * @version 2.0, $Revision: 18195 $, $Date: 2009-06-18 17:55:39 +0200 (Do, 18. Jun 2009) $
    095     *
    096     * @since 2.0
    097     */
    098
    099    public class WMSCapabilitiesDocument extends OGCCapabilitiesDocument {
    100
    101        private static final long serialVersionUID = -4165017479515126226L;
    102
    103        private static final String XML_TEMPLATE = "WMSCapabilitiesTemplate.xml";
    104
    105        private static final ILogger LOG = LoggerFactory.getLogger( WMSCapabilitiesDocument.class );
    106
    107        /**
    108         * Creates a skeleton capabilities document that contains the mandatory elements only.
    109         *
    110         * @throws IOException
    111         * @throws SAXException
    112         */
    113        public void createEmptyDocument()
    114                                throws IOException, SAXException {
    115
    116            URL url = WMSCapabilitiesDocument.class.getResource( XML_TEMPLATE );
    117            if ( url == null ) {
    118                throw new IOException( "The resource '" + XML_TEMPLATE + " could not be found." );
    119            }
    120            load( url );
    121        }
    122
    123        protected List<String> parseExceptionFormats( Element elem )
    124                                throws XMLParsingException {
    125            String[] formats = XMLTools.getRequiredNodesAsStrings( elem, "Format", nsContext );
    126            return Arrays.asList( formats );
    127        }
    128
    129        /**
    130         * Creates a class representation of the document.
    131         *
    132         * @return class representation of the configuration document
    133         * @throws InvalidCapabilitiesException
    134         */
    135        @Override
    136        public OGCCapabilities parseCapabilities()
    137                                throws InvalidCapabilitiesException {
    138            ServiceIdentification serviceIdentification = null;
    139            ServiceProvider serviceProvider = null;
    140            OperationsMetadata metadata = null;
    141            Layer layer = null;
    142            UserDefinedSymbolization uds = null;
    143            String version = parseVersion();
    144            List<String> exceptions;
    145
    146            String updateSeq = parseUpdateSequence();
    147            try {
    148                serviceIdentification = parseServiceIdentification();
    149                serviceProvider = parseServiceProvider();
    150                metadata = parseOperationsMetadata();
    151
    152                Element exceptionElement = XMLTools.getRequiredElement( getRootElement(), "Capability/Exception", nsContext );
    153                exceptions = parseExceptionFormats( exceptionElement );
    154
    155                uds = parseUserDefinedSymbolization();
    156                Element layerElem = XMLTools.getRequiredElement( getRootElement(), "./Capability/Layer", nsContext );
    157                layer = parseLayers( layerElem, null, null );
    158            } catch ( XMLParsingException e ) {
    159                e.printStackTrace();
    160                throw new InvalidCapabilitiesException( e.getMessage() + StringTools.stackTraceToString( e ) );
    161            } catch ( UnknownCRSException e ) {
    162                e.printStackTrace();
    163                throw new InvalidCapabilitiesException( e.getMessage() + StringTools.stackTraceToString( e ) );
    164            }
    165
    166            WMSCapabilities wmsCapabilities = new WMSCapabilities( version, updateSeq, serviceIdentification,
    167                                                                   serviceProvider, uds, metadata, layer, exceptions );
    168
    169            return wmsCapabilities;
    170        }
    171
    172        /**
    173         * @return the services indentification read from the WMS capabilities service section
    174         *
    175         * @throws XMLParsingException
    176         */
    177        protected ServiceIdentification parseServiceIdentification()
    178                                throws XMLParsingException {
    179            String name = XMLTools.getNodeAsString( getRootElement(), "./Service/Name", nsContext, null );
    180            String title = XMLTools.getNodeAsString( getRootElement(), "./Service/Title", nsContext, name );
    181            String serviceAbstract = XMLTools.getNodeAsString( getRootElement(), "./Service/Abstract", nsContext, null );
    182
    183            String[] kw = XMLTools.getNodesAsStrings( getRootElement(), "./Service/KeywordList/Keyword", nsContext );
    184
    185            Keywords[] keywordArray = new Keywords[] { new Keywords( kw ) };
    186            List<Keywords> keywords = Arrays.asList( keywordArray );
    187
    188            String fees = XMLTools.getNodeAsString( getRootElement(), "./Service/Fees", nsContext, null );
    189
    190            List<Constraints> accessConstraints = new ArrayList<Constraints>();
    191
    192            String[] constraints = XMLTools.getNodesAsStrings( getRootElement(), "./Service/AccessConstraints", nsContext );
    193
    194            for ( String constraint : constraints ) {
    195                List<String> limits = new ArrayList<String>();
    196                limits.add( constraint );
    197                accessConstraints.add( new Constraints( fees, null, null, null, limits, null, null, null ) );
    198            }
    199
    200            List<String> versions = new ArrayList<String>();
    201            versions.add( "1.0.0" );
    202            versions.add( "1.1.0" );
    203            versions.add( "1.1.1" );
    204            versions.add( "1.2.0" );
    205            versions.add( "1.3.0" );
    206
    207            ServiceIdentification serviceIdentification = new ServiceIdentification(
    208                                                                                     new Code( "OGC:WMS" ),
    209                                                                                     versions,
    210                                                                                     title,
    211                                                                                     null,
    212                                                                                     new Date( System.currentTimeMillis() ),
    213                                                                                     title, serviceAbstract, keywords,
    214                                                                                     accessConstraints );
    215
    216            return serviceIdentification;
    217        }
    218
    219        /**
    220         * returns WMS contact informaion encapsulated within a <code>ServiceProvider</code> object
    221         *
    222         * @return the service provider data
    223         * @throws XMLParsingException
    224         */
    225        protected ServiceProvider parseServiceProvider()
    226                                throws XMLParsingException {
    227            Node ci = XMLTools.getNode( getRootElement(), "./Service/ContactInformation", nsContext );
    228            // according to WMS 1.1.1 specification this element is mandatory
    229            // but there are several services online which does not contain
    230            // this element in its capabilities :-(
    231            String s = XMLTools.getNodeAsString( getRootElement(), "./Service/OnlineResource/@xlink:href", nsContext, null );
    232
    233            OnlineResource providerSite = null;
    234
    235            if ( s != null ) {
    236                try {
    237                    providerSite = new OnlineResource( new Linkage( new URL( s ) ) );
    238                } catch ( MalformedURLException e ) {
    239                    throw new XMLParsingException( "could not parse service online resource", e );
    240                }
    241            } else {
    242                // use default if no online resource is contained in the
    243                // capabilities (see comment above)
    244                try {
    245                    providerSite = new OnlineResource( new Linkage( new URL( "http://www.opengeospatial.org/" ) ) );
    246                } catch ( MalformedURLException neverHappens ) {
    247                    // useless exception
    248                }
    249            }
    250
    251            String person = null;
    252            String orga = null;
    253            String position = null;
    254            if ( ci != null ) {
    255                person = XMLTools.getNodeAsString( ci, "./ContactPersonPrimary/ContactPerson", nsContext, null );
    256                orga = XMLTools.getNodeAsString( ci, "./ContactPersonPrimary/ContactOrganization", nsContext, null );
    257                position = XMLTools.getNodeAsString( ci, "./ContactPosition", nsContext, null );
    258            }
    259            ContactInfo contact = parseContactInfo();
    260            // ServiceProvider sp = new ServiceProvider( orga, sLink, person, position, contact, null );
    261            CitedResponsibleParty party = new CitedResponsibleParty(
    262                                                                     contact == null ? new ContactInfo[] {}
    263                                                                                    : new ContactInfo[] { contact },
    264                                                                     person == null ? new String[] {}
    265                                                                                   : new String[] { person },
    266                                                                     orga == null ? new String[] {} : new String[] { orga },
    267                                                                     position == null ? new String[] {}
    268                                                                                     : new String[] { position }, null );
    269            ServiceProvider sp = new ServiceProvider( person, providerSite, party );
    270
    271            return sp;
    272        }
    273
    274        /**
    275         *
    276         * @return the contact information
    277         * @throws XMLParsingException
    278         */
    279        protected ContactInfo parseContactInfo()
    280                                throws XMLParsingException {
    281            Node ci = XMLTools.getNode( getRootElement(), "./Service/ContactInformation", nsContext );
    282            ContactInfo cont = null;
    283            if ( ci != null ) {
    284                String[] addr = XMLTools.getNodesAsStrings( ci, "./ContactAddress/Address", nsContext );
    285                // String addrType =
    286                // XMLTools.getNodeAsString( ci, "./ContactAddress/AddressType", nsContext, null );
    287                String city = XMLTools.getNodeAsString( ci, "./ContactAddress/City", nsContext, null );
    288                String state = XMLTools.getNodeAsString( ci, "./ContactAddress/StateOrProvince", nsContext, null );
    289                String pc = XMLTools.getNodeAsString( ci, "./ContactAddress/PostCode", nsContext, null );
    290                String country = XMLTools.getNodeAsString( ci, "./ContactAddress/Country", nsContext, null );
    291                String[] mail = XMLTools.getNodesAsStrings( ci, "./ContactElectronicMailAddress", nsContext );
    292                Address address = new Address( state, city, country, addr, mail, pc );
    293
    294                String[] phone = XMLTools.getNodesAsStrings( ci, "./ContactVoiceTelephone", nsContext );
    295                String[] fax = XMLTools.getNodesAsStrings( ci, "./ContactFacsimileTelephone", nsContext );
    296
    297                Phone ph = new Phone( fax, phone );
    298
    299                cont = new ContactInfo( address, null, null, null, ph );
    300            }
    301
    302            return cont;
    303        }
    304
    305        /**
    306         * returns the services capabilitiy read from the WMS capabilities file
    307         *
    308         * @return the operations metadata
    309         * @throws XMLParsingException
    310         */
    311        protected OperationsMetadata parseOperationsMetadata()
    312                                throws XMLParsingException {
    313
    314            Node opNode = XMLTools.getNode( getRootElement(), "./Capability/Request/GetCapabilities", nsContext );
    315
    316            if ( opNode == null ) {
    317                // may it is a WMS 1.0.0 capabilities document
    318                opNode = XMLTools.getRequiredNode( getRootElement(), "./Capability/Request/Capabilities", nsContext );
    319            }
    320            Operation getCapa = parseOperation( opNode );
    321
    322            opNode = XMLTools.getNode( getRootElement(), "./Capability/Request/GetMap", nsContext );
    323            if ( opNode == null ) {
    324                // may it is a WMS 1.0.0 capabilities document
    325                opNode = XMLTools.getRequiredNode( getRootElement(), "./Capability/Request/Map", nsContext );
    326            }
    327            Operation getMap = parseOperation( opNode );
    328
    329            opNode = XMLTools.getNode( getRootElement(), "./Capability/Request/GetFeatureInfo", nsContext );
    330            Operation getFI = null;
    331            if ( opNode != null ) {
    332                getFI = parseOperation( opNode );
    333            } else {
    334                // maybe its WMS 1.0.0
    335                opNode = XMLTools.getNode( getRootElement(), "./Capability/Request/FeatureInfo", nsContext );
    336                if ( opNode != null ) {
    337                    getFI = parseOperation( opNode );
    338                }
    339            }
    340
    341            opNode = XMLTools.getNode( getRootElement(), "./Capability/Request/GetLegendGraphic", nsContext );
    342            Operation getLG = null;
    343            if ( opNode != null ) {
    344                getLG = parseOperation( opNode );
    345            }
    346
    347            opNode = XMLTools.getNode( getRootElement(), "./Capability/Request/DescribeLayer", nsContext );
    348            Operation descL = null;
    349            if ( opNode != null ) {
    350                descL = parseOperation( opNode );
    351            }
    352
    353            opNode = XMLTools.getNode( getRootElement(), "./Capability/Request/GetStyles", nsContext );
    354            Operation getStyles = null;
    355            if ( opNode != null ) {
    356                getStyles = parseOperation( opNode );
    357            }
    358
    359            opNode = XMLTools.getNode( getRootElement(), "./Capability/Request/PutStyles", nsContext );
    360            Operation putStyles = null;
    361            if ( opNode != null ) {
    362                putStyles = parseOperation( opNode );
    363            }
    364
    365            List<Operation> operations = new ArrayList<Operation>();
    366            if ( getCapa != null )
    367                operations.add( getCapa );
    368            if ( getMap != null )
    369                operations.add( getMap );
    370            if ( getFI != null )
    371                operations.add( getFI );
    372            if ( getLG != null )
    373                operations.add( getLG );
    374            if ( descL != null )
    375                operations.add( descL );
    376            if ( getStyles != null )
    377                operations.add( getStyles );
    378            if ( putStyles != null )
    379                operations.add( putStyles );
    380
    381            OperationsMetadata metadata = new OperationsMetadata( null, null, operations, null );
    382
    383            return metadata;
    384        }
    385
    386        /**
    387         * Creates an <tt>Operation</tt>-instance according to the contents of the DOM-subtree starting at the given
    388         * <tt>Node</tt>.
    389         * <p>
    390         * Notice: operation to be parsed must be operations in sense of WMS 1.0.0 - 1.3.0 and not as defined in OWSCommons.
    391         * But the method will return an OWSCommon Operation which encapsulates parsed WMS operation
    392         * <p>
    393         *
    394         * @param node
    395         *            the <tt>Element</tt> that describes an <tt>Operation</tt>
    396         * @throws XMLParsingException
    397         *             if a syntactic or semantic error in the DOM-subtree is encountered
    398         * @return the constructed <tt>Operation</tt>-instance
    399         */
    400        protected Operation parseOperation( Node node )
    401                                throws XMLParsingException {
    402            // use node name as name of the Operation to be defined
    403            String name = node.getNodeName();
    404            if ( name.equals( "Capabilities" ) ) {
    405                name = "GetCapabilities";
    406            } else if ( name.equals( "Map" ) ) {
    407                name = "GetMap";
    408            } else if ( name.equals( "FeatureInfo" ) ) {
    409                name = "GetFeatureInfo";
    410            }
    411
    412            String[] tmp = XMLTools.getRequiredNodesAsStrings( node, "./Format", nsContext );
    413            List<TypedLiteral> values = new ArrayList<TypedLiteral>();
    414
    415            URI stringURI = null;
    416            try {
    417                stringURI = new URI( null, "String", null );
    418            } catch ( URISyntaxException e ) {
    419                // cannot happen, why do I have to catch this?
    420            }
    421
    422            for ( String str : tmp )
    423                values.add( new TypedLiteral( str, stringURI ) );
    424
    425            DomainType owsDomainType = new DomainType( false, true, null, 0, new QualifiedName( "Format" ), values, null,
    426                                                       null, false, null, false, null, null, null, null );
    427            List<Parameter> parameters = new ArrayList<Parameter>();
    428            parameters.add( owsDomainType );
    429
    430            List<Element> nl = XMLTools.getRequiredElements( node, "./DCPType", nsContext );
    431            List<DCP> dcps = new ArrayList<DCP>();
    432
    433            for ( Element element : nl ) {
    434                dcps.add( parseDCP( element ) );
    435            }
    436
    437            return new Operation( new QualifiedName( name ), dcps, parameters, null, null, null );
    438        }
    439
    440        /**
    441         * Parses a DCPType element. Does not override the method defined in the base class any more.
    442         *
    443         * @param element
    444         * @return created <code>DCPType</code>
    445         * @throws XMLParsingException
    446         * @see org.deegree.ogcwebservices.getcapabilities.OGCStandardCapabilities
    447         */
    448        protected DCP parseDCP( Element element )
    449                                throws XMLParsingException {
    450
    451            List<HTTP.Type> types = new ArrayList<HTTP.Type>();
    452            List<OnlineResource> links = new ArrayList<OnlineResource>();
    453
    454            Element elem = XMLTools.getRequiredElement( element, "HTTP", nsContext );
    455            String s = null;
    456            try {
    457                List<Node> nl = XMLTools.getNodes( elem, "Get", nsContext );
    458
    459                for ( int i = 0; i < nl.size(); i++ ) {
    460                    s = XMLTools.getNodeAsString( nl.get( i ), "./@xlink:href", nsContext, null );
    461                    if ( s == null ) {
    462                        s = XMLTools.getRequiredNodeAsString( nl.get( i ), "./OnlineResource/@xlink:href", nsContext );
    463                    }
    464                    types.add( HTTP.Type.Get );
    465                    links.add( new OnlineResource( new Linkage( new URL( s ) ) ) );
    466                }
    467            } catch ( Exception e ) {
    468                LOG.logError( e.getMessage(), e );
    469                throw new XMLParsingException( Messages.getMessage( "WMS_DCPGET", s ) );
    470            }
    471            try {
    472                List<Node> nl = XMLTools.getNodes( elem, "Post", nsContext );
    473
    474                for ( int i = 0; i < nl.size(); i++ ) {
    475                    s = XMLTools.getNodeAsString( nl.get( i ), "./@xlink:href", nsContext, null );
    476                    if ( s == null ) {
    477                        s = XMLTools.getRequiredNodeAsString( nl.get( i ), "./OnlineResource/@xlink:href", nsContext );
    478                    }
    479                    types.add( HTTP.Type.Post );
    480                    links.add( new OnlineResource( new Linkage( new URL( s ) ) ) );
    481                }
    482
    483            } catch ( MalformedURLException e ) {
    484                throw new XMLParsingException( Messages.getMessage( "WMS_DCPPOST", s ) );
    485            }
    486            HTTP http = new HTTP( links, null, types );
    487
    488            return http;
    489        }
    490
    491        /**
    492         *
    493         * @return the parsed data
    494         * @throws XMLParsingException
    495         */
    496        protected UserDefinedSymbolization parseUserDefinedSymbolization()
    497                                throws XMLParsingException {
    498
    499            boolean supportSLD = XMLTools.getNodeAsBoolean( getRootElement(),
    500                                                            "./Capability/UserDefinedSymbolization/@SupportSLD", nsContext,
    501                                                            false );
    502
    503            boolean userLayer = XMLTools.getNodeAsBoolean( getRootElement(),
    504                                                           "./Capability/UserDefinedSymbolization/@UserLayer", nsContext,
    505                                                           false );
    506
    507            boolean userStyle = XMLTools.getNodeAsBoolean( getRootElement(),
    508                                                           "./Capability/UserDefinedSymbolization/@UserStyle", nsContext,
    509                                                           false );
    510
    511            boolean remoteWFS = XMLTools.getNodeAsBoolean( getRootElement(),
    512                                                           "./Capability/UserDefinedSymbolization/@RemoteWFS", nsContext,
    513                                                           false );
    514
    515            UserDefinedSymbolization uds = new UserDefinedSymbolization( supportSLD, userLayer, remoteWFS, userStyle );
    516
    517            return uds;
    518        }
    519
    520        /**
    521         * @param layerElem
    522         * @param parent
    523         * @param scaleHint
    524         *
    525         * @return the layer created from the given element
    526         * @throws XMLParsingException
    527         * @throws UnknownCRSException
    528         */
    529        protected Layer parseLayers( Element layerElem, Layer parent, ScaleHint scaleHint )
    530                                throws XMLParsingException, UnknownCRSException {
    531
    532            boolean queryable = XMLTools.getNodeAsBoolean( layerElem, "./@queryable", nsContext, false );
    533
    534            int cascaded = XMLTools.getNodeAsInt( layerElem, "./@cascaded", nsContext, 0 );
    535            boolean opaque = XMLTools.getNodeAsBoolean( layerElem, "./@opaque", nsContext, false );
    536            boolean noSubsets = XMLTools.getNodeAsBoolean( layerElem, "./@noSubsets", nsContext, false );
    537            int fixedWidth = XMLTools.getNodeAsInt( layerElem, "./@fixedWidth", nsContext, 0 );
    538            int fixedHeight = XMLTools.getNodeAsInt( layerElem, "./@fixedHeight", nsContext, 0 );
    539            String name = XMLTools.getNodeAsString( layerElem, "./Name", nsContext, null );
    540            String title = XMLTools.getRequiredNodeAsString( layerElem, "./Title", nsContext );
    541            String layerAbstract = XMLTools.getNodeAsString( layerElem, "./Abstract", nsContext, null );
    542            String[] keywords = XMLTools.getNodesAsStrings( layerElem, "./KeywordList/Keyword", nsContext );
    543            String[] srs = XMLTools.getNodesAsStrings( layerElem, "./SRS", nsContext );
    544
    545            List<Element> nl = XMLTools.getElements( layerElem, "./BoundingBox", nsContext );
    546            // TODO
    547            // substitue with Envelope
    548            LayerBoundingBox[] bboxes = null;
    549            if ( nl.size() == 0 && parent != null ) {
    550                // inherit BoundingBoxes from parent layer
    551                bboxes = parent.getBoundingBoxes();
    552            } else {
    553                bboxes = parseLayerBoundingBoxes( nl );
    554            }
    555
    556            Element llBox = XMLTools.getElement( layerElem, "./LatLonBoundingBox", nsContext );
    557            Envelope llBoundingBox = null;
    558
    559            if ( llBox == null && parent != null ) {
    560                // inherit LatLonBoundingBox parent layer
    561                llBoundingBox = parent.getLatLonBoundingBox();
    562            } else if ( llBox != null ) {
    563                llBoundingBox = parseLatLonBoundingBox( llBox );
    564            } else {
    565                llBoundingBox = GeometryFactory.createEnvelope( -180, -90, 180, 90, CRSFactory.create( "EPSG:4326" ) );
    566            }
    567
    568            Dimension[] dimensions = parseDimensions( layerElem );
    569            Extent[] extents = parseExtents( layerElem );
    570
    571            Attribution attribution = parseAttribution( layerElem );
    572
    573            AuthorityURL[] authorityURLs = parseAuthorityURLs( layerElem );
    574
    575            MetadataURL[] metadataURLs = parseMetadataURLs( layerElem );
    576
    577            DataURL[] dataURLs = parseDataURL( layerElem );
    578
    579            Identifier[] identifiers = parseIdentifiers( layerElem );
    580
    581            FeatureListURL[] featureListURLs = parseFeatureListURL( layerElem );
    582
    583            Style[] styles = parseStyles( layerElem );
    584
    585            scaleHint = parseScaleHint( layerElem, scaleHint );
    586
    587            Layer layer = new Layer( queryable, cascaded, opaque, noSubsets, fixedWidth, fixedHeight, name, title,
    588                                     layerAbstract, llBoundingBox, attribution, scaleHint, keywords, srs, bboxes,
    589                                     dimensions, extents, authorityURLs, identifiers, metadataURLs, dataURLs,
    590                                     featureListURLs, styles, null, null, parent );
    591
    592            // get Child layers
    593            nl = XMLTools.getElements( layerElem, "./Layer", nsContext );
    594            Layer[] layers = new Layer[nl.size()];
    595            for ( int i = 0; i < layers.length; i++ ) {
    596                layers[i] = parseLayers( nl.get( i ), layer, scaleHint );
    597            }
    598
    599            // set child layers
    600            layer.setLayer( layers );
    601
    602            return layer;
    603        }
    604
    605        /**
    606         *
    607         * @param layerElem
    608         * @return the dimensions
    609         * @throws XMLParsingException
    610         */
    611        protected Dimension[] parseDimensions( Element layerElem )
    612                                throws XMLParsingException {
    613
    614            List<Node> nl = XMLTools.getNodes( layerElem, "./Dimension", nsContext );
    615            Dimension[] dimensions = new Dimension[nl.size()];
    616            for ( int i = 0; i < dimensions.length; i++ ) {
    617                String name = XMLTools.getNodeAsString( nl.get( i ), "./@name", nsContext, null );
    618                String units = XMLTools.getNodeAsString( nl.get( i ), "./@units", nsContext, null );
    619                String unitSymbol = XMLTools.getNodeAsString( nl.get( i ), "./@unitSymbol", nsContext, null );
    620                dimensions[i] = new Dimension( name, units, unitSymbol );
    621            }
    622
    623            return dimensions;
    624        }
    625
    626        /**
    627         *
    628         * @param layerElem
    629         * @return the extent
    630         * @throws XMLParsingException
    631         */
    632        protected Extent[] parseExtents( Element layerElem )
    633                                throws XMLParsingException {
    634
    635            List<Node> nl = XMLTools.getNodes( layerElem, "./Extent", nsContext );
    636            Extent[] extents = new Extent[nl.size()];
    637            for ( int i = 0; i < extents.length; i++ ) {
    638                String name = XMLTools.getNodeAsString( nl.get( i ), "./@name", nsContext, null );
    639                String deflt = XMLTools.getNodeAsString( nl.get( i ), "./@default", nsContext, null );
    640                boolean nearestValue = XMLTools.getNodeAsBoolean( nl.get( i ), "./@nearestValue", nsContext, false );
    641                String value = XMLTools.getNodeAsString( nl.get( i ), ".", nsContext, "" );
    642                extents[i] = new Extent( name, deflt, nearestValue, value );
    643            }
    644
    645            return extents;
    646        }
    647
    648        /**
    649         *
    650         * @param layerElem
    651         * @return the attribution
    652         * @throws XMLParsingException
    653         */
    654        protected Attribution parseAttribution( Element layerElem )
    655                                throws XMLParsingException {
    656
    657            Attribution attribution = null;
    658            Node node = XMLTools.getNode( layerElem, "./Attribution", nsContext );
    659            if ( node != null ) {
    660                String title = XMLTools.getRequiredNodeAsString( layerElem, "./Attribution/Title", nsContext );
    661                OnlineResource onLineResource = parseOnLineResource( XMLTools.getRequiredElement( node, "./OnlineResource",
    662                                                                                                  nsContext ) );
    663                node = XMLTools.getNode( node, "./LogoURL", nsContext );
    664                LogoURL logoURL = null;
    665                if ( node != null ) {
    666                    int width = XMLTools.getRequiredNodeAsInt( node, "./@width", nsContext );
    667                    int height = XMLTools.getRequiredNodeAsInt( node, "./@height", nsContext );
    668                    String format = XMLTools.getRequiredNodeAsString( node, "./Format", nsContext );
    669                    OnlineResource logoOR = parseOnLineResource( XMLTools.getRequiredElement( node, "./OnlineResource",
    670                                                                                              nsContext ) );
    671                    logoURL = new LogoURL( width, height, format, logoOR.getLinkage().getHref() );
    672                }
    673                attribution = new Attribution( title, onLineResource.getLinkage().getHref(), logoURL );
    674            }
    675
    676            return attribution;
    677        }
    678
    679        /**
    680         *
    681         * @param layerElem
    682         * @return the URLs
    683         * @throws XMLParsingException
    684         */
    685        protected AuthorityURL[] parseAuthorityURLs( Element layerElem )
    686                                throws XMLParsingException {
    687
    688            List<Node> nl = XMLTools.getNodes( layerElem, "./AuthorityURL", nsContext );
    689            AuthorityURL[] authorityURLs = new AuthorityURL[nl.size()];
    690            for ( int i = 0; i < authorityURLs.length; i++ ) {
    691                String name = XMLTools.getRequiredNodeAsString( nl.get( i ), "./@name", nsContext );
    692                Element tmp = XMLTools.getRequiredElement( nl.get( i ), "./OnlineResource", nsContext );
    693                OnlineResource olr = parseOnLineResource( tmp );
    694                authorityURLs[i] = new AuthorityURL( name, olr.getLinkage().getHref() );
    695            }
    696
    697            return authorityURLs;
    698        }
    699
    700        /**
    701         *
    702         * @param layerElem
    703         * @return the URLs
    704         * @throws XMLParsingException
    705         */
    706        protected MetadataURL[] parseMetadataURLs( Element layerElem )
    707                                throws XMLParsingException {
    708
    709            List<Node> nl = XMLTools.getNodes( layerElem, "./MetadataURL", nsContext );
    710            MetadataURL[] metadataURL = new MetadataURL[nl.size()];
    711            for ( int i = 0; i < metadataURL.length; i++ ) {
    712                String type = XMLTools.getRequiredNodeAsString( nl.get( i ), "./@type", nsContext );
    713                String format = XMLTools.getRequiredNodeAsString( nl.get( i ), "./Format", nsContext );
    714                Element tmp = XMLTools.getRequiredElement( nl.get( i ), "./OnlineResource", nsContext );
    715                OnlineResource olr = parseOnLineResource( tmp );
    716                metadataURL[i] = new MetadataURL( type, format, olr.getLinkage().getHref() );
    717
    718            }
    719
    720            return metadataURL;
    721        }
    722
    723        /**
    724         *
    725         * @param layerElem
    726         * @return the URLs
    727         * @throws XMLParsingException
    728         */
    729        protected DataURL[] parseDataURL( Element layerElem )
    730                                throws XMLParsingException {
    731
    732            List<Node> nl = XMLTools.getNodes( layerElem, "./DataURL", nsContext );
    733            DataURL[] dataURL = new DataURL[nl.size()];
    734            for ( int i = 0; i < dataURL.length; i++ ) {
    735
    736                String format = XMLTools.getRequiredNodeAsString( nl.get( i ), "./Format", nsContext );
    737                Element tmp = XMLTools.getRequiredElement( nl.get( i ), "./OnlineResource", nsContext );
    738                OnlineResource olr = parseOnLineResource( tmp );
    739                dataURL[i] = new DataURL( format, olr.getLinkage().getHref() );
    740
    741            }
    742
    743            return dataURL;
    744        }
    745
    746        /**
    747         *
    748         * @param layerElem
    749         * @return the URLs
    750         * @throws XMLParsingException
    751         */
    752        protected FeatureListURL[] parseFeatureListURL( Element layerElem )
    753                                throws XMLParsingException {
    754
    755            List<Node> nl = XMLTools.getNodes( layerElem, "./FeatureListURL", nsContext );
    756            FeatureListURL[] flURL = new FeatureListURL[nl.size()];
    757            for ( int i = 0; i < flURL.length; i++ ) {
    758
    759                String format = XMLTools.getRequiredNodeAsString( nl.get( i ), "./Format", nsContext );
    760                Element tmp = XMLTools.getRequiredElement( nl.get( i ), "./OnlineResource", nsContext );
    761                OnlineResource olr = parseOnLineResource( tmp );
    762                flURL[i] = new FeatureListURL( format, olr.getLinkage().getHref() );
    763
    764            }
    765
    766            return flURL;
    767        }
    768
    769        /**
    770         *
    771         * @param layerElem
    772         * @return the styles
    773         * @throws XMLParsingException
    774         */
    775        protected Style[] parseStyles( Element layerElem )
    776                                throws XMLParsingException {
    777
    778            List<Node> nl = XMLTools.getNodes( layerElem, "./Style", nsContext );
    779            Style[] styles = new Style[nl.size()];
    780            for ( int i = 0; i < styles.length; i++ ) {
    781                String name = XMLTools.getRequiredNodeAsString( nl.get( i ), "./Name", nsContext );
    782
    783                if ( name == null ) {
    784                    throw new XMLParsingException( Messages.getMessage( "WMS_STYLENAME" ) );
    785                }
    786                String title = XMLTools.getNodeAsString( nl.get( i ), "./Title", nsContext, null );
    787                if ( title == null ) {
    788                    throw new XMLParsingException( Messages.getMessage( "WMS_STYLETITLE" ) );
    789                }
    790                String styleAbstract = XMLTools.getNodeAsString( nl.get( i ), "./Abstract", nsContext, null );
    791                LegendURL[] legendURLs = parseLegendURL( nl.get( i ) );
    792                StyleURL styleURL = parseStyleURL( nl.get( i ) );
    793                StyleSheetURL styleSheetURL = parseStyleSheetURL( nl.get( i ) );
    794
    795                styles[i] = new Style( name, title, styleAbstract, legendURLs, styleSheetURL, styleURL, null );
    796            }
    797
    798            return styles;
    799        }
    800
    801        /**
    802         *
    803         * @param node
    804         * @return the URLs
    805         * @throws XMLParsingException
    806         */
    807        protected LegendURL[] parseLegendURL( Node node )
    808                                throws XMLParsingException {
    809
    810            List<Node> nl = XMLTools.getNodes( node, "./LegendURL", nsContext );
    811            LegendURL[] lURL = new LegendURL[nl.size()];
    812            for ( int i = 0; i < lURL.length; i++ ) {
    813                int width = XMLTools.getRequiredNodeAsInt( nl.get( i ), "./@width", nsContext );
    814                int height = XMLTools.getRequiredNodeAsInt( nl.get( i ), "./@height", nsContext );
    815                String format = XMLTools.getRequiredNodeAsString( nl.get( i ), "./Format", nsContext );
    816                Element tmp = XMLTools.getRequiredElement( nl.get( i ), "./OnlineResource", nsContext );
    817                OnlineResource olr = parseOnLineResource( tmp );
    818                lURL[i] = new LegendURL( width, height, format, olr.getLinkage().getHref() );
    819
    820            }
    821
    822            return lURL;
    823        }
    824
    825        /**
    826         *
    827         * @param node
    828         * @return the URL
    829         * @throws XMLParsingException
    830         */
    831        protected StyleURL parseStyleURL( Node node )
    832                                throws XMLParsingException {
    833
    834            StyleURL styleURL = null;
    835            Node styleNode = XMLTools.getNode( node, "./StyleURL", nsContext );
    836
    837            if ( styleNode != null ) {
    838                String format = XMLTools.getRequiredNodeAsString( styleNode, "./Format", nsContext );
    839                Element tmp = XMLTools.getRequiredElement( styleNode, "./OnlineResource", nsContext );
    840                OnlineResource olr = parseOnLineResource( tmp );
    841                styleURL = new StyleURL( format, olr.getLinkage().getHref() );
    842
    843            }
    844
    845            return styleURL;
    846        }
    847
    848        /**
    849         *
    850         * @param node
    851         * @return the URL
    852         * @throws XMLParsingException
    853         */
    854        protected StyleSheetURL parseStyleSheetURL( Node node )
    855                                throws XMLParsingException {
    856
    857            StyleSheetURL styleSheetURL = null;
    858            Node styleNode = XMLTools.getNode( node, "./StyleSheetURL", nsContext );
    859
    860            if ( styleNode != null ) {
    861                String format = XMLTools.getRequiredNodeAsString( styleNode, "./Format", nsContext );
    862                Element tmp = XMLTools.getRequiredElement( styleNode, "./OnlineResource", nsContext );
    863                OnlineResource olr = parseOnLineResource( tmp );
    864                styleSheetURL = new StyleSheetURL( format, olr.getLinkage().getHref() );
    865
    866            }
    867
    868            return styleSheetURL;
    869        }
    870
    871        /**
    872         *
    873         * @param layerElem
    874         * @param scaleHint
    875         *            the default scale hint
    876         * @return the scale hint
    877         * @throws XMLParsingException
    878         */
    879        protected ScaleHint parseScaleHint( Element layerElem, ScaleHint scaleHint )
    880                                throws XMLParsingException {
    881
    882            Node scNode = XMLTools.getNode( layerElem, "./ScaleHint", nsContext );
    883            if ( scNode != null ) {
    884                double mn = XMLTools.getNodeAsDouble( scNode, "./@min", nsContext, 0 );
    885                double mx = XMLTools.getNodeAsDouble( scNode, "./@max", nsContext, Double.MAX_VALUE );
    886                scaleHint = new ScaleHint( mn, mx );
    887            }
    888
    889            if ( scaleHint == null ) {
    890                // set default value to avoid NullPointerException
    891                // when accessing a layers scalehint
    892                scaleHint = new ScaleHint( 0, Double.MAX_VALUE );
    893            }
    894
    895            return scaleHint;
    896        }
    897
    898        /**
    899         *
    900         * @param layerElem
    901         * @return the identifiers
    902         * @throws XMLParsingException
    903         */
    904        protected Identifier[] parseIdentifiers( Element layerElem )
    905                                throws XMLParsingException {
    906
    907            List<Node> nl = XMLTools.getNodes( layerElem, "./Identifier", nsContext );
    908            Identifier[] identifiers = new Identifier[nl.size()];
    909            for ( int i = 0; i < identifiers.length; i++ ) {
    910                String value = XMLTools.getStringValue( nl.get( i ) );
    911                String authority = XMLTools.getNodeAsString( layerElem, "./@authority", nsContext, null );
    912                identifiers[i] = new Identifier( value, authority );
    913            }
    914
    915            return identifiers;
    916        }
    917
    918        /**
    919         *
    920         * @param nl
    921         * @return the bboxes
    922         * @throws XMLParsingException
    923         */
    924        protected LayerBoundingBox[] parseLayerBoundingBoxes( List<Element> nl )
    925                                throws XMLParsingException {
    926
    927            LayerBoundingBox[] llBoxes = new LayerBoundingBox[nl.size()];
    928            for ( int i = 0; i < llBoxes.length; i++ ) {
    929                double minx = XMLTools.getRequiredNodeAsDouble( nl.get( i ), "./@minx", nsContext );
    930                double maxx = XMLTools.getRequiredNodeAsDouble( nl.get( i ), "./@maxx", nsContext );
    931                double miny = XMLTools.getRequiredNodeAsDouble( nl.get( i ), "./@miny", nsContext );
    932                double maxy = XMLTools.getRequiredNodeAsDouble( nl.get( i ), "./@maxy", nsContext );
    933                double resx = XMLTools.getNodeAsDouble( nl.get( i ), "./@resx", nsContext, -1 );
    934                double resy = XMLTools.getNodeAsDouble( nl.get( i ), "./@resx", nsContext, -1 );
    935                String srs = XMLTools.getRequiredNodeAsString( nl.get( i ), "./@SRS", nsContext );
    936                Position min = GeometryFactory.createPosition( minx, miny );
    937                Position max = GeometryFactory.createPosition( maxx, maxy );
    938                llBoxes[i] = new LayerBoundingBox( min, max, srs, resx, resy );
    939            }
    940
    941            return llBoxes;
    942        }
    943
    944        /**
    945         *
    946         * @param llBox
    947         * @return the envelope
    948         * @throws XMLParsingException
    949         * @throws UnknownCRSException
    950         */
    951        protected Envelope parseLatLonBoundingBox( Element llBox )
    952                                throws XMLParsingException, UnknownCRSException {
    953
    954            double minx = XMLTools.getRequiredNodeAsDouble( llBox, "./@minx", nsContext );
    955            double maxx = XMLTools.getRequiredNodeAsDouble( llBox, "./@maxx", nsContext );
    956            double miny = XMLTools.getRequiredNodeAsDouble( llBox, "./@miny", nsContext );
    957            double maxy = XMLTools.getRequiredNodeAsDouble( llBox, "./@maxy", nsContext );
    958            CoordinateSystem crs = CRSFactory.create( "EPSG:4326" );
    959
    960            Envelope env = GeometryFactory.createEnvelope( minx, miny, maxx, maxy, crs );
    961
    962            return env;
    963        }
    964
    965    }