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;
}
/**
+ * 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
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;
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");
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,
* An enumeration packed into an {@code int}.
*
* @see android.view.inspector.IntEnumMapping
+ * @see IntEnumEntry
*/
INT_ENUM,
* 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;
+ }
+ }
}
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;
/**
* 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 = {
/**
* @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,
* 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) {
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);
}
/**
* 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
.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(
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;
}
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),
* 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();
}
/**
+ * 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
*
* @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())) {
}
/**
+ * 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
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;
"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
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());
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();
}
/**
}
/**
+ * 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
}
}
+ 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.
*/
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;
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())));
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;
import java.io.IOException;
import java.net.URL;
+import java.util.Arrays;
import java.util.Optional;
/**
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;
@Test
public void testNestedClass() {
mModel = new InspectableClassModel(
- ClassName.get("com.android.inspectable", "Outer", "Inner"));
+ ClassName.get("com.android.node", "Outer", "Inner"));
assertGeneratedFileEquals("NestedClass");
}
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);
--- /dev/null
+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());
+ }
+}
--- /dev/null
+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());
+ }
+}
-package com.android.inspectable;
+package com.android.node;
import android.view.inspector.InspectionCompanion;
import android.view.inspector.PropertyMapper;
}
@Override
- public void readProperties(Outer.Inner inspectable, PropertyReader propertyReader) {
+ public void readProperties(Outer.Inner node, PropertyReader propertyReader) {
if (!mPropertiesMapped) {
throw new InspectionCompanion.UninitializedPropertyMapException();
}
-package com.android.inspectable;
+package com.android.node;
import android.view.inspector.InspectionCompanion;
import android.view.inspector.PropertyMapper;
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.
*/
}
@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());
}
}
-package com.android.inspectable;
+package com.android.node;
import android.view.inspector.InspectionCompanion;
import android.view.inspector.PropertyMapper;
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.
*/
}
@Override
- public void readProperties(TestInspectable inspectable, PropertyReader propertyReader) {
+ public void readProperties(TestNode node, PropertyReader propertyReader) {
if (!mPropertiesMapped) {
throw new InspectionCompanion.UninitializedPropertyMapException();
}
-package com.android.inspectable;
+package com.android.node;
import android.R;
import android.view.inspector.InspectionCompanion;
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.
*/
}
@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());
}
}
-package com.android.inspectable;
+package com.android.node;
import android.view.inspector.InspectionCompanion;
import android.view.inspector.PropertyMapper;
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.
*/
}
@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());
}
}