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 classfor 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 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
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 nullfor 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 CharArrayBuffer buffer = new CharArrayBuffer(value.length());
94         buffer.append(value);
95         final 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 nullfor 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 CharArrayBuffer buffer = new CharArrayBuffer(value.length());
132         buffer.append(value);
133         final 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 nullfor 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 CharArrayBuffer buffer = new CharArrayBuffer(value.length());
185         buffer.append(value);
186         final 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 nullfor 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 CharArrayBuffer buffer = new CharArrayBuffer(value.length());
226         buffer.append(value);
227         final 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
305