001    //$HeadURL: svn+ssh://jwilden@svn.wald.intevation.org/deegree/base/branches/2.5_testing/src/org/deegree/ogcwebservices/wms/capabilities/Layer.java $
002    /*----------------------------------------------------------------------------
003     This file is part of deegree, http://deegree.org/
004     Copyright (C) 2001-2009 by:
005     Department of Geography, University of Bonn
006     and
007     lat/lon GmbH
008    
009     This library is free software; you can redistribute it and/or modify it under
010     the terms of the GNU Lesser General Public License as published by the Free
011     Software Foundation; either version 2.1 of the License, or (at your option)
012     any later version.
013     This library is distributed in the hope that it will be useful, but WITHOUT
014     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
015     FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
016     details.
017     You should have received a copy of the GNU Lesser General Public License
018     along with this library; if not, write to the Free Software Foundation, Inc.,
019     59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020    
021     Contact information:
022    
023     lat/lon GmbH
024     Aennchenstr. 19, 53177 Bonn
025     Germany
026     http://lat-lon.de/
027    
028     Department of Geography, University of Bonn
029     Prof. Dr. Klaus Greve
030     Postfach 1147, 53001 Bonn
031     Germany
032     http://www.geographie.uni-bonn.de/deegree/
033    
034     e-mail: info@deegree.org
035     ----------------------------------------------------------------------------*/
036    package org.deegree.ogcwebservices.wms.capabilities;
037    
038    import static java.util.Arrays.asList;
039    
040    import java.util.ArrayList;
041    import java.util.Arrays;
042    import java.util.HashMap;
043    import java.util.LinkedList;
044    import java.util.List;
045    import java.util.ListIterator;
046    
047    import org.deegree.graphics.sld.UserStyle;
048    import org.deegree.model.spatialschema.Envelope;
049    import org.deegree.ogcwebservices.getcapabilities.MetadataURL;
050    import org.deegree.ogcwebservices.wms.configuration.AbstractDataSource;
051    
052    /**
053     * Each available map is advertised by a <Layer> element in the Capabilities XML. A single parent Layer encloses
054     * any number of additional layers, which may be hierarchically nested as desired. Some properties defined in a parent
055     * layer are inherited by the children it encloses. These inherited properties may be either redefined or added to by
056     * the child.
057     * <p>
058     * A Map Server shall include at least one &lt;Layer&gt; element for each map layer offered. If desired, layers may be
059     * repeated in different categories when relevant. No controlled vocabulary has been defined, so at present Layer and
060     * Style Names, Titles and Keywords are arbitrary.
061     * </p>
062     * The &lt;Layer&gt; element can enclose child elements providing metadata about the Layer.
063     * 
064     * @author <a href="mailto:k.lupp@web.de">Katharina Lupp </a>
065     * @author <a href="mailto:mschneider@lat-lon.de">Markus Schneider </a>
066     * @author last edited by: $Author: aschmitz $
067     * @version 2002-03-01
068     */
069    public class Layer {
070    
071        private List<AuthorityURL> authorityURL;
072    
073        private List<Envelope> boundingBox;
074    
075        private List<AbstractDataSource> dataSource;
076    
077        private List<DataURL> dataURL;
078    
079        private List<Dimension> dimension;
080    
081        private List<Extent> extent;
082    
083        private List<FeatureListURL> featureListURL;
084    
085        private List<Identifier> identifier;
086    
087        private List<String> keywordList;
088    
089        private List<Layer> layer;
090    
091        private List<MetadataURL> metadataURL;
092    
093        private List<String> srs;
094    
095        private Attribution attribution;
096    
097        private Envelope latLonBoundingBox;
098    
099        private HashMap<String, Style> styles;
100    
101        private Style[] stylesArray;
102    
103        private Layer parent;
104    
105        private ScaleHint scaleHint;
106    
107        private String abstract_;
108    
109        private String name;
110    
111        private String title;
112    
113        private boolean noSubsets = false;
114    
115        private boolean opaque = false;
116    
117        private boolean queryable = false;
118    
119        private int cascaded = -1;
120    
121        private int fixedHeight = -1;
122    
123        private int fixedWidth = -1;
124    
125        /**
126         * default constructor
127         */
128        private Layer() {
129            keywordList = new ArrayList<String>( 20 );
130            srs = new ArrayList<String>( 20 );
131            boundingBox = new ArrayList<Envelope>();
132            dimension = new ArrayList<Dimension>();
133            extent = new ArrayList<Extent>();
134            authorityURL = new ArrayList<AuthorityURL>();
135            identifier = new ArrayList<Identifier>();
136            metadataURL = new ArrayList<MetadataURL>();
137            dataURL = new ArrayList<DataURL>();
138            featureListURL = new ArrayList<FeatureListURL>();
139            styles = new HashMap<String, Style>();
140            layer = new ArrayList<Layer>( 50 );
141            dataSource = new ArrayList<AbstractDataSource>();
142        }
143    
144        /**
145         * constructor initializing the class with the &lt;Layer&gt;
146         * 
147         * @param queryable
148         * @param cascaded
149         * @param opaque
150         * @param noSubsets
151         * @param fixedWidth
152         * @param fixedHeight
153         * @param name
154         * @param title
155         * @param abstract_
156         * @param latLonBoundingBox
157         * @param attribution
158         * @param scaleHint
159         * @param keywordList
160         * @param srs
161         * @param boundingBoxes
162         * @param dimensions
163         * @param extents
164         * @param authorityURLs
165         * @param identifiers
166         * @param metadataURLs
167         * @param dataURLs
168         * @param featureListURLs
169         * @param styles
170         * @param layers
171         * @param dataSource
172         * @param parent
173         */
174        public Layer( boolean queryable, int cascaded, boolean opaque, boolean noSubsets, int fixedWidth, int fixedHeight,
175                      String name, String title, String abstract_, Envelope latLonBoundingBox, Attribution attribution,
176                      ScaleHint scaleHint, String[] keywordList, String[] srs, LayerBoundingBox[] boundingBoxes,
177                      Dimension[] dimensions, Extent[] extents, AuthorityURL[] authorityURLs, Identifier[] identifiers,
178                      MetadataURL[] metadataURLs, DataURL[] dataURLs, FeatureListURL[] featureListURLs, Style[] styles,
179                      Layer[] layers, AbstractDataSource[] dataSource, Layer parent ) {
180            this();
181            this.queryable = queryable;
182            this.cascaded = cascaded;
183            this.opaque = opaque;
184            this.noSubsets = noSubsets;
185            this.fixedWidth = fixedWidth;
186            this.fixedHeight = fixedHeight;
187            setName( name );
188            setTitle( title );
189            setAbstract( abstract_ );
190            setLatLonBoundingBox( latLonBoundingBox );
191            setAttribution( attribution );
192            setScaleHint( scaleHint );
193            setKeywordList( keywordList );
194            setSrs( srs );
195            setBoundingBox( boundingBoxes );
196            setDimension( dimensions );
197            setExtent( extents );
198            setAuthorityURL( authorityURLs );
199            setIdentifier( identifiers );
200            setMetadataURL( metadataURLs );
201            setDataURL( dataURLs );
202            setFeatureListURL( featureListURLs );
203            setStyles( styles );
204            setLayer( layers );
205            setDataSource( dataSource );
206            setParent( parent );
207        }
208    
209        /**
210         * If, and only if, a layer has a &lt;Name&gt;, then it is a map layer that can be requested by using that Name in
211         * the LAYERS parameter of a GetMap request. If the layer has a Title but no Name, then that layer is only a
212         * category title for all the layers nested within. A Map Server that advertises a Layer containing a Name element
213         * shall be able to accept that Name as the value of LAYERS argument in a GetMap request and return the
214         * corresponding map. A Client shall not attempt to request a layer that has a Title but no Name.
215         * 
216         * @return the name
217         */
218        public String getName() {
219            return name;
220        }
221    
222        /**
223         * sets the name of the layer
224         * 
225         * @param name
226         */
227        public void setName( String name ) {
228            this.name = name;
229        }
230    
231        /**
232         * A &lt;Title&gt; is required for all layers; it is a human-readable string for presentation in a menu. The Title
233         * is not inherited by child Layers.
234         * 
235         * @return the title
236         */
237        public String getTitle() {
238            return title;
239        }
240    
241        /**
242         * sets the title for the layer
243         * 
244         * @param title
245         */
246        public void setTitle( String title ) {
247            this.title = title;
248        }
249    
250        /**
251         * Abstract is a narrative description of the map layer. The Abstract elements are not inherited by child Layers.
252         * 
253         * @return the abstract
254         */
255        public String getAbstract() {
256            return abstract_;
257        }
258    
259        /**
260         * sets the a narrative description of the map layer
261         * 
262         * @param abstract_
263         */
264        public void setAbstract( String abstract_ ) {
265            this.abstract_ = abstract_;
266        }
267    
268        /**
269         * KeywordList contains zero or more Keywords to aid in catalog searches. The KeywordList elements are not inherited
270         * by child Layers.
271         * 
272         * @return the keywords
273         */
274        public String[] getKeywordList() {
275            return keywordList.toArray( new String[keywordList.size()] );
276        }
277    
278        /**
279         * adds the keywordList
280         * 
281         * @param keyword
282         */
283        public void addKeyword( String keyword ) {
284            this.keywordList.add( keyword );
285        }
286    
287        /**
288         * sets the keywordList
289         * 
290         * @param keywordList
291         */
292        public void setKeywordList( String[] keywordList ) {
293            if ( keywordList == null )
294                this.keywordList.clear();
295            else
296                this.keywordList = Arrays.asList( keywordList );
297        }
298    
299        /**
300         * Every Layer is available in one or more spatial reference systems Every Layer shall have at least one &gt;SRS&gt;
301         * element that is either stated explicitly or inherited from a parent Layer . The root &lt;Layer&gt; element shall
302         * include a sequence of zero or more SRS elements listing all SRSes that are common to all subsidiary layers. Use a
303         * single SRS element with empty content (like so: "&lt;SRS&gt;&lt;/SRS&gt; ") if there is no common SRS. Layers may
304         * optionally add to the global SRS list, or to the list inherited from a parent layer. Any duplication shall be
305         * ignored by clients. When a Layer is available in several Spatial Reference Systems, there are two ways to encode
306         * the list of SRS values. The first of these is new in this version of the specification, the second is deprecated
307         * but still included for backwards compatibility.
308         * <p>
309         * 1. Optional, recommended: Multiple single-valued &lt;SRS&gt; elements: a list of SRS values is represented as a
310         * sequence of &lt;SRS&gt; elements, each of which contains only a single SRS name. Example:
311         * &lt;SRS&gt;EPSG:1234&lt;/SRS&gt; &lt;SRS&gt;EPSG:5678&lt;/SRS&gt;.
312         * </p>
313         * 2. Deprecated: Single list-valued &lt;SRS&gt; element: a list of SRS values is represented asa
314         * whitespace-separated list of SRS names contained within a single &lt;SRS&gt; element. Example:
315         * &lt;SRS&gt;EPSG:1234 EPSG:5678&lt;/SRS&gt;.
316         * 
317         * @return the srs
318         */
319        public String[] getSrs() {
320            String[] pSrs = null;
321    
322            if ( parent != null ) {
323                pSrs = parent.getSrs();
324            } else {
325                pSrs = new String[0];
326            }
327    
328            List<String> list = new ArrayList<String>( srs.size() + pSrs.length );
329            list.addAll( srs );
330            for ( int i = 0; i < pSrs.length; i++ ) {
331                if ( !list.contains( pSrs[i] ) ) {
332                    list.add( pSrs[i] );
333                }
334            }
335    
336            return list.toArray( new String[list.size()] );
337        }
338    
339        /**
340         * @param srs
341         * @return s true if the submitted srs (name) is supported by the layer
342         */
343        public boolean isSrsSupported( String srs ) {
344            String[] sr = getSrs();
345            for ( int i = 0; i < sr.length; i++ ) {
346                if ( sr[i].equals( srs ) ) {
347                    return true;
348                }
349            }
350            return false;
351        }
352    
353        /**
354         * adds the spatial reference system (srs)
355         * 
356         * @param srs
357         */
358        public void addSrs( String srs ) {
359            this.srs.add( srs );
360        }
361    
362        /**
363         * sets the srs
364         * 
365         * @param srs
366         */
367        public void setSrs( String[] srs ) {
368            if ( srs == null )
369                this.srs.clear();
370            else
371                this.srs = Arrays.asList( srs );
372        }
373    
374        /**
375         * Every Layer shall have exactly one &lt;LatLonBoundingBox&gt; element that is either stated explicitly or
376         * inherited from a parent Layer. LatLonBoundingBox states the minimum bounding rectangle of the map data in the
377         * EPSG:4326 geographic coordinate system. The LatLonBoundingBox attributes minx, miny, maxx, maxy indicate the
378         * edges of an enclosing rectangle in decimal degrees. LatLonBoundingBox shall be supplied regardless of what SRS
379         * the map server may support, but it may be approximate if EPSG:4326 is not supported. Its purpose is to facilitate
380         * geographic searches without requiring coordinate transformations by the search engine.
381         * 
382         * @return the bbox
383         */
384        public Envelope getLatLonBoundingBox() {
385            if ( ( latLonBoundingBox == null ) && ( parent != null ) ) {
386                return parent.getLatLonBoundingBox();
387            }
388            return latLonBoundingBox;
389        }
390    
391        /**
392         * sets the LatLonBoundingBox element that is either stated explicitly or inherited from a parent Layer.
393         * 
394         * @param latLonBoundingBox
395         */
396        public void setLatLonBoundingBox( Envelope latLonBoundingBox ) {
397            this.latLonBoundingBox = latLonBoundingBox;
398        }
399    
400        /**
401         * Layers may have zero or more &lt;BoundingBox&gt; elements that are either stated explicitly or inherited from a
402         * parent Layer. Each BoundingBox states the bounding rectangle of the map data in a particular spatial reference
403         * system; the attribute SRS indicates which SRS applies. If the data area is shaped irregularly then the
404         * BoundingBox gives the minimum enclosing rectangle. The attributes minx, miny, maxx, maxy indicate the edges of
405         * the bounding box in units of the specified SRS. Optional resx and resy attributes indicate the spatial resolution
406         * of the data in those same units.
407         * <p>
408         * A Layer may have multiple BoundingBox element, but each one shall state a different SRS. A Layer inherits any
409         * BoundingBox values defined by its parents. A BoundingBox inherited from the parent Layer for a particular SRS is
410         * replaced by any declaration for the same SRS in the child Layer. A BoundingBox in the child for a new SRS not
411         * already declared by the parent is added to the list of bounding boxes for the child Layer. A single Layer element
412         * shall not contain more than one BoundingBox for the same SRS.
413         * </p>
414         * 
415         * @return bounding boxes
416         */
417        public LayerBoundingBox[] getBoundingBoxes() {
418            HashMap<String, LayerBoundingBox> list = new HashMap<String, LayerBoundingBox>( 100 );
419    
420            if ( parent != null ) {
421                LayerBoundingBox[] plb = parent.getBoundingBoxes();
422    
423                for ( int i = 0; i < plb.length; i++ ) {
424                    list.put( plb[i].getSRS(), plb[i] );
425                }
426            }
427    
428            for ( int i = 0; i < boundingBox.size(); i++ ) {
429                LayerBoundingBox lb = (LayerBoundingBox) boundingBox.get( i );
430                list.put( lb.getSRS(), lb );
431            }
432    
433            LayerBoundingBox[] lbs = new LayerBoundingBox[list.size()];
434            return list.values().toArray( lbs );
435        }
436    
437        /**
438         * adds the &lt;BoundingBox&gt;
439         * 
440         * @param boundingBox
441         */
442        public void addBoundingBox( Envelope boundingBox ) {
443            this.boundingBox.add( boundingBox );
444        }
445    
446        /**
447         * sets the boundingBox
448         * 
449         * @param boundingBox
450         */
451        public void setBoundingBox( LayerBoundingBox[] boundingBox ) {
452            this.boundingBox.clear();
453    
454            if ( boundingBox != null ) {
455                for ( int i = 0; i < boundingBox.length; i++ ) {
456                    this.boundingBox.add( boundingBox[i] );
457                }
458            }
459        }
460    
461        /**
462         * Dimension declarations are inherited from parent Layers. Any new Dimension declarations in the child are added to
463         * the list inherited from the parent. A child shall not redefine a Dimension with the same name attribute as one
464         * that was inherited.
465         * 
466         * @return the dimensions
467         */
468        public Dimension[] getDimension() {
469            HashMap<String, Dimension> list = new HashMap<String, Dimension>();
470    
471            if ( parent != null ) {
472                Dimension[] pDim = parent.getDimension();
473    
474                for ( int i = 0; i < pDim.length; i++ ) {
475                    list.put( pDim[i].getName(), pDim[i] );
476                }
477            }
478    
479            for ( int i = 0; i < dimension.size(); i++ ) {
480                Dimension dim = dimension.get( i );
481    
482                if ( list.get( dim.getName() ) == null ) {
483                    list.put( dim.getName(), dim );
484                }
485            }
486    
487            return list.values().toArray( new Dimension[list.size()] );
488        }
489    
490        /**
491         * adds the dimension
492         * 
493         * @param dimension
494         */
495        public void addDimension( Dimension dimension ) {
496            this.dimension.add( dimension );
497        }
498    
499        /**
500         * sets the dimension
501         * 
502         * @param dimension
503         */
504        public void setDimension( Dimension[] dimension ) {
505            if ( dimension == null )
506                this.dimension.clear();
507            else
508                this.dimension = Arrays.asList( dimension );
509        }
510    
511        /**
512         * Extent declarations are inherited from parent Layers. Any Extent declarations in the child with the same name
513         * attribute as one inherited from the parent replaces the value declared by the parent. A Layer shall not declare
514         * an Extent unless a Dimension with the same name has been declared or inherited earlier in the Capabilities XML.
515         * 
516         * @return the extents
517         */
518        public Extent[] getExtent() {
519            HashMap<String, Extent> list = new HashMap<String, Extent>();
520    
521            if ( parent != null ) {
522                Extent[] pEx = parent.getExtent();
523    
524                for ( int i = 0; i < pEx.length; i++ ) {
525                    list.put( pEx[i].getName(), pEx[i] );
526                }
527            }
528    
529            for ( int i = 0; i < extent.size(); i++ ) {
530                Extent ex = extent.get( i );
531                list.put( ex.getName(), ex );
532            }
533    
534            return list.values().toArray( new Extent[list.size()] );
535        }
536    
537        /**
538         * adds the extent declarations
539         * 
540         * @param extent
541         */
542        public void addExtent( Extent extent ) {
543            this.extent.add( extent );
544        }
545    
546        /**
547         * sets the extent
548         * 
549         * @param extent
550         */
551        public void setExtent( Extent[] extent ) {
552            if ( extent == null )
553                this.extent.clear();
554            else
555                this.extent = Arrays.asList( extent );
556        }
557    
558        /**
559         * The optional &lt;Attribution&gt; element provides a way to identify the source of the map data used in a Layer or
560         * collection of Layers. Attribution encloses several optional elements: <OnlineResource>states the data provider's
561         * URL; &lt;Title&gt; is a human-readable string naming the data provider; &lt;LogoURL&gt; is the URL of a logo
562         * image. Client applications may choose to display one or more of these items. A &lt;Format&gt; element in LogoURL
563         * indicates the MIME type of the logo image, and the attributes width and height state the size of the image in
564         * pixels.
565         * 
566         * @return the attribution
567         */
568        public Attribution getAttribution() {
569            if ( ( parent != null ) && ( attribution == null ) ) {
570                return parent.getAttribution();
571            }
572            return attribution;
573        }
574    
575        /**
576         * sets the optional &lt;Attribution&gt; element
577         * 
578         * @param attribution
579         */
580        public void setAttribution( Attribution attribution ) {
581            this.attribution = attribution;
582        }
583    
584        /**
585         * The authority attribute of the Identifier element corresponds to the name attribute of a separate
586         * &lt;AuthorityURL&gt; element. AuthorityURL encloses an &lt;OnlineResource&gt; element which states the URL of a
587         * document defining the meaning of the Identifier values.
588         * 
589         * @return the authority url object
590         */
591        public AuthorityURL[] getAuthorityURL() {
592            HashMap<String, AuthorityURL> list = new HashMap<String, AuthorityURL>();
593    
594            if ( parent != null ) {
595                AuthorityURL[] pAu = parent.getAuthorityURL();
596    
597                for ( int i = 0; i < pAu.length; i++ ) {
598                    list.put( pAu[i].getName(), pAu[i] );
599                }
600            }
601    
602            for ( int i = 0; i < authorityURL.size(); i++ ) {
603                AuthorityURL au = authorityURL.get( i );
604    
605                if ( list.get( au.getName() ) == null ) {
606                    list.put( au.getName(), au );
607                }
608            }
609    
610            AuthorityURL[] aus = new AuthorityURL[list.size()];
611            return list.values().toArray( aus );
612        }
613    
614        /**
615         * adds the authority attribute of the Identifier element
616         * 
617         * @param authorityURL
618         */
619        public void addAuthorityURL( AuthorityURL authorityURL ) {
620            this.authorityURL.add( authorityURL );
621        }
622    
623        /**
624         * sets the authority attribute of the Identifier element
625         * 
626         * @param authorityURL
627         */
628        public void setAuthorityURL( AuthorityURL[] authorityURL ) {
629            if ( authorityURL == null )
630                this.authorityURL.clear();
631            else
632                this.authorityURL = Arrays.asList( authorityURL );
633        }
634    
635        /**
636         * A Map Server may use zero or more &lt;Identifier&gt; elements to list ID numbers or labels defined by a
637         * particular Authority. The text content of the Identifier element is the ID value.
638         * 
639         * @return the identifiers
640         */
641        public Identifier[] getIdentifier() {
642            HashMap<String, Identifier> list = new HashMap<String, Identifier>();
643    
644            if ( parent != null ) {
645                Identifier[] pIden = parent.getIdentifier();
646    
647                for ( int i = 0; i < pIden.length; i++ ) {
648                    list.put( pIden[i].getAuthority(), pIden[i] );
649                }
650            }
651    
652            for ( int i = 0; i < identifier.size(); i++ ) {
653                Identifier iden = identifier.get( i );
654    
655                if ( list.get( iden.getAuthority() ) == null ) {
656                    list.put( iden.getAuthority(), iden );
657                }
658            }
659    
660            Identifier[] ids = new Identifier[list.size()];
661            return list.values().toArray( ids );
662        }
663    
664        /**
665         * adds the &lt;Identifier&gt;
666         * 
667         * @param identifier
668         */
669        public void addIdentifier( Identifier identifier ) {
670            this.identifier.add( identifier );
671        }
672    
673        /**
674         * sets the &lt;Identifier&gt;
675         * 
676         * @param identifier
677         */
678        public void setIdentifier( Identifier[] identifier ) {
679            if ( identifier == null )
680                this.identifier.clear();
681            else
682                this.identifier = Arrays.asList( identifier );
683        }
684    
685        /**
686         * A Map Server should use one or more &lt;MetadataURL&gt; elements to offer detailed, standardized metadata about
687         * the data underneath a particular layer. The type attribute indicates the standard to which the metadata complies.
688         * Two types are defined at present: the value 'TC211' refers to [ISO 19115]; the value 'FGDC' refers to
689         * [FGDC-STD-001-1988]. The MetadataURL element shall not be used to reference metadata in a non-standardized
690         * metadata format; see DataURL instead. The enclosed &lt;Format&gt; element indicates the file format MIME type of
691         * the metadata record.
692         * 
693         * @return the metadata urls
694         */
695        public MetadataURL[] getMetadataURL() {
696            return metadataURL.toArray( new MetadataURL[metadataURL.size()] );
697        }
698    
699        /**
700         * adds the metadataURL
701         * 
702         * @param metadataURL
703         */
704        public void addMetadataURL( MetadataURL metadataURL ) {
705            this.metadataURL.add( metadataURL );
706        }
707    
708        /**
709         * sets the metadataURL
710         * 
711         * @param metadataURL
712         */
713        public void setMetadataURL( MetadataURL[] metadataURL ) {
714            if ( metadataURL == null )
715                this.metadataURL.clear();
716            else
717                this.metadataURL = Arrays.asList( metadataURL );
718        }
719    
720        /**
721         * A Map Server may use DataURL to offer more information about the data represented by a particular layer. While
722         * the semantics are not well-defined, as long as the results of an HTTP GET request against the DataURL are
723         * properly MIME-typed, Viewer Clients and Cascading Map Servers can make use of this. Use 6lt;MetadataURL&gt;
724         * instead for a precisely defined reference to standardized metadata records.
725         * 
726         * @return the data URLs
727         */
728        public DataURL[] getDataURL() {
729            return dataURL.toArray( new DataURL[dataURL.size()] );
730        }
731    
732        /**
733         * adds the dataURL
734         * 
735         * @param dataURL
736         */
737        public void addDataURL( DataURL dataURL ) {
738            this.dataURL.add( dataURL );
739        }
740    
741        /**
742         * sets the dataURL
743         * 
744         * @param dataURL
745         */
746        public void setDataURL( DataURL[] dataURL ) {
747            if ( dataURL == null )
748                this.dataURL.clear();
749            else
750                this.dataURL = Arrays.asList( dataURL );
751        }
752    
753        /**
754         * A Map Server may use a &lt;FeatureListURL&gt; element to point to a list of the features represented in a Layer.
755         * 
756         * @return the feature list urls
757         */
758        public FeatureListURL[] getFeatureListURL() {
759            return featureListURL.toArray( new FeatureListURL[featureListURL.size()] );
760        }
761    
762        /**
763         * adds the &lt;FeatureListURL&gt;
764         * 
765         * @param featureListURL
766         */
767        public void addFeatureListURL( FeatureListURL featureListURL ) {
768            this.featureListURL.add( featureListURL );
769        }
770    
771        /**
772         * sets the &lt;FeatureListURL&gt;
773         * 
774         * @param featureListURL
775         */
776        public void setFeatureListURL( FeatureListURL[] featureListURL ) {
777            if ( featureListURL == null )
778                this.featureListURL.clear();
779            else
780                this.featureListURL = Arrays.asList( featureListURL );
781        }
782    
783        /**
784         * @return a list of style that can be used for rendering the layer.
785         */
786        public Style[] getStyles() {
787            LinkedList<Style> list = new LinkedList<Style>();
788    
789            // styles are inherited here
790            // probably that's not what SLD/SE want, but let's keep it for backwards compatibility
791            if ( parent != null ) {
792                list.addAll( asList( parent.getStyles() ) );
793            }
794    
795            // overwrite the inherited styles with the ones defined here
796            for ( Style style : stylesArray ) {
797                ListIterator<Style> iter = list.listIterator();
798                while ( iter.hasNext() ) {
799                    if ( iter.next().getName().equals( style.getName() ) ) {
800                        iter.remove();
801                    }
802                }
803            }
804            list.addAll( asList( stylesArray ) );
805    
806            return list.toArray( new Style[list.size()] );
807        }
808    
809        /**
810         * adds a list of style that can be used form rendering the layer.
811         * 
812         * @param style
813         */
814        public void addStyles( Style style ) {
815            this.styles.put( style.getName(), style );
816        }
817    
818        /**
819         * sets a list of style that can be used form rendering the layer.
820         * 
821         * @param styles
822         */
823        public void setStyles( Style[] styles ) {
824            stylesArray = styles;
825            if ( styles == null ) {
826                this.styles.clear();
827            } else {
828                for ( Style style : styles ) {
829                    this.styles.put( style.getName(), style );
830                }
831            }
832        }
833    
834        /**
835         * returns the <tt>UserStyle</tt> (SLD) representation of the style identified by the submitted name.
836         * 
837         * @param name
838         *            of the requested style
839         * @return SLD - UserStyle
840         * 
841         */
842        public UserStyle getStyle( String name ) {
843    
844            Style style = styles.get( name );
845            UserStyle us = null;
846    
847            if ( style == null ) {
848                if ( parent != null ) {
849                    us = parent.getStyle( name );
850                }
851            } else {
852                us = style.getStyleContent();
853            }
854    
855            return us;
856        }
857    
858        /**
859         * returns the <tt>Style</tt> identified by the submitted name.
860         * 
861         * @param name
862         *            of the requested style
863         * @return Style
864         * 
865         */
866        public Style getStyleResource( String name ) {
867    
868            Style style = styles.get( name );
869    
870            if ( style == null && name.length() == 0 ) {
871                String tmpName = "default";
872                style = styles.get( tmpName );
873                if ( style == null && name.length() == 0 ) {
874                    tmpName = "default:" + this.name;
875                    style = styles.get( tmpName );
876                }
877            } else if ( style == null && "default".equals( name ) ) {
878                String tmpName = "default:" + this.name;
879                style = styles.get( tmpName );
880            }
881    
882            if ( style == null ) {
883                if ( parent != null ) {
884                    style = parent.getStyleResource( name );
885                }
886            }
887    
888            return style;
889        }
890    
891        /**
892         * Layers may include a &lt;ScaleHint&gt; element that suggests minimum and maximum scales for which it is
893         * appropriate to display this layer. Because WMS output is destined for output devices of arbitrary size and
894         * resolution, the usual definition of scale as the ratio of map size to real-world size is not appropriate here.
895         * The following definition of Scale Hint is recommended. Consider a hypothetical map with a given Bounding Box,
896         * width and height. The central pixel of that map (or the pixel just to the northwest of center) will have some
897         * size, which can be expressed as the ground distance in meters of the southwest to northeast diagonal of that
898         * pixel. The two values in ScaleHint are the minimum and maximum recommended values of that diagonal. It is
899         * recognized that this definition is not geodetically precise, but at the same time the hope is that by including
900         * it conventions will develop that can be later specified more clearly.
901         * 
902         * @return the scale hint
903         */
904        public ScaleHint getScaleHint() {
905            if ( ( parent != null ) && ( scaleHint == null ) ) {
906                return parent.getScaleHint();
907            }
908            return scaleHint;
909        }
910    
911        /**
912         * sets the <ScaleHint>
913         * 
914         * @param scaleHint
915         */
916        public void setScaleHint( ScaleHint scaleHint ) {
917            this.scaleHint = scaleHint;
918        }
919    
920        /**
921         * returns a list of layers the are enclosed by this layer.
922         * 
923         * @return the layers
924         */
925        public Layer[] getLayer() {
926            return layer.toArray( new Layer[layer.size()] );
927        }
928    
929        /**
930         * removes a Layer identified by its name from the parent Layer. A reference to the removed layer will be returned.
931         * If no Layer matching the passed name can be found nothing happens and <tt>null</tt> will be returned.
932         * 
933         * @param name
934         * 
935         * @return removerd Layer
936         */
937        public Layer removeLayer( String name ) {
938            for ( int i = 0; i < layer.size(); i++ ) {
939                Layer ly = layer.get( i );
940                if ( ly.getName() != null ) {
941                    if ( ly.getName().equals( name ) ) {
942                        layer.remove( i );
943                        return ly;
944                    }
945                }
946            }
947            return null;
948        }
949    
950        /**
951         * removes a Layer identified by its title from the parent Layer. A reference to the removed layer will be returned.
952         * If no Layer matching the passed title can be found nothing happens and <tt>null</tt> will be returned.
953         * 
954         * @param title
955         * 
956         * @return removerd Layer
957         */
958        public Layer removeLayerByTitle( String title ) {
959            for ( int i = 0; i < layer.size(); i++ ) {
960                Layer ly = layer.get( i );
961                if ( ly.getTitle().equals( title ) ) {
962                    layer.remove( i );
963                    return ly;
964                }
965            }
966            return null;
967        }
968    
969        /**
970         * adds a list of layers the are enclosed by this layer.
971         * 
972         * @param layer
973         */
974        public void addLayer( Layer layer ) {
975            this.layer.add( layer );
976        }
977    
978        /**
979         * sets a list of layers the are enclosed by this layer.
980         * 
981         * @param layer
982         */
983        public void setLayer( Layer[] layer ) {
984            if ( layer == null ) {
985                this.layer.clear();
986            } else {
987                this.layer = new ArrayList<Layer>( Arrays.asList( layer ) );
988            }
989        }
990    
991        /**
992         * source where the WMS can find the data of a layer.
993         * 
994         * @return the data sources
995         */
996        public AbstractDataSource[] getDataSource() {
997            return dataSource.toArray( new AbstractDataSource[dataSource.size()] );
998        }
999    
1000        /**
1001         * source where the WMS can find the data of a layer.
1002         * 
1003         * @param dataSource
1004         */
1005        public void setDataSource( AbstractDataSource[] dataSource ) {
1006            if ( dataSource == null )
1007                this.dataSource.clear();
1008            else
1009                this.dataSource = Arrays.asList( dataSource );
1010        }
1011    
1012        /**
1013         * source where the WMS can find the data of a layer.
1014         * 
1015         * @param dataSource
1016         */
1017        public void addDataSource( AbstractDataSource dataSource ) {
1018            this.dataSource.add( dataSource );
1019        }
1020    
1021        /**
1022         * @return the parent layer of this layer. If the method returns <tt>null</tt> the current layer is the root layer.
1023         *         In addition with the <tt>getLayer</tt> method this enables a program to traverse the layer tree in both
1024         *         directions.
1025         */
1026        public Layer getParent() {
1027            return parent;
1028        }
1029    
1030        /**
1031         * sets the parent layer of this layer.
1032         * 
1033         * @param parent
1034         */
1035        public void setParent( Layer parent ) {
1036            this.parent = parent;
1037        }
1038    
1039        /**
1040         * @return '0' if the layer is provided directly form the deegree WMS. other it returns the number of cascaded WMS
1041         *         servers the is passed through
1042         * 
1043         */
1044        public int getCascaded() {
1045            return cascaded;
1046        }
1047    
1048        /**
1049         * @return '0' if the WMS can resize map to arbitrary height. nonzero: map has a fixed height that cannot be changed
1050         *         by the WMS.
1051         * 
1052         */
1053        public int getFixedHeight() {
1054            return fixedHeight;
1055        }
1056    
1057        /**
1058         * @return '0' if the WMS can resize map to arbitrary width. nonzero: map has a fixed width that cannot be changed
1059         *         by the WMS.
1060         * 
1061         */
1062        public int getFixedWidth() {
1063            return fixedWidth;
1064        }
1065    
1066        /**
1067         * @return false if the WMS can map a subset of the full bounding box.
1068         * 
1069         */
1070        public boolean hasNoSubsets() {
1071            return noSubsets;
1072        }
1073    
1074        /**
1075         * @return false if map data represents vector features that probably do not completely fill space.
1076         * 
1077         */
1078        public boolean isOpaque() {
1079            return opaque;
1080        }
1081    
1082        /**
1083         * @return true if the layer is queryable. That means it can be targeted by a GetFeatureInfo request.
1084         * 
1085         */
1086        public boolean isQueryable() {
1087            return queryable;
1088        }
1089    
1090    }