aboutsummaryrefslogtreecommitdiff
path: root/v1/src/main/java/com/xtremelabs/robolectric/res/MenuLoader.java
blob: 8f2ba007b775e5c8b614b0fe17d2b59d896b34fe (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
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<String, MenuNode> menuNodesByMenuName = new HashMap<String, MenuNode>();
    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<String, String>());

        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<String, String> attrMap = new HashMap<String, String>();
        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<String, String> 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<String, String> 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<MenuNode> children = new ArrayList<MenuNode>();

        public MenuNode(String name, Map<String, String> attributes) {
            this.name = name;
            this.attributes = new TestAttributeSet(attributes,
                    resourceExtractor, attrResourceLoader, null, false);
        }

        public List<MenuNode> getChildren() {
            return children;
        }

        public void addChild(MenuNode MenuNode) {
            children.add(MenuNode);
        }

        private boolean isSubMenuItem(MenuNode child) {
            List<MenuLoader.MenuNode> 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);
        }
    }
}