1 /* 2 * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/BasicHeaderValueParser.java $ 3 * $Revision: 595670 $ 4 * $Date: 2007-11-16 06:15:01 -0800 (Fri, 16 Nov 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 32 package org.apache.http.message; 33 34 35 import java.util.List; 36 import java.util.ArrayList; 37 38 import org.apache.http.HeaderElement; 39 import org.apache.http.NameValuePair; 40 import org.apache.http.ParseException; 41 import org.apache.http.protocol.HTTP; 42 import org.apache.http.util.CharArrayBuffer; 43 44 45 46 /** 47 * Basic implementation for parsing header values into elements. 48 * Instances of this class are stateless and thread-safe. 49 * Derived classes are expected to maintain these properties. 50 * 51 * @author <a href="mailto:bcholmes (at) interlog.com">B.C. Holmes</a> 52 * @author <a href="mailto:jericho (at) thinkfree.com">Park, Sung-Gu</a> 53 * @author <a href="mailto:mbowler (at) GargoyleSoftware.com">Mike Bowler</a> 54 * @author <a href="mailto:oleg at ural.com">Oleg Kalnichevski</a> 55 * @author and others 56 * 57 * 58 * <!-- empty lines above to avoid 'svn diff' context problems --> 59 * @version $Revision: 595670 $ 60 * 61 * @since 4.0 62 */ 63 public class BasicHeaderValueParser implements HeaderValueParser { 64 65 /** 66 * A default instance of this class, for use as default or fallback. 67 * Note that {@link BasicHeaderValueParser} is not a singleton, there 68 * can be many instances of the class itself and of derived classes. 69 * The instance here provides non-customized, default behavior. 70 */ 71 public final static 72 BasicHeaderValueParser DEFAULT = new BasicHeaderValueParser(); 73 74 private final static char PARAM_DELIMITER = ';'; 75 private final static char ELEM_DELIMITER = ','; 76 private final static char[] ALL_DELIMITERS = new char[] { 77 PARAM_DELIMITER, 78 ELEM_DELIMITER 79 }; 80 81 // public default constructor 82 83 84 /** 85 * Parses elements with the given parser. 86 * 87 * @param value the header value to parse 88 * @param parser the parser to use, or <code>null</code> for default 89 * 90 * @return array holding the header elements, never <code>null</code> 91 */ 92 public final static 93 HeaderElement[] parseElements(final String value, 94 HeaderValueParser parser) 95 throws ParseException { 96 97 if (value == null) { 98 throw new IllegalArgumentException 99 ("Value to parse may not be null"); 100 } 101 102 if (parser == null) 103 parser = BasicHeaderValueParser.DEFAULT; 104 105 CharArrayBuffer buffer = new CharArrayBuffer(value.length()); 106 buffer.append(value); 107 ParserCursor cursor = new ParserCursor(0, value.length()); 108 return parser.parseElements(buffer, cursor); 109 } 110 111 112 // non-javadoc, see interface HeaderValueParser 113 public HeaderElement[] parseElements(final CharArrayBuffer buffer, 114 final ParserCursor cursor) { 115 116 if (buffer == null) { 117 throw new IllegalArgumentException("Char array buffer may not be null"); 118 } 119 if (cursor == null) { 120 throw new IllegalArgumentException("Parser cursor may not be null"); 121 } 122 123 List elements = new ArrayList(); 124 while (!cursor.atEnd()) { 125 HeaderElement element = parseHeaderElement(buffer, cursor); 126 if (!(element.getName().length() == 0 && element.getValue() == null)) { 127 elements.add(element); 128 } 129 } 130 return (HeaderElement[]) 131 elements.toArray(new HeaderElement[elements.size()]); 132 } 133 134 135 /** 136 * Parses an element with the given parser. 137 * 138 * @param value the header element to parse 139 * @param parser the parser to use, or <code>null</code> for default 140 * 141 * @return the parsed header element 142 */ 143 public final static 144 HeaderElement parseHeaderElement(final String value, 145 HeaderValueParser parser) 146 throws ParseException { 147 148 if (value == null) { 149 throw new IllegalArgumentException 150 ("Value to parse may not be null"); 151 } 152 153 if (parser == null) 154 parser = BasicHeaderValueParser.DEFAULT; 155 156 CharArrayBuffer buffer = new CharArrayBuffer(value.length()); 157 buffer.append(value); 158 ParserCursor cursor = new ParserCursor(0, value.length()); 159 return parser.parseHeaderElement(buffer, cursor); 160 } 161 162 163 // non-javadoc, see interface HeaderValueParser 164 public HeaderElement parseHeaderElement(final CharArrayBuffer buffer, 165 final ParserCursor cursor) { 166 167 if (buffer == null) { 168 throw new IllegalArgumentException("Char array buffer may not be null"); 169 } 170 if (cursor == null) { 171 throw new IllegalArgumentException("Parser cursor may not be null"); 172 } 173 174 NameValuePair nvp = parseNameValuePair(buffer, cursor); 175 NameValuePair[] params = null; 176 if (!cursor.atEnd()) { 177 char ch = buffer.charAt(cursor.getPos() - 1); 178 if (ch != ELEM_DELIMITER) { 179 params = parseParameters(buffer, cursor); 180 } 181 } 182 return createHeaderElement(nvp.getName(), nvp.getValue(), params); 183 } 184 185 186 /** 187 * Creates a header element. 188 * Called from {@link #parseHeaderElement}. 189 * 190 * @return a header element representing the argument 191 */ 192 protected HeaderElement createHeaderElement( 193 final String name, 194 final String value, 195 final NameValuePair[] params) { 196 return new BasicHeaderElement(name, value, params); 197 } 198 199 200 /** 201 * Parses parameters with the given parser. 202 * 203 * @param value the parameter list to parse 204 * @param parser the parser to use, or <code>null</code> for default 205 * 206 * @return array holding the parameters, never <code>null</code> 207 */ 208 public final static 209 NameValuePair[] parseParameters(final String value, 210 HeaderValueParser parser) 211 throws ParseException { 212 213 if (value == null) { 214 throw new IllegalArgumentException 215 ("Value to parse may not be null"); 216 } 217 218 if (parser == null) 219 parser = BasicHeaderValueParser.DEFAULT; 220 221 CharArrayBuffer buffer = new CharArrayBuffer(value.length()); 222 buffer.append(value); 223 ParserCursor cursor = new ParserCursor(0, value.length()); 224 return parser.parseParameters(buffer, cursor); 225 } 226 227 228 229 // non-javadoc, see interface HeaderValueParser 230 public NameValuePair[] parseParameters(final CharArrayBuffer buffer, 231 final ParserCursor cursor) { 232 233 if (buffer == null) { 234 throw new IllegalArgumentException("Char array buffer may not be null"); 235 } 236 if (cursor == null) { 237 throw new IllegalArgumentException("Parser cursor may not be null"); 238 } 239 240 int pos = cursor.getPos(); 241 int indexTo = cursor.getUpperBound(); 242 243 while (pos < indexTo) { 244 char ch = buffer.charAt(pos); 245 if (HTTP.isWhitespace(ch)) { 246 pos++; 247 } else { 248 break; 249 } 250 } 251 cursor.updatePos(pos); 252 if (cursor.atEnd()) { 253 return new NameValuePair[] {}; 254 } 255 256 List params = new ArrayList(); 257 while (!cursor.atEnd()) { 258 NameValuePair param = parseNameValuePair(buffer, cursor); 259 params.add(param); 260 char ch = buffer.charAt(cursor.getPos() - 1); 261 if (ch == ELEM_DELIMITER) { 262 break; 263 } 264 } 265 266 return (NameValuePair[]) 267 params.toArray(new NameValuePair[params.size()]); 268 } 269 270 /** 271 * Parses a name-value-pair with the given parser. 272 * 273 * @param value the NVP to parse 274 * @param parser the parser to use, or <code>null</code> for default 275 * 276 * @return the parsed name-value pair 277 */ 278 public final static 279 NameValuePair parseNameValuePair(final String value, 280 HeaderValueParser parser) 281 throws ParseException { 282 283 if (value == null) { 284 throw new IllegalArgumentException 285 ("Value to parse may not be null"); 286 } 287 288 if (parser == null) 289 parser = BasicHeaderValueParser.DEFAULT; 290 291 CharArrayBuffer buffer = new CharArrayBuffer(value.length()); 292 buffer.append(value); 293 ParserCursor cursor = new ParserCursor(0, value.length()); 294 return parser.parseNameValuePair(buffer, cursor); 295 } 296 297 298 // non-javadoc, see interface HeaderValueParser 299 public NameValuePair parseNameValuePair(final CharArrayBuffer buffer, 300 final ParserCursor cursor) { 301 return parseNameValuePair(buffer, cursor, ALL_DELIMITERS); 302 } 303 304 private static boolean isOneOf(final char ch, final char[] chs) { 305 if (chs != null) { 306 for (int i = 0; i < chs.length; i++) { 307 if (ch == chs[i]) { 308 return true; 309 } 310 } 311 } 312 return false; 313 } 314 315 public NameValuePair parseNameValuePair(final CharArrayBuffer buffer, 316 final ParserCursor cursor, 317 final char[] delimiters) { 318 319 if (buffer == null) { 320 throw new IllegalArgumentException("Char array buffer may not be null"); 321 } 322 if (cursor == null) { 323 throw new IllegalArgumentException("Parser cursor may not be null"); 324 } 325 326 boolean terminated = false; 327 328 int pos = cursor.getPos(); 329 int indexFrom = cursor.getPos(); 330 int indexTo = cursor.getUpperBound(); 331 332 // Find name 333 String name = null; 334 while (pos < indexTo) { 335 char ch = buffer.charAt(pos); 336 if (ch == '=') { 337 break; 338 } 339 if (isOneOf(ch, delimiters)) { 340 terminated = true; 341 break; 342 } 343 pos++; 344 } 345 346 if (pos == indexTo) { 347 terminated = true; 348 name = buffer.substringTrimmed(indexFrom, indexTo); 349 } else { 350 name = buffer.substringTrimmed(indexFrom, pos); 351 pos++; 352 } 353 354 if (terminated) { 355 cursor.updatePos(pos); 356 return createNameValuePair(name, null); 357 } 358 359 // Find value 360 String value = null; 361 int i1 = pos; 362 363 boolean qouted = false; 364 boolean escaped = false; 365 while (pos < indexTo) { 366 char ch = buffer.charAt(pos); 367 if (ch == '"' && !escaped) { 368 qouted = !qouted; 369 } 370 if (!qouted && !escaped && isOneOf(ch, delimiters)) { 371 terminated = true; 372 break; 373 } 374 if (escaped) { 375 escaped = false; 376 } else { 377 escaped = qouted && ch == '\\'; 378 } 379 pos++; 380 } 381 382 int i2 = pos; 383 // Trim leading white spaces 384 while (i1 < i2 && (HTTP.isWhitespace(buffer.charAt(i1)))) { 385 i1++; 386 } 387 // Trim trailing white spaces 388 while ((i2 > i1) && (HTTP.isWhitespace(buffer.charAt(i2 - 1)))) { 389 i2--; 390 } 391 // Strip away quotes if necessary 392 if (((i2 - i1) >= 2) 393 && (buffer.charAt(i1) == '"') 394 && (buffer.charAt(i2 - 1) == '"')) { 395 i1++; 396 i2--; 397 } 398 value = buffer.substring(i1, i2); 399 if (terminated) { 400 pos++; 401 } 402 cursor.updatePos(pos); 403 return createNameValuePair(name, value); 404 } 405 406 /** 407 * Creates a name-value pair. 408 * Called from {@link #parseNameValuePair}. 409 * 410 * @param name the name 411 * @param value the value, or <code>null</code> 412 * 413 * @return a name-value pair representing the arguments 414 */ 415 protected NameValuePair createNameValuePair(final String name, final String value) { 416 return new BasicNameValuePair(name, value); 417 } 418 419 } 420 421