1826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parkerpackage com.xtremelabs.robolectric.res;
2826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker
3d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultzimport java.io.File;
4d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultzimport java.util.ArrayList;
5d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultzimport java.util.HashMap;
6d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultzimport java.util.List;
7d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultzimport java.util.Map;
8bb2f85d397fa65f20c6932186d9156ced9beb184Michael Portuesi
9826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parkerimport org.w3c.dom.Document;
10826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parkerimport org.w3c.dom.NamedNodeMap;
11826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parkerimport org.w3c.dom.Node;
12826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parkerimport org.w3c.dom.NodeList;
13826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker
14d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultzimport android.content.Context;
15d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultzimport android.text.TextUtils;
16d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultzimport android.view.Menu;
17d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultzimport android.view.MenuItem;
18d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultzimport android.view.SubMenu;
19d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz
20d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultzimport com.xtremelabs.robolectric.tester.android.util.TestAttributeSet;
21d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultzimport com.xtremelabs.robolectric.util.I18nException;
22826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker
23826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parkerpublic class MenuLoader extends XmlLoader {
24826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker    private Map<String, MenuNode> menuNodesByMenuName = new HashMap<String, MenuNode>();
25ba7a1270d36fb28e5f90be135431700c4ce81504Phil Goodwin    private AttrResourceLoader attrResourceLoader;
26826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker
27ba7a1270d36fb28e5f90be135431700c4ce81504Phil Goodwin    public MenuLoader(ResourceExtractor resourceExtractor, AttrResourceLoader attrResourceLoader) {
28826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker        super(resourceExtractor);
29ba7a1270d36fb28e5f90be135431700c4ce81504Phil Goodwin        this.attrResourceLoader = attrResourceLoader;
30826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker    }
31826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker
32826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker    @Override
3329610b43047065c5dc66452d33f0113ac7e65fe8Phil Goodwin    protected void processResourceXml(File xmlFile, Document document, boolean ignored) throws Exception {
34bb2f85d397fa65f20c6932186d9156ced9beb184Michael Portuesi        MenuNode topLevelNode = new MenuNode("top-level", new HashMap<String, String>());
3540c6251719cccc0a84ae99c976d2836b14374ce6Christian Williams
3640c6251719cccc0a84ae99c976d2836b14374ce6Christian Williams        NodeList items = document.getChildNodes();
3740c6251719cccc0a84ae99c976d2836b14374ce6Christian Williams        if (items.getLength() != 1)
3840c6251719cccc0a84ae99c976d2836b14374ce6Christian Williams            throw new RuntimeException("Expected only one top-level item in menu file " + xmlFile.getName());
3940c6251719cccc0a84ae99c976d2836b14374ce6Christian Williams        if (items.item(0).getNodeName().compareTo("menu") != 0)
4040c6251719cccc0a84ae99c976d2836b14374ce6Christian Williams            throw new RuntimeException("Expected a top-level item called 'menu' in menu file " + xmlFile.getName());
41041a15421126d0667517def59cef9b284359d729Tom Parker
42041a15421126d0667517def59cef9b284359d729Tom Parker        processChildren(items.item(0).getChildNodes(), topLevelNode);
43d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz        menuNodesByMenuName.put("menu/" + xmlFile.getName().replace(".xml", ""), topLevelNode);
44826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker    }
45826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker
46826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker    private void processChildren(NodeList childNodes, MenuNode parent) {
47826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker        for (int i = 0; i < childNodes.getLength(); i++) {
48826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker            Node node = childNodes.item(i);
49826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker            processNode(node, parent);
50826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker        }
51826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker    }
52826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker
53826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker    private void processNode(Node node, MenuNode parent) {
54826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker        String name = node.getNodeName();
55826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker        NamedNodeMap attributes = node.getAttributes();
56826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker        Map<String, String> attrMap = new HashMap<String, String>();
57826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker        if (attributes != null) {
58826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker            int length = attributes.getLength();
59826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker            for (int i = 0; i < length; i++) {
60826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker                Node attr = attributes.item(i);
61826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker                attrMap.put(attr.getNodeName(), attr.getNodeValue());
62826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker            }
63826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker        }
64826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker
65826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker        if (!name.startsWith("#")) {
66bb2f85d397fa65f20c6932186d9156ced9beb184Michael Portuesi            MenuNode menuNode = new MenuNode(name, attrMap);
6740c6251719cccc0a84ae99c976d2836b14374ce6Christian Williams            parent.addChild(menuNode);
68d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz            NodeList children = node.getChildNodes();
69d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz            if (children != null && children.getLength() != 0) {
70d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz                for (int i = 0; i < children.getLength(); i++) {
71d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz                    Node nodei = children.item(i);
72d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz                    if (childToIgnore(nodei)) {
73d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz                        continue;
74d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz                    } else if (validChildren(nodei)) {
75d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz                        // recursively add all nodes
76d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz                        processNode(nodei, menuNode);
77d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz                    } else {
78d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz                        throw new RuntimeException("Unknown menu node"
79d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz                                + nodei.getNodeName());
80d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz                    }
81d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz                }
82d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz            }
8340c6251719cccc0a84ae99c976d2836b14374ce6Christian Williams        }
84826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker    }
85826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker
86d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz    private static boolean childToIgnore(Node nodei) {
87d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz        return TextUtils.isEmpty(nodei.getNodeName())
88d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz                || nodei.getNodeName().startsWith("#");
89d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz    }
90d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz
91d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz    private static boolean validChildren(Node nodei) {
92d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz        return nodei.getNodeName().equals("item")
93d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz                || nodei.getNodeName().equals("menu")
94d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz                || nodei.getNodeName().equals("group");
95d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz    }
96d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz
97041a15421126d0667517def59cef9b284359d729Tom Parker    public void inflateMenu(Context context, String key, Menu root) {
98041a15421126d0667517def59cef9b284359d729Tom Parker        inflateMenu(context, key, null, root);
99826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker    }
100826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker
101041a15421126d0667517def59cef9b284359d729Tom Parker    public void inflateMenu(Context context, int resourceId, Menu root) {
102d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz        inflateMenu(context, resourceExtractor.getResourceName(resourceId),
103d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz                root);
104826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker    }
105826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker
106d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz    private void inflateMenu(Context context, String key,
107d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz                             Map<String, String> attributes, Menu root) {
108826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker        MenuNode menuNode = menuNodesByMenuName.get(key);
109826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker        if (menuNode == null) {
110826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker            throw new RuntimeException("Could not find menu " + key);
111826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker        }
112826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker        try {
113826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker            if (attributes != null) {
114826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker                for (Map.Entry<String, String> entry : attributes.entrySet()) {
115826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker                    if (!entry.getKey().equals("menu")) {
116d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz                        menuNode.attributes.put(entry.getKey(),
117d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz                                entry.getValue());
118826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker                    }
119826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker                }
120826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker            }
121041a15421126d0667517def59cef9b284359d729Tom Parker            menuNode.inflate(context, root);
122bb2f85d397fa65f20c6932186d9156ced9beb184Michael Portuesi        } catch (I18nException e) {
123bb2f85d397fa65f20c6932186d9156ced9beb184Michael Portuesi            throw e;
124826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker        } catch (Exception e) {
125826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker            throw new RuntimeException("error inflating " + key, e);
126826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker        }
127826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker    }
128826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker
129826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker    public class MenuNode {
130826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker        private String name;
131ba7a1270d36fb28e5f90be135431700c4ce81504Phil Goodwin        private final TestAttributeSet attributes;
132826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker
133826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker        private List<MenuNode> children = new ArrayList<MenuNode>();
134826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker
135bb2f85d397fa65f20c6932186d9156ced9beb184Michael Portuesi        public MenuNode(String name, Map<String, String> attributes) {
136826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker            this.name = name;
137d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz            this.attributes = new TestAttributeSet(attributes,
138d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz                    resourceExtractor, attrResourceLoader, null, false);
139826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker        }
140826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker
141826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker        public List<MenuNode> getChildren() {
142826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker            return children;
143826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker        }
144826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker
145826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker        public void addChild(MenuNode MenuNode) {
146826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker            children.add(MenuNode);
147826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker        }
148826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker
149d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz        private boolean isSubMenuItem(MenuNode child) {
150d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz            List<MenuLoader.MenuNode> ch = child.children;
151d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz            return ch != null && ch.size() == 1 && "menu".equals(ch.get(0).name);
152d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz        }
153d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz
154d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz        private void addChildrenInGroup(MenuNode source, int groupId, Menu root) {
155d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz            for (MenuNode child : source.children) {
156d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz                String name = child.name;
157d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz                TestAttributeSet attributes = child.attributes;
158d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz                if (strictI18n) {
159d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz                    attributes.validateStrictI18n();
160d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz                }
161d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz                if (name.equals("item")) {
162d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz                    if (isSubMenuItem(child)) {
163d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz                        SubMenu sub = root.addSubMenu(groupId, attributes
164d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz                                .getAttributeResourceValue("android", "id", 0),
165d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz                                0, attributes.getAttributeValue("android", "title"));
166d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz                        MenuNode subMenuNode = child.children.get(0);
167d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz                        addChildrenInGroup(subMenuNode, groupId, sub);
168d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz                    } else {
169d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz                        MenuItem menuItem = root.add(groupId, attributes
170d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz                                .getAttributeResourceValue("android", "id", 0),
171d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz                                0, attributes.getAttributeValue("android", "title"));
172d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz                    }
173d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz                } else if (name.equals("group")) {
174d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz                    int newGroupId = attributes.getAttributeResourceValue("android", "id", 0);
175d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz                    addChildrenInGroup(child, newGroupId, root);
176bb2f85d397fa65f20c6932186d9156ced9beb184Michael Portuesi                }
177826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker            }
178826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker        }
179d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz
180d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz        public void inflate(Context context, Menu root) throws Exception {
181d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz            addChildrenInGroup(this, 0, root);
182d6cd04adb85895b340ce9b6df33b749b22099c43Joe Moore and Tyler Schultz        }
18340c6251719cccc0a84ae99c976d2836b14374ce6Christian Williams    }
184826bfc77015bfd1044cfb0f98b0fafe65f1e465eTom Parker}
185