Mega Code Archive

 
Categories / Java / Internationalization
 

Extends CharsetDecoder to create Base64 Decoder

/*  * Base64Decoder.java  *  * Created on 20 December 2005, 12:14  *  */ import java.nio.CharBuffer; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.nio.charset.CoderResult; /**  * <p>Base 64 decoder class. Converts byte array to characters.</p>  * <p>If measure is supplied this is a line width, and \n characters  * are inserted when it's exceeeded.</p>  *  * @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.      *      * <p> 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. </p>      *      * @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.      *      * <p> 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.      *      * <p> 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.      *      * <p> 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.      *      * <p> An implementation of this method may perform arbitrary lookahead by      * returning {@link CoderResult#UNDERFLOW} until it receives sufficient      * input.  </p>      *      * @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.      *      * <p> The default implementation of this method does nothing.  This method      * should be overridden by decoders that maintain internal state.  </p>      */     protected void implReset() {         bits = 0;         excessChars = null;         codeState = 0;         charCount = 0;              }      }