• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007 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.descriptors;
18 
19 import com.android.ide.eclipse.adt.AdtPlugin;
20 import com.android.ide.eclipse.adt.internal.editors.IconFactory;
21 import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
22 import com.android.sdklib.SdkConstants;
23 
24 import org.eclipse.jface.resource.ImageDescriptor;
25 import org.eclipse.swt.graphics.Image;
26 
27 import java.util.Collection;
28 import java.util.HashSet;
29 import java.util.Set;
30 
31 /**
32  * {@link ElementDescriptor} describes the properties expected for a given XML element node.
33  *
34  * {@link ElementDescriptor} have an XML name, UI name, a tooltip, an SDK url,
35  * an attributes list and a children list.
36  *
37  * An UI node can be "mandatory", meaning the UI node is never deleted and it may lack
38  * an actual XML node attached. A non-mandatory UI node MUST have an XML node attached
39  * and it will cease to exist when the XML node ceases to exist.
40  */
41 public class ElementDescriptor {
42     /** The XML element node name. Case sensitive. */
43     private String mXmlName;
44     /** The XML element name for the user interface, typically capitalized. */
45     private String mUiName;
46     /** The list of allowed attributes. */
47     private AttributeDescriptor[] mAttributes;
48     /** The list of allowed children */
49     private ElementDescriptor[] mChildren;
50     /* An optional tooltip. Can be empty. */
51     private String mTooltip;
52     /** An optional SKD URL. Can be empty. */
53     private String mSdkUrl;
54     /** Whether this UI node must always exist (even for empty models). */
55     private boolean mMandatory;
56 
57     /**
58      * Constructs a new {@link ElementDescriptor} based on its XML name, UI name,
59      * tooltip, SDK url, attributes list, children list and mandatory.
60      *
61      * @param xml_name The XML element node name. Case sensitive.
62      * @param ui_name The XML element name for the user interface, typically capitalized.
63      * @param tooltip An optional tooltip. Can be null or empty.
64      * @param sdk_url An optional SKD URL. Can be null or empty.
65      * @param attributes The list of allowed attributes. Can be null or empty.
66      * @param children The list of allowed children. Can be null or empty.
67      * @param mandatory Whether this node must always exist (even for empty models). A mandatory
68      *  UI node is never deleted and it may lack an actual XML node attached. A non-mandatory
69      *  UI node MUST have an XML node attached and it will cease to exist when the XML node
70      *  ceases to exist.
71      */
ElementDescriptor(String xml_name, String ui_name, String tooltip, String sdk_url, AttributeDescriptor[] attributes, ElementDescriptor[] children, boolean mandatory)72     public ElementDescriptor(String xml_name, String ui_name, String tooltip, String sdk_url,
73             AttributeDescriptor[] attributes,
74             ElementDescriptor[] children,
75             boolean mandatory) {
76         mMandatory = mandatory;
77         mXmlName = xml_name;
78         mUiName = ui_name;
79         mTooltip = (tooltip != null && tooltip.length() > 0) ? tooltip : null;
80         mSdkUrl = (sdk_url != null && sdk_url.length() > 0) ? sdk_url : null;
81         setAttributes(attributes != null ? attributes : new AttributeDescriptor[]{});
82         mChildren = children != null ? children : new ElementDescriptor[]{};
83     }
84 
85     /**
86      * Constructs a new {@link ElementDescriptor} based on its XML name and children list.
87      * The UI name is build by capitalizing the XML name.
88      * The UI nodes will be non-mandatory.
89      *
90      * @param xml_name The XML element node name. Case sensitive.
91      * @param children The list of allowed children. Can be null or empty.
92      * @param mandatory Whether this node must always exist (even for empty models). A mandatory
93      *  UI node is never deleted and it may lack an actual XML node attached. A non-mandatory
94      *  UI node MUST have an XML node attached and it will cease to exist when the XML node
95      *  ceases to exist.
96      */
ElementDescriptor(String xml_name, ElementDescriptor[] children, boolean mandatory)97     public ElementDescriptor(String xml_name, ElementDescriptor[] children, boolean mandatory) {
98         this(xml_name, prettyName(xml_name), null, null, null, children, mandatory);
99     }
100 
101     /**
102      * Constructs a new {@link ElementDescriptor} based on its XML name and children list.
103      * The UI name is build by capitalizing the XML name.
104      * The UI nodes will be non-mandatory.
105      *
106      * @param xml_name The XML element node name. Case sensitive.
107      * @param children The list of allowed children. Can be null or empty.
108      */
ElementDescriptor(String xml_name, ElementDescriptor[] children)109     public ElementDescriptor(String xml_name, ElementDescriptor[] children) {
110         this(xml_name, prettyName(xml_name), null, null, null, children, false);
111     }
112 
113     /**
114      * Constructs a new {@link ElementDescriptor} based on its XML name.
115      * The UI name is build by capitalizing the XML name.
116      * The UI nodes will be non-mandatory.
117      *
118      * @param xml_name The XML element node name. Case sensitive.
119      */
ElementDescriptor(String xml_name)120     public ElementDescriptor(String xml_name) {
121         this(xml_name, prettyName(xml_name), null, null, null, null, false);
122     }
123 
124     /** Returns whether this node must always exist (even for empty models) */
isMandatory()125     public boolean isMandatory() {
126         return mMandatory;
127     }
128 
129     /**
130      * Returns the XML element node local name (case sensitive)
131      */
getXmlLocalName()132     public final String getXmlLocalName() {
133         int pos = mXmlName.indexOf(':');
134         if (pos != -1) {
135             return mXmlName.substring(pos+1);
136         }
137         return mXmlName;
138     }
139 
140     /**
141      * Returns the XML element node name, including the prefix.
142      * Case sensitive.
143      * <p/>
144      * In Android resources, the element node name for Android resources typically does not
145      * have a prefix and is typically the simple Java class name (e.g. "View"), whereas for
146      * custom views it is generally the fully qualified class name of the view (e.g.
147      * "com.mycompany.myapp.MyView").
148      * <p/>
149      * Most of the time you'll probably want to use {@link #getXmlLocalName()} to get a local
150      * name guaranteed without a prefix.
151      * <p/>
152      * Note that the prefix that <em>may</em> be available in this descriptor has nothing to
153      * do with the actual prefix the node might have (or needs to have) in the actual XML file
154      * since descriptors are fixed and do not depend on any current namespace defined in the
155      * target XML.
156      */
getXmlName()157     public String getXmlName() {
158         return mXmlName;
159     }
160 
161     /**
162      * Returns the namespace of the attribute.
163      */
getNamespace()164     public final String getNamespace() {
165         // For now we hard-code the prefix as being "android"
166         if (mXmlName.startsWith("android:")) { //$NON-NLs-1$
167             return SdkConstants.NS_RESOURCES;
168         }
169 
170         return ""; //$NON-NLs-1$
171     }
172 
173 
174     /** Returns the XML element name for the user interface, typically capitalized. */
getUiName()175     public String getUiName() {
176         return mUiName;
177     }
178 
179     /**
180      * Returns an optional icon for the element.
181      * <p/>
182      * By default this tries to return an icon based on the XML name of the element.
183      * If this fails, it tries to return the default Android logo as defined in the
184      * plugin. If all fails, it returns null.
185      *
186      * @return An icon for this element or null.
187      */
getIcon()188     public Image getIcon() {
189         IconFactory factory = IconFactory.getInstance();
190         int color = hasChildren() ? IconFactory.COLOR_BLUE : IconFactory.COLOR_GREEN;
191         int shape = hasChildren() ? IconFactory.SHAPE_RECT : IconFactory.SHAPE_CIRCLE;
192         Image icon = factory.getIcon(mXmlName, color, shape);
193         return icon != null ? icon : AdtPlugin.getAndroidLogo();
194     }
195 
196     /**
197      * Returns an optional ImageDescriptor for the element.
198      * <p/>
199      * By default this tries to return an image based on the XML name of the element.
200      * If this fails, it tries to return the default Android logo as defined in the
201      * plugin. If all fails, it returns null.
202      *
203      * @return An ImageDescriptor for this element or null.
204      */
getImageDescriptor()205     public ImageDescriptor getImageDescriptor() {
206         IconFactory factory = IconFactory.getInstance();
207         int color = hasChildren() ? IconFactory.COLOR_BLUE : IconFactory.COLOR_GREEN;
208         int shape = hasChildren() ? IconFactory.SHAPE_RECT : IconFactory.SHAPE_CIRCLE;
209         ImageDescriptor id = factory.getImageDescriptor(mXmlName, color, shape);
210         return id != null ? id : AdtPlugin.getAndroidLogoDesc();
211     }
212 
213     /* Returns the list of allowed attributes. */
getAttributes()214     public AttributeDescriptor[] getAttributes() {
215         return mAttributes;
216     }
217 
218     /* Sets the list of allowed attributes. */
setAttributes(AttributeDescriptor[] attributes)219     public void setAttributes(AttributeDescriptor[] attributes) {
220         mAttributes = attributes;
221         for (AttributeDescriptor attribute : attributes) {
222             attribute.setParent(this);
223         }
224     }
225 
226     /** Returns the list of allowed children */
getChildren()227     public ElementDescriptor[] getChildren() {
228         return mChildren;
229     }
230 
231     /** @return True if this descriptor has children available */
hasChildren()232     public boolean hasChildren() {
233         return mChildren.length > 0;
234     }
235 
236     /** Sets the list of allowed children. */
setChildren(ElementDescriptor[] newChildren)237     public void setChildren(ElementDescriptor[] newChildren) {
238         mChildren = newChildren;
239     }
240 
241     /**
242      * Sets the list of allowed children.
243      * <p/>
244      * This is just a convenience method that converts a Collection into an array and
245      * calls {@link #setChildren(ElementDescriptor[])}.
246      * <p/>
247      * This means a <em>copy</em> of the collection is made. The collection is not
248      * stored by the recipient and can thus be altered by the caller.
249      */
setChildren(Collection<ElementDescriptor> newChildren)250     public void setChildren(Collection<ElementDescriptor> newChildren) {
251         setChildren(newChildren.toArray(new ElementDescriptor[newChildren.size()]));
252     }
253 
254     /**
255      * Returns an optional tooltip. Will be null if not present.
256      * <p/>
257      * The tooltip is based on the Javadoc of the element and already processed via
258      * {@link DescriptorsUtils#formatTooltip(String)} to be displayed right away as
259      * a UI tooltip.
260      */
getTooltip()261     public String getTooltip() {
262         return mTooltip;
263     }
264 
265     /** Returns an optional SKD URL. Will be null if not present. */
getSdkUrl()266     public String getSdkUrl() {
267         return mSdkUrl;
268     }
269 
270     /** Sets the optional tooltip. Can be null or empty. */
setTooltip(String tooltip)271     public void setTooltip(String tooltip) {
272         mTooltip = tooltip;
273     }
274 
275     /** Sets the optional SDK URL. Can be null or empty. */
setSdkUrl(String sdkUrl)276     public void setSdkUrl(String sdkUrl) {
277         mSdkUrl = sdkUrl;
278     }
279 
280     /**
281      * @return A new {@link UiElementNode} linked to this descriptor.
282      */
createUiNode()283     public UiElementNode createUiNode() {
284         return new UiElementNode(this);
285     }
286 
287     /**
288      * Returns the first children of this descriptor that describes the given XML element name.
289      * <p/>
290      * In recursive mode, searches the direct children first before descending in the hierarchy.
291      *
292      * @return The ElementDescriptor matching the requested XML node element name or null.
293      */
findChildrenDescriptor(String element_name, boolean recursive)294     public ElementDescriptor findChildrenDescriptor(String element_name, boolean recursive) {
295         return findChildrenDescriptorInternal(element_name, recursive, null);
296     }
297 
findChildrenDescriptorInternal(String element_name, boolean recursive, Set<ElementDescriptor> visited)298     private ElementDescriptor findChildrenDescriptorInternal(String element_name,
299             boolean recursive,
300             Set<ElementDescriptor> visited) {
301         if (recursive && visited == null) {
302             visited = new HashSet<ElementDescriptor>();
303         }
304 
305         for (ElementDescriptor e : getChildren()) {
306             if (e.getXmlName().equals(element_name)) {
307                 return e;
308             }
309         }
310 
311         if (visited != null) {
312             visited.add(this);
313         }
314 
315         if (recursive) {
316             for (ElementDescriptor e : getChildren()) {
317                 if (visited != null) {
318                     if (!visited.add(e)) {  // Set.add() returns false if element is already present
319                         continue;
320                     }
321                 }
322                 ElementDescriptor f = e.findChildrenDescriptorInternal(element_name,
323                         recursive, visited);
324                 if (f != null) {
325                     return f;
326                 }
327             }
328         }
329 
330         return null;
331     }
332 
333     /**
334      * Utility helper than pretty-formats an XML Name for the UI.
335      * This is used by the simplified constructor that takes only an XML element name.
336      *
337      * @param xml_name The XML name to convert.
338      * @return The XML name with dashes replaced by spaces and capitalized.
339      */
prettyName(String xml_name)340     private static String prettyName(String xml_name) {
341         char c[] = xml_name.toCharArray();
342         if (c.length > 0) {
343             c[0] = Character.toUpperCase(c[0]);
344         }
345         return new String(c).replace("-", " ");  //$NON-NLS-1$  //$NON-NLS-2$
346     }
347 
348 }
349