diff --git a/compiler/src/dotty/tools/dotc/classpath/DirectoryClassPath.scala b/compiler/src/dotty/tools/dotc/classpath/DirectoryClassPath.scala index 91620151bbb6..5e18859bae8d 100644 --- a/compiler/src/dotty/tools/dotc/classpath/DirectoryClassPath.scala +++ b/compiler/src/dotty/tools/dotc/classpath/DirectoryClassPath.scala @@ -3,14 +3,14 @@ */ package dotty.tools.dotc.classpath -import java.io.File +import java.io.{File => JFile} import java.net.{URI, URL} import java.nio.file.{FileSystems, Files, SimpleFileVisitor} import java.util.function.IntFunction import java.util import java.util.Comparator -import dotty.tools.io.{AbstractFile, PlainFile, ClassPath, ClassRepresentation, PlainNioFile} +import dotty.tools.io.{AbstractFile, PlainFile, ClassPath, ClassRepresentation} import FileUtils._ import scala.collection.JavaConverters._ @@ -86,15 +86,15 @@ trait DirectoryLookup[FileEntryType <: ClassRepresentation] extends ClassPath { } trait JFileDirectoryLookup[FileEntryType <: ClassRepresentation] extends DirectoryLookup[FileEntryType] { - type F = File + type F = JFile - protected def emptyFiles: Array[File] = Array.empty - protected def getSubDir(packageDirName: String): Option[File] = { - val packageDir = new File(dir, packageDirName) + protected def emptyFiles: Array[JFile] = Array.empty + protected def getSubDir(packageDirName: String): Option[JFile] = { + val packageDir = new JFile(dir, packageDirName) if (packageDir.exists && packageDir.isDirectory) Some(packageDir) else None } - protected def listChildren(dir: File, filter: Option[File => Boolean]): Array[File] = { + protected def listChildren(dir: JFile, filter: Option[JFile => Boolean]): Array[JFile] = { val listing = filter match { case Some(f) => dir.listFiles(mkFileFilter(f)) case None => dir.listFiles() @@ -112,15 +112,15 @@ trait JFileDirectoryLookup[FileEntryType <: ClassRepresentation] extends Directo // Note this behaviour can be enabled in javac with `javac -XDsortfiles`, but that's only // intended to improve determinism of the compiler for compiler hackers. java.util.Arrays.sort(listing, - new java.util.Comparator[File] { - def compare(o1: File, o2: File) = o1.getName.compareTo(o2.getName) + new java.util.Comparator[JFile] { + def compare(o1: JFile, o2: JFile) = o1.getName.compareTo(o2.getName) }) listing } else Array() } - protected def getName(f: File): String = f.getName - protected def toAbstractFile(f: File): AbstractFile = new PlainFile(new dotty.tools.io.File(f)) - protected def isPackage(f: File): Boolean = f.isPackage + protected def getName(f: JFile): String = f.getName + protected def toAbstractFile(f: JFile): AbstractFile = new PlainFile(new dotty.tools.io.File(f.toPath)) + protected def isPackage(f: JFile): Boolean = f.isPackage assert(dir != null, "Directory file in DirectoryFileLookup cannot be null") @@ -178,7 +178,7 @@ final class JrtClassPath(fs: java.nio.file.FileSystem) extends ClassPath with No else { packageToModuleBases.getOrElse(inPackage, Nil).flatMap(x => Files.list(x.resolve(inPackage.replace('.', '/'))).iterator().asScala.filter(_.getFileName.toString.endsWith(".class"))).map(x => - ClassFileEntryImpl(new PlainNioFile(x))).toVector + ClassFileEntryImpl(new PlainFile(new dotty.tools.io.File(x)))).toVector } } @@ -197,7 +197,7 @@ final class JrtClassPath(fs: java.nio.file.FileSystem) extends ClassPath with No val inPackage = packageOf(className) packageToModuleBases.getOrElse(inPackage, Nil).iterator.flatMap{x => val file = x.resolve(className.replace('.', '/') + ".class") - if (Files.exists(file)) new PlainNioFile(file) :: Nil else Nil + if (Files.exists(file)) new PlainFile(new dotty.tools.io.File(file)) :: Nil else Nil }.take(1).toList.headOption } } @@ -205,41 +205,41 @@ final class JrtClassPath(fs: java.nio.file.FileSystem) extends ClassPath with No dottedClassName.substring(0, dottedClassName.lastIndexOf(".")) } -case class DirectoryClassPath(dir: File) extends JFileDirectoryLookup[ClassFileEntryImpl] with NoSourcePaths { +case class DirectoryClassPath(dir: JFile) extends JFileDirectoryLookup[ClassFileEntryImpl] with NoSourcePaths { override def findClass(className: String): Option[ClassRepresentation] = findClassFile(className) map ClassFileEntryImpl def findClassFile(className: String): Option[AbstractFile] = { val relativePath = FileUtils.dirPath(className) - val classFile = new File(s"$dir/$relativePath.class") + val classFile = new JFile(s"$dir/$relativePath.class") if (classFile.exists) { - val wrappedClassFile = new dotty.tools.io.File(classFile) + val wrappedClassFile = new dotty.tools.io.File(classFile.toPath) val abstractClassFile = new PlainFile(wrappedClassFile) Some(abstractClassFile) } else None } protected def createFileEntry(file: AbstractFile): ClassFileEntryImpl = ClassFileEntryImpl(file) - protected def isMatchingFile(f: File): Boolean = f.isClass + protected def isMatchingFile(f: JFile): Boolean = f.isClass private[dotty] def classes(inPackage: String): Seq[ClassFileEntry] = files(inPackage) } -case class DirectorySourcePath(dir: File) extends JFileDirectoryLookup[SourceFileEntryImpl] with NoClassPaths { +case class DirectorySourcePath(dir: JFile) extends JFileDirectoryLookup[SourceFileEntryImpl] with NoClassPaths { def asSourcePathString: String = asClassPathString protected def createFileEntry(file: AbstractFile): SourceFileEntryImpl = SourceFileEntryImpl(file) - protected def isMatchingFile(f: File): Boolean = endsScalaOrJava(f.getName) + protected def isMatchingFile(f: JFile): Boolean = endsScalaOrJava(f.getName) override def findClass(className: String): Option[ClassRepresentation] = findSourceFile(className) map SourceFileEntryImpl private def findSourceFile(className: String): Option[AbstractFile] = { val relativePath = FileUtils.dirPath(className) val sourceFile = Stream("scala", "java") - .map(ext => new File(s"$dir/$relativePath.$ext")) + .map(ext => new JFile(s"$dir/$relativePath.$ext")) .collectFirst { case file if file.exists() => file } sourceFile.map { file => - val wrappedSourceFile = new dotty.tools.io.File(file) + val wrappedSourceFile = new dotty.tools.io.File(file.toPath) val abstractSourceFile = new PlainFile(wrappedSourceFile) abstractSourceFile } diff --git a/compiler/src/dotty/tools/dotc/classpath/ZipArchiveFileLookup.scala b/compiler/src/dotty/tools/dotc/classpath/ZipArchiveFileLookup.scala index 2b7d039a6bb9..77f628c673d4 100644 --- a/compiler/src/dotty/tools/dotc/classpath/ZipArchiveFileLookup.scala +++ b/compiler/src/dotty/tools/dotc/classpath/ZipArchiveFileLookup.scala @@ -23,7 +23,7 @@ trait ZipArchiveFileLookup[FileEntryType <: ClassRepresentation] extends ClassPa override def asURLs: Seq[URL] = Seq(zipFile.toURI.toURL) override def asClassPathStrings: Seq[String] = Seq(zipFile.getPath) - private val archive = new FileZipArchive(zipFile) + private val archive = new FileZipArchive(zipFile.toPath) override private[dotty] def packages(inPackage: String): Seq[PackageEntry] = { val prefix = PackageNameUtils.packagePrefix(inPackage) diff --git a/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala index a8c3667d8992..532aef36b723 100644 --- a/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala +++ b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala @@ -45,13 +45,13 @@ class ExtractAPI extends Phase { val dumpInc = ctx.settings.YdumpSbtInc.value val forceRun = dumpInc || ctx.settings.YforceSbtPhases.value if ((ctx.sbtCallback != null || forceRun) && !unit.isJava) { - val sourceFile = unit.source.file.file + val sourceFile = unit.source.file val apiTraverser = new ExtractAPICollector val source = apiTraverser.apiSource(unit.tpdTree) if (dumpInc) { // Append to existing file that should have been created by ExtractDependencies - val pw = new PrintWriter(Path(sourceFile).changeExtension("inc").toFile + val pw = new PrintWriter(Path(sourceFile.jpath).changeExtension("inc").toFile .bufferedWriter(append = true), true) try { pw.println(DefaultShowAPI(source)) @@ -59,7 +59,7 @@ class ExtractAPI extends Phase { } if (ctx.sbtCallback != null) - ctx.sbtCallback.api(sourceFile, source) + ctx.sbtCallback.api(sourceFile.file, source) } } } diff --git a/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala b/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala index 5992fe350c7d..32b9827e8535 100644 --- a/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala +++ b/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala @@ -47,7 +47,7 @@ class ExtractDependencies extends Phase { val dumpInc = ctx.settings.YdumpSbtInc.value val forceRun = dumpInc || ctx.settings.YforceSbtPhases.value if ((ctx.sbtCallback != null || forceRun) && !unit.isJava) { - val sourceFile = unit.source.file.file + val sourceFile = unit.source.file val extractDeps = new ExtractDependenciesCollector extractDeps.traverse(unit.tpdTree) @@ -59,7 +59,7 @@ class ExtractDependencies extends Phase { Arrays.sort(deps) Arrays.sort(inhDeps) - val pw = Path(sourceFile).changeExtension("inc").toFile.printWriter() + val pw = Path(sourceFile.jpath).changeExtension("inc").toFile.printWriter() try { pw.println(s"// usedNames: ${names.mkString(",")}") pw.println(s"// topLevelDependencies: ${deps.mkString(",")}") @@ -69,11 +69,11 @@ class ExtractDependencies extends Phase { if (ctx.sbtCallback != null) { extractDeps.usedNames.foreach(name => - ctx.sbtCallback.usedName(sourceFile, name.toString)) + ctx.sbtCallback.usedName(sourceFile.file, name.toString)) extractDeps.topLevelDependencies.foreach(dep => - recordDependency(sourceFile, dep, DependencyContext.DependencyByMemberRef)) + recordDependency(sourceFile.file, dep, DependencyContext.DependencyByMemberRef)) extractDeps.topLevelInheritanceDependencies.foreach(dep => - recordDependency(sourceFile, dep, DependencyContext.DependencyByInheritance)) + recordDependency(sourceFile.file, dep, DependencyContext.DependencyByInheritance)) } } } diff --git a/compiler/src/dotty/tools/io/AbstractFile.scala b/compiler/src/dotty/tools/io/AbstractFile.scala index c151739cdc31..5c30f4eecf69 100644 --- a/compiler/src/dotty/tools/io/AbstractFile.scala +++ b/compiler/src/dotty/tools/io/AbstractFile.scala @@ -10,6 +10,7 @@ import java.io.{ ByteArrayOutputStream } import java.net.URL +import java.nio.file.{FileAlreadyExistsException, Files} /** * An abstraction over files for use in the reflection/compiler libraries. @@ -41,7 +42,7 @@ object AbstractFile { */ def getDirectory(file: File): AbstractFile = if (file.isDirectory) new PlainFile(file) - else if (file.isFile && Path.isExtensionJarOrZip(file.jfile)) ZipArchive fromFile file + else if (file.isFile && Path.isExtensionJarOrZip(file.jpath)) ZipArchive fromFile file else null /** @@ -94,7 +95,7 @@ abstract class AbstractFile extends Iterable[AbstractFile] { def path: String /** Returns the path of this abstract file in a canonical form. */ - def canonicalPath: String = if (file == null) path else file.getCanonicalPath + def canonicalPath: String = if (jpath == null) path else jpath.normalize.toString /** Checks extension case insensitively. */ def hasExtension(other: String) = extension == other.toLowerCase @@ -107,18 +108,26 @@ abstract class AbstractFile extends Iterable[AbstractFile] { def container : AbstractFile /** Returns the underlying File if any and null otherwise. */ - def file: JFile + def file: JFile = try { + if (jpath == null) null + else jpath.toFile + } catch { + case _: UnsupportedOperationException => null + } + + /** Returns the underlying Path if any and null otherwise. */ + def jpath: JPath /** An underlying source, if known. Mostly, a zip/jar file. */ def underlyingSource: Option[AbstractFile] = None /** Does this abstract file denote an existing file? */ def exists: Boolean = { - (file eq null) || file.exists + (jpath eq null) || Files.exists(jpath) } /** Does this abstract file represent something which can contain classfiles? */ - def isClassContainer = isDirectory || (file != null && (extension == "jar" || extension == "zip")) + def isClassContainer = isDirectory || (jpath != null && (extension == "jar" || extension == "zip")) /** Create a file on disk, if one does not exist already. */ def create(): Unit @@ -147,7 +156,7 @@ abstract class AbstractFile extends Iterable[AbstractFile] { /** size of this file if it is a concrete file. */ def sizeOption: Option[Int] = None - def toURL: URL = if (file == null) null else file.toURI.toURL + def toURL: URL = if (jpath == null) null else jpath.toUri.toURL /** Returns contents of file (if applicable) in a Char array. * warning: use `Global.getSourceFile()` to use the proper @@ -232,9 +241,11 @@ abstract class AbstractFile extends Iterable[AbstractFile] { val lookup = lookupName(name, isDir) if (lookup != null) lookup else { - val jfile = new JFile(file, name) - if (isDir) jfile.mkdirs() else jfile.createNewFile() - new PlainFile(jfile) + Files.createDirectories(jpath) + val path = jpath.resolve(name) + if (isDir) Files.createDirectory(path) + else Files.createFile(path) + new PlainFile(new File(path)) } } diff --git a/compiler/src/dotty/tools/io/Directory.scala b/compiler/src/dotty/tools/io/Directory.scala index 6c23fd413194..4e670d50c39b 100644 --- a/compiler/src/dotty/tools/io/Directory.scala +++ b/compiler/src/dotty/tools/io/Directory.scala @@ -8,6 +8,11 @@ package dotty.tools.io +import java.nio.file.Files +import java.util.stream.Collectors + +import scala.collection.JavaConverters._ + /** * ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' */ @@ -18,6 +23,7 @@ object Directory { def Current: Option[Directory] = if (userDir == "") None else normalizePath(userDir) def apply(path: Path): Directory = path.toDirectory + def apply(jpath: JPath): Directory = new Directory(jpath) // Like File.makeTemp but creates a directory instead def makeTemp(prefix: String = Path.randomPrefix, suffix: String = null, dir: JFile = null): Directory = { @@ -34,19 +40,17 @@ object Directory { * * ''Note: This is library is considered experimental and should not be used unless you know what you are doing.'' */ -class Directory(jfile: JFile) extends Path(jfile) { +class Directory(jpath: JPath) extends Path(jpath) { override def toAbsolute: Directory = if (isAbsolute) this else super.toAbsolute.toDirectory override def toDirectory: Directory = this - override def toFile: File = new File(jfile) + override def toFile: File = new File(jpath) override def normalize: Directory = super.normalize.toDirectory /** An iterator over the contents of this directory. */ def list: Iterator[Path] = - jfile.listFiles match { - case null => Iterator.empty - case xs => xs.iterator map Path.apply - } + if (isDirectory) Files.list(jpath).iterator.asScala.map(Path.apply) + else Iterator.empty def dirs: Iterator[Directory] = list collect { case x: Directory => x } def files: Iterator[File] = list collect { case x: File => x } diff --git a/compiler/src/dotty/tools/io/File.scala b/compiler/src/dotty/tools/io/File.scala index 61dcb1a1b6e0..e41c954a9ea3 100644 --- a/compiler/src/dotty/tools/io/File.scala +++ b/compiler/src/dotty/tools/io/File.scala @@ -12,6 +12,8 @@ import java.io.{ FileInputStream, FileOutputStream, BufferedWriter, OutputStreamWriter, BufferedOutputStream, IOException, PrintWriter } +import java.nio.file.Files +import java.nio.file.StandardOpenOption._ import scala.io.Codec /** @@ -20,7 +22,7 @@ import scala.io.Codec object File { def pathSeparator = java.io.File.pathSeparator def separator = java.io.File.separator - def apply(path: Path)(implicit codec: Codec) = new File(path.jfile)(codec) + def apply(path: Path)(implicit codec: Codec) = new File(path.jpath)(codec) // Create a temporary file, which will be deleted upon jvm exit. def makeTemp(prefix: String = Path.randomPrefix, suffix: String = null, dir: JFile = null) = { @@ -41,12 +43,12 @@ object File { * * ''Note: This is library is considered experimental and should not be used unless you know what you are doing.'' */ -class File(jfile: JFile)(implicit constructorCodec: Codec) extends Path(jfile) with Streamable.Chars { +class File(jpath: JPath)(implicit constructorCodec: Codec) extends Path(jpath) with Streamable.Chars { override val creationCodec = constructorCodec override def addExtension(ext: String): File = super.addExtension(ext).toFile override def toAbsolute: File = if (isAbsolute) this else super.toAbsolute.toFile - override def toDirectory: Directory = new Directory(jfile) + override def toDirectory: Directory = new Directory(jpath) override def toFile: File = this override def normalize: File = super.normalize.toFile override def length = super[Path].length @@ -54,10 +56,12 @@ class File(jfile: JFile)(implicit constructorCodec: Codec) extends Path(jfile) w if (cond(this)) Iterator.single(this) else Iterator.empty /** Obtains an InputStream. */ - def inputStream() = new FileInputStream(jfile) + def inputStream() = Files.newInputStream(jpath) /** Obtains a OutputStream. */ - def outputStream(append: Boolean = false) = new FileOutputStream(jfile, append) + def outputStream(append: Boolean = false) = + if (append) Files.newOutputStream(jpath, APPEND) + else Files.newOutputStream(jpath) def bufferedOutput(append: Boolean = false) = new BufferedOutputStream(outputStream(append)) /** Obtains an OutputStreamWriter wrapped around a FileOutputStream. @@ -108,7 +112,7 @@ class File(jfile: JFile)(implicit constructorCodec: Codec) extends Path(jfile) w try classOf[JFile].getMethod("setExecutable", classOf[Boolean], classOf[Boolean]) catch { case _: NoSuchMethodException => return false } - try method.invoke(jfile, executable: JBoolean, ownerOnly: JBoolean).asInstanceOf[JBoolean].booleanValue + try method.invoke(jpath.toFile, executable: JBoolean, ownerOnly: JBoolean).asInstanceOf[JBoolean].booleanValue catch { case _: Exception => false } } } diff --git a/compiler/src/dotty/tools/io/Jar.scala b/compiler/src/dotty/tools/io/Jar.scala index b77a2c7df24f..7c84916b55b9 100644 --- a/compiler/src/dotty/tools/io/Jar.scala +++ b/compiler/src/dotty/tools/io/Jar.scala @@ -42,7 +42,7 @@ class Jar(file: File) extends Iterable[JarEntry] { private implicit def enrichManifest(m: JManifest): Jar.WManifest = Jar.WManifest(m) - lazy val jarFile = new JarFile(file.jfile) + lazy val jarFile = new JarFile(file.jpath.toFile) lazy val manifest = withJarInput(s => Option(s.getManifest)) def mainClass = manifest map (f => f(Name.MAIN_CLASS)) diff --git a/compiler/src/dotty/tools/io/NoAbstractFile.scala b/compiler/src/dotty/tools/io/NoAbstractFile.scala index 3b2bbeb58022..3ba5eaff7d3f 100644 --- a/compiler/src/dotty/tools/io/NoAbstractFile.scala +++ b/compiler/src/dotty/tools/io/NoAbstractFile.scala @@ -17,7 +17,7 @@ object NoAbstractFile extends AbstractFile { def container: AbstractFile = this def create(): Unit = ??? def delete(): Unit = ??? - def file: java.io.File = null + def jpath: JPath = null def input: InputStream = null def isDirectory: Boolean = false override def isVirtual: Boolean = true diff --git a/compiler/src/dotty/tools/io/Path.scala b/compiler/src/dotty/tools/io/Path.scala index 034360a67515..a55d033f748d 100644 --- a/compiler/src/dotty/tools/io/Path.scala +++ b/compiler/src/dotty/tools/io/Path.scala @@ -6,9 +6,14 @@ package dotty.tools.io import scala.language.implicitConversions - import java.io.RandomAccessFile -import java.net.{ URI, URL } +import java.nio.file._ +import java.net.{URI, URL} +import java.nio.file.attribute.BasicFileAttributes +import java.io.IOException + +import scala.collection.JavaConverters._ + import scala.util.Random.alphanumeric /** An abstraction for filesystem paths. The differences between @@ -28,7 +33,7 @@ import scala.util.Random.alphanumeric * ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' */ object Path { - def isExtensionJarOrZip(jfile: JFile): Boolean = isExtensionJarOrZip(jfile.getName) + def isExtensionJarOrZip(jpath: JPath): Boolean = isExtensionJarOrZip(jpath.getFileName.toString) def isExtensionJarOrZip(name: String): Boolean = { val ext = extension(name) ext == "jar" || ext == "zip" @@ -44,20 +49,20 @@ object Path { // not certain these won't be problematic, but looks good so far implicit def string2path(s: String): Path = apply(s) - implicit def jfile2path(jfile: JFile): Path = apply(jfile) + implicit def jfile2path(jfile: JFile): Path = apply(jfile.toPath) def onlyDirs(xs: Iterator[Path]): Iterator[Directory] = xs filter (_.isDirectory) map (_.toDirectory) def onlyDirs(xs: List[Path]): List[Directory] = xs filter (_.isDirectory) map (_.toDirectory) def onlyFiles(xs: Iterator[Path]): Iterator[File] = xs filter (_.isFile) map (_.toFile) - def roots: List[Path] = java.io.File.listRoots().toList map Path.apply + def roots: List[Path] = FileSystems.getDefault.getRootDirectories.iterator().asScala.map(Path.apply).toList - def apply(path: String): Path = apply(new JFile(path)) - def apply(jfile: JFile): Path = try { - if (jfile.isFile) new File(jfile) - else if (jfile.isDirectory) new Directory(jfile) - else new Path(jfile) - } catch { case ex: SecurityException => new Path(jfile) } + def apply(path: String): Path = apply(Paths.get(path)) + def apply(jpath: JPath): Path = try { + if (Files.isRegularFile(jpath)) new File(jpath) + else if (Files.isDirectory(jpath)) new Directory(jpath) + else new Path(jpath) + } catch { case ex: SecurityException => new Path(jpath) } /** Avoiding any shell/path issues by only using alphanumerics. */ private[io] def randomPrefix = alphanumeric take 6 mkString "" @@ -70,16 +75,16 @@ import Path._ * * ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' */ -class Path private[io] (val jfile: JFile) { +class Path private[io] (val jpath: JPath) { val separator = java.io.File.separatorChar val separatorStr = java.io.File.separator // conversions - def toFile: File = new File(jfile) - def toDirectory: Directory = new Directory(jfile) - def toAbsolute: Path = if (isAbsolute) this else Path(jfile.getAbsolutePath()) - def toCanonical: Path = Path(jfile.getCanonicalPath()) - def toURI: URI = jfile.toURI() + def toFile: File = new File(jpath) + def toDirectory: Directory = new Directory(jpath) + def toAbsolute: Path = if (isAbsolute) this else new Path(jpath.toAbsolutePath) + def toCanonical: Path = normalize.toAbsolute + def toURI: URI = jpath.toUri() def toURL: URL = toURI.toURL() /** If this path is absolute, returns it: otherwise, returns an absolute @@ -90,7 +95,7 @@ class Path private[io] (val jfile: JFile) { /** Creates a new Path with the specified path appended. Assumes * the type of the new component implies the type of the result. */ - def /(child: Path): Path = if (isEmpty) child else new Path(new JFile(jfile, child.path)) + def /(child: Path): Path = resolve(child) def /(child: Directory): Directory = /(child: Path).toDirectory def /(child: File): File = /(child: Path).toFile @@ -112,23 +117,12 @@ class Path private[io] (val jfile: JFile) { def walk: Iterator[Path] = walkFilter(_ => true) // identity - def name: String = jfile.getName() - def path: String = jfile.getPath() - def normalize: Path = Path(jfile.getAbsolutePath()) - - def resolve(other: Path) = if (other.isAbsolute || isEmpty) other else /(other) - def relativize(other: Path) = { - assert(isAbsolute == other.isAbsolute, "Paths not of same type: "+this+", "+other) - - def createRelativePath(baseSegs: List[String], otherSegs: List[String]) : String = { - (baseSegs, otherSegs) match { - case (b :: bs, o :: os) if b == o => createRelativePath(bs, os) - case (bs, os) => ((".."+separator)*bs.length)+os.mkString(separatorStr) - } - } + def name: String = jpath.getFileName().toString + def path: String = jpath.toString + def normalize: Path = Path(jpath.normalize) - Path(createRelativePath(segments, other.segments)) - } + def resolve(other: Path) = Path(jpath.resolve(other.jpath)) + def relativize(other: Path) = Path(jpath.relativize(other.jpath)) def segments: List[String] = (path split separator).toList filterNot (_.length == 0) @@ -141,7 +135,7 @@ class Path private[io] (val jfile: JFile) { // the only solution <-- a comment which could have used elaboration if (segments.nonEmpty && segments.last == "..") (path / "..").toDirectory - else jfile.getParent match { + else jpath.getParent match { case null => if (isAbsolute) toDirectory // it should be a root. BTW, don't need to worry about relative pathed root else Directory(".") // a dir under pwd @@ -173,67 +167,80 @@ class Path private[io] (val jfile: JFile) { def addExtension(ext: String): Path = Path(path + "." + ext) // changes the existing extension out for a new one, or adds it // if the current path has none. - def changeExtension(ext: String): Path = ( + def changeExtension(ext: String): Path = if (extension == "") addExtension(ext) else Path(path.stripSuffix(extension) + ext) - ) // conditionally execute def ifFile[T](f: File => T): Option[T] = if (isFile) Some(f(toFile)) else None def ifDirectory[T](f: Directory => T): Option[T] = if (isDirectory) Some(f(toDirectory)) else None // Boolean tests - def canRead = jfile.canRead() - def canWrite = jfile.canWrite() - def exists = try jfile.exists() catch { case ex: SecurityException => false } - def isFile = try jfile.isFile() catch { case ex: SecurityException => false } + def canRead = Files.isReadable(jpath) + def canWrite = Files.isWritable(jpath) + def exists = try Files.exists(jpath) catch { case ex: SecurityException => false } + def isFile = try Files.isRegularFile(jpath) catch { case ex: SecurityException => false } def isDirectory = - try jfile.isDirectory() - catch { case ex: SecurityException => jfile.getPath == "." } - def isAbsolute = jfile.isAbsolute() + try Files.isDirectory(jpath) + catch { case ex: SecurityException => jpath.toString == "." } + def isAbsolute = jpath.isAbsolute() def isEmpty = path.length == 0 // Information - def lastModified = jfile.lastModified() - def length = jfile.length() + def lastModified = Files.getLastModifiedTime(jpath) + def length = Files.size(jpath) // Boolean path comparisons def endsWith(other: Path) = segments endsWith other.segments def isSame(other: Path) = toCanonical == other.toCanonical - def isFresher(other: Path) = lastModified > other.lastModified + def isFresher(other: Path) = lastModified.compareTo(other.lastModified) > 0 // creations def createDirectory(force: Boolean = true, failIfExists: Boolean = false): Directory = { - val res = if (force) jfile.mkdirs() else jfile.mkdir() + val res = tryCreate(if (force) Files.createDirectories(jpath) else Files.createDirectory(jpath)) if (!res && failIfExists && exists) fail("Directory '%s' already exists." format name) else if (isDirectory) toDirectory - else new Directory(jfile) + else new Directory(jpath) } def createFile(failIfExists: Boolean = false): File = { - val res = jfile.createNewFile() + val res = tryCreate(Files.createFile(jpath)) + Files.createFile(jpath) if (!res && failIfExists && exists) fail("File '%s' already exists." format name) else if (isFile) toFile - else new File(jfile) + else new File(jpath) } + private def tryCreate(create: => JPath): Boolean = + try { create; true } catch { case _: FileAlreadyExistsException => false } + // deletions - def delete() = jfile.delete() + def delete(): Unit = + try { Files.deleteIfExists(jpath) } catch { case _: DirectoryNotEmptyException => } /** Deletes the path recursively. Returns false on failure. * Use with caution! */ - def deleteRecursively(): Boolean = deleteRecursively(jfile) - private def deleteRecursively(f: JFile): Boolean = { - if (f.isDirectory) f.listFiles match { - case null => - case xs => xs foreach deleteRecursively + def deleteRecursively(): Boolean = { + if (!exists) false + else { + Files.walkFileTree(jpath, new SimpleFileVisitor[JPath]() { + override def visitFile(file: JPath, attrs: BasicFileAttributes) = { + Files.delete(file) + FileVisitResult.CONTINUE + } + + override def postVisitDirectory(dir: JPath, exc: IOException) = { + Files.delete(dir) + FileVisitResult.CONTINUE + } + }) + true } - f.delete() } def truncate() = isFile && { - val raf = new RandomAccessFile(jfile, "rw") + val raf = new RandomAccessFile(jpath.toFile, "rw") raf setLength 0 raf.close() length == 0 diff --git a/compiler/src/dotty/tools/io/PlainFile.scala b/compiler/src/dotty/tools/io/PlainFile.scala index 76de56c11f01..b7e3e171f557 100644 --- a/compiler/src/dotty/tools/io/PlainFile.scala +++ b/compiler/src/dotty/tools/io/PlainFile.scala @@ -6,6 +6,8 @@ package dotty.tools package io +import java.nio.file.{Files, NotDirectoryException} + /** ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' */ class PlainDirectory(givenPath: Directory) extends PlainFile(givenPath) { override def isDirectory = true @@ -20,7 +22,7 @@ class PlainDirectory(givenPath: Directory) extends PlainFile(givenPath) { class PlainFile(val givenPath: Path) extends AbstractFile { assert(path ne null) - val file = givenPath.jfile + val jpath = givenPath.jpath override def underlyingSource = Some(this) private val fpath = givenPath.toAbsolute @@ -35,8 +37,8 @@ class PlainFile(val givenPath: Path) extends AbstractFile { def absolute = new PlainFile(givenPath.toAbsolute) override def container: AbstractFile = new PlainFile(givenPath.parent) - override def input = givenPath.toFile.inputStream() - override def output = givenPath.toFile.outputStream() + override def input = Files.newInputStream(jpath) + override def output = Files.newOutputStream(jpath) override def sizeOption = Some(givenPath.length.toInt) override def hashCode(): Int = fpath.hashCode() @@ -49,18 +51,17 @@ class PlainFile(val givenPath: Path) extends AbstractFile { def isDirectory: Boolean = givenPath.isDirectory /** Returns the time that this abstract file was last modified. */ - def lastModified: Long = givenPath.lastModified + def lastModified: Long = givenPath.lastModified.toMillis /** Returns all abstract subfiles of this abstract directory. */ def iterator: Iterator[AbstractFile] = { - // Optimization: Assume that the file was not deleted and did not have permissions changed - // between the call to `list` and the iteration. This saves a call to `exists`. - def existsFast(path: Path) = path match { - case (_: Directory | _: io.File) => true - case _ => path.exists + try { + import scala.collection.JavaConverters._ + val it = Files.newDirectoryStream(jpath).iterator() + it.asScala.map(p => new PlainFile(Path(p))) + } catch { + case _: NotDirectoryException => Iterator.empty } - if (!isDirectory) Iterator.empty - else givenPath.toDirectory.list filter existsFast map (new PlainFile(_)) } /** @@ -89,82 +90,3 @@ class PlainFile(val givenPath: Path) extends AbstractFile { def lookupNameUnchecked(name: String, directory: Boolean): AbstractFile = new PlainFile(givenPath / name) } - -private[dotty] class PlainNioFile(nioPath: java.nio.file.Path) extends AbstractFile { - import java.nio.file._ - - assert(nioPath ne null) - - /** Returns the underlying File if any and null otherwise. */ - override def file: java.io.File = try { - nioPath.toFile - } catch { - case _: UnsupportedOperationException => null - } - - override def underlyingSource = Some(this) - - private val fpath = nioPath.toAbsolutePath.toString - - /** Returns the name of this abstract file. */ - def name = nioPath.getFileName.toString - - /** Returns the path of this abstract file. */ - def path = nioPath.toString - - /** The absolute file. */ - def absolute = new PlainNioFile(nioPath.toAbsolutePath) - - override def container: AbstractFile = new PlainNioFile(nioPath.getParent) - override def input = Files.newInputStream(nioPath) - override def output = Files.newOutputStream(nioPath) - override def sizeOption = Some(Files.size(nioPath).toInt) - override def hashCode(): Int = fpath.hashCode() - override def equals(that: Any): Boolean = that match { - case x: PlainNioFile => fpath == x.fpath - case _ => false - } - - /** Is this abstract file a directory? */ - def isDirectory: Boolean = Files.isDirectory(nioPath) - - /** Returns the time that this abstract file was last modified. */ - def lastModified: Long = Files.getLastModifiedTime(nioPath).toMillis - - /** Returns all abstract subfiles of this abstract directory. */ - def iterator: Iterator[AbstractFile] = { - try { - import scala.collection.JavaConverters._ - val it = Files.newDirectoryStream(nioPath).iterator() - it.asScala.map(new PlainNioFile(_)) - } catch { - case _: NotDirectoryException => Iterator.empty - } - } - - /** - * Returns the abstract file in this abstract directory with the - * specified name. If there is no such file, returns null. The - * argument "directory" tells whether to look for a directory or - * or a regular file. - */ - def lookupName(name: String, directory: Boolean): AbstractFile = { - val child = nioPath.resolve(name) - if ((Files.isDirectory(child) && directory) || (Files.isRegularFile(child) && !directory)) new PlainNioFile(child) - else null - } - - /** Does this abstract file denote an existing file? */ - def create(): Unit = if (!exists) Files.createFile(nioPath) - - /** Delete the underlying file or directory (recursively). */ - def delete(): Unit = - if (Files.isRegularFile(nioPath)) Files.deleteIfExists(nioPath) - else if (Files.isDirectory(nioPath)) new Directory(nioPath.toFile).deleteRecursively() - - /** Returns a plain file with the given name. It does not - * check that it exists. - */ - def lookupNameUnchecked(name: String, directory: Boolean): AbstractFile = - new PlainNioFile(nioPath.resolve(name)) -} diff --git a/compiler/src/dotty/tools/io/VirtualDirectory.scala b/compiler/src/dotty/tools/io/VirtualDirectory.scala index d3e4ebb4dee3..b32eb78541ad 100644 --- a/compiler/src/dotty/tools/io/VirtualDirectory.scala +++ b/compiler/src/dotty/tools/io/VirtualDirectory.scala @@ -28,7 +28,7 @@ extends AbstractFile { override def isVirtual = true val lastModified: Long = System.currentTimeMillis - override def file = null + override def jpath = null override def input = sys.error("directories cannot be read") override def output = sys.error("directories cannot be written") diff --git a/compiler/src/dotty/tools/io/VirtualFile.scala b/compiler/src/dotty/tools/io/VirtualFile.scala index be2c6902cd39..5708b67607c7 100644 --- a/compiler/src/dotty/tools/io/VirtualFile.scala +++ b/compiler/src/dotty/tools/io/VirtualFile.scala @@ -35,7 +35,7 @@ class VirtualFile(val name: String, override val path: String) extends AbstractF def absolute = this /** Returns null. */ - def file: JFile = null + def jpath: JPath = null override def sizeOption: Option[Int] = Some(content.length) diff --git a/compiler/src/dotty/tools/io/ZipArchive.scala b/compiler/src/dotty/tools/io/ZipArchive.scala index 786163242b55..d83fd230f0a6 100644 --- a/compiler/src/dotty/tools/io/ZipArchive.scala +++ b/compiler/src/dotty/tools/io/ZipArchive.scala @@ -7,6 +7,7 @@ package dotty.tools.io import java.net.URL import java.io.{ IOException, InputStream, ByteArrayInputStream, FilterInputStream } +import java.nio.file.Files import java.util.zip.{ ZipEntry, ZipFile, ZipInputStream } import java.util.jar.Manifest import scala.collection.mutable @@ -30,10 +31,8 @@ object ZipArchive { * @param file a File * @return A ZipArchive if `file` is a readable zip file, otherwise null. */ - def fromFile(file: File): FileZipArchive = fromFile(file.jfile) - def fromFile(file: JFile): FileZipArchive = - try { new FileZipArchive(file) } - catch { case _: IOException => null } + def fromFile(file: File): FileZipArchive = fromPath(file.jpath) + def fromPath(jpath: JPath): FileZipArchive = new FileZipArchive(jpath) def fromManifestURL(url: URL): AbstractFile = new ManifestResources(url) @@ -54,7 +53,7 @@ object ZipArchive { } import ZipArchive._ /** ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' */ -abstract class ZipArchive(override val file: JFile) extends AbstractFile with Equals { +abstract class ZipArchive(override val jpath: JPath) extends AbstractFile with Equals { self => override def underlyingSource = Some(this) @@ -112,7 +111,7 @@ abstract class ZipArchive(override val file: JFile) extends AbstractFile with Eq } } /** ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' */ -final class FileZipArchive(file: JFile) extends ZipArchive(file) { +final class FileZipArchive(jpath: JPath) extends ZipArchive(jpath) { private[this] def openZipFile(): ZipFile = try { new ZipFile(file) } catch { @@ -182,16 +181,16 @@ final class FileZipArchive(file: JFile) extends ZipArchive(file) { def iterator: Iterator[Entry] = root.iterator - def name = file.getName - def path = file.getPath - def input = File(file).inputStream() - def lastModified = file.lastModified + def name = jpath.getFileName.toString + def path = jpath.toString + def input = Files.newInputStream(jpath) + def lastModified = Files.getLastModifiedTime(jpath).toMillis - override def sizeOption = Some(file.length.toInt) + override def sizeOption = Some(Files.size(jpath).toInt) override def canEqual(other: Any) = other.isInstanceOf[FileZipArchive] - override def hashCode() = file.hashCode + override def hashCode() = jpath.hashCode override def equals(that: Any) = that match { - case x: FileZipArchive => file.getAbsoluteFile == x.file.getAbsoluteFile + case x: FileZipArchive => jpath.toAbsolutePath == x.jpath.toAbsolutePath case _ => false } } diff --git a/compiler/src/dotty/tools/io/package.scala b/compiler/src/dotty/tools/io/package.scala index 7201bf0eb216..19bb6e4ad19b 100644 --- a/compiler/src/dotty/tools/io/package.scala +++ b/compiler/src/dotty/tools/io/package.scala @@ -8,4 +8,5 @@ package dotty.tools package object io { type JManifest = java.util.jar.Manifest type JFile = java.io.File + type JPath = java.nio.file.Path } diff --git a/compiler/test/dotty/tools/dotc/CompilerTest.scala b/compiler/test/dotty/tools/dotc/CompilerTest.scala index b32428362cb4..dd12bf2d148f 100644 --- a/compiler/test/dotty/tools/dotc/CompilerTest.scala +++ b/compiler/test/dotty/tools/dotc/CompilerTest.scala @@ -121,7 +121,7 @@ abstract class CompilerTest { def compileFiles(path: String, args: List[String] = Nil, verbose: Boolean = true, runTest: Boolean = false, compileSubDirs: Boolean = true)(implicit defaultOptions: List[String]): Unit = { val dir = Directory(path) - val fileNames = dir.files.toArray.map(_.jfile.getName).filter(name => (name endsWith ".scala") || (name endsWith ".java")) + val fileNames = dir.files.toArray.map(_.name).filter(name => (name endsWith ".scala") || (name endsWith ".java")) for (name <- fileNames) { if (verbose) log(s"testing $path$name") compileFile(path, name, args, "", runTest) @@ -129,7 +129,7 @@ abstract class CompilerTest { if (compileSubDirs) for (subdir <- dir.dirs) { if (verbose) log(s"testing $subdir") - compileDir(path, subdir.jfile.getName, args, runTest) + compileDir(path, subdir.name, args, runTest) } } def runFiles(path: String, args: List[String] = Nil, verbose: Boolean = true) @@ -363,13 +363,13 @@ abstract class CompilerTest { processFileDir(sourceFile, { sf => if (extensionsToCopy.contains(sf.extension)) { - dest.parent.jfile.mkdirs + dest.parent.createDirectory(force = true) copyfile(sf, false) } else { log(s"WARNING: ignoring $sf") } }, { sdir => - dest.jfile.mkdirs + dest.createDirectory(force = true) sdir.list.foreach(path => recCopyFiles(path, dest / path.name)) }, Some("DPCompilerTest.recCopyFiles: sourceFile not found: " + sourceFile)) } diff --git a/compiler/test/dotty/tools/dotc/parsing/ScannerTest.scala b/compiler/test/dotty/tools/dotc/parsing/ScannerTest.scala index 001da38ee3fb..20958e30126d 100644 --- a/compiler/test/dotty/tools/dotc/parsing/ScannerTest.scala +++ b/compiler/test/dotty/tools/dotc/parsing/ScannerTest.scala @@ -33,13 +33,13 @@ class ScannerTest extends DottyTest { def scanDir(path: String): Unit = scanDir(Directory(path)) def scanDir(dir: Directory): Unit = { - if (blackList exists (dir.jfile.toString endsWith _)) - println(s"blacklisted package: ${dir.jfile.getAbsolutePath}") + if (blackList exists (dir.jpath.toString endsWith _)) + println(s"blacklisted package: ${dir.toAbsolute.jpath}") else for (f <- dir.files) if (f.name.endsWith(".scala")) - if (blackList exists (f.jfile.toString endsWith _)) - println(s"blacklisted file: ${f.jfile.getAbsolutePath}") + if (blackList exists (f.jpath.toString endsWith _)) + println(s"blacklisted file: ${f.toAbsolute.jpath}") else scan(new PlainFile(f)) for (d <- dir.dirs) diff --git a/compiler/test/dotty/tools/dotc/transform/PatmatExhaustivityTest.scala b/compiler/test/dotty/tools/dotc/transform/PatmatExhaustivityTest.scala index 3ae26e4d421e..5d770557e056 100644 --- a/compiler/test/dotty/tools/dotc/transform/PatmatExhaustivityTest.scala +++ b/compiler/test/dotty/tools/dotc/transform/PatmatExhaustivityTest.scala @@ -4,6 +4,7 @@ package dotc package transform import java.io._ +import java.nio.file.{Path => JPath} import scala.io.Source._ import dotty.tools.io.Directory @@ -16,53 +17,53 @@ class PatmatExhaustivityTest { // stop-after: patmatexhaust-huge.scala crash compiler val options = List("-color:never", "-Ystop-after:splitter", "-Ycheck-all-patmat", "-classpath", TestConfiguration.classPath) - private def compileFile(file: File) = { + private def compileFile(path: JPath) = { val stringBuffer = new StringWriter() val reporter = TestReporter.simplifiedReporter(new PrintWriter(stringBuffer)) try { - Main.process((file.getPath::options).toArray, reporter, null) + Main.process((path.toString::options).toArray, reporter, null) } catch { case e: Throwable => - println(s"Compile $file exception:") + println(s"Compile $path exception:") e.printStackTrace() } val actual = stringBuffer.toString.trim.replaceAll("\\s+\n", "\n") - val checkFilePath = file.getAbsolutePath.stripSuffix(".scala") + ".check" + val checkFilePath = path.toAbsolutePath.toString.stripSuffix(".scala") + ".check" val checkContent = if (new File(checkFilePath).exists) fromFile(checkFilePath).getLines().map(_.replaceAll("\\s+$", "")).mkString("\n").trim else "" - (file, checkContent, actual) + (path, checkContent, actual) } /** A single test with multiple files grouped in a folder */ - private def compileDir(file: File) = { + private def compileDir(path: JPath) = { val stringBuffer = new StringWriter() val reporter = TestReporter.simplifiedReporter(new PrintWriter(stringBuffer)) - val files = Directory(file.getPath).list.toList + val files = Directory(path).list.toList .filter(f => f.extension == "scala" || f.extension == "java" ) - .map(_.jfile.getPath) + .map(_.jpath.toString) try { Main.process((options ++ files).toArray, reporter, null) } catch { case e: Throwable => - println(s"Compile $file exception:") + println(s"Compile $path exception:") e.printStackTrace() } val actual = stringBuffer.toString.trim.replaceAll("\\s+\n", "\n") - val checkFilePath = file.getPath + File.separator + "expected.check" + val checkFilePath = path + File.separator + "expected.check" val checkContent = if (new File(checkFilePath).exists) fromFile(checkFilePath).getLines().map(_.replaceAll("\\s+$", "")).mkString("\n").trim else "" - (file, checkContent, actual) + (path, checkContent, actual) } @Test @@ -71,9 +72,9 @@ class PatmatExhaustivityTest { .filter(f => f.extension == "scala" || f.isDirectory) .map { f => if (f.isDirectory) - compileDir(f.jfile) + compileDir(f.jpath) else - compileFile(f.jfile) + compileFile(f.jpath) } val failed = res.filter { case (_, expected, actual) => expected != actual } diff --git a/doc-tool/src/dotty/tools/dottydoc/staticsite/Site.scala b/doc-tool/src/dotty/tools/dottydoc/staticsite/Site.scala index 0f273f5c9f09..bc304cbc204f 100644 --- a/doc-tool/src/dotty/tools/dottydoc/staticsite/Site.scala +++ b/doc-tool/src/dotty/tools/dottydoc/staticsite/Site.scala @@ -409,7 +409,7 @@ case class Site( } private def toSourceFile(f: JFile): SourceFile = - SourceFile(AbstractFile.getFile(new File(f)), Source.fromFile(f).toArray) + SourceFile(AbstractFile.getFile(new File(f.toPath)), Source.fromFile(f).toArray) private def collectFiles(dir: JFile, includes: String => Boolean): Array[JFile] = dir