Home | History | Annotate | Download | only in res
      1 package com.xtremelabs.robolectric.res;
      2 
      3 import java.io.File;
      4 import java.lang.reflect.Constructor;
      5 import java.util.ArrayList;
      6 import java.util.HashMap;
      7 import java.util.List;
      8 import java.util.Map;
      9 
     10 import org.w3c.dom.Document;
     11 import org.w3c.dom.NamedNodeMap;
     12 import org.w3c.dom.Node;
     13 import org.w3c.dom.NodeList;
     14 
     15 import com.xtremelabs.robolectric.Robolectric;
     16 import com.xtremelabs.robolectric.tester.android.util.TestAttributeSet;
     17 import com.xtremelabs.robolectric.util.I18nException;
     18 
     19 import android.content.Context;
     20 import android.preference.Preference;
     21 import android.preference.PreferenceGroup;
     22 import android.preference.PreferenceScreen;
     23 import android.util.AttributeSet;
     24 
     25 public class PreferenceLoader extends XmlLoader {
     26 
     27     private Map<String, PreferenceNode> prefNodesByResourceName = new HashMap<String, PreferenceNode>();
     28 
     29 	public PreferenceLoader(ResourceExtractor resourceExtractor) {
     30 		super(resourceExtractor);
     31 	}
     32 
     33 	@Override
     34 	protected void processResourceXml(File xmlFile, Document document, boolean isSystem) throws Exception {
     35 		PreferenceNode topLevelNode = new PreferenceNode("top-level", new HashMap<String, String>());
     36 		processChildren(document.getChildNodes(), topLevelNode);
     37 		prefNodesByResourceName.put( "xml/" + xmlFile.getName().replace(".xml", ""), topLevelNode.getChildren().get(0));
     38 	}
     39 
     40     private void processChildren(NodeList childNodes, PreferenceNode parent) {
     41         for (int i = 0; i < childNodes.getLength(); i++) {
     42             Node node = childNodes.item(i);
     43             processNode(node, parent);
     44         }
     45     }
     46 
     47     private void processNode(Node node, PreferenceNode parent) {
     48         String name = node.getNodeName();
     49         NamedNodeMap attributes = node.getAttributes();
     50         Map<String, String> attrMap = new HashMap<String, String>();
     51 
     52         if (attributes != null) {
     53             int length = attributes.getLength();
     54             for (int i = 0; i < length; i++) {
     55                 Node attr = attributes.item(i);
     56                 attrMap.put(attr.getNodeName(), attr.getNodeValue());
     57             }
     58         }
     59 
     60         if (!name.startsWith("#")) {
     61 	        PreferenceNode prefNode = new PreferenceNode(name, attrMap);
     62 	        if (parent != null) parent.addChild(prefNode);
     63 
     64 	        processChildren(node.getChildNodes(), prefNode);
     65         }
     66     }
     67 
     68 	public PreferenceScreen inflatePreferences(Context context, int resourceId) {
     69 		return inflatePreferences(context, resourceExtractor.getResourceName(resourceId));
     70 	}
     71 
     72 	public PreferenceScreen inflatePreferences(Context context, String key) {
     73         try {
     74         	PreferenceNode prefNode = prefNodesByResourceName.get(key);
     75         	return (PreferenceScreen) prefNode.inflate(context, null);
     76         } catch (I18nException e) {
     77         	throw e;
     78         } catch (Exception e) {
     79             throw new RuntimeException("error inflating " + key, e);
     80         }
     81 	}
     82 
     83     public class PreferenceNode {
     84         private String name;
     85         private final Map<String, String> attributes;
     86 
     87         private List<PreferenceNode> children = new ArrayList<PreferenceNode>();
     88 
     89         public PreferenceNode(String name, Map<String, String> attributes) {
     90             this.name = name;
     91             this.attributes = attributes;
     92         }
     93 
     94         public List<PreferenceNode> getChildren() {
     95             return children;
     96         }
     97 
     98         public void addChild(PreferenceNode prefNode) {
     99             children.add(prefNode);
    100         }
    101 
    102         public Preference inflate(Context context, Preference parent) throws Exception {
    103         	Preference preference = create(context, (PreferenceGroup) parent);
    104 
    105             for (PreferenceNode child : children) {
    106                 child.inflate(context, preference);
    107             }
    108 
    109         	return preference;
    110         }
    111 
    112         private Preference create(Context context, PreferenceGroup parent) throws Exception {
    113         	Preference preference = constructPreference(context, parent);
    114             if (parent != null && parent != preference) {
    115                 parent.addPreference(preference);
    116             }
    117             return preference;
    118         }
    119 
    120         private Preference constructPreference(Context context, PreferenceGroup parent) throws Exception {
    121         	Class<? extends Preference> clazz = pickViewClass();
    122 
    123            	if (clazz.equals(PreferenceScreen.class)) {
    124         		return Robolectric.newInstanceOf(PreferenceScreen.class);
    125         	}
    126 
    127            	try {
    128                 TestAttributeSet attributeSet = new TestAttributeSet(attributes);
    129                 if (strictI18n) {
    130                 	attributeSet.validateStrictI18n();
    131                 }
    132                 return ((Constructor<? extends Preference>) clazz.getConstructor(Context.class, AttributeSet.class)).newInstance(context, attributeSet);
    133             } catch (NoSuchMethodException e) {
    134 	            try {
    135 	                return ((Constructor<? extends Preference>) clazz.getConstructor(Context.class)).newInstance(context);
    136 	            } catch (NoSuchMethodException e1) {
    137 	                return ((Constructor<? extends Preference>) clazz.getConstructor(Context.class, String.class)).newInstance(context, "");
    138 	            }
    139             }
    140         }
    141 
    142         private Class<? extends Preference> pickViewClass() {
    143             Class<? extends Preference> clazz = loadClass(name);
    144             if (clazz == null) {
    145                 clazz = loadClass("android.preference." + name);
    146             }
    147             if (clazz == null) {
    148                 throw new RuntimeException("couldn't find preference class " + name);
    149             }
    150             return clazz;
    151         }
    152 
    153         private Class<? extends Preference> loadClass(String className) {
    154             try {
    155                 //noinspection unchecked
    156                 return (Class<? extends Preference>) getClass().getClassLoader().loadClass(className);
    157             } catch (ClassNotFoundException e) {
    158                 return null;
    159             }
    160         }
    161     }
    162 }
    163