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