1 /*
2  * 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.tools.metalava.model
18 
19 /**
20  * An [ItemVisitor] that simply traverses an [Item] hierarchy calling [visitItem] for each [Item].
21  *
22  * The [visitItem] method can affect the traversal through the [TraversalAction] value it returns.
23  *
24  * It intentionally does not visit [ParameterItem]s.
25  */
26 abstract class TraversingVisitor : ItemVisitor {
27 
28     enum class TraversalAction {
29         /** Continue normal traversal. */
30         CONTINUE,
31 
32         /** Skip the children of the current [Item] but continue with its sibling. */
33         SKIP_CHILDREN,
34 
35         /** Skip the whole traversal. */
36         SKIP_TRAVERSAL,
37     }
38 
39     private var traversalFinished = false
40 
41     /** Visit the item returning an action for the [TraversingVisitor] to take. */
visitItemnull42     abstract fun visitItem(item: Item): TraversalAction
43 
44     final override fun visit(cls: ClassItem) {
45         when (visitItem(cls)) {
46             TraversalAction.SKIP_TRAVERSAL -> {
47                 traversalFinished = true
48             }
49             TraversalAction.SKIP_CHILDREN -> {
50                 // Do nothing
51             }
52             TraversalAction.CONTINUE -> {
53                 for (constructor in cls.constructors()) {
54                     constructor.accept(this)
55                     if (traversalFinished) return
56                 }
57 
58                 for (method in cls.methods()) {
59                     method.accept(this)
60                     if (traversalFinished) return
61                 }
62 
63                 for (property in cls.properties()) {
64                     property.accept(this)
65                     if (traversalFinished) return
66                 }
67 
68                 for (field in cls.fields()) {
69                     field.accept(this)
70                     if (traversalFinished) return
71                 }
72 
73                 for (innerCls in cls.innerClasses()) {
74                     innerCls.accept(this)
75                     if (traversalFinished) return
76                 }
77             }
78         }
79     }
80 
visitnull81     final override fun visit(field: FieldItem) {
82         val action = visitItem(field)
83         traversalFinished = action == TraversalAction.SKIP_TRAVERSAL
84     }
85 
visitnull86     final override fun visit(method: MethodItem) {
87         val action = visitItem(method)
88         traversalFinished = action == TraversalAction.SKIP_TRAVERSAL
89     }
90 
visitnull91     final override fun visit(pkg: PackageItem) {
92         when (visitItem(pkg)) {
93             TraversalAction.SKIP_TRAVERSAL -> {
94                 traversalFinished = true
95             }
96             TraversalAction.SKIP_CHILDREN -> {
97                 // Do nothing
98             }
99             TraversalAction.CONTINUE -> {
100                 for (cls in pkg.topLevelClasses()) {
101                     cls.accept(this)
102                     if (traversalFinished) return
103                 }
104             }
105         }
106     }
107 
visitnull108     final override fun visit(packageList: PackageList) {
109         for (it in packageList.packages) {
110             it.accept(this)
111             if (traversalFinished) return
112         }
113     }
114 
visitnull115     final override fun visit(parameter: ParameterItem) {
116         error("parameters should not be visited")
117     }
118 
visitnull119     final override fun visit(property: PropertyItem) {
120         val action = visitItem(property)
121         traversalFinished = action == TraversalAction.SKIP_TRAVERSAL
122     }
123 }
124