Python stubs: remove 3.6.8 version and add 3.9.0
[idea/community.git] / platform / projectModel-api / src / com / intellij / openapi / components / BaseState.kt
1 // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package com.intellij.openapi.components
3
4 import com.intellij.openapi.diagnostic.logger
5 import com.intellij.openapi.util.ModificationTracker
6 import com.intellij.serialization.PropertyAccessor
7 import com.intellij.util.xmlb.Accessor
8 import com.intellij.util.xmlb.SerializationFilter
9 import com.intellij.util.xmlb.annotations.Transient
10 import org.jetbrains.annotations.ApiStatus
11 import java.nio.charset.Charset
12 import java.util.*
13 import java.util.concurrent.atomic.AtomicLongFieldUpdater
14 import kotlin.collections.ArrayList
15 import kotlin.collections.LinkedHashMap
16
17 internal val LOG = logger<BaseState>()
18
19 private val factory: StatePropertyFactory = ServiceLoader.load(StatePropertyFactory::class.java, BaseState::class.java.classLoader).first()
20
21 abstract class BaseState : SerializationFilter, ModificationTracker {
22   companion object {
23     // should be part of class and not file level to access private field of class
24     private val MOD_COUNT_UPDATER = AtomicLongFieldUpdater.newUpdater(BaseState::class.java, "ownModificationCount")
25   }
26
27   // do not use SmartList because most objects have more than 1 property
28   private val properties: MutableList<StoredProperty<Any>> = ArrayList()
29
30   @Volatile
31   @Transient
32   private var ownModificationCount = 0L
33
34   private fun <T> addProperty(p: StoredPropertyBase<T>): StoredPropertyBase<T> {
35     @Suppress("UNCHECKED_CAST")
36     properties.add(p as StoredPropertyBase<Any>)
37     return p
38   }
39
40   @Suppress("RemoveExplicitTypeArguments")
41   protected fun <T : BaseState> property(): StoredPropertyBase<T?> = addProperty(factory.stateObject<T?>(null))
42
43   /**
44    * Value considered as default only if all properties have default values.
45    * Passed instance is not used for `isDefault` check. It is just an initial value.
46    */
47   protected fun <T : BaseState?> property(initialValue: T): StoredPropertyBase<T> = addProperty(factory.stateObject(initialValue))
48
49   /**
50    * For non-BaseState classes explicit `isDefault` must be provided, because no other way to check.
51    */
52   protected fun <T> property(initialValue: T, isDefault: (value: T) -> Boolean) = addProperty(factory.obj(initialValue, isDefault))
53
54   /**
55    * Collection considered as default if empty. It is *your* responsibility to call `incrementModificationCount` on collection modification.
56    * You cannot set value to a new collection - on set current collection is cleared and new collection is added to current.
57    */
58   protected fun stringSet(): StoredPropertyBase<MutableSet<String>> = addProperty(factory.stringSet(null))
59
60   /**
61    * Collection considered as default if contains only the specified default value. It is *your* responsibility to call `incrementModificationCount` on collection modification.
62    * You cannot set value to a new collection - on set current collection is cleared and new collection is added to current.
63    */
64   protected fun stringSet(defaultValue: String): StoredPropertyBase<MutableSet<String>> = addProperty(factory.stringSet(defaultValue))
65
66   /**
67    * Collection considered as default if empty. It is *your* responsibility to call `incrementModificationCount` on collection modification.
68    * You cannot set value to a new collection - on set current collection is cleared and new collection is added to current.
69    */
70   protected fun <E> treeSet(): StoredPropertyBase<MutableSet<E>> where E : Comparable<E>, E : BaseState = addProperty(factory.treeSet<E>())
71
72   /**
73    * Charset is an immutable, so, it is safe to use it as default value.
74    */
75   protected fun <T : Charset> property(initialValue: T) = addProperty(factory.obj(initialValue))
76
77   // Enum is an immutable, so, it is safe to use it as default value.
78   @Deprecated(message = "Use [enum] instead", replaceWith = ReplaceWith("enum(defaultValue)"), level = DeprecationLevel.ERROR)
79   protected fun <T : Enum<*>> property(defaultValue: T) = addProperty(factory.obj(defaultValue))
80
81   protected inline fun <reified T : Enum<*>> enum(defaultValue: T): StoredPropertyBase<T> {
82     @Suppress("UNCHECKED_CAST")
83     return doEnum(defaultValue, T::class.java) as StoredPropertyBase<T>
84   }
85
86   protected inline fun <reified T : Enum<*>> enum(): StoredPropertyBase<T?> = doEnum(null, T::class.java)
87
88   @PublishedApi
89   internal fun <T : Enum<*>> doEnum(defaultValue: T? = null, clazz: Class<T>): StoredPropertyBase<T?> = addProperty(factory.enum(defaultValue, clazz))
90
91   /**
92    * Not-null list. Initialized as SmartList.
93    */
94   protected fun <T : Any> list(): StoredPropertyBase<MutableList<T>> = addProperty(factory.list<T>())
95
96   protected fun <K : Any, V: Any> map(): StoredPropertyBase<MutableMap<K, V>> = addProperty(factory.map<K, V>(null))
97
98   protected fun <K : Any, V: Any> linkedMap(): StoredPropertyBase<MutableMap<K, V>> = addProperty(factory.map<K, V>(LinkedHashMap()))
99
100   @Deprecated(level = DeprecationLevel.ERROR, message = "Use map", replaceWith = ReplaceWith("map()"))
101   protected fun <K : Any, V: Any> map(value: MutableMap<K, V>): StoredPropertyBase<MutableMap<K, V>> = addProperty(factory.map(value))
102
103   @Deprecated(level = DeprecationLevel.ERROR, message = "Use string", replaceWith = ReplaceWith("string(defaultValue)"))
104   protected fun property(defaultValue: String?) = string(defaultValue)
105
106   /**
107    * Empty string is always normalized to null.
108    */
109   protected fun string(defaultValue: String? = null): StoredPropertyBase<String?> = addProperty(factory.string(defaultValue))
110
111   protected fun property(defaultValue: Int = 0): StoredPropertyBase<Int> = addProperty(factory.int(defaultValue))
112
113   protected fun property(defaultValue: Long = 0): StoredPropertyBase<Long> = addProperty(factory.long(defaultValue))
114
115   protected fun property(defaultValue: Float = 0f, valueNormalizer: ((value: Float) -> Float)? = null): StoredPropertyBase<Float> {
116     return addProperty(factory.float(defaultValue, valueNormalizer))
117   }
118
119   protected fun property(defaultValue: Boolean = false): StoredPropertyBase<Boolean> = addProperty(factory.bool(defaultValue))
120
121   // reset on load state
122   fun resetModificationCount() {
123     ownModificationCount = 0
124   }
125
126   protected fun incrementModificationCount() {
127     intIncrementModificationCount()
128   }
129
130   @ApiStatus.Internal
131   fun intIncrementModificationCount() {
132     MOD_COUNT_UPDATER.incrementAndGet(this)
133   }
134
135   override fun accepts(accessor: Accessor, bean: Any): Boolean {
136     val getterName = (accessor as? PropertyAccessor)?.getterName
137     for (property in properties) {
138       if (property.name == accessor.name || property.name == getterName) {
139         return !property.isEqualToDefault()
140       }
141     }
142
143     LOG.debug("Cannot find property by name: ${accessor.name}")
144     // do not return false - maybe accessor delegates actual set to our property
145     // default value in this case will be filtered by common filter (instance will be created in this case, as for non-smart state classes)
146     return true
147   }
148
149   fun isEqualToDefault(): Boolean = properties.all { it.isEqualToDefault() }
150
151   /**
152    * If you use [set], [treeSet] or [linkedMap], you must ensure that [incrementModificationCount] is called for each mutation operation on corresponding property value (e.g. add, remove).
153    * Setting property to a new value updates modification count, but direct modification of mutable collection or map doesn't.
154    *
155    * [list] and [map] track content mutation, but if key or value is not primitive value, you also have to [incrementModificationCount] in case of nested mutation.
156    */
157   @Transient
158   override fun getModificationCount(): Long {
159     var result = ownModificationCount
160     for (property in properties) {
161       result += property.getModificationCount()
162     }
163     return result
164   }
165
166   override fun equals(other: Any?): Boolean = this === other || (other is BaseState && properties == other.properties)
167
168   override fun hashCode(): Int = properties.hashCode()
169
170   override fun toString(): String {
171     if (properties.isEmpty()) {
172       return ""
173     }
174
175     val builder = StringBuilder()
176     for (property in properties) {
177       builder.append(property.toString()).append(" ")
178     }
179     builder.setLength(builder.length - 1)
180     return builder.toString()
181   }
182
183   @JvmOverloads
184   fun copyFrom(state: BaseState, isMustBeTheSameType: Boolean = true) {
185     val propertyCount = state.properties.size
186     if (isMustBeTheSameType) {
187       LOG.assertTrue(propertyCount == properties.size)
188     }
189
190     var changed = false
191     for ((index, otherProperty) in state.properties.withIndex()) {
192       val property = properties.get(index)
193       LOG.assertTrue(otherProperty.name == property.name)
194       if (property.setValue(otherProperty)) {
195         changed = true
196       }
197     }
198
199     if (changed) {
200       incrementModificationCount()
201     }
202   }
203
204   // internal usage only
205   @Suppress("FunctionName")
206   @ApiStatus.Internal
207   fun __getProperties() = properties
208 }
209
210 interface StatePropertyFactory {
211   fun bool(defaultValue: Boolean): StoredPropertyBase<Boolean>
212
213   fun <T> obj(defaultValue: T): StoredPropertyBase<T>
214
215   fun <T> obj(initialValue: T, isDefault: (value: T) -> Boolean): StoredPropertyBase<T>
216
217   fun <T : BaseState?> stateObject(initialValue: T): StoredPropertyBase<T>
218
219   fun <T : Any> list(): StoredPropertyBase<MutableList<T>>
220
221   fun <K : Any, V: Any> map(value: MutableMap<K, V>?): StoredPropertyBase<MutableMap<K, V>>
222
223   fun float(defaultValue: Float = 0f, valueNormalizer: ((value: Float) -> Float)? = null): StoredPropertyBase<Float>
224
225   fun long(defaultValue: Long): StoredPropertyBase<Long>
226
227   fun int(defaultValue: Int): StoredPropertyBase<Int>
228
229   // nullable default value is not a default value
230   fun stringSet(defaultValue: String?): StoredPropertyBase<MutableSet<String>>
231
232   fun <E> treeSet(): StoredPropertyBase<MutableSet<E>> where E : Comparable<E>, E : BaseState
233
234   fun <T : Enum<*>> enum(defaultValue: T?, clazz: Class<T>): StoredPropertyBase<T?>
235
236   fun string(defaultValue: String?): StoredPropertyBase<String?>
237 }