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 }