001 // $HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/owscommon/OWSCommonCapabilitiesDocument.java $
002 /*---------------- FILE HEADER ------------------------------------------
003
004 This file is part of deegree.
005 Copyright (C) 2001-2007 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: 6953 $, $Date: 2007-05-08 20:52:18 +0200 (Di, 08 Mai 2007) $
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 protected 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 + "' not defined in "
376 + "'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 * Creates a class representation of an element of type <code>ows:DomainType</code>.
395 *
396 * @param operation
397 * @param element
398 * @return domainType
399 * @throws XMLParsingException
400 */
401 protected OWSDomainType getOWSDomainType( String operation, Element element )
402 throws XMLParsingException {
403
404 // "name"-attribute
405 String name = XMLTools.getRequiredNodeAsString( element, "@name", nsContext );
406
407 // "ows:Value"-elements
408 String[] values = XMLTools.getNodesAsStrings( element, "ows:Value/text()", nsContext );
409 if ( values.length < 1 ) {
410 throw new XMLParsingException( "At least one 'ows:Value'-element must be defined in each "
411 + "element of type 'ows:DomainType'." );
412 }
413
414 // TODO: "ows:Metadata"-elements
415 OWSDomainType domainType = new OWSDomainType( name, values, null );
416
417 return domainType;
418 }
419
420 /**
421 * Creates a class representation of an element of type <code>ows:CodeType</code>.
422 *
423 * @param element
424 * an ows:CodeType element
425 * @return the TypeCode (which is defined as something like a dictionary, thesaurus etc.)
426 * @throws XMLParsingException
427 */
428 protected TypeCode getCodeType( Element element )
429 throws XMLParsingException {
430
431 String code = XMLTools.getRequiredNodeAsString( element, "text()", nsContext );
432
433 URI codeSpace = null;
434 String codeSpaceString = XMLTools.getNodeAsString( element, "@codeSpace", nsContext, null );
435 if ( codeSpaceString != null ) {
436 try {
437 codeSpace = new URI( codeSpaceString );
438 } catch ( URISyntaxException e ) {
439 throw new XMLParsingException( "'" + codeSpaceString + "' does not denote a valid URI in: "
440 + e.getMessage() );
441 }
442 }
443 return new TypeCode( code, codeSpace );
444 }
445
446 /**
447 * Creates a <code>ContactInfo</code> object from the given element of type
448 * <code>ows:ContactInfoType</code>.
449 *
450 * @param element
451 * @return ContactInfo
452 * @throws XMLParsingException
453 */
454 private ContactInfo getContactInfo( Element element )
455 throws XMLParsingException {
456
457 // 'Phone' element (optional)
458 Phone phone = null;
459 Element phoneElement = XMLTools.getChildElement( "Phone", OWSNS, element );
460 if ( phoneElement != null ) {
461 phone = parsePhone( phoneElement, OWSNS );
462 }
463
464 // 'Address' element (optional)
465 Address address = null;
466 Element addressElement = XMLTools.getChildElement( "Address", OWSNS, element );
467 if ( addressElement != null ) {
468 address = parseAddress( addressElement, OWSNS );
469 }
470
471 // 'OnlineResource' element (optional)
472 OnlineResource onlineResource = null;
473 Element onlineResourceElement = XMLTools.getChildElement( "OnlineResource", OWSNS, element );
474 if ( onlineResourceElement != null ) {
475 onlineResource = parseOnLineResource( onlineResourceElement );
476 }
477
478 String hoursOfService = XMLTools.getNodeAsString( element, "ows:HoursOfService/text()", nsContext, null );
479 String contactInstructions = XMLTools.getNodeAsString( element, "ows:ContactInstructions/text()", nsContext,
480 null );
481
482 return new ContactInfo( address, contactInstructions, hoursOfService, onlineResource, phone );
483 }
484
485 /**
486 * Creates an <code>Envelope</code> object from the given element of type
487 * <code>ows:WGS84BoundingBoxType</code>.
488 *
489 * @param element
490 * @return an <code>Envelope</code> object
491 * @throws XMLParsingException
492 */
493 protected Envelope getWGS84BoundingBoxType( Element element )
494 throws XMLParsingException {
495 double[] lowerCorner = XMLTools.getRequiredNodeAsDoubles( element, "ows:LowerCorner/text()", nsContext, " " );
496 if ( lowerCorner.length != 2 ) {
497 throw new XMLParsingException( "Element 'ows:LowerCorner' must contain exactly two double values." );
498 }
499 double[] upperCorner = XMLTools.getRequiredNodeAsDoubles( element, "ows:UpperCorner/text()", nsContext, " " );
500 if ( upperCorner.length != 2 ) {
501 throw new XMLParsingException( "Element 'ows:UpperCorner' must contain exactly two double values." );
502 }
503 return GeometryFactory.createEnvelope( lowerCorner[0], lowerCorner[1], upperCorner[0], upperCorner[1], null );
504 }
505 }