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