package android.content.res;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.CodeSource;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.regex.MatchResult;
import java.util.regex.Pattern;
import javax.swing.JOptionPane;
import javax.swing.UIManager;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import static com.applang.Util.*;
import static com.applang.Util2.*;
import static com.applang.SwingUtil.*;
public class Resources
{
private static final String TAG = Resources.class.getSimpleName();
private Context context;
public Resources(Context context) {
this.context = context;
}
public static String getSettingsPath() {
return System.getProperty("settings.dir", "");
}
public static String getAbsolutePath(String part, Object...params) {
Constraint constraint = param(Constraint.END, 0, params);
part = strip(Constraint.START, part, PATH_SEP);
File file = fileOf(getSettingsPath(), part);
String path = findFirstFile(file.getParentFile(), constraint, file.getName());
if (notNullOrEmpty(path))
return path;
else
return resourceUrlOf(part, constraint).getFile();
}
public static String[] resourceTypes =
{"array","attr","color","dimen","drawable","id","layout","menu","raw","string","style","asset"};
public static final Pattern XML_RESOURCE_PATTERN =
Pattern.compile("@([\\+]*)((([\\w]+\\.)*[\\w]+):)*(" + join("|", resourceTypes) + ")/([\\w\\.\\*\\?]+)");
public static String[] resourceLocations =
{"res/values/strings.xml", "attr",
"res/values/colors.xml",
"res/values/dimens.xml", // 3
"res/drawable/", "id",
"res/layout/", // 6
"res/menu/",
"res/raw/",
"res/values/strings.xml", // 9
"res/values/styles.xml",
"assets/"};
public static String getRelativePath(int loc, String...parts) {
parts = arrayextend(parts, true, resourceLocations[loc]);
String path = fileOf(parts).getPath();
return PATH_SEP + strip(Constraint.START, path, PATH_SEP);
}
// @[<package_name>:]<resource_type>/<resource_name>
@SuppressWarnings("unchecked")
public <T> T getXMLResourceItem(Object item) {
T value = (T) item;
if (notNullOrEmpty(item)) {
try {
MatchResult m = findFirstIn(stringValueOf(item), XML_RESOURCE_PATTERN);
if (m != null) {
String pkg = m.group(3);
String name = m.group(6), path;
int type = asList(resourceTypes).indexOf(m.group(5));
switch (type) {
default:
value = (T) name;
break;
case 4:
if ("android".equals(pkg)) {
value = (T) getDrawable (-1, pkg, name);
if (value != null)
break;
}
path = fileOf(resourceLocations[type], name).getPath();
path = Resources.getAbsolutePath(path, Constraint.MIDDLE);
if (notNullOrEmpty(path))
value = (T) new Drawable().setImage(path);
break;
case 2:
case 3:
case 9:
case 10:
if ("android".equals(pkg)) {
Job<Class<?>> job = new Job<Class<?>>() {
@SuppressWarnings("unused") // used via reflect
public String value;
public void perform(Class<?> t, Object[] parms) throws Exception {
int id = param_Integer(-1,2,parms);
value = getString(id);
}
};
if (lookup_R(-1, pkg, type, job, null, name)) {
value = (T) job.getClass().getField("value").get(job);
if (value != null)
break;
}
}
path = Resources.getAbsolutePath(resourceLocations[type], Constraint.END);
if (notNullOrEmpty(path))
value = getResourceByName(xmlDocument(new File(path)), name, type);
break;
}
return value;
}
} catch (Exception e) {
Log.e(TAG, "getXMLResourceItem", e);
}
}
return value;
}
public static Element styleElement(Context context, String s) {
if (s.startsWith("@")) {
return context.getResources().getXMLResourceItem(s);
}
return null;
}
public static String textValue(Context context, String s) {
if (s.startsWith("@"))
return context.getResources().getXMLResourceItem(s);
else
return s;
}
public static int colorValue(Context context, String s) {
Object o = context.getResources().getXMLResourceItem(s);
if (o instanceof Integer)
return (Integer) o;
else {
return android.graphics.Color.parseColor(s);
}
}
public static int dimensionalValue(Context context, String s) {
Object o = context.getResources().getXMLResourceItem(s);
if (o instanceof Integer)
return (Integer) o;
else
return toInt(0, stripUnits(stringValueOf(s)));
}
public static boolean booleanValue(Context context, String s) {
Object o = context.getResources().getXMLResourceItem(s);
if (o instanceof String)
return toBool(false, (String) o);
else
return toBool(false, s);
}
// NOTE methods further up do NOT correspond to Android APIs
public String getString(int id, Object...params) {
String pkg = param(context.getPackageName(), 0, params);
final int resourceType = 9;
switch (id) {
case android.R.string.close:
return (String) defaultOptions(JOptionPane.DEFAULT_OPTION).get(0);
case android.R.string.cancel:
return (String) UIManager.get("OptionPane.cancelButtonText");
case android.R.string.ok:
return (String) UIManager.get("OptionPane.okButtonText");
case android.R.string.yes:
return (String) UIManager.get("OptionPane.yesButtonText");
case android.R.string.no:
return (String) UIManager.get("OptionPane.noButtonText");
default:
params = params.length < 1 ? objects(_null()): params;
params[0] = null;
lookup_R(id, pkg, resourceType, new Job<Class<?>>() {
public void perform(Class<?> c, Object[] parms) throws Exception {
final String name = param_String(null,0,parms);
String path = getRelativePath(resourceType);
InputStream is = c.getResourceAsStream(path);
Document doc = xmlDocument(null, is);
Object[] params = param(null,1,parms);
if (params != null)
params[0] = getResourceByName(doc, name, resourceType);
}
}, params);
if (null != params[0])
return (String) params[0];
}
return "";
}
public int getDimensionPixelOffset(int id) {
final int resourceType = 3;
switch (id) {
default:
Object[] params = {0};
lookup_R(id, context.getPackageName(), resourceType, new Job<Class<?>>() {
public void perform(Class<?> c, Object[] parms) throws Exception {
final String name = param_String(null,0,parms);
InputStream is = c.getResourceAsStream(getRelativePath(resourceType));
Document doc = xmlDocument(null, is);
Object[] params = param(null,1,parms);
if (params != null)
params[0] = getResourceByName(doc, name, resourceType);
}
}, params);
if (null != params[0])
return (Integer) params[0];
}
return 0;
}
public Drawable getDrawable(int id, Object...params) {
String pkg = param(context.getPackageName(), 0, params);
final int resourceType = 4;
switch (id) {
default:
params = params.length < 1 ? objects(_null()): params;
params[0] = null;
lookup_R(id, pkg, resourceType, new Job<Class<?>>() {
public void perform(Class<?> c, Object[] parms) throws Exception {
final String name = getRelativePath(resourceType, param_String(null,0,parms));
InputStream is = getInputStreamByName(c, name, resourceType);
Object[] params = param(null, 1, parms);
if (params != null)
params[0] = is;
}
}, params);
if (null != params[0])
return new Drawable().setImage((InputStream) params[0]);
}
return null;
}
public InputStream openRawResource(int id) {
final int resourceType = 8;
switch (id) {
default:
Object[] params = {null};
lookup_R(id, context.getPackageName(), resourceType, new Job<Class<?>>() {
public void perform(Class<?> c, Object[] parms) throws Exception {
final String name = getRelativePath(resourceType, param_String(null,0,parms));
InputStream is = getInputStreamByName(c, name, resourceType);
Object[] params = param(null, 1, parms);
if (params != null)
params[0] = is;
}
}, params);
if (null != params[0])
return (InputStream) params[0];
}
return null;
}
public AssetManager getAssets() {
return new AssetManager();
}
// NOTE methods further down do NOT correspond to Android APIs
public Document getXml(int id) {
final int resourceType = 6;
switch (id) {
default:
Object[] params = {0};
lookup_R(id, context.getPackageName(), resourceType, new Job<Class<?>>() {
public void perform(Class<?> c, Object[] parms) throws Exception {
final String name = getRelativePath(resourceType, param_String(null,0,parms));
InputStream is = getInputStreamByName(c, name, resourceType);
Object[] params = param(null,1,parms);
if (params != null)
params[0] = xmlDocument(null, is);
}
}, params);
if (null != params[0])
return (Document) params[0];
}
return null;
}
private boolean lookup_R(int id, String pkg, int resourceType, Job<Class<?>> lookup, Object...params) {
try {
Class<?> c = Class.forName(pkg + (notNullOrEmpty(pkg) ? ".R" : "R"));
for (Class<?> inner : c.getDeclaredClasses()) {
if (resourceTypes[resourceType].equals(inner.getSimpleName())) {
String nameValue = param_String(null, 1, params);
for (Field field : inner.getDeclaredFields()) {
int idValue = field.getInt(null);
if ("int".equals(field.getType().getSimpleName()) &&
idValue == id ||
field.getName().equals(nameValue))
{
if (lookup != null)
lookup.perform(c, objects(field.getName(), params, idValue));
return true;
}
}
}
}
} catch (Exception e) {
Log.e(TAG, "lookup_R", e);
}
return false;
}
@SuppressWarnings("unchecked")
public <T> T getResourceByName(Document doc, final String name, int type) {
final String regex = wildcardRegex(name, DOT_REGEX);
final boolean names = !name.equals(regex);
ValList list = vlist(_null());
String tagName = resourceTypes[type] + SOMETHING_OR_NOTHING_REGEX;
for (Element elem : iterateElements(tagName, doc, new Predicate<Element>() {
public boolean apply(Element el) {
String value = el.getAttribute("name");
return names ? value.matches(regex) : name.equals(value);
}}))
{
if (names)
list.add(elem.getAttribute("name"));
else {
String value = elem.getTextContent();
switch (type) {
case 2:
return (T) new Integer(android.graphics.Color.parseColor(value));
case 3:
return (T) toInt(0, stripUnits(value));
case 10:
return (T) elem;
default:
return (T) value;
}
}
}
return (T) (names ? list.toArray() : null);
}
public InputStream getInputStreamByName(Class<?> c, final String name, int type) {
Set<URL> res = getResourceURLs(
new ResourceURLFilter() {
public boolean accept(URL resourceUrl) {
String url = resourceUrl.getFile();
return check(url, Constraint.MIDDLE, name + DOT);
}
});
if (res.iterator().hasNext()) {
URL url = res.iterator().next();
String path = url.getFile();
path = path.substring(path.indexOf(name));
return c.getResourceAsStream(path);
}
return null;
}
public static AttributeSet attributeSet(final Context context, Object...attrs) {
Object o = param(null, 0, attrs);
if (!(o instanceof Element)) {
StringBuilder sb = new StringBuilder();
sb.append("<AttributeSet xmlns:android=\"http://schemas.android.com/apk/res/android\"");
for (Object attr : attrs) {
sb.append(" " + attr);
}
sb.append(" />");
InputStream is = new ByteArrayInputStream(sb.toString().getBytes());
o = xmlDocument(null, is).getDocumentElement();
}
final Element element = (Element) o;
return new AttributeSet() {
{
resources = context.getResources();
attributes = element.getAttributes();
}
Resources resources;
NamedNodeMap attributes;
public int getAttributeCount() {
return attributes.getLength();
}
public String getAttributeName(int index) {
return attributes.item(index).getNodeName();
}
public String getAttributeValue(int index) {
return attributes.item(index).getNodeValue();
}
public String getAttributeValue(String namespace, String name) {
return attributes.getNamedItemNS(namespace, name).getNodeValue();
}
public boolean hasAttribute(String name) {
return attributes.getNamedItem(name) != null;
}
public String getAttributeValue(String name) {
Node item = attributes.getNamedItem(name);
String attr = item != null ? item.getNodeValue() : null;
if (nullOrEmpty(attr)) {
item = attributes.getNamedItem("style");
if (item != null) {
Element el = resources.getXMLResourceItem(item.getNodeValue());
if (el != null) {
el = selectElement(el, "./item[@name='" + name + "']");
if (el != null) {
attr = el.getTextContent();
}
}
}
}
return attr;
}
public <T> T getAttributeResourceItem(String name) {
String attr = getAttributeValue(name);
if (notNullOrEmpty(attr))
return resources.getXMLResourceItem(attr);
else
return null;
}
public int getIdAttributeResourceValue(int defaultValue) {
return toInt(defaultValue, getIdAttribute());
}
public String getIdAttribute() {
return getAttributeResourceItem("android:id");
}
public String getClassAttribute() {
return getAttributeResourceItem("class");
}
public int getStyleAttribute() {
return getAttributeResourceItem("style");
}
public String getPositionDescription() {
return null;
}
public int getAttributeUnsignedIntValue(int index, int defaultValue) {
// TODO Auto-generated method stub
return 0;
}
public int getAttributeUnsignedIntValue(String namespace, String attribute, int defaultValue) {
// TODO Auto-generated method stub
return 0;
}
public int getAttributeResourceValue(int index, int defaultValue) {
// TODO Auto-generated method stub
return 0;
}
public int getAttributeResourceValue(String namespace, String attribute, int defaultValue) {
// TODO Auto-generated method stub
return 0;
}
public int getAttributeNameResource(int index) {
// TODO Auto-generated method stub
return 0;
}
public int getAttributeListValue(int index, String[] options, int defaultValue) {
// TODO Auto-generated method stub
return 0;
}
public int getAttributeListValue(String namespace, String attribute, String[] options, int defaultValue) {
// TODO Auto-generated method stub
return 0;
}
public int getAttributeIntValue(int index, int defaultValue) {
// TODO Auto-generated method stub
return 0;
}
public int getAttributeIntValue(String namespace, String attribute, int defaultValue) {
// TODO Auto-generated method stub
return 0;
}
public float getAttributeFloatValue(int index, float defaultValue) {
// TODO Auto-generated method stub
return 0;
}
public float getAttributeFloatValue(String namespace, String attribute, float defaultValue) {
// TODO Auto-generated method stub
return 0;
}
public boolean getAttributeBooleanValue(int index, boolean defaultValue) {
// TODO Auto-generated method stub
return false;
}
public boolean getAttributeBooleanValue(String namespace, String attribute, boolean defaultValue) {
// TODO Auto-generated method stub
return false;
}
};
}
public interface ResourceURLFilter {
public boolean accept(URL resourceUrl);
}
private static void collectURL(ResourceURLFilter f, Set<URL> s, URL u) {
if (f == null || f.accept(u)) {
s.add(u);
}
}
private static void iterateFileSystem(File r, ResourceURLFilter f, Set<URL> s) throws MalformedURLException, IOException {
File[] files = r.listFiles();
for (File file: files)
if (file.isDirectory())
iterateFileSystem(file, f, s);
else if (file.isFile())
collectURL(f, s, file.toURI().toURL());
}
private static void iterateJarFile(File file, ResourceURLFilter f, Set<URL> s) throws MalformedURLException, IOException {
JarFile jarfile = new JarFile(file);
for(Enumeration<JarEntry> je = jarfile.entries(); je.hasMoreElements();) {
JarEntry j = je.nextElement();
if (!j.isDirectory()) {
collectURL(f, s, new URL("jar", "", file.toURI() + "!/" + j.getName()));
}
}
jarfile.close();
}
private static void iterateEntry(File p, ResourceURLFilter f, Set<URL> s) throws MalformedURLException, IOException {
if (p.isDirectory()) {
iterateFileSystem(p, f, s);
} else if (p.isFile() && p.getName().toLowerCase().endsWith(".jar")) {
iterateJarFile(p, f, s);
}
}
public static Set<URL> getResourceURLs() {
return getResourceURLs((ResourceURLFilter)null);
}
public static Set<URL> getResourceURLs(ResourceURLFilter filter) {
Set<URL> collectedURLs = new HashSet<URL>();
try {
URLClassLoader ucl = (URLClassLoader)ClassLoader.getSystemClassLoader();
for (URL url: ucl.getURLs()) {
no_println(url);
iterateEntry(new File(url.toURI()), filter, collectedURLs);
}
} catch (Exception e) {
Log.e(TAG, "getResourceURLs", e);
}
return collectedURLs;
}
public static Set<URL> getResourceURLs(Class<?> rootClass) throws Exception {
String packageName = rootClass.getName();
packageName = packageName.substring(0, packageName.lastIndexOf("."));
return getResourceURLs(packageName, (ResourceURLFilter)null);
}
public static Set<URL> getResourceURLs(String packageName, ResourceURLFilter filter) throws Exception {
Set<URL> collectedURLs = new HashSet<URL>();
URI location = getCodeSourceLocation(Resources.class);
// location = location.resolve(packageName.replace('.', '/'));
iterateEntry(new File(location), filter, collectedURLs);
return collectedURLs;
}
public static URI getCodeSourceLocation(Class<?> rootClass) {
try {
CodeSource src = rootClass.getProtectionDomain().getCodeSource();
return src.getLocation().toURI();
} catch (Exception e) {
Log.e(TAG, "getCodeSourceLocation", e);
diag_println(DIAG_OFF, "code source not available");
throw new RuntimeException("code source not available");
}
}
public static String className(String resourceClassFileName, String sourceLocation) {
return strip(Constraint.START, resourceClassFileName.substring(sourceLocation.length()), "!/").replace('/', '.');
}
public static void list(Writer writer, String packageName, ResourceURLFilter filter) {
try {
for (URL u: getResourceURLs(packageName, filter)) {
writer.append(u + NEWLINE);
}
} catch (Exception e) {
Log.e(TAG, "list", e);
}
}
}