mirror of
https://github.com/protocolbuffers/protobuf
synced 2026-08-26 02:23:14 -04:00
- Updates DslList and ExtensionList to take a lazy supplier lambda () -> List<E>, retaining secondary constructors for binary backward compatibility with existing compiled proto code.
PiperOrigin-RevId: 966853174
This commit is contained in:
parent
6e4f4ca54a
commit
37f78578e7
4 changed files with 188 additions and 5 deletions
|
|
@ -30,6 +30,10 @@
|
|||
|
||||
package com.google.protobuf.kotlin
|
||||
|
||||
private val UNINITIALIZED_SUPPLIER: () -> Nothing = {
|
||||
throw IllegalStateException("Should not be called")
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple wrapper around a [List] with an extra generic parameter that can be used to disambiguate
|
||||
* extension methods.
|
||||
|
|
@ -41,7 +45,42 @@ package com.google.protobuf.kotlin
|
|||
@Suppress("unused") // the unused type parameter
|
||||
class DslList<E, P : DslProxy>
|
||||
@OnlyForUseByGeneratedProtoCode
|
||||
constructor(private val delegate: List<E>) : List<E> by delegate {
|
||||
constructor(private val delegateSupplier: () -> List<E>) : List<E> {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@OnlyForUseByGeneratedProtoCode
|
||||
constructor(delegate: List<E>) : this(UNINITIALIZED_SUPPLIER as () -> List<E>) {
|
||||
memoizedDelegate = delegate
|
||||
}
|
||||
|
||||
private var memoizedDelegate: List<E>? = null
|
||||
|
||||
private val delegate: List<E>
|
||||
get() {
|
||||
var result = memoizedDelegate
|
||||
if (result == null) {
|
||||
result = delegateSupplier()
|
||||
memoizedDelegate = result
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
override val size: Int
|
||||
get() = delegate.size
|
||||
|
||||
override fun isEmpty(): Boolean = delegate.isEmpty()
|
||||
|
||||
override fun contains(element: E): Boolean = delegate.contains(element)
|
||||
|
||||
override fun containsAll(elements: Collection<E>): Boolean = delegate.containsAll(elements)
|
||||
|
||||
override fun get(index: Int): E = delegate[index]
|
||||
|
||||
override fun indexOf(element: E): Int = delegate.indexOf(element)
|
||||
|
||||
override fun lastIndexOf(element: E): Int = delegate.lastIndexOf(element)
|
||||
|
||||
override fun subList(fromIndex: Int, toIndex: Int): List<E> = delegate.subList(fromIndex, toIndex)
|
||||
|
||||
override fun iterator(): Iterator<E> = UnmodifiableIterator(delegate.iterator())
|
||||
|
||||
override fun listIterator(): ListIterator<E> = UnmodifiableListIterator(delegate.listIterator())
|
||||
|
|
|
|||
|
|
@ -33,6 +33,10 @@ package com.google.protobuf.kotlin
|
|||
import com.google.protobuf.ExtensionLite
|
||||
import com.google.protobuf.MessageLite
|
||||
|
||||
private val UNINITIALIZED_SUPPLIER: () -> Nothing = {
|
||||
throw IllegalStateException("Should not be called")
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation for ExtensionList and ExtensionListLite. Like [DslList], represents an
|
||||
* unmodifiable view of a repeated proto field -- in this case, an extension field -- but supports
|
||||
|
|
@ -40,8 +44,48 @@ import com.google.protobuf.MessageLite
|
|||
*/
|
||||
class ExtensionList<E, M : MessageLite>
|
||||
@OnlyForUseByGeneratedProtoCode
|
||||
constructor(val extension: ExtensionLite<M, List<E>>, private val delegate: List<E>) :
|
||||
List<E> by delegate {
|
||||
constructor(
|
||||
val extension: ExtensionLite<M, List<E>>,
|
||||
private val delegateSupplier: () -> List<E>,
|
||||
) : List<E> {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@OnlyForUseByGeneratedProtoCode
|
||||
constructor(
|
||||
extension: ExtensionLite<M, List<E>>,
|
||||
delegate: List<E>,
|
||||
) : this(extension, UNINITIALIZED_SUPPLIER as () -> List<E>) {
|
||||
memoizedDelegate = delegate
|
||||
}
|
||||
|
||||
private var memoizedDelegate: List<E>? = null
|
||||
|
||||
private val delegate: List<E>
|
||||
get() {
|
||||
var result = memoizedDelegate
|
||||
if (result == null) {
|
||||
result = delegateSupplier()
|
||||
memoizedDelegate = result
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
override val size: Int
|
||||
get() = delegate.size
|
||||
|
||||
override fun isEmpty(): Boolean = delegate.isEmpty()
|
||||
|
||||
override fun contains(element: E): Boolean = delegate.contains(element)
|
||||
|
||||
override fun containsAll(elements: Collection<E>): Boolean = delegate.containsAll(elements)
|
||||
|
||||
override fun get(index: Int): E = delegate[index]
|
||||
|
||||
override fun indexOf(element: E): Int = delegate.indexOf(element)
|
||||
|
||||
override fun lastIndexOf(element: E): Int = delegate.lastIndexOf(element)
|
||||
|
||||
override fun subList(fromIndex: Int, toIndex: Int): List<E> = delegate.subList(fromIndex, toIndex)
|
||||
|
||||
override fun iterator(): Iterator<E> = UnmodifiableIterator(delegate.iterator())
|
||||
|
||||
override fun listIterator(): ListIterator<E> = UnmodifiableListIterator(delegate.listIterator())
|
||||
|
|
|
|||
|
|
@ -85,13 +85,59 @@ class DslListTest {
|
|||
@Test
|
||||
fun equality() {
|
||||
EqualsTester()
|
||||
.addEqualityGroup(DslList<Int, DummyProxy>(listOf(1, 2)), listOf(1, 2))
|
||||
.addEqualityGroup(DslList<Int, DummyProxy>(listOf(2, 2)), listOf(2, 2))
|
||||
.addEqualityGroup(
|
||||
DslList<Int, DummyProxy>(listOf(1, 2)),
|
||||
DslList<Int, DummyProxy> { listOf(1, 2) },
|
||||
listOf(1, 2),
|
||||
)
|
||||
.addEqualityGroup(
|
||||
DslList<Int, DummyProxy>(listOf(2, 2)),
|
||||
DslList<Int, DummyProxy> { listOf(2, 2) },
|
||||
listOf(2, 2),
|
||||
)
|
||||
.addEqualityGroup(
|
||||
DslList<Int, DummyProxy>(emptyList()),
|
||||
DslList<String, DummyProxy>(emptyList()),
|
||||
DslList<Int, DummyProxy> { emptyList() },
|
||||
DslList<String, DummyProxy> { emptyList() },
|
||||
emptyList<Int>(),
|
||||
)
|
||||
.testEquals()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun supplierNotInvokedOnConstruction() {
|
||||
var supplierCalled = false
|
||||
val dslList = DslList<Int, DummyProxy> {
|
||||
supplierCalled = true
|
||||
listOf(1, 2, 3)
|
||||
}
|
||||
assertThat(supplierCalled).isFalse()
|
||||
assertThat(dslList).containsExactly(1, 2, 3).inOrder()
|
||||
assertThat(supplierCalled).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun supplierEvaluatedOnlyOnFirstReadOperation() {
|
||||
var callCount = 0
|
||||
val dslList = DslList<Int, DummyProxy> {
|
||||
callCount++
|
||||
listOf(1, 2, 1)
|
||||
}
|
||||
assertThat(callCount).isEqualTo(0)
|
||||
assertThat(dslList.size).isEqualTo(3)
|
||||
assertThat(callCount).isEqualTo(1)
|
||||
assertThat(dslList[0]).isEqualTo(1)
|
||||
assertThat(callCount).isEqualTo(1)
|
||||
assertThat(dslList.isEmpty()).isFalse()
|
||||
assertThat(dslList.contains(2)).isTrue()
|
||||
assertThat(dslList.containsAll(listOf(1, 2))).isTrue()
|
||||
assertThat(dslList.indexOf(1)).isEqualTo(0)
|
||||
assertThat(dslList.lastIndexOf(1)).isEqualTo(2)
|
||||
assertThat(dslList.subList(0, 2)).containsExactly(1, 2).inOrder()
|
||||
assertThat(dslList.iterator().hasNext()).isTrue()
|
||||
assertThat(dslList.listIterator().hasNext()).isTrue()
|
||||
assertThat(dslList.listIterator(1).next()).isEqualTo(2)
|
||||
assertThat(callCount).isEqualTo(1)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -118,17 +118,71 @@ class ExtensionListTest {
|
|||
EqualsTester()
|
||||
.addEqualityGroup(
|
||||
ExtensionList<Int, ExampleExtensibleMessage>(TestProto.repeatedExtension, listOf(1, 2)),
|
||||
ExtensionList<Int, ExampleExtensibleMessage>(TestProto.repeatedExtension) { listOf(1, 2) },
|
||||
ExtensionList<Int, ExampleExtensibleMessage>(TestProto.differentExtension, listOf(1, 2)),
|
||||
ExtensionList<Int, ExampleExtensibleMessage>(TestProto.differentExtension) { listOf(1, 2) },
|
||||
listOf(1, 2),
|
||||
)
|
||||
.addEqualityGroup(
|
||||
ExtensionList<Int, ExampleExtensibleMessage>(TestProto.repeatedExtension, listOf(2, 2)),
|
||||
ExtensionList<Int, ExampleExtensibleMessage>(TestProto.repeatedExtension) { listOf(2, 2) },
|
||||
listOf(2, 2),
|
||||
)
|
||||
.addEqualityGroup(
|
||||
ExtensionList<Int, ExampleExtensibleMessage>(TestProto.repeatedExtension, emptyList()),
|
||||
ExtensionList<Int, ExampleExtensibleMessage>(TestProto.repeatedExtension) { emptyList() },
|
||||
emptyList<Int>(),
|
||||
)
|
||||
.testEquals()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun supplierNotInvokedOnConstruction() {
|
||||
var supplierCalled = false
|
||||
val extensionList =
|
||||
ExtensionList<Int, ExampleExtensibleMessage>(TestProto.repeatedExtension) {
|
||||
supplierCalled = true
|
||||
listOf(1, 2, 3)
|
||||
}
|
||||
assertThat(supplierCalled).isFalse()
|
||||
assertThat(extensionList).containsExactly(1, 2, 3).inOrder()
|
||||
assertThat(supplierCalled).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun extensionPropertyAccessDoesNotInvokeSupplier() {
|
||||
var supplierCalled = false
|
||||
val extensionList =
|
||||
ExtensionList<Int, ExampleExtensibleMessage>(TestProto.repeatedExtension) {
|
||||
supplierCalled = true
|
||||
listOf(1, 2, 3)
|
||||
}
|
||||
assertThat(extensionList.extension).isEqualTo(TestProto.repeatedExtension)
|
||||
assertThat(supplierCalled).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun supplierEvaluatedOnlyOnFirstReadOperation() {
|
||||
var callCount = 0
|
||||
val extensionList =
|
||||
ExtensionList<Int, ExampleExtensibleMessage>(TestProto.repeatedExtension) {
|
||||
callCount++
|
||||
listOf(1, 2, 1)
|
||||
}
|
||||
assertThat(callCount).isEqualTo(0)
|
||||
assertThat(extensionList.size).isEqualTo(3)
|
||||
assertThat(callCount).isEqualTo(1)
|
||||
assertThat(extensionList[0]).isEqualTo(1)
|
||||
assertThat(callCount).isEqualTo(1)
|
||||
assertThat(extensionList.isEmpty()).isFalse()
|
||||
assertThat(extensionList.contains(2)).isTrue()
|
||||
assertThat(extensionList.containsAll(listOf(1, 2))).isTrue()
|
||||
assertThat(extensionList.indexOf(1)).isEqualTo(0)
|
||||
assertThat(extensionList.lastIndexOf(1)).isEqualTo(2)
|
||||
assertThat(extensionList.subList(0, 2)).containsExactly(1, 2).inOrder()
|
||||
assertThat(extensionList.iterator().hasNext()).isTrue()
|
||||
assertThat(extensionList.listIterator().hasNext()).isTrue()
|
||||
assertThat(extensionList.listIterator(1).next()).isEqualTo(2)
|
||||
assertThat(callCount).isEqualTo(1)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue