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 | } | ||