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