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