001 //$HeadURL: svn+ssh://jwilden@svn.wald.intevation.org/deegree/base/branches/2.5_testing/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.OGC_PREFIX;
040 import static org.deegree.ogcbase.CommonNamespaces.WFSNS;
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.util.Collection;
047 import java.util.HashSet;
048 import java.util.Iterator;
049 import java.util.List;
050 import java.util.Map;
051
052 import org.deegree.datatypes.QualifiedName;
053 import org.deegree.framework.log.ILogger;
054 import org.deegree.framework.log.LoggerFactory;
055 import org.deegree.framework.util.StringTools;
056 import org.deegree.framework.xml.XMLException;
057 import org.deegree.framework.xml.XMLParsingException;
058 import org.deegree.framework.xml.XMLTools;
059 import org.deegree.io.datastore.FeatureId;
060 import org.deegree.model.feature.Feature;
061 import org.deegree.model.feature.FeatureException;
062 import org.deegree.model.feature.FeatureProperty;
063 import org.deegree.model.feature.GMLFeatureAdapter;
064 import org.deegree.model.filterencoding.Filter;
065 import org.deegree.model.filterencoding.Function;
066 import org.deegree.model.filterencoding.capabilities.FilterCapabilities;
067 import org.deegree.model.metadata.iso19115.Keywords;
068 import org.deegree.model.spatialschema.Envelope;
069 import org.deegree.model.spatialschema.Geometry;
070 import org.deegree.model.spatialschema.GeometryException;
071 import org.deegree.ogcbase.CommonNamespaces;
072 import org.deegree.ogcbase.PropertyPath;
073 import org.deegree.ogcbase.SortProperty;
074 import org.deegree.ogcbase.XLinkPropertyPath;
075 import org.deegree.ogcwebservices.getcapabilities.Contents;
076 import org.deegree.ogcwebservices.getcapabilities.MetadataURL;
077 import org.deegree.ogcwebservices.getcapabilities.OperationsMetadata;
078 import org.deegree.ogcwebservices.getcapabilities.ServiceIdentification;
079 import org.deegree.ogcwebservices.getcapabilities.ServiceProvider;
080 import org.deegree.ogcwebservices.wfs.capabilities.FeatureTypeList;
081 import org.deegree.ogcwebservices.wfs.capabilities.FormatType;
082 import org.deegree.ogcwebservices.wfs.capabilities.GMLObject;
083 import org.deegree.ogcwebservices.wfs.capabilities.Operation;
084 import org.deegree.ogcwebservices.wfs.capabilities.WFSCapabilities;
085 import org.deegree.ogcwebservices.wfs.capabilities.WFSCapabilitiesDocument;
086 import org.deegree.ogcwebservices.wfs.capabilities.WFSFeatureType;
087 import org.deegree.ogcwebservices.wfs.operation.GetFeature;
088 import org.deegree.ogcwebservices.wfs.operation.GetFeatureDocument;
089 import org.deegree.ogcwebservices.wfs.operation.Lock;
090 import org.deegree.ogcwebservices.wfs.operation.LockFeature;
091 import org.deegree.ogcwebservices.wfs.operation.LockFeatureDocument;
092 import org.deegree.ogcwebservices.wfs.operation.LockFeatureResponse;
093 import org.deegree.ogcwebservices.wfs.operation.LockFeatureResponseDocument;
094 import org.deegree.ogcwebservices.wfs.operation.Query;
095 import org.deegree.ogcwebservices.wfs.operation.GetFeature.RESULT_TYPE;
096 import org.deegree.ogcwebservices.wfs.operation.transaction.Delete;
097 import org.deegree.ogcwebservices.wfs.operation.transaction.Insert;
098 import org.deegree.ogcwebservices.wfs.operation.transaction.InsertResults;
099 import org.deegree.ogcwebservices.wfs.operation.transaction.Transaction;
100 import org.deegree.ogcwebservices.wfs.operation.transaction.TransactionDocument;
101 import org.deegree.ogcwebservices.wfs.operation.transaction.TransactionOperation;
102 import org.deegree.ogcwebservices.wfs.operation.transaction.TransactionResponse;
103 import org.deegree.ogcwebservices.wfs.operation.transaction.TransactionResponseDocument;
104 import org.deegree.ogcwebservices.wfs.operation.transaction.Update;
105 import org.w3c.dom.Comment;
106 import org.w3c.dom.Document;
107 import org.w3c.dom.Element;
108 import org.xml.sax.SAXException;
109
110 /**
111 * Responsible for the generation of XML representations of objects from the WFS context.
112 *
113 * @author <a href="mailto:mschneider@lat-lon.de">Markus Schneider </a>
114 * @author last edited by: $Author: mschneider $
115 *
116 * @version $Revision: 18281 $, $Date: 2009-06-29 15:07:08 +0200 (Mo, 29 Jun 2009) $
117 */
118 public class XMLFactory extends org.deegree.owscommon.XMLFactory {
119
120 private static final URI WFS = CommonNamespaces.WFSNS;
121
122 private static final URI OGCNS = CommonNamespaces.OGCNS;
123
124 // private static final String PRE_WFS = CommonNamespaces.WFS_PREFIX + ":";
125
126 private static final ILogger LOG = LoggerFactory.getLogger( XMLFactory.class );
127
128 /**
129 * Exports a <code>WFSCapabilities</code> instance to a <code>WFSCapabilitiesDocument</code>.
130 *
131 * @param capabilities
132 * @return DOM representation of the <code>WFSCapabilities</code>
133 * @throws IOException
134 * if XML template could not be loaded
135 */
136 public static WFSCapabilitiesDocument export( WFSCapabilities capabilities )
137 throws IOException {
138 return export( capabilities, true, true, true, true );
139 }
140
141 /**
142 * Exports a <code>WFSCapabilities</code> instance to a <code>WFSCapabilitiesDocument</code>.
143 *
144 * @param capabilities
145 * @param sections
146 * names of sections to be exported, may contain 'All'
147 * @return DOM representation of the <code>WFSCapabilities</code>
148 * @throws IOException
149 * if XML template could not be loaded
150 */
151 public static WFSCapabilitiesDocument export( WFSCapabilities capabilities, String[] sections )
152 throws IOException {
153
154 if ( sections == null || sections.length == 0 ) {
155 return export( capabilities );
156 }
157
158 if ( sections.length == 1 && sections[0].equalsIgnoreCase( "all" ) ) {
159 return export( capabilities );
160 }
161
162 boolean ident = false, provider = false, md = false, ftlist = false;
163
164 HashSet<String> set = new HashSet<String>();
165 for ( String s : sections ) {
166 set.add( s.toLowerCase() );
167 }
168
169 LOG.logDebug( "The set of requested sections was", set );
170
171 if ( set.contains( "serviceidentification" ) ) {
172 ident = true;
173 }
174 if ( set.contains( "serviceprovider" ) ) {
175 provider = true;
176 }
177 if ( set.contains( "operationsmetadata" ) ) {
178 md = true;
179 }
180 if ( set.contains( "featuretypelist" ) ) {
181 ftlist = true;
182 }
183
184 return export( capabilities, ident, provider, md, ftlist );
185 }
186
187 /**
188 * @param capabilities
189 * @param serviceIdentification
190 * @param serviceProvider
191 * @param operationsMetadata
192 * @param featureTypeList
193 * @return the exported capabilities document (possibly missing some sections if one of the flags was set to false)
194 * @throws IOException
195 */
196 public static WFSCapabilitiesDocument export( WFSCapabilities capabilities, boolean serviceIdentification,
197 boolean serviceProvider, boolean operationsMetadata,
198 boolean featureTypeList )
199 throws IOException {
200 WFSCapabilitiesDocument capabilitiesDocument = new WFSCapabilitiesDocument();
201
202 try {
203 capabilitiesDocument.createEmptyDocument();
204 Element root = capabilitiesDocument.getRootElement();
205
206 root.setAttribute( "updateSequence", capabilities.getUpdateSequence() );
207
208 if ( serviceIdentification ) {
209 ServiceIdentification si = capabilities.getServiceIdentification();
210 if ( si != null ) {
211 appendServiceIdentification( root, si );
212 }
213 }
214
215 if ( serviceProvider ) {
216 ServiceProvider sp = capabilities.getServiceProvider();
217 if ( sp != null ) {
218 appendServiceProvider( root, sp );
219 }
220 }
221
222 if ( operationsMetadata ) {
223 OperationsMetadata om = capabilities.getOperationsMetadata();
224 if ( om != null ) {
225 appendOperationsMetadata( root, om );
226 }
227 }
228
229 if ( featureTypeList ) {
230 FeatureTypeList ftl = capabilities.getFeatureTypeList();
231 if ( ftl != null ) {
232 appendFeatureTypeList( root, ftl );
233 }
234 }
235
236 GMLObject[] servesGMLObjectTypes = capabilities.getServesGMLObjectTypeList();
237 if ( servesGMLObjectTypes != null ) {
238 appendGMLObjectTypeList( root, WFS, "ServesGMLObjectTypeList", servesGMLObjectTypes );
239 }
240 GMLObject[] supportsGMLObjectTypes = capabilities.getSupportsGMLObjectTypeList();
241 if ( supportsGMLObjectTypes != null ) {
242 appendGMLObjectTypeList( root, WFS, "SupportsGMLObjectTypeList", supportsGMLObjectTypes );
243 }
244 Contents contents = capabilities.getContents();
245 if ( contents != null ) {
246 // appendContents(root, contents);
247 }
248
249 FilterCapabilities fc = capabilities.getFilterCapabilities();
250 if ( fc != null ) {
251 org.deegree.model.filterencoding.XMLFactory.appendFilterCapabilities110( root, fc );
252 }
253
254 } catch ( SAXException e ) {
255 LOG.logError( e.getMessage(), e );
256 }
257 return capabilitiesDocument;
258 }
259
260 /**
261 * Appends the DOM representation of the {@link ServiceIdentification} section to the passed {@link Element}.
262 *
263 * @param root
264 * @param serviceIdentification
265 */
266 protected static void appendServiceIdentification( Element root, ServiceIdentification serviceIdentification ) {
267
268 // 'ServiceIdentification'-element
269 Element serviceIdentificationNode = XMLTools.appendElement( root, OWSNS, "ows:ServiceIdentification" );
270
271 // the optional title element
272 String tmp = serviceIdentification.getTitle();
273 if ( tmp != null && !"".equals( tmp ) ) {
274 XMLTools.appendElement( serviceIdentificationNode, OWSNS, "ows:Title", tmp );
275 }
276
277 // the optional abstract element
278 tmp = serviceIdentification.getAbstract();
279 if ( tmp != null && !"".equals( tmp ) ) {
280 XMLTools.appendElement( serviceIdentificationNode, OWSNS, "ows:Abstract", tmp );
281 }
282
283 // the optional keywords element
284 appendKeywords( serviceIdentificationNode, serviceIdentification.getKeywords(), OWSNS );
285
286 // 'ServiceType'-element
287 XMLTools.appendElement( serviceIdentificationNode, OWSNS, "ows:ServiceType",
288 serviceIdentification.getServiceType().getCode() );
289
290 // 'ServiceTypeVersion'-elements
291 String[] versions = serviceIdentification.getServiceTypeVersions();
292 for ( int i = 0; i < versions.length; i++ ) {
293 XMLTools.appendElement( serviceIdentificationNode, OWSNS, "ows:ServiceTypeVersion", versions[i] );
294 }
295
296 // 'Fees'-element
297 XMLTools.appendElement( serviceIdentificationNode, OWSNS, "ows:Fees", serviceIdentification.getFees() );
298
299 // 'AccessConstraints'-element
300 String[] constraints = serviceIdentification.getAccessConstraints();
301 if ( constraints != null ) {
302 for ( int i = 0; i < constraints.length; i++ ) {
303 XMLTools.appendElement( serviceIdentificationNode, OWSNS, "ows:AccessConstraints", constraints[i] );
304 }
305 }
306 }
307
308 /**
309 * Appends a <code>ows:Keywords</code> -element for each <code>Keywords</code> object of the passed array to the
310 * passed <code>Element</code>.
311 *
312 * @param xmlNode
313 * @param keywords
314 * @param namespaceURI
315 */
316 protected static void appendKeywords( Element xmlNode, Keywords[] keywords, URI namespaceURI ) {
317 if ( keywords != null ) {
318 for ( int i = 0; i < keywords.length; i++ ) {
319 Element node = XMLTools.appendElement( xmlNode, namespaceURI, "ows:Keywords" );
320 appendKeywords( node, keywords[i], namespaceURI );
321 }
322 }
323 }
324
325 /**
326 * Appends a <code>Keyword</code> -element to the passed <code>Element</code> and fills it with the available
327 * keywords.
328 *
329 * @param xmlNode
330 * @param keywords
331 * @param namespaceURI
332 */
333 protected static void appendKeywords( Element xmlNode, Keywords keywords, URI namespaceURI ) {
334 if ( keywords != null ) {
335 String[] kw = keywords.getKeywords();
336 for ( int i = 0; i < kw.length; i++ ) {
337 XMLTools.appendElement( xmlNode, namespaceURI, "ows:Keyword", kw[i] );
338 }
339 if ( keywords.getThesaurusName() != null ) {
340 XMLTools.appendElement( xmlNode, namespaceURI, "ows:Type", keywords.getThesaurusName() );
341 }
342 }
343 }
344
345 /**
346 * Exports a <code>GetFeature</code> instance to a <code>GetFeatureDocument</code>.
347 *
348 * @param getFeature
349 * request to be exported
350 * @return XML representation of the <code>GetFeature</code> request
351 * @throws IOException
352 * @throws XMLParsingException
353 */
354 public static GetFeatureDocument export( GetFeature getFeature )
355 throws IOException, XMLParsingException {
356
357 GetFeatureDocument xml = new GetFeatureDocument();
358 try {
359 xml.load( XMLFactory.class.getResource( "GetFeatureTemplate.xml" ) );
360 } catch ( SAXException e ) {
361 throw new XMLParsingException( "could not parse GetFeatureTemplate.xml", e );
362 }
363 Element root = xml.getRootElement();
364 root.setAttribute( "outputFormat", getFeature.getOutputFormat() );
365 root.setAttribute( "service", "WFS" );
366 root.setAttribute( "version", getFeature.getVersion() );
367 if ( getFeature.getHandle() != null ) {
368 root.setAttribute( "handle", getFeature.getHandle() );
369 }
370 if ( getFeature.getResultType() == RESULT_TYPE.HITS ) {
371 root.setAttribute( "resultType", "hits" );
372 } else {
373 root.setAttribute( "resultType", "results" );
374 }
375 if ( getFeature.getMaxFeatures() > 0 ) {
376 root.setAttribute( "maxFeatures", "" + getFeature.getMaxFeatures() );
377 }
378 if ( getFeature.getStartPosition() > 0 ) {
379 root.setAttribute( "startPosition", "" + getFeature.getStartPosition() );
380 }
381 if ( getFeature.getTraverseXLinkDepth() >= 0 ) {
382 root.setAttribute( "traverseXlinkDepth", "" + getFeature.getTraverseXLinkDepth() );
383 }
384 if ( getFeature.getTraverseXLinkExpiry() >= 0 ) {
385 root.setAttribute( "traverseXlinkExpiry", "" + getFeature.getTraverseXLinkExpiry() );
386 }
387 Query[] queries = getFeature.getQuery();
388 for ( int i = 0; i < queries.length; i++ ) {
389 appendQuery( root, queries[i] );
390 }
391 return xml;
392 }
393
394 /**
395 * Exports a {@link LockFeature} request instance to a {@link LockFeatureDocument}.
396 *
397 * @param request
398 * request to be exported
399 * @return XML representation of the <code>LockFeature</code> request
400 * @throws IOException
401 * @throws XMLParsingException
402 * @throws SAXException
403 */
404 public static LockFeatureDocument export( LockFeature request )
405 throws IOException, XMLParsingException, SAXException {
406
407 LockFeatureDocument doc = new LockFeatureDocument();
408 doc.createEmptyDocument();
409
410 Element root = doc.getRootElement();
411 root.setAttribute( "version", request.getVersion() );
412 root.setAttribute( "service", "WFS" );
413 if ( request.getHandle() != null ) {
414 root.setAttribute( "handle", request.getHandle() );
415 }
416 root.setAttribute( "expiry", "" + request.getExpiry() );
417 root.setAttribute( "lockAction", "" + request.getLockAction() );
418
419 List<Lock> locks = request.getLocks();
420 for ( Lock lock : locks ) {
421 appendLock( root, lock );
422 }
423 return doc;
424 }
425
426 /**
427 * Appends the XML representation of the given {@link Lock} to the given element.
428 *
429 * @param root
430 * @param lock
431 */
432 private static void appendLock( Element root, Lock lock )
433 throws IOException, XMLParsingException {
434
435 Element lockElement = XMLTools.appendElement( root, WFS, "Lock" );
436 if ( lock.getHandle() != null ) {
437 lockElement.setAttribute( "handle", lock.getHandle() );
438 }
439 QualifiedName typeName = lock.getTypeName();
440 if ( typeName.getPrefix() != null ) {
441 lockElement.setAttribute( "xmlns:" + typeName.getPrefix(), typeName.getNamespace().toASCIIString() );
442 }
443 lockElement.setAttribute( "typeName", typeName.getPrefixedName() );
444
445 // copy filter into Lock element
446 if ( lock.getFilter() != null ) {
447 StringReader sr = new StringReader( lock.getFilter().to110XML().toString() );
448 Document doc;
449 try {
450 doc = XMLTools.parse( sr );
451 } catch ( SAXException e ) {
452 throw new XMLParsingException( "Could not parse filter.", e );
453 }
454 Element elem = XMLTools.appendElement( lockElement, OGCNS, "ogc:Filter" );
455 XMLTools.copyNode( doc.getDocumentElement(), elem );
456 }
457 }
458
459 /**
460 * Exports a {@link LockFeatureResponse} instance to its XML representation.
461 *
462 * @param response
463 * response to be exported
464 * @return XML representation of the <code>LockFeatureResponse</code>
465 * @throws IOException
466 * @throws SAXException
467 */
468 public static LockFeatureResponseDocument export( LockFeatureResponse response )
469 throws IOException, SAXException {
470
471 LockFeatureResponseDocument doc = new LockFeatureResponseDocument();
472 doc.createEmptyDocument();
473
474 Element root = doc.getRootElement();
475 XMLTools.appendElement( root, WFS, "LockId", response.getLockId() );
476 String[] fids = response.getFeaturesLocked();
477 if ( fids.length != 0 ) {
478 Element featuresLockedElement = XMLTools.appendElement( root, WFS, "FeaturesLocked" );
479 for ( String fid : fids ) {
480 appendFeatureId( featuresLockedElement, fid );
481 }
482 }
483 fids = response.getFeaturesNotLocked();
484 if ( fids.length != 0 ) {
485 Element featuresNotLockedElement = XMLTools.appendElement( root, WFS, "FeaturesNotLocked" );
486 for ( String fid : fids ) {
487 appendFeatureId( featuresNotLockedElement, fid );
488 }
489 }
490 return doc;
491 }
492
493 /**
494 * Exports a {@link Transaction} instance to its XML representation.
495 *
496 * @param transaction
497 * transaction to export
498 * @return XML representation of transaction
499 * @throws IOException
500 * @throws XMLParsingException
501 */
502 public static TransactionDocument export( Transaction transaction )
503 throws IOException, XMLParsingException {
504
505 TransactionDocument xml = new TransactionDocument();
506 try {
507 xml.createEmptyDocument();
508 } catch ( SAXException e ) {
509 throw new IOException( e.getMessage() );
510 }
511 Element root = xml.getRootElement();
512 List<TransactionOperation> ops = transaction.getOperations();
513 for ( int i = 0; i < ops.size(); i++ ) {
514 try {
515 if ( ops.get( i ) instanceof Insert ) {
516 appendInsert( root, (Insert) ops.get( i ) );
517 } else if ( ops.get( i ) instanceof Update ) {
518 appendUpdate( root, (Update) ops.get( i ) );
519 } else if ( ops.get( i ) instanceof Delete ) {
520 appendDelete( root, (Delete) ops.get( i ) );
521 }
522 } catch ( Exception e ) {
523 LOG.logError( e.getMessage(), e );
524 throw new XMLParsingException( e.getMessage() );
525 }
526 }
527 return xml;
528 }
529
530 /**
531 * Adds the XML representation of a <code>Delete</code> operation to the given element.
532 *
533 * @param root
534 * @param delete
535 */
536 private static void appendDelete( Element root, Delete delete ) {
537 Element el = XMLTools.appendElement( root, WFS, "Delete" );
538 if ( delete.getHandle() != null ) {
539 el.setAttribute( "handle", delete.getHandle() );
540 }
541 el.setAttribute( "typeName", delete.getTypeName().getPrefixedName() );
542 // ensure that the type's namespace is declared
543 el.setAttribute( "xmlns:" + delete.getTypeName().getPrefix(),
544 delete.getTypeName().getNamespace().toASCIIString() );
545
546 Filter filter = delete.getFilter();
547 if ( filter != null ) {
548 org.deegree.model.filterencoding.XMLFactory.appendFilter( el, filter );
549 }
550 root.appendChild( el );
551 }
552
553 /**
554 * Adds the XML representation of an <code>Update</code> operation to the given element.
555 * <p>
556 * Respects the deegree-specific extension to the Update operation: instead of specifying properties and their
557 * values, it's also possible to only specify just one feature that replaces the matched feature.
558 *
559 * @param root
560 * @param update
561 * @throws SAXException
562 * @throws IOException
563 * @throws FeatureException
564 * @throws GeometryException
565 */
566 private static void appendUpdate( Element root, Update update )
567 throws FeatureException, IOException, SAXException, GeometryException {
568
569 Element el = XMLTools.appendElement( root, WFS, "Update" );
570 if ( update.getHandle() != null ) {
571 el.setAttribute( "handle", update.getHandle() );
572 }
573
574 el.setAttribute( "typeName", update.getTypeName().getPrefixedName() );
575
576 // ensure that the type's namespace is declared
577 el.setAttribute( "xmlns:" + update.getTypeName().getPrefix(),
578 update.getTypeName().getNamespace().toASCIIString() );
579
580 Feature replacement = update.getFeature();
581 if ( replacement != null ) {
582 GMLFeatureAdapter adapter = new GMLFeatureAdapter();
583 adapter.append( el, replacement );
584 } else {
585 Map<PropertyPath, FeatureProperty> replaces = update.getReplacementProperties();
586 for ( PropertyPath propertyName : replaces.keySet() ) {
587 Element propElement = XMLTools.appendElement( el, WFS, "Property" );
588 Element nameElement = XMLTools.appendElement( propElement, WFS, "Name" );
589 org.deegree.ogcbase.XMLFactory.appendPropertyPath( nameElement, propertyName );
590
591 // append property value
592 Object propValue = replaces.get( propertyName ).getValue();
593 if ( propValue != null ) {
594 Element valueElement = XMLTools.appendElement( propElement, WFS, "Value" );
595 if ( propValue instanceof Feature ) {
596 GMLFeatureAdapter adapter = new GMLFeatureAdapter();
597 adapter.append( valueElement, (Feature) propValue );
598 } else if ( propValue instanceof Geometry ) {
599 appendGeometry( valueElement, (Geometry) propValue );
600 } else {
601 XMLTools.setNodeValue( valueElement, propValue.toString() );
602 }
603 }
604 }
605 }
606
607 Filter filter = update.getFilter();
608 if ( filter != null ) {
609 org.deegree.model.filterencoding.XMLFactory.appendFilter( el, filter );
610 }
611 root.appendChild( el );
612 }
613
614 /**
615 * Adds the XML representation of an <code>Insert</code> operation to the given element.
616 *
617 * @param root
618 * @param insert
619 * @throws SAXException
620 * @throws IOException
621 * @throws FeatureException
622 */
623 private static void appendInsert( Element root, Insert insert )
624 throws IOException, FeatureException, XMLException, SAXException {
625
626 Element el = XMLTools.appendElement( root, WFS, "Insert" );
627 if ( insert.getHandle() != null ) {
628 el.setAttribute( "handle", insert.getHandle() );
629 }
630 if ( insert.getIdGen() != null ) {
631 switch ( insert.getIdGen() ) {
632 case USE_EXISTING:
633 el.setAttribute( "idgen", "UseExisting" );
634 break;
635 case GENERATE_NEW:
636 el.setAttribute( "idgen", "GenerateNew" );
637 break;
638 case REPLACE_DUPLICATE:
639 el.setAttribute( "idgen", "ReplaceDuplicate" );
640 break;
641 }
642 }
643
644 GMLFeatureAdapter adapter = new GMLFeatureAdapter();
645 adapter.append( el, insert.getFeatures() );
646 }
647
648 /**
649 * Exports an instance of {@link TransactionResponse} to its XML representation.
650 *
651 * @param response
652 * TransactionResponse to export
653 * @return XML representation of TransactionResponse
654 * @throws IOException
655 */
656 public static TransactionResponseDocument export( TransactionResponse response )
657 throws IOException {
658
659 TransactionResponseDocument xml = new TransactionResponseDocument();
660 try {
661 xml.createEmptyDocument();
662 } catch ( SAXException e ) {
663 throw new IOException( e.getMessage() );
664 }
665
666 Element root = xml.getRootElement();
667 appendTransactionSummary( root, response.getTotalInserted(), response.getTotalUpdated(),
668 response.getTotalDeleted() );
669 appendInsertResults( root, response.getInsertResults() );
670 return xml;
671 }
672
673 /**
674 * Appends a 'wfs:TransactionSummary' element to the given element.
675 *
676 * @param root
677 * @param totalInserted
678 * @param totalUpdated
679 * @param totalDeleted
680 */
681 private static void appendTransactionSummary( Element root, int totalInserted, int totalUpdated, int totalDeleted ) {
682 Element taSummary = XMLTools.appendElement( root, WFS, "TransactionSummary" );
683 XMLTools.appendElement( taSummary, WFS, "totalInserted", "" + totalInserted );
684 XMLTools.appendElement( taSummary, WFS, "totalUpdated", "" + totalUpdated );
685 XMLTools.appendElement( taSummary, WFS, "totalDeleted", "" + totalDeleted );
686 }
687
688 /**
689 * Appends an 'wfs:InsertResults' element to the given element (only if necessary).
690 *
691 * @param root
692 * @param insertResults
693 */
694 private static void appendInsertResults( Element root, Collection<InsertResults> insertResults ) {
695 Element insertResultsElement = appendElement( root, WFS, "InsertResults" );
696
697 // append synthetic ones because of the faulty schema
698 if ( insertResults.size() == 0 ) {
699 Document d = root.getOwnerDocument();
700 Comment comment = d.createComment( "Dummy InsertResults element for compliance with (faulty?) WFS schema" );
701 root.insertBefore( comment, insertResultsElement );
702 Element elem = appendElement( insertResultsElement, WFS, "Feature" );
703 appendElement( elem, OGCNS, OGC_PREFIX + ":FeatureId" ).setAttribute( "fid", "bogus" );
704 } else {
705 Iterator<InsertResults> iter = insertResults.iterator();
706 while ( iter.hasNext() ) {
707 appendFeatureIds( insertResultsElement, iter.next() );
708 }
709 }
710 }
711
712 /**
713 * Appends a 'wfs:Feature' element to the given element.
714 *
715 * @param root
716 * @param results
717 */
718 private static void appendFeatureIds( Element root, InsertResults results ) {
719 Element featureElement = XMLTools.appendElement( root, WFS, "Feature" );
720 String handle = results.getHandle();
721 if ( handle != null ) {
722 featureElement.setAttribute( "handle", handle );
723 }
724 Iterator<FeatureId> iter = results.getFeatureIDs().iterator();
725 while ( iter.hasNext() ) {
726 Element featureIdElement = XMLTools.appendElement( featureElement, OGCNS, "ogc:FeatureId" );
727 featureIdElement.setAttribute( "fid", iter.next().getAsString() );
728 }
729 }
730
731 /**
732 * Appends the XML representation of the given {@link Query} instance to an element.
733 *
734 * @param query
735 */
736 private static void appendQuery( Element root, Query query )
737 throws IOException, XMLParsingException {
738
739 Element queryElem = XMLTools.appendElement( root, WFS, "Query" );
740 if ( query.getHandle() != null ) {
741 queryElem.setAttribute( "handle", query.getHandle() );
742 }
743 if ( query.getFeatureVersion() != null ) {
744 queryElem.setAttribute( "featureVersion", query.getFeatureVersion() );
745 }
746 QualifiedName[] qn = query.getTypeNames();
747 String[] na = new String[qn.length];
748 for ( int i = 0; i < na.length; i++ ) {
749 na[i] = qn[i].getPrefixedName();
750 if ( qn[i].getNamespace() != null ) {
751 queryElem.setAttribute( "xmlns:" + qn[i].getPrefix(), qn[i].getNamespace().toASCIIString() );
752 }
753 }
754 String tn = StringTools.arrayToString( na, ',' );
755 queryElem.setAttribute( "typeName", tn );
756
757 if ( query.getSrsName() != null ) {
758 queryElem.setAttribute( "srsName", query.getSrsName() );
759 }
760
761 String[] aliases = query.getAliases();
762 if ( aliases != null && aliases.length != 0 ) {
763 StringBuffer aliasesList = new StringBuffer( aliases[0] );
764 for ( int i = 1; i < aliases.length; i++ ) {
765 aliasesList.append( ' ' );
766 aliasesList.append( aliases[i] );
767 }
768 queryElem.setAttribute( "aliases", aliasesList.toString() );
769 }
770
771 PropertyPath[] propertyNames = query.getPropertyNames();
772 for ( int i = 0; i < propertyNames.length; i++ ) {
773 if ( propertyNames[i] instanceof XLinkPropertyPath ) {
774 Element propertyNameElement = appendElement( queryElem, WFSNS, WFS_PREFIX + ":XlinkPropertyName" );
775 String depth = Integer.toString( ( (XLinkPropertyPath) propertyNames[i] ).getXlinkDepth() );
776 propertyNameElement.setAttribute( "traverseXlinkDepth", depth );
777 appendPropertyPath( propertyNameElement, propertyNames[i] );
778 } else {
779 Element propertyNameElement = appendElement( queryElem, WFSNS, WFS_PREFIX + ":PropertyName" );
780 appendPropertyPath( propertyNameElement, propertyNames[i] );
781 }
782 }
783 Function[] fn = query.getFunctions();
784 // copy function definitions into query node
785 if ( fn != null ) {
786 for ( int i = 0; i < fn.length; i++ ) {
787 StringReader sr = new StringReader( fn[i].toXML().toString() );
788 Document doc;
789 try {
790 doc = XMLTools.parse( sr );
791 } catch ( SAXException e ) {
792 throw new XMLParsingException( "could not parse filter function", e );
793 }
794 XMLTools.copyNode( doc.getDocumentElement(), queryElem );
795 }
796 }
797 // copy filter into query node
798 if ( query.getFilter() != null ) {
799 StringReader sr = new StringReader( query.getFilter().to110XML().toString() );
800 Document doc;
801 try {
802 doc = XMLTools.parse( sr );
803 } catch ( SAXException e ) {
804 throw new XMLParsingException( "could not parse filter", e );
805 }
806 Element elem = XMLTools.appendElement( queryElem, OGCNS, "ogc:Filter" );
807 XMLTools.copyNode( doc.getDocumentElement(), elem );
808 }
809
810 SortProperty[] sp = query.getSortProperties();
811 if ( sp != null ) {
812 Element sortBy = XMLTools.appendElement( queryElem, OGCNS, "ogc:SortBy" );
813 for ( int i = 0; i < sp.length; i++ ) {
814 Element sortProp = XMLTools.appendElement( sortBy, OGCNS, "ogc:SortProperty" );
815 XMLTools.appendElement( sortProp, OGCNS, "ogc:PropertyName", sp[i].getSortProperty().getAsString() );
816 if ( !sp[i].getSortOrder() ) {
817 XMLTools.appendElement( sortProp, OGCNS, "ogc:SortOrder", "DESC" );
818 }
819 }
820 }
821 }
822
823 /**
824 * Appends the XML representation of the <code>wfs:FeatureTypeList</code>- section to the passed
825 * <code>Element</code>.
826 *
827 * @param root
828 * @param featureTypeList
829 */
830 public static void appendFeatureTypeList( Element root, FeatureTypeList featureTypeList ) {
831
832 Element featureTypeListNode = XMLTools.appendElement( root, WFS, "FeatureTypeList", null );
833 Operation[] operations = featureTypeList.getGlobalOperations();
834 if ( operations != null ) {
835 Element operationsNode = XMLTools.appendElement( featureTypeListNode, WFS, "Operations" );
836 for ( int i = 0; i < operations.length; i++ ) {
837 XMLTools.appendElement( operationsNode, WFS, "Operation", operations[i].getOperation() );
838 }
839 }
840 WFSFeatureType[] featureTypes = featureTypeList.getFeatureTypes();
841 if ( featureTypes != null ) {
842 for ( int i = 0; i < featureTypes.length; i++ ) {
843 appendWFSFeatureType( featureTypeListNode, featureTypes[i] );
844 }
845 }
846
847 }
848
849 /**
850 * Appends the XML representation of the <code>WFSFeatureType</code> instance to the passed <code>Element</code>.
851 *
852 * @param root
853 * @param featureType
854 */
855 public static void appendWFSFeatureType( Element root, WFSFeatureType featureType ) {
856
857 Element featureTypeNode = XMLTools.appendElement( root, WFS, "FeatureType" );
858
859 if ( featureType.getName().getPrefix() != null ) {
860 XMLTools.appendNSBinding( featureTypeNode, featureType.getName().getPrefix(),
861 featureType.getName().getNamespace() );
862 }
863 XMLTools.appendElement( featureTypeNode, WFS, "Name", featureType.getName().getPrefixedName() );
864 XMLTools.appendElement( featureTypeNode, WFS, "Title", featureType.getTitle() );
865 String abstract_ = featureType.getAbstract();
866 if ( abstract_ != null ) {
867 XMLTools.appendElement( featureTypeNode, WFS, "Abstract", featureType.getAbstract() );
868 }
869 Keywords[] keywords = featureType.getKeywords();
870 if ( keywords != null ) {
871 appendOWSKeywords( featureTypeNode, keywords );
872 }
873 URI defaultSrs = featureType.getDefaultSRS();
874 if ( defaultSrs != null ) {
875 XMLTools.appendElement( featureTypeNode, WFS, "DefaultSRS", defaultSrs.toString() );
876 URI[] otherSrs = featureType.getOtherSrs();
877 if ( otherSrs != null ) {
878 for ( int i = 0; i < otherSrs.length; i++ ) {
879 XMLTools.appendElement( featureTypeNode, WFS, "OtherSRS", otherSrs[i].toString() );
880 }
881 }
882 } else {
883 XMLTools.appendElement( featureTypeNode, WFS, "Title" );
884 }
885 Operation[] operations = featureType.getOperations();
886 if ( operations != null ) {
887 Element operationsNode = XMLTools.appendElement( featureTypeNode, WFS, "Operations" );
888 for ( int i = 0; i < operations.length; i++ ) {
889 XMLTools.appendElement( operationsNode, WFS, "Operation", operations[i].getOperation() );
890 }
891 }
892 FormatType[] formats = featureType.getOutputFormats();
893 if ( formats != null ) {
894 appendOutputFormats( featureTypeNode, formats );
895 }
896 Envelope[] wgs84BoundingBoxes = featureType.getWgs84BoundingBoxes();
897 for ( int i = 0; i < wgs84BoundingBoxes.length; i++ ) {
898 appendWgs84BoundingBox( featureTypeNode, wgs84BoundingBoxes[i] );
899 }
900 if ( featureType.getMetadataUrls() != null ) {
901 for ( MetadataURL metadataURL : featureType.getMetadataUrls() ) {
902 appendMetadataURL( featureTypeNode, metadataURL );
903 }
904 }
905 }
906
907 /**
908 * Appends the XML representation of the <code>wfs:ServesGMLObjectTypeList</code>- section to the passed
909 * <code>Element</code> as a new element with the given qualified name.
910 *
911 * @param root
912 * @param elementNS
913 * @param elementName
914 * @param gmlObjectTypes
915 */
916 public static void appendGMLObjectTypeList( Element root, URI elementNS, String elementName,
917 GMLObject[] gmlObjectTypes ) {
918
919 Element gmlObjectTypeListNode = XMLTools.appendElement( root, elementNS, elementName );
920 for ( int i = 0; i < gmlObjectTypes.length; i++ ) {
921 appendGMLObjectTypeType( gmlObjectTypeListNode, gmlObjectTypes[i] );
922 }
923 }
924
925 /**
926 * Appends the XML representation of the given {@link GMLObject} (as a <code>wfs:GMLObjectType</code> element) to
927 * the passed <code>Element</code>.
928 *
929 * @param root
930 * @param gmlObjectType
931 */
932 public static void appendGMLObjectTypeType( Element root, GMLObject gmlObjectType ) {
933
934 Element gmlObjectTypeNode = XMLTools.appendElement( root, WFS, "GMLObjectType" );
935
936 if ( gmlObjectType.getName().getPrefix() != null ) {
937 XMLTools.appendNSBinding( gmlObjectTypeNode, gmlObjectType.getName().getPrefix(),
938 gmlObjectType.getName().getNamespace() );
939 }
940 XMLTools.appendElement( gmlObjectTypeNode, WFS, "Name", gmlObjectType.getName().getPrefixedName() );
941 if ( gmlObjectType.getTitle() != null ) {
942 XMLTools.appendElement( gmlObjectTypeNode, WFS, "Title", gmlObjectType.getTitle() );
943 }
944 String abstract_ = gmlObjectType.getAbstract();
945 if ( abstract_ != null ) {
946 XMLTools.appendElement( gmlObjectTypeNode, WFS, "Abstract", gmlObjectType.getAbstract() );
947 }
948 Keywords[] keywords = gmlObjectType.getKeywords();
949 if ( keywords != null ) {
950 appendOWSKeywords( gmlObjectTypeNode, keywords );
951 }
952 FormatType[] formats = gmlObjectType.getOutputFormats();
953 if ( formats != null ) {
954 appendOutputFormats( gmlObjectTypeNode, formats );
955 }
956 }
957
958 /**
959 * Appends the XML representation of the given {@link Envelope} (as an <code>ows:WGS84BoundingBoxType</code>
960 * element) to the passed <code>Element</code>.
961 *
962 * @param root
963 * @param envelope
964 */
965 public static void appendWgs84BoundingBox( Element root, Envelope envelope ) {
966 Element wgs84BoundingBoxElement = XMLTools.appendElement( root, OWSNS, "ows:WGS84BoundingBox" );
967 XMLTools.appendElement( wgs84BoundingBoxElement, OWSNS, "ows:LowerCorner", envelope.getMin().getX() + " "
968 + envelope.getMin().getY() );
969 XMLTools.appendElement( wgs84BoundingBoxElement, OWSNS, "ows:UpperCorner", envelope.getMax().getX() + " "
970 + envelope.getMax().getY() );
971 }
972
973 private static void appendMetadataURL( Element root, MetadataURL metadataURL ) {
974 Element metadataURLElement = XMLTools.appendElement( root, WFSNS, "wfs:MetadataURL",
975 metadataURL.getOnlineResource().toString() );
976 metadataURLElement.setAttribute( "format", metadataURL.getFormat() );
977 metadataURLElement.setAttribute( "type", metadataURL.getType() );
978 }
979
980 /**
981 * Appends the XML representation of the given {@link FormatType}s as (as a <code>wfs:OutputFormats</code> element)
982 * to the passed <code>Element</code>.
983 *
984 * @param root
985 * @param formats
986 */
987 public static void appendOutputFormats( Element root, FormatType[] formats ) {
988
989 Element outputFormatsNode = XMLTools.appendElement( root, WFS, "OutputFormats" );
990 for ( int i = 0; i < formats.length; i++ ) {
991 appendElement( outputFormatsNode, WFS, "Format", formats[i].getValue() );
992 }
993 }
994 }