Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||||||
UTF7StyleCharsetDecoder |
|
| 3.6666666666666665;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 | } |