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