class Zipper extends AnyRef
Zipper: Write zip and jar files more easily
The Zipper
class provides a convenient mechanism for writing zip and jar
files; it's a simplifying layer that sits on top of the existing Zip and
Jar classes provided by the JDK. A Zipper
object behaves somewhat like an
immutable Scala collection, into which you can drop File
objects,
InputStream
objects, Reader
objects, Source
objects, URLs and
pathnames. When you call writeZip
or writeJar
, the objects in Zipper
are written to the actual underlying zip or jar file.
A Zipper
can either preserve pathnames or flatten the paths down to single
components. When preserving pathnames, a Zipper
object converts absolute
paths to relative paths by stripping any leading "file system mount points."
On Unix-like systems, this means stripping the leading "/"; on Windows, it
means stripping any leading drive letter and the leading "\". (See
java.io.File.listRoots() for more information.) For instance, if you're not
flattening pathnames, and you add C:\Temp\hello.txt
to a Zipper
on
Windows, the Zipper
will strip the C:\
, adding Temp/hello.txt
. to the
zip or jar file. If you're on a Unix-like system, including Mac OS X, and
you add /tmp/foo/bar.txt
, the Zipper
will add tmp/foo/bar.txt
to the
file.
Directories
You can explicitly add directory entries to a Zipper
, using
addZipDirectory()
. When you're not flattening entries, a Zipper
object will
also ensure that any intermediate directories in a pathname are created in
the zip file. For instance, if you add file /tmp/foo/bar/baz.txt
to a
Zipper
, without flattening it, the Zipper
will create the following
entries in the underlying zip file:
tmp
(directory)tmp/foo
(directory)tmp/foo/bar
(directory)tmp/foo/bar/baz.txt
(the entry)
If you use the JDK's zip or jar classes directly, you have to create those
intermediate directory entries yourself. In addition, you have to be careful
not to create a directory more than once; doing so will cause an error.
Zipper
automatically creating unique intermediate directories for you.
Constructing a Zipper object
The class constructor is private; use the companion object's apply()
functions to instantiate Zipper
objects.
Using a Zipper object
The addFile()
methods all return Try
objects, and they do not modify
the original Zipper
object. On success, they return a Success
object
that contains a new Zipper
.
Because the addFile()
methods return Try
, they are unsuitable for use
in traditional "builder" patterns. For instance, the following will not
work:
// Will NOT work val zipper = Zipper() zipper.addFile("/tmp/foo/bar.txt").addFile("/tmp/baz.txt")
There are other patterns you can use, however. Since Try
is monadic, a
for
comprehension works nicely:
val zipper = Zipper() val newZipper = for { z1 <- zipper.addFile("/tmp/foo/bar.txt") z2 <- z1.addFile("/tmp/baz.txt") z3 <- z2.addFile("hello.txt") } yield z3 // newZipper is a Try[Zipper]
If you're trying to add a collection of objects, a for
comprehension
can be problematic. If you're not averse to using a local var
, you
can just use a traditional imperative loop:
val zipper = Zipper() var z = zipper val paths: List[String] = ... for (path <- paths) { val t = z.addFile(path) z = t.get // will throw an exception if the add failed }
You can also avoid a var
using foldLeft()
, though you still have to
contend with a thrown exception. (You can always wrap the code in a Try
.)
val zipper = Zipper() val paths: List[String] = ... paths.foldLeft(zipper) { case (z, path) => z.addFile(path).get // throws an exception if the add fails }
Finally, to avoid the exception and the var
, use tail-recursion:
import scala.annnotation.tailrec import scala.util.{Failure, Success, Try} @tailrec def addNext(paths: List[String], currentZipper: Zipper): Try[Zipper] = { paths match { case Nil => Success(currentZipper) case path :: rest => // Can't use currentZipper.addFile(path).map(), because the recursion // will then be invoked within the lambda, violating tail-recursion. currentZipper.addFile(path) match { case Failure(ex) => Failure(ex) case Success(z) => addNext(rest, z) } } } val paths: List[String] = ... val zipper = addNext(paths, Zipper())
Notes
A Zipper
is not a true Scala collection. It does not support extensively
querying its contents, looping over them, or transforming them. It is simply a
container to be filled and then written.
The Zipper
class currently provides no support for storing uncompressed
(i.e., fully inflated) entries. All data stored in the underlying zip is
compressed, even though the JDK-supplied zip classes support both compressed
and uncompressed entries. If necessary, the Zipper
class can be extended
to support storing uncompressed data.
- Alphabetic
- By Inheritance
- Zipper
- AnyRef
- Any
- Hide All
- Show All
- Public
- All
Value Members
-
final
def
!=(arg0: Any): Boolean
- Definition Classes
- AnyRef → Any
-
final
def
##(): Int
- Definition Classes
- AnyRef → Any
-
final
def
==(arg0: Any): Boolean
- Definition Classes
- AnyRef → Any
-
def
addBytes(bytes: Array[Byte], zipPath: String): Try[Zipper]
Add an array of bytes to the
Zipper
.Add an array of bytes to the
Zipper
. The bytes constitute an eventual entry in a zip file; a reference to the byte array is held within thisZipper
until it is garbage-collected.- bytes
the array of bytes representing the entry to be written to the zip file
- zipPath
the path for the entry in the zip file
-
def
addDirectory(dir: File, strip: Option[String] = None, flatten: Boolean = false, wildcard: Option[String] = None): Try[Zipper]
Recursively add all the files in a directory to the
Zipper
.Recursively add all the files in a directory to the
Zipper
. Does not currently work properly on Windows.- dir
the directory, which must exist
- strip
optional leading path to strip. If not specified, the full path to each file (minus file system root) is used.
- flatten
whether or not to flatten the entries. Note that a
true
value can cause errors if files in different directories have the same name.- wildcard
optional wildcard to match files against. If
None
, all files found are added. This is a simple glob pattern, acceptable to grizzled.file.util.fnmatch.- returns
A
Success
with the newZipper
, orFailure
on error.
-
def
addFile(f: File, zipPath: String): Try[Zipper]
Add a file to the
Zipper
, specifying the zip file entry name explicitly.Add a file to the
Zipper
, specifying the zip file entry name explicitly.Note: The existence or non-existence of the file isn't checked until you call
writeZip()
orwriteJar()
.- f
the
File
to be added- zipPath
the path of the entry in the zip or jar file. Any file system root will be stripped.
- returns
A
Success
with a newZipper
object, on success. AFailure
on error. The originalZipper
is not modified.
-
def
addFile(f: File, flatten: Boolean): Try[Zipper]
Add a file to the
Zipper
.Add a file to the
Zipper
. The entry in the zip file will be the base name of the file, ifflatten
is specified. Otherwise, it'll be the path itself (if the path is relative) or the path with the file system root removed (if it's absolute).Note: The existence or non-existence of the file isn't checked until you call
writeZip()
orwriteJar()
.- f
the
File
to be added- flatten
whether or not to flatten the path in the zip file
- returns
A
Success
with a newZipper
object, on success. AFailure
on error. The originalZipper
is not modified.
-
def
addFile(f: File): Try[Zipper]
Add a file to the
Zipper
.Add a file to the
Zipper
. The path in the resulting zip or jar file will be the path (if it's relative) or the path with the file system root removed (if it's absolute).Note: The existence or non-existence of the file isn't checked until you call
writeZip()
orwriteJar()
.- f
the
File
to be added- returns
A
Success
with a newZipper
object, on success. AFailure
on error. The originalZipper
is not modified.
-
def
addFile(path: String, zipPath: String): Try[Zipper]
Add a file to the
Zipper
, specifying the zip file entry name explicitly.Add a file to the
Zipper
, specifying the zip file entry name explicitly.Note: The existence or non-existence of the file isn't checked until you call
writeZip()
orwriteJar()
.- path
path to the file to add
- zipPath
the path of the entry in the zip or jar file. Any file system root will be stripped.
- returns
A
Success
with a newZipper
object, on success. AFailure
on error. The originalZipper
is not modified.
-
def
addFile(path: String, flatten: Boolean): Try[Zipper]
Add a file to the
Zipper
.Add a file to the
Zipper
. The entry in the zip file will be the base name of the file, ifflatten
is specified. Otherwise, it'll be the path itself (if the path is relative) or the path with the file system root removed (if it's absolute).Note: The existence or non-existence of the file isn't checked until you call
writeZip()
orwriteJar()
.- path
path to the file to add
- flatten
whether or not to flatten the path in the zip file
- returns
A
Success
with a newZipper
object, on success. AFailure
on error. The originalZipper
is not modified.
-
def
addFile(path: String): Try[Zipper]
Add a file to the
Zipper
.Add a file to the
Zipper
. The path in the resulting zip or jar file will be the path (if it's relative) or the path with the file system root removed (if it's absolute).Note: The existence or non-existence of the file isn't checked until you call
writeZip()
orwriteJar()
.- path
path to the file to add
- returns
A
Success
with a newZipper
object, on success. AFailure
on error. The originalZipper
is not modified.
-
def
addInputStream(inputStream: InputStream, zipPath: String, flatten: Boolean): Try[Zipper]
Add an
InputStream
to theZipper
, using the specified path in the zip file.Add an
InputStream
to theZipper
, using the specified path in the zip file. Ifflatten
is specified, all directories will be removed from the zip path; otherwise, it will be used as-is, with any file system root removed.Warning: An
InputStream
represents an open resource (e.g., an open file descriptor). Those resources are held open until you callwriteZip()
orwriteJar()
. If you add too manyInputStream
objects (orReader
orSource
objects) to aZipper
, you could theoretically, run out of open file descriptors.- inputStream
the
InputStream
to add- zipPath
the path to use within the zip file. Any file system root is removed from this path.
- flatten
whether or not to flatten the zip path
- returns
A
Success
with a newZipper
object, on success. AFailure
on error. The originalZipper
is not modified.
-
def
addInputStream(inputStream: InputStream, zipPath: String): Try[Zipper]
Add an
InputStream
to theZipper
, using the specified path in the zip file.Add an
InputStream
to theZipper
, using the specified path in the zip file.Warning: An
InputStream
represents an open resource (e.g., an open file descriptor). Those resources are held open until you callwriteZip()
orwriteJar()
. If you add too manyInputStream
objects (orReader
orSource
objects) to aZipper
, you could theoretically, run out of open file descriptors.- inputStream
the
InputStream
to add- zipPath
the path to use within the zip file. Any file system root is removed from this path.
- returns
A
Success
with a newZipper
object, on success. AFailure
on error. The originalZipper
is not modified.
-
def
addReader(reader: Reader, zipPath: String, flatten: Boolean): Try[Zipper]
Add a
Reader
to theZipper
, using the specified path in the zip file.Add a
Reader
to theZipper
, using the specified path in the zip file. Ifflatten
is specified, all directories will be removed from the zip path; otherwise, it will be used as-is, with any file system root removed.Warning: A
Reader
represents an open resource (e.g., an open file descriptor). Those resources are held open until you callwriteZip()
orwriteJar()
. If you add too manyInputStream
objects (orInputStream
orSource
objects) to aZipper
, you could theoretically, run out of open file descriptors.- reader
the
Reader
to add- zipPath
the path to use within the zip file. Any file system root is removed from this path.
- flatten
whether or not to flatten the zip path
- returns
A
Success
with a newZipper
object, on success. AFailure
on error. The originalZipper
is not modified.
-
def
addReader(reader: Reader, zipPath: String): Try[Zipper]
Add a
Reader
to theZipper
, using the specified path in the zip file.Add a
Reader
to theZipper
, using the specified path in the zip file.Warning: A
Reader
represents an open resource (e.g., an open file descriptor). Those resources are held open until you callwriteZip()
orwriteJar()
. If you add too manyInputStream
objects (orInputStream
orSource
objects) to aZipper
, you could theoretically, run out of open file descriptors.- reader
the
Reader
to add- zipPath
the path to use within the zip file. Any file system root is removed from this path.
- returns
A
Success
with a newZipper
object, on success. AFailure
on error. The originalZipper
is not modified.
-
def
addSource(source: Source, zipPath: String, flatten: Boolean): Try[Zipper]
Add a
scala.io.SOurce
to theZipper
, using the specified path in the zip file.Add a
scala.io.SOurce
to theZipper
, using the specified path in the zip file. Ifflatten
is specified, all directories will be removed from the zip path; otherwise, it will be used as-is, with any file system root removed.Warning: A
Source
represents an open resource (e.g., an open file descriptor). Those resources are held open until you callwriteZip()
orwriteJar()
. If you add too manySource
objects (orReader
orInputStream
objects) to aZipper
, you could theoretically, run out of open file descriptors.- source
the
Source
to add- zipPath
the path to use within the zip file. Any file system root is removed from this path.
- flatten
whether or not to flatten the zip path
- returns
A
Success
with a newZipper
object, on success. AFailure
on error. The originalZipper
is not modified.
-
def
addSource(source: Source, zipPath: String): Try[Zipper]
Add a
scala.io.Source
to theZipper
, using the specified path in the zip file.Add a
scala.io.Source
to theZipper
, using the specified path in the zip file.Warning: A
Source
represents an open resource (e.g., an open file descriptor). Those resources are held open until you callwriteZip()
orwriteJar()
. If you add too manySource
objects (orReader
orInputStream
objects) to aZipper
, you could theoretically, run out of open file descriptors.- source
the
Source
to add- zipPath
the path to use within the zip file. Any file system root is removed from this path.
- returns
A
Success
with a newZipper
object, on success. AFailure
on error. The originalZipper
is not modified.
-
def
addURL(url: URL, zipPath: String): Try[Zipper]
Add a
grizzled.net.URL
to theZipper
.Add a
grizzled.net.URL
to theZipper
. This method is just shorthand for:val gurl = grizzled.net.URL(...) zipper.addURL(gurl.javaURL)
- url
the URL to the resource to be added
- zipPath
the path within the zip file for the entry
- returns
A
Success
with a newZipper
object, on success. AFailure
on error. The originalZipper
is not modified.
-
def
addURL(url: URL, zipPath: String): Try[Zipper]
Add a
java.net.URL
to theZipper
.Add a
java.net.URL
to theZipper
. The path in the zip file will be taken from the path component of the URL. That means the URL must have a file name component. For instance, if you add the URLhttp://www.example.com/
, you'll get an error, because the path component is "/", and the corresponding relative path is "". In other words,Zipper
does not addindex.html
for you automatically. A URL likehttp://www.example.com/index.html
will work fine, resulting inindex.html
being added to the resulting zip file. Similarly, using this method to addhttp://www.example.com/music/My-Song.mp3
will writemusic/My-Song.mp3
to the zip or jar file.Note: The URL is not validated (i.e., no connection is made) until you call
writeZip()
orwriteJar()
.- url
the URL to the resource to be added
- zipPath
the path within the zip file for the entry
- returns
A
Success
with a newZipper
object, on success. AFailure
on error. The originalZipper
is not modified.
-
def
addZipDirectory(path: String): Try[Zipper]
Add a directory entry to the
Zipper
.Add a directory entry to the
Zipper
. The path should be in "/" form, even on Windows, since zip and jar files always use "/". Any leading "/" will be removed, converting it to a relative path.- path
the path of the directory entry to add
- returns
A
Success
with a newZipper
object, on success. AFailure
on error. The originalZipper
is not modified.
-
final
def
asInstanceOf[T0]: T0
- Definition Classes
- Any
-
def
clone(): AnyRef
- Attributes
- protected[java.lang]
- Definition Classes
- AnyRef
- Annotations
- @native() @throws( ... )
- val comment: Option[String]
-
final
def
eq(arg0: AnyRef): Boolean
- Definition Classes
- AnyRef
-
def
equals(arg0: Any): Boolean
- Definition Classes
- AnyRef → Any
-
def
finalize(): Unit
- Attributes
- protected[java.lang]
- Definition Classes
- AnyRef
- Annotations
- @throws( classOf[java.lang.Throwable] )
-
final
def
getClass(): Class[_]
- Definition Classes
- AnyRef → Any
- Annotations
- @native()
-
def
hashCode(): Int
- Definition Classes
- AnyRef → Any
- Annotations
- @native()
-
final
def
isInstanceOf[T0]: Boolean
- Definition Classes
- Any
-
final
def
ne(arg0: AnyRef): Boolean
- Definition Classes
- AnyRef
-
final
def
notify(): Unit
- Definition Classes
- AnyRef
- Annotations
- @native()
-
final
def
notifyAll(): Unit
- Definition Classes
- AnyRef
- Annotations
- @native()
-
val
paths: Set[String]
The unique paths in the
Zipper
.The unique paths in the
Zipper
. The directory entries will be suffixed with "/". Note that intermediate directory entries will not be represented in this list. Only the paths that have been explicitly added are represented. -
def
setComment(comment: String): Zipper
Set the comment to be written to the zip or jar file.
Set the comment to be written to the zip or jar file.
- comment
the comment.
- returns
a new
Zipper
with the comment. This operation cannot fail, so the new value is returned without being wrapped in aTry
.
-
final
def
synchronized[T0](arg0: ⇒ T0): T0
- Definition Classes
- AnyRef
-
def
toString(): String
- Definition Classes
- AnyRef → Any
-
final
def
wait(): Unit
- Definition Classes
- AnyRef
- Annotations
- @throws( ... )
-
final
def
wait(arg0: Long, arg1: Int): Unit
- Definition Classes
- AnyRef
- Annotations
- @throws( ... )
-
final
def
wait(arg0: Long): Unit
- Definition Classes
- AnyRef
- Annotations
- @native() @throws( ... )
-
def
writeJar(jarFile: File, manifest: Option[Manifest]): Try[File]
Write the contents of this
Zipper
to a jar file, with or without a jar manifest.Write the contents of this
Zipper
to a jar file, with or without a jar manifest. You can call this method more than once.Warning: While you can call this method multiple times (to write a single
Zipper
to multiple zip files, for instance), some entry sources cannot be read multiple times. For instance,Zipper
does not attempt to rewindReader
,InputStream
orSource
objects, so they cannot be read more than once; reusing aZipper
containing those types of sources will result in an error.- jarFile
the jar file to write. If it exists, it will be overwritten.
- manifest
optional jar manifest
- returns
A
Success
containing thejarFile
parameter, on success. AFailure
on error.
-
def
writeJar(jarFile: File): Try[File]
Write the contents of this
Zipper
to a jar file.Write the contents of this
Zipper
to a jar file. The jar file will not have a jar manifest. You can call this method more than once.Warning: While you can call this method multiple times (to write a single
Zipper
to multiple zip files, for instance), some entry sources cannot be read multiple times. For instance,Zipper
does not attempt to rewindReader
,InputStream
orSource
objects, so they cannot be read more than once; reusing aZipper
containing those types of sources will result in an error.- jarFile
the jar file to write. If it exists, it will be overwritten.
- returns
A
Success
containing thejarFile
parameter, on success. AFailure
on error.
-
def
writeJar(path: String): Try[File]
Write the contents of this
Zipper
to a jar file.Write the contents of this
Zipper
to a jar file. The jar file will not have a jar manifest. You can call this method more than once.Warning: While you can call this method multiple times (to write a single
Zipper
to multiple zip files, for instance), some entry sources cannot be read multiple times. For instance,Zipper
does not attempt to rewindReader
,InputStream
orSource
objects, so they cannot be read more than once; reusing aZipper
containing those types of sources will result in an error.- path
the path to the jar file to write. If it exists, it will be overwritten
- returns
A
Success
with aFile
of the written jar, on success. AFailure
on error.
-
def
writeZip(zipFile: File): Try[File]
Write the contents of this
Zipper
to a zip file.Write the contents of this
Zipper
to a zip file. You can call this method more than once.Warning: While you can call this method multiple times (to write a single
Zipper
to multiple zip files, for instance), some entry sources cannot be read multiple times. For instance,Zipper
does not attempt to rewindReader
,InputStream
orSource
objects, so they cannot be read more than once; reusing aZipper
containing those types of sources will result in an error.- zipFile
the zip file to write. If it exists, it will be overwritten.
- returns
A
Success
containing thezipFile
parameter, on success. AFailure
on error.
-
def
writeZip(path: String): Try[File]
Write the contents of this
Zipper
to a zip file.Write the contents of this
Zipper
to a zip file. You can call this method more than once.Warning: While you can call this method multiple times (to write a single
Zipper
to multiple zip files, for instance), some entry sources cannot be read multiple times. For instance,Zipper
does not attempt to rewindReader
,InputStream
orSource
objects, so they cannot be read more than once; reusing aZipper
containing those types of sources will result in an error.- path
the path to the zip file to write. If it exists, it will be overwritten
- returns
A
Success
with aFile
of the written zip, on success. AFailure
on error.