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