1 /* 2 * Copyright 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 package androidx.work 17 18 import java.lang.IllegalArgumentException 19 import java.lang.reflect.Array 20 import java.util.HashMap 21 22 /** 23 * An [InputMerger] that attempts to merge the inputs, creating arrays when necessary. For each 24 * input, we look at each key: 25 * * If this is the first time we encountered the key: 26 * * If it's an array, put it in the output 27 * * If it's a primitive, turn it into a size 1 array and put it in the output 28 * * Else (we have encountered the key before): 29 * * If the value type matches the old value type: 30 * * If they are arrays, concatenate them 31 * * If they are primitives, turn them into a size 2 array 32 * * Else if one is an array and the other is a primitive of that type: 33 * * Make a longer array and concatenate them 34 * * Else throw an [IllegalArgumentException] because the types don't match. 35 * 36 * If a value by a key is `null`, it is considered to have type `String`, because it is the only 37 * nullable typed allowed in [Data]. 38 */ 39 class ArrayCreatingInputMerger : InputMerger() { 40 @Suppress("DocumentExceptions") mergenull41 override fun merge(inputs: List<Data>): Data { 42 val output = Data.Builder() 43 // values are always arrays 44 val mergedValues: MutableMap<String, Any> = HashMap() 45 for (input in inputs) { 46 for ((key, value) in input.keyValueMap) { 47 val valueClass: Class<*> = value?.javaClass ?: String::class.java 48 val existingValue = mergedValues[key] 49 mergedValues[key] = 50 if (existingValue == null) { 51 // First time encountering this key. 52 if (valueClass.isArray) { 53 // Arrays carry over as-is. 54 // if valueClass.isArray is true then value isn't null 55 value as Any 56 } else { 57 // Primitives get turned into size 1 arrays. 58 createArrayFor(value, valueClass) 59 } 60 } else { 61 // We've encountered this key before. 62 val existingValueClass: Class<*> = existingValue.javaClass 63 when { 64 existingValueClass == valueClass -> { 65 // The classes match; we can merge. 66 // both classes are arrays in this case => value isn't null 67 concatenateArrays(existingValue, value as Any) 68 } 69 existingValueClass.componentType == valueClass -> { 70 // We have an existing array of the same type. 71 concatenateArrayAndNonArray(existingValue, value, valueClass) 72 } 73 else -> throw IllegalArgumentException() 74 } 75 } 76 } 77 } 78 output.putAll(mergedValues) 79 return output.build() 80 } 81 concatenateArraysnull82 private fun concatenateArrays(array1: Any, array2: Any): Any { 83 val length1 = Array.getLength(array1) 84 val length2 = Array.getLength(array2) 85 val newArray = Array.newInstance(array1.javaClass.componentType!!, length1 + length2) 86 System.arraycopy(array1, 0, newArray, 0, length1) 87 System.arraycopy(array2, 0, newArray, length1, length2) 88 return newArray 89 } 90 concatenateArrayAndNonArraynull91 private fun concatenateArrayAndNonArray(array: Any, obj: Any?, valueClass: Class<*>): Any { 92 val arrayLength = Array.getLength(array) 93 val newArray = Array.newInstance(valueClass, arrayLength + 1) 94 System.arraycopy(array, 0, newArray, 0, arrayLength) 95 Array.set(newArray, arrayLength, obj) 96 return newArray 97 } 98 createArrayFornull99 private fun createArrayFor(obj: Any?, valueClass: Class<*>): Any { 100 val newArray = Array.newInstance(valueClass, 1) 101 Array.set(newArray, 0, obj) 102 return newArray 103 } 104 } 105