Package org.bzdev.io

Class ZipDocWriter

java.lang.Object
org.bzdev.io.ZipDocWriter
All Implemented Interfaces:
AutoCloseable
Direct Known Subclasses:
ImageSequenceWriter

public class ZipDocWriter extends Object implements AutoCloseable
Writer for documents in ZipDoc format. A number of file formats are based on the Zip archive format including Java JAR files and Open Document files. The file format supported by this class is one in which documents based on Zip archives are self-labeled with their media types.

The Zip Document format is a Zip archive whose initial entry is a directory name, "META-INF/", with a size of 0, and with an extra field for that initial entry denoting a media type. By convention, the first element in the extra field for the first entry should be the one providing the media type, with a two-byte header ID of 0xFACE (stored in little-endian order). The media type uses UTF-8 encoding but legal characters in a media type make this compatible with U.S. ASCII. The media type is not null-terminated. This results in the following byte sequences for the first portion of a file:

 Bytes 0 to 3: 50 4B 03 04
 Bytes 8 to 9:  00 00
 Bytes 14 to 25: 00 00 00 00 00 00 00 00 00 00 00 00
 Bytes 26 to 27: 09 00
 Bytes 28 to 29: (4 + the length of the media type)
                 in little-endian byte order; a larger value if
                 other information is included
 Bytes 30 to 38: the characters "META-INF/"  (in UTF-8 encoding)
 Bytes 39 to 40: CE FA  (0xFACE in little-endian byte order)
 Bytes 41 to 42: the length of the media type in little-endian order
 Bytes 43 to (42 + mtlen):  the characters making up the media type
                 encoded using UTF-8, where mtlen is the number of
                 characters in the media type (only single-byte UTF-8
                 characters can occur in a media type.
 

The file can be read by any software that can process ZIP files. Applications using this file format can store data in the META-INF directory, typically meta data. The rationale for this format is to make it easy for classing engines or similar software to determine the a document type.

In a few cases (e.g., the files generated by ImageSequenceWriter), the zip file represent a sequence of objects, and some subsequences may consist of the same object repeated multiple times. To store these efficiently, the ZIP entry for the first can be tagged with a repetition count, provided in the method nextOutputStream(String,boolean,int,int). The names chosen for the entry should normally be such that the missing items can be filled in without risk of a name conflict. The tag is a ZIP-file extra header whose ID is 0xFCDA, whose length is 4, and whose value is a 32-bit positive integer, with all three fields stored in little-endian byte order, the normal convention for ZIP files.

Several entry names are reserved. These are "META-INF/" and "META-INF/repetitionMap" The reserved entry "META-INF/repetitionMap" is a US-ASCII file using CRLF as a newline separator. The line contains two values: an entry name and the actual entry name, separated by a space. Each of these names is URL encoded with the unencoded names using a UTF-8 character set. The repetitionMap entry might not be present if the repetition count is 1 for all entries. A repetition count of 1 is the default value - the count includes the original entry. Entries for which the repetition count is 1 are not present in a repetitionMap entry.

Subclasses and other users of this class may add entries whose names start with "META-INF" but must not add an entry whose name matches a reserved name.

  • Constructor Details

    • ZipDocWriter

      public ZipDocWriter(OutputStream os, String mimeType) throws IOException
      Constructor.
      Parameters:
      os - the output stream on which a zip-document file will be written
      mimeType - the file's media type
      Throws:
      IOException - an error occurred during processing
    • ZipDocWriter

      public ZipDocWriter(OutputStream os, String mimeType, byte[] extras) throws IOException
      Constructor with extra field. The extra header field should not include the media-type data that this class (and its superclass) will provide.
      Parameters:
      os - the output stream on which a zip-document file will be written
      mimeType - the file's media type
      extras - the extra field, excluding the header and value describing the file's media type
      Throws:
      IOException - an error occurred during processing
  • Method Details

    • repeatFile

      public void repeatFile(String nextName) throws IllegalArgumentException, IllegalStateException
      Provide the name for a repeated entry and create that entry. When an entry is created with a repetition count whose value is count in a call to nextOutputStream(String,boolean,int,int) or nextOutputStream(String,boolean,int,int,byte[]), then repeatFiles method must be called count-1 times before a another call to nextOutputStream or a call to close().
      Parameters:
      nextName - the name of a repeated entry
      Throws:
      IllegalStateException - if repeatFile cannot be called at this point
      IllegalArgumentException - if the name of the stream is a reserved name or null, or if a subclass restricts the name in some way
    • nextOutputStream

      public OutputStream nextOutputStream(String name, boolean compressed, int level) throws IOException, IllegalArgumentException, IllegalStateException
      Get an output stream for the next entry.

      The caller must finish writing all the data for an entry and must close this output stream before calling nextOutputStream to get a new stream.

      This method is provided for convenience: it merely calls nextOutputStream(String,boolean,int,byte[]) with its last argument set to null. Subclasses that need to override the the nextOutputStream methods will typically override nextOutputStream(String,boolean,int,byte[]) and nextOutputStream(String,boolean,int,int,byte[]).

      Parameters:
      name - the name of the entry
      compressed - true if the entry is compressed; false otherwise
      level - the compression level when the entry is compressed (0 to 9 or the constants Deflator.BEST_COMPRESSION, Deflator.BEST_SPEED, or Deflator.DEFAULT_COMPRESSION, where Deflator is a class in the package java.util.zip)
      Returns:
      the output stream to use for creating the next entry
      Throws:
      IOException - if an error occurred while writing the file
      IllegalArgumentException - if the name of the stream is a reserved name or null, or if a subclass restricts the name in some way
      IllegalStateException - if a repetition count has been previously specified and repeatFile(String) has not been called the required number of times
    • nextOutputStream

      public OutputStream nextOutputStream(String name, boolean compressed, int level, byte[] extra) throws IOException, IllegalArgumentException, IllegalStateException
      Get an output stream for the next entry and set an extra field.

      The caller must finish writing all the data for an entry and must close this output stream before calling nextOutputStream to get a new stream. The extra header field should not include the repetition-count data that this class (and its superclass) will provide.

      Subclasses that need to override the the nextOutputStream methods will typically override this method and nextOutputStream(String,boolean,int,int,byte[]).

      Parameters:
      name - the name of the entry
      compressed - true if the entry is compressed; false otherwise
      level - the compression level when the entry is compressed (0 to 9 or the constants Deflator.BEST_COMPRESSION, Deflator.BEST_SPEED, or Deflator.DEFAULT_COMPRESSION, where Deflator is a class in the package java.util.zip)
      extra - the extra field for a ZIP-file entry
      Returns:
      the output stream to use for creating the next entry
      Throws:
      IOException - if an error occurred while writing the file
      IllegalArgumentException - if the name of the stream is a reserved name or null, or if a subclass restricts the name in some way
      IllegalStateException - if a repetition count has been previously specified and repeatFile(String) has not been called the required number of times
    • nextOutputStream

      public OutputStream nextOutputStream(String name, boolean compressed, int level, int count) throws IOException, IllegalArgumentException, IllegalStateException
      Get an output stream with a repetition count for the next entry. A repetition count indicates that the entry represents a sequence of identical entries. It is indicated in the ZIP file being created by the presence of an extra header whose 16-bit length-field contains the value 4 and whose 16-bit ID is 0xFCDA, and whose value field is 32 bits long and contains the repetition count. Each field in this header is in little-endian order in order to match standard ZIP-file conventions.

      The caller must finish writing all the data for an entry and must close this output stream before calling nextOutputStream to get a new stream.

      This method is provided for convenience: it merely calls nextOutputStream(String,boolean,int,int,byte[]) with its last argument set to null. Subclasses that need to override the the nextOutputStream methods will typically override nextOutputStream(String,boolean,int,byte[]) and nextOutputStream(String,boolean,int,int,byte[]).

      Parameters:
      name - the name of the entry
      compressed - true if the entry is compressed; false otherwise
      level - the compression level when the entry is compressed (0 to 9 or the constants Deflator.BEST_COMPRESSION, Deflator.BEST_SPEED, or Deflator.DEFAULT_COMPRESSION, where Deflator is a class in the package java.util.zip)
      count - the repetition count for this entry; ignored if the value is less than 1
      Returns:
      the output stream to use for creating the next entry
      Throws:
      IOException - if an error occurred while writing the file
      IllegalArgumentException - if the name of the stream is a reserved name or null, or if a subclass restricts the name in some way
      IllegalStateException - if a repetition count has been previously specified and repeatFile(String) has not been called the required number of times
    • nextOutputStream

      public OutputStream nextOutputStream(String name, boolean compressed, int level, int count, byte[] extra) throws IOException, IllegalArgumentException, IllegalStateException
      Get an output stream with a repetition count for the next entry, specifying an extra field. A repetition count indicates that the entry represents a sequence of identical entries. It is indicated in the ZIP file being created by the presence of an extra header whose 16-bit length-field contains the value 4 and whose 16-bit ID is 0xFCDA, and whose value field is 32 bits long and contains the repetition count. Each field in this header is in little-endian order in order to match standard ZIP-file conventions.

      The caller must finish writing all the data for an entry and must close this output stream before calling nextOutputStream to get a new stream. The extra header field should not include the repetition-count data that this class (and its superclass) will provide.

      Subclasses that need to override the the nextOutputStream methods will typically override this method and nextOutputStream(String,boolean,int,byte[]).

      Parameters:
      name - the name of the entry
      compressed - true if the entry is compressed; false otherwise
      level - the compression level when the entry is compressed (0 to 9 or the constants Deflator.BEST_COMPRESSION, Deflator.BEST_SPEED, or Deflator.DEFAULT_COMPRESSION, where Deflator is a class in the package java.util.zip)
      count - the repetition count for this entry; ignored if the value is less than 1
      extra - the extra field for the next entry
      Returns:
      the output stream to use for creating the next entry
      Throws:
      IOException - if an error occurred while writing the file
      IllegalArgumentException - if the name of the stream is a reserved name or null, or if a subclass restricts the name in some way
      IllegalStateException - if a repetition count has been previously specified and repeatFile(String) has not been called the required number of times
    • close

      public void close() throws IllegalStateException, IOException
      Close the document. This method adds one additional entry with the name META-INF/repetitionMap, whose syntax is described above.
      Specified by:
      close in interface AutoCloseable
      Throws:
      IllegalStateException - if repeatFile(String) was not called the required number of times
      IOException - if an error occurred while writing the ZIP file.