1 /* 2 * Copyright (C) 2023 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 com.android.car.internal.util; 18 19 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.PRIVATE_CONSTRUCTOR; 20 21 import android.annotation.Nullable; 22 import android.util.ArrayMap; 23 import android.util.Slog; 24 import android.util.SparseArray; 25 26 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 27 28 import java.lang.reflect.Field; 29 import java.lang.reflect.Modifier; 30 import java.util.Collection; 31 import java.util.Map; 32 import java.util.concurrent.atomic.AtomicReference; 33 34 /** 35 * Utility class to convert integer constants to and from their value or name. 36 * 37 * @hide 38 */ 39 public final class ConstantDebugUtils { 40 private static final String TAG = ConstantDebugUtils.class.getSimpleName(); 41 private static final AtomicReference<Map<Class<?>, ConstantDebugUtils>> 42 CLAZZ_TO_CONSTANT_DEBUG_UTILS_HOLDER = new AtomicReference<>(); 43 private final Field[] mClazzDeclaredFields; 44 /* 45 * Used to cache the mapping of property names to IDs. This 46 * will be initialized during the first usage. 47 */ 48 private final AtomicReference<ArrayMap<String, Integer>> mNameToValueHolder = 49 new AtomicReference<>(); 50 /* 51 * Used to cache the mapping of property IDs to names. This 52 * will be initialized during the first usage. 53 */ 54 private final AtomicReference<SparseArray<String>> mValueToNameHolder = new AtomicReference<>(); 55 56 @ExcludeFromCodeCoverageGeneratedReport(reason = PRIVATE_CONSTRUCTOR) ConstantDebugUtils(Class<?> clazz)57 private ConstantDebugUtils(Class<?> clazz) { 58 mClazzDeclaredFields = clazz.getDeclaredFields(); 59 } 60 61 /** 62 * Gets the constant's name based on the value for the specified {@code clazz}. Returns 63 * {@code null} if value does not exist. 64 */ 65 @Nullable toName(Class<?> clazz, int value)66 public static String toName(Class<?> clazz, int value) { 67 return cacheClazzToConstantDebugUtilsMapping(clazz).toName(value); 68 } 69 70 /** 71 * Gets the constant's value based on the passed name for the specified {@code clazz}. Returns 72 * {@code null} if name does not exist. 73 */ 74 @Nullable toValue(Class<?> clazz, String name)75 public static Integer toValue(Class<?> clazz, String name) { 76 return cacheClazzToConstantDebugUtilsMapping(clazz).toValue(name); 77 } 78 79 /** 80 * Gets the all the constant values for the specified {@code clazz}. 81 */ getValues(Class<?> clazz)82 public static Collection<Integer> getValues(Class<?> clazz) { 83 return cacheClazzToConstantDebugUtilsMapping( 84 clazz).cacheConstantNameToValueMapping().values(); 85 } 86 cacheClazzToConstantDebugUtilsMapping(Class<?> clazz)87 private static ConstantDebugUtils cacheClazzToConstantDebugUtilsMapping(Class<?> clazz) { 88 89 Map<Class<?>, ConstantDebugUtils> clazzToConstantDebugUtils = 90 CLAZZ_TO_CONSTANT_DEBUG_UTILS_HOLDER.get(); 91 if (clazzToConstantDebugUtils == null || clazzToConstantDebugUtils.get(clazz) == null) { 92 clazzToConstantDebugUtils = getClazzToConstantDebugUtilsMapping( 93 clazzToConstantDebugUtils, clazz); 94 CLAZZ_TO_CONSTANT_DEBUG_UTILS_HOLDER.set(clazzToConstantDebugUtils); 95 } 96 return clazzToConstantDebugUtils.get(clazz); 97 } 98 getClazzToConstantDebugUtilsMapping( @ullable Map<Class<?>, ConstantDebugUtils> clazzToConstantDebugUtils, Class<?> clazz)99 private static Map<Class<?>, ConstantDebugUtils> getClazzToConstantDebugUtilsMapping( 100 @Nullable Map<Class<?>, ConstantDebugUtils> clazzToConstantDebugUtils, Class<?> clazz) { 101 Map<Class<?>, ConstantDebugUtils> outputClazzToConstantDebugsUtils; 102 if (clazzToConstantDebugUtils == null) { 103 outputClazzToConstantDebugsUtils = new ArrayMap<>(); 104 } else { 105 outputClazzToConstantDebugsUtils = new ArrayMap<>(clazzToConstantDebugUtils.size()); 106 outputClazzToConstantDebugsUtils.putAll(clazzToConstantDebugUtils); 107 } 108 outputClazzToConstantDebugsUtils.put(clazz, new ConstantDebugUtils(clazz)); 109 return outputClazzToConstantDebugsUtils; 110 } 111 isIntConstant(Field field)112 private static boolean isIntConstant(Field field) { 113 // We only want public static final int values 114 return field.getType() == int.class && field.getModifiers() == (Modifier.STATIC 115 | Modifier.FINAL | Modifier.PUBLIC); 116 } 117 118 @Nullable toName(int value)119 private String toName(int value) { 120 return cacheConstantValueToNameMapping().get(value); 121 } 122 123 @Nullable toValue(String name)124 private Integer toValue(String name) { 125 return cacheConstantNameToValueMapping().get(name); 126 } 127 cacheConstantNameToValueMapping()128 private ArrayMap<String, Integer> cacheConstantNameToValueMapping() { 129 ArrayMap<String, Integer> nameToValue = mNameToValueHolder.get(); 130 if (nameToValue == null) { 131 nameToValue = getConstantNameToValueMapping(); 132 mNameToValueHolder.compareAndSet(null, nameToValue); 133 } 134 return nameToValue; 135 } 136 cacheConstantValueToNameMapping()137 private SparseArray<String> cacheConstantValueToNameMapping() { 138 SparseArray<String> valueToName = mValueToNameHolder.get(); 139 if (valueToName == null) { 140 valueToName = getConstantValueToNameMapping(); 141 mValueToNameHolder.compareAndSet(null, valueToName); 142 } 143 return valueToName; 144 } 145 146 /** 147 * Creates a mapping property names to their IDs. 148 */ getConstantNameToValueMapping()149 private ArrayMap<String, Integer> getConstantNameToValueMapping() { 150 ArrayMap<String, Integer> constantNameToValue = new ArrayMap<>(); 151 for (int i = 0; i < mClazzDeclaredFields.length; i++) { 152 Field candidateField = mClazzDeclaredFields[i]; 153 try { 154 if (isIntConstant(candidateField)) { 155 constantNameToValue.put(candidateField.getName(), candidateField.getInt(null)); 156 } 157 } catch (IllegalAccessException e) { 158 Slog.wtf(TAG, "Failed trying to find value for " + candidateField.getName(), e); 159 } 160 } 161 return constantNameToValue; 162 } 163 164 /** 165 * Creates a SparseArray mapping constant values to their String representations 166 * directly from this class. 167 */ getConstantValueToNameMapping()168 private SparseArray<String> getConstantValueToNameMapping() { 169 SparseArray<String> constantValueToName = new SparseArray<>(); 170 for (int i = 0; i < mClazzDeclaredFields.length; i++) { 171 Field candidateField = mClazzDeclaredFields[i]; 172 try { 173 if (isIntConstant(candidateField)) { 174 constantValueToName.put(candidateField.getInt(null), candidateField.getName()); 175 } 176 } catch (IllegalAccessException e) { 177 Slog.wtf(TAG, "Failed trying to find value for " + candidateField.getName(), e); 178 } 179 } 180 return constantValueToName; 181 } 182 } 183