Merge remote-tracking branch 'origin/master' into develar/is
[idea/community.git] / platform / configuration-store-impl / src / StateStorageManagerImpl.kt
index 9b3a803ea398778ec9f94abc6150d802aa5768dd..0f0eea4a836776331c9223cf4964367ad506c754 100644 (file)
@@ -34,26 +34,26 @@ import com.intellij.util.containers.ContainerUtil
 import gnu.trove.THashMap
 import org.jdom.Element
 import org.jetbrains.annotations.TestOnly
-import java.io.File
 import java.io.IOException
 import java.nio.file.Path
 import java.nio.file.Paths
 import java.util.*
-import java.util.concurrent.locks.ReentrantLock
+import java.util.concurrent.locks.ReentrantReadWriteLock
 import java.util.regex.Pattern
-import kotlin.concurrent.withLock
+import kotlin.concurrent.read
+import kotlin.concurrent.write
 
-private val MACRO_PATTERN = Pattern.compile("(\\$[^\\$]*\\$)")
+private val MACRO_PATTERN = Pattern.compile("(\\$[^$]*\\$)")
 
 /**
- * If componentManager not specified, storage will not add file tracker (see VirtualFileTracker)
+ * If componentManager not specified, storage will not add file tracker
  */
 open class StateStorageManagerImpl(private val rootTagName: String,
                                    private val pathMacroSubstitutor: TrackingPathMacroSubstitutor? = null,
                                    val componentManager: ComponentManager? = null,
                                    private val virtualFileTracker: StorageVirtualFileTracker? = StateStorageManagerImpl.createDefaultVirtualTracker(componentManager) ) : StateStorageManager {
   private val macros: MutableList<Macro> = ContainerUtil.createLockFreeCopyOnWriteList()
-  private val storageLock = ReentrantLock()
+  private val storageLock = ReentrantReadWriteLock()
   private val storages = THashMap<String, StateStorage>()
 
   var streamProvider: StreamProvider? = null
@@ -132,11 +132,13 @@ open class StateStorageManagerImpl(private val rootTagName: String,
     }
   }
 
-  override final fun getStateStorage(storageSpec: Storage): StateStorage {
-    return getOrCreateStorage(storageSpec.path, storageSpec.roamingType,
-                              JavaAnnotationHelperForKotlin.getStorageClass(storageSpec),
-                              JavaAnnotationHelperForKotlin.getStateSplitterClass(storageSpec))
-  }
+  override final fun getStateStorage(storageSpec: Storage) = getOrCreateStorage(
+    storageSpec.path,
+    storageSpec.roamingType,
+    storageSpec.storageClass.java,
+    storageSpec.stateSplitter.java,
+    storageSpec.exclusive
+  )
 
   protected open fun normalizeFileSpec(fileSpec: String): String {
     val path = FileUtilRt.toSystemIndependentName(fileSpec)
@@ -147,7 +149,8 @@ open class StateStorageManagerImpl(private val rootTagName: String,
   fun getOrCreateStorage(collapsedPath: String,
                          roamingType: RoamingType = RoamingType.DEFAULT,
                          storageClass: Class<out StateStorage> = StateStorage::class.java,
-                         @Suppress("DEPRECATION") stateSplitter: Class<out StateSplitter> = StateSplitterEx::class.java): StateStorage {
+                         @Suppress("DEPRECATION") stateSplitter: Class<out StateSplitter> = StateSplitterEx::class.java,
+                         exclusive: Boolean = false): StateStorage {
     val normalizedCollapsedPath = normalizeFileSpec(collapsedPath)
     val key: String
     if (storageClass == StateStorage::class.java) {
@@ -159,17 +162,17 @@ open class StateStorageManagerImpl(private val rootTagName: String,
     else {
       key = storageClass.name!!
     }
-    storageLock.withLock {
+    storageLock.read {
       var storage = storages[key]
       if (storage == null) {
-        storage = createStateStorage(storageClass, normalizedCollapsedPath, roamingType, stateSplitter)
+        storage = createStateStorage(storageClass, normalizedCollapsedPath, roamingType, stateSplitter, exclusive)
         storages.put(key, storage)
       }
       return storage
     }
   }
 
-  fun getCachedFileStorages(changed: Collection<String>, deleted: Collection<String>, pathNormalizer: ((String) -> String)? = null) = storageLock.withLock {
+  fun getCachedFileStorages(changed: Collection<String>, deleted: Collection<String>, pathNormalizer: ((String) -> String)? = null) = storageLock.read {
     Pair(getCachedFileStorages(changed, pathNormalizer), getCachedFileStorages(deleted, pathNormalizer))
   }
 
@@ -178,7 +181,7 @@ open class StateStorageManagerImpl(private val rootTagName: String,
       return emptyList()
     }
 
-    storageLock.withLock {
+    storageLock.read {
       var result: MutableList<FileBasedStorage>? = null
       for (fileSpec in fileSpecs) {
         val path = normalizeFileSpec(pathNormalizer?.invoke(fileSpec) ?: fileSpec)
@@ -198,7 +201,8 @@ open class StateStorageManagerImpl(private val rootTagName: String,
   protected open fun createStateStorage(storageClass: Class<out StateStorage>,
                                         collapsedPath: String,
                                         roamingType: RoamingType,
-                                        @Suppress("DEPRECATION") stateSplitter: Class<out StateSplitter>): StateStorage {
+                                        @Suppress("DEPRECATION") stateSplitter: Class<out StateSplitter>,
+                                        exclusive: Boolean = false): StateStorage {
     if (storageClass != StateStorage::class.java) {
       val constructor = storageClass.constructors[0]!!
       constructor.isAccessible = true
@@ -238,8 +242,8 @@ open class StateStorageManagerImpl(private val rootTagName: String,
     return storage
   }
 
-  protected open fun createFileBasedStorage(path: String, collapsedPath: String, roamingType: RoamingType): StateStorage
-      = MyFileStorage(this, File(path), collapsedPath, rootTagName, roamingType, getMacroSubstitutor(collapsedPath), streamProvider)
+  protected open fun createFileBasedStorage(path: String, collapsedPath: String, roamingType: RoamingType, rootTagName: String?): StateStorage
+      = MyFileStorage(this, Paths.get(path), collapsedPath, rootTagName, roamingType, getMacroSubstitutor(collapsedPath), streamProvider)
 
   protected open fun createDirectoryBasedStorage(path: String, collapsedPath: String, @Suppress("DEPRECATION") splitter: StateSplitter): StateStorage
       = MyDirectoryStorage(this, Paths.get(path), splitter)
@@ -248,17 +252,19 @@ open class StateStorageManagerImpl(private val rootTagName: String,
     DirectoryBasedStorage(file, splitter, storageManager.pathMacroSubstitutor), StorageVirtualFileTracker.TrackedStorage
 
   private class MyFileStorage(override val storageManager: StateStorageManagerImpl,
-                              file: File,
+                              file: Path,
                               fileSpec: String,
-                              rootElementName: String,
+                              rootElementName: String?,
                               roamingType: RoamingType,
                               pathMacroManager: TrackingPathMacroSubstitutor? = null,
                               provider: StreamProvider? = null) : FileBasedStorage(file, fileSpec, rootElementName, pathMacroManager, roamingType, provider), StorageVirtualFileTracker.TrackedStorage {
     override val isUseXmlProlog: Boolean
-      get() = storageManager.isUseXmlProlog
+      get() = rootElementName != null && storageManager.isUseXmlProlog
 
     override fun beforeElementSaved(element: Element) {
-      storageManager.beforeElementSaved(element)
+      if (rootElementName != null) {
+        storageManager.beforeElementSaved(element)
+      }
       super.beforeElementSaved(element)
     }
 
@@ -282,19 +288,19 @@ open class StateStorageManagerImpl(private val rootTagName: String,
   }
 
   override final fun rename(path: String, newName: String) {
-    storageLock.withLock {
+    storageLock.write {
       val storage = getOrCreateStorage(collapseMacros(path), RoamingType.DEFAULT) as FileBasedStorage
 
-      val file = storage.getVirtualFile()
+      val file = storage.virtualFile
       try {
         if (file != null) {
           file.rename(storage, newName)
         }
-        else if (storage.file.name != newName) {
+        else if (storage.file.fileName.toString() != newName) {
           // old file didn't exist or renaming failed
           val expandedPath = expandMacros(path)
           val parentPath = PathUtilRt.getParentPath(expandedPath)
-          storage.setFile(null, File(parentPath, newName))
+          storage.setFile(null, Paths.get(parentPath, newName))
           pathRenamed(expandedPath, "$parentPath/$newName", null)
         }
       }
@@ -305,13 +311,13 @@ open class StateStorageManagerImpl(private val rootTagName: String,
   }
 
   fun clearStorages() {
-    storageLock.withLock {
+    storageLock.write {
       try {
-        if (virtualFileTracker != null) {
-          storages.forEachEntry({ collapsedPath, storage ->
-            virtualFileTracker.remove(expandMacros(collapsedPath))
+        virtualFileTracker?.let {
+          storages.forEachEntry { collapsedPath, storage ->
+            it.remove(expandMacros(collapsedPath))
             true
-          })
+          }
         }
       }
       finally {
@@ -328,8 +334,8 @@ open class StateStorageManagerImpl(private val rootTagName: String,
     matcherLoop@
     while (matcher.find()) {
       val m = matcher.group(1)
-      for (macro in macros) {
-        if (macro.key == m) {
+      for ((key) in macros) {
+        if (key == m) {
           continue@matcherLoop
         }
       }