View Javadoc

1   
2   /** 
3    * Clase que transforma un InputStream cualquiera en un InputStream en formato MIME, estableciendo
4    * en la cabecera el content-type que se le pasa como argumento al constructor y codificando los 
5    * bytes de entrada en base64. Se utiliza la codificación de caracteres UTF-8.
6    * @author  Jesús Reyes (3dígits)
7    * @version 0.98
8    */
9   package es.caib.signatura.impl;
10  import java.io.FilterInputStream;
11  import java.io.IOException;
12  import java.io.InputStream;
13  import java.io.UnsupportedEncodingException;
14  
15  
16  public class MIMEInputStream extends FilterInputStream {
17  
18      /** Flag que indica si se han leido todos los bytes de la entrada */
19      private boolean finished;
20  
21      private byte[] headData = null; //buffer con los bytes de la cabecera
22      private int headDataOff; // Posición dentro del buffer de la cabecera
23      private int headDataSize; // Tamaño del buffer de la cabecera
24      private String contentType = null; 
25      private boolean inHead; //flag que indica si estamos procesando la cabecera
26      private byte[] bodyData = new byte[66];//buffer de los bytes procesados
27      private int bodyDataOff;//Posición dentro del buffer de los bytes procesados
28      private int bodyDataSize;//Tamaño del buffer de los bytes procesados
29      
30  // Constructor de la clase
31  
32      /** Crea un nuevo MIMEInputStream 
33       * @throws UnsupportedEncodingException
34       * 
35       * 
36       * */
37      public MIMEInputStream(InputStream is, String contentType)  throws UnsupportedEncodingException{
38        super(is);
39        this.contentType = contentType;
40        
41        
42        //String mime = new String("MIME-Version: 1.0\r\n");
43        
44        //Inicialización de la cabecera
45        String mime = new String("");
46        mime = mime + "Content-Type: " + contentType + "\r\n";
47        mime = mime + "Content-Transfer-Encoding: base64\r\n\r\n";
48  
49        this.headData = mime.getBytes("UTF-8");
50        this.headDataOff = 0;
51        this.headDataSize = this.headData.length;
52        this.inHead = true;
53        
54        
55        
56      }
57       
58  
59      public synchronized void close ()
60      throws IOException {
61          finished = true;
62          super.close();
63      }
64  
65      /**
66       * Lee el siguiente byte procesado
67       * @return el siguiente byte de datos o bien -1 si se ha llegado al final del stream
68       * @throws IOException
69       */
70      public synchronized int read ()
71      throws IOException {
72          int b;
73          
74          //Si estamos en la cabecera leemos los bytes del buffer de cabecera
75          //Cuando se termina la cabecera se inicializa el buffer de los bytes procesados
76          if (inHead)
77          {
78              b = headData[headDataOff];
79              headDataOff++;
80              if (headDataOff==headDataSize)
81              {
82                inHead = false;
83                bodyDataSize = this.getBodyData(bodyData);
84                if (bodyDataSize==-1)
85                  finished = true;
86                else
87                  bodyDataOff = 0;
88              }
89          }
90          
91          else
92          {
93            if (finished) return -1;
94            b = bodyData[bodyDataOff];
95            bodyDataOff++;
96            if (bodyDataOff == bodyDataSize)
97            {
98              bodyDataSize = this.getBodyData(bodyData);
99              if (bodyDataSize==-1)
100               finished = true;
101             else
102               bodyDataOff = 0;
103           }                   
104          }
105         return b & 0xFF;
106     }
107 
108 
109 
110 
111     public synchronized int read (byte[] buffer, int offset, int length)
112     throws IOException {
113         for (int i = 0; i < length; ++i) {
114             int c = read();
115             if (c < 0) return i == 0 ? -1 : i;
116             buffer[offset++] = (byte) c;
117         }
118         return length;
119     }
120 
121 
122   
123     /**
124      * Skips over and discards <i>n</i> bytes of data from the
125      * input stream. 
126      * @param  n    the number of bytes to be skipped.
127      * @return the actual number of bytes skipped.
128      * @exception IOException if an I/O error occurs.
129      */
130     public synchronized long skip(long n) throws IOException {
131         for (long i = 0; i < n; i++)
132             if (this.read() < 0) return i;
133         return n;
134     }
135 
136     /**
137      * Devuelve el número de bytes que se pueden leer sin que se bloquee el stream
138      * 
139      * @throws IOException i
140      */
141     public synchronized int available()
142     throws IOException {
143         if (inHead)
144           return (headDataSize - headDataOff);
145         else
146           return (bodyDataSize - bodyDataOff);
147           
148     }
149 
150     /**
151      * Does nothing, since this class does not support mark/reset.
152      */
153     public void mark(int readlimit) {}
154 
155     /**
156      * Always throws an IOException, since this class does not support mark/reset.
157      */
158     public void reset() throws IOException {
159         throw new IOException("Base64InputStream does not support mark/reset");
160     }
161 
162     /**
163      * Tests if this input stream supports the <code>mark</code> and
164      * <code>reset</code> methods of InputStream, which it does not.
165      *
166      * @return <code>false</code>, since this class does not support the
167      *         <code>mark</code> and <code>reset</code> methods.
168      */
169     public boolean markSupported() { return false; }
170 
171 
172 // Own methods
173     
174     
175   /**
176    * 
177    * @throws java.io.IOException
178    * @return número de bytes procesados o -1 si se ha llegado al final del stream
179    * @param bodyData buffer de bytes procesados
180    */
181   
182     private int getBodyData(byte[] bodyData) throws IOException
183     {
184       int size = 0;
185       // La codificación base64 codifica 3 bytes de entrada en 4 bytes de salida
186       // El buffer de entrada es de 48 bytes, por lo que la codificación en base64 nos
187       // producirá 64 bytes. 
188       byte[] inputBuffer = new byte[48];
189       size = 0;
190       do
191       {
192     	  int read = in.read(inputBuffer, size, inputBuffer.length - size);
193     	  if (read < 0 && size == 0)
194     		  return -1;
195     	  else if (read < 0 )
196     		  break;
197     	  size = size + read;
198       } while (size < inputBuffer.length);
199       
200       if (size >= 0)
201       {
202         byte result[] = Base64.toBase64(inputBuffer, 0, size);
203         
204         size = result.length;
205         System.arraycopy( result, 0, bodyData, 0, size);
206       }
207       return size;
208     }
209 }