1/*
2 * Copyright (C) 2018 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
17package com.android.sdkparcelables
18
19/** A class that uses an ancestor map to find all classes that
20 * implement android.os.Parcelable, including indirectly through
21 * super classes or super interfaces.
22 */
23class ParcelableDetector {
24    companion object {
25        fun ancestorsToParcelables(ancestors: Map<String, Ancestors>): List<String> {
26            val impl = Impl(ancestors)
27            impl.build()
28            return impl.parcelables
29        }
30
31        const val PARCELABLE_CLASS = "android/os/Parcelable"
32    }
33
34    private class Impl(val ancestors: Map<String, Ancestors>) {
35        val isParcelableCache = HashMap<String, Boolean>()
36        val parcelables = ArrayList<String>()
37
38        fun build() {
39            val classList = ancestors.keys
40            classList.filterTo(parcelables, { (it != PARCELABLE_CLASS) && isParcelable(it) })
41            parcelables.sort()
42        }
43
44        private fun isParcelable(c: String?): Boolean {
45            if (c == null) {
46                return false
47            }
48
49            if (c == PARCELABLE_CLASS) {
50                return true
51            }
52
53            val old = isParcelableCache[c]
54            if (old != null) {
55                return old
56            }
57
58            val cAncestors = ancestors[c] ?:
59                    throw RuntimeException("class $c missing ancestor information")
60
61            val seq = (cAncestors.interfaces?.asSequence() ?: emptySequence()) +
62                    cAncestors.superName
63
64            val ancestorIsParcelable = seq.any(this::isParcelable)
65
66            isParcelableCache[c] = ancestorIsParcelable
67            return ancestorIsParcelable
68        }
69    }
70}
71