001    //$HeadURL: svn+ssh://jwilden@svn.wald.intevation.org/deegree/base/branches/2.5_testing/src/org/deegree/ogcwebservices/csw/discovery/XMLFactory.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.csw.discovery;
037    
038    import static org.deegree.ogcbase.CommonNamespaces.CSW202NS;
039    import static org.deegree.ogcbase.CommonNamespaces.CSWNS;
040    
041    import java.net.URI;
042    import java.net.URISyntaxException;
043    import java.util.Date;
044    import java.util.HashMap;
045    import java.util.List;
046    import java.util.Map;
047    import java.util.Set;
048    
049    import org.deegree.datatypes.QualifiedName;
050    import org.deegree.framework.log.ILogger;
051    import org.deegree.framework.log.LoggerFactory;
052    import org.deegree.framework.util.StringTools;
053    import org.deegree.framework.util.TimeTools;
054    import org.deegree.framework.xml.XMLException;
055    import org.deegree.framework.xml.XMLTools;
056    import org.deegree.ogcbase.CommonNamespaces;
057    import org.deegree.ogcbase.PropertyPath;
058    import org.deegree.ogcbase.SortProperty;
059    import org.deegree.ogcwebservices.InvalidParameterValueException;
060    import org.deegree.ogcwebservices.OGCWebServiceException;
061    import org.deegree.ogcwebservices.csw.CSWExceptionCode;
062    import org.deegree.ogcwebservices.csw.CSWPropertiesAccess;
063    import org.w3c.dom.Attr;
064    import org.w3c.dom.Document;
065    import org.w3c.dom.Element;
066    import org.w3c.dom.NamedNodeMap;
067    import org.w3c.dom.Node;
068    import org.w3c.dom.NodeList;
069    
070    /**
071     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth </a>
072     * @author <a href="mailto:mschneider@lat-lon.de">Markus Schneider </a>
073     * @author last edited by: $Author: apoth $
074     * 
075     * @version $Revision: 27404 $, $Date: 2010-10-19 17:45:39 +0200 (Di, 19 Okt 2010) $
076     */
077    public class XMLFactory extends org.deegree.ogcbase.XMLFactory {
078    
079        private static final ILogger LOG = LoggerFactory.getLogger( XMLFactory.class );
080    
081        /**
082         * Exports a <code>GetRecordsResponse</code> instance to a <code>GetRecordsResponseDocument</code>.
083         * 
084         * @param response
085         * @return DOM representation of the <code>GetRecordsResponse</code>
086         * @throws XMLException
087         *             if XML template could not be loaded
088         */
089        public static GetRecordsResultDocument export( GetRecordsResult response )
090                                throws XMLException {
091            // 'version'-attribute
092            String version = response.getRequest().getVersion();
093            if ( version == null || "".equals( version.trim() ) ) {
094                version = "2.0.0";
095            }
096    
097            GetRecordsResultDocument responseDocument = new GetRecordsResultDocument( version );
098    
099            try {
100                Element rootElement = responseDocument.getRootElement();
101                Document doc = rootElement.getOwnerDocument();
102    
103                // set required namespaces
104                Element recordRespRoot = response.getSearchResults().getRecords().getOwnerDocument().getDocumentElement();
105                NamedNodeMap nnm = recordRespRoot.getAttributes();
106                for ( int i = 0; i < nnm.getLength(); i++ ) {
107                    Node node = nnm.item( i );
108                    if ( node instanceof Attr ) {
109                        rootElement.setAttribute( node.getNodeName(), node.getNodeValue() );
110                    }
111                }
112    
113                rootElement.setAttribute( "version", version );
114                String namespace = ( version.equals( "2.0.2" ) ? CSW202NS.toString() : CSWNS.toString() );
115    
116                // 'RequestId'-element (optional)
117                if ( response.getRequest().getId() != null ) {
118                    Element requestIdElement = doc.createElementNS( namespace, "csw:RequestId" );
119                    requestIdElement.appendChild( doc.createTextNode( response.getRequest().getId() ) );
120                    rootElement.appendChild( requestIdElement );
121                }
122    
123                // 'SearchStatus'-element (required)
124                Element searchStatusElement = doc.createElementNS( namespace, "csw:SearchStatus" );
125                // 'status'-attribute (required)
126                if ( !version.equals( "2.0.2" ) ) {
127                    searchStatusElement.setAttribute( "status", response.getSearchStatus().getStatus() );
128                }
129                // 'timestamp'-attribute (optional)
130                if ( response.getSearchStatus().getTimestamp() != null ) {
131                    Date date = response.getSearchStatus().getTimestamp();
132                    String time = TimeTools.getISOFormattedTime( date );
133                    searchStatusElement.setAttribute( "timestamp", time );
134                }
135                rootElement.appendChild( searchStatusElement );
136    
137                // 'SeachResults'-element (required)
138                Element searchResultsElement = doc.createElementNS( namespace, "csw:SearchResults" );
139                SearchResults results = response.getSearchResults();
140    
141                // 'resultSetId'-attribute (optional)
142                if ( results.getResultSetId() != null ) {
143                    searchResultsElement.setAttribute( "resultSetId", results.getResultSetId().toString() );
144                }
145                // 'elementSet'-attribute (optional)
146                if ( results.getElementSet() != null ) {
147                    searchResultsElement.setAttribute( "elementSet", results.getElementSet().toString() );
148                }
149                // 'recordSchema'-attribute (optional)
150                if ( results.getRecordSchema() != null ) {
151                    searchResultsElement.setAttribute( "recordSchema", results.getRecordSchema().toString() );
152                }
153                // 'numberOfRecordsMatched'-attribute (required)
154                searchResultsElement.setAttribute( "numberOfRecordsMatched", "" + results.getNumberOfRecordsMatched() );
155                // 'numberOfRecordsReturned'-attribute (required)
156                searchResultsElement.setAttribute( "numberOfRecordsReturned", "" + results.getNumberOfRecordsReturned() );
157                // 'nextRecord'-attribute (required)
158                searchResultsElement.setAttribute( "nextRecord", "" + results.getNextRecord() );
159                // 'expires'-attribute (optional)
160                if ( results.getExpires() != null ) {
161                    Date date = results.getExpires();
162                    String time = TimeTools.getISOFormattedTime( date );
163                    searchResultsElement.setAttribute( "expires", time );
164                }
165                // append all children of the records container node
166                NodeList nl = results.getRecords().getChildNodes();
167                for ( int i = 0; i < nl.getLength(); i++ ) {
168                    Node copy = doc.importNode( nl.item( i ), true );
169                    searchResultsElement.appendChild( copy );
170                }
171                rootElement.appendChild( searchResultsElement );
172            } catch ( Exception e ) {
173                LOG.logError( e.getMessage(), e );
174                throw new XMLException( e.getMessage() );
175            }
176            return responseDocument;
177        }
178    
179        /**
180         * Exports a instance of {@link GetRecordByIdResult} to a {@link GetRecordByIdResultDocument}.
181         * 
182         * @param response
183         * @return a new document
184         * @throws XMLException
185         */
186        public static GetRecordByIdResultDocument export( GetRecordByIdResult response )
187                                throws XMLException {
188    
189            GetRecordByIdResultDocument doc = new GetRecordByIdResultDocument();
190    
191            try {
192                doc.createEmptyDocument( response.getRequest().getVersion() );
193                Document owner = doc.getRootElement().getOwnerDocument();
194                if ( response != null && response.getRecords() != null ) {
195                    for ( Node record : response.getRecords() ) {
196                        Node copy = owner.importNode( record, true );
197                        doc.getRootElement().appendChild( copy );
198                    }
199                } else if ( "2.0.2".equals( response.getRequest().getVersion() )
200                            && ( response == null || response.getRecord() == null ) ) {
201                    throw new OGCWebServiceException( "A record with the given ID does nor exist in the CSW",
202                                                      CSWExceptionCode.INVALIDPARAMETERVALUE );
203                }
204            } catch ( Exception e ) {
205                LOG.logError( e.getMessage(), e );
206                throw new XMLException( e.getMessage() );
207            }
208    
209            return doc;
210        }
211    
212        /**
213         * Exports a <code>DescribeRecordResponse</code> instance to a <code>DescribeRecordResponseDocument</code>.
214         * 
215         * @param response
216         * @return DOM representation of the <code>DescribeRecordResponse</code>
217         * @throws XMLException
218         *             if XML template could not be loaded
219         */
220        public static DescribeRecordResultDocument export( DescribeRecordResult response )
221                                throws XMLException {
222    
223            DescribeRecordResultDocument responseDocument = new DescribeRecordResultDocument();
224    
225            String ns = response.getRequest().getVersion().equals( "2.0.2" ) ? CSW202NS.toString() : CSWNS.toString();
226    
227            try {
228                responseDocument.createEmptyDocument( response.getRequest().getVersion() );
229                Element rootElement = responseDocument.getRootElement();
230                Document doc = rootElement.getOwnerDocument();
231    
232                // 'SchemaComponent'-elements (required)
233                SchemaComponent[] components = response.getSchemaComponents();
234                for ( int i = 0; i < components.length; i++ ) {
235                    Element schemaComponentElement = doc.createElementNS( ns, "csw:SchemaComponent" );
236    
237                    // 'targetNamespace'-attribute (required)
238                    schemaComponentElement.setAttribute( "targetNamespace", components[i].getTargetNamespace().toString() );
239    
240                    // 'parentSchema'-attribute (optional)
241                    if ( components[i].getParentSchema() != null ) {
242                        schemaComponentElement.setAttribute( "parentSchema", components[i].getParentSchema().toString() );
243                    }
244    
245                    // 'schemaLanguage'-attribute (required)
246                    schemaComponentElement.setAttribute( "schemaLanguage", components[i].getSchemaLanguage().toString() );
247    
248                    XMLTools.insertNodeInto( components[i].getSchema().getRootElement(), schemaComponentElement );
249                    rootElement.appendChild( schemaComponentElement );
250                }
251            } catch ( Exception e ) {
252                LOG.logError( e.getMessage(), e );
253                throw new XMLException( e.getMessage() );
254            }
255            return responseDocument;
256        }
257    
258        /**
259         * Exports a <code>GetRecords</code> instance to a <code>GetRecordsDocument</code>.
260         * 
261         * @param request
262         * @return DOM representation of the <code>GetRecords</code>
263         * @throws XMLException
264         *             if some elements could not be appended
265         * @throws OGCWebServiceException
266         *             if an error occurred while creating the xml-representation of the GetRecords bean.
267         */
268        public static GetRecordsDocument exportWithVersion( GetRecords request )
269                                throws XMLException, OGCWebServiceException {
270    
271            GetRecordsDocument getRecordsDocument = null;
272    
273            // read class for version depenging parsing of GetRecords request from properties
274            String className = CSWPropertiesAccess.getString( "GetRecords" + request.getVersion() );
275            Class<?> clzz = null;
276            try {
277                clzz = Class.forName( className );
278            } catch ( ClassNotFoundException e ) {
279                LOG.logError( e.getMessage(), e );
280                throw new InvalidParameterValueException( e.getMessage(), e );
281            }
282            try {
283                getRecordsDocument = (GetRecordsDocument) clzz.newInstance();
284            } catch ( InstantiationException e ) {
285                LOG.logError( e.getMessage(), e );
286                throw new InvalidParameterValueException( e.getMessage(), e );
287            } catch ( IllegalAccessException e ) {
288                LOG.logError( e.getMessage(), e );
289                throw new InvalidParameterValueException( e.getMessage(), e );
290            }
291    
292            // getRecordsDocument = new GetRecordsDocument();
293            try {
294                getRecordsDocument.createEmptyDocument();
295            } catch ( Exception e ) {
296                throw new XMLException( e.getMessage() );
297            }
298            Element rootElement = getRecordsDocument.getRootElement();
299            Document doc = rootElement.getOwnerDocument();
300    
301            // 'version'-attribute
302            rootElement.setAttribute( "version", request.getVersion() );
303    
304            // 'version'-attribute
305            rootElement.setAttribute( "service", "CSW" );
306    
307            // 'resultType'-attribute
308            rootElement.setAttribute( "resultType", request.getResultTypeAsString() );
309    
310            // 'outputFormat'-attribute
311            rootElement.setAttribute( "outputFormat", request.getOutputFormat() );
312    
313            // 'outputSchema'-attribute
314            rootElement.setAttribute( "outputSchema", request.getOutputSchema() );
315    
316            // 'startPosition'-attribute
317            rootElement.setAttribute( "startPosition", "" + request.getStartPosition() );
318    
319            // 'maxRecords'-attribute
320            rootElement.setAttribute( "maxRecords", "" + request.getMaxRecords() );
321    
322            URI localCSWNS = CSWNS;
323            if ( request.getVersion().equals( "2.0.2" ) ) {
324                localCSWNS = CSW202NS;
325            }
326    
327            // '<csw:DistributedSearch>'-element
328            if ( request.getHopCount() != -1 ) {
329                Element distributedSearchElement = doc.createElementNS( localCSWNS.toString(), "csw:DistributedSearch" );
330    
331                // 'hopCount'-attribute
332                distributedSearchElement.setAttribute( "hopCount", "" + request.getHopCount() );
333                rootElement.appendChild( distributedSearchElement );
334            }
335    
336            // '<csw:ResponseHandler>'-elements (optional)
337            URI responseHandler = request.getResponseHandler();
338            if ( responseHandler != null ) {
339                Element responseHandlerElement = doc.createElementNS( localCSWNS.toString(), "csw:ResponseHandler" );
340                responseHandlerElement.appendChild( doc.createTextNode( responseHandler.toASCIIString() ) );
341                rootElement.appendChild( responseHandlerElement );
342    
343            }
344    
345            // '<csw:Query>'-elements (required)
346            Query query = request.getQuery();
347            if ( query != null ) {
348                LOG.logDebug( "Adding the csw:Query element to the csw:GetRecords document" );
349                Element queryElement = doc.createElementNS( localCSWNS.toString(), "csw:Query" );
350    
351                // 'typeName'-attribute
352                // Testing for the list of typenames.
353                List<QualifiedName> typeNames = query.getTypeNamesAsList();
354                Map<String, QualifiedName> aliases = new HashMap<String, QualifiedName>(
355                                                                                         query.getDeclaredTypeNameVariables() );
356                if ( typeNames.size() > 0 ) {
357                    appendTypeNamesAttribute( rootElement, queryElement, typeNames, aliases );
358                } else {
359                    String s = StringTools.listToString( query.getTypeNamesAsList(), ',' );
360                    queryElement.setAttribute( "typeNames", s );
361                }
362    
363                // '<csw:ElementSetName>'-element (optional)
364                if ( query.getElementSetName() != null ) {
365                    Element elementSetNameElement = doc.createElementNS( localCSWNS.toString(), "csw:ElementSetName" );
366                    List<QualifiedName> elementSetNameTypeNamesList = query.getElementSetNameTypeNamesList();
367                    if ( query.getElementSetNameVariables() != null && query.getElementSetNameVariables().size() > 0 ) {
368                        throw new OGCWebServiceException(
369                                                          "The elementSetName element in a csw:GetRecords request may not refrerence variables (aka. aliases), aborting request" );
370                    }
371                    if ( elementSetNameTypeNamesList.size() > 0 ) {
372                        appendTypeNamesAttribute( rootElement, elementSetNameElement, elementSetNameTypeNamesList, null );
373                    }
374                    elementSetNameElement.appendChild( doc.createTextNode( query.getElementSetName() ) );
375                    queryElement.appendChild( elementSetNameElement );
376                }
377    
378                // '<csw:ElementName>'-elements (optional)
379                if ( query.getElementNamesAsPropertyPaths() != null ) {
380                    List<PropertyPath> elementNames = query.getElementNamesAsPropertyPaths();
381                    for ( int j = 0; j < elementNames.size(); j++ ) {
382                        Element elementNameElement = doc.createElementNS( localCSWNS.toString(), "csw:ElementName" );
383                        elementNameElement.appendChild( doc.createTextNode( elementNames.get( j ).getAsString() ) );
384                        queryElement.appendChild( elementNameElement );
385                    }
386                }
387    
388                // '<csw:Constraint>'-element (optional)
389                if ( query.getContraint() != null ) {
390                    Element constraintElement = doc.createElementNS( localCSWNS.toString(), "csw:Constraint" );
391                    constraintElement.setAttribute( "version", "1.1.0" );
392                    org.deegree.model.filterencoding.XMLFactory.appendFilter( constraintElement, query.getContraint() );
393                    queryElement.appendChild( constraintElement );
394                }
395    
396                // '<ogc:SortBy>'-element (optional)
397                SortProperty[] sortProperties = query.getSortProperties();
398                if ( sortProperties != null && sortProperties.length != 0 ) {
399                    Element sortByElement = doc.createElementNS( OGCNS.toString(), "ogc:SortBy" );
400    
401                    // '<ogc:SortProperty>'-elements
402                    for ( int j = 0; j < sortProperties.length; j++ ) {
403                        Element sortPropertiesElement = doc.createElementNS( OGCNS.toString(), "ogc:SortProperty" );
404    
405                        // '<ogc:PropertyName>'-element (required)
406                        Element propertyNameElement = doc.createElementNS( OGCNS.toString(), "ogc:PropertyName" );
407                        appendPropertyPath( propertyNameElement, sortProperties[j].getSortProperty() );
408    
409                        // '<ogc:SortOrder>'-element (optional)
410                        Element sortOrderElement = doc.createElementNS( OGCNS.toString(), "ogc:SortOrder" );
411                        Node tn = doc.createTextNode( sortProperties[j].getSortOrder() ? "ASC" : "DESC" );
412                        sortOrderElement.appendChild( tn );
413    
414                        sortPropertiesElement.appendChild( propertyNameElement );
415                        sortPropertiesElement.appendChild( sortOrderElement );
416                        sortByElement.appendChild( sortPropertiesElement );
417                    }
418                    queryElement.appendChild( sortByElement );
419                }
420                rootElement.appendChild( queryElement );
421            }
422            return getRecordsDocument;
423        }
424    
425        /**
426         * Exports a <code>GetRecords</code> instance to a <code>GetRecordsDocument</code>.
427         * 
428         * @param request
429         * @return DOM representation of the <code>GetRecords</code>
430         * @throws XMLException
431         *             if some elements could not be appended
432         * @throws OGCWebServiceException
433         *             if an error occurred while creating the xml-representation of the GetRecords bean.
434         */
435        public static GetRecordsDocument export( GetRecords request )
436                                throws XMLException, OGCWebServiceException {
437    
438            GetRecordsDocument getRecordsDocument = null;
439            
440            getRecordsDocument = new GetRecordsDocument();
441            try {
442                getRecordsDocument.createEmptyDocument();
443            } catch ( Exception e ) {
444                throw new XMLException( e.getMessage() );
445            }
446            Element rootElement = getRecordsDocument.getRootElement();
447            Document doc = rootElement.getOwnerDocument();
448    
449            // 'version'-attribute
450            rootElement.setAttribute( "version", request.getVersion() );
451    
452            // 'resultType'-attribute
453            rootElement.setAttribute( "resultType", request.getResultTypeAsString() );
454    
455            // 'outputFormat'-attribute
456            rootElement.setAttribute( "outputFormat", request.getOutputFormat() );
457    
458            // 'outputSchema'-attribute
459            rootElement.setAttribute( "outputSchema", request.getOutputSchema() );
460    
461            // 'startPosition'-attribute
462            rootElement.setAttribute( "startPosition", "" + request.getStartPosition() );
463    
464            // 'maxRecords'-attribute
465            rootElement.setAttribute( "maxRecords", "" + request.getMaxRecords() );
466    
467            // '<csw:DistributedSearch>'-element
468            if ( request.getHopCount() != -1 ) {
469                Element distributedSearchElement = doc.createElementNS( CSWNS.toString(), "csw:DistributedSearch" );
470    
471                // 'hopCount'-attribute
472                distributedSearchElement.setAttribute( "hopCount", "" + request.getHopCount() );
473                rootElement.appendChild( distributedSearchElement );
474            }
475    
476            // '<csw:ResponseHandler>'-elements (optional)
477            URI responseHandler = request.getResponseHandler();
478            if ( responseHandler != null ) {
479                Element responseHandlerElement = doc.createElementNS( CSWNS.toString(), "csw:ResponseHandler" );
480                responseHandlerElement.appendChild( doc.createTextNode( responseHandler.toASCIIString() ) );
481                rootElement.appendChild( responseHandlerElement );
482    
483            }
484    
485            // '<csw:Query>'-elements (required)
486            Query query = request.getQuery();
487            if ( query != null ) {
488                LOG.logDebug( "Adding the csw:Query element to the csw:GetRecords document" );
489                Element queryElement = doc.createElementNS( CSWNS.toString(), "csw:Query" );
490    
491                // 'typeName'-attribute
492                // Testing for the list of typenames.
493                List<QualifiedName> typeNames = query.getTypeNamesAsList();
494                Map<String, QualifiedName> aliases = new HashMap<String, QualifiedName>(
495                                                                                         query.getDeclaredTypeNameVariables() );
496                if ( typeNames.size() > 0 ) {
497                    appendTypeNamesAttribute( rootElement, queryElement, typeNames, aliases );
498                } else {
499    
500                    String s = StringTools.arrayToString( query.getTypeNames(), ',' );
501                    queryElement.setAttribute( "typeNames", s );
502                }
503    
504                // '<csw:ElementSetName>'-element (optional)
505                if ( query.getElementSetName() != null ) {
506                    Element elementSetNameElement = doc.createElementNS( CSWNS.toString(), "csw:ElementSetName" );
507                    List<QualifiedName> elementSetNameTypeNamesList = query.getElementSetNameTypeNamesList();
508                    if ( query.getElementSetNameVariables() != null && query.getElementSetNameVariables().size() > 0 ) {
509                        throw new OGCWebServiceException(
510                                                          "The elementSetName element in a csw:GetRecords request may not refrerence variables (aka. aliases), aborting request" );
511                    }
512                    if ( elementSetNameTypeNamesList.size() > 0 ) {
513                        appendTypeNamesAttribute( rootElement, elementSetNameElement, elementSetNameTypeNamesList, null );
514                    }
515                    elementSetNameElement.appendChild( doc.createTextNode( query.getElementSetName() ) );
516                    queryElement.appendChild( elementSetNameElement );
517                }
518    
519                // '<csw:ElementName>'-elements (optional)
520                if ( query.getElementNamesAsPropertyPaths() != null ) {
521                    List<PropertyPath> elementNames = query.getElementNamesAsPropertyPaths();
522                    for ( int j = 0; j < elementNames.size(); j++ ) {
523                        Element elementNameElement = doc.createElementNS( CSWNS.toString(), "csw:ElementName" );
524                        elementNameElement.appendChild( doc.createTextNode( elementNames.get( j ).getAsString() ) );
525                        queryElement.appendChild( elementNameElement );
526                    }
527                }
528    
529                // '<csw:Constraint>'-element (optional)
530                if ( query.getContraint() != null ) {
531                    Element constraintElement = doc.createElementNS( CSWNS.toString(), "csw:Constraint" );
532                    constraintElement.setAttribute( "version", "1.1.0" );
533                    org.deegree.model.filterencoding.XMLFactory.appendFilter( constraintElement, query.getContraint() );
534                    queryElement.appendChild( constraintElement );
535                }
536    
537                // '<ogc:SortBy>'-element (optional)
538                SortProperty[] sortProperties = query.getSortProperties();
539                if ( sortProperties != null && sortProperties.length != 0 ) {
540                    Element sortByElement = doc.createElementNS( OGCNS.toString(), "ogc:SortBy" );
541    
542                    // '<ogc:SortProperty>'-elements
543                    for ( int j = 0; j < sortProperties.length; j++ ) {
544                        Element sortPropertiesElement = doc.createElementNS( OGCNS.toString(), "ogc:SortProperty" );
545    
546                        // '<ogc:PropertyName>'-element (required)
547                        Element propertyNameElement = doc.createElementNS( OGCNS.toString(), "ogc:PropertyName" );
548                        appendPropertyPath( propertyNameElement, sortProperties[j].getSortProperty() );
549    
550                        // '<ogc:SortOrder>'-element (optional)
551                        Element sortOrderElement = doc.createElementNS( OGCNS.toString(), "ogc:SortOrder" );
552                        Node tn = doc.createTextNode( sortProperties[j].getSortOrder() ? "ASC" : "DESC" );
553                        sortOrderElement.appendChild( tn );
554    
555                        sortPropertiesElement.appendChild( propertyNameElement );
556                        sortPropertiesElement.appendChild( sortOrderElement );
557                        sortByElement.appendChild( sortPropertiesElement );
558                    }
559                    queryElement.appendChild( sortByElement );
560                }
561                rootElement.appendChild( queryElement );
562            }
563            return getRecordsDocument;
564        }
565    
566        /**
567         * 
568         * @param rootElement
569         *            the first node of the Docuement
570         * @param toBeInserted
571         *            to which the typeNames attribute will be appended
572         * @param typeNames
573         *            to be inserted into the toBeInserted element
574         * @param aliases
575         *            may be <code>null</code>, if not each typeName must have exactly one alias defined, which will be
576         *            inserted after the typename (e.g. typename=$o). If map must contain a mapping from variable to
577         *            qualifiedName (e.g. [o, typeName]);
578         */
579        protected static void appendTypeNamesAttribute( Element rootElement, Element toBeInserted,
580                                                        List<QualifiedName> typeNames, Map<String, QualifiedName> aliases ) {
581            if ( !typeNames.isEmpty() ) {
582                for ( QualifiedName qName : typeNames ) {
583                    LOG.logDebug( "found typeName: " + qName );
584                }
585                LOG.logDebug( "for the element: " + toBeInserted.getNodeName()
586                              + " we are trying to set the typeNames attribute." );
587                StringBuffer sb = new StringBuffer();
588                int count = 0;
589                for ( QualifiedName qName : typeNames ) {
590                    if ( qName.getLocalName() != null ) {
591                        URI ns = qName.getNamespace();
592                        String prefix = qName.getPrefix();
593                        if ( ns != null && prefix != null ) {
594                            URI boundNS = null;
595                            try {
596                                boundNS = XMLTools.getNamespaceForPrefix( prefix, toBeInserted );
597                            } catch ( URISyntaxException e ) {
598                                // why for crying out loud an UriSyntax exception while lookin up stuff
599                                // (without giving
600                                // an
601                                // uri).
602                            }
603                            LOG.logDebug( "ElementSetName/@typeNames: Found the namespace " + boundNS + " for the prefix: "
604                                          + prefix + " from typename (localname) : " + qName.getLocalName() );
605                            if ( boundNS == null ) {
606                                if ( CommonNamespaces.OASIS_EBRIMNS.equals( ns ) ) {
607                                    XMLTools.appendNSBinding( rootElement, "rim", ns );
608                                    LOG.logDebug( toBeInserted.getLocalName()
609                                                  + "/@typeName: While no namespace was bound to the prefix: " + prefix
610                                                  + " the namespace: " + ns
611                                                  + " has been bound to 'rim' in the the root element." );
612                                } else {
613                                    XMLTools.appendNSBinding( rootElement, prefix, ns );
614                                    LOG.logDebug( toBeInserted.getLocalName()
615                                                  + "/@typeName: While no namespace was bound to the prefix: " + prefix
616                                                  + " the namespace: " + ns + " has been bound to '" + prefix
617                                                  + "' in the the root element." );
618                                }
619    
620                            }
621                        }
622                        String typeName = prefix;
623                        if ( typeName != null ) {
624                            typeName += ":" + qName.getLocalName();
625                        }
626                        sb.append( typeName );
627                        if ( aliases != null ) {
628                            if ( aliases.containsValue( qName ) ) {
629                                Set<String> keys = aliases.keySet();
630                                for ( String key : keys ) {
631                                    if ( aliases.get( key ).equals( qName ) ) {
632                                        sb.append( "=" ).append( key );
633                                        aliases.remove( key );
634                                        break;
635                                    }
636                                }
637                            } else if ( aliases.size() > 0 ) {
638                                LOG.logError( "No variable mapping found for typename: "
639                                              + typeName
640                                              + " this may not be, because every single typename must or no typename may have a variable!" );
641                            }
642                        }
643                        if ( ++count < typeNames.size() ) {
644                            sb.append( " " );
645                        }
646                    }
647                }
648                if ( !"null".equals( sb.toString().trim() ) && !"".equals( sb.toString().trim() ) ) {
649                    LOG.logDebug( "for the element: " + toBeInserted.getNodeName()
650                                  + " we are settin the typeNames attribute to: " + sb.toString() );
651                    toBeInserted.setAttribute( "typeNames", sb.toString() );
652                }
653            }
654        }
655    
656        /**
657         * Exports a <code>GetRecordById</code> instance to a <code>GetRecordByIdDocument</code>.
658         * 
659         * @param request
660         * @return DOM representation of the <code>GetRecordById</code>
661         * @throws XMLException
662         *             if XML template could not be loaded
663         */
664        public static GetRecordByIdDocument export( GetRecordById request )
665                                throws XMLException {
666    
667            GetRecordByIdDocument getRecordByIdDoc = new GetRecordByIdDocument();
668            try {
669                getRecordByIdDoc.createEmptyDocument();
670            } catch ( Exception e ) {
671                throw new XMLException( e.getMessage() );
672            }
673            Element rootElement = getRecordByIdDoc.getRootElement();
674            Document doc = rootElement.getOwnerDocument();
675    
676            // 'version'-attribute
677            rootElement.setAttribute( "version", request.getVersion() );
678    
679            String[] ids = request.getIds();
680            for ( int i = 0; i < ids.length; i++ ) {
681                Element idElement = doc.createElementNS( CSWNS.toString(), "csw:Id" );
682                idElement.appendChild( doc.createTextNode( ids[i] ) );
683                rootElement.appendChild( idElement );
684            }
685    
686            String elementSetName = request.getElementSetName();
687            if ( elementSetName != null ) {
688                Element esnElement = doc.createElementNS( CSWNS.toString(), "csw:ElementSetName" );
689                esnElement.appendChild( doc.createTextNode( elementSetName ) );
690                rootElement.appendChild( esnElement );
691            }
692    
693            return getRecordByIdDoc;
694        }
695    }