Clover coverage report - Maven Clover report
Coverage timestamp: Sun Aug 20 2006 04:01:44 PDT
file stats: LOC: 259   Methods: 8
NCLOC: 128   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
RFC2231Encoder.java 54.2% 56.7% 62.5% 56.6%
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 org.apache.geronimo.mail.util;
 19   
 20    import java.io.ByteArrayOutputStream;
 21    import java.io.IOException;
 22    import java.io.OutputStream;
 23    import java.io.UnsupportedEncodingException;
 24   
 25    import javax.mail.internet.MimeUtility;
 26   
 27    /**
 28    * Encoder for RFC2231 encoded parameters
 29    *
 30    * RFC2231 string are encoded as
 31    *
 32    * charset'language'encoded-text
 33    *
 34    * and
 35    *
 36    * encoded-text = *(char / hexchar)
 37    *
 38    * where
 39    *
 40    * char is any ASCII character in the range 33-126, EXCEPT
 41    * the characters "%" and " ".
 42    *
 43    * hexchar is an ASCII "%" followed by two upper case
 44    * hexadecimal digits.
 45    */
 46   
 47    public class RFC2231Encoder implements Encoder
 48    {
 49    protected final byte[] encodingTable =
 50    {
 51    (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', (byte)'7',
 52    (byte)'8', (byte)'9', (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F'
 53    };
 54   
 55    protected String DEFAULT_SPECIALS = " *'%";
 56    protected String specials = DEFAULT_SPECIALS;
 57   
 58    /*
 59    * set up the decoding table.
 60    */
 61    protected final byte[] decodingTable = new byte[128];
 62   
 63  2 protected void initialiseDecodingTable()
 64    {
 65  2 for (int i = 0; i < encodingTable.length; i++)
 66    {
 67  32 decodingTable[encodingTable[i]] = (byte)i;
 68    }
 69    }
 70   
 71  0 public RFC2231Encoder()
 72    {
 73  0 this(null);
 74    }
 75   
 76  2 public RFC2231Encoder(String specials)
 77    {
 78  2 if (specials != null) {
 79  2 this.specials = DEFAULT_SPECIALS + specials;
 80    }
 81  2 initialiseDecodingTable();
 82    }
 83   
 84   
 85    /**
 86    * encode the input data producing an RFC2231 output stream.
 87    *
 88    * @return the number of bytes produced.
 89    */
 90  1 public int encode(byte[] data, int off, int length, OutputStream out) throws IOException {
 91   
 92  1 int bytesWritten = 0;
 93  1 for (int i = off; i < (off + length); i++)
 94    {
 95  16 int ch = data[i] & 0xff;
 96    // character tha must be encoded? Prefix with a '%' and encode in hex.
 97  16 if (ch <= 32 || ch >= 127 || specials.indexOf(ch) != -1) {
 98  13 out.write((byte)'%');
 99  13 out.write(encodingTable[ch >> 4]);
 100  13 out.write(encodingTable[ch & 0xf]);
 101  13 bytesWritten += 3;
 102    }
 103    else {
 104    // add unchanged.
 105  3 out.write((byte)ch);
 106  3 bytesWritten++;
 107    }
 108    }
 109   
 110  1 return bytesWritten;
 111    }
 112   
 113   
 114    /**
 115    * decode the RFC2231 encoded byte data writing it to the given output stream
 116    *
 117    * @return the number of bytes produced.
 118    */
 119  0 public int decode(byte[] data, int off, int length, OutputStream out) throws IOException {
 120  0 int outLen = 0;
 121  0 int end = off + length;
 122   
 123  0 int i = off;
 124  0 while (i < end)
 125    {
 126  0 byte v = data[i++];
 127    // a percent is a hex character marker, need to decode a hex value.
 128  0 if (v == '%') {
 129  0 byte b1 = decodingTable[data[i++]];
 130  0 byte b2 = decodingTable[data[i++]];
 131  0 out.write((b1 << 4) | b2);
 132    }
 133    else {
 134    // copied over unchanged.
 135  0 out.write(v);
 136    }
 137    // always just one byte added
 138  0 outLen++;
 139    }
 140   
 141  0 return outLen;
 142    }
 143   
 144    /**
 145    * decode the RFC2231 encoded String data writing it to the given output stream.
 146    *
 147    * @return the number of bytes produced.
 148    */
 149  1 public int decode(String data, OutputStream out) throws IOException
 150    {
 151  1 int length = 0;
 152  1 int end = data.length();
 153   
 154  1 int i = 0;
 155  1 while (i < end)
 156    {
 157  16 char v = data.charAt(i++);
 158  16 if (v == '%') {
 159  13 byte b1 = decodingTable[data.charAt(i++)];
 160  13 byte b2 = decodingTable[data.charAt(i++)];
 161   
 162  13 out.write((b1 << 4) | b2);
 163    }
 164    else {
 165  3 out.write((byte)v);
 166    }
 167  16 length++;
 168    }
 169   
 170  1 return length;
 171    }
 172   
 173   
 174    /**
 175    * Encode a string as an RFC2231 encoded parameter, using the
 176    * given character set and language.
 177    *
 178    * @param charset The source character set (the MIME version).
 179    * @param language The encoding language.
 180    * @param data The data to encode.
 181    *
 182    * @return The encoded string.
 183    */
 184  0 public String encode(String charset, String language, String data) throws IOException {
 185   
 186  0 byte[] bytes = null;
 187  0 try {
 188    // the charset we're adding is the MIME-defined name. We need the java version
 189    // in order to extract the bytes.
 190  0 bytes = data.getBytes(MimeUtility.javaCharset(charset));
 191    } catch (UnsupportedEncodingException e) {
 192    // we have a translation problem here.
 193  0 return null;
 194    }
 195   
 196  0 StringBuffer result = new StringBuffer();
 197   
 198    // append the character set, if we have it.
 199  0 if (charset != null) {
 200  0 result.append(charset);
 201    }
 202    // the field marker is required.
 203  0 result.append("'");
 204   
 205    // and the same for the language.
 206  0 if (language != null) {
 207  0 result.append(language);
 208    }
 209    // the field marker is required.
 210  0 result.append("'");
 211   
 212    // wrap an output stream around our buffer for the decoding
 213  0 OutputStream out = new StringBufferOutputStream(result);
 214   
 215    // encode the data stream
 216  0 encode(bytes, 0, bytes.length, out);
 217   
 218    // finis!
 219  0 return result.toString();
 220    }
 221   
 222   
 223    /**
 224    * Decode an RFC2231 encoded string.
 225    *
 226    * @param data The data to decode.
 227    *
 228    * @return The decoded string.
 229    * @exception IOException
 230    * @exception UnsupportedEncodingException
 231    */
 232  1 public String decode(String data) throws IOException, UnsupportedEncodingException {
 233    // get the end of the language field
 234  1 int charsetEnd = data.indexOf('\'');
 235    // uh oh, might not be there
 236  1 if (charsetEnd == -1) {
 237  0 throw new IOException("Missing charset in RFC2231 encoded value");
 238    }
 239   
 240  1 String charset = data.substring(0, charsetEnd);
 241   
 242    // now pull out the language the same way
 243  1 int languageEnd = data.indexOf('\'', charsetEnd + 1);
 244  1 if (languageEnd == -1) {
 245  0 throw new IOException("Missing language in RFC2231 encoded value");
 246    }
 247   
 248  1 String language = data.substring(charsetEnd + 1, languageEnd);
 249   
 250  1 ByteArrayOutputStream out = new ByteArrayOutputStream(data.length());
 251   
 252    // decode the data
 253  1 decode(data.substring(languageEnd + 1), out);
 254   
 255  1 byte[] bytes = out.toByteArray();
 256    // build a new string from this using the java version of the encoded charset.
 257  1 return new String(bytes, 0, bytes.length, MimeUtility.javaCharset(charset));
 258    }
 259    }