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