View Javadoc

    1   /*
    2    * $HeadURL: https://svn.apache.org/repos/asf/jakarta/httpcomponents/oac.hc3x/tags/HTTPCLIENT_3_1/src/java/org/apache/commons/httpclient/util/ParameterFormatter.java $
    3    * $Revision: 480424 $
    4    * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $
    5    *
    6    * ====================================================================
    7    *
    8    *  Licensed to the Apache Software Foundation (ASF) under one or more
    9    *  contributor license agreements.  See the NOTICE file distributed with
    10   *  this work for additional information regarding copyright ownership.
    11   *  The ASF licenses this file to You under the Apache License, Version 2.0
    12   *  (the "License"); you may not use this file except in compliance with
    13   *  the License.  You may obtain a copy of the License at
    14   *
    15   *      http://www.apache.org/licenses/LICENSE-2.0
    16   *
    17   *  Unless required by applicable law or agreed to in writing, software
    18   *  distributed under the License is distributed on an "AS IS" BASIS,
    19   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    20   *  See the License for the specific language governing permissions and
    21   *  limitations under the License.
    22   * ====================================================================
    23   *
    24   * This software consists of voluntary contributions made by many
    25   * individuals on behalf of the Apache Software Foundation.  For more
    26   * information on the Apache Software Foundation, please see
    27   * <http://www.apache.org/>.
    28   *
    29   */
    30
    31  package org.apache.commons.httpclient.util;
    32
    33  import org.apache.commons.httpclient.NameValuePair;
    34
    35  /**
    36   * <p>
    37   *  This formatter produces a textual representation of attribute/value pairs. It
    38   *  comforms to the generic grammar and formatting rules outlined in the
    39   *  <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.1">Section 2.1</a>
    40   *  and
    41   *  <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6">Section 3.6</a>
    42   *  of <a href="http://www.w3.org/Protocols/rfc2616/rfc2616.txt">RFC 2616</a>
    43   * </p>
    44   * <h>2.1 Augmented BNF</h>
    45   * <p>
    46   *  Many HTTP/1.1 header field values consist of words separated by LWS or special
    47   *  characters. These special characters MUST be in a quoted string to be used within
    48   *  a parameter value (as defined in section 3.6).
    49   * <p>
    50   * <pre>
    51   * token          = 1*<any CHAR except CTLs or separators>
    52   * separators     = "(" | ")" | "<" | ">" | "@"
    53   *                | "," | ";" | ":" | "\" | <">
    54   *                | "/" | "[" | "]" | "?" | "="
    55   *                | "{" | "}" | SP | HT
    56   * </pre>
    57   * <p>
    58   *  A string of text is parsed as a single word if it is quoted using double-quote marks.
    59   * </p>
    60   * <pre>
    61   * quoted-string  = ( <"> *(qdtext | quoted-pair ) <"> )
    62   * qdtext         = <any TEXT except <">>
    63   * </pre>
    64   * <p>
    65   *  The backslash character ("\") MAY be used as a single-character quoting mechanism only
    66   *  within quoted-string and comment constructs.
    67   * </p>
    68   * <pre>
    69   * quoted-pair    = "\" CHAR
    70   * </pre>
    71   * <h>3.6 Transfer Codings</h>
    72   * <p>
    73   *  Parameters are in the form of attribute/value pairs.
    74   * </p>
    75   * <pre>
    76   * parameter               = attribute "=" value
    77   * attribute               = token
    78   * value                   = token | quoted-string
    79   * </pre>
    80   *
    81   * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
    82   *
    83   * @since 3.0
    84   */
    85  public class ParameterFormatter {
    86
    87      /**
    88       * Special characters that can be used as separators in HTTP parameters.
    89       * These special characters MUST be in a quoted string to be used within
    90       * a parameter value
    91       */
    92      private static final char[] SEPARATORS   = {
    93              '(', ')', '<', '>', '@',
    94              ',', ';', ':', '\\', '"',
    95              '/', '[', ']', '?', '=',
    96              '{', '}', ' ', '\t'
    97              };
    98
    99      /**
    100      * Unsafe special characters that must be escaped using the backslash
    101      * character
    102      */
    103     private static final char[] UNSAFE_CHARS = {
    104             '"', '\\'
    105             };
    106
    107     /**
    108      * This flag determines whether all parameter values must be enclosed in
    109      * quotation marks, even if they do not contain any special characters
    110      */
    111     private boolean alwaysUseQuotes = true;
    112
    113     /** Default ParameterFormatter constructor */
    114     public ParameterFormatter() {
    115         super();
    116     }
    117
    118     private static boolean isOneOf(char[] chars, char ch) {
    119         for (int i = 0; i < chars.length; i++) {
    120             if (ch == chars[i]) {
    121                 return true;
    122             }
    123         }
    124         return false;
    125     }
    126
    127     private static boolean isUnsafeChar(char ch) {
    128         return isOneOf(UNSAFE_CHARS, ch);
    129     }
    130
    131     private static boolean isSeparator(char ch) {
    132         return isOneOf(SEPARATORS, ch);
    133     }
    134
    135     /**
    136      * Determines whether all parameter values must be enclosed in quotation
    137      * marks, even if they do not contain any special characters
    138      *
    139      * @return <tt>true</tt> if all parameter values must be enclosed in
    140      * quotation marks, <tt>false</tt> otherwise
    141      */
    142     public boolean isAlwaysUseQuotes() {
    143         return alwaysUseQuotes;
    144     }
    145
    146     /**
    147      * Defines whether all parameter values must be enclosed in quotation
    148      * marks, even if they do not contain any special characters
    149      *
    150      * @param alwaysUseQuotes
    151      */
    152     public void setAlwaysUseQuotes(boolean alwaysUseQuotes) {
    153         this.alwaysUseQuotes = alwaysUseQuotes;
    154     }
    155
    156     /**
    157      * Formats the given parameter value using formatting rules defined
    158      * in RFC 2616
    159      *
    160      * @param buffer output buffer
    161      * @param value the parameter value to be formatted
    162      * @param alwaysUseQuotes <tt>true</tt> if the parameter value must
    163      * be enclosed in quotation marks, even if it does not contain any special
    164      * characters<tt>, false</tt> only if the parameter value contains
    165      * potentially unsafe special characters
    166      */
    167     public static void formatValue(
    168             final StringBuffer buffer, final String value, boolean alwaysUseQuotes) {
    169         if (buffer == null) {
    170             throw new IllegalArgumentException("String buffer may not be null");
    171         }
    172         if (value == null) {
    173             throw new IllegalArgumentException("Value buffer may not be null");
    174         }
    175         if (alwaysUseQuotes) {
    176             buffer.append('"');
    177             for (int i = 0; i < value.length(); i++) {
    178                 char ch = value.charAt(i);
    179                 if (isUnsafeChar(ch)) {
    180                     buffer.append('\\');
    181                 }
    182                 buffer.append(ch);
    183             }
    184             buffer.append('"');
    185         } else {
    186             int offset = buffer.length();
    187             boolean unsafe = false;
    188             for (int i = 0; i < value.length(); i++) {
    189                 char ch = value.charAt(i);
    190                 if (isSeparator(ch)) {
    191                     unsafe = true;
    192                 }
    193                 if (isUnsafeChar(ch)) {
    194                     buffer.append('\\');
    195                 }
    196                 buffer.append(ch);
    197             }
    198             if (unsafe) {
    199                 buffer.insert(offset, '"');
    200                 buffer.append('"');
    201             }
    202         }
    203     }
    204
    205     /**
    206      * Produces textual representaion of the attribute/value pair using
    207      * formatting rules defined in RFC 2616
    208      *
    209      * @param buffer output buffer
    210      * @param param the parameter to be formatted
    211      */
    212     public void format(final StringBuffer buffer, final NameValuePair param) {
    213         if (buffer == null) {
    214             throw new IllegalArgumentException("String buffer may not be null");
    215         }
    216         if (param == null) {
    217             throw new IllegalArgumentException("Parameter may not be null");
    218         }
    219         buffer.append(param.getName());
    220         String value = param.getValue();
    221         if (value != null) {
    222             buffer.append("=");
    223             formatValue(buffer, value, this.alwaysUseQuotes);
    224         }
    225     }
    226
    227     /**
    228      * Produces textual representaion of the attribute/value pair using
    229      * formatting rules defined in RFC 2616
    230      *
    231      * @param param the parameter to be formatted
    232      *
    233      * @return RFC 2616 conformant textual representaion of the
    234      * attribute/value pair
    235      */
    236     public String format(final NameValuePair param) {
    237         StringBuffer buffer = new StringBuffer();
    238         format(buffer, param);
    239         return buffer.toString();
    240     }
    241
    242 }