001: /*
002: * Copyright 1995-2005 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package sun.misc;
027:
028: import java.io.InputStream;
029: import java.io.ByteArrayInputStream;
030: import java.io.OutputStream;
031: import java.io.ByteArrayOutputStream;
032: import java.io.PrintStream;
033: import java.io.IOException;
034: import java.nio.ByteBuffer;
035:
036: /**
037: * This class defines the encoding half of character encoders.
038: * A character encoder is an algorithim for transforming 8 bit binary
039: * data into text (generally 7 bit ASCII or 8 bit ISO-Latin-1 text)
040: * for transmition over text channels such as e-mail and network news.
041: *
042: * The character encoders have been structured around a central theme
043: * that, in general, the encoded text has the form:
044: *
045: * <pre>
046: * [Buffer Prefix]
047: * [Line Prefix][encoded data atoms][Line Suffix]
048: * [Buffer Suffix]
049: * </pre>
050: *
051: * In the CharacterEncoder and CharacterDecoder classes, one complete
052: * chunk of data is referred to as a <i>buffer</i>. Encoded buffers
053: * are all text, and decoded buffers (sometimes just referred to as
054: * buffers) are binary octets.
055: *
056: * To create a custom encoder, you must, at a minimum, overide three
057: * abstract methods in this class.
058: * <DL>
059: * <DD>bytesPerAtom which tells the encoder how many bytes to
060: * send to encodeAtom
061: * <DD>encodeAtom which encodes the bytes sent to it as text.
062: * <DD>bytesPerLine which tells the encoder the maximum number of
063: * bytes per line.
064: * </DL>
065: *
066: * Several useful encoders have already been written and are
067: * referenced in the See Also list below.
068: *
069: * @version 1.44, 05/05/07
070: * @author Chuck McManis
071: * @see CharacterDecoder;
072: * @see UCEncoder
073: * @see UUEncoder
074: * @see BASE64Encoder
075: */
076: public abstract class CharacterEncoder {
077:
078: /** Stream that understands "printing" */
079: protected PrintStream pStream;
080:
081: /** Return the number of bytes per atom of encoding */
082: abstract protected int bytesPerAtom();
083:
084: /** Return the number of bytes that can be encoded per line */
085: abstract protected int bytesPerLine();
086:
087: /**
088: * Encode the prefix for the entire buffer. By default is simply
089: * opens the PrintStream for use by the other functions.
090: */
091: protected void encodeBufferPrefix(OutputStream aStream)
092: throws IOException {
093: pStream = new PrintStream(aStream);
094: }
095:
096: /**
097: * Encode the suffix for the entire buffer.
098: */
099: protected void encodeBufferSuffix(OutputStream aStream)
100: throws IOException {
101: }
102:
103: /**
104: * Encode the prefix that starts every output line.
105: */
106: protected void encodeLinePrefix(OutputStream aStream, int aLength)
107: throws IOException {
108: }
109:
110: /**
111: * Encode the suffix that ends every output line. By default
112: * this method just prints a <newline> into the output stream.
113: */
114: protected void encodeLineSuffix(OutputStream aStream)
115: throws IOException {
116: pStream.println();
117: }
118:
119: /** Encode one "atom" of information into characters. */
120: abstract protected void encodeAtom(OutputStream aStream,
121: byte someBytes[], int anOffset, int aLength)
122: throws IOException;
123:
124: /**
125: * This method works around the bizarre semantics of BufferedInputStream's
126: * read method.
127: */
128: protected int readFully(InputStream in, byte buffer[])
129: throws java.io.IOException {
130: for (int i = 0; i < buffer.length; i++) {
131: int q = in.read();
132: if (q == -1)
133: return i;
134: buffer[i] = (byte) q;
135: }
136: return buffer.length;
137: }
138:
139: /**
140: * Encode bytes from the input stream, and write them as text characters
141: * to the output stream. This method will run until it exhausts the
142: * input stream, but does not print the line suffix for a final
143: * line that is shorter than bytesPerLine().
144: */
145: public void encode(InputStream inStream, OutputStream outStream)
146: throws IOException {
147: int j;
148: int numBytes;
149: byte tmpbuffer[] = new byte[bytesPerLine()];
150:
151: encodeBufferPrefix(outStream);
152:
153: while (true) {
154: numBytes = readFully(inStream, tmpbuffer);
155: if (numBytes == 0) {
156: break;
157: }
158: encodeLinePrefix(outStream, numBytes);
159: for (j = 0; j < numBytes; j += bytesPerAtom()) {
160:
161: if ((j + bytesPerAtom()) <= numBytes) {
162: encodeAtom(outStream, tmpbuffer, j, bytesPerAtom());
163: } else {
164: encodeAtom(outStream, tmpbuffer, j, (numBytes) - j);
165: }
166: }
167: if (numBytes < bytesPerLine()) {
168: break;
169: } else {
170: encodeLineSuffix(outStream);
171: }
172: }
173: encodeBufferSuffix(outStream);
174: }
175:
176: /**
177: * Encode the buffer in <i>aBuffer</i> and write the encoded
178: * result to the OutputStream <i>aStream</i>.
179: */
180: public void encode(byte aBuffer[], OutputStream aStream)
181: throws IOException {
182: ByteArrayInputStream inStream = new ByteArrayInputStream(
183: aBuffer);
184: encode(inStream, aStream);
185: }
186:
187: /**
188: * A 'streamless' version of encode that simply takes a buffer of
189: * bytes and returns a string containing the encoded buffer.
190: */
191: public String encode(byte aBuffer[]) {
192: ByteArrayOutputStream outStream = new ByteArrayOutputStream();
193: ByteArrayInputStream inStream = new ByteArrayInputStream(
194: aBuffer);
195: String retVal = null;
196: try {
197: encode(inStream, outStream);
198: // explicit ascii->unicode conversion
199: retVal = outStream.toString("8859_1");
200: } catch (Exception IOException) {
201: // This should never happen.
202: throw new Error("CharacterEncoder.encode internal error");
203: }
204: return (retVal);
205: }
206:
207: /**
208: * Return a byte array from the remaining bytes in this ByteBuffer.
209: * <P>
210: * The ByteBuffer's position will be advanced to ByteBuffer's limit.
211: * <P>
212: * To avoid an extra copy, the implementation will attempt to return the
213: * byte array backing the ByteBuffer. If this is not possible, a
214: * new byte array will be created.
215: */
216: private byte[] getBytes(ByteBuffer bb) {
217: /*
218: * This should never return a BufferOverflowException, as we're
219: * careful to allocate just the right amount.
220: */
221: byte[] buf = null;
222:
223: /*
224: * If it has a usable backing byte buffer, use it. Use only
225: * if the array exactly represents the current ByteBuffer.
226: */
227: if (bb.hasArray()) {
228: byte[] tmp = bb.array();
229: if ((tmp.length == bb.capacity())
230: && (tmp.length == bb.remaining())) {
231: buf = tmp;
232: bb.position(bb.limit());
233: }
234: }
235:
236: if (buf == null) {
237: /*
238: * This class doesn't have a concept of encode(buf, len, off),
239: * so if we have a partial buffer, we must reallocate
240: * space.
241: */
242: buf = new byte[bb.remaining()];
243:
244: /*
245: * position() automatically updated
246: */
247: bb.get(buf);
248: }
249:
250: return buf;
251: }
252:
253: /**
254: * Encode the <i>aBuffer</i> ByteBuffer and write the encoded
255: * result to the OutputStream <i>aStream</i>.
256: * <P>
257: * The ByteBuffer's position will be advanced to ByteBuffer's limit.
258: */
259: public void encode(ByteBuffer aBuffer, OutputStream aStream)
260: throws IOException {
261: byte[] buf = getBytes(aBuffer);
262: encode(buf, aStream);
263: }
264:
265: /**
266: * A 'streamless' version of encode that simply takes a ByteBuffer
267: * and returns a string containing the encoded buffer.
268: * <P>
269: * The ByteBuffer's position will be advanced to ByteBuffer's limit.
270: */
271: public String encode(ByteBuffer aBuffer) {
272: byte[] buf = getBytes(aBuffer);
273: return encode(buf);
274: }
275:
276: /**
277: * Encode bytes from the input stream, and write them as text characters
278: * to the output stream. This method will run until it exhausts the
279: * input stream. It differs from encode in that it will add the
280: * line at the end of a final line that is shorter than bytesPerLine().
281: */
282: public void encodeBuffer(InputStream inStream,
283: OutputStream outStream) throws IOException {
284: int j;
285: int numBytes;
286: byte tmpbuffer[] = new byte[bytesPerLine()];
287:
288: encodeBufferPrefix(outStream);
289:
290: while (true) {
291: numBytes = readFully(inStream, tmpbuffer);
292: if (numBytes == 0) {
293: break;
294: }
295: encodeLinePrefix(outStream, numBytes);
296: for (j = 0; j < numBytes; j += bytesPerAtom()) {
297: if ((j + bytesPerAtom()) <= numBytes) {
298: encodeAtom(outStream, tmpbuffer, j, bytesPerAtom());
299: } else {
300: encodeAtom(outStream, tmpbuffer, j, (numBytes) - j);
301: }
302: }
303: encodeLineSuffix(outStream);
304: if (numBytes < bytesPerLine()) {
305: break;
306: }
307: }
308: encodeBufferSuffix(outStream);
309: }
310:
311: /**
312: * Encode the buffer in <i>aBuffer</i> and write the encoded
313: * result to the OutputStream <i>aStream</i>.
314: */
315: public void encodeBuffer(byte aBuffer[], OutputStream aStream)
316: throws IOException {
317: ByteArrayInputStream inStream = new ByteArrayInputStream(
318: aBuffer);
319: encodeBuffer(inStream, aStream);
320: }
321:
322: /**
323: * A 'streamless' version of encode that simply takes a buffer of
324: * bytes and returns a string containing the encoded buffer.
325: */
326: public String encodeBuffer(byte aBuffer[]) {
327: ByteArrayOutputStream outStream = new ByteArrayOutputStream();
328: ByteArrayInputStream inStream = new ByteArrayInputStream(
329: aBuffer);
330: try {
331: encodeBuffer(inStream, outStream);
332: } catch (Exception IOException) {
333: // This should never happen.
334: throw new Error(
335: "CharacterEncoder.encodeBuffer internal error");
336: }
337: return (outStream.toString());
338: }
339:
340: /**
341: * Encode the <i>aBuffer</i> ByteBuffer and write the encoded
342: * result to the OutputStream <i>aStream</i>.
343: * <P>
344: * The ByteBuffer's position will be advanced to ByteBuffer's limit.
345: */
346: public void encodeBuffer(ByteBuffer aBuffer, OutputStream aStream)
347: throws IOException {
348: byte[] buf = getBytes(aBuffer);
349: encodeBuffer(buf, aStream);
350: }
351:
352: /**
353: * A 'streamless' version of encode that simply takes a ByteBuffer
354: * and returns a string containing the encoded buffer.
355: * <P>
356: * The ByteBuffer's position will be advanced to ByteBuffer's limit.
357: */
358: public String encodeBuffer(ByteBuffer aBuffer) {
359: byte[] buf = getBytes(aBuffer);
360: return encodeBuffer(buf);
361: }
362:
363: }
|