View Javadoc

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