Moving some logic into ApiVisitor.kt so it can be more easily modified by ComparisonVisitor.kt

Bug: 131633221
Test: ./gradlew test

Change-Id: Ie15c1890f4a141ffa46879367a1f28319f945a26
diff --git a/src/main/java/com/android/tools/metalava/model/ClassItem.kt b/src/main/java/com/android/tools/metalava/model/ClassItem.kt
index 768f648..f7c905e 100644
--- a/src/main/java/com/android/tools/metalava/model/ClassItem.kt
+++ b/src/main/java/com/android/tools/metalava/model/ClassItem.kt
@@ -298,9 +298,6 @@
     }
 
     fun accept(visitor: ApiVisitor) {
-        if (visitor.skip(this)) {
-            return
-        }
 
         if (!visitor.include(this)) {
             return
@@ -757,8 +754,8 @@
     fun addMethod(method: MethodItem): Unit = codebase.unsupported()
 }
 
-class VisitCandidate(private val cls: ClassItem, private val visitor: ApiVisitor) {
-    private val innerClasses: Sequence<VisitCandidate>
+class VisitCandidate(val cls: ClassItem, private val visitor: ApiVisitor) {
+    public val innerClasses: Sequence<VisitCandidate>
     private val constructors: Sequence<MethodItem>
     private val methods: Sequence<MethodItem>
     private val fields: Sequence<FieldItem>
@@ -810,48 +807,17 @@
             .map { VisitCandidate(it, visitor) }
     }
 
-    /** Will this class emit anything? */
-    private fun emit(): Boolean {
-        val emit = emitClass()
-        if (emit) {
-            return true
-        }
-
-        return emitInner()
-    }
-
-    private fun emitInner(): Boolean {
-        return innerClasses.any { it.emit() }
-    }
-
-    /** Does the body of this class (everything other than the inner classes) emit anything? */
-    private fun emitClass(): Boolean {
-        val classEmpty = (constructors.none() && methods.none() && enums.none() && fields.none() && properties.none())
-        return if (visitor.filterEmit.test(cls)) {
-            true
-        } else if (!classEmpty) {
-            visitor.filterReference.test(cls)
-        } else {
-            false
-        }
+    /** Whether the class body contains any Item's (other than inner Classes) */
+    public fun nonEmpty(): Boolean {
+        return !(constructors.none() && methods.none() && enums.none() && fields.none() && properties.none())
     }
 
     fun accept() {
-        if (visitor.skip(cls)) {
+        if (!visitor.include(this)) {
             return
         }
 
-        if (!visitor.include(cls)) {
-            return
-        }
-
-        val emitClass = emitClass()
-        val emit = emitClass || emitInner()
-        if (!emit) {
-            return
-        }
-
-        val emitThis = cls.emit && if (visitor.includeEmptyOuterClasses) emit else emitClass
+        val emitThis = visitor.shouldEmitClass(this)
         if (emitThis) {
             if (!visitor.visitingPackage) {
                 visitor.visitingPackage = true
diff --git a/src/main/java/com/android/tools/metalava/model/visitors/ApiVisitor.kt b/src/main/java/com/android/tools/metalava/model/visitors/ApiVisitor.kt
index 76b4a34..411f008 100644
--- a/src/main/java/com/android/tools/metalava/model/visitors/ApiVisitor.kt
+++ b/src/main/java/com/android/tools/metalava/model/visitors/ApiVisitor.kt
@@ -21,6 +21,7 @@
 import com.android.tools.metalava.model.FieldItem
 import com.android.tools.metalava.model.Item
 import com.android.tools.metalava.model.MethodItem
+import com.android.tools.metalava.model.VisitCandidate
 import com.android.tools.metalava.options
 import java.util.function.Predicate
 
@@ -109,7 +110,13 @@
     // this property keeps track of whether we've already visited the current package
     var visitingPackage = false
 
+    /**
+     * @return Whether this class is generally one that we want to recurse into
+     */
     open fun include(cls: ClassItem): Boolean {
+        if (skip(cls)) {
+            return false
+        }
         val filter = options.stubPackages
         if (filter != null && !filter.matches(cls.containingPackage())) {
             return false
@@ -117,4 +124,50 @@
 
         return cls.emit || cls.codebase.preFiltered
     }
+
+    /**
+     * @return Whether the given VisitCandidate's visitor should recurse into the given
+     * VisitCandidate's class
+     */
+    fun include(vc: VisitCandidate): Boolean {
+        if (!include(vc.cls)) {
+            return false
+        }
+        return shouldEmitClassBody(vc) || shouldEmitInnerClasses(vc)
+    }
+
+    /**
+     * @return Whether this class should be visited
+     * Note that if [include] returns true then we will still visit classes that are contained by this one
+     */
+    open fun shouldEmitClass(vc: VisitCandidate): Boolean {
+        return vc.cls.emit && (includeEmptyOuterClasses || shouldEmitClassBody(vc))
+    }
+
+    /**
+     * @return Whether the body of this class (everything other than the inner classes) emits anything
+     */
+    fun shouldEmitClassBody(vc: VisitCandidate): Boolean {
+        return if (filterEmit.test(vc.cls)) {
+            true
+        } else if (vc.nonEmpty()) {
+            filterReference.test(vc.cls)
+        } else {
+            false
+        }
+    }
+
+    /**
+     * @return Whether the inner classes of this class will emit anything
+     */
+    fun shouldEmitInnerClasses(vc: VisitCandidate): Boolean {
+        return vc.innerClasses.any { shouldEmitAnyClass(it) }
+    }
+
+    /**
+     * @return Whether this class will emit anything
+     */
+    fun shouldEmitAnyClass(vc: VisitCandidate): Boolean {
+        return shouldEmitClassBody(vc) || shouldEmitInnerClasses(vc)
+    }
 }