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