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 }