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 }