Skip to content

Commit bddcc77

Browse files
committed
Use nio.Path instead of io.File to properly support PlainNioFile
1 parent cbb481c commit bddcc77

11 files changed

+89
-70
lines changed

compiler/src/dotty/tools/dotc/classpath/DirectoryClassPath.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ trait JFileDirectoryLookup[FileEntryType <: ClassRepresentation] extends Directo
119119
} else Array()
120120
}
121121
protected def getName(f: File): String = f.getName
122-
protected def toAbstractFile(f: File): AbstractFile = new PlainFile(new dotty.tools.io.File(f))
122+
protected def toAbstractFile(f: File): AbstractFile = new PlainFile(new dotty.tools.io.File(f.toPath))
123123
protected def isPackage(f: File): Boolean = f.isPackage
124124

125125
assert(dir != null, "Directory file in DirectoryFileLookup cannot be null")
@@ -212,7 +212,7 @@ case class DirectoryClassPath(dir: File) extends JFileDirectoryLookup[ClassFileE
212212
val relativePath = FileUtils.dirPath(className)
213213
val classFile = new File(s"$dir/$relativePath.class")
214214
if (classFile.exists) {
215-
val wrappedClassFile = new dotty.tools.io.File(classFile)
215+
val wrappedClassFile = new dotty.tools.io.File(classFile.toPath)
216216
val abstractClassFile = new PlainFile(wrappedClassFile)
217217
Some(abstractClassFile)
218218
} else None
@@ -239,7 +239,7 @@ case class DirectorySourcePath(dir: File) extends JFileDirectoryLookup[SourceFil
239239
.collectFirst { case file if file.exists() => file }
240240

241241
sourceFile.map { file =>
242-
val wrappedSourceFile = new dotty.tools.io.File(file)
242+
val wrappedSourceFile = new dotty.tools.io.File(file.toPath)
243243
val abstractSourceFile = new PlainFile(wrappedSourceFile)
244244
abstractSourceFile
245245
}

compiler/src/dotty/tools/io/AbstractFile.scala

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import java.io.{
1010
ByteArrayOutputStream
1111
}
1212
import java.net.URL
13+
import java.nio.file.{FileAlreadyExistsException, Files}
1314

1415
/**
1516
* An abstraction over files for use in the reflection/compiler libraries.
@@ -94,7 +95,7 @@ abstract class AbstractFile extends Iterable[AbstractFile] {
9495
def path: String
9596

9697
/** Returns the path of this abstract file in a canonical form. */
97-
def canonicalPath: String = if (file == null) path else file.getCanonicalPath
98+
def canonicalPath: String = if (jpath == null) path else jpath.toFile.getCanonicalPath
9899

99100
/** Checks extension case insensitively. */
100101
def hasExtension(other: String) = extension == other.toLowerCase
@@ -107,18 +108,26 @@ abstract class AbstractFile extends Iterable[AbstractFile] {
107108
def container : AbstractFile
108109

109110
/** Returns the underlying File if any and null otherwise. */
110-
def file: JFile
111+
def file: JFile = try {
112+
if (jpath == null) null
113+
else jpath.toFile
114+
} catch {
115+
case _: UnsupportedOperationException => null
116+
}
117+
118+
/** Returns the underlying Path if any and null otherwise. */
119+
def jpath: JPath
111120

112121
/** An underlying source, if known. Mostly, a zip/jar file. */
113122
def underlyingSource: Option[AbstractFile] = None
114123

115124
/** Does this abstract file denote an existing file? */
116125
def exists: Boolean = {
117-
(file eq null) || file.exists
126+
(jpath eq null) || Files.exists(jpath)
118127
}
119128

120129
/** Does this abstract file represent something which can contain classfiles? */
121-
def isClassContainer = isDirectory || (file != null && (extension == "jar" || extension == "zip"))
130+
def isClassContainer = isDirectory || (jpath != null && (extension == "jar" || extension == "zip"))
122131

123132
/** Create a file on disk, if one does not exist already. */
124133
def create(): Unit
@@ -147,7 +156,7 @@ abstract class AbstractFile extends Iterable[AbstractFile] {
147156
/** size of this file if it is a concrete file. */
148157
def sizeOption: Option[Int] = None
149158

150-
def toURL: URL = if (file == null) null else file.toURI.toURL
159+
def toURL: URL = if (jpath == null) null else jpath.toUri.toURL
151160

152161
/** Returns contents of file (if applicable) in a Char array.
153162
* warning: use `Global.getSourceFile()` to use the proper
@@ -232,9 +241,14 @@ abstract class AbstractFile extends Iterable[AbstractFile] {
232241
val lookup = lookupName(name, isDir)
233242
if (lookup != null) lookup
234243
else {
235-
val jfile = new JFile(file, name)
236-
if (isDir) jfile.mkdirs() else jfile.createNewFile()
237-
new PlainFile(jfile)
244+
val path = jpath.resolve(name)
245+
try {
246+
if (isDir) Files.createDirectories(path)
247+
else Files.createFile(path)
248+
} catch {
249+
case _: FileAlreadyExistsException =>
250+
}
251+
new PlainNioFile(path)
238252
}
239253
}
240254

compiler/src/dotty/tools/io/Directory.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,10 @@ object Directory {
3434
*
3535
* ''Note: This is library is considered experimental and should not be used unless you know what you are doing.''
3636
*/
37-
class Directory(jfile: JFile) extends Path(jfile) {
37+
class Directory(jpath: JPath) extends Path(jpath) {
3838
override def toAbsolute: Directory = if (isAbsolute) this else super.toAbsolute.toDirectory
3939
override def toDirectory: Directory = this
40-
override def toFile: File = new File(jfile)
40+
override def toFile: File = new File(jpath)
4141
override def normalize: Directory = super.normalize.toDirectory
4242

4343
/** An iterator over the contents of this directory.

compiler/src/dotty/tools/io/File.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import scala.io.Codec
2020
object File {
2121
def pathSeparator = java.io.File.pathSeparator
2222
def separator = java.io.File.separator
23-
def apply(path: Path)(implicit codec: Codec) = new File(path.jfile)(codec)
23+
def apply(path: Path)(implicit codec: Codec) = new File(path.jpath)(codec)
2424

2525
// Create a temporary file, which will be deleted upon jvm exit.
2626
def makeTemp(prefix: String = Path.randomPrefix, suffix: String = null, dir: JFile = null) = {
@@ -41,12 +41,12 @@ object File {
4141
*
4242
* ''Note: This is library is considered experimental and should not be used unless you know what you are doing.''
4343
*/
44-
class File(jfile: JFile)(implicit constructorCodec: Codec) extends Path(jfile) with Streamable.Chars {
44+
class File(jpath: JPath)(implicit constructorCodec: Codec) extends Path(jpath) with Streamable.Chars {
4545
override val creationCodec = constructorCodec
4646

4747
override def addExtension(ext: String): File = super.addExtension(ext).toFile
4848
override def toAbsolute: File = if (isAbsolute) this else super.toAbsolute.toFile
49-
override def toDirectory: Directory = new Directory(jfile)
49+
override def toDirectory: Directory = new Directory(jfile.toPath)
5050
override def toFile: File = this
5151
override def normalize: File = super.normalize.toFile
5252
override def length = super[Path].length

compiler/src/dotty/tools/io/NoAbstractFile.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ object NoAbstractFile extends AbstractFile {
1717
def container: AbstractFile = this
1818
def create(): Unit = ???
1919
def delete(): Unit = ???
20-
def file: java.io.File = null
20+
def jpath: JPath = null
2121
def input: InputStream = null
2222
def isDirectory: Boolean = false
2323
override def isVirtual: Boolean = true

compiler/src/dotty/tools/io/Path.scala

Lines changed: 49 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@
66
package dotty.tools.io
77

88
import scala.language.implicitConversions
9-
109
import java.io.RandomAccessFile
11-
import java.net.{ URI, URL }
10+
import java.nio.file.{FileAlreadyExistsException, Files}
11+
import java.net.{URI, URL}
12+
1213
import scala.util.Random.alphanumeric
1314

1415
/** An abstraction for filesystem paths. The differences between
@@ -54,10 +55,10 @@ object Path {
5455

5556
def apply(path: String): Path = apply(new JFile(path))
5657
def apply(jfile: JFile): Path = try {
57-
if (jfile.isFile) new File(jfile)
58-
else if (jfile.isDirectory) new Directory(jfile)
59-
else new Path(jfile)
60-
} catch { case ex: SecurityException => new Path(jfile) }
58+
if (jfile.isFile) new File(jfile.toPath)
59+
else if (jfile.isDirectory) new Directory(jfile.toPath)
60+
else new Path(jfile.toPath)
61+
} catch { case ex: SecurityException => new Path(jfile.toPath) }
6162

6263
/** Avoiding any shell/path issues by only using alphanumerics. */
6364
private[io] def randomPrefix = alphanumeric take 6 mkString ""
@@ -70,16 +71,16 @@ import Path._
7071
*
7172
* ''Note: This library is considered experimental and should not be used unless you know what you are doing.''
7273
*/
73-
class Path private[io] (val jfile: JFile) {
74+
class Path private[io] (val jpath: JPath) {
7475
val separator = java.io.File.separatorChar
7576
val separatorStr = java.io.File.separator
7677

7778
// conversions
78-
def toFile: File = new File(jfile)
79-
def toDirectory: Directory = new Directory(jfile)
80-
def toAbsolute: Path = if (isAbsolute) this else Path(jfile.getAbsolutePath())
79+
def toFile: File = new File(jpath)
80+
def toDirectory: Directory = new Directory(jpath)
81+
def toAbsolute: Path = if (isAbsolute) this else new Path(jpath.toAbsolutePath)
8182
def toCanonical: Path = Path(jfile.getCanonicalPath())
82-
def toURI: URI = jfile.toURI()
83+
def toURI: URI = jpath.toUri()
8384
def toURL: URL = toURI.toURL()
8485

8586
/** If this path is absolute, returns it: otherwise, returns an absolute
@@ -90,7 +91,7 @@ class Path private[io] (val jfile: JFile) {
9091
/** Creates a new Path with the specified path appended. Assumes
9192
* the type of the new component implies the type of the result.
9293
*/
93-
def /(child: Path): Path = if (isEmpty) child else new Path(new JFile(jfile, child.path))
94+
def /(child: Path): Path = if (isEmpty) child else new Path(jpath.resolve(child.path))
9495
def /(child: Directory): Directory = /(child: Path).toDirectory
9596
def /(child: File): File = /(child: Path).toFile
9697

@@ -111,9 +112,11 @@ class Path private[io] (val jfile: JFile) {
111112
*/
112113
def walk: Iterator[Path] = walkFilter(_ => true)
113114

115+
def jfile: JFile = jpath.toFile
116+
114117
// identity
115-
def name: String = jfile.getName()
116-
def path: String = jfile.getPath()
118+
def name: String = jpath.getFileName().toString
119+
def path: String = jpath.toString
117120
def normalize: Path = Path(jfile.getAbsolutePath())
118121

119122
def resolve(other: Path) = if (other.isAbsolute || isEmpty) other else /(other)
@@ -141,12 +144,12 @@ class Path private[io] (val jfile: JFile) {
141144
// the only solution <-- a comment which could have used elaboration
142145
if (segments.nonEmpty && segments.last == "..")
143146
(path / "..").toDirectory
144-
else jfile.getParent match {
147+
else jpath.getParent match {
145148
case null =>
146149
if (isAbsolute) toDirectory // it should be a root. BTW, don't need to worry about relative pathed root
147150
else Directory(".") // a dir under pwd
148151
case x =>
149-
Directory(x)
152+
Directory(x.toFile)
150153
}
151154
}
152155
def parents: List[Directory] = {
@@ -173,62 +176,68 @@ class Path private[io] (val jfile: JFile) {
173176
def addExtension(ext: String): Path = Path(path + "." + ext)
174177
// changes the existing extension out for a new one, or adds it
175178
// if the current path has none.
176-
def changeExtension(ext: String): Path = (
179+
def changeExtension(ext: String): Path =
177180
if (extension == "") addExtension(ext)
178181
else Path(path.stripSuffix(extension) + ext)
179-
)
180182

181183
// conditionally execute
182184
def ifFile[T](f: File => T): Option[T] = if (isFile) Some(f(toFile)) else None
183185
def ifDirectory[T](f: Directory => T): Option[T] = if (isDirectory) Some(f(toDirectory)) else None
184186

185187
// Boolean tests
186-
def canRead = jfile.canRead()
187-
def canWrite = jfile.canWrite()
188-
def exists = try jfile.exists() catch { case ex: SecurityException => false }
189-
def isFile = try jfile.isFile() catch { case ex: SecurityException => false }
188+
def canRead = Files.isReadable(jpath)
189+
def canWrite = Files.isWritable(jpath)
190+
def exists = try Files.exists(jpath) catch { case ex: SecurityException => false }
191+
def isFile = try Files.isRegularFile(jpath) catch { case ex: SecurityException => false }
190192
def isDirectory =
191-
try jfile.isDirectory()
192-
catch { case ex: SecurityException => jfile.getPath == "." }
193-
def isAbsolute = jfile.isAbsolute()
193+
try Files.isDirectory(jpath)
194+
catch { case ex: SecurityException => jpath.toString == "." }
195+
def isAbsolute = jpath.isAbsolute()
194196
def isEmpty = path.length == 0
195197

196198
// Information
197-
def lastModified = jfile.lastModified()
198-
def length = jfile.length()
199+
def lastModified = Files.getLastModifiedTime(jpath)
200+
def length = Files.size(jpath)
199201

200202
// Boolean path comparisons
201203
def endsWith(other: Path) = segments endsWith other.segments
202204
def isSame(other: Path) = toCanonical == other.toCanonical
203-
def isFresher(other: Path) = lastModified > other.lastModified
205+
def isFresher(other: Path) = lastModified.compareTo(other.lastModified) > 0
204206

205207
// creations
206208
def createDirectory(force: Boolean = true, failIfExists: Boolean = false): Directory = {
207-
val res = if (force) jfile.mkdirs() else jfile.mkdir()
209+
val res = try {
210+
if (force) Files.createDirectories(jpath) else Files.createDirectory(jpath)
211+
true
212+
} catch {
213+
case _: FileAlreadyExistsException => false
214+
}
208215
if (!res && failIfExists && exists) fail("Directory '%s' already exists." format name)
209216
else if (isDirectory) toDirectory
210-
else new Directory(jfile)
217+
else new Directory(jpath)
211218
}
212219
def createFile(failIfExists: Boolean = false): File = {
213-
val res = jfile.createNewFile()
220+
val res = try { Files.createFile(jpath); true } catch { case e: FileAlreadyExistsException => false}
214221
if (!res && failIfExists && exists) fail("File '%s' already exists." format name)
215222
else if (isFile) toFile
216-
else new File(jfile)
223+
else new File(jpath)
217224
}
218225

219226
// deletions
220-
def delete() = jfile.delete()
227+
def delete(): Unit = Files.delete(jpath)
221228

222229
/** Deletes the path recursively. Returns false on failure.
223230
* Use with caution!
224231
*/
225-
def deleteRecursively(): Boolean = deleteRecursively(jfile)
226-
private def deleteRecursively(f: JFile): Boolean = {
227-
if (f.isDirectory) f.listFiles match {
228-
case null =>
229-
case xs => xs foreach deleteRecursively
230-
}
231-
f.delete()
232+
def deleteRecursively(): Boolean = deleteRecursively(jpath)
233+
private def deleteRecursively(p: JPath): Boolean = {
234+
import scala.collection.JavaConverters._
235+
if (Files.isDirectory(p))
236+
Files.list(p).iterator().asScala.foreach(deleteRecursively)
237+
try {
238+
Files.delete(p)
239+
true
240+
} catch { case _: Throwable => false }
232241
}
233242

234243
def truncate() =

compiler/src/dotty/tools/io/PlainFile.scala

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class PlainDirectory(givenPath: Directory) extends PlainFile(givenPath) {
2020
class PlainFile(val givenPath: Path) extends AbstractFile {
2121
assert(path ne null)
2222

23-
val file = givenPath.jfile
23+
val jpath = givenPath.jpath
2424
override def underlyingSource = Some(this)
2525

2626
private val fpath = givenPath.toAbsolute
@@ -49,7 +49,7 @@ class PlainFile(val givenPath: Path) extends AbstractFile {
4949
def isDirectory: Boolean = givenPath.isDirectory
5050

5151
/** Returns the time that this abstract file was last modified. */
52-
def lastModified: Long = givenPath.lastModified
52+
def lastModified: Long = givenPath.lastModified.toMillis
5353

5454
/** Returns all abstract subfiles of this abstract directory. */
5555
def iterator: Iterator[AbstractFile] = {
@@ -95,12 +95,7 @@ private[dotty] class PlainNioFile(nioPath: java.nio.file.Path) extends AbstractF
9595

9696
assert(nioPath ne null)
9797

98-
/** Returns the underlying File if any and null otherwise. */
99-
override def file: java.io.File = try {
100-
nioPath.toFile
101-
} catch {
102-
case _: UnsupportedOperationException => null
103-
}
98+
def jpath = nioPath
10499

105100
override def underlyingSource = Some(this)
106101

@@ -160,7 +155,7 @@ private[dotty] class PlainNioFile(nioPath: java.nio.file.Path) extends AbstractF
160155
/** Delete the underlying file or directory (recursively). */
161156
def delete(): Unit =
162157
if (Files.isRegularFile(nioPath)) Files.deleteIfExists(nioPath)
163-
else if (Files.isDirectory(nioPath)) new Directory(nioPath.toFile).deleteRecursively()
158+
else if (Files.isDirectory(nioPath)) new Directory(nioPath).deleteRecursively()
164159

165160
/** Returns a plain file with the given name. It does not
166161
* check that it exists.

compiler/src/dotty/tools/io/VirtualDirectory.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ extends AbstractFile {
2828
override def isVirtual = true
2929
val lastModified: Long = System.currentTimeMillis
3030

31-
override def file = null
31+
override def jpath = null
3232
override def input = sys.error("directories cannot be read")
3333
override def output = sys.error("directories cannot be written")
3434

compiler/src/dotty/tools/io/VirtualFile.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class VirtualFile(val name: String, override val path: String) extends AbstractF
3535
def absolute = this
3636

3737
/** Returns null. */
38-
def file: JFile = null
38+
def jpath: JPath = null
3939

4040
override def sizeOption: Option[Int] = Some(content.length)
4141

0 commit comments

Comments
 (0)