001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/ogcwebservices/csw/discovery/XMLFactory.java $
002 /*---------------- FILE HEADER ------------------------------------------
003
004 This file is part of deegree.
005 Copyright (C) 2001-2008 by:
006 EXSE, Department of Geography, University of Bonn
007 http://www.giub.uni-bonn.de/deegree/
008 lat/lon GmbH
009 http://www.lat-lon.de
010
011 This library is free software; you can redistribute it and/or
012 modify it under the terms of the GNU Lesser General Public
013 License as published by the Free Software Foundation; either
014 version 2.1 of the License, or (at your option) any later version.
015
016 This library is distributed in the hope that it will be useful,
017 but WITHOUT ANY WARRANTY; without even the implied warranty of
018 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
019 Lesser General Public License for more details.
020
021 You should have received a copy of the GNU Lesser General Public
022 License along with this library; if not, write to the Free Software
023 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
024
025 Contact:
026
027 Andreas Poth
028 lat/lon GmbH
029 Aennchenstr. 19
030 53115 Bonn
031 Germany
032 E-Mail: poth@lat-lon.de
033
034 Prof. Dr. Klaus Greve
035 Department of Geography
036 University of Bonn
037 Meckenheimer Allee 166
038 53115 Bonn
039 Germany
040 E-Mail: greve@giub.uni-bonn.de
041
042 ---------------------------------------------------------------------------*/
043 package org.deegree.ogcwebservices.csw.discovery;
044
045 import static org.deegree.ogcbase.CommonNamespaces.CSW202NS;
046 import static org.deegree.ogcbase.CommonNamespaces.CSWNS;
047
048 import java.net.URI;
049 import java.net.URISyntaxException;
050 import java.util.Date;
051 import java.util.HashMap;
052 import java.util.List;
053 import java.util.Map;
054 import java.util.Set;
055
056 import org.deegree.datatypes.QualifiedName;
057 import org.deegree.framework.log.ILogger;
058 import org.deegree.framework.log.LoggerFactory;
059 import org.deegree.framework.util.StringTools;
060 import org.deegree.framework.util.TimeTools;
061 import org.deegree.framework.xml.XMLException;
062 import org.deegree.framework.xml.XMLTools;
063 import org.deegree.ogcbase.CommonNamespaces;
064 import org.deegree.ogcbase.PropertyPath;
065 import org.deegree.ogcbase.SortProperty;
066 import org.deegree.ogcwebservices.OGCWebServiceException;
067 import org.w3c.dom.Attr;
068 import org.w3c.dom.Document;
069 import org.w3c.dom.Element;
070 import org.w3c.dom.NamedNodeMap;
071 import org.w3c.dom.Node;
072 import org.w3c.dom.NodeList;
073
074 /**
075 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth </a>
076 * @author <a href="mailto:mschneider@lat-lon.de">Markus Schneider </a>
077 * @author last edited by: $Author: aschmitz $
078 *
079 * @version $Revision: 9499 $, $Date: 2008-01-09 16:47:04 +0100 (Mi, 09 Jan 2008) $
080 */
081 public class XMLFactory extends org.deegree.ogcbase.XMLFactory {
082
083 private static final ILogger LOG = LoggerFactory.getLogger( XMLFactory.class );
084
085 /**
086 * Exports a <code>GetRecordsResponse</code> instance to a
087 * <code>GetRecordsResponseDocument</code>.
088 *
089 * @param response
090 * @return DOM representation of the <code>GetRecordsResponse</code>
091 * @throws XMLException
092 * if XML template could not be loaded
093 */
094 public static GetRecordsResultDocument export( GetRecordsResult response )
095 throws XMLException {
096 // 'version'-attribute
097 String version = response.getRequest().getVersion();
098 if ( version == null || "".equals( version.trim() ) ) {
099 version = "2.0.0";
100 }
101
102 GetRecordsResultDocument responseDocument = new GetRecordsResultDocument( version );
103
104 try {
105 Element rootElement = responseDocument.getRootElement();
106 Document doc = rootElement.getOwnerDocument();
107
108 // set required namespaces
109 Element recordRespRoot = response.getSearchResults().getRecords().getOwnerDocument().getDocumentElement();
110 NamedNodeMap nnm = recordRespRoot.getAttributes();
111 for ( int i = 0; i < nnm.getLength(); i++ ) {
112 Node node = nnm.item( i );
113 if ( node instanceof Attr ) {
114 rootElement.setAttribute( node.getNodeName(), node.getNodeValue() );
115 }
116 }
117
118 rootElement.setAttribute( "version", version );
119 String namespace = ( version.equals( "2.0.2" ) ? CSW202NS.toString() : CSWNS.toString() );
120
121 // 'RequestId'-element (optional)
122 if ( response.getRequest().getId() != null ) {
123 Element requestIdElement = doc.createElementNS( namespace, "csw:RequestId" );
124 requestIdElement.appendChild( doc.createTextNode( response.getRequest().getId() ) );
125 rootElement.appendChild( requestIdElement );
126 }
127
128 // 'SearchStatus'-element (required)
129 Element searchStatusElement = doc.createElementNS( namespace, "csw:SearchStatus" );
130 // 'status'-attribute (required)
131 if ( !version.equals( "2.0.2" ) ) {
132 searchStatusElement.setAttribute( "status", response.getSearchStatus().getStatus() );
133 }
134 // 'timestamp'-attribute (optional)
135 if ( response.getSearchStatus().getTimestamp() != null ) {
136 Date date = response.getSearchStatus().getTimestamp();
137 String time = TimeTools.getISOFormattedTime( date );
138 searchStatusElement.setAttribute( "timestamp", time );
139 }
140 rootElement.appendChild( searchStatusElement );
141
142 // 'SeachResults'-element (required)
143 Element searchResultsElement = doc.createElementNS( namespace, "csw:SearchResults" );
144 SearchResults results = response.getSearchResults();
145
146 // 'resultSetId'-attribute (optional)
147 if ( results.getResultSetId() != null ) {
148 searchResultsElement.setAttribute( "resultSetId", results.getResultSetId().toString() );
149 }
150 // 'elementSet'-attribute (optional)
151 if ( results.getElementSet() != null ) {
152 searchResultsElement.setAttribute( "elementSet", results.getElementSet().toString() );
153 }
154 // 'recordSchema'-attribute (optional)
155 if ( results.getRecordSchema() != null ) {
156 searchResultsElement.setAttribute( "recordSchema", results.getRecordSchema().toString() );
157 }
158 // 'numberOfRecordsMatched'-attribute (required)
159 searchResultsElement.setAttribute( "numberOfRecordsMatched", "" + results.getNumberOfRecordsMatched() );
160 // 'numberOfRecordsReturned'-attribute (required)
161 searchResultsElement.setAttribute( "numberOfRecordsReturned", "" + results.getNumberOfRecordsReturned() );
162 // 'nextRecord'-attribute (required)
163 searchResultsElement.setAttribute( "nextRecord", "" + results.getNextRecord() );
164 // 'expires'-attribute (optional)
165 if ( results.getExpires() != null ) {
166 Date date = results.getExpires();
167 String time = TimeTools.getISOFormattedTime( date );
168 searchResultsElement.setAttribute( "expires", time );
169 }
170 // append all children of the records container node
171 NodeList nl = results.getRecords().getChildNodes();
172 for ( int i = 0; i < nl.getLength(); i++ ) {
173 Node copy = doc.importNode( nl.item( i ), true );
174 searchResultsElement.appendChild( copy );
175 }
176 rootElement.appendChild( searchResultsElement );
177 } catch ( Exception e ) {
178 LOG.logError( e.getMessage(), e );
179 throw new XMLException( e.getMessage() );
180 }
181 return responseDocument;
182 }
183
184 /**
185 * Exports a instance of {@link GetRecordByIdResult} to a {@link GetRecordByIdResultDocument}.
186 *
187 * @param response
188 * @return a new document
189 * @throws XMLException
190 */
191 public static GetRecordByIdResultDocument export( GetRecordByIdResult response )
192 throws XMLException {
193
194 GetRecordByIdResultDocument doc = new GetRecordByIdResultDocument();
195
196 try {
197 doc.createEmptyDocument( response.getRequest().getVersion() );
198 Document owner = doc.getRootElement().getOwnerDocument();
199 if ( response != null && response.getRecord() != null ) {
200 Node copy = owner.importNode( response.getRecord(), true );
201 doc.getRootElement().appendChild( copy );
202 }
203 } catch ( Exception e ) {
204 LOG.logError( e.getMessage(), e );
205 throw new XMLException( e.getMessage() );
206 }
207
208 return doc;
209 }
210
211 /**
212 * Exports a <code>DescribeRecordResponse</code> instance to a
213 * <code>DescribeRecordResponseDocument</code>.
214 *
215 * @param response
216 * @return DOM representation of the <code>DescribeRecordResponse</code>
217 * @throws XMLException
218 * if XML template could not be loaded
219 */
220 public static DescribeRecordResultDocument export( DescribeRecordResult response )
221 throws XMLException {
222
223 DescribeRecordResultDocument responseDocument = new DescribeRecordResultDocument();
224
225 String ns = response.getRequest().getVersion().equals( "2.0.2" ) ? CSW202NS.toString() : CSWNS.toString();
226
227 try {
228 responseDocument.createEmptyDocument( response.getRequest().getVersion() );
229 Element rootElement = responseDocument.getRootElement();
230 Document doc = rootElement.getOwnerDocument();
231
232 // 'SchemaComponent'-elements (required)
233 SchemaComponent[] components = response.getSchemaComponents();
234 for ( int i = 0; i < components.length; i++ ) {
235 Element schemaComponentElement = doc.createElementNS( ns, "csw:SchemaComponent" );
236
237 // 'targetNamespace'-attribute (required)
238 schemaComponentElement.setAttribute( "targetNamespace", components[i].getTargetNamespace().toString() );
239
240 // 'parentSchema'-attribute (optional)
241 if ( components[i].getParentSchema() != null ) {
242 schemaComponentElement.setAttribute( "parentSchema", components[i].getParentSchema().toString() );
243 }
244
245 // 'schemaLanguage'-attribute (required)
246 schemaComponentElement.setAttribute( "schemaLanguage", components[i].getSchemaLanguage().toString() );
247
248 XMLTools.insertNodeInto( components[i].getSchema().getRootElement(), schemaComponentElement );
249 rootElement.appendChild( schemaComponentElement );
250 }
251 } catch ( Exception e ) {
252 LOG.logError( e.getMessage(), e );
253 throw new XMLException( e.getMessage() );
254 }
255 return responseDocument;
256 }
257
258 /**
259 * Exports a <code>GetRecords</code> instance to a <code>GetRecordsDocument</code>.
260 *
261 * @param request
262 * @return DOM representation of the <code>GetRecords</code>
263 * @throws XMLException
264 * if some elements could not be appended
265 * @throws OGCWebServiceException
266 * if an error occurred while creating the xml-representation of the GetRecords
267 * bean.
268 */
269 public static GetRecordsDocument export( GetRecords request )
270 throws XMLException, OGCWebServiceException {
271
272 GetRecordsDocument getRecordsDocument = new GetRecordsDocument();
273 try {
274 getRecordsDocument.createEmptyDocument();
275 } catch ( Exception e ) {
276 throw new XMLException( e.getMessage() );
277 }
278 Element rootElement = getRecordsDocument.getRootElement();
279 Document doc = rootElement.getOwnerDocument();
280
281 // 'version'-attribute
282 rootElement.setAttribute( "version", request.getVersion() );
283
284 // 'resultType'-attribute
285 rootElement.setAttribute( "resultType", request.getResultTypeAsString() );
286
287 // 'outputFormat'-attribute
288 rootElement.setAttribute( "outputFormat", request.getOutputFormat() );
289
290 // 'outputSchema'-attribute
291 rootElement.setAttribute( "outputSchema", request.getOutputSchema() );
292
293 // 'startPosition'-attribute
294 rootElement.setAttribute( "startPosition", "" + request.getStartPosition() );
295
296 // 'maxRecords'-attribute
297 rootElement.setAttribute( "maxRecords", "" + request.getMaxRecords() );
298
299 // '<csw:DistributedSearch>'-element
300 if ( request.getHopCount() != -1 ) {
301 Element distributedSearchElement = doc.createElementNS( CSWNS.toString(), "csw:DistributedSearch" );
302
303 // 'hopCount'-attribute
304 distributedSearchElement.setAttribute( "hopCount", "" + request.getHopCount() );
305 rootElement.appendChild( distributedSearchElement );
306 }
307
308 // '<csw:ResponseHandler>'-elements (optional)
309 URI responseHandler = request.getResponseHandler();
310 if ( responseHandler != null ) {
311 Element responseHandlerElement = doc.createElementNS( CSWNS.toString(), "csw:ResponseHandler" );
312 responseHandlerElement.appendChild( doc.createTextNode( responseHandler.toASCIIString() ) );
313 rootElement.appendChild( responseHandlerElement );
314
315 }
316
317 // '<csw:Query>'-elements (required)
318 Query query = request.getQuery();
319 if ( query != null ) {
320 LOG.logDebug( "Adding the csw:Query element to the csw:GetRecords document" );
321 Element queryElement = doc.createElementNS( CSWNS.toString(), "csw:Query" );
322
323 // 'typeName'-attribute
324 // Testing for the list of typenames.
325 List<QualifiedName> typeNames = query.getTypeNamesAsList();
326 Map<String, QualifiedName> aliases = new HashMap<String, QualifiedName>(
327 query.getDeclaredTypeNameVariables() );
328 if ( typeNames.size() > 0 ) {
329 appendTypeNamesAttribute( rootElement, queryElement, typeNames, aliases );
330 } else {
331
332 String s = StringTools.arrayToString( query.getTypeNames(), ',' );
333 queryElement.setAttribute( "typeNames", s );
334 }
335
336 // '<csw:ElementSetName>'-element (optional)
337 if ( query.getElementSetName() != null ) {
338 Element elementSetNameElement = doc.createElementNS( CSWNS.toString(), "csw:ElementSetName" );
339 List<QualifiedName> elementSetNameTypeNamesList = query.getElementSetNameTypeNamesList();
340 if ( query.getElementSetNameVariables() != null && query.getElementSetNameVariables().size() > 0 ) {
341 throw new OGCWebServiceException(
342 "The elementSetName element in a csw:GetRecords request may not refrerence variables (aka. aliases), aborting request" );
343 }
344 if ( elementSetNameTypeNamesList.size() > 0 ) {
345 appendTypeNamesAttribute( rootElement, elementSetNameElement, elementSetNameTypeNamesList, null );
346 }
347 elementSetNameElement.appendChild( doc.createTextNode( query.getElementSetName() ) );
348 queryElement.appendChild( elementSetNameElement );
349 }
350
351 // '<csw:ElementName>'-elements (optional)
352 if ( query.getElementNamesAsPropertyPaths() != null ) {
353 List<PropertyPath> elementNames = query.getElementNamesAsPropertyPaths();
354 for ( int j = 0; j < elementNames.size(); j++ ) {
355 Element elementNameElement = doc.createElementNS( CSWNS.toString(), "csw:ElementName" );
356 elementNameElement.appendChild( doc.createTextNode( elementNames.get( j ).getAsString() ) );
357 queryElement.appendChild( elementNameElement );
358 }
359 }
360
361 // '<csw:Constraint>'-element (optional)
362 if ( query.getContraint() != null ) {
363 Element constraintElement = doc.createElementNS( CSWNS.toString(), "csw:Constraint" );
364 constraintElement.setAttribute( "version", "1.0.0" );
365 org.deegree.model.filterencoding.XMLFactory.appendFilter( constraintElement, query.getContraint() );
366 queryElement.appendChild( constraintElement );
367 }
368
369 // '<ogc:SortBy>'-element (optional)
370 SortProperty[] sortProperties = query.getSortProperties();
371 if ( sortProperties != null && sortProperties.length != 0 ) {
372 Element sortByElement = doc.createElementNS( OGCNS.toString(), "ogc:SortBy" );
373
374 // '<ogc:SortProperty>'-elements
375 for ( int j = 0; j < sortProperties.length; j++ ) {
376 Element sortPropertiesElement = doc.createElementNS( OGCNS.toString(), "ogc:SortProperty" );
377
378 // '<ogc:PropertyName>'-element (required)
379 Element propertyNameElement = doc.createElementNS( OGCNS.toString(), "ogc:PropertyName" );
380 appendPropertyPath( propertyNameElement, sortProperties[j].getSortProperty() );
381
382 // '<ogc:SortOrder>'-element (optional)
383 Element sortOrderElement = doc.createElementNS( OGCNS.toString(), "ogc:SortOrder" );
384 Node tn = doc.createTextNode( sortProperties[j].getSortOrder() ? "ASC" : "DESC" );
385 sortOrderElement.appendChild( tn );
386
387 sortPropertiesElement.appendChild( propertyNameElement );
388 sortPropertiesElement.appendChild( sortOrderElement );
389 sortByElement.appendChild( sortPropertiesElement );
390 }
391 queryElement.appendChild( sortByElement );
392 }
393 rootElement.appendChild( queryElement );
394 }
395 return getRecordsDocument;
396 }
397
398 /**
399 *
400 * @param rootElement
401 * the first node of the Docuement
402 * @param toBeInserted
403 * to which the typeNames attribute will be appended
404 * @param typeNames
405 * to be inserted into the toBeInserted element
406 * @param aliases
407 * may be <code>null</code>, if not each typeName must have exactly one alias
408 * defined, which will be inserted after the typename (e.g. typename=$o). If map must
409 * contain a mapping from variable to qualifiedName (e.g. [o, typeName]);
410 */
411 protected static void appendTypeNamesAttribute( Element rootElement, Element toBeInserted,
412 List<QualifiedName> typeNames, Map<String, QualifiedName> aliases ) {
413 if ( !typeNames.isEmpty() ) {
414 for ( QualifiedName qName : typeNames ) {
415 LOG.logDebug( "found typeName: " + qName );
416 }
417 LOG.logDebug( "for the element: " + toBeInserted.getNodeName()
418 + " we are trying to set the typeNames attribute." );
419 StringBuffer sb = new StringBuffer();
420 int count = 0;
421 for ( QualifiedName qName : typeNames ) {
422 if ( qName.getLocalName() != null ) {
423 URI ns = qName.getNamespace();
424 String prefix = qName.getPrefix();
425 if ( ns != null && prefix != null ) {
426 URI boundNS = null;
427 try {
428 boundNS = XMLTools.getNamespaceForPrefix( prefix, toBeInserted );
429 } catch ( URISyntaxException e ) {
430 // why for crying out loud an UriSyntax exception while lookin up stuff
431 // (without giving
432 // an
433 // uri).
434 }
435 LOG.logDebug( "ElementSetName/@typeNames: Found the namespace " + boundNS + " for the prefix: "
436 + prefix + " from typename (localname) : " + qName.getLocalName() );
437 if ( boundNS == null ) {
438 if ( CommonNamespaces.OASIS_EBRIMNS.equals( ns ) ) {
439 XMLTools.appendNSBinding( rootElement, "rim", ns );
440 LOG.logDebug( toBeInserted.getLocalName()
441 + "/@typeName: While no namespace was bound to the prefix: " + prefix
442 + " the namespace: " + ns
443 + " has been bound to 'rim' in the the root element." );
444 } else {
445 XMLTools.appendNSBinding( rootElement, prefix, ns );
446 LOG.logDebug( toBeInserted.getLocalName()
447 + "/@typeName: While no namespace was bound to the prefix: " + prefix
448 + " the namespace: " + ns + " has been bound to '" + prefix
449 + "' in the the root element." );
450 }
451
452 }
453 }
454 String typeName = prefix;
455 if ( typeName != null ) {
456 typeName += ":" + qName.getLocalName();
457 }
458 sb.append( typeName );
459 if ( aliases != null ) {
460 if ( aliases.containsValue( qName ) ) {
461 Set<String> keys = aliases.keySet();
462 for ( String key : keys ) {
463 if ( aliases.get( key ).equals( qName ) ) {
464 sb.append( "=" ).append( key );
465 aliases.remove( key );
466 break;
467 }
468 }
469 } else if ( aliases.size() > 0 ) {
470 LOG.logError( "No variable mapping found for typename: "
471 + typeName
472 + " this may not be, because every single typename must or no typename may have a variable!" );
473 }
474 }
475 if ( ++count < typeNames.size() ) {
476 sb.append( " " );
477 }
478 }
479 }
480 if ( !"null".equals( sb.toString().trim() ) && !"".equals( sb.toString().trim() ) ) {
481 LOG.logDebug( "for the element: " + toBeInserted.getNodeName()
482 + " we are settin the typeNames attribute to: " + sb.toString() );
483 toBeInserted.setAttribute( "typeNames", sb.toString() );
484 }
485 }
486 }
487
488 /**
489 * Exports a <code>GetRecordById</code> instance to a <code>GetRecordByIdDocument</code>.
490 *
491 * @param request
492 * @return DOM representation of the <code>GetRecordById</code>
493 * @throws XMLException
494 * if XML template could not be loaded
495 */
496 public static GetRecordByIdDocument export( GetRecordById request )
497 throws XMLException {
498
499 GetRecordByIdDocument getRecordByIdDoc = new GetRecordByIdDocument();
500 try {
501 getRecordByIdDoc.createEmptyDocument();
502 } catch ( Exception e ) {
503 throw new XMLException( e.getMessage() );
504 }
505 Element rootElement = getRecordByIdDoc.getRootElement();
506 Document doc = rootElement.getOwnerDocument();
507
508 // 'version'-attribute
509 rootElement.setAttribute( "version", request.getVersion() );
510
511 String[] ids = request.getIds();
512 for ( int i = 0; i < ids.length; i++ ) {
513 Element idElement = doc.createElementNS( CSWNS.toString(), "csw:Id" );
514 idElement.appendChild( doc.createTextNode( ids[i] ) );
515 rootElement.appendChild( idElement );
516 }
517
518 String elementSetName = request.getElementSetName();
519 if ( elementSetName != null ) {
520 Element esnElement = doc.createElementNS( CSWNS.toString(), "csw:ElementSetName" );
521 esnElement.appendChild( doc.createTextNode( elementSetName ) );
522 rootElement.appendChild( esnElement );
523 }
524
525 return getRecordByIdDoc;
526 }
527 }