Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: cryptix/openpgp/io/PGPLengthDataOutputStream.java


1   /* $Id: PGPLengthDataOutputStream.java,v 1.2 2005/03/13 17:12:58 woudt Exp $
2    *
3    * Copyright (C) 1999-2005 The Cryptix Foundation Limited.
4    * All rights reserved.
5    *
6    * Use, modification, copying and distribution of this software is subject
7    * the terms and conditions of the Cryptix General Licence. You should have
8    * received a copy of the Cryptix General License along with this library;
9    * if not, you can download a copy from http://www.cryptix.org/ .
10   */
11  
12  package cryptix.openpgp.io;
13  
14  
15  import java.io.ByteArrayOutputStream;
16  import java.io.DataOutputStream;
17  import java.io.OutputStream;
18  import java.io.IOException;
19  import java.io.UnsupportedEncodingException;
20  
21  import java.math.BigInteger;
22  
23  
24  
25  /**
26   * Generic outputstream to write PGP formatted packet data that contains
27   * length information
28   *
29   * <p>This class basically has two modes, one where the length of the written
30   * data is (partly) known in advance and one when the length is not known.
31   * In the first mode all data will be written to the underlying outputstream
32   * immediately, which allows for full streaming. In the second mode all output
33   * will be buffered until the close method is called, where all data is then
34   * written in one big chunk to the outputstream.</p>
35   *
36   * <p>If the subclass wants, it can support writing data in chunks. This is 
37   * particularly useful when in streaming a lot of data, when you do not know
38   * in advance what the total length will be. In that case you just buffer the
39   * data and write a chunk every time your buffer is full.</p>
40   *
41   * <p>A subclass may decide not to support partial chunks.</p>
42   *
43   * @author Edwin Woudt (edwin@cryptix.org)
44   * @version $Revision: 1.2 $
45   */
46  public abstract class PGPLengthDataOutputStream extends PGPDataOutputStream {
47  
48  
49  // Instance variables
50  //............................................................................
51  
52      /** The underlying outputstream */
53      private OutputStream out;
54      /** Buffer in case we don't know the length */
55      private ByteArrayOutputStream buffer = null;
56      /** If true then length info has been written and we can write directly
57        * to the outputstream, if false then we have to buffer the info */
58      private boolean lengthHasBeenWritten = false;
59      /** If true then we are currently writing a partial length packet */
60      private boolean partial = false;
61      /** The number of bytes written in the current chunk */
62      private long chunkBytesWritten = 0;
63      /** The length of the current chunk if SetLength or setPartialLength has 
64        * been used. */ 
65      private long chunkLength = 0;
66      
67      
68  
69  // Constructor
70  //............................................................................
71  
72      /**
73       * Constructor that takes an outputstream
74       *
75       * <p>Subclasses should call this constructor from their own, after doing
76       * their own initialization.</p>
77       */
78      public PGPLengthDataOutputStream(OutputStream out) {
79          this.out = out;
80      }
81  
82  
83      /**
84       * Emtpy constructor for subclasses that handle their own writing
85       *
86       * <p>If a subclass decides to use this constructor, then the writeDirect
87       * method must be overridden.</p>
88       */
89      public PGPLengthDataOutputStream() {
90          this.out = null;
91      }
92  
93  
94  
95  // Implemented abstract methods
96  //............................................................................
97  
98  
99      /**
100      * Write one byte directly to the underlying outputstream.
101      */
102     protected void writeDirect(int b) throws IOException {
103     
104         out.write(b);
105         
106     }
107     
108 
109     /**
110      * Internal method used by all other methods to write one byte.
111      */
112     protected void writeInternal(int b) throws IOException {
113     
114         if (! lengthHasBeenWritten) {
115         
116             if (chunkBytesWritten == 0) {
117                 buffer = new ByteArrayOutputStream();
118             }
119 
120             buffer.write(b);
121             chunkBytesWritten++;
122             
123         } else {
124         
125             chunkBytesWritten++;
126             if (chunkBytesWritten > chunkLength) {
127                 throw new RuntimeException("Tried to write more bytes than "+
128                                            "set.");
129             }
130             
131             writeDirect(b);
132             
133         }
134     
135     }
136 
137 
138 
139 // Abstract methods
140 //............................................................................
141 
142 
143     /**
144      * Write a partial length to the outputstream.
145      *
146      * <p>If an implementation does not support partial lengths it should throw
147      * a (subclass of) RuntimeException</p>
148      */
149     protected abstract void writePartialLength(OutputStream out, long len)
150         throws IOException;
151     
152 
153     /**
154      * Write a length to the outputstream.
155      *
156      * <p>This method will only be called once for every packet. It may be
157      * preceded by several writePartialLength calls.</p>
158      */
159     protected abstract void writeLength(OutputStream out, long len)
160         throws IOException;
161 
162     
163 
164 // Public length control methods
165 //............................................................................
166 
167 
168     /**
169      * Set the length of the current packet
170      *
171      * <p>This method must only be called once for every packet. It may be 
172      * preceded by several setPartialLength calls. If this condition is not met
173      * an IllegalStateException will be thrown.</p>
174      * <p>If no calls have been made setPartialLength then no bytes must have 
175      * been written yet, otherwise an IllegalStateException will be thrown.</p>
176      *
177      * <p>This method calls writeLength.</p>
178      *
179      * @throws IOException only if writeLength returns an IOException
180      */
181     public void setLength(long len) throws IOException {
182 
183         if ((partial) && (chunkBytesWritten != chunkLength)) {
184 
185             throw new IllegalStateException("Partial length has not yet been " +
186                                             "completed");
187 
188         } else if (partial) {
189 
190             partial = false;
191 
192         } else if (lengthHasBeenWritten) {
193 
194             throw new IllegalStateException("Cannot write two non-partial " +
195                                             "lengths");
196 
197         } else if ((!lengthHasBeenWritten) && (chunkBytesWritten != 0)) {
198             
199             throw new IllegalStateException("Bytes have already been written");
200     
201         }
202 
203         chunkLength = len;
204         chunkBytesWritten = 0;
205         lengthHasBeenWritten = true;
206 
207         writeLength(out, len);
208         
209     }
210     
211     
212     /**
213      * Set the length of the current packet
214      *
215      * <p>If this is the first call to this method then no bytes must have 
216      * been written yet, otherwise an IllegalStateException will be thrown.</p>
217      * <p>After this method has been called one or more times a setLength must
218      * be called for the final chunk. This will be checked in the close method.
219      * </p>
220      *
221      * <p>This method calls writePartialLength.</p>
222      *
223      * @throws IOException only if writePartialLength returns an IOException
224      */
225     public void setPartialLength(long len) throws IOException {
226     
227         if ((partial) && (chunkBytesWritten != chunkLength)) {
228 
229             throw new IllegalStateException("Partial length has not yet been " +
230                                            "completed");
231         
232         } else if (lengthHasBeenWritten) {
233         
234             throw new IllegalStateException("Cannot set a partial length " +
235                                             "after a normal length");
236         
237         } else if ((!lengthHasBeenWritten) && (chunkBytesWritten != 0)) {
238             
239             throw new IllegalStateException("Bytes have already been written");
240 
241         }    
242         
243         partial = true;
244         chunkLength = len;
245         chunkBytesWritten = 0;
246         lengthHasBeenWritten = true;
247 
248         writePartialLength(out, len);
249 
250     }
251     
252 
253 
254 // Close method
255 //............................................................................
256 
257 
258     /**
259      * Close this inputstream
260      *
261      * <p>If setLength and setPartialLength have been used this method checks
262      * to see if all bytes of the chunk have been written, otherwise an
263      * IllegalStateException will be thrown.</p>
264      * <p>If setLength and setPartialLength have not been used and thus all 
265      * data has been buffered, the buffered data will be written after a
266      * call to writeLength.</p>
267      *
268      * <p>This method does not close the underlying outputstream.</p>
269      * <p>This method must always be called.</p>
270      *
271      * @throws IOException if either writeLength throws it or something goes
272      *                     wrong while writing the buffer.
273      */
274     public void close() throws IOException {
275     
276         if (partial) {
277         
278             throw new IllegalStateException("Packet cannot end with a partial "+
279                                             "length");
280         
281         } else if ((lengthHasBeenWritten)&&(chunkBytesWritten != chunkLength)) {
282         
283             throw new IllegalStateException("Packet has not been completely "+
284                                             "written");
285         
286         } else if (!lengthHasBeenWritten) {
287         
288             byte[] b;
289             if (buffer != null) {
290                 b = buffer.toByteArray();
291             } else {
292                 b = new byte[0];
293             }
294             
295             chunkLength = (long)b.length;
296             chunkBytesWritten = 0;
297             lengthHasBeenWritten = true;
298             
299             writeLength(out, (long)b.length);
300             writeFully(b);
301 
302         }        
303     
304     }
305 
306 
307 }