001: /*
002: * $HeadURL: https://svn.apache.org/repos/asf/httpcomponents/httpcore/tags/4.0-beta1/module-main/src/main/java/org/apache/http/message/BasicHeaderValueParser.java $
003: * $Revision: 595670 $
004: * $Date: 2007-11-16 15:15:01 +0100 (Fri, 16 Nov 2007) $
005: *
006: * ====================================================================
007: * Licensed to the Apache Software Foundation (ASF) under one
008: * or more contributor license agreements. See the NOTICE file
009: * distributed with this work for additional information
010: * regarding copyright ownership. The ASF licenses this file
011: * to you under the Apache License, Version 2.0 (the
012: * "License"); you may not use this file except in compliance
013: * with the License. You may obtain a copy of the License at
014: *
015: * http://www.apache.org/licenses/LICENSE-2.0
016: *
017: * Unless required by applicable law or agreed to in writing,
018: * software distributed under the License is distributed on an
019: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
020: * KIND, either express or implied. See the License for the
021: * specific language governing permissions and limitations
022: * under the License.
023: * ====================================================================
024: *
025: * This software consists of voluntary contributions made by many
026: * individuals on behalf of the Apache Software Foundation. For more
027: * information on the Apache Software Foundation, please see
028: * <http://www.apache.org/>.
029: *
030: */
031:
032: package org.apache.http.message;
033:
034: import java.util.List;
035: import java.util.ArrayList;
036:
037: import org.apache.http.HeaderElement;
038: import org.apache.http.NameValuePair;
039: import org.apache.http.ParseException;
040: import org.apache.http.protocol.HTTP;
041: import org.apache.http.util.CharArrayBuffer;
042:
043: /**
044: * Basic implementation for parsing header values into elements.
045: * Instances of this class are stateless and thread-safe.
046: * Derived classes are expected to maintain these properties.
047: *
048: * @author <a href="mailto:bcholmes@interlog.com">B.C. Holmes</a>
049: * @author <a href="mailto:jericho@thinkfree.com">Park, Sung-Gu</a>
050: * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
051: * @author <a href="mailto:oleg at ural.com">Oleg Kalnichevski</a>
052: * @author and others
053: *
054: *
055: * <!-- empty lines above to avoid 'svn diff' context problems -->
056: * @version $Revision: 595670 $
057: *
058: * @since 4.0
059: */
060: public class BasicHeaderValueParser implements HeaderValueParser {
061:
062: /**
063: * A default instance of this class, for use as default or fallback.
064: * Note that {@link BasicHeaderValueParser} is not a singleton, there
065: * can be many instances of the class itself and of derived classes.
066: * The instance here provides non-customized, default behavior.
067: */
068: public final static BasicHeaderValueParser DEFAULT = new BasicHeaderValueParser();
069:
070: private final static char PARAM_DELIMITER = ';';
071: private final static char ELEM_DELIMITER = ',';
072: private final static char[] ALL_DELIMITERS = new char[] {
073: PARAM_DELIMITER, ELEM_DELIMITER };
074:
075: // public default constructor
076:
077: /**
078: * Parses elements with the given parser.
079: *
080: * @param value the header value to parse
081: * @param parser the parser to use, or <code>null</code> for default
082: *
083: * @return array holding the header elements, never <code>null</code>
084: */
085: public final static HeaderElement[] parseElements(
086: final String value, HeaderValueParser parser)
087: throws ParseException {
088:
089: if (value == null) {
090: throw new IllegalArgumentException(
091: "Value to parse may not be null");
092: }
093:
094: if (parser == null)
095: parser = BasicHeaderValueParser.DEFAULT;
096:
097: CharArrayBuffer buffer = new CharArrayBuffer(value.length());
098: buffer.append(value);
099: ParserCursor cursor = new ParserCursor(0, value.length());
100: return parser.parseElements(buffer, cursor);
101: }
102:
103: // non-javadoc, see interface HeaderValueParser
104: public HeaderElement[] parseElements(final CharArrayBuffer buffer,
105: final ParserCursor cursor) {
106:
107: if (buffer == null) {
108: throw new IllegalArgumentException(
109: "Char array buffer may not be null");
110: }
111: if (cursor == null) {
112: throw new IllegalArgumentException(
113: "Parser cursor may not be null");
114: }
115:
116: List elements = new ArrayList();
117: while (!cursor.atEnd()) {
118: HeaderElement element = parseHeaderElement(buffer, cursor);
119: if (!(element.getName().length() == 0 && element.getValue() == null)) {
120: elements.add(element);
121: }
122: }
123: return (HeaderElement[]) elements
124: .toArray(new HeaderElement[elements.size()]);
125: }
126:
127: /**
128: * Parses an element with the given parser.
129: *
130: * @param value the header element to parse
131: * @param parser the parser to use, or <code>null</code> for default
132: *
133: * @return the parsed header element
134: */
135: public final static HeaderElement parseHeaderElement(
136: final String value, HeaderValueParser parser)
137: throws ParseException {
138:
139: if (value == null) {
140: throw new IllegalArgumentException(
141: "Value to parse may not be null");
142: }
143:
144: if (parser == null)
145: parser = BasicHeaderValueParser.DEFAULT;
146:
147: CharArrayBuffer buffer = new CharArrayBuffer(value.length());
148: buffer.append(value);
149: ParserCursor cursor = new ParserCursor(0, value.length());
150: return parser.parseHeaderElement(buffer, cursor);
151: }
152:
153: // non-javadoc, see interface HeaderValueParser
154: public HeaderElement parseHeaderElement(
155: final CharArrayBuffer buffer, final ParserCursor cursor) {
156:
157: if (buffer == null) {
158: throw new IllegalArgumentException(
159: "Char array buffer may not be null");
160: }
161: if (cursor == null) {
162: throw new IllegalArgumentException(
163: "Parser cursor may not be null");
164: }
165:
166: NameValuePair nvp = parseNameValuePair(buffer, cursor);
167: NameValuePair[] params = null;
168: if (!cursor.atEnd()) {
169: char ch = buffer.charAt(cursor.getPos() - 1);
170: if (ch != ELEM_DELIMITER) {
171: params = parseParameters(buffer, cursor);
172: }
173: }
174: return createHeaderElement(nvp.getName(), nvp.getValue(),
175: params);
176: }
177:
178: /**
179: * Creates a header element.
180: * Called from {@link #parseHeaderElement}.
181: *
182: * @return a header element representing the argument
183: */
184: protected HeaderElement createHeaderElement(final String name,
185: final String value, final NameValuePair[] params) {
186: return new BasicHeaderElement(name, value, params);
187: }
188:
189: /**
190: * Parses parameters with the given parser.
191: *
192: * @param value the parameter list to parse
193: * @param parser the parser to use, or <code>null</code> for default
194: *
195: * @return array holding the parameters, never <code>null</code>
196: */
197: public final static NameValuePair[] parseParameters(
198: final String value, HeaderValueParser parser)
199: throws ParseException {
200:
201: if (value == null) {
202: throw new IllegalArgumentException(
203: "Value to parse may not be null");
204: }
205:
206: if (parser == null)
207: parser = BasicHeaderValueParser.DEFAULT;
208:
209: CharArrayBuffer buffer = new CharArrayBuffer(value.length());
210: buffer.append(value);
211: ParserCursor cursor = new ParserCursor(0, value.length());
212: return parser.parseParameters(buffer, cursor);
213: }
214:
215: // non-javadoc, see interface HeaderValueParser
216: public NameValuePair[] parseParameters(
217: final CharArrayBuffer buffer, final ParserCursor cursor) {
218:
219: if (buffer == null) {
220: throw new IllegalArgumentException(
221: "Char array buffer may not be null");
222: }
223: if (cursor == null) {
224: throw new IllegalArgumentException(
225: "Parser cursor may not be null");
226: }
227:
228: int pos = cursor.getPos();
229: int indexTo = cursor.getUpperBound();
230:
231: while (pos < indexTo) {
232: char ch = buffer.charAt(pos);
233: if (HTTP.isWhitespace(ch)) {
234: pos++;
235: } else {
236: break;
237: }
238: }
239: cursor.updatePos(pos);
240: if (cursor.atEnd()) {
241: return new NameValuePair[] {};
242: }
243:
244: List params = new ArrayList();
245: while (!cursor.atEnd()) {
246: NameValuePair param = parseNameValuePair(buffer, cursor);
247: params.add(param);
248: char ch = buffer.charAt(cursor.getPos() - 1);
249: if (ch == ELEM_DELIMITER) {
250: break;
251: }
252: }
253:
254: return (NameValuePair[]) params
255: .toArray(new NameValuePair[params.size()]);
256: }
257:
258: /**
259: * Parses a name-value-pair with the given parser.
260: *
261: * @param value the NVP to parse
262: * @param parser the parser to use, or <code>null</code> for default
263: *
264: * @return the parsed name-value pair
265: */
266: public final static NameValuePair parseNameValuePair(
267: final String value, HeaderValueParser parser)
268: throws ParseException {
269:
270: if (value == null) {
271: throw new IllegalArgumentException(
272: "Value to parse may not be null");
273: }
274:
275: if (parser == null)
276: parser = BasicHeaderValueParser.DEFAULT;
277:
278: CharArrayBuffer buffer = new CharArrayBuffer(value.length());
279: buffer.append(value);
280: ParserCursor cursor = new ParserCursor(0, value.length());
281: return parser.parseNameValuePair(buffer, cursor);
282: }
283:
284: // non-javadoc, see interface HeaderValueParser
285: public NameValuePair parseNameValuePair(
286: final CharArrayBuffer buffer, final ParserCursor cursor) {
287: return parseNameValuePair(buffer, cursor, ALL_DELIMITERS);
288: }
289:
290: private static boolean isOneOf(final char ch, final char[] chs) {
291: if (chs != null) {
292: for (int i = 0; i < chs.length; i++) {
293: if (ch == chs[i]) {
294: return true;
295: }
296: }
297: }
298: return false;
299: }
300:
301: public NameValuePair parseNameValuePair(
302: final CharArrayBuffer buffer, final ParserCursor cursor,
303: final char[] delimiters) {
304:
305: if (buffer == null) {
306: throw new IllegalArgumentException(
307: "Char array buffer may not be null");
308: }
309: if (cursor == null) {
310: throw new IllegalArgumentException(
311: "Parser cursor may not be null");
312: }
313:
314: boolean terminated = false;
315:
316: int pos = cursor.getPos();
317: int indexFrom = cursor.getPos();
318: int indexTo = cursor.getUpperBound();
319:
320: // Find name
321: String name = null;
322: while (pos < indexTo) {
323: char ch = buffer.charAt(pos);
324: if (ch == '=') {
325: break;
326: }
327: if (isOneOf(ch, delimiters)) {
328: terminated = true;
329: break;
330: }
331: pos++;
332: }
333:
334: if (pos == indexTo) {
335: terminated = true;
336: name = buffer.substringTrimmed(indexFrom, indexTo);
337: } else {
338: name = buffer.substringTrimmed(indexFrom, pos);
339: pos++;
340: }
341:
342: if (terminated) {
343: cursor.updatePos(pos);
344: return createNameValuePair(name, null);
345: }
346:
347: // Find value
348: String value = null;
349: int i1 = pos;
350:
351: boolean qouted = false;
352: boolean escaped = false;
353: while (pos < indexTo) {
354: char ch = buffer.charAt(pos);
355: if (ch == '"' && !escaped) {
356: qouted = !qouted;
357: }
358: if (!qouted && !escaped && isOneOf(ch, delimiters)) {
359: terminated = true;
360: break;
361: }
362: if (escaped) {
363: escaped = false;
364: } else {
365: escaped = qouted && ch == '\\';
366: }
367: pos++;
368: }
369:
370: int i2 = pos;
371: // Trim leading white spaces
372: while (i1 < i2 && (HTTP.isWhitespace(buffer.charAt(i1)))) {
373: i1++;
374: }
375: // Trim trailing white spaces
376: while ((i2 > i1) && (HTTP.isWhitespace(buffer.charAt(i2 - 1)))) {
377: i2--;
378: }
379: // Strip away quotes if necessary
380: if (((i2 - i1) >= 2) && (buffer.charAt(i1) == '"')
381: && (buffer.charAt(i2 - 1) == '"')) {
382: i1++;
383: i2--;
384: }
385: value = buffer.substring(i1, i2);
386: if (terminated) {
387: pos++;
388: }
389: cursor.updatePos(pos);
390: return createNameValuePair(name, value);
391: }
392:
393: /**
394: * Creates a name-value pair.
395: * Called from {@link #parseNameValuePair}.
396: *
397: * @param name the name
398: * @param value the value, or <code>null</code>
399: *
400: * @return a name-value pair representing the arguments
401: */
402: protected NameValuePair createNameValuePair(final String name,
403: final String value) {
404: return new BasicNameValuePair(name, value);
405: }
406:
407: }
|