/Users/lyon/j4p/src/net/proxy/BASE64Decoder.java

1    package net.proxy; 
2     
3     
4    /** 
5     * Utility class to do Base64 decoding, as defined by RFC 2045, 
6     * section 6.8 (http://www.ietf.org/rfc/rfc2045.txt) 
7     * Uses the same class and function names as Sun's implementation from 
8     * sun.misc 
9     */ 
10   public class BASE64Decoder { 
11    
12     /** 
13      * Bit mask for one byte worth of bits in Base64 encoding. 
14      * Equivalent to binary value 11111111b. 
15      */ 
16     private static final int EIGHT_BIT_MASK = 0xFF; 
17    
18     /** 
19      * Decode an input String using Base64 
20      * @param data The String to be decoded 
21      * @return The appropriate byte array 
22      */ 
23     public byte[] decodeBuffer(String data) { 
24       // Create a wrapper around the input to screen out non-Base64 characters 
25       StringWrapper wrapper = new StringWrapper(data); 
26       // A Base64 byte array is 75% the size of its String representation 
27       int length = wrapper.getUsefulLength() * 3 / 4; 
28    
29       byte byteArray[] = new byte[length]; 
30    
31       int byteTriplet = 0; 
32       int index = 0; 
33    
34       // Continue until we have less than 4 full characters left to 
35       // decode in the input. 
36       while (index + 2 < length) { 
37    
38         index = processByteArray( 
39             wrapper, 
40             byteArray, 
41             index); 
42    
43       } 
44    
45       checkIfWeHave1ByteLeft( 
46           index, 
47           length, 
48           wrapper, byteArray); 
49    
50       checkIfWeHave2BytesLeft(index, 
51                               length, 
52                               wrapper, 
53                               byteArray); 
54    
55       return byteArray; 
56     } 
57    
58     private void checkIfWeHave2BytesLeft(int index, int length, StringWrapper wrapper, byte[] byteArray) { 
59       int byteTriplet; 
60       if (index == length - 2) { 
61         // Take out the last three characters from the String 
62         byteTriplet = charToInt(wrapper.getNextUsefulChar()); 
63         byteTriplet <<= 6; 
64         byteTriplet |= charToInt(wrapper.getNextUsefulChar()); 
65         byteTriplet <<= 6; 
66         byteTriplet |= charToInt(wrapper.getNextUsefulChar()); 
67    
68         // Remove the padded zeros 
69         byteTriplet >>= 2; 
70         byteArray[index + 1] = (byte) (byteTriplet & EIGHT_BIT_MASK); 
71         byteTriplet >>= 8; 
72         byteArray[index] = (byte) (byteTriplet & EIGHT_BIT_MASK); 
73       } 
74     } 
75    
76     private void checkIfWeHave1ByteLeft(int byteIndex, int byteArrayLength, StringWrapper wrapper, byte[] result) { 
77       int byteTriplet; 
78       if (byteIndex == byteArrayLength - 1) { 
79         // Take out the last two characters from the String 
80         byteTriplet = charToInt(wrapper.getNextUsefulChar()); 
81         byteTriplet <<= 6; 
82         byteTriplet |= charToInt(wrapper.getNextUsefulChar()); 
83    
84         // Remove the padded zeros 
85         byteTriplet >>= 4; 
86         result[byteIndex] = (byte) (byteTriplet & EIGHT_BIT_MASK); 
87       } 
88     } 
89    
90     private int processByteArray(StringWrapper wrapper, byte[] result, int byteIndex) { 
91       int byteTriplet; 
92       // Package a set of four characters into a byte triplet 
93       // Each character contributes 6 bits of useful information 
94       byteTriplet = charToInt(wrapper.getNextUsefulChar()); 
95       byteTriplet <<= 6; 
96       byteTriplet |= charToInt(wrapper.getNextUsefulChar()); 
97       byteTriplet <<= 6; 
98       byteTriplet |= charToInt(wrapper.getNextUsefulChar()); 
99       byteTriplet <<= 6; 
100      byteTriplet |= charToInt(wrapper.getNextUsefulChar()); 
101   
102      // Grab a normal byte (eight bits) out of the byte triplet 
103      // and put it in the byte array 
104      result[byteIndex + 2] = (byte) (byteTriplet & EIGHT_BIT_MASK); 
105      byteTriplet >>= 8; 
106      result[byteIndex + 1] = (byte) (byteTriplet & EIGHT_BIT_MASK); 
107      byteTriplet >>= 8; 
108      result[byteIndex] = (byte) (byteTriplet & EIGHT_BIT_MASK); 
109      byteIndex += 3; 
110      return byteIndex; 
111    } 
112   
113    /** 
114     * Convert a Base64 character to its 6 bit value as defined by the mapping. 
115     * @param c Base64 character to decode 
116     * @return int representation of 6 bit value 
117     */ 
118    private int charToInt(char c) { 
119      if (c >= 'A' && c <= 'Z') { 
120        return c - 'A'; 
121      } 
122   
123      if (c >= 'a' && c <= 'z') { 
124        return (c - 'a') + Constants.LOWER_CASE_A_VALUE; 
125      } 
126   
127      if (c >= '0' && c <= '9') { 
128        return (c - '0') + Constants.ZERO_VALUE; 
129      } 
130   
131      if (c == '+') { 
132        return Constants.PLUS_VALUE; 
133      } 
134   
135      if (c == '/') { 
136        return Constants.SLASH_VALUE; 
137      } 
138   
139      throw new IllegalArgumentException(c + " is not a valid Base64 character."); 
140    } 
141   
142    /** 
143     * Simple class to wrap around the String input to ignore all of the 
144     * non-Base64 characters in the input.  Note that although '=' is 
145     * a valid character, it does not contribute to the total number 
146     * of output bytes, and is therefore ignored 
147     */ 
148    private class StringWrapper { 
149   
150      /** 
151       * The input String to be decoded 
152       */ 
153      private String mString; 
154   
155      /** 
156       * Current position in the String 
157       */ 
158      private int mIndex = 0; 
159   
160      /** 
161       * Total number of Base64 characters in the input 
162       */ 
163      private int mUsefulLength; 
164   
165      /** 
166       * @param c Character to be examined 
167       * @return Whether or not the character is a Base64 character 
168       */ 
169      private boolean isUsefulChar(char c) { 
170        return (c >= 'A' && c <= 'Z') || 
171            (c >= 'a' && c <= 'z') || 
172            (c >= '0' && c <= '9') || 
173            (c == '+') || 
174            (c == '/'); 
175      } 
176   
177      /** 
178       * Create the wrapper and determine the number of Base64 characters in 
179       * the input 
180       * @param s Input String to be decoded 
181       */ 
182      public StringWrapper(String s) { 
183        mString = s; 
184        mUsefulLength = 0; 
185        int length = mString.length(); 
186        for (int i = 0; i < length; i++) { 
187          if (isUsefulChar(mString.charAt(i))) { 
188            mUsefulLength++; 
189          } 
190        } 
191      } 
192   
193      /** 
194       * @return Total number of Base64 characters in the input.  Does 
195       * not include '=' 
196       */ 
197      public int getUsefulLength() { 
198        return mUsefulLength; 
199      } 
200   
201      /** 
202       * Traverse the String until hitting the next Base64 character. 
203       * Assumes that there is still another valid Base64 character 
204       * left in the String. 
205       */ 
206      public char getNextUsefulChar() { 
207        char result = '_';  // Start with a non-Base64 character 
208        while (!isUsefulChar(result)) { 
209          result = mString.charAt(mIndex++); 
210        } 
211   
212        return result; 
213      } 
214    } 
215  } 
216