diff --git a/platform/workspaceModel-core-tests/intellij.platform.workspaceModel.core.tests.iml b/platform/workspaceModel-core-tests/intellij.platform.workspaceModel.core.tests.iml
index eb2d7a422a7a..9ba4d5938980 100644
--- a/platform/workspaceModel-core-tests/intellij.platform.workspaceModel.core.tests.iml
+++ b/platform/workspaceModel-core-tests/intellij.platform.workspaceModel.core.tests.iml
@@ -8,7 +8,6 @@
-
diff --git a/platform/workspaceModel-core-tests/test/com/intellij/workspace/api/SerializationInProxyBasedStorageTest.kt b/platform/workspaceModel-core-tests/test/com/intellij/workspace/api/SerializationInProxyBasedStorageTest.kt
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/platform/workspaceModel-core-tests/test/com/intellij/workspace/api/SimplePropertiesInProxyBasedStorageTest.kt b/platform/workspaceModel-core-tests/test/com/intellij/workspace/api/SimplePropertiesInProxyBasedStorageTest.kt
index 7a9ede933970..ea1bd7181f32 100644
--- a/platform/workspaceModel-core-tests/test/com/intellij/workspace/api/SimplePropertiesInProxyBasedStorageTest.kt
+++ b/platform/workspaceModel-core-tests/test/com/intellij/workspace/api/SimplePropertiesInProxyBasedStorageTest.kt
@@ -1,6 +1,5 @@
package com.intellij.workspace.api
-import com.intellij.workspace.ide.VirtualFileUrlManagerImpl
import org.junit.Assert.*
import org.junit.Before
import org.junit.Test
@@ -27,7 +26,7 @@ internal fun TypedEntityStorageBuilder.addSampleEntity(stringProperty: String,
source: EntitySource = SampleEntitySource("test"),
booleanProperty: Boolean = false,
stringListProperty: MutableList = ArrayList(),
- virtualFileManager: VirtualFileUrlManager = VirtualFileUrlManagerImpl(),
+ virtualFileManager: VirtualFileUrlManager = VirtualFileUrlManager(),
fileProperty: VirtualFileUrl = virtualFileManager.fromUrl("file:///tmp")): SampleEntity {
return addEntity(source) {
this.booleanProperty = booleanProperty
@@ -45,7 +44,7 @@ class SimplePropertiesInProxyBasedStorageTest {
private lateinit var virtualFileManager: VirtualFileUrlManager
@Before
fun setUp() {
- virtualFileManager = VirtualFileUrlManagerImpl()
+ virtualFileManager = VirtualFileUrlManager()
}
@Test
diff --git a/platform/workspaceModel-ide-tests/testSrc/com/intellij/workspace/jps/VirtualFileNameStoreTest.kt b/platform/workspaceModel-core-tests/test/com/intellij/workspace/api/VirtualFileNameStoreTest.kt
similarity index 92%
rename from platform/workspaceModel-ide-tests/testSrc/com/intellij/workspace/jps/VirtualFileNameStoreTest.kt
rename to platform/workspaceModel-core-tests/test/com/intellij/workspace/api/VirtualFileNameStoreTest.kt
index 9b38a14262d6..f845307e8548 100644
--- a/platform/workspaceModel-ide-tests/testSrc/com/intellij/workspace/jps/VirtualFileNameStoreTest.kt
+++ b/platform/workspaceModel-core-tests/test/com/intellij/workspace/api/VirtualFileNameStoreTest.kt
@@ -1,7 +1,6 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
-package com.intellij.workspace.jps
+package com.intellij.workspace.api
-import com.intellij.workspace.ide.VirtualFileNameStore
import org.junit.Assert
import org.junit.Test
diff --git a/platform/workspaceModel-ide-tests/testSrc/com/intellij/workspace/jps/VirtualFileUrlManagerTest.kt b/platform/workspaceModel-core-tests/test/com/intellij/workspace/api/VirtualFileUrlManagerTest.kt
similarity index 97%
rename from platform/workspaceModel-ide-tests/testSrc/com/intellij/workspace/jps/VirtualFileUrlManagerTest.kt
rename to platform/workspaceModel-core-tests/test/com/intellij/workspace/api/VirtualFileUrlManagerTest.kt
index 02eac107bb0d..12c24b152518 100644
--- a/platform/workspaceModel-ide-tests/testSrc/com/intellij/workspace/jps/VirtualFileUrlManagerTest.kt
+++ b/platform/workspaceModel-core-tests/test/com/intellij/workspace/api/VirtualFileUrlManagerTest.kt
@@ -1,17 +1,16 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
-package com.intellij.workspace.jps
+package com.intellij.workspace.api
import com.intellij.openapi.vfs.VfsUtil
-import com.intellij.workspace.ide.VirtualFileUrlManagerImpl
import org.junit.Assert
import org.junit.Before
import org.junit.Test
class VirtualFileUrlManagerTest {
- private lateinit var virtualFileManager: VirtualFileUrlManagerImpl
+ private lateinit var virtualFileManager: VirtualFileUrlManager
@Before
fun setUp() {
- virtualFileManager = VirtualFileUrlManagerImpl()
+ virtualFileManager = VirtualFileUrlManager()
}
@Test
diff --git a/platform/workspaceModel-core-tests/test/com/intellij/workspace/api/pstorage/ReferencesInPStorageTest.kt b/platform/workspaceModel-core-tests/test/com/intellij/workspace/api/pstorage/ReferencesInPStorageTest.kt
index 4e73f156d1eb..201bb09963dc 100644
--- a/platform/workspaceModel-core-tests/test/com/intellij/workspace/api/pstorage/ReferencesInPStorageTest.kt
+++ b/platform/workspaceModel-core-tests/test/com/intellij/workspace/api/pstorage/ReferencesInPStorageTest.kt
@@ -5,7 +5,6 @@ import com.intellij.workspace.api.*
import com.intellij.workspace.api.pstorage.references.ManyToOne
import com.intellij.workspace.api.pstorage.references.MutableManyToOne
import com.intellij.workspace.api.pstorage.references.OneToMany
-import com.intellij.workspace.ide.VirtualFileUrlManagerImpl
import org.junit.Assert
import org.junit.Assert.assertEquals
import org.junit.Before
@@ -154,7 +153,7 @@ class ReferencesInPStorageTest {
private lateinit var virtualFileManager: VirtualFileUrlManager
@Before
fun setUp() {
- virtualFileManager = VirtualFileUrlManagerImpl()
+ virtualFileManager = VirtualFileUrlManager()
}
@Test
diff --git a/platform/workspaceModel-core-tests/test/com/intellij/workspace/api/pstorage/SimplePropertiesInPStorageTest.kt b/platform/workspaceModel-core-tests/test/com/intellij/workspace/api/pstorage/SimplePropertiesInPStorageTest.kt
index 67e0d9b4591d..8762acf21338 100644
--- a/platform/workspaceModel-core-tests/test/com/intellij/workspace/api/pstorage/SimplePropertiesInPStorageTest.kt
+++ b/platform/workspaceModel-core-tests/test/com/intellij/workspace/api/pstorage/SimplePropertiesInPStorageTest.kt
@@ -3,7 +3,6 @@ package com.intellij.workspace.api.pstorage
import com.intellij.openapi.util.Ref
import com.intellij.workspace.api.*
-import com.intellij.workspace.ide.VirtualFileUrlManagerImpl
import org.junit.Assert.*
import org.junit.Before
import org.junit.Test
@@ -36,7 +35,7 @@ internal fun TypedEntityStorageBuilder.addPSampleEntity(stringProperty: String,
source: EntitySource = PSampleEntitySource("test"),
booleanProperty: Boolean = false,
stringListProperty: MutableList = ArrayList(),
- virtualFileManager: VirtualFileUrlManager = VirtualFileUrlManagerImpl(),
+ virtualFileManager: VirtualFileUrlManager = VirtualFileUrlManager(),
fileProperty: VirtualFileUrl = virtualFileManager.fromUrl(
"file:///tmp")): PSampleEntity {
return addEntity(ModifiablePSampleEntity::class.java, source) {
@@ -55,7 +54,7 @@ class PSimplePropertiesInProxyBasedStorageTest {
private lateinit var virtualFileManager: VirtualFileUrlManager
@Before
fun setUp() {
- virtualFileManager = VirtualFileUrlManagerImpl()
+ virtualFileManager = VirtualFileUrlManager()
}
@Test
diff --git a/platform/workspaceModel-core-tests/test/com/intellij/workspace/api/pstorage/VirtualFileIndexTest.kt b/platform/workspaceModel-core-tests/test/com/intellij/workspace/api/pstorage/VirtualFileIndexTest.kt
index 4beda2ac89f9..7c95f4351182 100644
--- a/platform/workspaceModel-core-tests/test/com/intellij/workspace/api/pstorage/VirtualFileIndexTest.kt
+++ b/platform/workspaceModel-core-tests/test/com/intellij/workspace/api/pstorage/VirtualFileIndexTest.kt
@@ -9,7 +9,6 @@ import com.intellij.workspace.api.*
import com.intellij.workspace.api.pstorage.indices.VirtualFileUrlListProperty
import com.intellij.workspace.api.pstorage.indices.VirtualFileUrlNullableProperty
import com.intellij.workspace.api.pstorage.indices.VirtualFileUrlProperty
-import com.intellij.workspace.ide.VirtualFileUrlManagerImpl
import org.junit.Assert.*
import org.junit.Before
import org.junit.Test
@@ -91,7 +90,7 @@ class VirtualFileIndexTest {
private lateinit var virtualFileManager: VirtualFileUrlManager
@Before
fun setUp() {
- virtualFileManager = VirtualFileUrlManagerImpl()
+ virtualFileManager = VirtualFileUrlManager()
}
@Test
diff --git a/platform/workspaceModel-ide/src/com/intellij/workspace/ide/VirtualFileNameStore.kt b/platform/workspaceModel-core/src/VirtualFileNameStore.kt
similarity index 93%
rename from platform/workspaceModel-ide/src/com/intellij/workspace/ide/VirtualFileNameStore.kt
rename to platform/workspaceModel-core/src/VirtualFileNameStore.kt
index 70a0382eafa8..134dd52578ff 100644
--- a/platform/workspaceModel-ide/src/com/intellij/workspace/ide/VirtualFileNameStore.kt
+++ b/platform/workspaceModel-core/src/VirtualFileNameStore.kt
@@ -1,11 +1,11 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
-package com.intellij.workspace.ide
+package com.intellij.workspace.api
import gnu.trove.THashMap
import org.jetbrains.annotations.TestOnly
internal class VirtualFileNameStore {
- private val generator = IdGenerator()
+ private val generator = IntIdGenerator()
private val name2IdStore = THashMap()
private val id2NameStore = THashMap()
@@ -58,7 +58,7 @@ private data class IdPerCount(val id: Int, var usageCount: Long) {
override fun hashCode() = 31 * id.hashCode()
}
-internal class IdGenerator {
+internal class IntIdGenerator {
private var generator: Int = 0
fun generateId() = ++generator
diff --git a/platform/workspaceModel-core/src/VirtualFileUrl.kt b/platform/workspaceModel-core/src/VirtualFileUrl.kt
index 58ca049fef2a..d074ffae5386 100644
--- a/platform/workspaceModel-core/src/VirtualFileUrl.kt
+++ b/platform/workspaceModel-core/src/VirtualFileUrl.kt
@@ -3,14 +3,6 @@ package com.intellij.workspace.api
import java.io.File
import java.nio.file.Path
-abstract class VirtualFileUrlManager {
- abstract fun fromUrl(url: String): VirtualFileUrl
- abstract fun fromPath(path: String): VirtualFileUrl
- abstract fun getParentVirtualUrlById(id: Int): VirtualFileUrl?
- abstract fun getUrlById(id: Int): String
- abstract fun isEqualOrParentOf(parentNodeId: Int, childNodeId: Int): Boolean
-}
-
// TODO Do we want to make it inline?
data class VirtualFileUrl(private val id: Int, internal val manager: VirtualFileUrlManager)
{
diff --git a/platform/workspaceModel-core/src/VirtualFileUrlManager.kt b/platform/workspaceModel-core/src/VirtualFileUrlManager.kt
new file mode 100644
index 000000000000..308b5fb13335
--- /dev/null
+++ b/platform/workspaceModel-core/src/VirtualFileUrlManager.kt
@@ -0,0 +1,242 @@
+// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package com.intellij.workspace.api
+
+import com.intellij.openapi.util.io.FileUtil
+import com.intellij.util.SmartList
+import com.intellij.util.containers.IntArrayList
+import gnu.trove.THashMap
+import kotlin.collections.set
+
+open class VirtualFileUrlManager {
+ private val idGenerator= IntIdGenerator()
+ private val EMPTY_URL = VirtualFileUrl(0, this)
+ private val fileNameStore = VirtualFileNameStore()
+ private val id2NodeMapping = THashMap()
+ private var segmentId2RootNodeMapping = THashMap()
+
+ fun fromUrl(url: String): VirtualFileUrl {
+ if (url.isEmpty()) return EMPTY_URL
+ return add(url)
+ }
+
+ fun fromPath(path: String): VirtualFileUrl {
+ if (path.isEmpty()) return EMPTY_URL
+ return fromUrl("file://${FileUtil.toSystemIndependentName(path)}")
+ }
+
+ fun getParentVirtualUrlById(id: Int): VirtualFileUrl? = id2NodeMapping[id]?.parent?.let { VirtualFileUrl(it.nodeId, this) }
+
+ fun getUrlById(id: Int): String {
+ if (id <= 0) return ""
+
+ var node = id2NodeMapping[id]
+ val contentIds = IntArrayList()
+ while (node != null) {
+ contentIds.add(node.contentId)
+ node = node.parent
+ }
+
+ if (contentIds.size() == 1) {
+ return fileNameStore.getNameForId(contentIds[0])?.let {
+ return@let if (it.isEmpty()) "/" else it
+ } ?: ""
+ }
+ val builder = StringBuilder()
+ for (index in contentIds.size() - 1 downTo 0) {
+ builder.append(fileNameStore.getNameForId(contentIds[index]))
+ if (index != 0) builder.append("/")
+ }
+ return builder.toString()
+ }
+
+ internal fun add(path: String): VirtualFileUrl {
+ val segments = splitNames(path)
+ var latestNode: FilePathNode? = findRootNode(segments.first())
+ val latestElement = segments.size - 1
+ for (index in segments.indices) {
+ val nameId = fileNameStore.generateIdForName(segments[index])
+ // Latest node can be NULL only if it's root node
+ if (latestNode == null) {
+ val nodeId = idGenerator.generateId()
+ val newNode = FilePathNode(nodeId, nameId)
+ id2NodeMapping[nodeId] = newNode
+ // If it's the latest name of folder or files, save entity Id as node value
+ if (index == latestElement) {
+ segmentId2RootNodeMapping[nameId] = newNode
+ return VirtualFileUrl(nodeId, this)
+ }
+ latestNode = newNode
+ segmentId2RootNodeMapping[nameId] = newNode
+ continue
+ }
+
+ if (latestNode === findRootNode(latestNode.contentId)) {
+ if (latestNode.contentId == nameId) {
+ if (index == latestElement) return VirtualFileUrl(latestNode.nodeId, this)
+ continue
+ }
+ }
+
+ val node = latestNode.findChild(nameId)
+ if (node == null) {
+ val nodeId = idGenerator.generateId()
+ val newNode = FilePathNode(nodeId, nameId, latestNode)
+ id2NodeMapping[nodeId] = newNode
+ latestNode.addChild(newNode)
+ latestNode = newNode
+ // If it's the latest name of folder or files, save entity Id as node value
+ if (index == latestElement) return VirtualFileUrl(nodeId, this)
+ } else {
+ // If it's the latest name of folder or files, save entity Id as node value
+ if (index == latestElement) return VirtualFileUrl(node.nodeId, this)
+ latestNode = node
+ }
+ }
+ return EMPTY_URL
+ }
+
+ internal fun remove(path: String) {
+ val node = findLatestFilePathNode(path)
+ if (node == null) {
+ println("File not found")
+ return
+ }
+ if (!node.isEmpty()) return
+
+ var currentNode: FilePathNode = node
+ do {
+ val parent = currentNode.parent
+ if (parent == null) {
+ if (currentNode === findRootNode(currentNode.contentId) && currentNode.isEmpty()) {
+ removeNameUsage(currentNode.contentId)
+ id2NodeMapping.remove(currentNode.nodeId)
+ segmentId2RootNodeMapping.remove(currentNode.contentId)
+ }
+ return
+ }
+
+ parent.removeChild(currentNode)
+ removeNameUsage(currentNode.contentId)
+ id2NodeMapping.remove(currentNode.nodeId)
+ currentNode = parent
+ } while (currentNode.isEmpty())
+ }
+
+ internal fun update(oldPath: String, newPath: String) {
+ val latestPathNode = findLatestFilePathNode(oldPath)
+ if (latestPathNode == null) return
+ remove(oldPath)
+ add(newPath)
+ }
+
+ private fun removeNameUsage(contentId: Int) {
+ val name = fileNameStore.getNameForId(contentId)
+ assert(name != null)
+ fileNameStore.removeName(name!!)
+ }
+
+ private fun findLatestFilePathNode(path: String): FilePathNode? {
+ val segments = splitNames(path)
+ var latestNode: FilePathNode? = findRootNode(segments.first())
+ val latestElement = segments.size - 1
+ for (index in segments.indices) {
+ val nameId = fileNameStore.getIdForName(segments[index]) ?: return null
+ // Latest node can be NULL only if it's root node
+ if (latestNode == null) return null
+
+ if (latestNode === findRootNode(latestNode.contentId)) {
+ if (latestNode.contentId == nameId) {
+ if (index == latestElement) return latestNode else continue
+ }
+ }
+
+ latestNode.findChild(nameId)?.let {
+ if (index == latestElement) return it
+ latestNode = it
+ } ?: return null
+ }
+ return null
+ }
+
+ private fun findRootNode(segment: String): FilePathNode? {
+ val segmentId = fileNameStore.getIdForName(segment) ?: return null
+ return segmentId2RootNodeMapping[segmentId]
+ }
+
+ private fun findRootNode(contentId: Int): FilePathNode? = segmentId2RootNodeMapping[contentId]
+
+ private fun splitNames(path: String): List = path.split('/', '\\')
+
+ override fun toString() = segmentId2RootNodeMapping.values.joinToString(separator = "\n") { it.toString() }
+
+ fun isEqualOrParentOf(parentNodeId: Int, childNodeId: Int): Boolean {
+ if (parentNodeId == 0 && childNodeId == 0) return true
+
+ var current = childNodeId
+ while (current > 0) {
+ if (parentNodeId == current) return true
+ current = id2NodeMapping[current]?.parent?.nodeId ?: return false
+ }
+ /*
+ TODO It may look like this + caching + invalidating
+ val segmentName = getSegmentName(id).toString()
+
+ val parent = id2parent.getValue(id)
+ val parentParent = id2parent.getValue(parent)
+ return if (parentParent <= 0) {
+ val fileSystem = VirtualFileManager.getInstance().getFileSystem(getSegmentName(parent).toString())
+ fileSystem?.findFileByPath(segmentName)
+ } else {
+ getVirtualFileById(parent)?.findChild(segmentName)
+ }
+ }
+ */
+ return false
+ }
+
+ private inner class FilePathNode(val nodeId: Int, val contentId: Int, val parent: FilePathNode? = null) {
+ private var children: MutableList? = null
+
+ internal fun findChild(nameId: Int): FilePathNode? {
+ // If search of child node will be slow, replace SmartList to THashSet
+ // For now SmartList reduce 500Kb memory on IDEA project
+ return children?.find { it.contentId == nameId }
+ }
+
+ internal fun addChild(newNode: FilePathNode) {
+ createChildrenList()
+ children!!.add(newNode)
+ }
+
+ internal fun removeChild(node: FilePathNode) {
+ children?.remove(node)
+ }
+
+ internal fun isEmpty() = children == null || children!!.isEmpty()
+
+ private fun createChildrenList() {
+ if (children == null) children = SmartList()
+ }
+
+ override fun toString(): String {
+ val buffer = StringBuilder()
+ print(buffer, "", "")
+ return buffer.toString()
+ }
+
+ private fun print(buffer: StringBuilder, prefix: String, childrenPrefix: String) {
+ val name = this@VirtualFileUrlManager.fileNameStore.getNameForId(contentId)
+ buffer.append("$prefix $name\n")
+ val iterator = children?.iterator() ?: return
+ while (iterator.hasNext()) {
+ val next = iterator.next()
+ if (iterator.hasNext()) {
+ next.print(buffer, "$childrenPrefix |- ", "$childrenPrefix | ")
+ }
+ else {
+ next.print(buffer, "$childrenPrefix '- ", "$childrenPrefix ")
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/platform/workspaceModel-ide/src/com/intellij/workspace/ide/VirtualFileUrlManagerImpl.kt b/platform/workspaceModel-ide/src/com/intellij/workspace/ide/VirtualFileUrlManagerImpl.kt
index 1bd5d900434d..5b74f350bd1c 100644
--- a/platform/workspaceModel-ide/src/com/intellij/workspace/ide/VirtualFileUrlManagerImpl.kt
+++ b/platform/workspaceModel-ide/src/com/intellij/workspace/ide/VirtualFileUrlManagerImpl.kt
@@ -3,247 +3,10 @@ package com.intellij.workspace.ide
import com.intellij.openapi.components.service
import com.intellij.openapi.project.Project
-import com.intellij.openapi.util.io.FileUtil
-import com.intellij.util.SmartList
-import com.intellij.workspace.api.VirtualFileUrl
import com.intellij.workspace.api.VirtualFileUrlManager
-import gnu.trove.THashMap
-import kotlin.collections.set
class VirtualFileUrlManagerImpl: VirtualFileUrlManager() {
- private val idGenerator= IdGenerator()
- private val EMPTY_URL = VirtualFileUrl(0, this)
- private val fileNameStore = VirtualFileNameStore()
- private val id2NodeMapping = THashMap()
- private var segmentId2RootNodeMapping = THashMap()
-
companion object {
fun getInstance(project: Project): VirtualFileUrlManager = project.service()
}
-
- override fun fromUrl(url: String): VirtualFileUrl {
- if (url.isEmpty()) return EMPTY_URL
- return add(url)
- }
-
- override fun fromPath(path: String): VirtualFileUrl {
- if (path.isEmpty()) return EMPTY_URL
- return fromUrl("file://${FileUtil.toSystemIndependentName(path)}")
- }
-
- override fun getParentVirtualUrlById(id: Int): VirtualFileUrl? = id2NodeMapping[id]?.parent?.let { VirtualFileUrl(it.nodeId, this) }
-
- override fun getUrlById(id: Int): String {
- if (id <= 0) return ""
-
- var node = id2NodeMapping[id]
- val contentIds = mutableListOf()
- while (node != null) {
- contentIds.add(node.contentId)
- node = node.parent
- }
-
- if (contentIds.size == 1) {
- return fileNameStore.getNameForId(contentIds[0])?.let {
- return@let if (it.isEmpty()) "/" else it
- } ?: ""
- }
- val builder = StringBuilder()
- for (index in contentIds.indices.reversed()) {
- builder.append(fileNameStore.getNameForId(contentIds[index]))
- if (index != 0) builder.append("/")
- }
- return builder.toString()
- }
-
- internal fun add(path: String): VirtualFileUrl {
- val segments = splitNames(path)
- var latestNode: FilePathNode? = findRootNode(segments.first())
- val latestElement = segments.size - 1
- for (index in segments.indices) {
- val nameId = fileNameStore.generateIdForName(segments[index])
- // Latest node can be NULL only if it's root node
- if (latestNode == null) {
- val nodeId = idGenerator.generateId()
- val newNode = FilePathNode(nodeId, nameId)
- id2NodeMapping[nodeId] = newNode
- // If it's the latest name of folder or files, save entity Id as node value
- if (index == latestElement) {
- segmentId2RootNodeMapping[nameId] = newNode
- return VirtualFileUrl(nodeId, this)
- }
- latestNode = newNode
- segmentId2RootNodeMapping[nameId] = newNode
- continue
- }
-
- if (latestNode === findRootNode(latestNode.contentId)) {
- if (latestNode.contentId == nameId) {
- if (index == latestElement) return VirtualFileUrl(latestNode.nodeId, this)
- continue
- }
- }
-
- val node = latestNode.findChild(nameId)
- if (node == null) {
- val nodeId = idGenerator.generateId()
- val newNode = FilePathNode(nodeId, nameId, latestNode)
- id2NodeMapping[nodeId] = newNode
- latestNode.addChild(newNode)
- latestNode = newNode
- // If it's the latest name of folder or files, save entity Id as node value
- if (index == latestElement) return VirtualFileUrl(nodeId, this)
- } else {
- // If it's the latest name of folder or files, save entity Id as node value
- if (index == latestElement) return VirtualFileUrl(node.nodeId, this)
- latestNode = node
- }
- }
- return EMPTY_URL
- }
-
- internal fun remove(path: String) {
- val node = findLatestFilePathNode(path)
- if (node == null) {
- println("File not found")
- return
- }
- if (!node.isEmpty()) return
-
- var currentNode: FilePathNode = node
- do {
- val parent = currentNode.parent
- if (parent == null) {
- if (currentNode === findRootNode(currentNode.contentId) && currentNode.isEmpty()) {
- removeNameUsage(currentNode.contentId)
- id2NodeMapping.remove(currentNode.nodeId)
- segmentId2RootNodeMapping.remove(currentNode.contentId)
- }
- return
- }
-
- parent.removeChild(currentNode)
- removeNameUsage(currentNode.contentId)
- id2NodeMapping.remove(currentNode.nodeId)
- currentNode = parent
- } while (currentNode.isEmpty())
- }
-
- internal fun update(oldPath: String, newPath: String) {
- val latestPathNode = findLatestFilePathNode(oldPath)
- if (latestPathNode == null) return
- remove(oldPath)
- add(newPath)
- }
-
- private fun removeNameUsage(contentId: Int) {
- val name = fileNameStore.getNameForId(contentId)
- assert(name != null)
- fileNameStore.removeName(name!!)
- }
-
- private fun findLatestFilePathNode(path: String): FilePathNode? {
- val segments = splitNames(path)
- var latestNode: FilePathNode? = findRootNode(segments.first())
- val latestElement = segments.size - 1
- for (index in segments.indices) {
- val nameId = fileNameStore.getIdForName(segments[index]) ?: return null
- // Latest node can be NULL only if it's root node
- if (latestNode == null) return null
-
- if (latestNode === findRootNode(latestNode.contentId)) {
- if (latestNode.contentId == nameId) {
- if (index == latestElement) return latestNode else continue
- }
- }
-
- latestNode.findChild(nameId)?.let {
- if (index == latestElement) return it
- latestNode = it
- } ?: return null
- }
- return null
- }
-
- private fun findRootNode(segment: String): FilePathNode? {
- val segmentId = fileNameStore.getIdForName(segment) ?: return null
- return segmentId2RootNodeMapping[segmentId]
- }
-
- private fun findRootNode(contentId: Int): FilePathNode? = segmentId2RootNodeMapping[contentId]
-
- private fun splitNames(path: String): List = path.split('/', '\\')
-
- override fun toString() = segmentId2RootNodeMapping.values.joinToString(separator = "\n") { it.toString() }
-
- override fun isEqualOrParentOf(parentNodeId: Int, childNodeId: Int): Boolean {
- if (parentNodeId == 0 && childNodeId == 0) return true
-
- var current = childNodeId
- while (current > 0) {
- if (parentNodeId == current) return true
- current = id2NodeMapping[current]?.parent?.nodeId ?: return false
- }
- /*
- TODO It may look like this + caching + invalidating
- val segmentName = getSegmentName(id).toString()
-
- val parent = id2parent.getValue(id)
- val parentParent = id2parent.getValue(parent)
- return if (parentParent <= 0) {
- val fileSystem = VirtualFileManager.getInstance().getFileSystem(getSegmentName(parent).toString())
- fileSystem?.findFileByPath(segmentName)
- } else {
- getVirtualFileById(parent)?.findChild(segmentName)
- }
- }
- */
- return false
- }
-
- private inner class FilePathNode(val nodeId: Int, val contentId: Int, val parent: FilePathNode? = null) {
- private var children: MutableList? = null
-
- internal fun findChild(nameId: Int): FilePathNode? {
- // If search of child node will be slow, replace SmartList to THashSet
- // For now SmartList reduce 500Kb memory on IDEA project
- return children?.find { it.contentId == nameId }
- }
-
- internal fun addChild(newNode: FilePathNode) {
- createChildrenList()
- children!!.add(newNode)
- }
-
- internal fun removeChild(node: FilePathNode) {
- children?.remove(node)
- }
-
- internal fun isEmpty() = children == null || children!!.isEmpty()
-
- private fun createChildrenList() {
- if (children == null) children = SmartList()
- }
-
- override fun toString(): String {
- val buffer = StringBuilder()
- print(buffer, "", "")
- return buffer.toString()
- }
-
- private fun print(buffer: StringBuilder, prefix: String, childrenPrefix: String) {
- val name = this@VirtualFileUrlManagerImpl.fileNameStore.getNameForId(contentId)
- buffer.append("$prefix $name\n")
- val iterator = children?.iterator() ?: return
- while (iterator.hasNext()) {
- val next = iterator.next()
- if (iterator.hasNext()) {
- next.print(buffer, "$childrenPrefix |- ", "$childrenPrefix | ")
- }
- else {
- next.print(buffer, "$childrenPrefix '- ", "$childrenPrefix ")
- }
- }
- }
- }
}
\ No newline at end of file