001    //$HeadURL: svn+ssh://jwilden@svn.wald.intevation.org/deegree/base/branches/2.5_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    }