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 }