|
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 */ |
|
27 |
|
28 package ch.boye.httpclientandroidlib.message; |
|
29 |
|
30 import ch.boye.httpclientandroidlib.HeaderElement; |
|
31 import ch.boye.httpclientandroidlib.NameValuePair; |
|
32 import ch.boye.httpclientandroidlib.util.CharArrayBuffer; |
|
33 |
|
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 { |
|
42 |
|
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(); |
|
51 |
|
52 |
|
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"; |
|
59 |
|
60 |
|
61 /** |
|
62 * Unsafe special characters that must be escaped using the backslash |
|
63 * character |
|
64 */ |
|
65 public final static String UNSAFE_CHARS = "\"\\"; |
|
66 |
|
67 |
|
68 |
|
69 // public default constructor |
|
70 |
|
71 |
|
72 |
|
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 } |
|
92 |
|
93 |
|
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 } |
|
102 |
|
103 int len = estimateElementsLen(elems); |
|
104 if (buffer == null) { |
|
105 buffer = new CharArrayBuffer(len); |
|
106 } else { |
|
107 buffer.ensureCapacity(len); |
|
108 } |
|
109 |
|
110 for (int i=0; i<elems.length; i++) { |
|
111 if (i > 0) { |
|
112 buffer.append(", "); |
|
113 } |
|
114 formatHeaderElement(buffer, elems[i], quote); |
|
115 } |
|
116 |
|
117 return buffer; |
|
118 } |
|
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 |
|
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 } |
|
161 |
|
162 |
|
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 } |
|
171 |
|
172 int len = estimateHeaderElementLen(elem); |
|
173 if (buffer == null) { |
|
174 buffer = new CharArrayBuffer(len); |
|
175 } else { |
|
176 buffer.ensureCapacity(len); |
|
177 } |
|
178 |
|
179 buffer.append(elem.getName()); |
|
180 final String value = elem.getValue(); |
|
181 if (value != null) { |
|
182 buffer.append('='); |
|
183 doFormatValue(buffer, value, quote); |
|
184 } |
|
185 |
|
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 } |
|
193 |
|
194 return buffer; |
|
195 } |
|
196 |
|
197 |
|
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; |
|
208 |
|
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 } |
|
215 |
|
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 } |
|
223 |
|
224 return result; |
|
225 } |
|
226 |
|
227 |
|
228 |
|
229 |
|
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 } |
|
249 |
|
250 |
|
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 } |
|
259 |
|
260 int len = estimateParametersLen(nvps); |
|
261 if (buffer == null) { |
|
262 buffer = new CharArrayBuffer(len); |
|
263 } else { |
|
264 buffer.ensureCapacity(len); |
|
265 } |
|
266 |
|
267 for (int i = 0; i < nvps.length; i++) { |
|
268 if (i > 0) { |
|
269 buffer.append("; "); |
|
270 } |
|
271 formatNameValuePair(buffer, nvps[i], quote); |
|
272 } |
|
273 |
|
274 return buffer; |
|
275 } |
|
276 |
|
277 |
|
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; |
|
288 |
|
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 } |
|
293 |
|
294 return result; |
|
295 } |
|
296 |
|
297 |
|
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 } |
|
317 |
|
318 |
|
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 } |
|
327 |
|
328 int len = estimateNameValuePairLen(nvp); |
|
329 if (buffer == null) { |
|
330 buffer = new CharArrayBuffer(len); |
|
331 } else { |
|
332 buffer.ensureCapacity(len); |
|
333 } |
|
334 |
|
335 buffer.append(nvp.getName()); |
|
336 final String value = nvp.getValue(); |
|
337 if (value != null) { |
|
338 buffer.append('='); |
|
339 doFormatValue(buffer, value, quote); |
|
340 } |
|
341 |
|
342 return buffer; |
|
343 } |
|
344 |
|
345 |
|
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; |
|
356 |
|
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 } |
|
365 |
|
366 |
|
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) { |
|
380 |
|
381 if (!quote) { |
|
382 for (int i = 0; (i < value.length()) && !quote; i++) { |
|
383 quote = isSeparator(value.charAt(i)); |
|
384 } |
|
385 } |
|
386 |
|
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 } |
|
401 |
|
402 |
|
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 } |
|
414 |
|
415 |
|
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 } |
|
427 |
|
428 |
|
429 } // class BasicHeaderValueFormatter |