View Javadoc

1   /*
2    * $HeadURL: https://svn.apache.org/repos/asf/httpcomponents/oac.hc3x/trunk/src/java/org/apache/commons/httpclient/util/ParameterFormatter.java $
3    * $Revision: 1425331 $
4    * $Date: 2012-12-22 19:29:41 +0100 (Sat, 22 Dec 2012) $
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 }