001: /*
002: * @(#)CharacterEncoder.java 1.36 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package sun.misc;
029:
030: import java.io.InputStream;
031: import java.io.ByteArrayInputStream;
032: import java.io.OutputStream;
033: import java.io.ByteArrayOutputStream;
034: import java.io.PrintStream;
035: import java.io.IOException;
036:
037: /**
038: * This class defines the encoding half of character encoders.
039: * A character encoder is an algorithim for transforming 8 bit binary
040: * data into text (generally 7 bit ASCII or 8 bit ISO-Latin-1 text)
041: * for transmition over text channels such as e-mail and network news.
042: *
043: * The character encoders have been structured around a central theme
044: * that, in general, the encoded text has the form:
045: *
046: * <pre>
047: * [Buffer Prefix]
048: * [Line Prefix][encoded data atoms][Line Suffix]
049: * [Buffer Suffix]
050: * </pre>
051: *
052: * In the CharacterEncoder and CharacterDecoder classes, one complete
053: * chunk of data is referred to as a <i>buffer</i>. Encoded buffers
054: * are all text, and decoded buffers (sometimes just referred to as
055: * buffers) are binary octets.
056: *
057: * To create a custom encoder, you must, at a minimum, overide three
058: * abstract methods in this class.
059: * <DL>
060: * <DD>bytesPerAtom which tells the encoder how many bytes to
061: * send to encodeAtom
062: * <DD>encodeAtom which encodes the bytes sent to it as text.
063: * <DD>bytesPerLine which tells the encoder the maximum number of
064: * bytes per line.
065: * </DL>
066: *
067: * Several useful encoders have already been written and are
068: * referenced in the See Also list below.
069: *
070: * @version 1.30, 02/02/00
071: * @author Chuck McManis
072: * @see CharacterDecoder;
073: * @see UCEncoder
074: * @see UUEncoder
075: * @see BASE64Encoder
076: */
077: public abstract class CharacterEncoder {
078:
079: /** Stream that understands "printing" */
080: protected PrintStream pStream;
081:
082: /** Return the number of bytes per atom of encoding */
083: abstract protected int bytesPerAtom();
084:
085: /** Return the number of bytes that can be encoded per line */
086: abstract protected int bytesPerLine();
087:
088: /**
089: * Encode the prefix for the entire buffer. By default is simply
090: * opens the PrintStream for use by the other functions.
091: */
092: protected void encodeBufferPrefix(OutputStream aStream)
093: throws IOException {
094: pStream = new PrintStream(aStream);
095: }
096:
097: /**
098: * Encode the suffix for the entire buffer.
099: */
100: protected void encodeBufferSuffix(OutputStream aStream)
101: throws IOException {
102: }
103:
104: /**
105: * Encode the prefix that starts every output line.
106: */
107: protected void encodeLinePrefix(OutputStream aStream, int aLength)
108: throws IOException {
109: }
110:
111: /**
112: * Encode the suffix that ends every output line. By default
113: * this method just prints a <newline> into the output stream.
114: */
115: protected void encodeLineSuffix(OutputStream aStream)
116: throws IOException {
117: pStream.println();
118: }
119:
120: /** Encode one "atom" of information into characters. */
121: abstract protected void encodeAtom(OutputStream aStream,
122: byte someBytes[], int anOffset, int aLength)
123: throws IOException;
124:
125: /**
126: * This method works around the bizarre semantics of BufferedInputStream's
127: * read method.
128: */
129: protected int readFully(InputStream in, byte buffer[])
130: throws java.io.IOException {
131: for (int i = 0; i < buffer.length; i++) {
132: int q = in.read();
133: if (q == -1)
134: return i;
135: buffer[i] = (byte) q;
136: }
137: return buffer.length;
138: }
139:
140: /**
141: * Encode bytes from the input stream, and write them as text characters
142: * to the output stream. This method will run until it exhausts the
143: * input stream, but does not print the line suffix for a final
144: * line that is shorter than bytesPerLine().
145: */
146: public void encode(InputStream inStream, OutputStream outStream)
147: throws IOException {
148: int j;
149: int numBytes;
150: byte tmpbuffer[] = new byte[bytesPerLine()];
151:
152: encodeBufferPrefix(outStream);
153:
154: while (true) {
155: numBytes = readFully(inStream, tmpbuffer);
156: if (numBytes == 0) {
157: break;
158: }
159: encodeLinePrefix(outStream, numBytes);
160: for (j = 0; j < numBytes; j += bytesPerAtom()) {
161:
162: if ((j + bytesPerAtom()) <= numBytes) {
163: encodeAtom(outStream, tmpbuffer, j, bytesPerAtom());
164: } else {
165: encodeAtom(outStream, tmpbuffer, j, (numBytes) - j);
166: }
167: }
168: if (numBytes < bytesPerLine()) {
169: break;
170: } else {
171: encodeLineSuffix(outStream);
172: }
173: }
174: encodeBufferSuffix(outStream);
175: }
176:
177: /**
178: * Encode the buffer in <i>aBuffer</i> and write the encoded
179: * result to the OutputStream <i>aStream</i>.
180: */
181: public void encode(byte aBuffer[], OutputStream aStream)
182: throws IOException {
183: ByteArrayInputStream inStream = new ByteArrayInputStream(
184: aBuffer);
185: encode(inStream, aStream);
186: }
187:
188: /**
189: * A 'streamless' version of encode that simply takes a buffer of
190: * bytes and returns a string containing the encoded buffer.
191: */
192: public String encode(byte aBuffer[]) {
193: ByteArrayOutputStream outStream = new ByteArrayOutputStream();
194: ByteArrayInputStream inStream = new ByteArrayInputStream(
195: aBuffer);
196: String retVal = null;
197: try {
198: encode(inStream, outStream);
199: // explicit ascii->unicode conversion
200: retVal = outStream.toString("8859_1");
201: } catch (Exception IOException) {
202: // This should never happen.
203: throw new Error(
204: "ChracterEncoder::encodeBuffer internal error");
205: }
206: return (retVal);
207: }
208:
209: /**
210: * Encode bytes from the input stream, and write them as text characters
211: * to the output stream. This method will run until it exhausts the
212: * input stream. It differs from encode in that it will add the
213: * line at the end of a final line that is shorter than bytesPerLine().
214: */
215: public void encodeBuffer(InputStream inStream,
216: OutputStream outStream) throws IOException {
217: int j;
218: int numBytes;
219: byte tmpbuffer[] = new byte[bytesPerLine()];
220:
221: encodeBufferPrefix(outStream);
222:
223: while (true) {
224: numBytes = readFully(inStream, tmpbuffer);
225: if (numBytes == 0) {
226: break;
227: }
228: encodeLinePrefix(outStream, numBytes);
229: for (j = 0; j < numBytes; j += bytesPerAtom()) {
230: if ((j + bytesPerAtom()) <= numBytes) {
231: encodeAtom(outStream, tmpbuffer, j, bytesPerAtom());
232: } else {
233: encodeAtom(outStream, tmpbuffer, j, (numBytes) - j);
234: }
235: }
236: encodeLineSuffix(outStream);
237: if (numBytes < bytesPerLine()) {
238: break;
239: }
240: }
241: encodeBufferSuffix(outStream);
242: }
243:
244: /**
245: * Encode the buffer in <i>aBuffer</i> and write the encoded
246: * result to the OutputStream <i>aStream</i>.
247: */
248: public void encodeBuffer(byte aBuffer[], OutputStream aStream)
249: throws IOException {
250: ByteArrayInputStream inStream = new ByteArrayInputStream(
251: aBuffer);
252: encodeBuffer(inStream, aStream);
253: }
254:
255: /**
256: * A 'streamless' version of encode that simply takes a buffer of
257: * bytes and returns a string containing the encoded buffer.
258: */
259: public String encodeBuffer(byte aBuffer[]) {
260: ByteArrayOutputStream outStream = new ByteArrayOutputStream();
261: ByteArrayInputStream inStream = new ByteArrayInputStream(
262: aBuffer);
263: try {
264: encodeBuffer(inStream, outStream);
265: } catch (Exception IOException) {
266: // This should never happen.
267: throw new Error(
268: "ChracterEncoder::encodeBuffer internal error");
269: }
270: return (outStream.toString());
271: }
272:
273: }
|