    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    }