| /* |
| * Copyright 2018 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.work; |
| |
| import androidx.annotation.NonNull; |
| |
| import java.lang.reflect.Array; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * An {@link InputMerger} that attempts to merge the inputs, creating arrays when necessary. For |
| * each input, we look at each key: |
| * <p><ul> |
| * <li>If this is the first time we encountered the key:</li> |
| * <ul> |
| * <li>If it's an array, put it in the output</li> |
| * <li>If it's a primitive, turn it into a size 1 array and put it in the output</li> |
| * </ul> |
| * <li>Else (we have encountered the key before):</li> |
| * <ul> |
| * <li>If the value type matches the old value type:</li> |
| * <ul> |
| * <li>If they are arrays, concatenate them</li> |
| * <li>If they are primitives, turn them into a size 2 array</li> |
| * </ul> |
| * <li>Else if one is an array and the other is a primitive of that type:</li> |
| * <ul> |
| * <li>Make a longer array and concatenate them</li> |
| * </ul> |
| * <li>Else throw an {@link IllegalArgumentException} because the types don't match</li> |
| * </ul> |
| * </ul> |
| */ |
| public final class ArrayCreatingInputMerger extends InputMerger { |
| |
| @Override |
| public @NonNull Data merge(@NonNull List<Data> inputs) { |
| Data.Builder output = new Data.Builder(); |
| Map<String, Object> mergedValues = new HashMap<>(); |
| |
| for (Data input : inputs) { |
| for (Map.Entry<String, Object> entry : input.getKeyValueMap().entrySet()) { |
| String key = entry.getKey(); |
| Object value = entry.getValue(); |
| Class<?> valueClass = value.getClass(); |
| Object mergedValue; |
| |
| Object existingValue = mergedValues.get(key); |
| if (existingValue == null) { |
| // First time encountering this key. |
| if (valueClass.isArray()) { |
| // Arrays carry over as-is. |
| mergedValue = value; |
| } else { |
| // Primitives get turned into size 1 arrays. |
| mergedValue = createArrayFor(value); |
| } |
| } else { |
| // We've encountered this key before. |
| Class<?> existingValueClass = existingValue.getClass(); |
| |
| if (existingValueClass.equals(valueClass)) { |
| // The classes match; we can merge. |
| if (existingValueClass.isArray()) { |
| mergedValue = concatenateArrays(existingValue, value); |
| } else { |
| mergedValue = concatenateNonArrays(existingValue, value); |
| } |
| } else if (existingValueClass.isArray() |
| && existingValueClass.getComponentType().equals(valueClass)) { |
| // We have an existing array of the same type. |
| mergedValue = concatenateArrayAndNonArray(existingValue, value); |
| } else if (valueClass.isArray() |
| && valueClass.getComponentType().equals(existingValueClass)) { |
| // We have an existing array of the same type. |
| mergedValue = concatenateArrayAndNonArray(value, existingValue); |
| } else { |
| throw new IllegalArgumentException(); |
| } |
| } |
| |
| mergedValues.put(key, mergedValue); |
| } |
| } |
| |
| output.putAll(mergedValues); |
| return output.build(); |
| } |
| |
| private Object concatenateArrays(Object array1, Object array2) { |
| int length1 = Array.getLength(array1); |
| int length2 = Array.getLength(array2); |
| Object newArray = Array.newInstance(array1.getClass().getComponentType(), |
| length1 + length2); |
| System.arraycopy(array1, 0, newArray, 0, length1); |
| System.arraycopy(array2, 0, newArray, length1, length2); |
| return newArray; |
| } |
| |
| private Object concatenateNonArrays(Object obj1, Object obj2) { |
| Object newArray = Array.newInstance(obj1.getClass(), 2); |
| Array.set(newArray, 0, obj1); |
| Array.set(newArray, 1, obj2); |
| return newArray; |
| } |
| |
| private Object concatenateArrayAndNonArray(Object array, Object obj) { |
| int arrayLength = Array.getLength(array); |
| Object newArray = Array.newInstance(obj.getClass(), arrayLength + 1); |
| System.arraycopy(array, 0, newArray, 0, arrayLength); |
| Array.set(newArray, arrayLength, obj); |
| return newArray; |
| } |
| |
| private Object createArrayFor(Object obj) { |
| Object newArray = Array.newInstance(obj.getClass(), 1); |
| Array.set(newArray, 0, obj); |
| return newArray; |
| } |
| } |