    001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.4_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     * &lt;PropertyName&gt;/MyProperty/value&lt;/PropertyName&gt;
    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         * &lt;PropertyName&gt;/MyProperty/value&lt;/PropertyName&gt;
    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         * &lt;PropertyName&gt;/MyProperty/value&lt;/PropertyName&gt;
    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         * &lt;PropertyName&gt;/MyProperty/value&lt;/PropertyName&gt;
    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    }