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