001    //$HeadURL: svn+ssh://jwilden@svn.wald.intevation.org/deegree/base/branches/2.5_testing/src/org/deegree/ogcwebservices/wps/describeprocess/ProcessDescriptionDocument.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.wps.describeprocess;
037    
038    import static org.deegree.ogcbase.CommonNamespaces.OWSNS;
039    
040    import java.net.URI;
041    import java.net.URISyntaxException;
042    import java.util.ArrayList;
043    import java.util.List;
044    
045    import org.deegree.datatypes.Code;
046    import org.deegree.datatypes.values.Closure;
047    import org.deegree.datatypes.values.TypedLiteral;
048    import org.deegree.datatypes.values.ValueRange;
049    import org.deegree.datatypes.xlink.SimpleLink;
050    import org.deegree.framework.log.ILogger;
051    import org.deegree.framework.log.LoggerFactory;
052    import org.deegree.framework.xml.XMLFragment;
053    import org.deegree.framework.xml.XMLParsingException;
054    import org.deegree.framework.xml.XMLTools;
055    import org.deegree.ogcwebservices.MetadataType;
056    import org.deegree.ogcwebservices.wps.describeprocess.ProcessDescription.DataInputs;
057    import org.deegree.ogcwebservices.wps.describeprocess.ProcessDescription.ProcessOutputs;
058    import org.deegree.owscommon.OWSMetadata;
059    import org.deegree.owscommon.com110.OWSAllowedValues;
060    import org.w3c.dom.Element;
061    
062    /**
063     * ProcessDescriptionDocument.java
064     *
065     * Created on 10.03.2006. 15:18:02h
066     *
067     * @author <a href="mailto:christian@kiehle.org">Christian Kiehle</a>
068     * @author <a href="mailto:christian.heier@gmx.de">Christian Heier</a>
069     *
070     * @version 1.0.
071     *
072     * @since 2.0
073     */
074    
075    public class ProcessDescriptionDocument extends XMLFragment {
076    
077        /**
078         *
079         */
080        private static final long serialVersionUID = 7104894360064209850L;
081    
082        private static ILogger LOG = LoggerFactory.getLogger( ProcessDescriptionDocument.class );
083    
084        /**
085         *
086         * @return the bean created from the root element.
087         * @throws XMLParsingException
088         */
089        public ProcessDescription parseProcessDescription()
090                                throws XMLParsingException {
091    
092            Element root = getRootElement();
093    
094            Element processDescriptionNode = (Element) XMLTools.getRequiredNode( root, "wps:ProcessDescription", nsContext );
095    
096            String responsibleClass = getResponsibleClass( processDescriptionNode );
097    
098            Code identifier = getIdentifier( processDescriptionNode );
099    
100            String title = getTitle( processDescriptionNode );
101    
102            String _abstract = getAbstract( processDescriptionNode );
103    
104            List<MetadataType> metadataTypeList = getMetadata( processDescriptionNode );
105    
106            DataInputs dataInputs = null;
107    
108            ProcessOutputs processOutputs = null;
109    
110            // Get optional attribute "processVersion" from <wps:ProcessDescription>
111            // node
112            String processVersion = null;
113            String versionAttribute = processDescriptionNode.getAttribute( "processVersion" );
114            if ( null != versionAttribute && !"".equals( versionAttribute ) ) {
115                processVersion = versionAttribute;
116            }
117    
118            // Get optional attribute "storeSupported" from <wps:ProcessDescription>
119            // node
120            Boolean storeSupported = new Boolean( false );
121            String storeSupportedAttribute = processDescriptionNode.getAttribute( "storeSupported" );
122            if ( null != storeSupportedAttribute && !"".equals( storeSupportedAttribute ) ) {
123                storeSupported = Boolean.valueOf( storeSupportedAttribute );
124            }
125    
126            // Get optional attribute "statusSupported" from
127            // <wps:ProcessDescription> node
128            Boolean statusSupported = new Boolean( false );
129            String statusSupportedAttribute = processDescriptionNode.getAttribute( "statusSupported" );
130            if ( null != statusSupportedAttribute && !"".equals( statusSupportedAttribute ) ) {
131                statusSupported = Boolean.valueOf( statusSupportedAttribute );
132            }
133    
134            // Get optional node <wps:DataInputs> from <wps:ProcessDescription> node
135    
136            Element dataInputsNode = XMLTools.getRequiredElement( processDescriptionNode, "wps:DataInputs", nsContext );
137    
138            if ( null != dataInputsNode ) {
139    
140                List<Element> inputNodesList = XMLTools.getElements( dataInputsNode, "wps:Input", nsContext );
141    
142                if ( null != inputNodesList && 0 != inputNodesList.size() ) {
143    
144                    int size = inputNodesList.size();
145                    dataInputs = new DataInputs();
146                    List<InputDescription> inputDescriptions = new ArrayList<InputDescription>( size );
147                    for ( int i = 0; i < size; i++ ) {
148                        if ( inputNodesList.get( i ) != null ) {
149                            inputDescriptions.add( i, getInputDescription( inputNodesList.get( i ) ) );
150                        }
151                    }
152                    dataInputs.setInputDescriptions( inputDescriptions );
153                }
154            }
155    
156            // Get mandatory node <wps:ProcessOutputs> from <wps:ProcessDescription>
157            // node.
158    
159            Element processOutputsNode = (Element) XMLTools.getRequiredNode( processDescriptionNode, "wps:ProcessOutputs",
160                                                                             nsContext );
161    
162            List<Element> outputNodesList = XMLTools.getRequiredElements( processOutputsNode, "wps:Output", nsContext );
163            int size = outputNodesList.size();
164            processOutputs = new ProcessOutputs();
165            processOutputs.output = new ArrayList<OutputDescription>( size );
166            for ( int i = 0; i < size; i++ ) {
167                processOutputs.output.add( i, getOutputDescription( outputNodesList.get( i ) ) );
168            }
169    
170            return new ProcessDescription( responsibleClass, identifier, title, _abstract, processVersion,
171                                           metadataTypeList, dataInputs, processOutputs, statusSupported, storeSupported );
172    
173        }
174    
175        private String getResponsibleClass( Element processDescriptionNode )
176                                throws XMLParsingException {
177    
178            // Get resonsible class for process execution from deegreeParams section
179            String responsibleClass = null;
180            Element deegreeParamsNode = (Element) XMLTools.getRequiredNode( processDescriptionNode,
181                                                                            "deegreewps:deegreeParams", nsContext );
182            responsibleClass = XMLTools.getRequiredNodeAsString( deegreeParamsNode, "deegreewps:responsibleClass/text()",
183                                                                 nsContext );
184    
185            return responsibleClass;
186        }
187    
188        /**
189         * @param e
190         *            processDescriptionNode
191         * @throws XMLParsingException
192         */
193        private List<MetadataType> getMetadata( Element e )
194                                throws XMLParsingException {
195            List<MetadataType> metadataTypeList = null;
196    
197            // Get optional nodes <ows:Metadata>
198            List<Element> metadataTypeNodes = XMLTools.getElements( e, "ows:Metadata", nsContext );
199            if ( null != metadataTypeNodes && 0 != metadataTypeNodes.size() ) {
200                int size = metadataTypeNodes.size();
201                metadataTypeList = new ArrayList<MetadataType>( size );
202                for ( int i = 0; i < size; i++ ) {
203                    metadataTypeList.add( i, getMetadataType( metadataTypeNodes.get( i ) ) );
204                }
205            }
206            return metadataTypeList;
207        }
208    
209        /**
210         * @param e
211         *            processDescriptionNode
212         * @throws XMLParsingException
213         */
214        private String getAbstract( Element e )
215                                throws XMLParsingException {
216            String _abstract = null;
217    
218            // Get optional node <ows:Abstract>
219            String owsAbstract = XMLTools.getNodeAsString( e, "ows:Abstract/text()", nsContext, null );
220            if ( null != owsAbstract && !"".equals( owsAbstract ) ) {
221                _abstract = owsAbstract;
222            }
223            return _abstract;
224        }
225    
226        /**
227         * @param e
228         *            processDescriptionNode
229         * @throws XMLParsingException
230         */
231        private String getTitle( Element e )
232                                throws XMLParsingException {
233            // Get required node <ows:Title>
234            return XMLTools.getRequiredNodeAsString( e, "ows:Title/text()", nsContext );
235        }
236    
237        /**
238         * @param e
239         *            processDescriptionNode
240         * @throws XMLParsingException
241         */
242        private Code getIdentifier( Element e )
243                                throws XMLParsingException {
244            // Get required node <ows:Identifier>
245            String identifierAsString = XMLTools.getRequiredNodeAsString( e, "ows:Identifier/text()", nsContext );
246            return new Code( identifierAsString, null );
247        }
248    
249        /**
250         * Creates an object representation of a <code>ows:Metadata</code> section.
251         *
252         * @param metadataTypeNode
253         * @return object representation of the <code>ows:Metadata</code> section
254         */
255        private MetadataType getMetadataType( Element metadataTypeNode ) {
256    
257            // FIXME MetadataType contained in Deegree does not correspond with
258            // current OWS MetadataType definition
259            MetadataType metadataType = null;
260    
261            // Only attribute xlink:title supported by now, e.g. <ows:Metadata
262            // xlink:title="buffer"/>
263            String title = metadataTypeNode.getAttributeNS( XLNNS.toString(), "title" );
264    
265            if ( null != title && !"".equals( title ) ) {
266                metadataType = new MetadataType();
267                metadataType.value = title;
268            }
269    
270            return metadataType;
271        }
272    
273        private InputDescription getInputDescription( Element inputDescriptionNode )
274                                throws XMLParsingException {
275    
276            Code identifier = getIdentifier( inputDescriptionNode );
277    
278            String title = getTitle( inputDescriptionNode );
279    
280            String _abstract = getAbstract( inputDescriptionNode );
281    
282            ComplexData complexData = null;
283    
284            LiteralInput literalData = null;
285    
286            SupportedCRSs boundingBoxData = null;
287    
288            Element boundingBoxDataNode = (Element) XMLTools.getNode( inputDescriptionNode, "wps:BoundingBoxData",
289                                                                      nsContext );
290    
291            Element complexDataNode = (Element) XMLTools.getNode( inputDescriptionNode, "wps:ComplexData", nsContext );
292    
293            Element literalDataNode = (Element) XMLTools.getNode( inputDescriptionNode, "wps:LiteralData", nsContext );
294    
295            if ( null == boundingBoxDataNode && null == complexDataNode && null == literalDataNode ) {
296                throw new XMLParsingException(
297                                               "A required data type is missing, one of wps:ComplexData or wps:LiteralData is missing from inputDescriptionNode with localname: "
298                                                                       + inputDescriptionNode.getLocalName() );
299            }
300    
301            if ( null != boundingBoxDataNode && null == complexDataNode && null == literalDataNode ) {
302                boundingBoxData = getSupportedCRSsType( boundingBoxDataNode );
303            }
304            if ( null == boundingBoxDataNode && null != complexDataNode && null == literalDataNode ) {
305                complexData = getComplexDataType( complexDataNode );
306            }
307            if ( null == boundingBoxDataNode && null == complexDataNode && null != literalDataNode ) {
308                literalData = getLiteralInputType( literalDataNode );
309    
310            }
311            int occurs = XMLTools.getNodeAsInt( inputDescriptionNode, "wps:MinimumOccurs/text()", nsContext, 1 );
312    
313            return new InputDescription( identifier, title, _abstract, boundingBoxData, complexData, literalData, occurs );
314        }
315    
316        @SuppressWarnings("unchecked")
317        private SupportedCRSs getSupportedCRSsType( Element boundingBoxDataNode )
318                                throws XMLParsingException {
319    
320            List<URI> crsList = null;
321    
322            // Get required nodes <wps:CRS>
323            List<Element> crsNodes = XMLTools.getRequiredElements( boundingBoxDataNode, "wps:CRS", nsContext );
324            if ( null != crsNodes && 0 != crsNodes.size() ) {
325                int size = crsNodes.size();
326                crsList = new ArrayList<URI>( size );
327                for ( int i = 0; i < size; i++ ) {
328    
329                    String crs = XMLTools.getNodeAsString( crsNodes.get( i ), "/text()", nsContext, null );
330    
331                    crsList.add( i, buildURIFromString( crs ) );
332                }
333            }
334    
335            // Get required attribute "defaultCRS" from node <wps:BoundingBoxData>
336            URI defaultCRS = buildURIFromString( boundingBoxDataNode.getAttribute( "defaultCRS" ) );
337    
338            return new SupportedCRSs( crsList, defaultCRS );
339    
340        }
341    
342        /**
343         * @param complexDataNode
344         * @return the complex data bean created from the node.
345         * @throws XMLParsingException
346         */
347        private ComplexData getComplexDataType( Element complexDataNode )
348                                throws XMLParsingException {
349            String defaultEncoding = null;
350            String defaultFormat = null;
351            String defaultSchema = null;
352            List<SupportedComplexData> supportedComplexDataList = null;
353    
354            // Get optional attribute "defaultFormat" from <wps:ComplexData> node
355            String defaultFormatAttribute = complexDataNode.getAttribute( "defaultFormat" );
356            if ( null != defaultFormatAttribute && !"".equals( defaultFormatAttribute ) ) {
357                defaultFormat = defaultFormatAttribute;
358            }
359    
360            // Get optional attribute "defaultEncoding" from <wps:ComplexData> node
361            String defaultEncodingAttribute = complexDataNode.getAttribute( "defaultEncoding" );
362            if ( null != defaultEncodingAttribute && !"".equals( defaultEncodingAttribute ) ) {
363                defaultEncoding = defaultEncodingAttribute;
364            }
365    
366            // Get optional attribute "defaultSchema" from <wps:ComplexData> node
367            String defaultSchemaAttribute = complexDataNode.getAttribute( "defaultSchema" );
368            if ( null != defaultSchemaAttribute && !"".equals( defaultSchemaAttribute ) ) {
369                defaultSchema = defaultSchemaAttribute;
370            }
371    
372            List<Element> supportedComplexDataNodes = XMLTools.getElements( complexDataNode, "wps:SupportedComplexData",
373                                                                            nsContext );
374            if ( null != supportedComplexDataNodes && 0 != supportedComplexDataNodes.size() ) {
375                int size = supportedComplexDataNodes.size();
376                supportedComplexDataList = new ArrayList<SupportedComplexData>( size );
377                for ( int i = 0; i < size; i++ ) {
378                    supportedComplexDataList.add( i, getSupportedComplexData( supportedComplexDataNodes.get( i ) ) );
379                }
380            }
381    
382            return new ComplexData( defaultEncoding, defaultFormat, defaultSchema, supportedComplexDataList );
383        }
384    
385        /**
386         * @param supportedComplexDataNode
387         *            element
388         * @return the bean created from the element
389         * @throws XMLParsingException
390         */
391        private SupportedComplexData getSupportedComplexData( Element supportedComplexDataNode )
392                                throws XMLParsingException {
393            String encoding = null;
394            String format = null;
395            String schema = null;
396    
397            // Get optional node <wps:Encoding>
398            String wpsEncoding = XMLTools.getNodeAsString( supportedComplexDataNode, "wps:Encoding/text()", nsContext, null );
399            if ( null != wpsEncoding && !"".equals( wpsEncoding ) ) {
400                encoding = wpsEncoding;
401            }
402    
403            // Get optional node <wps:Format>
404            String wpsFormat = XMLTools.getNodeAsString( supportedComplexDataNode, "wps:Format/text()", nsContext, null );
405            if ( null != wpsFormat && !"".equals( wpsFormat ) ) {
406                format = wpsFormat;
407            }
408    
409            // Get optional node <wps:Schema>
410            String wpsSchema = XMLTools.getNodeAsString( supportedComplexDataNode, "wps:Schema/text()", nsContext, null );
411            if ( null != wpsSchema && !"".equals( wpsSchema ) ) {
412                schema = wpsSchema;
413            }
414            return new SupportedComplexData( encoding, format, schema );
415        }
416    
417        /**
418         * @return the bean created from the element
419         */
420        private LiteralInput getLiteralInputType( Element literalDataNode )
421                                throws XMLParsingException {
422            OWSMetadata domainMetadataType = null;
423            SupportedUOMs supportedUOMsType = null;
424            OWSAllowedValues allowedValues = null;
425            boolean anyValueAllowed = false;
426            ValueRange defaultValue = null;
427            OWSMetadata valuesReference = null;
428    
429            // Get optional node <ows:DataType>
430            Element dataTypeNode = (Element) XMLTools.getNode( literalDataNode, "ows:DataType", nsContext );
431            if ( null != dataTypeNode ) {
432                domainMetadataType = getDomainMetadataTypeFromContent( dataTypeNode );
433            }
434    
435            // Get optional node <wps:SupportedUOMs>
436            Element supportedUOMsNode = (Element) XMLTools.getNode( literalDataNode, "wps:SupportedUOMs", nsContext );
437            if ( null != supportedUOMsNode ) {
438                supportedUOMsType = getSupportedUOMs( supportedUOMsNode );
439            }
440    
441            // Get optional node <wps:AllowedValues>
442            Element allowedValuesNode = (Element) XMLTools.getNode( literalDataNode, "ows:AllowedValues", nsContext );
443            // Get optional node <wps:AnyValue>
444            Element anyValueNode = (Element) XMLTools.getNode( literalDataNode, "ows:AnyValue", nsContext );
445            // Get optional node <wps:ValuesReference>
446            Element valuesReferenceNode = (Element) XMLTools.getNode( literalDataNode, "ows:ValuesReference", nsContext );
447    
448            if ( null != allowedValuesNode && null == anyValueNode && null == valuesReferenceNode ) {
449                allowedValues = getOWSAllowedValues( allowedValuesNode );
450            } else if ( null == allowedValuesNode && null != anyValueNode && null == valuesReferenceNode ) {
451                anyValueAllowed = true;
452            } else if ( null == allowedValuesNode && null == anyValueNode && null != valuesReferenceNode ) {
453                String reference = valuesReferenceNode.getAttributeNS( OWSNS.toString(), "reference" );
454                String value = XMLTools.getNodeAsString( valuesReferenceNode, "/text()", nsContext, null );
455                if ( null != value ) {
456                    URI referenceURI = buildURIFromString( reference );
457                    valuesReference = new OWSMetadata( null, new SimpleLink( referenceURI ), value );
458                }
459            } else {
460                throw new XMLParsingException(
461                                               "A required data type is missing, one of ows:AllowedValues, ows:AnyValue or ows:ValuesReference is missing from context node: "
462                                                                       + literalDataNode.getLocalName() );
463            }
464    
465            // Get optional node <wps:DefaultValue>
466            Element defaultValueNode = (Element) XMLTools.getNode( literalDataNode, "ows:DefaultValue", nsContext );
467            if ( null != defaultValueNode ) {
468                defaultValue = getOwsRange( defaultValueNode );
469            }
470    
471            return new LiteralInput( domainMetadataType, supportedUOMsType, allowedValues, anyValueAllowed, defaultValue,
472                                     valuesReference );
473        }
474    
475        private URI buildURIFromString( String reference )
476                                throws XMLParsingException {
477            URI referenceURI = null;
478            try {
479                referenceURI = new URI( reference );
480            } catch ( URISyntaxException e ) {
481                String msg = "The URI syntax is malformed. " + e.getMessage();
482                LOG.logError( msg );
483                throw new XMLParsingException( msg, e );
484            }
485            return referenceURI;
486        }
487    
488        private SupportedUOMs getSupportedUOMs( Element supportedUOMsNode )
489                                throws XMLParsingException {
490            List<Element> uomNodesList = XMLTools.getElements( supportedUOMsNode, "ows:UOM", nsContext );
491    
492            List<OWSMetadata> domainMetadataTypeList = null;
493            if ( null != uomNodesList && 0 != uomNodesList.size() ) {
494                int uomNodesListSize = uomNodesList.size();
495                domainMetadataTypeList = new ArrayList<OWSMetadata>( uomNodesListSize );
496                for ( int i = 0; i < uomNodesListSize; i++ ) {
497                    Element nodeListElement = uomNodesList.get( i );
498    
499                    domainMetadataTypeList.add( i, getDomainMetadataTypeFromAttribute( nodeListElement ) );
500                }
501            }
502            String defaultuom = supportedUOMsNode.getAttribute( "defaultUOM" );
503            URI defaultUOMURI = buildURIFromString( defaultuom );
504            OWSMetadata defaultUOMObject = new OWSMetadata( null, new SimpleLink( defaultUOMURI ), null );
505    
506            return new SupportedUOMs( defaultUOMObject, domainMetadataTypeList );
507        }
508    
509        private OWSMetadata getDomainMetadataTypeFromContent( Element e )
510                                throws XMLParsingException {
511            String owsDataType = XMLTools.getNodeAsString( e, "/text()", nsContext, null );
512            String reference = e.getAttributeNS( OWSNS.toString(), "reference" );
513            URI referenceURI = buildURIFromString( reference );
514            return new OWSMetadata( null, new SimpleLink( referenceURI ), owsDataType );
515        }
516    
517        private OWSMetadata getDomainMetadataTypeFromAttribute( Element e )
518                                throws XMLParsingException {
519            String reference = e.getAttributeNS( OWSNS.toString(), "reference" );
520            URI referenceURI = buildURIFromString( reference );
521    
522            return new OWSMetadata( null, new SimpleLink( referenceURI ), null );
523        }
524    
525        @SuppressWarnings("unchecked")
526        private OWSAllowedValues getOWSAllowedValues( Element e )
527                                throws XMLParsingException {
528            TypedLiteral[] owsValues = null;
529            ValueRange[] valueRanges = null;
530    
531            // gets a Node list of type ows:Value
532            List owsValueNodeList = XMLTools.getNodes( e, "ows:Value", nsContext );
533    
534            if ( null != owsValueNodeList && 0 != owsValueNodeList.size() ) {
535                int size = owsValueNodeList.size();
536                owsValues = new TypedLiteral[size];
537                for ( int i = 0; i < size; i++ ) {
538                    owsValues[i] = ( getOwsValue( (Element) owsValueNodeList.get( i ) ) );
539                }
540            }
541    
542            List owsRangeNodeList = XMLTools.getNodes( e, "ows:Range", nsContext );
543    
544            if ( null != owsRangeNodeList && 0 != owsRangeNodeList.size() ) {
545                int size = owsRangeNodeList.size();
546                valueRanges = new ValueRange[size];
547                for ( int i = 0; i < size; i++ ) {
548                    valueRanges[i] = ( getOwsRange( (Element) owsRangeNodeList.get( i ) ) );
549                }
550            }
551    
552            return new OWSAllowedValues( owsValues, valueRanges );
553        }
554    
555        /**
556         * @param element
557         * @return the bean created from the element
558         * @throws XMLParsingException
559         */
560        private ValueRange getOwsRange( Element element )
561                                throws XMLParsingException {
562    
563            TypedLiteral maximum = getOwsValue( (Element) XMLTools.getNode( element, "ows:MaximumValue", nsContext ) );
564    
565            TypedLiteral minimum = getOwsValue( (Element) XMLTools.getNode( element, "ows:MinimumValue", nsContext ) );
566    
567            Closure rangeClosure = null;
568    
569            String rangeClosureAttribute = element.getAttributeNS( OWSNS.toString(), "rangeClosure" );
570            if ( "closed".equalsIgnoreCase( rangeClosureAttribute ) ) {
571                rangeClosure = new Closure( Closure.CLOSED );
572            } else if ( "open".equalsIgnoreCase( rangeClosureAttribute ) ) {
573                rangeClosure = new Closure( Closure.OPENED );
574            } else if ( "closed-open".equalsIgnoreCase( rangeClosureAttribute ) ) {
575                rangeClosure = new Closure( Closure.CLOSED_OPENED );
576            } else if ( "open-closed".equalsIgnoreCase( rangeClosureAttribute ) ) {
577                rangeClosure = new Closure( Closure.OPENED_CLOSED );
578            } else {
579                throw new XMLParsingException( "Attribute range closure contains invalid value." );
580            }
581    
582            TypedLiteral spacing = null;
583    
584            Element spacingNode = (Element) XMLTools.getNode( element, "ows:Spacing", nsContext );
585            if ( null != spacingNode ) {
586                spacing = getOwsValue( spacingNode );
587            }
588    
589            return new ValueRange( minimum, maximum, spacing, null, null, false, rangeClosure );
590        }
591    
592        /**
593         * @param element
594         * @return the bean created from the element.
595         * @throws XMLParsingException
596         */
597        private TypedLiteral getOwsValue( Element element )
598                                throws XMLParsingException {
599            String value = XMLTools.getNodeAsString( element, "/text()", nsContext, null );
600            return new TypedLiteral( value, null );
601        }
602    
603        /**
604         * @param outputDescriptionNode
605         *            element
606         * @return the bean created from the element.
607         * @throws XMLParsingException
608         */
609        private OutputDescription getOutputDescription( Element outputDescriptionNode )
610                                throws XMLParsingException {
611            Code identifier = getIdentifier( outputDescriptionNode );
612    
613            String title = getTitle( outputDescriptionNode );
614    
615            String _abstract = getAbstract( outputDescriptionNode );
616    
617            Element boundingBoxOutputNode = (Element) XMLTools.getNode( outputDescriptionNode, "wps:BoundingBoxOutput",
618                                                                        nsContext );
619            Element complexOutputNode = (Element) XMLTools.getNode( outputDescriptionNode, "wps:ComplexOutput", nsContext );
620            Element literalOutputNode = (Element) XMLTools.getNode( outputDescriptionNode, "wps:LiteralOutput", nsContext );
621    
622            SupportedCRSs boundingBoxOutput = null;
623            ComplexData complexOutput = null;
624            LiteralOutput literalOutput = null;
625    
626            if ( null != boundingBoxOutputNode && null == complexOutputNode && null == literalOutputNode ) {
627                boundingBoxOutput = getSupportedCRSsType( boundingBoxOutputNode );
628            } else if ( null == boundingBoxOutputNode && null != complexOutputNode && null == literalOutputNode ) {
629                complexOutput = getComplexDataType( complexOutputNode );
630            } else if ( null == boundingBoxOutputNode && null == complexOutputNode && null != literalOutputNode ) {
631                Element dataTypeNode = (Element) XMLTools.getNode( literalOutputNode, "ows:DataType", nsContext );
632                OWSMetadata domainMetadataType = getDomainMetadataTypeFromContent( dataTypeNode );
633                Element supportedUOMsNode = (Element) XMLTools.getNode( literalOutputNode, "wps:SupportedUOMs", nsContext );
634                SupportedUOMs supportedUOMsType = null;
635                if ( null != supportedUOMsNode ) {
636                    supportedUOMsType = getSupportedUOMs( supportedUOMsNode );
637                }
638                literalOutput = new LiteralOutput( domainMetadataType, supportedUOMsType );
639            } else {
640                throw new XMLParsingException(
641                                               "A required data type is missing, one of wps:BoundingBoxOutput, wps:ComplexOutput or wps:LiteralOutput is missing from context node: "
642                                                                       + outputDescriptionNode.getLocalName() );
643            }
644    
645            return new OutputDescription( identifier, title, _abstract, boundingBoxOutput, complexOutput, literalOutput );
646        }
647    }