001/* 
002    Licensed to the Apache Software Foundation (ASF) under one
003    or more contributor license agreements.  See the NOTICE file
004    distributed with this work for additional information
005    regarding copyright ownership.  The ASF licenses this file
006    to you under the Apache License, Version 2.0 (the
007    "License"); you may not use this file except in compliance
008    with the License.  You may obtain a copy of the License at
009
010       http://www.apache.org/licenses/LICENSE-2.0
011
012    Unless required by applicable law or agreed to in writing,
013    software distributed under the License is distributed on an
014    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015    KIND, either express or implied.  See the License for the
016    specific language governing permissions and limitations
017    under the License.  
018 */
019package org.apache.wiki.util;
020
021import java.util.ArrayList;
022import java.util.Enumeration;
023import java.util.HashMap;
024import java.util.List;
025import java.util.Map;
026
027import javax.servlet.http.HttpServletRequest;
028
029/**
030 * A collection of (static) utilities used by the WikiForms code.
031 * FormUtil is mainly concerned with mapping HTTP parameters to
032 * WikiPlugin parameters.
033 *
034 */
035public final class FormUtil
036{
037    /**
038     * Private constructor to prevent direct instantiation.
039     */
040    private FormUtil()
041    {
042    }
043    
044    /**
045     * <p>Looks for a named value in the Map. Returns either the
046     * value named by key, or values named by key.0, key.1, ...
047     * if the direct value is not found. The values are packed
048     * in an ArrayList.</p>
049     * <p>This is a utility method, mainly used when we don't know
050     * whether there was just one value, or several, in a mapping list
051     * (e.g. an HttpRequest / FORM checkbox).</p>
052     * @param params the Map container form parameters
053     * @param key the key to look up
054     * @return the List of keys
055     */
056    public static List< ? > getValues(final Map< ? , ? > params, final String key )
057    {
058        if( params == null || key == null )
059            return new ArrayList<>(0);
060
061        final Object entry = params.get( key );
062        if( entry != null )
063        {
064            final ArrayList<Object> rval = new ArrayList<>(1);
065            rval.add( entry );
066            return rval;
067        }
068
069        return getNumberedValues( params, key );
070    }
071
072
073    /**
074     * Looks up all keys starting with a given prefix and returns
075     * the values in an ArrayList. The keys must be Strings.
076     *
077     * <p>For example, calling this method for a Map containing
078     * key-value pairs foo.1 = a, foo.2 = b, and foo.3 = c returns
079     * an ArrayList containing [a, b, c].
080     *
081     * <p>Handles both 0- and 1-indexed names. Parsing stops at the
082     * first gap in the numeric postfix.
083     *
084     * @param params a Map of string-object pairs, presumably containing
085     *               key.1, key.2,...
086     * @param keyPrefix a String prefix; values will be looked up by adding
087     *                  ".0", ".1", and so on, until the first gap.
088     * @return ArrayList, containing the values corresponding to the
089     *          keyPrefix, in order.
090     */
091    public static List< ? > getNumberedValues(final Map< ?, ? > params, final String keyPrefix )
092    {
093        final ArrayList<Object> rval = new ArrayList<>();
094        if( params == null || 
095            params.size() == 0 || 
096            keyPrefix == null || 
097            keyPrefix.isEmpty() )
098            return rval;
099
100        String fullPrefix = null;
101        if( keyPrefix.charAt( keyPrefix.length() - 1 ) == '.' )
102            fullPrefix = keyPrefix;
103        else
104            fullPrefix = keyPrefix + ".";
105
106        int ix = 0;
107        Object value = params.get( fullPrefix + (ix++) );
108        if( value == null )
109            value = params.get( fullPrefix + (ix++) );
110        if( value == null )
111            return rval;
112        while( true )
113        {
114            rval.add( value );
115            value = params.get( fullPrefix + (ix++) );
116            if( value == null )
117                break;
118        }
119
120        return rval;
121    }
122
123
124    /**
125     * <p>Converts the parameter contents of an HTTP request into a map,
126     * modifying the keys to preserve multiple values per key. This
127     * is done by adding an ordered suffix to the key:</p>
128     * <p><pre>foo=bar,baz,xyzzy</pre></p>
129     * <p>becomes</p>
130     * <p><pre>foo.0=bar foo.1=baz foo.2=xyzzy</pre></p>
131     * <p>If filterPrefix is specified, only keys starting with the prefix
132     * are included in the result map. If the prefix is null, all keys are
133     * checked.</p>
134     * <p>FIX: this is not necessarily encoding-safe: see
135     * WikiContext.getHttpParameter().</p>
136     * @param req the HTTP request
137     * @param filterPrefix the prefix
138     * @return the Map containing parsed key/value pairs
139     */
140    public static Map< String, String > requestToMap(final HttpServletRequest req, String filterPrefix )
141    {
142        final HashMap<String,String> params = new HashMap<>();
143        
144        if( filterPrefix == null ) filterPrefix = "";
145        
146        final Enumeration< String > en = req.getParameterNames();
147        while( en.hasMoreElements() )
148        {
149            final String param = en.nextElement();
150            
151            if( param.startsWith( filterPrefix ) )
152            {
153                final String realName = param.substring( filterPrefix.length() );
154                final String[] values = req.getParameterValues( param );
155                if( values != null )
156                {
157                    if( values.length == 1 )
158                    {
159                        params.put( realName, values[0] );
160                    }
161                    else
162                    {
163                        for( int i = 0; i < values.length; i++ )
164                        {
165                            if( values[i] != null && !values[i].isEmpty() )
166                            {
167                                params.put( realName + "." + i, values[i] );
168                            }
169                        }
170                    }
171                }
172            }
173        }
174
175        return params;
176    }
177
178}