mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-03-22 15:19:59 +07:00
IJPL-136 SC PSC bridge - set
GitOrigin-RevId: 85cb181bfc859b90c9b75e15ea30e83b5a0d46ad
This commit is contained in:
committed by
intellij-monorepo-bot
parent
295ce04b73
commit
6771806ea0
3
platform/configuration-store-impl/.editorconfig
Normal file
3
platform/configuration-store-impl/.editorconfig
Normal file
@@ -0,0 +1,3 @@
|
||||
[*]
|
||||
# critical subsystem - named params used a lot
|
||||
max_line_length = 180
|
||||
@@ -9,13 +9,9 @@ import com.intellij.openapi.util.WriteExternalException
|
||||
import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream
|
||||
import com.intellij.openapi.vfs.LargeFileWriteRequestor
|
||||
import com.intellij.openapi.vfs.SafeWriteRequestor
|
||||
import com.intellij.platform.settings.PersistenceStateComponentPropertyTag
|
||||
import com.intellij.platform.settings.SetResult
|
||||
import com.intellij.platform.settings.SettingsController
|
||||
import com.intellij.platform.settings.*
|
||||
import com.intellij.serialization.SerializationException
|
||||
import com.intellij.util.xmlb.BeanBinding
|
||||
import com.intellij.util.xmlb.RootBinding
|
||||
import com.intellij.util.xmlb.XmlSerializationException
|
||||
import com.intellij.util.xmlb.*
|
||||
import org.jdom.Element
|
||||
|
||||
abstract class SaveSessionProducerBase : SaveSessionProducer, SafeWriteRequestor, LargeFileWriteRequestor {
|
||||
@@ -75,14 +71,18 @@ internal fun serializeState(state: Any, componentName: String, pluginId: PluginI
|
||||
else -> {
|
||||
try {
|
||||
val filter = jdomSerializer.getDefaultSerializationFilter()
|
||||
val binding = __platformSerializer().getRootBinding(state.javaClass)
|
||||
if (binding is BeanBinding) {
|
||||
// top level expects not null (null indicates error, an empty element will be omitted)
|
||||
return binding.serializeProperties(bean = state, preCreatedElement = null, filter = filter)
|
||||
val rootBinding = __platformSerializer().getRootBinding(state.javaClass)
|
||||
if (rootBinding is BeanBinding) {
|
||||
if (controller == null) {
|
||||
return rootBinding.serializeProperties(bean = state, preCreatedElement = null, filter = filter)
|
||||
}
|
||||
else {
|
||||
return serializeWithController(rootBinding = rootBinding, bean = state, filter = filter, componentName = componentName, pluginId = pluginId, controller = controller)
|
||||
}
|
||||
}
|
||||
else {
|
||||
// maybe ArrayBinding
|
||||
return (binding as RootBinding).serialize(bean = state, filter = filter) as Element
|
||||
return (rootBinding as RootBinding).serialize(bean = state, filter = filter) as Element
|
||||
}
|
||||
}
|
||||
catch (e: SerializationException) {
|
||||
@@ -94,3 +94,37 @@ internal fun serializeState(state: Any, componentName: String, pluginId: PluginI
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun serializeWithController(
|
||||
rootBinding: BeanBinding,
|
||||
bean: Any,
|
||||
filter: SkipDefaultsSerializationFilter,
|
||||
componentName: String,
|
||||
pluginId: PluginId,
|
||||
controller: SettingsController,
|
||||
): Element? {
|
||||
val keyTags = java.util.List.of(PersistenceStateComponentPropertyTag(componentName))
|
||||
var element: Element? = null
|
||||
for (binding in rootBinding.bindings!!) {
|
||||
if (bean is SerializationFilter && !bean.accepts(binding.accessor, bean = bean)) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (isPropertySkipped(filter = filter, binding = binding, bean = bean, isFilterPropertyItself = true)) {
|
||||
continue
|
||||
}
|
||||
|
||||
val key = SettingDescriptor(key = "$componentName.${binding.propertyName}", pluginId = pluginId, tags = keyTags, serializer = JsonElementSettingSerializerDescriptor)
|
||||
val result = controller.doSetItem(key = key, value = binding.toJson(bean, filter))
|
||||
if (result != SetResult.INAPPLICABLE) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (element == null) {
|
||||
element = Element(rootBinding.tagName)
|
||||
}
|
||||
|
||||
binding.serialize(bean = bean, parent = element, filter = filter)
|
||||
}
|
||||
return element
|
||||
}
|
||||
@@ -47,7 +47,7 @@ internal fun <T : Any> deserializeStateWithController(
|
||||
|
||||
val serializer = __platformSerializer()
|
||||
// KotlinxSerializationBinding here is possible, do not cast to BeanBinding
|
||||
val rootBinding = serializer.getRootBinding(stateClass) as NotNullDeserializeBinding
|
||||
val rootBinding = serializer.getRootBinding(stateClass)
|
||||
try {
|
||||
if (mergeInto == null) {
|
||||
if (rootBinding !is BeanBinding || controller == null) {
|
||||
@@ -141,7 +141,7 @@ private fun <T : Any> getXmlSerializationState(
|
||||
|
||||
val keyTags = java.util.List.of(PersistenceStateComponentPropertyTag(componentName))
|
||||
for (binding in bindings) {
|
||||
val key = createSettingDescriptor(key = "${componentName}.${binding.accessor.name}", pluginId = pluginId, tags = keyTags)
|
||||
val key = createSettingDescriptor(key = "${componentName}.${binding.propertyName}", pluginId = pluginId, tags = keyTags)
|
||||
val value = try {
|
||||
controller.doGetItem(key)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
// Copyright 2000-2019 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-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.configurationStore.xml
|
||||
|
||||
import com.intellij.openapi.components.BaseState
|
||||
import com.intellij.util.xmlb.annotations.OptionTag
|
||||
import com.intellij.util.xmlb.annotations.Tag
|
||||
import com.intellij.util.xmlb.annotations.XCollection
|
||||
import com.intellij.util.xmlb.annotations.*
|
||||
import org.junit.Test
|
||||
import java.util.*
|
||||
|
||||
@@ -33,14 +31,25 @@ class XmlSerializerListTest {
|
||||
|
||||
val data = Bean()
|
||||
data.values = listOf("foo")
|
||||
testSerializer("""
|
||||
<bean>
|
||||
<option name="values">
|
||||
<list>
|
||||
<option value="foo" />
|
||||
</list>
|
||||
</option>
|
||||
</bean>""", data)
|
||||
testSerializer(
|
||||
expectedXml = """
|
||||
<bean>
|
||||
<option name="values">
|
||||
<list>
|
||||
<option value="foo" />
|
||||
</list>
|
||||
</option>
|
||||
</bean>
|
||||
""",
|
||||
expectedJson = """
|
||||
{
|
||||
"values": [
|
||||
"foo"
|
||||
]
|
||||
}
|
||||
""",
|
||||
bean = data,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -118,22 +127,113 @@ class XmlSerializerListTest {
|
||||
|
||||
val bean = Bean()
|
||||
|
||||
testSerializer("""<bean />""", bean)
|
||||
testSerializer(
|
||||
expectedXml = """<bean />""",
|
||||
expectedJson = """{}""",
|
||||
bean = bean,
|
||||
)
|
||||
|
||||
bean.specSources.clear()
|
||||
bean.specSources.addAll(listOf(SpecSource("foo"), SpecSource("bar")))
|
||||
|
||||
testSerializer("""
|
||||
<bean>
|
||||
<specSources>
|
||||
<SpecSource>
|
||||
<option name="pathOrUrl" value="foo" />
|
||||
</SpecSource>
|
||||
<SpecSource>
|
||||
<option name="pathOrUrl" value="bar" />
|
||||
</SpecSource>
|
||||
</specSources>
|
||||
</bean>""", bean)
|
||||
testSerializer(
|
||||
expectedXml = """
|
||||
<bean>
|
||||
<specSources>
|
||||
<SpecSource>
|
||||
<option name="pathOrUrl" value="foo" />
|
||||
</SpecSource>
|
||||
<SpecSource>
|
||||
<option name="pathOrUrl" value="bar" />
|
||||
</SpecSource>
|
||||
</specSources>
|
||||
</bean>
|
||||
""",
|
||||
expectedJson = """
|
||||
{
|
||||
"specSources": [
|
||||
{
|
||||
"pathOrUrl": "foo"
|
||||
},
|
||||
{
|
||||
"pathOrUrl": "bar"
|
||||
}
|
||||
]
|
||||
}
|
||||
""",
|
||||
bean = bean,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun elementTypes() {
|
||||
@Tag("selector")
|
||||
class Selector {
|
||||
@Attribute
|
||||
var name = ""
|
||||
@Attribute
|
||||
var path = ""
|
||||
}
|
||||
|
||||
@Transient
|
||||
abstract class H2
|
||||
|
||||
abstract class H : H2() {
|
||||
private val selectors = ArrayList<Selector>()
|
||||
|
||||
// test mutable list (in-place mutation must not be performed)
|
||||
@XCollection(propertyElementName = "selectors")
|
||||
fun getSelectors() = selectors.toMutableList()
|
||||
|
||||
fun setSelectors(value: List<Selector>) {
|
||||
selectors.clear()
|
||||
selectors.addAll(value)
|
||||
}
|
||||
}
|
||||
|
||||
class A : H()
|
||||
class B : H()
|
||||
class C : H()
|
||||
|
||||
class Bean {
|
||||
@XCollection(elementTypes = [A::class, B::class, C::class])
|
||||
val handlers = ArrayList<H>()
|
||||
}
|
||||
|
||||
val random = Random(42)
|
||||
fun s() = Selector().also { it.name = random.nextInt().toString(32); it.path = random.nextInt().toString(32) }
|
||||
|
||||
val bean = Bean()
|
||||
bean.handlers.add(A().also { it.setSelectors(listOf(s(), s())) })
|
||||
bean.handlers.add(B().also { it.setSelectors(listOf(s(), s())) })
|
||||
bean.handlers.add(C().also { it.setSelectors(listOf(s(), s())) })
|
||||
testSerializer(
|
||||
expectedXml = """
|
||||
<Bean>
|
||||
<option name="handlers">
|
||||
<A>
|
||||
<selectors>
|
||||
<selector name="-12rsomb" path="6vt2nn" />
|
||||
<selector name="-18hgh0v" path="64bg18" />
|
||||
</selectors>
|
||||
</A>
|
||||
<B>
|
||||
<selectors>
|
||||
<selector name="17ggf74" path="-7d8h5l" />
|
||||
<selector name="13et7c3" path="-15d6ukj" />
|
||||
</selectors>
|
||||
</B>
|
||||
<C>
|
||||
<selectors>
|
||||
<selector name="-1apt5a2" path="bm234q" />
|
||||
<selector name="-cbp623" path="1pob5us" />
|
||||
</selectors>
|
||||
</C>
|
||||
</option>
|
||||
</Bean>
|
||||
""",
|
||||
bean = bean,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -41,10 +41,10 @@ internal class XmlSerializerMapTest {
|
||||
""",
|
||||
expectedJson = """
|
||||
{
|
||||
"values": {
|
||||
"foo": "foo"
|
||||
"values": {
|
||||
"foo": "boo"
|
||||
}
|
||||
}
|
||||
}
|
||||
""",
|
||||
bean = data,
|
||||
)
|
||||
@@ -95,7 +95,7 @@ internal class XmlSerializerMapTest {
|
||||
{
|
||||
"option": "xxx",
|
||||
"map": {
|
||||
"a": "a"
|
||||
"a": "b"
|
||||
}
|
||||
}
|
||||
""",
|
||||
|
||||
@@ -45,7 +45,7 @@ internal class XmlSerializerTest {
|
||||
expectedJson = """
|
||||
{
|
||||
"places_map": {
|
||||
"foo": "foo"
|
||||
"foo": "bar"
|
||||
}
|
||||
}
|
||||
""",
|
||||
|
||||
@@ -4,8 +4,8 @@ package com.intellij.serialization.xml
|
||||
import com.intellij.openapi.diagnostic.debug
|
||||
import com.intellij.serialization.LOG
|
||||
import com.intellij.util.xml.dom.XmlElement
|
||||
import com.intellij.util.xmlb.Binding
|
||||
import com.intellij.util.xmlb.DomAdapter
|
||||
import com.intellij.util.xmlb.NotNullDeserializeBinding
|
||||
import com.intellij.util.xmlb.RootBinding
|
||||
import com.intellij.util.xmlb.SerializationFilter
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
@@ -30,7 +30,7 @@ private val lookup = MethodHandles.lookup()
|
||||
private val kotlinMethodType = MethodType.methodType(KSerializer::class.java)
|
||||
|
||||
@Internal
|
||||
class KotlinxSerializationBinding(aClass: Class<*>) : NotNullDeserializeBinding, RootBinding {
|
||||
class KotlinxSerializationBinding(aClass: Class<*>) : Binding, RootBinding {
|
||||
@JvmField
|
||||
val serializer: KSerializer<Any>
|
||||
|
||||
@@ -45,7 +45,7 @@ class KotlinxSerializationBinding(aClass: Class<*>) : NotNullDeserializeBinding,
|
||||
return json.encodeToJsonElement(serializer, bean)
|
||||
}
|
||||
|
||||
override fun fromJson(bean: Any?, element: JsonElement) = json.decodeFromJsonElement(serializer, element)
|
||||
override fun fromJson(currentValue: Any?, element: JsonElement) = json.decodeFromJsonElement(serializer, element)
|
||||
|
||||
override fun serialize(bean: Any, parent: Element, filter: SerializationFilter?) {
|
||||
val json = encodeToJson(bean)
|
||||
|
||||
@@ -91,7 +91,7 @@ private class JdomSerializerImpl : JdomSerializer {
|
||||
|
||||
try {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return (serializer.getRootBinding(clazz, clazz) as NotNullDeserializeBinding).deserialize(null, element, adapter) as T
|
||||
return serializer.getRootBinding(clazz, clazz).deserialize(context = null, element = element, adapter = adapter) as T
|
||||
}
|
||||
catch (e: SerializationException) {
|
||||
throw e
|
||||
|
||||
@@ -1,12 +1,33 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
@file:Suppress("ReplacePutWithAssignment", "ReplaceGetOrSet")
|
||||
|
||||
package com.intellij.platform.settings.local
|
||||
|
||||
import com.intellij.configurationStore.SettingsSavingComponent
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.PathManager
|
||||
import com.intellij.openapi.components.ComponentManager
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.extensions.ExtensionNotApplicableException
|
||||
import com.intellij.openapi.extensions.PluginId
|
||||
import com.intellij.platform.settings.DelegatedSettingsController
|
||||
import com.intellij.platform.settings.GetResult
|
||||
import com.intellij.platform.settings.SetResult
|
||||
import com.intellij.platform.settings.SettingDescriptor
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import java.nio.file.Files
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
import kotlin.time.DurationUnit
|
||||
import kotlin.time.toDuration
|
||||
|
||||
private class JsonMirrorController : DelegatedSettingsController {
|
||||
init {
|
||||
@@ -15,16 +36,65 @@ private class JsonMirrorController : DelegatedSettingsController {
|
||||
}
|
||||
}
|
||||
|
||||
private val service by lazy {
|
||||
service<JsonMirrorStorage>()
|
||||
}
|
||||
|
||||
override fun <T : Any> getItem(key: SettingDescriptor<T>): GetResult<T?> {
|
||||
//println("${key.pluginId.idString}/${key.key}")
|
||||
return GetResult.inapplicable()
|
||||
}
|
||||
|
||||
override fun <T : Any> setItem(key: SettingDescriptor<T>, value: T?): SetResult {
|
||||
if (value is JsonElement) {
|
||||
service.setItem(key, value)
|
||||
//println(value.toString())
|
||||
}
|
||||
return SetResult.INAPPLICABLE
|
||||
}
|
||||
|
||||
override fun createChild(container: ComponentManager): DelegatedSettingsController {
|
||||
return JsonMirrorController()
|
||||
}
|
||||
}
|
||||
|
||||
@Service
|
||||
private class JsonMirrorStorage : SettingsSavingComponent {
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
private val jsonFormat = Json {
|
||||
prettyPrint = true
|
||||
prettyPrintIndent = " "
|
||||
}
|
||||
|
||||
// yes - save is ignored first 5 minutes
|
||||
private var lastSaved: Duration = Duration.ZERO
|
||||
|
||||
private val storage = ConcurrentHashMap<PluginId, ConcurrentHashMap<String, JsonElement>>()
|
||||
|
||||
fun setItem(key: SettingDescriptor<*>, value: JsonElement) {
|
||||
storage.computeIfAbsent(key.pluginId) { ConcurrentHashMap() }
|
||||
.put(key.key, value)
|
||||
}
|
||||
|
||||
override suspend fun save() {
|
||||
val exitInProgress = ApplicationManager.getApplication().isExitInProgress
|
||||
val now = System.currentTimeMillis().toDuration(DurationUnit.MILLISECONDS)
|
||||
if (!exitInProgress && (now - lastSaved) < 30.seconds) {
|
||||
return
|
||||
}
|
||||
|
||||
val keys = storage.keys.toMutableList()
|
||||
keys.sort()
|
||||
val jsonMap = LinkedHashMap<String, JsonElement>()
|
||||
for (key in keys) {
|
||||
val value = storage.get(key) ?: continue
|
||||
jsonMap.put(key.idString, JsonObject(LinkedHashMap(value)))
|
||||
}
|
||||
val data = jsonFormat.encodeToString(jsonMap)
|
||||
withContext(Dispatchers.IO) {
|
||||
Files.writeString(PathManager.getConfigDir().resolve("json-controller-state.json"), data)
|
||||
}
|
||||
|
||||
lastSaved = now
|
||||
}
|
||||
}
|
||||
@@ -64,7 +64,7 @@ internal class StateStorageBackedByController(
|
||||
}
|
||||
else -> {
|
||||
try {
|
||||
val beanBinding = bindingProducer.getRootBinding(stateClass) as NotNullDeserializeBinding
|
||||
val beanBinding = bindingProducer.getRootBinding(stateClass)
|
||||
if (beanBinding is KotlinxSerializationBinding) {
|
||||
val data = controller.getItem(createSettingDescriptor(componentName, pluginId)) ?: return null
|
||||
return cborFormat.decodeFromByteArray(beanBinding.serializer, data) as T
|
||||
@@ -111,7 +111,7 @@ internal class StateStorageBackedByController(
|
||||
|
||||
private fun <T : Any> getXmlSerializationState(
|
||||
mergeInto: T?,
|
||||
beanBinding: NotNullDeserializeBinding,
|
||||
beanBinding: Binding,
|
||||
componentName: String,
|
||||
pluginId: PluginId,
|
||||
): T? {
|
||||
|
||||
@@ -16,5 +16,6 @@
|
||||
<orderEntry type="module" module-name="intellij.platform.extensions" />
|
||||
<orderEntry type="module" module-name="intellij.platform.core" />
|
||||
<orderEntry type="library" name="kotlinx-serialization-core" level="project" />
|
||||
<orderEntry type="library" name="kotlinx-serialization-json" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -1,7 +1,9 @@
|
||||
// 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.platform.settings
|
||||
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.serializer
|
||||
import org.jetbrains.annotations.ApiStatus.Internal
|
||||
import org.jetbrains.annotations.ApiStatus.NonExtendable
|
||||
|
||||
@@ -27,3 +29,8 @@ object RawSettingSerializerDescriptor : SettingSerializerDescriptor<ByteArray>,
|
||||
override val serializer: KSerializer<ByteArray>
|
||||
get() = throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
@Internal
|
||||
object JsonElementSettingSerializerDescriptor : SettingSerializerDescriptor<JsonElement>, SettingValueSerializer<JsonElement> {
|
||||
override val serializer: KSerializer<JsonElement> = serializer()
|
||||
}
|
||||
@@ -73,7 +73,7 @@ final class AccessorBindingWrapper implements MultiNodeBinding, NestedBinding {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable <T> Object deserializeUnsafe(@Nullable Object context, @NotNull T element, @NotNull DomAdapter<T> adapter) {
|
||||
public @Nullable <T> Object deserialize(@Nullable Object context, @NotNull T element, @NotNull DomAdapter<T> adapter) {
|
||||
if (adapter == JdomAdapter.INSTANCE) {
|
||||
return deserializeUnsafe(context, (Element)element);
|
||||
}
|
||||
@@ -109,7 +109,7 @@ final class AccessorBindingWrapper implements MultiNodeBinding, NestedBinding {
|
||||
}
|
||||
}
|
||||
else {
|
||||
deserializedValue = binding.deserializeUnsafe(currentValue, element, JdomAdapter.INSTANCE);
|
||||
deserializedValue = binding.deserialize(currentValue, element, JdomAdapter.INSTANCE);
|
||||
}
|
||||
|
||||
if (currentValue != deserializedValue) {
|
||||
@@ -144,7 +144,7 @@ final class AccessorBindingWrapper implements MultiNodeBinding, NestedBinding {
|
||||
}
|
||||
}
|
||||
else {
|
||||
deserializedValue = binding.deserializeUnsafe(currentValue, element, XmlDomAdapter.INSTANCE);
|
||||
deserializedValue = binding.deserialize(currentValue, element, XmlDomAdapter.INSTANCE);
|
||||
}
|
||||
|
||||
if (currentValue != deserializedValue) {
|
||||
|
||||
@@ -70,7 +70,7 @@ final class AttributeBinding implements PrimitiveValueBinding {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable <T> Object deserializeUnsafe(@Nullable Object context, @NotNull T element, @NotNull DomAdapter<T> adapter) {
|
||||
public @Nullable <T> Object deserialize(@Nullable Object context, @NotNull T element, @NotNull DomAdapter<T> adapter) {
|
||||
return context;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +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", "ReplacePutWithAssignment")
|
||||
@file:ApiStatus.Internal
|
||||
@file:Internal
|
||||
|
||||
package com.intellij.util.xmlb
|
||||
|
||||
@@ -23,7 +23,7 @@ import kotlinx.serialization.json.JsonPrimitive
|
||||
import org.jdom.Element
|
||||
import org.jdom.Namespace
|
||||
import org.jdom.Text
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import org.jetbrains.annotations.ApiStatus.Internal
|
||||
import java.lang.reflect.AccessibleObject
|
||||
import java.lang.reflect.AnnotatedElement
|
||||
import java.lang.reflect.InvocationTargetException
|
||||
@@ -53,9 +53,9 @@ fun getBeanAccessors(aClass: Class<*>): List<MutableAccessor> = PROPERTY_COLLECT
|
||||
|
||||
internal const val JSON_CLASS_DISCRIMINATOR_KEY: String = "_class"
|
||||
|
||||
open class BeanBinding(@JvmField val beanClass: Class<*>) : NotNullDeserializeBinding, RootBinding {
|
||||
open class BeanBinding(@JvmField val beanClass: Class<*>) : Binding, RootBinding {
|
||||
@JvmField
|
||||
internal val tagName: String
|
||||
val tagName: String
|
||||
|
||||
@JvmField
|
||||
var bindings: Array<NestedBinding>? = null
|
||||
@@ -321,7 +321,7 @@ fun deserializeBeanInto(
|
||||
data.computeIfAbsent(binding) { ArrayList() }.add(child)
|
||||
}
|
||||
else {
|
||||
binding.deserializeUnsafe(result, child, XmlDomAdapter)
|
||||
binding.deserialize(result, child, XmlDomAdapter)
|
||||
}
|
||||
continue@nextNode
|
||||
}
|
||||
@@ -330,7 +330,7 @@ fun deserializeBeanInto(
|
||||
for (i in start until end) {
|
||||
val binding = bindings[i]
|
||||
if (binding is AccessorBindingWrapper && binding.isFlat) {
|
||||
binding.deserializeUnsafe(result, element, XmlDomAdapter)
|
||||
binding.deserialize(result, element, XmlDomAdapter)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -379,7 +379,7 @@ internal fun deserializeBeanInto(
|
||||
}
|
||||
else {
|
||||
accessorNameTracker?.add(binding.accessor.name)
|
||||
binding.deserializeUnsafe(result, child, JdomAdapter)
|
||||
binding.deserialize(result, child, JdomAdapter)
|
||||
}
|
||||
continue@nextNode
|
||||
}
|
||||
@@ -388,7 +388,7 @@ internal fun deserializeBeanInto(
|
||||
|
||||
for (binding in bindings) {
|
||||
if (binding is AccessorBindingWrapper && binding.isFlat) {
|
||||
binding.deserializeUnsafe(result, element, JdomAdapter)
|
||||
binding.deserialize(result, element, JdomAdapter)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -428,14 +428,14 @@ fun deserializeBeanInto(result: Any, element: Element, binding: NestedBinding, c
|
||||
data.add(child)
|
||||
}
|
||||
else {
|
||||
binding.deserializeUnsafe(result, child, JdomAdapter)
|
||||
binding.deserialize(result, child, JdomAdapter)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (binding is AccessorBindingWrapper && binding.isFlat) {
|
||||
binding.deserializeUnsafe(result, element, JdomAdapter)
|
||||
binding.deserialize(result, element, JdomAdapter)
|
||||
}
|
||||
|
||||
return data
|
||||
@@ -452,14 +452,14 @@ fun deserializeBeanFromControllerInto(result: Any, element: XmlElement, binding:
|
||||
data.add(child)
|
||||
}
|
||||
else {
|
||||
binding.deserializeUnsafe(result, child, XmlDomAdapter)
|
||||
binding.deserialize(result, child, XmlDomAdapter)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (binding is AccessorBindingWrapper && binding.isFlat) {
|
||||
binding.deserializeUnsafe(result, element, XmlDomAdapter)
|
||||
binding.deserialize(result, element, XmlDomAdapter)
|
||||
}
|
||||
|
||||
return data
|
||||
@@ -686,7 +686,8 @@ private fun getTagName(aClass: Class<*>): String {
|
||||
|
||||
private fun getTagNameFromAnnotation(aClass: Class<*>): String? = aClass.getAnnotation(Tag::class.java)?.value?.takeIf { it.isNotEmpty() }
|
||||
|
||||
private fun isPropertySkipped(filter: SerializationFilter?, binding: NestedBinding, bean: Any, isFilterPropertyItself: Boolean): Boolean {
|
||||
@Internal
|
||||
fun isPropertySkipped(filter: SerializationFilter?, binding: NestedBinding, bean: Any, isFilterPropertyItself: Boolean): Boolean {
|
||||
val accessor = binding.accessor
|
||||
val property = accessor.getAnnotation(Property::class.java)
|
||||
if (property == null || !property.alwaysWrite) {
|
||||
|
||||
@@ -37,7 +37,7 @@ interface Binding {
|
||||
fun init(originalType: Type, serializer: Serializer) {
|
||||
}
|
||||
|
||||
fun <T : Any> deserializeUnsafe(context: Any?, element: T, adapter: DomAdapter<T>): Any?
|
||||
fun <T : Any> deserialize(context: Any?, element: T, adapter: DomAdapter<T>): Any?
|
||||
|
||||
fun toJson(bean: Any, filter: SerializationFilter?): JsonElement?
|
||||
}
|
||||
@@ -57,10 +57,4 @@ interface MultiNodeBinding : Binding {
|
||||
val isMulti: Boolean
|
||||
|
||||
fun <T : Any> deserializeList(currentValue: Any?, elements: List<T>, adapter: DomAdapter<T>): Any?
|
||||
}
|
||||
|
||||
interface NotNullDeserializeBinding : Binding {
|
||||
fun <T : Any> deserialize(context: Any?, element: T, adapter: DomAdapter<T>): Any
|
||||
|
||||
override fun <T : Any> deserializeUnsafe(context: Any?, element: T, adapter: DomAdapter<T>): Any = deserialize(context = context, element = element, adapter)
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import com.intellij.serialization.ClassUtil
|
||||
import com.intellij.serialization.MutableAccessor
|
||||
import com.intellij.util.ArrayUtilRt
|
||||
import com.intellij.util.SmartList
|
||||
import com.intellij.util.xmlb.annotations.Transient
|
||||
import com.intellij.util.xmlb.annotations.XCollection
|
||||
import kotlinx.serialization.json.*
|
||||
import org.jdom.Attribute
|
||||
@@ -64,13 +65,13 @@ internal class CollectionBinding(
|
||||
@JvmField internal val elementTypes: Array<Class<*>>,
|
||||
private val serializer: Serializer,
|
||||
private val strategy: CollectionStrategy,
|
||||
) : MultiNodeBinding, NotNullDeserializeBinding, RootBinding {
|
||||
) : MultiNodeBinding, RootBinding {
|
||||
private var itemBindings: List<Binding>? = null
|
||||
|
||||
override fun init(originalType: Type, serializer: Serializer) {
|
||||
assert(itemBindings == null)
|
||||
|
||||
val binding = getItemBinding(itemType)
|
||||
val binding = getItemBinding(itemType, serializer)
|
||||
val elementTypes = elementTypes
|
||||
if (elementTypes.isEmpty()) {
|
||||
itemBindings = if (binding == null) emptyList() else listOf(binding)
|
||||
@@ -86,7 +87,7 @@ internal class CollectionBinding(
|
||||
}
|
||||
|
||||
for (aClass in elementTypes) {
|
||||
val b = getItemBinding(aClass)
|
||||
val b = getItemBinding(aClass, serializer)
|
||||
if (b != null && !itemBindings.contains(b)) {
|
||||
itemBindings.add(b)
|
||||
}
|
||||
@@ -99,8 +100,9 @@ internal class CollectionBinding(
|
||||
override val isMulti: Boolean
|
||||
get() = true
|
||||
|
||||
private fun getItemBinding(aClass: Class<*>): Binding? {
|
||||
return if (ClassUtil.isPrimitive(aClass)) null else serializer.getRootBinding(aClass, aClass)
|
||||
private fun getItemBinding(aClass: Class<*>, serializer: Serializer): Binding? {
|
||||
// element types maybe specified explicitly, in this case do not try to resolve binding for a class that marked as transient (to avoid warning "no accessors")
|
||||
return if (ClassUtil.isPrimitive(aClass) || aClass.isAnnotationPresent(Transient::class.java)) null else serializer.getRootBinding(aClass, aClass)
|
||||
}
|
||||
|
||||
override fun toJson(bean: Any, filter: SerializationFilter?): JsonArray {
|
||||
@@ -116,7 +118,7 @@ internal class CollectionBinding(
|
||||
continue
|
||||
}
|
||||
|
||||
when (val binding = getItemBinding(value.javaClass)) {
|
||||
when (val binding = getItemBinding(value.javaClass, serializer)) {
|
||||
null -> content.add(primitiveToJsonElement(value))
|
||||
is BeanBinding -> content.add(binding.toJson(bean = value, filter = filter, includeClassDiscriminator = itemBindings!!.size > 1))
|
||||
else -> content.add(binding.toJson(value, filter) ?: JsonNull)
|
||||
@@ -239,7 +241,7 @@ internal class CollectionBinding(
|
||||
return
|
||||
}
|
||||
|
||||
val binding = getItemBinding(value.javaClass)
|
||||
val binding = getItemBinding(value.javaClass, serializer)
|
||||
if (binding == null) {
|
||||
val elementName = elementName
|
||||
if (elementName.isEmpty()) {
|
||||
@@ -272,7 +274,7 @@ internal class CollectionBinding(
|
||||
return XmlSerializerImpl.convert(value, itemType)
|
||||
}
|
||||
else {
|
||||
return binding.deserializeUnsafe(bean, node, adapter)
|
||||
return binding.deserialize(bean, node, adapter)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import java.util.List;
|
||||
/**
|
||||
* @see com.intellij.util.xmlb.annotations.CollectionBean
|
||||
*/
|
||||
final class CompactCollectionBinding implements NotNullDeserializeBinding, NestedBinding {
|
||||
final class CompactCollectionBinding implements Binding, NestedBinding {
|
||||
private final String name;
|
||||
private final MutableAccessor accessor;
|
||||
|
||||
|
||||
@@ -11,8 +11,6 @@ sealed interface DomAdapter<T : Any> {
|
||||
|
||||
fun firstElement(element: T): T?
|
||||
|
||||
fun hasElementContent(element: T): Boolean
|
||||
|
||||
fun getAttributeValue(element: T, name: String): String?
|
||||
|
||||
fun getName(element: T): String
|
||||
@@ -32,8 +30,6 @@ data object JdomAdapter : DomAdapter<Element> {
|
||||
|
||||
override fun firstElement(element: Element): Element? = element.content.firstOrNull() as Element?
|
||||
|
||||
override fun hasElementContent(element: Element): Boolean = element.content.any { it is Element }
|
||||
|
||||
override fun getAttributeValue(element: Element, name: String): String? = element.getAttributeValue(name)
|
||||
|
||||
override fun getChildren(element: Element): List<Element> = element.children
|
||||
@@ -49,8 +45,6 @@ data object XmlDomAdapter : DomAdapter<XmlElement> {
|
||||
|
||||
override fun firstElement(element: XmlElement): XmlElement? = element.children.firstOrNull()
|
||||
|
||||
override fun hasElementContent(element: XmlElement): Boolean = element.children.isNotEmpty()
|
||||
|
||||
override fun getAttributeValue(element: XmlElement, name: String): String? = element.getAttributeValue(name)
|
||||
|
||||
override fun getChildren(element: XmlElement): List<XmlElement> = element.children
|
||||
|
||||
@@ -20,7 +20,7 @@ import java.util.List;
|
||||
|
||||
import static com.intellij.openapi.util.SafeStAXStreamBuilderKt.buildNsUnawareJdom;
|
||||
|
||||
final class JDOMElementBinding implements MultiNodeBinding, NestedBinding, NotNullDeserializeBinding {
|
||||
final class JDOMElementBinding implements MultiNodeBinding, NestedBinding {
|
||||
private final String tagName;
|
||||
private final MutableAccessor accessor;
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ final class MapBinding implements MultiNodeBinding, RootBinding {
|
||||
Map<String, JsonElement> content = new LinkedHashMap<>();
|
||||
for (Object k : keys) {
|
||||
JsonElement kJ = keyOrValueToJson(k, keyBinding, filter);
|
||||
JsonElement vJ = keyOrValueToJson(k, valueBinding, filter);
|
||||
JsonElement vJ = keyOrValueToJson(map.get(k), valueBinding, filter);
|
||||
// todo non-primitive keys
|
||||
content.put(kJ == null ? null : ((JsonPrimitive)kJ).getContent(), vJ);
|
||||
}
|
||||
@@ -245,7 +245,7 @@ final class MapBinding implements MultiNodeBinding, RootBinding {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable <T> Object deserializeUnsafe(@Nullable Object context, @NotNull T element, @NotNull DomAdapter<T> adapter) {
|
||||
public @Nullable <T> Object deserialize(@Nullable Object context, @NotNull T element, @NotNull DomAdapter<T> adapter) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -359,7 +359,7 @@ final class MapBinding implements MultiNodeBinding, RootBinding {
|
||||
assert binding != null;
|
||||
for (Element element : entry.getChildren()) {
|
||||
if (binding.isBoundTo(element, JdomAdapter.INSTANCE)) {
|
||||
return binding.deserializeUnsafe(context, element, JdomAdapter.INSTANCE);
|
||||
return binding.deserialize(context, element, JdomAdapter.INSTANCE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -390,7 +390,7 @@ final class MapBinding implements MultiNodeBinding, RootBinding {
|
||||
assert binding != null;
|
||||
for (XmlElement element : entry.children) {
|
||||
if (binding.isBoundTo(element, XmlDomAdapter.INSTANCE)) {
|
||||
return binding.deserializeUnsafe(context, element, XmlDomAdapter.INSTANCE);
|
||||
return binding.deserialize(context, element, XmlDomAdapter.INSTANCE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import org.jdom.Content
|
||||
import org.jdom.Element
|
||||
import org.jdom.Namespace
|
||||
import org.jdom.Text
|
||||
import java.util.*
|
||||
|
||||
internal class OptionTagBinding(
|
||||
@JvmField internal val binding: Binding?,
|
||||
@@ -38,10 +37,6 @@ internal class OptionTagBinding(
|
||||
override val isPrimitive: Boolean
|
||||
get() = binding == null || converter != null
|
||||
|
||||
override fun <T : Any> deserializeUnsafe(context: Any?, element: T, adapter: DomAdapter<T>): Any {
|
||||
return deserialize(host = context!!, element = element, adapter = adapter)
|
||||
}
|
||||
|
||||
override fun setValue(bean: Any, value: String?) {
|
||||
if (converter == null) {
|
||||
try {
|
||||
@@ -139,56 +134,68 @@ internal class OptionTagBinding(
|
||||
parent.addContent(targetElement)
|
||||
}
|
||||
|
||||
private fun <T : Any> deserialize(host: Any, element: T, adapter: DomAdapter<T>): Any {
|
||||
override fun <T : Any> deserialize(context: Any?, element: T, adapter: DomAdapter<T>): Any {
|
||||
context!!
|
||||
if (valueAttribute == null) {
|
||||
if (converter == null && binding != null) {
|
||||
if (binding is BeanBinding) {
|
||||
// yes, we must set `null` as well
|
||||
val value = (if (serializeBeanBindingWithoutWrapperTag) element else adapter.firstElement(element))?.let {
|
||||
binding.deserialize(context = null, element = it, adapter)
|
||||
}
|
||||
accessor.set(host, value)
|
||||
}
|
||||
else if (adapter.hasElementContent(element)) {
|
||||
val oldValue = accessor.read(host)
|
||||
val newValue = deserializeList(binding = binding, currentValue = oldValue, nodes = adapter.getChildren(element), adapter = adapter)
|
||||
if (oldValue !== newValue) {
|
||||
accessor.set(host, newValue)
|
||||
binding.deserialize(context = null, element = it, adapter = adapter)
|
||||
}
|
||||
accessor.set(context, value)
|
||||
}
|
||||
else if (binding is CollectionBinding || binding is MapBinding) {
|
||||
val oldValue = accessor.read(host)
|
||||
// do nothing if the field is already null
|
||||
if (oldValue != null) {
|
||||
val newValue = (binding as MultiNodeBinding).deserializeList(currentValue = oldValue, elements = Collections.emptyList(), adapter = adapter)
|
||||
if (oldValue !== newValue) {
|
||||
accessor.set(host, newValue)
|
||||
val nodes = adapter.getChildren(element)
|
||||
// in-place mutation only for non-writable accessors (final) - getter can return a mutable list,
|
||||
// and if we mutate it in-place, the deserialization result may be lost (XmlSerializerListTest.elementTypes test)
|
||||
if (accessor.isWritable) {
|
||||
// we must pass the current value in any case - collection binding use it to infer a new collection type
|
||||
val oldValue = accessor.read(context)
|
||||
if (nodes.isEmpty() && oldValue == null) {
|
||||
// do nothing if the field is already null
|
||||
}
|
||||
else {
|
||||
val newValue = (binding as MultiNodeBinding).deserializeList(currentValue = oldValue, elements = nodes, adapter = adapter)
|
||||
accessor.set(context, newValue)
|
||||
}
|
||||
}
|
||||
else {
|
||||
val oldValue = accessor.read(context)
|
||||
if (oldValue == null && nodes.isEmpty()) {
|
||||
// do nothing if the field is already null
|
||||
}
|
||||
else {
|
||||
val newValue = (binding as MultiNodeBinding).deserializeList(currentValue = oldValue, elements = nodes, adapter = adapter)
|
||||
if (oldValue !== newValue) {
|
||||
accessor.set(context, newValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
accessor.set(host, null)
|
||||
throw UnsupportedOperationException("Binding $binding is not expected")
|
||||
}
|
||||
}
|
||||
else {
|
||||
setValue(bean = host, value = adapter.getTextValue(element = element, defaultText = textIfTagValueEmpty))
|
||||
setValue(bean = context, value = adapter.getTextValue(element = element, defaultText = textIfTagValueEmpty))
|
||||
}
|
||||
}
|
||||
else {
|
||||
val value = adapter.getAttributeValue(element, valueAttribute)
|
||||
if (converter == null) {
|
||||
if (binding == null) {
|
||||
XmlSerializerImpl.doSet(host, value, accessor, ClassUtil.typeToClass(accessor.genericType))
|
||||
XmlSerializerImpl.doSet(context, value, accessor, ClassUtil.typeToClass(accessor.genericType))
|
||||
}
|
||||
else {
|
||||
accessor.set(host, binding.deserializeUnsafe(host, element, adapter))
|
||||
accessor.set(context, binding.deserialize(context, element, adapter))
|
||||
}
|
||||
}
|
||||
else {
|
||||
accessor.set(host, value?.let { converter.fromString(it) })
|
||||
accessor.set(context, value?.let { converter.fromString(it) })
|
||||
}
|
||||
}
|
||||
return host
|
||||
return context
|
||||
}
|
||||
|
||||
override fun <T : Any> isBoundTo(element: T, adapter: DomAdapter<T>): Boolean {
|
||||
@@ -211,8 +218,8 @@ internal fun addContent(targetElement: Element, node: Any) {
|
||||
|
||||
internal fun <T : Any> deserializeList(binding: Binding, currentValue: Any?, nodes: List<T>, adapter: DomAdapter<T>): Any? {
|
||||
return when {
|
||||
binding is MultiNodeBinding -> binding.deserializeList(currentValue = currentValue, elements = nodes, adapter)
|
||||
nodes.size == 1 -> binding.deserializeUnsafe(currentValue, nodes.get(0), adapter)
|
||||
binding is MultiNodeBinding -> binding.deserializeList(currentValue = currentValue, elements = nodes, adapter = adapter)
|
||||
nodes.size == 1 -> binding.deserialize(context = currentValue, element = nodes.get(0), adapter = adapter)
|
||||
nodes.isEmpty() -> null
|
||||
else -> throw AssertionError("Duplicate data for $binding will be ignored")
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ final class TextBinding implements PrimitiveValueBinding {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public <T> Object deserializeUnsafe(@Nullable Object context, @NotNull T element, @NotNull DomAdapter<T> adapter) {
|
||||
public <T> Object deserialize(@Nullable Object context, @NotNull T element, @NotNull DomAdapter<T> adapter) {
|
||||
return context;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.Objects;
|
||||
|
||||
public final class XmlSerializer {
|
||||
private XmlSerializer() {
|
||||
@@ -35,8 +36,8 @@ public final class XmlSerializer {
|
||||
@SuppressWarnings("unchecked")
|
||||
public static @NotNull <T> T deserialize(@NotNull Element element, @NotNull Class<T> aClass) throws SerializationException {
|
||||
try {
|
||||
NotNullDeserializeBinding binding = (NotNullDeserializeBinding)XmlSerializerImpl.serializer.getRootBinding(aClass, aClass);
|
||||
return (T)binding.deserialize(null, element, JdomAdapter.INSTANCE);
|
||||
Binding binding = XmlSerializerImpl.serializer.getRootBinding(aClass, aClass);
|
||||
return (T)Objects.requireNonNull(binding.deserialize(null, element, JdomAdapter.INSTANCE));
|
||||
}
|
||||
catch (SerializationException e) {
|
||||
throw e;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// 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.
|
||||
// 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.tasks.generic;
|
||||
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
@@ -36,29 +36,26 @@ import static com.intellij.tasks.generic.TemplateVariable.FactoryVariable;
|
||||
* @author Evgeny.Zakrevsky
|
||||
*/
|
||||
@Tag("Generic")
|
||||
public class GenericRepository extends BaseRepositoryImpl {
|
||||
@NonNls public static final String SERVER_URL = "serverUrl";
|
||||
@NonNls public static final String USERNAME = "username";
|
||||
@NonNls public static final String PASSWORD = "password";
|
||||
public final class GenericRepository extends BaseRepositoryImpl {
|
||||
public static final @NonNls String SERVER_URL = "serverUrl";
|
||||
public static final @NonNls String USERNAME = "username";
|
||||
public static final @NonNls String PASSWORD = "password";
|
||||
|
||||
private final FactoryVariable myServerTemplateVariable = new FactoryVariable(SERVER_URL) {
|
||||
@NotNull
|
||||
@Override
|
||||
public String getValue() {
|
||||
public @NotNull String getValue() {
|
||||
return GenericRepository.this.getUrl();
|
||||
}
|
||||
};
|
||||
private final FactoryVariable myUserNameTemplateVariable = new FactoryVariable(USERNAME) {
|
||||
@NotNull
|
||||
@Override
|
||||
public String getValue() {
|
||||
public @NotNull String getValue() {
|
||||
return GenericRepository.this.getUsername();
|
||||
}
|
||||
};
|
||||
private final FactoryVariable myPasswordTemplateVariable = new FactoryVariable(PASSWORD, true) {
|
||||
@NotNull
|
||||
@Override
|
||||
public String getValue() {
|
||||
public @NotNull String getValue() {
|
||||
return GenericRepository.this.getPassword();
|
||||
}
|
||||
};
|
||||
@@ -76,7 +73,7 @@ public class GenericRepository extends BaseRepositoryImpl {
|
||||
|
||||
private ResponseType myResponseType = ResponseType.JSON;
|
||||
|
||||
private EnumMap<ResponseType, ResponseHandler> myResponseHandlersMap = new EnumMap<>(ResponseType.class);
|
||||
private EnumMap<ResponseType, ResponseHandler> responseHandlerMap = new EnumMap<>(ResponseType.class);
|
||||
|
||||
private List<TemplateVariable> myTemplateVariables = new ArrayList<>();
|
||||
|
||||
@@ -113,11 +110,11 @@ public class GenericRepository extends BaseRepositoryImpl {
|
||||
myTemplateVariables = other.getTemplateVariables();
|
||||
mySubtypeName = other.getSubtypeName();
|
||||
myDownloadTasksInSeparateRequests = other.getDownloadTasksInSeparateRequests();
|
||||
myResponseHandlersMap = new EnumMap<>(ResponseType.class);
|
||||
for (Map.Entry<ResponseType, ResponseHandler> e : other.myResponseHandlersMap.entrySet()) {
|
||||
responseHandlerMap = new EnumMap<>(ResponseType.class);
|
||||
for (Map.Entry<ResponseType, ResponseHandler> e : other.responseHandlerMap.entrySet()) {
|
||||
ResponseHandler handler = e.getValue().clone();
|
||||
handler.setRepository(this);
|
||||
myResponseHandlersMap.put(e.getKey(), handler);
|
||||
responseHandlerMap.put(e.getKey(), handler);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,15 +128,14 @@ public class GenericRepository extends BaseRepositoryImpl {
|
||||
mySingleTaskMethodType = HTTPMethod.GET;
|
||||
myResponseType = ResponseType.JSON;
|
||||
myTemplateVariables = new ArrayList<>();
|
||||
myResponseHandlersMap = new EnumMap<>(ResponseType.class);
|
||||
myResponseHandlersMap.put(ResponseType.XML, getXmlResponseHandlerDefault());
|
||||
myResponseHandlersMap.put(ResponseType.JSON, getJsonResponseHandlerDefault());
|
||||
myResponseHandlersMap.put(ResponseType.TEXT, getTextResponseHandlerDefault());
|
||||
responseHandlerMap = new EnumMap<>(ResponseType.class);
|
||||
responseHandlerMap.put(ResponseType.XML, getXmlResponseHandlerDefault());
|
||||
responseHandlerMap.put(ResponseType.JSON, getJsonResponseHandlerDefault());
|
||||
responseHandlerMap.put(ResponseType.TEXT, getTextResponseHandlerDefault());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public GenericRepository clone() {
|
||||
public @NotNull GenericRepository clone() {
|
||||
return new GenericRepository(this);
|
||||
}
|
||||
|
||||
@@ -173,7 +169,7 @@ public class GenericRepository extends BaseRepositoryImpl {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task[] getIssues(@Nullable final String query, final int max, final long since) throws Exception {
|
||||
public Task[] getIssues(final @Nullable String query, final int max, final long since) throws Exception {
|
||||
if (StringUtil.isEmpty(myTasksListUrl)) {
|
||||
throw new Exception(TaskBundle.message("task.list.url.configuration.parameter.is.mandatory"));
|
||||
}
|
||||
@@ -233,18 +229,16 @@ public class GenericRepository extends BaseRepositoryImpl {
|
||||
return getHttpMethod(requestUrl, myLoginMethodType);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Task findTask(@NotNull final String id) throws Exception {
|
||||
public @Nullable Task findTask(final @NotNull String id) throws Exception {
|
||||
List<TemplateVariable> variables = concat(getAllTemplateVariables(), new TemplateVariable("id", id));
|
||||
String requestUrl = substituteTemplateVariables(getSingleTaskUrl(), variables);
|
||||
HttpMethod method = getHttpMethod(requestUrl, mySingleTaskMethodType);
|
||||
return getActiveResponseHandler().parseIssue(executeMethod(method));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public CancellableConnection createCancellableConnection() {
|
||||
public @Nullable CancellableConnection createCancellableConnection() {
|
||||
return new CancellableConnection() {
|
||||
@Override
|
||||
protected void doTest() throws Exception {
|
||||
@@ -349,11 +343,11 @@ public class GenericRepository extends BaseRepositoryImpl {
|
||||
}
|
||||
|
||||
public ResponseHandler getResponseHandler(ResponseType type) {
|
||||
return myResponseHandlersMap.get(type);
|
||||
return responseHandlerMap.get(type);
|
||||
}
|
||||
|
||||
public ResponseHandler getActiveResponseHandler() {
|
||||
return myResponseHandlersMap.get(myResponseType);
|
||||
return responseHandlerMap.get(myResponseType);
|
||||
}
|
||||
|
||||
@XCollection(
|
||||
@@ -363,22 +357,19 @@ public class GenericRepository extends BaseRepositoryImpl {
|
||||
RegExResponseHandler.class
|
||||
}
|
||||
)
|
||||
public List<ResponseHandler> getResponseHandlers() {
|
||||
if (myResponseHandlersMap.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return Collections.unmodifiableList(new ArrayList<>(myResponseHandlersMap.values()));
|
||||
public @NotNull List<ResponseHandler> getResponseHandlers() {
|
||||
return responseHandlerMap.isEmpty() ? Collections.emptyList() : List.copyOf(responseHandlerMap.values());
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
public void setResponseHandlers(List<ResponseHandler> responseHandlers) {
|
||||
myResponseHandlersMap.clear();
|
||||
public void setResponseHandlers(@NotNull List<ResponseHandler> responseHandlers) {
|
||||
responseHandlerMap.clear();
|
||||
for (ResponseHandler handler : responseHandlers) {
|
||||
myResponseHandlersMap.put(handler.getResponseType(), handler);
|
||||
responseHandlerMap.put(handler.getResponseType(), handler);
|
||||
}
|
||||
// ResponseHandler#repository field is excluded from serialization to prevent
|
||||
// circular dependency so it has to be done manually during serialization process
|
||||
for (ResponseHandler handler : myResponseHandlersMap.values()) {
|
||||
// circular dependency, so it has to be done manually during a serialization process
|
||||
for (ResponseHandler handler : responseHandlerMap.values()) {
|
||||
handler.setRepository(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2018 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-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.tasks.generic;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
@@ -98,7 +98,7 @@ public final class RegExResponseHandler extends ResponseHandler {
|
||||
for (int i = 0; i < max && matcher.find(); i++) {
|
||||
String id = matcher.group(placeholders.indexOf(ID_PLACEHOLDER) + 1);
|
||||
String summary = matcher.group(placeholders.indexOf(SUMMARY_PLACEHOLDER) + 1);
|
||||
tasks.add(new GenericTask(id, summary, myRepository));
|
||||
tasks.add(new GenericTask(id, summary, repository));
|
||||
}
|
||||
return tasks.toArray(Task.EMPTY_ARRAY);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// 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.tasks.generic;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
@@ -10,16 +11,16 @@ import javax.swing.*;
|
||||
|
||||
/**
|
||||
* ResponseHandler subclasses represent different strategies of extracting tasks from
|
||||
* task server responses (e.g. using regular expressions, XPath, JSONPath, CSS selector etc.)
|
||||
* task server responses (e.g., using regular expressions, XPath, JSONPath, CSS selector, etc.)
|
||||
*
|
||||
* @see XPathResponseHandler
|
||||
* @see JsonPathResponseHandler
|
||||
* @see RegExResponseHandler
|
||||
* @author Mikhail Golubev
|
||||
*/
|
||||
@Transient
|
||||
public abstract class ResponseHandler implements Cloneable {
|
||||
|
||||
protected GenericRepository myRepository;
|
||||
protected GenericRepository repository;
|
||||
|
||||
/**
|
||||
* Serialization constructor
|
||||
@@ -29,29 +30,24 @@ public abstract class ResponseHandler implements Cloneable {
|
||||
}
|
||||
|
||||
public ResponseHandler(@NotNull GenericRepository repository) {
|
||||
myRepository = repository;
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
public void setRepository(@NotNull GenericRepository repository) {
|
||||
myRepository = repository;
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Transient
|
||||
public GenericRepository getRepository() {
|
||||
return myRepository;
|
||||
public @NotNull GenericRepository getRepository() {
|
||||
return repository;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public abstract JComponent getConfigurationComponent(@NotNull Project project);
|
||||
public abstract @NotNull JComponent getConfigurationComponent(@NotNull Project project);
|
||||
|
||||
@NotNull
|
||||
public abstract ResponseType getResponseType();
|
||||
public abstract @NotNull ResponseType getResponseType();
|
||||
|
||||
public abstract Task @NotNull [] parseIssues(@NotNull String response, int max) throws Exception;
|
||||
|
||||
@Nullable
|
||||
public abstract Task parseIssue(@NotNull String response) throws Exception;
|
||||
public abstract @Nullable Task parseIssue(@NotNull String response) throws Exception;
|
||||
|
||||
public abstract boolean isConfigured();
|
||||
|
||||
@@ -59,7 +55,8 @@ public abstract class ResponseHandler implements Cloneable {
|
||||
public ResponseHandler clone() {
|
||||
try {
|
||||
return (ResponseHandler) super.clone();
|
||||
} catch (CloneNotSupportedException e) {
|
||||
}
|
||||
catch (CloneNotSupportedException e) {
|
||||
throw new AssertionError("ResponseHandler#clone() should be supported");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.tasks.generic;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
@@ -29,25 +27,25 @@ public abstract class SelectorBasedResponseHandler extends ResponseHandler {
|
||||
private static final Logger LOG = Logger.getInstance(SelectorBasedResponseHandler.class);
|
||||
|
||||
// Supported selector names
|
||||
@NonNls protected static final String TASKS = "tasks";
|
||||
protected static final @NonNls String TASKS = "tasks";
|
||||
|
||||
@NonNls protected static final String SUMMARY = "summary";
|
||||
@NonNls protected static final String DESCRIPTION = "description";
|
||||
@NonNls protected static final String ISSUE_URL = "issueUrl";
|
||||
@NonNls protected static final String CLOSED = "closed";
|
||||
@NonNls protected static final String UPDATED = "updated";
|
||||
@NonNls protected static final String CREATED = "created";
|
||||
protected static final @NonNls String SUMMARY = "summary";
|
||||
protected static final @NonNls String DESCRIPTION = "description";
|
||||
protected static final @NonNls String ISSUE_URL = "issueUrl";
|
||||
protected static final @NonNls String CLOSED = "closed";
|
||||
protected static final @NonNls String UPDATED = "updated";
|
||||
protected static final @NonNls String CREATED = "created";
|
||||
|
||||
@NonNls protected static final String SINGLE_TASK_ID = "singleTask-id";
|
||||
@NonNls protected static final String SINGLE_TASK_SUMMARY = "singleTask-summary";
|
||||
@NonNls protected static final String SINGLE_TASK_DESCRIPTION = "singleTask-description";
|
||||
@NonNls protected static final String SINGLE_TASK_ISSUE_URL = "singleTask-issueUrl";
|
||||
@NonNls protected static final String SINGLE_TASK_CLOSED = "singleTask-closed";
|
||||
@NonNls protected static final String SINGLE_TASK_UPDATED = "singleTask-updated";
|
||||
@NonNls protected static final String SINGLE_TASK_CREATED = "singleTask-created";
|
||||
@NonNls protected static final String ID = "id";
|
||||
protected static final @NonNls String SINGLE_TASK_ID = "singleTask-id";
|
||||
protected static final @NonNls String SINGLE_TASK_SUMMARY = "singleTask-summary";
|
||||
protected static final @NonNls String SINGLE_TASK_DESCRIPTION = "singleTask-description";
|
||||
protected static final @NonNls String SINGLE_TASK_ISSUE_URL = "singleTask-issueUrl";
|
||||
protected static final @NonNls String SINGLE_TASK_CLOSED = "singleTask-closed";
|
||||
protected static final @NonNls String SINGLE_TASK_UPDATED = "singleTask-updated";
|
||||
protected static final @NonNls String SINGLE_TASK_CREATED = "singleTask-created";
|
||||
protected static final @NonNls String ID = "id";
|
||||
|
||||
protected LinkedHashMap<String, Selector> mySelectors = new LinkedHashMap<>();
|
||||
protected LinkedHashMap<String, Selector> selectors = new LinkedHashMap<>();
|
||||
|
||||
/**
|
||||
* Serialization constructor
|
||||
@@ -57,11 +55,11 @@ public abstract class SelectorBasedResponseHandler extends ResponseHandler {
|
||||
// empty
|
||||
}
|
||||
|
||||
protected SelectorBasedResponseHandler(GenericRepository repository) {
|
||||
protected SelectorBasedResponseHandler(@NotNull GenericRepository repository) {
|
||||
super(repository);
|
||||
// standard selectors
|
||||
setSelectors(List.of(
|
||||
// matched against list of tasks at whole downloaded from "taskListUrl"
|
||||
// matched against a list of tasks at whole downloaded from "taskListUrl"
|
||||
new Selector(TASKS),
|
||||
|
||||
// matched against single tasks extracted from the list downloaded from "taskListUrl"
|
||||
@@ -73,7 +71,7 @@ public abstract class SelectorBasedResponseHandler extends ResponseHandler {
|
||||
new Selector(CLOSED),
|
||||
new Selector(ISSUE_URL),
|
||||
|
||||
// matched against single task downloaded from "singleTaskUrl"
|
||||
// matched against a single task downloaded from "singleTaskUrl"
|
||||
new Selector(SINGLE_TASK_ID),
|
||||
new Selector(SINGLE_TASK_SUMMARY),
|
||||
new Selector(SINGLE_TASK_DESCRIPTION),
|
||||
@@ -85,35 +83,30 @@ public abstract class SelectorBasedResponseHandler extends ResponseHandler {
|
||||
}
|
||||
|
||||
@XCollection(propertyElementName = "selectors")
|
||||
@NotNull
|
||||
public List<Selector> getSelectors() {
|
||||
return new ArrayList<>(mySelectors.values());
|
||||
public @NotNull List<Selector> getSelectors() {
|
||||
return new ArrayList<>(selectors.values());
|
||||
}
|
||||
|
||||
public void setSelectors(@NotNull List<? extends Selector> selectors) {
|
||||
mySelectors.clear();
|
||||
public void setSelectors(@NotNull List<Selector> selectors) {
|
||||
this.selectors.clear();
|
||||
for (Selector selector : selectors) {
|
||||
mySelectors.put(selector.getName(), selector);
|
||||
this.selectors.put(selector.getName(), selector);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Only predefined selectors should be accessed.
|
||||
*/
|
||||
@NotNull
|
||||
protected Selector getSelector(@NotNull String name) {
|
||||
return mySelectors.get(name);
|
||||
protected @NotNull Selector getSelector(@NotNull String name) {
|
||||
return selectors.get(name);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected String getSelectorPath(@NotNull String name) {
|
||||
Selector s = getSelector(name);
|
||||
return s.getPath();
|
||||
protected @NotNull String getSelectorPath(@NotNull String name) {
|
||||
return getSelector(name).getPath();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public JComponent getConfigurationComponent(@NotNull Project project) {
|
||||
public @NotNull JComponent getConfigurationComponent(@NotNull Project project) {
|
||||
FileType fileType = getResponseType().getSelectorFileType();
|
||||
HighlightedSelectorsTable table = new HighlightedSelectorsTable(fileType, project, getSelectors());
|
||||
return new JBScrollPane(table);
|
||||
@@ -122,9 +115,9 @@ public abstract class SelectorBasedResponseHandler extends ResponseHandler {
|
||||
@Override
|
||||
public SelectorBasedResponseHandler clone() {
|
||||
SelectorBasedResponseHandler clone = (SelectorBasedResponseHandler)super.clone();
|
||||
clone.mySelectors = new LinkedHashMap<>(mySelectors.size());
|
||||
for (Selector selector : mySelectors.values()) {
|
||||
clone.mySelectors.put(selector.getName(), selector.clone());
|
||||
clone.selectors = new LinkedHashMap<>(selectors.size());
|
||||
for (Selector selector : selectors.values()) {
|
||||
clone.selectors.put(selector.getName(), selector.clone());
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
@@ -134,7 +127,7 @@ public abstract class SelectorBasedResponseHandler extends ResponseHandler {
|
||||
Selector idSelector = getSelector(ID);
|
||||
if (StringUtil.isEmpty(idSelector.getPath())) return false;
|
||||
Selector summarySelector = getSelector(SUMMARY);
|
||||
if (StringUtil.isEmpty(summarySelector.getPath()) && !myRepository.getDownloadTasksInSeparateRequests()) return false;
|
||||
if (StringUtil.isEmpty(summarySelector.getPath()) && !repository.getDownloadTasksInSeparateRequests()) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -143,21 +136,21 @@ public abstract class SelectorBasedResponseHandler extends ResponseHandler {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof SelectorBasedResponseHandler handler)) return false;
|
||||
|
||||
if (!mySelectors.equals(handler.mySelectors)) return false;
|
||||
if (!selectors.equals(handler.selectors)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return mySelectors.hashCode();
|
||||
return selectors.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Task @NotNull [] parseIssues(@NotNull String response, int max) throws Exception {
|
||||
if (StringUtil.isEmpty(getSelectorPath(TASKS)) ||
|
||||
StringUtil.isEmpty(getSelectorPath(ID)) ||
|
||||
(StringUtil.isEmpty(getSelectorPath(SUMMARY)) && !myRepository.getDownloadTasksInSeparateRequests())) {
|
||||
(StringUtil.isEmpty(getSelectorPath(SUMMARY)) && !repository.getDownloadTasksInSeparateRequests())) {
|
||||
throw new Exception("Selectors 'tasks', 'id' and 'summary' are mandatory");
|
||||
}
|
||||
List<Object> tasks = selectTasksList(response, max);
|
||||
@@ -166,13 +159,13 @@ public abstract class SelectorBasedResponseHandler extends ResponseHandler {
|
||||
for (Object context : tasks) {
|
||||
String id = selectString(getSelector(ID), context);
|
||||
GenericTask task;
|
||||
if (myRepository.getDownloadTasksInSeparateRequests()) {
|
||||
task = new GenericTask(id, "", myRepository);
|
||||
if (repository.getDownloadTasksInSeparateRequests()) {
|
||||
task = new GenericTask(id, "", repository);
|
||||
}
|
||||
else {
|
||||
String summary = selectString(getSelector(SUMMARY), context);
|
||||
assert id != null && summary != null;
|
||||
task = new GenericTask(id, summary, myRepository);
|
||||
task = new GenericTask(id, summary, repository);
|
||||
String description = selectString(getSelector(DESCRIPTION), context);
|
||||
if (description != null) {
|
||||
task.setDescription(description);
|
||||
@@ -199,8 +192,7 @@ public abstract class SelectorBasedResponseHandler extends ResponseHandler {
|
||||
return result.toArray(Task.EMPTY_ARRAY);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Date selectDate(@NotNull Selector selector, @NotNull Object context) throws Exception {
|
||||
private @Nullable Date selectDate(@NotNull Selector selector, @NotNull Object context) throws Exception {
|
||||
String s = selectString(selector, context);
|
||||
if (s == null) {
|
||||
return null;
|
||||
@@ -208,8 +200,7 @@ public abstract class SelectorBasedResponseHandler extends ResponseHandler {
|
||||
return TaskUtil.parseDate(s);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected Boolean selectBoolean(@NotNull Selector selector, @NotNull Object context) throws Exception {
|
||||
protected @Nullable Boolean selectBoolean(@NotNull Selector selector, @NotNull Object context) throws Exception {
|
||||
String s = selectString(selector, context);
|
||||
if (s == null) {
|
||||
return null;
|
||||
@@ -225,23 +216,21 @@ public abstract class SelectorBasedResponseHandler extends ResponseHandler {
|
||||
String.format("Expression '%s' should match boolean value. Got '%s' instead", selector.getName(), s));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected abstract List<Object> selectTasksList(@NotNull String response, int max) throws Exception;
|
||||
protected abstract @NotNull List<Object> selectTasksList(@NotNull String response, int max) throws Exception;
|
||||
|
||||
@Nullable
|
||||
protected abstract @Nls String selectString(@NotNull Selector selector, @NotNull Object context) throws Exception;
|
||||
protected abstract @Nullable @Nls String selectString(@NotNull Selector selector, @NotNull Object context) throws Exception;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public final Task parseIssue(@NotNull String response) throws Exception {
|
||||
public final @NotNull Task parseIssue(@NotNull String response) throws Exception {
|
||||
if (StringUtil.isEmpty(getSelectorPath(SINGLE_TASK_ID)) ||
|
||||
StringUtil.isEmpty(getSelectorPath(SINGLE_TASK_SUMMARY))) {
|
||||
throw new Exception("Selectors 'singleTask-id' and 'singleTask-summary' are mandatory");
|
||||
}
|
||||
|
||||
String id = selectString(getSelector(SINGLE_TASK_ID), response);
|
||||
String summary = selectString(getSelector(SINGLE_TASK_SUMMARY), response);
|
||||
assert id != null && summary != null;
|
||||
GenericTask task = new GenericTask(id, summary, myRepository);
|
||||
GenericTask task = new GenericTask(id, summary, repository);
|
||||
String description = selectString(getSelector(SINGLE_TASK_DESCRIPTION), response);
|
||||
if (description != null) {
|
||||
task.setDescription(description);
|
||||
|
||||
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2013 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-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.tasks.integration;
|
||||
|
||||
import com.intellij.tasks.Task;
|
||||
@@ -89,7 +75,7 @@ public class AsanaIntegrationTest extends GenericSubtypeTestCase {
|
||||
}
|
||||
|
||||
public void testParsingTaskList() throws Exception {
|
||||
// Don't forget to extract summary here, even though it doesn't happen actually when myRepository#getIssues is called
|
||||
// Don't forget to extract summary here, even though it doesn't happen when myRepository#getIssues is called
|
||||
myRepository.setDownloadTasksInSeparateRequests(false);
|
||||
Task[] tasks = myRepository.getActiveResponseHandler().parseIssues(TASK_LIST_RESPONSE, 50);
|
||||
List<Task> expected = List.of(new GenericTask("5479650606120", "Task #1", myRepository),
|
||||
|
||||
Reference in New Issue
Block a user