Clover coverage report - Maven Clover report
Coverage timestamp: Sun Aug 20 2006 04:01:04 PDT
file stats: LOC: 282   Methods: 15
NCLOC: 178   Classes: 2
 
 Source file Conditionals Statements Methods TOTAL
HeaderTokenizer.java 96.3% 98.9% 100% 98.1%
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    /**
 21    * @version $Rev: 381393 $ $Date: 2006-02-27 09:38:03 -0800 (Mon, 27 Feb 2006) $
 22    */
 23    public class HeaderTokenizer {
 24    public static class Token {
 25    // Constant values from J2SE 1.4 API Docs (Constant values)
 26    public static final int ATOM = -1;
 27    public static final int COMMENT = -3;
 28    public static final int EOF = -4;
 29    public static final int QUOTEDSTRING = -2;
 30    private int _type;
 31    private String _value;
 32   
 33  379 public Token(int type, String value) {
 34  379 _type = type;
 35  379 _value = value;
 36    }
 37   
 38  515 public int getType() {
 39  515 return _type;
 40    }
 41   
 42  252 public String getValue() {
 43  252 return _value;
 44    }
 45    }
 46   
 47    private static final Token EOF = new Token(Token.EOF, null);
 48    // characters not allowed in MIME
 49    public static final String MIME = "()<>@,;:\\\"\t []/?=";
 50    // charaters not allowed in RFC822
 51    public static final String RFC822 = "()<>@,;:\\\"\t .[]";
 52    private static final String WHITE = " \t\n\r";
 53    private String _delimiters;
 54    private String _header;
 55    private boolean _skip;
 56    private int pos;
 57   
 58  11 public HeaderTokenizer(String header) {
 59  11 this(header, RFC822);
 60    }
 61   
 62  171 public HeaderTokenizer(String header, String delimiters) {
 63  171 this(header, delimiters, true);
 64    }
 65   
 66  187 public HeaderTokenizer(String header,
 67    String delimiters,
 68    boolean skipComments) {
 69  187 _skip = skipComments;
 70  187 _header = header;
 71  187 _delimiters = delimiters;
 72    }
 73   
 74  73 public String getRemainder() {
 75  73 return _header.substring(pos);
 76    }
 77   
 78  460 public Token next() throws ParseException {
 79  460 return readToken();
 80    }
 81   
 82  7 public Token peek() throws ParseException {
 83  7 int start = pos;
 84  7 try {
 85  7 return readToken();
 86    } finally {
 87  7 pos = start;
 88    }
 89    }
 90   
 91    /**
 92    * Read an ATOM token from the parsed header.
 93    *
 94    * @return A token containing the value of the atom token.
 95    */
 96  210 private Token readAtomicToken() {
 97    // skip to next delimiter
 98  210 int start = pos;
 99  210 while (++pos < _header.length()) {
 100    // break on the first non-atom character.
 101  975 char ch = _header.charAt(pos);
 102  975 if (_delimiters.indexOf(_header.charAt(pos)) != -1 || ch < 32 || ch >= 127) {
 103  126 break;
 104    }
 105    }
 106   
 107  210 return new Token(Token.ATOM, _header.substring(start, pos));
 108    }
 109   
 110    /**
 111    * Read the next token from the header.
 112    *
 113    * @return The next token from the header. White space is skipped, and comment
 114    * tokens are also skipped if indicated.
 115    * @exception ParseException
 116    */
 117  493 private Token readToken() throws ParseException {
 118  493 if (pos >= _header.length()) {
 119  83 return EOF;
 120    } else {
 121  410 char c = _header.charAt(pos);
 122    // comment token...read and skip over this
 123  410 if (c == '(') {
 124  15 Token comment = readComment();
 125  11 if (_skip) {
 126  2 return readToken();
 127    } else {
 128  9 return comment;
 129    }
 130    // quoted literal
 131  395 } else if (c == '\"') {
 132  26 return readQuotedString();
 133    // white space, eat this and find a real token.
 134  369 } else if (WHITE.indexOf(c) != -1) {
 135  24 eatWhiteSpace();
 136  24 return readToken();
 137    // either a CTL or special. These characters have a self-defining token type.
 138  345 } else if (c < 32 || c >= 127 || _delimiters.indexOf(c) != -1) {
 139  135 pos++;
 140  135 return new Token((int)c, String.valueOf(c));
 141    } else {
 142    // start of an atom, parse it off.
 143  210 return readAtomicToken();
 144    }
 145    }
 146    }
 147   
 148    /**
 149    * Extract a substring from the header string and apply any
 150    * escaping/folding rules to the string.
 151    *
 152    * @param start The starting offset in the header.
 153    * @param end The header end offset + 1.
 154    *
 155    * @return The processed string value.
 156    * @exception ParseException
 157    */
 158  5 private String getEscapedValue(int start, int end) throws ParseException {
 159  5 StringBuffer value = new StringBuffer();
 160   
 161  5 for (int i = start; i < end; i++) {
 162  15 char ch = _header.charAt(i);
 163    // is this an escape character?
 164  15 if (ch == '\\') {
 165  3 i++;
 166  3 if (i == end) {
 167  0 throw new ParseException("Invalid escape character");
 168    }
 169  3 value.append(_header.charAt(i));
 170    }
 171    // line breaks are ignored, except for naked '\n' characters, which are consider
 172    // parts of linear whitespace.
 173  12 else if (ch == '\r') {
 174    // see if this is a CRLF sequence, and skip the second if it is.
 175  2 if (i < end - 1 && _header.charAt(i + 1) == '\n') {
 176  2 i++;
 177    }
 178    }
 179    else {
 180    // just append the ch value.
 181  10 value.append(ch);
 182    }
 183    }
 184  5 return value.toString();
 185    }
 186   
 187    /**
 188    * Read a comment from the header, applying nesting and escape
 189    * rules to the content.
 190    *
 191    * @return A comment token with the token value.
 192    * @exception ParseException
 193    */
 194  15 private Token readComment() throws ParseException {
 195  15 int start = pos + 1;
 196  15 int nesting = 1;
 197   
 198  15 boolean requiresEscaping = false;
 199   
 200    // skip to end of comment/string
 201  15 while (++pos < _header.length()) {
 202  142 char ch = _header.charAt(pos);
 203  142 if (ch == ')') {
 204  18 nesting--;
 205  18 if (nesting == 0) {
 206  11 break;
 207    }
 208    }
 209  124 else if (ch == '(') {
 210  7 nesting++;
 211    }
 212  117 else if (ch == '\\') {
 213  1 pos++;
 214  1 requiresEscaping = true;
 215    }
 216    // we need to process line breaks also
 217  116 else if (ch == '\r') {
 218  1 requiresEscaping = true;
 219    }
 220    }
 221   
 222  15 if (nesting != 0) {
 223  4 throw new ParseException("Unbalanced comments");
 224    }
 225   
 226  11 String value;
 227  11 if (requiresEscaping) {
 228  2 value = getEscapedValue(start, pos);
 229    }
 230    else {
 231  9 value = _header.substring(start, pos++);
 232    }
 233  11 return new Token(Token.COMMENT, value);
 234    }
 235   
 236    /**
 237    * Parse out a quoted string from the header, applying escaping
 238    * rules to the value.
 239    *
 240    * @return The QUOTEDSTRING token with the value.
 241    * @exception ParseException
 242    */
 243  26 private Token readQuotedString() throws ParseException {
 244  26 int start = pos+1;
 245  26 boolean requiresEscaping = false;
 246   
 247    // skip to end of comment/string
 248  26 while (++pos < _header.length()) {
 249  326 char ch = _header.charAt(pos);
 250  326 if (ch == '"') {
 251  22 String value;
 252  22 if (requiresEscaping) {
 253  3 value = getEscapedValue(start, pos);
 254    }
 255    else {
 256  19 value = _header.substring(start, pos++);
 257    }
 258  22 return new Token(Token.QUOTEDSTRING, value);
 259    }
 260  304 else if (ch == '\\') {
 261  4 pos++;
 262  4 requiresEscaping = true;
 263    }
 264    // we need to process line breaks also
 265  300 else if (ch == '\r') {
 266  1 requiresEscaping = true;
 267    }
 268    }
 269   
 270  4 throw new ParseException("Missing '\"'");
 271    }
 272   
 273    /**
 274    * Skip white space in the token string.
 275    */
 276  24 private void eatWhiteSpace() {
 277    // skip to end of whitespace
 278  24 while (++pos < _header.length()
 279    && WHITE.indexOf(_header.charAt(pos)) != -1)
 280    ;
 281    }
 282    }