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 }