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 17 package androidx.work; 18 19 import android.support.annotation.NonNull; 20 21 import java.lang.reflect.Array; 22 import java.util.HashMap; 23 import java.util.List; 24 import java.util.Map; 25 26 /** 27 * An {@link InputMerger} that attempts to merge the various inputs. For each input, we look at 28 * each key: 29 * <p><ul> 30 * <li>If this is the first time we encountered the key</li> 31 * <ul> 32 * <li>If it's an array, put it in the output</li> 33 * <li>If it's a primitive, turn it into a size 1 array and put it in the output</li> 34 * </ul> 35 * <li>Else</li> 36 * <ul> 37 * <li>If the value type matches the old value type</li> 38 * <ul> 39 * <li>If they are arrays, concatenate them</li> 40 * <li>If they are primitives, turn them into a size 2 array</li> 41 * </ul> 42 * <li>Else if one is an array and the other is a primitive</li> 43 * <ul> 44 * <li>Make a longer array and concatenate them</li> 45 * </ul> 46 * <li>Else throw an {@link IllegalArgumentException}</li> 47 * </ul> 48 * </ul> 49 */ 50 51 public final class ArrayCreatingInputMerger extends InputMerger { 52 53 @Override merge(@onNull List<Data> inputs)54 public @NonNull Data merge(@NonNull List<Data> inputs) { 55 Data.Builder output = new Data.Builder(); 56 Map<String, Object> mergedValues = new HashMap<>(); 57 58 for (Data input : inputs) { 59 for (Map.Entry<String, Object> entry : input.getKeyValueMap().entrySet()) { 60 String key = entry.getKey(); 61 Object value = entry.getValue(); 62 Class valueClass = value.getClass(); 63 Object mergedValue = null; 64 65 if (!mergedValues.containsKey(key)) { 66 // First time encountering this key. 67 if (valueClass.isArray()) { 68 // Arrays carry over as-is. 69 mergedValue = value; 70 } else { 71 // Primitives get turned into size 1 arrays. 72 mergedValue = createArrayFor(value); 73 } 74 } else { 75 // We've encountered this key before. 76 Object existingValue = mergedValues.get(key); 77 Class existingValueClass = existingValue.getClass(); 78 79 if (existingValueClass.equals(valueClass)) { 80 // The classes match; we can merge. 81 if (existingValueClass.isArray()) { 82 mergedValue = concatenateArrays(existingValue, value); 83 } else { 84 mergedValue = concatenateNonArrays(existingValue, value); 85 } 86 } else if (existingValueClass.isArray() 87 && existingValueClass.getComponentType().equals(valueClass)) { 88 // We have an existing array of the same type. 89 mergedValue = concatenateArrayAndNonArray(existingValue, value); 90 } else if (valueClass.isArray() 91 && valueClass.getComponentType().equals(existingValueClass)) { 92 // We have an existing array of the same type. 93 mergedValue = concatenateArrayAndNonArray(value, existingValue); 94 } else { 95 throw new IllegalArgumentException(); 96 } 97 } 98 99 mergedValues.put(key, mergedValue); 100 } 101 } 102 103 output.putAll(mergedValues); 104 return output.build(); 105 } 106 concatenateArrays(Object array1, Object array2)107 private Object concatenateArrays(Object array1, Object array2) { 108 int length1 = Array.getLength(array1); 109 int length2 = Array.getLength(array2); 110 Object newArray = Array.newInstance(array1.getClass().getComponentType(), 111 length1 + length2); 112 System.arraycopy(array1, 0, newArray, 0, length1); 113 System.arraycopy(array2, 0, newArray, length1, length2); 114 return newArray; 115 } 116 concatenateNonArrays(Object obj1, Object obj2)117 private Object concatenateNonArrays(Object obj1, Object obj2) { 118 Object newArray = Array.newInstance(obj1.getClass(), 2); 119 Array.set(newArray, 0, obj1); 120 Array.set(newArray, 1, obj2); 121 return newArray; 122 } 123 concatenateArrayAndNonArray(Object array, Object obj)124 private Object concatenateArrayAndNonArray(Object array, Object obj) { 125 int arrayLength = Array.getLength(array); 126 Object newArray = Array.newInstance(obj.getClass(), arrayLength + 1); 127 System.arraycopy(array, 0, newArray, 0, arrayLength); 128 Array.set(newArray, arrayLength, obj); 129 return newArray; 130 } 131 createArrayFor(Object obj)132 private Object createArrayFor(Object obj) { 133 Object newArray = Array.newInstance(obj.getClass(), 1); 134 Array.set(newArray, 0, obj); 135 return newArray; 136 } 137 } 138