updateByteValue
is called to perform the job.
+ */
+
+ protected final void checkByteValue() {
+ if ( raw == null ) {
+ updateByteValue();
+ roff = 0;
+ rlen = raw.length;
+ }
+ }
+
+ /**
+ * Validate the parsed value according to the last set raw value.
+ * This will trigger the header value parsing, if it is required at this
+ * point.
+ * @exception HttpInvalidValueException If the value couldn't be parsed
+ * properly.
+ */
+
+ protected final void validate()
+ throws HttpInvalidValueException
+ {
+ if ( isValid )
+ return;
+ try {
+ parse();
+ } catch (HttpParserException ex) {
+ throw new HttpInvalidValueException(ex.getMessage());
+ }
+ isValid = true;
+ }
+
+ /**
+ * Invalidate the current byte value for this header, if any.
+ */
+
+ protected void invalidateByteValue() {
+ raw = null;
+ }
+
+ /**
+ * Emit a parsing error.
+ * @param msg The error message.
+ * @exception HttpParserException If the parsing failed.
+ */
+
+ protected void error(String msg)
+ throws HttpParserException
+ {
+ throw new HttpParserException(msg);
+ }
+
+ /**
+ * Append this header byte value to the given buffer.
+ * @param buf The buffer to append the byte value to.
+ */
+
+ public void appendValue(HttpBuffer buf) {
+ checkByteValue();
+ buf.append(raw, roff, rlen);
+ }
+
+ /**
+ * Return the HTTP encoding for this header value.
+ * This method is slow, and defeats nearly all the over-engeneered
+ * optimization of the HTTP parser.
+ * @return A String representing the header value in a format compatible
+ * with HTTP.
+ */
+
+ public String toExternalForm() {
+ checkByteValue();
+ return new String(raw, 0, roff, rlen-roff);
+ }
+
+ /**
+ * Print this header value as it would be emitted.
+ * @return A String representation of this header value.
+ */
+
+ public String toString() {
+ return toExternalForm();
+ }
+
+ /**
+ * Set this Header Value by parsing the given String.
+ * @param strval The String value for that object.
+ * @return Itself.
+ */
+
+ public void setString(String strval) {
+ int slen = strval.length();
+ raw = new byte[slen];
+ roff = 0;
+ rlen = slen;
+ strval.getBytes(0, slen, raw, 0);
+ isValid = false;
+ }
+
+ /**
+ * Get this header parsed value, in its native type.
+ * HeaderValue implementors can be used as wrappers for the actual
+ * parsed header value. In such case this method should return the wrapped
+ * value (you would otherwise, probably want to return
+ * this).
+ */
+
+ abstract public Object getValue() ;
+
+ // needs to define it as this is an abstract class
+ protected Object clone()
+ throws CloneNotSupportedException
+ {
+ return super.clone();
+ }
+
+ public HeaderValue() {
+ isValid = false;
+ }
+
+}
diff --git a/org/w3c/www/http/HttpAcceptCharset.java b/org/w3c/www/http/HttpAcceptCharset.java
new file mode 100644
index 000000000..ae901c4d8
--- /dev/null
+++ b/org/w3c/www/http/HttpAcceptCharset.java
@@ -0,0 +1,111 @@
+// HttpAcceptCharset.java
+// $Id$
+// (c) COPYRIGHT MIT and INRIA, 1996.
+// Please first read the full copyright statement in file COPYRIGHT.html
+
+package org.w3c.www.http;
+
+public class HttpAcceptCharset extends HeaderValue {
+ String charset = null;
+ double quality = 1.0;
+ HttpAcceptCharsetList list = null;
+
+ /**
+ * parse.
+ * @exception HttpParserException if parsing failed.
+ */
+ protected void parse()
+ throws HttpParserException
+ {
+ ParseState ps = new ParseState(roff, rlen);
+ ps.separator = (byte) ';';
+ ps.spaceIsSep = false;
+ // Get the charset:
+ if ( HttpParser.nextItem(raw, ps) < 0 )
+ error("Invalid Accept-Charset: no charset.");
+ this.charset = new String(raw, 0, ps.start, ps.end-ps.start);
+ // And the optional quality:
+ ps.prepare();
+ ps.separator = (byte) '=';
+ if ( HttpParser.nextItem(raw, ps) < 0 ) {
+ this.quality = 1.0;
+ } else {
+ ps.prepare();
+ this.quality = HttpParser.parseQuality(raw, ps);
+ }
+ }
+
+ protected void invalidateByteValue() {
+ super.invalidateByteValue();
+ if ( list != null )
+ list.invalidateByteValue();
+ }
+
+ protected void updateByteValue() {
+ HttpBuffer buf = new HttpBuffer();
+ buf.append(charset);
+ buf.append(';');
+ buf.append(quality);
+ raw = buf.getByteCopy();
+ roff = 0;
+ rlen = raw.length;
+ }
+
+ public Object getValue() {
+ validate();
+ return this;
+ }
+
+ /**
+ * Get this accept charset clause charset.
+ * @return A String encoding the charset token.
+ */
+
+ public String getCharset() {
+ validate();
+ return charset;
+ }
+
+ /**
+ * Set the charset accepted by this clause.
+ * @param charset The accepted charset.
+ */
+
+ public void setCharset(String charset) {
+ if ( this.charset.equals(charset) )
+ return;
+ invalidateByteValue();
+ this.charset = charset;
+ }
+
+ /**
+ * Get the quality at which this charset is accepted.
+ * @return A double value, encoding the quality, or 1.0
+ * if undefined.
+ */
+
+ public double getQuality() {
+ validate();
+ return quality;
+ }
+
+ /**
+ * Set the quality under which this charset is accepted.
+ * @param quality The quality for this charset.
+ */
+
+ public void setQuality(double quality) {
+ if ( this.quality != quality )
+ invalidateByteValue();
+ this.quality = quality;
+ }
+
+ HttpAcceptCharset(HttpAcceptCharsetList list, byte raw[], int o, int l) {
+ this.list = list;
+ this.raw = raw;
+ this.roff = o;
+ this.rlen = l;
+ this.isValid = false;
+ }
+
+}
diff --git a/org/w3c/www/http/HttpAcceptCharsetList.java b/org/w3c/www/http/HttpAcceptCharsetList.java
new file mode 100644
index 000000000..d3ae5a8b1
--- /dev/null
+++ b/org/w3c/www/http/HttpAcceptCharsetList.java
@@ -0,0 +1,53 @@
+// HttpAcceptCharsetList.java
+// $Id$
+// (c) COPYRIGHT MIT and INRIA, 1996.
+// Please first read the full copyright statement in file COPYRIGHT.html
+
+package org.w3c.www.http;
+
+import java.util.Vector;
+
+public class HttpAcceptCharsetList extends HeaderValue {
+ HttpAcceptCharset charsets[] = null;
+
+ protected void parse() {
+ Vector vl = new Vector(4);
+ ParseState ps = new ParseState(roff, rlen);
+ ps.separator = (byte) ',';
+ ps.spaceIsSep = false;
+ while ( HttpParser.nextItem(raw, ps) >= 0 ) {
+ vl.addElement(new HttpAcceptCharset(this, raw, ps.start, ps.end));
+ ps.prepare();
+ }
+ charsets = new HttpAcceptCharset[vl.size()];
+ vl.copyInto(charsets);
+ }
+
+ protected void updateByteValue() {
+ HttpBuffer buf = new HttpBuffer();
+ if ( charsets != null ) {
+ for (int i = 0 ; i < charsets.length ; i++) {
+ if ( i > 0 )
+ buf.append(',');
+ charsets[i].appendValue(buf);
+ }
+ raw = buf.getByteCopy();
+ roff = 0;
+ rlen = raw.length;
+ } else {
+ raw = new byte[0];
+ roff = 0;
+ rlen = 0;
+ }
+ }
+
+ public Object getValue() {
+ validate();
+ return charsets;
+ }
+
+ public HttpAcceptCharsetList() {
+ this.isValid = false;
+ }
+
+}
diff --git a/org/w3c/www/http/HttpBuffer.java b/org/w3c/www/http/HttpBuffer.java
new file mode 100644
index 000000000..8c0a32fc4
--- /dev/null
+++ b/org/w3c/www/http/HttpBuffer.java
@@ -0,0 +1,87 @@
+// HttpBuffer.java
+// $Id$
+// (c) COPYRIGHT MIT and INRIA, 1996.
+// Please first read the full copyright statement in file COPYRIGHT.html
+
+package org.w3c.www.http;
+
+/**
+ * A cool StringBuffer like class, for converting header values to String.
+ * Note that for good reasons, this class is not public.
+ */
+
+class HttpBuffer {
+ private static final int INIT_SIZE = 128 ;
+
+ byte buf[];
+ int len = 0;
+
+ final void append(byte b) {
+ ensureCapacity(1);
+ buf[len++] = b;
+ }
+
+ final void append(char ch) {
+ append((byte) ch);
+ }
+
+ final void ensureCapacity(int sz) {
+ int req = len + sz ;
+ if ( req >= buf.length ) {
+ int nsz = buf.length << 1;
+ if ( nsz < req )
+ nsz = req + 1;
+ byte nb[] = new byte[nsz];
+ System.arraycopy(buf, 0, nb, 0, len);
+ buf = nb;
+ }
+ }
+
+ void append(byte b[], int o, int l) {
+ ensureCapacity(l);
+ System.arraycopy(b, o, buf, len, l);
+ len += l;
+ }
+
+ void append(String str) {
+ int l = str.length();
+ ensureCapacity(l);
+ str.getBytes(0, l, buf, len);
+ len += l;
+ }
+
+ void append(double d) {
+ append(Double.toString(d));
+ }
+
+ public String toString() {
+ return new String(buf, 0, 0, len);
+ }
+
+ /**
+ * Get a copy of the current byte buffer.
+ */
+
+ public byte[] getByteCopy() {
+ byte v[] = new byte[len];
+ System.arraycopy(buf, 0, v, 0, len);
+ return v;
+ }
+
+ public final byte[] getBytes() {
+ return buf;
+ }
+
+ public final int length() {
+ return len;
+ }
+
+ public final void reset() {
+ len = 0;
+ }
+
+ HttpBuffer() {
+ this.buf = new byte[INIT_SIZE];
+ }
+
+}
diff --git a/org/w3c/www/http/HttpInvalidValueException.java b/org/w3c/www/http/HttpInvalidValueException.java
new file mode 100644
index 000000000..222cf98b1
--- /dev/null
+++ b/org/w3c/www/http/HttpInvalidValueException.java
@@ -0,0 +1,14 @@
+// HttpInvalidValueException.java
+// $Id$
+// (c) COPYRIGHT MIT and INRIA, 1996.
+// Please first read the full copyright statement in file COPYRIGHT.html
+
+package org.w3c.www.http;
+
+public class HttpInvalidValueException extends RuntimeException {
+
+ public HttpInvalidValueException(String msg) {
+ super(msg);
+ }
+
+}
diff --git a/org/w3c/www/http/HttpParser.java b/org/w3c/www/http/HttpParser.java
new file mode 100644
index 000000000..4a8af7fa6
--- /dev/null
+++ b/org/w3c/www/http/HttpParser.java
@@ -0,0 +1,141 @@
+// HttpParser.java
+// $Id$
+// (c) COPYRIGHT MIT and INRIA, 1996.
+// Please first read the full copyright statement in file COPYRIGHT.html
+
+package org.w3c.www.http;
+
+/**
+ * A private class to help with the parsing.
+ * Contains only some static method, helping to parse various byte
+ * buffers into Java object (Yes, I am still and again trying to reduce
+ * memory consumption).
+ * I don't know wether this sucks or not. One hand I am sparing a tremedous
+ * amount of Strings creation, on the other end I am recoding a number of
+ * parsers that are available on String instances.
+ */
+
+public class HttpParser {
+ private static final boolean debug = false;
+
+ /**
+ * Emit an error.
+ * @param mth The method trigerring the error.
+ * @param msg An associated message.
+ * @exception HttpInvalidValueException To indicate the error to caller.
+ */
+
+ protected static void error(String mth, String msg)
+ throws HttpInvalidValueException
+ {
+ throw new HttpInvalidValueException(mth+": "+msg);
+ }
+
+ /**
+ * Skip leading LWS, not including CR LF.
+ * Update the input offset, after any leading space.
+ * @param buf The buffer to be parsed.
+ * @param ptr The buffer pointer to be updated on return.
+ * @return The potentially advanced buffer input offset.
+ */
+
+ public static final int skipSpaces(byte buf[], ParseState ps) {
+ int len = (ps.bufend > 0) ? ps.bufend : buf.length;
+ int off = ps.ioff;
+ while (off < len) {
+ if ((buf[off] != (byte) ' ')
+ && (buf[off] != (byte) '\t')
+ && (buf[off] != (byte) ps.separator)) {
+ ps.ioff = off;
+ return off;
+ }
+ off++;
+ }
+ return off;
+ }
+
+ /**
+ * Parse list of items, taking care of quotes and optional LWS.
+ * The output offset points to the next element of the list.
+ * @eturn The starting location (i.e. ps.start
value), or
+ * -1 if no item available (end of list).
+ */
+
+ public static final int nextItem(byte buf[], ParseState ps) {
+ // Skip leading spaces, if needed:
+ int off = -1;
+ int len = -1;
+ if ( ps.isSkipable )
+ ps.start = off = skipSpaces(buf, ps) ;
+ else
+ ps.start = off = ps.ioff ;
+ len = (ps.bufend > 0) ? ps.bufend : buf.length;
+ if ( debug )
+ System.out.println("parsing: ["+new String(buf, 0, off, len-off)+
+ "]");
+ // Parse !
+ if ( off >= len )
+ return -1;
+ // Setup for parsing, and parse
+ ps.start = off;
+ loop:
+ while (off < len) {
+ if ( buf[off] == (byte) '"' ) {
+ // A quoted item, read as one chunk
+ off++;
+ while (off < len ) {
+ if (buf[off] == (byte) '\\') {
+ off += 2;
+ } else if (buf[off] == (byte) '"') {
+ off++;
+ continue loop;
+ } else {
+ off++;
+ }
+ }
+ if ( off == len )
+ error("nextItem", "Un-terminated quoted item.");
+ } else if ((buf[off] == ps.separator)
+ || (ps.spaceIsSep
+ && ((buf[off] == ' ') || (buf[off] == '\t')))) {
+ break loop;
+ }
+ off++;
+ }
+ ps.end = off;
+ // Item start is set, we are right at the end of item
+ if ( ps.isSkipable ) {
+ ps.ioff = off ;
+ ps.ooff = skipSpaces(buf, ps);
+ }
+ // Check for either the end of the list, or the separator:
+ if (ps.ooff < ps.bufend) {
+ if (buf[ps.ooff] == (byte) ps.separator)
+ ps.ooff++;
+ }
+ if ( debug )
+ System.out.println("nextItem = ["+new String(buf, 0, ps.start,
+ ps.end-ps.start)+"]");
+ return (ps.end > ps.start) ? ps.start : -1;
+ }
+
+ public static double parseQuality(byte buf[], ParseState ps) {
+ // Skip spaces if needed
+ int off = -1;
+ if ( ps.isSkipable )
+ ps.start = off = skipSpaces(buf, ps);
+ else
+ ps.start = off = ps.ioff;
+ // Parse the integer from byte[] straight (without creating Strings)
+ int len = (ps.bufend > 0) ? ps.bufend : buf.length;
+ String str = new String(buf, 0, off, len-off);
+ try {
+ return Double.valueOf(str).doubleValue();
+ } catch (Exception ex) {
+ error("parseQuality", "Invalid floating point number.");
+ }
+ // Not reached:
+ return 1.0;
+ }
+
+}
diff --git a/org/w3c/www/http/HttpParserException.java b/org/w3c/www/http/HttpParserException.java
new file mode 100644
index 000000000..2373ad18a
--- /dev/null
+++ b/org/w3c/www/http/HttpParserException.java
@@ -0,0 +1,16 @@
+// HttpParserException.java
+// $Id$
+// (c) COPYRIGHT MIT and INRIA, 1996.
+// Please first read the full copyright statement in file COPYRIGHT.html
+
+package org.w3c.www.http;
+
+import org.w3c.www.mime.MimeParserException;
+
+public class HttpParserException extends MimeParserException {
+
+ public HttpParserException(String msg) {
+ super(msg);
+ }
+
+}
diff --git a/org/w3c/www/http/ParseState.java b/org/w3c/www/http/ParseState.java
new file mode 100644
index 000000000..86f7cf414
--- /dev/null
+++ b/org/w3c/www/http/ParseState.java
@@ -0,0 +1,44 @@
+// ParseState.java
+// $Id$
+// (c) COPYRIGHT MIT and INRIA, 1996.
+// Please first read the full copyright statement in file COPYRIGHT.html
+
+package org.w3c.www.http;
+
+class ParseState {
+ int ioff = -1; // input offset
+ int ooff = -1; // output ofset (where parsing should continue)
+ int start = -1 ; // Start of parsed item (if needed)
+ int end = -1; // End of parsed item (if needed)
+ int bufend = -1 ; // End of the buffer to parse
+
+ boolean isSkipable = true ; // Always skip space when this make sense
+ boolean isQuotable = true ; // Support quted string while parsing next item
+ boolean spaceIsSep = true;
+
+ byte separator = (byte) ','; // Separator for parsing list
+
+ final void prepare() {
+ ioff = ooff;
+ start = -1;
+ end = -1;
+ }
+
+ final void prepare(ParseState ps) {
+ this.ioff = ps.start;
+ this.bufend = ps.end;
+ }
+
+ ParseState(int ioff) {
+ this.ioff = ioff;
+ }
+
+ ParseState(int ioff, int bufend) {
+ this.ioff = ioff;
+ this.bufend = bufend;
+ }
+
+ ParseState() {
+ }
+
+}
diff --git a/org/w3c/www/mime/MimeParserException.java b/org/w3c/www/mime/MimeParserException.java
new file mode 100644
index 000000000..aabfa1e43
--- /dev/null
+++ b/org/w3c/www/mime/MimeParserException.java
@@ -0,0 +1,14 @@
+// MimeParserException.java
+// $Id$
+// (c) COPYRIGHT MIT and INRIA, 1996.
+// Please first read the full copyright statement in file COPYRIGHT.html
+
+package org.w3c.www.mime;
+
+public class MimeParserException extends Exception {
+
+ public MimeParserException(String msg) {
+ super(msg);
+ }
+
+}