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 }