/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. * */ package org.apache.http.message; import java.util.ArrayList; import java.util.BitSet; import java.util.List; import org.apache.http.HeaderElement; import org.apache.http.NameValuePair; import org.apache.http.ParseException; import org.apache.http.annotation.Immutable; import org.apache.http.util.Args; import org.apache.http.util.CharArrayBuffer; /** * Basic implementation for parsing header values into elements. * Instances of this class are stateless and thread-safe. * Derived classes are expected to maintain these properties. * * @since 4.0 */ @Immutable public class BasicHeaderValueParser implements HeaderValueParser { /** * A default instance of this class, for use as default or fallback. * Note that {@link BasicHeaderValueParser} is not a singleton, there * can be many instances of the class itself and of derived classes. * The instance here provides non-customized, default behavior. * * @deprecated (4.3) use {@link #INSTANCE} */ @Deprecated public final static BasicHeaderValueParser DEFAULT = new BasicHeaderValueParser(); public final static BasicHeaderValueParser INSTANCE = new BasicHeaderValueParser(); private final static char PARAM_DELIMITER = ';'; private final static char ELEM_DELIMITER = ','; // IMPORTANT! // These private static variables must be treated as immutable and never exposed outside this class private static final BitSet TOKEN_DELIMS = TokenParser.INIT_BITSET('=', PARAM_DELIMITER, ELEM_DELIMITER); private static final BitSet VALUE_DELIMS = TokenParser.INIT_BITSET(PARAM_DELIMITER, ELEM_DELIMITER); private final TokenParser tokenParser; public BasicHeaderValueParser() { this.tokenParser = TokenParser.INSTANCE; } /** * Parses elements with the given parser. * * @param value the header value to parse * @param parser the parser to use, or {@code null} for default * * @return array holding the header elements, never {@code null} * @throws ParseException in case of a parsing error */ public static HeaderElement[] parseElements(final String value, final HeaderValueParser parser) throws ParseException { Args.notNull(value, "Value"); final CharArrayBuffer buffer = new CharArrayBuffer(value.length()); buffer.append(value); final ParserCursor cursor = new ParserCursor(0, value.length()); return (parser != null ? parser : BasicHeaderValueParser.INSTANCE) .parseElements(buffer, cursor); } // non-javadoc, see interface HeaderValueParser @Override public HeaderElement[] parseElements(final CharArrayBuffer buffer, final ParserCursor cursor) { Args.notNull(buffer, "Char array buffer"); Args.notNull(cursor, "Parser cursor"); final List<HeaderElement> elements = new ArrayList<HeaderElement>(); while (!cursor.atEnd()) { final HeaderElement element = parseHeaderElement(buffer, cursor); if (!(element.getName().length() == 0 && element.getValue() == null)) { elements.add(element); } } return elements.toArray(new HeaderElement[elements.size()]); } /** * Parses an element with the given parser. * * @param value the header element to parse * @param parser the parser to use, or {@code null} for default * * @return the parsed header element */ public static HeaderElement parseHeaderElement(final String value, final HeaderValueParser parser) throws ParseException { Args.notNull(value, "Value"); final CharArrayBuffer buffer = new CharArrayBuffer(value.length()); buffer.append(value); final ParserCursor cursor = new ParserCursor(0, value.length()); return (parser != null ? parser : BasicHeaderValueParser.INSTANCE) .parseHeaderElement(buffer, cursor); } // non-javadoc, see interface HeaderValueParser @Override public HeaderElement parseHeaderElement(final CharArrayBuffer buffer, final ParserCursor cursor) { Args.notNull(buffer, "Char array buffer"); Args.notNull(cursor, "Parser cursor"); final NameValuePair nvp = parseNameValuePair(buffer, cursor); NameValuePair[] params = null; if (!cursor.atEnd()) { final char ch = buffer.charAt(cursor.getPos() - 1); if (ch != ELEM_DELIMITER) { params = parseParameters(buffer, cursor); } } return createHeaderElement(nvp.getName(), nvp.getValue(), params); } /** * Creates a header element. * Called from {@link #parseHeaderElement}. * * @return a header element representing the argument */ protected HeaderElement createHeaderElement( final String name, final String value, final NameValuePair[] params) { return new BasicHeaderElement(name, value, params); } /** * Parses parameters with the given parser. * * @param value the parameter list to parse * @param parser the parser to use, or {@code null} for default * * @return array holding the parameters, never {@code null} */ public static NameValuePair[] parseParameters(final String value, final HeaderValueParser parser) throws ParseException { Args.notNull(value, "Value"); final CharArrayBuffer buffer = new CharArrayBuffer(value.length()); buffer.append(value); final ParserCursor cursor = new ParserCursor(0, value.length()); return (parser != null ? parser : BasicHeaderValueParser.INSTANCE) .parseParameters(buffer, cursor); } // non-javadoc, see interface HeaderValueParser @Override public NameValuePair[] parseParameters(final CharArrayBuffer buffer, final ParserCursor cursor) { Args.notNull(buffer, "Char array buffer"); Args.notNull(cursor, "Parser cursor"); tokenParser.skipWhiteSpace(buffer, cursor); final List<NameValuePair> params = new ArrayList<NameValuePair>(); while (!cursor.atEnd()) { final NameValuePair param = parseNameValuePair(buffer, cursor); params.add(param); final char ch = buffer.charAt(cursor.getPos() - 1); if (ch == ELEM_DELIMITER) { break; } } return params.toArray(new NameValuePair[params.size()]); } /** * Parses a name-value-pair with the given parser. * * @param value the NVP to parse * @param parser the parser to use, or {@code null} for default * * @return the parsed name-value pair */ public static NameValuePair parseNameValuePair(final String value, final HeaderValueParser parser) throws ParseException { Args.notNull(value, "Value"); final CharArrayBuffer buffer = new CharArrayBuffer(value.length()); buffer.append(value); final ParserCursor cursor = new ParserCursor(0, value.length()); return (parser != null ? parser : BasicHeaderValueParser.INSTANCE) .parseNameValuePair(buffer, cursor); } // non-javadoc, see interface HeaderValueParser @Override public NameValuePair parseNameValuePair(final CharArrayBuffer buffer, final ParserCursor cursor) { Args.notNull(buffer, "Char array buffer"); Args.notNull(cursor, "Parser cursor"); final String name = tokenParser.parseToken(buffer, cursor, TOKEN_DELIMS); if (cursor.atEnd()) { return new BasicNameValuePair(name, null); } final int delim = buffer.charAt(cursor.getPos()); cursor.updatePos(cursor.getPos() + 1); if (delim != '=') { return createNameValuePair(name, null); } final String value = tokenParser.parseValue(buffer, cursor, VALUE_DELIMS); if (!cursor.atEnd()) { cursor.updatePos(cursor.getPos() + 1); } return createNameValuePair(name, value); } /** * @deprecated (4.4) use {@link org.apache.http.message.TokenParser} */ @Deprecated public NameValuePair parseNameValuePair(final CharArrayBuffer buffer, final ParserCursor cursor, final char[] delimiters) { Args.notNull(buffer, "Char array buffer"); Args.notNull(cursor, "Parser cursor"); final BitSet delimSet = new BitSet(); if (delimiters != null) { for (char delimiter: delimiters) { delimSet.set(delimiter); } } delimSet.set('='); final String name = tokenParser.parseToken(buffer, cursor, delimSet); if (cursor.atEnd()) { return new BasicNameValuePair(name, null); } final int delim = buffer.charAt(cursor.getPos()); cursor.updatePos(cursor.getPos() + 1); if (delim != '=') { return createNameValuePair(name, null); } delimSet.clear('='); final String value = tokenParser.parseValue(buffer, cursor, delimSet); if (!cursor.atEnd()) { cursor.updatePos(cursor.getPos() + 1); } return createNameValuePair(name, value); } /** * Creates a name-value pair. * Called from {@link #parseNameValuePair}. * * @param name the name * @param value the value, or {@code null} * * @return a name-value pair representing the arguments */ protected NameValuePair createNameValuePair(final String name, final String value) { return new BasicNameValuePair(name, value); } }