001 //$HeadURL: svn+ssh://developername@svn.wald.intevation.org/deegree/base/trunk/src/org/deegree/ogcwebservices/wms/capabilities/WMSCapabilitiesDocument.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.ogcwebservices.wms.capabilities; 045 046 import java.io.IOException; 047 import java.net.MalformedURLException; 048 import java.net.URI; 049 import java.net.URISyntaxException; 050 import java.net.URL; 051 import java.util.ArrayList; 052 import java.util.Arrays; 053 import java.util.Date; 054 import java.util.List; 055 056 import org.deegree.datatypes.Code; 057 import org.deegree.datatypes.QualifiedName; 058 import org.deegree.datatypes.values.TypedLiteral; 059 import org.deegree.framework.log.ILogger; 060 import org.deegree.framework.log.LoggerFactory; 061 import org.deegree.framework.util.StringTools; 062 import org.deegree.framework.xml.XMLParsingException; 063 import org.deegree.framework.xml.XMLTools; 064 import org.deegree.i18n.Messages; 065 import org.deegree.model.crs.CRSFactory; 066 import org.deegree.model.crs.CoordinateSystem; 067 import org.deegree.model.crs.UnknownCRSException; 068 import org.deegree.model.metadata.iso19115.Constraints; 069 import org.deegree.model.metadata.iso19115.Keywords; 070 import org.deegree.model.metadata.iso19115.Linkage; 071 import org.deegree.model.metadata.iso19115.OnlineResource; 072 import org.deegree.model.spatialschema.Envelope; 073 import org.deegree.model.spatialschema.GeometryFactory; 074 import org.deegree.model.spatialschema.Position; 075 import org.deegree.ogcwebservices.getcapabilities.InvalidCapabilitiesException; 076 import org.deegree.ogcwebservices.getcapabilities.OGCCapabilities; 077 import org.deegree.owscommon_new.DCP; 078 import org.deegree.owscommon_new.DomainType; 079 import org.deegree.owscommon_new.HTTP; 080 import org.deegree.owscommon_new.Operation; 081 import org.deegree.owscommon_new.OperationsMetadata; 082 import org.deegree.owscommon_new.Parameter; 083 import org.deegree.owscommon_new.ServiceIdentification; 084 import org.deegree.owscommon_new.ServiceProvider; 085 import org.w3c.dom.Element; 086 import org.w3c.dom.Node; 087 import org.xml.sax.SAXException; 088 089 /** 090 * <code>WMSCapabilitiesDocument</code> is the parser class for WMS capabilities documents that 091 * uses the new OWS common classes to encapsulate the data. 092 * 093 * @author <a href="mailto:schmitz@lat-lon.de">Andreas Schmitz</a> 094 * @author last edited by: $Author: aschmitz $ 095 * 096 * @version 2.0, $Revision: 8220 $, $Date: 2007-09-28 15:26:08 +0200 (Fr, 28 Sep 2007) $ 097 * 098 * @since 2.0 099 */ 100 101 public class WMSCapabilitiesDocument_1_0_0 extends WMSCapabilitiesDocument { 102 103 private static final long serialVersionUID = 4689978960047737035L; 104 105 private static final String XML_TEMPLATE = "WMSCapabilitiesTemplate.xml"; 106 107 private static final ILogger LOG = LoggerFactory.getLogger( WMSCapabilitiesDocument_1_0_0.class ); 108 109 /** 110 * Creates a skeleton capabilities document that contains the mandatory elements only. 111 * 112 * @throws IOException 113 * @throws SAXException 114 */ 115 @Override 116 public void createEmptyDocument() 117 throws IOException, SAXException { 118 119 URL url = WMSCapabilitiesDocument_1_0_0.class.getResource( XML_TEMPLATE ); 120 if ( url == null ) { 121 throw new IOException( "The resource '" + XML_TEMPLATE + " could not be found." ); 122 } 123 load( url ); 124 } 125 126 /** 127 * 128 * @param elem 129 * @return array of supported exception formats 130 * @throws XMLParsingException 131 */ 132 @Override 133 protected List<String> parseExceptionFormats( Element elem ) 134 throws XMLParsingException { 135 List<Node> nodes = XMLTools.getRequiredNodes( elem, "Format", nsContext ); 136 String[] formats = new String[nodes.size()]; 137 for ( int i = 0; i < formats.length; i++ ) { 138 formats[i] = nodes.get( i ).getLocalName(); 139 } 140 return Arrays.asList( formats ); 141 } 142 143 /** 144 * Creates a class representation of the document. 145 * 146 * @return class representation of the configuration document 147 * @throws InvalidCapabilitiesException 148 */ 149 @Override 150 public OGCCapabilities parseCapabilities() 151 throws InvalidCapabilitiesException { 152 ServiceIdentification serviceIdentification = null; 153 ServiceProvider serviceProvider = null; 154 OperationsMetadata metadata = null; 155 Layer layer = null; 156 List<String> exceptions; 157 158 String updateSeq = parseUpdateSequence(); 159 try { 160 serviceIdentification = parseServiceIdentification(); 161 metadata = parseOperationsMetadata(); 162 163 Element exceptionElement = XMLTools.getRequiredElement( getRootElement(), "Capability/Exception", nsContext ); 164 exceptions = parseExceptionFormats( exceptionElement ); 165 166 Element layerElem = XMLTools.getRequiredElement( getRootElement(), "./Capability/Layer", nsContext ); 167 layer = parseLayers( layerElem, null, null ); 168 } catch ( XMLParsingException e ) { 169 LOG.logError( e.getMessage(), e ); 170 throw new InvalidCapabilitiesException( e.getMessage() ); 171 } catch ( UnknownCRSException e ) { 172 LOG.logError( e.getMessage(), e ); 173 throw new InvalidCapabilitiesException( e.getMessage() ); 174 } 175 176 return new WMSCapabilities_1_0_0( updateSeq, serviceIdentification, serviceProvider, metadata, layer, 177 exceptions ); 178 179 } 180 181 /** 182 * returns the services indentification read from the WMS capabilities service section 183 * 184 * @throws XMLParsingException 185 */ 186 @Override 187 protected ServiceIdentification parseServiceIdentification() 188 throws XMLParsingException { 189 String name = XMLTools.getNodeAsString( getRootElement(), "./Service/Name", nsContext, null ); 190 String title = XMLTools.getNodeAsString( getRootElement(), "./Service/Title", nsContext, name ); 191 String serviceAbstract = XMLTools.getNodeAsString( getRootElement(), "./Service/Abstract", nsContext, null ); 192 193 String tmp = XMLTools.getNodeAsString( getRootElement(), "./Service/Keywords", nsContext, "" ); 194 String[] kw = StringTools.toArray( tmp, " ", false ); 195 196 Keywords[] keywordArray = new Keywords[] { new Keywords( kw ) }; 197 List<Keywords> keywords = Arrays.asList( keywordArray ); 198 199 String fees = XMLTools.getNodeAsString( getRootElement(), "./Service/Fees", nsContext, null ); 200 201 List<Constraints> accessConstraints = new ArrayList<Constraints>(); 202 203 String constraints = XMLTools.getNodeAsString( getRootElement(), "./Service/AccessConstraints", nsContext, null ); 204 205 if ( constraints != null ) { 206 List<String> limits = new ArrayList<String>(); 207 limits.add( constraints ); 208 accessConstraints.add( new Constraints( fees, null, null, null, limits, null, null, null ) ); 209 } 210 211 List<String> versions = new ArrayList<String>(); 212 versions.add( "1.0.0" ); 213 214 return new ServiceIdentification( new Code( "OGC:WMS" ), versions, title, null, 215 new Date( System.currentTimeMillis() ), title, serviceAbstract, keywords, 216 accessConstraints ); 217 218 } 219 220 /** 221 * returns the services capabilitiy read from the WMS capabilities file 222 * 223 * @return the operations metadata 224 * @throws XMLParsingException 225 */ 226 @Override 227 protected OperationsMetadata parseOperationsMetadata() 228 throws XMLParsingException { 229 230 Node opNode = XMLTools.getRequiredNode( getRootElement(), "./Capability/Request/Capabilities", nsContext ); 231 232 Operation getCapa = parseOperation( opNode ); 233 234 opNode = XMLTools.getRequiredNode( getRootElement(), "./Capability/Request/Map", nsContext ); 235 236 Operation getMap = parseOperation( opNode ); 237 238 Operation getFI = null; 239 opNode = XMLTools.getNode( getRootElement(), "./Capability/Request/FeatureInfo", nsContext ); 240 if ( opNode != null ) { 241 getFI = parseOperation( opNode ); 242 } 243 244 List<Operation> operations = new ArrayList<Operation>(); 245 if ( getCapa != null ) 246 operations.add( getCapa ); 247 if ( getMap != null ) 248 operations.add( getMap ); 249 if ( getFI != null ) 250 operations.add( getFI ); 251 252 return new OperationsMetadata( null, null, operations, null ); 253 254 } 255 256 /** 257 * Creates an <tt>Operation</tt>-instance according to the contents of the DOM-subtree 258 * starting at the given <tt>Node</tt>. 259 * <p> 260 * Notice: operation to be parsed must be operations in sense of WMS 1.0.0 - 1.3.0 and not as 261 * defined in OWSCommons. But the method will return an OWSCommon Operation which encapsulates 262 * parsed WMS operation 263 * <p> 264 * 265 * @param element 266 * the <tt>Element</tt> that describes an <tt>Operation</tt> 267 * @throws XMLParsingException 268 * if a syntactic or semantic error in the DOM-subtree is encountered 269 * @return the constructed <tt>Operation</tt>-instance 270 */ 271 @Override 272 protected Operation parseOperation( Node node ) 273 throws XMLParsingException { 274 // use node name as name of the Operation to be defined 275 String name = node.getNodeName(); 276 if ( name.equals( "Capabilities" ) ) { 277 name = "GetCapabilities"; 278 } else if ( name.equals( "Map" ) ) { 279 name = "GetMap"; 280 } else if ( name.equals( "FeatureInfo" ) ) { 281 name = "GetFeatureInfo"; 282 } 283 284 List<Node> nodes = XMLTools.getRequiredNodes( node, "./Format", nsContext ); 285 286 List<TypedLiteral> values = new ArrayList<TypedLiteral>(); 287 288 URI stringURI = null; 289 try { 290 stringURI = new URI( null, "String", null ); 291 } catch ( URISyntaxException e ) { 292 // cannot happen, why do I have to catch this? 293 } 294 295 for ( Node str : nodes ) 296 values.add( new TypedLiteral( str.getLocalName(), stringURI ) ); 297 298 DomainType owsDomainType = new DomainType( false, true, null, 0, new QualifiedName( "Format" ), values, null, 299 null, false, null, false, null, null, null, null ); 300 List<Parameter> parameters = new ArrayList<Parameter>(); 301 parameters.add( owsDomainType ); 302 303 List<Element> nl = XMLTools.getRequiredElements( node, "./DCPType", nsContext ); 304 List<DCP> dcps = new ArrayList<DCP>(); 305 306 for ( Element element : nl ) { 307 dcps.add( parseDCP( element ) ); 308 } 309 310 return new Operation( new QualifiedName( name ), dcps, parameters, null, null, null ); 311 } 312 313 /** 314 * Parses a DCPType element. Does not override the method defined in the base class any more. 315 * 316 * @param element 317 * @return created <code>DCPType</code> 318 * @throws XMLParsingException 319 * @see org.deegree.ogcwebservices.getcapabilities.OGCStandardCapabilities 320 */ 321 @Override 322 protected DCP parseDCP( Element element ) 323 throws XMLParsingException { 324 325 List<HTTP.Type> types = new ArrayList<HTTP.Type>(); 326 List<OnlineResource> links = new ArrayList<OnlineResource>(); 327 328 Element elem = XMLTools.getRequiredElement( element, "HTTP", nsContext ); 329 String s = null; 330 try { 331 List<Node> nl = XMLTools.getNodes( elem, "Get", nsContext ); 332 for ( int i = 0; i < nl.size(); i++ ) { 333 s = XMLTools.getNodeAsString( nl.get( i ), "./@onlineResource", nsContext, null ); 334 types.add( HTTP.Type.Get ); 335 links.add( new OnlineResource( new Linkage( new URL( s ) ) ) ); 336 } 337 } catch ( Exception e ) { 338 LOG.logError( e.getMessage(), e ); 339 throw new XMLParsingException( Messages.getMessage( "WMS_DCPGET", s ) ); 340 } 341 try { 342 List<Node> nl = XMLTools.getNodes( elem, "Post", nsContext ); 343 344 for ( int i = 0; i < nl.size(); i++ ) { 345 s = XMLTools.getNodeAsString( nl.get( i ), "./@onlineResource", nsContext, null ); 346 types.add( HTTP.Type.Post ); 347 links.add( new OnlineResource( new Linkage( new URL( s ) ) ) ); 348 } 349 350 } catch ( MalformedURLException e ) { 351 throw new XMLParsingException( Messages.getMessage( "WMS_DCPPOST", s ) ); 352 } 353 return new HTTP( links, null, types ); 354 355 } 356 357 /** 358 * returns the layers offered by the WMS 359 * 360 * @return the layer 361 * @throws XMLParsingException 362 * @throws UnknownCRSException 363 * @throws MalformedURLException 364 */ 365 @Override 366 protected Layer parseLayers( Element layerElem, Layer parent, ScaleHint scaleHint ) 367 throws XMLParsingException, UnknownCRSException { 368 369 boolean queryable = XMLTools.getNodeAsBoolean( layerElem, "./@queryable", nsContext, false ); 370 371 int cascaded = 0; 372 boolean opaque = false; 373 boolean noSubsets = false; 374 int fixedWidth = 0; 375 int fixedHeight = 0; 376 String name = XMLTools.getNodeAsString( layerElem, "./Name", nsContext, null ); 377 String title = XMLTools.getRequiredNodeAsString( layerElem, "./Title", nsContext ); 378 String layerAbstract = XMLTools.getNodeAsString( layerElem, "./Abstract", nsContext, null ); 379 String[] keywords = XMLTools.getNodesAsStrings( layerElem, "./Keywords", nsContext ); 380 String[] srs = XMLTools.getNodesAsStrings( layerElem, "./SRS", nsContext ); 381 382 List<Element> nl = XMLTools.getElements( layerElem, "./BoundingBox", nsContext ); 383 // TODO 384 // substitue with Envelope 385 LayerBoundingBox[] bboxes = null; 386 if ( nl.size() == 0 && parent != null ) { 387 // inherit BoundingBoxes from parent layer 388 bboxes = parent.getBoundingBoxes(); 389 } else { 390 bboxes = parseLayerBoundingBoxes( nl ); 391 } 392 393 Element llBox = XMLTools.getElement( layerElem, "./LatLonBoundingBox", nsContext ); 394 Envelope llBoundingBox = null; 395 396 if ( llBox == null && parent != null ) { 397 // inherit LatLonBoundingBox parent layer 398 llBoundingBox = parent.getLatLonBoundingBox(); 399 } else if ( llBox != null ) { 400 llBoundingBox = parseLatLonBoundingBox( llBox ); 401 } else { 402 llBoundingBox = GeometryFactory.createEnvelope( -180, -90, 180, 90, CRSFactory.create( "EPSG:4326" ) ); 403 } 404 405 DataURL[] dataURLs = parseDataURL( layerElem ); 406 407 Style[] styles = parseStyles( layerElem ); 408 409 scaleHint = parseScaleHint( layerElem, scaleHint ); 410 411 Layer layer = new Layer( queryable, cascaded, opaque, noSubsets, fixedWidth, fixedHeight, name, title, 412 layerAbstract, llBoundingBox, null, scaleHint, keywords, srs, bboxes, null, null, 413 null, null, null, dataURLs, null, styles, null, null, parent ); 414 415 // get Child layers 416 nl = XMLTools.getElements( layerElem, "./Layer", nsContext ); 417 Layer[] layers = new Layer[nl.size()]; 418 for ( int i = 0; i < layers.length; i++ ) { 419 layers[i] = parseLayers( nl.get( i ), layer, scaleHint ); 420 } 421 422 // set child layers 423 layer.setLayer( layers ); 424 425 return layer; 426 } 427 428 /** 429 * 430 * @param layerElem 431 * @return the URLs 432 * @throws XMLParsingException 433 * @throws MalformedURLException 434 */ 435 @Override 436 protected DataURL[] parseDataURL( Element layerElem ) 437 throws XMLParsingException { 438 439 List<Node> nl = XMLTools.getNodes( layerElem, "./DataURL", nsContext ); 440 DataURL[] dataURL = new DataURL[nl.size()]; 441 for ( int i = 0; i < dataURL.length; i++ ) { 442 URL url; 443 try { 444 url = new URL( XMLTools.getStringValue( nl.get( i ) ) ); 445 } catch ( MalformedURLException e ) { 446 throw new XMLParsingException( XMLTools.getStringValue( nl.get( i ) ) + " is not an URL" ); 447 } 448 dataURL[i] = new DataURL( null, url ); 449 450 } 451 452 return dataURL; 453 } 454 455 /** 456 * 457 * @param layerElem 458 * @return the styles 459 * @throws XMLParsingException 460 * @throws MalformedURLException 461 */ 462 @Override 463 protected Style[] parseStyles( Element layerElem ) 464 throws XMLParsingException { 465 466 List<Node> nl = XMLTools.getNodes( layerElem, "./Style", nsContext ); 467 Style[] styles = new Style[nl.size()]; 468 for ( int i = 0; i < styles.length; i++ ) { 469 String name = XMLTools.getRequiredNodeAsString( nl.get( i ), "./Name", nsContext ); 470 471 if ( name == null ) { 472 throw new XMLParsingException( Messages.getMessage( "WMS_STYLENAME" ) ); 473 } 474 String title = XMLTools.getNodeAsString( nl.get( i ), "./Title", nsContext, null ); 475 if ( title == null ) { 476 throw new XMLParsingException( Messages.getMessage( "WMS_STYLETITLE" ) ); 477 } 478 String styleAbstract = XMLTools.getNodeAsString( nl.get( i ), "./Abstract", nsContext, null ); 479 StyleURL styleURL = parseStyleURL( nl.get( i ) ); 480 481 styles[i] = new Style( name, title, styleAbstract, null, null, styleURL, null ); 482 } 483 484 return styles; 485 } 486 487 /** 488 * 489 * @param node 490 * @return the URL 491 * @throws XMLParsingException 492 * @throws MalformedURLException 493 */ 494 @Override 495 protected StyleURL parseStyleURL( Node node ) 496 throws XMLParsingException { 497 498 StyleURL styleURL = null; 499 Node styleNode = XMLTools.getNode( node, "./StyleURL", nsContext ); 500 501 if ( styleNode != null ) { 502 URL url; 503 try { 504 url = new URL( XMLTools.getStringValue( styleNode ) ); 505 } catch ( MalformedURLException e ) { 506 throw new XMLParsingException( XMLTools.getStringValue( styleNode ) + " is not an URL" ); 507 } 508 styleURL = new StyleURL( null, url ); 509 } 510 511 return styleURL; 512 } 513 514 /** 515 * 516 * @param layerElem 517 * @param scaleHint 518 * the default scale hint 519 * @return the scale hint 520 * @throws XMLParsingException 521 */ 522 @Override 523 protected ScaleHint parseScaleHint( Element layerElem, ScaleHint scaleHint ) 524 throws XMLParsingException { 525 526 Node scNode = XMLTools.getNode( layerElem, "./ScaleHint", nsContext ); 527 if ( scNode != null ) { 528 double mn = XMLTools.getNodeAsDouble( scNode, "./@min", nsContext, 0 ); 529 double mx = XMLTools.getNodeAsDouble( scNode, "./@max", nsContext, Double.MAX_VALUE ); 530 scaleHint = new ScaleHint( mn, mx ); 531 } 532 533 if ( scaleHint == null ) { 534 // set default value to avoid NullPointerException 535 // when accessing a layers scalehint 536 scaleHint = new ScaleHint( 0, Double.MAX_VALUE ); 537 } 538 539 return scaleHint; 540 } 541 542 /** 543 * 544 * @param nl 545 * @return the bboxes 546 * @throws XMLParsingException 547 */ 548 @Override 549 protected LayerBoundingBox[] parseLayerBoundingBoxes( List<Element> nl ) 550 throws XMLParsingException { 551 552 LayerBoundingBox[] llBoxes = new LayerBoundingBox[nl.size()]; 553 for ( int i = 0; i < llBoxes.length; i++ ) { 554 double minx = XMLTools.getRequiredNodeAsDouble( nl.get( i ), "./@minx", nsContext ); 555 double maxx = XMLTools.getRequiredNodeAsDouble( nl.get( i ), "./@maxx", nsContext ); 556 double miny = XMLTools.getRequiredNodeAsDouble( nl.get( i ), "./@miny", nsContext ); 557 double maxy = XMLTools.getRequiredNodeAsDouble( nl.get( i ), "./@maxy", nsContext ); 558 String srs = XMLTools.getRequiredNodeAsString( nl.get( i ), "./@SRS", nsContext ); 559 Position min = GeometryFactory.createPosition( minx, miny ); 560 Position max = GeometryFactory.createPosition( maxx, maxy ); 561 llBoxes[i] = new LayerBoundingBox( min, max, srs, -1, -1 ); 562 } 563 564 return llBoxes; 565 } 566 567 /** 568 * 569 * @param llBox 570 * @return the envelope 571 * @throws XMLParsingException 572 * @throws UnknownCRSException 573 */ 574 @Override 575 protected Envelope parseLatLonBoundingBox( Element llBox ) 576 throws XMLParsingException, UnknownCRSException { 577 578 double minx = XMLTools.getRequiredNodeAsDouble( llBox, "./@minx", nsContext ); 579 double maxx = XMLTools.getRequiredNodeAsDouble( llBox, "./@maxx", nsContext ); 580 double miny = XMLTools.getRequiredNodeAsDouble( llBox, "./@miny", nsContext ); 581 double maxy = XMLTools.getRequiredNodeAsDouble( llBox, "./@maxy", nsContext ); 582 CoordinateSystem crs = CRSFactory.create( "EPSG:4326" ); 583 584 Envelope env = GeometryFactory.createEnvelope( minx, miny, maxx, maxy, crs ); 585 586 return env; 587 } 588 589 }