michael@0: /* michael@0: * ==================================================================== michael@0: * Licensed to the Apache Software Foundation (ASF) under one michael@0: * or more contributor license agreements. See the NOTICE file michael@0: * distributed with this work for additional information michael@0: * regarding copyright ownership. The ASF licenses this file michael@0: * to you under the Apache License, Version 2.0 (the michael@0: * "License"); you may not use this file except in compliance michael@0: * with the License. You may obtain a copy of the License at michael@0: * michael@0: * http://www.apache.org/licenses/LICENSE-2.0 michael@0: * michael@0: * Unless required by applicable law or agreed to in writing, michael@0: * software distributed under the License is distributed on an michael@0: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY michael@0: * KIND, either express or implied. See the License for the michael@0: * specific language governing permissions and limitations michael@0: * under the License. michael@0: * ==================================================================== michael@0: * michael@0: * This software consists of voluntary contributions made by many michael@0: * individuals on behalf of the Apache Software Foundation. For more michael@0: * information on the Apache Software Foundation, please see michael@0: * . michael@0: * michael@0: */ michael@0: michael@0: package ch.boye.httpclientandroidlib.message; michael@0: michael@0: import ch.boye.httpclientandroidlib.HeaderElement; michael@0: import ch.boye.httpclientandroidlib.NameValuePair; michael@0: import ch.boye.httpclientandroidlib.util.CharArrayBuffer; michael@0: michael@0: /** michael@0: * Basic implementation for formatting header value elements. michael@0: * Instances of this class are stateless and thread-safe. michael@0: * Derived classes are expected to maintain these properties. michael@0: * michael@0: * @since 4.0 michael@0: */ michael@0: public class BasicHeaderValueFormatter implements HeaderValueFormatter { michael@0: michael@0: /** michael@0: * A default instance of this class, for use as default or fallback. michael@0: * Note that {@link BasicHeaderValueFormatter} is not a singleton, there michael@0: * can be many instances of the class itself and of derived classes. michael@0: * The instance here provides non-customized, default behavior. michael@0: */ michael@0: public final static michael@0: BasicHeaderValueFormatter DEFAULT = new BasicHeaderValueFormatter(); michael@0: michael@0: michael@0: /** michael@0: * Special characters that can be used as separators in HTTP parameters. michael@0: * These special characters MUST be in a quoted string to be used within michael@0: * a parameter value . michael@0: */ michael@0: public final static String SEPARATORS = " ;,:@()<>\\\"/[]?={}\t"; michael@0: michael@0: michael@0: /** michael@0: * Unsafe special characters that must be escaped using the backslash michael@0: * character michael@0: */ michael@0: public final static String UNSAFE_CHARS = "\"\\"; michael@0: michael@0: michael@0: michael@0: // public default constructor michael@0: michael@0: michael@0: michael@0: /** michael@0: * Formats an array of header elements. michael@0: * michael@0: * @param elems the header elements to format michael@0: * @param quote true to always format with quoted values, michael@0: * false to use quotes only when necessary michael@0: * @param formatter the formatter to use, or null michael@0: * for the {@link #DEFAULT default} michael@0: * michael@0: * @return the formatted header elements michael@0: */ michael@0: public final static michael@0: String formatElements(final HeaderElement[] elems, michael@0: final boolean quote, michael@0: HeaderValueFormatter formatter) { michael@0: if (formatter == null) michael@0: formatter = BasicHeaderValueFormatter.DEFAULT; michael@0: return formatter.formatElements(null, elems, quote).toString(); michael@0: } michael@0: michael@0: michael@0: // non-javadoc, see interface HeaderValueFormatter michael@0: public CharArrayBuffer formatElements(CharArrayBuffer buffer, michael@0: final HeaderElement[] elems, michael@0: final boolean quote) { michael@0: if (elems == null) { michael@0: throw new IllegalArgumentException michael@0: ("Header element array must not be null."); michael@0: } michael@0: michael@0: int len = estimateElementsLen(elems); michael@0: if (buffer == null) { michael@0: buffer = new CharArrayBuffer(len); michael@0: } else { michael@0: buffer.ensureCapacity(len); michael@0: } michael@0: michael@0: for (int i=0; i 0) { michael@0: buffer.append(", "); michael@0: } michael@0: formatHeaderElement(buffer, elems[i], quote); michael@0: } michael@0: michael@0: return buffer; michael@0: } michael@0: michael@0: michael@0: /** michael@0: * Estimates the length of formatted header elements. michael@0: * michael@0: * @param elems the header elements to format, or null michael@0: * michael@0: * @return a length estimate, in number of characters michael@0: */ michael@0: protected int estimateElementsLen(final HeaderElement[] elems) { michael@0: if ((elems == null) || (elems.length < 1)) michael@0: return 0; michael@0: michael@0: int result = (elems.length-1) * 2; // elements separated by ", " michael@0: for (int i=0; itrue to always format with quoted values, michael@0: * false to use quotes only when necessary michael@0: * @param formatter the formatter to use, or null michael@0: * for the {@link #DEFAULT default} michael@0: * michael@0: * @return the formatted header element michael@0: */ michael@0: public final static michael@0: String formatHeaderElement(final HeaderElement elem, michael@0: boolean quote, michael@0: HeaderValueFormatter formatter) { michael@0: if (formatter == null) michael@0: formatter = BasicHeaderValueFormatter.DEFAULT; michael@0: return formatter.formatHeaderElement(null, elem, quote).toString(); michael@0: } michael@0: michael@0: michael@0: // non-javadoc, see interface HeaderValueFormatter michael@0: public CharArrayBuffer formatHeaderElement(CharArrayBuffer buffer, michael@0: final HeaderElement elem, michael@0: final boolean quote) { michael@0: if (elem == null) { michael@0: throw new IllegalArgumentException michael@0: ("Header element must not be null."); michael@0: } michael@0: michael@0: int len = estimateHeaderElementLen(elem); michael@0: if (buffer == null) { michael@0: buffer = new CharArrayBuffer(len); michael@0: } else { michael@0: buffer.ensureCapacity(len); michael@0: } michael@0: michael@0: buffer.append(elem.getName()); michael@0: final String value = elem.getValue(); michael@0: if (value != null) { michael@0: buffer.append('='); michael@0: doFormatValue(buffer, value, quote); michael@0: } michael@0: michael@0: final int parcnt = elem.getParameterCount(); michael@0: if (parcnt > 0) { michael@0: for (int i=0; inull michael@0: * michael@0: * @return a length estimate, in number of characters michael@0: */ michael@0: protected int estimateHeaderElementLen(final HeaderElement elem) { michael@0: if (elem == null) michael@0: return 0; michael@0: michael@0: int result = elem.getName().length(); // name michael@0: final String value = elem.getValue(); michael@0: if (value != null) { michael@0: // assume quotes, but no escaped characters michael@0: result += 3 + value.length(); // ="value" michael@0: } michael@0: michael@0: final int parcnt = elem.getParameterCount(); michael@0: if (parcnt > 0) { michael@0: for (int i=0; i michael@0: estimateNameValuePairLen(elem.getParameter(i)); michael@0: } michael@0: } michael@0: michael@0: return result; michael@0: } michael@0: michael@0: michael@0: michael@0: michael@0: /** michael@0: * Formats a set of parameters. michael@0: * michael@0: * @param nvps the parameters to format michael@0: * @param quote true to always format with quoted values, michael@0: * false to use quotes only when necessary michael@0: * @param formatter the formatter to use, or null michael@0: * for the {@link #DEFAULT default} michael@0: * michael@0: * @return the formatted parameters michael@0: */ michael@0: public final static michael@0: String formatParameters(final NameValuePair[] nvps, michael@0: final boolean quote, michael@0: HeaderValueFormatter formatter) { michael@0: if (formatter == null) michael@0: formatter = BasicHeaderValueFormatter.DEFAULT; michael@0: return formatter.formatParameters(null, nvps, quote).toString(); michael@0: } michael@0: michael@0: michael@0: // non-javadoc, see interface HeaderValueFormatter michael@0: public CharArrayBuffer formatParameters(CharArrayBuffer buffer, michael@0: NameValuePair[] nvps, michael@0: boolean quote) { michael@0: if (nvps == null) { michael@0: throw new IllegalArgumentException michael@0: ("Parameters must not be null."); michael@0: } michael@0: michael@0: int len = estimateParametersLen(nvps); michael@0: if (buffer == null) { michael@0: buffer = new CharArrayBuffer(len); michael@0: } else { michael@0: buffer.ensureCapacity(len); michael@0: } michael@0: michael@0: for (int i = 0; i < nvps.length; i++) { michael@0: if (i > 0) { michael@0: buffer.append("; "); michael@0: } michael@0: formatNameValuePair(buffer, nvps[i], quote); michael@0: } michael@0: michael@0: return buffer; michael@0: } michael@0: michael@0: michael@0: /** michael@0: * Estimates the length of formatted parameters. michael@0: * michael@0: * @param nvps the parameters to format, or null michael@0: * michael@0: * @return a length estimate, in number of characters michael@0: */ michael@0: protected int estimateParametersLen(final NameValuePair[] nvps) { michael@0: if ((nvps == null) || (nvps.length < 1)) michael@0: return 0; michael@0: michael@0: int result = (nvps.length-1) * 2; // "; " between the parameters michael@0: for (int i=0; itrue to always format with a quoted value, michael@0: * false to use quotes only when necessary michael@0: * @param formatter the formatter to use, or null michael@0: * for the {@link #DEFAULT default} michael@0: * michael@0: * @return the formatted name-value pair michael@0: */ michael@0: public final static michael@0: String formatNameValuePair(final NameValuePair nvp, michael@0: final boolean quote, michael@0: HeaderValueFormatter formatter) { michael@0: if (formatter == null) michael@0: formatter = BasicHeaderValueFormatter.DEFAULT; michael@0: return formatter.formatNameValuePair(null, nvp, quote).toString(); michael@0: } michael@0: michael@0: michael@0: // non-javadoc, see interface HeaderValueFormatter michael@0: public CharArrayBuffer formatNameValuePair(CharArrayBuffer buffer, michael@0: final NameValuePair nvp, michael@0: final boolean quote) { michael@0: if (nvp == null) { michael@0: throw new IllegalArgumentException michael@0: ("NameValuePair must not be null."); michael@0: } michael@0: michael@0: int len = estimateNameValuePairLen(nvp); michael@0: if (buffer == null) { michael@0: buffer = new CharArrayBuffer(len); michael@0: } else { michael@0: buffer.ensureCapacity(len); michael@0: } michael@0: michael@0: buffer.append(nvp.getName()); michael@0: final String value = nvp.getValue(); michael@0: if (value != null) { michael@0: buffer.append('='); michael@0: doFormatValue(buffer, value, quote); michael@0: } michael@0: michael@0: return buffer; michael@0: } michael@0: michael@0: michael@0: /** michael@0: * Estimates the length of a formatted name-value pair. michael@0: * michael@0: * @param nvp the name-value pair to format, or null michael@0: * michael@0: * @return a length estimate, in number of characters michael@0: */ michael@0: protected int estimateNameValuePairLen(final NameValuePair nvp) { michael@0: if (nvp == null) michael@0: return 0; michael@0: michael@0: int result = nvp.getName().length(); // name michael@0: final String value = nvp.getValue(); michael@0: if (value != null) { michael@0: // assume quotes, but no escaped characters michael@0: result += 3 + value.length(); // ="value" michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: michael@0: /** michael@0: * Actually formats the value of a name-value pair. michael@0: * This does not include a leading = character. michael@0: * Called from {@link #formatNameValuePair formatNameValuePair}. michael@0: * michael@0: * @param buffer the buffer to append to, never null michael@0: * @param value the value to append, never null michael@0: * @param quote true to always format with quotes, michael@0: * false to use quotes only when necessary michael@0: */ michael@0: protected void doFormatValue(final CharArrayBuffer buffer, michael@0: final String value, michael@0: boolean quote) { michael@0: michael@0: if (!quote) { michael@0: for (int i = 0; (i < value.length()) && !quote; i++) { michael@0: quote = isSeparator(value.charAt(i)); michael@0: } michael@0: } michael@0: michael@0: if (quote) { michael@0: buffer.append('"'); michael@0: } michael@0: for (int i = 0; i < value.length(); i++) { michael@0: char ch = value.charAt(i); michael@0: if (isUnsafe(ch)) { michael@0: buffer.append('\\'); michael@0: } michael@0: buffer.append(ch); michael@0: } michael@0: if (quote) { michael@0: buffer.append('"'); michael@0: } michael@0: } michael@0: michael@0: michael@0: /** michael@0: * Checks whether a character is a {@link #SEPARATORS separator}. michael@0: * michael@0: * @param ch the character to check michael@0: * michael@0: * @return true if the character is a separator, michael@0: * false otherwise michael@0: */ michael@0: protected boolean isSeparator(char ch) { michael@0: return SEPARATORS.indexOf(ch) >= 0; michael@0: } michael@0: michael@0: michael@0: /** michael@0: * Checks whether a character is {@link #UNSAFE_CHARS unsafe}. michael@0: * michael@0: * @param ch the character to check michael@0: * michael@0: * @return true if the character is unsafe, michael@0: * false otherwise michael@0: */ michael@0: protected boolean isUnsafe(char ch) { michael@0: return UNSAFE_CHARS.indexOf(ch) >= 0; michael@0: } michael@0: michael@0: michael@0: } // class BasicHeaderValueFormatter