001 //$HeadURL: svn+ssh://jwilden@svn.wald.intevation.org/deegree/base/branches/2.5_testing/src/org/deegree/enterprise/servlet/WFSRequestMapping.java $ 002 /*---------------------------------------------------------------------------- 003 This file is part of deegree, http://deegree.org/ 004 Copyright (C) 2001-2009 by: 005 Department of Geography, University of Bonn 006 and 007 lat/lon GmbH 008 009 This library is free software; you can redistribute it and/or modify it under 010 the terms of the GNU Lesser General Public License as published by the Free 011 Software Foundation; either version 2.1 of the License, or (at your option) 012 any later version. 013 This library is distributed in the hope that it will be useful, but WITHOUT 014 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 015 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 016 details. 017 You should have received a copy of the GNU Lesser General Public License 018 along with this library; if not, write to the Free Software Foundation, Inc., 019 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 020 021 Contact information: 022 023 lat/lon GmbH 024 Aennchenstr. 19, 53177 Bonn 025 Germany 026 http://lat-lon.de/ 027 028 Department of Geography, University of Bonn 029 Prof. Dr. Klaus Greve 030 Postfach 1147, 53001 Bonn 031 Germany 032 http://www.geographie.uni-bonn.de/deegree/ 033 034 e-mail: info@deegree.org 035 ----------------------------------------------------------------------------*/ 036 package org.deegree.enterprise.servlet; 037 038 import java.io.BufferedReader; 039 import java.io.IOException; 040 import java.io.InputStream; 041 import java.io.InputStreamReader; 042 import java.net.URI; 043 import java.net.URISyntaxException; 044 import java.net.URL; 045 import java.util.ArrayList; 046 import java.util.List; 047 import java.util.Properties; 048 049 import org.deegree.datatypes.QualifiedName; 050 import org.deegree.framework.log.ILogger; 051 import org.deegree.framework.log.LoggerFactory; 052 import org.deegree.framework.util.StringTools; 053 import org.deegree.framework.xml.NamespaceContext; 054 import org.deegree.framework.xml.XMLTools; 055 import org.deegree.ogcbase.CommonNamespaces; 056 import org.deegree.ogcbase.PropertyPath; 057 import org.deegree.ogcbase.PropertyPathFactory; 058 import org.deegree.ogcbase.PropertyPathStep; 059 import org.deegree.ogcwebservices.InvalidParameterValueException; 060 import org.w3c.dom.Node; 061 062 /** 063 * This class respectively its method {@link #mapPropertyValue(Node)} can be used by XSLT scripts to map a node value 064 * (key) to another, corresponding value (value). The mappings are taken from the properties file 065 * <code>org.deegree.enterprise.servlet.wfsrequestmapping.properties</code>. If no matching value for a key is defined 066 * in the properties file, the returned <code>String</code> is null. 067 * <p> 068 * The node reference passed to this method must point to an element that contains a single text node, e.g. 069 * <PropertyName>/MyProperty/value</PropertyName> 070 * </p> 071 * <p> 072 * If a special behavior is needed by a deegree WFS instance and/or you do not want to edit the default properties and 073 * use your own one you should write a class that extends this or it as pattern. 074 * 075 * @see #mapPropertyValue(Node) 076 * @see java.util.Properties 077 * 078 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a> 079 * @author last edited by: $Author: mschneider $ 080 * 081 * @version $Revision: 21647 $, $Date: 2009-12-23 11:14:38 +0100 (Mi, 23 Dez 2009) $ 082 */ 083 public class WFSRequestMapping { 084 085 private static ILogger LOG = LoggerFactory.getLogger( WFSRequestMapping.class ); 086 087 /** TODO why is this here and ununused? */ 088 protected static String propertiesFile = "wfsrequestmapping.properties"; 089 090 private static Properties mapping = null; 091 092 private static Properties prefixToNs = new Properties(); 093 094 private static Properties nsToPrefix = new Properties(); 095 096 private static NamespaceContext nsc = CommonNamespaces.getNamespaceContext(); 097 static { 098 mapping = new Properties(); 099 try { 100 URL url = WFSRequestMapping.class.getResource( "/wfsrequestmapping.properties" ); 101 InputStream is = null; 102 if ( url != null ) { 103 is = url.openStream(); 104 } else { 105 is = WFSRequestMapping.class.getResourceAsStream( "wfsrequestmapping.properties" ); 106 } 107 InputStreamReader isr = new InputStreamReader( is ); 108 BufferedReader br = new BufferedReader( isr ); 109 String line = null; 110 while ( ( line = br.readLine() ) != null ) { 111 if ( !line.trim().startsWith( "#" ) ) { 112 String[] tmp = StringTools.toArray( line, "=", false ); 113 if ( tmp != null && tmp[0] != null && tmp[1] != null ) { 114 if ( tmp[0].startsWith( "$namespace." ) ) { 115 String pre = tmp[0].substring( tmp[0].indexOf( '.' ) + 1, tmp[0].length() ); 116 prefixToNs.put( pre, tmp[1] ); 117 nsToPrefix.put( tmp[1], pre ); 118 try { 119 nsc.addNamespace( pre, new URI( tmp[1] ) ); 120 } catch ( URISyntaxException e ) { 121 e.printStackTrace(); 122 } 123 } else { 124 mapping.put( tmp[0], tmp[1] ); 125 } 126 } 127 } 128 } 129 } catch ( IOException e ) { 130 e.printStackTrace(); 131 } 132 } 133 134 /** 135 * This method can be used by XSLT scripts to map a node value (key) to another, corresponding value (value). The 136 * mappings are taken from the properties file 137 * <code>org.deegree.enterprise.servlet.wfsrequestmapping.properties</code>. If no matching value for a key is 138 * defined in the properties file, the returned <code>String</code> is null. 139 * <p> 140 * The node reference passed to this method must point to an element that contains a single text node, e.g. 141 * <PropertyName>/MyProperty/value</PropertyName> 142 * </p> 143 * 144 * @param node 145 * node that will be mapped 146 * @return mapping for the node as an XPath, null if no mapping is defined 147 */ 148 public static String mapPropertyValue( Node node ) { 149 150 String nde = null; 151 String key = null; 152 try { 153 nde = XMLTools.getNodeAsString( node, ".", nsc, null ); 154 if ( nde.startsWith( "/" ) ) { 155 key = '.' + nde; 156 } else if ( nde.startsWith( "." ) ) { 157 key = nde; 158 } else { 159 key = "./" + nde; 160 } 161 } catch ( Exception e ) { 162 e.printStackTrace(); 163 } 164 165 if ( mapping.getProperty( key ) != null ) { 166 nde = mapping.getProperty( key ); 167 } 168 LOG.logDebug( "mapped property: " + nde ); 169 return nde; 170 } 171 172 /** 173 * This method can be used by XSLT scripts to map a node value (key) to another, corresponding value (value). The 174 * mappings are taken from the properties file 175 * <code>org.deegree.enterprise.servlet.wfsrequestmapping.properties</code>. If no matching value for a key is 176 * defined in the properties file, the returned <code>String</code> is null. 177 * <p> 178 * The node reference passed to this method must point to an element that contains a single text node, e.g. 179 * <PropertyName>/MyProperty/value</PropertyName> 180 * </p> 181 * 182 * @param node 183 * node that will be mapped 184 * @param typeName 185 * feature type name 186 * @return mapping for the node as an XPath, null if no mapping is defined 187 */ 188 public static String mapPropertyValue( Node node, String typeName ) { 189 String s = null; 190 try { 191 s = XMLTools.getNodeAsString( node, ".", nsc, null ); 192 if ( s.startsWith( "/" ) ) { 193 s = s.substring( 1, s.length() ); 194 } else if ( s.startsWith( "." ) ) { 195 s = s.substring( 2, s.length() ); 196 } 197 } catch ( Exception e ) { 198 e.printStackTrace(); 199 } 200 if ( s.indexOf( "SI_Gazetteer" ) > -1 ) { 201 s = ( new StringBuilder( "./" ) ).append( s ).toString(); 202 } else if ( s.indexOf( typeName ) < 0 ) { 203 s = ( new StringBuilder( String.valueOf( typeName ) ) ).append( '/' ).append( s ).toString(); 204 } 205 try { 206 PropertyPath pp = transformToPropertyPath( s, nsc, null ); 207 s = pp.getAsString(); 208 } catch ( InvalidParameterValueException e ) { 209 e.printStackTrace(); 210 } 211 LOG.logDebug( ( new StringBuilder( "mapped property: " ) ).append( mapping.getProperty( s ) ).toString() ); 212 String st = mapping.getProperty( s ); 213 if ( st == null ) { 214 st = s; 215 } 216 return st; 217 } 218 219 /** 220 * This method can be used by XSLT scripts to map a node value (key) to another, corresponding value (value). The 221 * mappings are taken from the properties file 222 * <code>org.deegree.enterprise.servlet.wfsrequestmapping.properties</code>. If no matching value for a key is 223 * defined in the properties file, the returned <code>String</code> is null. 224 * <p> 225 * The node reference passed to this method must point to an element that contains a single text node, e.g. 226 * <PropertyName>/MyProperty/value</PropertyName> 227 * </p> 228 * 229 * @param node 230 * node that will be mapped 231 * @param typeName 232 * feature type name 233 * @param nsp 234 * @return mapping for the node as an XPath, null if no mapping is defined 235 */ 236 public static String mapPropertyValue( Node node, String typeName, String nsp ) { 237 String s = null; 238 try { 239 s = XMLTools.getNodeAsString( node, ".", nsc, null ); 240 if ( s.startsWith( "/" ) ) { 241 s = s.substring( 1, s.length() ); 242 } else if ( s.startsWith( "." ) ) { 243 s = s.substring( 2, s.length() ); 244 } 245 } catch ( Exception e ) { 246 e.printStackTrace(); 247 } 248 if ( s.indexOf( "SI_Gazetteer" ) > -1 ) { 249 s = ( new StringBuilder( "./" ) ).append( s ).toString(); 250 } else if ( s.indexOf( typeName ) < 0 ) { 251 s = ( new StringBuilder( String.valueOf( typeName ) ) ).append( '/' ).append( s ).toString(); 252 } 253 try { 254 PropertyPath pp = transformToPropertyPath( s, nsc, createNsp( nsp ) ); 255 s = pp.getAsString(); 256 } catch ( InvalidParameterValueException e ) { 257 e.printStackTrace(); 258 } 259 LOG.logDebug( ( new StringBuilder( "mapped property: " ) ).append( mapping.getProperty( s ) ).toString() ); 260 String st = mapping.getProperty( s ); 261 if ( st == null ) { 262 st = s; 263 } 264 return st; 265 } 266 267 private static Properties createNsp( String nspDec ) { 268 Properties nsp = new Properties(); 269 String[] tmp = StringTools.toArray( nspDec, ";", false ); 270 for ( int i = 0; i < tmp.length; i++ ) { 271 int p = tmp[i].indexOf( ':' ); 272 String pre = tmp[i].substring( 0, p ); 273 String val = tmp[i].substring( p + 1 ); 274 nsp.put( pre, val ); 275 } 276 return nsp; 277 } 278 279 private static PropertyPath transformToPropertyPath( String propName, NamespaceContext nsContext, Properties nsp ) 280 throws InvalidParameterValueException { 281 String steps[] = propName.split( "/" ); 282 List<PropertyPathStep> propertyPathSteps = new ArrayList<PropertyPathStep>( steps.length ); 283 for ( int i = 0; i < steps.length; i++ ) { 284 PropertyPathStep propertyStep = null; 285 QualifiedName propertyName = null; 286 String step = steps[i]; 287 boolean isAttribute = false; 288 boolean isIndexed = false; 289 int selectedIndex = -1; 290 if ( step.startsWith( "@" ) ) { 291 if ( i != steps.length - 1 ) { 292 StringBuilder msg = new StringBuilder( "PropertyName '" ); 293 msg.append( propName ).append( "' is illegal: the attribute specifier may only " ); 294 msg.append( "be used for the final step." ); 295 throw new InvalidParameterValueException( msg.toString() ); 296 } 297 step = step.substring( 1 ); 298 isAttribute = true; 299 } 300 if ( step.endsWith( "]" ) ) { 301 if ( isAttribute ) { 302 StringBuilder msg = new StringBuilder( "PropertyName '" ); 303 msg.append( propName ).append( "' is illegal: if the attribute specifier ('@') is used, " ); 304 msg.append( "index selection ('[...']) is not possible." ); 305 throw new InvalidParameterValueException( msg.toString() ); 306 } 307 int bracketPos = step.indexOf( '[' ); 308 if ( bracketPos < 0 ) { 309 StringBuilder msg = new StringBuilder( "PropertyName '" ); 310 msg.append( propName ).append( "' is illegal. No opening brackets found for step '" ); 311 msg.append( step ).append( "'." ); 312 throw new InvalidParameterValueException( msg.toString() ); 313 } 314 try { 315 selectedIndex = Integer.parseInt( step.substring( bracketPos + 1, step.length() - 1 ) ); 316 } catch ( NumberFormatException e ) { 317 LOG.logError( e.getMessage(), e ); 318 StringBuilder msg = new StringBuilder( "PropertyName '" ); 319 msg.append( propName ).append( "' is illegal. Specified index '" ); 320 msg.append( step.substring( bracketPos + 1, step.length() - 1 ) ).append( "' is not a number." ); 321 throw new InvalidParameterValueException( msg.toString() ); 322 } 323 step = step.substring( 0, bracketPos ); 324 isIndexed = true; 325 } 326 int colonPos = step.indexOf( ':' ); 327 String prefix = ""; 328 String localName = step; 329 if ( colonPos > 0 ) { 330 prefix = step.substring( 0, colonPos ); 331 localName = step.substring( colonPos + 1 ); 332 } 333 URI nsURI = null; 334 if ( nsp == null ) { 335 nsURI = nsContext.getURI( prefix ); 336 } else { 337 try { 338 nsURI = new URI( nsp.getProperty( prefix ) ); 339 prefix = nsToPrefix.getProperty( nsURI.toString() ); 340 } catch ( URISyntaxException e ) { 341 throw new InvalidParameterValueException( e.getMessage(), e ); 342 } 343 } 344 if ( nsURI == null && prefix.length() > 0 ) { 345 String msg = ( new StringBuilder( "PropertyName '" ) ).append( propName ).append( 346 "' uses an unbound namespace prefix: " ).append( 347 prefix ).toString(); 348 throw new InvalidParameterValueException( msg ); 349 } 350 propertyName = new QualifiedName( prefix, localName, nsURI ); 351 if ( isAttribute ) { 352 propertyStep = PropertyPathFactory.createAttributePropertyPathStep( propertyName ); 353 } else if ( isIndexed ) { 354 propertyStep = PropertyPathFactory.createPropertyPathStep( propertyName, selectedIndex ); 355 } else { 356 propertyStep = PropertyPathFactory.createPropertyPathStep( propertyName ); 357 } 358 propertyPathSteps.add( propertyStep ); 359 } 360 361 return PropertyPathFactory.createPropertyPath( propertyPathSteps ); 362 } 363 }