//
// Copyright (c) 2006, Brian Frank and Andy Frank
// Licensed under the Academic Free License version 3.0
//
// History:
// 18 Aug 06 Brian Frank Creation
//
**
** Zip is used to read/write compressed zip files and streams. Zip may be
** used in three modes:
**
** 1. `Zip.open` is used to read a random access file and provides
** access to the entire contents with the ability to read select
** entries
** 2. `Zip.read` is used to read a zip file from an input stream.
** Each entry is pulled off the stream using `readNext`
** 3. `Zip.write` is used to write a zip file to an output stream.
** Each entry is written to the stream using `writeNext`
**
final class Zip
{
//////////////////////////////////////////////////////////////////////////
// Construction
//////////////////////////////////////////////////////////////////////////
**
** Open the specified file as a zip file for reading. If the specified
** file does not exist, is not a valid file, or does not support random
** access then throw IOErr.
**
** Example:
** zip := Zip.open(File(`test.zip`))
** txt := zip.contents[`/notice.txt`].readAllStr
** zip.close
**
static Zip open(File file)
**
** Create a Zip used to read a zip file from the specified input stream.
**
** Example:
** zip := Zip.read(File(`test.zip`).in)
** File? entry
** while ((entry = zip.readNext()) != null)
** {
** data := entry.readAllBuf
** echo("$entry size=$data.size")
** }
** zip.close
**
static Zip read(InStream in)
**
** Create a Zip used to write a zip file to the specified output stream.
**
** Example:
** zip := Zip.write(File(`test.zip`).out)
** out := zip.writeNext(`/path/hello.txt`)
** out.writeLine("hello zip")
** out.close
** zip.close
**
static Zip write(OutStream out)
**
** Private constructor
**
private new init(Uri uri)
//////////////////////////////////////////////////////////////////////////
// Methods
//////////////////////////////////////////////////////////////////////////
**
** Get the underlying file or null if using streams.
**
File? file()
**
** Return the contents of this zip as a map of Files. The Uri
** keys will start with a slash and be relative to this zip file.
** Return null if using streams.
**
[Uri:File]? contents()
**
** Read the next entry in the zip. Use the File's input stream to read the
** file contents. Some file meta-data such as size may not be available.
** Return null if at end of zip file. Throw UnsupportedErr if not reading
** from an input stream.
**
File? readNext()
**
** Call the specified function for every entry in the zip. Use the File's
** input stream to read the file contents. Some file meta-data such as size
** may not be available. Throw UnsupportedErr if not reading from an input
** stream.
**
Void readEach(|File| c)
**
** Append a new file to the end of this zip file and return an OutStream
** which may be used to write the file contents. The Uri must not contain
** a query or fragment; it may optionally start with a slash. Closing the
** OutStream will close only this file entry - use Zip.close() when finished
** writing the entire zip file. Throw UnsupportedErr if zip is not writing
** to an output stream.
**
** Next entry options:
** - comment: Str entry comment
** - crc: Int CRC-32 of the uncompressed data
** - extra: Buf for extra bytes data field
** - level: Int between 9 (best compression) to 0 (no compression)
** - compressedSize: Int for compressed size of data
** - uncompressedSize: Int for uncompressed size of data
**
** NOTE: setting level to 0 sets method to STORE, else to DEFLATED.
**
** Examples:
** out := zip.writeNext(`/docs/test.txt`)
** out.writeLine("test")
** out.close
**
OutStream writeNext(Uri path, DateTime modifyTime := DateTime.now, [Str:Obj?]? opts := null)
**
** Finish writing the contents of this zip file, but leave the underlying
** OutStream open. This method is guaranteed to never throw an IOErr.
** Return true if the stream was finished successfully or false if
** an error occurred. Throw UnsupportedErr if zip is not writing to
** an output stream.
**
Bool finish()
**
** Close this zip file for reading and writing. If this zip file is
** reading or writing a stream, then the underlying stream is also
** closed. This method is guaranteed to never throw an IOErr. Return
** true if the close was successful or false if an error occurred.
**
Bool close()
**
** If file is not null then return file.toStr, otherwise return
** a suitable string representation.
**
override Str toStr()
**
** Static utility to unzip a zip file to the given directory.
** Raise exception if there are any failures. Return number of
** files unzipped on success.
**
static Int unzipInto(File zip, File dir)
//////////////////////////////////////////////////////////////////////////
// GZIP
//////////////////////////////////////////////////////////////////////////
**
** Construct a new GZIP output stream which wraps the given output stream.
**
static OutStream gzipOutStream(OutStream out)
**
** Construct a new GZIP input stream which wraps the given input stream.
**
static InStream gzipInStream(InStream in)
//////////////////////////////////////////////////////////////////////////
// Deflate
//////////////////////////////////////////////////////////////////////////
**
** Construct a new deflate output stream which wraps the given output stream,
** and compresses data using the "deflate" compression format. Options:
** - level: Int between 9 (best compression) to 0 (no compression)
** - nowrap: Bool false to suppress defalate header and adler checksum
**
static OutStream deflateOutStream(OutStream out, [Str:Obj?]? opts := null)
**
** Construct a new deflate input stream which wraps the given input stream and
** inflates data written using the "deflate" compression format. Options:
** - nowrap: Bool false to suppress defalate header and adler checksum
**
static InStream deflateInStream(InStream in, [Str:Obj?]? opts := null)
}