/**
   * Parses elements with the given parser.
   *
   * @param value the header value to parse
   * @param parser the parser to use, or <code>null</code> for default
   * @return array holding the header elements, never <code>null</code>
   */
  public static final HeaderElement[] parseElements(final String value, HeaderValueParser parser)
      throws ParseException {

    if (value == null) {
      throw new IllegalArgumentException("Value to parse may not be null");
    }

    if (parser == null) parser = BasicHeaderValueParser.DEFAULT;

    CharArrayBuffer buffer = new CharArrayBuffer(value.length());
    buffer.append(value);
    ParserCursor cursor = new ParserCursor(0, value.length());
    return parser.parseElements(buffer, cursor);
  }
  // non-javadoc, see interface HeaderValueParser
  public NameValuePair[] parseParameters(final CharArrayBuffer buffer, final ParserCursor cursor) {

    if (buffer == null) {
      throw new IllegalArgumentException("Char array buffer may not be null");
    }
    if (cursor == null) {
      throw new IllegalArgumentException("Parser cursor may not be null");
    }

    int pos = cursor.getPos();
    int indexTo = cursor.getUpperBound();

    while (pos < indexTo) {
      char ch = buffer.charAt(pos);
      if (HTTP.isWhitespace(ch)) {
        pos++;
      } else {
        break;
      }
    }
    cursor.updatePos(pos);
    if (cursor.atEnd()) {
      return new NameValuePair[] {};
    }

    List params = new ArrayList();
    while (!cursor.atEnd()) {
      NameValuePair param = parseNameValuePair(buffer, cursor);
      params.add(param);
      char ch = buffer.charAt(cursor.getPos() - 1);
      if (ch == ELEM_DELIMITER) {
        break;
      }
    }

    return (NameValuePair[]) params.toArray(new NameValuePair[params.size()]);
  }
  // non-javadoc, see interface HeaderValueParser
  public HeaderElement parseHeaderElement(final CharArrayBuffer buffer, final ParserCursor cursor) {

    if (buffer == null) {
      throw new IllegalArgumentException("Char array buffer may not be null");
    }
    if (cursor == null) {
      throw new IllegalArgumentException("Parser cursor may not be null");
    }

    NameValuePair nvp = parseNameValuePair(buffer, cursor);
    NameValuePair[] params = null;
    if (!cursor.atEnd()) {
      char ch = buffer.charAt(cursor.getPos() - 1);
      if (ch != ELEM_DELIMITER) {
        params = parseParameters(buffer, cursor);
      }
    }
    return createHeaderElement(nvp.getName(), nvp.getValue(), params);
  }
  public NameValuePair parseNameValuePair(
      final CharArrayBuffer buffer, final ParserCursor cursor, final char[] delimiters) {

    if (buffer == null) {
      throw new IllegalArgumentException("Char array buffer may not be null");
    }
    if (cursor == null) {
      throw new IllegalArgumentException("Parser cursor may not be null");
    }

    boolean terminated = false;

    int pos = cursor.getPos();
    int indexFrom = cursor.getPos();
    int indexTo = cursor.getUpperBound();

    // Find name
    String name = null;
    while (pos < indexTo) {
      char ch = buffer.charAt(pos);
      if (ch == '=') {
        break;
      }
      if (isOneOf(ch, delimiters)) {
        terminated = true;
        break;
      }
      pos++;
    }

    if (pos == indexTo) {
      terminated = true;
      name = buffer.substringTrimmed(indexFrom, indexTo);
    } else {
      name = buffer.substringTrimmed(indexFrom, pos);
      pos++;
    }

    if (terminated) {
      cursor.updatePos(pos);
      return createNameValuePair(name, null);
    }

    // Find value
    String value = null;
    int i1 = pos;

    boolean qouted = false;
    boolean escaped = false;
    while (pos < indexTo) {
      char ch = buffer.charAt(pos);
      if (ch == '"' && !escaped) {
        qouted = !qouted;
      }
      if (!qouted && !escaped && isOneOf(ch, delimiters)) {
        terminated = true;
        break;
      }
      if (escaped) {
        escaped = false;
      } else {
        escaped = qouted && ch == '\\';
      }
      pos++;
    }

    int i2 = pos;
    // Trim leading white spaces
    while (i1 < i2 && (HTTP.isWhitespace(buffer.charAt(i1)))) {
      i1++;
    }
    // Trim trailing white spaces
    while ((i2 > i1) && (HTTP.isWhitespace(buffer.charAt(i2 - 1)))) {
      i2--;
    }
    // Strip away quotes if necessary
    if (((i2 - i1) >= 2) && (buffer.charAt(i1) == '"') && (buffer.charAt(i2 - 1) == '"')) {
      i1++;
      i2--;
    }
    value = buffer.substring(i1, i2);
    if (terminated) {
      pos++;
    }
    cursor.updatePos(pos);
    return createNameValuePair(name, value);
  }