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