KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > mail > util > QPDecoderStream


1 /*
2  * The contents of this file are subject to the terms
3  * of the Common Development and Distribution License
4  * (the "License"). You may not use this file except
5  * in compliance with the License.
6  *
7  * You can obtain a copy of the license at
8  * glassfish/bootstrap/legal/CDDLv1.0.txt or
9  * https://glassfish.dev.java.net/public/CDDLv1.0.html.
10  * See the License for the specific language governing
11  * permissions and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL
14  * HEADER in each file and include the License file at
15  * glassfish/bootstrap/legal/CDDLv1.0.txt. If applicable,
16  * add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your
18  * own identifying information: Portions Copyright [yyyy]
19  * [name of copyright owner]
20  */

21
22 /*
23  * @(#)QPDecoderStream.java 1.10 05/08/29
24  *
25  * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved.
26  */

27
28 package com.sun.mail.util;
29
30 import java.io.*;
31
32 /**
33  * This class implements a QP Decoder. It is implemented as
34  * a FilterInputStream, so one can just wrap this class around
35  * any input stream and read bytes from this filter. The decoding
36  * is done as the bytes are read out.
37  *
38  * @author John Mani
39  */

40
41 public class QPDecoderStream extends FilterInputStream {
42     protected byte[] ba = new byte[2];
43     protected int spaces = 0;
44
45     /**
46      * Create a Quoted Printable decoder that decodes the specified
47      * input stream.
48      * @param in the input stream
49      */

50     public QPDecoderStream(InputStream in) {
51     super(new PushbackInputStream(in, 2)); // pushback of size=2
52
}
53
54     /**
55      * Read the next decoded byte from this input stream. The byte
56      * is returned as an <code>int</code> in the range <code>0</code>
57      * to <code>255</code>. If no byte is available because the end of
58      * the stream has been reached, the value <code>-1</code> is returned.
59      * This method blocks until input data is available, the end of the
60      * stream is detected, or an exception is thrown.
61      *
62      * @return the next byte of data, or <code>-1</code> if the end of the
63      * stream is reached.
64      * @exception IOException if an I/O error occurs.
65      */

66     public int read() throws IOException {
67     if (spaces > 0) {
68         // We have cached space characters, return one
69
spaces--;
70         return ' ';
71     }
72     
73     int c = in.read();
74
75     if (c == ' ') {
76         // Got space, keep reading till we get a non-space char
77
while ((c = in.read()) == ' ')
78         spaces++;
79
80         if (c == '\r' || c == '\n' || c == -1)
81         // If the non-space char is CR/LF/EOF, the spaces we got
82
// so far is junk introduced during transport. Junk 'em.
83
spaces = 0;
84             else {
85         // The non-space char is NOT CR/LF, the spaces are valid.
86
((PushbackInputStream)in).unread(c);
87         c = ' ';
88         }
89         return c; // return either <SPACE> or <CR/LF>
90
}
91     else if (c == '=') {
92         // QP Encoded atom. Decode the next two bytes
93
int a = in.read();
94
95         if (a == '\n') {
96         /* Hmm ... not really confirming QP encoding, but lets
97          * allow this as a LF terminated encoded line .. and
98          * consider this a soft linebreak and recurse to fetch
99          * the next char.
100          */

101         return read();
102         } else if (a == '\r') {
103         // Expecting LF. This forms a soft linebreak to be ignored.
104
int b = in.read();
105         if (b != '\n')
106             /* Not really confirming QP encoding, but
107              * lets allow this as well.
108              */

109             ((PushbackInputStream)in).unread(b);
110         return read();
111         } else if (a == -1) {
112         // Not valid QP encoding, but we be nice and tolerant here !
113
return -1;
114         } else {
115         ba[0] = (byte)a;
116         ba[1] = (byte)in.read();
117         try {
118             return ASCIIUtility.parseInt(ba, 0, 2, 16);
119         } catch (NumberFormatException JavaDoc nex) {
120             /*
121             System.err.println(
122                 "Illegal characters in QP encoded stream: " +
123                 ASCIIUtility.toString(ba, 0, 2)
124             );
125             */

126
127             ((PushbackInputStream)in).unread(ba);
128             return c;
129         }
130         }
131     }
132     return c;
133     }
134
135     /**
136      * Reads up to <code>len</code> decoded bytes of data from this input stream
137      * into an array of bytes. This method blocks until some input is
138      * available.
139      * <p>
140      *
141      * @param buf the buffer into which the data is read.
142      * @param off the start offset of the data.
143      * @param len the maximum number of bytes read.
144      * @return the total number of bytes read into the buffer, or
145      * <code>-1</code> if there is no more data because the end of
146      * the stream has been reached.
147      * @exception IOException if an I/O error occurs.
148      */

149     public int read(byte[] buf, int off, int len) throws IOException {
150     int i, c;
151     for (i = 0; i < len; i++) {
152         if ((c = read()) == -1) {
153         if (i == 0) // At end of stream, so we should
154
i = -1; // return -1 , NOT 0.
155
break;
156         }
157         buf[off+i] = (byte)c;
158     }
159         return i;
160     }
161
162     /**
163      * Tests if this input stream supports marks. Currently this class
164      * does not support marks
165      */

166     public boolean markSupported() {
167     return false;
168     }
169
170     /**
171      * Returns the number of bytes that can be read from this input
172      * stream without blocking. The QP algorithm does not permit
173      * a priori knowledge of the number of bytes after decoding, so
174      * this method just invokes the <code>available</code> method
175      * of the original input stream.
176      */

177     public int available() throws IOException {
178     // This is bogus ! We don't really know how much
179
// bytes are available *after* decoding
180
return in.available();
181     }
182
183     /**** begin TEST program
184     public static void main(String argv[]) throws Exception {
185         FileInputStream infile = new FileInputStream(argv[0]);
186         QPDecoderStream decoder = new QPDecoderStream(infile);
187         int c;
188  
189         while ((c = decoder.read()) != -1)
190             System.out.print((char)c);
191         System.out.println();
192     }
193     *** end TEST program ****/

194 }
195
Popular Tags