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/BasicHeaderValueParser.java $
      3  * $Revision: 595670 $
      4  * $Date: 2007-11-16 06:15:01 -0800 (Fri, 16 Nov 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 
     35 import java.util.List;
     36 import java.util.ArrayList;
     37 
     38 import org.apache.http.HeaderElement;
     39 import org.apache.http.NameValuePair;
     40 import org.apache.http.ParseException;
     41 import org.apache.http.protocol.HTTP;
     42 import org.apache.http.util.CharArrayBuffer;
     43 
     44 
     45 
     46 /**
     47  * Basic implementation for parsing header values into elements.
     48  * Instances of this class are stateless and thread-safe.
     49  * Derived classes are expected to maintain these properties.
     50  *
     51  * @author <a href="mailto:bcholmes (at) interlog.com">B.C. Holmes</a>
     52  * @author <a href="mailto:jericho (at) thinkfree.com">Park, Sung-Gu</a>
     53  * @author <a href="mailto:mbowler (at) GargoyleSoftware.com">Mike Bowler</a>
     54  * @author <a href="mailto:oleg at ural.com">Oleg Kalnichevski</a>
     55  * @author and others
     56  *
     57  *
     58  * <!-- empty lines above to avoid 'svn diff' context problems -->
     59  * @version $Revision: 595670 $
     60  *
     61  * @since 4.0
     62  */
     63 public class BasicHeaderValueParser implements HeaderValueParser {
     64 
     65     /**
     66      * A default instance of this class, for use as default or fallback.
     67      * Note that {@link BasicHeaderValueParser} is not a singleton, there
     68      * can be many instances of the class itself and of derived classes.
     69      * The instance here provides non-customized, default behavior.
     70      */
     71     public final static
     72         BasicHeaderValueParser DEFAULT = new BasicHeaderValueParser();
     73 
     74     private final static char PARAM_DELIMITER                = ';';
     75     private final static char ELEM_DELIMITER                 = ',';
     76     private final static char[] ALL_DELIMITERS               = new char[] {
     77                                                                 PARAM_DELIMITER,
     78                                                                 ELEM_DELIMITER
     79                                                                 };
     80 
     81     // public default constructor
     82 
     83 
     84     /**
     85      * Parses elements with the given parser.
     86      *
     87      * @param value     the header value to parse
     88      * @param parser    the parser to use, or <code>null</code> for default
     89      *
     90      * @return  array holding the header elements, never <code>null</code>
     91      */
     92     public final static
     93         HeaderElement[] parseElements(final String value,
     94                                       HeaderValueParser parser)
     95         throws ParseException {
     96 
     97         if (value == null) {
     98             throw new IllegalArgumentException
     99                 ("Value to parse may not be null");
    100         }
    101 
    102         if (parser == null)
    103             parser = BasicHeaderValueParser.DEFAULT;
    104 
    105         CharArrayBuffer buffer = new CharArrayBuffer(value.length());
    106         buffer.append(value);
    107         ParserCursor cursor = new ParserCursor(0, value.length());
    108         return parser.parseElements(buffer, cursor);
    109     }
    110 
    111 
    112     // non-javadoc, see interface HeaderValueParser
    113     public HeaderElement[] parseElements(final CharArrayBuffer buffer,
    114                                          final ParserCursor cursor) {
    115 
    116         if (buffer == null) {
    117             throw new IllegalArgumentException("Char array buffer may not be null");
    118         }
    119         if (cursor == null) {
    120             throw new IllegalArgumentException("Parser cursor may not be null");
    121         }
    122 
    123         List elements = new ArrayList();
    124         while (!cursor.atEnd()) {
    125             HeaderElement element = parseHeaderElement(buffer, cursor);
    126             if (!(element.getName().length() == 0 && element.getValue() == null)) {
    127                 elements.add(element);
    128             }
    129         }
    130         return (HeaderElement[])
    131             elements.toArray(new HeaderElement[elements.size()]);
    132     }
    133 
    134 
    135     /**
    136      * Parses an element with the given parser.
    137      *
    138      * @param value     the header element to parse
    139      * @param parser    the parser to use, or <code>null</code> for default
    140      *
    141      * @return  the parsed header element
    142      */
    143     public final static
    144         HeaderElement parseHeaderElement(final String value,
    145                                          HeaderValueParser parser)
    146         throws ParseException {
    147 
    148         if (value == null) {
    149             throw new IllegalArgumentException
    150                 ("Value to parse may not be null");
    151         }
    152 
    153         if (parser == null)
    154             parser = BasicHeaderValueParser.DEFAULT;
    155 
    156         CharArrayBuffer buffer = new CharArrayBuffer(value.length());
    157         buffer.append(value);
    158         ParserCursor cursor = new ParserCursor(0, value.length());
    159         return parser.parseHeaderElement(buffer, cursor);
    160     }
    161 
    162 
    163     // non-javadoc, see interface HeaderValueParser
    164     public HeaderElement parseHeaderElement(final CharArrayBuffer buffer,
    165                                             final ParserCursor cursor) {
    166 
    167         if (buffer == null) {
    168             throw new IllegalArgumentException("Char array buffer may not be null");
    169         }
    170         if (cursor == null) {
    171             throw new IllegalArgumentException("Parser cursor may not be null");
    172         }
    173 
    174         NameValuePair nvp = parseNameValuePair(buffer, cursor);
    175         NameValuePair[] params = null;
    176         if (!cursor.atEnd()) {
    177             char ch = buffer.charAt(cursor.getPos() - 1);
    178             if (ch != ELEM_DELIMITER) {
    179                 params = parseParameters(buffer, cursor);
    180             }
    181         }
    182         return createHeaderElement(nvp.getName(), nvp.getValue(), params);
    183     }
    184 
    185 
    186     /**
    187      * Creates a header element.
    188      * Called from {@link #parseHeaderElement}.
    189      *
    190      * @return  a header element representing the argument
    191      */
    192     protected HeaderElement createHeaderElement(
    193             final String name,
    194             final String value,
    195             final NameValuePair[] params) {
    196         return new BasicHeaderElement(name, value, params);
    197     }
    198 
    199 
    200     /**
    201      * Parses parameters with the given parser.
    202      *
    203      * @param value     the parameter list to parse
    204      * @param parser    the parser to use, or <code>null</code> for default
    205      *
    206      * @return  array holding the parameters, never <code>null</code>
    207      */
    208     public final static
    209         NameValuePair[] parseParameters(final String value,
    210                                         HeaderValueParser parser)
    211         throws ParseException {
    212 
    213         if (value == null) {
    214             throw new IllegalArgumentException
    215                 ("Value to parse may not be null");
    216         }
    217 
    218         if (parser == null)
    219             parser = BasicHeaderValueParser.DEFAULT;
    220 
    221         CharArrayBuffer buffer = new CharArrayBuffer(value.length());
    222         buffer.append(value);
    223         ParserCursor cursor = new ParserCursor(0, value.length());
    224         return parser.parseParameters(buffer, cursor);
    225     }
    226 
    227 
    228 
    229     // non-javadoc, see interface HeaderValueParser
    230     public NameValuePair[] parseParameters(final CharArrayBuffer buffer,
    231                                            final ParserCursor cursor) {
    232 
    233         if (buffer == null) {
    234             throw new IllegalArgumentException("Char array buffer may not be null");
    235         }
    236         if (cursor == null) {
    237             throw new IllegalArgumentException("Parser cursor may not be null");
    238         }
    239 
    240         int pos = cursor.getPos();
    241         int indexTo = cursor.getUpperBound();
    242 
    243         while (pos < indexTo) {
    244             char ch = buffer.charAt(pos);
    245             if (HTTP.isWhitespace(ch)) {
    246                 pos++;
    247             } else {
    248                 break;
    249             }
    250         }
    251         cursor.updatePos(pos);
    252         if (cursor.atEnd()) {
    253             return new NameValuePair[] {};
    254         }
    255 
    256         List params = new ArrayList();
    257         while (!cursor.atEnd()) {
    258             NameValuePair param = parseNameValuePair(buffer, cursor);
    259             params.add(param);
    260             char ch = buffer.charAt(cursor.getPos() - 1);
    261             if (ch == ELEM_DELIMITER) {
    262                 break;
    263             }
    264         }
    265 
    266         return (NameValuePair[])
    267             params.toArray(new NameValuePair[params.size()]);
    268     }
    269 
    270     /**
    271      * Parses a name-value-pair with the given parser.
    272      *
    273      * @param value     the NVP to parse
    274      * @param parser    the parser to use, or <code>null</code> for default
    275      *
    276      * @return  the parsed name-value pair
    277      */
    278     public final static
    279        NameValuePair parseNameValuePair(final String value,
    280                                         HeaderValueParser parser)
    281         throws ParseException {
    282 
    283         if (value == null) {
    284             throw new IllegalArgumentException
    285                 ("Value to parse may not be null");
    286         }
    287 
    288         if (parser == null)
    289             parser = BasicHeaderValueParser.DEFAULT;
    290 
    291         CharArrayBuffer buffer = new CharArrayBuffer(value.length());
    292         buffer.append(value);
    293         ParserCursor cursor = new ParserCursor(0, value.length());
    294         return parser.parseNameValuePair(buffer, cursor);
    295     }
    296 
    297 
    298     // non-javadoc, see interface HeaderValueParser
    299     public NameValuePair parseNameValuePair(final CharArrayBuffer buffer,
    300                                             final ParserCursor cursor) {
    301         return parseNameValuePair(buffer, cursor, ALL_DELIMITERS);
    302     }
    303 
    304     private static boolean isOneOf(final char ch, final char[] chs) {
    305         if (chs != null) {
    306             for (int i = 0; i < chs.length; i++) {
    307                 if (ch == chs[i]) {
    308                     return true;
    309                 }
    310             }
    311         }
    312         return false;
    313     }
    314 
    315     public NameValuePair parseNameValuePair(final CharArrayBuffer buffer,
    316                                             final ParserCursor cursor,
    317                                             final char[] delimiters) {
    318 
    319         if (buffer == null) {
    320             throw new IllegalArgumentException("Char array buffer may not be null");
    321         }
    322         if (cursor == null) {
    323             throw new IllegalArgumentException("Parser cursor may not be null");
    324         }
    325 
    326         boolean terminated = false;
    327 
    328         int pos = cursor.getPos();
    329         int indexFrom = cursor.getPos();
    330         int indexTo = cursor.getUpperBound();
    331 
    332         // Find name
    333         String name = null;
    334         while (pos < indexTo) {
    335             char ch = buffer.charAt(pos);
    336             if (ch == '=') {
    337                 break;
    338             }
    339             if (isOneOf(ch, delimiters)) {
    340                 terminated = true;
    341                 break;
    342             }
    343             pos++;
    344         }
    345 
    346         if (pos == indexTo) {
    347             terminated = true;
    348             name = buffer.substringTrimmed(indexFrom, indexTo);
    349         } else {
    350             name = buffer.substringTrimmed(indexFrom, pos);
    351             pos++;
    352         }
    353 
    354         if (terminated) {
    355             cursor.updatePos(pos);
    356             return createNameValuePair(name, null);
    357         }
    358 
    359         // Find value
    360         String value = null;
    361         int i1 = pos;
    362 
    363         boolean qouted = false;
    364         boolean escaped = false;
    365         while (pos < indexTo) {
    366             char ch = buffer.charAt(pos);
    367             if (ch == '"' && !escaped) {
    368                 qouted = !qouted;
    369             }
    370             if (!qouted && !escaped && isOneOf(ch, delimiters)) {
    371                 terminated = true;
    372                 break;
    373             }
    374             if (escaped) {
    375                 escaped = false;
    376             } else {
    377                 escaped = qouted && ch == '\\';
    378             }
    379             pos++;
    380         }
    381 
    382         int i2 = pos;
    383         // Trim leading white spaces
    384         while (i1 < i2 && (HTTP.isWhitespace(buffer.charAt(i1)))) {
    385             i1++;
    386         }
    387         // Trim trailing white spaces
    388         while ((i2 > i1) && (HTTP.isWhitespace(buffer.charAt(i2 - 1)))) {
    389             i2--;
    390         }
    391         // Strip away quotes if necessary
    392         if (((i2 - i1) >= 2)
    393             && (buffer.charAt(i1) == '"')
    394             && (buffer.charAt(i2 - 1) == '"')) {
    395             i1++;
    396             i2--;
    397         }
    398         value = buffer.substring(i1, i2);
    399         if (terminated) {
    400             pos++;
    401         }
    402         cursor.updatePos(pos);
    403         return createNameValuePair(name, value);
    404     }
    405 
    406     /**
    407      * Creates a name-value pair.
    408      * Called from {@link #parseNameValuePair}.
    409      *
    410      * @param name      the name
    411      * @param value     the value, or <code>null</code>
    412      *
    413      * @return  a name-value pair representing the arguments
    414      */
    415     protected NameValuePair createNameValuePair(final String name, final String value) {
    416         return new BasicNameValuePair(name, value);
    417     }
    418 
    419 }
    420 
    421