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