Home | History | Annotate | Download | only in res
      1 package com.xtremelabs.robolectric.res;
      2 
      3 import java.io.File;
      4 import java.util.ArrayList;
      5 import java.util.HashMap;
      6 import java.util.List;
      7 import java.util.Map;
      8 
      9 import org.w3c.dom.Document;
     10 import org.w3c.dom.NamedNodeMap;
     11 import org.w3c.dom.Node;
     12 import org.w3c.dom.NodeList;
     13 
     14 import android.content.Context;
     15 import android.text.TextUtils;
     16 import android.view.Menu;
     17 import android.view.MenuItem;
     18 import android.view.SubMenu;
     19 
     20 import com.xtremelabs.robolectric.tester.android.util.TestAttributeSet;
     21 import com.xtremelabs.robolectric.util.I18nException;
     22 
     23 public class MenuLoader extends XmlLoader {
     24     private Map<String, MenuNode> menuNodesByMenuName = new HashMap<String, MenuNode>();
     25     private AttrResourceLoader attrResourceLoader;
     26 
     27     public MenuLoader(ResourceExtractor resourceExtractor, AttrResourceLoader attrResourceLoader) {
     28         super(resourceExtractor);
     29         this.attrResourceLoader = attrResourceLoader;
     30     }
     31 
     32     @Override
     33     protected void processResourceXml(File xmlFile, Document document, boolean ignored) throws Exception {
     34         MenuNode topLevelNode = new MenuNode("top-level", new HashMap<String, String>());
     35 
     36         NodeList items = document.getChildNodes();
     37         if (items.getLength() != 1)
     38             throw new RuntimeException("Expected only one top-level item in menu file " + xmlFile.getName());
     39         if (items.item(0).getNodeName().compareTo("menu") != 0)
     40             throw new RuntimeException("Expected a top-level item called 'menu' in menu file " + xmlFile.getName());
     41 
     42         processChildren(items.item(0).getChildNodes(), topLevelNode);
     43         menuNodesByMenuName.put("menu/" + xmlFile.getName().replace(".xml", ""), topLevelNode);
     44     }
     45 
     46     private void processChildren(NodeList childNodes, MenuNode parent) {
     47         for (int i = 0; i < childNodes.getLength(); i++) {
     48             Node node = childNodes.item(i);
     49             processNode(node, parent);
     50         }
     51     }
     52 
     53     private void processNode(Node node, MenuNode parent) {
     54         String name = node.getNodeName();
     55         NamedNodeMap attributes = node.getAttributes();
     56         Map<String, String> attrMap = new HashMap<String, String>();
     57         if (attributes != null) {
     58             int length = attributes.getLength();
     59             for (int i = 0; i < length; i++) {
     60                 Node attr = attributes.item(i);
     61                 attrMap.put(attr.getNodeName(), attr.getNodeValue());
     62             }
     63         }
     64 
     65         if (!name.startsWith("#")) {
     66             MenuNode menuNode = new MenuNode(name, attrMap);
     67             parent.addChild(menuNode);
     68             NodeList children = node.getChildNodes();
     69             if (children != null && children.getLength() != 0) {
     70                 for (int i = 0; i < children.getLength(); i++) {
     71                     Node nodei = children.item(i);
     72                     if (childToIgnore(nodei)) {
     73                         continue;
     74                     } else if (validChildren(nodei)) {
     75                         // recursively add all nodes
     76                         processNode(nodei, menuNode);
     77                     } else {
     78                         throw new RuntimeException("Unknown menu node"
     79                                 + nodei.getNodeName());
     80                     }
     81                 }
     82             }
     83         }
     84     }
     85 
     86     private static boolean childToIgnore(Node nodei) {
     87         return TextUtils.isEmpty(nodei.getNodeName())
     88                 || nodei.getNodeName().startsWith("#");
     89     }
     90 
     91     private static boolean validChildren(Node nodei) {
     92         return nodei.getNodeName().equals("item")
     93                 || nodei.getNodeName().equals("menu")
     94                 || nodei.getNodeName().equals("group");
     95     }
     96 
     97     public void inflateMenu(Context context, String key, Menu root) {
     98         inflateMenu(context, key, null, root);
     99     }
    100 
    101     public void inflateMenu(Context context, int resourceId, Menu root) {
    102         inflateMenu(context, resourceExtractor.getResourceName(resourceId),
    103                 root);
    104     }
    105 
    106     private void inflateMenu(Context context, String key,
    107                              Map<String, String> attributes, Menu root) {
    108         MenuNode menuNode = menuNodesByMenuName.get(key);
    109         if (menuNode == null) {
    110             throw new RuntimeException("Could not find menu " + key);
    111         }
    112         try {
    113             if (attributes != null) {
    114                 for (Map.Entry<String, String> entry : attributes.entrySet()) {
    115                     if (!entry.getKey().equals("menu")) {
    116                         menuNode.attributes.put(entry.getKey(),
    117                                 entry.getValue());
    118                     }
    119                 }
    120             }
    121             menuNode.inflate(context, root);
    122         } catch (I18nException e) {
    123             throw e;
    124         } catch (Exception e) {
    125             throw new RuntimeException("error inflating " + key, e);
    126         }
    127     }
    128 
    129     public class MenuNode {
    130         private String name;
    131         private final TestAttributeSet attributes;
    132 
    133         private List<MenuNode> children = new ArrayList<MenuNode>();
    134 
    135         public MenuNode(String name, Map<String, String> attributes) {
    136             this.name = name;
    137             this.attributes = new TestAttributeSet(attributes,
    138                     resourceExtractor, attrResourceLoader, null, false);
    139         }
    140 
    141         public List<MenuNode> getChildren() {
    142             return children;
    143         }
    144 
    145         public void addChild(MenuNode MenuNode) {
    146             children.add(MenuNode);
    147         }
    148 
    149         private boolean isSubMenuItem(MenuNode child) {
    150             List<MenuLoader.MenuNode> ch = child.children;
    151             return ch != null && ch.size() == 1 && "menu".equals(ch.get(0).name);
    152         }
    153 
    154         private void addChildrenInGroup(MenuNode source, int groupId, Menu root) {
    155             for (MenuNode child : source.children) {
    156                 String name = child.name;
    157                 TestAttributeSet attributes = child.attributes;
    158                 if (strictI18n) {
    159                     attributes.validateStrictI18n();
    160                 }
    161                 if (name.equals("item")) {
    162                     if (isSubMenuItem(child)) {
    163                         SubMenu sub = root.addSubMenu(groupId, attributes
    164                                 .getAttributeResourceValue("android", "id", 0),
    165                                 0, attributes.getAttributeValue("android", "title"));
    166                         MenuNode subMenuNode = child.children.get(0);
    167                         addChildrenInGroup(subMenuNode, groupId, sub);
    168                     } else {
    169                         MenuItem menuItem = root.add(groupId, attributes
    170                                 .getAttributeResourceValue("android", "id", 0),
    171                                 0, attributes.getAttributeValue("android", "title"));
    172                     }
    173                 } else if (name.equals("group")) {
    174                     int newGroupId = attributes.getAttributeResourceValue("android", "id", 0);
    175                     addChildrenInGroup(child, newGroupId, root);
    176                 }
    177             }
    178         }
    179 
    180         public void inflate(Context context, Menu root) throws Exception {
    181             addChildrenInGroup(this, 0, root);
    182         }
    183     }
    184 }
    185