Home | History | Annotate | Download | only in work
      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
     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 
    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 
    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 
    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 
    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