package com.xtremelabs.robolectric.res; import java.io.File; import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import com.xtremelabs.robolectric.Robolectric; import com.xtremelabs.robolectric.tester.android.util.TestAttributeSet; import com.xtremelabs.robolectric.util.I18nException; import android.content.Context; import android.preference.Preference; import android.preference.PreferenceGroup; import android.preference.PreferenceScreen; import android.util.AttributeSet; public class PreferenceLoader extends XmlLoader { private Map prefNodesByResourceName = new HashMap(); public PreferenceLoader(ResourceExtractor resourceExtractor) { super(resourceExtractor); } @Override protected void processResourceXml(File xmlFile, Document document, boolean isSystem) throws Exception { PreferenceNode topLevelNode = new PreferenceNode("top-level", new HashMap()); processChildren(document.getChildNodes(), topLevelNode); prefNodesByResourceName.put( "xml/" + xmlFile.getName().replace(".xml", ""), topLevelNode.getChildren().get(0)); } private void processChildren(NodeList childNodes, PreferenceNode parent) { for (int i = 0; i < childNodes.getLength(); i++) { Node node = childNodes.item(i); processNode(node, parent); } } private void processNode(Node node, PreferenceNode parent) { String name = node.getNodeName(); NamedNodeMap attributes = node.getAttributes(); Map attrMap = new HashMap(); if (attributes != null) { int length = attributes.getLength(); for (int i = 0; i < length; i++) { Node attr = attributes.item(i); attrMap.put(attr.getNodeName(), attr.getNodeValue()); } } if (!name.startsWith("#")) { PreferenceNode prefNode = new PreferenceNode(name, attrMap); if (parent != null) parent.addChild(prefNode); processChildren(node.getChildNodes(), prefNode); } } public PreferenceScreen inflatePreferences(Context context, int resourceId) { return inflatePreferences(context, resourceExtractor.getResourceName(resourceId)); } public PreferenceScreen inflatePreferences(Context context, String key) { try { PreferenceNode prefNode = prefNodesByResourceName.get(key); return (PreferenceScreen) prefNode.inflate(context, null); } catch (I18nException e) { throw e; } catch (Exception e) { throw new RuntimeException("error inflating " + key, e); } } public class PreferenceNode { private String name; private final Map attributes; private List children = new ArrayList(); public PreferenceNode(String name, Map attributes) { this.name = name; this.attributes = attributes; } public List getChildren() { return children; } public void addChild(PreferenceNode prefNode) { children.add(prefNode); } public Preference inflate(Context context, Preference parent) throws Exception { Preference preference = create(context, (PreferenceGroup) parent); for (PreferenceNode child : children) { child.inflate(context, preference); } return preference; } private Preference create(Context context, PreferenceGroup parent) throws Exception { Preference preference = constructPreference(context, parent); if (parent != null && parent != preference) { parent.addPreference(preference); } return preference; } private Preference constructPreference(Context context, PreferenceGroup parent) throws Exception { Class clazz = pickViewClass(); if (clazz.equals(PreferenceScreen.class)) { return Robolectric.newInstanceOf(PreferenceScreen.class); } try { TestAttributeSet attributeSet = new TestAttributeSet(attributes); if (strictI18n) { attributeSet.validateStrictI18n(); } return ((Constructor) clazz.getConstructor(Context.class, AttributeSet.class)).newInstance(context, attributeSet); } catch (NoSuchMethodException e) { try { return ((Constructor) clazz.getConstructor(Context.class)).newInstance(context); } catch (NoSuchMethodException e1) { return ((Constructor) clazz.getConstructor(Context.class, String.class)).newInstance(context, ""); } } } private Class pickViewClass() { Class clazz = loadClass(name); if (clazz == null) { clazz = loadClass("android.preference." + name); } if (clazz == null) { throw new RuntimeException("couldn't find preference class " + name); } return clazz; } private Class loadClass(String className) { try { //noinspection unchecked return (Class) getClass().getClassLoader().loadClass(className); } catch (ClassNotFoundException e) { return null; } } } }