blob: b11067ddf96fe75ee8d3e74d3e1f10b56069df8b [file] [log] [blame]
package org.robolectric.shadows;
import android.util.TypedValue;
import org.robolectric.res.AttrData;
import org.robolectric.res.FsFile;
import org.robolectric.res.ResType;
import org.robolectric.res.TypedResource;
import org.robolectric.util.Util;
import java.util.List;
public class Converter<T> {
private static int nextStringCookie = 0xbaaa5;
synchronized static int getNextStringCookie() {
return nextStringCookie++;
}
static Converter getConverterFor(AttrData attrData, String type) {
switch (type) {
case "enum":
return new EnumConverter(attrData);
case "flag":
return new FlagConverter(attrData);
case "boolean":
return new FromBoolean();
case "color":
return new FromColor();
case "dimension":
return new FromDimen();
case "float":
return new FromFloat();
case "integer":
return new FromInt();
case "string":
return new FromCharSequence();
case "fraction":
return new FromFraction();
default:
throw new UnsupportedOperationException("DataType not supported: " + type);
}
}
// TODO: Handle 'anim' resources
public static Converter getConverter(ResType resType) {
switch (resType) {
case ATTR_DATA:
return new FromAttrData();
case BOOLEAN:
return new FromBoolean();
case CHAR_SEQUENCE:
return new FromCharSequence();
case COLOR:
case DRAWABLE:
return new FromColor();
case COLOR_STATE_LIST:
case LAYOUT:
return new FromFilePath();
case DIMEN:
return new FromDimen();
case FILE:
return new FromFile();
case FLOAT:
return new FromFloat();
case INTEGER:
return new FromInt();
case FRACTION:
return new FromFraction();
case CHAR_SEQUENCE_ARRAY:
case INTEGER_ARRAY:
case TYPED_ARRAY:
return new FromArray();
case STYLE:
return new Converter();
default:
throw new UnsupportedOperationException("can't convert from " + resType.name());
}
}
public CharSequence asCharSequence(TypedResource typedResource) {
return typedResource.asString();
}
public int asInt(TypedResource typedResource) {
throw cantDo("asInt");
}
public List<TypedResource> getItems(TypedResource typedResource) {
throw cantDo("getItems");
}
public boolean fillTypedValue(T data, TypedValue typedValue) {
return false;
}
private UnsupportedOperationException cantDo(String operation) {
return new UnsupportedOperationException(getClass().getName() + " doesn't support " + operation);
}
public static class FromAttrData extends Converter<AttrData> {
@Override
public CharSequence asCharSequence(TypedResource typedResource) {
return typedResource.asString();
}
@Override
public boolean fillTypedValue(AttrData data, TypedValue typedValue) {
typedValue.type = TypedValue.TYPE_STRING;
return false;
}
}
public static class FromCharSequence extends Converter<String> {
@Override
public CharSequence asCharSequence(TypedResource typedResource) {
return typedResource.asString().trim();
}
@Override
public int asInt(TypedResource typedResource) {
return convertInt(typedResource.asString().trim());
}
@Override
public boolean fillTypedValue(String data, TypedValue typedValue) {
typedValue.type = TypedValue.TYPE_STRING;
typedValue.data = 0;
typedValue.assetCookie = getNextStringCookie();
typedValue.string = data;
return true;
}
}
public static class FromColor extends Converter<String> {
@Override
public boolean fillTypedValue(String data, TypedValue typedValue) {
try {
typedValue.type = TypedValue.TYPE_INT_COLOR_ARGB8;
typedValue.data = ResourceHelper.getColor(data);
typedValue.assetCookie = 0;
typedValue.string = null;
return true;
} catch (NumberFormatException nfe) {
return false;
}
}
@Override
public int asInt(TypedResource typedResource) {
return ResourceHelper.getColor(typedResource.asString().trim());
}
}
public static class FromFilePath extends Converter<String> {
@Override
public boolean fillTypedValue(String data, TypedValue typedValue) {
typedValue.type = TypedValue.TYPE_STRING;
typedValue.data = 0;
typedValue.string = data;
typedValue.assetCookie = getNextStringCookie();
return true;
}
}
public static class FromArray extends Converter {
@Override
public List<TypedResource> getItems(TypedResource typedResource) {
return (List<TypedResource>) typedResource.getData();
}
}
private static class FromInt extends Converter<String> {
@Override
public boolean fillTypedValue(String data, TypedValue typedValue) {
try {
typedValue.type = TypedValue.TYPE_INT_HEX;
typedValue.data = convertInt(data);
typedValue.assetCookie = 0;
typedValue.string = null;
return true;
} catch (NumberFormatException nfe) {
return false;
}
}
@Override
public int asInt(TypedResource typedResource) {
return convertInt(typedResource.asString().trim());
}
}
private static class FromFraction extends Converter<String> {
@Override
public boolean fillTypedValue(String data, TypedValue typedValue) {
return ResourceHelper.parseFloatAttribute(null, data, typedValue, false);
}
}
private static class FromFile extends Converter<FsFile> {
@Override
public boolean fillTypedValue(FsFile data, TypedValue typedValue) {
typedValue.type = TypedValue.TYPE_STRING;
typedValue.data = 0;
typedValue.string = data.getPath();
typedValue.assetCookie = getNextStringCookie();
return true;
}
}
private static class FromFloat extends Converter<String> {
@Override
public boolean fillTypedValue(String data, TypedValue typedValue) {
return ResourceHelper.parseFloatAttribute(null, data, typedValue, false);
}
}
private static class FromBoolean extends Converter<String> {
@Override
public boolean fillTypedValue(String data, TypedValue typedValue) {
typedValue.type = TypedValue.TYPE_INT_BOOLEAN;
typedValue.assetCookie = 0;
typedValue.string = null;
if ("true".equalsIgnoreCase(data)) {
typedValue.data = 1;
} else if ("false".equalsIgnoreCase(data)) {
typedValue.data = 0;
} else {
try {
int intValue = Integer.parseInt(data);
typedValue.data = intValue == 0 ? 0 : 1;
} catch (NumberFormatException e) {
return false;
}
}
return true;
}
}
private static class FromDimen extends Converter<String> {
@Override
public boolean fillTypedValue(String data, TypedValue typedValue) {
return ResourceHelper.parseFloatAttribute(null, data, typedValue, false);
}
}
private static int convertInt(String rawValue) {
try {
// Decode into long, because there are some large hex values in the android resource files
// (e.g. config_notificationsBatteryLowARGB = 0xFFFF0000 in sdk 14).
// Integer.decode() does not support large, i.e. negative values in hex numbers.
// try parsing decimal number
return (int) Long.parseLong(rawValue);
} catch (NumberFormatException nfe) {
// try parsing hex number
return Long.decode(rawValue).intValue();
}
}
private static class EnumConverter extends EnumOrFlagConverter {
public EnumConverter(AttrData attrData) {
super(attrData);
}
@Override
public boolean fillTypedValue(String data, TypedValue typedValue) {
try {
typedValue.type = TypedValue.TYPE_INT_HEX;
typedValue.data = findValueFor(data);
typedValue.assetCookie = 0;
typedValue.string = null;
return true;
} catch (Exception e) {
return false;
}
}
}
private static class FlagConverter extends EnumOrFlagConverter {
public FlagConverter(AttrData attrData) {
super(attrData);
}
@Override
public boolean fillTypedValue(String data, TypedValue typedValue) {
try {
int flags = 0;
for (String key : data.split("\\|")) {
flags |= findValueFor(key);
}
typedValue.type = TypedValue.TYPE_INT_HEX;
typedValue.data = flags;
typedValue.assetCookie = 0;
typedValue.string = null;
return true;
} catch (Exception e) {
return false;
}
}
}
private static class EnumOrFlagConverter extends Converter<String> {
private final AttrData attrData;
public EnumOrFlagConverter(AttrData attrData) {
this.attrData = attrData;
}
protected int findValueFor(String key) {
String valueFor = attrData.getValueFor(key);
if (valueFor == null) {
// Maybe they have passed in the value directly, rather than the name.
if (attrData.isValue(key)) {
valueFor = key;
} else {
throw new RuntimeException("no value found for " + key);
}
}
return Util.parseInt(valueFor);
}
}
}