001: /*
002: * MimeHeaders.java
003: *
004: * Brazil project web application Framework,
005: * export version: 1.1
006: * Copyright (c) 1999 Sun Microsystems, Inc.
007: *
008: * Sun Public License Notice
009: *
010: * The contents of this file are subject to the Sun Public License Version
011: * 1.0 (the "License"). You may not use this file except in compliance with
012: * the License. A copy of the License is included as the file "license.terms",
013: * and also available at http://www.sun.com/
014: *
015: * The Original Code is from:
016: * Brazil project web application Framework release 1.1.
017: * The Initial Developer of the Original Code is: cstevens.
018: * Portions created by cstevens are Copyright (C) Sun Microsystems, Inc.
019: * All Rights Reserved.
020: *
021: * Contributor(s): cstevens, suhler.
022: *
023: * Version: 1.12
024: * Created by cstevens on 99/09/15
025: * Last modified by cstevens on 99/11/16 14:48:57
026: */
027:
028: package sunlabs.brazil.util.http;
029:
030: import sunlabs.brazil.util.StringMap;
031:
032: import java.io.IOException;
033: import java.io.OutputStream;
034: import java.io.PrintStream;
035:
036: /**
037: * This class is build on top of the <code>StringMap</code> class and
038: * provides added methods that are of help when manipulating MIME headers.
039: * By creating an instance of this class, the user can conveniently read,
040: * write, and modify MIME headers.
041: *
042: * @author Colin Stevens (colin.stevens@sun.com)
043: * @version 1.12 99/11/16
044: */
045:
046: public class MimeHeaders extends StringMap {
047: /**
048: * Creates a new, empty <code>MimeHeaders</code> object.
049: */
050: public MimeHeaders() {
051: }
052:
053: /**
054: * Creates a new <code>MimeHeaders</code> object and then initializes
055: * it by reading MIME headers from the specified input stream.
056: *
057: * @param in
058: * The input stream to read.
059: */
060: public MimeHeaders(HttpInputStream in) throws IOException {
061: read(in);
062: }
063:
064: /**
065: * Reads MIME headers from the specified input stream. This method
066: * reads up to and consumes the blank line that marks the end of the
067: * MIME headers. It also stops reading if it reaches the end of
068: * the input stream.
069: * <p>
070: * The MIME headers read from the input stream are stored in this
071: * <code>MimeHeaders</code> object. All headers read are <b>added</b>
072: * to the existing headers; the new headers do not replace the
073: * existing ones. The order of the headers in this object will
074: * reflect the order of the headers from the input stream, but
075: * space characters surrounding the keys and values are not preserved.
076: * <p>
077: * In a set of MIME headers, the given key may appear multiple times
078: * (that is, on multiple lines, not necessarily consecutively).
079: * In that case, that key will appear multiple times in this
080: * <code>MimeHeaders</code> object also. The HTTP spec says that
081: * if a given key appears multiple times in a set of MIME headers, the
082: * values can be concatenated together with commas
083: * between them. However, in practice, it appears that some browsers
084: * and HTTP servers get confused when encountering such collapsed
085: * MIME headers, for instance, the Yahoo mail reader program.
086: * <p>
087: * MIME headers also support the idea of continuation lines, where
088: * a key (and optionally its value) is followed on subsequent line(s) by
089: * another value without a key. The HTTP spec says that in this case
090: * the values can be concatenated together with space characters
091: * between them. In practice, joining continuation lines together
092: * does not seem to confuse any browsers or HTTP servers. This method
093: * joins continuation lines together by putting the space-equivalent
094: * characters "\r\n\t" between the values so it can be easily parsed
095: * with <code>StringTokenizer</code> and also easily written to
096: * an output stream in a format that actually preserves its formatting
097: * as a continuation line.
098: *
099: * @param in
100: * The input stream to read from.
101: *
102: * @throws IOException
103: * if the input stream throws an IOException while being
104: * read.
105: */
106: public void read(HttpInputStream in) throws IOException {
107: while (true) {
108: String line = in.readLine();
109: if ((line == null) || (line.length() == 0)) {
110: break;
111: }
112:
113: if (Character.isSpaceChar(line.charAt(0)) == false) {
114: int index = line.indexOf(':');
115: if (index >= 0) {
116: String key = line.substring(0, index).trim();
117: String value = line.substring(index + 1).trim();
118: add(key, value);
119: }
120: } else if (size() > 0) {
121: String value = get(size() - 1);
122: put(size() - 1, value + "\r\n\t" + line.trim());
123: }
124: }
125: }
126:
127: /**
128: * Writes this <code>MimeHeaders</code> object to the given output
129: * stream. This method does <code>not</code> write a blank line after
130: * the headers are written.
131: *
132: * @param out
133: * The output stream.
134: */
135: public void print(OutputStream out) {
136: print(new PrintStream(out));
137: }
138:
139: /**
140: * Writes this <code>MimeHeaders</code> object to the given output
141: * stream. This method does <code>not</code> write a blank line after
142: * the headers are written.
143: *
144: * @param out
145: * The output stream.
146: */
147: public void print(PrintStream out) {
148: int length = size();
149: for (int i = 0; i < length; i++) {
150: String key = getKey(i);
151: String value = get(i);
152: out.print(key + ": " + value + "\r\n");
153: }
154: }
155:
156: /**
157: * Maps the given case-insensitive key to the specified value if the
158: * key does not already exist in this <code>MimeHeaders</code> object.
159: * <p>
160: * Often, when dealing with MIME headers, the user will want to set
161: * a header only if that header is not already set.
162: *
163: * @param key
164: * The new key. May not be <code>null</code>.
165: *
166: * @param value
167: * The new value. May be <code>null</code>.
168: */
169: public void putIfNotPresent(String key, String value) {
170: if (get(key) == null) {
171: put(key, value);
172: }
173: }
174:
175: /**
176: * Maps the given case-insensitive key to the specified value in this
177: * <code>MimeHeaders</code> object, replacing the old value.
178: * <p>
179: * This is convenience method that automatically converts the
180: * integer value to a string before calling the underlying
181: * <code>put</code> method.
182: *
183: * @param key
184: * The new key. May not be <code>null</code>.
185: *
186: * @param value
187: * The new value.
188: */
189: public void put(String key, int value) {
190: put(key, Integer.toString(value));
191: }
192:
193: /**
194: * Adds a mapping for the given case-insensitive key to the specified
195: * value in this <code>MimeHeaders</code> object. It leaves any existing
196: * key-value mapping alone.
197: * <p>
198: * This is convenience method that automatically converts the
199: * integer value to a string before calling the underlying
200: * <code>add</code> method.
201: *
202: * @param key
203: * The new key. May not be <code>null</code>.
204: *
205: * @param value
206: * The new value.
207: */
208: public void add(String key, int value) {
209: add(key, Integer.toString(value));
210: }
211:
212: /**
213: * Copies the contents of this <code>MimeHeaders</code> object,
214: * {@link #add adding} all the other's keys and values to the other.
215: *
216: * @param other
217: * The <code>MimeHeaders</code> object to copy to.
218: */
219: public void copyTo(MimeHeaders other) {
220: int length = size();
221: for (int i = 0; i < length; i++) {
222: other.add(getKey(i), get(i));
223: }
224: }
225: }
|