001 // $HeadURL: svn+ssh://jwilden@svn.wald.intevation.org/deegree/base/branches/2.5_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 }