PageRenderTime 116ms CodeModel.GetById 86ms app.highlight 25ms RepoModel.GetById 1ms app.codeStats 0ms

/jEdit/tags/jedit-4-2-pre14/installer/TarBuffer.java

#
Java | 484 lines | 302 code | 74 blank | 108 comment | 54 complexity | 4d099361fda19cf16c8735fa8e918382 MD5 | raw file
  1/*
  2** Authored by Timothy Gerard Endres
  3** <mailto:time@gjt.org>  <http://www.trustice.com>
  4** 
  5** This work has been placed into the public domain.
  6** You may use this work in any way and for any purpose you wish.
  7**
  8** THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND,
  9** NOT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR
 10** OF THIS SOFTWARE, ASSUMES _NO_ RESPONSIBILITY FOR ANY
 11** CONSEQUENCE RESULTING FROM THE USE, MODIFICATION, OR
 12** REDISTRIBUTION OF THIS SOFTWARE. 
 13** 
 14*/
 15
 16package installer;
 17
 18import java.io.*;
 19
 20
 21/**
 22 * The TarBuffer class implements the tar archive concept
 23 * of a buffered input stream. This concept goes back to the
 24 * days of blocked tape drives and special io devices. In the
 25 * Java universe, the only real function that this class
 26 * performs is to ensure that files have the correct "block"
 27 * size, or other tars will complain.
 28 * <p>
 29 * You should never have a need to access this class directly.
 30 * TarBuffers are created by Tar IO Streams.
 31 *
 32 * @version $Revision: 4480 $
 33 * @author Timothy Gerard Endres,
 34 *  <a href="mailto:time@gjt.org">time@trustice.com</a>.
 35 * @see TarArchive
 36 */
 37
 38public class
 39TarBuffer extends Object
 40	{
 41	public static final int		DEFAULT_RCDSIZE = ( 512 );
 42	public static final int		DEFAULT_BLKSIZE = ( DEFAULT_RCDSIZE * 20 );
 43
 44	private InputStream		inStream;
 45	private OutputStream	outStream;
 46
 47	private byte[]	blockBuffer;
 48	private int		currBlkIdx;
 49	private int		currRecIdx;
 50	private int		blockSize;
 51	private int		recordSize;
 52	private int		recsPerBlock;
 53
 54	private boolean	debug;
 55
 56
 57	public
 58	TarBuffer( InputStream inStream )
 59		{
 60		this( inStream, TarBuffer.DEFAULT_BLKSIZE );
 61		}
 62
 63	public
 64	TarBuffer( InputStream inStream, int blockSize )
 65		{
 66		this( inStream, blockSize, TarBuffer.DEFAULT_RCDSIZE );
 67		}
 68
 69	public
 70	TarBuffer( InputStream inStream, int blockSize, int recordSize )
 71		{
 72		this.inStream = inStream;
 73		this.outStream = null;
 74		this.initialize( blockSize, recordSize );
 75		}
 76
 77	public
 78	TarBuffer( OutputStream outStream )
 79		{
 80		this( outStream, TarBuffer.DEFAULT_BLKSIZE );
 81		}
 82
 83	public
 84	TarBuffer( OutputStream outStream, int blockSize )
 85		{
 86		this( outStream, blockSize, TarBuffer.DEFAULT_RCDSIZE );
 87		}
 88
 89	public
 90	TarBuffer( OutputStream outStream, int blockSize, int recordSize )
 91		{
 92		this.inStream = null;
 93		this.outStream = outStream;
 94		this.initialize( blockSize, recordSize );
 95		}
 96
 97	/**
 98	 * Initialization common to all constructors.
 99	 */
100	private void
101	initialize( int blockSize, int recordSize )
102		{
103		this.debug = false;
104		this.blockSize = blockSize;
105		this.recordSize = recordSize;
106		this.recsPerBlock = ( this.blockSize / this.recordSize );
107		this.blockBuffer = new byte[ this.blockSize ];
108
109		if ( this.inStream != null )
110			{
111			this.currBlkIdx = -1;
112			this.currRecIdx = this.recsPerBlock;
113			}
114		else
115			{
116			this.currBlkIdx = 0;
117			this.currRecIdx = 0;
118			}
119		}
120
121	/**
122	 * Get the TAR Buffer's block size. Blocks consist of multiple records.
123	 */
124	public int
125	getBlockSize()
126		{
127		return this.blockSize;
128		}
129
130	/**
131	 * Get the TAR Buffer's record size.
132	 */
133	public int
134	getRecordSize()
135		{
136		return this.recordSize;
137		}
138
139	/**
140	 * Set the debugging flag for the buffer.
141	 *
142	 * @param debug If true, print debugging output.
143	 */
144	public void
145	setDebug( boolean debug )
146		{
147		this.debug = debug;
148		}
149
150	/**
151	 * Determine if an archive record indicate End of Archive. End of
152	 * archive is indicated by a record that consists entirely of null bytes.
153	 *
154	 * @param record The record data to check.
155	 */
156	public boolean
157	isEOFRecord( byte[] record )
158		{
159		for ( int i = 0, sz = this.getRecordSize() ; i < sz ; ++i )
160			if ( record[i] != 0 )
161				return false;
162
163		return true;
164		}
165
166	/**
167	 * Skip over a record on the input stream.
168	 */
169
170	public void
171	skipRecord()
172		throws IOException
173		{
174		if ( this.debug )
175			{
176			System.err.println
177				( "SkipRecord: recIdx = " + this.currRecIdx
178					+ " blkIdx = " + this.currBlkIdx );
179			}
180
181		if ( this.inStream == null )
182			throw new IOException
183				( "reading (via skip) from an output buffer" );
184
185		if ( this.currRecIdx >= this.recsPerBlock )
186			{
187			if ( ! this.readBlock() )
188				return; // UNDONE
189			}
190
191		this.currRecIdx++;
192		}
193
194	/**
195	 * Read a record from the input stream and return the data.
196	 *
197	 * @return The record data.
198	 */
199
200	public byte[]
201	readRecord()
202		throws IOException
203		{
204		if ( this.debug )
205			{
206			System.err.println
207				( "ReadRecord: recIdx = " + this.currRecIdx
208					+ " blkIdx = " + this.currBlkIdx );
209			}
210
211		if ( this.inStream == null )
212			throw new IOException
213				( "reading from an output buffer" );
214
215		if ( this.currRecIdx >= this.recsPerBlock )
216			{
217			if ( ! this.readBlock() )
218				return null;
219			}
220
221		byte[] result = new byte[ this.recordSize ];
222
223		System.arraycopy(
224			this.blockBuffer, (this.currRecIdx * this.recordSize),
225			result, 0, this.recordSize );
226
227		this.currRecIdx++;
228
229		return result;
230		}
231
232	/**
233	 * @return false if End-Of-File, else true
234	 */
235
236	private boolean
237	readBlock()
238		throws IOException
239		{
240		if ( this.debug )
241			{
242			System.err.println
243				( "ReadBlock: blkIdx = " + this.currBlkIdx );
244			}
245
246		if ( this.inStream == null )
247			throw new IOException
248				( "reading from an output buffer" );
249
250		this.currRecIdx = 0;
251
252		int offset = 0;
253		int bytesNeeded = this.blockSize;
254		for ( ; bytesNeeded > 0 ; )
255			{
256			long numBytes =
257				this.inStream.read
258					( this.blockBuffer, offset, bytesNeeded );
259
260			//
261			// NOTE
262			// We have fit EOF, and the block is not full!
263			//
264			// This is a broken archive. It does not follow the standard
265			// blocking algorithm. However, because we are generous, and
266			// it requires little effort, we will simply ignore the error
267			// and continue as if the entire block were read. This does
268			// not appear to break anything upstream. We used to return
269			// false in this case.
270			//
271			// Thanks to 'Yohann.Roussel@alcatel.fr' for this fix.
272			//
273
274			if ( numBytes == -1 )
275				break;
276
277			offset += numBytes;
278			bytesNeeded -= numBytes;
279			if ( numBytes != this.blockSize )
280				{
281				if ( this.debug )
282					{
283					System.err.println
284						( "ReadBlock: INCOMPLETE READ " + numBytes
285							+ " of " + this.blockSize + " bytes read." );
286					}
287				}
288			}
289
290		this.currBlkIdx++;
291
292		return true;
293		}
294
295	/**
296	 * Get the current block number, zero based.
297	 *
298	 * @return The current zero based block number.
299	 */
300	public int
301	getCurrentBlockNum()
302		{
303		return this.currBlkIdx;
304		}
305
306	/**
307	 * Get the current record number, within the current block, zero based.
308	 * Thus, current offset = (currentBlockNum * recsPerBlk) + currentRecNum.
309	 *
310	 * @return The current zero based record number.
311	 */
312	public int
313	getCurrentRecordNum()
314		{
315		return this.currRecIdx - 1;
316		}
317
318	/**
319	 * Write an archive record to the archive.
320	 *
321	 * @param record The record data to write to the archive.
322	 */
323
324	public void
325	writeRecord( byte[] record )
326		throws IOException
327		{
328		if ( this.debug )
329			{
330			System.err.println
331				( "WriteRecord: recIdx = " + this.currRecIdx
332					+ " blkIdx = " + this.currBlkIdx );
333			}
334
335		if ( this.outStream == null )
336			throw new IOException
337				( "writing to an input buffer" );
338
339		if ( record.length != this.recordSize )
340			throw new IOException
341				( "record to write has length '" + record.length
342					+ "' which is not the record size of '"
343					+ this.recordSize + "'" );
344
345		if ( this.currRecIdx >= this.recsPerBlock )
346			{
347			this.writeBlock();
348			}
349
350		System.arraycopy(
351			record, 0,
352			this.blockBuffer, (this.currRecIdx * this.recordSize),
353			this.recordSize );
354
355		this.currRecIdx++;
356		}
357
358	/**
359	 * Write an archive record to the archive, where the record may be
360	 * inside of a larger array buffer. The buffer must be "offset plus
361	 * record size" long.
362	 *
363	 * @param buf The buffer containing the record data to write.
364	 * @param offset The offset of the record data within buf.
365	 */
366
367	public void
368	writeRecord( byte[] buf, int offset )
369		throws IOException
370		{
371		if ( this.debug )
372			{
373			System.err.println
374				( "WriteRecord: recIdx = " + this.currRecIdx
375					+ " blkIdx = " + this.currBlkIdx );
376			}
377
378		if ( this.outStream == null )
379			throw new IOException
380				( "writing to an input buffer" );
381
382		if ( (offset + this.recordSize) > buf.length )
383			throw new IOException
384				( "record has length '" + buf.length
385					+ "' with offset '" + offset
386					+ "' which is less than the record size of '"
387					+ this.recordSize + "'" );
388
389		if ( this.currRecIdx >= this.recsPerBlock )
390			{
391			this.writeBlock();
392			}
393
394		System.arraycopy(
395			buf, offset,
396			this.blockBuffer, (this.currRecIdx * this.recordSize),
397			this.recordSize );
398
399		this.currRecIdx++;
400		}
401
402	/**
403	 * Write a TarBuffer block to the archive.
404	 */
405	private void
406	writeBlock()
407		throws IOException
408		{
409		if ( this.debug )
410			{
411			System.err.println
412				( "WriteBlock: blkIdx = " + this.currBlkIdx );
413			}
414
415		if ( this.outStream == null )
416			throw new IOException
417				( "writing to an input buffer" );
418
419		this.outStream.write( this.blockBuffer, 0, this.blockSize );
420		this.outStream.flush();
421
422		this.currRecIdx = 0;
423		this.currBlkIdx++;
424		}
425
426	/**
427	 * Flush the current data block if it has any data in it.
428	 */
429
430	private void
431	flushBlock()
432		throws IOException
433		{
434		if ( this.debug )
435			{
436			System.err.println( "TarBuffer.flushBlock() called." );
437			}
438
439		if ( this.outStream == null )
440			throw new IOException
441				( "writing to an input buffer" );
442
443		if ( this.currRecIdx > 0 )
444			{
445			this.writeBlock();
446			}
447		}
448
449	/**
450	 * Close the TarBuffer. If this is an output buffer, also flush the
451	 * current block before closing.
452	 */
453	public void
454	close()
455		throws IOException
456		{
457		if ( this.debug )
458			{
459			System.err.println( "TarBuffer.closeBuffer()." );
460			}
461
462		if ( this.outStream != null )
463			{
464			this.flushBlock();
465
466			if ( this.outStream != System.out
467					&& this.outStream != System.err )
468				{
469				this.outStream.close();
470				this.outStream = null;
471				}
472			}
473		else if ( this.inStream != null )
474			{
475			if ( this.inStream != System.in )
476				{
477				this.inStream.close();
478				this.inStream = null;
479				}
480			}
481		}
482
483	}
484