1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Eclipse Public License, Version 1.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.eclipse.org/org/documents/epl-v10.php
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.ide.eclipse.adt.internal.editors.menu.descriptors;
18 
19 import static com.android.SdkConstants.ANDROID_NS_NAME;
20 import static com.android.SdkConstants.ANDROID_URI;
21 import static com.android.SdkConstants.TAG_MENU;
22 
23 import com.android.ide.common.resources.platform.DeclareStyleableInfo;
24 import com.android.ide.eclipse.adt.AdtUtils;
25 import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
26 import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils;
27 import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
28 import com.android.ide.eclipse.adt.internal.editors.descriptors.IDescriptorProvider;
29 import com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor;
30 
31 import java.util.ArrayList;
32 import java.util.Map;
33 
34 /**
35  * Complete description of the menu structure.
36  */
37 public final class MenuDescriptors implements IDescriptorProvider {
38 
39     /** The root element descriptor. */
40     private ElementDescriptor mDescriptor = null;
41 
42     /** @return the root descriptor. */
43     @Override
getDescriptor()44     public ElementDescriptor getDescriptor() {
45         return mDescriptor;
46     }
47 
48     @Override
getRootElementDescriptors()49     public ElementDescriptor[] getRootElementDescriptors() {
50         return mDescriptor.getChildren();
51     }
52 
53     /**
54      * Updates the document descriptor.
55      * <p/>
56      * It first computes the new children of the descriptor and then updates them
57      * all at once.
58      *
59      * @param styleMap The map style => attributes from the attrs.xml file
60      */
updateDescriptors(Map<String, DeclareStyleableInfo> styleMap)61     public synchronized void updateDescriptors(Map<String, DeclareStyleableInfo> styleMap) {
62 
63         // There are 3 elements: menu, item and group.
64         // The root element MUST be a menu.
65         // A top menu can contain items or group:
66         //  - top groups can contain top items
67         //  - top items can contain sub-menus
68         // A sub menu can contains sub items or sub groups:
69         //  - sub groups can contain sub items
70         //  - sub items cannot contain anything
71 
72         if (mDescriptor == null) {
73             mDescriptor = createElement(styleMap,
74                 TAG_MENU, // xmlName
75                 "Menu", // uiName,
76                 null, // TODO SDK URL
77                 null, // extraAttribute
78                 null, // childrenElements,
79                 true /* mandatory */);
80         }
81 
82         // -- sub menu can have sub_items, sub_groups but not sub_menus
83 
84         ElementDescriptor sub_item = createElement(styleMap,
85                 "item", // xmlName //$NON-NLS-1$
86                 "Item", // uiName,
87                 null, // TODO SDK URL
88                 null, // extraAttribute
89                 null, // childrenElements,
90                 false /* mandatory */);
91 
92         ElementDescriptor sub_group = createElement(styleMap,
93                 "group", // xmlName //$NON-NLS-1$
94                 "Group", // uiName,
95                 null, // TODO SDK URL
96                 null, // extraAttribute
97                 new ElementDescriptor[] { sub_item }, // childrenElements,
98                 false /* mandatory */);
99 
100         ElementDescriptor sub_menu = createElement(styleMap,
101                 TAG_MENU, // xmlName
102                 "Sub-Menu", // uiName,
103                 null, // TODO SDK URL
104                 null, // extraAttribute
105                 new ElementDescriptor[] { sub_item, sub_group }, // childrenElements,
106                 true /* mandatory */);
107 
108         // -- top menu can have all top groups and top items (which can have sub menus)
109 
110         ElementDescriptor top_item = createElement(styleMap,
111                 "item", // xmlName //$NON-NLS-1$
112                 "Item", // uiName,
113                 null, // TODO SDK URL
114                 null, // extraAttribute
115                 new ElementDescriptor[] { sub_menu }, // childrenElements,
116                 false /* mandatory */);
117 
118         ElementDescriptor top_group = createElement(styleMap,
119                 "group", // xmlName //$NON-NLS-1$
120                 "Group", // uiName,
121                 null, // TODO SDK URL
122                 null, // extraAttribute
123                 new ElementDescriptor[] { top_item }, // childrenElements,
124                 false /* mandatory */);
125 
126         XmlnsAttributeDescriptor xmlns = new XmlnsAttributeDescriptor(ANDROID_NS_NAME,
127                 ANDROID_URI);
128 
129         updateElement(mDescriptor, styleMap, "Menu", xmlns); //$NON-NLS-1$
130         mDescriptor.setChildren(new ElementDescriptor[] { top_item, top_group });
131     }
132 
133     /**
134      * Returns a new ElementDescriptor constructed from the information given here
135      * and the javadoc & attributes extracted from the style map if any.
136      */
createElement( Map<String, DeclareStyleableInfo> styleMap, String xmlName, String uiName, String sdkUrl, AttributeDescriptor extraAttribute, ElementDescriptor[] childrenElements, boolean mandatory)137     private ElementDescriptor createElement(
138             Map<String, DeclareStyleableInfo> styleMap,
139             String xmlName, String uiName, String sdkUrl,
140             AttributeDescriptor extraAttribute,
141             ElementDescriptor[] childrenElements, boolean mandatory) {
142 
143         ElementDescriptor element = new ElementDescriptor(xmlName, uiName, null, sdkUrl,
144                 null, childrenElements, mandatory);
145 
146         return updateElement(element, styleMap,
147                 getStyleName(xmlName),
148                 extraAttribute);
149     }
150 
151     /**
152      * Updates an ElementDescriptor with the javadoc & attributes extracted from the style
153      * map if any.
154      */
updateElement(ElementDescriptor element, Map<String, DeclareStyleableInfo> styleMap, String styleName, AttributeDescriptor extraAttribute)155     private ElementDescriptor updateElement(ElementDescriptor element,
156             Map<String, DeclareStyleableInfo> styleMap,
157             String styleName,
158             AttributeDescriptor extraAttribute) {
159         ArrayList<AttributeDescriptor> descs = new ArrayList<AttributeDescriptor>();
160 
161         DeclareStyleableInfo style = styleMap != null ? styleMap.get(styleName) : null;
162         if (style != null) {
163             DescriptorsUtils.appendAttributes(descs,
164                     null,   // elementName
165                     ANDROID_URI,
166                     style.getAttributes(),
167                     null,   // requiredAttributes
168                     null);  // overrides
169             element.setTooltip(style.getJavaDoc());
170         }
171 
172         if (extraAttribute != null) {
173             descs.add(extraAttribute);
174         }
175 
176         element.setAttributes(descs.toArray(new AttributeDescriptor[descs.size()]));
177         return element;
178     }
179 
180     /**
181      * Returns the style name (i.e. the <declare-styleable> name found in attrs.xml)
182      * for a given XML element name.
183      * <p/>
184      * The rule is that all elements have for style name:
185      * - their xml name capitalized
186      * - a "Menu" prefix, except for <menu> itself which is just "Menu".
187      */
getStyleName(String xmlName)188     private String getStyleName(String xmlName) {
189         String styleName = AdtUtils.capitalize(xmlName);
190 
191         // This is NOT the UI Name but the expected internal style name
192         final String MENU_STYLE_BASE_NAME = "Menu"; //$NON-NLS-1$
193 
194         if (!styleName.equals(MENU_STYLE_BASE_NAME)) {
195             styleName = MENU_STYLE_BASE_NAME + styleName;
196         }
197         return styleName;
198     }
199 }
200