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.BitSet;
32  import java.util.List;
33  
34  import org.apache.http.HeaderElement;
35  import org.apache.http.NameValuePair;
36  import org.apache.http.ParseException;
37  import org.apache.http.annotation.ThreadingBehavior;
38  import org.apache.http.annotation.Contract;
39  import org.apache.http.util.Args;
40  import org.apache.http.util.CharArrayBuffer;
41  
42  /**
43   * Basic implementation for parsing header values into elements.
44   * Instances of this class are stateless and thread-safe.
45   * Derived classes are expected to maintain these properties.
46   *
47   * @since 4.0
48   */
49  @Contract(threading = ThreadingBehavior.IMMUTABLE)
50  public class BasicHeaderValueParser implements HeaderValueParser {
51  
52      /**
53       * A default instance of this class, for use as default or fallback.
54       * Note that {@link BasicHeaderValueParser} is not a singleton, there
55       * can be many instances of the class itself and of derived classes.
56       * The instance here provides non-customized, default behavior.
57       *
58       * @deprecated (4.3) use {@link #INSTANCE}
59       */
60      @Deprecated
61      public final static BasicHeaderValueParserr.html#BasicHeaderValueParser">BasicHeaderValueParser DEFAULT = new BasicHeaderValueParser();
62  
63      public final static BasicHeaderValueParser.html#BasicHeaderValueParser">BasicHeaderValueParser INSTANCE = new BasicHeaderValueParser();
64  
65      private final static char PARAM_DELIMITER                = ';';
66      private final static char ELEM_DELIMITER                 = ',';
67  
68      // IMPORTANT!
69      // These private static variables must be treated as immutable and never exposed outside this class
70      private static final BitSet TOKEN_DELIMS = TokenParser.INIT_BITSET('=', PARAM_DELIMITER, ELEM_DELIMITER);
71      private static final BitSet VALUE_DELIMS = TokenParser.INIT_BITSET(PARAM_DELIMITER, ELEM_DELIMITER);
72  
73      private final TokenParser tokenParser;
74  
75      public BasicHeaderValueParser() {
76          this.tokenParser = TokenParser.INSTANCE;
77      }
78  
79      /**
80       * Parses elements with the given parser.
81       *
82       * @param value     the header value to parse
83       * @param parser    the parser to use, or {@code null} for default
84       *
85       * @return  array holding the header elements, never {@code null}
86       * @throws ParseException in case of a parsing error
87       */
88      public static
89          HeaderElement[] parseElements(final String value,
90                                        final HeaderValueParser parser) throws ParseException {
91          Args.notNull(value, "Value");
92  
93          final CharArrayBufferhtml#CharArrayBuffer">CharArrayBuffer buffer = new CharArrayBuffer(value.length());
94          buffer.append(value);
95          final ParserCursoror.html#ParserCursor">ParserCursor cursor = new ParserCursor(0, value.length());
96          return (parser != null ? parser : BasicHeaderValueParser.INSTANCE)
97              .parseElements(buffer, cursor);
98      }
99  
100 
101     // non-javadoc, see interface HeaderValueParser
102     @Override
103     public HeaderElement[] parseElements(final CharArrayBuffer buffer,
104                                          final ParserCursor cursor) {
105         Args.notNull(buffer, "Char array buffer");
106         Args.notNull(cursor, "Parser cursor");
107         final List<HeaderElement> elements = new ArrayList<HeaderElement>();
108         while (!cursor.atEnd()) {
109             final HeaderElement element = parseHeaderElement(buffer, cursor);
110             if (!(element.getName().isEmpty() && element.getValue() == null)) {
111                 elements.add(element);
112             }
113         }
114         return elements.toArray(new HeaderElement[elements.size()]);
115     }
116 
117 
118     /**
119      * Parses an element with the given parser.
120      *
121      * @param value     the header element to parse
122      * @param parser    the parser to use, or {@code null} for default
123      *
124      * @return  the parsed header element
125      */
126     public static
127         HeaderElement parseHeaderElement(final String value,
128                                          final HeaderValueParser parser) throws ParseException {
129         Args.notNull(value, "Value");
130 
131         final CharArrayBufferhtml#CharArrayBuffer">CharArrayBuffer buffer = new CharArrayBuffer(value.length());
132         buffer.append(value);
133         final ParserCursoror.html#ParserCursor">ParserCursor cursor = new ParserCursor(0, value.length());
134         return (parser != null ? parser : BasicHeaderValueParser.INSTANCE)
135                 .parseHeaderElement(buffer, cursor);
136     }
137 
138 
139     // non-javadoc, see interface HeaderValueParser
140     @Override
141     public HeaderElement parseHeaderElement(final CharArrayBuffer buffer,
142                                             final ParserCursor cursor) {
143         Args.notNull(buffer, "Char array buffer");
144         Args.notNull(cursor, "Parser cursor");
145         final NameValuePair nvp = parseNameValuePair(buffer, cursor);
146         NameValuePair[] params = null;
147         if (!cursor.atEnd()) {
148             final char ch = buffer.charAt(cursor.getPos() - 1);
149             if (ch != ELEM_DELIMITER) {
150                 params = parseParameters(buffer, cursor);
151             }
152         }
153         return createHeaderElement(nvp.getName(), nvp.getValue(), params);
154     }
155 
156 
157     /**
158      * Creates a header element.
159      * Called from {@link #parseHeaderElement}.
160      *
161      * @return  a header element representing the argument
162      */
163     protected HeaderElement createHeaderElement(
164             final String name,
165             final String value,
166             final NameValuePair[] params) {
167         return new BasicHeaderElement(name, value, params);
168     }
169 
170 
171     /**
172      * Parses parameters with the given parser.
173      *
174      * @param value     the parameter list to parse
175      * @param parser    the parser to use, or {@code null} for default
176      *
177      * @return  array holding the parameters, never {@code null}
178      */
179     public static
180         NameValuePair[] parseParameters(final String value,
181                                         final HeaderValueParser parser) throws ParseException {
182         Args.notNull(value, "Value");
183 
184         final CharArrayBufferhtml#CharArrayBuffer">CharArrayBuffer buffer = new CharArrayBuffer(value.length());
185         buffer.append(value);
186         final ParserCursoror.html#ParserCursor">ParserCursor cursor = new ParserCursor(0, value.length());
187         return (parser != null ? parser : BasicHeaderValueParser.INSTANCE)
188                 .parseParameters(buffer, cursor);
189     }
190 
191 
192 
193     // non-javadoc, see interface HeaderValueParser
194     @Override
195     public NameValuePair[] parseParameters(final CharArrayBuffer buffer,
196                                            final ParserCursor cursor) {
197         Args.notNull(buffer, "Char array buffer");
198         Args.notNull(cursor, "Parser cursor");
199         tokenParser.skipWhiteSpace(buffer, cursor);
200         final List<NameValuePair> params = new ArrayList<NameValuePair>();
201         while (!cursor.atEnd()) {
202             final NameValuePair param = parseNameValuePair(buffer, cursor);
203             params.add(param);
204             final char ch = buffer.charAt(cursor.getPos() - 1);
205             if (ch == ELEM_DELIMITER) {
206                 break;
207             }
208         }
209         return params.toArray(new NameValuePair[params.size()]);
210     }
211 
212     /**
213      * Parses a name-value-pair with the given parser.
214      *
215      * @param value     the NVP to parse
216      * @param parser    the parser to use, or {@code null} for default
217      *
218      * @return  the parsed name-value pair
219      */
220     public static
221        NameValuePair parseNameValuePair(final String value,
222                                         final HeaderValueParser parser) throws ParseException {
223         Args.notNull(value, "Value");
224 
225         final CharArrayBufferhtml#CharArrayBuffer">CharArrayBuffer buffer = new CharArrayBuffer(value.length());
226         buffer.append(value);
227         final ParserCursoror.html#ParserCursor">ParserCursor cursor = new ParserCursor(0, value.length());
228         return (parser != null ? parser : BasicHeaderValueParser.INSTANCE)
229                 .parseNameValuePair(buffer, cursor);
230     }
231 
232 
233     // non-javadoc, see interface HeaderValueParser
234     @Override
235     public NameValuePair parseNameValuePair(final CharArrayBuffer buffer,
236                                             final ParserCursor cursor) {
237         Args.notNull(buffer, "Char array buffer");
238         Args.notNull(cursor, "Parser cursor");
239 
240         final String name = tokenParser.parseToken(buffer, cursor, TOKEN_DELIMS);
241         if (cursor.atEnd()) {
242             return new BasicNameValuePair(name, null);
243         }
244         final int delim = buffer.charAt(cursor.getPos());
245         cursor.updatePos(cursor.getPos() + 1);
246         if (delim != '=') {
247             return createNameValuePair(name, null);
248         }
249         final String value = tokenParser.parseValue(buffer, cursor, VALUE_DELIMS);
250         if (!cursor.atEnd()) {
251             cursor.updatePos(cursor.getPos() + 1);
252         }
253         return createNameValuePair(name, value);
254     }
255 
256     /**
257      * @deprecated (4.4) use {@link org.apache.http.message.TokenParser}
258      */
259     @Deprecated
260     public NameValuePair parseNameValuePair(final CharArrayBuffer buffer,
261                                             final ParserCursor cursor,
262                                             final char[] delimiters) {
263         Args.notNull(buffer, "Char array buffer");
264         Args.notNull(cursor, "Parser cursor");
265 
266         final BitSet delimSet = new BitSet();
267         if (delimiters != null) {
268             for (final char delimiter: delimiters) {
269                 delimSet.set(delimiter);
270             }
271         }
272         delimSet.set('=');
273         final String name = tokenParser.parseToken(buffer, cursor, delimSet);
274         if (cursor.atEnd()) {
275             return new BasicNameValuePair(name, null);
276         }
277         final int delim = buffer.charAt(cursor.getPos());
278         cursor.updatePos(cursor.getPos() + 1);
279         if (delim != '=') {
280             return createNameValuePair(name, null);
281         }
282         delimSet.clear('=');
283         final String value = tokenParser.parseValue(buffer, cursor, delimSet);
284         if (!cursor.atEnd()) {
285             cursor.updatePos(cursor.getPos() + 1);
286         }
287         return createNameValuePair(name, value);
288     }
289 
290     /**
291      * Creates a name-value pair.
292      * Called from {@link #parseNameValuePair}.
293      *
294      * @param name      the name
295      * @param value     the value, or {@code null}
296      *
297      * @return  a name-value pair representing the arguments
298      */
299     protected NameValuePair createNameValuePair(final String name, final String value) {
300         return new BasicNameValuePair(name, value);
301     }
302 
303 }
304