001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/ogcwebservices/wmps/configuration/WMPSConfigurationDocument.java $
002    /*----------------    FILE HEADER  ------------------------------------------
003    
004     This file is part of deegree.
005     Copyright (C) 2001-2008 by:
006     EXSE, Department of Geography, University of Bonn
007     http://www.giub.uni-bonn.de/deegree/
008     lat/lon GmbH
009     http://www.lat-lon.de
010    
011     This library is free software; you can redistribute it and/or
012     modify it under the terms of the GNU Lesser General Public
013     License as published by the Free Software Foundation; either
014     version 2.1 of the License, or (at your option) any later version.
015    
016     This library is distributed in the hope that it will be useful,
017     but WITHOUT ANY WARRANTY; without even the implied warranty of
018     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
019     Lesser General Public License for more details.
020    
021     You should have received a copy of the GNU Lesser General Public
022     License along with this library; if not, write to the Free Software
023     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
024    
025     Contact:
026    
027     Andreas Poth
028     lat/lon GmbH
029     Aennchenstraße 19
030     53177 Bonn
031     Germany
032     E-Mail: poth@lat-lon.de
033    
034     Prof. Dr. Klaus Greve
035     Department of Geography
036     University of Bonn
037     Meckenheimer Allee 166
038     53115 Bonn
039     Germany
040     E-Mail: greve@giub.uni-bonn.de
041     
042     ---------------------------------------------------------------------------*/
043    package org.deegree.ogcwebservices.wmps.configuration;
044    
045    import java.awt.Color;
046    import java.io.IOException;
047    import java.net.MalformedURLException;
048    import java.net.URI;
049    import java.net.URL;
050    import java.util.ArrayList;
051    import java.util.HashMap;
052    import java.util.List;
053    import java.util.Map;
054    
055    import javax.xml.transform.TransformerException;
056    
057    import org.deegree.datatypes.QualifiedName;
058    import org.deegree.enterprise.Proxy;
059    import org.deegree.framework.log.ILogger;
060    import org.deegree.framework.log.LoggerFactory;
061    import org.deegree.framework.util.BootLogger;
062    import org.deegree.framework.util.IDGenerator;
063    import org.deegree.framework.util.KVP2Map;
064    import org.deegree.framework.util.StringTools;
065    import org.deegree.framework.xml.InvalidConfigurationException;
066    import org.deegree.framework.xml.XMLFragment;
067    import org.deegree.framework.xml.XMLParsingException;
068    import org.deegree.framework.xml.XMLTools;
069    import org.deegree.framework.xml.XSLTDocument;
070    import org.deegree.model.crs.UnknownCRSException;
071    import org.deegree.model.metadata.iso19115.OnlineResource;
072    import org.deegree.model.spatialschema.Envelope;
073    import org.deegree.model.spatialschema.GMLGeometryAdapter;
074    import org.deegree.model.spatialschema.Geometry;
075    import org.deegree.model.spatialschema.GeometryException;
076    import org.deegree.ogcbase.CommonNamespaces;
077    import org.deegree.ogcwebservices.OGCWebService;
078    import org.deegree.ogcwebservices.getcapabilities.MetadataURL;
079    import org.deegree.ogcwebservices.getcapabilities.OperationsMetadata;
080    import org.deegree.ogcwebservices.getcapabilities.ServiceIdentification;
081    import org.deegree.ogcwebservices.getcapabilities.ServiceProvider;
082    import org.deegree.ogcwebservices.wcs.WCService;
083    import org.deegree.ogcwebservices.wcs.configuration.WCSConfiguration;
084    import org.deegree.ogcwebservices.wcs.getcoverage.GetCoverage;
085    import org.deegree.ogcwebservices.wfs.WFServiceFactory;
086    import org.deegree.ogcwebservices.wfs.capabilities.WFSCapabilities;
087    import org.deegree.ogcwebservices.wfs.capabilities.WFSCapabilitiesDocument;
088    import org.deegree.ogcwebservices.wfs.configuration.WFSConfiguration;
089    import org.deegree.ogcwebservices.wfs.configuration.WFSConfigurationDocument;
090    import org.deegree.ogcwebservices.wfs.operation.Query;
091    import org.deegree.ogcwebservices.wmps.capabilities.WMPSCapabilitiesDocument;
092    import org.deegree.ogcwebservices.wms.RemoteWMService;
093    import org.deegree.ogcwebservices.wms.capabilities.Attribution;
094    import org.deegree.ogcwebservices.wms.capabilities.AuthorityURL;
095    import org.deegree.ogcwebservices.wms.capabilities.DataURL;
096    import org.deegree.ogcwebservices.wms.capabilities.Dimension;
097    import org.deegree.ogcwebservices.wms.capabilities.Extent;
098    import org.deegree.ogcwebservices.wms.capabilities.FeatureListURL;
099    import org.deegree.ogcwebservices.wms.capabilities.GazetteerParam;
100    import org.deegree.ogcwebservices.wms.capabilities.Identifier;
101    import org.deegree.ogcwebservices.wms.capabilities.Layer;
102    import org.deegree.ogcwebservices.wms.capabilities.LayerBoundingBox;
103    import org.deegree.ogcwebservices.wms.capabilities.LegendURL;
104    import org.deegree.ogcwebservices.wms.capabilities.ScaleHint;
105    import org.deegree.ogcwebservices.wms.capabilities.Style;
106    import org.deegree.ogcwebservices.wms.capabilities.StyleSheetURL;
107    import org.deegree.ogcwebservices.wms.capabilities.StyleURL;
108    import org.deegree.ogcwebservices.wms.capabilities.UserDefinedSymbolization;
109    import org.deegree.ogcwebservices.wms.capabilities.WMSCapabilities;
110    import org.deegree.ogcwebservices.wms.capabilities.WMSCapabilitiesDocument;
111    import org.deegree.ogcwebservices.wms.capabilities.WMSCapabilitiesDocumentFactory;
112    import org.deegree.ogcwebservices.wms.configuration.AbstractDataSource;
113    import org.deegree.ogcwebservices.wms.configuration.LocalWCSDataSource;
114    import org.deegree.ogcwebservices.wms.configuration.LocalWFSDataSource;
115    import org.deegree.ogcwebservices.wms.configuration.RemoteWFSDataSource;
116    import org.deegree.ogcwebservices.wms.configuration.RemoteWMSDataSource;
117    import org.deegree.ogcwebservices.wms.operation.GetMap;
118    import org.w3c.dom.Element;
119    import org.w3c.dom.Node;
120    import org.xml.sax.SAXException;
121    
122    /**
123     * Represents an XML configuration document for a deegree WMPS 1.0 instance, i.e. it consists of all
124     * sections common to an OGC WMS 1.1.1 capabilities document plus a deegree specific section named
125     * <code>deegreeParams</code> and... TODO
126     * 
127     * @author <a href="mailto:deshmukh@lat-lon.de">Anup Deshmukh</a>
128     * @author last edited by: $Author: apoth $
129     * 
130     * @version 2.0, $Revision: 9345 $, $Date: 2007-12-27 17:22:25 +0100 (Do, 27 Dez 2007) $
131     */
132    public class WMPSConfigurationDocument extends WMPSCapabilitiesDocument {
133    
134        private static final long serialVersionUID = -7940857863171829848L;
135    
136        private static final ILogger LOG = LoggerFactory.getLogger( WMPSConfigurationDocument.class );
137    
138        protected static final URI DEEGREEWPSNS = CommonNamespaces.DEEGREEWMPS;
139    
140        private static final String XML_TEMPLATE = "WMPSConfigurationTemplate.xml";
141    
142        private static final String XSLT_TEMPLATE_NAME = "WMPSConfigurationTransform.xsl";
143    
144        private static XSLTDocument XSLT_TEMPLATE;
145    
146        private static Map<URL, Object> capaCache = new HashMap<URL, Object>();
147    
148        static {
149            XSLT_TEMPLATE = new XSLTDocument();
150            try {
151                XSLT_TEMPLATE.load( WMPSConfigurationDocument.class.getResource( XSLT_TEMPLATE_NAME ) );
152            } catch ( Exception e ) {
153                BootLogger.logError( "Error loading XSLT sheet in WMPSConfigurationDocument.", e );
154            }
155        }
156        
157        /**
158         * 
159         */
160        public static void resetCapabilitiesCache() {
161            capaCache.clear();
162        }
163    
164        /**
165         * Creates a skeleton capabilities document that contains the mandatory elements only.
166         * 
167         * @throws IOException
168         * @throws SAXException
169         */
170        @Override
171        public void createEmptyDocument()
172                                throws IOException, SAXException {
173            URL url = WMPSConfigurationDocument.class.getResource( XML_TEMPLATE );
174            if ( url == null ) {
175                throw new IOException( "The resource '" + XML_TEMPLATE + " could not be found." );
176            }
177            load( url );
178        }
179    
180        /**
181         * Creates a class representation of the document.
182         * 
183         * @return class representation of the configuration document
184         * @throws InvalidConfigurationException
185         */
186        public WMPSConfiguration parseConfiguration()
187                                throws InvalidConfigurationException {
188    
189            try {
190                // transform document to fill missing elements and attributes with default values
191                XMLFragment frag = XSLT_TEMPLATE.transform( this );
192                this.setRootElement( frag.getRootElement() );
193            } catch ( TransformerException e ) {
194                String msg = "Error transforming WMPS configuration document (in order to fill " + "in default value). "
195                             + e.getMessage();
196                LOG.logError( msg, e );
197                throw new InvalidConfigurationException( msg, e );
198            }
199    
200            ServiceIdentification serviceIdentification = null;
201            ServiceProvider serviceProvider = null;
202            UserDefinedSymbolization uds = null;
203            OperationsMetadata metadata = null;
204            Layer layer = null;
205            WMPSDeegreeParams params = null;
206            String version = parseVersion();
207            try {
208                Node node = XMLTools.getRequiredNode( getRootElement(), "deegreewmps:DeegreeParam", nsContext );
209                params = parseDeegreeParams( node );
210                serviceIdentification = parseServiceIdentification();
211                serviceProvider = parseServiceProvider();
212                uds = parseUserDefinedSymbolization();
213                metadata = parseOperationsMetadata();
214                Element layerElem = (Element) XMLTools.getRequiredNode( getRootElement(), "./Capability/Layer", nsContext );
215                layer = parseLayers( layerElem, null );
216            } catch ( XMLParsingException e ) {
217                e.printStackTrace();
218                throw new InvalidConfigurationException( e.getMessage() + StringTools.stackTraceToString( e ) );
219            } catch ( MalformedURLException e ) {
220                throw new InvalidConfigurationException( e.getMessage() + " - " + StringTools.stackTraceToString( e ) );
221            } catch ( UnknownCRSException e ) {
222                throw new InvalidConfigurationException( getClass().getName(), e.getMessage() );
223            }
224            WMPSConfiguration wmpsConfiguration = new WMPSConfiguration( version, serviceIdentification, serviceProvider,
225                                                                         uds, metadata, layer, params, getSystemId() );
226    
227            return wmpsConfiguration;
228        }
229    
230        /**
231         * Creates a class representation of the <code>deegreeParams</code>- section.
232         * 
233         * @param root
234         * @return WMPSDeegreeParams
235         * @throws XMLParsingException
236         * @throws MalformedURLException
237         */
238        public WMPSDeegreeParams parseDeegreeParams( Node root )
239                                throws XMLParsingException, MalformedURLException {
240    
241            String xPath = "./deegreewmps:DefaultOnlineResource";
242            Element elem = (Element) XMLTools.getRequiredNode( root, xPath, nsContext );
243    
244            OnlineResource ol = parseOnLineResource( elem );
245            xPath = "./deegreewmps:CacheSize";
246            int cache = XMLTools.getNodeAsInt( root, xPath, nsContext, 100 );
247            xPath = "./deegreewmps:MaxLifeTime";
248            int maxLifeTime = XMLTools.getNodeAsInt( root, xPath, nsContext, 3600 );
249            xPath = "./deegreewmps:RequestTimeLimit";
250            int reqTimeLimit = XMLTools.getNodeAsInt( root, xPath, nsContext, 15 );
251            reqTimeLimit *= 1000;
252            xPath = "./deegreewmps:MapQuality";
253            double mapQuality = XMLTools.getNodeAsDouble( root, xPath, nsContext, 0.95 );
254            xPath = "./deegreewmps:MaxMapWidth";
255            int maxMapWidth = XMLTools.getNodeAsInt( root, xPath, nsContext, 1000 );
256            xPath = "./deegreewmps:MaxMapHeight";
257            int maxMapHeight = XMLTools.getNodeAsInt( root, xPath, nsContext, 1000 );
258            xPath = "./deegreewmps:FeatureInfoRadius";
259            int featureInfoRadius = XMLTools.getNodeAsInt( root, xPath, nsContext, 5 );
260            xPath = "./deegreewmps:Copyright";
261            String copyright = XMLTools.getNodeAsString( root, xPath, nsContext, "" );
262            URL dtdLocation = null;
263            xPath = "deegreewmps:DTDLocation";
264            if ( XMLTools.getNode( root, xPath, nsContext ) != null ) {
265                xPath = "./deegreewmps:DTDLocation/deegreewmps:OnlineResource";
266                elem = (Element) XMLTools.getRequiredNode( root, xPath, nsContext );
267                OnlineResource olr = parseOnLineResource( elem );
268                dtdLocation = olr.getLinkage().getHref();
269            } else {
270                dtdLocation = new URL( "http://schemas.opengis.net/wms/1.1.1/WMS_MS_Capabilities.dtd" );
271            }
272            xPath = "./deegreewmps:AntiAliased";
273            boolean antiAliased = XMLTools.getNodeAsBoolean( root, xPath, nsContext, true );
274            xPath = "./deegreewmps:GazetteerParameter";
275            elem = (Element) XMLTools.getNode( root, xPath, nsContext );
276            GazetteerParam gazetteer = null;
277            if ( elem != null ) {
278                gazetteer = parseGazetteerParameter( elem );
279            }
280    
281            Proxy proxy = parseProxy( root );
282    
283            List<String> synchList = parseSynchTemplates( root );
284    
285            CacheDatabase cacheDatabase = parseCacheDatabase( root );
286    
287            PrintMapParam printMapParam = parsePrintMapParam( root );
288    
289            WMPSDeegreeParams deegreeParams = new WMPSDeegreeParams( cache, maxLifeTime, reqTimeLimit, (float) mapQuality,
290                                                                     ol, maxMapWidth, maxMapHeight, antiAliased,
291                                                                     featureInfoRadius, copyright, gazetteer, null,
292                                                                     dtdLocation, proxy, synchList, cacheDatabase,
293                                                                     printMapParam );
294    
295            return deegreeParams;
296        }
297    
298        /**
299         * Parse the cache database parameters used by the wmps to store the asynchronous requests.
300         * 
301         * @param root
302         * @return CacheDatabase
303         * @throws XMLParsingException
304         */
305        private CacheDatabase parseCacheDatabase( Node root )
306                                throws XMLParsingException {
307    
308            CacheDatabase cacheDatabase = null;
309            String xPath = "./deegreewmps:CacheDatabase";
310            Node cacheDb = XMLTools.getNode( root, xPath, nsContext );
311            if ( cacheDb != null ) {
312                xPath = "./deegreewmps:JDBCConnection";
313                Node jdbcConnection = XMLTools.getRequiredNode( cacheDb, xPath, nsContext );
314                String driver = XMLTools.getRequiredNodeAsString( jdbcConnection, "./deegreewmps:Driver", nsContext );
315                String url = XMLTools.getRequiredNodeAsString( jdbcConnection, "./deegreewmps:Url", nsContext );
316                String user = XMLTools.getRequiredNodeAsString( jdbcConnection, "./deegreewmps:User", nsContext );
317                String password = XMLTools.getRequiredNodeAsString( jdbcConnection, "./deegreewmps:Password", nsContext );
318                cacheDatabase = new CacheDatabase( driver, url, user, password );
319                LOG.logDebug( "Successfully parsed the deegree wmps cache database parameters." );
320            }
321    
322            return cacheDatabase;
323        }
324    
325        /**
326         * Parse the PrintMapParam node and retrieve the (jasper reports)template directory, the default
327         * template name if none is specified in the request. Also parse the output directory location
328         * and the location of the printed image output directory.
329         * 
330         * @param root
331         * @return PrintMapParam
332         * @throws XMLParsingException
333         * @throws MalformedURLException
334         */
335        private PrintMapParam parsePrintMapParam( Node root )
336                                throws XMLParsingException, MalformedURLException {
337    
338            Node printParam = XMLTools.getRequiredNode( root, "./deegreewmps:PrintMapParam", nsContext );
339            // set default as pdf
340            String format = XMLTools.getNodeAsString( printParam, "./deegreewmps:Format", nsContext, "pdf" );
341            Node templ = XMLTools.getRequiredNode( printParam, "./deegreewmps:Template", nsContext );
342            String templateDirectory = XMLTools.getRequiredNodeAsString( templ, "./deegreewmps:Directory", nsContext );
343    
344            templateDirectory = this.resolve( templateDirectory ).toExternalForm();
345            if ( !templateDirectory.endsWith( "/" ) ) {
346                templateDirectory = templateDirectory + '/';
347            }
348            String plotDirectory = XMLTools.getRequiredNodeAsString( printParam, "./deegreewmps:PlotDirectory", nsContext );
349            plotDirectory = this.resolve( plotDirectory ).toExternalForm();
350            String onlineResource = XMLTools.getRequiredNodeAsString( printParam, "./deegreewmps:OnlineResource", nsContext );
351            String plotImgDir = XMLTools.getRequiredNodeAsString( printParam, "./deegreewmps:PlotImageDirectory", nsContext );
352            plotImgDir = this.resolve( plotImgDir ).toExternalForm();
353            String adminMail = XMLTools.getRequiredNodeAsString( printParam, "./deegreewmps:AdministratorEMailAddress",
354                                                                 nsContext );
355            String mailHost = XMLTools.getRequiredNodeAsString( printParam, "./deegreewmps:MailHost", nsContext );
356    
357            String mailTextTemplate = XMLTools.getNodeAsString( printParam, "./deegreewmps:MailTextTemplate", nsContext,
358                                                                "You can access the printMap result at {1}" );
359    
360            int targetRes = XMLTools.getNodeAsInt( printParam, "./deegreewmps:TargetResolution", nsContext, 300 );
361    
362            PrintMapParam printMapParam = new PrintMapParam( format, templateDirectory, onlineResource, plotDirectory,
363                                                             plotImgDir, adminMail, mailHost, mailTextTemplate, targetRes );
364    
365            return printMapParam;
366        }
367    
368        /**
369         * parses the list of templates that shall be handled synchronously
370         * 
371         * @param root
372         * @return List
373         * @throws XMLParsingException
374         */
375        private List<String> parseSynchTemplates( Node root )
376                                throws XMLParsingException {
377    
378            String xPath = "./deegreewmps:SynchronousTemplates/deegreewmps:Template";
379            String[] nodes = XMLTools.getNodesAsStrings( root, xPath, nsContext );
380            List<String> list = new ArrayList<String>( nodes.length );
381            for ( int i = 0; i < nodes.length; i++ ) {
382                list.add( nodes[i] );
383            }
384    
385            return list;
386        }
387    
388        /**
389         * @param root
390         * @return Proxy
391         * @throws XMLParsingException
392         */
393        private Proxy parseProxy( Node root )
394                                throws XMLParsingException {
395    
396            Proxy proxy = null;
397            Node pro = XMLTools.getNode( root, "./deegreewmps:Proxy", nsContext );
398            if ( pro != null ) {
399                String proxyHost = XMLTools.getRequiredNodeAsString( pro, "./@proxyHost", nsContext );
400                String proxyPort = XMLTools.getRequiredNodeAsString( pro, "./@proxyPort", nsContext );
401                proxy = new Proxy( proxyHost, proxyPort );
402            }
403    
404            return proxy;
405        }
406    
407        /**
408         * creates an object that describes the access to a gazetteer if one have been defined at the
409         * <DeegreeParam>section of the capabilities/configuration
410         * 
411         * @param element
412         * @return GazetteerParam
413         * @throws XMLParsingException
414         */
415        private GazetteerParam parseGazetteerParameter( Element element )
416                                throws XMLParsingException {
417    
418            GazetteerParam gazetteer = null;
419    
420            if ( element != null ) {
421                String xPath = "./deegreewmps:OnlineResource";
422                Element elem = (Element) XMLTools.getRequiredNode( element, xPath, nsContext );
423                OnlineResource olr = parseOnLineResource( elem );
424                URL onlineResource = olr.getLinkage().getHref();
425                // optional: <LocationRadius>, default: 10
426                double radius = XMLTools.getNodeAsDouble( element, "LocationRadius", nsContext, 10 );
427                gazetteer = new GazetteerParam( onlineResource, radius );
428            }
429    
430            return gazetteer;
431        }
432    
433        /**
434         * returns the layers offered by the WMS
435         * 
436         * @param layerElem
437         * @param parent
438         * @return Layer
439         * @throws XMLParsingException
440         * @throws UnknownCRSException
441         */
442        @Override
443        protected Layer parseLayers( Element layerElem, Layer parent )
444                                throws XMLParsingException, UnknownCRSException {
445    
446            boolean queryable = XMLTools.getNodeAsBoolean( layerElem, "./@queryable", nsContext, false );
447            int cascaded = XMLTools.getNodeAsInt( layerElem, "./@cascaded", nsContext, 0 );
448            boolean opaque = XMLTools.getNodeAsBoolean( layerElem, "./@opaque", nsContext, false );
449            boolean noSubsets = XMLTools.getNodeAsBoolean( layerElem, "./@noSubsets", nsContext, false );
450            int fixedWidth = XMLTools.getNodeAsInt( layerElem, "./@fixedWidth", nsContext, 0 );
451            int fixedHeight = XMLTools.getNodeAsInt( layerElem, "./@fixedHeight", nsContext, 0 );
452            String name = XMLTools.getNodeAsString( layerElem, "./Name", nsContext, null );
453            String title = XMLTools.getRequiredNodeAsString( layerElem, "./Title", nsContext );
454            String layerAbstract = XMLTools.getNodeAsString( layerElem, "./Abstract", nsContext, null );
455            String[] keywords = XMLTools.getNodesAsStrings( layerElem, "./KeywordList/Keyword", nsContext );
456            String[] srs = XMLTools.getNodesAsStrings( layerElem, "./SRS", nsContext );
457            List nl = XMLTools.getNodes( layerElem, "./BoundingBox", nsContext );
458            // substitue with Envelope
459            LayerBoundingBox[] bboxes = null;
460            if ( nl.size() == 0 && parent != null ) {
461                // inherit BoundingBoxes from parent layer
462                bboxes = parent.getBoundingBoxes();
463            } else {
464                bboxes = parseLayerBoundingBoxes( nl );
465            }
466    
467            Element llBox = (Element) XMLTools.getNode( layerElem, "./LatLonBoundingBox", nsContext );
468            Envelope llBoundingBox = null;
469            if ( llBox == null && parent != null ) {
470                // inherit LatLonBoundingBox parent layer
471                llBoundingBox = parent.getLatLonBoundingBox();
472            } else {
473                llBoundingBox = parseLatLonBoundingBox( llBox );
474            }
475    
476            Dimension[] dimensions = parseDimensions( layerElem );
477            Extent[] extents = parseExtents( layerElem );
478            Attribution attribution = parseAttribution( layerElem );
479            AuthorityURL[] authorityURLs = parseAuthorityURLs( layerElem );
480            MetadataURL[] metadataURLs = parseMetadataURLs( layerElem );
481            DataURL[] dataURLs = parseDataURL( layerElem );
482            Identifier[] identifiers = parseIdentifiers( layerElem );
483            FeatureListURL[] featureListURLs = parseFeatureListURL( layerElem );
484            Style[] styles = parseStyles( layerElem );
485            ScaleHint scaleHint = parseScaleHint( layerElem );
486            AbstractDataSource[] ds = parseDataSources( layerElem, name );
487    
488            Layer layer = new Layer( queryable, cascaded, opaque, noSubsets, fixedWidth, fixedHeight, name, title,
489                                     layerAbstract, llBoundingBox, attribution, scaleHint, keywords, srs, bboxes,
490                                     dimensions, extents, authorityURLs, identifiers, metadataURLs, dataURLs,
491                                     featureListURLs, styles, null, ds, parent );
492    
493            // get Child layers
494            nl = XMLTools.getNodes( layerElem, "./Layer", nsContext );
495            Layer[] layers = new Layer[nl.size()];
496            for ( int i = 0; i < layers.length; i++ ) {
497                layers[i] = parseLayers( (Element) nl.get( i ), layer );
498            }
499            // set child layers
500            layer.setLayer( layers );
501    
502            return layer;
503        }
504    
505        /**
506         * Parse the Datasources element
507         * 
508         * @param layerElem
509         * @param layerName
510         * @return AbstractDataSource[]
511         * @throws XMLParsingException
512         */
513        private AbstractDataSource[] parseDataSources( Element layerElem, String layerName )
514                                throws XMLParsingException {
515    
516            List nl = XMLTools.getNodes( layerElem, "./deegreewmps:DataSource", nsContext );
517    
518            AbstractDataSource[] ds = new AbstractDataSource[nl.size()];
519            for ( int i = 0; i < ds.length; i++ ) {
520    
521                boolean failOnEx = XMLTools.getNodeAsBoolean( (Node) nl.get( i ), "./@failOnException", nsContext, true );
522    
523                boolean queryable = XMLTools.getNodeAsBoolean( (Node) nl.get( i ), "./@queryable", nsContext, false );
524    
525                QualifiedName name = XMLTools.getNodeAsQualifiedName( (Node) nl.get( i ), "./deegreewmps:Name/text()",
526                                                                      nsContext, new QualifiedName( layerName ) );
527    
528                String stype = XMLTools.getRequiredNodeAsString( (Node) nl.get( i ), "./deegreewmps:Type", nsContext );
529    
530                String xPath = "./deegreewmps:OWSCapabilities/deegreewmps:OnlineResource";
531                Node node = XMLTools.getRequiredNode( (Node) nl.get( i ), xPath, nsContext );
532    
533                URL url = parseOnLineResource( (Element) node ).getLinkage().getHref();
534                ScaleHint scaleHint = parseScaleHint( (Node) nl.get( i ) );
535                Geometry validArea = parseValidArea( (Node) nl.get( i ) );
536    
537                try {
538                    if ( "LOCALWFS".equals( stype ) ) {
539                        ds[i] = createLocalWFSDataSource( (Node) nl.get( i ), failOnEx, queryable, name, url, scaleHint,
540                                                          validArea, 60 );
541                    } else if ( "LOCALWCS".equals( stype ) ) {
542                        ds[i] = createLocalWCSDataSource( (Node) nl.get( i ), failOnEx, queryable, name, url, scaleHint,
543                                                          validArea, 60 );
544                    } else if ( "REMOTEWFS".equals( stype ) ) {
545                        ds[i] = createRemoteWFSDataSource( (Node) nl.get( i ), failOnEx, queryable, name, url, scaleHint,
546                                                           validArea, 60 );
547                    } else if ( "REMOTEWCS".equals( stype ) ) {
548                        // TODO
549                        // int type = AbstractDataSource.REMOTEWCS;
550                        // GetCoverage getCoverage = parseWCSFilterCondition( (Node) nl.get( i ) );
551                        // Color[] colors = parseTransparentColors( (Node) nl.get( i ) );
552                        throw new UnsupportedOperationException( "REMOTEWCS is not supported as yet!" );
553                    } else if ( "REMOTEWMS".equals( stype ) ) {
554                        ds[i] = createRemoteWMSDataSource( (Node) nl.get( i ), failOnEx, queryable, name, url, scaleHint,
555                                                           validArea, 60 );
556                    } else {
557                        throw new XMLParsingException( "invalid DataSource type: " + stype + " defined in deegree WMS "
558                                                       + "configuration for DataSource: " + name );
559                    }
560                } catch ( Exception e ) {
561                    throw new XMLParsingException( "could not create service instance for " + "WMPS datasource: " + name, e );
562                }
563            }
564    
565            return ds;
566        }
567    
568        /**
569         * returns the area a data source is valid. If the optional element <ValidArea>is not defined in
570         * the configuration returns <code>null</code>.
571         * 
572         * @param node
573         * @return Geometry
574         * @throws XMLParsingException
575         */
576        private Geometry parseValidArea( Node node )
577                                throws XMLParsingException {
578    
579            Geometry geom = null;
580            List nl = XMLTools.getNodes( node, "./deegreewmps:ValidArea/*", nsContext );
581            if ( node != null ) {
582                try {
583                    for ( int i = 0; i < nl.size(); i++ ) {
584                        if ( ( (Node) nl.get( 0 ) ).getNamespaceURI().equals( GMLNS ) ) {
585                            geom = GMLGeometryAdapter.wrap( (Element) nl.get( 0 ), null );
586                            break;
587                        }
588                    }
589                } catch ( GeometryException e ) {
590                    throw new XMLParsingException( "couldn't parse/create valid area of a " + "datasource", e );
591                }
592            }
593    
594            return geom;
595        }
596    
597        /**
598         * Create a RemoteWMS datasource.
599         * 
600         * @param node
601         * @param failOnEx
602         * @param queryable
603         * @param name
604         * @param url
605         * @param scaleHint
606         * @param validArea
607         * @param reqTimeLimit
608         * @return XMLParsingException
609         * @throws Exception
610         */
611        private RemoteWMSDataSource createRemoteWMSDataSource( Node node, boolean failOnEx, boolean queryable,
612                                                               QualifiedName name, URL url, ScaleHint scaleHint,
613                                                               Geometry validArea, int reqTimeLimit )
614                                throws Exception {
615    
616            int type = AbstractDataSource.REMOTEWMS;
617            String xPath = "./deegreewmps:FeatureInfoTransformation/deegreewmps:OnlineResource";
618            Node fitNode = XMLTools.getNode( node, xPath, nsContext );
619            URL fitURL = null;
620            if ( fitNode != null ) {
621                fitURL = parseOnLineResource( (Element) fitNode ).getLinkage().getHref();
622            }
623    
624            GetMap getMap = parseWMSFilterCondition( node );
625            Color[] colors = parseTransparentColors( node );
626            WMSCapabilities wCapa = null;
627            try {
628                if ( capaCache.get( url ) != null ) {
629                    wCapa = (WMSCapabilities) capaCache.get( url );
630                } else {
631                    WMSCapabilitiesDocument doc = WMSCapabilitiesDocumentFactory.getWMSCapabilitiesDocument( url );
632                    wCapa = (WMSCapabilities) doc.parseCapabilities();
633                    capaCache.put( url, wCapa );
634                }
635            } catch ( Exception e ) {
636                LOG.logError( "could not connet: " + url, e );
637            }
638            OGCWebService ows = new RemoteWMService( wCapa );
639    
640            // parse added/passed parameter map/list
641            Node vendor = XMLTools.getNode( node,
642                                            "deegreewmps:FilterCondition/deegreewmps:VendorspecificParameterDefinition",
643                                            nsContext );
644    
645            List<String> passedParameters = parsePassedParameters( vendor );
646            Map<String, String> addedParameters = parseAddedParameters( vendor );
647    
648            return new RemoteWMSDataSource( queryable, failOnEx, name, type, ows, url, scaleHint, validArea, getMap,
649                                            colors, fitURL, reqTimeLimit, passedParameters, addedParameters );
650        }
651    
652        /**
653         * @param vendor
654         * @return the parsed list
655         * @throws XMLParsingException
656         */
657        private static List<String> parsePassedParameters( Node vendor )
658                                throws XMLParsingException {
659            List<String> passedParameters = new ArrayList<String>( 10 );
660    
661            if ( vendor == null ) {
662                return passedParameters;
663            }
664    
665            List<Node> nl = XMLTools.getNodes( vendor, "deegreewmps:PassedVendorspecificParameter/deegreewmps:Name",
666                                               nsContext );
667    
668            for ( Object obj : nl ) {
669                passedParameters.add( ( (Node) obj ).getTextContent().toUpperCase() );
670            }
671    
672            return passedParameters;
673        }
674    
675        /**
676         * @param vendor
677         * @return the parsed map
678         * @throws XMLParsingException
679         */
680        private static Map<String, String> parseAddedParameters( Node vendor )
681                                throws XMLParsingException {
682            Map<String, String> addedParameters = new HashMap<String, String>( 10 );
683    
684            if ( vendor == null ) {
685                return addedParameters;
686            }
687    
688            List<Node> nl = XMLTools.getNodes(
689                                               vendor,
690                                               "deegreewmps:AddedVendorspecificParameter/deegreewmps:VendorspecificParameter",
691                                               nsContext );
692    
693            for ( Object obj : nl ) {
694                String pName = XMLTools.getRequiredNodeAsString( (Node) obj, "deegreewmps:Name", nsContext );
695                String pValue = XMLTools.getRequiredNodeAsString( (Node) obj, "deegreewmps:Value", nsContext );
696    
697                addedParameters.put( pName, pValue );
698            }
699            return addedParameters;
700        }
701    
702        /**
703         * Create a Remote WFS datasource.
704         * 
705         * @param node
706         * @param failOnEx
707         * @param queryable
708         * @param name
709         * @param url
710         * @param scaleHint
711         * @param validArea
712         * @param reqTimeLimit
713         * @return RemoteWFSDataSource
714         * @throws Exception
715         */
716        private RemoteWFSDataSource createRemoteWFSDataSource( Node node, boolean failOnEx, boolean queryable,
717                                                               QualifiedName name, URL url, ScaleHint scaleHint,
718                                                               Geometry validArea, int reqTimeLimit )
719                                throws Exception {
720    
721            int type = AbstractDataSource.REMOTEWFS;
722            String xPath = "./deegreewmps:FeatureInfoTransformation/deegreewmps:OnlineResource";
723            Node fitNode = XMLTools.getNode( node, xPath, nsContext );
724            URL fitURL = null;
725            if ( fitNode != null ) {
726                fitURL = parseOnLineResource( (Element) fitNode ).getLinkage().getHref();
727            }
728            Query query = parseWFSFilterCondition( node );
729    
730            WFSCapabilities wfsCapa = null;
731            if ( capaCache.get( url ) != null ) {
732                wfsCapa = (WFSCapabilities) capaCache.get( url );
733            } else {
734                WFSCapabilitiesDocument wfsDoc = new WFSCapabilitiesDocument();
735                wfsDoc.load( url );
736                wfsCapa = (WFSCapabilities) wfsDoc.parseCapabilities();
737                capaCache.put( url, wfsCapa );
738            }
739            // OGCWebService ows = new RemoteWFService( wfsCapa );
740            OGCWebService ows = null;
741            xPath = "./deegreewmps:GeometryProperty/text()";
742            QualifiedName geom = new QualifiedName( "app", "GEOM", new URI( "http://www.deegree.org/app" ) );
743            QualifiedName geoProp = XMLTools.getNodeAsQualifiedName( node, xPath, nsContext, geom );
744    
745            return new RemoteWFSDataSource( queryable, failOnEx, name, type, geoProp, ows, url, scaleHint, validArea,
746                                            query, fitURL, reqTimeLimit );
747    
748        }
749    
750        /**
751         * Create a Local WCS Datasource
752         * 
753         * @param node
754         * @param failOnEx
755         * @param queryable
756         * @param name
757         * @param url
758         * @param scaleHint
759         * @param validArea
760         * @param reqTimeLimit
761         * @return RemoteWFSDataSource
762         * @throws Exception
763         */
764        private LocalWCSDataSource createLocalWCSDataSource( Node node, boolean failOnEx, boolean queryable,
765                                                             QualifiedName name, URL url, ScaleHint scaleHint,
766                                                             Geometry validArea, int reqTimeLimit )
767                                throws Exception {
768    
769            int type = AbstractDataSource.LOCALWCS;
770            GetCoverage getCoverage = parseWCSFilterCondition( node );
771            Color[] colors = parseTransparentColors( node );
772            WCSConfiguration configuration = null;
773            if ( capaCache.get( url ) != null ) {
774                configuration = (WCSConfiguration) capaCache.get( url );
775            } else {
776                configuration = WCSConfiguration.create( url );
777                capaCache.put( url, configuration );
778            }
779    
780            OGCWebService ows = new WCService( configuration );
781    
782            return new LocalWCSDataSource( queryable, failOnEx, name, type, ows, url, scaleHint, validArea, getCoverage,
783                                           colors, reqTimeLimit );
784        }
785    
786        /**
787         * Create a Local WFS Datasource
788         * 
789         * @param node
790         * @param failOnEx
791         * @param queryable
792         * @param name
793         * @param url
794         * @param scaleHint
795         * @param validArea
796         * @param reqTimeLimit
797         * @return LocalWFSDataSource
798         * @throws Exception
799         */
800        private LocalWFSDataSource createLocalWFSDataSource( Node node, boolean failOnEx, boolean queryable,
801                                                             QualifiedName name, URL url, ScaleHint scaleHint,
802                                                             Geometry validArea, int reqTimeLimit )
803                                throws Exception {
804    
805            int type = AbstractDataSource.LOCALWFS;
806            String xPath = null;
807            Query query = parseWFSFilterCondition( node );
808            WFSConfiguration wfsCapa = null;
809            if ( capaCache.get( url ) != null ) {
810                wfsCapa = (WFSConfiguration) capaCache.get( url );
811            } else {
812                WFSConfigurationDocument wfsDoc = new WFSConfigurationDocument();
813                wfsDoc.load( url );
814                wfsCapa = wfsDoc.getConfiguration();
815                capaCache.put( url, wfsCapa );
816            }
817            // OGCWebService ows = WFServiceFactory.getUncachedService( wfsCapa );
818            OGCWebService ows = WFServiceFactory.createInstance( wfsCapa );
819    
820            QualifiedName geom = new QualifiedName( "app", "GEOM", new URI( "http://www.deegree.org/app" ) );
821            xPath = "./deegreewmps:GeometryProperty/text()";
822            QualifiedName geoProp = XMLTools.getNodeAsQualifiedName( node, xPath, nsContext, geom );
823    
824            return new LocalWFSDataSource( queryable, failOnEx, name, type, geoProp, ows, url, scaleHint, validArea, query,
825                                           null, reqTimeLimit );
826        }
827    
828        /**
829         * Parse trasparent colors.
830         * 
831         * @param node
832         * @return Color[]
833         * @throws XMLParsingException
834         */
835        private Color[] parseTransparentColors( Node node )
836                                throws XMLParsingException {
837    
838            String xPath = "./deegreewmps:TransparentColors/deegreewmps:Color";
839            List clnl = XMLTools.getNodes( node, xPath, nsContext );
840            Color[] colors = new Color[clnl.size()];
841            for ( int i = 0; i < colors.length; i++ ) {
842                colors[i] = Color.decode( XMLTools.getStringValue( (Node) clnl.get( i ) ) );
843            }
844    
845            return colors;
846        }
847    
848        /**
849         * Parse Scale hint
850         * 
851         * @param node
852         * @return ScaleHint
853         * @throws XMLParsingException
854         */
855        private ScaleHint parseScaleHint( Node node )
856                                throws XMLParsingException {
857    
858            String xPath = "./deegreewmps:ScaleHint/@min";
859            double minScale = XMLTools.getNodeAsDouble( node, xPath, nsContext, 0 );
860            xPath = "./deegreewmps:ScaleHint/@max";
861            double maxScale = XMLTools.getNodeAsDouble( node, xPath, nsContext, Double.MAX_VALUE );
862    
863            return new ScaleHint( minScale, maxScale );
864        }
865    
866        /**
867         * Parse WFS Filter condition
868         * 
869         * @param node
870         * @return Query
871         * @throws XMLParsingException
872         */
873        private Query parseWFSFilterCondition( Node node )
874                                throws XMLParsingException {
875    
876            Query query = null;
877            String xPath = "./deegreewmps:FilterCondition/wfs:Query";
878            Node queryNode = XMLTools.getNode( node, xPath, nsContext );
879            if ( queryNode != null ) {
880                try {
881                    query = Query.create( (Element) queryNode );
882                } catch ( Exception e ) {
883                    throw new XMLParsingException( StringTools.stackTraceToString( e ) );
884                }
885            }
886    
887            return query;
888        }
889    
890        /**
891         * Parse WCS Filter Condition
892         * 
893         * @param node
894         * @return GetCoverage
895         * @throws XMLParsingException
896         */
897        private GetCoverage parseWCSFilterCondition( Node node )
898                                throws XMLParsingException {
899    
900            GetCoverage getCoverage = null;
901    
902            String id = "" + IDGenerator.getInstance().generateUniqueID();
903    
904            StringBuffer sd = new StringBuffer( 1000 );
905            sd.append( "version=1.0.0&Coverage=%default%&" );
906            sd.append( "CRS=EPSG:4326&BBOX=0,0,1,1&Width=1" );
907            sd.append( "&Height=1&Format=%default%&" );
908            String xPath = "./deegreewmps:FilterCondition/deegreewmps:WCSRequest";
909            String s = XMLTools.getNodeAsString( node, xPath, nsContext, "" );
910            sd.append( s );
911            try {
912                getCoverage = GetCoverage.create( id, sd.toString() );
913            } catch ( Exception e ) {
914                throw new XMLParsingException( "could not create GetCoverage from WMS " + "FilterCondition", e );
915            }
916    
917            return getCoverage;
918        }
919    
920        /**
921         * Returns a GetMap instance , parsing the WMS Filter condition
922         * 
923         * @param node
924         * @return GetMap
925         * @throws XMLParsingException
926         */
927        private GetMap parseWMSFilterCondition( Node node )
928                                throws XMLParsingException {
929    
930            GetMap getMap = null;
931    
932            String id = "" + IDGenerator.getInstance().generateUniqueID();
933    
934            StringBuffer sd = new StringBuffer( 1000 );
935            sd.append( "REQUEST=GetMap&LAYERS=%default%&" );
936            sd.append( "STYLES=&SRS=EPSG:4326&BBOX=0,0,1,1&WIDTH=1&" );
937            sd.append( "HEIGHT=1&FORMAT=%default%" );
938            Map<String, String> map1 = KVP2Map.toMap( sd.toString() );
939            String xPath = "./deegreewmps:FilterCondition/deegreewmps:WMSRequest";
940            String s = XMLTools.getRequiredNodeAsString( node, xPath, nsContext );
941            Map<String, String> map2 = KVP2Map.toMap( s );
942            if ( map2.get( "VERSION" ) == null && map2.get( "WMTVER" ) == null ) {
943                map2.put( "VERSION", "1.1.1" );
944            }
945            // if no service is set use WMS as default
946            if ( map2.get( "SERVICE" ) == null ) {
947                map2.put( "SERVICE", "WMS" );
948            }
949            map1.putAll( map2 );
950            try {
951                map1.put( "ID", id );
952                getMap = GetMap.create( map1 );
953            } catch ( Exception e ) {
954                throw new XMLParsingException( "could not create GetCoverage from WMS " + "FilterCondition", e );
955            }
956    
957            return getMap;
958        }
959    
960        /**
961         * Returns a list of Style elements, parsing the style element
962         * 
963         * @param layerElem
964         * @return Style[]
965         * @throws XMLParsingException
966         */
967        @Override
968        protected Style[] parseStyles( Element layerElem )
969                                throws XMLParsingException {
970    
971            List nl = XMLTools.getNodes( layerElem, "./Style", nsContext );
972            Style[] styles = new Style[nl.size()];
973            for ( int i = 0; i < styles.length; i++ ) {
974                String name = XMLTools.getRequiredNodeAsString( (Node) nl.get( i ), "./Name", nsContext );
975                String title = XMLTools.getNodeAsString( (Node) nl.get( i ), "./Title", nsContext, null );
976                String styleAbstract = XMLTools.getNodeAsString( (Node) nl.get( i ), "./Abstract", nsContext, null );
977                LegendURL[] legendURLs = parseLegendURL( (Node) nl.get( i ) );
978                StyleURL styleURL = parseStyleURL( (Node) nl.get( i ) );
979                StyleSheetURL styleSheetURL = parseStyleSheetURL( (Node) nl.get( i ) );
980                String xPath = "deegreewmps:StyleResource";
981                String styleResource = XMLTools.getNodeAsString( (Node) nl.get( i ), xPath, nsContext, "styles.xml" );
982                URL sr = null;
983                try {
984                    sr = resolve( styleResource );
985                } catch ( MalformedURLException e ) {
986                    throw new XMLParsingException( "could not parse style resource of " + "style: " + name, e );
987                }
988                styles[i] = new Style( name, title, styleAbstract, legendURLs, styleSheetURL, styleURL, sr );
989            }
990    
991            return styles;
992        }
993    
994    }