001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/trunk/src/org/deegree/ogcwebservices/wfs/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.wfs;
037    
038    import static org.deegree.framework.xml.XMLTools.appendElement;
039    import static org.deegree.ogcbase.CommonNamespaces.OGCNS;
040    import static org.deegree.ogcbase.CommonNamespaces.OGC_PREFIX;
041    import static org.deegree.ogcbase.CommonNamespaces.WFS_PREFIX;
042    
043    import java.io.IOException;
044    import java.io.StringReader;
045    import java.net.URI;
046    import java.net.URL;
047    import java.util.List;
048    
049    import org.deegree.datatypes.QualifiedName;
050    import org.deegree.datatypes.xlink.SimpleLink;
051    import org.deegree.enterprise.DeegreeParams;
052    import org.deegree.framework.log.ILogger;
053    import org.deegree.framework.log.LoggerFactory;
054    import org.deegree.framework.util.StringTools;
055    import org.deegree.framework.xml.XMLFragment;
056    import org.deegree.framework.xml.XMLParsingException;
057    import org.deegree.framework.xml.XMLTools;
058    import org.deegree.i18n.Messages;
059    import org.deegree.io.datastore.FeatureId;
060    import org.deegree.model.filterencoding.Function;
061    import org.deegree.model.filterencoding.capabilities.FilterCapabilities;
062    import org.deegree.model.metadata.iso19115.Keywords;
063    import org.deegree.model.metadata.iso19115.Linkage;
064    import org.deegree.model.metadata.iso19115.OnlineResource;
065    import org.deegree.model.spatialschema.Envelope;
066    import org.deegree.model.spatialschema.Position;
067    import org.deegree.ogcbase.CommonNamespaces;
068    import org.deegree.ogcbase.PropertyPath;
069    import org.deegree.ogcbase.SortProperty;
070    import org.deegree.ogcwebservices.getcapabilities.DCPType;
071    import org.deegree.ogcwebservices.getcapabilities.HTTP;
072    import org.deegree.ogcwebservices.getcapabilities.MetadataURL;
073    import org.deegree.ogcwebservices.getcapabilities.OperationsMetadata;
074    import org.deegree.ogcwebservices.getcapabilities.Protocol;
075    import org.deegree.ogcwebservices.getcapabilities.ServiceIdentification;
076    import org.deegree.ogcwebservices.getcapabilities.ServiceProvider;
077    import org.deegree.ogcwebservices.wfs.capabilities.FeatureTypeList;
078    import org.deegree.ogcwebservices.wfs.capabilities.Operation;
079    import org.deegree.ogcwebservices.wfs.capabilities.WFSCapabilitiesDocument;
080    import org.deegree.ogcwebservices.wfs.capabilities.WFSFeatureType;
081    import org.deegree.ogcwebservices.wfs.capabilities.WFSOperationsMetadata;
082    import org.deegree.ogcwebservices.wfs.configuration.WFSConfiguration;
083    import org.deegree.ogcwebservices.wfs.operation.GetFeature;
084    import org.deegree.ogcwebservices.wfs.operation.GetFeatureDocument;
085    import org.deegree.ogcwebservices.wfs.operation.LockFeatureResponse;
086    import org.deegree.ogcwebservices.wfs.operation.Query;
087    import org.deegree.ogcwebservices.wfs.operation.GetFeature.RESULT_TYPE;
088    import org.deegree.ogcwebservices.wfs.operation.transaction.Insert;
089    import org.deegree.ogcwebservices.wfs.operation.transaction.InsertResults;
090    import org.deegree.ogcwebservices.wfs.operation.transaction.Transaction;
091    import org.deegree.ogcwebservices.wfs.operation.transaction.TransactionOperation;
092    import org.deegree.ogcwebservices.wfs.operation.transaction.TransactionResponse;
093    import org.deegree.ogcwebservices.wfs.operation.transaction.Update;
094    import org.w3c.dom.Document;
095    import org.w3c.dom.Element;
096    import org.w3c.dom.Text;
097    import org.xml.sax.SAXException;
098    
099    /**
100     * Responsible for the generation of XML representations of objects from the WFS context that comply to WFS
101     * specification 1.0.0.
102     *
103     * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a>
104     * @author last edited by: $Author:$
105     *
106     * @version $Revision:$, $Date:$
107     */
108    public class XMLFactory_1_0_0 {
109    
110        private static final URI WFSNS = CommonNamespaces.WFSNS;
111    
112        // private static final String PRE_WFS = CommonNamespaces.WFS_PREFIX + ":";
113    
114        private static final ILogger LOG = LoggerFactory.getLogger( XMLFactory_1_0_0.class );
115    
116        private static XMLFactory_1_0_0 factory = null;
117    
118        /**
119         * @return a cached instance of this XMLFactory.
120         */
121        public static synchronized XMLFactory_1_0_0 getInstance() {
122            if ( factory == null ) {
123                factory = new XMLFactory_1_0_0();
124            }
125            return factory;
126        }
127    
128        /**
129         * Exports a <code>WFSCapabilities</code> instance to a <code>WFSCapabilitiesDocument</code> with version 1_0.
130         *
131         * @param config
132         * @return DOM representation of the <code>WFSCapabilities</code>
133         */
134        public WFSCapabilitiesDocument export( WFSConfiguration config ) {
135    
136            WFSCapabilitiesDocument capabilitiesDocument = new WFSCapabilitiesDocument();
137    
138            capabilitiesDocument.createEmptyDocument( "1.0.0" );
139            Element root = capabilitiesDocument.getRootElement();
140    
141            // Find the default online resource
142            DeegreeParams deegreeParams = config.getDeegreeParams();
143            String defaultOnlineResource = "http://localhost:8080/deegree/services";
144            if ( deegreeParams != null ) {
145                OnlineResource or = deegreeParams.getDefaultOnlineResource();
146                if ( or != null ) {
147                    Linkage link = or.getLinkage();
148                    if ( link != null ) {
149                        URL uri = link.getHref();
150                        if ( uri != null ) {
151                            String tmp = uri.toExternalForm();
152                            if ( !"".equals( tmp.toString() ) ) {
153                                defaultOnlineResource = tmp;
154                            }
155                        }
156                    }
157                }
158            }
159    
160            ServiceIdentification serviceIdentification = config.getServiceIdentification();
161            ServiceProvider serviceProvider = config.getServiceProvider();
162            OperationsMetadata operationsMetadata = config.getOperationsMetadata();
163            FeatureTypeList featureTypeList = config.getFeatureTypeList();
164    
165            if ( serviceIdentification != null ) {
166                String onlineResource = null;
167                if ( serviceProvider != null ) {
168                    SimpleLink resource = serviceProvider.getProviderSite();
169                    URI online = resource.getHref();
170                    onlineResource = online.toASCIIString();
171                } else {
172                    onlineResource = defaultOnlineResource;
173                }
174                appendService( root, serviceIdentification, onlineResource );
175            }
176    
177            // Add the capabilities element
178            if ( operationsMetadata != null ) {
179                appendCapability( root, (WFSOperationsMetadata) operationsMetadata, defaultOnlineResource );
180            }
181    
182            if ( featureTypeList != null ) {
183                appendFeatureTypeList( root, featureTypeList );
184            }
185    
186            FilterCapabilities filterCapabilities = config.getFilterCapabilities();
187            if ( filterCapabilities != null ) {
188                org.deegree.model.filterencoding.XMLFactory.appendFilterCapabilities100( root, filterCapabilities );
189            }
190            return capabilitiesDocument;
191        }
192    
193        /**
194         * Appends the DOM representation of the {@link ServiceIdentification} section to the passed {@link Element}.
195         *
196         * @param root
197         *            to which to append the service section.
198         * @param serviceIdentification
199         *            bean of the service identification element.
200         * @param onlineResource
201         *            to be used as a default onlineResource.
202         */
203        private void appendService( Element root, ServiceIdentification serviceIdentification, String onlineResource ) {
204    
205            // 'Service'-element
206            Element service = XMLTools.appendElement( root, WFSNS, "Service" );
207    
208            // 'Name'-element
209            String tmpValue = serviceIdentification.getName();
210            tmpValue = checkForEmptyValue( tmpValue, "Name", "WFS" );
211            XMLTools.appendElement( service, WFSNS, "Name", tmpValue );
212    
213            tmpValue = serviceIdentification.getTitle();
214            tmpValue = checkForEmptyValue( tmpValue, "Title", "A Web Feature Service" );
215            XMLTools.appendElement( service, WFSNS, "Title", tmpValue );
216    
217            tmpValue = serviceIdentification.getAbstract();
218            if ( tmpValue != null && "".equals( tmpValue.trim() ) ) {
219                XMLTools.appendElement( service, WFSNS, "Abstract", tmpValue );
220            }
221    
222            Keywords[] keywords = serviceIdentification.getKeywords();
223            appendKeyWords( service, keywords );
224    
225            XMLTools.appendElement( service, WFSNS, "OnlineResource", onlineResource );
226    
227            tmpValue = serviceIdentification.getFees();
228            if ( tmpValue == null || "".equals( tmpValue.trim() ) ) {
229                tmpValue = "NONE";
230            }
231            XMLTools.appendElement( service, WFSNS, "Fees", tmpValue );
232    
233            String[] constraints = serviceIdentification.getAccessConstraints();
234            StringBuffer sb = new StringBuffer();
235            if ( constraints == null || constraints.length > 0 ) {
236                sb.append( "NONE" );
237            } else {
238                for ( int i = 0; i < constraints.length; ++i ) {
239                    String constraint = constraints[i];
240                    if ( constraint != null && "".equals( constraint.trim() ) ) {
241                        sb.append( constraint );
242                        if ( ( i + 1 ) < constraints.length ) {
243                            sb.append( " " );
244                        }
245                    }
246                }
247                if ( sb.length() == 0 ) {
248                    sb.append( "NONE" );
249                }
250            }
251            XMLTools.appendElement( service, WFSNS, "AccessConstraints", sb.toString() );
252    
253        }
254    
255        /**
256         * Appends the wfs:Capability element to the root element
257         *
258         * @param root
259         * @param operationsMetadata
260         */
261        private void appendCapability( Element root, WFSOperationsMetadata operationsMetadata, String defaultOnlineResource ) {
262            Element capability = XMLTools.appendElement( root, WFSNS, "Capability" );
263            Element request = XMLTools.appendElement( capability, WFSNS, "Request" );
264            org.deegree.ogcwebservices.getcapabilities.Operation[] ops = operationsMetadata.getOperations();
265            if ( ops != null && ops.length > 0 ) {
266                for ( org.deegree.ogcwebservices.getcapabilities.Operation op : ops ) {
267                    String name = op.getName();
268                    if ( !( name == null || "".equals( name.trim() ) || "GetGMLObject".equals( name.trim() ) ) ) {
269                        name = name.trim();
270    
271                        Element operation = XMLTools.appendElement( request, WFSNS, name );
272                        if ( "DescribeFeatureType".equalsIgnoreCase( name ) ) {
273                            Element sdl = XMLTools.appendElement( operation, WFSNS, "SchemaDescriptionLanguage" );
274                            XMLTools.appendElement( sdl, WFSNS, "XMLSCHEMA" );
275                        } else if ( "GetFeature".equalsIgnoreCase( name ) || "GetFeatureWithLock".equalsIgnoreCase( name ) ) {
276                            Element resultFormat = XMLTools.appendElement( operation, WFSNS, "ResultFormat" );
277                            XMLTools.appendElement( resultFormat, WFSNS, "GML2" );
278                        }
279                        DCPType[] dcpTypes = op.getDCPs();
280                        if ( dcpTypes != null && dcpTypes.length > 0 ) {
281                            for ( DCPType dcpType : dcpTypes ) {
282                                appendDCPType( operation, dcpType, defaultOnlineResource );
283                            }
284                        } else {
285                            appendDCPType( operation, null, defaultOnlineResource );
286                        }
287                    }
288                }
289            }
290    
291        }
292    
293        /**
294         * Appends the XML representation of the <code>wfs:FeatureTypeList</code>- section to the passed
295         * <code>Element</code>.
296         *
297         * @param root
298         * @param featureTypeList
299         */
300        private void appendFeatureTypeList( Element root, FeatureTypeList featureTypeList ) {
301            Element featureTypeListNode = XMLTools.appendElement( root, WFSNS, "FeatureTypeList" );
302            Operation[] operations = featureTypeList.getGlobalOperations();
303            if ( operations != null ) {
304                Element operationsNode = XMLTools.appendElement( featureTypeListNode, WFSNS, "Operations" );
305                for ( int i = 0; i < operations.length; i++ ) {
306                    XMLTools.appendElement( operationsNode, WFSNS, operations[i].getOperation() );
307                }
308            }
309            WFSFeatureType[] featureTypes = featureTypeList.getFeatureTypes();
310            if ( featureTypes != null ) {
311                for ( int i = 0; i < featureTypes.length; i++ ) {
312                    appendWFSFeatureType( featureTypeListNode, featureTypes[i] );
313                }
314            }
315        }
316    
317        /**
318         * Appends the DCPType in the WFS namespace... pre-ows stuff.
319         *
320         * @param operation
321         *            to add the dcptype to.
322         * @param type
323         *            a bean containing necessary information if <code>null</code> a http-get/post dcp with the
324         *            defaultonline resource will be inserted.
325         * @param defaultOnlineResource
326         *            if no dcpType is given or no URL were inserted in the config, this will be inserted.
327         */
328        private void appendDCPType( Element operation, DCPType type, String defaultOnlineResource ) {
329            Element dcpType = XMLTools.appendElement( operation, WFSNS, "DCPType" );
330            Element http = XMLTools.appendElement( dcpType, WFSNS, "HTTP" );
331            boolean appendDefaultProtocol = true;
332            if ( type != null ) {
333                Protocol pr = type.getProtocol();
334                if ( pr != null ) {
335                    if ( pr instanceof HTTP ) {
336                        HTTP prot = (HTTP) pr;
337                        URL[] getters = prot.getGetOnlineResources();
338                        URL[] posters = prot.getPostOnlineResources();
339                        if ( ( getters != null && getters.length > 0 ) ) {
340                            for ( URL get : getters ) {
341                                appendGetURL( http, get.toExternalForm() );
342                            }
343                        } else {
344                            appendGetURL( http, defaultOnlineResource );
345                        }
346                        if ( posters != null && posters.length > 0 ) {
347                            for ( URL post : posters ) {
348                                appendPostURL( http, post.toExternalForm() );
349                            }
350                        } else {
351                            appendPostURL( http, defaultOnlineResource );
352                        }
353                        appendDefaultProtocol = false;
354                    }
355                }
356            }
357            if ( appendDefaultProtocol ) {
358                appendGetURL( http, defaultOnlineResource );
359                appendPostURL( http, defaultOnlineResource );
360            }
361        }
362    
363        private void appendGetURL( Element http, String resourceURL ) {
364            Element get = XMLTools.appendElement( http, WFSNS, "Get" );
365            get.setAttribute( "onlineResource", resourceURL );
366        }
367    
368        private void appendPostURL( Element http, String resourceURL ) {
369            Element post = XMLTools.appendElement( http, WFSNS, "Post" );
370            post.setAttribute( "onlineResource", resourceURL );
371        }
372    
373        private void appendKeyWords( Element root, Keywords[] keywords ) {
374            if ( keywords != null && keywords.length > 0 ) {
375                StringBuffer sb = new StringBuffer();
376                for ( int k = 0; k < keywords.length; ++k ) {
377                    String[] words = keywords[k].getKeywords();
378                    if ( words != null && words.length > 0 ) {
379                        for ( int i = 0; i < words.length; ++i ) {
380                            sb.append( words[i] );
381                            if ( ( i + 1 ) < words.length ) {
382                                sb.append( " " );
383                            }
384                        }
385                    }
386                    if ( ( k + 1 ) < keywords.length ) {
387                        sb.append( " " );
388                    }
389                }
390                XMLTools.appendElement( root, WFSNS, "Keywords", sb.toString() );
391            }
392        }
393    
394        /**
395         * Appends the XML representation of the <code>WFSFeatureType</code> instance to the passed <code>Element</code>.
396         *
397         * @param root
398         * @param featureType
399         */
400        private void appendWFSFeatureType( Element root, WFSFeatureType featureType ) {
401    
402            Element featureTypeNode = XMLTools.appendElement( root, WFSNS, "FeatureType" );
403    
404            if ( featureType.getName().getPrefix() != null ) {
405                XMLTools.appendNSBinding( featureTypeNode, featureType.getName().getPrefix(),
406                                          featureType.getName().getNamespace() );
407            }
408            XMLTools.appendElement( featureTypeNode, WFSNS, "Name", featureType.getName().getPrefixedName() );
409            XMLTools.appendElement( featureTypeNode, WFSNS, "Title", featureType.getTitle() );
410            String tmpValue = featureType.getAbstract();
411            if ( tmpValue != null && !"".equals( tmpValue.trim() ) ) {
412                XMLTools.appendElement( featureTypeNode, WFSNS, "Abstract", tmpValue );
413            }
414            Keywords[] keywords = featureType.getKeywords();
415            appendKeyWords( featureTypeNode, keywords );
416    
417            URI defaultSrs = featureType.getDefaultSRS();
418            if ( defaultSrs != null ) {
419                XMLTools.appendElement( featureTypeNode, WFSNS, "SRS", defaultSrs.toASCIIString() );
420            } else if ( featureType.getOtherSrs() != null && featureType.getOtherSrs().length > 0 ) {
421                for ( URI srs : featureType.getOtherSrs() ) {
422                    if ( srs != null ) {
423                        XMLTools.appendElement( featureTypeNode, WFSNS, "SRS", srs.toASCIIString() );
424                        break;
425                    }
426                }
427            } else {
428                // defaulting to EPSG:4326 is this correct????
429                LOG.logDebug( "Found no default- or other-SRS, setting to EPGS:4326, is this correct?" );
430                XMLTools.appendElement( featureTypeNode, WFSNS, "SRS", "EPSG:4326" );
431            }
432            Operation[] operations = featureType.getOperations();
433            if ( operations != null && operations.length > 0 ) {
434                Element opEl = XMLTools.appendElement( featureTypeNode, WFSNS, "Operations" );
435                for ( Operation op : operations ) {
436                    if ( op != null && !"".equals( op.getOperation().trim() )
437                         && !"GetGMLObject".equalsIgnoreCase( op.getOperation().trim() ) ) {
438                        XMLTools.appendElement( opEl, WFSNS, op.getOperation().trim() );
439    
440                    }
441                }
442            }
443    
444            Envelope[] wgs84BoundingBoxes = featureType.getWgs84BoundingBoxes();
445            for ( Envelope bbox : wgs84BoundingBoxes ) {
446                if ( bbox != null && bbox.getMin().getCoordinateDimension() == 2 ) {
447                    Element latlon = XMLTools.appendElement( featureTypeNode, WFSNS, "LatLongBoundingBox" );
448                    Position min = bbox.getMin();
449                    Position max = bbox.getMax();
450                    latlon.setAttribute( "minx", Double.toString( min.getX() ) );
451                    latlon.setAttribute( "miny", Double.toString( min.getY() ) );
452                    latlon.setAttribute( "maxx", Double.toString( max.getX() ) );
453                    latlon.setAttribute( "maxy", Double.toString( max.getY() ) );
454                }
455            }
456            MetadataURL[] mdURLs = featureType.getMetadataUrls();
457            if ( mdURLs != null ) {
458                for ( MetadataURL mdURL : mdURLs ) {
459                    if ( mdURL != null && mdURL.getOnlineResource() != null ) {
460                        // first check if the format and type are acceptable.
461                        String format = mdURL.getFormat();
462                        boolean formatOK = true;
463                        if ( format != null ) {
464                            if ( "text/xml".equals( format ) ) {
465                                format = "XML";
466                            } else if ( "text/sgml".equals( format ) ) {
467                                format = "SGML";
468                            } else if ( "text/plain".equals( format ) ) {
469                                format = "TXT";
470                            } else {
471                                formatOK = false;
472                            }
473                        }
474    
475                        String type = mdURL.getType();
476                        boolean typeOK = true;
477                        if ( type != null ) {
478                            if ( !( "TC211".equals( type ) || "FGDC".equals( type ) ) ) {
479                                typeOK = false;
480                            }
481                        }
482                        if ( formatOK && typeOK ) {
483                            Element metadata = XMLTools.appendElement( featureTypeNode, WFSNS, "MetadataURL",
484                                                                       mdURL.getOnlineResource().toExternalForm() );
485                            metadata.setAttribute( "type", type );
486                            metadata.setAttribute( "format", format );
487                        }
488                    }
489                }
490            }
491        }
492    
493        private String checkForEmptyValue( String value, String elementName, String defaultValue ) {
494            if ( value == null || "".equals( value.trim() ) ) {
495                LOG.logError( Messages.getMessage( "WFS_MISSING_REQUIRED_ELEMENT", elementName ) );
496                value = defaultValue;
497            }
498            return value;
499        }
500    
501        /**
502         * @param response
503         * @return a WFS 1.0.0 style response
504         */
505        public static XMLFragment export( TransactionResponse response ) {
506    
507            XMLFragment doc = new XMLFragment( new QualifiedName( WFS_PREFIX, "WFS_TransactionResponse", WFSNS ) );
508    
509            Element root = doc.getRootElement();
510            root.setAttribute( "version", "1.0.0" );
511    
512            if ( response.getInsertResults().size() > 0 ) {
513                Element e = appendElement( root, WFSNS, WFS_PREFIX + ":InsertResult" );
514                // this is obviously not always correct, but it works around limitations in the test scripts...
515                e.setAttribute( "handle", response.getInsertResults().get( 0 ).getHandle() );
516                for ( InsertResults res : response.getInsertResults() ) {
517                    for ( FeatureId fid : res.getFeatureIDs() ) {
518                        appendElement( e, OGCNS, OGC_PREFIX + ":FeatureId" ).setAttribute( "fid", fid.getAsString() );
519                    }
520                }
521            }
522    
523            Element e = appendElement( root, WFSNS, WFS_PREFIX + ":TransactionResult" );
524            List<Exception> exceptions = response.getExceptions();
525            String status = exceptions == null || exceptions.size() == 0 ? "SUCCESS" : null;
526    
527            if ( exceptions != null && exceptions.size() > 0
528                 && ( response.getTotalDeleted() > 0 || response.getTotalInserted() > 0 || response.getTotalUpdated() > 0 ) ) {
529                status = "PARTIAL";
530            }
531            if ( exceptions != null && exceptions.size() > 0
532                 && ( response.getTotalDeleted() + response.getTotalInserted() + response.getTotalUpdated() == 0 ) ) {
533                status = "FAILED";
534            }
535    
536            if ( response.getTotalDeleted() + response.getTotalInserted() + response.getTotalUpdated() == 0 ) {
537                Transaction t = (Transaction) response.getRequest();
538                boolean delOnly = true;
539                for ( TransactionOperation op : t.getOperations() ) {
540                    if ( op instanceof Update || op instanceof Insert ) {
541                        delOnly = false;
542                    }
543                }
544                status = delOnly ? "SUCCESS" : "FAILED";
545            }
546    
547            e = appendElement( e, WFSNS, WFS_PREFIX + ":Status" );
548            appendElement( e, WFSNS, WFS_PREFIX + ":" + ( status == null ? "FAILED" : status ) );
549    
550            return doc;
551        }
552    
553        /**
554         * @param response
555         * @return a WFS 1.0.0 style lock feature response
556         */
557        public static XMLFragment export( LockFeatureResponse response ) {
558            XMLFragment doc = new XMLFragment( new QualifiedName( WFS_PREFIX, "WFS_LockFeatureResponse", WFSNS ) );
559    
560            Element root = doc.getRootElement();
561    
562            appendElement( root, WFSNS, WFS_PREFIX + ":LockId", response.getLockId() );
563            if ( response.getFeaturesLocked() != null && response.getFeaturesLocked().length > 0 ) {
564                Element e = appendElement( root, WFSNS, WFS_PREFIX + ":FeaturesLocked" );
565                for ( String fid : response.getFeaturesLocked() ) {
566                    appendElement( e, OGCNS, OGC_PREFIX + ":FeatureId" ).setAttribute( "fid", fid );
567                }
568            }
569            if ( response.getFeaturesNotLocked() != null && response.getFeaturesNotLocked().length > 0 ) {
570                Element e = appendElement( root, WFSNS, WFS_PREFIX + ":FeaturesNotLocked" );
571                for ( String fid : response.getFeaturesNotLocked() ) {
572                    appendElement( e, OGCNS, OGC_PREFIX + ":FeatureId" ).setAttribute( "fid", fid );
573                }
574            }
575    
576            return doc;
577        }
578    
579        /**
580         * Exports a <code>GetFeature</code> instance to a <code>GetFeatureDocument</code>.
581         *
582         * @param getFeature
583         *            request to be exported
584         * @return XML representation of the <code>GetFeature</code> request
585         * @throws IOException
586         * @throws XMLParsingException
587         */
588        public static GetFeatureDocument export( GetFeature getFeature )
589                                throws IOException, XMLParsingException {
590    
591            GetFeatureDocument xml = new GetFeatureDocument();
592            try {
593                xml.load( XMLFactory.class.getResource( "GetFeatureTemplate.xml" ) );
594            } catch ( SAXException e ) {
595                throw new XMLParsingException( "could not parse GetFeatureTemplate.xml", e );
596            }
597            Element root = xml.getRootElement();
598            root.setAttribute( "outputFormat", getFeature.getOutputFormat() );
599            root.setAttribute( "service", "WFS" );
600            root.setAttribute( "version", getFeature.getVersion() );
601            if ( getFeature.getHandle() != null ) {
602                root.setAttribute( "handle", getFeature.getHandle() );
603            }
604            if ( getFeature.getResultType() == RESULT_TYPE.HITS ) {
605                root.setAttribute( "resultType", "hits" );
606            } else {
607                root.setAttribute( "resultType", "results" );
608            }
609            root.setAttribute( "maxFeatures", "" + getFeature.getMaxFeatures() );
610            if ( getFeature.getStartPosition() > 0 ) {
611                root.setAttribute( "startPosition", "" + getFeature.getStartPosition() );
612            }
613            if ( getFeature.getTraverseXLinkDepth() > 0 ) {
614                root.setAttribute( "traverseXLinkDepth", "" + getFeature.getTraverseXLinkDepth() );
615            }
616            if ( getFeature.getTraverseXLinkExpiry() > 0 ) {
617                root.setAttribute( "traverseXLinkExpiry", "" + getFeature.getTraverseXLinkExpiry() );
618            }
619            Query[] queries = getFeature.getQuery();
620            for ( int i = 0; i < queries.length; i++ ) {
621                appendQuery( root, queries[i] );
622            }
623            return xml;
624        }
625    
626        /**
627         * Appends the XML representation of the given {@link Query} instance to an element.
628         *
629         * @param query
630         */
631        private static void appendQuery( Element root, Query query )
632                                throws IOException, XMLParsingException {
633    
634            Element queryElem = XMLTools.appendElement( root, WFSNS, "Query" );
635            if ( query.getHandle() != null ) {
636                queryElem.setAttribute( "handle", query.getHandle() );
637            }
638            if ( query.getFeatureVersion() != null ) {
639                queryElem.setAttribute( "featureVersion", query.getFeatureVersion() );
640            }
641            QualifiedName[] qn = query.getTypeNames();
642            String[] na = new String[qn.length];
643            for ( int i = 0; i < na.length; i++ ) {
644                na[i] = qn[i].getPrefixedName();
645                if ( qn[i].getNamespace() != null ) {
646                    queryElem.setAttribute( "xmlns:" + qn[i].getPrefix(), qn[i].getNamespace().toASCIIString() );
647                }
648            }
649            String tn = StringTools.arrayToString( na, ',' );
650            queryElem.setAttribute( "typeName", tn );
651    
652            if ( query.getSrsName() != null ) {
653                queryElem.setAttribute( "srsName", query.getSrsName() );
654            }
655    
656            String[] aliases = query.getAliases();
657            if ( aliases != null && aliases.length != 0 ) {
658                StringBuffer aliasesList = new StringBuffer( aliases[0] );
659                for ( int i = 1; i < aliases.length; i++ ) {
660                    aliasesList.append( ' ' );
661                    aliasesList.append( aliases[i] );
662                }
663                queryElem.setAttribute( "aliases", aliasesList.toString() );
664            }
665    
666            PropertyPath[] propertyNames = query.getPropertyNames();
667            for ( int i = 0; i < propertyNames.length; i++ ) {
668                Element propertyNameElement = appendElement( queryElem, OGCNS, OGC_PREFIX + ":PropertyName" );
669                appendPropertyPath( propertyNameElement, propertyNames[i] );
670            }
671            Function[] fn = query.getFunctions();
672            // copy function definitions into query node
673            if ( fn != null ) {
674                for ( int i = 0; i < fn.length; i++ ) {
675                    StringReader sr = new StringReader( fn[i].toXML().toString() );
676                    Document doc;
677                    try {
678                        doc = XMLTools.parse( sr );
679                    } catch ( SAXException e ) {
680                        throw new XMLParsingException( "could not parse filter function", e );
681                    }
682                    XMLTools.copyNode( doc.getDocumentElement(), queryElem );
683                }
684            }
685            // copy filter into query node
686            if ( query.getFilter() != null ) {
687                StringReader sr = new StringReader( query.getFilter().to100XML().toString() );
688                Document doc;
689                try {
690                    doc = XMLTools.parse( sr );
691                } catch ( SAXException e ) {
692                    throw new XMLParsingException( "could not parse filter", e );
693                }
694                Element elem = XMLTools.appendElement( queryElem, OGCNS, "ogc:Filter" );
695                XMLTools.copyNode( doc.getDocumentElement(), elem );
696            }
697    
698            SortProperty[] sp = query.getSortProperties();
699            if ( sp != null ) {
700                Element sortBy = XMLTools.appendElement( queryElem, OGCNS, "ogc:SortBy" );
701                for ( int i = 0; i < sp.length; i++ ) {
702                    Element sortProp = XMLTools.appendElement( sortBy, OGCNS, "ogc:SortProperty" );
703                    XMLTools.appendElement( sortProp, OGCNS, "ogc:PropertyName", sp[i].getSortProperty().getAsString() );
704                    if ( !sp[i].getSortOrder() ) {
705                        XMLTools.appendElement( sortProp, OGCNS, "ogc:SortOrder", "DESC" );
706                    }
707                }
708            }
709        }
710    
711        /**
712         * Appends the <code>DOM</code> representation of the given <code>PropertyPath</code> as a new text node to the
713         * given element (including necessary namespace bindings).
714         *
715         * @param element
716         *            Element node where the PropertyPath is appended to
717         * @param propertyPath
718         */
719        protected static void appendPropertyPath( Element element, PropertyPath propertyPath ) {
720            StringBuffer sb = new StringBuffer();
721            sb.append( propertyPath );
722    
723            Text textNode = element.getOwnerDocument().createTextNode( sb.toString() );
724            element.appendChild( textNode );
725            XMLTools.appendNSBindings( element, propertyPath.getNamespaceContext() );
726        }
727    }