xref: /aosp_15_r20/build/soong/scripts/hiddenapi/signature_trie.py (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
1*333d2b36SAndroid Build Coastguard Worker#!/usr/bin/env python
2*333d2b36SAndroid Build Coastguard Worker#
3*333d2b36SAndroid Build Coastguard Worker# Copyright (C) 2022 The Android Open Source Project
4*333d2b36SAndroid Build Coastguard Worker#
5*333d2b36SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
6*333d2b36SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
7*333d2b36SAndroid Build Coastguard Worker# You may obtain a copy of the License at
8*333d2b36SAndroid Build Coastguard Worker#
9*333d2b36SAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
10*333d2b36SAndroid Build Coastguard Worker#
11*333d2b36SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
12*333d2b36SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
13*333d2b36SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*333d2b36SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
15*333d2b36SAndroid Build Coastguard Worker# limitations under the License.
16*333d2b36SAndroid Build Coastguard Worker"""Verify that one set of hidden API flags is a subset of another."""
17*333d2b36SAndroid Build Coastguard Workerimport dataclasses
18*333d2b36SAndroid Build Coastguard Workerimport typing
19*333d2b36SAndroid Build Coastguard Worker
20*333d2b36SAndroid Build Coastguard Workerfrom itertools import chain
21*333d2b36SAndroid Build Coastguard Worker
22*333d2b36SAndroid Build Coastguard Worker
23*333d2b36SAndroid Build Coastguard Worker@dataclasses.dataclass()
24*333d2b36SAndroid Build Coastguard Workerclass Node:
25*333d2b36SAndroid Build Coastguard Worker    """A node in the signature trie."""
26*333d2b36SAndroid Build Coastguard Worker
27*333d2b36SAndroid Build Coastguard Worker    # The type of the node.
28*333d2b36SAndroid Build Coastguard Worker    #
29*333d2b36SAndroid Build Coastguard Worker    # Leaf nodes are of type "member".
30*333d2b36SAndroid Build Coastguard Worker    # Interior nodes can be either "package", or "class".
31*333d2b36SAndroid Build Coastguard Worker    type: str
32*333d2b36SAndroid Build Coastguard Worker
33*333d2b36SAndroid Build Coastguard Worker    # The selector of the node.
34*333d2b36SAndroid Build Coastguard Worker    #
35*333d2b36SAndroid Build Coastguard Worker    # That is a string that can be used to select the node, e.g. in a pattern
36*333d2b36SAndroid Build Coastguard Worker    # that is passed to InteriorNode.get_matching_rows().
37*333d2b36SAndroid Build Coastguard Worker    selector: str
38*333d2b36SAndroid Build Coastguard Worker
39*333d2b36SAndroid Build Coastguard Worker    def values(self, selector):
40*333d2b36SAndroid Build Coastguard Worker        """Get the values from a set of selected nodes.
41*333d2b36SAndroid Build Coastguard Worker
42*333d2b36SAndroid Build Coastguard Worker        :param selector: a function that can be applied to a key in the nodes
43*333d2b36SAndroid Build Coastguard Worker            attribute to determine whether to return its values.
44*333d2b36SAndroid Build Coastguard Worker
45*333d2b36SAndroid Build Coastguard Worker        :return: A list of iterables of all the values associated with
46*333d2b36SAndroid Build Coastguard Worker            this node and its children.
47*333d2b36SAndroid Build Coastguard Worker        """
48*333d2b36SAndroid Build Coastguard Worker        values = []
49*333d2b36SAndroid Build Coastguard Worker        self.append_values(values, selector)
50*333d2b36SAndroid Build Coastguard Worker        return values
51*333d2b36SAndroid Build Coastguard Worker
52*333d2b36SAndroid Build Coastguard Worker    def append_values(self, values, selector):
53*333d2b36SAndroid Build Coastguard Worker        """Append the values associated with this node and its children.
54*333d2b36SAndroid Build Coastguard Worker
55*333d2b36SAndroid Build Coastguard Worker        For each item (key, child) in nodes the child node's values are returned
56*333d2b36SAndroid Build Coastguard Worker        if and only if the selector returns True when called on its key. A child
57*333d2b36SAndroid Build Coastguard Worker        node's values are all the values associated with it and all its
58*333d2b36SAndroid Build Coastguard Worker        descendant nodes.
59*333d2b36SAndroid Build Coastguard Worker
60*333d2b36SAndroid Build Coastguard Worker        :param selector: a function that can be applied to a key in the nodes
61*333d2b36SAndroid Build Coastguard Worker        attribute to determine whether to return its values.
62*333d2b36SAndroid Build Coastguard Worker        :param values: a list of a iterables of values.
63*333d2b36SAndroid Build Coastguard Worker        """
64*333d2b36SAndroid Build Coastguard Worker        raise NotImplementedError("Please Implement this method")
65*333d2b36SAndroid Build Coastguard Worker
66*333d2b36SAndroid Build Coastguard Worker    def child_nodes(self):
67*333d2b36SAndroid Build Coastguard Worker        """Get an iterable of the child nodes of this node."""
68*333d2b36SAndroid Build Coastguard Worker        raise NotImplementedError("Please Implement this method")
69*333d2b36SAndroid Build Coastguard Worker
70*333d2b36SAndroid Build Coastguard Worker
71*333d2b36SAndroid Build Coastguard Worker# pylint: disable=line-too-long
72*333d2b36SAndroid Build Coastguard Worker@dataclasses.dataclass()
73*333d2b36SAndroid Build Coastguard Workerclass InteriorNode(Node):
74*333d2b36SAndroid Build Coastguard Worker    """An interior node in a trie.
75*333d2b36SAndroid Build Coastguard Worker
76*333d2b36SAndroid Build Coastguard Worker    Each interior node has a dict that maps from an element of a signature to
77*333d2b36SAndroid Build Coastguard Worker    either another interior node or a leaf. Each interior node represents either
78*333d2b36SAndroid Build Coastguard Worker    a package, class or nested class. Class members are represented by a Leaf.
79*333d2b36SAndroid Build Coastguard Worker
80*333d2b36SAndroid Build Coastguard Worker    Associating the set of flags [public-api] with the signature
81*333d2b36SAndroid Build Coastguard Worker    "Ljava/lang/Object;->String()Ljava/lang/String;" will cause the following
82*333d2b36SAndroid Build Coastguard Worker    nodes to be created:
83*333d2b36SAndroid Build Coastguard Worker    Node()
84*333d2b36SAndroid Build Coastguard Worker    ^- package:java -> Node()
85*333d2b36SAndroid Build Coastguard Worker       ^- package:lang -> Node()
86*333d2b36SAndroid Build Coastguard Worker           ^- class:Object -> Node()
87*333d2b36SAndroid Build Coastguard Worker              ^- member:String()Ljava/lang/String; -> Leaf([public-api])
88*333d2b36SAndroid Build Coastguard Worker
89*333d2b36SAndroid Build Coastguard Worker    Associating the set of flags [blocked,core-platform-api] with the signature
90*333d2b36SAndroid Build Coastguard Worker    "Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;"
91*333d2b36SAndroid Build Coastguard Worker    will cause the following nodes to be created:
92*333d2b36SAndroid Build Coastguard Worker    Node()
93*333d2b36SAndroid Build Coastguard Worker    ^- package:java -> Node()
94*333d2b36SAndroid Build Coastguard Worker       ^- package:lang -> Node()
95*333d2b36SAndroid Build Coastguard Worker           ^- class:Character -> Node()
96*333d2b36SAndroid Build Coastguard Worker              ^- class:UnicodeScript -> Node()
97*333d2b36SAndroid Build Coastguard Worker                 ^- member:of(I)Ljava/lang/Character$UnicodeScript;
98*333d2b36SAndroid Build Coastguard Worker                    -> Leaf([blocked,core-platform-api])
99*333d2b36SAndroid Build Coastguard Worker    """
100*333d2b36SAndroid Build Coastguard Worker
101*333d2b36SAndroid Build Coastguard Worker    # pylint: enable=line-too-long
102*333d2b36SAndroid Build Coastguard Worker
103*333d2b36SAndroid Build Coastguard Worker    # A dict from an element of the signature to the Node/Leaf containing the
104*333d2b36SAndroid Build Coastguard Worker    # next element/value.
105*333d2b36SAndroid Build Coastguard Worker    nodes: typing.Dict[str, Node] = dataclasses.field(default_factory=dict)
106*333d2b36SAndroid Build Coastguard Worker
107*333d2b36SAndroid Build Coastguard Worker    # pylint: disable=line-too-long
108*333d2b36SAndroid Build Coastguard Worker    @staticmethod
109*333d2b36SAndroid Build Coastguard Worker    def signature_to_elements(signature):
110*333d2b36SAndroid Build Coastguard Worker        """Split a signature or a prefix into a number of elements:
111*333d2b36SAndroid Build Coastguard Worker
112*333d2b36SAndroid Build Coastguard Worker        1. The packages (excluding the leading L preceding the first package).
113*333d2b36SAndroid Build Coastguard Worker        2. The class names, from outermost to innermost.
114*333d2b36SAndroid Build Coastguard Worker        3. The member signature.
115*333d2b36SAndroid Build Coastguard Worker        e.g.
116*333d2b36SAndroid Build Coastguard Worker        Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;
117*333d2b36SAndroid Build Coastguard Worker        will be broken down into these elements:
118*333d2b36SAndroid Build Coastguard Worker        1. package:java
119*333d2b36SAndroid Build Coastguard Worker        2. package:lang
120*333d2b36SAndroid Build Coastguard Worker        3. class:Character
121*333d2b36SAndroid Build Coastguard Worker        4. class:UnicodeScript
122*333d2b36SAndroid Build Coastguard Worker        5. member:of(I)Ljava/lang/Character$UnicodeScript;
123*333d2b36SAndroid Build Coastguard Worker        """
124*333d2b36SAndroid Build Coastguard Worker        # Remove the leading L.
125*333d2b36SAndroid Build Coastguard Worker        #  - java/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;
126*333d2b36SAndroid Build Coastguard Worker        text = signature.removeprefix("L")
127*333d2b36SAndroid Build Coastguard Worker        # Split the signature between qualified class name and the class member
128*333d2b36SAndroid Build Coastguard Worker        # signature.
129*333d2b36SAndroid Build Coastguard Worker        #  0 - java/lang/Character$UnicodeScript
130*333d2b36SAndroid Build Coastguard Worker        #  1 - of(I)Ljava/lang/Character$UnicodeScript;
131*333d2b36SAndroid Build Coastguard Worker        parts = text.split(";->")
132*333d2b36SAndroid Build Coastguard Worker        # If there is no member then this will be an empty list.
133*333d2b36SAndroid Build Coastguard Worker        member = parts[1:]
134*333d2b36SAndroid Build Coastguard Worker        # Split the qualified class name into packages, and class name.
135*333d2b36SAndroid Build Coastguard Worker        #  0 - java
136*333d2b36SAndroid Build Coastguard Worker        #  1 - lang
137*333d2b36SAndroid Build Coastguard Worker        #  2 - Character$UnicodeScript
138*333d2b36SAndroid Build Coastguard Worker        elements = parts[0].split("/")
139*333d2b36SAndroid Build Coastguard Worker        last_element = elements[-1]
140*333d2b36SAndroid Build Coastguard Worker        wildcard = []
141*333d2b36SAndroid Build Coastguard Worker        classes = []
142*333d2b36SAndroid Build Coastguard Worker        if "*" in last_element:
143*333d2b36SAndroid Build Coastguard Worker            if last_element not in ("*", "**"):
144*333d2b36SAndroid Build Coastguard Worker                raise Exception(f"Invalid signature '{signature}': invalid "
145*333d2b36SAndroid Build Coastguard Worker                                f"wildcard '{last_element}'")
146*333d2b36SAndroid Build Coastguard Worker            packages = elements[0:-1]
147*333d2b36SAndroid Build Coastguard Worker            # Cannot specify a wildcard and target a specific member
148*333d2b36SAndroid Build Coastguard Worker            if member:
149*333d2b36SAndroid Build Coastguard Worker                raise Exception(f"Invalid signature '{signature}': contains "
150*333d2b36SAndroid Build Coastguard Worker                                f"wildcard '{last_element}' and "
151*333d2b36SAndroid Build Coastguard Worker                                f"member signature '{member[0]}'")
152*333d2b36SAndroid Build Coastguard Worker            wildcard = [last_element]
153*333d2b36SAndroid Build Coastguard Worker        else:
154*333d2b36SAndroid Build Coastguard Worker            packages = elements[0:-1]
155*333d2b36SAndroid Build Coastguard Worker            # Split the class name into outer / inner classes
156*333d2b36SAndroid Build Coastguard Worker            #  0 - Character
157*333d2b36SAndroid Build Coastguard Worker            #  1 - UnicodeScript
158*333d2b36SAndroid Build Coastguard Worker            classes = last_element.removesuffix(";").split("$")
159*333d2b36SAndroid Build Coastguard Worker
160*333d2b36SAndroid Build Coastguard Worker        # Assemble the parts into a single list, adding prefixes to identify
161*333d2b36SAndroid Build Coastguard Worker        # the different parts. If a wildcard is provided then it looks something
162*333d2b36SAndroid Build Coastguard Worker        # like this:
163*333d2b36SAndroid Build Coastguard Worker        #  0 - package:java
164*333d2b36SAndroid Build Coastguard Worker        #  1 - package:lang
165*333d2b36SAndroid Build Coastguard Worker        #  2 - *
166*333d2b36SAndroid Build Coastguard Worker        #
167*333d2b36SAndroid Build Coastguard Worker        # Otherwise, it looks something like this:
168*333d2b36SAndroid Build Coastguard Worker        #  0 - package:java
169*333d2b36SAndroid Build Coastguard Worker        #  1 - package:lang
170*333d2b36SAndroid Build Coastguard Worker        #  2 - class:Character
171*333d2b36SAndroid Build Coastguard Worker        #  3 - class:UnicodeScript
172*333d2b36SAndroid Build Coastguard Worker        #  4 - member:of(I)Ljava/lang/Character$UnicodeScript;
173*333d2b36SAndroid Build Coastguard Worker        return list(
174*333d2b36SAndroid Build Coastguard Worker            chain([("package", x) for x in packages],
175*333d2b36SAndroid Build Coastguard Worker                  [("class", x) for x in classes],
176*333d2b36SAndroid Build Coastguard Worker                  [("member", x) for x in member],
177*333d2b36SAndroid Build Coastguard Worker                  [("wildcard", x) for x in wildcard]))
178*333d2b36SAndroid Build Coastguard Worker
179*333d2b36SAndroid Build Coastguard Worker    # pylint: enable=line-too-long
180*333d2b36SAndroid Build Coastguard Worker
181*333d2b36SAndroid Build Coastguard Worker    @staticmethod
182*333d2b36SAndroid Build Coastguard Worker    def split_element(element):
183*333d2b36SAndroid Build Coastguard Worker        element_type, element_value = element
184*333d2b36SAndroid Build Coastguard Worker        return element_type, element_value
185*333d2b36SAndroid Build Coastguard Worker
186*333d2b36SAndroid Build Coastguard Worker    @staticmethod
187*333d2b36SAndroid Build Coastguard Worker    def element_type(element):
188*333d2b36SAndroid Build Coastguard Worker        element_type, _ = InteriorNode.split_element(element)
189*333d2b36SAndroid Build Coastguard Worker        return element_type
190*333d2b36SAndroid Build Coastguard Worker
191*333d2b36SAndroid Build Coastguard Worker    @staticmethod
192*333d2b36SAndroid Build Coastguard Worker    def elements_to_selector(elements):
193*333d2b36SAndroid Build Coastguard Worker        """Compute a selector for a set of elements.
194*333d2b36SAndroid Build Coastguard Worker
195*333d2b36SAndroid Build Coastguard Worker        A selector uniquely identifies a specific Node in the trie. It is
196*333d2b36SAndroid Build Coastguard Worker        essentially a prefix of a signature (without the leading L).
197*333d2b36SAndroid Build Coastguard Worker
198*333d2b36SAndroid Build Coastguard Worker        e.g. a trie containing "Ljava/lang/Object;->String()Ljava/lang/String;"
199*333d2b36SAndroid Build Coastguard Worker        would contain nodes with the following selectors:
200*333d2b36SAndroid Build Coastguard Worker        * "java"
201*333d2b36SAndroid Build Coastguard Worker        * "java/lang"
202*333d2b36SAndroid Build Coastguard Worker        * "java/lang/Object"
203*333d2b36SAndroid Build Coastguard Worker        * "java/lang/Object;->String()Ljava/lang/String;"
204*333d2b36SAndroid Build Coastguard Worker        """
205*333d2b36SAndroid Build Coastguard Worker        signature = ""
206*333d2b36SAndroid Build Coastguard Worker        preceding_type = ""
207*333d2b36SAndroid Build Coastguard Worker        for element in elements:
208*333d2b36SAndroid Build Coastguard Worker            element_type, element_value = InteriorNode.split_element(element)
209*333d2b36SAndroid Build Coastguard Worker            separator = ""
210*333d2b36SAndroid Build Coastguard Worker            if element_type == "package":
211*333d2b36SAndroid Build Coastguard Worker                separator = "/"
212*333d2b36SAndroid Build Coastguard Worker            elif element_type == "class":
213*333d2b36SAndroid Build Coastguard Worker                if preceding_type == "class":
214*333d2b36SAndroid Build Coastguard Worker                    separator = "$"
215*333d2b36SAndroid Build Coastguard Worker                else:
216*333d2b36SAndroid Build Coastguard Worker                    separator = "/"
217*333d2b36SAndroid Build Coastguard Worker            elif element_type == "wildcard":
218*333d2b36SAndroid Build Coastguard Worker                separator = "/"
219*333d2b36SAndroid Build Coastguard Worker            elif element_type == "member":
220*333d2b36SAndroid Build Coastguard Worker                separator += ";->"
221*333d2b36SAndroid Build Coastguard Worker
222*333d2b36SAndroid Build Coastguard Worker            if signature:
223*333d2b36SAndroid Build Coastguard Worker                signature += separator
224*333d2b36SAndroid Build Coastguard Worker
225*333d2b36SAndroid Build Coastguard Worker            signature += element_value
226*333d2b36SAndroid Build Coastguard Worker
227*333d2b36SAndroid Build Coastguard Worker            preceding_type = element_type
228*333d2b36SAndroid Build Coastguard Worker
229*333d2b36SAndroid Build Coastguard Worker        return signature
230*333d2b36SAndroid Build Coastguard Worker
231*333d2b36SAndroid Build Coastguard Worker    def add(self, signature, value, only_if_matches=False):
232*333d2b36SAndroid Build Coastguard Worker        """Associate the value with the specific signature.
233*333d2b36SAndroid Build Coastguard Worker
234*333d2b36SAndroid Build Coastguard Worker        :param signature: the member signature
235*333d2b36SAndroid Build Coastguard Worker        :param value: the value to associated with the signature
236*333d2b36SAndroid Build Coastguard Worker        :param only_if_matches: True if the value is added only if the signature
237*333d2b36SAndroid Build Coastguard Worker             matches at least one of the existing top level packages.
238*333d2b36SAndroid Build Coastguard Worker        :return: n/a
239*333d2b36SAndroid Build Coastguard Worker        """
240*333d2b36SAndroid Build Coastguard Worker        # Split the signature into elements.
241*333d2b36SAndroid Build Coastguard Worker        elements = self.signature_to_elements(signature)
242*333d2b36SAndroid Build Coastguard Worker        # Find the Node associated with the deepest class.
243*333d2b36SAndroid Build Coastguard Worker        node = self
244*333d2b36SAndroid Build Coastguard Worker        for index, element in enumerate(elements[:-1]):
245*333d2b36SAndroid Build Coastguard Worker            if element in node.nodes:
246*333d2b36SAndroid Build Coastguard Worker                node = node.nodes[element]
247*333d2b36SAndroid Build Coastguard Worker            elif only_if_matches and index == 0:
248*333d2b36SAndroid Build Coastguard Worker                return
249*333d2b36SAndroid Build Coastguard Worker            else:
250*333d2b36SAndroid Build Coastguard Worker                selector = self.elements_to_selector(elements[0:index + 1])
251*333d2b36SAndroid Build Coastguard Worker                next_node = InteriorNode(
252*333d2b36SAndroid Build Coastguard Worker                    type=InteriorNode.element_type(element), selector=selector)
253*333d2b36SAndroid Build Coastguard Worker                node.nodes[element] = next_node
254*333d2b36SAndroid Build Coastguard Worker                node = next_node
255*333d2b36SAndroid Build Coastguard Worker        # Add a Leaf containing the value and associate it with the member
256*333d2b36SAndroid Build Coastguard Worker        # signature within the class.
257*333d2b36SAndroid Build Coastguard Worker        last_element = elements[-1]
258*333d2b36SAndroid Build Coastguard Worker        last_element_type = self.element_type(last_element)
259*333d2b36SAndroid Build Coastguard Worker        if last_element_type != "member":
260*333d2b36SAndroid Build Coastguard Worker            raise Exception(
261*333d2b36SAndroid Build Coastguard Worker                f"Invalid signature: {signature}, does not identify a "
262*333d2b36SAndroid Build Coastguard Worker                "specific member")
263*333d2b36SAndroid Build Coastguard Worker        if last_element in node.nodes:
264*333d2b36SAndroid Build Coastguard Worker            raise Exception(f"Duplicate signature: {signature}")
265*333d2b36SAndroid Build Coastguard Worker        leaf = Leaf(
266*333d2b36SAndroid Build Coastguard Worker            type=last_element_type,
267*333d2b36SAndroid Build Coastguard Worker            selector=signature,
268*333d2b36SAndroid Build Coastguard Worker            value=value,
269*333d2b36SAndroid Build Coastguard Worker        )
270*333d2b36SAndroid Build Coastguard Worker        node.nodes[last_element] = leaf
271*333d2b36SAndroid Build Coastguard Worker
272*333d2b36SAndroid Build Coastguard Worker    def get_matching_rows(self, pattern):
273*333d2b36SAndroid Build Coastguard Worker        """Get the values (plural) associated with the pattern.
274*333d2b36SAndroid Build Coastguard Worker
275*333d2b36SAndroid Build Coastguard Worker        e.g. If the pattern is a full signature then this will return a list
276*333d2b36SAndroid Build Coastguard Worker        containing the value associated with that signature.
277*333d2b36SAndroid Build Coastguard Worker
278*333d2b36SAndroid Build Coastguard Worker        If the pattern is a class then this will return a list containing the
279*333d2b36SAndroid Build Coastguard Worker        values associated with all members of that class.
280*333d2b36SAndroid Build Coastguard Worker
281*333d2b36SAndroid Build Coastguard Worker        If the pattern ends with "*" then the preceding part is treated as a
282*333d2b36SAndroid Build Coastguard Worker        package and this will return a list containing the values associated
283*333d2b36SAndroid Build Coastguard Worker        with all the members of all the classes in that package.
284*333d2b36SAndroid Build Coastguard Worker
285*333d2b36SAndroid Build Coastguard Worker        If the pattern ends with "**" then the preceding part is treated
286*333d2b36SAndroid Build Coastguard Worker        as a package and this will return a list containing the values
287*333d2b36SAndroid Build Coastguard Worker        associated with all the members of all the classes in that package and
288*333d2b36SAndroid Build Coastguard Worker        all sub-packages.
289*333d2b36SAndroid Build Coastguard Worker
290*333d2b36SAndroid Build Coastguard Worker        :param pattern: the pattern which could be a complete signature or a
291*333d2b36SAndroid Build Coastguard Worker        class, or package wildcard.
292*333d2b36SAndroid Build Coastguard Worker        :return: an iterable containing all the values associated with the
293*333d2b36SAndroid Build Coastguard Worker        pattern.
294*333d2b36SAndroid Build Coastguard Worker        """
295*333d2b36SAndroid Build Coastguard Worker        elements = self.signature_to_elements(pattern)
296*333d2b36SAndroid Build Coastguard Worker        node = self
297*333d2b36SAndroid Build Coastguard Worker
298*333d2b36SAndroid Build Coastguard Worker        # Include all values from this node and all its children.
299*333d2b36SAndroid Build Coastguard Worker        selector = lambda x: True
300*333d2b36SAndroid Build Coastguard Worker
301*333d2b36SAndroid Build Coastguard Worker        last_element = elements[-1]
302*333d2b36SAndroid Build Coastguard Worker        last_element_type, last_element_value = self.split_element(last_element)
303*333d2b36SAndroid Build Coastguard Worker        if last_element_type == "wildcard":
304*333d2b36SAndroid Build Coastguard Worker            elements = elements[:-1]
305*333d2b36SAndroid Build Coastguard Worker            if last_element_value == "*":
306*333d2b36SAndroid Build Coastguard Worker                # Do not include values from sub-packages.
307*333d2b36SAndroid Build Coastguard Worker                selector = lambda x: InteriorNode.element_type(x) != "package"
308*333d2b36SAndroid Build Coastguard Worker
309*333d2b36SAndroid Build Coastguard Worker        for element in elements:
310*333d2b36SAndroid Build Coastguard Worker            if element in node.nodes:
311*333d2b36SAndroid Build Coastguard Worker                node = node.nodes[element]
312*333d2b36SAndroid Build Coastguard Worker            else:
313*333d2b36SAndroid Build Coastguard Worker                return []
314*333d2b36SAndroid Build Coastguard Worker
315*333d2b36SAndroid Build Coastguard Worker        return node.values(selector)
316*333d2b36SAndroid Build Coastguard Worker
317*333d2b36SAndroid Build Coastguard Worker    def append_values(self, values, selector):
318*333d2b36SAndroid Build Coastguard Worker        for key, node in self.nodes.items():
319*333d2b36SAndroid Build Coastguard Worker            if selector(key):
320*333d2b36SAndroid Build Coastguard Worker                node.append_values(values, lambda x: True)
321*333d2b36SAndroid Build Coastguard Worker
322*333d2b36SAndroid Build Coastguard Worker    def child_nodes(self):
323*333d2b36SAndroid Build Coastguard Worker        return self.nodes.values()
324*333d2b36SAndroid Build Coastguard Worker
325*333d2b36SAndroid Build Coastguard Worker
326*333d2b36SAndroid Build Coastguard Worker@dataclasses.dataclass()
327*333d2b36SAndroid Build Coastguard Workerclass Leaf(Node):
328*333d2b36SAndroid Build Coastguard Worker    """A leaf of the trie"""
329*333d2b36SAndroid Build Coastguard Worker
330*333d2b36SAndroid Build Coastguard Worker    # The value associated with this leaf.
331*333d2b36SAndroid Build Coastguard Worker    value: typing.Any
332*333d2b36SAndroid Build Coastguard Worker
333*333d2b36SAndroid Build Coastguard Worker    def append_values(self, values, selector):
334*333d2b36SAndroid Build Coastguard Worker        values.append(self.value)
335*333d2b36SAndroid Build Coastguard Worker
336*333d2b36SAndroid Build Coastguard Worker    def child_nodes(self):
337*333d2b36SAndroid Build Coastguard Worker        return []
338*333d2b36SAndroid Build Coastguard Worker
339*333d2b36SAndroid Build Coastguard Worker
340*333d2b36SAndroid Build Coastguard Workerdef signature_trie():
341*333d2b36SAndroid Build Coastguard Worker    return InteriorNode(type="root", selector="")
342