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