Clover coverage report - Maven Clover report
Coverage timestamp: Sun Aug 20 2006 04:01:04 PDT
file stats: LOC: 467   Methods: 25
NCLOC: 306   Classes: 3
 
 Source file Conditionals Statements Methods TOTAL
InternetHeaders.java 47.6% 59.9% 52% 55.7%
coverage coverage
 1    /**
 2    *
 3    * Copyright 2003-2004 The Apache Software Foundation
 4    *
 5    * Licensed under the Apache License, Version 2.0 (the "License");
 6    * you may not use this file except in compliance with the License.
 7    * You may obtain a copy of the License at
 8    *
 9    * http://www.apache.org/licenses/LICENSE-2.0
 10    *
 11    * Unless required by applicable law or agreed to in writing, software
 12    * distributed under the License is distributed on an "AS IS" BASIS,
 13    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14    * See the License for the specific language governing permissions and
 15    * limitations under the License.
 16    */
 17   
 18    package javax.mail.internet;
 19   
 20    import java.io.IOException;
 21    import java.io.InputStream;
 22    import java.io.OutputStream;
 23    import java.util.ArrayList;
 24    import java.util.Arrays;
 25    import java.util.Collections;
 26    import java.util.Enumeration;
 27    import java.util.HashSet;
 28    import java.util.Iterator;
 29    import java.util.LinkedHashMap;
 30    import java.util.List;
 31    import java.util.Map;
 32    import java.util.Set;
 33   
 34    import javax.mail.Address;
 35    import javax.mail.Header;
 36    import javax.mail.MessagingException;
 37   
 38    /**
 39    * Class that represents the RFC822 headers associated with a message.
 40    *
 41    * @version $Rev: 398618 $ $Date: 2006-05-01 08:18:57 -0700 (Mon, 01 May 2006) $
 42    */
 43    public class InternetHeaders {
 44    // RFC822 imposes an ordering on its headers so we use a LinkedHashedMap
 45    private final LinkedHashMap headers = new LinkedHashMap();
 46   
 47    private transient String lastHeaderName;
 48   
 49    /**
 50    * Create an empty InternetHeaders
 51    */
 52  35 public InternetHeaders() {
 53    // we need to initialize the headers in the correct order
 54    // fields: dates source destination optional-field
 55    // dates:
 56  35 setHeaderList("Date", null);
 57  35 setHeaderList("Resent-Date", null);
 58    // source: trace originator resent
 59    // trace: return received
 60  35 setHeaderList("Return-path", null);
 61  35 setHeaderList("Received", null);
 62    // originator: authentic reply-to
 63  35 setHeaderList("Sender", null);
 64  35 setHeaderList("From", null);
 65  35 setHeaderList("Reply-To", null);
 66    // resent: resent-authentic resent-reply-to
 67  35 setHeaderList("Resent-Sender", null);
 68  35 setHeaderList("Resent-From", null);
 69  35 setHeaderList("Resent-Reply-To", null);
 70    // destination:
 71  35 setHeaderList("To", null);
 72  35 setHeaderList("Resent-To", null);
 73  35 setHeaderList("cc", null);
 74  35 setHeaderList("Resent-cc", null);
 75  35 setHeaderList("bcc", null);
 76  35 setHeaderList("Resent-bcc", null);
 77    // optional-field:
 78  35 setHeaderList("Message-ID", null);
 79  35 setHeaderList("Resent-Message-ID", null);
 80  35 setHeaderList("In-Reply-To", null);
 81  35 setHeaderList("References", null);
 82  35 setHeaderList("Keywords", null);
 83  35 setHeaderList("Subject", null);
 84  35 setHeaderList("Comments", null);
 85  35 setHeaderList("Encrypted", null);
 86    }
 87   
 88    /**
 89    * Create a new InternetHeaders initialized by reading headers from the
 90    * stream.
 91    *
 92    * @param in
 93    * the RFC822 input stream to load from
 94    * @throws MessagingException
 95    * if there is a problem pasring the stream
 96    */
 97  5 public InternetHeaders(InputStream in) throws MessagingException {
 98  5 load(in);
 99    }
 100   
 101    /**
 102    * Read and parse the supplied stream and add all headers to the current
 103    * set.
 104    *
 105    * @param in
 106    * the RFC822 input stream to load from
 107    * @throws MessagingException
 108    * if there is a problem pasring the stream
 109    */
 110  6 public void load(InputStream in) throws MessagingException {
 111  6 try {
 112  6 StringBuffer name = new StringBuffer(32);
 113  6 StringBuffer value = new StringBuffer(128);
 114  6 done: while (true) {
 115  23 int c = in.read();
 116  23 char ch = (char) c;
 117  23 if (c == -1) {
 118  0 break;
 119  23 } else if (c == 13) {
 120    // empty line terminates header
 121  6 in.read(); // skip LF
 122  6 break;
 123  17 } else if (Character.isWhitespace(ch)) {
 124    // handle continuation
 125  0 do {
 126  0 c = in.read();
 127  0 if (c == -1) {
 128  0 break done;
 129    }
 130  0 ch = (char) c;
 131  0 } while (Character.isWhitespace(ch));
 132    } else {
 133    // new header
 134  17 if (name.length() > 0) {
 135  11 addHeader(name.toString().trim(), value.toString().trim());
 136    }
 137  17 name.setLength(0);
 138  17 value.setLength(0);
 139  17 while (true) {
 140  196 name.append((char) c);
 141  196 c = in.read();
 142  196 if (c == -1) {
 143  0 break done;
 144  196 } else if (c == ':') {
 145  17 break;
 146    }
 147    }
 148  17 c = in.read();
 149  17 if (c == -1) {
 150  0 break done;
 151    }
 152    }
 153   
 154  17 while (c != 13) {
 155  326 ch = (char) c;
 156  326 value.append(ch);
 157  326 c = in.read();
 158  326 if (c == -1) {
 159  0 break done;
 160    }
 161    }
 162    // skip LF
 163  17 c = in.read();
 164  17 if (c == -1) {
 165  0 break;
 166    }
 167    }
 168  6 if (name.length() > 0) {
 169  6 addHeader(name.toString().trim(), value.toString().trim());
 170    }
 171    } catch (IOException e) {
 172  0 throw new MessagingException("Error loading headers", e);
 173    }
 174    }
 175   
 176    /**
 177    * Return all the values for the specified header.
 178    *
 179    * @param name
 180    * the header to return
 181    * @return the values for that header, or null if the header is not present
 182    */
 183  73 public String[] getHeader(String name) {
 184  73 List headers = getHeaderList(name);
 185  73 if (headers == null) {
 186  30 return null;
 187    } else {
 188  43 String[] result = new String[headers.size()];
 189  43 for (int i = 0; i < headers.size(); i++) {
 190  40 InternetHeader header = (InternetHeader) headers.get(i);
 191  40 result[i] = header.getValue();
 192    }
 193  43 return result;
 194    }
 195    }
 196   
 197    /**
 198    * Return the values for the specified header as a single String. If the
 199    * header has more than one value then all values are concatenated together
 200    * separated by the supplied delimiter.
 201    *
 202    * @param name
 203    * the header to return
 204    * @param delimiter
 205    * the delimiter used in concatenation
 206    * @return the header as a single String
 207    */
 208  78 public String getHeader(String name, String delimiter) {
 209  78 List list = getHeaderList(name);
 210  78 if (list == null) {
 211  20 return null;
 212  58 } else if (list.isEmpty()) {
 213  12 return "";
 214  46 } else if (list.size() == 1 || delimiter == null) {
 215  32 return ((InternetHeader) list.get(0)).getValue();
 216    } else {
 217  14 StringBuffer buf = new StringBuffer(20 * list.size());
 218  14 buf.append(((InternetHeader) list.get(0)).getValue());
 219  14 for (int i = 1; i < list.size(); i++) {
 220  14 buf.append(delimiter);
 221  14 buf.append(((InternetHeader) list.get(i)).getValue());
 222    }
 223  14 return buf.toString();
 224    }
 225    }
 226   
 227    /**
 228    * Set the value of the header to the supplied value; any existing headers
 229    * are removed.
 230    *
 231    * @param name
 232    * the name of the header
 233    * @param value
 234    * the new value
 235    */
 236  41 public void setHeader(String name, String value) {
 237  41 List list = new ArrayList();
 238  41 list.add(new InternetHeader(name, value));
 239  41 setHeaderList(name, list);
 240    }
 241   
 242    /**
 243    * Add a new value to the header with the supplied name.
 244    *
 245    * @param name
 246    * the name of the header to add a new value for
 247    * @param value
 248    * another value
 249    */
 250  29 public void addHeader(String name, String value) {
 251  29 List list = getHeaderList(name);
 252  29 if (list == null) {
 253  20 list = new ArrayList();
 254  20 headers.put(name.toLowerCase(), list);
 255    }
 256  29 list.add(new InternetHeader(name, value));
 257    }
 258   
 259    /**
 260    * Remove all header entries with the supplied name
 261    *
 262    * @param name
 263    * the header to remove
 264    */
 265  33 public void removeHeader(String name) {
 266  33 List list = getHeaderList(name);
 267    // it's possible we might not have the named header. This is a nop if the header doesn't exist.
 268  33 if (list != null) {
 269  15 list.clear();
 270    }
 271    }
 272   
 273    /**
 274    * Return all headers.
 275    *
 276    * @return an Enumeration<Header> containing all headers
 277    */
 278  0 public Enumeration getAllHeaders() {
 279  0 List result = new ArrayList(headers.size() * 2);
 280  0 Iterator it = headers.values().iterator();
 281  0 while (it.hasNext()) {
 282  0 List list = (List) it.next();
 283  0 if (list != null) {
 284  0 result.addAll(list);
 285    }
 286    }
 287  0 return Collections.enumeration(result);
 288    }
 289   
 290    /**
 291    * Return all matching Header objects.
 292    */
 293  0 public Enumeration getMatchingHeaders(String[] names) {
 294  0 Set include = new HashSet(names.length);
 295  0 for (int i = 0; i < names.length; i++) {
 296  0 String name = names[i];
 297  0 include.add(name.toLowerCase());
 298    }
 299  0 List result = new ArrayList(headers.size());
 300  0 for (Iterator i = headers.entrySet().iterator(); i.hasNext();) {
 301  0 Map.Entry entry = (Map.Entry) i.next();
 302  0 if (entry.getValue() != null && include.contains(((String) entry.getKey()).toLowerCase())) {
 303  0 result.addAll((List) entry.getValue());
 304    }
 305    }
 306  0 return Collections.enumeration(result);
 307    }
 308   
 309    /**
 310    * Return all non matching Header objects.
 311    */
 312  0 public Enumeration getNonMatchingHeaders(String[] names) {
 313  0 Set exclude = new HashSet(names.length);
 314  0 for (int i = 0; i < names.length; i++) {
 315  0 String name = names[i];
 316  0 exclude.add(name.toLowerCase());
 317    }
 318  0 List result = new ArrayList(headers.size());
 319  0 for (Iterator i = headers.entrySet().iterator(); i.hasNext();) {
 320  0 Map.Entry entry = (Map.Entry) i.next();
 321  0 if (entry.getValue() != null && !exclude.contains(((String) entry.getKey()).toLowerCase())) {
 322  0 result.addAll((List) entry.getValue());
 323    }
 324    }
 325  0 return Collections.enumeration(result);
 326    }
 327   
 328    /**
 329    * Add an RFC822 header line to the header store. If the line starts with a
 330    * space or tab (a continuation line), add it to the last header line in the
 331    * list. Otherwise, append the new header line to the list.
 332    *
 333    * Note that RFC822 headers can only contain US-ASCII characters
 334    *
 335    * @param line
 336    * raw RFC822 header line
 337    */
 338  0 public void addHeaderLine(String line) {
 339  0 StringBuffer name = new StringBuffer(32);
 340  0 StringBuffer value = new StringBuffer(128);
 341  0 boolean inName = true;
 342  0 boolean continuation = false;
 343  0 if (Character.isWhitespace(line.charAt(0))) {
 344  0 continuation = true;
 345  0 inName = false;
 346    }
 347  0 for (int i = 0; i < line.length(); i++) {
 348  0 char c = line.charAt(i);
 349  0 if (inName && c == ':') {
 350  0 inName = false;
 351  0 } else if (inName) {
 352  0 name.append(c);
 353    } else {
 354  0 value.append(c);
 355    }
 356    }
 357  0 if (continuation) {
 358  0 List list = getHeaderList(lastHeaderName);
 359  0 Header h = (Header) list.remove(list.size() - 1);
 360  0 list.add(new InternetHeader(lastHeaderName, (h.getValue() + value.toString()).trim()));
 361    } else {
 362  0 lastHeaderName = name.toString().trim();
 363  0 addHeader(lastHeaderName, value.toString().trim());
 364    }
 365    }
 366   
 367    /**
 368    * Return all the header lines as an Enumeration of Strings.
 369    */
 370  0 public Enumeration getAllHeaderLines() {
 371  0 return new HeaderLineEnumeration(getAllHeaders());
 372    }
 373   
 374    /**
 375    * Return all matching header lines as an Enumeration of Strings.
 376    */
 377  0 public Enumeration getMatchingHeaderLines(String[] names) {
 378  0 return new HeaderLineEnumeration(getMatchingHeaders(names));
 379    }
 380   
 381    /**
 382    * Return all non-matching header lines.
 383    */
 384  0 public Enumeration getNonMatchingHeaderLines(String[] names) {
 385  0 return new HeaderLineEnumeration(getNonMatchingHeaders(names));
 386    }
 387   
 388  11 void setHeader(String name, Address[] addresses) {
 389  11 List list = new ArrayList(addresses.length);
 390  11 for (int i = 0; i < addresses.length; i++) {
 391  16 Address address = addresses[i];
 392  16 list.add(new InternetHeader(name, address.toString()));
 393    }
 394  11 headers.put(name.toLowerCase(), list);
 395    }
 396   
 397  213 private List getHeaderList(String name) {
 398  213 return (List) headers.get(name.toLowerCase());
 399    }
 400   
 401  881 private void setHeaderList(String name, List list) {
 402  881 headers.put(name.toLowerCase(), list);
 403    }
 404   
 405  11 void writeTo(OutputStream out, String[] ignore) throws IOException {
 406  11 Map map = new LinkedHashMap(headers);
 407  11 if (ignore != null) {
 408    // remove each of these from the header list (note, they are stored as lower case keys).
 409  0 for (int i = 0; i < ignore.length; i++) {
 410  0 String key = ignore[i].toLowerCase();
 411  0 map.remove(key);
 412    }
 413    }
 414  11 for (Iterator i = map.entrySet().iterator(); i.hasNext();) {
 415  289 Map.Entry entry = (Map.Entry) i.next();
 416  289 String name = (String) entry.getKey();
 417  289 List headers = (List) entry.getValue();
 418  289 if (headers != null) {
 419  27 for (int j = 0; j < headers.size(); j++) {
 420  27 InternetHeader header = (InternetHeader) headers.get(j);
 421  27 out.write(header.getName().getBytes());
 422  27 out.write(':');
 423  27 out.write(' ');
 424  27 out.write(header.getValue().getBytes());
 425  27 out.write(13);
 426  27 out.write(10);
 427    }
 428    }
 429    }
 430    }
 431   
 432    private static class InternetHeader extends Header {
 433  86 public InternetHeader(String name, String value) {
 434  86 super(name, value);
 435    }
 436   
 437  0 public boolean equals(Object obj) {
 438  0 if (this == obj)
 439  0 return true;
 440  0 if (obj instanceof InternetHeader == false)
 441  0 return false;
 442  0 final InternetHeader other = (InternetHeader) obj;
 443  0 return getName().equalsIgnoreCase(other.getName());
 444    }
 445   
 446  0 public int hashCode() {
 447  0 return getName().toLowerCase().hashCode();
 448    }
 449    }
 450   
 451    private static class HeaderLineEnumeration implements Enumeration {
 452    private Enumeration headers;
 453   
 454  0 public HeaderLineEnumeration(Enumeration headers) {
 455  0 this.headers = headers;
 456    }
 457   
 458  0 public boolean hasMoreElements() {
 459  0 return headers.hasMoreElements();
 460    }
 461   
 462  0 public Object nextElement() {
 463  0 Header h = (Header) headers.nextElement();
 464  0 return h.getName() + ": " + h.getValue();
 465    }
 466    }
 467    }