• 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/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@interlog.com">B.C. Holmes</a>
52  * @author <a href="mailto:jericho@thinkfree.com">Park, Sung-Gu</a>
53  * @author <a href="mailto:mbowler@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
parseElements(final String value, HeaderValueParser parser)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
parseElements(final CharArrayBuffer buffer, final ParserCursor cursor)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
parseHeaderElement(final String value, HeaderValueParser parser)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
parseHeaderElement(final CharArrayBuffer buffer, final ParserCursor cursor)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      */
createHeaderElement( final String name, final String value, final NameValuePair[] params)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
parseParameters(final String value, HeaderValueParser parser)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
parseParameters(final CharArrayBuffer buffer, final ParserCursor cursor)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
parseNameValuePair(final String value, HeaderValueParser parser)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
parseNameValuePair(final CharArrayBuffer buffer, final ParserCursor cursor)299     public NameValuePair parseNameValuePair(final CharArrayBuffer buffer,
300                                             final ParserCursor cursor) {
301         return parseNameValuePair(buffer, cursor, ALL_DELIMITERS);
302     }
303 
isOneOf(final char ch, final char[] chs)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 
parseNameValuePair(final CharArrayBuffer buffer, final ParserCursor cursor, final char[] delimiters)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      */
createNameValuePair(final String name, final String value)415     protected NameValuePair createNameValuePair(final String name, final String value) {
416         return new BasicNameValuePair(name, value);
417     }
418 
419 }
420 
421