001 // $HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/ogcbase/OGCDocument.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.ogcbase; 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.ArrayList; 051 import java.util.List; 052 053 import org.deegree.datatypes.CodeList; 054 import org.deegree.datatypes.QualifiedName; 055 import org.deegree.datatypes.time.TimeDuration; 056 import org.deegree.datatypes.time.TimePeriod; 057 import org.deegree.datatypes.time.TimePosition; 058 import org.deegree.datatypes.time.TimeSequence; 059 import org.deegree.datatypes.values.Closure; 060 import org.deegree.datatypes.values.Interval; 061 import org.deegree.datatypes.values.TypedLiteral; 062 import org.deegree.datatypes.values.Values; 063 import org.deegree.framework.util.StringTools; 064 import org.deegree.framework.xml.ElementList; 065 import org.deegree.framework.xml.XMLFragment; 066 import org.deegree.framework.xml.XMLParsingException; 067 import org.deegree.framework.xml.XMLTools; 068 import org.deegree.model.metadata.iso19115.Keywords; 069 import org.deegree.model.metadata.iso19115.Linkage; 070 import org.deegree.model.metadata.iso19115.OnlineResource; 071 import org.deegree.model.spatialschema.Point; 072 import org.deegree.ogcwebservices.LonLatEnvelope; 073 import org.deegree.ogcwebservices.OGCWebServiceException; 074 import org.deegree.ogcwebservices.wcs.describecoverage.InvalidCoverageDescriptionExcpetion; 075 import org.w3c.dom.Element; 076 import org.w3c.dom.Text; 077 078 /** 079 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth </a> 080 * @author last edited by: $Author: apoth $ 081 * 082 * @version 1.0. $Revision: 6818 $, $Date: 2007-05-04 15:54:56 +0200 (Fr, 04 Mai 2007) $ 083 * 084 * @since 1.1 085 */ 086 public abstract class OGCDocument extends XMLFragment { 087 088 protected static final URI GMLNS = CommonNamespaces.GMLNS; 089 090 /** 091 * creates a <tt>LonLatEnvelope</tt> object from the passed element 092 * 093 * @param element 094 * @return created <tt>LonLatEnvelope</tt> 095 * @throws XMLParsingException 096 * @throws InvalidCoverageDescriptionExcpetion 097 */ 098 protected LonLatEnvelope parseLonLatEnvelope( Element element ) 099 throws XMLParsingException, OGCWebServiceException { 100 101 String srs = XMLTools.getRequiredAttrValue( "srsName", null, element ); 102 if ( !"WGS84(DD)".equals( srs ) ) { 103 throw new OGCWebServiceException( "srsName must be WGS84(DD) for lonLatEnvelope." ); 104 } 105 106 ElementList el = XMLTools.getChildElements( "pos", GMLNS, element ); 107 if ( el == null || el.getLength() != 2 ) { 108 throw new OGCWebServiceException( "A lonLatEnvelope must contain two gml:pos elements" ); 109 } 110 111 Point min = GMLDocument.parsePos( el.item( 0 ) ); 112 Point max = GMLDocument.parsePos( el.item( 1 ) ); 113 114 el = XMLTools.getChildElements( "timePosition", GMLNS, element ); 115 TimePosition[] timePositions = parseTimePositions( el ); 116 117 return new LonLatEnvelope( min, max, timePositions, "WGS84(DD)" ); 118 } 119 120 /** 121 * creates an array of <tt>TimePosition</tt> s from the passed element 122 * 123 * @param el 124 * @return created array of <tt>TimePosition</tt> s 125 * @throws XMLParsingException 126 * @throws InvalidCoverageDescriptionExcpetion 127 */ 128 protected TimePosition[] parseTimePositions( ElementList el ) 129 throws XMLParsingException, OGCWebServiceException { 130 TimePosition[] timePos = new TimePosition[el.getLength()]; 131 for ( int i = 0; i < timePos.length; i++ ) { 132 timePos[i] = GMLDocument.parseTimePosition( el.item( i ) ); 133 } 134 return timePos; 135 } 136 137 /** 138 * Creates an array of <code>Keywords</code> from the passed list of <code>keyword</code> 139 * -elements. 140 * 141 * This appears to be pretty superfluous (as one <code>keywords</code>- element may contain 142 * several <code>keyword</code> -elements). However, the schema in the OGC document "Web 143 * Coverage Service (WCS), Version 1.0.0", contains the following line (in the definition of the 144 * CoverageOfferingBriefType): 145 * 146 * <code><xs:element ref="keywords" minOccurs="0" maxOccurs="unbounded"/></code> 147 * 148 * @param el 149 * @return created array of <tt>Keywords</tt> 150 * @throws XMLParsingException 151 */ 152 protected Keywords[] parseKeywords( ElementList el, URI namespaceURI ) { 153 Keywords[] kws = new Keywords[el.getLength()]; 154 for ( int i = 0; i < kws.length; i++ ) { 155 kws[i] = parseKeywords( el.item( i ), namespaceURI ); 156 } 157 return kws; 158 } 159 160 /** 161 * Creates a <code>Keywords</code> instance from the given <code>keywords</code> -element. 162 * 163 * @param element 164 * @param namespaceURI 165 * @return created <code>Keywords</code> 166 * @throws XMLParsingException 167 */ 168 protected Keywords parseKeywords( Element element, URI namespaceURI ) { 169 ElementList el = XMLTools.getChildElements( "keyword", namespaceURI, element ); 170 String[] kws = new String[el.getLength()]; 171 for ( int i = 0; i < kws.length; i++ ) { 172 kws[i] = XMLTools.getStringValue( el.item( i ) ); 173 } 174 return new Keywords( kws ); 175 } 176 177 /** 178 * creates an <tt>TimeSequence</tt> from the passed element 179 * 180 * @param element 181 * @return created <tt>TimeSequence</tt> 182 * @throws XMLParsingException 183 * @throws InvalidCoverageDescriptionExcpetion 184 */ 185 protected TimeSequence parseTimeSequence( Element element, URI namespaceURI ) 186 throws XMLParsingException, OGCWebServiceException { 187 ElementList el = XMLTools.getChildElements( "timePerdiod", namespaceURI, element ); 188 TimePeriod[] timePerdiods = parseTimePeriods( el, namespaceURI ); 189 el = XMLTools.getChildElements( "timePosition", GMLNS, element ); 190 TimePosition[] timePositions = parseTimePositions( el ); 191 192 return new TimeSequence( timePerdiods, timePositions ); 193 } 194 195 /** 196 * creates an array of <tt>TimePeriod</tt> s from the passed element 197 * 198 * @param el 199 * @return created array of <tt>TimePeriod</tt> s 200 * @throws XMLParsingException 201 * @throws InvalidCoverageDescriptionExcpetion 202 */ 203 protected TimePeriod[] parseTimePeriods( ElementList el, URI namespaceURI ) 204 throws XMLParsingException, OGCWebServiceException { 205 TimePeriod[] timePeriods = new TimePeriod[el.getLength()]; 206 for ( int i = 0; i < timePeriods.length; i++ ) { 207 timePeriods[i] = parseTimePeriod( el.item( i ), namespaceURI ); 208 } 209 return timePeriods; 210 } 211 212 /** 213 * creates a <tt>TimePeriod</tt> from the passed element 214 * 215 * @param element 216 * @return created <tt>TimePeriod</tt> 217 * @throws XMLParsingException 218 * @throws InvalidCoverageDescriptionExcpetion 219 */ 220 protected TimePeriod parseTimePeriod( Element element, URI namespaceURI ) 221 throws XMLParsingException, OGCWebServiceException { 222 try { 223 Element begin = XMLTools.getRequiredChildElement( "beginPosition", namespaceURI, element ); 224 TimePosition beginPosition = GMLDocument.parseTimePosition( begin ); 225 Element end = XMLTools.getRequiredChildElement( "endPosition", namespaceURI, element ); 226 TimePosition endPosition = GMLDocument.parseTimePosition( end ); 227 String dur = XMLTools.getRequiredStringValue( "timeResolution", namespaceURI, element ); 228 TimeDuration resolution = TimeDuration.createTimeDuration( dur ); 229 230 return new TimePeriod( beginPosition, endPosition, resolution ); 231 } catch ( InvalidGMLException e ) { 232 LOG.logError( e.getMessage(), e ); 233 String s = e.getMessage() + "\n" + StringTools.stackTraceToString( e ); 234 throw new OGCWebServiceException( s ); 235 } 236 237 } 238 239 /** 240 * creates a <tt>Values</tt> object from the passed element 241 * 242 * @param element 243 * @return created <tt>Values</tt> 244 * @throws XMLParsingException 245 */ 246 protected Values parseValues( Element element, URI namespaceURI ) 247 throws XMLParsingException { 248 249 String type = XMLTools.getAttrValue( element, namespaceURI, "type", null ); 250 String semantic = XMLTools.getAttrValue( element, namespaceURI, "semantic", null ); 251 252 ElementList el = XMLTools.getChildElements( "interval", namespaceURI, element ); 253 Interval[] intervals = new Interval[el.getLength()]; 254 for ( int i = 0; i < intervals.length; i++ ) { 255 intervals[i] = parseInterval( el.item( i ), namespaceURI ); 256 } 257 258 el = XMLTools.getChildElements( "singleValue", namespaceURI, element ); 259 TypedLiteral[] singleValues = new TypedLiteral[el.getLength()]; 260 for ( int i = 0; i < singleValues.length; i++ ) { 261 singleValues[i] = parseTypedLiteral( el.item( i ) ); 262 } 263 264 Element elem = XMLTools.getChildElement( "default", namespaceURI, element ); 265 TypedLiteral def = null; 266 if ( elem != null ) { 267 def = parseTypedLiteral( elem ); 268 } 269 270 try { 271 URI sem = null; 272 if ( semantic != null ) 273 sem = new URI( semantic ); 274 URI tp = null; 275 if ( type != null ) 276 tp = new URI( type ); 277 return new Values( intervals, singleValues, tp, sem, def ); 278 } catch ( URISyntaxException e ) { 279 LOG.logError( e.getMessage(), e ); 280 throw new XMLParsingException( "couldn't parse URI from valuesl\n" + StringTools.stackTraceToString( e ) ); 281 } 282 } 283 284 /** 285 * creates an <tt>Interval</tt> object from the passed element 286 * 287 * @param element 288 * @return created <tt>Interval</tt> 289 * @throws XMLParsingException 290 */ 291 protected Interval parseInterval( Element element, URI namespaceURI ) 292 throws XMLParsingException { 293 294 try { 295 String tmp = XMLTools.getAttrValue( element, namespaceURI, "type", null ); 296 URI type = null; 297 if ( tmp != null ) 298 type = new URI( tmp ); 299 String semantic = XMLTools.getAttrValue( element, namespaceURI, "semantic", null ); 300 tmp = XMLTools.getAttrValue( element, null, "atomic", null ); 301 boolean atomic = "true".equals( tmp ) || "1".equals( tmp ); 302 String clos = XMLTools.getAttrValue( element, namespaceURI, "closure", null ); 303 304 Closure closure = new Closure( clos ); 305 306 Element elem = XMLTools.getRequiredChildElement( "min", namespaceURI, element ); 307 TypedLiteral min = parseTypedLiteral( elem ); 308 309 elem = XMLTools.getRequiredChildElement( "min", namespaceURI, element ); 310 TypedLiteral max = parseTypedLiteral( elem ); 311 312 elem = XMLTools.getRequiredChildElement( "res", namespaceURI, element ); 313 TypedLiteral res = parseTypedLiteral( elem ); 314 315 URI sem = null; 316 if ( semantic != null ) 317 sem = new URI( semantic ); 318 319 return new Interval( min, max, type, sem, atomic, closure, res ); 320 } catch ( URISyntaxException e ) { 321 LOG.logError( e.getMessage(), e ); 322 throw new XMLParsingException( "couldn't parse URI from interval\n" + StringTools.stackTraceToString( e ) ); 323 } 324 325 } 326 327 /** 328 * creates a <tt>TypedLiteral</tt> from the passed element 329 * 330 * @param element 331 * @return created <tt>TypedLiteral</tt> 332 * @throws XMLParsingException 333 */ 334 protected TypedLiteral parseTypedLiteral( Element element ) 335 throws XMLParsingException { 336 try { 337 String tmp = XMLTools.getStringValue( element ); 338 String mtype = XMLTools.getAttrValue( element, null, "type", null ); 339 URI mt = null; 340 if ( mtype != null ) 341 mt = new URI( mtype ); 342 return new TypedLiteral( tmp, mt ); 343 } catch ( URISyntaxException e ) { 344 LOG.logError( e.getMessage(), e ); 345 throw new XMLParsingException( "couldn't parse URI from typedLiteral\n" 346 + StringTools.stackTraceToString( e ) ); 347 } 348 } 349 350 /** 351 * creates an array of <tt>CodeList</tt> objects from the passed element list 352 * 353 * @param el 354 * @return created array of <tt>CodeList</tt> 355 * @throws XMLParsingException 356 */ 357 protected CodeList[] parseCodeListArray( ElementList el ) 358 throws XMLParsingException { 359 CodeList[] cl = new CodeList[el.getLength()]; 360 for ( int i = 0; i < cl.length; i++ ) { 361 cl[i] = parseCodeList( el.item( i ) ); 362 } 363 return cl; 364 } 365 366 /** 367 * creates a <tt>CodeList</tt> object from the passed element 368 * 369 * @param element 370 * @return created <tt>CodeList</tt> 371 * @throws XMLParsingException 372 */ 373 protected CodeList parseCodeList( Element element ) 374 throws XMLParsingException { 375 try { 376 String tmp = XMLTools.getAttrValue( element, null, "codeSpace", null ); 377 URI codeSpace = null; 378 if ( tmp != null ) { 379 codeSpace = new URI( tmp ); 380 } 381 tmp = XMLTools.getStringValue( element ); 382 String[] ar = StringTools.toArray( tmp, " ,;", true ); 383 return new CodeList( element.getNodeName(), ar, codeSpace ); 384 } catch ( URISyntaxException e ) { 385 LOG.logError( e.getMessage(), e ); 386 throw new XMLParsingException( "couldn't parse URI from CodeList\n" + StringTools.stackTraceToString( e ) ); 387 } 388 } 389 390 /** 391 * Creates an <tt>OnLineResource</tt> instance from the passed element. The element contains 392 * an OnlineResourse as it is used in the OGC Web XXX CapabilitiesService specifications. 393 * 394 * TODO Compare with XMLFragment#parseSimpleLink 395 * 396 * @param element 397 * @return 398 * @throws XMLParsingException 399 */ 400 protected OnlineResource parseOnLineResource( Element element ) 401 throws XMLParsingException { 402 403 OnlineResource olr = null; 404 String attrValue = XMLTools.getRequiredAttrValue( "href", XLNNS, element ); 405 URL href = null; 406 try { 407 href = resolve( attrValue ); 408 } catch ( MalformedURLException e ) { 409 LOG.logError( e.getMessage(), e ); 410 throw new XMLParsingException( "Given value '" + attrValue + "' in attribute 'href' " + "(namespace: " 411 + XLNNS + ") of element '" + element.getLocalName() + "' (namespace: " 412 + element.getNamespaceURI() + ") is not a valid URL." ); 413 } 414 Linkage linkage = new Linkage( href, Linkage.SIMPLE ); 415 String title = XMLTools.getAttrValue( element, XLNNS, "title", null ); 416 olr = new OnlineResource( null, null, linkage, null, title, href.getProtocol() ); 417 return olr; 418 } 419 420 /** 421 * Creates a new instance of <code>PropertyPath</code> from the given text node. 422 * <p> 423 * NOTE: Namespace prefices used in the property path must be bound using XML namespace 424 * mechanisms (i.e. using xmlns attributes in the document). 425 * 426 * @param textNode 427 * string representation of the property path 428 * @return new PropertyPath instance 429 * @see PropertyPath 430 */ 431 public static PropertyPath parsePropertyPath( Text textNode ) 432 throws XMLParsingException { 433 434 String path = XMLTools.getStringValue( textNode ); 435 String[] steps = StringTools.toArray( path, "/", false ); 436 List<PropertyPathStep> propertyPathSteps = new ArrayList<PropertyPathStep>( steps.length ); 437 438 for ( int i = 0; i < steps.length; i++ ) { 439 PropertyPathStep propertyStep = null; 440 QualifiedName propertyName = null; 441 String step = steps[i]; 442 boolean isAttribute = false; 443 boolean isIndexed = false; 444 int selectedIndex = -1; 445 446 // check if step begins with '@' -> must be the final step then 447 if ( step.startsWith( "@" ) ) { 448 if ( i != steps.length - 1 ) { 449 String msg = "PropertyName '" + path + "' is illegal: the attribute specifier may only " 450 + "be used for the final step."; 451 throw new XMLParsingException( msg ); 452 } 453 step = step.substring( 1 ); 454 isAttribute = true; 455 } 456 457 // check if the step ends with brackets ([...]) 458 if ( step.endsWith( "]" ) ) { 459 if ( isAttribute ) { 460 String msg = "PropertyName '" + path + "' is illegal: if the attribute specifier ('@') is used, " 461 + "index selection ('[...']) is not possible."; 462 throw new XMLParsingException( msg ); 463 } 464 int bracketPos = step.indexOf( '[' ); 465 if ( bracketPos < 0 ) { 466 String msg = "PropertyName '" + path + "' is illegal. No opening brackets found for step '" + step 467 + "'."; 468 throw new XMLParsingException( msg ); 469 } 470 try { 471 selectedIndex = Integer.parseInt( step.substring( bracketPos + 1, step.length() - 1 ) ); 472 } catch ( NumberFormatException e ) { 473 String msg = "PropertyName '" + path + "' is illegal. Specified index '" 474 + step.substring( bracketPos + 1, step.length() - 1 ) + "' is not a number."; 475 throw new XMLParsingException( msg ); 476 } 477 step = step.substring( 0, bracketPos ); 478 isIndexed = true; 479 } 480 481 // determine namespace prefix and binding (if any) 482 int colonPos = step.indexOf( ':' ); 483 if ( colonPos < 0 ) { 484 propertyName = new QualifiedName( step ); 485 } else { 486 String prefix = step.substring( 0, colonPos ); 487 step = step.substring( colonPos + 1 ); 488 URI namespace = null; 489 try { 490 namespace = XMLTools.getNamespaceForPrefix( prefix, textNode ); 491 } catch ( URISyntaxException e ) { 492 throw new XMLParsingException( "Error parsing PropertyName: " + e.getMessage() ); 493 } 494 if ( namespace == null ) { 495 throw new XMLParsingException( "PropertyName '" + path + "' uses an unbound namespace prefix: " 496 + prefix ); 497 } 498 propertyName = new QualifiedName( prefix, step, namespace ); 499 } 500 501 if ( isAttribute ) { 502 propertyStep = PropertyPathFactory.createAttributePropertyPathStep( propertyName ); 503 } else if ( isIndexed ) { 504 propertyStep = PropertyPathFactory.createPropertyPathStep( propertyName, selectedIndex ); 505 } else { 506 propertyStep = PropertyPathFactory.createPropertyPathStep( propertyName ); 507 } 508 propertyPathSteps.add( propertyStep ); 509 } 510 return PropertyPathFactory.createPropertyPath( propertyPathSteps ); 511 } 512 }