ToolWindowManager — do not write defaults (workspace component, but anyway reduce workspace file size)

This commit is contained in:
Vladimir Krivosheev
2017-12-19 12:26:21 +01:00
parent c5eb952e07
commit 8ea059d591
23 changed files with 628 additions and 710 deletions
@@ -33,7 +33,7 @@ class KotlinXmlSerializerTest {
val data = Foo()
data.PLACES_MAP = "new"
doSerializerTest("""
testSerializer("""
<bean>
<option name="PLACES_MAP" value="new" />
</bean>""", data)
@@ -42,7 +42,7 @@ class KotlinXmlSerializerTest {
@Test fun floatProperty() {
val state = AState()
state.floatProperty = 3.4f
doSerializerTest("""
testSerializer("""
<AState>
<option name="floatProperty" value="3.4" />
</AState>
@@ -58,7 +58,7 @@ class KotlinXmlSerializerTest {
val data = Foo()
data.PLACES_MAP.put("", PlaceSettings())
doSerializerTest("""
testSerializer("""
<bean>
<option name="PLACES_MAP">
<entry key="">
@@ -25,7 +25,7 @@ internal class XmlSerializerCollectionTest {
bean.list.add("\u0001one")
bean.list.add("two")
bean.list.add("three")
doSerializerTest("<b>\n" + " <list>\n" + " <item value=\"one\" />\n" + " <item value=\"two\" />\n" + " <item value=\"three\" />\n" + " </list>\n" + "</b>", bean, SkipDefaultsSerializationFilter())
testSerializer("<b>\n" + " <list>\n" + " <item value=\"one\" />\n" + " <item value=\"two\" />\n" + " <item value=\"three\" />\n" + " </list>\n" + "</b>", bean, SkipDefaultsSerializationFilter())
}
@Test fun CollectionBean() {
@@ -33,7 +33,7 @@ internal class XmlSerializerCollectionTest {
bean.list.add("one")
bean.list.add("two")
bean.list.add("three")
doSerializerTest("<b>\n" + " <list>\n" + " <item value=\"one\" />\n" + " <item value=\"two\" />\n" + " <item value=\"three\" />\n" + " </list>\n" + "</b>", bean, SkipDefaultsSerializationFilter())
testSerializer("<b>\n" + " <list>\n" + " <item value=\"one\" />\n" + " <item value=\"two\" />\n" + " <item value=\"three\" />\n" + " </list>\n" + "</b>", bean, SkipDefaultsSerializationFilter())
}
@Test fun CollectionBeanReadJDOMExternalizableStringList() {
@@ -58,11 +58,11 @@ internal class XmlSerializerCollectionTest {
val bean = BeanWithPolymorphicArray()
doSerializerTest("<bean>\n <option name=\"v\">\n <array />\n </option>\n</bean>", bean)
testSerializer("<bean>\n <option name=\"v\">\n <array />\n </option>\n</bean>", bean)
bean.v = arrayOf(BeanWithPublicFields(), BeanWithPublicFieldsDescendant(), BeanWithPublicFields())
doSerializerTest("""<bean>
testSerializer("""<bean>
<option name="v">
<array>
<BeanWithPublicFields>
@@ -85,7 +85,7 @@ internal class XmlSerializerCollectionTest {
@Test fun xCollection() {
val bean = BeanWithArrayWithoutTagName()
doSerializerTest(
testSerializer(
"""
<BeanWithArrayWithoutTagName>
<option name="foo">
@@ -102,7 +102,7 @@ internal class XmlSerializerCollectionTest {
val bean = Bean()
doSerializerTest("""
testSerializer("""
<bean>
<option name="v">
<array>
@@ -114,7 +114,7 @@ internal class XmlSerializerCollectionTest {
bean.v = arrayOf("1", "2", "3")
doSerializerTest(
testSerializer(
"<bean>\n" + " <option name=\"v\">\n" + " <array>\n" + " <vvalue v=\"1\" />\n" + " <vvalue v=\"2\" />\n" + " <vvalue v=\"3\" />\n" + " </array>\n" + " </option>\n" + "</bean>",
bean)
}
@@ -129,7 +129,7 @@ internal class XmlSerializerCollectionTest {
val bean = Bean()
doSerializerTest("""
testSerializer("""
<bean>
<option name="INT_V" value="1" />
<option name="v">
@@ -140,7 +140,7 @@ internal class XmlSerializerCollectionTest {
bean.v = arrayOf("1", "2", "3")
doSerializerTest("""
testSerializer("""
<bean>
<option name="INT_V" value="1" />
<option name="v">
@@ -155,12 +155,12 @@ internal class XmlSerializerCollectionTest {
@Test fun array() {
val bean = BeanWithArray()
doSerializerTest(
testSerializer(
"<BeanWithArray>\n <option name=\"ARRAY_V\">\n <array>\n <option value=\"a\" />\n <option value=\"b\" />\n </array>\n </option>\n</BeanWithArray>",
bean)
bean.ARRAY_V = arrayOf("1", "2", "3", "")
doSerializerTest(
testSerializer(
"<BeanWithArray>\n <option name=\"ARRAY_V\">\n <array>\n <option value=\"1\" />\n <option value=\"2\" />\n <option value=\"3\" />\n <option value=\"\" />\n </array>\n </option>\n</BeanWithArray>",
bean)
}
@@ -172,13 +172,13 @@ internal class XmlSerializerCollectionTest {
@Test fun ListSerialization() {
val bean = BeanWithList()
doSerializerTest(
testSerializer(
"<BeanWithList>\n <option name=\"VALUES\">\n <list>\n <option value=\"a\" />\n <option value=\"b\" />\n <option value=\"c\" />\n </list>\n </option>\n</BeanWithList>",
bean)
bean.VALUES = ArrayList(Arrays.asList("1", "2", "3"))
doSerializerTest(
testSerializer(
"<BeanWithList>\n <option name=\"VALUES\">\n <list>\n <option value=\"1\" />\n <option value=\"2\" />\n <option value=\"3\" />\n </list>\n </option>\n</BeanWithList>",
bean)
}
@@ -189,12 +189,12 @@ internal class XmlSerializerCollectionTest {
@Test fun SetSerialization() {
val bean = BeanWithSet()
doSerializerTest(
testSerializer(
"<BeanWithSet>\n <option name=\"VALUES\">\n <set>\n <option value=\"a\" />\n <option value=\"b\" />\n <option value=\"w\" />\n </set>\n </option>\n</BeanWithSet>",
bean)
bean.VALUES = LinkedHashSet(Arrays.asList("1", "2", "3"))
doSerializerTest(
testSerializer(
"<BeanWithSet>\n <option name=\"VALUES\">\n <set>\n <option value=\"1\" />\n <option value=\"2\" />\n <option value=\"3\" />\n </set>\n </option>\n</BeanWithSet>",
bean)
}
@@ -0,0 +1,108 @@
package com.intellij.configurationStore.xml
import com.intellij.openapi.util.Ref
import com.intellij.openapi.util.text.StringUtil
import com.intellij.util.xmlb.Converter
import com.intellij.util.xmlb.SkipDefaultsSerializationFilter
import com.intellij.util.xmlb.annotations.Attribute
import com.intellij.util.xmlb.annotations.OptionTag
import com.intellij.util.xmlb.annotations.Tag
import org.junit.Test
internal class XmlSerializerConversionTest {
@Test
fun converter() {
val bean = BeanWithConverter()
testSerializer("""
<bean>
<option name="bar" />
</bean>""", bean)
bean.foo = Ref.create("testValue")
testSerializer("""
<bean foo="testValue">
<option name="bar" />
</bean>""", bean)
bean.foo = Ref.create<String>()
bean.bar = Ref.create("testValue2")
testSerializer("""
<bean foo="">
<option name="bar" value="testValue2" />
</bean>""", bean)
}
@Test
fun converterUsingSkipDefaultsFilter() {
val bean = BeanWithConverter()
testSerializer("<bean />", bean, SkipDefaultsSerializationFilter())
bean.foo = Ref.create("testValue")
testSerializer("""<bean foo="testValue" />""", bean, SkipDefaultsSerializationFilter())
bean.foo = Ref.create<String>()
bean.bar = Ref.create("testValue2")
testSerializer("""
<bean foo="">
<option name="bar" value="testValue2" />
</bean>""", bean)
}
// @Test
// fun `bean fromString`() {
// val bean = BeanWithConverter2()
// testSerializer("""
// <bean>
// <option name="bar" />
// </bean>""", bean)
//
// bean.foo = StringRef("testValue")
// testSerializer("""
// <bean foo="testValue">
// <option name="bar" />
// </bean>""", bean)
//
// bean.foo = StringRef()
// bean.bar = StringRef("testValue2")
// testSerializer("""
// <bean foo="">
// <option name="bar" value="testValue2" />
// </bean>""", bean)
// }
}
@Tag("bean")
private class BeanWithConverter {
private class MyConverter : Converter<Ref<String>>() {
override fun fromString(value: String): Ref<String> = Ref.create(value)
override fun toString(o: Ref<String>) = StringUtil.notNullize(o.get())
}
@Attribute(converter = MyConverter::class)
var foo: Ref<String>? = null
@OptionTag(converter = MyConverter::class)
var bar: Ref<String>? = null
}
//private class StringRef(private var value: String? = null) : Converter<StringRef>() {
// companion object {
// @Suppress("unused")
// @JvmStatic
// fun fromText(value: String): StringRef = StringRef(value)
// }
//
// override fun fromString(value: String) = StringRef(value)
//
// override fun toString(value: StringRef) = value.value
//}
//
//@Tag("bean")
//private class BeanWithConverter2 {
// @Attribute
// var foo: StringRef? = null
//
// @OptionTag
// var bar: StringRef? = null
//}
@@ -32,7 +32,7 @@ internal class XmlSerializerMapTest {
}
val bean = BeanWithMapWithBeanValue2()
doSerializerTest("<bean />", bean, SkipDefaultsSerializationFilter())
testSerializer("<bean />", bean, SkipDefaultsSerializationFilter())
}
@Test fun mapAtTopLevel() {
@@ -48,7 +48,7 @@ internal class XmlSerializerMapTest {
val bean = BeanWithMapAtTopLevel()
bean.map.put("a", "b")
bean.option = "xxx"
doSerializerTest("""
testSerializer("""
<bean>
<option name="option" value="xxx" />
<entry key="a" value="b" />
@@ -69,7 +69,7 @@ internal class XmlSerializerMapTest {
bean.MAP.put(BeanWithPublicFields(3, "c"), BeanWithTextAnnotation(4, "d"))
bean.MAP.put(BeanWithPublicFields(5, "e"), BeanWithTextAnnotation(6, "f"))
doSerializerTest("""
testSerializer("""
<bean>
<map>
<pair>
@@ -118,7 +118,7 @@ internal class XmlSerializerMapTest {
bean.myValues.put("a", LinkedHashSet(Arrays.asList("first1", "second1")))
bean.myValues.put("b", LinkedHashSet(Arrays.asList("first2", "second2")))
doSerializerTest("""
testSerializer("""
<bean>
<option name="myValues">
<entry-tag key-attr="a">
@@ -153,13 +153,13 @@ internal class XmlSerializerMapTest {
@Test fun serialization() {
val bean = BeanWithMap()
doSerializerTest("<BeanWithMap>\n <option name=\"VALUES\">\n <map>\n <entry key=\"a\" value=\"1\" />\n <entry key=\"b\" value=\"2\" />\n <entry key=\"c\" value=\"3\" />\n </map>\n </option>\n</BeanWithMap>", bean)
testSerializer("<BeanWithMap>\n <option name=\"VALUES\">\n <map>\n <entry key=\"a\" value=\"1\" />\n <entry key=\"b\" value=\"2\" />\n <entry key=\"c\" value=\"3\" />\n </map>\n </option>\n</BeanWithMap>", bean)
bean.VALUES.clear()
bean.VALUES.put("1", "a")
bean.VALUES.put("2", "b")
bean.VALUES.put("3", "c")
doSerializerTest("<BeanWithMap>\n <option name=\"VALUES\">\n <map>\n <entry key=\"1\" value=\"a\" />\n <entry key=\"2\" value=\"b\" />\n <entry key=\"3\" value=\"c\" />\n </map>\n </option>\n</BeanWithMap>", bean)
testSerializer("<BeanWithMap>\n <option name=\"VALUES\">\n <map>\n <entry key=\"1\" value=\"a\" />\n <entry key=\"2\" value=\"b\" />\n <entry key=\"3\" value=\"c\" />\n </map>\n </option>\n</BeanWithMap>", bean)
}
private class BeanWithMapWithAnnotations {
@@ -176,13 +176,13 @@ internal class XmlSerializerMapTest {
@Test fun serializationWithAnnotations() {
val bean = BeanWithMapWithAnnotations()
doSerializerTest("<BeanWithMapWithAnnotations>\n <option name=\"a\" value=\"1\" />\n <option name=\"b\" value=\"2\" />\n <option name=\"c\" value=\"3\" />\n</BeanWithMapWithAnnotations>", bean)
testSerializer("<BeanWithMapWithAnnotations>\n <option name=\"a\" value=\"1\" />\n <option name=\"b\" value=\"2\" />\n <option name=\"c\" value=\"3\" />\n</BeanWithMapWithAnnotations>", bean)
bean.VALUES.clear()
bean.VALUES.put("1", "a")
bean.VALUES.put("2", "b")
bean.VALUES.put("3", "c")
doSerializerTest("<BeanWithMapWithAnnotations>\n <option name=\"1\" value=\"a\" />\n <option name=\"2\" value=\"b\" />\n <option name=\"3\" value=\"c\" />\n</BeanWithMapWithAnnotations>", bean)
testSerializer("<BeanWithMapWithAnnotations>\n <option name=\"1\" value=\"a\" />\n <option name=\"2\" value=\"b\" />\n <option name=\"3\" value=\"c\" />\n</BeanWithMapWithAnnotations>", bean)
}
private class BeanWithMapWithBeanValue {
@@ -196,7 +196,7 @@ internal class XmlSerializerMapTest {
bean.VALUES.put("b", BeanWithProperty("Bond"))
bean.VALUES.put("c", BeanWithProperty("Bill"))
doSerializerTest("<BeanWithMapWithBeanValue>\n <option name=\"VALUES\">\n <map>\n <entry key=\"a\">\n <value>\n <BeanWithProperty>\n <option name=\"name\" value=\"James\" />\n </BeanWithProperty>\n </value>\n </entry>\n <entry key=\"b\">\n <value>\n <BeanWithProperty>\n <option name=\"name\" value=\"Bond\" />\n </BeanWithProperty>\n </value>\n </entry>\n <entry key=\"c\">\n <value>\n <BeanWithProperty>\n <option name=\"name\" value=\"Bill\" />\n </BeanWithProperty>\n </value>\n </entry>\n </map>\n </option>\n</BeanWithMapWithBeanValue>", bean)
testSerializer("<BeanWithMapWithBeanValue>\n <option name=\"VALUES\">\n <map>\n <entry key=\"a\">\n <value>\n <BeanWithProperty>\n <option name=\"name\" value=\"James\" />\n </BeanWithProperty>\n </value>\n </entry>\n <entry key=\"b\">\n <value>\n <BeanWithProperty>\n <option name=\"name\" value=\"Bond\" />\n </BeanWithProperty>\n </value>\n </entry>\n <entry key=\"c\">\n <value>\n <BeanWithProperty>\n <option name=\"name\" value=\"Bill\" />\n </BeanWithProperty>\n </value>\n </entry>\n </map>\n </option>\n</BeanWithMapWithBeanValue>", bean)
}
@Test fun setKeysInMap() {
@@ -209,7 +209,7 @@ internal class XmlSerializerMapTest {
bean.myMap.put(LinkedHashSet(Arrays.asList("a", "b", "c")), "letters")
bean.myMap.put(LinkedHashSet(Arrays.asList("1", "2", "3")), "numbers")
val bb = doSerializerTest("<bean>\n <option name=\"myMap\">\n <map>\n <entry value=\"letters\">\n <key>\n <set>\n <option value=\"a\" />\n <option value=\"b\" />\n <option value=\"c\" />\n </set>\n </key>\n </entry>\n <entry value=\"numbers\">\n <key>\n <set>\n <option value=\"1\" />\n <option value=\"2\" />\n <option value=\"3\" />\n </set>\n </key>\n </entry>\n </map>\n </option>\n</bean>", bean)
val bb = testSerializer("<bean>\n <option name=\"myMap\">\n <map>\n <entry value=\"letters\">\n <key>\n <set>\n <option value=\"a\" />\n <option value=\"b\" />\n <option value=\"c\" />\n </set>\n </key>\n </entry>\n <entry value=\"numbers\">\n <key>\n <set>\n <option value=\"1\" />\n <option value=\"2\" />\n <option value=\"3\" />\n </set>\n </key>\n </entry>\n </map>\n </option>\n</bean>", bean)
for (collection in bb.myMap.keys) {
Assertions.assertThat(collection).isInstanceOf(Set::class.java)
@@ -18,15 +18,13 @@ package com.intellij.configurationStore.xml
import com.intellij.configurationStore.StoredPropertyStateTest
import com.intellij.configurationStore.deserialize
import com.intellij.configurationStore.serialize
import com.intellij.openapi.util.JDOMUtil
import com.intellij.openapi.util.Ref
import com.intellij.openapi.util.text.StringUtil
import com.intellij.testFramework.assertConcurrent
import com.intellij.testFramework.assertions.Assertions.assertThat
import com.intellij.util.loadElement
import com.intellij.util.xmlb.*
import com.intellij.util.xmlb.annotations.*
import junit.framework.TestCase
import org.assertj.core.api.Assertions.assertThat
import org.intellij.lang.annotations.Language
import org.jdom.Element
import org.junit.Test
@@ -40,7 +38,8 @@ import java.util.*
XmlSerializerMapTest::class,
XmlSerializerCollectionTest::class,
StoredPropertyStateTest::class,
KotlinXmlSerializerTest::class
KotlinXmlSerializerTest::class,
XmlSerializerConversionTest::class
)
class XmlSerializerTestSuite
@@ -53,7 +52,7 @@ internal class XmlSerializerTest {
val data = Bean()
data.PLACES_MAP.put("foo", "bar")
doSerializerTest("""
testSerializer("""
<Bean>
<option name="PLACES_MAP">
<entry key="foo" value="bar" />
@@ -64,31 +63,31 @@ internal class XmlSerializerTest {
@Test fun emptyBeanSerialization() {
class EmptyBean
doSerializerTest("<EmptyBean />", EmptyBean())
testSerializer("<EmptyBean />", EmptyBean())
}
@Tag("Bean")
private class EmptyBeanWithCustomName
@Test fun emptyBeanSerializationWithCustomName() {
doSerializerTest("<Bean />", EmptyBeanWithCustomName())
testSerializer("<Bean />", EmptyBeanWithCustomName())
}
@Test fun publicFieldSerialization() {
val bean = BeanWithPublicFields()
doSerializerTest("<BeanWithPublicFields>\n <option name=\"INT_V\" value=\"1\" />\n <option name=\"STRING_V\" value=\"hello\" />\n</BeanWithPublicFields>", bean)
testSerializer("<BeanWithPublicFields>\n <option name=\"INT_V\" value=\"1\" />\n <option name=\"STRING_V\" value=\"hello\" />\n</BeanWithPublicFields>", bean)
bean.INT_V = 2
bean.STRING_V = "bye"
doSerializerTest("<BeanWithPublicFields>\n <option name=\"INT_V\" value=\"2\" />\n <option name=\"STRING_V\" value=\"bye\" />\n</BeanWithPublicFields>", bean)
testSerializer("<BeanWithPublicFields>\n <option name=\"INT_V\" value=\"2\" />\n <option name=\"STRING_V\" value=\"bye\" />\n</BeanWithPublicFields>", bean)
}
@Test fun publicFieldSerializationWithInheritance() {
val bean = BeanWithPublicFieldsDescendant()
doSerializerTest("""
testSerializer("""
<BeanWithPublicFieldsDescendant>
<option name="NEW_S" value="foo" />
<option name="INT_V" value="1" />
@@ -99,7 +98,7 @@ internal class XmlSerializerTest {
bean.STRING_V = "bye"
bean.NEW_S = "bar"
doSerializerTest("""<BeanWithPublicFieldsDescendant>
testSerializer("""<BeanWithPublicFieldsDescendant>
<option name="NEW_S" value="bar" />
<option name="INT_V" value="2" />
<option name="STRING_V" value="bye" />
@@ -113,16 +112,16 @@ internal class XmlSerializerTest {
@Test fun subBeanSerialization() {
val bean = BeanWithSubBean()
doSerializerTest("<BeanWithSubBean>\n" + " <option name=\"BEAN1\">\n" + " <Bean />\n" + " </option>\n" + " <option name=\"BEAN2\">\n" + " <BeanWithPublicFields>\n" + " <option name=\"INT_V\" value=\"1\" />\n" + " <option name=\"STRING_V\" value=\"hello\" />\n" + " </BeanWithPublicFields>\n" + " </option>\n" + "</BeanWithSubBean>", bean)
testSerializer("<BeanWithSubBean>\n" + " <option name=\"BEAN1\">\n" + " <Bean />\n" + " </option>\n" + " <option name=\"BEAN2\">\n" + " <BeanWithPublicFields>\n" + " <option name=\"INT_V\" value=\"1\" />\n" + " <option name=\"STRING_V\" value=\"hello\" />\n" + " </BeanWithPublicFields>\n" + " </option>\n" + "</BeanWithSubBean>", bean)
bean.BEAN2!!.INT_V = 2
bean.BEAN2!!.STRING_V = "bye"
doSerializerTest("<BeanWithSubBean>\n" + " <option name=\"BEAN1\">\n" + " <Bean />\n" + " </option>\n" + " <option name=\"BEAN2\">\n" + " <BeanWithPublicFields>\n" + " <option name=\"INT_V\" value=\"2\" />\n" + " <option name=\"STRING_V\" value=\"bye\" />\n" + " </BeanWithPublicFields>\n" + " </option>\n" + "</BeanWithSubBean>", bean)
testSerializer("<BeanWithSubBean>\n" + " <option name=\"BEAN1\">\n" + " <Bean />\n" + " </option>\n" + " <option name=\"BEAN2\">\n" + " <BeanWithPublicFields>\n" + " <option name=\"INT_V\" value=\"2\" />\n" + " <option name=\"STRING_V\" value=\"bye\" />\n" + " </BeanWithPublicFields>\n" + " </option>\n" + "</BeanWithSubBean>", bean)
}
@Test fun SubBeanSerializationAndSkipDefaults() {
val bean = BeanWithSubBean()
doSerializerTest("<BeanWithSubBean />", bean, SkipDefaultsSerializationFilter())
testSerializer("<BeanWithSubBean />", bean, SkipDefaultsSerializationFilter())
}
@Suppress("EqualsOrHashCode")
@@ -143,26 +142,26 @@ internal class XmlSerializerTest {
@Test fun subBeanWithEqualsSerializationAndSkipDefaults() {
val bean = BeanWithSubBeanWithEquals()
val filter = SkipDefaultsSerializationFilter()
doSerializerTest("<BeanWithSubBeanWithEquals />", bean, filter)
testSerializer("<BeanWithSubBeanWithEquals />", bean, filter)
bean.BEAN2.STRING_V = "new"
doSerializerTest("<BeanWithSubBeanWithEquals />", bean, filter)
testSerializer("<BeanWithSubBeanWithEquals />", bean, filter)
}
@Test fun nullFieldValue() {
val bean1 = BeanWithPublicFields()
doSerializerTest("<BeanWithPublicFields>\n" + " <option name=\"INT_V\" value=\"1\" />\n" + " <option name=\"STRING_V\" value=\"hello\" />\n" + "</BeanWithPublicFields>", bean1)
testSerializer("<BeanWithPublicFields>\n" + " <option name=\"INT_V\" value=\"1\" />\n" + " <option name=\"STRING_V\" value=\"hello\" />\n" + "</BeanWithPublicFields>", bean1)
bean1.STRING_V = null
doSerializerTest("<BeanWithPublicFields>\n" + " <option name=\"INT_V\" value=\"1\" />\n" + " <option name=\"STRING_V\" />\n" + "</BeanWithPublicFields>", bean1)
testSerializer("<BeanWithPublicFields>\n" + " <option name=\"INT_V\" value=\"1\" />\n" + " <option name=\"STRING_V\" />\n" + "</BeanWithPublicFields>", bean1)
val bean2 = BeanWithSubBean()
bean2.BEAN1 = null
bean2.BEAN2 = null
doSerializerTest("<BeanWithSubBean>\n" + " <option name=\"BEAN1\" />\n" + " <option name=\"BEAN2\" />\n" + "</BeanWithSubBean>", bean2)
testSerializer("<BeanWithSubBean>\n" + " <option name=\"BEAN1\" />\n" + " <option name=\"BEAN2\" />\n" + "</BeanWithSubBean>", bean2)
}
private data class BeanWithOption(@OptionTag("path") var PATH: String? = null)
@@ -170,7 +169,7 @@ internal class XmlSerializerTest {
@Test fun optionTag() {
val bean = BeanWithOption()
bean.PATH = "123"
doSerializerTest("<BeanWithOption>\n" + " <option name=\"path\" value=\"123\" />\n" + "</BeanWithOption>", bean)
testSerializer("<BeanWithOption>\n" + " <option name=\"path\" value=\"123\" />\n" + "</BeanWithOption>", bean)
}
private data class BeanWithCustomizedOption(@OptionTag(tag = "setting", nameAttribute = "key", valueAttribute = "saved") var PATH: String? = null)
@@ -178,14 +177,14 @@ internal class XmlSerializerTest {
@Test fun customizedOptionTag() {
val bean = BeanWithCustomizedOption()
bean.PATH = "123"
doSerializerTest("<BeanWithCustomizedOption>\n" + " <setting key=\"PATH\" saved=\"123\" />\n" + "</BeanWithCustomizedOption>", bean)
testSerializer("<BeanWithCustomizedOption>\n" + " <setting key=\"PATH\" saved=\"123\" />\n" + "</BeanWithCustomizedOption>", bean)
}
@Test fun propertySerialization() {
val bean = BeanWithProperty()
doSerializerTest("<BeanWithProperty>\n" + " <option name=\"name\" value=\"James\" />\n" + "</BeanWithProperty>", bean)
testSerializer("<BeanWithProperty>\n" + " <option name=\"name\" value=\"James\" />\n" + "</BeanWithProperty>", bean)
bean.name = "Bond"
doSerializerTest("<BeanWithProperty>\n" + " <option name=\"name\" value=\"Bond\" />\n" + "</BeanWithProperty>", bean)
testSerializer("<BeanWithProperty>\n" + " <option name=\"name\" value=\"Bond\" />\n" + "</BeanWithProperty>", bean)
}
private class BeanWithFieldWithTagAnnotation {
@@ -210,7 +209,7 @@ internal class XmlSerializerTest {
}
@Test fun `self class reference deserialization`() {
doSerializerTest("""
testSerializer("""
<Complex>
<option name="foo" />
</Complex>""", Complex())
@@ -218,22 +217,22 @@ internal class XmlSerializerTest {
@Test fun fieldWithTagAnnotation() {
val bean = BeanWithFieldWithTagAnnotation()
doSerializerTest("<BeanWithFieldWithTagAnnotation>\n" + " <name>hello</name>\n" + "</BeanWithFieldWithTagAnnotation>", bean)
testSerializer("<BeanWithFieldWithTagAnnotation>\n" + " <name>hello</name>\n" + "</BeanWithFieldWithTagAnnotation>", bean)
bean.STRING_V = "bye"
doSerializerTest("<BeanWithFieldWithTagAnnotation>\n" + " <name>bye</name>\n" + "</BeanWithFieldWithTagAnnotation>", bean)
testSerializer("<BeanWithFieldWithTagAnnotation>\n" + " <name>bye</name>\n" + "</BeanWithFieldWithTagAnnotation>", bean)
}
@Test fun escapeCharsInTagText() {
val bean = BeanWithFieldWithTagAnnotation()
bean.STRING_V = "a\nb\"<"
doSerializerTest("<BeanWithFieldWithTagAnnotation>\n" + " <name>a\nb&quot;&lt;</name>\n" + "</BeanWithFieldWithTagAnnotation>", bean)
testSerializer("<BeanWithFieldWithTagAnnotation>\n" + " <name>a\nb&quot;&lt;</name>\n" + "</BeanWithFieldWithTagAnnotation>", bean)
}
@Test fun escapeCharsInAttributeValue() {
val bean = BeanWithPropertiesBoundToAttribute()
bean.name = "a\nb\"<"
doSerializerTest("<BeanWithPropertiesBoundToAttribute count=\"3\" name=\"a&#10;b&quot;&lt;\" />", bean)
testSerializer("<BeanWithPropertiesBoundToAttribute count=\"3\" name=\"a&#10;b&quot;&lt;\" />", bean)
}
@Test fun shuffledDeserialize() {
@@ -266,7 +265,7 @@ internal class XmlSerializerTest {
@Transient fun getValue(): String = "foo"
}
doSerializerTest("<Bean />", Bean())
testSerializer("<Bean />", Bean())
}
@Test fun PropertyWithoutTagWithPrimitiveType() {
@@ -277,7 +276,7 @@ internal class XmlSerializerTest {
val bean = BeanWithPropertyWithoutTagOnPrimitiveValue()
try {
doSerializerTest("<BeanWithPropertyWithoutTagOnPrimitiveValue><name>hello</name></BeanWithPropertyWithoutTagOnPrimitiveValue>", bean)
testSerializer("<BeanWithPropertyWithoutTagOnPrimitiveValue><name>hello</name></BeanWithPropertyWithoutTagOnPrimitiveValue>", bean)
}
catch (e: XmlSerializationException) {
return
@@ -296,7 +295,7 @@ internal class XmlSerializerTest {
val bean = BeanWithPropertyWithoutTag()
doSerializerTest("""<bean>
testSerializer("""<bean>
<option name="INT_V" value="1" />
<BeanWithPublicFields>
<option name="INT_V" value="1" />
@@ -307,7 +306,7 @@ internal class XmlSerializerTest {
bean.INT_V = 2
bean.BEAN1.STRING_V = "junk"
doSerializerTest("""<bean>
testSerializer("""<bean>
<option name="INT_V" value="2" />
<BeanWithPublicFields>
<option name="INT_V" value="1" />
@@ -328,7 +327,7 @@ internal class XmlSerializerTest {
@Test fun arrayWithoutAllTags() {
val bean = BeanWithArrayWithoutAllTag()
doSerializerTest("""<bean>
testSerializer("""<bean>
<option name="intV" value="1" />
<vvalue v="a" />
<vvalue v="b" />
@@ -337,7 +336,7 @@ internal class XmlSerializerTest {
bean.intV = 2
bean.v = arrayOf("1", "2", "3")
doSerializerTest("""<bean>
testSerializer("""<bean>
<option name="intV" value="2" />
<vvalue v="1" />
<vvalue v="2" />
@@ -356,7 +355,7 @@ internal class XmlSerializerTest {
val bean = BeanWithArrayWithoutAllTag2()
doSerializerTest("""<bean>
testSerializer("""<bean>
<option name="intV" value="1" />
<vvalue>a</vvalue>
<vvalue>b</vvalue>
@@ -365,7 +364,7 @@ internal class XmlSerializerTest {
bean.intV = 2
bean.v = arrayOf("1", "2", "3")
doSerializerTest("""<bean>
testSerializer("""<bean>
<option name="intV" value="2" />
<vvalue>1</vvalue>
<vvalue>2</vvalue>
@@ -391,12 +390,12 @@ internal class XmlSerializerTest {
@Test fun BeanWithPrimitivePropertyBoundToAttribute() {
val bean = BeanWithPropertiesBoundToAttribute()
doSerializerTest("<BeanWithPropertiesBoundToAttribute count=\"3\" name=\"James\" />", bean)
testSerializer("<BeanWithPropertiesBoundToAttribute count=\"3\" name=\"James\" />", bean)
bean.COUNT = 10
bean.name = "Bond"
doSerializerTest("<BeanWithPropertiesBoundToAttribute count=\"10\" name=\"Bond\" />", bean)
testSerializer("<BeanWithPropertiesBoundToAttribute count=\"10\" name=\"Bond\" />", bean)
}
@@ -413,11 +412,11 @@ internal class XmlSerializerTest {
@Test fun propertyFilter() {
val bean = BeanWithPropertyFilter()
doSerializerTest("<BeanWithPropertyFilter>\n" + " <option name=\"STRING_V\" value=\"hello\" />\n" + "</BeanWithPropertyFilter>", bean)
testSerializer("<BeanWithPropertyFilter>\n" + " <option name=\"STRING_V\" value=\"hello\" />\n" + "</BeanWithPropertyFilter>", bean)
bean.STRING_V = "bye"
doSerializerTest("<BeanWithPropertyFilter>\n" + " <option name=\"STRING_V\" value=\"bye\" />\n" + "</BeanWithPropertyFilter>", bean)
testSerializer("<BeanWithPropertyFilter>\n" + " <option name=\"STRING_V\" value=\"bye\" />\n" + "</BeanWithPropertyFilter>", bean)
bean.STRING_V = "skip"
@@ -466,21 +465,21 @@ internal class XmlSerializerTest {
bean.actions = null
val newText = "<BeanWithJDOMElementArray>\n" + " <option name=\"STRING_V\" value=\"bye\" />\n" + "</BeanWithJDOMElementArray>"
doSerializerTest(newText, bean)
testSerializer(newText, bean)
bean.actions = emptyArray()
doSerializerTest(newText, bean)
testSerializer(newText, bean)
}
@Test fun textAnnotation() {
val bean = BeanWithTextAnnotation()
doSerializerTest("<BeanWithTextAnnotation>\n" + " <option name=\"INT_V\" value=\"1\" />\n" + " hello\n" + "</BeanWithTextAnnotation>", bean)
testSerializer("<BeanWithTextAnnotation>\n" + " <option name=\"INT_V\" value=\"1\" />\n" + " hello\n" + "</BeanWithTextAnnotation>", bean)
bean.INT_V = 2
bean.STRING_V = "bye"
doSerializerTest("<BeanWithTextAnnotation>\n" + " <option name=\"INT_V\" value=\"2\" />\n" + " bye\n" + "</BeanWithTextAnnotation>", bean)
testSerializer("<BeanWithTextAnnotation>\n" + " <option name=\"INT_V\" value=\"2\" />\n" + " bye\n" + "</BeanWithTextAnnotation>", bean)
}
private class BeanWithEnum {
@@ -496,11 +495,11 @@ internal class XmlSerializerTest {
@Test fun enums() {
val bean = BeanWithEnum()
doSerializerTest("<BeanWithEnum>\n" + " <option name=\"FLD\" value=\"VALUE_1\" />\n" + "</BeanWithEnum>", bean)
testSerializer("<BeanWithEnum>\n" + " <option name=\"FLD\" value=\"VALUE_1\" />\n" + "</BeanWithEnum>", bean)
bean.FLD = BeanWithEnum.TestEnum.VALUE_3
doSerializerTest("<BeanWithEnum>\n" + " <option name=\"FLD\" value=\"VALUE_3\" />\n" + "</BeanWithEnum>", bean)
testSerializer("<BeanWithEnum>\n" + " <option name=\"FLD\" value=\"VALUE_3\" />\n" + "</BeanWithEnum>", bean)
}
@Tag("condition")
@@ -520,11 +519,11 @@ internal class XmlSerializerTest {
var bean = Bean()
bean.conditionBean.oldCondition = "2+2"
doSerializerTest("<bean>\n <condition>2+2</condition>\n</bean>", bean)
testSerializer("<bean>\n <condition>2+2</condition>\n</bean>", bean)
bean = Bean()
bean.conditionBean.newCondition = "2+2"
doSerializerTest("<bean>\n <condition expression=\"2+2\" />\n" + "</bean>", bean)
testSerializer("<bean>\n <condition expression=\"2+2\" />\n" + "</bean>", bean)
}
@Test fun `no_wrap`() {
@@ -536,11 +535,11 @@ internal class XmlSerializerTest {
var bean = Bean()
bean.conditionBean.oldCondition = "2+2"
doSerializerTest("<bean>2+2</bean>", bean)
testSerializer("<bean>2+2</bean>", bean)
bean = Bean()
bean.conditionBean.newCondition = "2+2"
doSerializerTest("<bean expression=\"2+2\" />", bean)
testSerializer("<bean expression=\"2+2\" />", bean)
}
@Test fun deserializeInto() {
@@ -553,48 +552,6 @@ internal class XmlSerializerTest {
assertThat(bean.STRING_V).isEqualTo("zzz")
}
private class BeanWithConverter {
private class MyConverter : Converter<Ref<String>>() {
override fun fromString(value: String): Ref<String>? {
return Ref.create(value)
}
override fun toString(o: Ref<String>): String {
return StringUtil.notNullize(o.get())
}
}
@Attribute(converter = MyConverter::class)
var foo: Ref<String>? = null
@OptionTag(converter = MyConverter::class)
var bar: Ref<String>? = null
}
@Test fun converter() {
val bean = BeanWithConverter()
doSerializerTest("<BeanWithConverter>\n" + " <option name=\"bar\" />\n" + "</BeanWithConverter>", bean)
bean.foo = Ref.create("testValue")
doSerializerTest("<BeanWithConverter foo=\"testValue\">\n" + " <option name=\"bar\" />\n" + "</BeanWithConverter>", bean)
bean.foo = Ref.create<String>()
bean.bar = Ref.create("testValue2")
doSerializerTest("<BeanWithConverter foo=\"\">\n" + " <option name=\"bar\" value=\"testValue2\" />\n" + "</BeanWithConverter>", bean)
}
@Test fun converterUsingSkipDefaultsFilter() {
val bean = BeanWithConverter()
doSerializerTest("<BeanWithConverter />", bean, SkipDefaultsSerializationFilter())
bean.foo = Ref.create("testValue")
doSerializerTest("<BeanWithConverter foo=\"testValue\" />", bean, SkipDefaultsSerializationFilter())
bean.foo = Ref.create<String>()
bean.bar = Ref.create("testValue2")
doSerializerTest("<BeanWithConverter foo=\"\">\n" + " <option name=\"bar\" value=\"testValue2\" />\n" + "</BeanWithConverter>", bean)
}
@Test fun defaultAttributeName() {
class BeanWithDefaultAttributeName {
@Attribute fun getFoo() = "foo"
@@ -603,7 +560,7 @@ internal class XmlSerializerTest {
}
}
doSerializerTest("<BeanWithDefaultAttributeName foo=\"foo\" />", BeanWithDefaultAttributeName())
testSerializer("<BeanWithDefaultAttributeName foo=\"foo\" />", BeanWithDefaultAttributeName())
}
private class Bean2 {
@@ -621,13 +578,7 @@ internal class XmlSerializerTest {
val bean = Bean2()
bean.module = "module"
bean.ab = "ab"
doSerializerTest("<Bean2 ab=\"ab\" module=\"module\" />", bean, SkipDefaultsSerializationFilter())
checkSmartSerialization(Bean2(), "<Bean2 module=\"1\" ab=\"2\" ac=\"32\" />")
checkSmartSerialization(Bean2(), "<Bean2 ab=\"2\" module=\"1\" ac=\"32\" />")
checkSmartSerialization(Bean2(), "<Bean2 ac=\"2\" module=\"1\" ab=\"32\" />")
checkSmartSerialization(Bean2(), "<Bean2 ac=\"2\" ab=\"32\" />")
checkSmartSerialization(Bean2(), "<Bean2 ac=\"2\" ab=\"32\" module=\"\" />")
testSerializer("<Bean2 ab=\"ab\" module=\"module\" />", bean, SkipDefaultsSerializationFilter())
}
@Test fun cdataAfterNewLine() {
@@ -646,30 +597,17 @@ internal class XmlSerializerTest {
bean = loadElement("""<bean><description><![CDATA[<h4>Node.js integration</h4>]]></description></bean>""").deserialize<Bean>()
assertThat(bean.description).isEqualTo("<h4>Node.js integration</h4>")
}
private fun checkSmartSerialization(bean: Bean2, serialized: String) {
val serializer = SmartSerializer()
serializer.readExternal(bean, JDOMUtil.load(serialized))
val serializedState = Element("Bean2")
serializer.writeExternal(bean, serializedState)
assertThat(JDOMUtil.writeElement(serializedState)).isEqualTo(serialized)
}
}
private val XML_PREFIX = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
internal fun assertSerializer(bean: Any, expected: String, filter: SerializationFilter?, description: String = "Serialization failure"): Element {
val element = bean.serialize(filter, createElementIfEmpty = true)!!
var actual = JDOMUtil.writeElement(element).trim()
if (!expected.startsWith(XML_PREFIX) && actual.startsWith(XML_PREFIX)) {
actual = actual.substring(XML_PREFIX.length).trim()
}
assertThat(actual).`as`(description).isEqualTo(expected)
assertThat(element).`as`(description).isEqualTo(expected)
return element
}
internal fun <T: Any> doSerializerTest(@Language("XML") expectedText: String, bean: T, filter: SerializationFilter? = null): T {
// no filter by default
// ? = SerializationFilter { _, _ -> true }
internal fun <T: Any> testSerializer(@Language("XML") expectedText: String, bean: T, filter: SerializationFilter? = SerializationFilter { _, _ -> true }): T {
val expectedTrimmed = expectedText.trimIndent()
val element = assertSerializer(bean, expectedTrimmed, filter)
@@ -1,17 +1,5 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
*/
package com.intellij.openapi.wm.impl;
@@ -26,8 +14,6 @@ import java.util.Arrays;
/**
* @author Vassiliy Kudryashov
*/
public class HiddenSidebarButtonTest extends ToolWindowManagerTestCase {
private static final String LAYOUT = "<layout>" +
"<window_info id=\"TODO\" active=\"false\" anchor=\"bottom\" auto_hide=\"false\" internal_type=\"DOCKED\" type=\"DOCKED\" visible=\"false\" show_stripe_button=\"false\" weight=\"0.42947903\" sideWeight=\"0.4874552\" order=\"6\" side_tool=\"false\" content_ui=\"tabs\" x=\"119\" y=\"106\" width=\"619\" height=\"748\"/>" +
@@ -58,8 +44,8 @@ public class HiddenSidebarButtonTest extends ToolWindowManagerTestCase {
for (int i = 0; i < IDS.length; i++) {
assertTrue(layout.isToolWindowRegistered(IDS[i]));
assertFalse(layout.isToolWindowUnregistered(IDS[i]));
assertTrue(ESTIMATED_TO_SHOW[i] == layout.getInfo(IDS[i], true).isShowStripeButton());
assertTrue(ESTIMATED_VISIBILITY[i] == myManager.getStripeButton(IDS[i]).isVisible());
assertEquals(ESTIMATED_TO_SHOW[i], layout.getInfo(IDS[i], true).isShowStripeButton());
assertEquals(ESTIMATED_VISIBILITY[i], myManager.getStripeButton(IDS[i]).isVisible());
}
}
}
@@ -1,23 +1,12 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
*/
package com.intellij.openapi.wm;
import com.intellij.ide.ui.UISettings;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
@@ -34,7 +23,7 @@ public final class ToolWindowAnchor {
myText = text;
}
public String toString(){
public String toString() {
return myText;
}
@@ -64,7 +53,7 @@ public final class ToolWindowAnchor {
}
@NotNull
public static ToolWindowAnchor fromText(String anchor) {
public static ToolWindowAnchor fromText(@Nullable String anchor) {
for (ToolWindowAnchor a : new ToolWindowAnchor[]{TOP, LEFT, BOTTOM, RIGHT}) {
if (a.myText.equals(anchor)) {
return a;
@@ -1,24 +1,13 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
*/
package com.intellij.openapi.wm;
import com.intellij.openapi.diagnostic.Logger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class ToolWindowContentUiType {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.wm.ToolWindowContentUiType");
public static final ToolWindowContentUiType TABBED = new ToolWindowContentUiType("tabs");
@@ -34,7 +23,8 @@ public class ToolWindowContentUiType {
return myName;
}
public static ToolWindowContentUiType getInstance(String name) {
@NotNull
public static ToolWindowContentUiType getInstance(@Nullable String name) {
if (TABBED.getName().equals(name)) {
return TABBED;
}
@@ -1,25 +1,17 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
*/
package com.intellij.openapi.wm.impl;
import com.intellij.configurationStore.XmlSerializer;
import com.intellij.ide.ui.UISettings;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.JDOMUtil;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.openapi.wm.ToolWindowAnchor;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.HashMap;
import gnu.trove.THashMap;
import org.jdom.Element;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
@@ -27,10 +19,14 @@ import org.jetbrains.annotations.Nullable;
import java.util.*;
import static com.intellij.configurationStore.XmlSerializer.serialize;
/**
* @author Vladimir Kondratyev
*/
public final class DesktopLayout {
private static final Logger LOG = Logger.getInstance(DesktopLayout.class);
@NonNls static final String TAG = "layout";
/**
* Map between {@code id}s and registered {@code WindowInfo}s.
@@ -39,7 +35,7 @@ public final class DesktopLayout {
/**
* Map between {@code id}s and unregistered {@code WindowInfo}s.
*/
private final Map<String, WindowInfoImpl> myUnregisteredId2Info = new HashMap<>();
private final Map<String, WindowInfoImpl> myUnregisteredId2Info = new THashMap<>();
/**
*
*/
@@ -62,8 +58,6 @@ public final class DesktopLayout {
* if the cached data is invalid.
*/
private WindowInfoImpl[] myAllInfos;
@NonNls private static final String ID_ATTR = "id";
/**
* Copies itself from the passed
@@ -78,11 +72,11 @@ public final class DesktopLayout {
continue;
}
info = myUnregisteredId2Info.get(info1.getId());
if (info != null) {
info.copyFrom(info1);
if (info == null) {
myUnregisteredId2Info.put(info1.getId(), info1.copy());
}
else {
myUnregisteredId2Info.put(info1.getId(), info1.copy());
info.copyFrom(info1);
}
}
// invalidate caches
@@ -105,20 +99,22 @@ public final class DesktopLayout {
*/
final WindowInfoImpl register(@NotNull String id, @NotNull ToolWindowAnchor anchor, final boolean splitMode) {
WindowInfoImpl info = myUnregisteredId2Info.get(id);
if (info != null) { // tool window has been already registered some time
myUnregisteredId2Info.remove(id);
}
else { // tool window is being registered first time
info = new WindowInfoImpl(id);
if (info == null) {
// tool window is being registered first time
info = new WindowInfoImpl();
info.setId(id);
info.setAnchor(anchor);
info.setSplit(splitMode);
}
else {
// tool window has been already registered some time
myUnregisteredId2Info.remove(id);
}
myRegisteredId2Info.put(id, info);
// invalidate caches
myRegisteredInfos = null;
myUnregisteredInfos = null;
myAllInfos = null;
//
return info;
}
@@ -136,7 +132,7 @@ public final class DesktopLayout {
* If {@code onlyRegistered} is {@code true} then returns not {@code null}
* value if and only if window with {@code id} is registered one.
*/
final WindowInfoImpl getInfo(String id, final boolean onlyRegistered) {
final WindowInfoImpl getInfo(@NotNull String id, final boolean onlyRegistered) {
final WindowInfoImpl info = myRegisteredId2Info.get(id);
if (onlyRegistered || info != null) {
return info;
@@ -216,11 +212,11 @@ public final class DesktopLayout {
}
}
final boolean isToolWindowRegistered(final String id) {
final boolean isToolWindowRegistered(@NotNull String id) {
return myRegisteredId2Info.containsKey(id);
}
final boolean isToolWindowUnregistered(final String id) {
final boolean isToolWindowUnregistered(@NotNull String id) {
return myUnregisteredId2Info.containsKey(id);
}
@@ -287,17 +283,18 @@ public final class DesktopLayout {
public final void readExternal(@NotNull Element layoutElement) {
myUnregisteredInfos = null;
for (Element e : layoutElement.getChildren()) {
if (WindowInfoImpl.TAG.equals(e.getName())) {
String id = e.getAttributeValue(ID_ATTR);
assert id != null;
final WindowInfoImpl info = new WindowInfoImpl(id);
info.readExternal(e);
if (info.getOrder() == -1) { // if order isn't defined then window's button will be the last one in the stripe
info.setOrder(getMaxOrder(info.getAnchor()) + 1);
}
myUnregisteredId2Info.put(info.getId(), info);
for (Element e : layoutElement.getChildren(WindowInfoImpl.TAG)) {
WindowInfoImpl info = XmlSerializer.deserialize(e, WindowInfoImpl.class);
if (info.getId() == null) {
LOG.warn("Skip invalid window info (no id): " + JDOMUtil.writeElement(e));
continue;
}
if (info.getOrder() == -1) {
// if order isn't defined then window's button will be the last one in the stripe
info.setOrder(getMaxOrder(info.getAnchor()) + 1);
}
myUnregisteredId2Info.put(info.getId(), info);
}
}
@@ -310,9 +307,10 @@ public final class DesktopLayout {
Element state = new Element(tagName);
for (WindowInfoImpl info : infos) {
Element element = new Element(WindowInfoImpl.TAG);
info.writeExternal(element);
state.addContent(element);
Element element = serialize(info);
if (element != null) {
state.addContent(element);
}
}
return state;
}
@@ -1,17 +1,5 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
*/
package com.intellij.openapi.wm.impl;
@@ -1974,7 +1962,6 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements
}
}
/**
* This command creates and shows {@code FloatingDecorator}.
*/
@@ -1993,9 +1980,10 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements
bounds.width > 0 &&
bounds.height > 0 &&
myWindowManager.isInsideScreenBounds(bounds.x, bounds.y, bounds.width)) {
myFloatingDecorator.setBounds(bounds);
myFloatingDecorator.setBounds(new Rectangle(bounds));
}
else { // place new frame at the center of main frame if there are no floating bounds
else {
// place new frame at the center of main frame if there are no floating bounds
Dimension size = decorator.getSize();
if (size.width == 0 || size.height == 0) {
size = decorator.getPreferredSize();
@@ -2065,7 +2053,7 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements
bounds.width > 0 &&
bounds.height > 0 &&
myWindowManager.isInsideScreenBounds(bounds.x, bounds.y, bounds.width)) {
window.setBounds(bounds);
window.setBounds(new Rectangle(bounds));
}
else { // place new frame at the center of main frame if there are no floating bounds
Dimension size = decorator.getSize();
@@ -1,377 +1,29 @@
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
/*
* Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
*/
package com.intellij.openapi.wm.impl
import com.intellij.openapi.util.Comparing
import com.intellij.openapi.util.JDOMExternalizable
import com.intellij.openapi.util.text.StringUtilRt
import com.intellij.openapi.components.BaseState
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.wm.*
import com.intellij.util.xmlb.Converter
import com.intellij.util.xmlb.annotations.Attribute
import com.intellij.util.xmlb.annotations.Property
import com.intellij.util.xmlb.annotations.Tag
import org.jdom.Element
import org.jetbrains.annotations.NonNls
import java.awt.Rectangle
/**
* @author Anton Katilin
* @author Vladimir Kondratyev
*/
class WindowInfoImpl
/**
* Creates `WindowInfo` for tool window with specified `ID`.
*/
internal constructor(id: String) : Cloneable, JDOMExternalizable, WindowInfo {
private var myActive: Boolean = false
private var myAnchor = ToolWindowAnchor.LEFT
private var myAutoHide: Boolean = false
/**
* Bounds of window in "floating" mode. It equals to `null` if
* floating bounds are undefined.
*/
private var myFloatingBounds: Rectangle? = null
/**
* @return `ID` of the tool window.
*/
internal var id: String? = null
private set
/**
* @return type of the tool window in internal (docked or sliding) mode. Actually the tool
* window can be in floating mode, but this method has sense if you want to know what type
* tool window had when it was internal one. The method never returns `null`.
*/
internal var internalType: ToolWindowType? = null
private set
private var myType: ToolWindowType? = null
internal var isVisible: Boolean = false
private var myShowStripeButton = true
private var myWeight = DEFAULT_WEIGHT
private var mySideWeight = DEFAULT_SIDE_WEIGHT
private var mySplitMode: Boolean = false
private var myContentUiType = ToolWindowContentUiType.TABBED
/**
* Defines order of tool window button inside the stripe.
* The default value is `-1`.
*/
var order = -1
private var myWasRead: Boolean = false
/**
* @return internal weight of tool window. "weigth" means how much of internal desktop
* area the tool window is occupied. The weight has sense if the tool window is docked or
* sliding.
*/
/**
* Sets window weight and adjust it to [0..1] range if necessary.
*/
internal var weight: Float
get() = myWeight
set(weight) {
myWeight = Math.max(0f, Math.min(1f, weight))
}
internal var sideWeight: Float
get() = mySideWeight
set(weight) {
mySideWeight = Math.max(0f, Math.min(1f, weight))
}
init {
this.id = id
setType(ToolWindowType.DOCKED)
}
/**
* Creates copy of `WindowInfo` object.
*/
fun copy(): WindowInfoImpl {
try {
val info = clone() as WindowInfoImpl
if (myFloatingBounds != null) {
info.myFloatingBounds = myFloatingBounds!!.clone() as Rectangle
}
return info
}
catch (e: CloneNotSupportedException) {
throw RuntimeException(e)
}
}
/**
* Copies all data from the passed `WindowInfo` into itself.
*/
internal fun copyFrom(info: WindowInfoImpl) {
myActive = info.myActive
myAnchor = info.myAnchor
myAutoHide = info.myAutoHide
myFloatingBounds = if (info.myFloatingBounds == null) null else info.myFloatingBounds!!.clone() as Rectangle
id = info.id
setTypeAndCheck(info.myType!!)
internalType = info.internalType
isVisible = info.isVisible
myWeight = info.myWeight
mySideWeight = info.mySideWeight
order = info.order
mySplitMode = info.mySplitMode
myContentUiType = info.myContentUiType
}
/**
* @return tool window's anchor in internal mode.
*/
override fun getAnchor(): ToolWindowAnchor {
return myAnchor
}
override fun getContentUiType(): ToolWindowContentUiType {
return myContentUiType
}
internal fun setContentUiType(type: ToolWindowContentUiType) {
myContentUiType = type
}
/**
* @return bound of tool window in floating mode.
*/
override fun getFloatingBounds(): Rectangle? {
return if (myFloatingBounds != null) Rectangle(myFloatingBounds!!) else null
}
/**
* @return current type of tool window.
* @see ToolWindowType.DOCKED
*
* @see ToolWindowType.FLOATING
*
* @see ToolWindowType.SLIDING
*/
override fun getType(): ToolWindowType? {
return myType
}
override fun isActive(): Boolean {
return myActive
}
override fun isAutoHide(): Boolean {
return myAutoHide
}
override fun isDocked(): Boolean {
return ToolWindowType.DOCKED == myType
}
override fun isFloating(): Boolean {
return ToolWindowType.FLOATING == myType
}
override fun isWindowed(): Boolean {
return ToolWindowType.WINDOWED == myType
}
override fun isSliding(): Boolean {
return ToolWindowType.SLIDING == myType
}
override fun isShowStripeButton(): Boolean {
return myShowStripeButton
}
internal fun setShowStripeButton(showStripeButton: Boolean) {
myShowStripeButton = showStripeButton
}
override fun isSplit(): Boolean {
return mySplitMode
}
fun setSplit(sideTool: Boolean) {
mySplitMode = sideTool
}
override fun readExternal(element: Element) {
id = element.getAttributeValue(ID_ATTR)
myWasRead = true
myActive = java.lang.Boolean.parseBoolean(element.getAttributeValue(ACTIVE_ATTR)) && canActivateOnStart(id)
try {
myAnchor = ToolWindowAnchor.fromText(element.getAttributeValue(ANCHOR_ATTR))
}
catch (ignored: IllegalArgumentException) {
}
myAutoHide = java.lang.Boolean.parseBoolean(element.getAttributeValue(AUTOHIDE_ATTR))
try {
internalType = ToolWindowType.valueOf(element.getAttributeValue(INTERNAL_TYPE_ATTR))
}
catch (ignored: IllegalArgumentException) {
}
try {
setTypeAndCheck(ToolWindowType.valueOf(element.getAttributeValue(TYPE_ATTR)))
}
catch (ignored: IllegalArgumentException) {
}
isVisible = java.lang.Boolean.parseBoolean(element.getAttributeValue(VISIBLE_ATTR)) && canActivateOnStart(id)
myShowStripeButton = java.lang.Boolean.parseBoolean(element.getAttributeValue(SHOW_STRIPE_BUTTON, "true"))
try {
myWeight = java.lang.Float.parseFloat(element.getAttributeValue(WEIGHT_ATTR)!!)
}
catch (ignored: NumberFormatException) {
}
try {
val value = element.getAttributeValue(SIDE_WEIGHT_ATTR)
if (value != null) {
mySideWeight = java.lang.Float.parseFloat(value)
}
}
catch (ignored: NumberFormatException) {
mySideWeight = DEFAULT_SIDE_WEIGHT
}
order = StringUtilRt.parseInt(element.getAttributeValue(ORDER_ATTR), order)
myFloatingBounds = deserializeBounds(element)
mySplitMode = java.lang.Boolean.parseBoolean(element.getAttributeValue(SIDE_TOOL_ATTR))
myContentUiType = ToolWindowContentUiType.getInstance(element.getAttributeValue(CONTENT_UI_ATTR))
}
/**
* Sets new anchor.
*/
internal fun setAnchor(anchor: ToolWindowAnchor) {
myAnchor = anchor
}
internal fun setActive(active: Boolean) {
myActive = active
}
internal fun setAutoHide(autoHide: Boolean) {
myAutoHide = autoHide
}
internal fun setFloatingBounds(floatingBounds: Rectangle) {
myFloatingBounds = floatingBounds
}
internal fun setType(type: ToolWindowType) {
if (ToolWindowType.DOCKED == type || ToolWindowType.SLIDING == type) {
internalType = type
}
setTypeAndCheck(type)
}
//Hardcoded to avoid single-usage-API
private fun setTypeAndCheck(type: ToolWindowType) {
myType = if (ToolWindowId.PREVIEW === id && type == ToolWindowType.DOCKED) ToolWindowType.SLIDING else type
}
override fun writeExternal(element: Element) {
element.setAttribute(ID_ATTR, id!!)
if (myActive) {
element.setAttribute(ACTIVE_ATTR, java.lang.Boolean.toString(true))
}
element.setAttribute(ANCHOR_ATTR, myAnchor.toString())
if (myAutoHide) {
element.setAttribute(AUTOHIDE_ATTR, java.lang.Boolean.toString(true))
}
element.setAttribute(INTERNAL_TYPE_ATTR, internalType!!.toString())
element.setAttribute(TYPE_ATTR, myType!!.toString())
element.setAttribute(VISIBLE_ATTR, java.lang.Boolean.toString(isVisible))
if (!myShowStripeButton) {
element.setAttribute(SHOW_STRIPE_BUTTON, java.lang.Boolean.toString(false))
}
element.setAttribute(WEIGHT_ATTR, java.lang.Float.toString(myWeight))
if (mySideWeight != DEFAULT_SIDE_WEIGHT) {
element.setAttribute(SIDE_WEIGHT_ATTR, java.lang.Float.toString(mySideWeight))
}
element.setAttribute(ORDER_ATTR, Integer.toString(order))
element.setAttribute(SIDE_TOOL_ATTR, java.lang.Boolean.toString(mySplitMode))
element.setAttribute(CONTENT_UI_ATTR, myContentUiType.name)
if (myFloatingBounds != null) {
serializeBounds(myFloatingBounds!!, element)
}
}
override fun equals(obj: Any?): Boolean {
if (obj !is WindowInfoImpl) {
return false
}
val info = obj as WindowInfoImpl?
return myActive == info!!.myActive &&
myAnchor == info.myAnchor &&
id == info.id &&
myAutoHide == info.myAutoHide &&
Comparing.equal(myFloatingBounds, info.myFloatingBounds) &&
internalType == info.internalType &&
myType == info.myType &&
isVisible == info.isVisible &&
myShowStripeButton == info.myShowStripeButton &&
myWeight == info.myWeight &&
mySideWeight == info.mySideWeight &&
order == info.order &&
mySplitMode == info.mySplitMode &&
myContentUiType === info.myContentUiType
}
override fun hashCode(): Int {
return myAnchor.hashCode() + id!!.hashCode() + myType!!.hashCode() + order
}
override fun toString(): String {
return (javaClass.name + "[myId=" + id
+ "; myVisible=" + isVisible
+ "; myShowStripeButton=" + myShowStripeButton
+ "; myActive=" + myActive
+ "; myAnchor=" + myAnchor
+ "; myOrder=" + order
+ "; myAutoHide=" + myAutoHide
+ "; myWeight=" + myWeight
+ "; mySideWeight=" + mySideWeight
+ "; myType=" + myType
+ "; myInternalType=" + internalType
+ "; myFloatingBounds=" + myFloatingBounds
+ "; mySplitMode=" + mySplitMode
+ "; myContentUiType=" + myContentUiType.name +
']')
}
internal fun wasRead(): Boolean {
return myWasRead
}
private val LOG = logger<WindowInfoImpl>()
@Suppress("EqualsOrHashCode")
@Tag("window_info")
@Property(style = Property.Style.ATTRIBUTE)
class WindowInfoImpl : Cloneable, WindowInfo, BaseState() {
companion object {
/**
* XML tag.
*/
@NonNls internal val TAG = "window_info"
/**
* Default window weight.
*/
internal val DEFAULT_WEIGHT = 0.33f
private val DEFAULT_SIDE_WEIGHT = 0.5f
@NonNls private val ID_ATTR = "id"
@NonNls private val ACTIVE_ATTR = "active"
@NonNls private val ANCHOR_ATTR = "anchor"
@NonNls private val AUTOHIDE_ATTR = "auto_hide"
@NonNls private val INTERNAL_TYPE_ATTR = "internal_type"
@NonNls private val TYPE_ATTR = "type"
@NonNls private val VISIBLE_ATTR = "visible"
@NonNls private val WEIGHT_ATTR = "weight"
@NonNls private val SIDE_WEIGHT_ATTR = "sideWeight"
@NonNls private val ORDER_ATTR = "order"
@NonNls private val SIDE_TOOL_ATTR = "side_tool"
@NonNls private val CONTENT_UI_ATTR = "content_ui"
@NonNls private val SHOW_STRIPE_BUTTON = "show_stripe_button"
@JvmField
internal val TAG = "window_info"
@JvmField
val DEFAULT_WEIGHT = 0.33f
private fun canActivateOnStart(id: String?): Boolean {
for (ep in ToolWindowEP.EP_NAME.extensions) {
@@ -383,4 +35,136 @@ internal constructor(id: String) : Cloneable, JDOMExternalizable, WindowInfo {
return true
}
}
override var isActive by property(false)
@get:Attribute(converter = ToolWindowAnchorConverter::class)
override var anchor: ToolWindowAnchor by property(ToolWindowAnchor.LEFT) { it == ToolWindowAnchor.LEFT }
@get:Attribute("auto_hide")
override var isAutoHide by property(false)
/**
* Bounds of window in "floating" mode. It equals to `null` if
* floating bounds are undefined.
*/
@get:Property(flat = true, style = Property.Style.ATTRIBUTE)
override var floatingBounds by property<Rectangle?>()
/**
* @return `ID` of the tool window
*/
var id by string()
/**
* @return type of the tool window in internal (docked or sliding) mode. Actually the tool
* window can be in floating mode, but this method has sense if you want to know what type
* tool window had when it was internal one.
*/
@get:Attribute("internal_type")
var internalType by property<ToolWindowType?>()
override var type: ToolWindowType by property(ToolWindowType.DOCKED) { it == ToolWindowType.DOCKED }
@get:Attribute("visible")
var isVisible by property(false)
@get:Attribute("show_stripe_button")
override var isShowStripeButton by property(true)
/**
* Internal weight of tool window. "weight" means how much of internal desktop
* area the tool window is occupied. The weight has sense if the tool window is docked or
* sliding.
*/
var weight by property(DEFAULT_WEIGHT) { Math.max(0f, Math.min(1f, it)) }
var sideWeight by property(0.5f) { Math.max(0f, Math.min(1f, it)) }
@get:Attribute("side_tool")
override var isSplit by property(false)
@get:Attribute("content_ui", converter = ContentUiTypeConverter::class)
override var contentUiType: ToolWindowContentUiType by property(ToolWindowContentUiType.TABBED, { it == ToolWindowContentUiType.TABBED })
/**
* Defines order of tool window button inside the stripe.
*/
var order by property(-1)
private var wasRead: Boolean = false
init {
this.id = id
}
fun copy(): WindowInfoImpl {
val info = WindowInfoImpl()
info.copyFrom(this)
return info
}
override val isDocked: Boolean
get() = type == ToolWindowType.DOCKED
override val isFloating: Boolean
get() = type == ToolWindowType.FLOATING
override val isWindowed: Boolean
get() = type == ToolWindowType.WINDOWED
override val isSliding: Boolean
get() = type == ToolWindowType.SLIDING
fun readExternal(element: Element) {
wasRead = true
try {
setTypeAndCheck(ToolWindowType.valueOf(element.getAttributeValue("type")))
}
catch (ignored: IllegalArgumentException) {
}
if (isVisible && !canActivateOnStart(id)) {
isVisible = false
}
}
internal fun setType(type: ToolWindowType) {
if (ToolWindowType.DOCKED == type || ToolWindowType.SLIDING == type) {
internalType = type
}
setTypeAndCheck(type)
}
//Hardcoded to avoid single-usage-API
private fun setTypeAndCheck(value: ToolWindowType) {
type = if (ToolWindowId.PREVIEW === id && value == ToolWindowType.DOCKED) ToolWindowType.SLIDING else value
}
override fun hashCode(): Int {
return anchor.hashCode() + id!!.hashCode() + type.hashCode() + order
}
fun wasRead() = wasRead
}
private class ContentUiTypeConverter : Converter<ToolWindowContentUiType>() {
override fun fromString(value: String): ToolWindowContentUiType = ToolWindowContentUiType.getInstance(value)
override fun toString(value: ToolWindowContentUiType): String = value.name
}
private class ToolWindowAnchorConverter : Converter<ToolWindowAnchor>() {
override fun fromString(value: String): ToolWindowAnchor {
try {
return ToolWindowAnchor.fromText(value)
}
catch (e: IllegalArgumentException) {
LOG.warn(e)
return ToolWindowAnchor.LEFT
}
}
override fun toString(value: ToolWindowAnchor): String = value.toString()
}
@@ -0,0 +1,73 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
*/
package com.intellij.wm
import com.intellij.configurationStore.deserialize
import com.intellij.configurationStore.serialize
import com.intellij.openapi.components.BaseState
import com.intellij.openapi.util.JDOMUtil
import com.intellij.openapi.wm.ToolWindowAnchor
import com.intellij.openapi.wm.impl.WindowInfoImpl
import com.intellij.testFramework.ProjectRule
import com.intellij.testFramework.assertions.Assertions.assertThat
import org.intellij.lang.annotations.Language
import org.jdom.Element
import org.junit.ClassRule
import org.junit.Test
import java.awt.Rectangle
internal class WindowInfoTest {
companion object {
@JvmField
@ClassRule
val projectRule = ProjectRule()
}
@Test
fun test() {
val a = WindowInfoImpl()
a.id = "a"
doSerializerTest("""<window_info id="a" />""", a)
a.isActive = true
doSerializerTest("""<window_info active="true" id="a" />""", a)
}
@Test
fun `anchor, isAutoHide`() {
val a = WindowInfoImpl()
a.id = "a"
a.anchor = ToolWindowAnchor.BOTTOM
a.isAutoHide = false
doSerializerTest("""<window_info anchor="bottom" id="a" />""", a)
a.isAutoHide = true
doSerializerTest("""<window_info anchor="bottom" auto_hide="true" id="a" />""", a)
}
@Test
fun `floatingBounds`() {
val a = WindowInfoImpl()
a.id = "a"
a.floatingBounds = Rectangle(1, 42, 23, 4)
doSerializerTest("""<window_info x="1" y="42" width="23" height="4" id="a" />""", a)
}
}
private fun <T : BaseState> doSerializerTest(@Language("XML") expectedText: String, bean: T): T {
// test deserializer
val expectedTrimmed = expectedText.trimIndent()
val element = assertSerializer(bean, expectedTrimmed)
// test deserializer
val o = (element ?: Element("state")).deserialize(bean.javaClass)
assertSerializer(o, expectedTrimmed, "Deserialization failure")
return o
}
private fun assertSerializer(bean: Any, expected: String, description: String = "Serialization failure"): Element? {
val element = bean.serialize()
assertThat(element?.let { JDOMUtil.writeElement(element).trim() }).`as`(description).isEqualTo(expected)
return element
}
@@ -1,4 +1,6 @@
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
/*
* Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
*/
@file:JvmName("XmlSerializer")
package com.intellij.configurationStore
@@ -31,16 +33,21 @@ private fun getDefaultSerializationFilter(): SkipDefaultsSerializationFilter {
}
@JvmOverloads
fun <T : Any> T.serialize(filter: SerializationFilter? = getDefaultSerializationFilter(), createElementIfEmpty: Boolean = false): Element? {
fun <T : Any> T.serialize(filter: SerializationFilter? = null, createElementIfEmpty: Boolean = false): Element? {
try {
val clazz = javaClass
val binding = serializer.getClassBinding(clazz)
return if (binding is BeanBinding) {
// top level expects not null (null indicates error, empty element will be omitted)
binding.serialize(this, createElementIfEmpty, filter)
val effectiveFilter = when {
filter != null -> filter
this !is BaseState -> getDefaultSerializationFilter()
else -> null /* BaseState implements SerializationFilter filter */
}
binding.serialize(this, createElementIfEmpty, effectiveFilter)
}
else {
binding.serialize(this, null, filter) as Element
binding.serialize(this, null, filter ?: getDefaultSerializationFilter()) as Element
}
}
catch (e: XmlSerializationException) {
@@ -125,8 +125,8 @@ abstract class BaseState : SerializationFilter, ModificationTracker {
return result
}
fun property(defaultValue: Float = 0f): StoredPropertyBase<Float> {
val result = FloatStoredProperty(defaultValue)
fun property(defaultValue: Float = 0f, valueNormalizer: ((value: Float) -> Float)? = null): StoredPropertyBase<Float> {
val result = FloatStoredProperty(defaultValue, valueNormalizer)
properties.add(result)
return result
}
@@ -63,15 +63,16 @@ internal class LongStoredProperty(override val defaultValue: Long) : PrimitiveSt
}
}
internal class FloatStoredProperty(override val defaultValue: Float) : PrimitiveStoredPropertyBase<Float>() {
internal class FloatStoredProperty(override val defaultValue: Float, private val valueNormalizer: ((value: Float) -> Float)?) : PrimitiveStoredPropertyBase<Float>() {
override var value = defaultValue
override operator fun getValue(thisRef: BaseState, property: KProperty<*>) = value
override fun setValue(thisRef: BaseState, property: KProperty<*>, value: Float) {
if (this.value != value) {
override fun setValue(thisRef: BaseState, property: KProperty<*>, @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") rawNewValue: Float) {
val newValue = valueNormalizer?.invoke(value) ?: rawNewValue
if (value != newValue) {
thisRef.ownModificationCount++
this.value = value
value = newValue
}
}
@@ -3,22 +3,30 @@
*/
package com.intellij.util.xmlb;
import com.intellij.openapi.util.text.StringUtilRt;
import com.intellij.util.xmlb.annotations.Property;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.util.List;
class AccessorBindingWrapper extends Binding implements MultiNodeBinding {
protected final Binding myBinding;
private final boolean myFlat;
private final Property.Style beanStyle;
public AccessorBindingWrapper(@NotNull MutableAccessor accessor, @NotNull Binding binding, boolean flat) {
public AccessorBindingWrapper(@NotNull MutableAccessor accessor,
@NotNull Binding binding,
boolean flat,
Property.Style beanStyle) {
super(accessor);
myBinding = binding;
myFlat = flat;
this.beanStyle = beanStyle;
}
public boolean isFlat() {
@@ -33,7 +41,18 @@ class AccessorBindingWrapper extends Binding implements MultiNodeBinding {
throw new XmlSerializationException("Property " + myAccessor + " of object " + o + " (" + o.getClass() + ") must not be null");
}
if (myFlat) {
((BeanBinding)myBinding).serializeInto(value, (Element)context, filter);
Element element = (Element)context;
if (beanStyle == Property.Style.ATTRIBUTE && value instanceof Rectangle) {
Rectangle bounds = (Rectangle)value;
LOG.assertTrue(element != null);
element.setAttribute("x", Integer.toString(bounds.x));
element.setAttribute("y", Integer.toString(bounds.y));
element.setAttribute("width", Integer.toString(bounds.width));
element.setAttribute("height", Integer.toString(bounds.height));
}
else {
((BeanBinding)myBinding).serializeInto(value, element, filter);
}
return null;
}
else {
@@ -53,7 +72,18 @@ class AccessorBindingWrapper extends Binding implements MultiNodeBinding {
((BeanBinding)myBinding).deserializeInto(currentValue, element);
}
else {
Object deserializedValue = myBinding.deserializeUnsafe(currentValue, element);
Object deserializedValue;
if (beanStyle == Property.Style.ATTRIBUTE && myBinding instanceof BeanBinding && ((BeanBinding)myBinding).myBeanClass == Rectangle.class) {
int x = StringUtilRt.parseInt(element.getAttributeValue("x"), 0);
int y = StringUtilRt.parseInt(element.getAttributeValue("y"), 0);
int w = StringUtilRt.parseInt(element.getAttributeValue("width"), 0);
int h = StringUtilRt.parseInt(element.getAttributeValue("height"), 0);
deserializedValue = new Rectangle(x, y, w, h);
}
else {
deserializedValue = myBinding.deserializeUnsafe(currentValue, element);
}
if (currentValue != deserializedValue) {
myAccessor.set(context, deserializedValue);
}
@@ -1,17 +1,5 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
*/
package com.intellij.util.xmlb;
@@ -22,8 +10,8 @@ import org.jetbrains.annotations.Nullable;
class AttributeBinding extends BasePrimitiveBinding {
private final Class<?> valueClass;
public AttributeBinding(@NotNull MutableAccessor accessor, @NotNull Attribute attribute) {
super(accessor, attribute.value(), attribute.converter());
public AttributeBinding(@NotNull MutableAccessor accessor, @Nullable Attribute attribute) {
super(accessor, attribute == null ? null : attribute.value(), attribute == null ? null : attribute.converter());
valueClass = XmlSerializerImpl.typeToClass(accessor.getGenericType());
}
@@ -36,12 +24,16 @@ class AttributeBinding extends BasePrimitiveBinding {
return null;
}
Converter<Object> converter = getConverter();
String stringValue;
if (myConverter == null) {
if (converter == null) {
stringValue = XmlSerializerImpl.convertToString(value);
}
else {
stringValue = myConverter.toString(value);
stringValue = converter.toString(value);
if (stringValue == null) {
return null;
}
}
return new org.jdom.Attribute(myName, stringValue);
}
@@ -1,17 +1,5 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
*/
package com.intellij.util.xmlb;
@@ -65,4 +53,19 @@ abstract class BasePrimitiveBinding extends Binding {
public final Object deserializeUnsafe(Object context, @NotNull Element element) {
return deserialize(context, element);
}
@Nullable
protected final Converter<Object> getConverter() {
//Converter<Object> converter = myConverter;
//if (converter == null && o instanceof Converter) {
// try {
// //noinspection unchecked
// converter = (Converter<Object>)o;
// }
// catch (Exception e) {
// LOG.warn(e);
// }
//}
return myConverter;
}
}
@@ -50,10 +50,12 @@ public class BeanBinding extends NotNullDeserializeBinding {
public synchronized void init(@NotNull Type originalType, @NotNull Serializer serializer) {
assert myBindings == null;
Property classAnnotation = myBeanClass.getAnnotation(Property.class);
List<MutableAccessor> accessors = getAccessors(myBeanClass);
myBindings = new Binding[accessors.size()];
for (int i = 0, size = accessors.size(); i < size; i++) {
Binding binding = createBinding(accessors.get(i), serializer);
Binding binding = createBinding(accessors.get(i), serializer, classAnnotation == null ? Property.Style.OLD : classAnnotation.style());
binding.init(originalType, serializer);
myBindings[i] = binding;
}
@@ -251,6 +253,7 @@ public class BeanBinding extends NotNullDeserializeBinding {
return name;
}
}
String name = aClass.getSimpleName();
if (name.isEmpty()) {
name = aClass.getSuperclass().getSimpleName();
@@ -263,7 +266,8 @@ public class BeanBinding extends NotNullDeserializeBinding {
return name;
}
private static String getTagNameFromAnnotation(Class<?> aClass) {
@Nullable
private static String getTagNameFromAnnotation(@NotNull Class<?> aClass) {
Tag tag = aClass.getAnnotation(Tag.class);
return tag != null && !tag.value().isEmpty() ? tag.value() : null;
}
@@ -278,11 +282,12 @@ public class BeanBinding extends NotNullDeserializeBinding {
accessors = new ArrayList<MutableAccessor>();
Map<String, Couple<Method>> nameToAccessors;
if (aClass != Rectangle.class) { // special case for Rectangle.class to avoid infinite recursion during serialization due to bounds() method
nameToAccessors = collectPropertyAccessors(aClass, accessors);
// special case for Rectangle.class to avoid infinite recursion during serialization due to bounds() method
if (aClass == Rectangle.class) {
nameToAccessors = Collections.emptyMap();
}
else {
nameToAccessors = Collections.emptyMap();
nameToAccessors = collectPropertyAccessors(aClass, accessors);
}
int propertyAccessorCount = accessors.size();
@@ -351,6 +356,7 @@ public class BeanBinding extends NotNullDeserializeBinding {
}
private static boolean hasStoreAnnotations(@NotNull AccessibleObject object) {
//noinspection deprecation
return object.getAnnotation(OptionTag.class) != null ||
object.getAnnotation(Tag.class) != null ||
object.getAnnotation(Attribute.class) != null ||
@@ -414,7 +420,7 @@ public class BeanBinding extends NotNullDeserializeBinding {
}
@NotNull
private static Binding createBinding(@NotNull MutableAccessor accessor, @NotNull Serializer serializer) {
private static Binding createBinding(@NotNull MutableAccessor accessor, @NotNull Serializer serializer, @NotNull Property.Style propertyStyle) {
Binding binding = serializer.getBinding(accessor);
if (binding instanceof JDOMElementBinding) {
return binding;
@@ -436,7 +442,7 @@ public class BeanBinding extends NotNullDeserializeBinding {
}
if (binding instanceof CompactCollectionBinding) {
return new AccessorBindingWrapper(accessor, binding, false);
return new AccessorBindingWrapper(accessor, binding, false, Property.Style.OLD);
}
boolean surroundWithTag = true;
@@ -454,7 +460,7 @@ public class BeanBinding extends NotNullDeserializeBinding {
if (binding == null || binding instanceof TextBinding) {
throw new XmlSerializationException("Text-serializable properties can't be serialized without surrounding tags: " + accessor);
}
return new AccessorBindingWrapper(accessor, binding, inline);
return new AccessorBindingWrapper(accessor, binding, inline, property.style());
}
XCollection xCollection = accessor.getAnnotation(XCollection.class);
@@ -462,6 +468,9 @@ public class BeanBinding extends NotNullDeserializeBinding {
return new TagBinding(accessor, xCollection.propertyElementName());
}
if (propertyStyle == Property.Style.ATTRIBUTE) {
return new AttributeBinding(accessor, null);
}
return new OptionTagBinding(accessor, accessor.getAnnotation(OptionTag.class));
}
}
@@ -10,6 +10,6 @@ public abstract class Converter<T> {
@Nullable
public abstract T fromString(@NotNull String value);
@NotNull
@Nullable
public abstract String toString(@NotNull T value);
}
@@ -1,16 +1,6 @@
// Copyright 2000-2017 JetBrains s.r.o.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
* Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
*/
package com.intellij.util.xmlb;
import com.intellij.openapi.util.text.StringUtil;
@@ -62,7 +52,8 @@ class OptionTagBinding extends BasePrimitiveBinding {
return targetElement;
}
if (myConverter == null) {
Converter<Object> converter = getConverter();
if (converter == null) {
if (myBinding == null) {
targetElement.setAttribute(myValueAttribute, XmlSerializerImpl.removeControlChars(XmlSerializerImpl.convertToString(value)));
}
@@ -77,7 +68,10 @@ class OptionTagBinding extends BasePrimitiveBinding {
}
}
else {
targetElement.setAttribute(myValueAttribute, XmlSerializerImpl.removeControlChars(myConverter.toString(value)));
String text = converter.toString(value);
if (text != null) {
targetElement.setAttribute(myValueAttribute, XmlSerializerImpl.removeControlChars(text));
}
}
return targetElement;
}
@@ -102,16 +96,20 @@ class OptionTagBinding extends BasePrimitiveBinding {
}
}
}
else if (myConverter == null) {
try {
XmlSerializerImpl.doSet(context, valueAttribute.getValue(), myAccessor, XmlSerializerImpl.typeToClass(myAccessor.getGenericType()));
}
catch (Exception e) {
throw new RuntimeException("Cannot set value for field " + myName, e);
}
}
else {
myAccessor.set(context, myConverter.fromString(valueAttribute.getValue()));
String value = valueAttribute.getValue();
if (myConverter == null) {
try {
XmlSerializerImpl
.doSet(context, value, myAccessor, XmlSerializerImpl.typeToClass(myAccessor.getGenericType()));
}
catch (Exception e) {
throw new RuntimeException("Cannot set value for field " + myName, e);
}
}
else {
myAccessor.set(context, myConverter.fromString(value));
}
}
return context;
}
@@ -16,10 +16,7 @@ import org.jetbrains.annotations.Nullable;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.lang.reflect.*;
import java.util.Collection;
import java.util.Date;
import java.util.List;
@@ -293,10 +290,43 @@ public final class XmlSerializerImpl {
else if (valueClass == Float.class) {
deserializedValue = Float.parseFloat(value);
}
else if (callFromStringIfDefined(host, value, accessor, valueClass)) {
return;
}
accessor.set(host, deserializedValue);
}
}
private static boolean callFromStringIfDefined(@NotNull Object host,
@NotNull String value,
@NotNull MutableAccessor accessor,
@NotNull Class<?> valueClass) {
Method fromText;
try {
fromText = valueClass.getMethod("fromText", String.class);
}
catch (NoSuchMethodException ignored) {
return false;
}
try {
fromText.setAccessible(true);
}
catch (SecurityException ignored) {
}
try {
accessor.set(host, fromText.invoke(null, value));
return true;
}
catch (IllegalAccessException ignored) {
}
catch (InvocationTargetException ignored) {
}
return false;
}
@NotNull
static String convertToString(@NotNull Object value) {
if (value instanceof Date) {
@@ -1,17 +1,5 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
*/
package com.intellij.util.xmlb.annotations;
@@ -36,6 +24,12 @@ public @interface Property {
boolean flat() default false;
Class<? extends SerializationFilter> filter() default SerializationFilter.class;
boolean alwaysWrite() default false;
enum Style {
OLD, ATTRIBUTE
}
Style style() default Style.OLD;
}