Home | History | Annotate | Download | only in message
      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
     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
    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      */
    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
    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
    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      */
    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
    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
    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      */
    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
    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
    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      */
    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      */
    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      */
    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      */
    436     protected boolean isUnsafe(char ch) {
    437         return UNSAFE_CHARS.indexOf(ch) >= 0;
    438     }
    439 
    440 
    441 } // class BasicHeaderValueFormatter
    442