in designer/src/com/android/tools/idea/uibuilder/model/LayoutParamsManager.java [382:519]
static boolean setAttribute(@Nullable AttributeDefinition attributeDefinition,
@NotNull Object layoutParams,
@NotNull String attributeName,
@Nullable String value,
@NotNull Module module,
@NotNull Configuration configuration) {
// Try to get the types from the attribute definition
EnumSet<AttributeFormat> inferredTypes =
attributeDefinition != null && !attributeDefinition.getFormats().isEmpty()
? EnumSet.copyOf(attributeDefinition.getFormats())
: EnumSet.noneOf(AttributeFormat.class);
if (value != null &&
(value.startsWith(SdkConstants.PREFIX_RESOURCE_REF) || value.startsWith(SdkConstants.PREFIX_THEME_REF)) &&
configuration.getResourceResolver() != null) {
// This is a reference so we resolve the actual value and we try to infer the type from the given reference type
ResourceValue resourceValue = configuration.getResourceResolver().findResValue(value, false);
if (resourceValue != null) {
value = resourceValue.getValue();
// Try to use the reference to infer the type
//noinspection EnumSwitchStatementWhichMissesCases
switch (resourceValue.getResourceType()) {
case INTEGER:
case ID:
case DIMEN:
inferredTypes.add(AttributeFormat.INTEGER);
break;
case FRACTION:
inferredTypes.add(AttributeFormat.FLOAT);
break;
}
if (resourceValue.getResourceType() == ID) {
// TODO: Remove this wrapping/unwrapping
value = String.valueOf(StudioResourceIdManager.get(module).getOrGenerateId(resourceValue.asReference()));
}
}
}
// Now we have a value and an attributeName. We now try to map the given attributeName to the field in the LayoutParams that
// stores its value.
MappedField mappedField = mapField(layoutParams, attributeName);
if (inferredTypes.isEmpty()) {
// If we don't know the type yet, use the field type.
inferredTypes.addAll(mappedField.type);
}
// If we still don't have a type, we will now try to infer the type from:
// 1. The value (ex. if it contains "px" or "dp", we know it's a dimension
// 2. The field type in the LayoutParams class
// 3. Lastly, if we do not have a better option, we try to infer the value from the default value in the class
if (inferredTypes.isEmpty()) {
inferredTypes.addAll(inferTypeFromValue(value));
}
if (inferredTypes.isEmpty()) {
inferredTypes.addAll(inferTypeFromField(layoutParams, mappedField));
}
Object defaultValue = null;
try {
defaultValue = getDefaultValue(layoutParams, mappedField);
} catch (NoSuchElementException ignore) {
}
if (defaultValue != null && inferredTypes.isEmpty()) {
inferredTypes.addAll(attributeFormatFromType(defaultValue.getClass()));
}
if (value == null) {
return setField(layoutParams, mappedField, defaultValue);
}
else {
boolean fieldSet = false;
for (AttributeFormat type : inferredTypes) {
switch (type) {
case DIMENSION:
fieldSet = setField(layoutParams, mappedField, getDimensionValue(value, configuration));
break;
case INTEGER:
try {
fieldSet = setField(layoutParams, mappedField, Integer.parseInt(value));
}
catch (NumberFormatException e) {
fieldSet = false;
}
break;
case STRING:
fieldSet = setField(layoutParams, mappedField, value);
break;
case BOOLEAN:
fieldSet = setField(layoutParams, mappedField, Boolean.parseBoolean(value));
break;
case FLOAT:
try {
fieldSet = setField(layoutParams, mappedField, Float.parseFloat(value));
}
catch (NumberFormatException e) {
fieldSet = false;
}
break;
case ENUM: {
Integer intValue = attributeDefinition != null ? attributeDefinition.getValueMapping(value) : null;
if (intValue != null) {
fieldSet = setField(layoutParams, mappedField, intValue);
}
}
break;
case FLAGS: {
if (attributeDefinition == null) {
continue;
}
OptionalInt flagValue = Splitter.on('|').splitToList(value).stream()
.map(StringUtil::trim)
.map(attributeDefinition::getValueMapping)
.filter(Objects::nonNull)
.mapToInt(Integer::intValue)
.reduce((a, b) -> a | b);
if (flagValue.isPresent()) {
fieldSet = setField(layoutParams, mappedField, flagValue.getAsInt());
}
}
break;
default:
// Couldn't be applied. If there are more types, try the rest
}
if (fieldSet) {
return true;
}
}
return false;
}
}