mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-19 21:11:28 +07:00
IJPL-158097 refactor Registry
GitOrigin-RevId: fc12759c96be04ffcca4e0df12f5d2502556d28d
This commit is contained in:
committed by
intellij-monorepo-bot
parent
f83776b01b
commit
dc3c3f53ab
@@ -11805,10 +11805,9 @@ f:com.intellij.openapi.actionSystem.impl.ActionMenuItem
|
||||
- f:updateFromPresentation(com.intellij.openapi.actionSystem.Presentation):V
|
||||
- updateUI():V
|
||||
c:com.intellij.openapi.actionSystem.impl.ActionToolbarImpl
|
||||
- javax.swing.JPanel
|
||||
- com.intellij.openapi.actionSystem.ActionToolbar
|
||||
- com.intellij.ui.switcher.QuickActionProvider
|
||||
- com.intellij.util.animation.AlphaAnimated
|
||||
- javax.swing.JPanel
|
||||
- sf:DO_NOT_ADD_CUSTOMIZATION_HANDLER:java.lang.String
|
||||
- sf:IMPORTANT_TOOLBAR_KEY:java.lang.String
|
||||
- sf:SUPPRESS_FAST_TRACK:java.lang.String
|
||||
@@ -28697,28 +28696,6 @@ com.intellij.util.User32Ex
|
||||
- a:SystemParametersInfo(com.sun.jna.platform.win32.WinDef$UINT,com.sun.jna.platform.win32.WinDef$UINT,com.sun.jna.platform.win32.WinDef$BOOLByReference,com.sun.jna.platform.win32.WinDef$UINT):Z
|
||||
- a:SystemParametersInfo(com.sun.jna.platform.win32.WinDef$UINT,com.sun.jna.platform.win32.WinDef$UINT,com.sun.jna.platform.win32.WinDef$UINT,com.sun.jna.platform.win32.WinDef$UINT):Z
|
||||
- a:SystemParametersInfo(com.sun.jna.platform.win32.WinDef$UINT,com.sun.jna.platform.win32.WinDef$UINT,com.sun.jna.platform.win32.WinDef$UINTByReference,com.sun.jna.platform.win32.WinDef$UINT):Z
|
||||
*:com.intellij.util.animation.AlphaAnimated
|
||||
- a:getAlphaContext():com.intellij.util.animation.AlphaAnimationContext
|
||||
*f:com.intellij.util.animation.AlphaAnimationContext
|
||||
- <init>(java.awt.AlphaComposite,java.util.function.Consumer):V
|
||||
- <init>(java.awt.Component):V
|
||||
- <init>(java.util.function.Consumer):V
|
||||
- f:getAnimator():com.intellij.util.animation.ShowHideAnimator
|
||||
- f:getComponent():java.awt.Component
|
||||
- f:getComposite():java.awt.AlphaComposite
|
||||
- f:getConsumer():java.util.function.Consumer
|
||||
- f:getDisposable():com.intellij.openapi.Disposable
|
||||
- f:isVisible():Z
|
||||
- f:paint(java.awt.Graphics,java.lang.Runnable):V
|
||||
- f:paintWithComposite(java.awt.Graphics2D,java.lang.Runnable):V
|
||||
- f:setComponent(java.awt.Component):V
|
||||
- f:setVisible(Z):V
|
||||
*c:com.intellij.util.animation.ShowHideAnimator
|
||||
- <init>(com.intellij.util.animation.Easing,java.util.function.DoubleConsumer):V
|
||||
- <init>(java.util.function.DoubleConsumer):V
|
||||
- f:getDisposable():com.intellij.openapi.Disposable
|
||||
- f:setVisible(Z,kotlin.jvm.functions.Function0):V
|
||||
- f:setVisibleImmediately(Z):V
|
||||
a:com.intellij.util.concurrency.Invoker
|
||||
- com.intellij.openapi.Disposable
|
||||
- f:compute(java.util.function.Supplier):org.jetbrains.concurrency.CancellablePromise
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.openapi.util.registry
|
||||
|
||||
import com.intellij.diagnostic.runActivity
|
||||
@@ -73,7 +73,7 @@ internal class RegistryManagerImpl : PersistentStateComponent<Element>, Registry
|
||||
Registry.setValueChangeListener(defaultValueChangeListener)
|
||||
}
|
||||
|
||||
override fun getState(): Element = Registry.getInstance().state
|
||||
override fun getState(): Element = Registry.getInstance().getState()
|
||||
|
||||
override fun noStateLoaded() {
|
||||
Registry.loadState(/* state = */ null, /* earlyAccess = */ EarlyAccessRegistryManager.getOrLoadMap())
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
@file:Suppress("ReplaceGetOrSet")
|
||||
|
||||
package com.intellij.openapi.util.registry
|
||||
|
||||
import com.intellij.ide.util.PropertiesComponent
|
||||
@@ -19,7 +21,7 @@ internal suspend fun migrateRegistryToAdvSettings() {
|
||||
return
|
||||
}
|
||||
|
||||
val userProperties = Registry.getInstance().userProperties
|
||||
val userProperties = Registry.getInstance().getUserProperties()
|
||||
for (setting in AdvancedSettingBean.EP_NAME.extensionList) {
|
||||
when (setting.id) {
|
||||
"editor.tab.painting" -> migrateEditorTabPainting(userProperties, setting)
|
||||
@@ -29,7 +31,7 @@ internal suspend fun migrateRegistryToAdvSettings() {
|
||||
val userProperty = userProperties[setting.id] ?: continue
|
||||
try {
|
||||
AdvancedSettings.getInstance().setSetting(setting.id, setting.valueFromString(userProperty), setting.type())
|
||||
userProperties -= setting.id
|
||||
userProperties.remove(setting.id)
|
||||
}
|
||||
catch (_: IllegalArgumentException) { }
|
||||
}
|
||||
@@ -40,11 +42,11 @@ internal suspend fun migrateRegistryToAdvSettings() {
|
||||
|
||||
private fun migrateEditorTabPainting(userProperties: MutableMap<String, String>, setting: AdvancedSettingBean) {
|
||||
val mode = if (userProperties["editor.old.tab.painting"] == "true") {
|
||||
userProperties -= "editor.old.tab.painting"
|
||||
userProperties.remove("editor.old.tab.painting")
|
||||
TabCharacterPaintMode.LONG_ARROW
|
||||
}
|
||||
else if (userProperties["editor.arrow.tab.painting"] == "true") {
|
||||
userProperties -= "editor.arrow.tab.painting"
|
||||
userProperties.remove("editor.arrow.tab.painting")
|
||||
TabCharacterPaintMode.ARROW
|
||||
}
|
||||
else {
|
||||
@@ -55,13 +57,13 @@ private fun migrateEditorTabPainting(userProperties: MutableMap<String, String>,
|
||||
|
||||
private fun migrateVcsIgnoreProcessing(userProperties: MutableMap<String, String>, setting: AdvancedSettingBean) {
|
||||
if (userProperties["git.process.ignored"] == "false") {
|
||||
userProperties -= "git.process.ignored"
|
||||
userProperties.remove("git.process.ignored")
|
||||
}
|
||||
else if (userProperties["hg4idea.process.ignored"] == "false") {
|
||||
userProperties -= "hg4idea.process.ignored"
|
||||
userProperties.remove("hg4idea.process.ignored")
|
||||
}
|
||||
else if (userProperties["p4.process.ignored"] == "false") {
|
||||
userProperties -= "p4.process.ignored"
|
||||
userProperties.remove("p4.process.ignored")
|
||||
}
|
||||
else {
|
||||
return
|
||||
@@ -71,11 +73,11 @@ private fun migrateVcsIgnoreProcessing(userProperties: MutableMap<String, String
|
||||
|
||||
private fun migrateNativeChooser(userProperties: MutableMap<String, String>, setting: AdvancedSettingBean) {
|
||||
val enabled = when {
|
||||
SystemInfo.isWindows -> userProperties["ide.win.file.chooser.native"] ?: System.getProperty("ide.win.file.chooser.native")
|
||||
SystemInfo.isMac -> userProperties["ide.mac.file.chooser.native"] ?: System.getProperty("ide.mac.file.chooser.native") ?: "true"
|
||||
SystemInfo.isWindows -> userProperties.get("ide.win.file.chooser.native") ?: System.getProperty("ide.win.file.chooser.native")
|
||||
SystemInfo.isMac -> userProperties.get("ide.mac.file.chooser.native") ?: System.getProperty("ide.mac.file.chooser.native") ?: "true"
|
||||
else -> null
|
||||
} ?: return
|
||||
userProperties -= "ide.win.file.chooser.native"
|
||||
userProperties -= "ide.mac.file.chooser.native"
|
||||
userProperties.remove("ide.win.file.chooser.native")
|
||||
userProperties.remove("ide.mac.file.chooser.native")
|
||||
AdvancedSettings.getInstance().setSetting(setting.id, enabled.toBoolean(), setting.type())
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.util.animation
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus.Experimental
|
||||
|
||||
@Experimental
|
||||
interface AlphaAnimated {
|
||||
internal interface AlphaAnimated {
|
||||
val alphaContext: AlphaAnimationContext
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.util.animation
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
@@ -8,7 +8,7 @@ import java.awt.Graphics
|
||||
import java.awt.Graphics2D
|
||||
import java.util.function.Consumer
|
||||
|
||||
class AlphaAnimationContext(private val base: AlphaComposite, val consumer: Consumer<AlphaComposite?>) {
|
||||
internal class AlphaAnimationContext(private val base: AlphaComposite, private val consumer: Consumer<AlphaComposite?>) {
|
||||
constructor(consumer: Consumer<AlphaComposite?>) : this(AlphaComposite.SrcOver, consumer)
|
||||
constructor(component: Component) : this({ if (component.isShowing) component.repaint() }) {
|
||||
this.component = component
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.util.animation
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.util.registry.Registry.intValue
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import java.util.function.DoubleConsumer
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
open class ShowHideAnimator(easing: Easing, private val consumer: DoubleConsumer) {
|
||||
internal open class ShowHideAnimator(easing: Easing, private val consumer: DoubleConsumer) {
|
||||
private val animator = JBAnimator()
|
||||
private val atomicVisible = AtomicBoolean()
|
||||
private val statefulEasing = easing.stateful()
|
||||
@@ -38,16 +38,16 @@ open class ShowHideAnimator(easing: Easing, private val consumer: DoubleConsumer
|
||||
}
|
||||
|
||||
private val showingDelay
|
||||
get() = intValue("ide.animation.showing.delay", 0)
|
||||
get() = Registry.intValue("ide.animation.showing.delay", 0)
|
||||
|
||||
private val showingDuration
|
||||
get() = intValue("ide.animation.showing.duration", 130)
|
||||
get() = Registry.intValue("ide.animation.showing.duration", 130)
|
||||
|
||||
private val hidingDelay
|
||||
get() = intValue("ide.animation.hiding.delay", 140)
|
||||
get() = Registry.intValue("ide.animation.hiding.delay", 140)
|
||||
|
||||
private val hidingDuration
|
||||
get() = intValue("ide.animation.hiding.duration", 150)
|
||||
get() = Registry.intValue("ide.animation.hiding.duration", 150)
|
||||
|
||||
private fun createShowingAnimation(value: Double, updateVisibility: () -> Unit) = Animation(consumer).apply {
|
||||
if (value > 0.0) {
|
||||
@@ -60,7 +60,8 @@ open class ShowHideAnimator(easing: Easing, private val consumer: DoubleConsumer
|
||||
easing = statefulEasing
|
||||
}
|
||||
}.runWhenScheduled {
|
||||
if (atomicVisible.get()) { // Most likely not needed, just for consistency with hide. In the worst case we just avoid minor flickering here.
|
||||
if (atomicVisible.get()) { // Most likely not needed, just for consistency with hide.
|
||||
// In the worst case, we just avoid minor flickering here.
|
||||
updateVisibility()
|
||||
}
|
||||
}
|
||||
@@ -76,7 +77,7 @@ open class ShowHideAnimator(easing: Easing, private val consumer: DoubleConsumer
|
||||
easing = statefulEasing.reverse()
|
||||
}
|
||||
}.runWhenExpiredOrCancelled {
|
||||
if (!atomicVisible.get()) { // If the animation is cancelled and the component was already made visible, we do NOT want to hide it again!
|
||||
if (!atomicVisible.get()) { // If the animation is canceled and the component was already made visible, we do NOT want to hide it again!
|
||||
updateVisibility()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.testFramework;
|
||||
|
||||
import com.intellij.concurrency.IdeaForkJoinWorkerThreadFactory;
|
||||
@@ -139,7 +139,7 @@ public abstract class ParsingTestCase extends UsefulTestCase {
|
||||
|
||||
// That's for reparse routines
|
||||
project.registerService(PomModel.class, new PomModelImpl(project));
|
||||
Registry.markAsLoaded();
|
||||
Registry.Companion.markAsLoaded();
|
||||
LoadingState.setCurrentState(LoadingState.PROJECT_OPENED);
|
||||
}
|
||||
|
||||
|
||||
@@ -1644,23 +1644,42 @@ f:com.intellij.openapi.util.io.WindowsRegistryUtil
|
||||
- s:readRegistryDefault(java.lang.String):java.lang.String
|
||||
- s:readRegistryValue(java.lang.String,java.lang.String):java.lang.String
|
||||
f:com.intellij.openapi.util.registry.Registry
|
||||
- sf:Companion:com.intellij.openapi.util.registry.Registry$Companion
|
||||
- sf:REGISTRY_BUNDLE:java.lang.String
|
||||
- <init>():V
|
||||
- s:doubleValue(java.lang.String):D
|
||||
- s:doubleValue(java.lang.String,D):D
|
||||
- s:get(java.lang.String):com.intellij.openapi.util.registry.RegistryValue
|
||||
- getBundleValueOrNull(java.lang.String):java.lang.String
|
||||
- s:getColor(java.lang.String,java.awt.Color):java.awt.Color
|
||||
- s:getInstance():com.intellij.openapi.util.registry.Registry
|
||||
- getState():org.jdom.Element
|
||||
- s:intValue(java.lang.String):I
|
||||
- s:intValue(java.lang.String,I):I
|
||||
- s:intValue(java.lang.String,I,I,I):I
|
||||
- s:is(java.lang.String):Z
|
||||
- s:is(java.lang.String,Z):Z
|
||||
- isLoaded():Z
|
||||
- isRestartNeeded():Z
|
||||
- s:stringValue(java.lang.String):java.lang.String
|
||||
- sf:doubleValue(java.lang.String):D
|
||||
- sf:doubleValue(java.lang.String,D):D
|
||||
- sf:get(java.lang.String):com.intellij.openapi.util.registry.RegistryValue
|
||||
- f:getBundleValue(java.lang.String):java.lang.String
|
||||
- f:getBundleValueOrNull(java.lang.String):java.lang.String
|
||||
- sf:getColor(java.lang.String,java.awt.Color):java.awt.Color
|
||||
- sf:getInstance():com.intellij.openapi.util.registry.Registry
|
||||
- f:getState():org.jdom.Element
|
||||
- f:getValueChangeListener():com.intellij.openapi.util.registry.RegistryValueListener
|
||||
- sf:intValue(java.lang.String):I
|
||||
- sf:intValue(java.lang.String,I):I
|
||||
- sf:intValue(java.lang.String,I,I,I):I
|
||||
- sf:is(java.lang.String):Z
|
||||
- sf:is(java.lang.String,Z):Z
|
||||
- f:isInDefaultState():Z
|
||||
- f:isLoaded():Z
|
||||
- f:isRestartNeeded():Z
|
||||
- f:reset():V
|
||||
- f:restoreDefaults():V
|
||||
- sf:stringValue(java.lang.String):java.lang.String
|
||||
f:com.intellij.openapi.util.registry.Registry$Companion
|
||||
- f:awaitLoad():java.util.concurrent.CompletionStage
|
||||
- f:doubleValue(java.lang.String):D
|
||||
- f:doubleValue(java.lang.String,D):D
|
||||
- f:get(java.lang.String):com.intellij.openapi.util.registry.RegistryValue
|
||||
- f:getColor(java.lang.String,java.awt.Color):java.awt.Color
|
||||
- f:getInstance():com.intellij.openapi.util.registry.Registry
|
||||
- f:intValue(java.lang.String):I
|
||||
- f:intValue(java.lang.String,I):I
|
||||
- f:intValue(java.lang.String,I,I,I):I
|
||||
- f:is(java.lang.String):Z
|
||||
- f:is(java.lang.String,Z):Z
|
||||
- f:stringValue(java.lang.String):java.lang.String
|
||||
c:com.intellij.openapi.util.registry.RegistryValue
|
||||
- addListener(com.intellij.openapi.util.registry.RegistryValueListener,com.intellij.openapi.Disposable):V
|
||||
- addListener(com.intellij.openapi.util.registry.RegistryValueListener,kotlinx.coroutines.CoroutineScope):V
|
||||
|
||||
@@ -1,402 +1,382 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.openapi.util.registry;
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
@file:Suppress("ReplaceGetOrSet", "ReplacePutWithAssignment")
|
||||
|
||||
import com.intellij.diagnostic.LoadingState;
|
||||
import com.intellij.openapi.util.NlsSafe;
|
||||
import com.intellij.util.MathUtil;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.*;
|
||||
package com.intellij.openapi.util.registry
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
import com.intellij.diagnostic.LoadingState
|
||||
import com.intellij.openapi.util.NlsSafe
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import org.jdom.Element
|
||||
import org.jetbrains.annotations.ApiStatus.Internal
|
||||
import org.jetbrains.annotations.NonNls
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import java.awt.Color
|
||||
import java.io.IOException
|
||||
import java.lang.ref.Reference
|
||||
import java.lang.ref.SoftReference
|
||||
import java.util.*
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import kotlin.concurrent.Volatile
|
||||
|
||||
/**
|
||||
* Provides a UI to configure internal settings of the IDE.
|
||||
* <p>
|
||||
*
|
||||
*
|
||||
* Plugins can provide their own registry keys using the
|
||||
* {@code com.intellij.registryKey} extension point (see {@link RegistryKeyBean} for more details).
|
||||
* `com.intellij.registryKey` extension point (see [com.intellij.openapi.util.registry.RegistryKeyBean] for more details).
|
||||
*/
|
||||
public final class Registry {
|
||||
private static Reference<Map<String, String>> bundledRegistry;
|
||||
class Registry {
|
||||
private val userProperties = LinkedHashMap<String, String>()
|
||||
private val values = ConcurrentHashMap<String, RegistryValue>()
|
||||
private var contributedKeys = emptyMap<String, RegistryKeyDescriptor>()
|
||||
|
||||
public static final @NonNls String REGISTRY_BUNDLE = "misc.registry";
|
||||
@Volatile
|
||||
var isLoaded: Boolean = false
|
||||
private set
|
||||
|
||||
private static final RegistryValueListener EMPTY_VALUE_LISTENER = new RegistryValueListener() {
|
||||
};
|
||||
@Volatile
|
||||
private var loadFuture = CompletableFuture<Void?>()
|
||||
|
||||
private final Map<String, String> myUserProperties = new LinkedHashMap<>();
|
||||
private final Map<String, RegistryValue> myValues = new ConcurrentHashMap<>();
|
||||
private Map<String, RegistryKeyDescriptor> myContributedKeys = Collections.emptyMap();
|
||||
@Volatile
|
||||
var valueChangeListener: RegistryValueListener = EMPTY_VALUE_LISTENER
|
||||
private set
|
||||
|
||||
private static final Registry ourInstance = new Registry();
|
||||
private volatile boolean isLoaded;
|
||||
private volatile CompletableFuture<Void> myLoadFuture = new CompletableFuture<>();
|
||||
companion object {
|
||||
private var bundledRegistry: Reference<Map<String, String>>? = null
|
||||
|
||||
private volatile @NotNull RegistryValueListener valueChangeListener = EMPTY_VALUE_LISTENER;
|
||||
const val REGISTRY_BUNDLE: @NonNls String = "misc.registry"
|
||||
|
||||
public static @NotNull RegistryValue get(@NonNls @NotNull String key) {
|
||||
return getInstance().doGet(key);
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static @NotNull RegistryValue _getWithoutStateCheck(@NonNls @NotNull String key) {
|
||||
return ourInstance.doGet(key);
|
||||
}
|
||||
|
||||
private @NotNull RegistryValue doGet(@NonNls @NotNull String key) {
|
||||
return myValues.computeIfAbsent(key, s -> new RegistryValue(this, s, myContributedKeys.get(s)));
|
||||
}
|
||||
|
||||
public static boolean is(@NonNls @NotNull String key) throws MissingResourceException {
|
||||
return get(key).asBoolean();
|
||||
}
|
||||
|
||||
public static boolean is(@NonNls @NotNull String key, boolean defaultValue) {
|
||||
if (!LoadingState.COMPONENTS_LOADED.isOccurred()) {
|
||||
return defaultValue;
|
||||
private val EMPTY_VALUE_LISTENER: RegistryValueListener = object : RegistryValueListener {
|
||||
}
|
||||
|
||||
try {
|
||||
return getInstance().doGet(key).asBoolean();
|
||||
private val registry = Registry()
|
||||
|
||||
@JvmStatic
|
||||
fun get(key: @NonNls String): RegistryValue = getInstance().doGet(key)
|
||||
|
||||
@Suppress("FunctionName")
|
||||
@Internal
|
||||
@JvmStatic
|
||||
fun _getWithoutStateCheck(key: @NonNls String): RegistryValue = registry.doGet(key)
|
||||
|
||||
@Throws(MissingResourceException::class)
|
||||
@JvmStatic
|
||||
fun `is`(key: @NonNls String): Boolean = get(key).asBoolean()
|
||||
|
||||
@JvmStatic
|
||||
fun `is`(key: @NonNls String, defaultValue: Boolean): Boolean {
|
||||
if (!LoadingState.COMPONENTS_LOADED.isOccurred) {
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
try {
|
||||
return registry.doGet(key).asBoolean()
|
||||
}
|
||||
catch (ignore: MissingResourceException) {
|
||||
return defaultValue
|
||||
}
|
||||
}
|
||||
catch (MissingResourceException ignore) {
|
||||
return defaultValue;
|
||||
|
||||
@Throws(MissingResourceException::class)
|
||||
@JvmStatic
|
||||
fun intValue(key: @NonNls String): Int = getInstance().doGet(key).asInteger()
|
||||
|
||||
@JvmStatic
|
||||
fun intValue(key: @NonNls String, defaultValue: Int): Int {
|
||||
if (!LoadingState.COMPONENTS_LOADED.isOccurred) {
|
||||
LoadingState.COMPONENTS_REGISTERED.checkOccurred()
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
try {
|
||||
return registry.doGet(key).asInteger()
|
||||
}
|
||||
catch (ignore: MissingResourceException) {
|
||||
return defaultValue
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun doubleValue(key: @NonNls String, defaultValue: Double): Double {
|
||||
if (!LoadingState.COMPONENTS_LOADED.isOccurred) {
|
||||
LoadingState.COMPONENTS_REGISTERED.checkOccurred()
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
try {
|
||||
return registry.doGet(key).asDouble()
|
||||
}
|
||||
catch (ignore: MissingResourceException) {
|
||||
return defaultValue
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(MissingResourceException::class)
|
||||
@JvmStatic
|
||||
fun doubleValue(key: @NonNls String): Double = get(key).asDouble()
|
||||
|
||||
@Throws(MissingResourceException::class)
|
||||
@JvmStatic
|
||||
fun stringValue(key: @NonNls String): String = get(key).asString()
|
||||
|
||||
@Throws(MissingResourceException::class)
|
||||
@JvmStatic
|
||||
fun getColor(key: @NonNls String, defaultValue: Color?): Color = get(key).asColor(defaultValue)
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun loadFromBundledConfig(): Map<String, String>? {
|
||||
bundledRegistry?.get()?.let {
|
||||
return it
|
||||
}
|
||||
|
||||
val map: MutableMap<String, String> = LinkedHashMap(1800)
|
||||
val mainFound = loadFromResource("misc/registry.properties", map)
|
||||
val overrideFound = loadFromResource("misc/registry.override.properties", map)
|
||||
if (!mainFound && !overrideFound) {
|
||||
return null
|
||||
}
|
||||
|
||||
bundledRegistry = SoftReference(map)
|
||||
return map
|
||||
}
|
||||
|
||||
private fun loadFromResource(sourceResourceName: String, targetMap: MutableMap<String, String>): Boolean {
|
||||
val stream = Registry::class.java.classLoader.getResourceAsStream(sourceResourceName) ?: return false
|
||||
stream.use {
|
||||
object : Properties() {
|
||||
override fun put(key: Any, value: Any): Any? {
|
||||
return targetMap.put(key as String, value as String)
|
||||
}
|
||||
}.load(stream)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getInstance(): Registry {
|
||||
LoadingState.COMPONENTS_LOADED.checkOccurred()
|
||||
return registry
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun intValue(key: @NonNls String, defaultValue: Int, minValue: Int, maxValue: Int): Int {
|
||||
require(!(defaultValue < minValue || defaultValue > maxValue)) {
|
||||
"Wrong values for default:min:max ($defaultValue:$minValue:$maxValue)"
|
||||
}
|
||||
return intValue(key, defaultValue).coerceIn(minValue, maxValue)
|
||||
}
|
||||
|
||||
private fun fromState(state: Element): Map<String, String> {
|
||||
val map = LinkedHashMap<String, String>()
|
||||
for (eachEntry in state.getChildren("entry")) {
|
||||
val key = eachEntry.getAttributeValue("key") ?: continue
|
||||
val value = eachEntry.getAttributeValue("value") ?: continue
|
||||
map.put(key, value)
|
||||
}
|
||||
return map
|
||||
}
|
||||
|
||||
private fun updateStateInternal(registry: Registry, state: Element?): Map<String, String> {
|
||||
val userProperties = registry.userProperties
|
||||
if (state == null) {
|
||||
userProperties.clear()
|
||||
return userProperties
|
||||
}
|
||||
|
||||
val map = fromState(state)
|
||||
val keysToProcess = HashSet(userProperties.keys)
|
||||
for ((key, value) in map) {
|
||||
val registryValue = registry.doGet(key)
|
||||
val currentValue = registryValue.get(key, null, false)
|
||||
// currentValue == null means value is not in the bundle. Ignore it
|
||||
if (currentValue != null && currentValue != value) {
|
||||
registryValue.setValue(value)
|
||||
}
|
||||
keysToProcess.remove(key)
|
||||
}
|
||||
|
||||
// keys that are not in the state; we need to reset them to default value
|
||||
for (key in keysToProcess) {
|
||||
registry.doGet(key).resetToDefault()
|
||||
}
|
||||
|
||||
return userProperties
|
||||
}
|
||||
|
||||
@Internal
|
||||
fun loadState(state: Element?, earlyAccess: Map<String, String>?): Map<String, String> {
|
||||
val registry = registry
|
||||
if (registry.isLoaded) {
|
||||
return updateStateInternal(registry, state)
|
||||
}
|
||||
else {
|
||||
return loadStateInternal(registry = registry, state = state, earlyAccess = earlyAccess)
|
||||
}
|
||||
}
|
||||
|
||||
@Internal
|
||||
@JvmStatic
|
||||
fun markAsLoaded() {
|
||||
registry.isLoaded = true
|
||||
registry.loadFuture.complete(null)
|
||||
}
|
||||
|
||||
fun awaitLoad(): CompletionStage<Void?> = registry.loadFuture
|
||||
|
||||
@Internal
|
||||
@JvmStatic
|
||||
fun getAll(): List<RegistryValue> {
|
||||
var bundle: Map<String, String>? = null
|
||||
try {
|
||||
bundle = loadFromBundledConfig()
|
||||
}
|
||||
catch (ignored: IOException) {
|
||||
}
|
||||
val keys = bundle?.keys ?: emptySet()
|
||||
val result = ArrayList<RegistryValue>()
|
||||
// don't use getInstance here - https://youtrack.jetbrains.com/issue/IDEA-271748
|
||||
val instance = registry
|
||||
val contributedKeys = instance.contributedKeys
|
||||
for (key in keys) {
|
||||
if (key.endsWith(".description") || key.endsWith(".restartRequired") || contributedKeys.containsKey(key)) {
|
||||
continue
|
||||
}
|
||||
result.add(instance.doGet(key))
|
||||
}
|
||||
|
||||
for (key in contributedKeys.keys) {
|
||||
result.add(instance.doGet(key))
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun isRestartNeeded(map: Map<String, String>): Boolean {
|
||||
val instance = getInstance()
|
||||
for (s in map.keys) {
|
||||
val eachValue = instance.doGet(s)
|
||||
if (eachValue.isRestartRequired && eachValue.isChangedSinceAppStart) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@Internal
|
||||
@Synchronized
|
||||
fun setKeys(descriptors: Map<String, RegistryKeyDescriptor>) {
|
||||
// getInstance must be not used here - phase COMPONENT_REGISTERED is not yet completed
|
||||
registry.contributedKeys = descriptors
|
||||
}
|
||||
|
||||
@Internal
|
||||
@Synchronized
|
||||
fun mutateContributedKeys(mutator: (Map<String, RegistryKeyDescriptor>) -> Map<String, RegistryKeyDescriptor>) {
|
||||
// getInstance must be not used here - phase COMPONENT_REGISTERED is not yet completed
|
||||
registry.contributedKeys = mutator(registry.contributedKeys)
|
||||
}
|
||||
|
||||
@Internal
|
||||
fun setValueChangeListener(listener: RegistryValueListener?) {
|
||||
registry.valueChangeListener = listener ?: EMPTY_VALUE_LISTENER
|
||||
}
|
||||
|
||||
private fun loadStateInternal(
|
||||
registry: Registry,
|
||||
state: Element?,
|
||||
earlyAccess: Map<String, String>?
|
||||
): Map<String, String> {
|
||||
val userProperties = registry.userProperties
|
||||
userProperties.clear()
|
||||
if (state != null) {
|
||||
val map = fromState(state)
|
||||
for ((key, value) in map) {
|
||||
val registryValue = registry.doGet(key)
|
||||
if (registryValue.isChangedFromDefault(value, registry)) {
|
||||
userProperties[key] = value
|
||||
registryValue.resetCache()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (earlyAccess != null) {
|
||||
// yes, earlyAccess overrides user properties
|
||||
userProperties.putAll(earlyAccess)
|
||||
}
|
||||
registry.isLoaded = true
|
||||
registry.loadFuture.complete(null)
|
||||
return userProperties
|
||||
}
|
||||
}
|
||||
|
||||
public static int intValue(@NonNls @NotNull String key) throws MissingResourceException {
|
||||
return getInstance().doGet(key).asInteger();
|
||||
}
|
||||
|
||||
public static int intValue(@NonNls @NotNull String key, int defaultValue) {
|
||||
if (!LoadingState.COMPONENTS_LOADED.isOccurred()) {
|
||||
LoadingState.COMPONENTS_REGISTERED.checkOccurred();
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
try {
|
||||
return getInstance().doGet(key).asInteger();
|
||||
}
|
||||
catch (MissingResourceException ignore) {
|
||||
return defaultValue;
|
||||
private fun doGet(key: @NonNls String): RegistryValue {
|
||||
return values.computeIfAbsent(key) {
|
||||
RegistryValue(this, it, contributedKeys.get(it))
|
||||
}
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
void reset() {
|
||||
myUserProperties.clear();
|
||||
myValues.clear();
|
||||
isLoaded = false;
|
||||
myLoadFuture.cancel(false);
|
||||
myLoadFuture = new CompletableFuture<>();
|
||||
fun reset() {
|
||||
userProperties.clear()
|
||||
values.clear()
|
||||
isLoaded = false
|
||||
loadFuture.cancel(false)
|
||||
loadFuture = CompletableFuture()
|
||||
}
|
||||
|
||||
public static double doubleValue(@NonNls @NotNull String key, double defaultValue) {
|
||||
if (!LoadingState.COMPONENTS_LOADED.isOccurred()) {
|
||||
LoadingState.COMPONENTS_REGISTERED.checkOccurred();
|
||||
return defaultValue;
|
||||
fun getBundleValueOrNull(key: @NonNls String): @NlsSafe String? {
|
||||
contributedKeys.get(key)?.let {
|
||||
return it.defaultValue
|
||||
}
|
||||
|
||||
try {
|
||||
return getInstance().doGet(key).asDouble();
|
||||
}
|
||||
catch (MissingResourceException ignore) {
|
||||
return defaultValue;
|
||||
}
|
||||
return loadFromBundledConfig()?.get(key)
|
||||
}
|
||||
|
||||
public static double doubleValue(@NonNls @NotNull String key) throws MissingResourceException {
|
||||
return get(key).asDouble();
|
||||
@Throws(MissingResourceException::class)
|
||||
fun getBundleValue(key: @NonNls String): @NlsSafe String {
|
||||
contributedKeys.get(key)?.let {
|
||||
return it.defaultValue
|
||||
}
|
||||
|
||||
return getBundleValueOrNull(key) ?: throw MissingResourceException("Registry key $key is not defined", REGISTRY_BUNDLE, key)
|
||||
}
|
||||
|
||||
public static @NotNull String stringValue(@NonNls @NotNull String key) throws MissingResourceException {
|
||||
return get(key).asString();
|
||||
}
|
||||
|
||||
public static Color getColor(@NonNls @NotNull String key, Color defaultValue) throws MissingResourceException {
|
||||
return get(key).asColor(defaultValue);
|
||||
}
|
||||
|
||||
private static @Nullable Map<String, String> loadFromBundledConfig() throws IOException {
|
||||
Reference<Map<String, String>> bundleRef = bundledRegistry;
|
||||
Map<String, String> result = bundleRef == null ? null : bundleRef.get();
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Map<String, String> map = new LinkedHashMap<>(1_800);
|
||||
boolean mainFound = loadFromResource("misc/registry.properties", map);
|
||||
boolean overrideFound = loadFromResource("misc/registry.override.properties", map);
|
||||
if (!mainFound && !overrideFound) {
|
||||
return null;
|
||||
}
|
||||
|
||||
bundledRegistry = new SoftReference<>(map);
|
||||
return map;
|
||||
}
|
||||
|
||||
private static boolean loadFromResource(String sourceResourceName, Map<String, String> targetMap) throws IOException {
|
||||
InputStream stream = Registry.class.getClassLoader().getResourceAsStream(sourceResourceName);
|
||||
if (stream == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
//noinspection NonSynchronizedMethodOverridesSynchronizedMethod
|
||||
new Properties() {
|
||||
@Override
|
||||
public Object put(Object key, Object value) {
|
||||
return targetMap.put((String)key, (String)value);
|
||||
}
|
||||
}.load(stream);
|
||||
}
|
||||
finally {
|
||||
stream.close();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public @NlsSafe @Nullable String getBundleValueOrNull(@NonNls @NotNull String key) {
|
||||
RegistryKeyDescriptor contributed = myContributedKeys.get(key);
|
||||
if (contributed != null) {
|
||||
return contributed.getDefaultValue();
|
||||
}
|
||||
|
||||
try {
|
||||
Map<String, String> bundle = loadFromBundledConfig();
|
||||
return bundle == null ? null : bundle.get(key);
|
||||
}
|
||||
catch (IOException e) {
|
||||
// critical start-up error (cannot parse properties file), don't bother clients
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@NlsSafe @NotNull String getBundleValue(@NonNls @NotNull String key) throws MissingResourceException {
|
||||
RegistryKeyDescriptor contributed = myContributedKeys.get(key);
|
||||
if (contributed != null) {
|
||||
return contributed.getDefaultValue();
|
||||
}
|
||||
|
||||
String result = getBundleValueOrNull(key);
|
||||
if (result == null) {
|
||||
throw new MissingResourceException("Registry key " + key + " is not defined", REGISTRY_BUNDLE, key);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static @NotNull Registry getInstance() {
|
||||
LoadingState.COMPONENTS_LOADED.checkOccurred();
|
||||
return ourInstance;
|
||||
}
|
||||
|
||||
public @NotNull Element getState() {
|
||||
Element state = new Element("registry");
|
||||
for (Map.Entry<String, String> entry : myUserProperties.entrySet()) {
|
||||
RegistryValue registryValue = get(entry.getKey());
|
||||
if (registryValue.isChangedFromDefault()) {
|
||||
Element entryElement = new Element("entry");
|
||||
entryElement.setAttribute("key", entry.getKey());
|
||||
entryElement.setAttribute("value", entry.getValue());
|
||||
state.addContent(entryElement);
|
||||
fun getState(): Element {
|
||||
val state = Element("registry")
|
||||
for ((key, value) in userProperties) {
|
||||
val registryValue = getInstance().doGet(key)
|
||||
if (registryValue.isChangedFromDefault) {
|
||||
val entryElement = Element("entry")
|
||||
entryElement.setAttribute("key", key)
|
||||
entryElement.setAttribute("value", value)
|
||||
state.addContent(entryElement)
|
||||
}
|
||||
}
|
||||
return state;
|
||||
return state
|
||||
}
|
||||
|
||||
public static int intValue(@NonNls @NotNull String key, int defaultValue, int minValue, int maxValue) {
|
||||
if (defaultValue < minValue || defaultValue > maxValue) {
|
||||
throw new IllegalArgumentException("Wrong values for default:min:max (" + defaultValue + ":" + minValue + ":" + maxValue + ")");
|
||||
}
|
||||
return MathUtil.clamp(intValue(key, defaultValue), minValue, maxValue);
|
||||
}
|
||||
@Internal
|
||||
fun getUserProperties(): MutableMap<String, String> = userProperties
|
||||
|
||||
private static @NotNull Map<String, String> fromState(@NotNull Element state) {
|
||||
Map<String, String> map = new LinkedHashMap<>();
|
||||
for (Element eachEntry : state.getChildren("entry")) {
|
||||
String key = eachEntry.getAttributeValue("key");
|
||||
String value = eachEntry.getAttributeValue("value");
|
||||
if (key != null && value != null) {
|
||||
map.put(key, value);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private static @NotNull Map<String, String> updateStateInternal(@NotNull Registry registry, @Nullable Element state) {
|
||||
Map<String, String> userProperties = registry.myUserProperties;
|
||||
if (state == null) {
|
||||
userProperties.clear();
|
||||
return userProperties;
|
||||
}
|
||||
|
||||
Map<String, String> map = fromState(state);
|
||||
Set<String> keys2process = new HashSet<>(userProperties.keySet());
|
||||
for (Map.Entry<String, String> entry : map.entrySet()) {
|
||||
RegistryValue registryValue = registry.doGet(entry.getKey());
|
||||
String currentValue = registryValue.get(entry.getKey(), null, false);
|
||||
// currentValue == null means value is not in the bundle. Simply ignore it
|
||||
if (currentValue != null && !currentValue.equals(entry.getValue())) {
|
||||
registryValue.setValue(entry.getValue());
|
||||
}
|
||||
keys2process.remove(entry.getKey());
|
||||
}
|
||||
|
||||
// keys that are not in the state, we need to reset them to default value
|
||||
for (String key : keys2process) {
|
||||
registry.doGet(key).resetToDefault();
|
||||
}
|
||||
|
||||
return userProperties;
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static @NotNull Map<String, String> loadState(@Nullable Element state, @Nullable Map<String, String> earlyAccess) {
|
||||
Registry registry = ourInstance;
|
||||
if (registry.isLoaded) {
|
||||
return updateStateInternal(registry, state);
|
||||
}
|
||||
else {
|
||||
return loadStateInternal(registry, state, earlyAccess);
|
||||
}
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static void markAsLoaded() {
|
||||
ourInstance.isLoaded = true;
|
||||
ourInstance.myLoadFuture.complete(null);
|
||||
}
|
||||
|
||||
public boolean isLoaded() {
|
||||
return isLoaded;
|
||||
}
|
||||
|
||||
static CompletionStage<Void> awaitLoad() {
|
||||
return ourInstance.myLoadFuture;
|
||||
}
|
||||
|
||||
@NotNull Map<String, String> getUserProperties() {
|
||||
return myUserProperties;
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static @NotNull List<RegistryValue> getAll() {
|
||||
Map<String, String> bundle = null;
|
||||
try {
|
||||
bundle = loadFromBundledConfig();
|
||||
}
|
||||
catch (IOException ignored) {
|
||||
}
|
||||
Set<String> keys = bundle == null ? Collections.emptySet() : bundle.keySet();
|
||||
List<RegistryValue> result = new ArrayList<>();
|
||||
// don't use getInstance here - https://youtrack.jetbrains.com/issue/IDEA-271748
|
||||
Registry instance = ourInstance;
|
||||
Map<String, RegistryKeyDescriptor> contributedKeys = instance.myContributedKeys;
|
||||
for (String key : keys) {
|
||||
if (key.endsWith(".description") || key.endsWith(".restartRequired") || contributedKeys.containsKey(key)) {
|
||||
continue;
|
||||
}
|
||||
result.add(instance.doGet(key));
|
||||
}
|
||||
|
||||
for (String key : contributedKeys.keySet()) {
|
||||
result.add(instance.doGet(key));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void restoreDefaults() {
|
||||
Map<String, String> old = new LinkedHashMap<>(myUserProperties);
|
||||
Registry instance = getInstance();
|
||||
for (String key : old.keySet()) {
|
||||
String v = instance.getBundleValueOrNull(key);
|
||||
fun restoreDefaults() {
|
||||
val old = LinkedHashMap(userProperties)
|
||||
val registry = getInstance()
|
||||
for (key in old.keys) {
|
||||
val v = registry.getBundleValueOrNull(key)
|
||||
if (v == null) {
|
||||
// outdated property that is not declared in registry.properties anymore
|
||||
myValues.remove(key);
|
||||
values.remove(key)
|
||||
}
|
||||
else {
|
||||
RegistryValue value = instance.myValues.get(key);
|
||||
if (value != null) {
|
||||
value.setValue(v);
|
||||
}
|
||||
registry.values.get(key)?.setValue(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean isInDefaultState() {
|
||||
return myUserProperties.isEmpty();
|
||||
}
|
||||
val isInDefaultState: Boolean
|
||||
get() = userProperties.isEmpty()
|
||||
|
||||
public boolean isRestartNeeded() {
|
||||
return isRestartNeeded(myUserProperties);
|
||||
}
|
||||
|
||||
private static boolean isRestartNeeded(@NotNull Map<String, String> map) {
|
||||
Registry instance = getInstance();
|
||||
for (String s : map.keySet()) {
|
||||
RegistryValue eachValue = instance.doGet(s);
|
||||
if (eachValue.isRestartRequired() && eachValue.isChangedSinceAppStart()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static synchronized void setKeys(@NotNull Map<String, RegistryKeyDescriptor> descriptors) {
|
||||
// getInstance must be not used here - phase COMPONENT_REGISTERED is not yet completed
|
||||
ourInstance.myContributedKeys = descriptors;
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static synchronized void mutateContributedKeys(@NotNull Function<? super Map<String, RegistryKeyDescriptor>, ? extends Map<String, RegistryKeyDescriptor>> mutator) {
|
||||
// getInstance must be not used here - phase COMPONENT_REGISTERED is not yet completed
|
||||
ourInstance.myContributedKeys = mutator.apply(ourInstance.myContributedKeys);
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static void setValueChangeListener(@Nullable RegistryValueListener listener) {
|
||||
ourInstance.valueChangeListener = listener == null ? EMPTY_VALUE_LISTENER : listener;
|
||||
}
|
||||
|
||||
@NotNull RegistryValueListener getValueChangeListener() {
|
||||
return valueChangeListener;
|
||||
}
|
||||
|
||||
private static @NotNull Map<String, String> loadStateInternal(@NotNull Registry registry,
|
||||
@Nullable Element state,
|
||||
@Nullable Map<String, String> earlyAccess) {
|
||||
Map<String, String> userProperties = registry.myUserProperties;
|
||||
userProperties.clear();
|
||||
if (state != null) {
|
||||
Map<String, String> map = fromState(state);
|
||||
for (Map.Entry<String, String> entry : map.entrySet()) {
|
||||
RegistryValue registryValue = registry.doGet(entry.getKey());
|
||||
if (registryValue.isChangedFromDefault(entry.getValue(), registry)) {
|
||||
userProperties.put(entry.getKey(), entry.getValue());
|
||||
registryValue.resetCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (earlyAccess != null) {
|
||||
// yes, earlyAccess overrides user properties
|
||||
userProperties.putAll(earlyAccess);
|
||||
}
|
||||
registry.isLoaded = true;
|
||||
registry.myLoadFuture.complete(null);
|
||||
return userProperties;
|
||||
}
|
||||
val isRestartNeeded: Boolean
|
||||
get() = isRestartNeeded(userProperties)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.openapi.util.registry;
|
||||
|
||||
import com.intellij.openapi.Disposable;
|
||||
@@ -28,59 +28,59 @@ import java.util.Objects;
|
||||
public class RegistryValue {
|
||||
private static final Logger LOG = Logger.getInstance(RegistryValue.class);
|
||||
|
||||
private final Registry myRegistry;
|
||||
private final String myKey;
|
||||
private final @Nullable RegistryKeyDescriptor myKeyDescriptor;
|
||||
private final Registry registry;
|
||||
private final String key;
|
||||
private final @Nullable RegistryKeyDescriptor keyDescriptor;
|
||||
|
||||
private final List<RegistryValueListener> myListeners = ContainerUtil.createLockFreeCopyOnWriteList();
|
||||
private final List<RegistryValueListener> listeners = ContainerUtil.createLockFreeCopyOnWriteList();
|
||||
|
||||
private boolean myChangedSinceStart;
|
||||
|
||||
private String myStringCachedValue;
|
||||
private Integer myIntCachedValue;
|
||||
private double myDoubleCachedValue = Double.NaN;
|
||||
private Boolean myBooleanCachedValue;
|
||||
private String stringCachedValue;
|
||||
private Integer intCachedValue;
|
||||
private double doubleCachedValue = Double.NaN;
|
||||
private Boolean booleanCachedValue;
|
||||
|
||||
RegistryValue(@NotNull Registry registry, @NonNls @NotNull String key, @Nullable RegistryKeyDescriptor keyDescriptor) {
|
||||
myRegistry = registry;
|
||||
myKey = key;
|
||||
myKeyDescriptor = keyDescriptor;
|
||||
this.registry = registry;
|
||||
this.key = key;
|
||||
this.keyDescriptor = keyDescriptor;
|
||||
}
|
||||
|
||||
public @NotNull @NlsSafe String getKey() {
|
||||
return myKey;
|
||||
return key;
|
||||
}
|
||||
|
||||
public @NotNull @NlsSafe String asString() {
|
||||
final String value = get(myKey, null, true);
|
||||
assert value != null : myKey;
|
||||
final String value = get(key, null, true);
|
||||
assert value != null : key;
|
||||
return value;
|
||||
}
|
||||
|
||||
public boolean asBoolean() {
|
||||
Boolean result = myBooleanCachedValue;
|
||||
Boolean result = booleanCachedValue;
|
||||
if (result == null) {
|
||||
result = Boolean.valueOf(get(myKey, "false", true));
|
||||
myBooleanCachedValue = result;
|
||||
result = Boolean.valueOf(get(key, "false", true));
|
||||
booleanCachedValue = result;
|
||||
}
|
||||
return result.booleanValue();
|
||||
}
|
||||
|
||||
public int asInteger() {
|
||||
Integer result = myIntCachedValue;
|
||||
Integer result = intCachedValue;
|
||||
if (result == null) {
|
||||
result = calcInt();
|
||||
myIntCachedValue = result;
|
||||
intCachedValue = result;
|
||||
}
|
||||
return result.intValue();
|
||||
}
|
||||
|
||||
private @NotNull Integer calcInt() {
|
||||
try {
|
||||
return Integer.valueOf(get(myKey, "0", true));
|
||||
return Integer.valueOf(get(key, "0", true));
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
return Integer.valueOf(myRegistry.getBundleValue(myKey));
|
||||
return Integer.valueOf(registry.getBundleValue(key));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ public class RegistryValue {
|
||||
}
|
||||
|
||||
public String[] getOptions() {
|
||||
return getOptions(myRegistry.getBundleValue(myKey));
|
||||
return getOptions(registry.getBundleValue(key));
|
||||
}
|
||||
|
||||
private static String[] getOptions(String value) {
|
||||
@@ -134,28 +134,28 @@ public class RegistryValue {
|
||||
}
|
||||
|
||||
public double asDouble() {
|
||||
double result = myDoubleCachedValue;
|
||||
double result = doubleCachedValue;
|
||||
if (Double.isNaN(result)) {
|
||||
result = calcDouble();
|
||||
myDoubleCachedValue = result;
|
||||
doubleCachedValue = result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private double calcDouble() {
|
||||
try {
|
||||
return Double.parseDouble(get(myKey, "0.0", true));
|
||||
return Double.parseDouble(get(key, "0.0", true));
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
return Double.parseDouble(myRegistry.getBundleValue(myKey));
|
||||
return Double.parseDouble(registry.getBundleValue(key));
|
||||
}
|
||||
}
|
||||
|
||||
Color asColor(Color defaultValue) {
|
||||
final String s = get(myKey, null, true);
|
||||
final String s = get(key, null, true);
|
||||
if (s != null) {
|
||||
Color color = ColorHexUtil.fromHex(s, null);
|
||||
if (color != null && (myKey.endsWith(".color") || myKey.endsWith(".color.dark") || myKey.endsWith(".color.light"))) {
|
||||
if (color != null && (key.endsWith(".color") || key.endsWith(".color.dark") || key.endsWith(".color.light"))) {
|
||||
return color;
|
||||
}
|
||||
final String[] rgb = s.split(",");
|
||||
@@ -171,43 +171,43 @@ public class RegistryValue {
|
||||
}
|
||||
|
||||
public @NotNull @NlsSafe String getDescription() {
|
||||
if (myKeyDescriptor != null) {
|
||||
return myKeyDescriptor.getDescription();
|
||||
if (keyDescriptor != null) {
|
||||
return keyDescriptor.getDescription();
|
||||
}
|
||||
return get(myKey + ".description", "", false);
|
||||
return get(key + ".description", "", false);
|
||||
}
|
||||
|
||||
boolean isRestartRequired() {
|
||||
if (myKeyDescriptor != null) {
|
||||
return myKeyDescriptor.isRestartRequired();
|
||||
if (keyDescriptor != null) {
|
||||
return keyDescriptor.isRestartRequired();
|
||||
}
|
||||
return Boolean.parseBoolean(get(myKey + ".restartRequired", "false", false));
|
||||
return Boolean.parseBoolean(get(key + ".restartRequired", "false", false));
|
||||
}
|
||||
|
||||
public boolean isChangedFromDefault() {
|
||||
return isChangedFromDefault(asString(), myRegistry);
|
||||
return isChangedFromDefault(asString(), registry);
|
||||
}
|
||||
|
||||
public @Nullable String getPluginId() {
|
||||
return myKeyDescriptor != null ? myKeyDescriptor.getPluginId() : null;
|
||||
return keyDescriptor != null ? keyDescriptor.getPluginId() : null;
|
||||
}
|
||||
|
||||
final boolean isChangedFromDefault(@NotNull String newValue, @NotNull Registry registry) {
|
||||
return !newValue.equals(registry.getBundleValueOrNull(myKey));
|
||||
return !newValue.equals(registry.getBundleValueOrNull(key));
|
||||
}
|
||||
|
||||
protected String get(@NonNls @NotNull String key, String defaultValue, boolean isValue) throws MissingResourceException {
|
||||
if (isValue) {
|
||||
if (myStringCachedValue == null) {
|
||||
myStringCachedValue = _get(key, defaultValue, true);
|
||||
if (stringCachedValue == null) {
|
||||
stringCachedValue = _get(key, defaultValue, true);
|
||||
}
|
||||
return myStringCachedValue;
|
||||
return stringCachedValue;
|
||||
}
|
||||
return _get(key, defaultValue, false);
|
||||
}
|
||||
|
||||
private @Nullable String _get(@NonNls @NotNull String key, @Nullable String defaultValue, boolean mustExistInBundle) throws MissingResourceException {
|
||||
String userValue = myRegistry.getUserProperties().get(key);
|
||||
String userValue = registry.getUserProperties().get(key);
|
||||
if (userValue != null) {
|
||||
return userValue;
|
||||
}
|
||||
@@ -217,7 +217,7 @@ public class RegistryValue {
|
||||
return systemProperty;
|
||||
}
|
||||
|
||||
if (!myRegistry.isLoaded()) {
|
||||
if (!registry.isLoaded()) {
|
||||
String message = "Attempt to load key '" + key + "' for not yet loaded registry";
|
||||
// use Disposer.isDebugMode as "we are in internal mode or inside test" flag
|
||||
if (Disposer.isDebugMode()) {
|
||||
@@ -229,10 +229,10 @@ public class RegistryValue {
|
||||
}
|
||||
|
||||
if (mustExistInBundle) {
|
||||
return myRegistry.getBundleValue(key);
|
||||
return registry.getBundleValue(key);
|
||||
}
|
||||
else {
|
||||
String bundleValue = myRegistry.getBundleValueOrNull(key);
|
||||
String bundleValue = registry.getBundleValueOrNull(key);
|
||||
return bundleValue == null ? defaultValue : bundleValue;
|
||||
}
|
||||
}
|
||||
@@ -246,22 +246,22 @@ public class RegistryValue {
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
RegistryValueListener globalValueChangeListener = myRegistry.getValueChangeListener();
|
||||
RegistryValueListener globalValueChangeListener = registry.getValueChangeListener();
|
||||
globalValueChangeListener.beforeValueChanged(this);
|
||||
for (RegistryValueListener each : myListeners) {
|
||||
for (RegistryValueListener each : listeners) {
|
||||
each.beforeValueChanged(this);
|
||||
}
|
||||
resetCache();
|
||||
myRegistry.getUserProperties().put(myKey, value);
|
||||
LOG.info("Registry value '" + myKey + "' has changed to '" + value + '\'');
|
||||
registry.getUserProperties().put(key, value);
|
||||
LOG.info("Registry value '" + key + "' has changed to '" + value + '\'');
|
||||
|
||||
globalValueChangeListener.afterValueChanged(this);
|
||||
for (RegistryValueListener each : myListeners) {
|
||||
for (RegistryValueListener each : listeners) {
|
||||
each.afterValueChanged(this);
|
||||
}
|
||||
|
||||
if (!isChangedFromDefault() && !isRestartRequired()) {
|
||||
myRegistry.getUserProperties().remove(myKey);
|
||||
registry.getUserProperties().remove(key);
|
||||
}
|
||||
|
||||
myChangedSinceStart = true;
|
||||
@@ -290,37 +290,37 @@ public class RegistryValue {
|
||||
}
|
||||
|
||||
public void resetToDefault() {
|
||||
String value = myRegistry.getBundleValueOrNull(myKey);
|
||||
String value = registry.getBundleValueOrNull(key);
|
||||
if (value == null) {
|
||||
myRegistry.getUserProperties().remove(myKey);
|
||||
registry.getUserProperties().remove(key);
|
||||
} else {
|
||||
setValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void addListener(@NotNull RegistryValueListener listener, @NotNull Disposable parent) {
|
||||
myListeners.add(listener);
|
||||
Disposer.register(parent, () -> myListeners.remove(listener));
|
||||
listeners.add(listener);
|
||||
Disposer.register(parent, () -> listeners.remove(listener));
|
||||
}
|
||||
|
||||
public void addListener(@NotNull RegistryValueListener listener, @NotNull CoroutineScope coroutineScope) {
|
||||
myListeners.add(listener);
|
||||
listeners.add(listener);
|
||||
Objects.requireNonNull(coroutineScope.getCoroutineContext().get(Job.Key)).invokeOnCompletion(__ -> {
|
||||
myListeners.remove(listener);
|
||||
listeners.remove(listener);
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return myKey + "=" + asString();
|
||||
return key + "=" + asString();
|
||||
}
|
||||
|
||||
void resetCache() {
|
||||
myStringCachedValue = null;
|
||||
myIntCachedValue = null;
|
||||
myDoubleCachedValue = Double.NaN;
|
||||
myBooleanCachedValue = null;
|
||||
stringCachedValue = null;
|
||||
intCachedValue = null;
|
||||
doubleCachedValue = Double.NaN;
|
||||
booleanCachedValue = null;
|
||||
}
|
||||
|
||||
public boolean isBoolean() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.openapi.util.registry;
|
||||
|
||||
import com.intellij.idea.TestFor;
|
||||
@@ -21,7 +21,7 @@ public class RegistryTest {
|
||||
|
||||
@After
|
||||
public void tearDown(){
|
||||
Registry.setValueChangeListener(null);
|
||||
Registry.Companion.setValueChangeListener(null);
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ public class RegistryTest {
|
||||
String firstVal = "first.value1";
|
||||
String secondValue = "second.value1";
|
||||
final AtomicReference<Pair<String, String>> lastChangedPair = new AtomicReference<>();
|
||||
Registry.setValueChangeListener(new RegistryValueListener() {
|
||||
Registry.Companion.setValueChangeListener(new RegistryValueListener() {
|
||||
@Override
|
||||
public void afterValueChanged(@NotNull RegistryValue value) {
|
||||
if (!value.getKey().equals(key))
|
||||
@@ -50,13 +50,13 @@ public class RegistryTest {
|
||||
|
||||
Map<String, String> firstMap = new LinkedHashMap<>();
|
||||
firstMap.put(key, firstVal);
|
||||
Registry.loadState(registryElementFromMap(firstMap), null);
|
||||
Registry.Companion.loadState(registryElementFromMap(firstMap), null);
|
||||
assertEquals(firstVal, Registry.get(key).asString());
|
||||
assertNull(lastChangedPair.get());
|
||||
|
||||
Map<String, String> secondMap = new LinkedHashMap<>();
|
||||
secondMap.put(key, secondValue);
|
||||
Registry.loadState(registryElementFromMap(secondMap), null);
|
||||
Registry.Companion.loadState(registryElementFromMap(secondMap), null);
|
||||
assertEquals(secondValue, Registry.get(key).asString());
|
||||
assertEquals(Pair.create(key, secondValue), lastChangedPair.get());
|
||||
}
|
||||
@@ -71,7 +71,7 @@ public class RegistryTest {
|
||||
String eaFirstVal = "ea.first.value";
|
||||
String eaSecondValue = "ea.second.value";
|
||||
final AtomicReference<Pair<String, String>> lastChangedPair = new AtomicReference<>();
|
||||
Registry.setValueChangeListener(new RegistryValueListener() {
|
||||
Registry.Companion.setValueChangeListener(new RegistryValueListener() {
|
||||
@Override
|
||||
public void afterValueChanged(@NotNull RegistryValue value) {
|
||||
if (!(value.getKey().equals(key) || value.getKey().equals(eaKey)))
|
||||
@@ -80,12 +80,12 @@ public class RegistryTest {
|
||||
}
|
||||
});
|
||||
|
||||
Registry.loadState(registryElementFromMap(Map.of(key, firstVal)), Map.of(eaKey, eaFirstVal));
|
||||
Registry.Companion.loadState(registryElementFromMap(Map.of(key, firstVal)), Map.of(eaKey, eaFirstVal));
|
||||
assertEquals(firstVal, Registry.get(key).asString());
|
||||
assertEquals(eaFirstVal, Registry.get(eaKey).asString());
|
||||
assertNull(lastChangedPair.get());
|
||||
|
||||
Registry.loadState(registryElementFromMap(Map.of(key, firstVal, eaKey, eaSecondValue)), null);
|
||||
Registry.Companion.loadState(registryElementFromMap(Map.of(key, firstVal, eaKey, eaSecondValue)), null);
|
||||
assertEquals(firstVal, Registry.get(key).asString());
|
||||
assertEquals(eaSecondValue, Registry.get(eaKey).asString());
|
||||
assertEquals(Pair.create(eaKey, eaSecondValue), lastChangedPair.get());
|
||||
@@ -103,7 +103,7 @@ public class RegistryTest {
|
||||
String newValue = "another.value";
|
||||
final AtomicReference<Pair<String, String>> lastChangedPair = new AtomicReference<>();
|
||||
final List<Pair<String, String>> changedPairs = new ArrayList<>();
|
||||
Registry.setValueChangeListener(new RegistryValueListener() {
|
||||
Registry.Companion.setValueChangeListener(new RegistryValueListener() {
|
||||
@Override
|
||||
public void afterValueChanged(@NotNull RegistryValue value) {
|
||||
if (!(value.getKey().equals(key)))
|
||||
@@ -116,14 +116,14 @@ public class RegistryTest {
|
||||
|
||||
Map<String, String> firstMap = new LinkedHashMap<>();
|
||||
firstMap.put(key, firstVal);
|
||||
Registry.loadState(registryElementFromMap(firstMap), null);
|
||||
Registry.Companion.loadState(registryElementFromMap(firstMap), null);
|
||||
assertEquals(firstVal, Registry.get(key).asString());
|
||||
assertNull(lastChangedPair.get());
|
||||
|
||||
Map<String, String> secondMap = new LinkedHashMap<>();
|
||||
secondMap.put(key, secondValue);
|
||||
secondMap.put(newKey, newValue);
|
||||
Registry.loadState(registryElementFromMap(secondMap), null);
|
||||
Registry.Companion.loadState(registryElementFromMap(secondMap), null);
|
||||
assertEquals(secondValue, Registry.get(key).asString());
|
||||
RegistryValue newRegistryValue = Registry.get(newKey);
|
||||
String loadedNewValue = newRegistryValue.get(newRegistryValue.getKey(), null, false);
|
||||
@@ -143,7 +143,7 @@ public class RegistryTest {
|
||||
String secondKeyChangedValue = "second.initValue";
|
||||
|
||||
// initialize registry
|
||||
Registry.loadState(registryElementFromMap(new LinkedHashMap<>(){{
|
||||
Registry.Companion.loadState(registryElementFromMap(new LinkedHashMap<>(){{
|
||||
put(firstKey, firstKeyInitValue);
|
||||
put(secondKey, secondKeyInitValue);
|
||||
}}), null);
|
||||
@@ -151,7 +151,7 @@ public class RegistryTest {
|
||||
assertEquals(secondKeyInitValue, Registry.get(secondKey).asString());
|
||||
|
||||
// add custom values
|
||||
Registry.loadState(registryElementFromMap(new LinkedHashMap<>(){{
|
||||
Registry.Companion.loadState(registryElementFromMap(new LinkedHashMap<>(){{
|
||||
put(firstKey, firstKeyChangedVal);
|
||||
put(secondKey, secondKeyChangedValue);
|
||||
}}), null);
|
||||
@@ -159,7 +159,7 @@ public class RegistryTest {
|
||||
assertEquals(secondKeyChangedValue, Registry.get(secondKey).asString());
|
||||
|
||||
// drop key - reset to original
|
||||
Registry.loadState(registryElementFromMap(new LinkedHashMap<>(){{
|
||||
Registry.Companion.loadState(registryElementFromMap(new LinkedHashMap<>(){{
|
||||
put(firstKey, firstKeyChangedVal);
|
||||
}}), null);
|
||||
assertEquals(firstKeyChangedVal, Registry.get(firstKey).asString());
|
||||
@@ -184,7 +184,7 @@ public class RegistryTest {
|
||||
String registryValue = "testBoolean";
|
||||
RegistryValue regValue = new RegistryValue(Registry.getInstance(), registryValue, null);
|
||||
regValue.setValue(false);
|
||||
Registry.setValueChangeListener(new RegistryValueListener() {
|
||||
Registry.Companion.setValueChangeListener(new RegistryValueListener() {
|
||||
@Override
|
||||
public void beforeValueChanged(@NotNull RegistryValue value) {
|
||||
regValue.asBoolean();
|
||||
@@ -201,17 +201,17 @@ public class RegistryTest {
|
||||
{
|
||||
Map<String, String> map = populateMap(Comparator.naturalOrder(), "value");
|
||||
Element state2load = registryElementFromMap(map);
|
||||
Registry.loadState(state2load, null);
|
||||
Registry.Companion.loadState(state2load, null);
|
||||
assertEquals(JDOMUtil.writeElement(state2load), JDOMUtil.writeElement(Registry.getInstance().getState()));
|
||||
}
|
||||
// load state with elements reversed
|
||||
Registry.loadState(registryElementFromMap(populateMap(Comparator.reverseOrder(), "AnotherValue1111")), null);
|
||||
Registry.Companion.loadState(registryElementFromMap(populateMap(Comparator.reverseOrder(), "AnotherValue1111")), null);
|
||||
|
||||
assertEquals(JDOMUtil.writeElement(registryElementFromMap(populateMap(Comparator.naturalOrder(), "AnotherValue1111"))),
|
||||
JDOMUtil.writeElement(Registry.getInstance().getState()));
|
||||
}
|
||||
|
||||
private Map<String, String> populateMap(Comparator<String> comparator, String valueBase) {
|
||||
private static Map<String, String> populateMap(Comparator<String> comparator, String valueBase) {
|
||||
Map<String, String> map = new TreeMap<>(comparator);
|
||||
map.put("first.key", "first." + valueBase);
|
||||
for (int i = 0; i < 20; i++) {
|
||||
@@ -225,7 +225,7 @@ public class RegistryTest {
|
||||
}
|
||||
|
||||
|
||||
private Element registryElementFromMap(Map<String, String> map){
|
||||
private static Element registryElementFromMap(Map<String, String> map){
|
||||
Element registryElement = new Element("registry");
|
||||
for (Map.Entry<String, String> entry : map.entrySet()) {
|
||||
Element entryElement = new Element("entry");
|
||||
|
||||
Reference in New Issue
Block a user