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