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 package com.android.intentresolver.validation.types
17 
18 import com.android.intentresolver.validation.Importance
19 import com.android.intentresolver.validation.Invalid
20 import com.android.intentresolver.validation.NoValue
21 import com.android.intentresolver.validation.Valid
22 import com.android.intentresolver.validation.ValidationResult
23 import com.android.intentresolver.validation.Validator
24 import com.android.intentresolver.validation.ValueIsWrongType
25 import com.android.intentresolver.validation.WrongElementType
26 import kotlin.reflect.KClass
27 import kotlin.reflect.cast
28 
29 class ParceledArray<T : Any>(
30     override val key: String,
31     private val elementType: KClass<T>,
32 ) : Validator<List<T>> {
33 
validatenull34     override fun validate(
35         source: (String) -> Any?,
36         importance: Importance
37     ): ValidationResult<List<T>> {
38         return when (val value: Any? = source(key)) {
39             // No value present.
40             null ->
41                 when (importance) {
42                     Importance.WARNING -> Invalid() // No warnings if optional, but missing
43                     Importance.CRITICAL -> Invalid(NoValue(key, importance, elementType))
44                 }
45             // A parcel does not transfer the element type information for parcelable
46             // arrays. This leads to a restored type of Array<Parcelable>, which is
47             // incompatible with Array<T : Parcelable>.
48 
49             // To handle this safely, treat as Array<*>, assert contents of the expected
50             // parcelable type, and return as a list.
51 
52             is Array<*> -> {
53                 val invalid = value.filterNotNull().firstOrNull { !elementType.isInstance(it) }
54                 when (invalid) {
55                     // No invalid elements, result is ok.
56                     null -> Valid(value.map { elementType.cast(it) })
57 
58                     // At least one incorrect element type found.
59                     else ->
60                         Invalid(
61                             WrongElementType(
62                                 key,
63                                 importance,
64                                 actualType = invalid::class,
65                                 container = Array::class,
66                                 expectedType = elementType
67                             )
68                         )
69                 }
70             }
71 
72             // The value is not an Array at all.
73             else ->
74                 Invalid(
75                     ValueIsWrongType(
76                         key,
77                         importance,
78                         actualType = value::class,
79                         allowedTypes = listOf(elementType)
80                     )
81                 )
82         }
83     }
84 }
85