• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/BasicHeaderValueFormatter.java $
3  * $Revision: 574185 $
4  * $Date: 2007-09-10 02:19:47 -0700 (Mon, 10 Sep 2007) $
5  *
6  * ====================================================================
7  * Licensed to the Apache Software Foundation (ASF) under one
8  * or more contributor license agreements.  See the NOTICE file
9  * distributed with this work for additional information
10  * regarding copyright ownership.  The ASF licenses this file
11  * to you under the Apache License, Version 2.0 (the
12  * "License"); you may not use this file except in compliance
13  * with 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,
18  * software distributed under the License is distributed on an
19  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
20  * KIND, either express or implied.  See the License for the
21  * specific language governing permissions and limitations
22  * under the License.
23  * ====================================================================
24  *
25  * This software consists of voluntary contributions made by many
26  * individuals on behalf of the Apache Software Foundation.  For more
27  * information on the Apache Software Foundation, please see
28  * <http://www.apache.org/>.
29  *
30  */
31 
32 package org.apache.http.message;
33 
34 import org.apache.http.HeaderElement;
35 import org.apache.http.NameValuePair;
36 import org.apache.http.util.CharArrayBuffer;
37 
38 
39 /**
40  * Basic implementation for formatting header value elements.
41  * Instances of this class are stateless and thread-safe.
42  * Derived classes are expected to maintain these properties.
43  *
44  * @author <a href="mailto:oleg at ural.com">Oleg Kalnichevski</a>
45  * @author and others
46  *
47  *
48  * <!-- empty lines above to avoid 'svn diff' context problems -->
49  * @version $Revision: 574185 $
50  *
51  * @since 4.0
52  */
53 public class BasicHeaderValueFormatter implements HeaderValueFormatter {
54 
55     /**
56      * A default instance of this class, for use as default or fallback.
57      * Note that {@link BasicHeaderValueFormatter} is not a singleton, there
58      * can be many instances of the class itself and of derived classes.
59      * The instance here provides non-customized, default behavior.
60      */
61     public final static
62         BasicHeaderValueFormatter DEFAULT = new BasicHeaderValueFormatter();
63 
64 
65     /**
66      * Special characters that can be used as separators in HTTP parameters.
67      * These special characters MUST be in a quoted string to be used within
68      * a parameter value .
69      */
70     public final static String SEPARATORS = " ;,:@()<>\\\"/[]?={}\t";
71 
72 
73     /**
74      * Unsafe special characters that must be escaped using the backslash
75      * character
76      */
77     public final static String UNSAFE_CHARS = "\"\\";
78 
79 
80 
81     // public default constructor
82 
83 
84 
85     /**
86      * Formats an array of header elements.
87      *
88      * @param elems     the header elements to format
89      * @param quote     <code>true</code> to always format with quoted values,
90      *                  <code>false</code> to use quotes only when necessary
91      * @param formatter         the formatter to use, or <code>null</code>
92      *                          for the {@link #DEFAULT default}
93      *
94      * @return  the formatted header elements
95      */
96     public final static
formatElements(final HeaderElement[] elems, final boolean quote, HeaderValueFormatter formatter)97         String formatElements(final HeaderElement[] elems,
98                               final boolean quote,
99                               HeaderValueFormatter formatter) {
100         if (formatter == null)
101             formatter = BasicHeaderValueFormatter.DEFAULT;
102         return formatter.formatElements(null, elems, quote).toString();
103     }
104 
105 
106     // non-javadoc, see interface HeaderValueFormatter
formatElements(CharArrayBuffer buffer, final HeaderElement[] elems, final boolean quote)107     public CharArrayBuffer formatElements(CharArrayBuffer buffer,
108                                           final HeaderElement[] elems,
109                                           final boolean quote) {
110         if (elems == null) {
111             throw new IllegalArgumentException
112                 ("Header element array must not be null.");
113         }
114 
115         int len = estimateElementsLen(elems);
116         if (buffer == null) {
117             buffer = new CharArrayBuffer(len);
118         } else {
119             buffer.ensureCapacity(len);
120         }
121 
122         for (int i=0; i<elems.length; i++) {
123             if (i > 0) {
124                 buffer.append(", ");
125             }
126             formatHeaderElement(buffer, elems[i], quote);
127         }
128 
129         return buffer;
130     }
131 
132 
133     /**
134      * Estimates the length of formatted header elements.
135      *
136      * @param elems     the header elements to format, or <code>null</code>
137      *
138      * @return  a length estimate, in number of characters
139      */
estimateElementsLen(final HeaderElement[] elems)140     protected int estimateElementsLen(final HeaderElement[] elems) {
141         if ((elems == null) || (elems.length < 1))
142             return 0;
143 
144         int result = (elems.length-1) * 2; // elements separated by ", "
145         for (int i=0; i<elems.length; i++) {
146             result += estimateHeaderElementLen(elems[i]);
147         }
148 
149         return result;
150     }
151 
152 
153 
154     /**
155      * Formats a header element.
156      *
157      * @param elem      the header element to format
158      * @param quote     <code>true</code> to always format with quoted values,
159      *                  <code>false</code> to use quotes only when necessary
160      * @param formatter         the formatter to use, or <code>null</code>
161      *                          for the {@link #DEFAULT default}
162      *
163      * @return  the formatted header element
164      */
165     public final static
formatHeaderElement(final HeaderElement elem, boolean quote, HeaderValueFormatter formatter)166         String formatHeaderElement(final HeaderElement elem,
167                                    boolean quote,
168                                    HeaderValueFormatter formatter) {
169         if (formatter == null)
170             formatter = BasicHeaderValueFormatter.DEFAULT;
171         return formatter.formatHeaderElement(null, elem, quote).toString();
172     }
173 
174 
175     // non-javadoc, see interface HeaderValueFormatter
formatHeaderElement(CharArrayBuffer buffer, final HeaderElement elem, final boolean quote)176     public CharArrayBuffer formatHeaderElement(CharArrayBuffer buffer,
177                                                final HeaderElement elem,
178                                                final boolean quote) {
179         if (elem == null) {
180             throw new IllegalArgumentException
181                 ("Header element must not be null.");
182         }
183 
184         int len = estimateHeaderElementLen(elem);
185         if (buffer == null) {
186             buffer = new CharArrayBuffer(len);
187         } else {
188             buffer.ensureCapacity(len);
189         }
190 
191         buffer.append(elem.getName());
192         final String value = elem.getValue();
193         if (value != null) {
194             buffer.append('=');
195             doFormatValue(buffer, value, quote);
196         }
197 
198         final int parcnt = elem.getParameterCount();
199         if (parcnt > 0) {
200             for (int i=0; i<parcnt; i++) {
201                 buffer.append("; ");
202                 formatNameValuePair(buffer, elem.getParameter(i), quote);
203             }
204         }
205 
206         return buffer;
207     }
208 
209 
210     /**
211      * Estimates the length of a formatted header element.
212      *
213      * @param elem      the header element to format, or <code>null</code>
214      *
215      * @return  a length estimate, in number of characters
216      */
estimateHeaderElementLen(final HeaderElement elem)217     protected int estimateHeaderElementLen(final HeaderElement elem) {
218         if (elem == null)
219             return 0;
220 
221         int result = elem.getName().length(); // name
222         final String value = elem.getValue();
223         if (value != null) {
224             // assume quotes, but no escaped characters
225             result += 3 + value.length(); // ="value"
226         }
227 
228         final int parcnt = elem.getParameterCount();
229         if (parcnt > 0) {
230             for (int i=0; i<parcnt; i++) {
231                 result += 2 +                   // ; <param>
232                     estimateNameValuePairLen(elem.getParameter(i));
233             }
234         }
235 
236         return result;
237     }
238 
239 
240 
241 
242     /**
243      * Formats a set of parameters.
244      *
245      * @param nvps      the parameters to format
246      * @param quote     <code>true</code> to always format with quoted values,
247      *                  <code>false</code> to use quotes only when necessary
248      * @param formatter         the formatter to use, or <code>null</code>
249      *                          for the {@link #DEFAULT default}
250      *
251      * @return  the formatted parameters
252      */
253     public final static
formatParameters(final NameValuePair[] nvps, final boolean quote, HeaderValueFormatter formatter)254         String formatParameters(final NameValuePair[] nvps,
255                                 final boolean quote,
256                                 HeaderValueFormatter formatter) {
257         if (formatter == null)
258             formatter = BasicHeaderValueFormatter.DEFAULT;
259         return formatter.formatParameters(null, nvps, quote).toString();
260     }
261 
262 
263     // non-javadoc, see interface HeaderValueFormatter
formatParameters(CharArrayBuffer buffer, NameValuePair[] nvps, boolean quote)264     public CharArrayBuffer formatParameters(CharArrayBuffer buffer,
265                                             NameValuePair[] nvps,
266                                             boolean quote) {
267         if (nvps == null) {
268             throw new IllegalArgumentException
269                 ("Parameters must not be null.");
270         }
271 
272         int len = estimateParametersLen(nvps);
273         if (buffer == null) {
274             buffer = new CharArrayBuffer(len);
275         } else {
276             buffer.ensureCapacity(len);
277         }
278 
279         for (int i = 0; i < nvps.length; i++) {
280             if (i > 0) {
281                 buffer.append("; ");
282             }
283             formatNameValuePair(buffer, nvps[i], quote);
284         }
285 
286         return buffer;
287     }
288 
289 
290     /**
291      * Estimates the length of formatted parameters.
292      *
293      * @param nvps      the parameters to format, or <code>null</code>
294      *
295      * @return  a length estimate, in number of characters
296      */
estimateParametersLen(final NameValuePair[] nvps)297     protected int estimateParametersLen(final NameValuePair[] nvps) {
298         if ((nvps == null) || (nvps.length < 1))
299             return 0;
300 
301         int result = (nvps.length-1) * 2; // "; " between the parameters
302         for (int i=0; i<nvps.length; i++) {
303             result += estimateNameValuePairLen(nvps[i]);
304         }
305 
306         return result;
307     }
308 
309 
310     /**
311      * Formats a name-value pair.
312      *
313      * @param nvp       the name-value pair to format
314      * @param quote     <code>true</code> to always format with a quoted value,
315      *                  <code>false</code> to use quotes only when necessary
316      * @param formatter         the formatter to use, or <code>null</code>
317      *                          for the {@link #DEFAULT default}
318      *
319      * @return  the formatted name-value pair
320      */
321     public final static
formatNameValuePair(final NameValuePair nvp, final boolean quote, HeaderValueFormatter formatter)322         String formatNameValuePair(final NameValuePair nvp,
323                                    final boolean quote,
324                                    HeaderValueFormatter formatter) {
325         if (formatter == null)
326             formatter = BasicHeaderValueFormatter.DEFAULT;
327         return formatter.formatNameValuePair(null, nvp, quote).toString();
328     }
329 
330 
331     // non-javadoc, see interface HeaderValueFormatter
formatNameValuePair(CharArrayBuffer buffer, final NameValuePair nvp, final boolean quote)332     public CharArrayBuffer formatNameValuePair(CharArrayBuffer buffer,
333                                                final NameValuePair nvp,
334                                                final boolean quote) {
335         if (nvp == null) {
336             throw new IllegalArgumentException
337                 ("NameValuePair must not be null.");
338         }
339 
340         int len = estimateNameValuePairLen(nvp);
341         if (buffer == null) {
342             buffer = new CharArrayBuffer(len);
343         } else {
344             buffer.ensureCapacity(len);
345         }
346 
347         buffer.append(nvp.getName());
348         final String value = nvp.getValue();
349         if (value != null) {
350             buffer.append('=');
351             doFormatValue(buffer, value, quote);
352         }
353 
354         return buffer;
355     }
356 
357 
358     /**
359      * Estimates the length of a formatted name-value pair.
360      *
361      * @param nvp       the name-value pair to format, or <code>null</code>
362      *
363      * @return  a length estimate, in number of characters
364      */
estimateNameValuePairLen(final NameValuePair nvp)365     protected int estimateNameValuePairLen(final NameValuePair nvp) {
366         if (nvp == null)
367             return 0;
368 
369         int result = nvp.getName().length(); // name
370         final String value = nvp.getValue();
371         if (value != null) {
372             // assume quotes, but no escaped characters
373             result += 3 + value.length(); // ="value"
374         }
375         return result;
376     }
377 
378 
379     /**
380      * Actually formats the value of a name-value pair.
381      * This does not include a leading = character.
382      * Called from {@link #formatNameValuePair formatNameValuePair}.
383      *
384      * @param buffer    the buffer to append to, never <code>null</code>
385      * @param value     the value to append, never <code>null</code>
386      * @param quote     <code>true</code> to always format with quotes,
387      *                  <code>false</code> to use quotes only when necessary
388      */
doFormatValue(final CharArrayBuffer buffer, final String value, boolean quote)389     protected void doFormatValue(final CharArrayBuffer buffer,
390                                  final String value,
391                                  boolean quote) {
392 
393         if (!quote) {
394             for (int i = 0; (i < value.length()) && !quote; i++) {
395                 quote = isSeparator(value.charAt(i));
396             }
397         }
398 
399         if (quote) {
400             buffer.append('"');
401         }
402         for (int i = 0; i < value.length(); i++) {
403             char ch = value.charAt(i);
404             if (isUnsafe(ch)) {
405                 buffer.append('\\');
406             }
407             buffer.append(ch);
408         }
409         if (quote) {
410             buffer.append('"');
411         }
412     }
413 
414 
415     /**
416      * Checks whether a character is a {@link #SEPARATORS separator}.
417      *
418      * @param ch        the character to check
419      *
420      * @return  <code>true</code> if the character is a separator,
421      *          <code>false</code> otherwise
422      */
isSeparator(char ch)423     protected boolean isSeparator(char ch) {
424         return SEPARATORS.indexOf(ch) >= 0;
425     }
426 
427 
428     /**
429      * Checks whether a character is {@link #UNSAFE_CHARS unsafe}.
430      *
431      * @param ch        the character to check
432      *
433      * @return  <code>true</code> if the character is unsafe,
434      *          <code>false</code> otherwise
435      */
isUnsafe(char ch)436     protected boolean isUnsafe(char ch) {
437         return UNSAFE_CHARS.indexOf(ch) >= 0;
438     }
439 
440 
441 } // class BasicHeaderValueFormatter
442