[go: nahoru, domu]

Add `find` to `CompositionData`

Fixes: 239709530
Test: ./gradlew :compose:r:r:tDUT
Relnote: """A `find` method was added to `CompositionData` to
allow developer tools using this API to quickly find a
sub-group of composition using its identity."""

Change-Id: I5794ff3ee6a036d3e425b21d092dba3caaeed4bb
diff --git a/compose/runtime/runtime/api/current.txt b/compose/runtime/runtime/api/current.txt
index a7e4a54..f77f754 100644
--- a/compose/runtime/runtime/api/current.txt
+++ b/compose/runtime/runtime/api/current.txt
@@ -963,6 +963,7 @@
 package androidx.compose.runtime.tooling {
 
   public interface CompositionData {
+    method public default androidx.compose.runtime.tooling.CompositionGroup? find(Object identityToFind);
     method public Iterable<androidx.compose.runtime.tooling.CompositionGroup> getCompositionGroups();
     method public boolean isEmpty();
     property public abstract Iterable<androidx.compose.runtime.tooling.CompositionGroup> compositionGroups;
diff --git a/compose/runtime/runtime/api/public_plus_experimental_current.txt b/compose/runtime/runtime/api/public_plus_experimental_current.txt
index 416bb72..36d633f 100644
--- a/compose/runtime/runtime/api/public_plus_experimental_current.txt
+++ b/compose/runtime/runtime/api/public_plus_experimental_current.txt
@@ -1039,6 +1039,7 @@
 package androidx.compose.runtime.tooling {
 
   public interface CompositionData {
+    method public default androidx.compose.runtime.tooling.CompositionGroup? find(Object identityToFind);
     method public Iterable<androidx.compose.runtime.tooling.CompositionGroup> getCompositionGroups();
     method public boolean isEmpty();
     property public abstract Iterable<androidx.compose.runtime.tooling.CompositionGroup> compositionGroups;
diff --git a/compose/runtime/runtime/api/restricted_current.txt b/compose/runtime/runtime/api/restricted_current.txt
index 67b7b6f..f5864c1 100644
--- a/compose/runtime/runtime/api/restricted_current.txt
+++ b/compose/runtime/runtime/api/restricted_current.txt
@@ -1002,6 +1002,7 @@
 package androidx.compose.runtime.tooling {
 
   public interface CompositionData {
+    method public default androidx.compose.runtime.tooling.CompositionGroup? find(Object identityToFind);
     method public Iterable<androidx.compose.runtime.tooling.CompositionGroup> getCompositionGroups();
     method public boolean isEmpty();
     property public abstract Iterable<androidx.compose.runtime.tooling.CompositionGroup> compositionGroups;
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SlotTable.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SlotTable.kt
index b60b182..8764766 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SlotTable.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SlotTable.kt
@@ -596,6 +596,9 @@
 
     override fun iterator(): Iterator<CompositionGroup> =
         GroupIterator(this, 0, groupsSize)
+
+    override fun find(identityToFind: Any): CompositionGroup? =
+         SlotTableGroup(this, 0).find(identityToFind)
 }
 
 /**
@@ -2963,6 +2966,64 @@
         if (index > parentAnchorPivot) index else size + index - parentAnchorPivot
 }
 
+private class SlotTableGroup(
+    val table: SlotTable,
+    val group: Int,
+    val version: Int = table.version
+) : CompositionGroup, Iterable<CompositionGroup> {
+    override val isEmpty: Boolean get() = table.groups.groupSize(group) == 0
+
+    override val key: Any
+        get() = if (table.groups.hasObjectKey(group))
+            table.slots[table.groups.objectKeyIndex(group)]!!
+        else table.groups.key(group)
+
+    override val sourceInfo: String?
+        get() = if (table.groups.hasAux(group))
+            table.slots[table.groups.auxIndex(group)] as? String
+        else null
+
+    override val node: Any?
+        get() = if (table.groups.isNode(group))
+            table.slots[table.groups.nodeIndex(group)] else
+            null
+
+    override val data: Iterable<Any?> get() = DataIterator(table, group)
+
+    override val identity: Any
+        get() {
+            validateRead()
+            return table.read { it.anchor(group) }
+        }
+
+    override val compositionGroups: Iterable<CompositionGroup> get() = this
+
+    override fun iterator(): Iterator<CompositionGroup> {
+        validateRead()
+        return GroupIterator(
+            table,
+            group + 1,
+            group + table.groups.groupSize(group)
+        )
+    }
+
+    private fun validateRead() {
+        if (table.version != version) {
+            throw ConcurrentModificationException()
+        }
+    }
+
+    override fun find(identityToFind: Any): CompositionGroup? =
+        (identityToFind as? Anchor)?.let { anchor ->
+            if (table.ownsAnchor(anchor)) {
+                val anchorGroup = table.anchorIndex(anchor)
+                if (anchorGroup >= group && (anchorGroup - group < table.groups.groupSize(group)))
+                    SlotTableGroup(table, anchorGroup, version)
+                else null
+            } else null
+        }
+}
+
 private class GroupIterator(
     val table: SlotTable,
     start: Int,
@@ -2982,43 +3043,7 @@
         val group = index
 
         index += table.groups.groupSize(group)
-        return object : CompositionGroup, Iterable<CompositionGroup> {
-            override val isEmpty: Boolean get() = table.groups.groupSize(group) == 0
-
-            override val key: Any
-                get() = if (table.groups.hasObjectKey(group))
-                    table.slots[table.groups.objectKeyIndex(group)]!!
-                else table.groups.key(group)
-
-            override val sourceInfo: String?
-                get() = if (table.groups.hasAux(group))
-                    table.slots[table.groups.auxIndex(group)] as? String
-                else null
-
-            override val node: Any?
-                get() = if (table.groups.isNode(group))
-                    table.slots[table.groups.nodeIndex(group)] else
-                    null
-
-            override val data: Iterable<Any?> get() = DataIterator(table, group)
-
-            override val identity: Any
-                get() {
-                    validateRead()
-                    return table.read { it.anchor(group) }
-                }
-
-            override val compositionGroups: Iterable<CompositionGroup> get() = this
-
-            override fun iterator(): Iterator<CompositionGroup> {
-                validateRead()
-                return GroupIterator(
-                    table,
-                    group + 1,
-                    group + table.groups.groupSize(group)
-                )
-            }
-        }
+        return SlotTableGroup(table, group, version)
     }
 
     private fun validateRead() {
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/tooling/CompositionData.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/tooling/CompositionData.kt
index 856363c..16e9076 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/tooling/CompositionData.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/tooling/CompositionData.kt
@@ -39,6 +39,14 @@
      * doesn't contain any child groups.
      */
     val isEmpty: Boolean
+
+    /**
+     * Find a sub-group by identity. Returns `null` if the group is not found or the implementation
+     * of this interface does not support finding groups by their identity. In other words, a
+     * `null` result from this method should not be interpreted as the identity is not a group in
+     * the composition data.
+     */
+    fun find(identityToFind: Any): CompositionGroup? = null
 }
 
 /**
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/tooling/CompositionDataTests.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/tooling/CompositionDataTests.kt
index c68333f..bd2a660 100644
--- a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/tooling/CompositionDataTests.kt
+++ b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/tooling/CompositionDataTests.kt
@@ -232,4 +232,22 @@
             }
         }
     }
+
+    @Test
+    fun canFindAGroupInCompositionData() {
+        val slots = SlotTable().also {
+            it.write { writer ->
+                writer.insert {
+                    writer.group(0) {
+                        repeat(10) { index ->
+                            writer.group(100 + index) { }
+                        }
+                    }
+                }
+            }
+        }
+
+        val identity = slots.compositionGroups.first().compositionGroups.drop(5).first().identity
+        assertEquals(identity, slots.find(identity!!)?.identity)
+    }
 }
\ No newline at end of file