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 }