blob: 26150eb4fad994276b7b30af76e9ccba6284c607 [file] [log] [blame]
package org.robolectric.res;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import org.robolectric.util.Logger;
public class ResourceTableFactory {
/**
* Builds an Android framework resource table in the "android" package space.
*/
public PackageResourceTable newFrameworkResourceTable(ResourcePath resourcePath) {
PackageResourceTable resourceTable = new PackageResourceTable("android");
if (resourcePath.getRClass() != null) {
addRClassValues(resourceTable, resourcePath.getRClass());
addMissingStyleableAttributes(resourceTable, resourcePath.getRClass());
}
if (resourcePath.getInternalRClass() != null) {
addRClassValues(resourceTable, resourcePath.getInternalRClass());
addMissingStyleableAttributes(resourceTable, resourcePath.getInternalRClass());
}
parseResourceFiles(resourcePath, resourceTable);
return resourceTable;
}
/**
* Creates an application resource table which can be constructed with multiple resources paths representing
* overlayed resource libraries.
*/
public PackageResourceTable newResourceTable(String packageName, ResourcePath... resourcePaths) {
PackageResourceTable resourceTable = new PackageResourceTable(packageName);
for (ResourcePath resourcePath : resourcePaths) {
if (resourcePath.getRClass() != null) {
addRClassValues(resourceTable, resourcePath.getRClass());
}
}
for (ResourcePath resourcePath : resourcePaths) {
parseResourceFiles(resourcePath, resourceTable);
}
return resourceTable;
}
private void addRClassValues(PackageResourceTable resourceTable, Class<?> rClass) {
for (Class innerClass : rClass.getClasses()) {
String resourceType = innerClass.getSimpleName();
if (!resourceType.equals("styleable")) {
for (Field field : innerClass.getDeclaredFields()) {
if (field.getType().equals(Integer.TYPE) && Modifier.isStatic(field.getModifiers())) {
int id;
try {
id = field.getInt(null);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
String resourceName = field.getName();
resourceTable.addResource(id, resourceType, resourceName);
}
}
}
}
}
/**
* Check the stylable elements. Not for aapt generated R files but for framework R files it is possible to
* have attributes in the styleable array for which there is no corresponding R.attr field.
*/
private void addMissingStyleableAttributes(PackageResourceTable resourceTable, Class<?> rClass) {
for (Class innerClass : rClass.getClasses()) {
if (innerClass.getSimpleName().equals("styleable")) {
String styleableName = null; // Current styleable name
int[] styleableArray = null; // Current styleable value array or references
for (Field field : innerClass.getDeclaredFields()) {
if (field.getType().equals(int[].class) && Modifier.isStatic(field.getModifiers())) {
styleableName = field.getName();
try {
styleableArray = (int[]) (field.get(null));
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
} else if (field.getType().equals(Integer.TYPE) && Modifier.isStatic(field.getModifiers())) {
String attributeName = field.getName().substring(styleableName.length() + 1);
try {
int styleableIndex = field.getInt(null);
int attributeResId = styleableArray[styleableIndex];
resourceTable.addResource(attributeResId, "attr", attributeName);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
}
}
}
private void parseResourceFiles(ResourcePath resourcePath, PackageResourceTable resourceTable) {
if (!resourcePath.hasResources()) {
Logger.debug("No resources for %s", resourceTable.getPackageName());
return;
}
Logger.debug("Loading resources for %s from %s...", resourceTable.getPackageName(), resourcePath.getResourceBase());
try {
new StaxDocumentLoader(resourceTable.getPackageName(), resourcePath.getResourceBase(),
new NodeHandler()
.addHandler("resources", new NodeHandler()
.addHandler("bool", new StaxValueLoader(resourceTable, "bool", ResType.BOOLEAN))
.addHandler("item[@type='bool']", new StaxValueLoader(resourceTable, "bool", ResType.BOOLEAN))
.addHandler("color", new StaxValueLoader(resourceTable, "color", ResType.COLOR))
.addHandler("item[@type='color']", new StaxValueLoader(resourceTable, "color", ResType.COLOR))
.addHandler("drawable", new StaxValueLoader(resourceTable, "drawable", ResType.DRAWABLE))
.addHandler("item[@type='drawable']", new StaxValueLoader(resourceTable, "drawable", ResType.DRAWABLE))
.addHandler("item[@type='mipmap']", new StaxValueLoader(resourceTable, "mipmap", ResType.DRAWABLE))
.addHandler("dimen", new StaxValueLoader(resourceTable, "dimen", ResType.DIMEN))
.addHandler("item[@type='dimen']", new StaxValueLoader(resourceTable, "dimen", ResType.DIMEN))
.addHandler("integer", new StaxValueLoader(resourceTable, "integer", ResType.INTEGER))
.addHandler("item[@type='integer']", new StaxValueLoader(resourceTable, "integer", ResType.INTEGER))
.addHandler("integer-array", new StaxArrayLoader(resourceTable, "array", ResType.INTEGER_ARRAY, ResType.INTEGER))
.addHandler("fraction", new StaxValueLoader(resourceTable, "fraction", ResType.FRACTION))
.addHandler("item[@type='fraction']", new StaxValueLoader(resourceTable, "fraction", ResType.FRACTION))
.addHandler("item[@type='layout']", new StaxValueLoader(resourceTable, "layout", ResType.LAYOUT))
.addHandler("plurals", new StaxPluralsLoader(resourceTable, "plurals", ResType.CHAR_SEQUENCE))
.addHandler("string", new StaxValueLoader(resourceTable, "string", ResType.CHAR_SEQUENCE))
.addHandler("item[@type='string']", new StaxValueLoader(resourceTable, "string", ResType.CHAR_SEQUENCE))
.addHandler("string-array", new StaxArrayLoader(resourceTable, "array", ResType.CHAR_SEQUENCE_ARRAY, ResType.CHAR_SEQUENCE))
.addHandler("array", new StaxArrayLoader(resourceTable, "array", ResType.TYPED_ARRAY, null))
.addHandler("id", new StaxValueLoader(resourceTable, "id", ResType.CHAR_SEQUENCE))
.addHandler("item[@type='id']", new StaxValueLoader(resourceTable, "id", ResType.CHAR_SEQUENCE))
.addHandler("attr", new StaxAttrLoader(resourceTable, "attr", ResType.ATTR_DATA))
.addHandler("declare-styleable", new NodeHandler()
.addHandler("attr", new StaxAttrLoader(resourceTable, "attr", ResType.ATTR_DATA))
)
.addHandler("style", new StaxStyleLoader(resourceTable, "style", ResType.STYLE))
)).load("values");
loadOpaque(resourcePath, resourceTable, "layout", ResType.LAYOUT);
loadOpaque(resourcePath, resourceTable, "menu", ResType.LAYOUT);
loadOpaque(resourcePath, resourceTable, "drawable", ResType.DRAWABLE);
loadOpaque(resourcePath, resourceTable, "mipmap", ResType.DRAWABLE);
loadOpaque(resourcePath, resourceTable, "anim", ResType.LAYOUT);
loadOpaque(resourcePath, resourceTable, "animator", ResType.LAYOUT);
loadOpaque(resourcePath, resourceTable, "color", ResType.COLOR_STATE_LIST);
loadOpaque(resourcePath, resourceTable, "xml", ResType.LAYOUT);
loadOpaque(resourcePath, resourceTable, "transition", ResType.LAYOUT);
loadOpaque(resourcePath, resourceTable, "interpolator", ResType.LAYOUT);
new DrawableResourceLoader(resourceTable).findDrawableResources(resourcePath);
new RawResourceLoader(resourcePath).loadTo(resourceTable);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void loadOpaque(ResourcePath resourcePath, final PackageResourceTable resourceTable, final String type, final ResType resType) {
new DocumentLoader(resourceTable.getPackageName(), resourcePath.getResourceBase()) {
@Override
protected void loadResourceXmlFile(XmlContext xmlContext) {
resourceTable.addResource(type, xmlContext.getXmlFile().getBaseName(),
new FileTypedResource(xmlContext.getXmlFile(), resType, xmlContext));
}
}.load(type);
}
}