001 /*---------------------------------------------------------------------------- 002 This file is part of deegree, http://deegree.org/ 003 Copyright (C) 2001-2009 by: 004 Department of Geography, University of Bonn 005 and 006 lat/lon GmbH 007 008 This library is free software; you can redistribute it and/or modify it under 009 the terms of the GNU Lesser General Public License as published by the Free 010 Software Foundation; either version 2.1 of the License, or (at your option) 011 any later version. 012 This library is distributed in the hope that it will be useful, but WITHOUT 013 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 014 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 015 details. 016 You should have received a copy of the GNU Lesser General Public License 017 along with this library; if not, write to the Free Software Foundation, Inc., 018 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 019 020 Contact information: 021 022 lat/lon GmbH 023 Aennchenstr. 19, 53177 Bonn 024 Germany 025 http://lat-lon.de/ 026 027 Department of Geography, University of Bonn 028 Prof. Dr. Klaus Greve 029 Postfach 1147, 53001 Bonn 030 Germany 031 http://www.geographie.uni-bonn.de/deegree/ 032 033 e-mail: info@deegree.org 034 ----------------------------------------------------------------------------*/ 035 package org.deegree.ogcwebservices.csw.iso_profile.ebrim; 036 037 import java.io.BufferedReader; 038 import java.io.IOException; 039 import java.io.InputStream; 040 import java.io.InputStreamReader; 041 import java.net.URI; 042 import java.net.URISyntaxException; 043 import java.util.ArrayList; 044 import java.util.HashMap; 045 import java.util.LinkedList; 046 import java.util.List; 047 import java.util.Map; 048 import java.util.Properties; 049 import java.util.Queue; 050 051 import org.deegree.datatypes.QualifiedName; 052 import org.deegree.framework.log.ILogger; 053 import org.deegree.framework.log.LoggerFactory; 054 import org.deegree.framework.util.StringTools; 055 import org.deegree.framework.xml.NamespaceContext; 056 import org.deegree.framework.xml.XMLParsingException; 057 import org.deegree.framework.xml.XMLTools; 058 import org.deegree.ogcbase.CommonNamespaces; 059 import org.deegree.ogcwebservices.OGCWebServiceException; 060 import org.deegree.ogcwebservices.csw.discovery.GetRecordsDocument; 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 * Maps valid ebrim propertypaths including variables (aka aliases) e.g. $o1 to the according wfs-propertyPaths. 069 * 070 * @version $Revision: 1.8 $ 071 * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a> 072 * @author last edited by: $Author: bezema $ 073 * 074 * @version 1.0. $Revision: 1.8 $, $Date: 2007-06-21 13:53:57 $ 075 * 076 * @since 2.0 077 */ 078 public class EBRIM_Mapping { 079 private final static ILogger LOG = LoggerFactory.getLogger( EBRIM_Mapping.class ); 080 081 private final Properties mapping = new Properties(); 082 083 private final static NamespaceContext nsContext = CommonNamespaces.getNamespaceContext(); 084 085 private final static String rimNS = CommonNamespaces.OASIS_EBRIMNS.toString(); 086 087 private final static String wrsNS = CommonNamespaces.WRS_EBRIMNS.toString(); 088 089 private Map<String, QualifiedName> variables; 090 091 // Used to do some parsing of QNames and variables. 092 private GetRecordsDocument gd; 093 094 // the root document node to handle the prefixes. 095 private Node rootNode = null; 096 097 // /** 098 // * A very important method, it allows the retrieval of the original dom tree from the xslt-processor, which in 099 // it's 100 // * turn can be used to find the prefix mappings used in the document. 101 // * 102 // * @param context 103 // * (org.apache.xalan.extensions.XSLProcessorContext) the xslt Context, which will be given by the xalan 104 // * processor 105 // * @param extElem 106 // * (org.apache.xalan.templates.ElemExtensionCall) a class encapsulating some calling parameters from the 107 // * xslt script, it is not used in this class. 108 // */ 109 // public void init( Object context, @SuppressWarnings("unused") 110 // Object extElem ) { 111 // // public void init(org.apache.xalan.extensions.XSLProcessorContext context, @SuppressWarnings("unused") 112 // // org.apache.xalan.templates.ElemExtensionCall extElem ) { 113 // LOG.logWarning( "The EBRIM_Mapping should use the xalan api, but currently not supported" ); 114 // // rootNode = context.getSourceTree(); 115 // if ( LOG.getLevel() == ILogger.LOG_DEBUG ) { 116 // LOG.logDebug( " in init, a test to find the 'rim' prefix" ); 117 // URI ns = null; 118 // try { 119 // ns = XMLTools.getNamespaceForPrefix( "rim", rootNode ); 120 // LOG.logDebug( " The sourcetree we got from the XSLProcessorContext was the following:" ); 121 // LOG.logDebug( "-----------------------------------------------------" ); 122 // XMLFragment frag = new XMLFragment( (Element) rootNode ); 123 // frag.prettyPrint( System.out ); 124 // LOG.logDebug( "-----------------------------------------------------" ); 125 // } catch ( URISyntaxException e ) { 126 // LOG.logError( "CSW (ebRIM) EBRIM_Mapping: Couldn't get a namespace for prefix rim because: ", e ); 127 // } catch ( TransformerException e ) { 128 // LOG.logError( "CSW (ebRIM) EBRIM_Mapping: Couldn't output the sourcetree because: ", e ); 129 // } 130 // if ( ns != null ) { 131 // LOG.logDebug( " For the 'rim:' prefix we found following ns: " + ns.toASCIIString() ); 132 // } else { 133 // LOG.logError( "CSW (ebRIM) EBRIM_Mapping: No namespace found for 'rim:' prefix!" ); 134 // } 135 // } 136 // } 137 138 /** 139 * The constructor parses the configuration file 'adv_catalog.properties' containing the propertypath mappings from 140 * ebrim to gml. 141 * 142 */ 143 public EBRIM_Mapping() { 144 try { 145 InputStream is = EBRIM_Mapping.class.getResourceAsStream( "ebrim_catalog.properties" ); 146 BufferedReader br = new BufferedReader( new InputStreamReader( is ) ); 147 String line = null; 148 int lineCount = 1; 149 while ( ( line = br.readLine() ) != null ) { 150 line = line.trim(); 151 if ( !( line.startsWith( "#" ) || "".equals( line ) ) ) { 152 String[] tmp = StringTools.toArray( line, "=", false ); 153 if ( tmp == null ) { 154 LOG.logError( lineCount + ") Found an error, please correct following line: " + line ); 155 } else { 156 if ( tmp.length < 2 ) { 157 if ( tmp.length > 0 ) { 158 LOG.logError( lineCount + ") No value applied for key: " + tmp[0] 159 + ". Key-value pairs must be seperated by an '='sign." ); 160 } else { 161 LOG.logError( lineCount + ") The : " + tmp[0] 162 + ". Key-value pairs must be seperated by an '='sign." ); 163 } 164 } else if ( tmp.length > 2 ) { 165 LOG.logError( lineCount 166 + ") Only one seperator '=' is allowed in a key-value pair, please correct following line: " 167 + line ); 168 } else { 169 mapping.put( tmp[0], tmp[1] ); 170 } 171 } 172 } 173 lineCount++; 174 } 175 176 } catch ( IOException e ) { 177 LOG.logError( 178 "CSW (ebRIM) EBRIM_Mapping: An error occurred while trying to parse the 'adv_catalog.properties' file.", 179 e ); 180 e.printStackTrace(); 181 } 182 LOG.logDebug( " The Ebrim-Mapper found following Mappings:\n" + mapping ); 183 variables = new HashMap<String, QualifiedName>(); 184 gd = new GetRecordsDocument(); 185 } 186 187 /** 188 * maps a property name of GetRecords, Delete and Update request from the catalogue schema to the underlying WFS 189 * schema 190 * 191 * @param node 192 * containing the propertyPath 193 * @return the mapped propertypath 194 * @throws XMLParsingException 195 */ 196 public String mapPropertyPath( Node node ) 197 throws XMLParsingException { 198 199 String propertyNode = XMLTools.getNodeAsString( node, ".", nsContext, null ); 200 propertyNode = stripRoot( propertyNode ); 201 LOG.logDebug( "The supplied xml-Node results into following (normalized) propertypath: " + propertyNode ); 202 LOG.logDebug( "EBRIMG_Mapping#mapPropertyPath: We have got the variables: " + variables.toString() ); 203 204 // Setting the node which will be used to find the prefixes. 205 Node prefixResolverNode = getPrefixResolverNode( node ); 206 207 String[] props = propertyNode.split( "/" ); 208 StringBuffer result = new StringBuffer( "/" ); 209 int count = 0; 210 String previousMapping = null; 211 for ( String propertyName : props ) { 212 LOG.logDebug( "Trying to map propertyName: " + propertyName ); 213 if ( propertyName != null ) { 214 // check if the propertyname references a variable. 215 String tmpVarReference = null; 216 String mappedName = null; 217 if ( propertyName.startsWith( "$" ) ) { 218 tmpVarReference = propertyName; 219 if ( !variables.containsKey( propertyName.substring( 1 ) ) 220 || variables.get( propertyName.substring( 1 ) ) == null ) { 221 222 LOG.logError( "CSW (ebRIM) EBRIM_Mapping: The referenced Variable '" + propertyName 223 + "' in the propertyNode: '" + propertyNode 224 + "' has not been declared this can't be!" ); 225 LOG.logError( "CSW (ebRIM) EBRIM_Mapping: We have got the variables: " + variables.toString() ); 226 } else { 227 // get the rim/wrs object to which this variable is bound to. 228 propertyName = createMapStringForQName( variables.get( propertyName.substring( 1 ) ) ); 229 LOG.logDebug( " trying to find mapping for a property: " + propertyName 230 + ", with found variable: " + tmpVarReference ); 231 mappedName = mapping.getProperty( propertyName ); 232 } 233 } else if ( propertyName.startsWith( "@" ) ) { 234 if ( previousMapping == null ) { 235 LOG.logError( "CSW (ebRIM) EBRIM_Mapping: a propertyName may not start with the @ sign, trying to find a mapping without the @ for: " 236 + propertyNode ); 237 mappedName = mapping.getProperty( propertyName.substring( 1 ) ); 238 } else { 239 // first check the RegistryObject attributes 240 // @id=app:iduri 241 // @home=app:home 242 // @lid=app:liduri 243 // @objectType=app:objectType 244 // @status=app:status 245 LOG.logDebug( " trying to find mapping for attribute: " + propertyName ); 246 mappedName = mapping.getProperty( propertyName ); 247 if ( mappedName == null || "".equals( mappedName ) ) { 248 LOG.logDebug( " the single attribute had no result, trying to find mapping with the dereferenced previous property @property: " 249 + ( previousMapping + "/" + propertyName ) ); 250 mappedName = mapping.getProperty( previousMapping + "/" + propertyName ); 251 } 252 } 253 } else { 254 LOG.logDebug( " Trying to find mapping for simple propertyName: " + propertyName ); 255 QualifiedName qName = null; 256 try { 257 qName = gd.parseQNameFromString( propertyName, prefixResolverNode, true ); 258 } catch ( URISyntaxException e ) { 259 LOG.logError( "CSW (ebRIM) EBRIM_Mapping: Could not create QualifiedName from propertyName: " 260 + propertyName + ", creating simple QualifiedName.", e ); 261 qName = new QualifiedName( propertyName ); 262 } 263 propertyName = createMapStringForQName( qName ); 264 LOG.logDebug( " trying to find mapping for a property: " + propertyName ); 265 /** 266 * This hack is needed, because both person and organization use the following features, 267 * telephoneNumber@*, EmailAddress/@* and a link to Address. 268 */ 269 if ( propertyName.endsWith( "TelephoneNumber" ) || propertyName.endsWith( "Address" ) ) { 270 propertyName = previousMapping + "/" + propertyName; 271 } 272 mappedName = mapping.getProperty( propertyName ); 273 } 274 if ( mappedName == null ) { 275 LOG.logDebug( "The propertyName: " + propertyName 276 + " could not be mapped, maybe a Valuelist or AnyValue?" ); 277 // Very dirty trick to map 278 // app:slots/app:Slot/app:values/app:SlotValues/app:geometry 279 // and app:slots/app:Slot/app:values/app:SlotValues/stringValue. 280 // from incoming rim:ValueList/rim:Value 281 // as well as the wrs:ValueList/wrs:AnyValue (defined in pkg-basic.xsd) 282 if ( ( "rim:Value".equals( propertyName ) && ( "rim:ValueList".equals( previousMapping ) || "wrs:ValueList".equals( previousMapping ) ) ) 283 || ( ( "wrs:AnyValue".equals( propertyName ) || "rim:AnyValue".equals( propertyName ) ) && ( "wrs:ValueList".equals( previousMapping ) || "rim:ValueList".equals( previousMapping ) ) ) ) { 284 if ( ( count + 1 ) != props.length ) { 285 LOG.logError( "CSW (ebRIM) EBRIM_Mapping: More properties will follow this one, this is strange, because a rim:Value can only be located at the end of a propertyPath" ); 286 } 287 Node parentNode = XMLTools.getNode( node, "..", nsContext ); 288 String parent = parentNode.getLocalName(); 289 290 LOG.logDebug( " It seems we want to map the rim:Slot/rim:ValueList/rim:Value or rim:Slot/wrs:ValueList/wrs:AnyValue, the value of the parent of this propertyName is: " 291 + parent ); 292 if ( parent != null ) { 293 if ( parent.startsWith( "Property" ) ) {// hurray a property therefore a 294 // stringValue 295 LOG.logDebug( parent + " starts with 'Property' we therefore map to app:stringValue." ); 296 mappedName = "app:stringValue"; 297 } else { 298 LOG.logDebug( parent 299 + " doesn't start with 'Property' we therefore map to app:geometry." ); 300 mappedName = "app:geometry"; 301 } 302 } 303 } else { 304 LOG.logInfo( "CSW (ebRIM) EBRIM_Mapping: found no mapping for: " + propertyName ); 305 mappedName = ""; 306 } 307 } 308 if ( !"".equals( mappedName ) ) { 309 // save the found Mapping, it might be needed to map the following propertyName 310 // if it is a property (starting with @) 311 previousMapping = propertyName; 312 result.append( addWFSPropertyPath( mappedName, tmpVarReference, ( count == 0 ) ) ); 313 if ( ( count + 1 ) != props.length ) { 314 result.append( "/" ); 315 } 316 } 317 // next property/type 318 count++; 319 } 320 } 321 322 return result.toString(); 323 } 324 325 /** 326 * maps the property names of given typenames in the typeNames attribute of a GetRecords request to the catalogue 327 * schema of the underlying WFS schema 328 * 329 * @param node 330 * the GetRecords request Node 331 * @return the mapped propertypath 332 * @throws XMLParsingException 333 */ 334 public String mapTypeNames( Node node ) 335 throws XMLParsingException { 336 String typeNamesAttribute = XMLTools.getNodeAsString( node, ".", nsContext, null ); 337 Map<String, QualifiedName> tmpVariables = new HashMap<String, QualifiedName>(); 338 // Setting the node which will be used to find the prefixes. 339 Node prefixResolverNode = getPrefixResolverNode( node ); 340 341 List<QualifiedName> typeNames = parseTypeList( prefixResolverNode, typeNamesAttribute, tmpVariables ); 342 LOG.logDebug( " found following qNames of the typeNames: " + typeNames ); 343 // adding new aliases to the query/@typenames 344 String newAliasPreFix = "kQhtYHHp_"; 345 StringBuffer queryTypeNameAttrSB = new StringBuffer(); 346 if ( tmpVariables.size() == 0 ) { 347 for ( int i = 0; i < typeNames.size(); ++i ) { 348 String aliasPrefix = newAliasPreFix + i; 349 tmpVariables.put( newAliasPreFix + i, typeNames.get( i ) ); 350 queryTypeNameAttrSB.append( "rim:" + typeNames.get( i ).getLocalName() ).append( "=" ).append( 351 aliasPrefix ); 352 if ( ( i + 1 ) < typeNames.size() ) { 353 queryTypeNameAttrSB.append( " " ); 354 } 355 } 356 } 357 358 StringBuffer resultString = new StringBuffer(); 359 int qNameCounter = 0; 360 // Because we need to reuse the member variables map, we've made a local copy of it. 361 variables.putAll( tmpVariables ); 362 LOG.logDebug( " EBRIM_Mapping#mapTypeNames: We have got the variables: " + variables.toString() ); 363 List<String> varDefs = new ArrayList<String>(); 364 for ( QualifiedName qName : typeNames ) { 365 URI ns = qName.getNamespace(); 366 String prefix = qName.getPrefix(); 367 // for debugging purposes 368 if ( prefix == null ) { 369 prefix = ""; 370 } else { 371 prefix += ":"; 372 } 373 if ( ns == null || rimNS.equals( ns.toString() ) ) { 374 LOG.logDebug( " We found the following namespace for the ElementSetName/@typeName: " + ns 375 + " so we map to the prefix rim." ); 376 prefix = "rim:"; 377 } 378 String result = mapping.getProperty( prefix + qName.getLocalName() ); 379 LOG.logDebug( " for the FeatureType: " + prefix + qName.getLocalName() + " we found following mapping: " 380 + result ); 381 if ( result != null ) { 382 resultString.append( result ); 383 // get the mapped variable for this qName 384 String var = getVariableForQName( qName, tmpVariables ); 385 if ( var != null ) { 386 // resultString.append( "=" ).append( var ); 387 varDefs.add( var ); 388 } 389 } else { 390 LOG.logInfo( "CSW (ebRIM) EBRIM_Mapping: Found no mapping for: " + prefix + qName.getLocalName() 391 + ", so ignoring it." ); 392 } 393 if ( ++qNameCounter < typeNames.size() ) { 394 resultString.append( " " ); 395 } 396 } 397 // append the aliases= variables to the resultstring 398 if ( varDefs.size() != 0 ) { 399 LOG.logDebug( " The defined variables list is not empty, we therefore append the alias keyword to the typeName" ); 400 resultString.append( " aliases=" ); 401 for ( int i = 0; i < varDefs.size(); ++i ) { 402 resultString.append( varDefs.get( i ) ); 403 if ( ( i + 1 ) < varDefs.size() ) { 404 resultString.append( " " ); 405 } 406 } 407 } 408 409 return resultString.toString(); 410 } 411 412 /** 413 * This method take an elementSetName node and conerts it's content into a list of wfs:PropertyName nodes. Depending 414 * on the (String) value of the elementSetNameNode (brief, summary, full) the propertyNames will have different 415 * values. For this method to work a temporal document is builded from which one element is created, this element 416 * will hold all child <wfs:PropertyName> elements. 417 * 418 * @param elementSetNameNode 419 * the ElementSetName Node of the incoming GetRecords request. 420 * @return a Nodelist containing <wfs:PropertyName> nodes. 421 */ 422 public NodeList mapElementSetName( Node elementSetNameNode ) { 423 424 // creating an empty document, so we can append nodes to it, which will be returned as a 425 // Nodelist to the xslt script. 426 Document doc = XMLTools.create(); 427 Element resultElement = doc.createElement( "wfs:result" ); 428 Node newQueryNode = doc.importNode( elementSetNameNode.getParentNode(), true ); 429 try { 430 String elementSetName = XMLTools.getNodeAsString( elementSetNameNode, ".", nsContext, null ); 431 LOG.logDebug( " Found following elementSetName: " + elementSetName ); 432 if ( elementSetName != null ) { 433 // Setting the node which will be used to find the prefixes. 434 Node prefixResolverNode = getPrefixResolverNode( newQueryNode ); 435 String typeNamesAttribute = XMLTools.getNodeAsString( elementSetNameNode, "@typeNames", nsContext, null ); 436 // Element queryElement = (Element) newElementSetNameNode.getParentNode(); 437 Element queryElement = (Element) newQueryNode; 438 String queryTypeNamesAttribute = XMLTools.getNodeAsString( queryElement, "@typeNames", nsContext, null ); 439 if ( queryTypeNamesAttribute == null ) { 440 LOG.logError( "CSW (ebRIM) EBRIM_Mapping: no typeNames attribute found in the csw:Query element, this may not be!!!" ); 441 } 442 443 // First find the variables in the csw:Query/@typeNames. 444 Map<String, QualifiedName> varsInQuery = new HashMap<String, QualifiedName>(); 445 varsInQuery.putAll( variables ); 446 // List<QualifiedName> queryTypeNames = 447 // parseTypeList( prefixResolverNode, queryTypeNamesAttribute, varsInQuery ); 448 if ( varsInQuery.size() == 0 ) { 449 LOG.logError( "CSW (ebRIM) EBRIM_Mapping: We found no variables in the query, something is terribly wrong" ); 450 } 451 // StringBuffer queryTypeNameAttrSB = new StringBuffer( queryTypeNamesAttribute ); 452 // boolean resetQueryTypeNames = false; 453 454 if ( typeNamesAttribute == null ) { 455 // if ( varsInQuery.size() == 0 ) { 456 // resetQueryTypeNames = true; 457 // } 458 LOG.logDebug( " no typeNames attribute found in the csw:ElementSetName node, therefore taking the typeNames of the query node." ); 459 typeNamesAttribute = queryTypeNamesAttribute; 460 } 461 // if ( varsInQuery.size() == 0 && !typeNamesAttribute.equals( queryTypeNamesAttribute ) ) { 462 // // adding new aliases to the query/@typenames 463 // String newAliasPreFix = "kQhtYHHp_"; 464 // queryTypeNameAttrSB = new StringBuffer(); 465 // for ( int i = 0; i < queryTypeNames.size(); ++i ) { 466 // String aliasPrefix = newAliasPreFix + i; 467 // varsInQuery.put( newAliasPreFix + i, queryTypeNames.get( i ) ); 468 // queryTypeNameAttrSB.append( "rim:" + queryTypeNames.get( i ).getLocalName() ).append( "=" ).append( 469 // aliasPrefix ); 470 // if ( ( i + 1 ) < queryTypeNames.size() ) { 471 // queryTypeNameAttrSB.append( " " ); 472 // } 473 // } 474 // queryElement.setAttribute( "typeNames", queryTypeNameAttrSB.toString() ); 475 // } 476 477 if ( typeNamesAttribute != null ) { 478 479 Map<String, QualifiedName> vars = new HashMap<String, QualifiedName>(); 480 List<QualifiedName> typeNames = parseTypeList( prefixResolverNode, typeNamesAttribute, vars ); 481 if ( vars.size() != 0 && !typeNamesAttribute.equals( queryTypeNamesAttribute ) ) { 482 LOG.logError( "CSW (ebRIM) EBRIM_Mapping: Found variables (aliases) in the ElementSetName/@typeNames attribute this is not allowed, we will not process them." ); 483 } 484 LOG.logDebug( " Parent of the elementSetName has a local name (should be query): " 485 + queryElement.getLocalName() ); 486 Element constraint = XMLTools.getElement( queryElement, "csw:Constraint", nsContext ); 487 if ( constraint == null ) { 488 LOG.logDebug( " No contraint node found, therefore creating one." ); 489 Element a = queryElement.getOwnerDocument().createElementNS( 490 CommonNamespaces.CSWNS.toASCIIString(), 491 "csw:Constraint" ); 492 queryElement.getOwnerDocument().importNode( a, false ); 493 constraint = (Element) queryElement.appendChild( a ); 494 } 495 Element filter = XMLTools.getElement( constraint, "ogc:Filter", nsContext ); 496 if ( filter == null ) { 497 LOG.logDebug( " No filter node found, therefore creating one." ); 498 // Element a = queryElement.getOwnerDocument().createElementNS( 499 // CommonNamespaces.OGCNS.toASCIIString(), 500 // "ogc:Filter" ); 501 Element a = doc.createElementNS( CommonNamespaces.OGCNS.toASCIIString(), "ogc:Filter" ); 502 // queryElement.getOwnerDocument().importNode( a, false ); 503 doc.importNode( a, false ); 504 filter = (Element) constraint.appendChild( a ); 505 } 506 Node firstOriginalFilterNode = filter.getFirstChild(); 507 Element andNode = null; 508 if ( firstOriginalFilterNode != null ) { 509 LOG.logDebug( " The ogc:Filter has a firstChild node, therefore creating an extra ogc:And." ); 510 Element a = doc.createElementNS( CommonNamespaces.OGCNS.toASCIIString(), "ogc:And" ); 511 // queryElement.getOwnerDocument().importNode( a, false ); 512 doc.importNode( a, false ); 513 andNode = (Element) filter.appendChild( a ); 514 andNode.appendChild( firstOriginalFilterNode ); 515 } 516 // Element tmpNode = queryElement.getOwnerDocument().createElementNS( 517 // CommonNamespaces.OGCNS.toASCIIString(), 518 // "ogc:And" ); 519 Element tmpNode = doc.createElementNS( CommonNamespaces.OGCNS.toASCIIString(), "ogc:And" ); 520 // queryElement.getOwnerDocument().importNode( tmpNode, false ); 521 doc.importNode( tmpNode, false ); 522 Element topNode = null; 523 if ( andNode != null ) { 524 if ( typeNames.size() > 1 ) { 525 topNode = (Element) andNode.appendChild( tmpNode ); 526 } else { 527 topNode = andNode; 528 } 529 } else { 530 if ( typeNames.size() > 1 ) { 531 topNode = (Element) filter.appendChild( tmpNode ); 532 } else { 533 topNode = filter; 534 } 535 536 } 537 // a random string to be used for the extra filter. 538 539 for ( int i = 0; i < typeNames.size(); ++i ) { 540 QualifiedName qName = typeNames.get( i ); 541 URI ns = qName.getNamespace(); 542 String prefix = qName.getPrefix(); 543 // for debugging purposes 544 if ( prefix == null ) { 545 prefix = ""; 546 } else { 547 prefix += ":"; 548 } 549 if ( ns == null || rimNS.equals( ns.toString() ) ) { 550 LOG.logDebug( " We found the following namespace for the ElementSetName/@typeName: " + ns 551 + " so we map to the prefix rim." ); 552 prefix = "rim:"; 553 } 554 String result = mapping.getProperty( prefix + qName.getLocalName() ); 555 LOG.logDebug( " for the FeatureType: " + prefix + qName.getLocalName() 556 + " we found following mapping: " + result ); 557 if ( result != null ) { 558 if ( !"app:RegistryObject".equals( result ) ) { 559 LOG.logError( "CSW (ebRIM) EBRIM_Mapping: the given typeName is not a RegistryObject, and can therefore not be returned: " 560 + prefix + qName.getLocalName() ); 561 } else { 562 String aliasPrefix = getVariableForQName( qName, varsInQuery ); 563 LOG.logDebug( " in elementSetName found alias: " + aliasPrefix + " for the typename: " 564 + qName.getLocalName() ); 565 if ( aliasPrefix == null || "".equals( aliasPrefix ) ) { 566 LOG.logError( "CSW (ebRIM) EBRIM_Mapping: in elementSetName found no alias for the typeName: " 567 + qName.getLocalName() + " this cannot be!" ); 568 } 569 570 // 571 appendRegistryObjects( resultElement, "/$" + aliasPrefix ); 572 // appendRegistryObjects( resultElement, elementSetName, "/" + result ); 573 574 // now appending an ogc:PropertyIsEqual to the ogc:Or node of the ogc:Filter 575 // tmpNode = queryElement.getOwnerDocument().createElementNS( 576 // CommonNamespaces.OGCNS.toASCIIString(), 577 // "ogc:PropertyIsEqualTo" ); 578 tmpNode = doc.createElementNS( CommonNamespaces.OGCNS.toASCIIString(), 579 "ogc:PropertyIsEqualTo" ); 580 581 // queryElement.getOwnerDocument().importNode( tmpNode, false ); 582 doc.importNode( tmpNode, false ); 583 Element equalTo = (Element) topNode.appendChild( tmpNode ); 584 equalTo.setAttribute( "matchCase", "true" ); 585 586 // create the propertyName node referencing the app:RegistryObject/app:type 587 // tmpNode = queryElement.getOwnerDocument().createElementNS( 588 // CommonNamespaces.OGCNS.toASCIIString(), 589 // "ogc:PropertyName" ); 590 tmpNode = doc.createElementNS( CommonNamespaces.OGCNS.toASCIIString(), 591 "ogc:PropertyName" ); 592 // queryElement.getOwnerDocument().importNode( tmpNode, false ); 593 doc.importNode( tmpNode, false ); 594 Element propName = (Element) equalTo.appendChild( tmpNode ); 595 XMLTools.setNodeValue( propName, "$" + aliasPrefix + "/rim:type" ); 596 597 // create the literal (localname) node to which the propertyname 598 // app:RegistryObject/app:type should match 599 // tmpNode = queryElement.getOwnerDocument().createElementNS( 600 // CommonNamespaces.OGCNS.toASCIIString(), 601 // "ogc:Literal" ); 602 tmpNode = doc.createElementNS( CommonNamespaces.OGCNS.toASCIIString(), "ogc:Literal" ); 603 // queryElement.getOwnerDocument().importNode( tmpNode, false ); 604 doc.importNode( tmpNode, false ); 605 Element literal = (Element) equalTo.appendChild( tmpNode ); 606 XMLTools.setNodeValue( literal, qName.getLocalName() ); 607 608 // define a new alias for the typeNames 609 // queryTypeNameAttrSB.append( "rim:" + qName.getLocalName() ).append( "=" ).append( 610 // aliasPrefix ); 611 // if ( ( i + 1 ) < typeNames.size() ) { 612 // queryTypeNameAttrSB.append( " " ); 613 // } 614 615 } 616 } else { 617 LOG.logInfo( "CSW (ebRIM) EBRIM_Mapping: Found no mapping for: " + prefix 618 + qName.getLocalName() + ", so ignoring it." ); 619 } 620 } 621 // add the typeNames attribute to the QueryElement 622 // if ( resetQueryTypeNames ) { 623 // queryElement.setAttribute( "typeNames", queryTypeNameAttrSB.toString() ); 624 // } 625 // if ( LOG.getLevel() == ILogger.LOG_DEBUG ) { 626 // XMLFragment docTest = new XMLFragment( queryElement ); 627 // LOG.logDebug( " newly created query Element: \n" 628 // + docTest.getAsPrettyString() ); 629 // } 630 } 631 } 632 } catch ( XMLParsingException e ) { 633 LOG.logError( "CSW (ebRIM) EBRIM_Mapping: following error occured while trying to map elementSetName node", 634 e ); 635 } 636 NodeList nodeList = resultElement.getChildNodes(); 637 for ( int i = 0; i < nodeList.getLength(); ++i ) { 638 LOG.logDebug( " Node " + i + " has a localName: " + nodeList.item( i ).getLocalName() ); 639 } 640 641 return resultElement.getChildNodes(); 642 } 643 644 /** 645 * Maps the typename of the given Element if the targetNamespace attribute of the given Node equals the rimNS. 646 * 647 * @param typeNameElement 648 * the TypeName Element inside a DescribeRecord request. 649 * @return the mapping of the featureType requested or an empty string (e.g. ""). 650 */ 651 public String mapTypeNameElement( Node typeNameElement ) { 652 String typeNameValue = null; 653 if ( typeNameElement != null ) { 654 try { 655 String targetNamespace = XMLTools.getRequiredNodeAsString( typeNameElement, "@targetNamespace", 656 nsContext ); 657 if ( rimNS.equals( targetNamespace ) ) { 658 typeNameValue = XMLTools.getNodeAsString( typeNameElement, ".", nsContext, null ); 659 if ( typeNameValue != null ) { 660 typeNameValue = stripRoot( typeNameValue.trim() ); 661 int index = typeNameValue.lastIndexOf( ":" ); 662 if ( index != -1 ) { 663 typeNameValue = typeNameValue.substring( index ); 664 } 665 typeNameValue = mapping.getProperty( "rim:" + typeNameValue ); 666 } 667 } else { 668 LOG.logDebug( " The given namespace: " + targetNamespace 669 + " can not be mapped to the rim namespace: " + rimNS + " so no mapping is done" ); 670 } 671 } catch ( XMLParsingException e ) { 672 LOG.logInfo( e.getMessage() ); 673 } 674 } 675 if ( typeNameValue == null ) { 676 typeNameValue = ""; 677 } 678 return typeNameValue; 679 680 } 681 682 /** 683 * Searches for a given qName in the given map and removes the mapping if it is found. 684 * 685 * @param qName 686 * to search for 687 * @param tmpVariables 688 * some temporary variables 689 * @return the mapped variable or <code>null</code> if none was found. 690 */ 691 private String getVariableForQName( QualifiedName qName, Map<String, QualifiedName> tmpVariables ) { 692 if ( tmpVariables.containsValue( qName ) ) { 693 for ( String variable : tmpVariables.keySet() ) { 694 if ( qName.equals( tmpVariables.get( variable ) ) ) { 695 tmpVariables.remove( variable ); 696 return variable; 697 } 698 } 699 } 700 return null; 701 } 702 703 /** 704 * A simple method to split the given typeNamesAttribute in to type names and finds the optional variables inside 705 * them. 706 * 707 * @param node 708 * the context node which will be used to search for the prefixes. 709 * @param typeNamesAttribute 710 * the String containing the typenames attribute values. 711 * @param vars 712 * a map in which the found variables will be stored 713 * @return a list with typenames parsed from the typeNamesAttribute String, if no typenames were found the size will 714 * be 0. 715 */ 716 private List<QualifiedName> parseTypeList( Node node, String typeNamesAttribute, Map<String, QualifiedName> vars ) { 717 LOG.logDebug( " Trying to map following typeName: " + typeNamesAttribute ); 718 // System.out.println( "einmal: " + root ); 719 // DOMPrinter.printNode( root, ""); 720 String[] splitter = typeNamesAttribute.split( " " ); 721 List<QualifiedName> typeNames = new ArrayList<QualifiedName>(); 722 723 for ( String s : splitter ) { 724 try { 725 LOG.logDebug( " Trying to parse following typeName (with/without variables): " + s ); 726 if ( !s.startsWith( "$" ) ) { 727 gd.findVariablesInTypeName( s, node, typeNames, vars, true ); 728 } else { 729 // Attention, I don't know if this is the wanted behavior, we loose the variable declaration. 730 LOG.logDebug( " Because the given typeName starts with an '$'-sign, we first dereference the alias: " 731 + s.substring( 1 ) ); 732 if ( variables.containsKey( s.substring( 1 ) ) ) { 733 QualifiedName qName = variables.get( s.substring( 1 ) ); 734 LOG.logDebug( " \t the alias: " + s.substring( 1 ) 735 + " was therefore replaced with the propertyName: " + qName.getPrefix() + ":" 736 + qName.getLocalName() ); 737 typeNames.add( qName ); 738 } else { 739 LOG.logError( "CSW (ebRIM) EBRIM_Mapping: \t the alias was not declared, this cannot be!" ); 740 } 741 } 742 743 } catch ( OGCWebServiceException e ) { 744 LOG.logError( e.getMessage(), e ); 745 } catch ( URISyntaxException e ) { 746 LOG.logError( e.getMessage(), e ); 747 } 748 } 749 return typeNames; 750 } 751 752 /** 753 * 754 * @param mappedName 755 * to which the appropriate propertyPath will be added. 756 * @param variable 757 * starting with the "$" which will replace the mappedName. 758 * @param isFirst 759 * true if the given mappedName was the the firstelement on the requested xpath. 760 */ 761 private String addWFSPropertyPath( String mappedName, String variable, boolean isFirst ) { 762 StringBuffer toBeAdded = new StringBuffer(); 763 if ( "app:RegistryObject".equalsIgnoreCase( mappedName ) ) { 764 if ( !isFirst ) { 765 toBeAdded.append( "app:linkedRegistryObject/app:LINK_RegObj_RegObj/app:registryObject/" ); 766 } 767 } else { 768 if ( isFirst ) { 769 toBeAdded.append( "app:RegistryObject/" ); 770 } 771 if ( "app:Name".equals( mappedName ) ) { 772 toBeAdded.append( "app:name/" ); 773 } else if ( "app:Description".equalsIgnoreCase( mappedName ) ) { 774 toBeAdded.append( "app:description/" ); 775 } else if ( "app:Slot".equals( mappedName ) ) { 776 toBeAdded.append( "app:slots/" ); 777 } else if ( "app:VersionInfo".equals( mappedName ) ) { 778 toBeAdded.append( "app:versionInfo/" ); 779 } else if ( "app:ObjectRef".equals( mappedName ) ) { 780 toBeAdded.append( "app:auditableEvent/app:AuditableEvent/app:affectedObjects/" ); 781 } else if ( "app:ExtrinsicObject".equals( mappedName ) ) { 782 // this is actually the rim:ExtrinsicObject/rim:ContentVersionInfo element 783 toBeAdded.append( "app:extrinsicObject" ); 784 } 785 } 786 if ( variable != null && !"".equals( variable.trim() ) ) { 787 mappedName = variable; 788 } 789 return ( toBeAdded.toString() + mappedName ); 790 791 } 792 793 /** 794 * Recursively strips all leading '.' and '/' from the given String. 795 * 796 * @param toBeStripped 797 * the String to be stripped 798 * @return the stripped String. 799 */ 800 private String stripRoot( String toBeStripped ) { 801 if ( toBeStripped != null ) { 802 if ( toBeStripped.startsWith( "/" ) || toBeStripped.startsWith( "." ) ) { 803 LOG.logDebug( " stripping first character of: " + toBeStripped ); 804 return stripRoot( toBeStripped.substring( 1 ) ); 805 } 806 } 807 return toBeStripped; 808 } 809 810 /** 811 * appends the given mapped elementSetName/@typeNames value according to the elementSetNameValue to the given 812 * resultElement. 813 * 814 * @param resultElement 815 * to append the <wfs:PropertyNames> to 816 * @param resultedMapping 817 * the mapped TypeNames-Value 818 */ 819 private void appendRegistryObjects( Element resultElement, String resultedMapping ) { 820 // if ( "full".equalsIgnoreCase( elementSetNameValue ) ) { 821 XMLTools.appendElement( resultElement, CommonNamespaces.WFSNS, "wfs:PropertyName", resultedMapping ); 822 // } else { 823 // XMLTools.appendElement( resultElement, CommonNamespaces.WFSNS, "wfs:PropertyName", resultedMapping 824 // + "/app:iduri" ); 825 // XMLTools.appendElement( resultElement, CommonNamespaces.WFSNS, "wfs:PropertyName", resultedMapping 826 // + "/app:liduri" ); 827 // XMLTools.appendElement( resultElement, CommonNamespaces.WFSNS, "wfs:PropertyName", resultedMapping 828 // + "/app:objectType" ); 829 // XMLTools.appendElement( resultElement, CommonNamespaces.WFSNS, "wfs:PropertyName", resultedMapping 830 // + "/app:status" ); 831 // XMLTools.appendElement( resultElement, CommonNamespaces.WFSNS, "wfs:PropertyName", resultedMapping 832 // + "/app:versionInfo" ); 833 // if ( "summary".equalsIgnoreCase( elementSetNameValue ) ) { 834 // XMLTools.appendElement( resultElement, CommonNamespaces.WFSNS, "wfs:PropertyName", resultedMapping 835 // + "/app:slots" ); 836 // XMLTools.appendElement( resultElement, CommonNamespaces.WFSNS, "wfs:PropertyName", 837 // resultedMapping + "/app:name/app:Name" ); 838 // XMLTools.appendElement( resultElement, CommonNamespaces.WFSNS, "wfs:PropertyName", 839 // resultedMapping + "/app:description/app:Description" ); 840 // } 841 // } 842 } 843 844 /** 845 * Sets the prefix to the localname of the given qName to rim: if the NS of the QName equals the rimNS or the qName 846 * prefix == null (or empty) or the namespace of the given QName == null. Else the found prefix of the QName is 847 * taken (happens for example with the wrs prefix) 848 * 849 * @param qName 850 * to be parsed. 851 * @return the prefix:localname part of the qName with prefix bound to "rim" or the qNames prefix. 852 */ 853 private String createMapStringForQName( QualifiedName qName ) { 854 URI ns = qName.getNamespace(); 855 String prefix = qName.getPrefix(); 856 if ( ns == null || rimNS.equals( ns.toString() ) || qName.getPrefix() == null 857 || "".equals( qName.getPrefix().trim() ) ) { 858 prefix = "rim:"; 859 } else if ( wrsNS.equals( ns.toString() ) ) { 860 prefix = CommonNamespaces.WRS_EBRIM_PREFIX + ":"; 861 } 862 return prefix + qName.getLocalName(); 863 } 864 865 /** 866 * A simple method, which will return a node in which a prefix can be search. 867 * 868 * @param xsltNode 869 * the local node from the calling xslt processor to one of the functions. 870 * @return the original rootNode if it is not null, otherwise the given xsltNode. 871 */ 872 private Node getPrefixResolverNode( Node xsltNode ) { 873 Node tmpNode = rootNode; 874 if ( tmpNode == null ) { 875 tmpNode = xsltNode; 876 } 877 Queue<Node> nodes = new LinkedList<Node>(); 878 nodes.offer( tmpNode ); 879 while ( !nodes.isEmpty() ) { 880 tmpNode = nodes.poll(); 881 if ( xsltNode.getNodeType() == Node.ATTRIBUTE_NODE ) { 882 NamedNodeMap nnm = tmpNode.getAttributes(); 883 for ( int j = 0; j < nnm.getLength(); ++j ) { 884 if ( xsltNode.isEqualNode( nnm.item( j ) ) ) { 885 return nnm.item( j ); 886 } 887 } 888 } else { 889 if ( xsltNode.isEqualNode( tmpNode ) ) { 890 return tmpNode; 891 } 892 } 893 NodeList nl = tmpNode.getChildNodes(); 894 for ( int i = 0; i < nl.getLength(); ++i ) { 895 nodes.offer( nl.item( i ) ); 896 } 897 } 898 return xsltNode; 899 } 900 }