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 }