mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
reduce SchemeManagerImpl complexity — extract SchemeListManager
This commit is contained in:
@@ -24,7 +24,10 @@ import com.intellij.openapi.components.impl.stores.FileStorageCoreUtil
|
||||
import com.intellij.openapi.components.impl.stores.FileStorageCoreUtil.DEFAULT_EXT
|
||||
import com.intellij.openapi.diagnostic.runAndLogException
|
||||
import com.intellij.openapi.extensions.AbstractExtensionPointBean
|
||||
import com.intellij.openapi.options.*
|
||||
import com.intellij.openapi.options.ExternalizableScheme
|
||||
import com.intellij.openapi.options.NonLazySchemeProcessor
|
||||
import com.intellij.openapi.options.SchemeProcessor
|
||||
import com.intellij.openapi.options.SchemeState
|
||||
import com.intellij.openapi.progress.ProcessCanceledException
|
||||
import com.intellij.openapi.util.Condition
|
||||
import com.intellij.openapi.util.WriteExternalException
|
||||
@@ -44,7 +47,6 @@ import com.intellij.util.*
|
||||
import com.intellij.util.containers.ConcurrentList
|
||||
import com.intellij.util.containers.ContainerUtil
|
||||
import com.intellij.util.containers.catch
|
||||
import com.intellij.util.containers.filterSmart
|
||||
import com.intellij.util.io.*
|
||||
import com.intellij.util.messages.MessageBus
|
||||
import com.intellij.util.text.UniqueNameGenerator
|
||||
@@ -57,34 +59,24 @@ import java.io.InputStream
|
||||
import java.nio.file.Path
|
||||
import java.util.*
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
import java.util.function.Function
|
||||
|
||||
class SchemeManagerImpl<T : Scheme, MUTABLE_SCHEME : T>(val fileSpec: String,
|
||||
private val processor: SchemeProcessor<T, MUTABLE_SCHEME>,
|
||||
class SchemeManagerImpl<T : Any, MUTABLE_SCHEME : T>(val fileSpec: String,
|
||||
processor: SchemeProcessor<T, MUTABLE_SCHEME>,
|
||||
private val provider: StreamProvider?,
|
||||
private val ioDirectory: Path,
|
||||
val roamingType: RoamingType = RoamingType.DEFAULT,
|
||||
val presentableName: String? = null,
|
||||
private val schemeNameToFileName: SchemeNameToFileName = CURRENT_NAME_CONVERTER,
|
||||
private val messageBus: MessageBus? = null) : SchemesManager<T>(), SafeWriteRequestor {
|
||||
private val messageBus: MessageBus? = null) : SchemeManagerBase<T, MUTABLE_SCHEME>(processor), SafeWriteRequestor {
|
||||
private val isOldSchemeNaming = schemeNameToFileName == OLD_NAME_CONVERTER
|
||||
|
||||
private val isLoadingSchemes = AtomicBoolean()
|
||||
|
||||
private val schemesRef = AtomicReference(ContainerUtil.createLockFreeCopyOnWriteList<T>() as ConcurrentList<T>)
|
||||
private val schemeListManager = SchemeListManager(this)
|
||||
|
||||
private val schemes: ConcurrentList<T>
|
||||
get() = schemesRef.get()
|
||||
|
||||
private val readOnlyExternalizableSchemes = ContainerUtil.newConcurrentMap<String, T>()
|
||||
|
||||
/**
|
||||
* Schemes can be lazy loaded, so, client should be able to set current scheme by name, not only by instance.
|
||||
*/
|
||||
private @Volatile var currentPendingSchemeName: String? = null
|
||||
|
||||
private var currentScheme: T? = null
|
||||
get() = schemeListManager.schemes
|
||||
|
||||
private var cachedVirtualDirectory: VirtualFile? = null
|
||||
|
||||
@@ -113,6 +105,9 @@ class SchemeManagerImpl<T : Scheme, MUTABLE_SCHEME : T>(val fileSpec: String,
|
||||
}
|
||||
}
|
||||
|
||||
override val allSchemeNames: Collection<String>
|
||||
get() = schemes.let { if (it.isEmpty()) emptyList() else it.map { processor.getSchemeKey(it) } }
|
||||
|
||||
private inner class SchemeFileTracker : BulkFileListener {
|
||||
private fun isMy(file: VirtualFile) = canRead(file.nameSequence)
|
||||
|
||||
@@ -217,9 +212,11 @@ class SchemeManagerImpl<T : Scheme, MUTABLE_SCHEME : T>(val fileSpec: String,
|
||||
val newSchemes = SmartList<T>()
|
||||
val readScheme = readSchemeFromFile(file, newSchemes)
|
||||
if (readScheme != null) {
|
||||
val existingScheme = findSchemeByName(readScheme.name)
|
||||
if (existingScheme != null && readOnlyExternalizableSchemes.get(existingScheme.name) !== existingScheme) {
|
||||
LOG.warn("Ignore incorrect VFS create scheme event: schema ${readScheme.name} is already exists")
|
||||
val readSchemeKey = processor.getSchemeKey(readScheme)
|
||||
val existingScheme = findSchemeByName(readSchemeKey)
|
||||
@Suppress("SuspiciousEqualsCombination")
|
||||
if (existingScheme != null && schemeListManager.readOnlyExternalizableSchemes.get(processor.getSchemeKey(existingScheme)) !== existingScheme) {
|
||||
LOG.warn("Ignore incorrect VFS create scheme event: schema ${readSchemeKey} is already exists")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -291,9 +288,10 @@ class SchemeManagerImpl<T : Scheme, MUTABLE_SCHEME : T>(val fileSpec: String,
|
||||
attributeProvider, true)
|
||||
val oldInfo = schemeToInfo.put(scheme, externalInfo)
|
||||
LOG.assertTrue(oldInfo == null)
|
||||
val oldScheme = readOnlyExternalizableSchemes.put(scheme.name, scheme)
|
||||
val schemeKey = processor.getSchemeKey(scheme)
|
||||
val oldScheme = schemeListManager.readOnlyExternalizableSchemes.put(schemeKey, scheme)
|
||||
if (oldScheme != null) {
|
||||
LOG.warn("Duplicated scheme ${scheme.name} - old: $oldScheme, new $scheme")
|
||||
LOG.warn("Duplicated scheme ${schemeKey} - old: $oldScheme, new $scheme")
|
||||
}
|
||||
schemes.add(scheme)
|
||||
}
|
||||
@@ -307,17 +305,11 @@ class SchemeManagerImpl<T : Scheme, MUTABLE_SCHEME : T>(val fileSpec: String,
|
||||
}
|
||||
|
||||
private fun getFileExtension(fileName: CharSequence, allowAny: Boolean): String {
|
||||
return if (StringUtilRt.endsWithIgnoreCase(fileName, schemeExtension)) {
|
||||
schemeExtension
|
||||
}
|
||||
else if (StringUtilRt.endsWithIgnoreCase(fileName, DEFAULT_EXT)) {
|
||||
DEFAULT_EXT
|
||||
}
|
||||
else if (allowAny) {
|
||||
PathUtil.getFileExtension(fileName.toString())!!
|
||||
}
|
||||
else {
|
||||
throw IllegalStateException("Scheme file extension $fileName is unknown, must be filtered out")
|
||||
return when {
|
||||
StringUtilRt.endsWithIgnoreCase(fileName, schemeExtension) -> schemeExtension
|
||||
StringUtilRt.endsWithIgnoreCase(fileName, DEFAULT_EXT) -> DEFAULT_EXT
|
||||
allowAny -> PathUtil.getFileExtension(fileName.toString())!!
|
||||
else -> throw IllegalStateException("Scheme file extension $fileName is unknown, must be filtered out")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -335,7 +327,7 @@ class SchemeManagerImpl<T : Scheme, MUTABLE_SCHEME : T>(val fileSpec: String,
|
||||
catchAndLog(name) {
|
||||
val scheme = loadScheme(name, input, schemes, filesToDelete)
|
||||
if (readOnly && scheme != null) {
|
||||
readOnlyExternalizableSchemes.put(scheme.name, scheme)
|
||||
schemeListManager.readOnlyExternalizableSchemes.put(processor.getSchemeKey(scheme), scheme)
|
||||
}
|
||||
}
|
||||
true
|
||||
@@ -356,7 +348,7 @@ class SchemeManagerImpl<T : Scheme, MUTABLE_SCHEME : T>(val fileSpec: String,
|
||||
}
|
||||
|
||||
this.filesToDelete.addAll(filesToDelete)
|
||||
replaceSchemeList(oldSchemes, schemes)
|
||||
schemeListManager.replaceSchemeList(oldSchemes, schemes)
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
for (i in newSchemesOffset until schemes.size) {
|
||||
@@ -375,12 +367,6 @@ class SchemeManagerImpl<T : Scheme, MUTABLE_SCHEME : T>(val fileSpec: String,
|
||||
}
|
||||
}
|
||||
|
||||
private fun replaceSchemeList(oldList: ConcurrentList<T>, newList: List<T>) {
|
||||
if (!schemesRef.compareAndSet(oldList, ContainerUtil.createLockFreeCopyOnWriteList(newList) as ConcurrentList<T>)) {
|
||||
throw IllegalStateException("Scheme list was modified")
|
||||
}
|
||||
}
|
||||
|
||||
override fun reload() {
|
||||
processor.beforeReloaded(this)
|
||||
// we must not remove non-persistent (e.g. predefined) schemes, because we cannot load it (obviously)
|
||||
@@ -398,7 +384,7 @@ class SchemeManagerImpl<T : Scheme, MUTABLE_SCHEME : T>(val fileSpec: String,
|
||||
|
||||
currentScheme?.let {
|
||||
if (scheme === it) {
|
||||
currentPendingSchemeName = it.name
|
||||
currentPendingSchemeName = processor.getSchemeKey(it)
|
||||
currentScheme = null
|
||||
}
|
||||
}
|
||||
@@ -450,8 +436,8 @@ class SchemeManagerImpl<T : Scheme, MUTABLE_SCHEME : T>(val fileSpec: String,
|
||||
return true
|
||||
}
|
||||
|
||||
schemes.firstOrNull({ it.name == schemeName})?.let { existingScheme ->
|
||||
if (readOnlyExternalizableSchemes.get(existingScheme.name) === existingScheme) {
|
||||
schemes.firstOrNull({ processor.getSchemeKey(it) == schemeName})?.let { existingScheme ->
|
||||
if (schemeListManager.readOnlyExternalizableSchemes.get(processor.getSchemeKey(existingScheme)) === existingScheme) {
|
||||
// so, bundled scheme is shadowed
|
||||
removeFirstScheme(schemes, scheduleDelete = false) { it === existingScheme }
|
||||
return true
|
||||
@@ -460,7 +446,7 @@ class SchemeManagerImpl<T : Scheme, MUTABLE_SCHEME : T>(val fileSpec: String,
|
||||
removeFirstScheme(schemes) { it === existingScheme }
|
||||
}
|
||||
else {
|
||||
if (schemeExtension != extension && schemeToInfo.get(existingScheme as Scheme)?.fileNameWithoutExtension == fileNameWithoutExtension) {
|
||||
if (schemeExtension != extension && schemeToInfo.get(existingScheme)?.fileNameWithoutExtension == fileNameWithoutExtension) {
|
||||
// 1.oldExt is loading after 1.newExt - we should delete 1.oldExt
|
||||
filesToDelete.add(fileName)
|
||||
}
|
||||
@@ -508,12 +494,12 @@ class SchemeManagerImpl<T : Scheme, MUTABLE_SCHEME : T>(val fileSpec: String,
|
||||
else {
|
||||
val element = loadElement(input)
|
||||
scheme = (processor as NonLazySchemeProcessor).readScheme(element, duringLoad) ?: return null
|
||||
val schemeName = scheme!!.name
|
||||
if (!checkExisting(schemeName)) {
|
||||
val schemeKey = processor.getSchemeKey(scheme!!)
|
||||
if (!checkExisting(schemeKey)) {
|
||||
return null
|
||||
}
|
||||
|
||||
schemeToInfo.put(scheme, createInfo(schemeName, element))
|
||||
schemeToInfo.put(scheme, createInfo(schemeKey, element))
|
||||
this.filesToDelete.remove(fileName)
|
||||
}
|
||||
|
||||
@@ -638,7 +624,7 @@ class SchemeManagerImpl<T : Scheme, MUTABLE_SCHEME : T>(val fileSpec: String,
|
||||
|
||||
var fileNameWithoutExtension = currentFileNameWithoutExtension
|
||||
if (fileNameWithoutExtension == null || isRenamed(scheme)) {
|
||||
fileNameWithoutExtension = nameGenerator.generateUniqueName(schemeNameToFileName.schemeNameToFileName(scheme.name))
|
||||
fileNameWithoutExtension = nameGenerator.generateUniqueName(schemeNameToFileName.schemeNameToFileName(processor.getSchemeKey(scheme)))
|
||||
}
|
||||
|
||||
val newDigest = element!!.digest()
|
||||
@@ -672,6 +658,7 @@ class SchemeManagerImpl<T : Scheme, MUTABLE_SCHEME : T>(val fileSpec: String,
|
||||
}
|
||||
|
||||
// if another new scheme uses old name of this scheme, we must not delete it (as part of rename operation)
|
||||
@Suppress("SuspiciousEqualsCombination")
|
||||
val renamed = externalInfo != null && fileNameWithoutExtension !== currentFileNameWithoutExtension && currentFileNameWithoutExtension != null && nameGenerator.isUnique(currentFileNameWithoutExtension)
|
||||
if (providerPath == null) {
|
||||
if (useVfs) {
|
||||
@@ -726,7 +713,7 @@ class SchemeManagerImpl<T : Scheme, MUTABLE_SCHEME : T>(val fileSpec: String,
|
||||
externalInfo.setFileNameWithoutExtension(fileNameWithoutExtension, schemeExtension)
|
||||
}
|
||||
externalInfo.digest = newDigest
|
||||
externalInfo.schemeName = scheme.name
|
||||
externalInfo.schemeName = processor.getSchemeKey(scheme)
|
||||
}
|
||||
|
||||
private fun isEqualToBundledScheme(externalInfo: ExternalInfo?, newDigest: ByteArray, scheme: MUTABLE_SCHEME): Boolean {
|
||||
@@ -739,9 +726,9 @@ class SchemeManagerImpl<T : Scheme, MUTABLE_SCHEME : T>(val fileSpec: String,
|
||||
return null
|
||||
}
|
||||
|
||||
val bundledScheme = readOnlyExternalizableSchemes.get(scheme.name)
|
||||
val bundledScheme = schemeListManager.readOnlyExternalizableSchemes.get(processor.getSchemeKey(scheme))
|
||||
if (bundledScheme == null) {
|
||||
if ((processor as? LazySchemeProcessor)?.isSchemeEqualToBundled(scheme) ?: false) {
|
||||
if ((processor as? LazySchemeProcessor)?.isSchemeEqualToBundled(scheme) == true) {
|
||||
externalInfo?.scheduleDelete()
|
||||
return true
|
||||
}
|
||||
@@ -767,7 +754,7 @@ class SchemeManagerImpl<T : Scheme, MUTABLE_SCHEME : T>(val fileSpec: String,
|
||||
|
||||
private fun isRenamed(scheme: T): Boolean {
|
||||
val info = schemeToInfo.get(scheme)
|
||||
return info != null && scheme.name != info.schemeName
|
||||
return info != null && processor.getSchemeKey(scheme) != info.schemeName
|
||||
}
|
||||
|
||||
private fun deleteFiles(errors: MutableList<Throwable>, filesToDelete: MutableSet<String>) {
|
||||
@@ -816,52 +803,19 @@ class SchemeManagerImpl<T : Scheme, MUTABLE_SCHEME : T>(val fileSpec: String,
|
||||
return result
|
||||
}
|
||||
|
||||
override fun getRootDirectory(): File = ioDirectory.toFile()
|
||||
override val rootDirectory: File
|
||||
get() = ioDirectory.toFile()
|
||||
|
||||
override fun setSchemes(newSchemes: List<T>, newCurrentScheme: T?, removeCondition: Condition<T>?) {
|
||||
if (schemes.isNotEmpty()) {
|
||||
if (removeCondition == null) {
|
||||
schemes.clear()
|
||||
}
|
||||
else {
|
||||
// we must not use remove or removeAll to avoid "equals" call
|
||||
schemesRef.set(ContainerUtil.createConcurrentList(schemes.filterSmart { !removeCondition.value(it) }))
|
||||
}
|
||||
}
|
||||
override fun setSchemes(newSchemes: List<T>, newCurrentScheme: T?, removeCondition: Condition<T>?) = schemeListManager.setSchemes(newSchemes, newCurrentScheme, removeCondition)
|
||||
|
||||
schemes.addAll(newSchemes)
|
||||
|
||||
val oldCurrentScheme = currentScheme
|
||||
retainExternalInfo()
|
||||
|
||||
if (oldCurrentScheme != newCurrentScheme) {
|
||||
val newScheme: T?
|
||||
if (newCurrentScheme != null) {
|
||||
currentScheme = newCurrentScheme
|
||||
newScheme = newCurrentScheme
|
||||
}
|
||||
else if (oldCurrentScheme != null && !schemes.contains(oldCurrentScheme)) {
|
||||
newScheme = schemes.firstOrNull()
|
||||
currentScheme = newScheme
|
||||
}
|
||||
else {
|
||||
newScheme = null
|
||||
}
|
||||
|
||||
if (oldCurrentScheme != newScheme) {
|
||||
processor.onCurrentSchemeSwitched(oldCurrentScheme, newScheme)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun retainExternalInfo() {
|
||||
internal fun retainExternalInfo() {
|
||||
if (schemeToInfo.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
val iterator = schemeToInfo.entries.iterator()
|
||||
l@ for ((scheme, info) in iterator) {
|
||||
if (readOnlyExternalizableSchemes.get(scheme.name) == scheme) {
|
||||
if (schemeListManager.readOnlyExternalizableSchemes.get(processor.getSchemeKey(scheme)) == scheme) {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -881,7 +835,7 @@ class SchemeManagerImpl<T : Scheme, MUTABLE_SCHEME : T>(val fileSpec: String,
|
||||
var toReplace = -1
|
||||
val schemes = schemes
|
||||
for ((index, existing) in schemes.withIndex()) {
|
||||
if (existing.name != scheme.name) {
|
||||
if (processor.getSchemeKey(existing) != processor.getSchemeKey(scheme)) {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -892,7 +846,7 @@ class SchemeManagerImpl<T : Scheme, MUTABLE_SCHEME : T>(val fileSpec: String,
|
||||
}
|
||||
|
||||
if (existing.javaClass != scheme.javaClass) {
|
||||
LOG.warn("'${scheme.name}' ${existing.javaClass.simpleName} replaced with ${scheme.javaClass.simpleName}")
|
||||
LOG.warn("'${processor.getSchemeKey(scheme)}' ${existing.javaClass.simpleName} replaced with ${scheme.javaClass.simpleName}")
|
||||
}
|
||||
|
||||
if (replaceExisting && processor.isExternalizable(existing)) {
|
||||
@@ -912,7 +866,7 @@ class SchemeManagerImpl<T : Scheme, MUTABLE_SCHEME : T>(val fileSpec: String,
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
(scheme as ExternalizableScheme).renameScheme(UniqueNameGenerator.generateUniqueName(scheme.name, collectExistingNames(schemes)))
|
||||
(scheme as ExternalizableScheme).renameScheme(UniqueNameGenerator.generateUniqueName(scheme.name, schemeListManager.collectExistingNames(schemes)))
|
||||
schemes.add(scheme)
|
||||
}
|
||||
}
|
||||
@@ -926,12 +880,6 @@ class SchemeManagerImpl<T : Scheme, MUTABLE_SCHEME : T>(val fileSpec: String,
|
||||
processPendingCurrentSchemeName(scheme)
|
||||
}
|
||||
|
||||
private fun collectExistingNames(schemes: Collection<T>): Collection<String> {
|
||||
val result = THashSet<String>(schemes.size)
|
||||
schemes.mapTo(result) { it.name }
|
||||
return result
|
||||
}
|
||||
|
||||
override fun clearAllSchemes() {
|
||||
for (it in schemeToInfo.values) {
|
||||
it.scheduleDelete()
|
||||
@@ -942,43 +890,15 @@ class SchemeManagerImpl<T : Scheme, MUTABLE_SCHEME : T>(val fileSpec: String,
|
||||
schemeToInfo.clear()
|
||||
}
|
||||
|
||||
override fun getAllSchemes(): List<T> = Collections.unmodifiableList(schemes)
|
||||
override val allSchemes: List<T>
|
||||
get() = Collections.unmodifiableList(schemes)
|
||||
|
||||
override fun isEmpty() = schemes.isEmpty()
|
||||
override val isEmpty: Boolean
|
||||
get() = schemes.isEmpty()
|
||||
|
||||
override fun findSchemeByName(schemeName: String) = schemes.firstOrNull { it.name == schemeName }
|
||||
override fun findSchemeByName(schemeName: String) = schemes.firstOrNull { processor.getSchemeKey(it) == schemeName }
|
||||
|
||||
override fun setCurrent(scheme: T?, notify: Boolean) {
|
||||
currentPendingSchemeName = null
|
||||
|
||||
val oldCurrent = currentScheme
|
||||
currentScheme = scheme
|
||||
if (notify && oldCurrent !== scheme) {
|
||||
processor.onCurrentSchemeSwitched(oldCurrent, scheme)
|
||||
}
|
||||
}
|
||||
|
||||
override fun setCurrentSchemeName(schemeName: String?, notify: Boolean) {
|
||||
currentPendingSchemeName = schemeName
|
||||
|
||||
val scheme = if (schemeName == null) null else findSchemeByName(schemeName)
|
||||
// don't set current scheme if no scheme by name - pending resolution (see currentSchemeName field comment)
|
||||
if (scheme != null || schemeName == null) {
|
||||
setCurrent(scheme, notify)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getCurrentScheme() = currentScheme
|
||||
|
||||
override fun getCurrentSchemeName() = currentScheme?.name ?: currentPendingSchemeName
|
||||
|
||||
private fun processPendingCurrentSchemeName(newScheme: T) {
|
||||
if (newScheme.name == currentPendingSchemeName) {
|
||||
setCurrent(newScheme, false)
|
||||
}
|
||||
}
|
||||
|
||||
override fun removeScheme(schemeName: String) = removeFirstScheme(schemes) {it.name == schemeName}
|
||||
override fun removeScheme(name: String) = removeFirstScheme(schemes) {processor.getSchemeKey(it) == name }
|
||||
|
||||
override fun removeScheme(scheme: T) = removeFirstScheme(schemes) { it == scheme } != null
|
||||
|
||||
@@ -1004,9 +924,7 @@ class SchemeManagerImpl<T : Scheme, MUTABLE_SCHEME : T>(val fileSpec: String,
|
||||
return null
|
||||
}
|
||||
|
||||
override fun getAllSchemeNames() = schemes.let { if (it.isEmpty()) emptyList() else it.map { it.name } }
|
||||
|
||||
override fun isMetadataEditable(scheme: T) = !readOnlyExternalizableSchemes.containsKey(scheme.name)
|
||||
override fun isMetadataEditable(scheme: T) = !schemeListManager.readOnlyExternalizableSchemes.containsKey(processor.getSchemeKey(scheme))
|
||||
|
||||
override fun toString() = fileSpec
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.intellij.configurationStore.schemeManager
|
||||
|
||||
import com.intellij.configurationStore.SchemeManagerImpl
|
||||
import com.intellij.openapi.util.Condition
|
||||
import com.intellij.util.containers.ConcurrentList
|
||||
import com.intellij.util.containers.ContainerUtil
|
||||
import com.intellij.util.containers.filterSmart
|
||||
import gnu.trove.THashSet
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
|
||||
internal class SchemeListManager<T : Any>(private val schemeManager: SchemeManagerImpl<T, *>) {
|
||||
val schemesRef = AtomicReference(ContainerUtil.createLockFreeCopyOnWriteList<T>() as ConcurrentList<T>)
|
||||
|
||||
val readOnlyExternalizableSchemes = ContainerUtil.newConcurrentMap<String, T>()
|
||||
|
||||
val schemes: ConcurrentList<T>
|
||||
get() = schemesRef.get()
|
||||
|
||||
fun replaceSchemeList(oldList: ConcurrentList<T>, newList: List<T>) {
|
||||
if (!schemesRef.compareAndSet(oldList, ContainerUtil.createLockFreeCopyOnWriteList(newList) as ConcurrentList<T>)) {
|
||||
throw IllegalStateException("Scheme list was modified")
|
||||
}
|
||||
}
|
||||
|
||||
fun setSchemes(newSchemes: List<T>, newCurrentScheme: T?, removeCondition: Condition<T>?) {
|
||||
if (schemes.isNotEmpty()) {
|
||||
if (removeCondition == null) {
|
||||
schemes.clear()
|
||||
}
|
||||
else {
|
||||
// we must not use remove or removeAll to avoid "equals" call
|
||||
schemesRef.set(ContainerUtil.createConcurrentList(schemes.filterSmart { !removeCondition.value(it) }))
|
||||
}
|
||||
}
|
||||
|
||||
schemes.addAll(newSchemes)
|
||||
|
||||
val oldCurrentScheme = schemeManager.currentScheme
|
||||
schemeManager.retainExternalInfo()
|
||||
|
||||
if (oldCurrentScheme != newCurrentScheme) {
|
||||
val newScheme: T?
|
||||
if (newCurrentScheme != null) {
|
||||
schemeManager.currentScheme = newCurrentScheme
|
||||
newScheme = newCurrentScheme
|
||||
}
|
||||
else if (oldCurrentScheme != null && !schemes.contains(oldCurrentScheme)) {
|
||||
newScheme = schemes.firstOrNull()
|
||||
schemeManager.currentScheme = newScheme
|
||||
}
|
||||
else {
|
||||
newScheme = null
|
||||
}
|
||||
|
||||
if (oldCurrentScheme != newScheme) {
|
||||
schemeManager.processor.onCurrentSchemeSwitched(oldCurrentScheme, newScheme)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun collectExistingNames(schemes: Collection<T>): Collection<String> {
|
||||
val result = THashSet<String>(schemes.size)
|
||||
schemes.mapTo(result) { schemeManager.processor.getSchemeKey(it) }
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright 2000-2017 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.configurationStore.schemeManager
|
||||
|
||||
import com.intellij.openapi.options.SchemeProcessor
|
||||
import com.intellij.openapi.options.SchemesManager
|
||||
|
||||
abstract class SchemeManagerBase<T : Any, in MUTABLE_SCHEME : T>(internal val processor: SchemeProcessor<T, MUTABLE_SCHEME>) : SchemesManager<T>() {
|
||||
/**
|
||||
* Schemes can be lazy loaded, so, client should be able to set current scheme by name, not only by instance.
|
||||
*/
|
||||
@Volatile
|
||||
protected var currentPendingSchemeName: String? = null
|
||||
|
||||
override var currentScheme: T? = null
|
||||
set(value) {
|
||||
field = value
|
||||
currentSchemeName = value?.let { processor.getSchemeKey(it) }
|
||||
}
|
||||
|
||||
override var currentSchemeName: String?
|
||||
get() = currentScheme?.let { processor.getSchemeKey(it) } ?: currentPendingSchemeName
|
||||
set(schemeName) = setCurrentSchemeName(schemeName, true)
|
||||
|
||||
protected fun processPendingCurrentSchemeName(newScheme: T) {
|
||||
if (processor.getSchemeKey(newScheme) == currentPendingSchemeName) {
|
||||
setCurrent(newScheme, false)
|
||||
}
|
||||
}
|
||||
|
||||
override fun setCurrent(scheme: T?, notify: Boolean) {
|
||||
currentPendingSchemeName = null
|
||||
|
||||
val oldCurrent = currentScheme
|
||||
currentScheme = scheme
|
||||
if (notify && oldCurrent !== scheme) {
|
||||
processor.onCurrentSchemeSwitched(oldCurrent, scheme)
|
||||
}
|
||||
}
|
||||
|
||||
override fun setCurrentSchemeName(schemeName: String?, notify: Boolean) {
|
||||
currentPendingSchemeName = schemeName
|
||||
|
||||
val scheme = schemeName?.let { findSchemeByName(it) }
|
||||
// don't set current scheme if no scheme by name - pending resolution (see currentSchemeName field comment)
|
||||
if (scheme != null || schemeName == null) {
|
||||
setCurrent(scheme, notify)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
// Copyright 2000-2017 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.
|
||||
// Copyright 2000-2017 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.configurationStore
|
||||
|
||||
import com.intellij.openapi.extensions.AbstractExtensionPointBean
|
||||
@@ -33,7 +31,7 @@ val MODERN_NAME_CONVERTER = object : SchemeNameToFileName {
|
||||
override fun schemeNameToFileName(name: String) = sanitizeFileName(name)
|
||||
}
|
||||
|
||||
interface SchemeDataHolder<in T : Scheme> {
|
||||
interface SchemeDataHolder<in T> {
|
||||
/**
|
||||
* You should call updateDigest() after read on init.
|
||||
*/
|
||||
@@ -56,7 +54,7 @@ interface SchemeExtensionProvider {
|
||||
}
|
||||
|
||||
// applicable only for LazySchemeProcessor
|
||||
interface SchemeContentChangedHandler<MUTABLE_SCHEME : Scheme> {
|
||||
interface SchemeContentChangedHandler<MUTABLE_SCHEME> {
|
||||
fun schemeContentChanged(scheme: MUTABLE_SCHEME, name: String, dataHolder: SchemeDataHolder<MUTABLE_SCHEME>)
|
||||
}
|
||||
|
||||
|
||||
+27
-20
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2017 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.openapi.options;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -23,10 +9,10 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class EmptySchemesManager extends SchemeManager {
|
||||
public class EmptySchemesManager extends SchemeManager<Scheme> {
|
||||
@Override
|
||||
@NotNull
|
||||
public Collection loadSchemes() {
|
||||
public Collection<Scheme> loadSchemes() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@@ -40,7 +26,7 @@ public class EmptySchemesManager extends SchemeManager {
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public List getAllSchemes() {
|
||||
public List<Scheme> getAllSchemes() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@@ -49,8 +35,9 @@ public class EmptySchemesManager extends SchemeManager {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Scheme getCurrentScheme() {
|
||||
public String getCurrentSchemeName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -61,16 +48,36 @@ public class EmptySchemesManager extends SchemeManager {
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Collection getAllSchemeNames() {
|
||||
public Collection<String> getAllSchemeNames() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public File getRootDirectory() {
|
||||
//noinspection ConstantConditions
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCurrentSchemeName(@Nullable String schemeName, boolean notify) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCurrent(@Nullable Scheme scheme, boolean notify) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCurrentSchemeName(@Nullable String s) {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Scheme getCurrentScheme() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCurrentScheme(@Nullable Scheme scheme) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2017 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.openapi.options
|
||||
|
||||
import com.intellij.openapi.util.Condition
|
||||
@@ -39,8 +25,7 @@ abstract class SchemeManager<T> {
|
||||
open val isEmpty: Boolean
|
||||
get() = allSchemes.isEmpty()
|
||||
|
||||
var currentScheme: T? = null
|
||||
protected set
|
||||
abstract var currentScheme: T?
|
||||
|
||||
/**
|
||||
* If schemes are lazy loaded, you can use this method to postpone scheme selection (scheme will be found by name on first use)
|
||||
|
||||
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2017 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.openapi.options
|
||||
|
||||
import com.intellij.configurationStore.CURRENT_NAME_CONVERTER
|
||||
@@ -25,7 +11,7 @@ import org.jdom.Parent
|
||||
import java.nio.file.Path
|
||||
|
||||
@Deprecated("Please use SchemeManager")
|
||||
abstract class SchemesManager<T : Scheme> : SchemeManager<T>()
|
||||
abstract class SchemesManager<T> : SchemeManager<T>()
|
||||
|
||||
interface ExternalizableScheme : Scheme {
|
||||
fun setName(value: String)
|
||||
@@ -64,7 +50,11 @@ enum class SchemeState {
|
||||
UNCHANGED, NON_PERSISTENT, POSSIBLY_CHANGED
|
||||
}
|
||||
|
||||
abstract class SchemeProcessor<SCHEME : Scheme, MUTABLE_SCHEME: SCHEME> {
|
||||
abstract class SchemeProcessor<SCHEME, in MUTABLE_SCHEME: SCHEME> {
|
||||
open fun getSchemeKey(scheme: SCHEME): String {
|
||||
return (scheme as Scheme).name
|
||||
}
|
||||
|
||||
open fun isExternalizable(scheme: SCHEME) = scheme is ExternalizableScheme
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user