You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@clerezza.apache.org by re...@apache.org on 2011/01/26 00:19:37 UTC

svn commit: r1063515 [1/2] - in /incubator/clerezza/trunk: org.apache.clerezza.parent/ org.apache.clerezza.parent/org.apache.clerezza.ext.org.ops4j.pax.swissbox.tinybundles/ org.apache.clerezza.parent/org.apache.clerezza.platform.launcher.storageless.p...

Author: reto
Date: Tue Jan 25 23:19:36 2011
New Revision: 1063515

URL: http://svn.apache.org/viewvc?rev=1063515&view=rev
Log:
CLEREZZA-410: added sourcebundle service that updates a bundle based on changes in source files

Added:
    incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.ext.org.ops4j.pax.swissbox.tinybundles/
      - copied from r1059916, incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.ext.org.json.simple/
    incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/
      - copied from r1059916, incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.shell/
    incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/org/apache/clerezza/sourcebundle/
    incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/org/apache/clerezza/sourcebundle/BundleRoot.scala
    incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/
    incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/ErrorHandling.scala
    incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/
    incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/Hash.scala
    incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/IO.scala
    incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/IPC.scala
    incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/NameFilter.scala
    incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/Pack.scala
    incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/Path.scala
    incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/PathMapper.scala
    incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/Resources.scala
    incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/SourceModificationWatch.scala
    incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/Using.scala
Removed:
    incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/org/apache/clerezza/shell/
Modified:
    incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.ext.org.ops4j.pax.swissbox.tinybundles/pom.xml
    incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.platform.launcher.storageless.parent/pom.xml
    incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/NOTICE.txt
    incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/pom.xml
    incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/resources/OSGI-INF/serviceComponents.xml
    incubator/clerezza/trunk/org.apache.clerezza.parent/pom.xml
    incubator/clerezza/trunk/scala-scripting/script-engine/src/main/scala/org/apache/clerezza/scala/scripting/CompilerService.scala
    incubator/clerezza/trunk/scala-scripting/script-engine/src/main/scala/org/apache/clerezza/scala/scripting/TrackingCompiler.scala

Modified: incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.ext.org.ops4j.pax.swissbox.tinybundles/pom.xml
URL: http://svn.apache.org/viewvc/incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.ext.org.ops4j.pax.swissbox.tinybundles/pom.xml?rev=1063515&r1=1059916&r2=1063515&view=diff
==============================================================================
--- incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.ext.org.ops4j.pax.swissbox.tinybundles/pom.xml (original)
+++ incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.ext.org.ops4j.pax.swissbox.tinybundles/pom.xml Tue Jan 25 23:19:36 2011
@@ -1,20 +1,27 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-  	<modelVersion>4.0.0</modelVersion>
-  	<parent>
-    	<artifactId>org.apache.clerezza.parent</artifactId>
-    	<groupId>org.apache.clerezza</groupId>
-    	<version>0.2-incubating-SNAPSHOT</version>
-  	</parent>
-  	<groupId>org.apache.clerezza.ext</groupId>
-  	<artifactId>org.json.simple</artifactId>
-  	<name>Clerezza Ext - Json.simple OSGi Bundle</name>
-  	<version>0.3-incubating-SNAPSHOT</version>
-  	<packaging>bundle</packaging>
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<artifactId>org.apache.clerezza.parent</artifactId>
+		<groupId>org.apache.clerezza</groupId>
+		<version>0.2-incubating-SNAPSHOT</version>
+	</parent>
+	<groupId>org.apache.clerezza.ext</groupId>
+	<artifactId>org.ops4j.pax.swissbox.tinybundles</artifactId>
+	<name>Clerezza Ext - SwissBox TinyBundles OSGi Bundle</name>
+	<version>0.1-incubating-SNAPSHOT</version>
+	<packaging>bundle</packaging>
 	<description>json.simple parses and serializes json</description>
-  	<dependencies>
+	<dependencies>
 		<dependency>
-			<groupId>com.googlecode.json-simple</groupId>
-			<artifactId>json-simple</artifactId>
+			<groupId>org.ops4j.pax.swissbox</groupId>
+			<artifactId>pax-swissbox-tinybundles</artifactId>
+			<exclusions>
+				<exclusion>
+					<artifactId>org.slf4j</artifactId>
+					<groupId>slf4j-api</groupId>
+				</exclusion>
+			</exclusions>
 		</dependency>
 	</dependencies>
 	<build>
@@ -25,7 +32,10 @@
 				<extensions>true</extensions>
 				<configuration>
 					<instructions>
-						<Export-Package>org.json.simple.*</Export-Package>
+						<Embed-Transitive>true</Embed-Transitive>
+						<Export-Package>org.ops4j.pax.swissbox.tinybundles.core;version="1.3.0"</Export-Package>
+						<!-- <Private-Package>*</Private-Package> -->
+						<Embed-Dependency>*;artifactId=!slf4j-api;scope=compile;inline=true</Embed-Dependency>
 					</instructions>
 				</configuration>
 			</plugin>

Modified: incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.platform.launcher.storageless.parent/pom.xml
URL: http://svn.apache.org/viewvc/incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.platform.launcher.storageless.parent/pom.xml?rev=1063515&r1=1063514&r2=1063515&view=diff
==============================================================================
--- incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.platform.launcher.storageless.parent/pom.xml (original)
+++ incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.platform.launcher.storageless.parent/pom.xml Tue Jan 25 23:19:36 2011
@@ -66,6 +66,11 @@
 		</dependency>
 		<dependency>
 			<groupId>org.apache.clerezza.ext</groupId>
+			<artifactId>org.ops4j.pax.swissbox.tinybundles</artifactId>
+			<scope>runtime</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.clerezza.ext</groupId>
 			<artifactId>javax.mail</artifactId>
 			<scope>runtime</scope>
 		</dependency>
@@ -314,6 +319,11 @@
 		</dependency>
 		<dependency>
 			<groupId>org.apache.clerezza</groupId>
+			<artifactId>org.apache.clerezza.sourcebundle</artifactId>
+			<scope>runtime</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.clerezza</groupId>
 			<artifactId>org.apache.clerezza.sshshell</artifactId>
 			<scope>runtime</scope>
 		</dependency>

Modified: incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/NOTICE.txt
URL: http://svn.apache.org/viewvc/incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/NOTICE.txt?rev=1063515&r1=1059916&r2=1063515&view=diff
==============================================================================
--- incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/NOTICE.txt (original)
+++ incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/NOTICE.txt Tue Jan 25 23:19:36 2011
@@ -1 +1 @@
-
+Uses code from: http://code.google.com/p/simple-build-tool/
\ No newline at end of file

Modified: incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/pom.xml
URL: http://svn.apache.org/viewvc/incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/pom.xml?rev=1063515&r1=1059916&r2=1063515&view=diff
==============================================================================
--- incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/pom.xml (original)
+++ incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/pom.xml Tue Jan 25 23:19:36 2011
@@ -5,10 +5,10 @@
 		<artifactId>org.apache.clerezza.parent</artifactId>
 		<version>0.2-incubating-SNAPSHOT</version>
 	</parent>
-	<artifactId>org.apache.clerezza.shell</artifactId>
+	<artifactId>org.apache.clerezza.sourcebundle</artifactId>
 	<version>0.1-incubating-SNAPSHOT</version>
 	<packaging>bundle</packaging>
-	<name>Clerezza - Shell Service</name>
+	<name>Clerezza - Source-Bundle Service</name>
 	<dependencies>
 		<dependency>
 			<groupId>org.apache.clerezza.scala</groupId>
@@ -39,6 +39,11 @@
 			<groupId>org.osgi</groupId>
 			<artifactId>org.osgi.core</artifactId>
 		</dependency>
+		<dependency>
+			<groupId>org.ops4j.pax.swissbox</groupId>
+			<artifactId>pax-swissbox-tinybundles</artifactId>
+			<version>1.3.0</version>
+		</dependency>
 	</dependencies>
 	<build>
 		<sourceDirectory>src/main/scala</sourceDirectory>
@@ -50,8 +55,9 @@
 				<configuration>
 					<instructions>
 						<Service-Component>OSGI-INF/serviceComponents.xml</Service-Component>
-						<Export-Package>org.apache.clerezza.shell</Export-Package>
-						<Bundle-SymbolicName>org.apache.clerezza.shell</Bundle-SymbolicName>
+						<Export-Package>org.apache.clerezza.sourcebundle</Export-Package>
+						<Private-Package>sbt,sbt.*</Private-Package>
+						<Bundle-SymbolicName>org.apache.clerezza.sourcebundle</Bundle-SymbolicName>
 					</instructions>
 				</configuration>
 			</plugin>

Modified: incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/resources/OSGI-INF/serviceComponents.xml
URL: http://svn.apache.org/viewvc/incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/resources/OSGI-INF/serviceComponents.xml?rev=1063515&r1=1059916&r2=1063515&view=diff
==============================================================================
--- incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/resources/OSGI-INF/serviceComponents.xml (original)
+++ incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/resources/OSGI-INF/serviceComponents.xml Tue Jan 25 23:19:36 2011
@@ -1,26 +1,18 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <components xmlns:scr="http://www.osgi.org/xmlns/scr/v1.0.0">
-	<scr:component enabled="true" immediate="true" name="org.apache.clerezza.shell.ConsoleShell">
-        <implementation class="org.apache.clerezza.shell.ConsoleShell"/>
-        <property name="service.pid" value="org.apache.clerezza.shell.ConsoleShell"/>
-		<reference name="shellFactory"
-				interface="org.apache.clerezza.shell.ShellFactory"
-				cardinality="1..1"
-				bind="bindShellFactory" unbind="unbindShellFactory"/>
-    </scr:component>
-	<scr:component enabled="true" immediate="true" name="org.apache.clerezza.shell.ShellFactory">
-        <implementation class="org.apache.clerezza.shell.ShellFactory"/>
+	<scr:component enabled="true" immediate="true" name="org.apache.clerezza.sourcebundle.BundleRoot">
+        <implementation class="org.apache.clerezza.sourcebundle.BundleRoot"/>
+        <property name="service.pid" value="org.apache.clerezza.sourcebundle.BundleRoot"/>
 		<service servicefactory="false">
-            <provide interface="org.apache.clerezza.shell.ShellFactory"/>
+            <provide interface="org.apache.clerezza.sourcebundle.BundleRoot"/>
         </service>
-        <property name="service.pid" value="org.apache.clerezza.shell.ShellFactory"/>
-		<reference name="interpreterFactory"
-				interface="org.apache.clerezza.scala.scripting.InterpreterFactory"
+		<reference name="CompilerService"
+				interface="org.apache.clerezza.scala.scripting.CompilerService"
+				cardinality="1..1"
+				bind="bindCompilerService" unbind="unbindCompilerService"/>
+		<reference name="PackageAdmin"
+				interface="org.osgi.service.packageadmin.PackageAdmin"
 				cardinality="1..1"
-				bind="bindInterpreterFactory" unbind="unbindInterpreterFactory"/>
-		<reference name="commands"
-				interface="org.apache.clerezza.shell.ShellCommand"
-				cardinality="0..n"
-				bind="bindCommand" unbind="unbindCommand"/>
+				bind="bindPackageAdmin" unbind="unbindPackageAdmin"/>
     </scr:component>
 </components>

Added: incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/org/apache/clerezza/sourcebundle/BundleRoot.scala
URL: http://svn.apache.org/viewvc/incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/org/apache/clerezza/sourcebundle/BundleRoot.scala?rev=1063515&view=auto
==============================================================================
--- incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/org/apache/clerezza/sourcebundle/BundleRoot.scala (added)
+++ incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/org/apache/clerezza/sourcebundle/BundleRoot.scala Tue Jan 25 23:19:36 2011
@@ -0,0 +1,222 @@
+/*
+ *  Copyright 2011 reto.
+ * 
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ * 
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *  under the License.
+ */
+
+package org.apache.clerezza.sourcebundle
+
+import java.io._
+import scala.actors.DaemonActor
+import scala.io._
+import org.osgi.framework.Bundle
+import org.osgi.framework.BundleContext
+import sbt._
+import scala.actors.Actor
+import org.ops4j.pax.swissbox.tinybundles.core.TinyBundles._
+import org.ops4j.pax.swissbox.tinybundles.core.TinyBundle
+import org.apache.clerezza.scala.scripting.CompilerService
+import org.osgi.framework.Constants
+import org.osgi.service.component.ComponentContext
+import org.slf4j.LoggerFactory
+import scala.tools.nsc.io.AbstractFile
+import scala.tools.nsc.io.VirtualDirectory
+import scala.collection.mutable
+import org.osgi.service.packageadmin.PackageAdmin
+
+class BundleRoot {
+
+	var compilerService: CompilerService = null
+	var packageAdmin: PackageAdmin = null
+
+	var bundleContext: BundleContext = null
+
+	val sourceBundles = mutable.ListBuffer[SourceBundle]()
+
+	protected def activate(c: ComponentContext) {
+		this.bundleContext = c.getBundleContext
+		sourceBundles.clear()
+		for (bundle <- bundleContext.getBundles) {
+			val location = bundle.getLocation
+			if (location.startsWith(BundleRoot.sourceBundleUriPrefix)) {
+				val dir = new File(location.substring(
+						BundleRoot.sourceBundleUriPrefix.length))
+				val sourceBundle = new SourceBundle(dir, bundle)
+				sourceBundle.start()
+				sourceBundles += sourceBundle
+			}
+		}
+	}
+
+	protected def deactivate(c: ComponentContext) {
+		for (sb <- sourceBundles) sb.stop()
+	}
+
+	def createSourceBundle(dir: File) = {
+		val sourceBundle = new SourceBundle(dir)
+		sourceBundle.start()
+		sourceBundles += sourceBundle
+		sourceBundle
+	}
+
+	def bindCompilerService(cs: CompilerService) {
+		compilerService = cs;
+	}
+
+	def unbindCompilerService(cs: CompilerService) {
+		compilerService = null;
+	}
+
+	def bindPackageAdmin(pa: PackageAdmin) {
+		packageAdmin = pa
+	}
+
+	def unbindPackageAdmin(pa: PackageAdmin) {
+		packageAdmin = null
+	}
+
+	class SourceBundle(dir: File, existingBundle: Bundle) extends DaemonActor {
+
+		def this(dir: File) {
+			this(dir, null)
+		}
+
+		var stopped = false
+		var logger = LoggerFactory.getLogger(classOf[SourceBundle])
+
+		val sourcePath = Path.fromFile(dir)
+		var watchState = WatchState.empty
+		var bundle: Bundle = existingBundle
+
+		def getFilesAsCharArrays(file: File): List[Array[Char]] = {
+			logger.debug("getting sources in "+file)
+			var result: List[Array[Char]] = Nil
+			if (file.isDirectory) {
+				val children = file.listFiles
+				import scala.collection.JavaConversions._
+				for(child <- children) {
+					if (!child.getName.startsWith(".")) {
+						result = getFilesAsCharArrays(child) ::: result
+					}
+				}
+			} else {
+				if (file.getName.endsWith(".scala")) {
+					val in = Source.fromFile(file, "utf-8")
+					val stream = in.toStream
+					result = stream.toArray :: result
+				} 
+			}
+			result
+		}
+
+		private[this] def updateBundle() {
+			logger.info("updating source bundle with root "+dir)
+
+			val tinyBundle: TinyBundle = newBundle()
+
+			def compileDir(sourceDir: File) {
+				
+				val charArrays = getFilesAsCharArrays(sourceDir)
+				logger.debug("compiling "+charArrays.size+" files")
+				
+				val vdPathPrefix = "(memory)"
+				val virtualDirectory = new VirtualDirectory(vdPathPrefix, None)
+				//val wrappedDirectory = VirtualDirectoryWrapper.wrap(virtualDirectory, outputListener)
+
+				val writtenClasses = compilerService.compileToDir(charArrays, virtualDirectory)
+				logger.debug("virtualDirectory "+virtualDirectory.size)
+				for (writtenClass <- writtenClasses) {
+					val fullPath = writtenClass.path
+					val path = fullPath.substring(vdPathPrefix.length+1)
+					tinyBundle.add(path, new ByteArrayInputStream(writtenClass.toByteArray))
+				}
+			}
+
+			def copyResource(resourcesDir: File) {
+				def copyResource(resourcesDir: File, prefix: String) {
+					val children = resourcesDir.listFiles
+					import scala.collection.JavaConversions._
+					for(child <- children) {
+						val childName = child.getName
+						if (!childName.startsWith(".")) {
+							if (child.isDirectory) {
+								copyResource(child, prefix+childName+"/")
+							} else {
+								tinyBundle.add(prefix+childName, new FileInputStream(child))
+							}
+						}
+					}
+				}
+				copyResource(resourcesDir, "")
+			}
+
+			val symName = dir.getPath.substring(1).replace(File.separatorChar, '.')
+
+			tinyBundle.set("Bundle-SymbolicName", symName)
+
+			val scalaSourceDir = new File(dir, "src/main/scala")
+			if (scalaSourceDir.exists) {
+				compileDir(scalaSourceDir)
+			} else {
+				logger.debug("No source dir "+scalaSourceDir)
+			}
+			val resourcesDir = new File(dir, "src/main/resources")
+			if (resourcesDir.exists) {
+				copyResource(resourcesDir)
+			} else {
+				logger.debug("No resources dir "+resourcesDir)
+			}
+			val serviceComponentsFile = new File(resourcesDir, "OSGI-INF/serviceComponents.xml")
+			if (serviceComponentsFile.exists) {
+				tinyBundle.set("Service-Component", "OSGI-INF/serviceComponents.xml")
+				tinyBundle.set(Constants.EXPORT_PACKAGE, "!OSGI-INF, *" )
+			}
+			tinyBundle.set(Constants.IMPORT_PACKAGE, "*" );
+			val in = tinyBundle.build(
+					withBnd()
+				)
+
+
+			if (bundle == null) {
+				bundle = bundleContext.installBundle(BundleRoot.sourceBundleUriPrefix+dir.toString, in)
+				bundle.start()
+			} else {
+				bundle.update(in)
+			}
+			
+		}
+
+		def act() {
+			while (!stopped) {
+				val (triggered, newWatchState) =
+					SourceModificationWatch.watch(sourcePath**(-HiddenFileFilter), 1, watchState)(stopped)
+				if (!stopped) {
+					updateBundle()
+					watchState = newWatchState
+				}
+			}
+		}
+
+		def stop() {
+			stopped = true
+		}
+	}
+}
+
+
+object BundleRoot {
+
+	val sourceBundleUriPrefix = "sourcebundle:"
+
+}

Added: incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/ErrorHandling.scala
URL: http://svn.apache.org/viewvc/incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/ErrorHandling.scala?rev=1063515&view=auto
==============================================================================
--- incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/ErrorHandling.scala (added)
+++ incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/ErrorHandling.scala Tue Jan 25 23:19:36 2011
@@ -0,0 +1,28 @@
+/* sbt -- Simple Build Tool
+ * Copyright 2009  Mark Harrah
+ */
+package sbt
+
+object ErrorHandling
+{
+	def translate[T](msg: => String)(f: => T) =
+		try { f }
+		catch { case e: Exception => throw new TranslatedException(msg + e.toString, e) }
+
+	def wideConvert[T](f: => T): Either[Throwable, T] =
+		try { Right(f) }
+		catch
+		{
+			case ex @ (_: Exception | _: StackOverflowError) => Left(ex)
+			case err @ (_: ThreadDeath | _: VirtualMachineError) => throw err
+			case x => Left(x)
+		}
+
+	def convert[T](f: => T): Either[Exception, T] =
+		try { Right(f) }
+		catch { case e: Exception => Left(e) }
+}
+final class TranslatedException private[sbt](msg: String, cause: Throwable) extends RuntimeException(msg, cause)
+{
+	override def toString = msg
+}
\ No newline at end of file

Added: incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/Hash.scala
URL: http://svn.apache.org/viewvc/incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/Hash.scala?rev=1063515&view=auto
==============================================================================
--- incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/Hash.scala (added)
+++ incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/Hash.scala Tue Jan 25 23:19:36 2011
@@ -0,0 +1,76 @@
+/* sbt -- Simple Build Tool
+ * Copyright 2009 Mark Harrah
+ */
+package sbt
+
+import java.io.{ByteArrayInputStream, File, InputStream}
+
+object Hash
+{
+	private val BufferSize = 8192
+	def toHex(bytes: Array[Byte]): String =
+	{
+		val buffer = new StringBuilder(bytes.length * 2)
+		for(i <- 0 until bytes.length)
+		{
+			val b = bytes(i)
+			val bi: Int = if(b < 0) b + 256 else b
+			buffer append toHex((bi >>> 4).asInstanceOf[Byte])
+			buffer append toHex((bi & 0x0F).asInstanceOf[Byte])
+		}
+		buffer.toString
+	}
+	def fromHex(hex: String): Array[Byte] =
+	{
+		require((hex.length & 1) == 0, "Hex string must have length 2n.")
+		val array = new Array[Byte](hex.length >> 1)
+		for(i <- 0 until hex.length by 2)
+		{
+			val c1 = hex.charAt(i)
+			val c2 = hex.charAt(i+1)
+			array(i >> 1) = ((fromHex(c1) << 4) | fromHex(c2)).asInstanceOf[Byte]
+		}
+		array
+	}
+	/** Calculates the SHA-1 hash of the given String.*/
+	def apply(s: String): Array[Byte] = apply(new ByteArrayInputStream(s.getBytes("UTF-8")))
+	/** Calculates the SHA-1 hash of the given file.*/
+	def apply(file: File): Array[Byte] = Using.fileInputStream(file)(apply)
+	/** Calculates the SHA-1 hash of the given stream, closing it when finished.*/
+	def apply(stream: InputStream): Array[Byte] =
+	{
+		import java.security.{MessageDigest, DigestInputStream}
+		val digest = MessageDigest.getInstance("SHA")
+		try
+		{
+			val dis = new DigestInputStream(stream, digest)
+			val buffer = new Array[Byte](BufferSize)
+			while(dis.read(buffer) >= 0) {}
+			dis.close()
+			digest.digest
+		}
+		finally { stream.close() }
+	}
+
+	private def toHex(b: Byte): Char =
+	{
+		require(b >= 0 && b <= 15, "Byte " + b + " was not between 0 and 15")
+		if(b < 10)
+			('0'.asInstanceOf[Int] + b).asInstanceOf[Char]
+		else
+			('a'.asInstanceOf[Int] + (b-10)).asInstanceOf[Char]
+	}
+	private def fromHex(c: Char): Int =
+	{
+		val b =
+			if(c >= '0' && c <= '9')
+				(c - '0')
+			else if(c >= 'a' && c <= 'f')
+				(c - 'a') + 10
+			else if(c >= 'A' && c <= 'F')
+				(c - 'A') + 10
+			else
+				throw new RuntimeException("Invalid hex character: '" + c + "'.")
+		b
+	}
+}
\ No newline at end of file

Added: incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/IO.scala
URL: http://svn.apache.org/viewvc/incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/IO.scala?rev=1063515&view=auto
==============================================================================
--- incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/IO.scala (added)
+++ incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/IO.scala Tue Jan 25 23:19:36 2011
@@ -0,0 +1,591 @@
+/* sbt -- Simple Build Tool
+ * Copyright 2008, 2009, 2010 Mark Harrah, Viktor Klang, Ross McDonald
+ */
+package sbt
+
+import Using._
+import ErrorHandling.translate
+
+import java.io.{BufferedReader, ByteArrayOutputStream, BufferedWriter, File, FileInputStream, InputStream, OutputStream}
+import java.net.{URI, URISyntaxException, URL}
+import java.nio.charset.Charset
+import java.util.Properties
+import java.util.jar.{Attributes, JarEntry, JarFile, JarInputStream, JarOutputStream, Manifest}
+import java.util.zip.{CRC32, GZIPOutputStream, ZipEntry, ZipFile, ZipInputStream, ZipOutputStream}
+import scala.collection.immutable.TreeSet
+import scala.collection.mutable.{HashMap,HashSet}
+import scala.reflect.{Manifest => SManifest}
+import Function.tupled
+
+object IO
+{
+	/** The maximum number of times a unique temporary filename is attempted to be created.*/
+	private val MaximumTries = 10
+	/** The producer of randomness for unique name generation.*/
+	private lazy val random = new java.util.Random
+	val temporaryDirectory = new File(System.getProperty("java.io.tmpdir"))
+	/** The size of the byte or char buffer used in various methods.*/
+	private val BufferSize = 8192
+	val Newline = System.getProperty("line.separator")
+
+	val utf8 = Charset.forName("UTF-8")
+
+	def classLocation(cl: Class[_]): URL =
+	{
+		val codeSource = cl.getProtectionDomain.getCodeSource
+		if(codeSource == null) error("No class location for " + cl)
+		else codeSource.getLocation
+	}
+	def classLocationFile(cl: Class[_]): File = toFile(classLocation(cl))
+	def classLocation[T](implicit mf: SManifest[T]): URL = classLocation(mf.erasure)
+	def classLocationFile[T](implicit mf: SManifest[T]): File = classLocationFile(mf.erasure)
+
+	def toFile(url: URL) =
+		try { new File(url.toURI) }
+		catch { case _: URISyntaxException => new File(url.getPath) }
+
+	/** Converts the given URL to a File.  If the URL is for an entry in a jar, the File for the jar is returned. */
+	def asFile(url: URL): File =
+	{
+		url.getProtocol match
+		{
+			case "file" => toFile(url)
+			case "jar" =>
+				val path = url.getPath
+				val end = path.indexOf('!')
+				new File(new URI(if(end == -1) path else path.substring(0, end)))
+			case _ => error("Invalid protocol " + url.getProtocol)
+		}
+	}
+	def assertDirectory(file: File) { assert(file.isDirectory, (if(file.exists) "Not a directory: " else "Directory not found: ") + file) }
+	def assertDirectories(file: File*) { file.foreach(assertDirectory) }
+
+	// "base.extension" -> (base, extension)
+	def split(name: String): (String, String) =
+	{
+		val lastDot = name.lastIndexOf('.')
+		if(lastDot >= 0)
+			(name.substring(0, lastDot), name.substring(lastDot+1))
+		else
+			(name, "")
+	}
+
+	def touch(files: Traversable[File]): Unit = files.foreach(touch)
+	/** Creates a file at the given location.*/
+	def touch(file: File)
+	{
+		createDirectory(file.getParentFile)
+		val created = translate("Could not create file " + file) { file.createNewFile() }
+		if(created || file.isDirectory)
+			()
+		else if(!file.setLastModified(System.currentTimeMillis))
+			error("Could not update last modified time for file " + file)
+	}
+	def createDirectories(dirs: Traversable[File]): Unit =
+		dirs.foreach(createDirectory)
+	def createDirectory(dir: File): Unit =
+	{
+		def failBase = "Could not create directory " + dir
+		if(dir.isDirectory || dir.mkdirs())
+			()
+		else if(dir.exists)
+			error(failBase + ": file exists and is not a directory.")
+		else
+			error(failBase)
+	}
+
+	/** Gzips the file 'in' and writes it to 'out'.  'in' cannot be the same file as 'out'. */
+	def gzip(in: File, out: File)
+	{
+		require(in != out, "Input file cannot be the same as the output file.")
+		Using.fileInputStream(in) { inputStream =>
+			Using.fileOutputStream()(out) { outputStream =>
+				gzip(inputStream, outputStream)
+			}
+		}
+	}
+	/** Gzips the InputStream 'in' and writes it to 'output'.  Neither stream is closed.*/
+	def gzip(input: InputStream, output: OutputStream): Unit =
+		gzipOutputStream(output) { gzStream => transfer(input, gzStream) }
+
+	/** Gunzips the file 'in' and writes it to 'out'.  'in' cannot be the same file as 'out'. */
+	def gunzip(in: File, out: File)
+	{
+		require(in != out, "Input file cannot be the same as the output file.")
+		Using.fileInputStream(in) { inputStream =>
+			Using.fileOutputStream()(out) { outputStream =>
+				gunzip(inputStream, outputStream)
+			}
+		}
+	}
+	/** Gunzips the InputStream 'input' and writes it to 'output'.  Neither stream is closed.*/
+	def gunzip(input: InputStream, output: OutputStream): Unit =
+		gzipInputStream(input) { gzStream => transfer(gzStream, output) }
+
+	def unzip(from: File, toDirectory: File, filter: NameFilter = AllPassFilter): Set[File] = fileInputStream(from)(in => unzipStream(in, toDirectory, filter))
+	def unzipURL(from: URL, toDirectory: File, filter: NameFilter = AllPassFilter): Set[File] = urlInputStream(from)(in => unzipStream(in, toDirectory, filter))
+	def unzipStream(from: InputStream, toDirectory: File, filter: NameFilter = AllPassFilter): Set[File] =
+	{
+		createDirectory(toDirectory)
+		zipInputStream(from) { zipInput => extract(zipInput, toDirectory, filter) }
+	}
+	private def extract(from: ZipInputStream, toDirectory: File, filter: NameFilter) =
+	{
+		val set = new HashSet[File]
+		def next()
+		{
+			val entry = from.getNextEntry
+			if(entry == null)
+				()
+			else
+			{
+				val name = entry.getName
+				if(filter.accept(name))
+				{
+					val target = new File(toDirectory, name)
+					//log.debug("Extracting zip entry '" + name + "' to '" + target + "'")
+					if(entry.isDirectory)
+						createDirectory(target)
+					else
+					{
+						set += target
+						translate("Error extracting zip entry '" + name + "' to '" + target + "': ") {
+							fileOutputStream(false)(target) { out => transfer(from, out) }
+						}
+					}
+					//target.setLastModified(entry.getTime)
+				}
+				else
+				{
+					//log.debug("Ignoring zip entry '" + name + "'")
+				}
+				from.closeEntry()
+				next()
+			}
+		}
+		next()
+		Set() ++ set
+	}
+
+	/** Retrieves the content of the given URL and writes it to the given File. */
+	def download(url: URL, to: File) =
+		Using.urlInputStream(url) { inputStream =>
+			transfer(inputStream, to)
+		}
+
+	def transfer(in: File, out: File): Unit =
+		fileInputStream(in){ in => transfer(in, out) }
+
+	def transfer(in: File, out: OutputStream): Unit =
+		fileInputStream(in){ in => transfer(in, out) }
+
+	/** Copies all bytes from the given input stream to the given File.*/
+	def transfer(in: InputStream, to: File): Unit =
+		Using.fileOutputStream()(to) { outputStream =>
+			transfer(in, outputStream)
+		}
+
+	/** Copies all bytes from the given input stream to the given output stream.
+	* Neither stream is closed.*/
+	def transfer(in: InputStream, out: OutputStream): Unit = transferImpl(in, out, false)
+	/** Copies all bytes from the given input stream to the given output stream.  The
+	* input stream is closed after the method completes.*/
+	def transferAndClose(in: InputStream, out: OutputStream): Unit = transferImpl(in, out, true)
+	private def transferImpl(in: InputStream, out: OutputStream, close: Boolean)
+	{
+		try
+		{
+			val buffer = new Array[Byte](BufferSize)
+			def read()
+			{
+				val byteCount = in.read(buffer)
+				if(byteCount >= 0)
+				{
+					out.write(buffer, 0, byteCount)
+					read()
+				}
+			}
+			read()
+		}
+		finally { if(close) in.close }
+	}
+
+	/** Creates a temporary directory and provides its location to the given function.  The directory
+	* is deleted after the function returns.*/
+	def withTemporaryDirectory[T](action: File => T): T =
+	{
+		val dir = createTemporaryDirectory
+		try { action(dir) }
+		finally { delete(dir) }
+	}
+	def createTemporaryDirectory: File =
+	{
+		def create(tries: Int): File =
+		{
+			if(tries > MaximumTries)
+				error("Could not create temporary directory.")
+			else
+			{
+				val randomName = "sbt_" + java.lang.Integer.toHexString(random.nextInt)
+				val f = new File(temporaryDirectory, randomName)
+
+				try { createDirectory(f); f }
+				catch { case e: Exception => create(tries + 1) }
+			}
+		}
+		create(0)
+	}
+	def withTemporaryFile[T](prefix: String, postfix: String)(action: File => T): T =
+	{
+		val file = File.createTempFile(prefix, postfix)
+		try { action(file) }
+		finally { file.delete() }
+	}
+
+	private[sbt] def jars(dir: File): Iterable[File] = listFiles(dir, GlobFilter("*.jar"))
+
+	def deleteIfEmpty(dirs: collection.Set[File]): Unit =
+	{
+		val isEmpty = new HashMap[File, Boolean]
+		def visit(f: File): Boolean = isEmpty.getOrElseUpdate(f, dirs(f) && f.isDirectory && (f.listFiles forall visit) )
+
+		dirs foreach visit
+		for( (f, true) <- isEmpty) f.delete
+	}
+
+	def delete(files: Iterable[File]): Unit = files.foreach(delete)
+	def delete(file: File)
+	{
+		translate("Error deleting file " + file + ": ")
+		{
+			if(file.isDirectory)
+			{
+				delete(listFiles(file))
+				file.delete
+			}
+			else if(file.exists)
+				file.delete
+		}
+	}
+	def listFiles(filter: java.io.FileFilter)(dir: File): Array[File] = wrapNull(dir.listFiles(filter))
+	def listFiles(dir: File, filter: java.io.FileFilter): Array[File] = wrapNull(dir.listFiles(filter))
+	def listFiles(dir: File): Array[File] = wrapNull(dir.listFiles())
+	private[sbt] def wrapNull(a: Array[File]) =
+	{
+		if(a == null)
+			new Array[File](0)
+		else
+			a
+	}
+
+
+	/** Creates a jar file.
+	* @param sources The files to include in the jar file paired with the entry name in the jar.
+	* @param outputJar The file to write the jar to.
+	* @param manifest The manifest for the jar.*/
+	def jar(sources: Traversable[(File,String)], outputJar: File, manifest: Manifest): Unit =
+		archive(sources.toSeq, outputJar, Some(manifest))
+	/** Creates a zip file.
+	* @param sources The files to include in the zip file paired with the entry name in the zip.
+	* @param outputZip The file to write the zip to.*/
+	def zip(sources: Traversable[(File,String)], outputZip: File): Unit =
+		archive(sources.toSeq, outputZip, None)
+
+	private def archive(sources: Seq[(File,String)], outputFile: File, manifest: Option[Manifest])
+	{
+		if(outputFile.isDirectory)
+			error("Specified output file " + outputFile + " is a directory.")
+		else
+		{
+			val outputDir = outputFile.getParentFile
+			createDirectory(outputDir)
+			withZipOutput(outputFile, manifest)
+			{ output =>
+				val createEntry: (String => ZipEntry) = if(manifest.isDefined) new JarEntry(_) else new ZipEntry(_)
+				writeZip(sources, output)(createEntry)
+			}
+		}
+	}
+	private def writeZip(sources: Seq[(File,String)], output: ZipOutputStream)(createEntry: String => ZipEntry)
+	{
+			import Path.{lazyPathFinder => pf}
+		val files = sources.collect { case (file,name) if file.isFile => (file, normalizeName(name)) }
+		val now = System.currentTimeMillis
+		// The CRC32 for an empty value, needed to store directories in zip files
+		val emptyCRC = new CRC32().getValue()
+
+		def addDirectoryEntry(name: String)
+		{
+			output putNextEntry makeDirectoryEntry(name)
+			output.closeEntry()
+		}
+
+		def makeDirectoryEntry(name: String) =
+		{
+//			log.debug("\tAdding directory " + relativePath + " ...")
+			val e = createEntry(name)
+			e setTime now
+			e setSize 0
+			e setMethod ZipEntry.STORED
+			e setCrc emptyCRC
+			e
+		}
+
+		def makeFileEntry(file: File, name: String) =
+		{
+//			log.debug("\tAdding " + file + " as " + name + " ...")
+			val e = createEntry(name)
+			e setTime file.lastModified
+			e
+		}
+		def addFileEntry(file: File, name: String)
+		{
+			output putNextEntry makeFileEntry(file, name)
+			transfer(file, output)
+			output.closeEntry()
+		}
+
+		//Calculate directories and add them to the generated Zip
+		allDirectoryPaths(files) foreach addDirectoryEntry
+
+		//Add all files to the generated Zip
+		files foreach { case (file, name) => addFileEntry(file, name) }
+	}
+
+	// map a path a/b/c to List("a", "b")
+	private def relativeComponents(path: String): List[String] =
+		path.split("/").toList.dropRight(1)
+
+	// map components List("a", "b", "c") to List("a/b/c/", "a/b/", "a/", "")
+	private def directories(path: List[String]): List[String] =
+		path.foldLeft(List(""))( (e,l) => (e.head + l + "/") :: e )
+
+	// map a path a/b/c to List("a/b/", "a/")
+	private def directoryPaths(path: String): List[String] =
+		directories(relativeComponents(path)).filter(_.length > 1)
+
+	// produce a sorted list of all the subdirectories of all provided files
+	private def allDirectoryPaths(files: Iterable[(File,String)]) =
+		TreeSet[String]() ++ (files flatMap { case (file, name) => directoryPaths(name) })
+
+	private def normalizeDirName(name: String) =
+	{
+		val norm1 = normalizeName(name)
+		if(norm1.endsWith("/")) norm1 else (norm1 + "/")
+	}
+	private def normalizeName(name: String) =
+	{
+		val sep = File.separatorChar
+		if(sep == '/') name else name.replace(sep, '/')
+	}
+
+	private def withZipOutput(file: File, manifest: Option[Manifest])(f: ZipOutputStream => Unit)
+	{
+		fileOutputStream(false)(file) { fileOut =>
+			val (zipOut, ext) =
+				manifest match
+				{
+					case Some(mf) =>
+					{
+						import Attributes.Name.MANIFEST_VERSION
+						val main = mf.getMainAttributes
+						if(!main.containsKey(MANIFEST_VERSION))
+							main.put(MANIFEST_VERSION, "1.0")
+						(new JarOutputStream(fileOut, mf), "jar")
+					}
+					case None => (new ZipOutputStream(fileOut), "zip")
+				}
+			try { f(zipOut) }
+			catch { case e: Exception => "Error writing " + ext + ": " + e.toString }
+			finally { zipOut.close }
+		}
+	}
+	def relativize(base: File, file: File): Option[String] =
+	{
+		val pathString = file.getAbsolutePath
+		baseFileString(base) flatMap
+		{
+			baseString =>
+			{
+				if(pathString.startsWith(baseString))
+					Some(pathString.substring(baseString.length))
+				else
+					None
+			}
+		}
+	}
+	private def baseFileString(baseFile: File): Option[String] =
+	{
+		if(baseFile.isDirectory)
+		{
+			val cp = baseFile.getAbsolutePath
+			assert(cp.length > 0)
+			val normalized = if(cp.charAt(cp.length - 1) == File.separatorChar) cp else cp + File.separatorChar
+			Some(normalized)
+		}
+		else
+			None
+	}
+	def copy(sources: Traversable[(File,File)], overwrite: Boolean = false, preserveLastModified: Boolean = false): Set[File] =
+		sources.map( tupled(copyImpl(overwrite, preserveLastModified)) ).toSet
+	private def copyImpl(overwrite: Boolean, preserveLastModified: Boolean)(from: File, to: File): File =
+	{
+		if(overwrite || !to.exists || from.lastModified > to.lastModified)
+		{
+			if(from.isDirectory)
+				createDirectory(to)
+			else
+			{
+				createDirectory(to.getParentFile)
+				copyFile(from, to, preserveLastModified)
+			}
+		}
+		to
+	}
+	def copyDirectory(source: File, target: File, overwrite: Boolean = false, preserveLastModified: Boolean = false): Unit =
+		copy( (Path.fromFile(source) ***) x Path.rebase(source, target), overwrite, preserveLastModified)
+
+	def copyFile(sourceFile: File, targetFile: File, preserveLastModified: Boolean = false)
+	{
+		require(sourceFile.exists, "Source file '" + sourceFile.getAbsolutePath + "' does not exist.")
+		require(!sourceFile.isDirectory, "Source file '" + sourceFile.getAbsolutePath + "' is a directory.")
+		fileInputChannel(sourceFile) { in =>
+			fileOutputChannel(targetFile) { out =>
+				val copied = out.transferFrom(in, 0, in.size)
+				if(copied != in.size)
+					error("Could not copy '" + sourceFile + "' to '" + targetFile + "' (" + copied + "/" + in.size + " bytes copied)")
+			}
+		}
+		if(preserveLastModified)
+			copyLastModified(sourceFile, targetFile)
+	}
+	def copyLastModified(sourceFile: File, targetFile: File) = targetFile.setLastModified( sourceFile.lastModified )
+	def defaultCharset = utf8
+
+	def write(file: File, content: String, charset: Charset = defaultCharset, append: Boolean = false): Unit =
+		writer(file, content, charset, append) { _.write(content)  }
+
+	def writer[T](file: File, content: String, charset: Charset, append: Boolean = false)(f: BufferedWriter => T): T =
+	{
+		if(charset.newEncoder.canEncode(content))
+			fileWriter(charset, append)(file) { f }
+		else
+			error("String cannot be encoded by charset " + charset.name)
+	}
+
+	def reader[T](file: File, charset: Charset = defaultCharset)(f: BufferedReader => T): T =
+		fileReader(charset)(file) { f }
+
+	def read(file: File, charset: Charset = defaultCharset): String =
+	{
+		val out = new ByteArrayOutputStream(file.length.toInt)
+		transfer(file, out)
+		out.toString(charset.name)
+	}
+	/** doesn't close the InputStream */
+	def readStream(in: InputStream, charset: Charset = defaultCharset): String =
+	{
+		val out = new ByteArrayOutputStream
+		transfer(in, out)
+		out.toString(charset.name)
+	}
+	def readBytes(file: File): Array[Byte] = fileInputStream(file)(readBytes)
+	/** doesn't close the InputStream */
+	def readBytes(in: InputStream): Array[Byte] =
+	{
+		val out = new ByteArrayOutputStream
+		transfer(in, out)
+		out.toByteArray
+	}
+
+	def append(file: File, content: String, charset: Charset = defaultCharset): Unit =
+		write(file, content, charset, true)
+	def append(file: File, bytes: Array[Byte]): Unit =
+		writeBytes(file, bytes, true)
+
+	def write(file: File, bytes: Array[Byte]): Unit =
+		writeBytes(file, bytes, false)
+	private def writeBytes(file: File, bytes: Array[Byte], append: Boolean): Unit =
+		fileOutputStream(append)(file) { _.write(bytes) }
+
+
+	// Not optimized for large files
+	def readLines(file: File, charset: Charset = defaultCharset): List[String] =
+		fileReader(charset)(file)(readLines)
+		
+	// Not optimized for large files
+	def readLines(in: BufferedReader): List[String] = 
+		foldLines[List[String]](in, Nil)( (accum, line) => line :: accum ).reverse
+	
+	def foreachLine(in: BufferedReader)(f: String => Unit): Unit =
+		foldLines(in, ())( (_, line) => f(line) )
+		
+	def foldLines[T](in: BufferedReader, init: T)(f: (T, String) => T): T =
+	{
+		def readLine(accum: T): T =
+		{
+			val line = in.readLine()
+			if(line eq null) accum else readLine(f(accum, line))
+		}
+		readLine(init)
+	}
+	
+	def writeLines(file: File, lines: Seq[String], charset: Charset = defaultCharset, append: Boolean = false): Unit =
+		writer(file, lines.headOption.getOrElse(""), charset, append) { w =>
+			lines.foreach { line => w.write(line); w.newLine() }
+		}
+		
+	def write(properties: Properties, label: String, to: File) =
+		fileOutputStream()(to) { output => properties.store(output, label) }
+	def load(properties: Properties, from: File): Unit =
+		if(from.exists)
+			fileInputStream(from){ input => properties.load(input) }
+
+	/** A pattern used to split a String by path separator characters.*/
+	private val PathSeparatorPattern = java.util.regex.Pattern.compile(File.pathSeparator)
+
+	/** Splits a String around path separator characters. */
+	def pathSplit(s: String) = PathSeparatorPattern.split(s)
+
+	/** Move the provided files to a temporary location.
+	*   If 'f' returns normally, delete the files.
+	*   If 'f' throws an Exception, return the files to their original location.*/
+	def stash[T](files: Set[File])(f: => T): T =
+		withTemporaryDirectory { dir =>
+			val stashed = stashLocations(dir, files.toArray)
+			move(stashed)
+
+			try { f } catch { case e: Exception =>
+				try { move(stashed.map(_.swap)); throw e }
+				catch { case _: Exception => throw e }
+			}
+		}
+
+	private def stashLocations(dir: File, files: Array[File]) =
+		for( (file, index) <- files.zipWithIndex) yield
+			(file, new File(dir, index.toHexString))
+
+	def move(files: Traversable[(File, File)]): Unit =
+		files.foreach(Function.tupled(move))
+		
+	def move(a: File, b: File): Unit =
+	{
+		if(b.exists)
+			delete(b)
+		if(!a.renameTo(b))
+		{
+			copyFile(a, b, true)
+			delete(a)
+		}
+	}
+
+	def gzipFileOut[T](file: File)(f: OutputStream => T): T =
+		Using.fileOutputStream()(file) { fout =>
+		Using.gzipOutputStream(fout) { outg =>
+		Using.bufferedOutputStream(outg)(f) }}
+
+	def gzipFileIn[T](file: File)(f: InputStream => T): T =
+		Using.fileInputStream(file) { fin =>
+		Using.gzipInputStream(fin) { ing =>
+		Using.bufferedInputStream(ing)(f) }}
+}

Added: incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/IPC.scala
URL: http://svn.apache.org/viewvc/incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/IPC.scala?rev=1063515&view=auto
==============================================================================
--- incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/IPC.scala (added)
+++ incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/IPC.scala Tue Jan 25 23:19:36 2011
@@ -0,0 +1,72 @@
+/* sbt -- Simple Build Tool
+ * Copyright 2009 Mark Harrah
+ */
+package xsbt
+
+import java.io.{BufferedReader, BufferedWriter, InputStream, InputStreamReader, OutputStreamWriter, OutputStream}
+import java.net.{InetAddress, ServerSocket, Socket}
+
+object IPC
+{
+	private val portMin = 1025
+	private val portMax = 65536
+	private val loopback = InetAddress.getByName(null) // loopback
+	
+	def client[T](port: Int)(f: IPC => T): T =
+		ipc(new Socket(loopback, port))(f)
+	
+	def pullServer[T](f: Server => T): T =
+	{
+		val server = makeServer
+		try { f(new Server(server)) } 
+		finally { server.close() }
+	}
+	def makeServer: ServerSocket =
+	{
+		val random = new java.util.Random
+		def nextPort = random.nextInt(portMax - portMin + 1) + portMin
+		def createServer(attempts: Int): ServerSocket =
+			if(attempts > 0)
+				try { new ServerSocket(nextPort, 1, loopback) }
+				catch { case _: Exception => createServer(attempts - 1) }
+			else
+				error("Could not connect to socket: maximum attempts exceeded")
+		createServer(10)
+	}
+	def server[T](f: IPC => Option[T]): T = serverImpl(makeServer, f)
+	def server[T](port: Int)(f: IPC => Option[T]): T =
+		serverImpl(new ServerSocket(port, 1, loopback), f)
+	private def serverImpl[T](server: ServerSocket, f: IPC => Option[T]): T =
+	{
+		def listen(): T =
+		{
+			ipc(server.accept())(f) match
+			{
+				case Some(done) => done
+				case None => listen()
+			}
+		}
+		
+		try { listen() }
+		finally { server.close() }
+	}
+	private def ipc[T](s: Socket)(f: IPC => T): T =
+		try { f(new IPC(s)) }
+		finally { s.close() }
+		
+	final class Server private[IPC](s: ServerSocket) extends NotNull
+	{
+		def port = s.getLocalPort
+		def close() = s.close()
+		def connection[T](f: IPC => T): T = IPC.ipc(s.accept())(f)
+	}
+}
+final class IPC private(s: Socket) extends NotNull
+{
+	def port = s.getLocalPort
+	private val in = new BufferedReader(new InputStreamReader(s.getInputStream))
+	private val out = new BufferedWriter(new OutputStreamWriter(s.getOutputStream))
+	
+	def send(s: String) = { out.write(s); out.newLine(); out.flush() }
+	def receive: String = in.readLine()
+}
\ No newline at end of file

Added: incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/NameFilter.scala
URL: http://svn.apache.org/viewvc/incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/NameFilter.scala?rev=1063515&view=auto
==============================================================================
--- incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/NameFilter.scala (added)
+++ incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/NameFilter.scala Tue Jan 25 23:19:36 2011
@@ -0,0 +1,72 @@
+/* sbt -- Simple Build Tool
+ * Copyright 2008, 2009  Mark Harrah
+ */
+package sbt
+
+import java.io.File
+import java.util.regex.Pattern
+
+trait FileFilter extends java.io.FileFilter with NotNull
+{
+	def || (filter: FileFilter): FileFilter = new SimpleFileFilter( file => accept(file) || filter.accept(file) )
+	def && (filter: FileFilter): FileFilter = new SimpleFileFilter( file => accept(file) && filter.accept(file) )
+	def -- (filter: FileFilter): FileFilter = new SimpleFileFilter( file => accept(file) && !filter.accept(file) )
+	def unary_- : FileFilter = new SimpleFileFilter( file => !accept(file) )
+}
+trait NameFilter extends FileFilter with NotNull
+{
+	def accept(name: String): Boolean
+	final def accept(file: File): Boolean = accept(file.getName)
+	def | (filter: NameFilter): NameFilter = new SimpleFilter( name => accept(name) || filter.accept(name) )
+	def & (filter: NameFilter): NameFilter = new SimpleFilter( name => accept(name) && filter.accept(name) )
+	def - (filter: NameFilter): NameFilter = new SimpleFilter( name => accept(name) && !filter.accept(name) )
+	override def unary_- : NameFilter = new SimpleFilter( name => !accept(name) )
+}
+object HiddenFileFilter extends FileFilter {
+	def accept(file: File) = file.isHidden && file.getName != "."
+}
+object ExistsFileFilter extends FileFilter {
+	def accept(file: File) = file.exists
+}
+object DirectoryFilter extends FileFilter {
+	def accept(file: File) = file.isDirectory
+}
+class SimpleFileFilter(val acceptFunction: File => Boolean) extends FileFilter
+{
+	def accept(file: File) = acceptFunction(file)
+}
+class ExactFilter(val matchName: String) extends NameFilter
+{
+	def accept(name: String) = matchName == name
+}
+class SimpleFilter(val acceptFunction: String => Boolean) extends NameFilter
+{
+	def accept(name: String) = acceptFunction(name)
+}
+class PatternFilter(val pattern: Pattern) extends NameFilter
+{
+	def accept(name: String) = pattern.matcher(name).matches
+}
+object AllPassFilter extends NameFilter
+{
+	def accept(name: String) = true
+}
+object NothingFilter extends NameFilter
+{
+	def accept(name: String) = false
+}
+
+object GlobFilter
+{
+	implicit def apply(expression: String): NameFilter =
+	{
+		require(!expression.exists(java.lang.Character.isISOControl), "Control characters not allowed in filter expression.")
+		if(expression == "*")
+			AllPassFilter
+		else if(expression.indexOf('*') < 0) // includes case where expression is empty
+			new ExactFilter(expression)
+		else
+			new PatternFilter(Pattern.compile(expression.split("\\*", -1).map(quote).mkString(".*")))
+	}
+	private def quote(s: String) = if(s.isEmpty) "" else Pattern.quote(s.replaceAll("\n", """\n"""))
+}
\ No newline at end of file

Added: incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/Pack.scala
URL: http://svn.apache.org/viewvc/incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/Pack.scala?rev=1063515&view=auto
==============================================================================
--- incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/Pack.scala (added)
+++ incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/Pack.scala Tue Jan 25 23:19:36 2011
@@ -0,0 +1,79 @@
+/* sbt -- Simple Build Tool
+ * Copyright 2009 Mark Harrah
+ */
+package sbt
+
+import java.io.{File, FileOutputStream}
+import java.util.jar.{JarEntry, JarFile, JarOutputStream, Pack200}
+import IO._
+
+object Pack
+{
+	def pack(jarPath: File, out: File): Unit = pack(jarPath, out, defaultPackerOptions)
+	def pack(jarPath: File, out: File, options: Iterable[(String, String)])
+	{
+		val packer = Pack200.newPacker
+		import collection.JavaConversions._
+		packer.properties ++= options
+		 
+		Using.jarFile(false)(jarPath) { f =>
+			Using.fileOutputStream()(out) { stream =>
+				packer.pack(f, stream)
+			}
+		}
+	}
+	def unpack(packedPath: File, toJarPath: File)
+	{
+		val unpacker = Pack200.newUnpacker
+		Using.fileOutputStream()(toJarPath) { fileStream =>
+			Using.jarOutputStream(fileStream) { jarOut =>
+				unpacker.unpack(packedPath, jarOut)
+			}
+		}
+	}
+	def defaultPackerOptions = scala.collection.immutable.Map()
+}
+
+import java.net.URL
+/** This is somewhat of a mess and is not entirely correct.  jarsigner doesn't work properly
+* on scalaz and it is difficult to determine whether a jar is both signed and valid.  */
+object SignJar
+{
+	final class SignOption private[SignJar](val toList: List[String], val signOnly: Boolean)
+	{
+		override def toString = toList.mkString(" ")
+	}
+	def keyStore(url: URL) = new SignOption("-keystore" :: url.toExternalForm :: Nil, true)
+	def signedJar(p: Path) = new SignOption("-signedjar" :: p.asFile.getAbsolutePath :: Nil, true)
+	def verbose = new SignOption("-verbose" :: Nil, false)
+	def sigFile(name: String) = new SignOption("-sigfile" :: name :: Nil, true)
+	def storeType(t: String) = new SignOption("-storetype" :: t :: Nil, false)
+	def provider(p: String) = new SignOption("-provider" :: p :: Nil, false)
+	def providerName(p: String) = new SignOption("-providerName" :: p :: Nil, false)
+	def storePassword(p: String) = new SignOption("-storepass" :: p :: Nil, true)
+	def keyPassword(p: String) = new SignOption("-keypass" :: p :: Nil, true)
+	
+	private def VerifyOption = "-verify"
+	
+	/** Uses jarsigner to sign the given jar.  */
+	def sign(jarPath: File, alias: String, options: Seq[SignOption])(fork: (String, List[String]) => Int)
+	{
+		require(!alias.trim.isEmpty, "Alias cannot be empty")
+		val arguments = options.toList.flatMap(_.toList) ::: jarPath.getAbsolutePath :: alias :: Nil
+		execute("signing", arguments)(fork)
+	}
+	/** Uses jarsigner to verify the given jar.*/
+	def verify(jarPath: File, options: Seq[SignOption])(fork: (String, List[String]) => Int)
+	{
+		val arguments = options.filter(!_.signOnly).toList.flatMap(_.toList) ::: VerifyOption :: jarPath.getAbsolutePath :: Nil
+		execute("verifying", arguments)(fork)
+	}
+	private def execute(action: String, arguments: List[String])(fork: (String, List[String]) => Int)
+	{
+		val exitCode = fork(CommandName, arguments)
+		if(exitCode != 0)
+			error("Error " + action + " jar (exit code was " + exitCode + ".)")
+	}
+	
+	private val CommandName = "jarsigner"
+}
\ No newline at end of file

Added: incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/Path.scala
URL: http://svn.apache.org/viewvc/incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/Path.scala?rev=1063515&view=auto
==============================================================================
--- incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/Path.scala (added)
+++ incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/Path.scala Tue Jan 25 23:19:36 2011
@@ -0,0 +1,420 @@
+/* sbt -- Simple Build Tool
+ * Copyright 2008, 2009, 2010  Mark Harrah
+ */
+package sbt
+
+import Path._
+import IO.{pathSplit, wrapNull}
+import java.io.File
+import java.net.URL
+import scala.collection.{generic, immutable, mutable, TraversableLike}
+
+/** A Path represents a file in a project.
+* @see sbt.PathFinder*/
+sealed abstract class Path extends PathFinder
+{
+	/** Creates a base directory for this path.  This is used by copy and zip functions
+	* to determine the relative path that should be used in the destination.  For example,
+	* if the following path is specified to be copied to directory 'd',
+	* 
+	* <code>((a / b) ###) / x / y</code>
+	*
+	* the copied path would be 
+	*
+	* <code>d / x / y</code>
+	*
+	* The <code>relativePath</code> method is used to return the relative path to the base directory. */
+	override def ### : Path = new BaseDirectory(this)
+	private[sbt] def addTo(pathSet: mutable.Set[Path])
+	{
+		if(asFile.exists)
+			pathSet += this
+	}
+	override def / (component: String): Path = if(component == ".") this else new RelativePath(this, component)
+	/** True if and only if the file represented by this path exists.*/
+	def exists = asFile.exists
+	/** True if and only if the file represented by this path is a directory.*/
+	def isDirectory = asFile.isDirectory
+	/** The last modified time of the file represented by this path.*/
+	def lastModified = asFile.lastModified
+	/* True if and only if file that this path represents exists and the file represented by the path 'p'
+	* does not exist or was modified before the file for this path.*/
+	def newerThan(p: Path): Boolean = exists && (!p.exists || lastModified > p.lastModified)
+	/* True if and only if file that this path represents does not exist or the file represented by the path 'p'
+	* exists and was modified after the file for this path.*/
+	def olderThan(p: Path): Boolean = p newerThan this
+	/** The file represented by this path.*/
+	def asFile: File
+	/** The file represented by this path converted to a <code>URL</code>.*/
+	def asURL = asFile.toURI.toURL
+	/** The string representation of this path relative to the base directory.  The project directory is the
+	* default base directory if one is not specified explicitly using the <code>###</code> operator.*/
+	lazy val relativePath: String = relativePathString(sep.toString)
+	def relativePathString(separator: String): String
+	final def projectRelativePath: String = projectRelativePathString(sep.toString)
+	def projectRelativePathString(separator: String): String
+	def absolutePath: String = asFile.getAbsolutePath
+	private[sbt] def prependTo(s: String): String
+	
+	/** The last component of this path.*/
+	def name = asFile.getName
+	/** The extension part of the name of this path.  This is the part of the name after the last period, or the empty string if there is no period.*/
+	def ext = baseAndExt._2
+	/** The base of the name of this path.  This is the part of the name before the last period, or the full name if there is no period.*/
+	def base = baseAndExt._1
+	def baseAndExt: (String, String) =
+	{
+		val nme = name
+		val dot = nme.lastIndexOf('.')
+		if(dot < 0) (nme, "") else (nme.substring(0, dot), nme.substring(dot+1))
+	}
+	
+	/** Equality of Paths is defined in terms of the underlying <code>File</code>.*/
+	override final def equals(other: Any) =
+		other match
+		{
+			case op: Path => asFile == op.asFile
+			case _ => false
+		}
+	/** The hash code of a Path is that of the underlying <code>File</code>.*/
+	override final def hashCode = asFile.hashCode
+}
+private final class BaseDirectory(private[sbt] val path: Path) extends Path
+{
+	override def ### : Path = this
+	override def toString = path.toString
+	def asFile = path.asFile
+	def relativePathString(separator: String) = ""
+	def projectRelativePathString(separator: String) = path.projectRelativePathString(separator)
+	private[sbt] def prependTo(s: String) = "." + sep + s
+}
+private[sbt] final class FilePath(file: File) extends Path
+{
+	lazy val asFile = absolute(file)
+	override def toString = absolutePath
+	def relativePathString(separator: String) = asFile.getName
+	def projectRelativePathString(separator: String) = relativePathString(separator)
+	private[sbt] def prependTo(s: String) = absolutePath + sep + s
+}
+// toRoot is the path between this and the root project path and is used for toString
+private[sbt] final class ProjectDirectory(file: File, toRoot: Option[Path]) extends Path
+{
+	def this(file: File) = this(file, None)
+	lazy val asFile = absolute(file)
+	override def toString = foldToRoot(_.toString, ".")
+	def relativePathString(separator: String) = ""
+	def projectRelativePathString(separator: String) = ""
+	private[sbt] def prependTo(s: String) = foldToRoot(_.prependTo(s), "." + sep + s)
+	private[sbt] def foldToRoot[T](f: Path => T, orElse: T) = toRoot.map(f).getOrElse(orElse)
+}
+private[sbt] final class RelativePath(val parentPath: Path, val component: String) extends Path
+{
+	checkComponent(component)
+	override def toString = parentPath prependTo component
+	lazy val asFile = new File(parentPath.asFile, component)
+	private[sbt] def prependTo(s: String) =  parentPath prependTo (component + sep + s)
+	def relativePathString(separator: String) = relative(parentPath.relativePathString(separator), separator)
+	def projectRelativePathString(separator: String) = relative(parentPath.projectRelativePathString(separator), separator)
+	private def relative(parentRelative: String, separator: String) =
+	{
+		if(parentRelative.isEmpty)
+			component
+		else
+			parentRelative + separator + component
+	}
+}
+	import java.io.File
+	import File.pathSeparator
+trait PathExtra extends Alternatives with Mapper
+{
+	implicit def fileToPath(file: File): Path = Path.fromFile(file)
+	implicit def pathToFile(path: Path): File = path.asFile
+	implicit def pathsToFiles[CC[X] <: TraversableLike[X,CC[X]]](cc: CC[Path])(implicit cb: generic.CanBuildFrom[CC[Path], File, CC[File]]): CC[File] =
+		cc.map(_.asFile)
+	implicit def filesToPaths[CC[X] <: TraversableLike[X,CC[X]]](cc: CC[File])(implicit cb: generic.CanBuildFrom[CC[File], Path, CC[Path]]): CC[Path] =
+		cc.map(fileToPath)
+	implicit def filesToFinder(cc: Traversable[File]): PathFinder = finder(cc)
+	implicit def pathsToFinder(cc: Traversable[Path]): PathFinder = lazyPathFinder(cc)
+}
+object Path extends PathExtra
+{
+	def fileProperty(name: String) = Path.fromFile(System.getProperty(name))
+	def userHome = fileProperty("user.home")
+	
+	def absolute(file: File) = new File(file.toURI.normalize).getAbsoluteFile
+	/** Constructs a String representation of <code>Path</code>s.  The absolute path String of each <code>Path</code> is
+	* separated by the platform's path separator.*/
+	def makeString(paths: Iterable[Path]): String = makeString(paths, pathSeparator)
+	/** Constructs a String representation of <code>Path</code>s.  The absolute path String of each <code>Path</code> is
+	* separated by the given separator String.*/
+	def makeString(paths: Iterable[Path], sep: String): String = paths.map(_.absolutePath).mkString(sep)
+
+	def makeString(paths: Seq[File]): String = makeString(paths, pathSeparator)
+	def makeString(paths: Seq[File], sep: String): String = paths.map(_.getAbsolutePath).mkString(sep)
+	
+	/** Constructs a String representation of <code>Path</code>s.  The relative path String of each <code>Path</code> is
+	* separated by the platform's path separator.*/
+	def makeRelativeString(paths: Iterable[Path]): String = paths.map(_.relativePathString(sep.toString)).mkString(pathSeparator)
+	
+	def splitString(projectPath: Path, value: String): Iterable[Path] =
+	{
+		for(pathString <- pathSplit(value) if pathString.length > 0) yield
+			Path.fromString(projectPath, pathString)
+	}
+	
+	/** A <code>PathFinder</code> that always produces the empty set of <code>Path</code>s.*/
+	def emptyPathFinder =
+		new PathFinder
+		{
+			private[sbt] def addTo(pathSet: mutable.Set[Path]) {}
+		}
+	/** A <code>PathFinder</code> that selects the paths provided by the <code>paths</code> argument, which is
+	* reevaluated on each call to the <code>PathFinder</code>'s <code>get</code> method.  */
+	def lazyPathFinder(paths: => Traversable[Path]): PathFinder =
+		new PathFinder
+		{
+			private[sbt] def addTo(pathSet: mutable.Set[Path]) = pathSet ++= paths
+		}
+	def finder(files: => Traversable[File]): PathFinder =  lazyPathFinder { fromFiles(files) }
+		
+	/** The separator character of the platform.*/
+	val sep = java.io.File.separatorChar
+	
+	/** Checks the string to verify that it is a legal path component.  The string must be non-empty,
+	* not a slash, and not '.' or '..'.*/
+	def checkComponent(c: String): String =
+	{
+		require(c.length > 0, "Path component must not be empty")
+		require(c.indexOf('/') == -1, "Path component '" + c + "' must not have forward slashes in it")
+		require(c.indexOf('\\') == -1, "Path component '" + c + "' must not have backslashes in it")
+		require(c != "..", "Path component cannot be '..'")
+		require(c != ".", "Path component cannot be '.'")
+		c
+	}
+	/** Converts a path string relative to the given base path to a <code>Path</code>. */
+	def fromString(basePath: Path, value: String): Path =
+	{
+		if(value.isEmpty)
+			basePath
+		else
+		{
+			val f = new File(value)
+			if(f.isAbsolute)
+				fromFile(f)
+			else
+			{
+				val components = value.split("""[/\\]""")
+				(basePath /: components)( (path, component) => path / component )
+			}
+		}
+	}
+	def baseAncestor(path: Path): Option[Path] =
+		path match
+		{
+			case pd: ProjectDirectory => None
+			case fp: FilePath => None
+			case rp: RelativePath => baseAncestor(rp.parentPath)
+			case b: BaseDirectory => Some(b.path)
+		}
+	
+	def relativize(basePath: Path, path: Path): Option[Path] = relativize(basePath, path.asFile)
+	def relativize(basePath: Path, file: File): Option[Path] =
+		basePathString(basePath) flatMap { baseString => relativize(basePath, baseString, file) }
+	def relativize(basePath: Path, basePathString: String, file: File): Option[Path] =
+	{
+		val pathString = file.getAbsolutePath
+		if(pathString.startsWith(basePathString))
+			Some(fromString(basePath, pathString.substring(basePathString.length)))
+		else
+			None
+	}
+	def relativizeFile(baseFile: File, file: File): Option[File] = relativize(baseFile, file).map { path => new File(path) }
+	private[sbt] def relativize(baseFile: File, file: File): Option[String] =
+	{
+		val pathString = file.getAbsolutePath
+		baseFileString(baseFile) flatMap
+		{
+			baseString =>
+			{
+				if(pathString.startsWith(baseString))
+					Some(pathString.substring(baseString.length))
+				else
+					None
+			}
+		}
+	}
+	private[sbt] def basePathString(basePath: Path): Option[String] = baseFileString(basePath.asFile)
+	private def baseFileString(baseFile: File): Option[String] =
+	{
+		if(baseFile.isDirectory)
+		{
+			val cp = baseFile.getAbsolutePath
+			assert(cp.length > 0)
+			if(cp.charAt(cp.length - 1) == File.separatorChar)
+				Some(cp)
+			else
+				Some(cp + File.separatorChar)
+		}
+		else
+			None
+	}
+	def fromFile(file: String): Path = fromFile(new File(file))
+	def fromFile(file: File): Path = new FilePath(file)
+	import collection.generic.{CanBuildFrom, FilterMonadic}
+	def fromFiles[Repr, That](files: FilterMonadic[File, Repr])(implicit bf: CanBuildFrom[Repr, Path, That]): That =  files.map(fromFile)
+
+	def getFiles(files: Traversable[Path]): immutable.Set[File] = files.map(_.asFile).toSet
+	def getURLs(files: Traversable[Path]): Array[URL] = files.map(_.asURL).toArray
+
+	def toURLs(files: Seq[File]): Array[URL] = files.map(_.toURI.toURL).toArray
+}
+
+/** A path finder constructs a set of paths.  The set is evaluated by a call to the <code>get</code>
+* method.  The set will be different for different calls to <code>get</code> if the underlying filesystem
+* has changed.*/
+sealed abstract class PathFinder extends NotNull
+{
+	/** The union of the paths found by this <code>PathFinder</code> with the paths found by 'paths'.*/
+	def +++(paths: PathFinder): PathFinder = new Paths(this, paths)
+	/** Excludes all paths from <code>excludePaths</code> from the paths selected by this <code>PathFinder</code>.*/
+	def ---(excludePaths: PathFinder): PathFinder = new ExcludePaths(this, excludePaths)
+	/** Constructs a new finder that selects all paths with a name that matches <code>filter</code> and are
+	* descendents of paths selected by this finder.*/
+	def **(filter: FileFilter): PathFinder = new DescendentOrSelfPathFinder(this, filter)
+	def *** : PathFinder = **(AllPassFilter)
+	/** Constructs a new finder that selects all paths with a name that matches <code>filter</code> and are
+	* immediate children of paths selected by this finder.*/
+	def *(filter: FileFilter): PathFinder = new ChildPathFinder(this, filter)
+	/** Constructs a new finder that selects all paths with name <code>literal</code> that are immediate children
+	* of paths selected by this finder.*/
+	def / (literal: String): PathFinder = new ChildPathFinder(this, new ExactFilter(literal))
+	/** Constructs a new finder that selects all paths with name <code>literal</code> that are immediate children
+	* of paths selected by this finder.*/
+	final def \ (literal: String): PathFinder = this / literal
+
+	/** Makes the paths selected by this finder into base directories.
+	* @see Path.###
+	*/
+	def ### : PathFinder = new BasePathFinder(this)
+
+	def x_![T](mapper: File => Option[T]): Traversable[(File,T)] = x(mapper, false)
+	/** Applies `mapper` to each path selected by this PathFinder and returns the path paired with the non-empty result.
+	* If the result is empty (None) and `errorIfNone` is true, an exception is thrown.
+	* If `errorIfNone` is false, the path is dropped from the returned Traversable.*/
+	def x[T](mapper: File => Option[T], errorIfNone: Boolean = true): Traversable[(File,T)] =
+	{
+		val apply = if(errorIfNone) mapper | fail else mapper
+		for(file <- getFiles; mapped <- apply(file)) yield (file, mapped)
+	}
+	/** Pairs each path selected by this PathFinder with its relativePath.*/
+	def xx: Traversable[(File, String)] = get.map(path => (path.asFile, path.relativePath))
+
+	/** Selects all descendent paths with a name that matches <code>include</code> and do not have an intermediate
+	* path with a name that matches <code>intermediateExclude</code>.  Typical usage is:
+	*
+	* <code>descendentsExcept("*.jar", ".svn")</code>*/
+	def descendentsExcept(include: FileFilter, intermediateExclude: FileFilter): PathFinder =
+		(this ** include) --- (this ** intermediateExclude ** include)
+	
+	/** Evaluates this finder.  The set returned by this method will reflect the underlying filesystem at the
+	* time of calling.  If the filesystem changes, two calls to this method might be different.*/
+	final def get: immutable.Set[Path] =
+	{
+		val pathSet = new mutable.HashSet[Path]
+		addTo(pathSet)
+		pathSet.toSet
+	}
+	/** Only keeps paths for which `f` returns true.  It is non-strict, so it is not evaluated until the returned finder is evaluated.*/
+	final def filter(f: Path => Boolean): PathFinder = Path.lazyPathFinder(get.filter(f))
+	/* Non-strict flatMap: no evaluation occurs until the returned finder is evaluated.*/
+	final def flatMap(f: Path => PathFinder): PathFinder = Path.lazyPathFinder(get.flatMap(p => f(p).get))
+	/** Evaluates this finder and converts the results to an `Array` of `URL`s..*/
+	final def getURLs: Array[URL] = Path.getURLs(get)
+	/** Evaluates this finder and converts the results to a `Set` of `File`s.*/
+	final def getFiles: immutable.Set[File] = Path.getFiles(get)
+	/** Evaluates this finder and converts the results to a `Set` of absolute path strings.*/
+	final def getPaths: immutable.Set[String] = strictMap(_.absolutePath)
+	/** Evaluates this finder and converts the results to a `Set` of relative path strings.*/
+	final def getRelativePaths: immutable.Set[String] = strictMap(_.relativePath)
+	final def strictMap[T](f: Path => T): immutable.Set[T] = get.map(f).toSet
+	private[sbt] def addTo(pathSet: mutable.Set[Path])
+
+	/** Create a PathFinder from this one where each path has a unique name.
+	* A single path is arbitrarily selected from the set of paths with the same name.*/
+	def distinct: PathFinder = Path.lazyPathFinder((Map() ++ get.map(p => (p.asFile.getName, p))) .values.toList )
+
+	/** Constructs a string by evaluating this finder, converting the resulting Paths to absolute path strings, and joining them with the platform path separator.*/
+	final def absString = Path.makeString(get)
+	/** Constructs a string by evaluating this finder, converting the resulting Paths to relative path strings, and joining them with the platform path separator.*/
+	final def relativeString = Path.makeRelativeString(get)
+	/** Constructs a debugging string for this finder by evaluating it and separating paths by newlines.*/
+	override def toString = get.mkString("\n   ", "\n   ","")
+}
+private class BasePathFinder(base: PathFinder) extends PathFinder
+{
+	private[sbt] def addTo(pathSet: mutable.Set[Path])
+	{
+		for(path <- base.get)
+			pathSet += (path ###)
+	}
+}
+private abstract class FilterPath extends PathFinder with FileFilter
+{
+	def parent: PathFinder
+	def filter: FileFilter
+	final def accept(file: File) = filter.accept(file)
+	
+	protected def handlePath(path: Path, pathSet: mutable.Set[Path])
+	{
+		for(matchedFile <- wrapNull(path.asFile.listFiles(this)))
+			pathSet += path / matchedFile.getName
+	}
+}
+private class DescendentOrSelfPathFinder(val parent: PathFinder, val filter: FileFilter) extends FilterPath
+{
+	private[sbt] def addTo(pathSet: mutable.Set[Path])
+	{
+		for(path <- parent.get)
+		{
+			if(accept(path.asFile))
+				pathSet += path
+			handlePathDescendent(path, pathSet)
+		}
+	}
+	private def handlePathDescendent(path: Path, pathSet: mutable.Set[Path])
+	{
+		handlePath(path, pathSet)
+		for(childDirectory <- wrapNull(path.asFile.listFiles(DirectoryFilter)))
+			handlePathDescendent(path / childDirectory.getName, pathSet)
+	}
+}
+private class ChildPathFinder(val parent: PathFinder, val filter: FileFilter) extends FilterPath
+{
+	private[sbt] def addTo(pathSet: mutable.Set[Path])
+	{
+		for(path <- parent.get)
+			handlePath(path, pathSet)
+	}
+}
+private class Paths(a: PathFinder, b: PathFinder) extends PathFinder
+{
+	private[sbt] def addTo(pathSet: mutable.Set[Path])
+	{
+		a.addTo(pathSet)
+		b.addTo(pathSet)
+	}
+}
+private class ExcludePaths(include: PathFinder, exclude: PathFinder) extends PathFinder
+{
+	private[sbt] def addTo(pathSet: mutable.Set[Path])
+	{
+		val includeSet = new mutable.HashSet[Path]
+		include.addTo(includeSet)
+		
+		val excludeSet = new mutable.HashSet[Path]
+		exclude.addTo(excludeSet)
+		
+		includeSet --= excludeSet
+		pathSet ++= includeSet
+	}
+}
\ No newline at end of file

Added: incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/PathMapper.scala
URL: http://svn.apache.org/viewvc/incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/PathMapper.scala?rev=1063515&view=auto
==============================================================================
--- incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/PathMapper.scala (added)
+++ incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/PathMapper.scala Tue Jan 25 23:19:36 2011
@@ -0,0 +1,60 @@
+/* sbt -- Simple Build Tool
+ * Copyright 2008, 2009  Mark Harrah
+ */
+package sbt
+
+	import java.io.File
+
+trait Mapper
+{
+	type PathMap = File => Option[String]
+	type FileMap = File => Option[File]
+
+	val basic: PathMap = f => Some(f.getPath)
+	def relativeTo(base: File): PathMap = IO.relativize(base, _)
+	def rebase(oldBase: File, newBase0: String): PathMap =
+	{
+		val newBase = normalizeBase(newBase0)
+		(file: File) =>
+			if(file == oldBase)
+				Some( if(newBase.isEmpty) "." else newBase )
+			else
+				IO.relativize(oldBase, file).map(newBase + _)
+	}
+	def fail: Any => Nothing = f => error("No mapping for " + f)
+	val flat: PathMap = f => Some(f.getName)
+	def flatRebase(newBase0: String): PathMap =
+	{
+		val newBase = normalizeBase(newBase0)
+		f => Some(newBase + f.getName)
+	}
+	def some[A,B](f: A => B): A => Some[B] = x => Some(f(x))
+
+	def normalizeBase(base: String) = if(!base.isEmpty && !base.endsWith("/"))  base + "/" else base
+
+	def abs: FileMap = f => Some(f.getAbsoluteFile)
+	def resolve(newDirectory: File): FileMap = file => Some(new File(newDirectory, file.getPath))
+	def rebase(oldBase: File, newBase: File): FileMap =
+		file =>
+			if(file == oldBase)
+				Some(newBase)
+			else
+				IO.relativize(oldBase, file) map { r => new File(newBase, r) }
+
+	def flat(newDirectory: File): FileMap = file => Some(new File(newDirectory, file.getName))
+}
+
+trait Alternative[A,B] { def | (g: A => Option[B]): A => Option[B] }
+trait Alternatives
+{
+	implicit def alternative[A,B](f:A => Option[B]): Alternative[A,B] =
+		new Alternative[A,B] { def | (g: A => Option[B]) =
+			(a: A) => f(a) orElse g(a)
+		}
+	final def alternatives[A,B](alts: Seq[A => Option[B]]): A => Option[B] =
+		alts match
+		{
+			case Seq(f, fs @ _*) => f | alternatives(fs)
+			case Seq() => a => None
+		}
+}
\ No newline at end of file

Added: incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/Resources.scala
URL: http://svn.apache.org/viewvc/incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/Resources.scala?rev=1063515&view=auto
==============================================================================
--- incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/Resources.scala (added)
+++ incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/Resources.scala Tue Jan 25 23:19:36 2011
@@ -0,0 +1,67 @@
+/* sbt -- Simple Build Tool
+ * Copyright 2008, 2009  Mark Harrah
+ */
+package sbt
+
+import java.io.File
+import IO._
+import Resources.error
+
+object Resources
+{
+	def apply(basePath: String) =
+	{
+		require(basePath.startsWith("/"))
+		val resource = getClass.getResource(basePath)
+		if(resource == null)
+			error("Resource base directory '" + basePath + "' not on classpath.")
+		else
+		{
+			val file = toFile(resource)
+			if(file.exists)
+				new Resources(file)
+			else
+				error("Resource base directory '" + basePath + "' does not exist.")
+		}
+	}
+	def error(msg: String) = throw new ResourcesException(msg)
+	private val LoadErrorPrefix = "Error loading initial project: "
+}
+class ResourcesException(msg: String) extends Exception(msg)
+
+class Resources(val baseDirectory: File)
+{
+	import Resources._
+	// The returned directory is not actually read-only, but it should be treated that way
+	def readOnlyResourceDirectory(group: String, name: String): File =
+	{
+		val groupDirectory = new File(baseDirectory, group)
+		if(groupDirectory.isDirectory)
+		{
+			val resourceDirectory = new File(groupDirectory, name)
+			if(resourceDirectory.isDirectory)
+				resourceDirectory
+			else
+				error("Resource directory '" + name + "' in group '" + group + "' not found.")
+		}
+		else
+			error("Group '" + group + "' not found.")
+	}
+	def readWriteResourceDirectory[T](group: String, name: String)(withDirectory: File => T): T =
+	{
+		val file = readOnlyResourceDirectory(group, name)
+		readWriteResourceDirectory(file)(withDirectory)
+	}
+
+	def readWriteResourceDirectory[T](readOnly: File)(withDirectory: File => T): T =
+	{
+		require(readOnly.isDirectory)
+		def readWrite(readOnly: File)(temporary: File): T =
+		{
+			val readWriteDirectory = new File(temporary, readOnly.getName)
+			copyDirectory(readOnly, readWriteDirectory)
+			withDirectory(readWriteDirectory)
+		}
+		withTemporaryDirectory(readWrite(readOnly))
+	}
+}
\ No newline at end of file

Added: incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/SourceModificationWatch.scala
URL: http://svn.apache.org/viewvc/incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/SourceModificationWatch.scala?rev=1063515&view=auto
==============================================================================
--- incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/SourceModificationWatch.scala (added)
+++ incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/SourceModificationWatch.scala Tue Jan 25 23:19:36 2011
@@ -0,0 +1,48 @@
+/* sbt -- Simple Build Tool
+ * Copyright 2009, 2010  Mikko Peltonen, Stuart Roebuck, Mark Harrah
+ */
+package sbt
+
+	import annotation.tailrec
+
+object SourceModificationWatch
+{
+	@tailrec def watch(sourcesFinder: PathFinder, pollDelaySec: Int, state: WatchState)(terminationCondition: => Boolean): (Boolean, WatchState) =
+	{
+			import state._
+
+		def sourceFiles: Iterable[java.io.File] = sourcesFinder.getFiles
+		val (lastModifiedTime, fileCount) =
+			( (0L, 0) /: sourceFiles) {(acc, file) => /*println("processing "+file);*/ (math.max(acc._1, file.lastModified), acc._2 + 1)}
+
+		//println("lastModifiedTime:"+new java.util.Date(lastModifiedTime))
+		//println("lastModifiedTime - lastCallbackCallTime"+(lastModifiedTime - lastCallbackCallTime))
+		val sourcesModified =
+			lastModifiedTime > lastCallbackCallTime ||
+			previousFileCount != fileCount
+
+		val (triggered, newCallbackCallTime) =
+			if (sourcesModified) {
+				(false, System.currentTimeMillis)
+			}
+			else
+				(awaitingQuietPeriod, lastCallbackCallTime)
+
+		val newState = new WatchState(newCallbackCallTime, fileCount, sourcesModified, if(triggered) count + 1 else count)
+		if(triggered)
+			(true, newState)
+		else
+		{
+			Thread.sleep(pollDelaySec * 1000)
+			if(terminationCondition)
+				(false, newState)
+			else
+				watch(sourcesFinder, pollDelaySec, newState)(terminationCondition)
+		}
+	}
+}
+final class WatchState(val lastCallbackCallTime: Long, val previousFileCount: Int, val awaitingQuietPeriod:Boolean, val count: Int)
+object WatchState
+{
+	def empty = new WatchState(0L, 0, false, 0)
+}
\ No newline at end of file

Added: incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/Using.scala
URL: http://svn.apache.org/viewvc/incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/Using.scala?rev=1063515&view=auto
==============================================================================
--- incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/Using.scala (added)
+++ incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.sourcebundle/src/main/scala/sbt/io/Using.scala Tue Jan 25 23:19:36 2011
@@ -0,0 +1,98 @@
+/* sbt -- Simple Build Tool
+ * Copyright 2008, 2009 Mark Harrah
+ */
+package sbt
+
+import java.io.{Closeable, File, FileInputStream, FileOutputStream, InputStream, OutputStream}
+import java.io.{BufferedInputStream, BufferedOutputStream, ByteArrayOutputStream, InputStreamReader, OutputStreamWriter}
+import java.io.{BufferedReader, BufferedWriter, FileReader, FileWriter, Reader, Writer}
+import java.util.zip.{GZIPInputStream, GZIPOutputStream}
+import java.net.{URL, URISyntaxException}
+import java.nio.charset.{Charset, CharsetDecoder, CharsetEncoder}
+import java.nio.channels.FileChannel
+import java.util.jar.{Attributes, JarEntry, JarFile, JarInputStream, JarOutputStream, Manifest}
+import java.util.zip.{GZIPOutputStream, ZipEntry, ZipFile, ZipInputStream, ZipOutputStream}
+
+import ErrorHandling.translate
+import Using._
+
+abstract class Using[Source, T]
+{
+	protected def open(src: Source): T
+	def apply[R](src: Source)(f: T => R): R =
+	{
+		val resource = open(src)
+		try { f(resource) }
+		finally { close(resource) }
+	}
+	protected def close(out: T): Unit
+}
+import scala.reflect.{Manifest => SManifest}
+abstract class WrapUsing[Source, T](implicit srcMf: SManifest[Source], targetMf: SManifest[T]) extends Using[Source, T]
+{
+	protected def label[S](m: SManifest[S]) = m.erasure.getSimpleName
+	protected def openImpl(source: Source): T
+	protected final def open(source: Source): T =
+		translate("Error wrapping " + label(srcMf) + " in " + label(targetMf) + ": ") { openImpl(source) }
+}
+trait OpenFile[T] extends Using[File, T]
+{
+	protected def openImpl(file: File): T
+	protected final def open(file: File): T =
+	{
+		val parent = file.getParentFile
+		if(parent != null)
+			IO.createDirectory(parent)
+		openImpl(file)
+	}
+}
+object Using
+{
+	def wrap[Source, T<: Closeable](openF: Source => T)(implicit srcMf: SManifest[Source], targetMf: SManifest[T]): Using[Source,T] =
+		wrap(openF, closeCloseable)
+	def wrap[Source, T](openF: Source => T, closeF: T => Unit)(implicit srcMf: SManifest[Source], targetMf: SManifest[T]): Using[Source,T] =
+		new WrapUsing[Source, T]
+		{
+			def openImpl(source: Source) = openF(source)
+			def close(t: T) = closeF(t)
+		}
+
+	def resource[Source, T <: Closeable](openF: Source => T): Using[Source,T] =
+		resource(openF, closeCloseable)
+	def resource[Source, T](openF: Source => T, closeF: T => Unit): Using[Source,T] =
+		new Using[Source,T]
+		{
+			def open(s: Source) = openF(s)
+			def close(s: T) = closeF(s)
+		}
+	def file[T <: Closeable](openF: File => T): OpenFile[T] = file(openF, closeCloseable)
+	def file[T](openF: File => T, closeF: T => Unit): OpenFile[T] =
+		new OpenFile[T]
+		{
+			def openImpl(file: File) = openF(file)
+			def close(t: T) = closeF(t)
+		}
+	private def closeCloseable[T <: Closeable]: T => Unit = _.close()
+
+	def bufferedOutputStream = wrap( (out: OutputStream) => new BufferedOutputStream(out) )
+	def bufferedInputStream = wrap( (in: InputStream) => new BufferedInputStream(in) )
+	def fileOutputStream(append: Boolean = false) = file(f => new BufferedOutputStream(new FileOutputStream(f, append)))
+	def fileInputStream = file(f => new BufferedInputStream(new FileInputStream(f)))
+	def urlInputStream = resource( (u: URL) => translate("Error opening " + u + ": ")(u.openStream))
+	def fileOutputChannel = file(f => new FileOutputStream(f).getChannel)
+	def fileInputChannel = file(f => new FileInputStream(f).getChannel)
+	def fileWriter(charset: Charset = IO.utf8, append: Boolean = false) =
+		file(f => new BufferedWriter(new OutputStreamWriter(new FileOutputStream(f, append), charset)) )
+	def fileReader(charset: Charset) = file(f => new BufferedReader(new InputStreamReader(new FileInputStream(f), charset)) )
+	def jarFile(verify: Boolean) = file(f => new JarFile(f, verify), (_: JarFile).close())
+	def zipFile = file(f => new ZipFile(f), (_: ZipFile).close())
+	def streamReader = wrap{ (_: (InputStream, Charset)) match { case (in, charset) => new InputStreamReader(in, charset) } }
+	def gzipInputStream = wrap( (in: InputStream) => new GZIPInputStream(in, 8192) )
+	def zipInputStream = wrap( (in: InputStream) => new ZipInputStream(in))
+	def zipOutputStream = wrap( (out: OutputStream) => new ZipOutputStream(out))
+	def gzipOutputStream = wrap((out: OutputStream) => new GZIPOutputStream(out, 8192), (_: GZIPOutputStream).finish())
+	def jarOutputStream = wrap( (out: OutputStream) => new JarOutputStream(out))
+	def jarInputStream = wrap( (in: InputStream) => new JarInputStream(in))
+	def zipEntry(zip: ZipFile) = resource( (entry: ZipEntry) =>
+		translate("Error opening " + entry.getName + " in " + zip + ": ") { zip.getInputStream(entry) } )
+}
\ No newline at end of file