001/* 002 * ==================================================================== 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, 014 * software distributed under the License is distributed on an 015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 016 * KIND, either express or implied. See the License for the 017 * specific language governing permissions and limitations 018 * under the License. 019 * ==================================================================== 020 * 021 * This software consists of voluntary contributions made by many 022 * individuals on behalf of the Apache Software Foundation. For more 023 * information on the Apache Software Foundation, please see 024 * <http://www.apache.org/>. 025 * 026 */ 027 028package org.apache.http.message; 029 030import java.util.ArrayList; 031import java.util.BitSet; 032import java.util.List; 033 034import org.apache.http.HeaderElement; 035import org.apache.http.NameValuePair; 036import org.apache.http.ParseException; 037import org.apache.http.annotation.ThreadingBehavior; 038import org.apache.http.annotation.Contract; 039import org.apache.http.util.Args; 040import org.apache.http.util.CharArrayBuffer; 041 042/** 043 * Basic implementation for parsing header values into elements. 044 * Instances of this class are stateless and thread-safe. 045 * Derived classes are expected to maintain these properties. 046 * 047 * @since 4.0 048 */ 049@Contract(threading = ThreadingBehavior.IMMUTABLE) 050public class BasicHeaderValueParser implements HeaderValueParser { 051 052 /** 053 * A default instance of this class, for use as default or fallback. 054 * Note that {@link BasicHeaderValueParser} is not a singleton, there 055 * can be many instances of the class itself and of derived classes. 056 * The instance here provides non-customized, default behavior. 057 * 058 * @deprecated (4.3) use {@link #INSTANCE} 059 */ 060 @Deprecated 061 public final static BasicHeaderValueParser DEFAULT = new BasicHeaderValueParser(); 062 063 public final static BasicHeaderValueParser INSTANCE = new BasicHeaderValueParser(); 064 065 private final static char PARAM_DELIMITER = ';'; 066 private final static char ELEM_DELIMITER = ','; 067 068 // IMPORTANT! 069 // These private static variables must be treated as immutable and never exposed outside this class 070 private static final BitSet TOKEN_DELIMS = TokenParser.INIT_BITSET('=', PARAM_DELIMITER, ELEM_DELIMITER); 071 private static final BitSet VALUE_DELIMS = TokenParser.INIT_BITSET(PARAM_DELIMITER, ELEM_DELIMITER); 072 073 private final TokenParser tokenParser; 074 075 public BasicHeaderValueParser() { 076 this.tokenParser = TokenParser.INSTANCE; 077 } 078 079 /** 080 * Parses elements with the given parser. 081 * 082 * @param value the header value to parse 083 * @param parser the parser to use, or {@code null} for default 084 * 085 * @return array holding the header elements, never {@code null} 086 * @throws ParseException in case of a parsing error 087 */ 088 public static 089 HeaderElement[] parseElements(final String value, 090 final HeaderValueParser parser) throws ParseException { 091 Args.notNull(value, "Value"); 092 093 final CharArrayBuffer buffer = new CharArrayBuffer(value.length()); 094 buffer.append(value); 095 final ParserCursor cursor = new ParserCursor(0, value.length()); 096 return (parser != null ? parser : BasicHeaderValueParser.INSTANCE) 097 .parseElements(buffer, cursor); 098 } 099 100 101 // non-javadoc, see interface HeaderValueParser 102 @Override 103 public HeaderElement[] parseElements(final CharArrayBuffer buffer, 104 final ParserCursor cursor) { 105 Args.notNull(buffer, "Char array buffer"); 106 Args.notNull(cursor, "Parser cursor"); 107 final List<HeaderElement> elements = new ArrayList<HeaderElement>(); 108 while (!cursor.atEnd()) { 109 final HeaderElement element = parseHeaderElement(buffer, cursor); 110 if (!(element.getName().length() == 0 && element.getValue() == null)) { 111 elements.add(element); 112 } 113 } 114 return elements.toArray(new HeaderElement[elements.size()]); 115 } 116 117 118 /** 119 * Parses an element with the given parser. 120 * 121 * @param value the header element to parse 122 * @param parser the parser to use, or {@code null} for default 123 * 124 * @return the parsed header element 125 */ 126 public static 127 HeaderElement parseHeaderElement(final String value, 128 final HeaderValueParser parser) throws ParseException { 129 Args.notNull(value, "Value"); 130 131 final CharArrayBuffer buffer = new CharArrayBuffer(value.length()); 132 buffer.append(value); 133 final ParserCursor cursor = new ParserCursor(0, value.length()); 134 return (parser != null ? parser : BasicHeaderValueParser.INSTANCE) 135 .parseHeaderElement(buffer, cursor); 136 } 137 138 139 // non-javadoc, see interface HeaderValueParser 140 @Override 141 public HeaderElement parseHeaderElement(final CharArrayBuffer buffer, 142 final ParserCursor cursor) { 143 Args.notNull(buffer, "Char array buffer"); 144 Args.notNull(cursor, "Parser cursor"); 145 final NameValuePair nvp = parseNameValuePair(buffer, cursor); 146 NameValuePair[] params = null; 147 if (!cursor.atEnd()) { 148 final char ch = buffer.charAt(cursor.getPos() - 1); 149 if (ch != ELEM_DELIMITER) { 150 params = parseParameters(buffer, cursor); 151 } 152 } 153 return createHeaderElement(nvp.getName(), nvp.getValue(), params); 154 } 155 156 157 /** 158 * Creates a header element. 159 * Called from {@link #parseHeaderElement}. 160 * 161 * @return a header element representing the argument 162 */ 163 protected HeaderElement createHeaderElement( 164 final String name, 165 final String value, 166 final NameValuePair[] params) { 167 return new BasicHeaderElement(name, value, params); 168 } 169 170 171 /** 172 * Parses parameters with the given parser. 173 * 174 * @param value the parameter list to parse 175 * @param parser the parser to use, or {@code null} for default 176 * 177 * @return array holding the parameters, never {@code null} 178 */ 179 public static 180 NameValuePair[] parseParameters(final String value, 181 final HeaderValueParser parser) throws ParseException { 182 Args.notNull(value, "Value"); 183 184 final CharArrayBuffer buffer = new CharArrayBuffer(value.length()); 185 buffer.append(value); 186 final ParserCursor cursor = new ParserCursor(0, value.length()); 187 return (parser != null ? parser : BasicHeaderValueParser.INSTANCE) 188 .parseParameters(buffer, cursor); 189 } 190 191 192 193 // non-javadoc, see interface HeaderValueParser 194 @Override 195 public NameValuePair[] parseParameters(final CharArrayBuffer buffer, 196 final ParserCursor cursor) { 197 Args.notNull(buffer, "Char array buffer"); 198 Args.notNull(cursor, "Parser cursor"); 199 tokenParser.skipWhiteSpace(buffer, cursor); 200 final List<NameValuePair> params = new ArrayList<NameValuePair>(); 201 while (!cursor.atEnd()) { 202 final NameValuePair param = parseNameValuePair(buffer, cursor); 203 params.add(param); 204 final char ch = buffer.charAt(cursor.getPos() - 1); 205 if (ch == ELEM_DELIMITER) { 206 break; 207 } 208 } 209 return params.toArray(new NameValuePair[params.size()]); 210 } 211 212 /** 213 * Parses a name-value-pair with the given parser. 214 * 215 * @param value the NVP to parse 216 * @param parser the parser to use, or {@code null} for default 217 * 218 * @return the parsed name-value pair 219 */ 220 public static 221 NameValuePair parseNameValuePair(final String value, 222 final HeaderValueParser parser) throws ParseException { 223 Args.notNull(value, "Value"); 224 225 final CharArrayBuffer buffer = new CharArrayBuffer(value.length()); 226 buffer.append(value); 227 final ParserCursor cursor = new ParserCursor(0, value.length()); 228 return (parser != null ? parser : BasicHeaderValueParser.INSTANCE) 229 .parseNameValuePair(buffer, cursor); 230 } 231 232 233 // non-javadoc, see interface HeaderValueParser 234 @Override 235 public NameValuePair parseNameValuePair(final CharArrayBuffer buffer, 236 final ParserCursor cursor) { 237 Args.notNull(buffer, "Char array buffer"); 238 Args.notNull(cursor, "Parser cursor"); 239 240 final String name = tokenParser.parseToken(buffer, cursor, TOKEN_DELIMS); 241 if (cursor.atEnd()) { 242 return new BasicNameValuePair(name, null); 243 } 244 final int delim = buffer.charAt(cursor.getPos()); 245 cursor.updatePos(cursor.getPos() + 1); 246 if (delim != '=') { 247 return createNameValuePair(name, null); 248 } 249 final String value = tokenParser.parseValue(buffer, cursor, VALUE_DELIMS); 250 if (!cursor.atEnd()) { 251 cursor.updatePos(cursor.getPos() + 1); 252 } 253 return createNameValuePair(name, value); 254 } 255 256 /** 257 * @deprecated (4.4) use {@link org.apache.http.message.TokenParser} 258 */ 259 @Deprecated 260 public NameValuePair parseNameValuePair(final CharArrayBuffer buffer, 261 final ParserCursor cursor, 262 final char[] delimiters) { 263 Args.notNull(buffer, "Char array buffer"); 264 Args.notNull(cursor, "Parser cursor"); 265 266 final BitSet delimSet = new BitSet(); 267 if (delimiters != null) { 268 for (final char delimiter: delimiters) { 269 delimSet.set(delimiter); 270 } 271 } 272 delimSet.set('='); 273 final String name = tokenParser.parseToken(buffer, cursor, delimSet); 274 if (cursor.atEnd()) { 275 return new BasicNameValuePair(name, null); 276 } 277 final int delim = buffer.charAt(cursor.getPos()); 278 cursor.updatePos(cursor.getPos() + 1); 279 if (delim != '=') { 280 return createNameValuePair(name, null); 281 } 282 delimSet.clear('='); 283 final String value = tokenParser.parseValue(buffer, cursor, delimSet); 284 if (!cursor.atEnd()) { 285 cursor.updatePos(cursor.getPos() + 1); 286 } 287 return createNameValuePair(name, value); 288 } 289 290 /** 291 * Creates a name-value pair. 292 * Called from {@link #parseNameValuePair}. 293 * 294 * @param name the name 295 * @param value the value, or {@code null} 296 * 297 * @return a name-value pair representing the arguments 298 */ 299 protected NameValuePair createNameValuePair(final String name, final String value) { 300 return new BasicNameValuePair(name, value); 301 } 302 303} 304