• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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