1/* 2 * Copyright (C) 2017 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 17package android.os; 18 19import java.util.Arrays; 20import java.util.Collection; 21import java.util.Iterator; 22import java.util.List; 23import java.util.Map; 24import java.util.Objects; 25import java.util.stream.IntStream; 26 27/** @hide */ 28public class HidlSupport { 29 /** 30 * Similar to Objects.deepEquals, but also take care of lists. 31 * Two objects of HIDL types are considered equal if: 32 * 1. Both null 33 * 2. Both non-null, and of the same class, and: 34 * 2.1 Both are primitive arrays / enum arrays, elements are equal using == check 35 * 2.2 Both are object arrays, elements are checked recursively 36 * 2.3 Both are Lists, elements are checked recursively 37 * 2.4 (If both are collections other than lists or maps, throw an error) 38 * 2.5 lft.equals(rgt) returns true 39 */ 40 public static boolean deepEquals(Object lft, Object rgt) { 41 if (lft == rgt) { 42 return true; 43 } 44 if (lft == null || rgt == null) { 45 return false; 46 } 47 48 Class<?> lftClazz = lft.getClass(); 49 Class<?> rgtClazz = rgt.getClass(); 50 if (lftClazz != rgtClazz) { 51 return false; 52 } 53 54 if (lftClazz.isArray()) { 55 Class<?> lftElementType = lftClazz.getComponentType(); 56 if (lftElementType != rgtClazz.getComponentType()) { 57 return false; 58 } 59 60 if (lftElementType.isPrimitive()) { 61 return Objects.deepEquals(lft, rgt); 62 } 63 64 Object[] lftArray = (Object[])lft; 65 Object[] rgtArray = (Object[])rgt; 66 return (lftArray.length == rgtArray.length) && 67 IntStream.range(0, lftArray.length).allMatch( 68 i -> deepEquals(lftArray[i], rgtArray[i])); 69 } 70 71 if (lft instanceof List<?>) { 72 List<Object> lftList = (List<Object>)lft; 73 List<Object> rgtList = (List<Object>)rgt; 74 if (lftList.size() != rgtList.size()) { 75 return false; 76 } 77 78 Iterator<Object> lftIter = lftList.iterator(); 79 return rgtList.stream() 80 .allMatch(rgtElement -> deepEquals(lftIter.next(), rgtElement)); 81 } 82 83 throwErrorIfUnsupportedType(lft); 84 85 return lft.equals(rgt); 86 } 87 88 /** 89 * Similar to Arrays.deepHashCode, but also take care of lists. 90 */ 91 public static int deepHashCode(Object o) { 92 if (o == null) { 93 return 0; 94 } 95 Class<?> clazz = o.getClass(); 96 if (clazz.isArray()) { 97 Class<?> elementType = clazz.getComponentType(); 98 if (elementType.isPrimitive()) { 99 return primitiveArrayHashCode(o); 100 } 101 return Arrays.hashCode(Arrays.stream((Object[])o) 102 .mapToInt(element -> deepHashCode(element)) 103 .toArray()); 104 } 105 106 if (o instanceof List<?>) { 107 return Arrays.hashCode(((List<Object>)o).stream() 108 .mapToInt(element -> deepHashCode(element)) 109 .toArray()); 110 } 111 112 throwErrorIfUnsupportedType(o); 113 114 return o.hashCode(); 115 } 116 117 private static void throwErrorIfUnsupportedType(Object o) { 118 if (o instanceof Collection<?> && !(o instanceof List<?>)) { 119 throw new UnsupportedOperationException( 120 "Cannot check equality on collections other than lists: " + 121 o.getClass().getName()); 122 } 123 124 if (o instanceof Map<?, ?>) { 125 throw new UnsupportedOperationException( 126 "Cannot check equality on maps"); 127 } 128 } 129 130 private static int primitiveArrayHashCode(Object o) { 131 Class<?> elementType = o.getClass().getComponentType(); 132 if (elementType == boolean.class) { 133 return Arrays.hashCode(((boolean[])o)); 134 } 135 if (elementType == byte.class) { 136 return Arrays.hashCode(((byte[])o)); 137 } 138 if (elementType == char.class) { 139 return Arrays.hashCode(((char[])o)); 140 } 141 if (elementType == double.class) { 142 return Arrays.hashCode(((double[])o)); 143 } 144 if (elementType == float.class) { 145 return Arrays.hashCode(((float[])o)); 146 } 147 if (elementType == int.class) { 148 return Arrays.hashCode(((int[])o)); 149 } 150 if (elementType == long.class) { 151 return Arrays.hashCode(((long[])o)); 152 } 153 if (elementType == short.class) { 154 return Arrays.hashCode(((short[])o)); 155 } 156 // Should not reach here. 157 throw new UnsupportedOperationException(); 158 } 159} 160