Clover coverage report - Maven Clover report
Coverage timestamp: Sun Aug 20 2006 04:01:44 PDT
file stats: LOC: 300   Methods: 15
NCLOC: 182   Classes: 2
 
 Source file Conditionals Statements Methods TOTAL
ParameterList.java 64.3% 84.5% 86.7% 80.7%
coverage coverage
 1    /**
 2    *
 3    * Copyright 2003-2006 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.ByteArrayOutputStream;
 21    import java.io.UnsupportedEncodingException;
 22    import java.util.ArrayList;// Represents lists in things like
 23    import java.util.Collections;
 24    import java.util.Enumeration;
 25    import java.util.HashMap;
 26    import java.util.Iterator;
 27    import java.util.List;
 28    import java.util.Map;
 29    import java.util.StringTokenizer;
 30   
 31    import org.apache.geronimo.mail.util.ASCIIUtil;
 32    import org.apache.geronimo.mail.util.RFC2231Encoder;
 33    import org.apache.geronimo.mail.util.SessionUtil;
 34   
 35    // Content-Type: text/plain;charset=klingon
 36    //
 37    // The ;charset=klingon is the parameter list, may have more of them with ';'
 38   
 39    /**
 40    * @version $Rev: 421852 $ $Date: 2006-07-14 03:02:19 -0700 (Fri, 14 Jul 2006) $
 41    */
 42    public class ParameterList {
 43    private static final String MIME_ENCODEPARAMETERS = "mail.mime.encodeparameters";
 44    private static final String MIME_DECODEPARAMETERS = "mail.mime.decodeparameters";
 45    private static final String MIME_DECODEPARAMETERS_STRICT = "mail.mime.decodeparameters.strict";
 46   
 47    private static final int HEADER_SIZE_LIMIT = 76;
 48   
 49    private Map _parameters = new HashMap();
 50   
 51    private boolean encodeParameters = false;
 52    private boolean decodeParameters = false;
 53    private boolean decodeParametersStrict = false;
 54   
 55  6 public ParameterList() {
 56    // figure out how parameter handling is to be performed.
 57  6 getInitialProperties();
 58    }
 59   
 60  151 public ParameterList(String list) throws ParseException {
 61    // figure out how parameter handling is to be performed.
 62  151 getInitialProperties();
 63    // get a token parser for the type information
 64  151 HeaderTokenizer tokenizer = new HeaderTokenizer(list, HeaderTokenizer.MIME);
 65  151 while (true) {
 66  201 HeaderTokenizer.Token token = tokenizer.next();
 67   
 68  201 switch (token.getType()) {
 69    // the EOF token terminates parsing.
 70  150 case HeaderTokenizer.Token.EOF:
 71  150 return;
 72   
 73    // each new parameter is separated by a semicolon, including the first, which separates
 74    // the parameters from the main part of the header.
 75  50 case ';':
 76    // the next token needs to be a parameter name
 77  50 token = tokenizer.next();
 78    // allow a trailing semicolon on the parameters.
 79  50 if (token.getType() == HeaderTokenizer.Token.EOF) {
 80  0 return;
 81    }
 82   
 83  50 if (token.getType() != HeaderTokenizer.Token.ATOM) {
 84  0 throw new ParseException("Invalid parameter name: " + token.getValue());
 85    }
 86   
 87    // get the parameter name as a lower case version for better mapping.
 88  50 String name = token.getValue().toLowerCase();
 89   
 90  50 token = tokenizer.next();
 91   
 92    // parameters are name=value, so we must have the "=" here.
 93  50 if (token.getType() != '=') {
 94  0 throw new ParseException("Missing '='");
 95    }
 96   
 97    // now the value, which may be an atom or a literal
 98  50 token = tokenizer.next();
 99   
 100  50 if (token.getType() != HeaderTokenizer.Token.ATOM && token.getType() != HeaderTokenizer.Token.QUOTEDSTRING) {
 101  0 throw new ParseException("Invalid parameter value: " + token.getValue());
 102    }
 103   
 104  50 String value = token.getValue();
 105  50 String decodedValue = null;
 106   
 107    // we might have to do some additional decoding. A name that ends with "*"
 108    // is marked as being encoded, so if requested, we decode the value.
 109  50 if (decodeParameters && name.endsWith("*")) {
 110    // the name needs to be pruned of the marker, and we need to decode the value.
 111  1 name = name.substring(0, name.length() - 1);
 112    // get a new decoder
 113  1 RFC2231Encoder decoder = new RFC2231Encoder(HeaderTokenizer.MIME);
 114   
 115  1 try {
 116    // decode the value
 117  1 decodedValue = decoder.decode(value);
 118    } catch (Exception e) {
 119    // if we're doing things strictly, then raise a parsing exception for errors.
 120    // otherwise, leave the value in its current state.
 121  0 if (decodeParametersStrict) {
 122  0 throw new ParseException("Invalid RFC2231 encoded parameter");
 123    }
 124    }
 125  1 _parameters.put(name, new ParameterValue(name, decodedValue, value));
 126    }
 127    else {
 128  49 _parameters.put(name, new ParameterValue(name, value));
 129    }
 130   
 131  50 break;
 132   
 133  1 default:
 134  1 throw new ParseException("Missing ';'");
 135   
 136    }
 137    }
 138    }
 139   
 140    /**
 141    * Get the initial parameters that control parsing and values.
 142    * These parameters are controlled by System properties.
 143    */
 144  157 private void getInitialProperties() {
 145  157 decodeParameters = SessionUtil.getBooleanProperty(MIME_DECODEPARAMETERS, false);
 146  157 decodeParametersStrict = SessionUtil.getBooleanProperty(MIME_DECODEPARAMETERS_STRICT, false);
 147  157 encodeParameters = SessionUtil.getBooleanProperty(MIME_ENCODEPARAMETERS, true);
 148    }
 149   
 150  1 public int size() {
 151  1 return _parameters.size();
 152    }
 153   
 154  49 public String get(String name) {
 155  49 ParameterValue value = (ParameterValue)_parameters.get(name.toLowerCase());
 156  49 if (value != null) {
 157  40 return value.value;
 158    }
 159  9 return null;
 160    }
 161   
 162  17 public void set(String name, String value) {
 163  17 name = name.toLowerCase();
 164  17 _parameters.put(name, new ParameterValue(name, value));
 165    }
 166   
 167  1 public void set(String name, String value, String charset) {
 168  1 name = name.toLowerCase();
 169    // only encode if told to and this contains non-ASCII charactes.
 170  1 if (encodeParameters && !ASCIIUtil.isAscii(value)) {
 171  1 ByteArrayOutputStream out = new ByteArrayOutputStream();
 172   
 173  1 try {
 174  1 RFC2231Encoder encoder = new RFC2231Encoder(HeaderTokenizer.MIME);
 175   
 176    // extract the bytes using the given character set and encode
 177  1 byte[] valueBytes = value.getBytes(MimeUtility.javaCharset(charset));
 178   
 179    // the string format is charset''data
 180  1 out.write(charset.getBytes());
 181  1 out.write('\'');
 182  1 out.write('\'');
 183  1 encoder.encode(valueBytes, 0, valueBytes.length, out);
 184   
 185    // default in case there is an exception
 186  1 _parameters.put(name, new ParameterValue(name, value, new String(out.toByteArray())));
 187  1 return;
 188   
 189    } catch (Exception e) {
 190    // just fall through and set the value directly if there is an error
 191    }
 192    }
 193    // default in case there is an exception
 194  0 _parameters.put(name, new ParameterValue(name, value));
 195    }
 196   
 197  0 public void remove(String name) {
 198  0 _parameters.remove(name);
 199    }
 200   
 201  0 public Enumeration getNames() {
 202  0 return Collections.enumeration(_parameters.keySet());
 203    }
 204   
 205  27 public String toString() {
 206    // we need to perform folding, but out starting point is 0.
 207  27 return toString(0);
 208    }
 209   
 210  32 public String toString(int used) {
 211  32 StringBuffer stringValue = new StringBuffer();
 212   
 213  32 Iterator values = _parameters.values().iterator();
 214   
 215  32 while (values.hasNext()) {
 216  35 ParameterValue parm = (ParameterValue)values.next();
 217    // get the values we're going to encode in here.
 218  35 String name = parm.getEncodedName();
 219  35 String value = parm.toString();
 220   
 221    // add the semicolon separator. We also add a blank so that folding/unfolding rules can be used.
 222  35 stringValue.append("; ");
 223  35 used += 2;
 224   
 225    // too big for the current header line?
 226  35 if ((used + name.length() + value.length() + 1) > HEADER_SIZE_LIMIT) {
 227    // and a CRLF-whitespace combo.
 228  2 stringValue.append("\r\n ");
 229    // reset the counter for a fresh line
 230  2 used = 3;
 231    }
 232    // now add the keyword/value pair.
 233  35 stringValue.append(name);
 234  35 stringValue.append("=");
 235   
 236  35 used += name.length() + 1;
 237   
 238    // we're not out of the woods yet. It is possible that the keyword/value pair by itself might
 239    // be too long for a single line. If that's the case, the we need to fold the value, if possible
 240  35 if (used + value.length() > HEADER_SIZE_LIMIT) {
 241  0 String foldedValue = MimeUtility.fold(used, value);
 242   
 243  0 stringValue.append(foldedValue);
 244   
 245    // now we need to sort out how much of the current line is in use.
 246  0 int lastLineBreak = foldedValue.lastIndexOf('\n');
 247   
 248  0 if (lastLineBreak != -1) {
 249  0 used = foldedValue.length() - lastLineBreak + 1;
 250    }
 251    else {
 252  0 used += foldedValue.length();
 253    }
 254    }
 255    else {
 256    // no folding required, just append.
 257  35 stringValue.append(value);
 258  35 used += value.length();
 259    }
 260    }
 261   
 262  32 return stringValue.toString();
 263    }
 264   
 265   
 266    /**
 267    * Utility class for representing parameter values in the list.
 268    */
 269    class ParameterValue {
 270    public String name; // the name of the parameter
 271    public String value; // the original set value
 272    public String encodedValue; // an encoded value, if encoding is requested.
 273   
 274  66 public ParameterValue(String name, String value) {
 275  66 this.name = name;
 276  66 this.value = value;
 277  66 this.encodedValue = null;
 278    }
 279   
 280  2 public ParameterValue(String name, String value, String encodedValue) {
 281  2 this.name = name;
 282  2 this.value = value;
 283  2 this.encodedValue = encodedValue;
 284    }
 285   
 286  35 public String toString() {
 287  35 if (encodedValue != null) {
 288  2 return MimeUtility.quote(encodedValue, HeaderTokenizer.MIME);
 289    }
 290  33 return MimeUtility.quote(value, HeaderTokenizer.MIME);
 291    }
 292   
 293  35 public String getEncodedName() {
 294  35 if (encodedValue != null) {
 295  2 return name + "*";
 296    }
 297  33 return name;
 298    }
 299    }
 300    }