Base 64 decoder class. Converts byte array to characters.
*If measure is supplied this is a line width, and \n characters * are inserted when it's exceeeded.
* * @author Malcolm McMahon * @version $Revision: 1.1 $ */ public class Base64Decoder extends CharsetDecoder { Integer measure; int charCount; char padding; String codeChars; char[] excessChars; int codeState; int bits; /** * Creates a new instance of Base64Decoder * @param cs The Charset that created this object * @param padding The padding character, usually = * @param measure The line width in characters */ public Base64Decoder(Charset cs,String codeChars, char padding, int measure) { super(cs, 1.33f, 4f); this.measure = measure; this.codeChars = codeChars; this.padding = padding; } /** * @param cs The Charset that created this object * @param padding The padding character, usually = */ public Base64Decoder(Charset cs,String codeChars, char padding) { super(cs, 1.33f, 4f); this.codeChars = codeChars; this.padding = padding; } private boolean flushExcess(CharBuffer out) { if(excessChars == null) return true; for(int i = 0; i < excessChars.length; i++) if(out.remaining() > 0) out.put(excessChars[i]); else { char[] nc = new char[excessChars.length - i]; System.arraycopy(excessChars, i, nc, 0, nc.length); excessChars = nc; return false; } excessChars = null; return true; } /** * Output one character. These character output methods * park characters in excessChars if the buffer overflows. * The also add newlines if the measure calls for it. * @return true if space found */ private boolean out(CharBuffer out, char ch) { if(measure != null && ++charCount > measure) { charCount = -1; return out(out, '\n', ch); } if(out.remaining() > 0) { out.put(ch); return true; } else { excessChars = new char[]{ch}; return false; } } /** * Output two characters * @return true if space found */ private boolean out(CharBuffer out, char ch1, char ch2) { if(measure != null && (charCount += 2) > measure) { if(charCount - 1 != measure) { charCount = 2; return out(out, '\n', ch1, ch2); } else { charCount = 1; return out(out, ch1, '\n', ch2); } } switch(out.remaining()) { case 0: excessChars = new char[]{ch1, ch2}; return false; case 1: out.put(ch1); excessChars = new char[]{ch2}; return false; default: out.put(ch1); out.put(ch2); return true; } } private boolean out(CharBuffer out, char ch1, char ch2, char ch3) { switch(out.remaining()) { case 0: excessChars = new char[]{ch1, ch2, ch3}; return false; case 1: out.put(ch1); excessChars = new char[]{ch2, ch3}; return false; case 2: out.put(ch1); out.put(ch2); excessChars = new char[]{ch3}; return false; default: out.put(ch1); out.put(ch2); out.put(ch3); return true; } } /** * Flushes this decoder. * *The default implementation of this method does nothing, and always * returns {@link CoderResult#UNDERFLOW}. This method should be overridden * by decoders that may need to write final characters to the output buffer * once the entire input sequence has been read.
* * @param out * The output character buffer * * @return A coder-result object, either {@link CoderResult#UNDERFLOW} or * {@link CoderResult#OVERFLOW} */ protected java.nio.charset.CoderResult implFlush(CharBuffer out) { if(!flushExcess(out)) return CoderResult.OVERFLOW; switch(codeState) { case 0: // full quanta count implReset(); return CoderResult.UNDERFLOW; case 1: if(!out(out, codeChars.charAt(bits), padding)) return CoderResult.OVERFLOW; break; case 2: if(!out(out, codeChars.charAt(bits))) return CoderResult.OVERFLOW; break; } if(out(out, padding)) { implReset(); return CoderResult.UNDERFLOW; } else return CoderResult.OVERFLOW; } /** * Decodes one or more bytes into one or more characters. * *This method encapsulates the basic decoding loop, decoding as many * bytes as possible until it either runs out of input, runs out of room * in the output buffer, or encounters a decoding error. This method is * invoked by the {@link #decode decode} method, which handles result * interpretation and error recovery. * *
The buffers are read from, and written to, starting at their current * positions. At most {@link Buffer#remaining in.remaining()} bytes * will be read, and at most {@link Buffer#remaining out.remaining()} * characters will be written. The buffers' positions will be advanced to * reflect the bytes read and the characters written, but their marks and * limits will not be modified. * *
This method returns a {@link CoderResult} object to describe its * reason for termination, in the same manner as the {@link #decode decode} * method. Most implementations of this method will handle decoding errors * by returning an appropriate result object for interpretation by the * {@link #decode decode} method. An optimized implementation may instead * examine the relevant error action and implement that action itself. * *
An implementation of this method may perform arbitrary lookahead by * returning {@link CoderResult#UNDERFLOW} until it receives sufficient * input.
* * @param in * The input byte buffer * * @param out * The output character buffer * * @return A coder-result object describing the reason for termination */ public java.nio.charset.CoderResult decodeLoop(java.nio.ByteBuffer in, java.nio.CharBuffer out) { if(!flushExcess(out)) return CoderResult.OVERFLOW; while(in.remaining() > 0) { int b = in.get() & 0xff; switch(codeState) { case 0: bits = (b << 4) & 0x30; codeState = 1; if(!out(out, codeChars.charAt((b >> 2) & 0x3f))) return CoderResult.OVERFLOW; break; case 1: int cc = bits | (b >> 4) & 0xf; bits = (b << 2) & 0x3c; codeState = 2; if(!out(out, codeChars.charAt(cc))) return CoderResult.OVERFLOW; break; case 2: cc = bits | (b >> 6) & 3; codeState = 0; if(!out(out, codeChars.charAt(cc), codeChars.charAt(b & 0x3f))) return CoderResult.OVERFLOW; } } return CoderResult.UNDERFLOW; } /** * Resets this decoder, clearing any charset-specific internal state. * *The default implementation of this method does nothing. This method * should be overridden by decoders that maintain internal state.
*/ protected void implReset() { bits = 0; excessChars = null; codeState = 0; charCount = 0; } }