001: /*
002: * Copyright © World Wide Web Consortium, (Massachusetts Institute of Technology,
003: * Institut National de Recherche en Informatique et en Automatique, Keio University).
004: * All Rights Reserved. http://www.w3.org/Consortium/Legal/
005: */
006: package org.w3c.tools.codec;
007:
008: import java.io.ByteArrayInputStream;
009: import java.io.ByteArrayOutputStream;
010: import java.io.IOException;
011: import java.io.InputStream;
012: import java.io.OutputStream;
013: import java.io.UnsupportedEncodingException;
014:
015: /**
016: * BASE64 encoder implementation.
017: * This object takes as parameter an input stream and an output stream. It
018: * encodes the input stream, using the BASE64 encoding rules, as defined
019: * in <a href="http://ds.internic.net/rfc/rfc1521.txt">MIME specification</a>
020: * and emit the resulting data to the output stream.
021: * @see org.w3c.tools.codec.Base64Decoder
022: */
023: public class Base64Encoder {
024: private static final int BUFFER_SIZE = 1024;
025: private static byte encoding[] = {
026: (byte) 'A',
027: (byte) 'B',
028: (byte) 'C',
029: (byte) 'D',
030: (byte) 'E',
031: (byte) 'F',
032: (byte) 'G',
033: (byte) 'H', // 0-7
034: (byte) 'I',
035: (byte) 'J',
036: (byte) 'K',
037: (byte) 'L',
038: (byte) 'M',
039: (byte) 'N',
040: (byte) 'O',
041: (byte) 'P', // 8-15
042: (byte) 'Q', (byte) 'R',
043: (byte) 'S',
044: (byte) 'T',
045: (byte) 'U',
046: (byte) 'V',
047: (byte) 'W',
048: (byte) 'X', // 16-23
049: (byte) 'Y', (byte) 'Z', (byte) 'a',
050: (byte) 'b',
051: (byte) 'c',
052: (byte) 'd',
053: (byte) 'e',
054: (byte) 'f', // 24-31
055: (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j',
056: (byte) 'k',
057: (byte) 'l',
058: (byte) 'm',
059: (byte) 'n', // 32-39
060: (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's',
061: (byte) 't',
062: (byte) 'u',
063: (byte) 'v', // 40-47
064: (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z', (byte) '0',
065: (byte) '1', (byte) '2',
066: (byte) '3', // 48-55
067: (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8',
068: (byte) '9', (byte) '+', (byte) '/', // 56-63
069: (byte) '=' // 64
070: };
071:
072: InputStream in = null;
073: OutputStream out = null;
074: boolean stringp = false;
075:
076: private final int get1(byte buf[], int off) {
077: return (buf[off] & 0xfc) >> 2;
078: }
079:
080: private final int get2(byte buf[], int off) {
081: return ((buf[off] & 0x3) << 4) | ((buf[off + 1] & 0xf0) >>> 4);
082: }
083:
084: private final int get3(byte buf[], int off) {
085: return ((buf[off + 1] & 0x0f) << 2)
086: | ((buf[off + 2] & 0xc0) >>> 6);
087: }
088:
089: private static final int get4(byte buf[], int off) {
090: return buf[off + 2] & 0x3f;
091: }
092:
093: /**
094: * Process the data: encode the input stream to the output stream.
095: * This method runs through the input stream, encoding it to the output
096: * stream.
097: * @exception IOException If we weren't able to access the input stream or
098: * the output stream.
099: */
100: public void process() throws IOException {
101: byte buffer[] = new byte[BUFFER_SIZE];
102: int got = -1;
103: int off = 0;
104: int count = 0;
105: while ((got = in.read(buffer, off, BUFFER_SIZE - off)) > 0) {
106: if (got >= 3) {
107: got += off;
108: off = 0;
109: while (off + 3 <= got) {
110: int c1 = get1(buffer, off);
111: int c2 = get2(buffer, off);
112: int c3 = get3(buffer, off);
113: int c4 = get4(buffer, off);
114: switch (count) {
115: case 73:
116: out.write(encoding[c1]);
117: out.write(encoding[c2]);
118: out.write(encoding[c3]);
119: out.write('\n');
120: out.write(encoding[c4]);
121: count = 1;
122: break;
123: case 74:
124: out.write(encoding[c1]);
125: out.write(encoding[c2]);
126: out.write('\n');
127: out.write(encoding[c3]);
128: out.write(encoding[c4]);
129: count = 2;
130: break;
131: case 75:
132: out.write(encoding[c1]);
133: out.write('\n');
134: out.write(encoding[c2]);
135: out.write(encoding[c3]);
136: out.write(encoding[c4]);
137: count = 3;
138: break;
139: case 76:
140: out.write('\n');
141: out.write(encoding[c1]);
142: out.write(encoding[c2]);
143: out.write(encoding[c3]);
144: out.write(encoding[c4]);
145: count = 4;
146: break;
147: default:
148: out.write(encoding[c1]);
149: out.write(encoding[c2]);
150: out.write(encoding[c3]);
151: out.write(encoding[c4]);
152: count += 4;
153: break;
154: }
155: off += 3;
156: }
157: // Copy remaining bytes to beginning of buffer:
158: for (int i = 0; i < 3; i++)
159: buffer[i] = (i < got - off) ? buffer[off + i]
160: : ((byte) 0);
161: off = got - off;
162: } else {
163: // Total read amount is less then 3 bytes:
164: off += got;
165: }
166: }
167: // Manage the last bytes, from 0 to off:
168: switch (off) {
169: case 1:
170: out.write(encoding[get1(buffer, 0)]);
171: out.write(encoding[get2(buffer, 0)]);
172: out.write('=');
173: out.write('=');
174: break;
175: case 2:
176: out.write(encoding[get1(buffer, 0)]);
177: out.write(encoding[get2(buffer, 0)]);
178: out.write(encoding[get3(buffer, 0)]);
179: out.write('=');
180: }
181: return;
182: }
183:
184: /**
185: * Encode the content of this encoder, as a string.
186: * This methods encode the String content, that was provided at creation
187: * time, following the BASE64 rules, as specified in the rfc1521.
188: * @return A String, reprenting the encoded content of the input String.
189: */
190: public String processString() {
191: if (!stringp)
192: throw new RuntimeException(this .getClass().getName()
193: + "[processString]" + "invalid call (not a String)");
194: try {
195: process();
196: } catch (IOException e) {
197: }
198: return ((ByteArrayOutputStream) out).toString();
199: }
200:
201: /**
202: * Create a new Base64 encoder, to encode the given string.
203: * @param input The String to be encoded.
204: */
205: public Base64Encoder(String input) {
206: byte bytes[];
207: try {
208: bytes = input.getBytes("ISO-8859-1");
209: } catch (UnsupportedEncodingException ex) {
210: throw new RuntimeException(this .getClass().getName()
211: + "[Constructor] Unable to convert"
212: + "properly char to bytes");
213: }
214: this .stringp = true;
215: this .in = new ByteArrayInputStream(bytes);
216: this .out = new ByteArrayOutputStream();
217: }
218:
219: /**
220: * Create a new Base64 encoder, encoding input to output.
221: * @param in The input stream to be encoded.
222: * @param out The output stream, to write encoded data to.
223: */
224: public Base64Encoder(InputStream in, OutputStream out) {
225: this .in = in;
226: this .out = out;
227: this .stringp = false;
228: }
229:
230: /**
231: * Testing the encoder.
232: * Run with one argument, prints the encoded version of it.
233: */
234: public static void main(String args[]) {
235: if (args.length != 1) {
236: System.out.println("Base64Encoder <string>");
237: System.exit(0);
238: }
239: Base64Encoder b = new Base64Encoder(args[0]);
240: System.out.println("[" + b.processString() + "]");
241: // joe:eoj -> am9lOmVvag==
242: // 12345678:87654321 -> MTIzNDU2Nzg6ODc2NTQzMjE=
243: }
244: }
|