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 }