package zip
The grizzled.zip package contains classes and functions to make it easier
to operate on zip and jar files.
- Alphabetic
- By Inheritance
- zip
- AnyRef
- Any
- Hide All
- Show All
- Public
- All
Type Members
-
class
Zipper extends AnyRef
The
Zipperclass 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.Zipper: Write zip and jar files more easily
The
Zipperclass 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. AZipperobject behaves somewhat like an immutable Scala collection, into which you can dropFileobjects,InputStreamobjects,Readerobjects,Sourceobjects, URLs and pathnames. When you callwriteZiporwriteJar, the objects inZipperare written to the actual underlying zip or jar file.A
Zippercan either preserve pathnames or flatten the paths down to single components. When preserving pathnames, aZipperobject 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 addC:\Temp\hello.txtto aZipperon Windows, theZipperwill strip theC:\, addingTemp/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, theZipperwill addtmp/foo/bar.txtto the file.Directories
You can explicitly add directory entries to a
Zipper, usingaddZipDirectory(). When you're not flattening entries, aZipperobject 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.txtto aZipper, without flattening it, theZipperwill 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.
Zipperautomatically creating unique intermediate directories for you.Constructing a Zipper object
The class constructor is private; use the companion object's
apply()functions to instantiateZipperobjects.Using a Zipper object
The
addFile()methods all returnTryobjects, and they do not modify the originalZipperobject. On success, they return aSuccessobject that contains a newZipper.Because the
addFile()methods returnTry, 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
Tryis monadic, aforcomprehension 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
forcomprehension can be problematic. If you're not averse to using a localvar, 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
varusingfoldLeft(), though you still have to contend with a thrown exception. (You can always wrap the code in aTry.)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
Zipperis 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
Zipperclass 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, theZipperclass can be extended to support storing uncompressed data.