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     * &lt;PropertyName&gt;/MyProperty/value&lt;/PropertyName&gt;
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         * &lt;PropertyName&gt;/MyProperty/value&lt;/PropertyName&gt;
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         * &lt;PropertyName&gt;/MyProperty/value&lt;/PropertyName&gt;
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    }