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 }