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 }