Wed, 31 Dec 2014 07:22:50 +0100
Correct previous dual key logic pending first delivery installment.
1 /*
2 * ====================================================================
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 * ====================================================================
20 *
21 * This software consists of voluntary contributions made by many
22 * individuals on behalf of the Apache Software Foundation. For more
23 * information on the Apache Software Foundation, please see
24 * <http://www.apache.org/>.
25 *
26 */
28 package ch.boye.httpclientandroidlib.message;
30 import ch.boye.httpclientandroidlib.HeaderElement;
31 import ch.boye.httpclientandroidlib.NameValuePair;
32 import ch.boye.httpclientandroidlib.util.CharArrayBuffer;
34 /**
35 * Basic implementation for formatting header value elements.
36 * Instances of this class are stateless and thread-safe.
37 * Derived classes are expected to maintain these properties.
38 *
39 * @since 4.0
40 */
41 public class BasicHeaderValueFormatter implements HeaderValueFormatter {
43 /**
44 * A default instance of this class, for use as default or fallback.
45 * Note that {@link BasicHeaderValueFormatter} is not a singleton, there
46 * can be many instances of the class itself and of derived classes.
47 * The instance here provides non-customized, default behavior.
48 */
49 public final static
50 BasicHeaderValueFormatter DEFAULT = new BasicHeaderValueFormatter();
53 /**
54 * Special characters that can be used as separators in HTTP parameters.
55 * These special characters MUST be in a quoted string to be used within
56 * a parameter value .
57 */
58 public final static String SEPARATORS = " ;,:@()<>\\\"/[]?={}\t";
61 /**
62 * Unsafe special characters that must be escaped using the backslash
63 * character
64 */
65 public final static String UNSAFE_CHARS = "\"\\";
69 // public default constructor
73 /**
74 * Formats an array of header elements.
75 *
76 * @param elems the header elements to format
77 * @param quote <code>true</code> to always format with quoted values,
78 * <code>false</code> to use quotes only when necessary
79 * @param formatter the formatter to use, or <code>null</code>
80 * for the {@link #DEFAULT default}
81 *
82 * @return the formatted header elements
83 */
84 public final static
85 String formatElements(final HeaderElement[] elems,
86 final boolean quote,
87 HeaderValueFormatter formatter) {
88 if (formatter == null)
89 formatter = BasicHeaderValueFormatter.DEFAULT;
90 return formatter.formatElements(null, elems, quote).toString();
91 }
94 // non-javadoc, see interface HeaderValueFormatter
95 public CharArrayBuffer formatElements(CharArrayBuffer buffer,
96 final HeaderElement[] elems,
97 final boolean quote) {
98 if (elems == null) {
99 throw new IllegalArgumentException
100 ("Header element array must not be null.");
101 }
103 int len = estimateElementsLen(elems);
104 if (buffer == null) {
105 buffer = new CharArrayBuffer(len);
106 } else {
107 buffer.ensureCapacity(len);
108 }
110 for (int i=0; i<elems.length; i++) {
111 if (i > 0) {
112 buffer.append(", ");
113 }
114 formatHeaderElement(buffer, elems[i], quote);
115 }
117 return buffer;
118 }
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;
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 }
137 return result;
138 }
142 /**
143 * Formats a header element.
144 *
145 * @param elem the header element to format
146 * @param quote <code>true</code> to always format with quoted values,
147 * <code>false</code> to use quotes only when necessary
148 * @param formatter the formatter to use, or <code>null</code>
149 * for the {@link #DEFAULT default}
150 *
151 * @return the formatted header element
152 */
153 public final static
154 String formatHeaderElement(final HeaderElement elem,
155 boolean quote,
156 HeaderValueFormatter formatter) {
157 if (formatter == null)
158 formatter = BasicHeaderValueFormatter.DEFAULT;
159 return formatter.formatHeaderElement(null, elem, quote).toString();
160 }
163 // non-javadoc, see interface HeaderValueFormatter
164 public CharArrayBuffer formatHeaderElement(CharArrayBuffer buffer,
165 final HeaderElement elem,
166 final boolean quote) {
167 if (elem == null) {
168 throw new IllegalArgumentException
169 ("Header element must not be null.");
170 }
172 int len = estimateHeaderElementLen(elem);
173 if (buffer == null) {
174 buffer = new CharArrayBuffer(len);
175 } else {
176 buffer.ensureCapacity(len);
177 }
179 buffer.append(elem.getName());
180 final String value = elem.getValue();
181 if (value != null) {
182 buffer.append('=');
183 doFormatValue(buffer, value, quote);
184 }
186 final int parcnt = elem.getParameterCount();
187 if (parcnt > 0) {
188 for (int i=0; i<parcnt; i++) {
189 buffer.append("; ");
190 formatNameValuePair(buffer, elem.getParameter(i), quote);
191 }
192 }
194 return buffer;
195 }
198 /**
199 * Estimates the length of a formatted header element.
200 *
201 * @param elem the header element to format, or <code>null</code>
202 *
203 * @return a length estimate, in number of characters
204 */
205 protected int estimateHeaderElementLen(final HeaderElement elem) {
206 if (elem == null)
207 return 0;
209 int result = elem.getName().length(); // name
210 final String value = elem.getValue();
211 if (value != null) {
212 // assume quotes, but no escaped characters
213 result += 3 + value.length(); // ="value"
214 }
216 final int parcnt = elem.getParameterCount();
217 if (parcnt > 0) {
218 for (int i=0; i<parcnt; i++) {
219 result += 2 + // ; <param>
220 estimateNameValuePairLen(elem.getParameter(i));
221 }
222 }
224 return result;
225 }
230 /**
231 * Formats a set of parameters.
232 *
233 * @param nvps the parameters to format
234 * @param quote <code>true</code> to always format with quoted values,
235 * <code>false</code> to use quotes only when necessary
236 * @param formatter the formatter to use, or <code>null</code>
237 * for the {@link #DEFAULT default}
238 *
239 * @return the formatted parameters
240 */
241 public final static
242 String formatParameters(final NameValuePair[] nvps,
243 final boolean quote,
244 HeaderValueFormatter formatter) {
245 if (formatter == null)
246 formatter = BasicHeaderValueFormatter.DEFAULT;
247 return formatter.formatParameters(null, nvps, quote).toString();
248 }
251 // non-javadoc, see interface HeaderValueFormatter
252 public CharArrayBuffer formatParameters(CharArrayBuffer buffer,
253 NameValuePair[] nvps,
254 boolean quote) {
255 if (nvps == null) {
256 throw new IllegalArgumentException
257 ("Parameters must not be null.");
258 }
260 int len = estimateParametersLen(nvps);
261 if (buffer == null) {
262 buffer = new CharArrayBuffer(len);
263 } else {
264 buffer.ensureCapacity(len);
265 }
267 for (int i = 0; i < nvps.length; i++) {
268 if (i > 0) {
269 buffer.append("; ");
270 }
271 formatNameValuePair(buffer, nvps[i], quote);
272 }
274 return buffer;
275 }
278 /**
279 * Estimates the length of formatted parameters.
280 *
281 * @param nvps the parameters to format, or <code>null</code>
282 *
283 * @return a length estimate, in number of characters
284 */
285 protected int estimateParametersLen(final NameValuePair[] nvps) {
286 if ((nvps == null) || (nvps.length < 1))
287 return 0;
289 int result = (nvps.length-1) * 2; // "; " between the parameters
290 for (int i=0; i<nvps.length; i++) {
291 result += estimateNameValuePairLen(nvps[i]);
292 }
294 return result;
295 }
298 /**
299 * Formats a name-value pair.
300 *
301 * @param nvp the name-value pair to format
302 * @param quote <code>true</code> to always format with a quoted value,
303 * <code>false</code> to use quotes only when necessary
304 * @param formatter the formatter to use, or <code>null</code>
305 * for the {@link #DEFAULT default}
306 *
307 * @return the formatted name-value pair
308 */
309 public final static
310 String formatNameValuePair(final NameValuePair nvp,
311 final boolean quote,
312 HeaderValueFormatter formatter) {
313 if (formatter == null)
314 formatter = BasicHeaderValueFormatter.DEFAULT;
315 return formatter.formatNameValuePair(null, nvp, quote).toString();
316 }
319 // non-javadoc, see interface HeaderValueFormatter
320 public CharArrayBuffer formatNameValuePair(CharArrayBuffer buffer,
321 final NameValuePair nvp,
322 final boolean quote) {
323 if (nvp == null) {
324 throw new IllegalArgumentException
325 ("NameValuePair must not be null.");
326 }
328 int len = estimateNameValuePairLen(nvp);
329 if (buffer == null) {
330 buffer = new CharArrayBuffer(len);
331 } else {
332 buffer.ensureCapacity(len);
333 }
335 buffer.append(nvp.getName());
336 final String value = nvp.getValue();
337 if (value != null) {
338 buffer.append('=');
339 doFormatValue(buffer, value, quote);
340 }
342 return buffer;
343 }
346 /**
347 * Estimates the length of a formatted name-value pair.
348 *
349 * @param nvp the name-value pair to format, or <code>null</code>
350 *
351 * @return a length estimate, in number of characters
352 */
353 protected int estimateNameValuePairLen(final NameValuePair nvp) {
354 if (nvp == null)
355 return 0;
357 int result = nvp.getName().length(); // name
358 final String value = nvp.getValue();
359 if (value != null) {
360 // assume quotes, but no escaped characters
361 result += 3 + value.length(); // ="value"
362 }
363 return result;
364 }
367 /**
368 * Actually formats the value of a name-value pair.
369 * This does not include a leading = character.
370 * Called from {@link #formatNameValuePair formatNameValuePair}.
371 *
372 * @param buffer the buffer to append to, never <code>null</code>
373 * @param value the value to append, never <code>null</code>
374 * @param quote <code>true</code> to always format with quotes,
375 * <code>false</code> to use quotes only when necessary
376 */
377 protected void doFormatValue(final CharArrayBuffer buffer,
378 final String value,
379 boolean quote) {
381 if (!quote) {
382 for (int i = 0; (i < value.length()) && !quote; i++) {
383 quote = isSeparator(value.charAt(i));
384 }
385 }
387 if (quote) {
388 buffer.append('"');
389 }
390 for (int i = 0; i < value.length(); i++) {
391 char ch = value.charAt(i);
392 if (isUnsafe(ch)) {
393 buffer.append('\\');
394 }
395 buffer.append(ch);
396 }
397 if (quote) {
398 buffer.append('"');
399 }
400 }
403 /**
404 * Checks whether a character is a {@link #SEPARATORS separator}.
405 *
406 * @param ch the character to check
407 *
408 * @return <code>true</code> if the character is a separator,
409 * <code>false</code> otherwise
410 */
411 protected boolean isSeparator(char ch) {
412 return SEPARATORS.indexOf(ch) >= 0;
413 }
416 /**
417 * Checks whether a character is {@link #UNSAFE_CHARS unsafe}.
418 *
419 * @param ch the character to check
420 *
421 * @return <code>true</code> if the character is unsafe,
422 * <code>false</code> otherwise
423 */
424 protected boolean isUnsafe(char ch) {
425 return UNSAFE_CHARS.indexOf(ch) >= 0;
426 }
429 } // class BasicHeaderValueFormatter