    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 jakarta.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.isEmpty() ||
    096            keyPrefix == null ||
    097            keyPrefix.isEmpty() )
    098            return rval;
    099
    100        String fullPrefix;
    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}