KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sourceforge > groboutils > util > io > v1 > MimeInputStream


1 /*
2  * MimeInputStream.java - A Filter stream for MIME encoding
3  *
4  * Copyright (C) 2000,,2003 2002 Matt Albrecht
5  * groboclown@users.sourceforge.net
6  * http://groboutils.sourceforge.net
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21  */

22
23
24 package net.sourceforge.groboutils.util.io.v1;
25
26 import java.io.FilterInputStream JavaDoc;
27 import java.io.InputStream JavaDoc;
28 import java.io.IOException JavaDoc;
29
30 /**
31  * java.io.FilterInputStream implementation for Mime base 64. Not incredibly
32  * efficient, but it works and is small.
33  *
34  * All we need to implement are:
35  * read(int)
36  * read( byte b[], int off, int len )
37  * skip( long n ) - for translating the # of bytes to skip into mime bytes (4-to-3 ratio)
38  * available() - for the same reason as skip
39  *
40  * @author Matt Albrecht <a HREF="mailto:groboclown@users.sourceforge.net">groboclown@users.sourceforge.net</a>
41  * @since 0.9.0 Alpha (early 2000)
42  * @version $Date: 2003/02/10 22:52:45 $
43  */

44 public class MimeInputStream extends FilterInputStream JavaDoc
45 {
46     private int bits = 0, spare = 0;
47
48     /**
49      * Mime character set translation
50      */

51     private static final int[] charset = {
52        'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R',
53        'S','T','U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','i','j',
54        'k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1',
55        '2','3','4','5','6','7','8','9','+','/' };
56     private static final int UPPER_START = 0;
57     private static final int LOWER_START = 26;
58     private static final int NUM_START = LOWER_START+26;
59     private static final int PLUS = NUM_START+10;
60     private static final int SLASH = PLUS+1;
61     private static final int pad = '=';
62
63     /** Constructor! */
64     public MimeInputStream( InputStream JavaDoc i )
65     { super(i); }
66
67     /**
68      * Write the specified <code>byte</code>, performing mime encoding.
69      *
70      * <p>Override this method, since all other write methods call it.
71      *
72      * @exception IOException If an I/O error occurs
73      */

74     public int read() throws IOException JavaDoc
75     {
76        int s;
77        int c, val;
78
79        // find the mime value - fast way doesn't loop through the mime charset
80
for (;;)
81        {
82           c = super.read();
83           if (c < 0) return c; // EOF
84

85           if (c >= 'A' && c <= 'Z') { val = c - 'A' + UPPER_START; }
86           else if (c >= 'a' && c <= 'z') { val = c - 'a' + LOWER_START; }
87           else if (c >= '0' && c <= '9') { val = c - '0' + NUM_START; }
88           else if (c == '+') { val = PLUS; }
89           else if (c == '/') { val = SLASH; }
90           else if (c == pad)
91             { throw new IOException JavaDoc("end-of-mime character encountered"); }
92           else // ignore out-of-bounds characters, per specs
93
continue;
94           switch (bits)
95           {
96              case 0: bits++;
97                  spare = val << 2;
98                  // didn't get a full byte - continue the read
99
break;
100              case 1: bits++;
101                  s = spare | ((val >> 4) & 0x03);
102                  spare = (val << 4) & 0xF0;
103                  return s;
104              case 2: bits++;
105                  s = spare | ((val >> 2) & 0x0F);
106                  spare = (val << 6) & 0xC0;
107                  return s;
108              case 3: bits = 0;
109                  // val is already masked (&) with 0x3f
110
return spare | val;
111           }
112        }
113     }
114
115     /**
116      * Reads up to <code>len</code> bytes of data from this input stream
117      * into an array of bytes. This method blocks until some input is
118      * available.
119      * <p>
120      * This method performs <code>in.read(b, off, len)</code>, translates the
121      * mime characters, and returns the result.
122      *
123      * @param b the buffer into which the data is read.
124      * @param off the start offset of the data.
125      * @param len the maximum number of bytes read.
126      * @return the total number of bytes read into the buffer, or
127      * <code>-1</code> if there is no more data because the end of
128      * the stream has been reached.
129      * @exception IOException if an I/O error occurs.
130      * @see java.io.FilterInputStream#in
131     public int read(byte b[], int off, int len) throws IOException
132     {
133        // size checking
134        if (b == null || b.length <= off || b.length <= off+len)
135          throw new IllegalArgumentException();
136
137        int s, i = off, j, sofar = 0;
138        int c, val,
139            ourlen;
140        byte buffer[];
141
142        // out-of-bounds values may throw us off the correct count
143        while (sofar < len)
144        {
145           j = 0;
146           ourlen = (len - sofar) * 4;
147           if ((ourlen % 3) != 0) { ourlen /= 3; ourlen++; }
148           else ourlen /= 3;
149
150           buffer = new byte[ ourlen ];
151
152           // translate the length to mime size
153           in.read( buffer, 0, ourlen );
154
155           // find the mime value - fast way doesn't loop through the mime charset
156           for (; j < ourlen; j++)
157           {
158              c = buffer[j];
159              if (c >= 'A' && c <= 'Z') { val = c - 'A' + UPPER_START; }
160              else if (c >= 'a' && c <= 'z') { val = c - 'a' + LOWER_START; }
161              else if (c >= '0' && c <= '9') { val = c - '0' + NUM_START; }
162              else if (c == '+') { val = PLUS; }
163              else if (c == '/') { val = SLASH; }
164              else if (c == pad)
165              {
166                 // end of mime
167                 b[i] = (byte)spare;
168                 return sofar + j + 1;
169              }
170              else // ignore out-of-bounds characters, per specs
171                continue;
172
173              switch (bits)
174              {
175                 case 0: bits++;
176                     spare = val << 2;
177                     // didn't get a full byte - continue the read
178                   break;
179                 case 1: bits++;
180                     b[i++] = (byte)(spare | ((val >> 4) & 0x03));
181                     spare = (val << 4) & 0x03;
182                   break;
183                 case 2: bits = 0;
184                     // val is already masked (&) with 0x3f
185                     b[i++] = (byte)(spare | val);
186                   break;
187              }
188           } // end of for loop
189           sofar += j;
190        }
191        return sofar+1;
192     }
193      */

194
195
196     /**
197      * Skips over and discards <code>n</code> bytes of data from the
198      * input stream. The <code>skip</code> method may, for a variety of
199      * reasons, end up skipping over some smaller number of bytes,
200      * possibly <code>0</code>. The actual number of bytes skipped is
201      * returned.
202      * <p>
203      * This method performs <code>in.skip(n)</code>, followed by a quick
204      * translation to mime size.
205      *
206      * @param n the number of bytes to be skipped.
207      * @return the actual number of bytes skipped.
208      * @exception IOException if an I/O error occurs.
209      */

210     public long skip(long n) throws IOException JavaDoc
211     {
212        long p = n * 4;
213        if ((p % 3) != 0) { p /= 3; p++; }
214                     else { p /= 3; }
215      p = in.skip(p);
216        p *= 3;
217        if ((p & 0x03) != 0) { p >>= 2; p++; }
218                        else { p >>= 2; }
219        return p;
220     }
221
222
223     /**
224      * Returns the number of bytes that can be read from this input
225      * stream without blocking.
226      * <p>
227      * This method performs <code>in.available(n)</code>, does the mime-size
228      * conversion, and returns the result.
229      *
230      * @return the number of bytes that can be read from the input stream
231      * without blocking.
232      * @exception IOException if an I/O error occurs.
233      * @see java.io.FilterInputStream#in
234      */

235     public int available() throws IOException JavaDoc
236     {
237      int p = in.available() * 3;
238        if ((p & 0x03) != 0) { p >>= 2; p++; }
239                        else { p >>= 2; }
240        return p;
241     }
242 }
243
Popular Tags