Coverage Report - com.beetstra.jutf7.UTF7StyleCharsetDecoder
 
Classes in this File Line Coverage Branch Coverage Complexity
UTF7StyleCharsetDecoder
100% 
100% 
3,667
 
 1  
 /* ====================================================================
 2  
  * Copyright (c) 2006 J.T. Beetstra
 3  
  *
 4  
  * Permission is hereby granted, free of charge, to any person obtaining 
 5  
  * a copy of this software and associated documentation files (the 
 6  
  * "Software"), to deal in the Software without restriction, including 
 7  
  * without limitation the rights to use, copy, modify, merge, publish, 
 8  
  * distribute, sublicense, and/or sell copies of the Software, and to 
 9  
  * permit persons to whom the Software is furnished to do so, subject to 
 10  
  * the following conditions:
 11  
  *
 12  
  * The above copyright notice and this permission notice shall be 
 13  
  * included in all copies or substantial portions of the Software.
 14  
  *
 15  
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
 16  
  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
 17  
  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
 18  
  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 
 19  
  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 
 20  
  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
 21  
  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 22  
  * ====================================================================
 23  
  */
 24  
 package com.beetstra.jutf7;
 25  
 
 26  
 import java.nio.ByteBuffer;
 27  
 import java.nio.CharBuffer;
 28  
 import java.nio.charset.CharsetDecoder;
 29  
 import java.nio.charset.CoderResult;
 30  
 
 31  
 /**
 32  
  * <p>The CharsetDecoder used to decode both variants of the UTF-7 charset and the 
 33  
  * modified-UTF-7 charset.</p>
 34  
  * 
 35  
  * @author Jaap Beetstra
 36  
  */
 37  
 class UTF7StyleCharsetDecoder extends CharsetDecoder {
 38  
         private final Base64Util base64;
 39  
         private final byte shift;
 40  
         private final byte unshift;
 41  
         private final boolean strict;
 42  
         private boolean base64mode;
 43  
         private int bitsRead;
 44  
         private int tempChar;
 45  
         private boolean justShifted;
 46  
         private boolean justUnshifted;
 47  
 
 48  
         UTF7StyleCharsetDecoder(UTF7StyleCharset cs, Base64Util base64, boolean strict) {
 49  66
                 super(cs, 0.6f, 1.0f);
 50  66
                 this.base64 = base64;
 51  66
                 this.strict = strict;
 52  66
                 this.shift = cs.shift();
 53  66
                 this.unshift = cs.unshift();
 54  66
         }
 55  
 
 56  
         /* (non-Javadoc)
 57  
          * @see java.nio.charset.CharsetDecoder#decodeLoop(java.nio.ByteBuffer, java.nio.CharBuffer)
 58  
          */
 59  
         protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) {
 60  1554350
                 while (in.hasRemaining()) {
 61  1523560
                         byte b = in.get();
 62  1523560
                         if (base64mode) {
 63  1071658
                                 if (b == unshift) {
 64  152966
                                         if (base64bitsWaiting())
 65  8
                                                 return malformed(in);
 66  152958
                                         if (justShifted) {
 67  15654
                                                 if (!out.hasRemaining())
 68  154
                                                         return overflow(in);
 69  15500
                                                 out.put((char) shift);
 70  
                                         } else
 71  137304
                                                 justUnshifted = true;
 72  152804
                                         setUnshifted();
 73  
                                 } else {
 74  918692
                                         if (!out.hasRemaining())
 75  2962
                                                 return overflow(in);
 76  915730
                                         CoderResult result = handleBase64(in, out, b);
 77  915730
                                         if (result != null)
 78  10
                                                 return result;
 79  
                                 }
 80  1068524
                                 justShifted = false;
 81  
                         } else {
 82  451902
                                 if (b == shift) {
 83  155544
                                         base64mode = true;
 84  155544
                                         if (justUnshifted && strict)
 85  2
                                                 return malformed(in);
 86  155542
                                         justShifted = true;
 87  155542
                                         continue;
 88  
                                 }
 89  296358
                                 if (!out.hasRemaining())
 90  3040
                                         return overflow(in);
 91  293318
                                 out.put((char) b);
 92  293318
                                 justUnshifted = false;
 93  
                         }
 94  1361842
                 }
 95  30790
                 return CoderResult.UNDERFLOW;
 96  
         }
 97  
 
 98  
         private CoderResult overflow(ByteBuffer in) {
 99  6156
                 in.position(in.position() - 1);
 100  6156
                 return CoderResult.OVERFLOW;
 101  
         }
 102  
 
 103  
         /**
 104  
          * <p>Decodes a byte in <i>base 64 mode</i>. Will directly write a character to the output 
 105  
          * buffer if completed.</p>
 106  
          * 
 107  
          * @param in The input buffer
 108  
          * @param out The output buffer
 109  
          * @param lastRead Last byte read from the input buffer
 110  
          * @return CoderResult.malformed if a non-base 64 character was encountered in strict 
 111  
          *   mode, null otherwise
 112  
          */
 113  
         private CoderResult handleBase64(ByteBuffer in, CharBuffer out, byte lastRead) {
 114  915730
                 CoderResult result = null;
 115  915730
                 int sextet = base64.getSextet(lastRead);
 116  915730
                 if (sextet >= 0) {
 117  913018
                         bitsRead += 6;
 118  913018
                         if (bitsRead < 16) {
 119  587440
                                 tempChar += sextet << (16 - bitsRead);
 120  
                         } else {
 121  325578
                                 bitsRead -= 16;
 122  325578
                                 tempChar += sextet >> (bitsRead);
 123  325578
                                 out.put((char) tempChar);
 124  325578
                                 tempChar = (sextet << (16 - bitsRead)) & 0xFFFF;
 125  
                         }
 126  
                 } else {
 127  2712
                         if (strict)
 128  4
                                 return malformed(in);
 129  2708
                         out.put((char) lastRead);
 130  2708
                         if (base64bitsWaiting())
 131  6
                                 result = malformed(in);
 132  2708
                         setUnshifted();
 133  
                 }
 134  915726
                 return result;
 135  
         }
 136  
 
 137  
         /* (non-Javadoc)
 138  
          * @see java.nio.charset.CharsetDecoder#implFlush(java.nio.CharBuffer)
 139  
          */
 140  
         protected CoderResult implFlush(CharBuffer out) {
 141  5780
                 if ((base64mode && strict) || base64bitsWaiting())
 142  10
                         return CoderResult.malformedForLength(1);
 143  5770
                 return CoderResult.UNDERFLOW;
 144  
         }
 145  
 
 146  
         /* (non-Javadoc)
 147  
          * @see java.nio.charset.CharsetDecoder#implReset()
 148  
          */
 149  
         protected void implReset() {
 150  11972
                 setUnshifted();
 151  11972
                 justUnshifted = false;
 152  11972
         }
 153  
 
 154  
         /**
 155  
          * <p>Resets the input buffer position to just before the last byte read, and returns
 156  
          * a result indicating to skip the last byte.</p>
 157  
          * 
 158  
          * @param in The input buffer
 159  
          * @return CoderResult.malformedForLength(1);
 160  
          */
 161  
         private CoderResult malformed(ByteBuffer in) {
 162  20
                 in.position(in.position() - 1);
 163  20
                 return CoderResult.malformedForLength(1);
 164  
         }
 165  
 
 166  
         /**
 167  
          * @return True if there are base64 encoded characters waiting to be written
 168  
          */
 169  
         private boolean base64bitsWaiting() {
 170  161444
                 return tempChar != 0 || bitsRead >= 6;
 171  
         }
 172  
 
 173  
         /**
 174  
          * <p>Updates internal state to reflect the decoder is no longer in <i>base 64 
 175  
          * mode</i></p>
 176  
          */
 177  
         private void setUnshifted() {
 178  167484
                 base64mode = false;
 179  167484
                 bitsRead = 0;
 180  167484
                 tempChar = 0;
 181  167484
         }
 182  
 }