001/*
002 * ====================================================================
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements.  See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership.  The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License.  You may obtain a copy of the License at
010 *
011 *   http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing,
014 * software distributed under the License is distributed on an
015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
016 * KIND, either express or implied.  See the License for the
017 * specific language governing permissions and limitations
018 * under the License.
019 * ====================================================================
020 *
021 * This software consists of voluntary contributions made by many
022 * individuals on behalf of the Apache Software Foundation.  For more
023 * information on the Apache Software Foundation, please see
024 * <http://www.apache.org/>.
025 *
026 */
027
028package org.apache.http.message;
029
030import java.util.ArrayList;
031import java.util.BitSet;
032import java.util.List;
033
034import org.apache.http.HeaderElement;
035import org.apache.http.NameValuePair;
036import org.apache.http.ParseException;
037import org.apache.http.annotation.ThreadingBehavior;
038import org.apache.http.annotation.Contract;
039import org.apache.http.util.Args;
040import org.apache.http.util.CharArrayBuffer;
041
042/**
043 * Basic implementation for parsing header values into elements.
044 * Instances of this class are stateless and thread-safe.
045 * Derived classes are expected to maintain these properties.
046 *
047 * @since 4.0
048 */
049@Contract(threading = ThreadingBehavior.IMMUTABLE)
050public class BasicHeaderValueParser implements HeaderValueParser {
051
052    /**
053     * A default instance of this class, for use as default or fallback.
054     * Note that {@link BasicHeaderValueParser} is not a singleton, there
055     * can be many instances of the class itself and of derived classes.
056     * The instance here provides non-customized, default behavior.
057     *
058     * @deprecated (4.3) use {@link #INSTANCE}
059     */
060    @Deprecated
061    public final static BasicHeaderValueParser DEFAULT = new BasicHeaderValueParser();
062
063    public final static BasicHeaderValueParser INSTANCE = new BasicHeaderValueParser();
064
065    private final static char PARAM_DELIMITER                = ';';
066    private final static char ELEM_DELIMITER                 = ',';
067
068    // IMPORTANT!
069    // These private static variables must be treated as immutable and never exposed outside this class
070    private static final BitSet TOKEN_DELIMS = TokenParser.INIT_BITSET('=', PARAM_DELIMITER, ELEM_DELIMITER);
071    private static final BitSet VALUE_DELIMS = TokenParser.INIT_BITSET(PARAM_DELIMITER, ELEM_DELIMITER);
072
073    private final TokenParser tokenParser;
074
075    public BasicHeaderValueParser() {
076        this.tokenParser = TokenParser.INSTANCE;
077    }
078
079    /**
080     * Parses elements with the given parser.
081     *
082     * @param value     the header value to parse
083     * @param parser    the parser to use, or {@code null} for default
084     *
085     * @return  array holding the header elements, never {@code null}
086     * @throws ParseException in case of a parsing error
087     */
088    public static
089        HeaderElement[] parseElements(final String value,
090                                      final HeaderValueParser parser) throws ParseException {
091        Args.notNull(value, "Value");
092
093        final CharArrayBuffer buffer = new CharArrayBuffer(value.length());
094        buffer.append(value);
095        final ParserCursor cursor = new ParserCursor(0, value.length());
096        return (parser != null ? parser : BasicHeaderValueParser.INSTANCE)
097            .parseElements(buffer, cursor);
098    }
099
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().length() == 0 && 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 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 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 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 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 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