001    // $HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/owscommon/OWSCommonCapabilitiesDocument.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.owscommon;
045    
046    import java.net.MalformedURLException;
047    import java.net.URI;
048    import java.net.URISyntaxException;
049    import java.net.URL;
050    import java.util.List;
051    import java.util.Map;
052    
053    import org.deegree.datatypes.Code;
054    import org.deegree.datatypes.xlink.SimpleLink;
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.XMLParsingException;
060    import org.deegree.framework.xml.XMLTools;
061    import org.deegree.model.metadata.iso19115.Address;
062    import org.deegree.model.metadata.iso19115.ContactInfo;
063    import org.deegree.model.metadata.iso19115.Keywords;
064    import org.deegree.model.metadata.iso19115.OnlineResource;
065    import org.deegree.model.metadata.iso19115.Phone;
066    import org.deegree.model.metadata.iso19115.TypeCode;
067    import org.deegree.model.spatialschema.Envelope;
068    import org.deegree.model.spatialschema.GeometryFactory;
069    import org.deegree.ogcbase.CommonNamespaces;
070    import org.deegree.ogcwebservices.getcapabilities.DCPType;
071    import org.deegree.ogcwebservices.getcapabilities.HTTP;
072    import org.deegree.ogcwebservices.getcapabilities.OGCCapabilitiesDocument;
073    import org.deegree.ogcwebservices.getcapabilities.Operation;
074    import org.deegree.ogcwebservices.getcapabilities.Protocol;
075    import org.deegree.ogcwebservices.getcapabilities.ServiceIdentification;
076    import org.deegree.ogcwebservices.getcapabilities.ServiceProvider;
077    import org.w3c.dom.Element;
078    import org.w3c.dom.Node;
079    
080    /**
081     * Represents a configuration document for an OGC-Webservice according to the
082     * <code>OWS Common Implementation Specification 0.3</code>.
083     * <p>
084     * It consists of the following elements: <table border="1">
085     * <tr>
086     * <th>Name</th>
087     * <th>Function</th>
088     * </tr>
089     * <tr>
090     * <td>ServiceIdentification</td>
091     * <td>corresponds to and expands the SV_ServiceIdentification class in ISO 19119</td>
092     * </tr>
093     * <tr>
094     * <td>ServiceProvider</td>
095     * <td>corresponds to and expands the SV_ServiceProvider class in ISO 19119 </td>
096     * </tr>
097     * <tr>
098     * <td>OperationsMetadata</td>
099     * <td>contains set of Operation elements that each corresponds to and expand the
100     * SV_OperationsMetadata class in ISO 19119</td>
101     * </tr>
102     * <tr>
103     * <td>Contents</td>
104     * <td>whenever relevant, contains set of elements that each corresponds to the
105     * MD_DataIdentification class in ISO 19119 and 19115</td>
106     * </tr>
107     * </table>
108     * 
109     * @author <a href="mailto:mschneider@lat-lon.de">Markus Schneider </a>
110     * @author last edited by: $Author: apoth $
111     * 
112     * @version $Revision: 9501 $, $Date: 2008-01-10 10:41:39 +0100 (Do, 10 Jan 2008) $
113     */
114    public abstract class OWSCommonCapabilitiesDocument extends OGCCapabilitiesDocument {
115    
116        private static final ILogger LOG = LoggerFactory.getLogger( OWSCommonCapabilitiesDocument.class );
117    
118        public final static String ALL_NAME = "All";
119    
120        public final static String SERVICE_IDENTIFICATION_NAME = "ServiceIdentification";
121    
122        public final static String SERVICE_PROVIDER_NAME = "ServiceProvider";
123    
124        public final static String OPERATIONS_METADATA_NAME = "OperationsMetadata";
125    
126        public final static String CONTENTS_NAME = "Contents";
127    
128        protected static final URI OWSNS = CommonNamespaces.OWSNS;
129    
130        protected static final URI OGCNS = CommonNamespaces.OGCNS;
131    
132        /**
133         * Returns the class representation for the <code>ServiceProvider</code> section of the
134         * document.
135         * 
136         * @return class representation for the <code>ServiceProvider</code> section
137         * @throws XMLParsingException
138         */
139        public ServiceProvider getServiceProvider()
140                                throws XMLParsingException {
141    
142            Element element = XMLTools.getRequiredChildElement( "ServiceProvider", OWSNS, getRootElement() );
143    
144            // 'ProviderName' element (optional, default value: 'deegree')
145            String providerName = XMLTools.getStringValue( "ProviderName", OWSNS, element, "deegree" );
146    
147            // 'ProviderSite' element (optional)
148            Element providerSiteElement = XMLTools.getChildElement( "ProviderSite", OWSNS, element );
149            SimpleLink providerSite = null;
150            if ( providerSiteElement != null ) {
151                providerSite = parseSimpleLink( providerSiteElement );
152            }
153    
154            // 'ServiceContact' element (mandatory)
155            Element serviceContactElement = XMLTools.getRequiredChildElement( "ServiceContact", OWSNS, element );
156    
157            // 'IndividualName' element (optional)
158            String individualName = XMLTools.getStringValue( "IndividualName", OWSNS, serviceContactElement, null );
159    
160            // 'PositionName' element (optional)
161            String positionName = XMLTools.getStringValue( "PositionName", OWSNS, serviceContactElement, null );
162    
163            // 'ContactInfo' element (optional)
164            ContactInfo contactInfo = null;
165            Element contactInfoElement = XMLTools.getChildElement( "ContactInfo", OWSNS, serviceContactElement );
166            if ( contactInfoElement != null ) {
167                contactInfo = getContactInfo( contactInfoElement );
168            }
169            TypeCode role = null;
170            Element roleElement = (Element) XMLTools.getNode( serviceContactElement, "ows:Role", nsContext );
171            if ( roleElement != null ) {
172                role = getCodeType( roleElement );
173            }
174            ServiceProvider serviceProvider = new ServiceProvider( providerName, providerSite, individualName,
175                                                                   positionName, contactInfo, role );
176    
177            return serviceProvider;
178        }
179    
180        /**
181         * Returns the class representation for the <code>ServiceIdentification</code> section of the
182         * document.
183         * 
184         * @return class representation for the <code>ServiceIdentification</code> section
185         * @throws XMLParsingException
186         */
187        public ServiceIdentification getServiceIdentification()
188                                throws XMLParsingException {
189    
190            Element element = XMLTools.getRequiredChildElement( "ServiceIdentification", OWSNS, getRootElement() );
191    
192            // 'ServiceType' element (mandatory)
193            Element serviceTypeElement = XMLTools.getRequiredChildElement( "ServiceType", OWSNS, element );
194            Code serviceType = null;
195            try {
196                String codeSpace = XMLTools.getAttrValue( serviceTypeElement, OWSNS, "codeSpace", null );
197                URI uri = codeSpace != null ? new URI( codeSpace ) : null;
198                serviceType = new Code( XMLTools.getStringValue( serviceTypeElement ), uri );
199            } catch ( URISyntaxException e ) {
200                throw new XMLParsingException( "Given value '"
201                                               + XMLTools.getAttrValue( serviceTypeElement, OWSNS, "codeSpace", null )
202                                               + "' in attribute 'codeSpace' of element 'ServiceType' " + "(namespace: '"
203                                               + OWSNS + "') is not a valid URI." );
204            }
205    
206            // 'ServiceTypeVersion' elements (mandatory)
207            String[] serviceTypeVersions = XMLTools.getRequiredNodeAsStrings( element, "ows:ServiceTypeVersion", nsContext,
208                                                                              ",;" );
209    
210            // 'Title' element (mandatory)
211            String title = XMLTools.getRequiredStringValue( "Title", OWSNS, element );
212    
213            // 'Abstract' element (optional)
214            String serviceAbstract = XMLTools.getRequiredStringValue( "Abstract", OWSNS, element );
215    
216            // 'Keywords' elements (optional)
217            List keywordsList = XMLTools.getNodes( element, "ows:Keywords", nsContext );
218            Keywords[] keywords = getKeywords( keywordsList );
219    
220            // 'Fees' element (optional)
221            String fees = XMLTools.getStringValue( "Fees", OWSNS, element, null );
222    
223            // 'AccessConstraints' elements (optional)
224            String accessConstraints[] = XMLTools.getNodesAsStrings( element, "ows:AccessConstraints", nsContext );
225    
226            ServiceIdentification serviceIdentification = new ServiceIdentification( serviceType, serviceTypeVersions,
227                                                                                     title, serviceAbstract, keywords,
228                                                                                     fees, accessConstraints );
229    
230            return serviceIdentification;
231        }
232    
233        /**
234         * Creates a <code>Keywords</code> instance from the given element of type
235         * <code>ows:KeywordsType</code>.
236         * 
237         * NOTE: This method is redefined here (it is already defined in <code>OGCDocument</code>),
238         * because the spelling of the first letter ('K') changed in the OWS Common Implementation
239         * Specification 0.2 from lowercase to uppercase.
240         * 
241         * @param element
242         * @return created <code>Keywords</code>
243         * @throws XMLParsingException
244         */
245        protected Keywords getKeywords( Element element )
246                                throws XMLParsingException {
247            TypeCode codeType = null;
248            Element codeTypeElement = (Element) XMLTools.getNode( element, "ows:Type", nsContext );
249            if ( codeTypeElement != null ) {
250                codeType = getCodeType( codeTypeElement );
251            }
252            Keywords keywords = new Keywords( XMLTools.getNodesAsStrings( element, "ows:Keyword/text()", nsContext ), null,
253                                              codeType );
254            return keywords;
255        }
256    
257        /**
258         * Creates an array of <code> Keywords </code> instances from the passed list of elements of
259         * type <code> ows:KeywordsType </code>.
260         * 
261         * This may appear to be pretty superfluous (as one <code> ows:KeywordsType
262         * </code> can hold
263         * several elements of type <code> ows:Keyword
264         * </code>.
265         * 
266         * @param nl
267         *            may be null
268         * @return created array of <code> Keywords </code>, null if <code>NodeList</code> constains
269         *         zero elements
270         * @throws XMLParsingException
271         */
272        public Keywords[] getKeywords( List nl )
273                                throws XMLParsingException {
274            Keywords[] kws = null;
275            if ( nl.size() > 0 ) {
276                kws = new Keywords[nl.size()];
277                for ( int i = 0; i < kws.length; i++ ) {
278                    kws[i] = getKeywords( (Element) nl.get( i ) );
279                }
280            }
281            return kws;
282        }
283    
284        /**
285         * Creates a <code>DCPType</code> object from the passed <code>DCP</code> element.
286         * <p>
287         * NOTE: Currently the <code>OnlineResources</code> included in the <code>DCPType</code> are
288         * just stored as simple <code>URLs</code> (not as <code>OnLineResource</code> instances)!
289         * <p>
290         * NOTE: In an <code>OGCStandardCapabilitiesDocument</code> the <code>XLinks</code> (the
291         * <code>URLs</code>) are stored in separate elements (<code>OnlineResource</code>), in
292         * an <code>OGCCommonCapabilitiesDocument</code> they are the
293         * <code>Get<code>/<code>Post</code> elements themselves.
294         * 
295         * @param element
296         * @return created <code>DCPType</code>
297         * @throws XMLParsingException
298         * @see org.deegree.ogcwebservices.getcapabilities.OGCStandardCapabilities
299         */
300        protected DCPType getDCP( Element element )
301                                throws XMLParsingException {
302    
303            DCPType dcpType = null;
304            try {
305                Element elem = (Element) XMLTools.getRequiredNode( element, "ows:HTTP", nsContext );
306                List nl = XMLTools.getNodes( elem, "ows:Get", nsContext );
307    
308                URL[] get = new URL[nl.size()];
309                for ( int i = 0; i < get.length; i++ ) {
310                    String s = XMLTools.getNodeAsString( (Node) nl.get( i ), "./@xlink:href", nsContext, null );
311                    if ( s == null ) {
312                        s = XMLTools.getRequiredNodeAsString( (Node) nl.get( i ), "./ows:OnlineResource/@xlink:href",
313                                                              nsContext );
314                    }
315                    get[i] = new URL( s );
316                }
317                nl = XMLTools.getNodes( elem, "ows:Post", nsContext );
318    
319                URL[] post = new URL[nl.size()];
320                for ( int i = 0; i < post.length; i++ ) {
321                    String s = XMLTools.getNodeAsString( (Node) nl.get( i ), "./@xlink:href", nsContext, null );
322                    if ( s == null ) {
323                        s = XMLTools.getRequiredNodeAsString( (Node) nl.get( i ), "./ows:OnlineResource/@xlink:href",
324                                                              nsContext );
325                    }
326                    post[i] = new URL( s );
327                }
328                Protocol protocol = new HTTP( get, post );
329                dcpType = new DCPType( protocol );
330            } catch ( MalformedURLException e ) {
331                throw new XMLParsingException( "Couldn't parse DCPType onlineresource URL about: "
332                                               + StringTools.stackTraceToString( e ) );
333            }
334    
335            return dcpType;
336        }
337    
338        /**
339         * Creates an array of <code>DCPType</code> objects from the passed element list.
340         * <p>
341         * NOTE: Currently the <code>OnlineResources</code> included in the <code>DCPType</code> are
342         * just stored as simple <code>URLs</code> (not as <code>OnLineResource</code> instances)!
343         * 
344         * @param el
345         * @return array of <code>DCPType</code>
346         * @throws XMLParsingException
347         */
348        protected DCPType[] getDCPs( List el )
349                                throws XMLParsingException {
350    
351            DCPType[] dcpTypes = new DCPType[el.size()];
352            for ( int i = 0; i < dcpTypes.length; i++ ) {
353                dcpTypes[i] = getDCP( (Element) el.get( i ) );
354            }
355    
356            return dcpTypes;
357        }
358    
359        /**
360         * Creates a class representation of an <code>ows:Operation</code>- element.
361         * 
362         * @param name
363         * @param isMandatory
364         * @param operations
365         * @return operation
366         * @throws XMLParsingException
367         */
368        protected Operation getOperation( String name, boolean isMandatory, Map operations )
369                                throws XMLParsingException {
370    
371            Operation operation = null;
372            Element operationElement = (Element) operations.get( name );
373            if ( operationElement == null ) {
374                if ( isMandatory ) {
375                    throw new XMLParsingException( "Mandatory operation '" + name
376                                                   + "' not defined in 'OperationsMetadata'-section." );
377                }
378            } else {
379                // "ows:Parameter"-elements
380                ElementList parameterElements = XMLTools.getChildElements( "Parameter", OWSNS, operationElement );
381                OWSDomainType[] parameters = new OWSDomainType[parameterElements.getLength()];
382                for ( int i = 0; i < parameters.length; i++ ) {
383                    parameters[i] = getOWSDomainType( name, parameterElements.item( i ) );
384                }
385                DCPType[] dcps = getDCPs( XMLTools.getRequiredNodes( operationElement, "ows:DCP", nsContext ) );
386                operation = new Operation( name, dcps, parameters );
387    
388            }
389    
390            return operation;
391        }
392    
393        /**
394         * 
395         * @param root
396         * @return constraints as array of OWSDomainType
397         * @throws XMLParsingException
398         */
399        protected OWSDomainType[] getContraints( Element root )
400                                throws XMLParsingException {
401    
402            OWSDomainType[] contraints = null;
403            // "ows:Contraint"-elements
404            ElementList contraintElements = XMLTools.getChildElements( "Constraint", OWSNS, root );
405            contraints = new OWSDomainType[contraintElements.getLength()];
406            for ( int i = 0; i < contraints.length; i++ ) {
407                contraints[i] = getOWSDomainType( null, contraintElements.item( i ) );
408            }
409    
410            return contraints;
411        }
412    
413        /**
414         * Creates a class representation of an element of type <code>ows:DomainType</code>.
415         * 
416         * @param element
417         * @return domainType
418         * @throws XMLParsingException
419         */
420        protected OWSDomainType getOWSDomainType( String opname, Element element )
421                                throws XMLParsingException {
422    
423            // "name"-attribute
424            String name = XMLTools.getRequiredNodeAsString( element, "@name", nsContext );
425    
426            // "ows:Value"-elements
427            String[] values = XMLTools.getNodesAsStrings( element, "ows:Value/text()", nsContext );
428            if ( values.length < 1 ) {
429                throw new XMLParsingException( "At least one 'ows:Value'-element must be defined in each "
430                                               + "element of type 'ows:DomainType'." );
431            }
432    
433            // TODO: "ows:Metadata"-elements
434            OWSDomainType domainType = new OWSDomainType( name, values, null );
435    
436            return domainType;
437        }
438    
439        /**
440         * Creates a class representation of an element of type <code>ows:CodeType</code>.
441         * 
442         * @param element
443         *            an ows:CodeType element
444         * @return the TypeCode (which is defined as something like a dictionary, thesaurus etc.)
445         * @throws XMLParsingException
446         */
447        protected TypeCode getCodeType( Element element )
448                                throws XMLParsingException {
449    
450            String code = XMLTools.getRequiredNodeAsString( element, "text()", nsContext );
451    
452            URI codeSpace = null;
453            String codeSpaceString = XMLTools.getNodeAsString( element, "@codeSpace", nsContext, null );
454            if ( codeSpaceString != null ) {
455                try {
456                    codeSpace = new URI( codeSpaceString );
457                } catch ( URISyntaxException e ) {
458                    throw new XMLParsingException( "'" + codeSpaceString + "' does not denote a valid URI in: "
459                                                   + e.getMessage() );
460                }
461            }
462            return new TypeCode( code, codeSpace );
463        }
464    
465        /**
466         * Creates a <code>ContactInfo</code> object from the given element of type
467         * <code>ows:ContactInfoType</code>.
468         * 
469         * @param element
470         * @return ContactInfo
471         * @throws XMLParsingException
472         */
473        private ContactInfo getContactInfo( Element element )
474                                throws XMLParsingException {
475    
476            // 'Phone' element (optional)
477            Phone phone = null;
478            Element phoneElement = XMLTools.getChildElement( "Phone", OWSNS, element );
479            if ( phoneElement != null ) {
480                phone = parsePhone( phoneElement, OWSNS );
481            }
482    
483            // 'Address' element (optional)
484            Address address = null;
485            Element addressElement = XMLTools.getChildElement( "Address", OWSNS, element );
486            if ( addressElement != null ) {
487                address = parseAddress( addressElement, OWSNS );
488            }
489    
490            // 'OnlineResource' element (optional)
491            OnlineResource onlineResource = null;
492            Element onlineResourceElement = XMLTools.getChildElement( "OnlineResource", OWSNS, element );
493            if ( onlineResourceElement != null ) {
494                onlineResource = parseOnLineResource( onlineResourceElement );
495            }
496    
497            String hoursOfService = XMLTools.getNodeAsString( element, "ows:HoursOfService/text()", nsContext, null );
498            String contactInstructions = XMLTools.getNodeAsString( element, "ows:ContactInstructions/text()", nsContext,
499                                                                   null );
500    
501            return new ContactInfo( address, contactInstructions, hoursOfService, onlineResource, phone );
502        }
503    
504        /**
505         * Creates an <code>Envelope</code> object from the given element of type
506         * <code>ows:WGS84BoundingBoxType</code>.
507         * 
508         * @param element
509         * @return an <code>Envelope</code> object
510         * @throws XMLParsingException
511         */
512        protected Envelope getWGS84BoundingBoxType( Element element )
513                                throws XMLParsingException {
514            double[] lowerCorner = XMLTools.getRequiredNodeAsDoubles( element, "ows:LowerCorner/text()", nsContext, " " );
515            if ( lowerCorner.length != 2 ) {
516                throw new XMLParsingException( "Element 'ows:LowerCorner' must contain exactly two double values." );
517            }
518            double[] upperCorner = XMLTools.getRequiredNodeAsDoubles( element, "ows:UpperCorner/text()", nsContext, " " );
519            if ( upperCorner.length != 2 ) {
520                throw new XMLParsingException( "Element 'ows:UpperCorner' must contain exactly two double values." );
521            }
522            return GeometryFactory.createEnvelope( lowerCorner[0], lowerCorner[1], upperCorner[0], upperCorner[1], null );
523        }
524    }