| /* |
| * Copyright 2022 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package androidx.window.core |
| |
| import android.annotation.SuppressLint |
| import android.util.Pair |
| import java.lang.reflect.InvocationHandler |
| import java.lang.reflect.Method |
| import java.lang.reflect.Proxy |
| import kotlin.reflect.KClass |
| import kotlin.reflect.cast |
| |
| /** |
| * An adapter over {@link java.util.function.Predicate} to workaround mismatch in expected extension |
| * API signatures after library desugaring. See b/203472665 |
| */ |
| @SuppressLint("BanUncheckedReflection") |
| internal class PredicateAdapter( |
| private val loader: ClassLoader |
| ) { |
| internal fun predicateClassOrNull(): Class<*>? { |
| return try { |
| predicateClassOrThrow() |
| } catch (e: ClassNotFoundException) { |
| null |
| } |
| } |
| |
| private fun predicateClassOrThrow(): Class<*> { |
| return loader.loadClass("java.util.function.Predicate") |
| } |
| |
| fun <T : Any> buildPredicate(clazz: KClass<T>, predicate: (T) -> Boolean): Any { |
| val predicateHandler = PredicateStubHandler( |
| clazz, |
| predicate |
| ) |
| return Proxy.newProxyInstance(loader, arrayOf(predicateClassOrThrow()), predicateHandler) |
| } |
| |
| fun <T : Any, U : Any> buildPairPredicate( |
| firstClazz: KClass<T>, |
| secondClazz: KClass<U>, |
| predicate: (T, U) -> Boolean |
| ): Any { |
| val predicateHandler = PairPredicateStubHandler( |
| firstClazz, |
| secondClazz, |
| predicate |
| ) |
| |
| return Proxy.newProxyInstance(loader, arrayOf(predicateClassOrThrow()), predicateHandler) |
| } |
| |
| private abstract class BaseHandler<T : Any>(private val clazz: KClass<T>) : InvocationHandler { |
| override fun invoke(obj: Any, method: Method, parameters: Array<out Any>?): Any { |
| return when { |
| method.isTest(parameters) -> { |
| val argument = clazz.cast(parameters?.get(0)) |
| invokeTest(obj, argument) |
| } |
| method.isEquals(parameters) -> { |
| obj === parameters?.get(0)!! |
| } |
| method.isHashCode(parameters) -> { |
| hashCode() |
| } |
| method.isToString(parameters) -> { |
| toString() |
| } |
| else -> { |
| throw UnsupportedOperationException( |
| "Unexpected method call object:$obj, method: $method, args: $parameters" |
| ) |
| } |
| } |
| } |
| |
| abstract fun invokeTest(obj: Any, parameter: T): Boolean |
| |
| protected fun Method.isEquals(args: Array<out Any>?): Boolean { |
| return name == "equals" && returnType.equals(Boolean::class.java) && args?.size == 1 |
| } |
| |
| protected fun Method.isHashCode(args: Array<out Any>?): Boolean { |
| return name == "hashCode" && returnType.equals(Int::class.java) && args == null |
| } |
| |
| protected fun Method.isTest(args: Array<out Any>?): Boolean { |
| return name == "test" && returnType.equals(Boolean::class.java) && args?.size == 1 |
| } |
| |
| protected fun Method.isToString(args: Array<out Any>?): Boolean { |
| return name == "toString" && returnType.equals(String::class.java) && args == null |
| } |
| } |
| |
| private class PredicateStubHandler<T : Any>( |
| clazzT: KClass<T>, |
| private val predicate: (T) -> Boolean |
| ) : BaseHandler<T>(clazzT) { |
| override fun invokeTest(obj: Any, parameter: T): Boolean { |
| return predicate(parameter) |
| } |
| |
| override fun hashCode(): Int { |
| return predicate.hashCode() |
| } |
| |
| override fun toString(): String { |
| return predicate.toString() |
| } |
| } |
| |
| private class PairPredicateStubHandler<T : Any, U : Any>( |
| private val clazzT: KClass<T>, |
| private val clazzU: KClass<U>, |
| private val predicate: (T, U) -> Boolean |
| ) : BaseHandler<Pair<*, *>>(Pair::class) { |
| override fun invokeTest(obj: Any, parameter: Pair<*, *>): Boolean { |
| val t = clazzT.cast(parameter.first) |
| val u = clazzU.cast(parameter.second) |
| return predicate(t, u) |
| } |
| |
| override fun hashCode(): Int { |
| return predicate.hashCode() |
| } |
| |
| override fun toString(): String { |
| return predicate.toString() |
| } |
| } |
| } |