/* * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/BasicHeaderValueParser.java $ * $Revision: 595670 $ * $Date: 2007-11-16 06:15:01 -0800 (Fri, 16 Nov 2007) $ * * ==================================================================== * 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.List; import java.util.ArrayList; import org.apache.http.HeaderElement; import org.apache.http.NameValuePair; import org.apache.http.ParseException; import org.apache.http.protocol.HTTP; 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. * * @author <a href="mailto:bcholmes@interlog.com">B.C. Holmes</a> * @author <a href="mailto:jericho@thinkfree.com">Park, Sung-Gu</a> * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a> * @author <a href="mailto:oleg at ural.com">Oleg Kalnichevski</a> * @author and others * * * <!-- empty lines above to avoid 'svn diff' context problems --> * @version $Revision: 595670 $ * * @since 4.0 */ 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. */ public final static BasicHeaderValueParser DEFAULT = new BasicHeaderValueParser(); private final static char PARAM_DELIMITER = ';'; private final static char ELEM_DELIMITER = ','; private final static char[] ALL_DELIMITERS = new char[] { PARAM_DELIMITER, ELEM_DELIMITER }; // public default constructor /** * Parses elements with the given parser. * * @param value the header value to parse * @param parser the parser to use, or <code>null</code> for default * * @return array holding the header elements, never <code>null</code> */ public final static HeaderElement[] parseElements(final String value, HeaderValueParser parser) throws ParseException { if (value == null) { throw new IllegalArgumentException ("Value to parse may not be null"); } if (parser == null) parser = BasicHeaderValueParser.DEFAULT; CharArrayBuffer buffer = new CharArrayBuffer(value.length()); buffer.append(value); ParserCursor cursor = new ParserCursor(0, value.length()); return parser.parseElements(buffer, cursor); } // non-javadoc, see interface HeaderValueParser public HeaderElement[] parseElements(final CharArrayBuffer buffer, final ParserCursor cursor) { if (buffer == null) { throw new IllegalArgumentException("Char array buffer may not be null"); } if (cursor == null) { throw new IllegalArgumentException("Parser cursor may not be null"); } List elements = new ArrayList(); while (!cursor.atEnd()) { HeaderElement element = parseHeaderElement(buffer, cursor); if (!(element.getName().length() == 0 && element.getValue() == null)) { elements.add(element); } } return (HeaderElement[]) 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</code> for default * * @return the parsed header element */ public final static HeaderElement parseHeaderElement(final String value, HeaderValueParser parser) throws ParseException { if (value == null) { throw new IllegalArgumentException ("Value to parse may not be null"); } if (parser == null) parser = BasicHeaderValueParser.DEFAULT; CharArrayBuffer buffer = new CharArrayBuffer(value.length()); buffer.append(value); ParserCursor cursor = new ParserCursor(0, value.length()); return parser.parseHeaderElement(buffer, cursor); } // non-javadoc, see interface HeaderValueParser public HeaderElement parseHeaderElement(final CharArrayBuffer buffer, final ParserCursor cursor) { if (buffer == null) { throw new IllegalArgumentException("Char array buffer may not be null"); } if (cursor == null) { throw new IllegalArgumentException("Parser cursor may not be null"); } NameValuePair nvp = parseNameValuePair(buffer, cursor); NameValuePair[] params = null; if (!cursor.atEnd()) { 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</code> for default * * @return array holding the parameters, never <code>null</code> */ public final static NameValuePair[] parseParameters(final String value, HeaderValueParser parser) throws ParseException { if (value == null) { throw new IllegalArgumentException ("Value to parse may not be null"); } if (parser == null) parser = BasicHeaderValueParser.DEFAULT; CharArrayBuffer buffer = new CharArrayBuffer(value.length()); buffer.append(value); ParserCursor cursor = new ParserCursor(0, value.length()); return parser.parseParameters(buffer, cursor); } // non-javadoc, see interface HeaderValueParser public NameValuePair[] parseParameters(final CharArrayBuffer buffer, final ParserCursor cursor) { if (buffer == null) { throw new IllegalArgumentException("Char array buffer may not be null"); } if (cursor == null) { throw new IllegalArgumentException("Parser cursor may not be null"); } int pos = cursor.getPos(); int indexTo = cursor.getUpperBound(); while (pos < indexTo) { char ch = buffer.charAt(pos); if (HTTP.isWhitespace(ch)) { pos++; } else { break; } } cursor.updatePos(pos); if (cursor.atEnd()) { return new NameValuePair[] {}; } List params = new ArrayList(); while (!cursor.atEnd()) { NameValuePair param = parseNameValuePair(buffer, cursor); params.add(param); char ch = buffer.charAt(cursor.getPos() - 1); if (ch == ELEM_DELIMITER) { break; } } return (NameValuePair[]) 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</code> for default * * @return the parsed name-value pair */ public final static NameValuePair parseNameValuePair(final String value, HeaderValueParser parser) throws ParseException { if (value == null) { throw new IllegalArgumentException ("Value to parse may not be null"); } if (parser == null) parser = BasicHeaderValueParser.DEFAULT; CharArrayBuffer buffer = new CharArrayBuffer(value.length()); buffer.append(value); ParserCursor cursor = new ParserCursor(0, value.length()); return parser.parseNameValuePair(buffer, cursor); } // non-javadoc, see interface HeaderValueParser public NameValuePair parseNameValuePair(final CharArrayBuffer buffer, final ParserCursor cursor) { return parseNameValuePair(buffer, cursor, ALL_DELIMITERS); } private static boolean isOneOf(final char ch, final char[] chs) { if (chs != null) { for (int i = 0; i < chs.length; i++) { if (ch == chs[i]) { return true; } } } return false; } public NameValuePair parseNameValuePair(final CharArrayBuffer buffer, final ParserCursor cursor, final char[] delimiters) { if (buffer == null) { throw new IllegalArgumentException("Char array buffer may not be null"); } if (cursor == null) { throw new IllegalArgumentException("Parser cursor may not be null"); } boolean terminated = false; int pos = cursor.getPos(); int indexFrom = cursor.getPos(); int indexTo = cursor.getUpperBound(); // Find name String name = null; while (pos < indexTo) { char ch = buffer.charAt(pos); if (ch == '=') { break; } if (isOneOf(ch, delimiters)) { terminated = true; break; } pos++; } if (pos == indexTo) { terminated = true; name = buffer.substringTrimmed(indexFrom, indexTo); } else { name = buffer.substringTrimmed(indexFrom, pos); pos++; } if (terminated) { cursor.updatePos(pos); return createNameValuePair(name, null); } // Find value String value = null; int i1 = pos; boolean qouted = false; boolean escaped = false; while (pos < indexTo) { char ch = buffer.charAt(pos); if (ch == '"' && !escaped) { qouted = !qouted; } if (!qouted && !escaped && isOneOf(ch, delimiters)) { terminated = true; break; } if (escaped) { escaped = false; } else { escaped = qouted && ch == '\\'; } pos++; } int i2 = pos; // Trim leading white spaces while (i1 < i2 && (HTTP.isWhitespace(buffer.charAt(i1)))) { i1++; } // Trim trailing white spaces while ((i2 > i1) && (HTTP.isWhitespace(buffer.charAt(i2 - 1)))) { i2--; } // Strip away quotes if necessary if (((i2 - i1) >= 2) && (buffer.charAt(i1) == '"') && (buffer.charAt(i2 - 1) == '"')) { i1++; i2--; } value = buffer.substring(i1, i2); if (terminated) { pos++; } cursor.updatePos(pos); return createNameValuePair(name, value); } /** * Creates a name-value pair. * Called from {@link #parseNameValuePair}. * * @param name the name * @param value the value, or <code>null</code> * * @return a name-value pair representing the arguments */ protected NameValuePair createNameValuePair(final String name, final String value) { return new BasicNameValuePair(name, value); } }