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