Showing the changes to java/pkg/sun/net/www/MessageHeader.java (1 of 18352 files). Show all
Unified · Split
| @@ -0,0 +1,479 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved. | |
| 3 | + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| 4 | + * | |
| 5 | + * This code is free software; you can redistribute it and/or modify it | |
| 6 | + * under the terms of the GNU General Public License version 2 only, as | |
| 7 | + * published by the Free Software Foundation. Oracle designates this | |
| 8 | + * particular file as subject to the "Classpath" exception as provided | |
| 9 | + * by Oracle in the LICENSE file that accompanied this code. | |
| 10 | + * | |
| 11 | + * This code is distributed in the hope that it will be useful, but WITHOUT | |
| 12 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 13 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| 14 | + * version 2 for more details (a copy is included in the LICENSE file that | |
| 15 | + * accompanied this code). | |
| 16 | + * | |
| 17 | + * You should have received a copy of the GNU General Public License version | |
| 18 | + * 2 along with this work; if not, write to the Free Software Foundation, | |
| 19 | + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| 20 | + * | |
| 21 | + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| 22 | + * or visit www.oracle.com if you need additional information or have any | |
| 23 | + * questions. | |
| 24 | + */ | |
| 25 | + | |
| 26 | +/*- | |
| 27 | + * news stream opener | |
| 28 | + */ | |
| 29 | + | |
| 30 | +package sun.net.www; | |
| 31 | + | |
| 32 | +import java.io.*; | |
| 33 | +import java.util.Collections; | |
| 34 | +import java.util.Map; | |
| 35 | +import java.util.HashMap; | |
| 36 | +import java.util.List; | |
| 37 | +import java.util.ArrayList; | |
| 38 | +import java.util.Set; | |
| 39 | +import java.util.Iterator; | |
| 40 | +import java.util.NoSuchElementException; | |
| 41 | + | |
| 42 | +/** An RFC 844 or MIME message header. Includes methods | |
| 43 | + for parsing headers from incoming streams, fetching | |
| 44 | + values, setting values, and printing headers. | |
| 45 | + Key values of null are legal: they indicate lines in | |
| 46 | + the header that don't have a valid key, but do have | |
| 47 | + a value (this isn't legal according to the standard, | |
| 48 | + but lines like this are everywhere). */ | |
| 49 | +public | |
| 50 | +class MessageHeader { | |
| 51 | + private String keys[]; | |
| 52 | + private String values[]; | |
| 53 | + private int nkeys; | |
| 54 | + | |
| 55 | + public MessageHeader () { | |
| 56 | + grow(); | |
| 57 | + } | |
| 58 | + | |
| 59 | + public MessageHeader (InputStream is) throws java.io.IOException { | |
| 60 | + parseHeader(is); | |
| 61 | + } | |
| 62 | + | |
| 63 | + /** | |
| 64 | + * Reset a message header (all key/values removed) | |
| 65 | + */ | |
| 66 | + public synchronized void reset() { | |
| 67 | + keys = null; | |
| 68 | + values = null; | |
| 69 | + nkeys = 0; | |
| 70 | + grow(); | |
| 71 | + } | |
| 72 | + | |
| 73 | + /** | |
| 74 | + * Find the value that corresponds to this key. | |
| 75 | + * It finds only the first occurrence of the key. | |
| 76 | + * @param k the key to find. | |
| 77 | + * @return null if not found. | |
| 78 | + */ | |
| 79 | + public synchronized String findValue(String k) { | |
| 80 | + if (k == null) { | |
| 81 | + for (int i = nkeys; --i >= 0;) | |
| 82 | + if (keys[i] == null) | |
| 83 | + return values[i]; | |
| 84 | + } else | |
| 85 | + for (int i = nkeys; --i >= 0;) { | |
| 86 | + if (k.equalsIgnoreCase(keys[i])) | |
| 87 | + return values[i]; | |
| 88 | + } | |
| 89 | + return null; | |
| 90 | + } | |
| 91 | + | |
| 92 | + // return the location of the key | |
| 93 | + public synchronized int getKey(String k) { | |
| 94 | + for (int i = nkeys; --i >= 0;) | |
| 95 | + if ((keys[i] == k) || | |
| 96 | + (k != null && k.equalsIgnoreCase(keys[i]))) | |
| 97 | + return i; | |
| 98 | + return -1; | |
| 99 | + } | |
| 100 | + | |
| 101 | + public synchronized String getKey(int n) { | |
| 102 | + if (n < 0 || n >= nkeys) return null; | |
| 103 | + return keys[n]; | |
| 104 | + } | |
| 105 | + | |
| 106 | + public synchronized String getValue(int n) { | |
| 107 | + if (n < 0 || n >= nkeys) return null; | |
| 108 | + return values[n]; | |
| 109 | + } | |
| 110 | + | |
| 111 | + /** Deprecated: Use multiValueIterator() instead. | |
| 112 | + * | |
| 113 | + * Find the next value that corresponds to this key. | |
| 114 | + * It finds the first value that follows v. To iterate | |
| 115 | + * over all the values of a key use: | |
| 116 | + * <pre> | |
| 117 | + * for(String v=h.findValue(k); v!=null; v=h.findNextValue(k, v)) { | |
| 118 | + * ... | |
| 119 | + * } | |
| 120 | + * </pre> | |
| 121 | + */ | |
| 122 | + public synchronized String findNextValue(String k, String v) { | |
| 123 | + boolean foundV = false; | |
| 124 | + if (k == null) { | |
| 125 | + for (int i = nkeys; --i >= 0;) | |
| 126 | + if (keys[i] == null) | |
| 127 | + if (foundV) | |
| 128 | + return values[i]; | |
| 129 | + else if (values[i] == v) | |
| 130 | + foundV = true; | |
| 131 | + } else | |
| 132 | + for (int i = nkeys; --i >= 0;) | |
| 133 | + if (k.equalsIgnoreCase(keys[i])) | |
| 134 | + if (foundV) | |
| 135 | + return values[i]; | |
| 136 | + else if (values[i] == v) | |
| 137 | + foundV = true; | |
| 138 | + return null; | |
| 139 | + } | |
| 140 | + | |
| 141 | + class HeaderIterator implements Iterator<String> { | |
| 142 | + int index = 0; | |
| 143 | + int next = -1; | |
| 144 | + String key; | |
| 145 | + boolean haveNext = false; | |
| 146 | + Object lock; | |
| 147 | + | |
| 148 | + public HeaderIterator (String k, Object lock) { | |
| 149 | + key = k; | |
| 150 | + this.lock = lock; | |
| 151 | + } | |
| 152 | + public boolean hasNext () { | |
| 153 | + synchronized (lock) { | |
| 154 | + if (haveNext) { | |
| 155 | + return true; | |
| 156 | + } | |
| 157 | + while (index < nkeys) { | |
| 158 | + if (key.equalsIgnoreCase (keys[index])) { | |
| 159 | + haveNext = true; | |
| 160 | + next = index++; | |
| 161 | + return true; | |
| 162 | + } | |
| 163 | + index ++; | |
| 164 | + } | |
| 165 | + return false; | |
| 166 | + } | |
| 167 | + } | |
| 168 | + public String next() { | |
| 169 | + synchronized (lock) { | |
| 170 | + if (haveNext) { | |
| 171 | + haveNext = false; | |
| 172 | + return values [next]; | |
| 173 | + } | |
| 174 | + if (hasNext()) { | |
| 175 | + return next(); | |
| 176 | + } else { | |
| 177 | + throw new NoSuchElementException ("No more elements"); | |
| 178 | + } | |
| 179 | + } | |
| 180 | + } | |
| 181 | + public void remove () { | |
| 182 | + throw new UnsupportedOperationException ("remove not allowed"); | |
| 183 | + } | |
| 184 | + } | |
| 185 | + | |
| 186 | + /** | |
| 187 | + * return an Iterator that returns all values of a particular | |
| 188 | + * key in sequence | |
| 189 | + */ | |
| 190 | + public Iterator<String> multiValueIterator (String k) { | |
| 191 | + return new HeaderIterator (k, this); | |
| 192 | + } | |
| 193 | + | |
| 194 | + public synchronized Map<String, List<String>> getHeaders() { | |
| 195 | + return getHeaders(null); | |
| 196 | + } | |
| 197 | + | |
| 198 | + public synchronized Map<String, List<String>> getHeaders(String[] excludeList) { | |
| 199 | + return filterAndAddHeaders(excludeList, null); | |
| 200 | + } | |
| 201 | + | |
| 202 | + public synchronized Map<String, List<String>> filterAndAddHeaders(String[] excludeList, Map<String, List<String>> include) { | |
| 203 | + boolean skipIt = false; | |
| 204 | + Map<String, List<String>> m = new HashMap<String, List<String>>(); | |
| 205 | + for (int i = nkeys; --i >= 0;) { | |
| 206 | + if (excludeList != null) { | |
| 207 | + // check if the key is in the excludeList. | |
| 208 | + // if so, don't include it in the Map. | |
| 209 | + for (int j = 0; j < excludeList.length; j++) { | |
| 210 | + if ((excludeList[j] != null) && | |
| 211 | + (excludeList[j].equalsIgnoreCase(keys[i]))) { | |
| 212 | + skipIt = true; | |
| 213 | + break; | |
| 214 | + } | |
| 215 | + } | |
| 216 | + } | |
| 217 | + if (!skipIt) { | |
| 218 | + List<String> l = m.get(keys[i]); | |
| 219 | + if (l == null) { | |
| 220 | + l = new ArrayList<String>(); | |
| 221 | + m.put(keys[i], l); | |
| 222 | + } | |
| 223 | + l.add(values[i]); | |
| 224 | + } else { | |
| 225 | + // reset the flag | |
| 226 | + skipIt = false; | |
| 227 | + } | |
| 228 | + } | |
| 229 | + | |
| 230 | + if (include != null) { | |
| 231 | + Iterator entries = include.entrySet().iterator(); | |
| 232 | + while (entries.hasNext()) { | |
| 233 | + Map.Entry entry = (Map.Entry)entries.next(); | |
| 234 | + List l = (List)m.get(entry.getKey()); | |
| 235 | + if (l == null) { | |
| 236 | + l = new ArrayList(); | |
| 237 | + m.put((String)entry.getKey(), l); | |
| 238 | + } | |
| 239 | + l.add(entry.getValue()); | |
| 240 | + } | |
| 241 | + } | |
| 242 | + | |
| 243 | + for (String key : m.keySet()) { | |
| 244 | + m.put(key, Collections.unmodifiableList(m.get(key))); | |
| 245 | + } | |
| 246 | + | |
| 247 | + return Collections.unmodifiableMap(m); | |
| 248 | + } | |
| 249 | + | |
| 250 | + /** Prints the key-value pairs represented by this | |
| 251 | + header. Also prints the RFC required blank line | |
| 252 | + at the end. Omits pairs with a null key. */ | |
| 253 | + public synchronized void print(PrintStream p) { | |
| 254 | + for (int i = 0; i < nkeys; i++) | |
| 255 | + if (keys[i] != null) { | |
| 256 | + p.print(keys[i] + | |
| 257 | + (values[i] != null ? ": "+values[i]: "") + "\r\n"); | |
| 258 | + } | |
| 259 | + p.print("\r\n"); | |
| 260 | + p.flush(); | |
| 261 | + } | |
| 262 | + | |
| 263 | + /** Adds a key value pair to the end of the | |
| 264 | + header. Duplicates are allowed */ | |
| 265 | + public synchronized void add(String k, String v) { | |
| 266 | + grow(); | |
| 267 | + keys[nkeys] = k; | |
| 268 | + values[nkeys] = v; | |
| 269 | + nkeys++; | |
| 270 | + } | |
| 271 | + | |
| 272 | + /** Prepends a key value pair to the beginning of the | |
| 273 | + header. Duplicates are allowed */ | |
| 274 | + public synchronized void prepend(String k, String v) { | |
| 275 | + grow(); | |
| 276 | + for (int i = nkeys; i > 0; i--) { | |
| 277 | + keys[i] = keys[i-1]; | |
| 278 | + values[i] = values[i-1]; | |
| 279 | + } | |
| 280 | + keys[0] = k; | |
| 281 | + values[0] = v; | |
| 282 | + nkeys++; | |
| 283 | + } | |
| 284 | + | |
| 285 | + /** Overwrite the previous key/val pair at location 'i' | |
| 286 | + * with the new k/v. If the index didn't exist before | |
| 287 | + * the key/val is simply tacked onto the end. | |
| 288 | + */ | |
| 289 | + | |
| 290 | + public synchronized void set(int i, String k, String v) { | |
| 291 | + grow(); | |
| 292 | + if (i < 0) { | |
| 293 | + return; | |
| 294 | + } else if (i >= nkeys) { | |
| 295 | + add(k, v); | |
| 296 | + } else { | |
| 297 | + keys[i] = k; | |
| 298 | + values[i] = v; | |
| 299 | + } | |
| 300 | + } | |
| 301 | + | |
| 302 | + | |
| 303 | + /** grow the key/value arrays as needed */ | |
| 304 | + | |
| 305 | + private void grow() { | |
| 306 | + if (keys == null || nkeys >= keys.length) { | |
| 307 | + String[] nk = new String[nkeys + 4]; | |
| 308 | + String[] nv = new String[nkeys + 4]; | |
| 309 | + if (keys != null) | |
| 310 | + System.arraycopy(keys, 0, nk, 0, nkeys); | |
| 311 | + if (values != null) | |
| 312 | + System.arraycopy(values, 0, nv, 0, nkeys); | |
| 313 | + keys = nk; | |
| 314 | + values = nv; | |
| 315 | + } | |
| 316 | + } | |
| 317 | + | |
| 318 | + /** | |
| 319 | + * Remove the key from the header. If there are multiple values under | |
| 320 | + * the same key, they are all removed. | |
| 321 | + * Nothing is done if the key doesn't exist. | |
| 322 | + * After a remove, the other pairs' order are not changed. | |
| 323 | + * @param k the key to remove | |
| 324 | + */ | |
| 325 | + public synchronized void remove(String k) { | |
| 326 | + if(k == null) { | |
| 327 | + for (int i = 0; i < nkeys; i++) { | |
| 328 | + while (keys[i] == null && i < nkeys) { | |
| 329 | + for(int j=i; j<nkeys-1; j++) { | |
| 330 | + keys[j] = keys[j+1]; | |
| 331 | + values[j] = values[j+1]; | |
| 332 | + } | |
| 333 | + nkeys--; | |
| 334 | + } | |
| 335 | + } | |
| 336 | + } else { | |
| 337 | + for (int i = 0; i < nkeys; i++) { | |
| 338 | + while (k.equalsIgnoreCase(keys[i]) && i < nkeys) { | |
| 339 | + for(int j=i; j<nkeys-1; j++) { | |
| 340 | + keys[j] = keys[j+1]; | |
| 341 | + values[j] = values[j+1]; | |
| 342 | + } | |
| 343 | + nkeys--; | |
| 344 | + } | |
| 345 | + } | |
| 346 | + } | |
| 347 | + } | |
| 348 | + | |
| 349 | + /** Sets the value of a key. If the key already | |
| 350 | + exists in the header, it's value will be | |
| 351 | + changed. Otherwise a new key/value pair will | |
| 352 | + be added to the end of the header. */ | |
| 353 | + public synchronized void set(String k, String v) { | |
| 354 | + for (int i = nkeys; --i >= 0;) | |
| 355 | + if (k.equalsIgnoreCase(keys[i])) { | |
| 356 | + values[i] = v; | |
| 357 | + return; | |
| 358 | + } | |
| 359 | + add(k, v); | |
| 360 | + } | |
| 361 | + | |
| 362 | + /** Set's the value of a key only if there is no | |
| 363 | + * key with that value already. | |
| 364 | + */ | |
| 365 | + | |
| 366 | + public synchronized void setIfNotSet(String k, String v) { | |
| 367 | + if (findValue(k) == null) { | |
| 368 | + add(k, v); | |
| 369 | + } | |
| 370 | + } | |
| 371 | + | |
| 372 | + /** Convert a message-id string to canonical form (strips off | |
| 373 | + leading and trailing <>s) */ | |
| 374 | + public static String canonicalID(String id) { | |
| 375 | + if (id == null) | |
| 376 | + return ""; | |
| 377 | + int st = 0; | |
| 378 | + int len = id.length(); | |
| 379 | + boolean substr = false; | |
| 380 | + int c; | |
| 381 | + while (st < len && ((c = id.charAt(st)) == '<' || | |
| 382 | + c <= ' ')) { | |
| 383 | + st++; | |
| 384 | + substr = true; | |
| 385 | + } | |
| 386 | + while (st < len && ((c = id.charAt(len - 1)) == '>' || | |
| 387 | + c <= ' ')) { | |
| 388 | + len--; | |
| 389 | + substr = true; | |
| 390 | + } | |
| 391 | + return substr ? id.substring(st, len) : id; | |
| 392 | + } | |
| 393 | + | |
| 394 | + /** Parse a MIME header from an input stream. */ | |
| 395 | + public void parseHeader(InputStream is) throws java.io.IOException { | |
| 396 | + synchronized (this) { | |
| 397 | + nkeys = 0; | |
| 398 | + } | |
| 399 | + mergeHeader(is); | |
| 400 | + } | |
| 401 | + | |
| 402 | + /** Parse and merge a MIME header from an input stream. */ | |
| 403 | + public void mergeHeader(InputStream is) throws java.io.IOException { | |
| 404 | + if (is == null) | |
| 405 | + return; | |
| 406 | + char s[] = new char[10]; | |
| 407 | + int firstc = is.read(); | |
| 408 | + while (firstc != '\n' && firstc != '\r' && firstc >= 0) { | |
| 409 | + int len = 0; | |
| 410 | + int keyend = -1; | |
| 411 | + int c; | |
| 412 | + boolean inKey = firstc > ' '; | |
| 413 | + s[len++] = (char) firstc; | |
| 414 | + parseloop:{ | |
| 415 | + while ((c = is.read()) >= 0) { | |
| 416 | + switch (c) { | |
| 417 | + case ':': | |
| 418 | + if (inKey && len > 0) | |
| 419 | + keyend = len; | |
| 420 | + inKey = false; | |
| 421 | + break; | |
| 422 | + case '\t': | |
| 423 | + c = ' '; | |
| 424 | + case ' ': | |
| 425 | + inKey = false; | |
| 426 | + break; | |
| 427 | + case '\r': | |
| 428 | + case '\n': | |
| 429 | + firstc = is.read(); | |
| 430 | + if (c == '\r' && firstc == '\n') { | |
| 431 | + firstc = is.read(); | |
| 432 | + if (firstc == '\r') | |
| 433 | + firstc = is.read(); | |
| 434 | + } | |
| 435 | + if (firstc == '\n' || firstc == '\r' || firstc > ' ') | |
| 436 | + break parseloop; | |
| 437 | + /* continuation */ | |
| 438 | + c = ' '; | |
| 439 | + break; | |
| 440 | + } | |
| 441 | + if (len >= s.length) { | |
| 442 | + char ns[] = new char[s.length * 2]; | |
| 443 | + System.arraycopy(s, 0, ns, 0, len); | |
| 444 | + s = ns; | |
| 445 | + } | |
| 446 | + s[len++] = (char) c; | |
| 447 | + } | |
| 448 | + firstc = -1; | |
| 449 | + } | |
| 450 | + while (len > 0 && s[len - 1] <= ' ') | |
| 451 | + len--; | |
| 452 | + String k; | |
| 453 | + if (keyend <= 0) { | |
| 454 | + k = null; | |
| 455 | + keyend = 0; | |
| 456 | + } else { | |
| 457 | + k = String.copyValueOf(s, 0, keyend); | |
| 458 | + if (keyend < len && s[keyend] == ':') | |
| 459 | + keyend++; | |
| 460 | + while (keyend < len && s[keyend] <= ' ') | |
| 461 | + keyend++; | |
| 462 | + } | |
| 463 | + String v; | |
| 464 | + if (keyend >= len) | |
| 465 | + v = new String(); | |
| 466 | + else | |
| 467 | + v = String.copyValueOf(s, keyend, len - keyend); | |
| 468 | + add(k, v); | |
| 469 | + } | |
| 470 | + } | |
| 471 | + | |
| 472 | + public synchronized String toString() { | |
| 473 | + String result = super.toString() + nkeys + " pairs: "; | |
| 474 | + for (int i = 0; i < keys.length && i < nkeys; i++) { | |
| 475 | + result += "{"+keys[i]+": "+values[i]+"}"; | |
| 476 | + } | |
| 477 | + return result; | |
| 478 | + } | |
| 479 | +} | |