MessageHeader.java 14.3 KB
Newer Older
D
duke 已提交
1
/*
X
xdono 已提交
2
 * Copyright 1995-2008 Sun Microsystems, Inc.  All Rights Reserved.
D
duke 已提交
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */

/*-
 *      news stream opener
 */

package sun.net.www;

import java.io.*;
import java.util.Collections;
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.Set;
import java.util.Iterator;
import java.util.NoSuchElementException;

/** An RFC 844 or MIME message header.  Includes methods
    for parsing headers from incoming streams, fetching
    values, setting values, and printing headers.
    Key values of null are legal: they indicate lines in
    the header that don't have a valid key, but do have
    a value (this isn't legal according to the standard,
    but lines like this are everywhere). */
public
class MessageHeader {
    private String keys[];
    private String values[];
    private int nkeys;

    public MessageHeader () {
        grow();
    }

    public MessageHeader (InputStream is) throws java.io.IOException {
        parseHeader(is);
    }

    /**
     * Reset a message header (all key/values removed)
     */
    public synchronized void reset() {
        keys = null;
        values = null;
        nkeys = 0;
        grow();
    }

    /**
     * Find the value that corresponds to this key.
     * It finds only the first occurrence of the key.
     * @param k the key to find.
     * @return null if not found.
     */
    public synchronized String findValue(String k) {
        if (k == null) {
            for (int i = nkeys; --i >= 0;)
                if (keys[i] == null)
                    return values[i];
        } else
            for (int i = nkeys; --i >= 0;) {
                if (k.equalsIgnoreCase(keys[i]))
                    return values[i];
            }
        return null;
    }

    // return the location of the key
    public synchronized int getKey(String k) {
        for (int i = nkeys; --i >= 0;)
            if ((keys[i] == k) ||
                (k != null && k.equalsIgnoreCase(keys[i])))
                return i;
        return -1;
    }

    public synchronized String getKey(int n) {
        if (n < 0 || n >= nkeys) return null;
        return keys[n];
    }

    public synchronized String getValue(int n) {
        if (n < 0 || n >= nkeys) return null;
        return values[n];
    }

    /** Deprecated: Use multiValueIterator() instead.
     *
     *  Find the next value that corresponds to this key.
     *  It finds the first value that follows v. To iterate
     *  over all the values of a key use:
     *  <pre>
     *          for(String v=h.findValue(k); v!=null; v=h.findNextValue(k, v)) {
     *              ...
     *          }
     *  </pre>
     */
    public synchronized String findNextValue(String k, String v) {
        boolean foundV = false;
        if (k == null) {
            for (int i = nkeys; --i >= 0;)
                if (keys[i] == null)
                    if (foundV)
                        return values[i];
                    else if (values[i] == v)
                        foundV = true;
        } else
            for (int i = nkeys; --i >= 0;)
                if (k.equalsIgnoreCase(keys[i]))
                    if (foundV)
                        return values[i];
                    else if (values[i] == v)
                        foundV = true;
        return null;
    }

141
    class HeaderIterator implements Iterator<String> {
D
duke 已提交
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
        int index = 0;
        int next = -1;
        String key;
        boolean haveNext = false;
        Object lock;

        public HeaderIterator (String k, Object lock) {
            key = k;
            this.lock = lock;
        }
        public boolean hasNext () {
            synchronized (lock) {
                if (haveNext) {
                    return true;
                }
                while (index < nkeys) {
                    if (key.equalsIgnoreCase (keys[index])) {
                        haveNext = true;
                        next = index++;
                        return true;
                    }
                    index ++;
                }
                return false;
            }
        }
168
        public String next() {
D
duke 已提交
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
            synchronized (lock) {
                if (haveNext) {
                    haveNext = false;
                    return values [next];
                }
                if (hasNext()) {
                    return next();
                } else {
                    throw new NoSuchElementException ("No more elements");
                }
            }
        }
        public void remove () {
            throw new UnsupportedOperationException ("remove not allowed");
        }
    }

    /**
     * return an Iterator that returns all values of a particular
     * key in sequence
     */
190
    public Iterator<String> multiValueIterator (String k) {
D
duke 已提交
191 192 193
        return new HeaderIterator (k, this);
    }

194
    public synchronized Map<String, List<String>> getHeaders() {
D
duke 已提交
195 196 197
        return getHeaders(null);
    }

198
    public synchronized Map<String, List<String>> getHeaders(String[] excludeList) {
D
duke 已提交
199
        boolean skipIt = false;
200
        Map<String, List<String>> m = new HashMap<String, List<String>>();
D
duke 已提交
201 202 203 204 205 206 207 208 209 210 211 212 213
        for (int i = nkeys; --i >= 0;) {
            if (excludeList != null) {
                // check if the key is in the excludeList.
                // if so, don't include it in the Map.
                for (int j = 0; j < excludeList.length; j++) {
                    if ((excludeList[j] != null) &&
                        (excludeList[j].equalsIgnoreCase(keys[i]))) {
                        skipIt = true;
                        break;
                    }
                }
            }
            if (!skipIt) {
214
                List<String> l = m.get(keys[i]);
D
duke 已提交
215
                if (l == null) {
216
                    l = new ArrayList<String>();
D
duke 已提交
217 218 219 220 221 222 223 224 225
                    m.put(keys[i], l);
                }
                l.add(values[i]);
            } else {
                // reset the flag
                skipIt = false;
            }
        }

226 227
        for (String key : m.keySet()) {
            m.put(key, Collections.unmodifiableList(m.get(key)));
D
duke 已提交
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462
        }

        return Collections.unmodifiableMap(m);
    }

    /** Prints the key-value pairs represented by this
        header.  Also prints the RFC required blank line
        at the end. Omits pairs with a null key. */
    public synchronized void print(PrintStream p) {
        for (int i = 0; i < nkeys; i++)
            if (keys[i] != null) {
                p.print(keys[i] +
                    (values[i] != null ? ": "+values[i]: "") + "\r\n");
            }
        p.print("\r\n");
        p.flush();
    }

    /** Adds a key value pair to the end of the
        header.  Duplicates are allowed */
    public synchronized void add(String k, String v) {
        grow();
        keys[nkeys] = k;
        values[nkeys] = v;
        nkeys++;
    }

    /** Prepends a key value pair to the beginning of the
        header.  Duplicates are allowed */
    public synchronized void prepend(String k, String v) {
        grow();
        for (int i = nkeys; i > 0; i--) {
            keys[i] = keys[i-1];
            values[i] = values[i-1];
        }
        keys[0] = k;
        values[0] = v;
        nkeys++;
    }

    /** Overwrite the previous key/val pair at location 'i'
     * with the new k/v.  If the index didn't exist before
     * the key/val is simply tacked onto the end.
     */

    public synchronized void set(int i, String k, String v) {
        grow();
        if (i < 0) {
            return;
        } else if (i >= nkeys) {
            add(k, v);
        } else {
            keys[i] = k;
            values[i] = v;
        }
    }


    /** grow the key/value arrays as needed */

    private void grow() {
        if (keys == null || nkeys >= keys.length) {
            String[] nk = new String[nkeys + 4];
            String[] nv = new String[nkeys + 4];
            if (keys != null)
                System.arraycopy(keys, 0, nk, 0, nkeys);
            if (values != null)
                System.arraycopy(values, 0, nv, 0, nkeys);
            keys = nk;
            values = nv;
        }
    }

    /**
     * Remove the key from the header. If there are multiple values under
     * the same key, they are all removed.
     * Nothing is done if the key doesn't exist.
     * After a remove, the other pairs' order are not changed.
     * @param k the key to remove
     */
    public synchronized void remove(String k) {
        if(k == null) {
            for (int i = 0; i < nkeys; i++) {
                while (keys[i] == null && i < nkeys) {
                    for(int j=i; j<nkeys-1; j++) {
                        keys[j] = keys[j+1];
                        values[j] = values[j+1];
                    }
                    nkeys--;
                }
            }
        } else {
            for (int i = 0; i < nkeys; i++) {
                while (k.equalsIgnoreCase(keys[i]) && i < nkeys) {
                    for(int j=i; j<nkeys-1; j++) {
                        keys[j] = keys[j+1];
                        values[j] = values[j+1];
                    }
                    nkeys--;
                }
            }
        }
    }

    /** Sets the value of a key.  If the key already
        exists in the header, it's value will be
        changed.  Otherwise a new key/value pair will
        be added to the end of the header. */
    public synchronized void set(String k, String v) {
        for (int i = nkeys; --i >= 0;)
            if (k.equalsIgnoreCase(keys[i])) {
                values[i] = v;
                return;
            }
        add(k, v);
    }

    /** Set's the value of a key only if there is no
     *  key with that value already.
     */

    public synchronized void setIfNotSet(String k, String v) {
        if (findValue(k) == null) {
            add(k, v);
        }
    }

    /** Convert a message-id string to canonical form (strips off
        leading and trailing <>s) */
    public static String canonicalID(String id) {
        if (id == null)
            return "";
        int st = 0;
        int len = id.length();
        boolean substr = false;
        int c;
        while (st < len && ((c = id.charAt(st)) == '<' ||
                            c <= ' ')) {
            st++;
            substr = true;
        }
        while (st < len && ((c = id.charAt(len - 1)) == '>' ||
                            c <= ' ')) {
            len--;
            substr = true;
        }
        return substr ? id.substring(st, len) : id;
    }

    /** Parse a MIME header from an input stream. */
    public void parseHeader(InputStream is) throws java.io.IOException {
        synchronized (this) {
            nkeys = 0;
        }
        mergeHeader(is);
    }

    /** Parse and merge a MIME header from an input stream. */
    public void mergeHeader(InputStream is) throws java.io.IOException {
        if (is == null)
            return;
        char s[] = new char[10];
        int firstc = is.read();
        while (firstc != '\n' && firstc != '\r' && firstc >= 0) {
            int len = 0;
            int keyend = -1;
            int c;
            boolean inKey = firstc > ' ';
            s[len++] = (char) firstc;
    parseloop:{
                while ((c = is.read()) >= 0) {
                    switch (c) {
                      case ':':
                        if (inKey && len > 0)
                            keyend = len;
                        inKey = false;
                        break;
                      case '\t':
                        c = ' ';
                      case ' ':
                        inKey = false;
                        break;
                      case '\r':
                      case '\n':
                        firstc = is.read();
                        if (c == '\r' && firstc == '\n') {
                            firstc = is.read();
                            if (firstc == '\r')
                                firstc = is.read();
                        }
                        if (firstc == '\n' || firstc == '\r' || firstc > ' ')
                            break parseloop;
                        /* continuation */
                        c = ' ';
                        break;
                    }
                    if (len >= s.length) {
                        char ns[] = new char[s.length * 2];
                        System.arraycopy(s, 0, ns, 0, len);
                        s = ns;
                    }
                    s[len++] = (char) c;
                }
                firstc = -1;
            }
            while (len > 0 && s[len - 1] <= ' ')
                len--;
            String k;
            if (keyend <= 0) {
                k = null;
                keyend = 0;
            } else {
                k = String.copyValueOf(s, 0, keyend);
                if (keyend < len && s[keyend] == ':')
                    keyend++;
                while (keyend < len && s[keyend] <= ' ')
                    keyend++;
            }
            String v;
            if (keyend >= len)
                v = new String();
            else
                v = String.copyValueOf(s, keyend, len - keyend);
            add(k, v);
        }
    }

    public synchronized String toString() {
        String result = super.toString() + nkeys + " pairs: ";
        for (int i = 0; i < keys.length && i < nkeys; i++) {
            result += "{"+keys[i]+": "+values[i]+"}";
        }
        return result;
    }
}