    001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.4_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 &lt;Layer&gt; 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    }