001    // $HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_testing/src/org/deegree/ogcwebservices/wcs/configuration/ExtensionDocument.java $
002    /*----------------------------------------------------------------------------
003     This file is part of deegree, http://deegree.org/
004     Copyright (C) 2001-2009 by:
005     Department of Geography, University of Bonn
006     and
007     lat/lon GmbH
008    
009     This library is free software; you can redistribute it and/or modify it under
010     the terms of the GNU Lesser General Public License as published by the Free
011     Software Foundation; either version 2.1 of the License, or (at your option)
012     any later version.
013     This library is distributed in the hope that it will be useful, but WITHOUT
014     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
015     FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
016     details.
017     You should have received a copy of the GNU Lesser General Public License
018     along with this library; if not, write to the Free Software Foundation, Inc.,
019     59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020    
021     Contact information:
022    
023     lat/lon GmbH
024     Aennchenstr. 19, 53177 Bonn
025     Germany
026     http://lat-lon.de/
027    
028     Department of Geography, University of Bonn
029     Prof. Dr. Klaus Greve
030     Postfach 1147, 53001 Bonn
031     Germany
032     http://www.geographie.uni-bonn.de/deegree/
033    
034     e-mail: info@deegree.org
035     ----------------------------------------------------------------------------*/
036    package org.deegree.ogcwebservices.wcs.configuration;
037    
038    import java.io.File;
039    import java.net.MalformedURLException;
040    import java.net.URI;
041    import java.net.URISyntaxException;
042    import java.net.URL;
043    import java.util.ArrayList;
044    import java.util.List;
045    
046    import org.deegree.datatypes.values.Interval;
047    import org.deegree.datatypes.values.TypedLiteral;
048    import org.deegree.framework.log.ILogger;
049    import org.deegree.framework.log.LoggerFactory;
050    import org.deegree.framework.util.StringTools;
051    import org.deegree.framework.xml.ElementList;
052    import org.deegree.framework.xml.NamespaceContext;
053    import org.deegree.framework.xml.XMLFragment;
054    import org.deegree.framework.xml.XMLParsingException;
055    import org.deegree.framework.xml.XMLTools;
056    import org.deegree.io.IODocument;
057    import org.deegree.io.JDBCConnection;
058    import org.deegree.model.crs.CRSFactory;
059    import org.deegree.model.crs.CoordinateSystem;
060    import org.deegree.model.crs.UnknownCRSException;
061    import org.deegree.model.spatialschema.Envelope;
062    import org.deegree.model.spatialschema.GMLGeometryAdapter;
063    import org.deegree.ogcbase.CommonNamespaces;
064    import org.deegree.ogcbase.InvalidGMLException;
065    import org.deegree.ogcwebservices.InvalidParameterValueException;
066    import org.w3c.dom.Element;
067    import org.w3c.dom.Node;
068    
069    /**
070     * This class creates a class representation of the Extension section of a deegree WCS coverage offering (coverage
071     * configuration) element. the extension section contains informations about data access/sources for different
072     * resolutions and ranges.<BR>
073     * an extension section must contain at least one Resolution element but can contains as much as the user may defined. A
074     * resoluton contains a access informations for data and the ranges the data are assigned to. because of this it is
075     * possible that more than one Resoultion element with same resolution range but with different other ranges (e.g. time
076     * or elevation)
077     * 
078     * @version $Revision: 20592 $
079     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
080     * @author last edited by: $Author: rbezema $
081     * 
082     * @version 1.0. $Revision: 20592 $, $Date: 2009-11-05 13:24:27 +0100 (Do, 05. Nov 2009) $
083     * 
084     * @since 1.1
085     */
086    public class ExtensionDocument {
087    
088        private static final ILogger LOG = LoggerFactory.getLogger( ExtensionDocument.class );
089    
090        private static URI GMLNS = CommonNamespaces.GMLNS;
091    
092        private static URI DGRNS = CommonNamespaces.DEEGREEWCS;
093    
094        private static NamespaceContext nsc = CommonNamespaces.getNamespaceContext();
095    
096        private URL systemId = null;
097    
098        private Element root = null;
099    
100        /**
101         * constructing the ExtensionBuilder by passing the root element of a deegree WCS CoverageOffering Extension.
102         * 
103         * @param root
104         * @param systemId
105         */
106        public ExtensionDocument( Element root, URL systemId ) {
107            this.root = root;
108            this.systemId = systemId;
109        }
110    
111        /**
112         * returns the content of the Extension element of te deegree WCS coverage description (configuration document). the
113         * extension section contains informations about data access/sources for different resolutions and ranges.<BR>
114         * an extension section must contain at least one Resolution element but can contains as much as the user may
115         * defined. A resoluton contains a access informations for data and the ranges the data are assigned to. because of
116         * this it is possible that more than one Resoultion element with same resolution range but with different other
117         * ranges (e.g. time or elevation)
118         * 
119         * @return content of the Extension element of te deegree WCS coverage description
120         * @throws InvalidCVExtensionException
121         * @throws UnknownCVExtensionException
122         * @throws InvalidParameterValueException
123         * @throws InvalidGMLException
124         * @throws UnknownCRSException
125         */
126        public Extension getExtension()
127                                throws InvalidCVExtensionException, UnknownCVExtensionException,
128                                InvalidParameterValueException, InvalidGMLException, UnknownCRSException {
129            Extension extension = null;
130            try {
131                String type = XMLTools.getRequiredNodeAsString( root, "./@type", nsc );
132                double offset = XMLTools.getNodeAsDouble( root, "./@offset", nsc, 0 );
133                double scaleFactor = XMLTools.getNodeAsDouble( root, "./@scaleFactor", nsc, 1 );
134                ElementList el = XMLTools.getChildElements( "Resolution", DGRNS, root );
135                Resolution[] resolutions = getResolutions( type, el );
136                extension = new DefaultExtension( type, resolutions, offset, scaleFactor );
137            } catch ( XMLParsingException e ) {
138                throw new InvalidCVExtensionException( StringTools.stackTraceToString( e ) );
139            }
140            return extension;
141        }
142    
143        /**
144         * returns the resolutions definitions within the Extension element of the deegree WCS coverage offering. Each
145         * resoultion contains access description for its data and an optional description of the ranges the data are valid
146         * for.
147         * 
148         * @param type
149         * @param el
150         * @return resolutions definitions within the Extension element of the deegree WCS coverage offering
151         * @throws XMLParsingException
152         * @throws InvalidParameterValueException
153         * @throws UnknownCRSException
154         */
155        private Resolution[] getResolutions( String type, ElementList el )
156                                throws XMLParsingException, InvalidParameterValueException, InvalidGMLException,
157                                UnknownCRSException {
158            Resolution[] resolutions = new Resolution[el.getLength()];
159            for ( int i = 0; i < resolutions.length; i++ ) {
160                resolutions[i] = getResolution( type, el.item( i ) );
161            }
162            return resolutions;
163        }
164    
165        /**
166         * creates an instance of <tt>Resoltuion</tt> from the passed <tt>Element</tt> and the type of the coverage source.
167         * Valid values for type are:
168         * <ul>
169         * <li>shapeIndexed
170         * <li>nameIndexed
171         * <li>file
172         * </ul>
173         * if an unknown typed is passed an <tt>InvalidParameterValueException</tt> will be thrown
174         * 
175         * @param type
176         * @param element
177         * @return created Resoltuion
178         * @throws XMLParsingException
179         * @throws UnknownCRSException
180         */
181        private Resolution getResolution( String type, Element element )
182                                throws XMLParsingException, InvalidParameterValueException, InvalidGMLException,
183                                UnknownCRSException {
184            String tmp = XMLTools.getRequiredAttrValue( "min", null, element );
185            double min = Double.parseDouble( tmp );
186            tmp = XMLTools.getRequiredAttrValue( "max", null, element );
187            double max = Double.parseDouble( tmp );
188            // ElementList el = XMLTools.getChildElements( "Range", DGRNS, element );
189            NamespaceContext nsContext = CommonNamespaces.getNamespaceContext();
190            List<Node> el = XMLTools.getNodes( element, "deegreewcs:Range", nsContext );
191            Range[] ranges = getRanges( el );
192            Resolution resolution = null;
193    
194            if ( type.equals( Extension.SHAPEINDEXED ) ) {
195                // TODO
196                // enable more than one shape
197                Element elem = XMLTools.getRequiredElement( element, "deegreewcs:Shape", nsContext );
198                Shape shape = getShape( elem );
199                resolution = new ShapeResolution( min, max, ranges, shape );
200            } else if ( type.equals( Extension.NAMEINDEXED ) ) {
201                ElementList ell = XMLTools.getChildElements( "Directory", DGRNS, element );
202                Directory[] dirs = new Directory[ell.getLength()];
203                for ( int i = 0; i < dirs.length; i++ ) {
204                    dirs[i] = getDirectory( ell.item( i ) );
205                }
206                resolution = new DirectoryResolution( min, max, ranges, dirs );
207            } else if ( type.equals( Extension.FILEBASED ) ) {
208                ElementList ell = XMLTools.getChildElements( "File", DGRNS, element );
209                org.deegree.ogcwebservices.wcs.configuration.File[] files = new org.deegree.ogcwebservices.wcs.configuration.File[ell.getLength()];
210                for ( int i = 0; i < files.length; i++ ) {
211                    files[i] = getFile( ell.item( i ) );
212                }
213                resolution = new FileResolution( min, max, ranges, files );
214            } else if ( type.equals( Extension.ORACLEGEORASTER ) ) {
215                resolution = getOracleGeoRasterResolution( element, min, max, ranges );
216    
217            } else if ( type.equals( Extension.DATABASEINDEXED ) ) {
218                resolution = getDatabaseIndexed( element, min, max, ranges );
219            } else if ( type.equals( Extension.SCRIPTBASED ) ) {
220                resolution = getScriptBased( element, min, max, ranges );
221            } else {
222                String msg = StringTools.concat( 200, "type: ", type, " not known ", "by the deegree WCS" );
223                throw new InvalidParameterValueException( msg );
224            }
225            return resolution;
226        }
227    
228        private Resolution getScriptBased( Element element, double min, double max, Range[] ranges )
229                                throws XMLParsingException, InvalidParameterValueException {
230    
231            XMLFragment xml = new XMLFragment( element );
232            try {
233                xml.setSystemId( systemId.toExternalForm() );
234            } catch ( MalformedURLException e ) {
235                // should never happen because systemID is read from a valid URL
236            }
237    
238            NamespaceContext nsc = CommonNamespaces.getNamespaceContext();
239            String xpath = "deegreewcs:Script/deegreewcs:Name";
240            String script = XMLTools.getRequiredNodeAsString( element, xpath, nsc );
241    
242            xpath = "deegreewcs:Script/deegreewcs:Parameter";
243            List<Node> list = XMLTools.getNodes( element, xpath, nsc );
244            List<String> parameter = new ArrayList<String>( list.size() );
245            for ( Node node : list ) {
246                parameter.add( XMLTools.getStringValue( node ) );
247            }
248            xpath = "deegreewcs:Script/deegreewcs:ResultFormat";
249            String resultFormat = XMLTools.getRequiredNodeAsString( element, xpath, nsc );
250            xpath = "deegreewcs:Script/deegreewcs:StorageLocation";
251            String storageLocation = XMLTools.getRequiredNodeAsString( element, xpath, nsc );
252            try {
253                storageLocation = new File( xml.resolve( storageLocation ).getFile() ).getAbsolutePath();
254            } catch ( MalformedURLException e ) {
255                e.printStackTrace();
256                throw new InvalidParameterValueException( e.getMessage(), e );
257            }
258            return new ScriptResolution( min, max, ranges, script, parameter, resultFormat, storageLocation );
259        }
260    
261        /**
262         * creates a <tt>DatabaseResolution</tt> object from the passed element
263         * 
264         * @param element
265         * @param min
266         * @param max
267         * @param ranges
268         * @return the resolution
269         * @throws XMLParsingException
270         */
271        private Resolution getDatabaseIndexed( Element element, double min, double max, Range[] ranges )
272                                throws XMLParsingException {
273    
274            NamespaceContext nsc = CommonNamespaces.getNamespaceContext();
275            String xpath = "deegreewcs:Database/dgjdbc:JDBCConnection";
276            Node node = XMLTools.getRequiredNode( element, xpath, nsc );
277            IODocument io = new IODocument( (Element) node );
278            JDBCConnection jdbc = io.parseJDBCConnection();
279            xpath = "deegreewcs:Database/deegreewcs:Table/text()";
280            String table = XMLTools.getRequiredNodeAsString( element, xpath, nsc );
281            xpath = "deegreewcs:Database/deegreewcs:DataRootDirectory/text()";
282            String rootDir = XMLTools.getNodeAsString( element, xpath, nsc, "./" );
283            File dir = new File( rootDir );
284            if ( !dir.isAbsolute() ) {
285                XMLFragment xml = new XMLFragment();
286                xml.setRootElement( root );
287                xml.setSystemId( systemId );
288                try {
289                    URL url = xml.resolve( rootDir );
290                    dir = new File( url.toURI() );
291                } catch ( Exception e ) {
292                    LOG.logError( e.getMessage(), e );
293                }
294            }
295            return new DatabaseResolution( min, max, ranges, jdbc, table, dir.getAbsolutePath() );
296        }
297    
298        /**
299         * creates a <tt>OracleGeoRasterResolution</tt> object from the passed element
300         * 
301         * @param element
302         * @param min
303         * @param max
304         * @param ranges
305         * @return the Resolution
306         * @throws XMLParsingException
307         */
308        private Resolution getOracleGeoRasterResolution( Element element, double min, double max, Range[] ranges )
309                                throws XMLParsingException {
310            Resolution resolution;
311            NamespaceContext nsc = CommonNamespaces.getNamespaceContext();
312            String xpath = "deegreewcs:OracleGeoRaster/dgjdbc:JDBCConnection";
313            Node node = XMLTools.getRequiredNode( element, xpath, nsc );
314            IODocument io = new IODocument( (Element) node );
315            JDBCConnection jdbc = io.parseJDBCConnection();
316            xpath = "deegreewcs:OracleGeoRaster/deegreewcs:Table/text()";
317            String table = XMLTools.getRequiredNodeAsString( element, xpath, nsc );
318            xpath = "deegreewcs:OracleGeoRaster/deegreewcs:RDTTable/text()";
319            String rdtTable = XMLTools.getRequiredNodeAsString( element, xpath, nsc );
320            xpath = "deegreewcs:OracleGeoRaster/deegreewcs:Column/text()";
321            String column = XMLTools.getRequiredNodeAsString( element, xpath, nsc );
322            xpath = "deegreewcs:OracleGeoRaster/deegreewcs:Identification/text()";
323            String identification = XMLTools.getRequiredNodeAsString( element, xpath, nsc );
324            xpath = "deegreewcs:OracleGeoRaster/deegreewcs:Level/text()";
325            int level = XMLTools.getNodeAsInt( element, xpath, nsc, 1 );
326            resolution = new OracleGeoRasterResolution( min, max, ranges, jdbc, table, rdtTable, column, identification,
327                                                        level );
328            return resolution;
329        }
330    
331        /**
332         * creates a <tt>Shape</tt> object from the passed element
333         * 
334         * @param element
335         * @return created Shape
336         * @throws XMLParsingException
337         * @throws UnknownCRSException
338         */
339        private Shape getShape( Element element )
340                                throws XMLParsingException, UnknownCRSException {
341            String tilePoperty = XMLTools.getRequiredAttrValue( "tileProperty", null, element );
342            String directoryProperty = XMLTools.getRequiredAttrValue( "directoryProperty", null, element );
343            String srsName = XMLTools.getRequiredAttrValue( "srsName", null, element );
344            CoordinateSystem crs = CRSFactory.create( srsName );
345            String rootFileName = XMLTools.getStringValue( element );
346            rootFileName.trim();
347            XMLFragment xml = new XMLFragment();
348            xml.setRootElement( root );
349            xml.setSystemId( systemId );
350            java.io.File file = null;
351            try {
352                URL url = xml.resolve( rootFileName + ".shp" );
353                file = new java.io.File( url.toURI() );
354            } catch ( Exception e ) {
355                LOG.logError( e.getMessage(), e );
356            }
357            rootFileName = file.getAbsolutePath();
358            rootFileName = rootFileName.substring( 0, rootFileName.lastIndexOf( "." ) );
359            return new Shape( crs, rootFileName, tilePoperty, directoryProperty );
360        }
361    
362        /**
363         * creates a <tt>File</tt> object from the passed Element that describes the extensions and locations of the
364         * coverages assigned to a <tt>Resolution</tt>
365         * 
366         * @param element
367         * @return <tt>File</tt> object from the passed Element that describes the extensions and locations of the coverages
368         *         assigned to a <tt>Resolution</tt>
369         * @throws XMLParsingException
370         * @throws UnknownCRSException
371         */
372        private org.deegree.ogcwebservices.wcs.configuration.File getFile( Element element )
373                                throws XMLParsingException, InvalidGMLException, UnknownCRSException {
374            String name = XMLTools.getRequiredStringValue( "Name", DGRNS, element );
375            XMLFragment xml = new XMLFragment();
376            xml.setRootElement( element );
377            xml.setSystemId( systemId );
378            File file = new File( name );
379            try {
380                URL url = xml.resolve( name );
381                file = new java.io.File( url.toURI() );
382            } catch ( Exception e ) {
383                LOG.logError( e.getMessage(), e );
384                e.printStackTrace();
385            }
386            name = file.getAbsolutePath();
387    
388            Element elem = XMLTools.getRequiredChildElement( "Envelope", GMLNS, element );
389            Envelope envelope = GMLGeometryAdapter.wrapBox( elem, null );
390            String srs = XMLTools.getRequiredAttrValue( "srsName", null, elem );
391    
392            String[] tmp = StringTools.toArray( srs, "#", false );
393            // just a heuristic because it is not guarranteed that the URL
394            // in the srsName attribute can be solved
395            if ( srs.toLowerCase().indexOf( "epsg" ) > -1 ) {
396                srs = "EPSG:" + tmp[1];
397            } else {
398                srs = "CRS:" + tmp[1];
399            }
400            if ( tmp[1].equals( "0" ) ) {
401                srs = null;
402            }
403    
404            CoordinateSystem crs = CRSFactory.create( srs );
405            return new org.deegree.ogcwebservices.wcs.configuration.File( crs, name, envelope );
406        }
407    
408        /**
409         * creates a <tt>Directory</tt> object from the passed Elememt that describes the extensions and locations of the
410         * coverages assigned to a <tt>Resolution</tt>
411         * 
412         * @param element
413         * @return <tt>Directory</tt> object from the passed Elememt that describes the extensions and locations of the
414         *         coverages assigned to a <tt>Resolution</tt>
415         * @throws XMLParsingException
416         * @throws UnknownCRSException
417         */
418        private Directory getDirectory( Element element )
419                                throws XMLParsingException, InvalidGMLException, UnknownCRSException {
420            // get valid file extension for this directory
421            String temp = XMLTools.getRequiredAttrValue( "extensions", null, element );
422            String[] extensions = StringTools.toArray( temp, ",;", true );
423            // get the width and height (in pixels) af the tiles in this directory
424            temp = XMLTools.getRequiredAttrValue( "tileWidth", null, element );
425            double tileWidth = 0;
426            try {
427                tileWidth = Double.parseDouble( temp );
428            } catch ( Exception e ) {
429                throw new XMLParsingException( "tileWidth attribute isn't a number" );
430            }
431            double tileHeight = 0;
432            try {
433                tileHeight = Double.parseDouble( temp );
434            } catch ( Exception e ) {
435                throw new XMLParsingException( "tileHeight attribute isn't a number" );
436            }
437            // get the directroy name
438            String name = XMLTools.getRequiredStringValue( "Name", DGRNS, element );
439            XMLFragment xml = new XMLFragment();
440            xml.setRootElement( element );
441            xml.setSystemId( systemId );
442            try {
443                // resolve name if relative
444                name = xml.resolve( name ).toExternalForm();
445            } catch ( MalformedURLException e ) {
446                throw new XMLParsingException( "invalid file name/path: " + name );
447            }
448            // get the bounding envelope of all tiles in the directory
449            Element elem = XMLTools.getRequiredChildElement( "Envelope", GMLNS, element );
450            Envelope envelope = GMLGeometryAdapter.wrapBox( elem, null );
451            String srs = XMLTools.getRequiredAttrValue( "srsName", null, elem );
452            if ( srs != null ) {
453                String[] tmp = StringTools.toArray( srs, "#", false );
454                // just a heuristic because it is not guarranteed that the URL
455                // in the srsName attribute can be solved
456                if ( srs.toLowerCase().indexOf( "epsg" ) > -1 ) {
457                    srs = "EPSG:" + tmp[1];
458                } else {
459                    srs = "CRS:" + tmp[1];
460                }
461                if ( tmp[1].equals( "0" ) )
462                    srs = null;
463            }
464            CoordinateSystem crs = CRSFactory.create( srs );
465            return new GridDirectory( name, envelope, crs, extensions, tileWidth, tileHeight );
466        }
467    
468        /**
469         * creates an array of <tt>Ranges</tt> from the passed element list
470         * 
471         * @param el
472         * @return created array of <tt>Ranges</tt>
473         * @throws XMLParsingException
474         */
475        private Range[] getRanges( List<Node> el )
476                                throws XMLParsingException {
477            Range[] ranges = new Range[el.size()];
478            for ( int i = 0; i < ranges.length; i++ ) {
479                ranges[i] = getRange( (Element) el.get( i ) );
480            }
481            return ranges;
482        }
483    
484        /**
485         * creates a <tt>Range</tt> object from the passed element
486         * 
487         * @param element
488         * @return created <tt>Range</tt>
489         * @throws XMLParsingException
490         */
491        private Range getRange( Element element )
492                                throws XMLParsingException {
493            String name = XMLTools.getRequiredStringValue( "Name", DGRNS, element );
494            ElementList el = XMLTools.getChildElements( "Axis", DGRNS, element );
495            Axis[] axis = getAxis( el );
496            return new Range( name, axis );
497        }
498    
499        /**
500         * creates an array of <tt>Axis</tt> objects from the passed element list
501         * 
502         * @param el
503         * @return created array of <tt>Axis</tt>
504         * @throws XMLParsingException
505         */
506        private Axis[] getAxis( ElementList el )
507                                throws XMLParsingException {
508            Axis[] axis = new Axis[el.getLength()];
509            for ( int i = 0; i < axis.length; i++ ) {
510                axis[i] = getAxis( el.item( i ) );
511            }
512            return axis;
513        }
514    
515        /**
516         * creates an <tt>Axis</tt> object from the passed element. The <tt>Interval</tt> included in the <tt>Axis</tt>
517         * doesn't have a resolution because it isn't required.
518         * 
519         * @param element
520         * @return created <tt>Axis</tt>
521         * @throws XMLParsingException
522         */
523        private Axis getAxis( Element element )
524                                throws XMLParsingException {
525            try {
526                String name = XMLTools.getRequiredStringValue( "Name", DGRNS, element );
527                Element elem = XMLTools.getRequiredChildElement( "Interval", DGRNS, element );
528                String tmp = XMLTools.getRequiredStringValue( "min", DGRNS, elem );
529                TypedLiteral min = new TypedLiteral( tmp, new URI( "xs:double" ) );
530                tmp = XMLTools.getRequiredStringValue( "max", DGRNS, elem );
531                TypedLiteral max = new TypedLiteral( tmp, new URI( "xs:double" ) );
532                Interval interval = new Interval( min, max, null, null, null );
533                return new Axis( name, interval );
534            } catch ( URISyntaxException e ) {
535                LOG.logError( e.getMessage(), e );
536                throw new XMLParsingException( e.getMessage() );
537            }
538        }
539    
540    }