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 }