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 java.util.List;
31 import java.util.ArrayList;
33 import ch.boye.httpclientandroidlib.HeaderElement;
34 import ch.boye.httpclientandroidlib.NameValuePair;
35 import ch.boye.httpclientandroidlib.ParseException;
36 import ch.boye.httpclientandroidlib.protocol.HTTP;
37 import ch.boye.httpclientandroidlib.util.CharArrayBuffer;
39 /**
40 * Basic implementation for parsing header values into elements.
41 * Instances of this class are stateless and thread-safe.
42 * Derived classes are expected to maintain these properties.
43 *
44 * @since 4.0
45 */
46 public class BasicHeaderValueParser implements HeaderValueParser {
48 /**
49 * A default instance of this class, for use as default or fallback.
50 * Note that {@link BasicHeaderValueParser} is not a singleton, there
51 * can be many instances of the class itself and of derived classes.
52 * The instance here provides non-customized, default behavior.
53 */
54 public final static
55 BasicHeaderValueParser DEFAULT = new BasicHeaderValueParser();
57 private final static char PARAM_DELIMITER = ';';
58 private final static char ELEM_DELIMITER = ',';
59 private final static char[] ALL_DELIMITERS = new char[] {
60 PARAM_DELIMITER,
61 ELEM_DELIMITER
62 };
64 // public default constructor
67 /**
68 * Parses elements with the given parser.
69 *
70 * @param value the header value to parse
71 * @param parser the parser to use, or <code>null</code> for default
72 *
73 * @return array holding the header elements, never <code>null</code>
74 */
75 public final static
76 HeaderElement[] parseElements(final String value,
77 HeaderValueParser parser)
78 throws ParseException {
80 if (value == null) {
81 throw new IllegalArgumentException
82 ("Value to parse may not be null");
83 }
85 if (parser == null)
86 parser = BasicHeaderValueParser.DEFAULT;
88 CharArrayBuffer buffer = new CharArrayBuffer(value.length());
89 buffer.append(value);
90 ParserCursor cursor = new ParserCursor(0, value.length());
91 return parser.parseElements(buffer, cursor);
92 }
95 // non-javadoc, see interface HeaderValueParser
96 public HeaderElement[] parseElements(final CharArrayBuffer buffer,
97 final ParserCursor cursor) {
99 if (buffer == null) {
100 throw new IllegalArgumentException("Char array buffer may not be null");
101 }
102 if (cursor == null) {
103 throw new IllegalArgumentException("Parser cursor may not be null");
104 }
106 List elements = new ArrayList();
107 while (!cursor.atEnd()) {
108 HeaderElement element = parseHeaderElement(buffer, cursor);
109 if (!(element.getName().length() == 0 && element.getValue() == null)) {
110 elements.add(element);
111 }
112 }
113 return (HeaderElement[])
114 elements.toArray(new HeaderElement[elements.size()]);
115 }
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</code> for default
123 *
124 * @return the parsed header element
125 */
126 public final static
127 HeaderElement parseHeaderElement(final String value,
128 HeaderValueParser parser)
129 throws ParseException {
131 if (value == null) {
132 throw new IllegalArgumentException
133 ("Value to parse may not be null");
134 }
136 if (parser == null)
137 parser = BasicHeaderValueParser.DEFAULT;
139 CharArrayBuffer buffer = new CharArrayBuffer(value.length());
140 buffer.append(value);
141 ParserCursor cursor = new ParserCursor(0, value.length());
142 return parser.parseHeaderElement(buffer, cursor);
143 }
146 // non-javadoc, see interface HeaderValueParser
147 public HeaderElement parseHeaderElement(final CharArrayBuffer buffer,
148 final ParserCursor cursor) {
150 if (buffer == null) {
151 throw new IllegalArgumentException("Char array buffer may not be null");
152 }
153 if (cursor == null) {
154 throw new IllegalArgumentException("Parser cursor may not be null");
155 }
157 NameValuePair nvp = parseNameValuePair(buffer, cursor);
158 NameValuePair[] params = null;
159 if (!cursor.atEnd()) {
160 char ch = buffer.charAt(cursor.getPos() - 1);
161 if (ch != ELEM_DELIMITER) {
162 params = parseParameters(buffer, cursor);
163 }
164 }
165 return createHeaderElement(nvp.getName(), nvp.getValue(), params);
166 }
169 /**
170 * Creates a header element.
171 * Called from {@link #parseHeaderElement}.
172 *
173 * @return a header element representing the argument
174 */
175 protected HeaderElement createHeaderElement(
176 final String name,
177 final String value,
178 final NameValuePair[] params) {
179 return new BasicHeaderElement(name, value, params);
180 }
183 /**
184 * Parses parameters with the given parser.
185 *
186 * @param value the parameter list to parse
187 * @param parser the parser to use, or <code>null</code> for default
188 *
189 * @return array holding the parameters, never <code>null</code>
190 */
191 public final static
192 NameValuePair[] parseParameters(final String value,
193 HeaderValueParser parser)
194 throws ParseException {
196 if (value == null) {
197 throw new IllegalArgumentException
198 ("Value to parse may not be null");
199 }
201 if (parser == null)
202 parser = BasicHeaderValueParser.DEFAULT;
204 CharArrayBuffer buffer = new CharArrayBuffer(value.length());
205 buffer.append(value);
206 ParserCursor cursor = new ParserCursor(0, value.length());
207 return parser.parseParameters(buffer, cursor);
208 }
212 // non-javadoc, see interface HeaderValueParser
213 public NameValuePair[] parseParameters(final CharArrayBuffer buffer,
214 final ParserCursor cursor) {
216 if (buffer == null) {
217 throw new IllegalArgumentException("Char array buffer may not be null");
218 }
219 if (cursor == null) {
220 throw new IllegalArgumentException("Parser cursor may not be null");
221 }
223 int pos = cursor.getPos();
224 int indexTo = cursor.getUpperBound();
226 while (pos < indexTo) {
227 char ch = buffer.charAt(pos);
228 if (HTTP.isWhitespace(ch)) {
229 pos++;
230 } else {
231 break;
232 }
233 }
234 cursor.updatePos(pos);
235 if (cursor.atEnd()) {
236 return new NameValuePair[] {};
237 }
239 List params = new ArrayList();
240 while (!cursor.atEnd()) {
241 NameValuePair param = parseNameValuePair(buffer, cursor);
242 params.add(param);
243 char ch = buffer.charAt(cursor.getPos() - 1);
244 if (ch == ELEM_DELIMITER) {
245 break;
246 }
247 }
249 return (NameValuePair[])
250 params.toArray(new NameValuePair[params.size()]);
251 }
253 /**
254 * Parses a name-value-pair with the given parser.
255 *
256 * @param value the NVP to parse
257 * @param parser the parser to use, or <code>null</code> for default
258 *
259 * @return the parsed name-value pair
260 */
261 public final static
262 NameValuePair parseNameValuePair(final String value,
263 HeaderValueParser parser)
264 throws ParseException {
266 if (value == null) {
267 throw new IllegalArgumentException
268 ("Value to parse may not be null");
269 }
271 if (parser == null)
272 parser = BasicHeaderValueParser.DEFAULT;
274 CharArrayBuffer buffer = new CharArrayBuffer(value.length());
275 buffer.append(value);
276 ParserCursor cursor = new ParserCursor(0, value.length());
277 return parser.parseNameValuePair(buffer, cursor);
278 }
281 // non-javadoc, see interface HeaderValueParser
282 public NameValuePair parseNameValuePair(final CharArrayBuffer buffer,
283 final ParserCursor cursor) {
284 return parseNameValuePair(buffer, cursor, ALL_DELIMITERS);
285 }
287 private static boolean isOneOf(final char ch, final char[] chs) {
288 if (chs != null) {
289 for (int i = 0; i < chs.length; i++) {
290 if (ch == chs[i]) {
291 return true;
292 }
293 }
294 }
295 return false;
296 }
298 public NameValuePair parseNameValuePair(final CharArrayBuffer buffer,
299 final ParserCursor cursor,
300 final char[] delimiters) {
302 if (buffer == null) {
303 throw new IllegalArgumentException("Char array buffer may not be null");
304 }
305 if (cursor == null) {
306 throw new IllegalArgumentException("Parser cursor may not be null");
307 }
309 boolean terminated = false;
311 int pos = cursor.getPos();
312 int indexFrom = cursor.getPos();
313 int indexTo = cursor.getUpperBound();
315 // Find name
316 String name = null;
317 while (pos < indexTo) {
318 char ch = buffer.charAt(pos);
319 if (ch == '=') {
320 break;
321 }
322 if (isOneOf(ch, delimiters)) {
323 terminated = true;
324 break;
325 }
326 pos++;
327 }
329 if (pos == indexTo) {
330 terminated = true;
331 name = buffer.substringTrimmed(indexFrom, indexTo);
332 } else {
333 name = buffer.substringTrimmed(indexFrom, pos);
334 pos++;
335 }
337 if (terminated) {
338 cursor.updatePos(pos);
339 return createNameValuePair(name, null);
340 }
342 // Find value
343 String value = null;
344 int i1 = pos;
346 boolean qouted = false;
347 boolean escaped = false;
348 while (pos < indexTo) {
349 char ch = buffer.charAt(pos);
350 if (ch == '"' && !escaped) {
351 qouted = !qouted;
352 }
353 if (!qouted && !escaped && isOneOf(ch, delimiters)) {
354 terminated = true;
355 break;
356 }
357 if (escaped) {
358 escaped = false;
359 } else {
360 escaped = qouted && ch == '\\';
361 }
362 pos++;
363 }
365 int i2 = pos;
366 // Trim leading white spaces
367 while (i1 < i2 && (HTTP.isWhitespace(buffer.charAt(i1)))) {
368 i1++;
369 }
370 // Trim trailing white spaces
371 while ((i2 > i1) && (HTTP.isWhitespace(buffer.charAt(i2 - 1)))) {
372 i2--;
373 }
374 // Strip away quotes if necessary
375 if (((i2 - i1) >= 2)
376 && (buffer.charAt(i1) == '"')
377 && (buffer.charAt(i2 - 1) == '"')) {
378 i1++;
379 i2--;
380 }
381 value = buffer.substring(i1, i2);
382 if (terminated) {
383 pos++;
384 }
385 cursor.updatePos(pos);
386 return createNameValuePair(name, value);
387 }
389 /**
390 * Creates a name-value pair.
391 * Called from {@link #parseNameValuePair}.
392 *
393 * @param name the name
394 * @param value the value, or <code>null</code>
395 *
396 * @return a name-value pair representing the arguments
397 */
398 protected NameValuePair createNameValuePair(final String name, final String value) {
399 return new BasicNameValuePair(name, value);
400 }
402 }