Index: java/org/apache/commons/httpclient/HeaderElement.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HeaderElement.java,v retrieving revision 1.18 diff -u -r1.18 HeaderElement.java --- java/org/apache/commons/httpclient/HeaderElement.java 14 Apr 2003 04:06:55 -0000 1.18 +++ java/org/apache/commons/httpclient/HeaderElement.java 12 Jul 2003 14:35:52 -0000 @@ -63,15 +63,13 @@ package org.apache.commons.httpclient; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.httpclient.util.ParameterParser; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import java.util.BitSet; -import java.util.NoSuchElementException; -import java.util.StringTokenizer; -import java.util.Vector; - - /** *
One element of an HTTP header's value.
*
@@ -114,6 +112,7 @@
* @author B.C. Holmes
* @author Park, Sung-Gu
* @author Mike Bowler
+ * @author Oleg Kalnichevski
*
* @since 1.0
* @version $Revision: 1.18 $ $Date: 2003/04/14 04:06:55 $
@@ -148,87 +147,47 @@
public HeaderElement(String name, String value,
NameValuePair[] parameters) {
super(name, value);
- setParameters(parameters);
+ this.parameters = parameters;
}
- // -------------------------------------------------------- Constants
-
- /** Log object for this class. */
- private static final Log LOG = LogFactory.getLog(HeaderElement.class);
-
/**
- * Map of numeric values to whether or not the
- * corresponding character is a "separator
- * character" (tspecial).
- */
- private static final BitSet SEPARATORS = new BitSet(128);
-
- /**
- * Map of numeric values to whether or not the
- * corresponding character is a "token
- * character".
- */
- private static final BitSet TOKEN_CHAR = new BitSet(128);
+ * Constructor with array of characters.
+ *
+ * @param chars the array of characters
+ * @param offset - the initial offset.
+ * @param length - the length.
+ */
+ public HeaderElement(char[] chars, int offset, int length) {
+ this();
+ if (chars == null) {
+ return;
+ }
+ ParameterParser parser = new ParameterParser();
+ List params = parser.parse(chars, offset, length, ';');
+ if (params.size() > 0) {
+ NameValuePair element = (NameValuePair)params.remove(0);
+ setName(element.getName());
+ setValue(element.getValue());
+ if (params.size() > 0) {
+ this.parameters = (NameValuePair[])
+ params.toArray(new NameValuePair[params.size()]);
+ }
+ }
+ }
/**
- * Map of numeric values to whether or not the
- * corresponding character is an "unsafe
- * character".
+ * Constructor with array of characters.
+ *
+ * @param chars the array of characters
*/
- private static final BitSet UNSAFE_CHAR = new BitSet(128);
+ public HeaderElement(char[] chars) {
+ this(chars, 0, chars.length);
+ }
- /**
- * Static initializer for {@link #SEPARATORS},
- * {@link #TOKEN_CHAR}, and {@link #UNSAFE_CHAR}.
- */
- static {
- // rfc-2068 tspecial
- SEPARATORS.set('(');
- SEPARATORS.set(')');
- SEPARATORS.set('<');
- SEPARATORS.set('>');
- SEPARATORS.set('@');
- SEPARATORS.set(',');
- SEPARATORS.set(';');
- SEPARATORS.set(':');
- SEPARATORS.set('\\');
- SEPARATORS.set('"');
- SEPARATORS.set('/');
- SEPARATORS.set('[');
- SEPARATORS.set(']');
- SEPARATORS.set('?');
- SEPARATORS.set('=');
- SEPARATORS.set('{');
- SEPARATORS.set('}');
- SEPARATORS.set(' ');
- SEPARATORS.set('\t');
-
- // rfc-2068 token
- for (int ch = 32; ch < 127; ch++) {
- TOKEN_CHAR.set(ch);
- }
- TOKEN_CHAR.xor(SEPARATORS);
+ // -------------------------------------------------------- Constants
- // rfc-1738 unsafe characters, including CTL and SP, and excluding
- // "#" and "%"
- for (int ch = 0; ch < 32; ch++) {
- UNSAFE_CHAR.set(ch);
- }
- UNSAFE_CHAR.set(' ');
- UNSAFE_CHAR.set('<');
- UNSAFE_CHAR.set('>');
- UNSAFE_CHAR.set('"');
- UNSAFE_CHAR.set('{');
- UNSAFE_CHAR.set('}');
- UNSAFE_CHAR.set('|');
- UNSAFE_CHAR.set('\\');
- UNSAFE_CHAR.set('^');
- UNSAFE_CHAR.set('~');
- UNSAFE_CHAR.set('[');
- UNSAFE_CHAR.set(']');
- UNSAFE_CHAR.set('`');
- UNSAFE_CHAR.set(127);
- }
+ /** Log object for this class. */
+ private static final Log LOG = LogFactory.getLog(HeaderElement.class);
// ----------------------------------------------------- Instance Variables
@@ -247,187 +206,91 @@
return this.parameters;
}
- /**
- *
- * @param pairs The new parameters. May be null.
- */
- protected void setParameters(final NameValuePair[] pairs) {
- parameters = pairs;
- }
// --------------------------------------------------------- Public Methods
/**
* This parses the value part of a header. The result is an array of
* HeaderElement objects.
*
- * @param headerValue the string representation of the header value
+ * @param headerValue the array of char representation of the header value
* (as received from the web server).
- * @return the header elements containing Header elements.
- * @throws HttpException if the above syntax rules are violated.
+ * @return array of {@link HeaderElement}s.
*/
- public static final HeaderElement[] parse(String headerValue)
- throws HttpException {
+ public static final HeaderElement[] parseElements(char[] headerValue) {
- LOG.trace("enter HeaderElement.parse(String)");
+ LOG.trace("enter HeaderElement.parseElements(char[])");
if (headerValue == null) {
return null;
}
+ List elements = new ArrayList();
- Vector elements = new Vector();
- StringTokenizer tokenizer =
- new StringTokenizer(headerValue.trim(), ",");
-
- while (tokenizer.countTokens() > 0) {
- String nextToken = tokenizer.nextToken();
-
- // FIXME: refactor into private method named ?
- // careful... there may have been a comma in a quoted string
- try {
- while (HeaderElement.hasOddNumberOfQuotationMarks(nextToken)) {
- nextToken += "," + tokenizer.nextToken();
- }
- } catch (NoSuchElementException exception) {
- throw new HttpException(
- "Bad header format: wrong number of quotation marks");
- }
-
- // FIXME: Refactor out into a private method named ?
- try {
- /*
- * Following to RFC 2109 and 2965, in order not to conflict
- * with the next header element, make it sure to parse tokens.
- * the expires date format is "Wdy, DD-Mon-YY HH:MM:SS GMT".
- * Notice that there is always comma(',') sign.
- * For the general cases, rfc1123-date, rfc850-date.
- */
- if (tokenizer.hasMoreTokens()) {
- String s = nextToken.toLowerCase();
- if (s.endsWith("mon")
- || s.endsWith("tue")
- || s.endsWith("wed")
- || s.endsWith("thu")
- || s.endsWith("fri")
- || s.endsWith("sat")
- || s.endsWith("sun")
- || s.endsWith("monday")
- || s.endsWith("tuesday")
- || s.endsWith("wednesday")
- || s.endsWith("thursday")
- || s.endsWith("friday")
- || s.endsWith("saturday")
- || s.endsWith("sunday")) {
-
- nextToken += "," + tokenizer.nextToken();
- }
- }
- } catch (NoSuchElementException exception) {
- throw new HttpException
- ("Bad header format: parsing with wrong header elements");
- }
-
- String tmp = nextToken.trim();
- if (!tmp.endsWith(";")) {
- tmp += ";";
+ int i = 0;
+ int from = 0;
+ int len = headerValue.length;
+ boolean qouted = false;
+ while(i < len) {
+ char ch = headerValue[i];
+ if (ch == '"') {
+ qouted = !qouted;
}
- char[] header = tmp.toCharArray();
-
- // FIXME: refactor into a private method named? parseElement?
- boolean inAString = false;
- int startPos = 0;
- HeaderElement element = new HeaderElement();
- Vector paramlist = new Vector();
- for (int i = 0 ; i < header.length ; i++) {
- if (header[i] == ';' && !inAString) {
- NameValuePair pair = parsePair(header, startPos, i);
- if (pair == null) {
- throw new HttpException(
- "Bad header format: empty name/value pair in"
- + nextToken);
-
- // the first name/value pair are handled differently
- } else if (startPos == 0) {
- element.setName(pair.getName());
- element.setValue(pair.getValue());
- } else {
- paramlist.addElement(pair);
- }
- startPos = i + 1;
- } else if (header[i] == '"'
- && !(inAString && i > 0 && header[i - 1] == '\\')) {
- inAString = !inAString;
- }
+ HeaderElement element = null;
+ if ((!qouted) && (ch == ',')) {
+ element = new HeaderElement(headerValue, from, i);
+ from = i + 1;
+ } else if (i == len - 1) {
+ element = new HeaderElement(headerValue, from, len);
}
-
- // now let's add all the parameters into the header element
- if (paramlist.size() > 0) {
- NameValuePair[] tmp2 = new NameValuePair[paramlist.size()];
- paramlist.copyInto((NameValuePair[]) tmp2);
- element.setParameters (tmp2);
- paramlist.removeAllElements();
+ if ((element != null) && (element.getName() != null)) {
+ elements.add(element);
}
-
- // and save the header element into the list of header elements
- elements.addElement(element);
+ i++;
}
-
- HeaderElement[] headerElements = new HeaderElement[elements.size()];
- elements.copyInto((HeaderElement[]) headerElements);
- return headerElements;
+ return (HeaderElement[])
+ elements.toArray(new HeaderElement[elements.size()]);
}
/**
- * Return true if string has
- * an odd number of " characters.
+ * This parses the value part of a header. The result is an array of
+ * HeaderElement objects.
*
- * @param string the string to test
- * @return true if there are an odd number of quotation marks, false
- * otherwise
+ * @param headerValue the string representation of the header value
+ * (as received from the web server).
+ * @return array of {@link HeaderElement}s.
+ * @throws HttpException if the above syntax rules are violated.
*/
- private static final boolean hasOddNumberOfQuotationMarks(String string) {
- boolean odd = false;
- int start = -1;
- while ((start = string.indexOf('"', start + 1)) != -1) {
- odd = !odd;
+ public static final HeaderElement[] parseElements(String headerValue) {
+
+ LOG.trace("enter HeaderElement.parseElements(String)");
+
+ if (headerValue == null) {
+ return null;
}
- return odd;
+ return parseElements(headerValue.toCharArray());
}
/**
- * Parse a header character array into a {@link NameValuePair}
+ * This parses the value part of a header. The result is an array of
+ * HeaderElement objects.
*
- * @param header the character array to parse
- * @param start the starting position of the text within the array
- * @param end the end position of the text within the array
- * @return a {@link NameValuePair} representing the header
+ * @param headerValue the string representation of the header value
+ * (as received from the web server).
+ * @return array of {@link HeaderElement}s.
+ * @throws HttpException if the above syntax rules are violated.
+ *
+ * @deprecated Use #parseElements(String).
*/
- private static final NameValuePair parsePair(char[] header,
- int start, int end) {
+ public static final HeaderElement[] parse(String headerValue)
+ throws HttpException {
- LOG.trace("enter HeaderElement.parsePair(char[], int, int)");
+ LOG.trace("enter HeaderElement.parse(String)");
- NameValuePair pair = null;
- String name = new String(header, start, end - start).trim();
- String value = null;
-
- //TODO: This would certainly benefit from a StringBuffer
- int index = name.indexOf("=");
- if (index >= 0) {
- if ((index + 1) < name.length()) {
- value = name.substring(index + 1).trim();
- // strip quotation marks
- if (value.startsWith("\"") && value.endsWith("\"")) {
- value = value.substring(1, value.length() - 1);
- }
- }
- name = name.substring(0, index).trim();
+ if (headerValue == null) {
+ return null;
}
-
- pair = new NameValuePair(name, value);
-
- return pair;
+ return parseElements(headerValue.toCharArray());
}
-
+
/**
* Returns parameter with the given name, if found. Otherwise null
@@ -438,8 +301,11 @@
*/
public NameValuePair getParameterByName(String name) {
+
+ LOG.trace("enter HeaderElement.getParameterByName(String)");
+
if (name == null) {
- throw new NullPointerException("Name is null");
+ throw new IllegalArgumentException("Name may not be null");
}
NameValuePair found = null;
NameValuePair parameters[] = getParameters();
@@ -454,7 +320,6 @@
}
return found;
}
-
}
Index: java/org/apache/commons/httpclient/auth/AuthChallengeParser.java
===================================================================
RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/auth/AuthChallengeParser.java,v
retrieving revision 1.4
diff -u -r1.4 AuthChallengeParser.java
--- java/org/apache/commons/httpclient/auth/AuthChallengeParser.java 6 Apr 2003 22:31:53 -0000 1.4
+++ java/org/apache/commons/httpclient/auth/AuthChallengeParser.java 12 Jul 2003 14:35:54 -0000
@@ -63,8 +63,12 @@
package org.apache.commons.httpclient.auth;
-import java.util.Map;
import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.httpclient.NameValuePair;
+import org.apache.commons.httpclient.util.ParameterParser;
/**
* This class provides utility methods for parsing HTTP www and proxy authentication
@@ -92,12 +96,12 @@
if (challengeStr == null) {
throw new IllegalArgumentException("Challenge may not be null");
}
- int i = challengeStr.indexOf(' ');
+ int idx = challengeStr.indexOf(' ');
String s = null;
- if (i == -1) {
+ if (idx == -1) {
s = challengeStr;
} else {
- s = challengeStr.substring(0, i);
+ s = challengeStr.substring(0, idx);
}
if (s.equals("")) {
throw new MalformedChallengeException("Invalid challenge: " + challengeStr);
@@ -121,114 +125,18 @@
if (challengeStr == null) {
throw new IllegalArgumentException("Challenge may not be null");
}
- int i = challengeStr.indexOf(' ');
- if (i == -1) {
+ int idx = challengeStr.indexOf(' ');
+ if (idx == -1) {
throw new MalformedChallengeException("Invalid challenge: " + challengeStr);
}
-
- Map elements = new HashMap();
-
- i++;
- int len = challengeStr.length();
-
- String name = null;
- String value = null;
-
- StringBuffer buffer = new StringBuffer();
-
- boolean parsingName = true;
- boolean inQuote = false;
- boolean gotIt = false;
-
- while (i < len) {
- // Parse one char at a time
- char ch = challengeStr.charAt(i);
- i++;
- // Process the char
- if (parsingName) {
- // parsing name
- if (ch == '=') {
- name = buffer.toString().trim();
- parsingName = false;
- buffer.setLength(0);
- } else if (ch == ',') {
- name = buffer.toString().trim();
- value = null;
- gotIt = true;
- buffer.setLength(0);
- } else {
- buffer.append(ch);
- }
- // Have I reached the end of the challenge string?
- if (i == len) {
- name = buffer.toString().trim();
- value = null;
- gotIt = true;
- }
- } else {
- //parsing value
- if (!inQuote) {
- // Value is not quoted or not found yet
- if (ch == ',') {
- value = buffer.toString().trim();
- gotIt = true;
- buffer.setLength(0);
- } else {
- // no value yet
- if (buffer.length() == 0) {
- if (ch == ' ') {
- //discard
- } else if (ch == '\t') {
- //discard
- } else if (ch == '\n') {
- //discard
- } else if (ch == '\r') {
- //discard
- } else {
- // otherwise add to the buffer
- buffer.append(ch);
- if (ch == '"') {
- inQuote = true;
- }
- }
- } else {
- // already got something
- // just keep on adding to the buffer
- buffer.append(ch);
- }
- }
- } else {
- // Value is quoted
- // Keep on adding until closing quote is encountered
- buffer.append(ch);
- if (ch == '"') {
- inQuote = false;
- }
- }
- // Have I reached the end of the challenge string?
- if (i == len) {
- value = buffer.toString().trim();
- gotIt = true;
- }
- }
- if (gotIt) {
- // Got something
- if ((name == null) || (name.equals(""))) {
- throw new MalformedChallengeException("Invalid challenge: " + challengeStr);
- }
- // Strip quotes when present
- if ((value != null) && (value.length() > 1)) {
- if ((value.charAt(0) == '"')
- && (value.charAt(value.length() - 1) == '"')) {
- value = value.substring(1, value.length() - 1);
- }
- }
-
- elements.put(name, value);
- parsingName = true;
- gotIt = false;
- }
+ Map map = new HashMap();
+ ParameterParser parser = new ParameterParser();
+ List params = parser.parse(
+ challengeStr.substring(idx + 1, challengeStr.length()), ',');
+ for (int i = 0; i < params.size(); i++) {
+ NameValuePair param = (NameValuePair)params.get(i);
+ map.put(param.getName().toLowerCase(), param.getValue());
}
- return elements;
+ return map;
}
}
Index: java/org/apache/commons/httpclient/cookie/CookieSpecBase.java
===================================================================
RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/cookie/CookieSpecBase.java,v
retrieving revision 1.16
diff -u -r1.16 CookieSpecBase.java
--- java/org/apache/commons/httpclient/cookie/CookieSpecBase.java 12 Jun 2003 19:12:16 -0000 1.16
+++ java/org/apache/commons/httpclient/cookie/CookieSpecBase.java 12 Jul 2003 14:35:58 -0000
@@ -1,5 +1,5 @@
/*
- * $Header: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/cookie/CookieSpecBase.java,v 1.16 2003/06/12 19:12:16 olegk Exp $
+ * $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/cookie/CookieSpecBase.java,v 1.16 2003/06/12 19:12:16 olegk Exp $
* $Revision: 1.16 $
* $Date: 2003/06/12 19:12:16 $
*
@@ -70,7 +70,6 @@
import org.apache.commons.httpclient.Cookie;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HeaderElement;
-import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.util.DateParseException;
import org.apache.commons.httpclient.util.DateParser;
@@ -166,14 +165,7 @@
path = PATH_DELIM;
}
host = host.toLowerCase();
-
- HeaderElement[] headerElements = null;
- try {
- headerElements = HeaderElement.parse(header);
- } catch (HttpException e) {
- throw new MalformedCookieException(e.getMessage());
- }
-
+
String defaultPath = path;
int lastSlashIndex = defaultPath.lastIndexOf(PATH_DELIM);
if (lastSlashIndex >= 0) {
@@ -183,6 +175,31 @@
}
defaultPath = defaultPath.substring(0, lastSlashIndex);
}
+
+ HeaderElement[] headerElements = null;
+
+ boolean isNetscapeCookie = false;
+ int i1 = header.toLowerCase().indexOf("expires=");
+ if (i1 != -1) {
+ i1 += "expires=".length();
+ int i2 = header.indexOf(";", i1);
+ if (i2 == -1) {
+ i2 = header.length();
+ }
+ try {
+ DateParser.parseDate(header.substring(i1, i2));
+ isNetscapeCookie = true;
+ } catch(DateParseException e) {
+ // Does not look like a valid expiry date
+ }
+ }
+ if (isNetscapeCookie) {
+ headerElements = new HeaderElement[] {
+ new HeaderElement(header.toCharArray())
+ };
+ } else {
+ headerElements = HeaderElement.parseElements(header.toCharArray());
+ }
Cookie[] cookies = new Cookie[headerElements.length];
@@ -330,14 +347,6 @@
if (paramValue == null) {
throw new MalformedCookieException(
"Missing value for expires attribute");
- }
- // trim single quotes around expiry if present
- // see http://nagoya.apache.org/bugzilla/show_bug.cgi?id=5279
- if (paramValue.length() > 1
- && paramValue.startsWith("'")
- && paramValue.endsWith("'")) {
- paramValue
- = paramValue.substring (1, paramValue.length() - 1);
}
try {
Index: java/org/apache/commons/httpclient/util/DateParser.java
===================================================================
RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/util/DateParser.java,v
retrieving revision 1.3
diff -u -r1.3 DateParser.java
--- java/org/apache/commons/httpclient/util/DateParser.java 26 May 2003 21:51:37 -0000 1.3
+++ java/org/apache/commons/httpclient/util/DateParser.java 12 Jul 2003 14:35:59 -0000
@@ -144,6 +144,15 @@
if (dateValue == null) {
throw new IllegalArgumentException("dateValue is null");
}
+
+ // trim single quotes around date if present
+ // see http://nagoya.apache.org/bugzilla/show_bug.cgi?id=5279
+ if (dateValue.length() > 1
+ && dateValue.startsWith("'")
+ && dateValue.endsWith("'"))
+ {
+ dateValue = dateValue.substring (1, dateValue.length() - 1);
+ }
SimpleDateFormat dateParser = null;
Index: java/org/apache/commons/httpclient/util/ParameterParser.java
===================================================================
RCS file: java/org/apache/commons/httpclient/util/ParameterParser.java
diff -N java/org/apache/commons/httpclient/util/ParameterParser.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ java/org/apache/commons/httpclient/util/ParameterParser.java 12 Jul 2003 14:36:00 -0000
@@ -0,0 +1,261 @@
+/*
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999-2003 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ * any, must include the following acknowlegement:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowlegement may appear in the software itself,
+ * if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
+ * Foundation" must not be used to endorse or promote products derived
+ * from this software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ *
+ * param1 = value; param2 = "anything goes; really"; param3
+ *