package com.xtremelabs.robolectric.res; import java.io.File; 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 android.content.Context; import android.text.TextUtils; import android.view.Menu; import android.view.MenuItem; import android.view.SubMenu; import com.xtremelabs.robolectric.tester.android.util.TestAttributeSet; import com.xtremelabs.robolectric.util.I18nException; public class MenuLoader extends XmlLoader { private Map menuNodesByMenuName = new HashMap(); private AttrResourceLoader attrResourceLoader; public MenuLoader(ResourceExtractor resourceExtractor, AttrResourceLoader attrResourceLoader) { super(resourceExtractor); this.attrResourceLoader = attrResourceLoader; } @Override protected void processResourceXml(File xmlFile, Document document, boolean ignored) throws Exception { MenuNode topLevelNode = new MenuNode("top-level", new HashMap()); NodeList items = document.getChildNodes(); if (items.getLength() != 1) throw new RuntimeException("Expected only one top-level item in menu file " + xmlFile.getName()); if (items.item(0).getNodeName().compareTo("menu") != 0) throw new RuntimeException("Expected a top-level item called 'menu' in menu file " + xmlFile.getName()); processChildren(items.item(0).getChildNodes(), topLevelNode); menuNodesByMenuName.put("menu/" + xmlFile.getName().replace(".xml", ""), topLevelNode); } private void processChildren(NodeList childNodes, MenuNode parent) { for (int i = 0; i < childNodes.getLength(); i++) { Node node = childNodes.item(i); processNode(node, parent); } } private void processNode(Node node, MenuNode 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("#")) { MenuNode menuNode = new MenuNode(name, attrMap); parent.addChild(menuNode); NodeList children = node.getChildNodes(); if (children != null && children.getLength() != 0) { for (int i = 0; i < children.getLength(); i++) { Node nodei = children.item(i); if (childToIgnore(nodei)) { continue; } else if (validChildren(nodei)) { // recursively add all nodes processNode(nodei, menuNode); } else { throw new RuntimeException("Unknown menu node" + nodei.getNodeName()); } } } } } private static boolean childToIgnore(Node nodei) { return TextUtils.isEmpty(nodei.getNodeName()) || nodei.getNodeName().startsWith("#"); } private static boolean validChildren(Node nodei) { return nodei.getNodeName().equals("item") || nodei.getNodeName().equals("menu") || nodei.getNodeName().equals("group"); } public void inflateMenu(Context context, String key, Menu root) { inflateMenu(context, key, null, root); } public void inflateMenu(Context context, int resourceId, Menu root) { inflateMenu(context, resourceExtractor.getResourceName(resourceId), root); } private void inflateMenu(Context context, String key, Map attributes, Menu root) { MenuNode menuNode = menuNodesByMenuName.get(key); if (menuNode == null) { throw new RuntimeException("Could not find menu " + key); } try { if (attributes != null) { for (Map.Entry entry : attributes.entrySet()) { if (!entry.getKey().equals("menu")) { menuNode.attributes.put(entry.getKey(), entry.getValue()); } } } menuNode.inflate(context, root); } catch (I18nException e) { throw e; } catch (Exception e) { throw new RuntimeException("error inflating " + key, e); } } public class MenuNode { private String name; private final TestAttributeSet attributes; private List children = new ArrayList(); public MenuNode(String name, Map attributes) { this.name = name; this.attributes = new TestAttributeSet(attributes, resourceExtractor, attrResourceLoader, null, false); } public List getChildren() { return children; } public void addChild(MenuNode MenuNode) { children.add(MenuNode); } private boolean isSubMenuItem(MenuNode child) { List ch = child.children; return ch != null && ch.size() == 1 && "menu".equals(ch.get(0).name); } private void addChildrenInGroup(MenuNode source, int groupId, Menu root) { for (MenuNode child : source.children) { String name = child.name; TestAttributeSet attributes = child.attributes; if (strictI18n) { attributes.validateStrictI18n(); } if (name.equals("item")) { if (isSubMenuItem(child)) { SubMenu sub = root.addSubMenu(groupId, attributes .getAttributeResourceValue("android", "id", 0), 0, attributes.getAttributeValue("android", "title")); MenuNode subMenuNode = child.children.get(0); addChildrenInGroup(subMenuNode, groupId, sub); } else { MenuItem menuItem = root.add(groupId, attributes .getAttributeResourceValue("android", "id", 0), 0, attributes.getAttributeValue("android", "title")); } } else if (name.equals("group")) { int newGroupId = attributes.getAttributeResourceValue("android", "id", 0); addChildrenInGroup(child, newGroupId, root); } } } public void inflate(Context context, Menu root) throws Exception { addChildrenInGroup(this, 0, root); } } }