Skip to content

Commit 798f60a

Browse files
authored
Merge pull request #454 from efenderbosch/kotlin-feature
Add KotlinFeature enum and refactor Builder
2 parents 0469040 + bd17c79 commit 798f60a

File tree

13 files changed

+221
-94
lines changed

13 files changed

+221
-94
lines changed

src/main/kotlin/com/fasterxml/jackson/module/kotlin/Extensions.kt

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import java.io.File
1515
import java.io.InputStream
1616
import java.io.Reader
1717
import java.net.URL
18+
import java.util.*
1819
import kotlin.reflect.KClass
1920

2021
fun kotlinModule(initializer: KotlinModule.Builder.() -> Unit = {}): KotlinModule {
@@ -64,4 +65,18 @@ inline fun <reified T : Any> SimpleModule.addSerializer(kClass: KClass<T>, seria
6465
inline fun <reified T : Any> SimpleModule.addDeserializer(kClass: KClass<T>, deserializer: JsonDeserializer<T>) = this.apply {
6566
addDeserializer(kClass.java, deserializer)
6667
addDeserializer(kClass.javaObjectType, deserializer)
67-
}
68+
}
69+
70+
internal fun Int.toBitSet(): BitSet {
71+
var i = this
72+
var index = 0
73+
val bits = BitSet(32)
74+
while (i != 0) {
75+
if (i % 2 != 0) {
76+
bits.set(index)
77+
}
78+
++index
79+
i = i shr 1
80+
}
81+
return bits
82+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.fasterxml.jackson.module.kotlin
2+
3+
import java.util.BitSet
4+
import kotlin.math.pow
5+
6+
enum class KotlinFeature(val enabledByDefault: Boolean) {
7+
NullToEmptyCollection(enabledByDefault = false),
8+
NullToEmptyMap(enabledByDefault = false),
9+
NullIsSameAsDefault(enabledByDefault = false),
10+
SingletonSupport(enabledByDefault = false),
11+
StrictNullChecks(enabledByDefault = false);
12+
13+
internal val bitSet: BitSet = 2.0.pow(ordinal).toInt().toBitSet()
14+
}

src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinModule.kt

Lines changed: 125 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,16 @@ package com.fasterxml.jackson.module.kotlin
22

33
import com.fasterxml.jackson.databind.MapperFeature
44
import com.fasterxml.jackson.databind.module.SimpleModule
5+
import com.fasterxml.jackson.module.kotlin.KotlinFeature.NullIsSameAsDefault
6+
import com.fasterxml.jackson.module.kotlin.KotlinFeature.NullToEmptyCollection
7+
import com.fasterxml.jackson.module.kotlin.KotlinFeature.NullToEmptyMap
8+
import com.fasterxml.jackson.module.kotlin.KotlinFeature.StrictNullChecks
59
import com.fasterxml.jackson.module.kotlin.SingletonSupport.CANONICALIZE
610
import com.fasterxml.jackson.module.kotlin.SingletonSupport.DISABLED
11+
import java.util.*
712
import kotlin.reflect.KClass
813

9-
private val metadataFqName = "kotlin.Metadata"
14+
private const val metadataFqName = "kotlin.Metadata"
1015

1116
fun Class<*>.isKotlinClass(): Boolean {
1217
return declaredAnnotations.any { it.annotationClass.java.name == metadataFqName }
@@ -27,7 +32,7 @@ fun Class<*>.isKotlinClass(): Boolean {
2732
* (e.g. List<String>) may contain null values after deserialization. Enabling it
2833
* protects against this but has significant performance impact.
2934
*/
30-
class KotlinModule constructor (
35+
class KotlinModule @Deprecated(level = DeprecationLevel.WARNING, message = "Use KotlinModule.Builder") constructor(
3136
val reflectionCacheSize: Int = 512,
3237
val nullToEmptyCollection: Boolean = false,
3338
val nullToEmptyMap: Boolean = false,
@@ -37,26 +42,42 @@ class KotlinModule constructor (
3742
) : SimpleModule(PackageVersion.VERSION) {
3843
@Deprecated(level = DeprecationLevel.HIDDEN, message = "For ABI compatibility")
3944
constructor(
40-
reflectionCacheSize: Int = 512,
41-
nullToEmptyCollection: Boolean = false,
42-
nullToEmptyMap: Boolean = false
43-
) : this(reflectionCacheSize, nullToEmptyCollection, nullToEmptyMap, false)
45+
reflectionCacheSize: Int,
46+
nullToEmptyCollection: Boolean,
47+
nullToEmptyMap: Boolean
48+
) : this(
49+
Builder()
50+
.withReflectionCacheSize(reflectionCacheSize)
51+
.configure(NullToEmptyCollection, nullToEmptyCollection)
52+
.configure(NullToEmptyMap, nullToEmptyMap)
53+
.disable(NullIsSameAsDefault)
54+
)
4455

4556
@Deprecated(level = DeprecationLevel.HIDDEN, message = "For ABI compatibility")
4657
constructor(
47-
reflectionCacheSize: Int = 512,
48-
nullToEmptyCollection: Boolean = false,
49-
nullToEmptyMap: Boolean = false,
50-
nullIsSameAsDefault: Boolean = false
51-
) : this(reflectionCacheSize, nullToEmptyCollection, nullToEmptyMap, nullIsSameAsDefault)
58+
reflectionCacheSize: Int,
59+
nullToEmptyCollection: Boolean,
60+
nullToEmptyMap: Boolean,
61+
nullIsSameAsDefault: Boolean
62+
) : this(
63+
Builder()
64+
.withReflectionCacheSize(reflectionCacheSize)
65+
.configure(NullToEmptyCollection, nullToEmptyCollection)
66+
.configure(NullToEmptyMap, nullToEmptyMap)
67+
.configure(NullIsSameAsDefault, nullIsSameAsDefault)
68+
)
5269

70+
@Suppress("DEPRECATION")
5371
private constructor(builder: Builder) : this(
5472
builder.reflectionCacheSize,
55-
builder.nullToEmptyCollection,
56-
builder.nullToEmptyMap,
57-
builder.nullIsSameAsDefault,
58-
builder.singletonSupport,
59-
builder.strictNullChecks
73+
builder.isEnabled(NullToEmptyCollection),
74+
builder.isEnabled(NullToEmptyMap),
75+
builder.isEnabled(NullIsSameAsDefault),
76+
when {
77+
builder.isEnabled(KotlinFeature.SingletonSupport) -> CANONICALIZE
78+
else -> DISABLED
79+
},
80+
builder.isEnabled(StrictNullChecks)
6081
)
6182

6283
companion object {
@@ -76,7 +97,7 @@ class KotlinModule constructor (
7697

7798
context.addValueInstantiators(KotlinInstantiators(cache, nullToEmptyCollection, nullToEmptyMap, nullIsSameAsDefault, strictNullChecks))
7899

79-
when(singletonSupport) {
100+
when (singletonSupport) {
80101
DISABLED -> Unit
81102
CANONICALIZE -> {
82103
context.addBeanDeserializerModifier(KotlinBeanDeserializerModifier)
@@ -104,38 +125,104 @@ class KotlinModule constructor (
104125
var reflectionCacheSize: Int = 512
105126
private set
106127

107-
var nullToEmptyCollection: Boolean = false
108-
private set
128+
private val bitSet: BitSet = 0.toBitSet().apply {
129+
KotlinFeature.values().filter { it.enabledByDefault }.forEach { or(it.bitSet) }
130+
}
109131

110-
var nullToEmptyMap: Boolean = false
111-
private set
132+
fun withReflectionCacheSize(reflectionCacheSize: Int) = apply {
133+
this.reflectionCacheSize = reflectionCacheSize
134+
}
112135

113-
var nullIsSameAsDefault: Boolean = false
114-
private set
136+
fun enable(feature: KotlinFeature) = apply {
137+
bitSet.or(feature.bitSet)
138+
}
115139

116-
var singletonSupport = DISABLED
117-
private set
140+
fun disable(feature: KotlinFeature) = apply {
141+
bitSet.andNot(feature.bitSet)
142+
}
118143

119-
var strictNullChecks = false
120-
private set
144+
fun configure(feature: KotlinFeature, enabled: Boolean) = when {
145+
enabled -> enable(feature)
146+
else -> disable(feature)
147+
}
121148

122-
fun reflectionCacheSize(reflectionCacheSize: Int) = apply { this.reflectionCacheSize = reflectionCacheSize }
149+
fun isEnabled(feature: KotlinFeature): Boolean = bitSet.intersects(feature.bitSet)
123150

124-
fun nullToEmptyCollection(nullToEmptyCollection: Boolean) =
125-
apply { this.nullToEmptyCollection = nullToEmptyCollection }
151+
@Deprecated(
152+
message = "Deprecated, use withReflectionCacheSize(reflectionCacheSize) instead.",
153+
replaceWith = ReplaceWith("isEnabled(reflectionCacheSize)")
154+
)
155+
fun reflectionCacheSize(reflectionCacheSize: Int) = apply {
156+
this.reflectionCacheSize = reflectionCacheSize
157+
}
158+
159+
@Deprecated(
160+
message = "Deprecated, use isEnabled(NullToEmptyCollection) instead.",
161+
replaceWith = ReplaceWith("isEnabled(NullToEmptyCollection)")
162+
)
163+
fun getNullToEmptyCollection() = isEnabled(NullToEmptyCollection)
126164

127-
fun nullToEmptyMap(nullToEmptyMap: Boolean) = apply { this.nullToEmptyMap = nullToEmptyMap }
165+
@Deprecated(
166+
message = "Deprecated, use configure(NullToEmptyCollection, enabled) instead.",
167+
replaceWith = ReplaceWith("configure(NullToEmptyCollection, enabled)")
168+
)
169+
fun nullToEmptyCollection(nullToEmptyCollection: Boolean) =
170+
configure(NullToEmptyCollection, nullToEmptyCollection)
171+
172+
@Deprecated(
173+
message = "Deprecated, use isEnabled(NullToEmptyMap) instead.",
174+
replaceWith = ReplaceWith("isEnabled(NullToEmptyMap)")
175+
)
176+
fun getNullToEmptyMap() = isEnabled(NullToEmptyMap)
177+
178+
@Deprecated(
179+
message = "Deprecated, use configure(NullToEmptyMap, enabled) instead.",
180+
replaceWith = ReplaceWith("configure(NullToEmptyMap, enabled)")
181+
)
182+
fun nullToEmptyMap(nullToEmptyMap: Boolean) = configure(NullToEmptyMap, nullToEmptyMap)
183+
184+
@Deprecated(
185+
message = "Deprecated, use isEnabled(NullIsSameAsDefault) instead.",
186+
replaceWith = ReplaceWith("isEnabled(NullIsSameAsDefault)")
187+
)
188+
fun getNullIsSameAsDefault() = isEnabled(NullIsSameAsDefault)
189+
190+
@Deprecated(
191+
message = "Deprecated, use configure(NullIsSameAsDefault, enabled) instead.",
192+
replaceWith = ReplaceWith("configure(NullIsSameAsDefault, enabled)")
193+
)
194+
fun nullIsSameAsDefault(nullIsSameAsDefault: Boolean) = configure(NullIsSameAsDefault, nullIsSameAsDefault)
195+
196+
@Deprecated(
197+
message = "Deprecated, use isEnabled(SingletonSupport) instead.",
198+
replaceWith = ReplaceWith("isEnabled(SingletonSupport)")
199+
)
200+
fun getSingletonSupport() = when {
201+
isEnabled(KotlinFeature.SingletonSupport) -> CANONICALIZE
202+
else -> DISABLED
203+
}
128204

129-
fun nullIsSameAsDefault(nullIsSameAsDefault: Boolean) = apply { this.nullIsSameAsDefault = nullIsSameAsDefault }
205+
@Deprecated(
206+
message = "Deprecated, use configure(SingletonSupport, enabled) instead.",
207+
replaceWith = ReplaceWith("configure(SingletonSupport, enabled)")
208+
)
209+
fun singletonSupport(singletonSupport: SingletonSupport) = when (singletonSupport) {
210+
CANONICALIZE -> enable(KotlinFeature.SingletonSupport)
211+
else -> disable(KotlinFeature.SingletonSupport)
212+
}
130213

131-
fun singletonSupport(singletonSupport: SingletonSupport) =
132-
apply { this.singletonSupport = singletonSupport }
214+
@Deprecated(
215+
message = "Deprecated, use isEnabled(StrictNullChecks) instead.",
216+
replaceWith = ReplaceWith("isEnabled(StrictNullChecks)")
217+
)
218+
fun getStrictNullChecks() = isEnabled(StrictNullChecks)
133219

134-
fun strictNullChecks(strictNullChecks: Boolean) =
135-
apply { this.strictNullChecks = strictNullChecks }
220+
@Deprecated(
221+
message = "Deprecated, use configure(StrictNullChecks, enabled) instead.",
222+
replaceWith = ReplaceWith("configure(StrictNullChecks, enabled)")
223+
)
224+
fun strictNullChecks(strictNullChecks: Boolean) = configure(StrictNullChecks, strictNullChecks)
136225

137226
fun build() = KotlinModule(this)
138227
}
139228
}
140-
141-

src/test/kotlin/com/fasterxml/jackson/module/kotlin/DslTest.kt

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@ package com.fasterxml.jackson.module.kotlin
22

33
import com.fasterxml.jackson.core.json.JsonReadFeature
44
import com.fasterxml.jackson.core.json.JsonWriteFeature
5+
import com.fasterxml.jackson.module.kotlin.KotlinFeature.NullIsSameAsDefault
6+
import com.fasterxml.jackson.module.kotlin.KotlinFeature.NullToEmptyCollection
7+
import com.fasterxml.jackson.module.kotlin.KotlinFeature.NullToEmptyMap
8+
import com.fasterxml.jackson.module.kotlin.KotlinFeature.SingletonSupport
9+
import com.fasterxml.jackson.module.kotlin.KotlinFeature.StrictNullChecks
510
import com.fasterxml.jackson.module.kotlin.SingletonSupport.CANONICALIZE
611
import org.junit.Assert.assertNotNull
712
import org.junit.Test
@@ -26,12 +31,12 @@ class DslTest {
2631
@Test
2732
fun createModuleWithBuilderOptions() {
2833
val module = kotlinModule {
29-
reflectionCacheSize(123)
30-
nullToEmptyCollection(true)
31-
nullToEmptyMap(true)
32-
nullIsSameAsDefault(true)
33-
singletonSupport(CANONICALIZE)
34-
strictNullChecks(true)
34+
withReflectionCacheSize(123)
35+
enable(NullToEmptyCollection)
36+
enable(NullToEmptyMap)
37+
enable(NullIsSameAsDefault)
38+
enable(SingletonSupport)
39+
enable(StrictNullChecks)
3540
}
3641

3742
assertNotNull(module)
@@ -63,7 +68,7 @@ class DslTest {
6368
configure(JsonReadFeature.ALLOW_SINGLE_QUOTES, true)
6469

6570
addModule(kotlinModule {
66-
nullIsSameAsDefault(true)
71+
enable(NullIsSameAsDefault)
6772
})
6873
}
6974

0 commit comments

Comments
 (0)