• 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     /** Returns the XML element node name. Case sensitive. */
getXmlName()141     public String getXmlName() {
142         return mXmlName;
143     }
144 
145     /**
146      * Returns the namespace of the attribute.
147      */
getNamespace()148     public final String getNamespace() {
149         // For now we hard-code the prefix as being "android"
150         if (mXmlName.startsWith("android:")) { //$NON-NLs-1$
151             return SdkConstants.NS_RESOURCES;
152         }
153 
154         return ""; //$NON-NLs-1$
155     }
156 
157 
158     /** Returns the XML element name for the user interface, typically capitalized. */
getUiName()159     public String getUiName() {
160         return mUiName;
161     }
162 
163     /**
164      * Returns an optional icon for the element.
165      * <p/>
166      * By default this tries to return an icon based on the XML name of the element.
167      * If this fails, it tries to return the default Android logo as defined in the
168      * plugin. If all fails, it returns null.
169      *
170      * @return An icon for this element or null.
171      */
getIcon()172     public Image getIcon() {
173         IconFactory factory = IconFactory.getInstance();
174         int color = hasChildren() ? IconFactory.COLOR_BLUE : IconFactory.COLOR_GREEN;
175         int shape = hasChildren() ? IconFactory.SHAPE_RECT : IconFactory.SHAPE_CIRCLE;
176         Image icon = factory.getIcon(mXmlName, color, shape);
177         return icon != null ? icon : AdtPlugin.getAndroidLogo();
178     }
179 
180     /**
181      * Returns an optional ImageDescriptor for the element.
182      * <p/>
183      * By default this tries to return an image based on the XML name of the element.
184      * If this fails, it tries to return the default Android logo as defined in the
185      * plugin. If all fails, it returns null.
186      *
187      * @return An ImageDescriptor for this element or null.
188      */
getImageDescriptor()189     public ImageDescriptor getImageDescriptor() {
190         IconFactory factory = IconFactory.getInstance();
191         int color = hasChildren() ? IconFactory.COLOR_BLUE : IconFactory.COLOR_GREEN;
192         int shape = hasChildren() ? IconFactory.SHAPE_RECT : IconFactory.SHAPE_CIRCLE;
193         ImageDescriptor id = factory.getImageDescriptor(mXmlName, color, shape);
194         return id != null ? id : AdtPlugin.getAndroidLogoDesc();
195     }
196 
197     /* Returns the list of allowed attributes. */
getAttributes()198     public AttributeDescriptor[] getAttributes() {
199         return mAttributes;
200     }
201 
202     /* Sets the list of allowed attributes. */
setAttributes(AttributeDescriptor[] attributes)203     public void setAttributes(AttributeDescriptor[] attributes) {
204         mAttributes = attributes;
205         for (AttributeDescriptor attribute : attributes) {
206             attribute.setParent(this);
207         }
208     }
209 
210     /** Returns the list of allowed children */
getChildren()211     public ElementDescriptor[] getChildren() {
212         return mChildren;
213     }
214 
215     /** @return True if this descriptor has children available */
hasChildren()216     public boolean hasChildren() {
217         return mChildren.length > 0;
218     }
219 
220     /** Sets the list of allowed children. */
setChildren(ElementDescriptor[] newChildren)221     public void setChildren(ElementDescriptor[] newChildren) {
222         mChildren = newChildren;
223     }
224 
225     /** Sets the list of allowed children.
226      * <p/>
227      * This is just a convenience method that converts a Collection into an array and
228      * calls {@link #setChildren(ElementDescriptor[])}.
229      * <p/>
230      * This means a <em>copy</em> of the collection is made. The collection is not
231      * stored by the recipient and can thus be altered by the caller.
232      */
setChildren(Collection<ElementDescriptor> newChildren)233     public void setChildren(Collection<ElementDescriptor> newChildren) {
234         setChildren(newChildren.toArray(new ElementDescriptor[newChildren.size()]));
235     }
236 
237     /**
238      * Returns an optional tooltip. Will be null if not present.
239      * <p/>
240      * The tooltip is based on the Javadoc of the element and already processed via
241      * {@link DescriptorsUtils#formatTooltip(String)} to be displayed right away as
242      * a UI tooltip.
243      */
getTooltip()244     public String getTooltip() {
245         return mTooltip;
246     }
247 
248     /** Returns an optional SKD URL. Will be null if not present. */
getSdkUrl()249     public String getSdkUrl() {
250         return mSdkUrl;
251     }
252 
253     /** Sets the optional tooltip. Can be null or empty. */
setTooltip(String tooltip)254     public void setTooltip(String tooltip) {
255         mTooltip = tooltip;
256     }
257 
258     /** Sets the optional SDK URL. Can be null or empty. */
setSdkUrl(String sdkUrl)259     public void setSdkUrl(String sdkUrl) {
260         mSdkUrl = sdkUrl;
261     }
262 
263     /**
264      * @return A new {@link UiElementNode} linked to this descriptor.
265      */
createUiNode()266     public UiElementNode createUiNode() {
267         return new UiElementNode(this);
268     }
269 
270     /**
271      * Returns the first children of this descriptor that describes the given XML element name.
272      * <p/>
273      * In recursive mode, searches the direct children first before descending in the hierarchy.
274      *
275      * @return The ElementDescriptor matching the requested XML node element name or null.
276      */
findChildrenDescriptor(String element_name, boolean recursive)277     public ElementDescriptor findChildrenDescriptor(String element_name, boolean recursive) {
278         return findChildrenDescriptorInternal(element_name, recursive, null);
279     }
280 
findChildrenDescriptorInternal(String element_name, boolean recursive, Set<ElementDescriptor> visited)281     private ElementDescriptor findChildrenDescriptorInternal(String element_name,
282             boolean recursive,
283             Set<ElementDescriptor> visited) {
284         if (recursive && visited == null) {
285             visited = new HashSet<ElementDescriptor>();
286         }
287 
288         for (ElementDescriptor e : getChildren()) {
289             if (e.getXmlName().equals(element_name)) {
290                 return e;
291             }
292         }
293 
294         if (visited != null) {
295             visited.add(this);
296         }
297 
298         if (recursive) {
299             for (ElementDescriptor e : getChildren()) {
300                 if (visited != null) {
301                     if (!visited.add(e)) {  // Set.add() returns false if element is already present
302                         continue;
303                     }
304                 }
305                 ElementDescriptor f = e.findChildrenDescriptorInternal(element_name,
306                         recursive, visited);
307                 if (f != null) {
308                     return f;
309                 }
310             }
311         }
312 
313         return null;
314     }
315 
316     /**
317      * Utility helper than pretty-formats an XML Name for the UI.
318      * This is used by the simplified constructor that takes only an XML element name.
319      *
320      * @param xml_name The XML name to convert.
321      * @return The XML name with dashes replaced by spaces and capitalized.
322      */
prettyName(String xml_name)323     private static String prettyName(String xml_name) {
324         char c[] = xml_name.toCharArray();
325         if (c.length > 0) {
326             c[0] = Character.toUpperCase(c[0]);
327         }
328         return new String(c).replace("-", " ");  //$NON-NLS-1$  //$NON-NLS-2$
329     }
330 
331 }
332