diff options
author | Ashley Rose <ashleyrose@google.com> | 2019-01-25 15:41:29 -0500 |
---|---|---|
committer | Ashley Rose <ashleyrose@google.com> | 2019-01-28 18:21:30 +0000 |
commit | 0b671da4b19c7bd36ac7de01c65912fd09d2f36f (patch) | |
tree | b4e3688893296eb690b07cf77eedfdf78d182f01 /tools/processors | |
parent | e8d1eaa1dcc8308c5959a83ce483f3194a40cd82 (diff) |
Annotation processing for int enum and flag mapping
Bug: 117616612
Test: atest --host view-inspector-annotation-processor-test
Change-Id: I791ffd8ce6bf6ec3ba408bb2a781fd91871b0ed6
Diffstat (limited to 'tools/processors')
13 files changed, 653 insertions, 64 deletions
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/AnnotationUtils.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/AnnotationUtils.java index fc4cd01a5a2a..2690ee87f5b2 100644 --- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/AnnotationUtils.java +++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/AnnotationUtils.java @@ -16,8 +16,12 @@ package android.processor.view.inspector; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.stream.Collectors; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.AnnotationMirror; @@ -102,6 +106,83 @@ final class AnnotationUtils { } /** + * Get a typed list of values for an annotation array property by name. + * + * The returned list will be empty if the value was left at the default. + * + * @param propertyName The name of the property to search for + * @param valueClass The expected class of the property value + * @param element The element the annotation is on, used for exceptions + * @param annotationMirror An annotation mirror to search for the property + * @param <T> The type of the value + * @return A list containing the requested types + */ + <T> List<T> typedArrayValuesByName( + String propertyName, + Class<T> valueClass, + Element element, + AnnotationMirror annotationMirror) { + return untypedArrayValuesByName(propertyName, element, annotationMirror) + .stream() + .map(annotationValue -> { + final Object value = annotationValue.getValue(); + + if (value == null) { + throw new ProcessingException( + "Unexpected null in array.", + element, + annotationMirror, + annotationValue); + } + + if (valueClass.isAssignableFrom(value.getClass())) { + return valueClass.cast(value); + } else { + throw new ProcessingException( + String.format( + "Expected array entry to have type %s, but got %s.", + valueClass.getCanonicalName(), + value.getClass().getCanonicalName()), + element, + annotationMirror, + annotationValue); + } + }) + .collect(Collectors.toList()); + } + + /** + * Get a list of values for an annotation array property by name. + * + * @param propertyName The name of the property to search for + * @param element The element the annotation is on, used for exceptions + * @param annotationMirror An annotation mirror to search for the property + * @return A list of annotation values, empty list if none found + */ + List<AnnotationValue> untypedArrayValuesByName( + String propertyName, + Element element, + AnnotationMirror annotationMirror) { + return typedValueByName(propertyName, List.class, element, annotationMirror) + .map(untypedValues -> { + List<AnnotationValue> typedValues = new ArrayList<>(untypedValues.size()); + + for (Object untypedValue : untypedValues) { + if (untypedValue instanceof AnnotationValue) { + typedValues.add((AnnotationValue) untypedValue); + } else { + throw new ProcessingException( + "Unable to convert array entry to AnnotationValue", + element, + annotationMirror); + } + } + + return typedValues; + }).orElseGet(Collections::emptyList); + } + + /** * Get the typed value of an annotation property by name. * * The returned optional will be empty if the value was left at the default, or if the value diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java index f1ebb87fed4d..6f588935c44c 100644 --- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java +++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java @@ -19,7 +19,9 @@ package android.processor.view.inspector; import com.squareup.javapoet.ClassName; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -92,6 +94,8 @@ public final class InspectableClassModel { private final Type mType; private boolean mAttributeIdInferrableFromR = true; private int mAttributeId = 0; + private List<IntEnumEntry> mIntEnumEntries; + private List<IntFlagEntry> mIntFlagEntries; public Property(String name, String getter, Type type) { mName = Objects.requireNonNull(name, "Name must not be null"); @@ -133,6 +137,40 @@ public final class InspectableClassModel { return mType; } + /** + * Get the mapping for an {@code int} enumeration, if present. + * + * @return A list of mapping entries, empty if absent + */ + public List<IntEnumEntry> getIntEnumEntries() { + if (mIntEnumEntries != null) { + return mIntEnumEntries; + } else { + return Collections.emptyList(); + } + } + + public void setIntEnumEntries(List<IntEnumEntry> intEnumEntries) { + mIntEnumEntries = intEnumEntries; + } + + /** + * Get the mapping of {@code int} flags, if present. + * + * @return A list of mapping entries, empty if absent + */ + public List<IntFlagEntry> getIntFlagEntries() { + if (mIntFlagEntries != null) { + return mIntFlagEntries; + } else { + return Collections.emptyList(); + } + } + + public void setIntFlagEntries(List<IntFlagEntry> intFlagEntries) { + mIntFlagEntries = intFlagEntries; + } + public enum Type { /** Primitive or boxed {@code boolean} */ BOOLEAN, @@ -181,6 +219,7 @@ public final class InspectableClassModel { * An enumeration packed into an {@code int}. * * @see android.view.inspector.IntEnumMapping + * @see IntEnumEntry */ INT_ENUM, @@ -188,8 +227,74 @@ public final class InspectableClassModel { * Non-exclusive or partially-exclusive flags packed into an {@code int}. * * @see android.view.inspector.IntFlagMapping + * @see IntFlagEntry */ INT_FLAG } } + + /** + * Model one entry in a int enum mapping. + * + * @see android.view.inspector.IntEnumMapping + */ + public static final class IntEnumEntry { + private final String mName; + private final int mValue; + + public IntEnumEntry(String name, int value) { + mName = Objects.requireNonNull(name, "Name must not be null"); + mValue = value; + } + + public String getName() { + return mName; + } + + public int getValue() { + return mValue; + } + } + + /** + * Model one entry in an int flag mapping. + * + * @see android.view.inspector.IntFlagMapping + */ + public static final class IntFlagEntry { + private final String mName; + private final int mTarget; + private final int mMask; + + public IntFlagEntry(String name, int target, int mask) { + mName = Objects.requireNonNull(name, "Name must not be null"); + mTarget = target; + mMask = mask; + } + + public IntFlagEntry(String name, int target) { + this(name, target, target); + } + + /** + * Determine if this entry has a bitmask. + * + * @return True if the bitmask and target are different, false otherwise + */ + public boolean hasMask() { + return mTarget != mMask; + } + + public String getName() { + return mName; + } + + public int getTarget() { + return mTarget; + } + + public int getMask() { + return mMask; + } + } } diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectablePropertyProcessor.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectablePropertyProcessor.java index f666be7a2a61..42ae890640cb 100644 --- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectablePropertyProcessor.java +++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectablePropertyProcessor.java @@ -16,13 +16,19 @@ package android.processor.view.inspector; +import android.processor.view.inspector.InspectableClassModel.IntEnumEntry; +import android.processor.view.inspector.InspectableClassModel.IntFlagEntry; import android.processor.view.inspector.InspectableClassModel.Property; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.regex.Pattern; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; @@ -63,6 +69,7 @@ public final class InspectablePropertyProcessor implements ModelProcessor { /** * Set of android and androidx annotation qualified names for colors packed into {@code long}. + * * @see android.annotation.ColorLong */ private static final String[] COLOR_LONG_ANNOTATION_NAMES = { @@ -71,7 +78,7 @@ public final class InspectablePropertyProcessor implements ModelProcessor { /** * @param annotationQualifiedName The qualified name of the annotation to process - * @param processingEnv The processing environment from the parent processor + * @param processingEnv The processing environment from the parent processor */ public InspectablePropertyProcessor( String annotationQualifiedName, @@ -109,8 +116,8 @@ public final class InspectablePropertyProcessor implements ModelProcessor { * Check that an element is shaped like a getter. * * @param element An element that hopefully represents a getter - * @throws ProcessingException if the element isn't a getter * @return An {@link ExecutableElement} that represents a getter method. + * @throws ProcessingException if the element isn't a getter */ private ExecutableElement ensureGetter(Element element) { if (element.getKind() != ElementKind.METHOD) { @@ -144,7 +151,7 @@ public final class InspectablePropertyProcessor implements ModelProcessor { throw new ProcessingException( String.format( "Expected a getter method to take no parameters, " - + "but got %d parameters.", + + "but got %d parameters.", method.getParameters().size()), element); } @@ -167,10 +174,10 @@ public final class InspectablePropertyProcessor implements ModelProcessor { /** * Build a {@link Property} from a getter and an inspectable property annotation. * - * @param getter An element representing the getter to build from + * @param getter An element representing the getter to build from * @param annotation A mirror of an inspectable property-shaped annotation - * @throws ProcessingException If the supplied data is invalid and a property cannot be modeled * @return A property for the getter and annotation + * @throws ProcessingException If the supplied data is invalid and a property cannot be modeled */ private Property buildProperty(ExecutableElement getter, AnnotationMirror annotation) { final String name = mAnnotationUtils @@ -190,16 +197,25 @@ public final class InspectablePropertyProcessor implements ModelProcessor { .typedValueByName("attributeId", Integer.class, getter, annotation) .ifPresent(property::setAttributeId); + switch (property.getType()) { + case INT_ENUM: + property.setIntEnumEntries(processEnumMapping(getter, annotation)); + break; + case INT_FLAG: + property.setIntFlagEntries(processFlagMapping(getter, annotation)); + break; + } + return property; } /** * Determine the property type from the annotation, return type, or context clues. * - * @param getter An element representing the getter to build from + * @param getter An element representing the getter to build from * @param annotation A mirror of an inspectable property-shaped annotation * @return The resolved property type - * @throws ProcessingException If the property type cannot be resolved + * @throws ProcessingException If the property type cannot be resolved or is invalid * @see android.view.inspector.InspectableProperty#valueType() */ private Property.Type determinePropertyType( @@ -213,10 +229,62 @@ public final class InspectablePropertyProcessor implements ModelProcessor { final Property.Type returnType = convertReturnTypeToPropertyType(getter); + final boolean hasColor = hasColorAnnotation(getter); + final Optional<AnnotationValue> enumMapping = + mAnnotationUtils.valueByName("enumMapping", annotation); + final Optional<AnnotationValue> flagMapping = + mAnnotationUtils.valueByName("flagMapping", annotation); + + if (returnType != Property.Type.INT) { + enumMapping.ifPresent(value -> { + throw new ProcessingException( + String.format( + "Can only use enumMapping on int types, got %s.", + returnType.toString().toLowerCase()), + getter, + annotation, + value); + }); + flagMapping.ifPresent(value -> { + throw new ProcessingException( + String.format( + "Can only use flagMapping on int types, got %s.", + returnType.toString().toLowerCase()), + getter, + annotation, + value); + }); + } + switch (valueType) { case "INFERRED": - if (hasColorAnnotation(getter)) { + if (hasColor) { + enumMapping.ifPresent(value -> { + throw new ProcessingException( + "Cannot use enumMapping on a color type.", + getter, + annotation, + value); + }); + flagMapping.ifPresent(value -> { + throw new ProcessingException( + "Cannot use flagMapping on a color type.", + getter, + annotation, + value); + }); return Property.Type.COLOR; + } else if (enumMapping.isPresent()) { + flagMapping.ifPresent(value -> { + throw new ProcessingException( + "Cannot use flagMapping and enumMapping simultaneously.", + getter, + annotation, + value); + }); + return Property.Type.INT_ENUM; + } else if (flagMapping.isPresent()) { + return Property.Type.INT_FLAG; } else { return returnType; } @@ -235,17 +303,14 @@ public final class InspectablePropertyProcessor implements ModelProcessor { annotation); } case "GRAVITY": - if (returnType == Property.Type.INT) { - return Property.Type.GRAVITY; - } else { - throw new ProcessingException( - String.format("Gravity must be an integer, got %s", returnType), - getter, - annotation); - } + requirePackedIntToReturnInt("Gravity", returnType, getter, annotation); + return Property.Type.GRAVITY; case "INT_ENUM": + requirePackedIntToReturnInt("IntEnum", returnType, getter, annotation); + return Property.Type.INT_ENUM; case "INT_FLAG": - throw new ProcessingException("Not implemented", getter, annotation); + requirePackedIntToReturnInt("IntFlag", returnType, getter, annotation); + return Property.Type.INT_FLAG; default: throw new ProcessingException( String.format("Unknown value type enumeration value: %s", valueType), @@ -258,8 +323,8 @@ public final class InspectablePropertyProcessor implements ModelProcessor { * Get a property type from the return type of a getter. * * @param getter The getter to extract the return type of - * @throws ProcessingException If the return type is not a primitive or an object * @return The property type returned by the getter + * @throws ProcessingException If the return type is not a primitive or an object */ private Property.Type convertReturnTypeToPropertyType(ExecutableElement getter) { final TypeMirror returnType = getter.getReturnType(); @@ -295,6 +360,31 @@ public final class InspectablePropertyProcessor implements ModelProcessor { } /** + * Require that a value type packed into an integer be on a getter that returns an int. + * + * @param typeName The name of the type to use in the exception + * @param returnType The return type of the getter to check + * @param getter The getter, to use in the exception + * @param annotation The annotation, to use in the exception + * @throws ProcessingException If the return type is not an int + */ + private static void requirePackedIntToReturnInt( + String typeName, + Property.Type returnType, + ExecutableElement getter, + AnnotationMirror annotation) { + if (returnType != Property.Type.INT) { + throw new ProcessingException( + String.format( + "%s can only be defined on a method that returns int, got %s.", + typeName, + returnType.toString().toLowerCase()), + getter, + annotation); + } + } + + /** * Determine if a getter is annotated with color annotation matching its return type. * * Note that an {@code int} return value annotated with {@link android.annotation.ColorLong} is @@ -303,7 +393,6 @@ public final class InspectablePropertyProcessor implements ModelProcessor { * * @param getter The getter to query * @return True if the getter has a color annotation, false otherwise - * */ private boolean hasColorAnnotation(ExecutableElement getter) { switch (unboxType(getter.getReturnType())) { @@ -353,6 +442,117 @@ public final class InspectablePropertyProcessor implements ModelProcessor { } /** + * Build a model of an {@code int} enumeration mapping from annotation values. + * + * This method only handles the one-to-one mapping of mirrors of + * {@link android.view.inspector.InspectableProperty.EnumMap} annotations into + * {@link IntEnumEntry} objects. Further validation should be handled elsewhere + * + * @see android.view.inspector.IntEnumMapping + * @see android.view.inspector.InspectableProperty#enumMapping() + * @param getter The getter of the property, used for exceptions + * @param annotation The {@link android.view.inspector.InspectableProperty} annotation to + * extract enum mapping values from. + * @return A list of int enum entries, in the order specified in source + * @throws ProcessingException if mapping doesn't exist or is invalid + */ + private List<IntEnumEntry> processEnumMapping( + ExecutableElement getter, + AnnotationMirror annotation) { + List<AnnotationMirror> enumAnnotations = mAnnotationUtils.typedArrayValuesByName( + "enumMapping", AnnotationMirror.class, getter, annotation); + List<IntEnumEntry> enumEntries = new ArrayList<>(enumAnnotations.size()); + + if (enumAnnotations.isEmpty()) { + throw new ProcessingException( + "Encountered an empty array for enumMapping", getter, annotation); + } + + for (AnnotationMirror enumAnnotation : enumAnnotations) { + final String name = mAnnotationUtils.typedValueByName( + "name", String.class, getter, enumAnnotation) + .orElseThrow(() -> { + throw new ProcessingException( + "Name is required for @EnumMap", + getter, + enumAnnotation); + }); + + final int value = mAnnotationUtils.typedValueByName( + "value", Integer.class, getter, enumAnnotation) + .orElseThrow(() -> { + throw new ProcessingException( + "Value is required for @EnumMap", + getter, + enumAnnotation); + }); + + enumEntries.add(new IntEnumEntry(name, value)); + } + + return enumEntries; + } + + /** + * Build a model of an {@code int} flag mapping from annotation values. + * + * This method only handles the one-to-one mapping of mirrors of + * {@link android.view.inspector.InspectableProperty.FlagMap} annotations into + * {@link IntFlagEntry} objects. Further validation should be handled elsewhere + * + * @see android.view.inspector.IntFlagMapping + * @see android.view.inspector.InspectableProperty#flagMapping() + * @param getter The getter of the property, used for exceptions + * @param annotation The {@link android.view.inspector.InspectableProperty} annotation to + * extract flag mapping values from. + * @return A list of int flags entries, in the order specified in source + * @throws ProcessingException if mapping doesn't exist or is invalid + */ + private List<IntFlagEntry> processFlagMapping( + ExecutableElement getter, + AnnotationMirror annotation) { + List<AnnotationMirror> flagAnnotations = mAnnotationUtils.typedArrayValuesByName( + "flagMapping", AnnotationMirror.class, getter, annotation); + List<IntFlagEntry> flagEntries = new ArrayList<>(flagAnnotations.size()); + + if (flagAnnotations.isEmpty()) { + throw new ProcessingException( + "Encountered an empty array for flagMapping", getter, annotation); + } + + for (AnnotationMirror flagAnnotation : flagAnnotations) { + final String name = mAnnotationUtils.typedValueByName( + "name", String.class, getter, flagAnnotation) + .orElseThrow(() -> { + throw new ProcessingException( + "Name is required for @FlagMap", + getter, + flagAnnotation); + }); + + final int target = mAnnotationUtils.typedValueByName( + "target", Integer.class, getter, flagAnnotation) + .orElseThrow(() -> { + throw new ProcessingException( + "Target is required for @FlagMap", + getter, + flagAnnotation); + }); + + final Optional<Integer> mask = mAnnotationUtils.typedValueByName( + "mask", Integer.class, getter, flagAnnotation); + + if (mask.isPresent()) { + flagEntries.add(new IntFlagEntry(name, target, mask.get())); + } else { + flagEntries.add(new IntFlagEntry(name, target)); + } + } + + return flagEntries; + } + + /** * Determine if a {@link TypeMirror} is a boxed or unboxed boolean. * * @param type The type mirror to check diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java index dd4d8f54fb68..7b04645e9f44 100644 --- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java +++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java @@ -16,6 +16,8 @@ package android.processor.view.inspector; +import android.processor.view.inspector.InspectableClassModel.IntEnumEntry; +import android.processor.view.inspector.InspectableClassModel.IntFlagEntry; import android.processor.view.inspector.InspectableClassModel.Property; import com.squareup.javapoet.ClassName; @@ -69,6 +71,18 @@ public final class InspectionCompanionGenerator { "android.view.inspector", "PropertyReader"); /** + * The class name of {@link android.view.inspector.IntEnumMapping}. + */ + private static final ClassName INT_ENUM_MAPPING = ClassName.get( + "android.view.inspector", "IntEnumMapping"); + + /** + * The class name of {@link android.view.inspector.IntFlagMapping}. + */ + private static final ClassName INT_FLAG_MAPPING = ClassName.get( + "android.view.inspector", "IntFlagMapping"); + + /** * The {@code mPropertiesMapped} field. */ private static final FieldSpec M_PROPERTIES_MAPPED = FieldSpec @@ -248,13 +262,13 @@ public final class InspectionCompanionGenerator { final MethodSpec.Builder builder = MethodSpec.methodBuilder("readProperties") .addAnnotation(Override.class) .addModifiers(Modifier.PUBLIC) - .addParameter(model.getClassName(), "inspectable") + .addParameter(model.getClassName(), "node") .addParameter(PROPERTY_READER, "propertyReader") .addCode(generatePropertyMapInitializationCheck()); for (PropertyIdField propertyIdField : propertyIdFields) { builder.addStatement( - "propertyReader.read$L($N, inspectable.$L())", + "propertyReader.read$L($N, node.$L())", methodSuffixForPropertyType(propertyIdField.mProperty.getType()), propertyIdField.mFieldSpec, propertyIdField.mProperty.getGetter()); @@ -286,21 +300,22 @@ public final class InspectionCompanionGenerator { if (property.getAttributeId() == ID_NULL) { builder.add("$L", ID_NULL); } else { - builder.add("$L", String.format("0x%08x", property.getAttributeId())); + builder.add("$L", hexLiteral(property.getAttributeId())); } } switch (property.getType()) { case INT_ENUM: - throw new RuntimeException("IntEnumMapping generation not implemented"); + builder.add(",$W"); + builder.add(generateIntEnumMappingBuilder(property.getIntEnumEntries())); + break; case INT_FLAG: - throw new RuntimeException("IntFlagMapping generation not implemented"); - default: - builder.add(")"); + builder.add(",$W"); + builder.add(generateIntFlagMappingBuilder(property.getIntFlagEntries())); break; } - return builder.build(); + return builder.add(")").build(); } /** @@ -327,6 +342,56 @@ public final class InspectionCompanionGenerator { } /** + * Generate an invocation of {@link android.view.inspector.IntEnumMapping.Builder}. + * + * <pre> + * new IntEnumMapping.Builder() + * .addValue("ONE", 1) + * .build() + * </pre> + * + * @return A codeblock containing the an int enum mapping builder + */ + private CodeBlock generateIntEnumMappingBuilder(List<IntEnumEntry> intEnumEntries) { + final ArrayList<IntEnumEntry> sortedEntries = new ArrayList<>(intEnumEntries); + sortedEntries.sort(Comparator.comparing(IntEnumEntry::getValue)); + + final CodeBlock.Builder builder = CodeBlock.builder() + .add("new $T()$>", INT_ENUM_MAPPING.nestedClass("Builder")); + + for (IntEnumEntry entry : sortedEntries) { + builder.add("\n.addValue($S, $L)", entry.getName(), entry.getValue()); + } + + return builder.add("\n.build()$<").build(); + } + + private CodeBlock generateIntFlagMappingBuilder(List<IntFlagEntry> intFlagEntries) { + final ArrayList<IntFlagEntry> sortedEntries = new ArrayList<>(intFlagEntries); + sortedEntries.sort(Comparator.comparing(IntFlagEntry::getName)); + + final CodeBlock.Builder builder = CodeBlock.builder() + .add("new $T()$>", INT_FLAG_MAPPING.nestedClass("Builder")); + + for (IntFlagEntry entry : sortedEntries) { + if (entry.hasMask()) { + builder.add( + "\n.addFlag($S, $L, $L)", + entry.getName(), + hexLiteral(entry.getTarget()), + hexLiteral(entry.getMask())); + } else { + builder.add( + "\n.addFlag($S, $L)", + entry.getName(), + hexLiteral(entry.getTarget())); + } + } + + return builder.add("\n.build()$<").build(); + } + + /** * Generate the final class name for the inspection companion from the model's class name. * * The generated class is added to the same package as the source class. If the class in the @@ -385,6 +450,10 @@ public final class InspectionCompanionGenerator { } } + private static String hexLiteral(int value) { + return String.format("0x%08x", value); + } + /** * Value class that holds a {@link Property} and a {@link FieldSpec} for that property. */ diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/PlatformInspectableProcessor.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/PlatformInspectableProcessor.java index 455f5b08e49e..01d94307f871 100644 --- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/PlatformInspectableProcessor.java +++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/PlatformInspectableProcessor.java @@ -32,6 +32,7 @@ import javax.annotation.processing.SupportedAnnotationTypes; import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; +import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; @@ -118,6 +119,12 @@ public final class PlatformInspectableProcessor extends AbstractProcessor { break; } + final Set<Modifier> classModifiers = classElement.get().getModifiers(); + + if (classModifiers.contains(Modifier.PRIVATE)) { + fail("Enclosing class cannot be private", element); + } + final InspectableClassModel model = modelMap.computeIfAbsent( classElement.get().getQualifiedName().toString(), k -> new InspectableClassModel(ClassName.get(classElement.get()))); diff --git a/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java b/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java index b0775dc77f45..f6d8bb0939db 100644 --- a/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java +++ b/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java @@ -16,11 +16,13 @@ package android.processor.view.inspector; -import android.processor.view.inspector.InspectableClassModel.Property; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertNotNull; -import static junit.framework.Assert.fail; +import android.processor.view.inspector.InspectableClassModel.IntEnumEntry; +import android.processor.view.inspector.InspectableClassModel.IntFlagEntry; +import android.processor.view.inspector.InspectableClassModel.Property; import com.google.common.base.Charsets; import com.google.common.io.Resources; @@ -31,6 +33,7 @@ import org.junit.Test; import java.io.IOException; import java.net.URL; +import java.util.Arrays; import java.util.Optional; /** @@ -40,7 +43,7 @@ public class InspectionCompanionGeneratorTest { private static final String RESOURCE_PATH_TEMPLATE = "android/processor/view/inspector/InspectionCompanionGeneratorTest/%s.java.txt"; private static final ClassName TEST_CLASS_NAME = - ClassName.get("com.android.inspectable", "TestInspectable"); + ClassName.get("com.android.node", "TestNode"); private InspectableClassModel mModel; private InspectionCompanionGenerator mGenerator; @@ -59,7 +62,7 @@ public class InspectionCompanionGeneratorTest { @Test public void testNestedClass() { mModel = new InspectableClassModel( - ClassName.get("com.android.inspectable", "Outer", "Inner")); + ClassName.get("com.android.node", "Outer", "Inner")); assertGeneratedFileEquals("NestedClass"); } @@ -105,6 +108,42 @@ public class InspectionCompanionGeneratorTest { assertGeneratedFileEquals("SuppliedAttributeId"); } + @Test + public void testIntEnum() { + final Property property = new Property( + "intEnumProperty", + "getIntEnumProperty", + Property.Type.INT_ENUM); + + property.setIntEnumEntries(Arrays.asList( + new IntEnumEntry("THREE", 3), + new IntEnumEntry("TWO", 2), + new IntEnumEntry("ONE", 1))); + + mModel.putProperty(property); + + assertGeneratedFileEquals("IntEnum"); + } + + @Test + public void testIntFlag() { + final Property property = new Property( + "intFlag", + "getIntFlag", + Property.Type.INT_FLAG); + + property.setAttributeIdInferrableFromR(false); + property.setIntFlagEntries(Arrays.asList( + new IntFlagEntry("TURBO", 0x1, 0x3), + new IntFlagEntry("OVERDRIVE", 0x2, 0x3), + new IntFlagEntry("WARP", 0x4) + )); + + mModel.putProperty(property); + + assertGeneratedFileEquals("IntFlag"); + } + private Property addProperty(String name, String getter, Property.Type type) { final Property property = new Property(name, getter, type); mModel.putProperty(property); diff --git a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/IntEnum.java.txt b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/IntEnum.java.txt new file mode 100644 index 000000000000..764aa8bfbd63 --- /dev/null +++ b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/IntEnum.java.txt @@ -0,0 +1,45 @@ +package com.android.node; + +import android.R; +import android.view.inspector.InspectionCompanion; +import android.view.inspector.IntEnumMapping; +import android.view.inspector.PropertyMapper; +import android.view.inspector.PropertyReader; +import java.lang.Override; + +/** + * Inspection companion for {@link TestNode}. + * + * Generated by {@link android.processor.view.inspector.InspectionCompanionGenerator} + * on behalf of {@link android.processor.view.inspector.InspectionCompanionGeneratorTest}. + */ +public final class TestNode$$InspectionCompanion implements InspectionCompanion<TestNode> { + /** + * Set by {@link #mapProperties(PropertyMapper)} once properties have been mapped. + */ + private boolean mPropertiesMapped = false; + + /** + * Property ID of {@code intEnumProperty}. + */ + private int mIntEnumPropertyId; + + @Override + public void mapProperties(PropertyMapper propertyMapper) { + mIntEnumPropertyId = propertyMapper.mapIntEnum("intEnumProperty", R.attr.intEnumProperty, + new IntEnumMapping.Builder() + .addValue("ONE", 1) + .addValue("TWO", 2) + .addValue("THREE", 3) + .build()); + mPropertiesMapped = true; + } + + @Override + public void readProperties(TestNode node, PropertyReader propertyReader) { + if (!mPropertiesMapped) { + throw new InspectionCompanion.UninitializedPropertyMapException(); + } + propertyReader.readIntEnum(mIntEnumPropertyId, node.getIntEnumProperty()); + } +} diff --git a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/IntFlag.java.txt b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/IntFlag.java.txt new file mode 100644 index 000000000000..75f281314965 --- /dev/null +++ b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/IntFlag.java.txt @@ -0,0 +1,43 @@ +package com.android.node; + +import android.view.inspector.InspectionCompanion; +import android.view.inspector.IntFlagMapping; +import android.view.inspector.PropertyMapper; +import android.view.inspector.PropertyReader; +import java.lang.Override; + +/** + * Inspection companion for {@link TestNode}. + * + * Generated by {@link android.processor.view.inspector.InspectionCompanionGenerator} + * on behalf of {@link android.processor.view.inspector.InspectionCompanionGeneratorTest}. + */ +public final class TestNode$$InspectionCompanion implements InspectionCompanion<TestNode> { + /** + * Set by {@link #mapProperties(PropertyMapper)} once properties have been mapped. + */ + private boolean mPropertiesMapped = false; + + /** + * Property ID of {@code intFlag}. + */ + private int mIntFlagId; + + @Override + public void mapProperties(PropertyMapper propertyMapper) { + mIntFlagId = propertyMapper.mapIntFlag("intFlag", 0, new IntFlagMapping.Builder() + .addFlag("OVERDRIVE", 0x00000002, 0x00000003) + .addFlag("TURBO", 0x00000001, 0x00000003) + .addFlag("WARP", 0x00000004) + .build()); + mPropertiesMapped = true; + } + + @Override + public void readProperties(TestNode node, PropertyReader propertyReader) { + if (!mPropertiesMapped) { + throw new InspectionCompanion.UninitializedPropertyMapException(); + } + propertyReader.readIntFlag(mIntFlagId, node.getIntFlag()); + } +} diff --git a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NestedClass.java.txt b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NestedClass.java.txt index 2fc242c6cf4c..0cac462fba51 100644 --- a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NestedClass.java.txt +++ b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NestedClass.java.txt @@ -1,4 +1,4 @@ -package com.android.inspectable; +package com.android.node; import android.view.inspector.InspectionCompanion; import android.view.inspector.PropertyMapper; @@ -23,7 +23,7 @@ public final class Outer$Inner$$InspectionCompanion implements InspectionCompani } @Override - public void readProperties(Outer.Inner inspectable, PropertyReader propertyReader) { + public void readProperties(Outer.Inner node, PropertyReader propertyReader) { if (!mPropertiesMapped) { throw new InspectionCompanion.UninitializedPropertyMapException(); } diff --git a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NoAttributeId.java.txt b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NoAttributeId.java.txt index 23d0f7807aa5..ce0f867d5332 100644 --- a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NoAttributeId.java.txt +++ b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NoAttributeId.java.txt @@ -1,4 +1,4 @@ -package com.android.inspectable; +package com.android.node; import android.view.inspector.InspectionCompanion; import android.view.inspector.PropertyMapper; @@ -6,12 +6,12 @@ import android.view.inspector.PropertyReader; import java.lang.Override; /** - * Inspection companion for {@link TestInspectable}. + * Inspection companion for {@link TestNode}. * * Generated by {@link android.processor.view.inspector.InspectionCompanionGenerator} * on behalf of {@link android.processor.view.inspector.InspectionCompanionGeneratorTest}. */ -public final class TestInspectable$$InspectionCompanion implements InspectionCompanion<TestInspectable> { +public final class TestNode$$InspectionCompanion implements InspectionCompanion<TestNode> { /** * Set by {@link #mapProperties(PropertyMapper)} once properties have been mapped. */ @@ -29,10 +29,10 @@ public final class TestInspectable$$InspectionCompanion implements InspectionCom } @Override - public void readProperties(TestInspectable inspectable, PropertyReader propertyReader) { + public void readProperties(TestNode node, PropertyReader propertyReader) { if (!mPropertiesMapped) { throw new InspectionCompanion.UninitializedPropertyMapException(); } - propertyReader.readInt(mNoAttributePropertyId, inspectable.getNoAttributeProperty()); + propertyReader.readInt(mNoAttributePropertyId, node.getNoAttributeProperty()); } } diff --git a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NodeName.java.txt b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NodeName.java.txt index 11425482ce94..f7357fece2bf 100644 --- a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NodeName.java.txt +++ b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NodeName.java.txt @@ -1,4 +1,4 @@ -package com.android.inspectable; +package com.android.node; import android.view.inspector.InspectionCompanion; import android.view.inspector.PropertyMapper; @@ -7,12 +7,12 @@ import java.lang.Override; import java.lang.String; /** - * Inspection companion for {@link TestInspectable}. + * Inspection companion for {@link TestNode}. * * Generated by {@link android.processor.view.inspector.InspectionCompanionGenerator} * on behalf of {@link android.processor.view.inspector.InspectionCompanionGeneratorTest}. */ -public final class TestInspectable$$InspectionCompanion implements InspectionCompanion<TestInspectable> { +public final class TestNode$$InspectionCompanion implements InspectionCompanion<TestNode> { /** * Set by {@link #mapProperties(PropertyMapper)} once properties have been mapped. */ @@ -24,7 +24,7 @@ public final class TestInspectable$$InspectionCompanion implements InspectionCom } @Override - public void readProperties(TestInspectable inspectable, PropertyReader propertyReader) { + public void readProperties(TestNode node, PropertyReader propertyReader) { if (!mPropertiesMapped) { throw new InspectionCompanion.UninitializedPropertyMapException(); } diff --git a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SimpleProperties.java.txt b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SimpleProperties.java.txt index 57eb08041131..dfc1bce8afcc 100644 --- a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SimpleProperties.java.txt +++ b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SimpleProperties.java.txt @@ -1,4 +1,4 @@ -package com.android.inspectable; +package com.android.node; import android.R; import android.view.inspector.InspectionCompanion; @@ -7,12 +7,12 @@ import android.view.inspector.PropertyReader; import java.lang.Override; /** - * Inspection companion for {@link TestInspectable}. + * Inspection companion for {@link TestNode}. * * Generated by {@link android.processor.view.inspector.InspectionCompanionGenerator} * on behalf of {@link android.processor.view.inspector.InspectionCompanionGeneratorTest}. */ -public final class TestInspectable$$InspectionCompanion implements InspectionCompanion<TestInspectable> { +public final class TestNode$$InspectionCompanion implements InspectionCompanion<TestNode> { /** * Set by {@link #mapProperties(PropertyMapper)} once properties have been mapped. */ @@ -90,20 +90,20 @@ public final class TestInspectable$$InspectionCompanion implements InspectionCom } @Override - public void readProperties(TestInspectable inspectable, PropertyReader propertyReader) { + public void readProperties(TestNode node, PropertyReader propertyReader) { if (!mPropertiesMapped) { throw new InspectionCompanion.UninitializedPropertyMapException(); } - propertyReader.readBoolean(mBooleanId, inspectable.getBoolean()); - propertyReader.readByte(mByteId, inspectable.getByte()); - propertyReader.readChar(mCharId, inspectable.getChar()); - propertyReader.readColor(mColorId, inspectable.getColor()); - propertyReader.readDouble(mDoubleId, inspectable.getDouble()); - propertyReader.readFloat(mFloatId, inspectable.getFloat()); - propertyReader.readGravity(mGravityId, inspectable.getGravity()); - propertyReader.readInt(mIntId, inspectable.getInt()); - propertyReader.readLong(mLongId, inspectable.getLong()); - propertyReader.readObject(mObjectId, inspectable.getObject()); - propertyReader.readShort(mShortId, inspectable.getShort()); + propertyReader.readBoolean(mBooleanId, node.getBoolean()); + propertyReader.readByte(mByteId, node.getByte()); + propertyReader.readChar(mCharId, node.getChar()); + propertyReader.readColor(mColorId, node.getColor()); + propertyReader.readDouble(mDoubleId, node.getDouble()); + propertyReader.readFloat(mFloatId, node.getFloat()); + propertyReader.readGravity(mGravityId, node.getGravity()); + propertyReader.readInt(mIntId, node.getInt()); + propertyReader.readLong(mLongId, node.getLong()); + propertyReader.readObject(mObjectId, node.getObject()); + propertyReader.readShort(mShortId, node.getShort()); } } diff --git a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SuppliedAttributeId.java.txt b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SuppliedAttributeId.java.txt index 6b6ce2157481..d72cdd533205 100644 --- a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SuppliedAttributeId.java.txt +++ b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SuppliedAttributeId.java.txt @@ -1,4 +1,4 @@ -package com.android.inspectable; +package com.android.node; import android.view.inspector.InspectionCompanion; import android.view.inspector.PropertyMapper; @@ -6,12 +6,12 @@ import android.view.inspector.PropertyReader; import java.lang.Override; /** - * Inspection companion for {@link TestInspectable}. + * Inspection companion for {@link TestNode}. * * Generated by {@link android.processor.view.inspector.InspectionCompanionGenerator} * on behalf of {@link android.processor.view.inspector.InspectionCompanionGeneratorTest}. */ -public final class TestInspectable$$InspectionCompanion implements InspectionCompanion<TestInspectable> { +public final class TestNode$$InspectionCompanion implements InspectionCompanion<TestNode> { /** * Set by {@link #mapProperties(PropertyMapper)} once properties have been mapped. */ @@ -30,10 +30,10 @@ public final class TestInspectable$$InspectionCompanion implements InspectionCom } @Override - public void readProperties(TestInspectable inspectable, PropertyReader propertyReader) { + public void readProperties(TestNode node, PropertyReader propertyReader) { if (!mPropertiesMapped) { throw new InspectionCompanion.UninitializedPropertyMapException(); } - propertyReader.readInt(mSuppliedAttributePropertyId, inspectable.getSuppliedAttributeProperty()); + propertyReader.readInt(mSuppliedAttributePropertyId, node.getSuppliedAttributeProperty()); } } |