    001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.4_testing/src/org/deegree/ogcwebservices/wpvs/configuration/WPVSConfigurationDocument.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
    037    package org.deegree.ogcwebservices.wpvs.configuration;
    038
    039    import java.awt.Color;
    040    import java.net.MalformedURLException;
    041    import java.net.URI;
    042    import java.net.URL;
    043    import java.util.ArrayList;
    044    import java.util.HashMap;
    045    import java.util.List;
    046    import java.util.Map;
    047
    048    import org.deegree.datatypes.QualifiedName;
    049    import org.deegree.framework.log.ILogger;
    050    import org.deegree.framework.log.LoggerFactory;
    051    import org.deegree.framework.util.IDGenerator;
    052    import org.deegree.framework.util.KVP2Map;
    053    import org.deegree.framework.util.StringTools;
    054    import org.deegree.framework.xml.InvalidConfigurationException;
    055    import org.deegree.framework.xml.XMLParsingException;
    056    import org.deegree.framework.xml.XMLTools;
    057    import org.deegree.i18n.Messages;
    058    import org.deegree.model.crs.CoordinateSystem;
    059    import org.deegree.model.filterencoding.AbstractFilter;
    060    import org.deegree.model.filterencoding.Filter;
    061    import org.deegree.model.metadata.iso19115.Keywords;
    062    import org.deegree.model.metadata.iso19115.OnlineResource;
    063    import org.deegree.model.spatialschema.Envelope;
    064    import org.deegree.model.spatialschema.GMLGeometryAdapter;
    065    import org.deegree.model.spatialschema.Geometry;
    066    import org.deegree.model.spatialschema.GeometryException;
    067    import org.deegree.model.spatialschema.GeometryFactory;
    068    import org.deegree.model.spatialschema.Surface;
    069    import org.deegree.ogcbase.CommonNamespaces;
    070    import org.deegree.ogcbase.PropertyPath;
    071    import org.deegree.ogcwebservices.InvalidParameterValueException;
    072    import org.deegree.ogcwebservices.MissingParameterValueException;
    073    import org.deegree.ogcwebservices.OGCWebServiceException;
    074    import org.deegree.ogcwebservices.getcapabilities.InvalidCapabilitiesException;
    075    import org.deegree.ogcwebservices.wcs.getcoverage.GetCoverage;
    076    import org.deegree.ogcwebservices.wms.operation.GetMap;
    077    import org.deegree.ogcwebservices.wpvs.capabilities.DataProvider;
    078    import org.deegree.ogcwebservices.wpvs.capabilities.Dataset;
    079    import org.deegree.ogcwebservices.wpvs.capabilities.DatasetReference;
    080    import org.deegree.ogcwebservices.wpvs.capabilities.Dimension;
    081    import org.deegree.ogcwebservices.wpvs.capabilities.ElevationModel;
    082    import org.deegree.ogcwebservices.wpvs.capabilities.FeatureListReference;
    083    import org.deegree.ogcwebservices.wpvs.capabilities.Identifier;
    084    import org.deegree.ogcwebservices.wpvs.capabilities.MetaData;
    085    import org.deegree.ogcwebservices.wpvs.capabilities.OWSCapabilities;
    086    import org.deegree.ogcwebservices.wpvs.capabilities.Style;
    087    import org.deegree.ogcwebservices.wpvs.capabilities.WPVSCapabilitiesDocument;
    088    import org.w3c.dom.Element;
    089    import org.w3c.dom.Node;
    090    import org.w3c.dom.Text;
    091
    092    /**
    093     * Parser for WPVS configuration documents.
    094     *
    095     * @author <a href="mailto:mays@lat-lon.de">Judit Mays</a>
    096     * @author last edited by: $Author: aschmitz $
    097     *
    098     * @version $Revision: 23585 $, $Date: 2010-04-13 14:07:18 +0200 (Di, 13. Apr 2010) $
    099     */
    100    public class WPVSConfigurationDocument extends WPVSCapabilitiesDocument {
    101
    102        private static final long serialVersionUID = 1511898601495679163L;
    103
    104        private static final ILogger LOG = LoggerFactory.getLogger( WPVSConfigurationDocument.class );
    105
    106        private static String PRE_DWPVS = CommonNamespaces.DEEGREEWPVS_PREFIX + ":";
    107
    108        private static String PRE_OWS = CommonNamespaces.OWS_PREFIX + ":";
    109
    110        // The smallestMinimalScaleDenomiator is needed to calculate the smallest resolutionstripe
    111        // possible
    112        private double smallestMinimalScaleDenominator = Double.MAX_VALUE;
    113
    114        /**
    115         * Creates a class representation of the <code>WPVSConfiguration</code> document.
    116         *
    117         * @return Returns a WPVSConfiguration object.
    118         * @throws InvalidConfigurationException
    119         */
    120        public WPVSConfiguration parseConfiguration()
    121                                throws InvalidConfigurationException {
    122            WPVSConfiguration wpvsConfiguration = null;
    123            try {
    124
    125                // TODO 'contents' field not verified, therefore null! Check spec.
    126                Element requestedNode = (Element) XMLTools.getRequiredNode( getRootElement(), PRE_DWPVS + "deegreeParams",
    127                                                                            nsContext );
    128                WPVSDeegreeParams wpvsDeegreeParams = parseDeegreeParams( requestedNode );
    129
    130                requestedNode = (Element) XMLTools.getRequiredNode( getRootElement(), PRE_DWPVS + "Dataset", nsContext );
    131                Dataset rootDataset = parseDataset( requestedNode, null, null, 0, 9E9,
    132                                                    wpvsDeegreeParams.getMinimalWCS_DGMResolution() );
    133
    134                wpvsConfiguration = new WPVSConfiguration(
    135                                                           parseVersion(),
    136                                                           parseUpdateSequence(),
    137                                                           getServiceIdentification(),
    138                                                           getServiceProvider(),
    139                                                           parseOperationsMetadata( wpvsDeegreeParams.getDefaultOnlineResource() ),
    140                                                           null,
    141                                                           rootDataset,
    142                                                           wpvsDeegreeParams,
    143                                                           ( Double.isInfinite( smallestMinimalScaleDenominator ) ? 1.0
    144                                                                                                                 : smallestMinimalScaleDenominator ) );
    145
    146            } catch ( XMLParsingException e ) {
    147                throw new InvalidConfigurationException( e.getMessage() + "\n" + StringTools.stackTraceToString( e ) );
    148
    149            } catch ( MissingParameterValueException e ) {
    150                throw new InvalidConfigurationException( e.getMessage() + "\n" + StringTools.stackTraceToString( e ) );
    151
    152            } catch ( InvalidParameterValueException e ) {
    153                throw new InvalidConfigurationException( e.getMessage() + "\n" + StringTools.stackTraceToString( e ) );
    154
    155            } catch ( OGCWebServiceException e ) {
    156                throw new InvalidConfigurationException( e.getMessage() + "\n" + StringTools.stackTraceToString( e ) );
    157
    158            } catch ( InvalidConfigurationException e ) {
    159                throw new InvalidConfigurationException( e.getMessage() + "\n" + StringTools.stackTraceToString( e ) );
    160
    161            }
    162            return wpvsConfiguration;
    163        }
    164
    165        /**
    166         * Creates and returns a new <code>WPVSDeegreeParams</code> object from the given <code>Node</code>.
    167         *
    168         * @param deegreeNode
    169         * @return Returns a new WPVSDeegreeParams object.
    170         * @throws XMLParsingException
    171         * @throws InvalidConfigurationException
    172         */
    173        private WPVSDeegreeParams parseDeegreeParams( Node deegreeNode )
    174                                throws XMLParsingException, InvalidConfigurationException {
    175
    176            Element deegreeElement = (Element) XMLTools.getRequiredNode( deegreeNode, PRE_DWPVS + "DefaultOnlineResource",
    177                                                                         nsContext );
    178            OnlineResource defaultOnlineResource = parseOnLineResource( deegreeElement );
    179
    180            int cacheSize = XMLTools.getNodeAsInt( deegreeNode, PRE_DWPVS + "CacheSize", nsContext, 100 );
    181
    182            int maxLifeTime = XMLTools.getNodeAsInt( deegreeNode, PRE_DWPVS + "MaxLifeTime", nsContext, 3600 );
    183
    184            int reqTimeLimit = XMLTools.getNodeAsInt( deegreeNode, PRE_DWPVS + "RequestTimeLimit", nsContext, 60 );
    185            reqTimeLimit *= 1000;
    186
    187            int maxTextureDimension = XMLTools.getNodeAsInt( deegreeNode, PRE_DWPVS + "MaxTextureDimension", nsContext,
    188                                                             Integer.MAX_VALUE );
    189
    190            int quadMergeCount = XMLTools.getNodeAsInt( deegreeNode, PRE_DWPVS + "QuadMergeCount", nsContext, 10 );
    191
    192            float viewQuality = (float) XMLTools.getNodeAsDouble( deegreeNode, PRE_DWPVS + "ViewQuality", nsContext, 0.95f );
    193
    194            int maxMapWidth = XMLTools.getNodeAsInt( deegreeNode, PRE_DWPVS + "MaxViewWidth", nsContext, 1000 );
    195
    196            int maxMapHeight = XMLTools.getNodeAsInt( deegreeNode, PRE_DWPVS + "MaxViewHeight", nsContext, 1000 );
    197
    198            String charSet = XMLTools.getNodeAsString( deegreeNode, PRE_DWPVS + "CharacterSet", nsContext, "UTF-8" );
    199
    200            Node copyrightNode = XMLTools.getNode( deegreeNode, PRE_DWPVS + "Copyright", nsContext );
    201            String copyright = null;
    202            boolean isWatermarked = false;
    203            if ( copyrightNode != null ) {
    204
    205                Node copyTextNode = XMLTools.getNode( copyrightNode, PRE_DWPVS + "Text", nsContext );
    206                Node copyURLNode = XMLTools.getNode( copyrightNode, PRE_DWPVS + "ImageURL/@xlink:href", nsContext );
    207
    208                if ( copyTextNode != null ) {
    209                    copyright = XMLTools.getRequiredNodeAsString( copyrightNode, PRE_DWPVS + "Text/text()", nsContext );
    210                } else if ( copyURLNode != null ) {
    211                    copyright = XMLTools.getRequiredNodeAsString( copyrightNode, PRE_DWPVS + "ImageURL/@xlink:href",
    212                                                                  nsContext );
    213
    214                    isWatermarked = XMLTools.getNodeAsBoolean( copyrightNode, PRE_DWPVS + "ImageURL/@watermark", nsContext,
    215                                                               isWatermarked );
    216
    217                    try {
    218                        copyright = resolve( copyright ).toString();
    219                    } catch ( MalformedURLException e ) {
    220                        throw new InvalidConfigurationException( "Copyright/ImageURL '" + copyright
    221                                                                 + "' doesn't seem to be a valid URL!" );
    222                    }
    223
    224                } else {
    225                    throw new InvalidConfigurationException( "Copyright must contain either "
    226                                                             + "a Text-Element or an ImageURL-Element!" );
    227                }
    228            }
    229
    230            Map<String, URL> backgroundMap = new HashMap<String, URL>( 10 );
    231            Element backgrounds = (Element) XMLTools.getNode( deegreeNode, PRE_DWPVS + "BackgroundList", nsContext );
    232            if ( backgrounds != null ) {
    233                List<Element> backgroundList = XMLTools.getElements( backgrounds, PRE_DWPVS + "Background", nsContext );
    234                for ( Element background : backgroundList ) {
    235
    236                    String bgName = background.getAttribute( "name" );
    237                    String bgHref = background.getAttribute( "href" );
    238
    239                    if ( bgName == null || bgName.length() == 0 || bgHref == null || bgHref.length() == 0 )
    240                        throw new InvalidConfigurationException(
    241                                                                 "Background must contain a 'name' and a "
    242                                                                                         + " 'href' attribute, both if which must contain non-empty strings." );
    243
    244                    try {
    245
    246                        backgroundMap.put( bgName, resolve( bgHref ) );
    247                    } catch ( MalformedURLException e ) {
    248                        throw new InvalidConfigurationException( "Background", e.getMessage() );
    249                    }
    250                }
    251
    252            }
    253
    254            boolean quality = XMLTools.getNodeAsBoolean( deegreeNode, PRE_DWPVS + "RequestQualityPreferred", nsContext,
    255                                                         true );
    256            double maximumFarClippingPlane = XMLTools.getNodeAsDouble( deegreeNode, PRE_DWPVS
    257                                                                                    + "RequestsMaximumFarClippingPlane",
    258                                                                       nsContext, 15000 );
    259
    260            double nearClippingPlane = XMLTools.getNodeAsDouble( deegreeNode, PRE_DWPVS + "NearClippingPlane", nsContext, 2 );
    261
    262            String defaultSplitter = XMLTools.getNodeAsString( deegreeNode, PRE_DWPVS + "DefaultSplitter", nsContext,
    263                                                               "QUAD" ).toUpperCase();
    264
    265            double minimalTerrainHeight = XMLTools.getNodeAsDouble( deegreeNode, PRE_DWPVS + "MinimalTerrainHeight",
    266                                                                    nsContext, 0 );
    267
    268            double minimalWCS_DGMResolution = XMLTools.getNodeAsDouble( deegreeNode,
    269                                                                        PRE_DWPVS + "MinimalWCSElevationModelResolution",
    270                                                                        nsContext, 0 );
    271
    272            double extendRequestPercentage = XMLTools.getNodeAsDouble( deegreeNode, PRE_DWPVS + "ExtendRequestPercentage",
    273                                                                       nsContext, 0 );
    274            if ( extendRequestPercentage > 100 ) {
    275                LOG.logWarning( Messages.getMessage( "WPVS_WRONG_EXTEND_REQUEST_PERCENTAGE",
    276                                                     Double.valueOf( extendRequestPercentage ), Double.valueOf( 100 ) ) );
    277                extendRequestPercentage = 100d;
    278            } else if ( extendRequestPercentage < -0.00000001 ) {
    279                LOG.logWarning( Messages.getMessage( "WPVS_WRONG_EXTEND_REQUEST_PERCENTAGE",
    280                                                     Double.valueOf( extendRequestPercentage ), Double.valueOf( 0 ) ) );
    281                extendRequestPercentage = 0d;
    282            }
    283
    284            boolean antialiased = XMLTools.getNodeAsBoolean( deegreeNode, PRE_DWPVS + "RenderAntialiased", nsContext, true );
    285            WPVSDeegreeParams wpvsDeegreeParams = new WPVSDeegreeParams( defaultOnlineResource, cacheSize, reqTimeLimit,
    286                                                                         charSet, copyright, isWatermarked, maxLifeTime,
    287                                                                         viewQuality, backgroundMap, maxMapWidth,
    288                                                                         maxMapHeight, quality, maximumFarClippingPlane,
    289                                                                         nearClippingPlane, defaultSplitter,
    290                                                                         minimalTerrainHeight, minimalWCS_DGMResolution,
    291                                                                         extendRequestPercentage * 0.01,
    292                                                                         maxTextureDimension, quadMergeCount, antialiased );
    293
    294            return wpvsDeegreeParams;
    295        }
    296
    297        /**
    298         * Creates and returns a new <code>Dataset</code> object from the given <code>Element</code> and the parent
    299         * <code>Dataset</code> object.
    300         *
    301         * @param datasetElement
    302         * @param parent
    303         *            may be null if root Dataset
    304         * @return Returns a new Dataset object.
    305         * @throws XMLParsingException
    306         * @throws MissingParameterValueException
    307         * @throws InvalidParameterValueException
    308         * @throws OGCWebServiceException
    309         * @throws InvalidConfigurationException
    310         */
    311        private Dataset parseDataset( Element datasetElement, Dataset parent, CoordinateSystem defaultCoordinateSystem,
    312                                      double defaultMinScaleDonominator, double defaultMaxScaleDenominator,
    313                                      double minimalWCS_DGMResolution )
    314                                throws XMLParsingException, MissingParameterValueException, InvalidParameterValueException,
    315                                OGCWebServiceException, InvalidConfigurationException {
    316            // attributes
    317            boolean queryable = XMLTools.getNodeAsBoolean( datasetElement, "@queryable", nsContext, false );
    318            boolean opaque = XMLTools.getNodeAsBoolean( datasetElement, "@opaque", nsContext, false );
    319            boolean noSubsets = XMLTools.getNodeAsBoolean( datasetElement, "@noSubsets", nsContext, false );
    320            int fixedWidth = XMLTools.getNodeAsInt( datasetElement, "@fixedWidth", nsContext, 0 );
    321            int fixedHeight = XMLTools.getNodeAsInt( datasetElement, "@fixedHeight", nsContext, 0 );
    322
    323            // elements
    324            String name = XMLTools.getNodeAsString( datasetElement, PRE_DWPVS + "Name/text()", nsContext, null );
    325            String title = XMLTools.getRequiredNodeAsString( datasetElement, PRE_DWPVS + "Title/text()", nsContext );
    326            String abstract_ = XMLTools.getNodeAsString( datasetElement, PRE_DWPVS + "Abstract/text()", nsContext, null );
    327            Keywords[] keywords = getKeywords( XMLTools.getNodes( datasetElement, PRE_OWS + "Keywords", nsContext ) );
    328            String[] crsStrings = XMLTools.getNodesAsStrings( datasetElement, PRE_DWPVS + "CRS/text()", nsContext );
    329            List<CoordinateSystem> crsList = parseCoordinateSystems( crsStrings );
    330
    331            if ( parent == null ) { // root dataset
    332                if ( crsList.size() == 0 || crsList.get( 0 ) == null ) {
    333                    throw new InvalidCapabilitiesException( Messages.getMessage( "WPVS_NO_TOPLEVEL_DATASET_CRS", title ) );
    334                }
    335                defaultCoordinateSystem = crsList.get( 0 );
    336            }
    337
    338            String[] format = XMLTools.getRequiredNodesAsStrings( datasetElement, PRE_DWPVS + "Format/text()", nsContext );
    339            // wgs84 == mandatory
    340            Element boundingBoxElement = (Element) XMLTools.getRequiredNode( datasetElement, PRE_OWS + "WGS84BoundingBox",
    341                                                                             nsContext );
    342            Envelope wgs84BoundingBox = getWGS84BoundingBoxType( boundingBoxElement );
    343
    344            Envelope[] boundingBoxes = getBoundingBoxes( datasetElement, defaultCoordinateSystem );
    345            Dimension[] dimensions = parseDimensions( datasetElement );
    346            DataProvider dataProvider = parseDataProvider( datasetElement );
    347            Identifier identifier = parseDatasetIdentifier( datasetElement, PRE_DWPVS + "Identifier" );
    348            MetaData[] metaData = parseMetaData( datasetElement );
    349            DatasetReference[] datasetRefs = parseDatasetReferences( datasetElement );
    350            FeatureListReference[] featureListRefs = parseFeatureListReferences( datasetElement );
    351            Style[] style = parseStyles( datasetElement );
    352            double minScaleDenom = XMLTools.getNodeAsDouble( datasetElement, PRE_DWPVS + "MinimumScaleDenominator/text()",
    353                                                             nsContext, defaultMinScaleDonominator );
    354
    355            // update the smallestMinimalScaleDenomiator
    356            if ( minScaleDenom < smallestMinimalScaleDenominator ) {
    357                smallestMinimalScaleDenominator = minScaleDenom;
    358            }
    359
    360            double maxScaleDenom = XMLTools.getNodeAsDouble( datasetElement, PRE_DWPVS + "MaximumScaleDenominator/text()",
    361                                                             nsContext, defaultMaxScaleDenominator );
    362
    363            if ( parent == null ) {// toplevel dataset sets the default minScaleDenominator
    364                defaultMinScaleDonominator = minScaleDenom;
    365                defaultMaxScaleDenominator = maxScaleDenom;
    366            }
    367
    368            if ( minScaleDenom >= maxScaleDenom ) {
    369                throw new InvalidCapabilitiesException( "MinimumScaleDenominator must be "
    370                                                        + "less than MaximumScaleDenominator!" );
    371            }
    372            CoordinateSystem currentCRS = defaultCoordinateSystem;
    373            if ( crsList.size() > 0 && crsList.get( 0 ) != null ) {
    374                currentCRS = crsList.get( 0 );
    375            }
    376            ElevationModel elevationModel = parseElevationModel( datasetElement, name, minimalWCS_DGMResolution,
    377                                                                 currentCRS, defaultMinScaleDonominator,
    378                                                                 defaultMaxScaleDenominator );
    379            AbstractDataSource[] dataSources = parseAbstractDatasources( datasetElement, name, defaultMinScaleDonominator,
    380                                                                         defaultMaxScaleDenominator, currentCRS );
    381
    382            // create new root dataset
    383            Dataset dataset = new Dataset( queryable, opaque, noSubsets, fixedWidth, fixedHeight, name, title, abstract_,
    384                                           keywords, crsList, format, wgs84BoundingBox, boundingBoxes, dimensions,
    385                                           dataProvider, identifier, metaData, datasetRefs, featureListRefs, style,
    386                                           minScaleDenom, maxScaleDenom, null, elevationModel, dataSources, parent );
    387
    388            // get child datasets
    389            List<Element> nl = XMLTools.getElements( datasetElement, PRE_DWPVS + "Dataset", nsContext );
    390            Dataset[] childDatasets = new Dataset[nl.size()];
    391            for ( int i = 0; i < childDatasets.length; i++ ) {
    392                childDatasets[i] = parseDataset( nl.get( i ), dataset, defaultCoordinateSystem, defaultMinScaleDonominator,
    393                                                 defaultMaxScaleDenominator, minimalWCS_DGMResolution );
    394
    395            }
    396
    397            // set child datasets
    398            dataset.setDatasets( childDatasets );
    399
    400            return dataset;
    401        }
    402
    403        /**
    404         * Creates and returns a new <code>ElevationModel</code> object from the given <code>Element</code> and the parent
    405         * <code>Dataset</code>.
    406         *
    407         * The OGC ElevationModel contains only a String. The Deegree ElevationModel additionally contains a complex
    408         * dataSource.
    409         *
    410         * @param datasetElement
    411         * @param parentName
    412         * @return Returns the ElevationModel object.
    413         * @throws XMLParsingException
    414         * @throws OGCWebServiceException
    415         * @throws InvalidParameterValueException
    416         * @throws MissingParameterValueException
    417         * @throws InvalidConfigurationException
    418         */
    419        private ElevationModel parseElevationModel( Element datasetElement, String parentName,
    420                                                    double minimalWCS_DGMResolution, CoordinateSystem defaultCRS,
    421                                                    double defaultMinScaleDenominator, double defaultMaxScaleDenominator )
    422                                throws XMLParsingException, MissingParameterValueException, InvalidParameterValueException,
    423                                OGCWebServiceException, InvalidConfigurationException {
    424
    425            Element elevationElement = null;
    426            String name = null;
    427            ElevationModel elevationModel = null;
    428
    429            elevationElement = (Element) XMLTools.getNode( datasetElement, PRE_DWPVS + "ElevationModel", nsContext );
    430
    431            AbstractDataSource[] dataSources = null;
    432            if ( elevationElement != null ) {
    433
    434                name = XMLTools.getRequiredNodeAsString( elevationElement, PRE_DWPVS + "Name/text()", nsContext );
    435
    436                dataSources = parseAbstractDatasources( elevationElement, parentName, defaultMinScaleDenominator,
    437                                                        defaultMaxScaleDenominator, defaultCRS );
    438                if ( dataSources.length < 1 ) {
    439                    throw new InvalidCapabilitiesException( "Each '" + elevationElement.getNodeName()
    440                                                            + "' must contain at least one data source!" );
    441                }
    442                if ( !Double.isNaN( minimalWCS_DGMResolution ) ) {
    443                    // little trick to know which dgm datasources have a configured minimal resolution,
    444                    // if the minimalWCS_DGMResolution is not set (e.g Double.nan) nothing has to be
    445                    // done (a value of 0d is presumed)
    446                    for ( AbstractDataSource source : dataSources ) {
    447                        if ( source.getServiceType() == AbstractDataSource.LOCAL_WCS
    448                             || source.getServiceType() == AbstractDataSource.REMOTE_WCS ) {
    449                            ( (LocalWCSDataSource) source ).setConfiguredMinimalDGMResolution( minimalWCS_DGMResolution );
    450                        }
    451                    }
    452                }
    453            }
    454
    455            elevationModel = new ElevationModel( name, dataSources );
    456
    457            return elevationModel;
    458        }
    459
    460        /**
    461         * Creates and returns a new array of <code>AbstractDataSource</code> objects from the given <code>Element</code>.
    462         *
    463         * If the objects are used within an ElevationModel object, they may be of the following types: LocalWCSDataSource,
    464         * RemoteWCSDataSource, LocalWFSDataSource, RemoteWFSDataSource. If the objects are used within a Dataset object,
    465         * they may additionaly be of the types: LocalWMSDataSource, RemoteWMSDataSource.
    466         *
    467         * @param element
    468         * @return Returns a new array of AbstractDataSource objects.
    469         * @throws XMLParsingException
    470         * @throws OGCWebServiceException
    471         * @throws InvalidConfigurationException
    472         */
    473        private AbstractDataSource[] parseAbstractDatasources( Element element, String parentName,
    474                                                               double defaultMinScaleDenominator,
    475                                                               double defaultMaxScaleDenominator,
    476                                                               CoordinateSystem defaultCRS )
    477                                throws XMLParsingException, OGCWebServiceException, InvalidConfigurationException {
    478
    479            List<Element> abstractDataSources = XMLTools.getElements( element, "*", nsContext );
    480            List<AbstractDataSource> tempDataSources = new ArrayList<AbstractDataSource>( abstractDataSources.size() );
    481
    482            for ( Element dataSourceElement : abstractDataSources ) {
    483
    484                // String nodeName = dataSourceElement.getNodeName();
    485                String nodeName = dataSourceElement.getLocalName();
    486
    487                if ( nodeName != null && nodeName.endsWith( "DataSource" ) ) {
    488                    QualifiedName pn = null;
    489                    if ( parentName != null ) {
    490                        pn = new QualifiedName( PRE_DWPVS, parentName, nsContext.getURI( PRE_DWPVS ) );
    491                    }
    492                    QualifiedName name = XMLTools.getNodeAsQualifiedName( dataSourceElement, PRE_DWPVS + "Name/text()",
    493                                                                          nsContext, pn );
    494
    495                    OWSCapabilities owsCapabilities = parseOWSCapabilities( dataSourceElement );
    496
    497                    double minScaleDenom = XMLTools.getNodeAsDouble( dataSourceElement, PRE_DWPVS
    498                                                                                        + "MinimumScaleDenominator/text()",
    499                                                                     nsContext, defaultMinScaleDenominator );
    500
    501                    // update the smallestMinimalScaleDenomiator
    502                    if ( minScaleDenom < smallestMinimalScaleDenominator )
    503                        smallestMinimalScaleDenominator = minScaleDenom;
    504
    505                    double maxScaleDenom = XMLTools.getNodeAsDouble( dataSourceElement, PRE_DWPVS
    506                                                                                        + "MaximumScaleDenominator/text()",
    507                                                                     nsContext, defaultMaxScaleDenominator );
    508
    509                    Surface validArea = parseValidArea( dataSourceElement, defaultCRS );
    510                    AbstractDataSource dataSource = null;
    511
    512                    if ( nodeName.equals( "LocalWCSDataSource" ) || "RemoteWCSDataSource".equals( nodeName ) ) {
    513                        Element filterElement = (Element) XMLTools.getRequiredNode( dataSourceElement, PRE_DWPVS
    514                                                                                                       + "FilterCondition",
    515                                                                                    nsContext );
    516                        GetCoverage getCoverage = parseWCSFilterCondition( filterElement );
    517                        Color[] transparentColors = parseTransparentColors( dataSourceElement );
    518
    519                        if ( "RemoteWCSDataSource".equals( nodeName ) ) {
    520                            dataSource = new RemoteWCSDataSource( name, owsCapabilities, validArea, minScaleDenom,
    521                                                                  maxScaleDenom, getCoverage, transparentColors );
    522                            LOG.logDebug( "created remote wcs with name: " + name );
    523                        } else {
    524                            dataSource = new LocalWCSDataSource( name, owsCapabilities, validArea, minScaleDenom,
    525                                                                 maxScaleDenom, getCoverage, transparentColors );
    526                            LOG.logDebug( "created local wcs with name: " + name );
    527                        }
    528                    } else if ( "RemoteWFSDataSource".equals( nodeName ) || "LocalWFSDataSource".equals( nodeName ) ) {
    529                        Text geoPropNode = (Text) XMLTools.getRequiredNode( dataSourceElement, PRE_DWPVS
    530                                                                                               + "GeometryProperty/text()",
    531                                                                            nsContext );
    532                        PropertyPath geometryProperty = parsePropertyPath( geoPropNode );
    533
    534                        Element filterElement = (Element) XMLTools.getNode( dataSourceElement,
    535                                                                            PRE_DWPVS + "FilterCondition/ogc:Filter",
    536                                                                            nsContext );
    537
    538                        int maxFeatures = XMLTools.getNodeAsInt( dataSourceElement, PRE_DWPVS + "MaxFeatures", nsContext,
    539                                                                 -1 );
    540
    541                        Filter filterCondition = null;
    542                        if ( filterElement != null ) {
    543                            filterCondition = AbstractFilter.buildFromDOM( filterElement, false );
    544                        }
    545
    546                        // FeatureCollectionAdapter adapter = createFCAdapterFromAdapterClassName(
    547                        // dataSourceElement );
    548
    549                        if ( "LocalWFSDataSource".equals( nodeName ) ) {
    550                            dataSource = new LocalWFSDataSource( name, owsCapabilities, validArea, minScaleDenom,
    551                                                                 maxScaleDenom, geometryProperty, filterCondition,
    552                                                                 maxFeatures );
    553                            LOG.logDebug( "created local wfs with name: " + name );
    554                        } else {
    555                            dataSource = new RemoteWFSDataSource( name, owsCapabilities, validArea, minScaleDenom,
    556                                                                  maxScaleDenom, geometryProperty, filterCondition,
    557                                                                  maxFeatures );
    558                            LOG.logDebug( "created remote wfs with name: " + name );
    559                        }
    560                    } else if ( nodeName.equals( "LocalWMSDataSource" ) ) {
    561                        if ( element.getNodeName().endsWith( "ElevationModel" ) ) {
    562                            throw new InvalidConfigurationException( "An ElevationModel cannot "
    563                                                                     + "contain a LocalWMSDataSource!" );
    564                        }
    565                        Element filterElement = (Element) XMLTools.getRequiredNode( dataSourceElement, PRE_DWPVS
    566                                                                                                       + "FilterCondition",
    567                                                                                    nsContext );
    568                        GetMap getMap = parseWMSFilterCondition( filterElement );
    569
    570                        Color[] transparentColors = parseTransparentColors( dataSourceElement );
    571
    572                        dataSource = new LocalWMSDataSource( name, owsCapabilities, validArea, minScaleDenom,
    573                                                             maxScaleDenom, getMap, transparentColors );
    574                        LOG.logDebug( "created local wms with name: " + name );
    575
    576                    } else if ( nodeName.equals( "RemoteWMSDataSource" ) ) {
    577                        if ( element.getNodeName().endsWith( "ElevationModel" ) ) {
    578                            throw new InvalidConfigurationException( "An ElevationModel cannot "
    579                                                                     + "contain a LocalWMSDataSource!" );
    580                        }
    581                        Element filterElement = (Element) XMLTools.getRequiredNode( dataSourceElement, PRE_DWPVS
    582                                                                                                       + "FilterCondition",
    583                                                                                    nsContext );
    584                        GetMap getMap = parseWMSFilterCondition( filterElement );
    585
    586                        Color[] transparentColors = parseTransparentColors( dataSourceElement );
    587
    588                        dataSource = new RemoteWMSDataSource( name, owsCapabilities, validArea, minScaleDenom,
    589                                                              maxScaleDenom, getMap, transparentColors );
    590                        LOG.logDebug( "created remote wms with name: " + name );
    591                    } else {
    592                        throw new InvalidCapabilitiesException( "Unknown data source: '" + nodeName + "'" );
    593                    }
    594
    595                    tempDataSources.add( dataSource );
    596                }
    597            }
    598
    599            AbstractDataSource[] dataSources = tempDataSources.toArray( new AbstractDataSource[tempDataSources.size()] );
    600
    601            return dataSources;
    602        }
    603
    604        /**
    605         * FIXME check content of StringBuffer and Map! This is an adapted copy from:
    606         * org.deegree.ogcwebservices.wms.configuration#parseWMSFilterCondition(Node)
    607         *
    608         * Creates and returns a new <code>GetMap</code> object from the given <code>Element</code>.
    609         *
    610         * @param filterElement
    611         * @return a partial wms GetMap request instance
    612         * @throws XMLParsingException
    613         */
    614        private GetMap parseWMSFilterCondition( Element filterElement )
    615                                throws XMLParsingException {
    616
    617            GetMap getMap = null;
    618
    619            String wmsRequest = XMLTools.getRequiredNodeAsString( filterElement, PRE_DWPVS + "WMSRequest/text()", nsContext );
    620
    621            StringBuffer sd = new StringBuffer( 1000 );
    622            sd.append( "REQUEST=GetMap&LAYERS=%default%&STYLES=&SRS=EPSG:4326&" );
    623            sd.append( "BBOX=0,0,1,1&WIDTH=1&HEIGHT=1&FORMAT=%default%" );
    624
    625            Map<String, String> map1 = KVP2Map.toMap( sd.toString() );
    626
    627            Map<String, String> map2 = KVP2Map.toMap( wmsRequest );
    628            if ( map2.get( "VERSION" ) == null && map2.get( "WMTVER" ) == null ) {
    629                map2.put( "VERSION", "1.1.1" );
    630            }
    631            // if no service is set use WMS as default
    632            if ( map2.get( "SERVICE" ) == null ) {
    633                map2.put( "SERVICE", "WMS" );
    634            }
    635
    636            map1.putAll( map2 );
    637
    638            String id = Long.toString( IDGenerator.getInstance().generateUniqueID() );
    639            map1.put( "ID", id );
    640            try {
    641                getMap = GetMap.create( map1 );
    642            } catch ( Exception e ) {
    643                throw new XMLParsingException( "could not create GetMap from WMS FilterCondition", e );
    644            }
    645
    646            return getMap;
    647        }
    648
    649        /**
    650         * FIXME check content of StringBuffer ! This is an adapted copy from:
    651         * org.deegree.ogcwebservices.wms.configuration#parseWCSFilterCondition(Node)
    652         *
    653         * Creates and returns a new <code>GetCoverage</code> object from the given <code>Element</code>.
    654         *
    655         * @param filterElement
    656         * @return a partial GetCoverage request
    657         * @throws XMLParsingException
    658         */
    659        private GetCoverage parseWCSFilterCondition( Element filterElement )
    660                                throws XMLParsingException {
    661
    662            GetCoverage coverage = null;
    663
    664            String wcsRequest = XMLTools.getRequiredNodeAsString( filterElement, PRE_DWPVS + "WCSRequest/text()", nsContext );
    665
    666            StringBuffer sd = new StringBuffer( 1000 );
    667            sd.append( "version=1.0.0&Coverage=%default%&CRS=EPSG:4326&BBOX=0,0,1,1" );
    668            sd.append( "&Width=1&Height=1&Format=%default%&" );
    669            sd.append( wcsRequest );
    670
    671            String id = "" + IDGenerator.getInstance().generateUniqueID();
    672
    673            try {
    674                coverage = GetCoverage.create( id, sd.toString() );
    675            } catch ( Exception e ) {
    676                throw new XMLParsingException( "Could not create GetCoverage " + "from WPVS FilterCondition", e );
    677            }
    678
    679            return coverage;
    680        }
    681
    682        /**
    683         * Creates and returns a new <code>OWSCapabilities</code> object from the given <code>Element</code>.
    684         *
    685         * @param element
    686         * @return Returns a new OWSCapabilities object.
    687         * @throws XMLParsingException
    688         * @throws InvalidCapabilitiesException
    689         */
    690        private OWSCapabilities parseOWSCapabilities( Element element )
    691                                throws XMLParsingException, InvalidCapabilitiesException {
    692
    693            Element owsCapabilitiesElement = (Element) XMLTools.getRequiredNode( element, PRE_DWPVS + "OWSCapabilities",
    694                                                                                 nsContext );
    695
    696            String format = null;
    697
    698            // FIXME
    699            // schema has onlineResourceType as not optional, so it should be mandatory.
    700            // but in other examples onlineResource is never created with this onlineResourceType.
    701            // therefore it gets omitted here, too.
    702
    703            // String onlineResourceType = XMLTools.getRequiredNodeAsString(
    704            // owsCapabilitiesElement, PRE_DWPVS+"OnlineResource/@xlink:type", nsContext );
    705
    706            String onlineResourceURI = XMLTools.getRequiredNodeAsString( owsCapabilitiesElement,
    707                                                                         PRE_DWPVS + "OnlineResource/@xlink:href",
    708                                                                         nsContext );
    709
    710            URL onlineResource;
    711            try {
    712                onlineResource = resolve( onlineResourceURI );
    713            } catch ( MalformedURLException e ) {
    714                throw new InvalidCapabilitiesException( onlineResourceURI + " does not represent a valid URL: "
    715                                                        + e.getMessage() );
    716            }
    717            LOG.logDebug( "found following onlineResource: " + onlineResource );
    718            return new OWSCapabilities( format, onlineResource );
    719
    720            // FIXME
    721            // if onlineResourceType is going to be used, the returned new OnlineResource should be
    722            // created with different constructor:
    723            // return new OWSCapabilities( format, onlineResourceType, onlineResource );
    724        }
    725
    726        /**
    727         * Creates and returns a new <code>Geometry</code> object from the given Element.
    728         *
    729         * @param dataSource
    730         * @return Returns a new Geometry object.
    731         * @throws XMLParsingException
    732         * @throws InvalidConfigurationException
    733         */
    734        private Surface parseValidArea( Element dataSource, CoordinateSystem defaultCRS )
    735                                throws XMLParsingException, InvalidConfigurationException {
    736            List<Element> nl = XMLTools.getElements( dataSource, PRE_DWPVS + "ValidArea/*", nsContext );
    737            if ( nl.size() == 0 ) {
    738                return null;
    739            }
    740
    741            if ( nl.size() > 1 ) {
    742                LOG.logWarning( Messages.getMessage( "WPVS_WRONG_NUMBER_OF_VALID_AREAS", GMLNS.toASCIIString(),
    743                                                     dataSource.getLocalName() ) );
    744                return null;
    745            }
    746            Surface validArea = null;
    747            Element firsElement = nl.get( 0 );
    748            if ( firsElement.getNamespaceURI().equals( GMLNS.toASCIIString() ) ) {
    749                try {
    750                    String srsName = XMLTools.getNodeAsString( firsElement, "@srsName", nsContext, null );
    751                    if ( srsName == null ) {
    752                        srsName = defaultCRS.getFormattedString();
    753                    }
    754
    755                    Geometry geom = GMLGeometryAdapter.wrap( firsElement, srsName );
    756                    if ( !( geom instanceof Surface ) ) {
    757                        throw new InvalidConfigurationException( Messages.getMessage( "WPVS_WRONG_GEOMTERY_VALID_AREA",
    758                                                                                      dataSource.getLocalName() ) );
    759                    }
    760                    validArea = (Surface) geom;
    761                } catch ( GeometryException e ) {
    762                    throw new InvalidConfigurationException( Messages.getMessage( "WPVS_WRONG_GEOMTERY_VALID_AREA",
    763                                                                                  dataSource.getLocalName() )
    764                                                             + "\n" + e.getMessage(),
    765
    766                    e );
    767                }
    768
    769            } else if ( URI.create( firsElement.getNamespaceURI() ).equals( CommonNamespaces.OWSNS ) ) {
    770                try {
    771                    if ( !"BoundingBox".equals( firsElement.getLocalName() ) ) {
    772                        throw new InvalidConfigurationException( Messages.getMessage( "WPVS_WRONG_GEOMTERY_VALID_AREA",
    773                                                                                      dataSource.getLocalName() ) );
    774                    }
    775                    Envelope env = parseBoundingBox( firsElement, null );
    776                    validArea = GeometryFactory.createSurface( env, env.getCoordinateSystem() );
    777                } catch ( GeometryException e ) {
    778                    throw new InvalidConfigurationException( Messages.getMessage( "WPVS_WRONG_GEOMTERY_VALID_AREA",
    779                                                                                  dataSource.getLocalName() )
    780                                                             + "\n" + e.getMessage(), e );
    781                } catch ( InvalidParameterValueException e ) {
    782                    throw new InvalidConfigurationException( Messages.getMessage( "WPVS_WRONG_GEOMTERY_VALID_AREA",
    783                                                                                  dataSource.getLocalName() )
    784                                                             + "\n" + e.getMessage(), e );
    785                }
    786            } else {
    787                LOG.logWarning( Messages.getMessage( "WPVS_WRONG_GEOMTERY_VALID_AREA", dataSource.getLocalName() ) );
    788                return null;
    789            }
    790            return validArea;
    791        }
    792
    793        /**
    794         * Creates and returns a new array of <code>Color</code> objects from the given Element.
    795         *
    796         * @param dataSourceElement
    797         * @return Returns a new array of Color objects.
    798         * @throws XMLParsingException
    799         */
    800        private Color[] parseTransparentColors( Element dataSourceElement )
    801                                throws XMLParsingException {
    802
    803            List<Element> colorList = XMLTools.getElements( dataSourceElement, PRE_DWPVS + "TransparentColors/" + PRE_DWPVS
    804                                                                               + "Color", nsContext );
    805
    806            Color[] transparentColors = null;
    807            if ( colorList != null ) {
    808                transparentColors = new Color[colorList.size()];
    809
    810                for ( int i = 0; i < transparentColors.length; i++ ) {
    811
    812                    Element colorElement = colorList.get( i );
    813                    String color = XMLTools.getRequiredNodeAsString( colorElement, "text()", nsContext );
    814
    815                    transparentColors[i] = Color.decode( color );
    816                }
    817            }
    818
    819            return transparentColors;
    820        }
    821
    822    }