lose embedded source jars from upstream branch
[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 import java.util.Random;
7
8 public class Base64 {
9
10         public static final int DEFAULT_CHARS_PER_LINE = 72;
11         
12         private static final char[] ALPHABET = new char[] {
13                         'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
14                         'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
15                         'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
16                         'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
17         };
18         private static final char PAD = '=';
19
20 //      private static final byte[] REVERSE;
21 //      static {
22 //              REVERSE = new byte[128];
23 //              Arrays.fill(REVERSE, (byte)-1);
24 //              for (int i=0; i<64; i++) {
25 //                      REVERSE[ALPHABET[i]] = (byte)i;
26 //              }
27 //              REVERSE['-'] = 62;
28 //              REVERSE['_'] = 63;
29 //              REVERSE[PAD] = 0;
30 //      }
31         
32         private static final Map<Character,Integer> REVERSE = new HashMap<Character,Integer>();
33         static {
34                 for (int i=0; i<64; i++) {
35                         REVERSE.put(ALPHABET[i], i);
36                 }
37                 REVERSE.put('-', 62);
38                 REVERSE.put('_', 63);
39                 REVERSE.put(PAD, 0);
40         }
41         
42         
43         public static String encode(byte[] data) {
44                 return encode(data, DEFAULT_CHARS_PER_LINE);
45         }
46         
47         public static String encode(byte[] data, int maxColumn) {
48                 StringBuilder builder = new StringBuilder();
49                 int column = 0;
50                 
51                 for (int position=0; position < data.length; position+=3) {
52                         if (column+4 > maxColumn) {
53                                 builder.append('\n');
54                                 column = 0;
55                         }
56                         builder.append(encodeGroup(data, position));
57                         column += 4;
58                 }
59                 builder.append('\n');
60                 return builder.toString();
61         }
62         
63         
64
65         
66         public static byte[] decode(String data) {
67                 byte[] array = new byte[data.length()*3/4];
68                 char[] block = new char[4];
69                 int length = 0;
70                 
71                 for (int position=0; position < data.length(); ) {
72                         int p;
73                         for (p=0; p<4 && position < data.length(); position++) {
74                                 char c = data.charAt(position);
75                                 if (!Character.isWhitespace(c)) {
76                                         block[p] = c;
77                                         p++;
78                                 }
79                         }
80                         
81                         if (p==0)
82                                 break;
83                         if (p!=4) {
84                                 throw new IllegalArgumentException("Data ended when decoding Base64, p="+p);
85                         }
86                         
87                         int l = decodeGroup(block, array, length);
88                         length += l;
89                         if (l < 3)
90                                 break;
91                 }
92                 return Arrays.copyOf(array, length);
93         }
94         
95         
96         ////  Helper methods
97         
98         
99         /**
100          * Encode three bytes of data into four characters.
101          */
102         private static char[] encodeGroup(byte[] data, int position) {
103                 char[] c = new char[] { '=','=','=','=' };
104                 int b1=0, b2=0, b3=0;
105                 int length = data.length - position;
106                 
107                 if (length == 0)
108                         return c;
109                 
110                 if (length >= 1) {
111                         b1 = ((int)data[position])&0xFF;
112                 }
113                 if (length >= 2) {
114                         b2 = ((int)data[position+1])&0xFF;
115                 }
116                 if (length >= 3) {
117                         b3 = ((int)data[position+2])&0xFF;
118                 }
119                 
120                 c[0] = ALPHABET[b1>>2];
121                 c[1] = ALPHABET[(b1 & 3)<<4 | (b2>>4)];
122                 if (length == 1)
123                         return c;
124                 c[2] = ALPHABET[(b2 & 15)<<2 | (b3>>6)];
125                 if (length == 2)
126                         return c;
127                 c[3] = ALPHABET[b3 & 0x3f];
128                 return c;
129         }
130         
131         
132         /**
133          * Decode four chars from data into 0-3 bytes of data starting at position in array.
134          * @return      the number of bytes decoded.
135          */
136         private static int decodeGroup(char[] data, byte[] array, int position) {
137                 int b1, b2, b3, b4;
138                 
139                 try {
140                         b1 = REVERSE.get(data[0]);
141                         b2 = REVERSE.get(data[1]);
142                         b3 = REVERSE.get(data[2]);
143                         b4 = REVERSE.get(data[3]);
144                 } catch (NullPointerException e) {
145                         // If auto-boxing fails
146                         throw new IllegalArgumentException("Illegal characters in the sequence to be "+
147                                         "decoded: "+Arrays.toString(data));
148                 }
149                 
150                 array[position]   = (byte)((b1 << 2) | (b2 >> 4)); 
151                 array[position+1] = (byte)((b2 << 4) | (b3 >> 2)); 
152                 array[position+2] = (byte)((b3 << 6) | (b4)); 
153                 
154                 // Check the amount of data decoded
155                 if (data[0] == PAD)
156                         return 0;
157                 if (data[1] == PAD) {
158                         throw new IllegalArgumentException("Illegal character padding in sequence to be "+
159                                         "decoded: "+Arrays.toString(data));
160                 }
161                 if (data[2] == PAD)
162                         return 1;
163                 if (data[3] == PAD)
164                         return 2;
165                 
166                 return 3;
167         }
168         
169         
170         
171         public static void main(String[] arg) {
172                 Random rnd = new Random();
173                 
174                 for (int round=0; round < 1000; round++) {
175                         int n = rnd.nextInt(1000);
176                         n = 100000;
177                         
178                         byte[] array = new byte[n];
179                         rnd.nextBytes(array);
180
181                         String encoded = encode(array);
182                         
183                         System.out.println(encoded);
184                         System.exit(0);
185 //                      for (int i=0; i<1000; i++) {
186 //                              int pos = rnd.nextInt(encoded.length());
187 //                              String s1 = encoded.substring(0, pos);
188 //                              String s2 = encoded.substring(pos);
189 //                              switch (rnd.nextInt(15)) {
190 //                              case 0:
191 //                                      encoded = s1 + " " + s2;
192 //                                      break;
193 //                              case 1:
194 //                                      encoded = s1 + "\u0009" + s2;
195 //                                      break;
196 //                              case 2:
197 //                                      encoded = s1 + "\n" + s2;
198 //                                      break;
199 //                              case 3:
200 //                                      encoded = s1 + "\u000B" + s2;
201 //                                      break;
202 //                              case 4:
203 //                                      encoded = s1 + "\r" + s2;
204 //                                      break;
205 //                              case 5:
206 //                                      encoded = s1 + "\u000C" + s2;
207 //                                      break;
208 //                              case 6:
209 //                                      encoded = s1 + "\u001C" + s2;
210 //                                      break;
211 //                              }
212 //                      }
213                         
214                         byte[] decoded = null;
215                         try {
216                                 decoded = decode(encoded);
217                         } catch (IllegalArgumentException e) {
218                                 e.printStackTrace();
219                                 System.err.println("Bad data:\n"+encoded);
220                                 System.exit(1);
221                         }
222                         
223                         if (!Arrays.equals(array, decoded)) {
224                                 System.err.println("Data differs!  n="+n);
225                                 System.exit(1);
226                         }
227                         System.out.println("n="+n+" ok!");
228                 }
229         }
230         
231         
232 }