diff --git a/libraries/stdlib/src/kotlin/io/files/FilePathComponents.kt b/libraries/stdlib/src/kotlin/io/files/FilePathComponents.kt index 8f2d9e5189b..ba57839f125 100644 --- a/libraries/stdlib/src/kotlin/io/files/FilePathComponents.kt +++ b/libraries/stdlib/src/kotlin/io/files/FilePathComponents.kt @@ -81,30 +81,39 @@ public val File.root: File? /** * Represents the path to a file as a collection of directories. * - * @property rootName the name of the root of the path (for example, `/` or `C:`). - * @property fileList the list of [File] objects representing every directory in the path to the file, + * @property root the [File] object representing root of the path (for example, `/` or `C:` or empty for relative paths). + * @property segments the list of [File] objects representing every directory in the path to the file, * up to an including the file itself. */ -public data class FilePathComponents(public val rootName: String, public val fileList: List) { +public data class FilePathComponents + internal constructor(public val root: File, public val segments: List) { + + @Deprecated("This constructor will be removed soon. Use File.toComponents() extension to create an instance of FilePathComponents.") + constructor (rootName: String, fileList: List): this(File(rootName), fileList) + + @Deprecated("Use 'root' property or 'root.path' instead.", ReplaceWith("root.path")) + public val rootName: String get() = root.path + + @Deprecated("Use 'segments' property instead.", ReplaceWith("segments")) + public val fileList: List get() = segments + /** * Returns the number of elements in the path to the file. */ - public fun size(): Int = fileList.size + public val size: Int get() = segments.size - /** - * [File] representing root of the path (for example, `/` or `C:` or empty for relative paths). - */ - public val root: File = File(rootName) + @Deprecated("Use 'size' property instead.", ReplaceWith("size")) + public fun size(): Int = size /** * Returns a sub-path of the path, starting with the directory at the specified [beginIndex] and up * to the specified [endIndex]. */ public fun subPath(beginIndex: Int, endIndex: Int): File { - if (beginIndex < 0 || beginIndex > endIndex || endIndex > size()) + if (beginIndex < 0 || beginIndex > endIndex || endIndex > size) throw IllegalArgumentException() - return File(fileList.subList(beginIndex, endIndex).joinToString(File.separator)) + return File(segments.subList(beginIndex, endIndex).joinToString(File.separator)) } } @@ -112,7 +121,7 @@ public data class FilePathComponents(public val rootName: String, public val fil * Splits the file into path components (the names of containing directories and the name of the file * itself) and returns the resulting collection of components. */ -public fun File.filePathComponents(): FilePathComponents { +public fun File.toComponents(): FilePathComponents { val path = separatorsToSystem() val rootName = path.getRootName() val subPath = path.substring(rootName.length) @@ -121,9 +130,12 @@ public fun File.filePathComponents(): FilePathComponents { val list = if (subPath.isEmpty()) listOf() else // Looks awful but we split just by /+ or \+ depending on OS subPath.split(Regex.fromLiteral(File.separatorChar.toString())).map { it -> File(it) } - return FilePathComponents(rootName, list) + return FilePathComponents(File(rootName), list) } +@Deprecated("Use 'toComponents' instead.", ReplaceWith("toComponents()")) +public fun File.filePathComponents(): FilePathComponents = toComponents() + /** * Returns a relative pathname which is a subsequence of this pathname, * beginning from component [beginIndex], inclusive, @@ -134,4 +146,4 @@ public fun File.filePathComponents(): FilePathComponents { * or [endIndex] is greater than existing number of components, * or [beginIndex] is greater than [endIndex]. */ -public fun File.subPath(beginIndex: Int, endIndex: Int): File = filePathComponents().subPath(beginIndex, endIndex) +public fun File.subPath(beginIndex: Int, endIndex: Int): File = toComponents().subPath(beginIndex, endIndex) diff --git a/libraries/stdlib/src/kotlin/io/files/Utils.kt b/libraries/stdlib/src/kotlin/io/files/Utils.kt index 255c0a0ff64..b51c0cf0343 100644 --- a/libraries/stdlib/src/kotlin/io/files/Utils.kt +++ b/libraries/stdlib/src/kotlin/io/files/Utils.kt @@ -129,19 +129,19 @@ public fun File.relativeTo(base: File): String private fun File.relativeToOrNull(base: File): String? { // Check roots - val thisComponents = this.filePathComponents().normalize() - val baseComponents = base.filePathComponents().normalize() + val thisComponents = this.toComponents().normalize() + val baseComponents = base.toComponents().normalize() if (thisComponents.root != baseComponents.root) { return null } - val baseCount = baseComponents.size() - val thisCount = thisComponents.size() + val baseCount = baseComponents.size + val thisCount = thisComponents.size val sameCount = run countSame@ { var i = 0 val maxSameCount = Math.min(thisCount, baseCount) - while (i < maxSameCount && thisComponents.fileList[i] == baseComponents.fileList[i]) + while (i < maxSameCount && thisComponents.segments[i] == baseComponents.segments[i]) i++ return@countSame i } @@ -149,7 +149,7 @@ private fun File.relativeToOrNull(base: File): String? { // Annihilate differing base components by adding required number of .. parts val res = StringBuilder() for (i in baseCount - 1 downTo sameCount) { - if (baseComponents.fileList[i].name == "..") { + if (baseComponents.segments[i].name == "..") { return null } @@ -166,7 +166,7 @@ private fun File.relativeToOrNull(base: File): String? { if (sameCount < baseCount) res.append(File.separatorChar) - res.append(thisComponents.subPath(sameCount, thisCount)) + thisComponents.segments.drop(sameCount).joinTo(res, File.separator) } return res.toString() @@ -331,12 +331,12 @@ public fun File.listFiles(filter: (file: File) -> Boolean): Array? = listF * @return `true` if this path starts with [other] path, `false` otherwise. */ public fun File.startsWith(other: File): Boolean { - val components = filePathComponents() - val otherComponents = other.filePathComponents() + val components = toComponents() + val otherComponents = other.toComponents() if (components.root != otherComponents.root && otherComponents.rootName != "") return false - return if (components.size() < otherComponents.size()) false - else components.fileList.subList(0, otherComponents.size()).equals(otherComponents.fileList) + return if (components.size < otherComponents.size) false + else components.segments.subList(0, otherComponents.size).equals(otherComponents.segments) } /** @@ -358,13 +358,13 @@ public fun File.startsWith(other: String): Boolean = startsWith(File(other)) * @return `true` if this path ends with [other] path, `false` otherwise. */ public fun File.endsWith(other: File): Boolean { - val components = filePathComponents() - val otherComponents = other.filePathComponents() + val components = toComponents() + val otherComponents = other.toComponents() if (components.root != otherComponents.root && otherComponents.rootName != "") return false - val shift = components.size() - otherComponents.size() + val shift = components.size - otherComponents.size return if (shift < 0) false - else components.fileList.subList(shift, components.size()).equals(otherComponents.fileList) + else components.segments.subList(shift, components.size).equals(otherComponents.segments) } /** @@ -384,10 +384,10 @@ public fun File.endsWith(other: String): Boolean = endsWith(File(other)) * @return normalized pathname with . and possibly .. removed. */ public fun File.normalize(): File - = filePathComponents().let { File(it.fileList.normalize().joinToString(File.separator, it.rootName)) } + = with (toComponents()) { root.resolve(segments.normalize().joinToString(File.separator)) } private fun FilePathComponents.normalize(): FilePathComponents - = FilePathComponents(rootName, fileList.normalize()) + = FilePathComponents(root, segments.normalize()) private fun List.normalize(): List { val list: MutableList = ArrayList(this.size) @@ -434,8 +434,8 @@ public fun File.resolve(relative: String): File = resolve(File(relative)) * @return concatenated this.parent and [relative] paths, or just [relative] if it's absolute or this has no parent. */ public fun File.resolveSibling(relative: File): File { - val components = filePathComponents() - val parentSubPath = if (components.size() == 0) File("..") else components.subPath(0, components.size() - 1) + val components = this.toComponents() + val parentSubPath = if (components.size == 0) File("..") else components.subPath(0, components.size - 1) return components.root.resolve(parentSubPath).resolve(relative) } diff --git a/libraries/stdlib/test/io/Files.kt b/libraries/stdlib/test/io/Files.kt index 93a27bf5cd5..4fb7022c006 100644 --- a/libraries/stdlib/test/io/Files.kt +++ b/libraries/stdlib/test/io/Files.kt @@ -189,7 +189,7 @@ class FilesTest { private fun checkFileElements(f: File, root: File?, elements: List) { var i = 0 assertEquals(root, f.root) - for (elem in f.filePathComponents().fileList) { + for (elem in f.toComponents().segments) { assertTrue(i < elements.size, i.toString()) assertEquals(elements[i++], elem.toString()) }