001 //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_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: 21632 $, $Date: 2009-12-23 10:31:16 +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 }