1 /*
<lambda>null2  * Copyright 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.compose.runtime.mock
18 
19 import androidx.compose.runtime.Stable
20 
21 fun indent(indent: Int, builder: StringBuilder) {
22     repeat(indent) { builder.append(' ') }
23 }
24 
25 @Stable
26 interface Modifier {
27     companion object : Modifier {}
28 }
29 
30 open class View {
31     var name: String = ""
32     val children = mutableListOf<View>()
33     val attributes = mutableMapOf<String, Any>()
<lambda>null34     var onAttach = {}
<lambda>null35     var onDetach = {}
36 
37     // Used to validated insert/remove constraints
38     private var parent: View? = null
39 
rendernull40     private fun render(indent: Int = 0, builder: StringBuilder) {
41         indent(indent, builder)
42         builder.append("<$name$attributesAsString")
43         if (children.size > 0) {
44             builder.appendLine(">")
45             children.forEach { it.render(indent + 2, builder) }
46             indent(indent, builder)
47             builder.appendLine("</$name>")
48         } else {
49             builder.appendLine(" />")
50         }
51     }
52 
addAtnull53     fun addAt(index: Int, view: View) {
54         val parent = view.parent
55         if (parent != null) {
56             error(
57                 "Inserting a view named ${view.name} into a view named $name which already has " +
58                     "a parent named ${parent.name}"
59             )
60         }
61         view.parent = this
62         view.onAttach()
63         children.add(index, view)
64     }
65 
removeAtnull66     fun removeAt(index: Int, count: Int) {
67         if (index < children.count()) {
68             if (count == 1) {
69                 val removedChild = children.removeAt(index)
70                 removedChild.onDetach()
71                 removedChild.parent = null
72             } else {
73                 val removedChildren = children.subList(index, index + count)
74                 removedChildren.forEach { child ->
75                     child.onDetach()
76                     child.parent = null
77                 }
78                 removedChildren.clear()
79             }
80         }
81     }
82 
moveAtnull83     fun moveAt(from: Int, to: Int, count: Int) {
84         if (count == 1) {
85             val insertLocation = if (from > to) to else (to - 1)
86             children.add(insertLocation, children.removeAt(from))
87         } else {
88             val insertLocation = if (from > to) to else (to - count)
89             val itemsToMove = children.subList(from, from + count)
90             val copyOfItems = itemsToMove.map { it }
91             itemsToMove.clear()
92             children.addAll(insertLocation, copyOfItems)
93         }
94     }
95 
removeAllChildrennull96     fun removeAllChildren() {
97         children.forEach { child -> child.parent = null }
98         children.clear()
99     }
100 
attributenull101     fun attribute(name: String, value: Any) {
102         attributes[name] = value
103     }
104 
105     var value: String?
106         get() = attributes["value"] as? String
107         set(value) {
108             if (value != null) {
109                 attributes["value"] = value
110             } else {
111                 attributes.remove("value")
112             }
113         }
114 
115     var text: String?
116         get() = attributes["text"] as? String
117         set(value) {
118             if (value != null) {
119                 attributes["text"] = value
120             } else {
121                 attributes.remove("text")
122             }
123         }
124 
125     private val attributesAsString
126         get() =
127             if (attributes.isEmpty()) ""
<lambda>null128             else attributes.map { " ${it.key}='${it.value}'" }.joinToString()
129 
130     private val childrenAsString: String
<lambda>null131         get() = children.map { it.toString() }.joinToString(" ")
132 
toStringnull133     override fun toString() =
134         if (children.isEmpty()) "<$name$attributesAsString>"
135         else "<$name$attributesAsString>$childrenAsString</$name>"
136 
137     fun toFmtString() =
138         StringBuilder().let {
139             render(0, it)
140             it.toString()
141         }
142 
findFirstOrNullnull143     private fun findFirstOrNull(predicate: (view: View) -> Boolean): View? {
144         if (predicate(this)) return this
145         for (child in children) {
146             child.findFirstOrNull(predicate)?.let {
147                 return it
148             }
149         }
150         return null
151     }
152 
findFirstnull153     fun findFirst(predicate: (view: View) -> Boolean) =
154         findFirstOrNull(predicate) ?: error("View not found")
155 }
156 
157 fun View.flatten(): List<View> = listOf(this) + children.flatMap { it.flatten() }
158