001: /*
002: * @(#)MessageHeader.java 1.29 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: /*-
029: * news stream opener
030: */
031:
032: package sun.net.www;
033:
034: import java.io.*;
035: import java.util.Collections;
036: import java.util.Map;
037: import java.util.HashMap;
038: import java.util.List;
039: import java.util.ArrayList;
040: import java.util.Set;
041: import java.util.Iterator;
042: import java.util.NoSuchElementException;
043:
044: /** An RFC 844 or MIME message header. Includes methods
045: for parsing headers from incoming streams, fetching
046: values, setting values, and printing headers.
047: Key values of null are legal: they indicate lines in
048: the header that don't have a valid key, but do have
049: a value (this isn't legal according to the standard,
050: but lines like this are everywhere). */
051: public class MessageHeader {
052: private String keys[];
053: private String values[];
054: private int nkeys;
055:
056: public MessageHeader() {
057: grow();
058: }
059:
060: public MessageHeader(InputStream is) throws java.io.IOException {
061: parseHeader(is);
062: }
063:
064: /**
065: * Reset a message header (all key/values removed)
066: */
067: public synchronized void reset() {
068: keys = null;
069: values = null;
070: nkeys = 0;
071: grow();
072: }
073:
074: /**
075: * Find the value that corresponds to this key.
076: * It finds only the first occurrence of the key.
077: * @param k the key to find.
078: * @return null if not found.
079: */
080: public synchronized String findValue(String k) {
081: if (k == null) {
082: for (int i = nkeys; --i >= 0;)
083: if (keys[i] == null)
084: return values[i];
085: } else
086: for (int i = nkeys; --i >= 0;) {
087: if (k.equalsIgnoreCase(keys[i]))
088: return values[i];
089: }
090: return null;
091: }
092:
093: // return the location of the key
094: public synchronized int getKey(String k) {
095: for (int i = nkeys; --i >= 0;)
096: if ((keys[i] == k)
097: || (k != null && k.equalsIgnoreCase(keys[i])))
098: return i;
099: return -1;
100: }
101:
102: public synchronized String getKey(int n) {
103: if (n < 0 || n >= nkeys)
104: return null;
105: return keys[n];
106: }
107:
108: public synchronized String getValue(int n) {
109: if (n < 0 || n >= nkeys)
110: return null;
111: return values[n];
112: }
113:
114: /** Deprecated: Use multiValueIterator() instead.
115: *
116: * Find the next value that corresponds to this key.
117: * It finds the first value that follows v. To iterate
118: * over all the values of a key use:
119: * <pre>
120: * for(String v=h.findValue(k); v!=null; v=h.findNextValue(k, v)) {
121: * ...
122: * }
123: * </pre>
124: */
125: public synchronized String findNextValue(String k, String v) {
126: boolean foundV = false;
127: if (k == null) {
128: for (int i = nkeys; --i >= 0;)
129: if (keys[i] == null)
130: if (foundV)
131: return values[i];
132: else if (values[i] == v)
133: foundV = true;
134: } else
135: for (int i = nkeys; --i >= 0;)
136: if (k.equalsIgnoreCase(keys[i]))
137: if (foundV)
138: return values[i];
139: else if (values[i] == v)
140: foundV = true;
141: return null;
142: }
143:
144: class HeaderIterator implements Iterator {
145: int index = 0;
146: int next = -1;
147: String key;
148: boolean haveNext = false;
149: Object lock;
150:
151: public HeaderIterator(String k, Object lock) {
152: key = k;
153: this .lock = lock;
154: }
155:
156: public boolean hasNext() {
157: synchronized (lock) {
158: if (haveNext) {
159: return true;
160: }
161: while (index < nkeys) {
162: if (key.equalsIgnoreCase(keys[index])) {
163: haveNext = true;
164: next = index++;
165: return true;
166: }
167: index++;
168: }
169: return false;
170: }
171: }
172:
173: public Object next() {
174: synchronized (lock) {
175: if (haveNext) {
176: haveNext = false;
177: return values[next];
178: }
179: if (hasNext()) {
180: return next();
181: } else {
182: throw new NoSuchElementException("No more elements");
183: }
184: }
185: }
186:
187: public void remove() {
188: throw new UnsupportedOperationException(
189: "remove not allowed");
190: }
191: }
192:
193: /**
194: * return an Iterator that returns all values of a particular
195: * key in sequence
196: */
197: public Iterator multiValueIterator(String k) {
198: return new HeaderIterator(k, this );
199: }
200:
201: public synchronized Map getHeaders() {
202: return getHeaders(null);
203: }
204:
205: public synchronized Map getHeaders(String[] excludeList) {
206: boolean skipIt = false;
207: Map m = new HashMap();
208: for (int i = nkeys; --i >= 0;) {
209: if (excludeList != null) {
210: // check if the key is in the excludeList.
211: // if so, don't include it in the Map.
212: for (int j = 0; j < excludeList.length; j++) {
213: if ((excludeList[j] != null)
214: && (excludeList[j]
215: .equalsIgnoreCase(keys[i]))) {
216: skipIt = true;
217: break;
218: }
219: }
220: }
221: if (!skipIt) {
222: List l = (List) m.get(keys[i]);
223: if (l == null) {
224: l = new ArrayList();
225: m.put(keys[i], l);
226: }
227: l.add(values[i]);
228: } else {
229: // reset the flag
230: skipIt = false;
231: }
232: }
233:
234: Set keySet = m.keySet();
235: for (Iterator i = keySet.iterator(); i.hasNext();) {
236: Object key = i.next();
237: List l = (List) m.get(key);
238: m.put(key, Collections.unmodifiableList(l));
239: }
240:
241: return Collections.unmodifiableMap(m);
242: }
243:
244: /** Prints the key-value pairs represented by this
245: header. Also prints the RFC required blank line
246: at the end. Omits pairs with a null key. */
247: public synchronized void print(PrintStream p) {
248: for (int i = 0; i < nkeys; i++)
249: if (keys[i] != null) {
250: p.print(keys[i]
251: + (values[i] != null ? ": " + values[i] : "")
252: + "\r\n");
253: }
254: p.print("\r\n");
255: p.flush();
256: }
257:
258: /** Adds a key value pair to the end of the
259: header. Duplicates are allowed */
260: public synchronized void add(String k, String v) {
261: grow();
262: keys[nkeys] = k;
263: values[nkeys] = v;
264: nkeys++;
265: }
266:
267: /** Prepends a key value pair to the beginning of the
268: header. Duplicates are allowed */
269: public synchronized void prepend(String k, String v) {
270: grow();
271: for (int i = nkeys; i > 0; i--) {
272: keys[i] = keys[i - 1];
273: values[i] = values[i - 1];
274: }
275: keys[0] = k;
276: values[0] = v;
277: nkeys++;
278: }
279:
280: /** Overwrite the previous key/val pair at location 'i'
281: * with the new k/v. If the index didn't exist before
282: * the key/val is simply tacked onto the end.
283: */
284:
285: public synchronized void set(int i, String k, String v) {
286: grow();
287: if (i < 0) {
288: return;
289: } else if (i > nkeys) {
290: add(k, v);
291: } else {
292: keys[i] = k;
293: values[i] = v;
294: }
295: }
296:
297: /** grow the key/value arrays as needed */
298:
299: private void grow() {
300: if (keys == null || nkeys >= keys.length) {
301: String[] nk = new String[nkeys + 4];
302: String[] nv = new String[nkeys + 4];
303: if (keys != null)
304: System.arraycopy(keys, 0, nk, 0, nkeys);
305: if (values != null)
306: System.arraycopy(values, 0, nv, 0, nkeys);
307: keys = nk;
308: values = nv;
309: }
310: }
311:
312: /** Sets the value of a key. If the key already
313: exists in the header, it's value will be
314: changed. Otherwise a new key/value pair will
315: be added to the end of the header. */
316: public synchronized void set(String k, String v) {
317: for (int i = nkeys; --i >= 0;)
318: if (k.equalsIgnoreCase(keys[i])) {
319: values[i] = v;
320: return;
321: }
322: add(k, v);
323: }
324:
325: /** Set's the value of a key only if there is no
326: * key with that value already.
327: */
328:
329: public synchronized void setIfNotSet(String k, String v) {
330: if (findValue(k) == null) {
331: add(k, v);
332: }
333: }
334:
335: /** Convert a message-id string to canonical form (strips off
336: leading and trailing <>s) */
337: public static String canonicalID(String id) {
338: if (id == null)
339: return "";
340: int st = 0;
341: int len = id.length();
342: boolean substr = false;
343: int c;
344: while (st < len && ((c = id.charAt(st)) == '<' || c <= ' ')) {
345: st++;
346: substr = true;
347: }
348: while (st < len
349: && ((c = id.charAt(len - 1)) == '>' || c <= ' ')) {
350: len--;
351: substr = true;
352: }
353: return substr ? id.substring(st, len) : id;
354: }
355:
356: /** Parse a MIME header from an input stream. */
357: public void parseHeader(InputStream is) throws java.io.IOException {
358: synchronized (this ) {
359: nkeys = 0;
360: }
361: mergeHeader(is);
362: }
363:
364: /** Parse and merge a MIME header from an input stream. */
365: public void mergeHeader(InputStream is) throws java.io.IOException {
366: if (is == null)
367: return;
368: char s[] = new char[10];
369: int firstc = is.read();
370: while (firstc != '\n' && firstc != '\r' && firstc >= 0) {
371: int len = 0;
372: int keyend = -1;
373: int c;
374: boolean inKey = firstc > ' ';
375: s[len++] = (char) firstc;
376: parseloop: {
377: while ((c = is.read()) >= 0) {
378: switch (c) {
379: case ':':
380: if (inKey && len > 0)
381: keyend = len;
382: inKey = false;
383: break;
384: case '\t':
385: c = ' ';
386: case ' ':
387: inKey = false;
388: break;
389: case '\r':
390: case '\n':
391: firstc = is.read();
392: if (c == '\r' && firstc == '\n') {
393: firstc = is.read();
394: if (firstc == '\r')
395: firstc = is.read();
396: }
397: if (firstc == '\n' || firstc == '\r'
398: || firstc > ' ')
399: break parseloop;
400: /* continuation */
401: c = ' ';
402: break;
403: }
404: if (len >= s.length) {
405: char ns[] = new char[s.length * 2];
406: System.arraycopy(s, 0, ns, 0, len);
407: s = ns;
408: }
409: s[len++] = (char) c;
410: }
411: firstc = -1;
412: }
413: while (len > 0 && s[len - 1] <= ' ')
414: len--;
415: String k;
416: if (keyend <= 0) {
417: k = null;
418: keyend = 0;
419: } else {
420: k = String.copyValueOf(s, 0, keyend);
421: if (keyend < len && s[keyend] == ':')
422: keyend++;
423: while (keyend < len && s[keyend] <= ' ')
424: keyend++;
425: }
426: String v;
427: if (keyend >= len)
428: v = new String();
429: else
430: v = String.copyValueOf(s, keyend, len - keyend);
431: add(k, v);
432: }
433: }
434:
435: public synchronized String toString() {
436: String result = super .toString();
437: for (int i = 0; i < keys.length; i++) {
438: result += "{" + keys[i] + ": " + values[i] + "}";
439: }
440: return result;
441: }
442: }
|