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