View Javadoc

1   package org.apache.maven.util;
2   
3   /*
4    * ====================================================================
5    * Copyright 2001-2004 The Apache Software Foundation.
6    * 
7    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
8    * use this file except in compliance with the License. You may obtain a copy
9    * of the License at
10   * 
11   * http://www.apache.org/licenses/LICENSE-2.0
12   * 
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16   * License for the specific language governing permissions and limitations
17   * under the License.
18   * ====================================================================
19   */
20  
21  import java.io.ByteArrayOutputStream;
22  
23  // import org.apache.commons.logging.Log;
24  // import org.apache.commons.logging.LogFactory;
25  
26  /**
27   * Encode/Decode Base-64.
28   * 
29   * @author John Casey
30   */
31  public final class Base64
32  {
33  
34      // private static final Log LOG = LogFactory.getLog( Base64.class );
35  
36      private static final String CRLF = System.getProperty( "line.separator" );
37  
38      private static final int LINE_END = 64;
39  
40      public static String encode( byte[] data )
41      {
42          return Base64.encode( data, true );
43      }
44  
45      public static String encode( byte[] data, boolean useLineDelimiter )
46      {
47          if ( data == null )
48          {
49              return null;
50          }
51          else if ( data.length == 0 )
52          {
53              return "";
54          }
55  
56          int padding = 3 - ( data.length % 3 );
57  
58          // if ( LOG.isDebugEnabled() )
59          // {
60          // LOG.debug( "padding = " + padding + "characters." );
61          // }
62  
63          StringBuffer buffer = new StringBuffer();
64  
65          for ( int i = 0; i < data.length; i += 3 )
66          {
67              // if ( LOG.isDebugEnabled() )
68              // {
69              // LOG.debug( "iteration base offset = " + i );
70              // }
71  
72              int neutral = ( data[i] < 0 ? data[i] + 256 : data[i] );
73  
74              int block = ( neutral & 0xff );
75              // if ( LOG.isDebugEnabled() )
76              // {
77              // LOG.debug( "after first byte, block = " + Integer.toBinaryString( block ) );
78              // }
79  
80              boolean inLastSegment = false;
81  
82              block <<= 8;
83              if ( i + 1 < data.length )
84              {
85                  neutral = ( data[i + 1] < 0 ? data[i + 1] + 256 : data[i + 1] );
86                  block |= ( neutral & 0xff );
87              }
88              else
89              {
90                  inLastSegment = true;
91              }
92              // if ( LOG.isDebugEnabled() )
93              // {
94              // LOG.debug( "after second byte, block = " + Integer.toBinaryString( block ) + "; inLastSegment = "
95              // + inLastSegment );
96              // }
97  
98              block <<= 8;
99              if ( i + 2 < data.length )
100             {
101                 neutral = ( data[i + 2] < 0 ? data[i + 2] + 256 : data[i + 2] );
102                 block |= ( neutral & 0xff );
103             }
104             else
105             {
106                 inLastSegment = true;
107             }
108             // if ( LOG.isDebugEnabled() )
109             // {
110             // LOG.debug( "after third byte, block = " + Integer.toBinaryString( block ) + "; inLastSegment = "
111             // + inLastSegment );
112             // }
113 
114             char[] encoded = new char[4];
115 
116             encoded[0] = toBase64Char( ( block >>> 18 ) & 0x3f );
117             // if ( LOG.isDebugEnabled() )
118             // {
119             // LOG.debug( "first character = " + encoded[0] );
120             // }
121 
122             encoded[1] = toBase64Char( ( block >>> 12 ) & 0x3f );
123             // if ( LOG.isDebugEnabled() )
124             // {
125             // LOG.debug( "second character = " + encoded[1] );
126             // }
127 
128             if ( inLastSegment && ( padding > 1 ) )
129             {
130                 encoded[2] = '=';
131             }
132             else
133             {
134                 encoded[2] = toBase64Char( ( block >>> 6 ) & 0x3f );
135             }
136             // if ( LOG.isDebugEnabled() )
137             // {
138             // LOG.debug( "third character = " + encoded[2] );
139             // }
140 
141             if ( inLastSegment && ( padding > 0 ) )
142             {
143                 encoded[3] = '=';
144             }
145             else
146             {
147                 encoded[3] = toBase64Char( block & 0x3f );
148             }
149             // if ( LOG.isDebugEnabled() )
150             // {
151             // LOG.debug( "fourth character = " + encoded[3] );
152             // }
153 
154             buffer.append( encoded );
155         }
156 
157         if ( useLineDelimiter )
158         {
159             return canonicalize( buffer.toString() );
160         }
161         else
162         {
163             return buffer.toString();
164         }
165     }
166 
167     public static byte[] decode( String src )
168     {
169         return Base64.decode( src, true );
170     }
171 
172     public static byte[] decode( String src, boolean useLineDelimiter )
173     {
174         if ( src == null )
175         {
176             return null;
177         }
178         else if ( src.length() < 1 )
179         {
180             return new byte[0];
181         }
182 
183         // if ( LOG.isDebugEnabled() )
184         // {
185         // LOG.debug( "pre-canonicalization = \n" + src );
186         // }
187         String data = src;
188 
189         if ( useLineDelimiter )
190         {
191             data = deCanonicalize( src );
192         }
193         // if ( LOG.isDebugEnabled() )
194         // {
195         // LOG.debug( "post-canonicalization = \n" + data );
196         // }
197 
198         ByteArrayOutputStream baos = new ByteArrayOutputStream();
199 
200         char[] input = data.toCharArray();
201 
202         for ( int i = 0; i < input.length; i += 4 )
203         {
204             // if ( LOG.isDebugEnabled() )
205             // {
206             // LOG.debug( "iteration base offset = " + i );
207             // }
208 
209             int block = ( toBase64Int( input[i] ) & 0x3f );
210             // if ( LOG.isDebugEnabled() )
211             // {
212             // LOG.debug( "block after first char [" + input[i] + "] = " + Integer.toBinaryString( block ) );
213             // }
214 
215             block <<= 6;
216             block |= ( toBase64Int( input[i + 1] ) & 0x3f );
217             // if ( LOG.isDebugEnabled() )
218             // {
219             // LOG.debug( "block after second char [" + input[i + 1] + "] = " + Integer.toBinaryString( block ) );
220             // }
221 
222             boolean inPadding = false;
223             boolean twoCharPadding = false;
224             block <<= 6;
225             if ( input[i + 2] != '=' )
226             {
227                 block |= ( toBase64Int( input[i + 2] ) & 0x3f );
228             }
229             else
230             {
231                 twoCharPadding = true;
232                 inPadding = true;
233             }
234 
235             // if ( LOG.isDebugEnabled() )
236             // {
237             // LOG.debug( "block after third char [" + input[i + 2] + "] = " + Integer.toBinaryString( block ) );
238             // }
239 
240             block <<= 6;
241             if ( input[i + 3] != '=' )
242             {
243                 block |= ( toBase64Int( input[i + 3] ) & 0x3f );
244             }
245             else
246             {
247                 inPadding = true;
248             }
249 
250             // if ( LOG.isDebugEnabled() )
251             // {
252             // LOG.debug( "block after fourth char [" + input[i + 3] + "] = " + Integer.toBinaryString( block ) );
253             // }
254 
255             baos.write( ( block >>> 16 ) & 0xff );
256             // if ( LOG.isDebugEnabled() )
257             // {
258             // LOG.debug( "byte[" + ( index++ ) + "] = " + ( ( block >>> 16 ) & 0xff ) );
259             // }
260 
261             if ( !inPadding || !twoCharPadding )
262             {
263                 baos.write( ( block >>> 8 ) & 0xff );
264                 // if ( LOG.isDebugEnabled() )
265                 // {
266                 // LOG.debug( "byte[" + ( index++ ) + "] = " + ( ( block >>> 8 ) & 0xff ) );
267                 // }
268             }
269 
270             if ( !inPadding )
271             {
272                 baos.write( block & 0xff );
273                 // if ( LOG.isDebugEnabled() )
274                 // {
275                 // LOG.debug( "byte[" + ( index++ ) + "] = " + ( block & 0xff ) );
276                 // }
277             }
278         }
279 
280         byte[] result = baos.toByteArray();
281         // if ( LOG.isDebugEnabled() )
282         // {
283         // LOG.debug( "byte array is " + result.length + " bytes long." );
284         // }
285 
286         return result;
287     }
288 
289     private static char toBase64Char( int input )
290     {
291         if ( ( input > -1 ) && ( input < 26 ) )
292         {
293             return (char) ( 'A' + input );
294         }
295         else if ( ( input > 25 ) && ( input < 52 ) )
296         {
297             return (char) ( 'a' + input - 26 );
298         }
299         else if ( ( input > 51 ) && ( input < 62 ) )
300         {
301             return (char) ( '0' + input - 52 );
302         }
303         else if ( input == 62 )
304         {
305             return '+';
306         }
307         else if ( input == 63 )
308         {
309             return '/';
310         }
311         else
312         {
313             return '?';
314         }
315     }
316 
317     private static int toBase64Int( char input )
318     {
319         if ( ( input >= 'A' ) && ( input <= 'Z' ) )
320         {
321             return input - 'A';
322         }
323         else if ( ( input >= 'a' ) && ( input <= 'z' ) )
324         {
325             return input + 26 - 'a';
326         }
327         else if ( ( input >= '0' ) && ( input <= '9' ) )
328         {
329             return input + 52 - '0';
330         }
331         else if ( input == '+' )
332         {
333             return 62;
334         }
335         else if ( input == '/' )
336         {
337             return 63;
338         }
339         else
340         {
341             return 0;
342         }
343     }
344 
345     private static String deCanonicalize( String data )
346     {
347         if ( data == null )
348         {
349             return null;
350         }
351 
352         StringBuffer buffer = new StringBuffer( data.length() );
353         for ( int i = 0; i < data.length(); i++ )
354         {
355             char c = data.charAt( i );
356             if ( ( c != '\r' ) && ( c != '\n' ) )
357             {
358                 buffer.append( c );
359             }
360         }
361 
362         return buffer.toString();
363     }
364 
365     private static String canonicalize( String data )
366     {
367         StringBuffer buffer = new StringBuffer( (int) ( data.length() * 1.1 ) );
368 
369         int col = 0;
370         for ( int i = 0; i < data.length(); i++ )
371         {
372             if ( col == LINE_END )
373             {
374                 buffer.append( CRLF );
375                 col = 0;
376             }
377 
378             buffer.append( data.charAt( i ) );
379             col++;
380         }
381 
382         buffer.append( CRLF );
383 
384         return buffer.toString();
385     }
386 
387 }