create changelog entry
[debian/openrocket] / core / src / net / sf / openrocket / util / Base64.java
1 package net.sf.openrocket.util;
2
3 import java.util.Arrays;
4 import java.util.HashMap;
5 import java.util.Map;
6
7 public class Base64 {
8
9         public static final int DEFAULT_CHARS_PER_LINE = 72;
10         
11         private static final char[] ALPHABET = new char[] {
12                         'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
13                         'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
14                         'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
15                         'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
16         };
17         private static final char PAD = '=';
18
19 //      private static final byte[] REVERSE;
20 //      static {
21 //              REVERSE = new byte[128];
22 //              Arrays.fill(REVERSE, (byte)-1);
23 //              for (int i=0; i<64; i++) {
24 //                      REVERSE[ALPHABET[i]] = (byte)i;
25 //              }
26 //              REVERSE['-'] = 62;
27 //              REVERSE['_'] = 63;
28 //              REVERSE[PAD] = 0;
29 //      }
30         
31         private static final Map<Character,Integer> REVERSE = new HashMap<Character,Integer>();
32         static {
33                 for (int i=0; i<64; i++) {
34                         REVERSE.put(ALPHABET[i], i);
35                 }
36                 REVERSE.put('-', 62);
37                 REVERSE.put('_', 63);
38                 REVERSE.put(PAD, 0);
39         }
40         
41         
42         public static String encode(byte[] data) {
43                 return encode(data, DEFAULT_CHARS_PER_LINE);
44         }
45         
46         public static String encode(byte[] data, int maxColumn) {
47                 StringBuilder builder = new StringBuilder();
48                 int column = 0;
49                 
50                 for (int position=0; position < data.length; position+=3) {
51                         if (column+4 > maxColumn) {
52                                 builder.append('\n');
53                                 column = 0;
54                         }
55                         builder.append(encodeGroup(data, position));
56                         column += 4;
57                 }
58                 builder.append('\n');
59                 return builder.toString();
60         }
61         
62         
63
64         
65         public static byte[] decode(String data) {
66                 byte[] array = new byte[data.length()*3/4];
67                 char[] block = new char[4];
68                 int length = 0;
69                 
70                 for (int position=0; position < data.length(); ) {
71                         int p;
72                         for (p=0; p<4 && position < data.length(); position++) {
73                                 char c = data.charAt(position);
74                                 if (!Character.isWhitespace(c)) {
75                                         block[p] = c;
76                                         p++;
77                                 }
78                         }
79                         
80                         if (p==0)
81                                 break;
82                         if (p!=4) {
83                                 throw new IllegalArgumentException("Data ended when decoding Base64, data=" + data + ", p="+p);
84                         }
85                         
86                         int l = decodeGroup(block, array, length);
87                         length += l;
88                         if (l < 3)
89                                 break;
90                 }
91                 return Arrays.copyOf(array, length);
92         }
93         
94         
95         ////  Helper methods
96         
97         
98         /**
99          * Encode three bytes of data into four characters.
100          */
101         private static char[] encodeGroup(byte[] data, int position) {
102                 char[] c = new char[] { '=','=','=','=' };
103                 int b1=0, b2=0, b3=0;
104                 int length = data.length - position;
105                 
106                 if (length == 0)
107                         return c;
108                 
109                 if (length >= 1) {
110                         b1 = ((int)data[position])&0xFF;
111                 }
112                 if (length >= 2) {
113                         b2 = ((int)data[position+1])&0xFF;
114                 }
115                 if (length >= 3) {
116                         b3 = ((int)data[position+2])&0xFF;
117                 }
118                 
119                 c[0] = ALPHABET[b1>>2];
120                 c[1] = ALPHABET[(b1 & 3)<<4 | (b2>>4)];
121                 if (length == 1)
122                         return c;
123                 c[2] = ALPHABET[(b2 & 15)<<2 | (b3>>6)];
124                 if (length == 2)
125                         return c;
126                 c[3] = ALPHABET[b3 & 0x3f];
127                 return c;
128         }
129         
130         
131         /**
132          * Decode four chars from data into 0-3 bytes of data starting at position in array.
133          * @return      the number of bytes decoded.
134          */
135         private static int decodeGroup(char[] data, byte[] array, int position) {
136                 int b1, b2, b3, b4;
137                 
138                 try {
139                         b1 = REVERSE.get(data[0]);
140                         b2 = REVERSE.get(data[1]);
141                         b3 = REVERSE.get(data[2]);
142                         b4 = REVERSE.get(data[3]);
143                 } catch (NullPointerException e) {
144                         // If auto-boxing fails
145                         throw new IllegalArgumentException("Illegal characters in the sequence to be "+
146                                         "decoded: "+Arrays.toString(data));
147                 }
148                 
149                 array[position]   = (byte)((b1 << 2) | (b2 >> 4)); 
150                 array[position+1] = (byte)((b2 << 4) | (b3 >> 2)); 
151                 array[position+2] = (byte)((b3 << 6) | (b4)); 
152                 
153                 // Check the amount of data decoded
154                 if (data[0] == PAD)
155                         return 0;
156                 if (data[1] == PAD) {
157                         throw new IllegalArgumentException("Illegal character padding in sequence to be "+
158                                         "decoded: "+Arrays.toString(data));
159                 }
160                 if (data[2] == PAD)
161                         return 1;
162                 if (data[3] == PAD)
163                         return 2;
164                 
165                 return 3;
166         }
167         
168 }