001: /*
002: * $HeadURL: https://svn.apache.org/repos/asf/httpcomponents/httpcore/tags/4.0-beta1/module-main/src/main/java/org/apache/http/message/BasicHeaderValueFormatter.java $
003: * $Revision: 574185 $
004: * $Date: 2007-09-10 11:19:47 +0200 (Mon, 10 Sep 2007) $
005: *
006: * ====================================================================
007: * Licensed to the Apache Software Foundation (ASF) under one
008: * or more contributor license agreements. See the NOTICE file
009: * distributed with this work for additional information
010: * regarding copyright ownership. The ASF licenses this file
011: * to you under the Apache License, Version 2.0 (the
012: * "License"); you may not use this file except in compliance
013: * with the License. You may obtain a copy of the License at
014: *
015: * http://www.apache.org/licenses/LICENSE-2.0
016: *
017: * Unless required by applicable law or agreed to in writing,
018: * software distributed under the License is distributed on an
019: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
020: * KIND, either express or implied. See the License for the
021: * specific language governing permissions and limitations
022: * under the License.
023: * ====================================================================
024: *
025: * This software consists of voluntary contributions made by many
026: * individuals on behalf of the Apache Software Foundation. For more
027: * information on the Apache Software Foundation, please see
028: * <http://www.apache.org/>.
029: *
030: */
031:
032: package org.apache.http.message;
033:
034: import org.apache.http.HeaderElement;
035: import org.apache.http.NameValuePair;
036: import org.apache.http.util.CharArrayBuffer;
037:
038: /**
039: * Basic implementation for formatting header value elements.
040: * Instances of this class are stateless and thread-safe.
041: * Derived classes are expected to maintain these properties.
042: *
043: * @author <a href="mailto:oleg at ural.com">Oleg Kalnichevski</a>
044: * @author and others
045: *
046: *
047: * <!-- empty lines above to avoid 'svn diff' context problems -->
048: * @version $Revision: 574185 $
049: *
050: * @since 4.0
051: */
052: public class BasicHeaderValueFormatter implements HeaderValueFormatter {
053:
054: /**
055: * A default instance of this class, for use as default or fallback.
056: * Note that {@link BasicHeaderValueFormatter} is not a singleton, there
057: * can be many instances of the class itself and of derived classes.
058: * The instance here provides non-customized, default behavior.
059: */
060: public final static BasicHeaderValueFormatter DEFAULT = new BasicHeaderValueFormatter();
061:
062: /**
063: * Special characters that can be used as separators in HTTP parameters.
064: * These special characters MUST be in a quoted string to be used within
065: * a parameter value .
066: */
067: public final static String SEPARATORS = " ;,:@()<>\\\"/[]?={}\t";
068:
069: /**
070: * Unsafe special characters that must be escaped using the backslash
071: * character
072: */
073: public final static String UNSAFE_CHARS = "\"\\";
074:
075: // public default constructor
076:
077: /**
078: * Formats an array of header elements.
079: *
080: * @param elems the header elements to format
081: * @param quote <code>true</code> to always format with quoted values,
082: * <code>false</code> to use quotes only when necessary
083: * @param formatter the formatter to use, or <code>null</code>
084: * for the {@link #DEFAULT default}
085: *
086: * @return the formatted header elements
087: */
088: public final static String formatElements(
089: final HeaderElement[] elems, final boolean quote,
090: HeaderValueFormatter formatter) {
091: if (formatter == null)
092: formatter = BasicHeaderValueFormatter.DEFAULT;
093: return formatter.formatElements(null, elems, quote).toString();
094: }
095:
096: // non-javadoc, see interface HeaderValueFormatter
097: public CharArrayBuffer formatElements(CharArrayBuffer buffer,
098: final HeaderElement[] elems, final boolean quote) {
099: if (elems == null) {
100: throw new IllegalArgumentException(
101: "Header element array must not be null.");
102: }
103:
104: int len = estimateElementsLen(elems);
105: if (buffer == null) {
106: buffer = new CharArrayBuffer(len);
107: } else {
108: buffer.ensureCapacity(len);
109: }
110:
111: for (int i = 0; i < elems.length; i++) {
112: if (i > 0) {
113: buffer.append(", ");
114: }
115: formatHeaderElement(buffer, elems[i], quote);
116: }
117:
118: return buffer;
119: }
120:
121: /**
122: * Estimates the length of formatted header elements.
123: *
124: * @param elems the header elements to format, or <code>null</code>
125: *
126: * @return a length estimate, in number of characters
127: */
128: protected int estimateElementsLen(final HeaderElement[] elems) {
129: if ((elems == null) || (elems.length < 1))
130: return 0;
131:
132: int result = (elems.length - 1) * 2; // elements separated by ", "
133: for (int i = 0; i < elems.length; i++) {
134: result += estimateHeaderElementLen(elems[i]);
135: }
136:
137: return result;
138: }
139:
140: /**
141: * Formats a header element.
142: *
143: * @param elem the header element to format
144: * @param quote <code>true</code> to always format with quoted values,
145: * <code>false</code> to use quotes only when necessary
146: * @param formatter the formatter to use, or <code>null</code>
147: * for the {@link #DEFAULT default}
148: *
149: * @return the formatted header element
150: */
151: public final static String formatHeaderElement(
152: final HeaderElement elem, boolean quote,
153: HeaderValueFormatter formatter) {
154: if (formatter == null)
155: formatter = BasicHeaderValueFormatter.DEFAULT;
156: return formatter.formatHeaderElement(null, elem, quote)
157: .toString();
158: }
159:
160: // non-javadoc, see interface HeaderValueFormatter
161: public CharArrayBuffer formatHeaderElement(CharArrayBuffer buffer,
162: final HeaderElement elem, final boolean quote) {
163: if (elem == null) {
164: throw new IllegalArgumentException(
165: "Header element must not be null.");
166: }
167:
168: int len = estimateHeaderElementLen(elem);
169: if (buffer == null) {
170: buffer = new CharArrayBuffer(len);
171: } else {
172: buffer.ensureCapacity(len);
173: }
174:
175: buffer.append(elem.getName());
176: final String value = elem.getValue();
177: if (value != null) {
178: buffer.append('=');
179: doFormatValue(buffer, value, quote);
180: }
181:
182: final int parcnt = elem.getParameterCount();
183: if (parcnt > 0) {
184: for (int i = 0; i < parcnt; i++) {
185: buffer.append("; ");
186: formatNameValuePair(buffer, elem.getParameter(i), quote);
187: }
188: }
189:
190: return buffer;
191: }
192:
193: /**
194: * Estimates the length of a formatted header element.
195: *
196: * @param elem the header element to format, or <code>null</code>
197: *
198: * @return a length estimate, in number of characters
199: */
200: protected int estimateHeaderElementLen(final HeaderElement elem) {
201: if (elem == null)
202: return 0;
203:
204: int result = elem.getName().length(); // name
205: final String value = elem.getValue();
206: if (value != null) {
207: // assume quotes, but no escaped characters
208: result += 3 + value.length(); // ="value"
209: }
210:
211: final int parcnt = elem.getParameterCount();
212: if (parcnt > 0) {
213: for (int i = 0; i < parcnt; i++) {
214: result += 2 + // ; <param>
215: estimateNameValuePairLen(elem.getParameter(i));
216: }
217: }
218:
219: return result;
220: }
221:
222: /**
223: * Formats a set of parameters.
224: *
225: * @param nvps the parameters to format
226: * @param quote <code>true</code> to always format with quoted values,
227: * <code>false</code> to use quotes only when necessary
228: * @param formatter the formatter to use, or <code>null</code>
229: * for the {@link #DEFAULT default}
230: *
231: * @return the formatted parameters
232: */
233: public final static String formatParameters(
234: final NameValuePair[] nvps, final boolean quote,
235: HeaderValueFormatter formatter) {
236: if (formatter == null)
237: formatter = BasicHeaderValueFormatter.DEFAULT;
238: return formatter.formatParameters(null, nvps, quote).toString();
239: }
240:
241: // non-javadoc, see interface HeaderValueFormatter
242: public CharArrayBuffer formatParameters(CharArrayBuffer buffer,
243: NameValuePair[] nvps, boolean quote) {
244: if (nvps == null) {
245: throw new IllegalArgumentException(
246: "Parameters must not be null.");
247: }
248:
249: int len = estimateParametersLen(nvps);
250: if (buffer == null) {
251: buffer = new CharArrayBuffer(len);
252: } else {
253: buffer.ensureCapacity(len);
254: }
255:
256: for (int i = 0; i < nvps.length; i++) {
257: if (i > 0) {
258: buffer.append("; ");
259: }
260: formatNameValuePair(buffer, nvps[i], quote);
261: }
262:
263: return buffer;
264: }
265:
266: /**
267: * Estimates the length of formatted parameters.
268: *
269: * @param nvps the parameters to format, or <code>null</code>
270: *
271: * @return a length estimate, in number of characters
272: */
273: protected int estimateParametersLen(final NameValuePair[] nvps) {
274: if ((nvps == null) || (nvps.length < 1))
275: return 0;
276:
277: int result = (nvps.length - 1) * 2; // "; " between the parameters
278: for (int i = 0; i < nvps.length; i++) {
279: result += estimateNameValuePairLen(nvps[i]);
280: }
281:
282: return result;
283: }
284:
285: /**
286: * Formats a name-value pair.
287: *
288: * @param nvp the name-value pair to format
289: * @param quote <code>true</code> to always format with a quoted value,
290: * <code>false</code> to use quotes only when necessary
291: * @param formatter the formatter to use, or <code>null</code>
292: * for the {@link #DEFAULT default}
293: *
294: * @return the formatted name-value pair
295: */
296: public final static String formatNameValuePair(
297: final NameValuePair nvp, final boolean quote,
298: HeaderValueFormatter formatter) {
299: if (formatter == null)
300: formatter = BasicHeaderValueFormatter.DEFAULT;
301: return formatter.formatNameValuePair(null, nvp, quote)
302: .toString();
303: }
304:
305: // non-javadoc, see interface HeaderValueFormatter
306: public CharArrayBuffer formatNameValuePair(CharArrayBuffer buffer,
307: final NameValuePair nvp, final boolean quote) {
308: if (nvp == null) {
309: throw new IllegalArgumentException(
310: "NameValuePair must not be null.");
311: }
312:
313: int len = estimateNameValuePairLen(nvp);
314: if (buffer == null) {
315: buffer = new CharArrayBuffer(len);
316: } else {
317: buffer.ensureCapacity(len);
318: }
319:
320: buffer.append(nvp.getName());
321: final String value = nvp.getValue();
322: if (value != null) {
323: buffer.append('=');
324: doFormatValue(buffer, value, quote);
325: }
326:
327: return buffer;
328: }
329:
330: /**
331: * Estimates the length of a formatted name-value pair.
332: *
333: * @param nvp the name-value pair to format, or <code>null</code>
334: *
335: * @return a length estimate, in number of characters
336: */
337: protected int estimateNameValuePairLen(final NameValuePair nvp) {
338: if (nvp == null)
339: return 0;
340:
341: int result = nvp.getName().length(); // name
342: final String value = nvp.getValue();
343: if (value != null) {
344: // assume quotes, but no escaped characters
345: result += 3 + value.length(); // ="value"
346: }
347: return result;
348: }
349:
350: /**
351: * Actually formats the value of a name-value pair.
352: * This does not include a leading = character.
353: * Called from {@link #formatNameValuePair formatNameValuePair}.
354: *
355: * @param buffer the buffer to append to, never <code>null</code>
356: * @param value the value to append, never <code>null</code>
357: * @param quote <code>true</code> to always format with quotes,
358: * <code>false</code> to use quotes only when necessary
359: */
360: protected void doFormatValue(final CharArrayBuffer buffer,
361: final String value, boolean quote) {
362:
363: if (!quote) {
364: for (int i = 0; (i < value.length()) && !quote; i++) {
365: quote = isSeparator(value.charAt(i));
366: }
367: }
368:
369: if (quote) {
370: buffer.append('"');
371: }
372: for (int i = 0; i < value.length(); i++) {
373: char ch = value.charAt(i);
374: if (isUnsafe(ch)) {
375: buffer.append('\\');
376: }
377: buffer.append(ch);
378: }
379: if (quote) {
380: buffer.append('"');
381: }
382: }
383:
384: /**
385: * Checks whether a character is a {@link #SEPARATORS separator}.
386: *
387: * @param ch the character to check
388: *
389: * @return <code>true</code> if the character is a separator,
390: * <code>false</code> otherwise
391: */
392: protected boolean isSeparator(char ch) {
393: return SEPARATORS.indexOf(ch) >= 0;
394: }
395:
396: /**
397: * Checks whether a character is {@link #UNSAFE_CHARS unsafe}.
398: *
399: * @param ch the character to check
400: *
401: * @return <code>true</code> if the character is unsafe,
402: * <code>false</code> otherwise
403: */
404: protected boolean isUnsafe(char ch) {
405: return UNSAFE_CHARS.indexOf(ch) >= 0;
406: }
407:
408: } // class BasicHeaderValueFormatter
|