View Javadoc

1   /*
2    * Copyright (c) 1995, 2005, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  package es.accv.arangi.base.util.base64;
26  
27  import java.io.InputStream;
28  import java.io.ByteArrayInputStream;
29  import java.io.OutputStream;
30  import java.io.ByteArrayOutputStream;
31  import java.io.PrintStream;
32  import java.io.IOException;
33  import java.nio.ByteBuffer;
34  
35  
36  /**
37   * This class defines the encoding half of character encoders.
38   * A character encoder is an algorithim for transforming 8 bit binary
39   * data into text (generally 7 bit ASCII or 8 bit ISO-Latin-1 text)
40   * for transmition over text channels such as e-mail and network news.
41   *
42   * The character encoders have been structured around a central theme
43   * that, in general, the encoded text has the form:
44   *
45   * <pre>
46   *      [Buffer Prefix]
47   *      [Line Prefix][encoded data atoms][Line Suffix]
48   *      [Buffer Suffix]
49   * </pre>
50   *
51   * In the CharacterEncoder and CharacterDecoder classes, one complete
52   * chunk of data is referred to as a <i>buffer</i>. Encoded buffers
53   * are all text, and decoded buffers (sometimes just referred to as
54   * buffers) are binary octets.
55   *
56   * To create a custom encoder, you must, at a minimum,  overide three
57   * abstract methods in this class.
58   * <DL>
59   * <DD>bytesPerAtom which tells the encoder how many bytes to
60   * send to encodeAtom
61   * <DD>encodeAtom which encodes the bytes sent to it as text.
62   * <DD>bytesPerLine which tells the encoder the maximum number of
63   * bytes per line.
64   * </DL>
65   *
66   * Several useful encoders have already been written and are
67   * referenced in the See Also list below.
68   *
69   * @author      Chuck McManis
70   * @see         CharacterDecoder;
71   * @see         UCEncoder
72   * @see         UUEncoder
73   * @see         BASE64Encoder
74   */
75  public abstract class CharacterEncoder {
76  
77      /** Stream that understands "printing" */
78      protected PrintStream pStream;
79  
80      /** Return the number of bytes per atom of encoding */
81      abstract protected int bytesPerAtom();
82  
83      /** Return the number of bytes that can be encoded per line */
84      abstract protected int bytesPerLine();
85  
86      /**
87       * Encode the prefix for the entire buffer. By default is simply
88       * opens the PrintStream for use by the other functions.
89       */
90      protected void encodeBufferPrefix(OutputStream aStream) throws IOException {
91          pStream = new PrintStream(aStream);
92      }
93  
94      /**
95       * Encode the suffix for the entire buffer.
96       */
97      protected void encodeBufferSuffix(OutputStream aStream) throws IOException {
98      }
99  
100     /**
101      * Encode the prefix that starts every output line.
102      */
103     protected void encodeLinePrefix(OutputStream aStream, int aLength)
104     throws IOException {
105     }
106 
107     /**
108      * Encode the suffix that ends every output line. By default
109      * this method just prints a <newline> into the output stream.
110      */
111     protected void encodeLineSuffix(OutputStream aStream) throws IOException {
112         pStream.println();
113     }
114 
115     /** Encode one "atom" of information into characters. */
116     abstract protected void encodeAtom(OutputStream aStream, byte someBytes[],
117                 int anOffset, int aLength) throws IOException;
118 
119     /**
120      * This method works around the bizarre semantics of BufferedInputStream's
121      * read method.
122      */
123     protected int readFully(InputStream in, byte buffer[])
124         throws java.io.IOException {
125         for (int i = 0; i < buffer.length; i++) {
126             int q = in.read();
127             if (q == -1)
128                 return i;
129             buffer[i] = (byte)q;
130         }
131         return buffer.length;
132     }
133 
134     /**
135      * Encode bytes from the input stream, and write them as text characters
136      * to the output stream. This method will run until it exhausts the
137      * input stream, but does not print the line suffix for a final
138      * line that is shorter than bytesPerLine().
139      */
140     public void encode(InputStream inStream, OutputStream outStream)
141         throws IOException {
142         int     j;
143         int     numBytes;
144         byte    tmpbuffer[] = new byte[bytesPerLine()];
145 
146         encodeBufferPrefix(outStream);
147 
148         while (true) {
149             numBytes = readFully(inStream, tmpbuffer);
150             if (numBytes == 0) {
151                 break;
152             }
153             encodeLinePrefix(outStream, numBytes);
154             for (j = 0; j < numBytes; j += bytesPerAtom()) {
155 
156                 if ((j + bytesPerAtom()) <= numBytes) {
157                     encodeAtom(outStream, tmpbuffer, j, bytesPerAtom());
158                 } else {
159                     encodeAtom(outStream, tmpbuffer, j, (numBytes)- j);
160                 }
161             }
162             if (numBytes < bytesPerLine()) {
163                 break;
164             } else {
165                 encodeLineSuffix(outStream);
166             }
167         }
168         encodeBufferSuffix(outStream);
169     }
170 
171     /**
172      * Encode the buffer in <i>aBuffer</i> and write the encoded
173      * result to the OutputStream <i>aStream</i>.
174      */
175     public void encode(byte aBuffer[], OutputStream aStream)
176     throws IOException {
177         ByteArrayInputStream inStream = new ByteArrayInputStream(aBuffer);
178         encode(inStream, aStream);
179     }
180 
181     /**
182      * A 'streamless' version of encode that simply takes a buffer of
183      * bytes and returns a string containing the encoded buffer.
184      */
185     public String encode(byte aBuffer[]) {
186         ByteArrayOutputStream   outStream = new ByteArrayOutputStream();
187         ByteArrayInputStream    inStream = new ByteArrayInputStream(aBuffer);
188         String retVal = null;
189         try {
190             encode(inStream, outStream);
191             // explicit ascii->unicode conversion
192             retVal = outStream.toString("8859_1");
193         } catch (Exception IOException) {
194             // This should never happen.
195             throw new Error("CharacterEncoder.encode internal error");
196         }
197         return (retVal);
198     }
199 
200     /**
201      * Return a byte array from the remaining bytes in this ByteBuffer.
202      * <P>
203      * The ByteBuffer's position will be advanced to ByteBuffer's limit.
204      * <P>
205      * To avoid an extra copy, the implementation will attempt to return the
206      * byte array backing the ByteBuffer.  If this is not possible, a
207      * new byte array will be created.
208      */
209     private byte [] getBytes(ByteBuffer bb) {
210         /*
211          * This should never return a BufferOverflowException, as we're
212          * careful to allocate just the right amount.
213          */
214         byte [] buf = null;
215 
216         /*
217          * If it has a usable backing byte buffer, use it.  Use only
218          * if the array exactly represents the current ByteBuffer.
219          */
220         if (bb.hasArray()) {
221             byte [] tmp = bb.array();
222             if ((tmp.length == bb.capacity()) &&
223                     (tmp.length == bb.remaining())) {
224                 buf = tmp;
225                 bb.position(bb.limit());
226             }
227         }
228 
229         if (buf == null) {
230             /*
231              * This class doesn't have a concept of encode(buf, len, off),
232              * so if we have a partial buffer, we must reallocate
233              * space.
234              */
235             buf = new byte[bb.remaining()];
236 
237             /*
238              * position() automatically updated
239              */
240             bb.get(buf);
241         }
242 
243         return buf;
244     }
245 
246     /**
247      * Encode the <i>aBuffer</i> ByteBuffer and write the encoded
248      * result to the OutputStream <i>aStream</i>.
249      * <P>
250      * The ByteBuffer's position will be advanced to ByteBuffer's limit.
251      */
252     public void encode(ByteBuffer aBuffer, OutputStream aStream)
253         throws IOException {
254         byte [] buf = getBytes(aBuffer);
255         encode(buf, aStream);
256     }
257 
258     /**
259      * A 'streamless' version of encode that simply takes a ByteBuffer
260      * and returns a string containing the encoded buffer.
261      * <P>
262      * The ByteBuffer's position will be advanced to ByteBuffer's limit.
263      */
264     public String encode(ByteBuffer aBuffer) {
265         byte [] buf = getBytes(aBuffer);
266         return encode(buf);
267     }
268 
269     /**
270      * Encode bytes from the input stream, and write them as text characters
271      * to the output stream. This method will run until it exhausts the
272      * input stream. It differs from encode in that it will add the
273      * line at the end of a final line that is shorter than bytesPerLine().
274      */
275     public void encodeBuffer(InputStream inStream, OutputStream outStream)
276         throws IOException {
277         int     j;
278         int     numBytes;
279         byte    tmpbuffer[] = new byte[bytesPerLine()];
280 
281         encodeBufferPrefix(outStream);
282 
283         while (true) {
284             numBytes = readFully(inStream, tmpbuffer);
285             if (numBytes == 0) {
286                 break;
287             }
288             encodeLinePrefix(outStream, numBytes);
289             for (j = 0; j < numBytes; j += bytesPerAtom()) {
290                 if ((j + bytesPerAtom()) <= numBytes) {
291                     encodeAtom(outStream, tmpbuffer, j, bytesPerAtom());
292                 } else {
293                     encodeAtom(outStream, tmpbuffer, j, (numBytes)- j);
294                 }
295             }
296             encodeLineSuffix(outStream);
297             if (numBytes < bytesPerLine()) {
298                 break;
299             }
300         }
301         encodeBufferSuffix(outStream);
302     }
303 
304     /**
305      * Encode the buffer in <i>aBuffer</i> and write the encoded
306      * result to the OutputStream <i>aStream</i>.
307      */
308     public void encodeBuffer(byte aBuffer[], OutputStream aStream)
309     throws IOException {
310         ByteArrayInputStream inStream = new ByteArrayInputStream(aBuffer);
311         encodeBuffer(inStream, aStream);
312     }
313 
314     /**
315      * A 'streamless' version of encode that simply takes a buffer of
316      * bytes and returns a string containing the encoded buffer.
317      */
318     public String encodeBuffer(byte aBuffer[]) {
319         ByteArrayOutputStream   outStream = new ByteArrayOutputStream();
320         ByteArrayInputStream    inStream = new ByteArrayInputStream(aBuffer);
321         try {
322             encodeBuffer(inStream, outStream);
323         } catch (Exception IOException) {
324             // This should never happen.
325             throw new Error("CharacterEncoder.encodeBuffer internal error");
326         }
327         return (outStream.toString());
328     }
329 
330     /**
331      * Encode the <i>aBuffer</i> ByteBuffer and write the encoded
332      * result to the OutputStream <i>aStream</i>.
333      * <P>
334      * The ByteBuffer's position will be advanced to ByteBuffer's limit.
335      */
336     public void encodeBuffer(ByteBuffer aBuffer, OutputStream aStream)
337         throws IOException {
338         byte [] buf = getBytes(aBuffer);
339         encodeBuffer(buf, aStream);
340     }
341 
342     /**
343      * A 'streamless' version of encode that simply takes a ByteBuffer
344      * and returns a string containing the encoded buffer.
345      * <P>
346      * The ByteBuffer's position will be advanced to ByteBuffer's limit.
347      */
348     public String encodeBuffer(ByteBuffer aBuffer) {
349         byte [] buf = getBytes(aBuffer);
350         return encodeBuffer(buf);
351     }
352 
353 }