diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/jarfs/FastJarHandler.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/jarfs/FastJarHandler.kt index a0cdffa6e0f..812fd04401a 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/jarfs/FastJarHandler.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/jarfs/FastJarHandler.kt @@ -4,18 +4,17 @@ */ package org.jetbrains.kotlin.cli.jvm.compiler.jarfs -import com.intellij.openapi.util.text.StringUtil +import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.vfs.VirtualFile -import com.intellij.openapi.vfs.impl.ZipHandler import com.intellij.util.containers.FactoryMap -import com.intellij.util.text.ByteArrayCharSequence +import java.io.File import java.io.FileNotFoundException -import java.io.IOException import java.io.RandomAccessFile import java.nio.channels.FileChannel -class FastJarHandler(val fileSystem: FastJarFileSystem, path: String) : ZipHandler(path) { +class FastJarHandler(val fileSystem: FastJarFileSystem, path: String) { private val myRoot: VirtualFile? + internal val file = File(path) private val ourEntryMap: Map private val cachedManifest: ByteArray? @@ -33,112 +32,80 @@ class FastJarHandler(val fileSystem: FastJarFileSystem, path: String) : ZipHandl } } - val entries: MutableMap = HashMap() - val entriesMap = entriesMap val childrenMap = FactoryMap.create> { ArrayList() } - for (info in entriesMap.values) { - val file = getOrCreateFile(info, entries) - val parent = file.parent - if (parent != null) { - childrenMap[parent]?.add(file) - } + + myRoot = FastJarVirtualFile(this, "", -1, DEFAULT_TIMESTAMP, null) + + val filesByRelativePath = mutableMapOf() + filesByRelativePath[""] = myRoot + + for (info in ourEntryMap.values) { + getOrCreateFile(info, filesByRelativePath) } - val rootInfo = getEntryInfo("") - myRoot = rootInfo?.let { getOrCreateFile(it, entries) } + for (child in filesByRelativePath.values) { + child.parent?.let(childrenMap::get)?.add(child) + } for ((key, childList) in childrenMap) { key.children = childList.toTypedArray() } } - private fun getOrCreateFile(info: EntryInfo, entries: MutableMap): FastJarVirtualFile { - var file = entries[info] - if (file == null) { - val parent = info.parent - file = FastJarVirtualFile(this, info.shortName, - if (info.isDirectory) -1 else info.length, - info.timestamp, - parent?.let { getOrCreateFile(it, entries) }) - entries[info] = file + private fun getOrCreateFile(entry: ZipEntryDescription, map: MutableMap): FastJarVirtualFile { + val entryName = entry.relativePath.normalizePath() + + return map.getOrPut(entryName) { + val (parentName, shortName) = entryName.splitPath() + + val parentInfo = getOrCreateDirectory(parentName, map) + if ("." == shortName) { + return parentInfo + } + + FastJarVirtualFile( + this, shortName, + if (entry.isDirectory) -1 else entry.uncompressedSize.toLong(), + DEFAULT_TIMESTAMP, + parentInfo + ) } - return file + } + + private fun getOrCreateDirectory(entryName: String, map: MutableMap): FastJarVirtualFile { + return map.getOrPut(entryName) { + val entry = ourEntryMap["$entryName/"] + if (entry != null) { + return getOrCreateFile(entry, map) + } + val (parentPath, shortName) = entryName.splitPath() + require(entryName != parentPath) { + "invalid entry name: '" + entryName + "' in " + this.file.absolutePath + "; after split: " + Pair(parentPath, shortName) + } + val parentInfo = getOrCreateDirectory(parentPath, map) + + FastJarVirtualFile(this, shortName, -1, DEFAULT_TIMESTAMP, parentInfo) + } + } + + private fun String.normalizePath(): String { + if (endsWith('/')) return substring(0, length - 1).normalizePath() + if (startsWith('/') || startsWith('\\')) return substring(1, length).normalizePath() + if (!contains('\\')) return this + return FileUtil.normalize(this) + } + + private fun String.splitPath(): Pair { + val slashIndex = lastIndexOf('/') + if (slashIndex == -1) return Pair("", this) + return Pair(substring(0, slashIndex), substring(slashIndex + 1)) } fun findFileByPath(pathInJar: String): VirtualFile? { return myRoot?.findFileByRelativePath(pathInJar) } - @Throws(IOException::class) - override fun createEntriesMap(): Map { - val mapToEntryInfo = mutableMapOf() - mapToEntryInfo[""] = EntryInfo("", true, DEFAULT_LENGTH, DEFAULT_TIMESTAMP, null) - for (zipEntry in ourEntryMap.values) { - getOrCreate(zipEntry, mapToEntryInfo) - } - - return mapToEntryInfo - } - - private fun getOrCreate(entry: ZipEntryDescription, map: MutableMap): EntryInfo { - var isDirectory = entry.isDirectory - var entryName = entry.relativePath - if (StringUtil.endsWithChar(entryName, '/')) { - entryName = entryName.substring(0, entryName.length - 1) - isDirectory = true - } - if (StringUtil.startsWithChar(entryName, '/') || StringUtil.startsWithChar(entryName, '\\')) { - entryName = entryName.substring(1) - } - - var info = map[entryName] - if (info != null) return info - - val path = splitPathAndFix(entryName) - - val parentInfo = getOrCreateDirectory(path.first, map) - if ("." == path.second) { - return parentInfo - } - info = store(map, parentInfo, path.second, isDirectory, entry.uncompressedSize.toLong(), 0, path.third) - return info - } - - private fun getOrCreateDirectory(entryName: String, map: MutableMap): EntryInfo { - var info = map[entryName] - - if (info == null) { - val entry = ourEntryMap["$entryName/"] - if (entry != null) { - return getOrCreate(entry, map) - } - val path = splitPathAndFix(entryName) - require(entryName != path.first) { - "invalid entry name: '" + entryName + "' in " + this.file.absolutePath + "; after split: " + path - } - val parentInfo = getOrCreateDirectory(path.first, map) - info = store(map, parentInfo, path.second, true, DEFAULT_LENGTH, DEFAULT_TIMESTAMP, path.third) - } - - return info - } - - private fun store( - map: MutableMap, - parentInfo: EntryInfo?, - shortName: CharSequence, - isDirectory: Boolean, - size: Long, - time: Long, - entryName: String - ): EntryInfo { - val sequence = ByteArrayCharSequence.convertToBytesIfPossible(shortName) - val info = EntryInfo(sequence, isDirectory, size, time, parentInfo) - map[entryName] = info - return info - } - - override fun contentsToByteArray(relativePath: String): ByteArray { + fun contentsToByteArray(relativePath: String): ByteArray { if (relativePath == MANIFEST_PATH) return cachedManifest ?: throw FileNotFoundException("$file!/$relativePath") val zipEntryDescription = ourEntryMap[relativePath] ?: throw FileNotFoundException("$file!/$relativePath") return fileSystem.cachedOpenFileHandles[file].use { @@ -151,4 +118,4 @@ class FastJarHandler(val fileSystem: FastJarFileSystem, path: String) : ZipHandl private const val MANIFEST_PATH = "META-INF/MANIFEST.MF" - +private const val DEFAULT_TIMESTAMP = 0L