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