View Javadoc

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