1 /*
<lambda>null2  * Copyright (C) 2023 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 com.android.systemui.util
18 
19 import com.android.app.tracing.traceSection
20 import java.util.concurrent.CopyOnWriteArrayList
21 import java.util.function.Consumer
22 
23 /**
24  * A collection of listeners, observers, callbacks, etc.
25  *
26  * This container is optimized for infrequent mutation and frequent iteration, with thread safety
27  * and reentrant-safety guarantees as well. Specifically, to ensure that
28  * [ConcurrentModificationException] is never thrown, this iterator will not reflect changes made to
29  * the set after the iterator is constructed.
30  *
31  * This class provides all the abilities of [ListenerSet], except that each listener has a name
32  * calculated at runtime which can be used for time-efficient tracing of listener invocations.
33  */
34 class NamedListenerSet<E : Any>(
35     private val getName: (E) -> String = { it.javaClass.name },
36 ) : IListenerSet<E> {
37     private val listeners = CopyOnWriteArrayList<NamedListener>()
38 
39     override val size: Int
40         get() = listeners.size
41 
isEmptynull42     override fun isEmpty() = listeners.isEmpty()
43 
44     override fun iterator(): Iterator<E> = iterator {
45         listeners.iterator().forEach { yield(it.listener) }
46     }
47 
containsAllnull48     override fun containsAll(elements: Collection<E>) =
49         listeners.count { it.listener in elements } == elements.size
50 
<lambda>null51     override fun contains(element: E) = listeners.firstOrNull { it.listener == element } != null
52 
addIfAbsentnull53     override fun addIfAbsent(element: E): Boolean = listeners.addIfAbsent(NamedListener(element))
54 
55     override fun remove(element: E): Boolean = listeners.removeIf { it.listener == element }
56 
57     /** A wrapper for the listener with an associated name. */
58     inner class NamedListener(val listener: E) {
59         val name: String = getName(listener)
60 
hashCodenull61         override fun hashCode(): Int {
62             return listener.hashCode()
63         }
64 
equalsnull65         override fun equals(other: Any?): Boolean =
66             when {
67                 other === null -> false
68                 other === this -> true
69                 other !is NamedListenerSet<*>.NamedListener -> false
70                 listener == other.listener -> true
71                 else -> false
72             }
73     }
74 
75     /** Iterate the listeners in the set, providing the name for each one as well. */
forEachNamednull76     inline fun forEachNamed(block: (String, E) -> Unit) =
77         namedIterator().forEach { element -> block(element.name, element.listener) }
78 
79     /**
80      * Iterate the listeners in the set, wrapping each call to the block with [traceSection] using
81      * the listener name.
82      */
namenull83     inline fun forEachTraced(block: (E) -> Unit) = forEachNamed { name, listener ->
84         traceSection(name) { block(listener) }
85     }
86 
87     /**
88      * Iterate the listeners in the set, wrapping each call to the block with [traceSection] using
89      * the listener name.
90      */
namenull91     fun forEachTraced(consumer: Consumer<E>) = forEachNamed { name, listener ->
92         traceSection(name) { consumer.accept(listener) }
93     }
94 
95     /** Iterate over the [NamedListener]s currently in the set. */
namedIteratornull96     fun namedIterator(): Iterator<NamedListener> = listeners.iterator()
97 }
98