001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_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    }