• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
25 package com.beetstra.jutf7;
26 
27 import java.nio.ByteBuffer;
28 import java.nio.CharBuffer;
29 import java.nio.charset.CharsetDecoder;
30 import java.nio.charset.CoderResult;
31 
32 /**
33  * <p>
34  * The CharsetDecoder used to decode both variants of the UTF-7 charset and the
35  * modified-UTF-7 charset.
36  * </p>
37  *
38  * @author Jaap Beetstra
39  */
40 class UTF7StyleCharsetDecoder extends CharsetDecoder {
41     private final Base64Util base64;
42     private final byte shift;
43     private final byte unshift;
44     private final boolean strict;
45     private boolean base64mode;
46     private int bitsRead;
47     private int tempChar;
48     private boolean justShifted;
49     private boolean justUnshifted;
50 
UTF7StyleCharsetDecoder(UTF7StyleCharset cs, Base64Util base64, boolean strict)51     UTF7StyleCharsetDecoder(UTF7StyleCharset cs, Base64Util base64, boolean strict) {
52         super(cs, 0.6f, 1.0f);
53         this.base64 = base64;
54         this.strict = strict;
55         this.shift = cs.shift();
56         this.unshift = cs.unshift();
57     }
58 
59     /*
60      * (non-Javadoc)
61      * @see java.nio.charset.CharsetDecoder#decodeLoop(java.nio.ByteBuffer,
62      * java.nio.CharBuffer)
63      */
decodeLoop(ByteBuffer in, CharBuffer out)64     protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) {
65         while (in.hasRemaining()) {
66             byte b = in.get();
67             if (base64mode) {
68                 if (b == unshift) {
69                     if (base64bitsWaiting())
70                         return malformed(in);
71                     if (justShifted) {
72                         if (!out.hasRemaining())
73                             return overflow(in);
74                         out.put((char)shift);
75                     } else
76                         justUnshifted = true;
77                     setUnshifted();
78                 } else {
79                     if (!out.hasRemaining())
80                         return overflow(in);
81                     CoderResult result = handleBase64(in, out, b);
82                     if (result != null)
83                         return result;
84                 }
85                 justShifted = false;
86             } else {
87                 if (b == shift) {
88                     base64mode = true;
89                     if (justUnshifted && strict)
90                         return malformed(in);
91                     justShifted = true;
92                     continue;
93                 }
94                 if (!out.hasRemaining())
95                     return overflow(in);
96                 out.put((char)b);
97                 justUnshifted = false;
98             }
99         }
100         return CoderResult.UNDERFLOW;
101     }
102 
overflow(ByteBuffer in)103     private CoderResult overflow(ByteBuffer in) {
104         in.position(in.position() - 1);
105         return CoderResult.OVERFLOW;
106     }
107 
108     /**
109      * <p>
110      * Decodes a byte in <i>base 64 mode</i>. Will directly write a character to
111      * the output buffer if completed.
112      * </p>
113      *
114      * @param in The input buffer
115      * @param out The output buffer
116      * @param lastRead Last byte read from the input buffer
117      * @return CoderResult.malformed if a non-base 64 character was encountered
118      *         in strict mode, null otherwise
119      */
handleBase64(ByteBuffer in, CharBuffer out, byte lastRead)120     private CoderResult handleBase64(ByteBuffer in, CharBuffer out, byte lastRead) {
121         CoderResult result = null;
122         int sextet = base64.getSextet(lastRead);
123         if (sextet >= 0) {
124             bitsRead += 6;
125             if (bitsRead < 16) {
126                 tempChar += sextet << (16 - bitsRead);
127             } else {
128                 bitsRead -= 16;
129                 tempChar += sextet >> (bitsRead);
130                 out.put((char)tempChar);
131                 tempChar = (sextet << (16 - bitsRead)) & 0xFFFF;
132             }
133         } else {
134             if (strict)
135                 return malformed(in);
136             out.put((char)lastRead);
137             if (base64bitsWaiting())
138                 result = malformed(in);
139             setUnshifted();
140         }
141         return result;
142     }
143 
144     /*
145      * (non-Javadoc)
146      * @see java.nio.charset.CharsetDecoder#implFlush(java.nio.CharBuffer)
147      */
implFlush(CharBuffer out)148     protected CoderResult implFlush(CharBuffer out) {
149         if ((base64mode && strict) || base64bitsWaiting())
150             return CoderResult.malformedForLength(1);
151         return CoderResult.UNDERFLOW;
152     }
153 
154     /*
155      * (non-Javadoc)
156      * @see java.nio.charset.CharsetDecoder#implReset()
157      */
implReset()158     protected void implReset() {
159         setUnshifted();
160         justUnshifted = false;
161     }
162 
163     /**
164      * <p>
165      * Resets the input buffer position to just before the last byte read, and
166      * returns a result indicating to skip the last byte.
167      * </p>
168      *
169      * @param in The input buffer
170      * @return CoderResult.malformedForLength(1);
171      */
malformed(ByteBuffer in)172     private CoderResult malformed(ByteBuffer in) {
173         in.position(in.position() - 1);
174         return CoderResult.malformedForLength(1);
175     }
176 
177     /**
178      * @return True if there are base64 encoded characters waiting to be written
179      */
base64bitsWaiting()180     private boolean base64bitsWaiting() {
181         return tempChar != 0 || bitsRead >= 6;
182     }
183 
184     /**
185      * <p>
186      * Updates internal state to reflect the decoder is no longer in <i>base 64
187      * mode</i>
188      * </p>
189      */
setUnshifted()190     private void setUnshifted() {
191         base64mode = false;
192         bitsRead = 0;
193         tempChar = 0;
194     }
195 }
196