aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/ElementDescriptor.java
diff options
context:
space:
mode:
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/ElementDescriptor.java')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/ElementDescriptor.java485
1 files changed, 485 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/ElementDescriptor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/ElementDescriptor.java
new file mode 100644
index 000000000..0d62ec00c
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/ElementDescriptor.java
@@ -0,0 +1,485 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.internal.editors.descriptors;
+
+import static com.android.SdkConstants.ANDROID_NS_NAME_PREFIX;
+import static com.android.SdkConstants.ANDROID_URI;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.swt.graphics.Image;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * {@link ElementDescriptor} describes the properties expected for a given XML element node.
+ *
+ * {@link ElementDescriptor} have an XML name, UI name, a tooltip, an SDK url,
+ * an attributes list and a children list.
+ *
+ * An UI node can be "mandatory", meaning the UI node is never deleted and it may lack
+ * an actual XML node attached. A non-mandatory UI node MUST have an XML node attached
+ * and it will cease to exist when the XML node ceases to exist.
+ */
+public class ElementDescriptor implements Comparable<ElementDescriptor> {
+ private static final String ELEMENT_ICON_FILENAME = "element"; //$NON-NLS-1$
+
+ /** The XML element node name. Case sensitive. */
+ protected final String mXmlName;
+ /** The XML element name for the user interface, typically capitalized. */
+ private final String mUiName;
+ /** The list of allowed attributes. */
+ private AttributeDescriptor[] mAttributes;
+ /** The list of allowed children */
+ private ElementDescriptor[] mChildren;
+ /* An optional tooltip. Can be empty. */
+ private String mTooltip;
+ /** An optional SKD URL. Can be empty. */
+ private String mSdkUrl;
+ /** Whether this UI node must always exist (even for empty models). */
+ private final Mandatory mMandatory;
+
+ public enum Mandatory {
+ NOT_MANDATORY,
+ MANDATORY,
+ MANDATORY_LAST
+ }
+
+ /**
+ * Constructs a new {@link ElementDescriptor} based on its XML name, UI name,
+ * tooltip, SDK url, attributes list, children list and mandatory.
+ *
+ * @param xml_name The XML element node name. Case sensitive.
+ * @param ui_name The XML element name for the user interface, typically capitalized.
+ * @param tooltip An optional tooltip. Can be null or empty.
+ * @param sdk_url An optional SKD URL. Can be null or empty.
+ * @param attributes The list of allowed attributes. Can be null or empty.
+ * @param children The list of allowed children. Can be null or empty.
+ * @param mandatory Whether this node must always exist (even for empty models). A mandatory
+ * UI node is never deleted and it may lack an actual XML node attached. A non-mandatory
+ * UI node MUST have an XML node attached and it will cease to exist when the XML node
+ * ceases to exist.
+ */
+ public ElementDescriptor(String xml_name, String ui_name, String tooltip, String sdk_url,
+ AttributeDescriptor[] attributes,
+ ElementDescriptor[] children,
+ Mandatory mandatory) {
+ mMandatory = mandatory;
+ mXmlName = xml_name;
+ mUiName = ui_name;
+ mTooltip = (tooltip != null && tooltip.length() > 0) ? tooltip : null;
+ mSdkUrl = (sdk_url != null && sdk_url.length() > 0) ? sdk_url : null;
+ setAttributes(attributes != null ? attributes : new AttributeDescriptor[]{});
+ mChildren = children != null ? children : new ElementDescriptor[]{};
+ }
+
+ /**
+ * Constructs a new {@link ElementDescriptor} based on its XML name, UI name,
+ * tooltip, SDK url, attributes list, children list and mandatory.
+ *
+ * @param xml_name The XML element node name. Case sensitive.
+ * @param ui_name The XML element name for the user interface, typically capitalized.
+ * @param tooltip An optional tooltip. Can be null or empty.
+ * @param sdk_url An optional SKD URL. Can be null or empty.
+ * @param attributes The list of allowed attributes. Can be null or empty.
+ * @param children The list of allowed children. Can be null or empty.
+ * @param mandatory Whether this node must always exist (even for empty models). A mandatory
+ * UI node is never deleted and it may lack an actual XML node attached. A non-mandatory
+ * UI node MUST have an XML node attached and it will cease to exist when the XML node
+ * ceases to exist.
+ */
+ public ElementDescriptor(String xml_name, String ui_name, String tooltip, String sdk_url,
+ AttributeDescriptor[] attributes,
+ ElementDescriptor[] children,
+ boolean mandatory) {
+ mMandatory = mandatory ? Mandatory.MANDATORY : Mandatory.NOT_MANDATORY;
+ mXmlName = xml_name;
+ mUiName = ui_name;
+ mTooltip = (tooltip != null && tooltip.length() > 0) ? tooltip : null;
+ mSdkUrl = (sdk_url != null && sdk_url.length() > 0) ? sdk_url : null;
+ setAttributes(attributes != null ? attributes : new AttributeDescriptor[]{});
+ mChildren = children != null ? children : new ElementDescriptor[]{};
+ }
+
+ /**
+ * Constructs a new {@link ElementDescriptor} based on its XML name and children list.
+ * The UI name is build by capitalizing the XML name.
+ * The UI nodes will be non-mandatory.
+ *
+ * @param xml_name The XML element node name. Case sensitive.
+ * @param children The list of allowed children. Can be null or empty.
+ * @param mandatory Whether this node must always exist (even for empty models). A mandatory
+ * UI node is never deleted and it may lack an actual XML node attached. A non-mandatory
+ * UI node MUST have an XML node attached and it will cease to exist when the XML node
+ * ceases to exist.
+ */
+ public ElementDescriptor(String xml_name, ElementDescriptor[] children, Mandatory mandatory) {
+ this(xml_name, prettyName(xml_name), null, null, null, children, mandatory);
+ }
+
+ /**
+ * Constructs a new {@link ElementDescriptor} based on its XML name and children list.
+ * The UI name is build by capitalizing the XML name.
+ * The UI nodes will be non-mandatory.
+ *
+ * @param xml_name The XML element node name. Case sensitive.
+ * @param children The list of allowed children. Can be null or empty.
+ */
+ public ElementDescriptor(String xml_name, ElementDescriptor[] children) {
+ this(xml_name, prettyName(xml_name), null, null, null, children, false);
+ }
+
+ /**
+ * Constructs a new {@link ElementDescriptor} based on its XML name.
+ * The UI name is build by capitalizing the XML name.
+ * The UI nodes will be non-mandatory.
+ *
+ * @param xml_name The XML element node name. Case sensitive.
+ */
+ public ElementDescriptor(String xml_name) {
+ this(xml_name, prettyName(xml_name), null, null, null, null, false);
+ }
+
+ /** Returns whether this node must always exist (even for empty models) */
+ public Mandatory getMandatory() {
+ return mMandatory;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s [%s, attr %d, children %d%s]", //$NON-NLS-1$
+ this.getClass().getSimpleName(),
+ mXmlName,
+ mAttributes != null ? mAttributes.length : 0,
+ mChildren != null ? mChildren.length : 0,
+ mMandatory != Mandatory.NOT_MANDATORY ? ", " + mMandatory.toString() : "" //$NON-NLS-1$ //$NON-NLS-2$
+ );
+ }
+
+ /**
+ * Returns the XML element node local name (case sensitive)
+ */
+ public final String getXmlLocalName() {
+ int pos = mXmlName.indexOf(':');
+ if (pos != -1) {
+ return mXmlName.substring(pos+1);
+ }
+ return mXmlName;
+ }
+
+ /**
+ * Returns the XML element node name, including the prefix.
+ * Case sensitive.
+ * <p/>
+ * In Android resources, the element node name for Android resources typically does not
+ * have a prefix and is typically the simple Java class name (e.g. "View"), whereas for
+ * custom views it is generally the fully qualified class name of the view (e.g.
+ * "com.mycompany.myapp.MyView").
+ * <p/>
+ * Most of the time you'll probably want to use {@link #getXmlLocalName()} to get a local
+ * name guaranteed without a prefix.
+ * <p/>
+ * Note that the prefix that <em>may</em> be available in this descriptor has nothing to
+ * do with the actual prefix the node might have (or needs to have) in the actual XML file
+ * since descriptors are fixed and do not depend on any current namespace defined in the
+ * target XML.
+ */
+ public String getXmlName() {
+ return mXmlName;
+ }
+
+ /**
+ * Returns the namespace of the attribute.
+ */
+ public final String getNamespace() {
+ // For now we hard-code the prefix as being "android"
+ if (mXmlName.startsWith(ANDROID_NS_NAME_PREFIX)) {
+ return ANDROID_URI;
+ }
+
+ return ""; //$NON-NLs-1$
+ }
+
+
+ /** Returns the XML element name for the user interface, typically capitalized. */
+ public String getUiName() {
+ return mUiName;
+ }
+
+ /**
+ * Returns an icon for the element.
+ * This icon is generic, that is all element descriptors have the same icon
+ * no matter what they represent.
+ *
+ * @return An icon for this element or null.
+ * @see #getCustomizedIcon()
+ */
+ public Image getGenericIcon() {
+ return IconFactory.getInstance().getIcon(ELEMENT_ICON_FILENAME);
+ }
+
+ /**
+ * Returns an optional icon for the element, typically to be used in XML form trees.
+ * <p/>
+ * This icon is customized to the given descriptor, that is different elements
+ * will have different icons.
+ * <p/>
+ * By default this tries to return an icon based on the XML name of the element.
+ * If this fails, it tries to return the default Android logo as defined in the
+ * plugin. If all fails, it returns null.
+ *
+ * @return An icon for this element. This is never null.
+ */
+ public Image getCustomizedIcon() {
+ IconFactory factory = IconFactory.getInstance();
+ int color = hasChildren() ? IconFactory.COLOR_BLUE
+ : IconFactory.COLOR_GREEN;
+ int shape = hasChildren() ? IconFactory.SHAPE_RECT
+ : IconFactory.SHAPE_CIRCLE;
+ String name = mXmlName;
+
+ int pos = name.lastIndexOf('.');
+ if (pos != -1) {
+ // If the user uses a fully qualified name, such as
+ // "android.gesture.GestureOverlayView" in their XML, we need to
+ // look up only by basename
+ name = name.substring(pos + 1);
+ }
+ Image icon = factory.getIcon(name, color, shape);
+ if (icon == null) {
+ icon = getGenericIcon();
+ }
+ if (icon == null) {
+ icon = AdtPlugin.getAndroidLogo();
+ }
+ return icon;
+ }
+
+ /**
+ * Returns an optional ImageDescriptor for the element.
+ * <p/>
+ * By default this tries to return an image based on the XML name of the element.
+ * If this fails, it tries to return the default Android logo as defined in the
+ * plugin. If all fails, it returns null.
+ *
+ * @return An ImageDescriptor for this element or null.
+ */
+ public ImageDescriptor getImageDescriptor() {
+ IconFactory factory = IconFactory.getInstance();
+ int color = hasChildren() ? IconFactory.COLOR_BLUE : IconFactory.COLOR_GREEN;
+ int shape = hasChildren() ? IconFactory.SHAPE_RECT : IconFactory.SHAPE_CIRCLE;
+ ImageDescriptor id = factory.getImageDescriptor(mXmlName, color, shape);
+ return id != null ? id : AdtPlugin.getAndroidLogoDesc();
+ }
+
+ /* Returns the list of allowed attributes. */
+ public AttributeDescriptor[] getAttributes() {
+ return mAttributes;
+ }
+
+ /** Sets the list of allowed attributes. */
+ public void setAttributes(AttributeDescriptor[] attributes) {
+ mAttributes = attributes;
+ for (AttributeDescriptor attribute : attributes) {
+ attribute.setParent(this);
+ }
+ }
+
+ /** Returns the list of allowed children */
+ public ElementDescriptor[] getChildren() {
+ return mChildren;
+ }
+
+ /** @return True if this descriptor has children available */
+ public boolean hasChildren() {
+ return mChildren.length > 0;
+ }
+
+ /**
+ * Checks whether this descriptor can accept the given descriptor type
+ * as a direct child.
+ *
+ * @return True if this descriptor can accept children of the given descriptor type.
+ * False if not accepted, no children allowed, or target is null.
+ */
+ public boolean acceptChild(ElementDescriptor target) {
+ if (target != null && mChildren.length > 0) {
+ String targetXmlName = target.getXmlName();
+ for (ElementDescriptor child : mChildren) {
+ if (child.getXmlName().equals(targetXmlName)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /** Sets the list of allowed children. */
+ public void setChildren(ElementDescriptor[] newChildren) {
+ mChildren = newChildren;
+ }
+
+ /**
+ * Sets the list of allowed children.
+ * <p/>
+ * This is just a convenience method that converts a Collection into an array and
+ * calls {@link #setChildren(ElementDescriptor[])}.
+ * <p/>
+ * This means a <em>copy</em> of the collection is made. The collection is not
+ * stored by the recipient and can thus be altered by the caller.
+ */
+ public void setChildren(Collection<ElementDescriptor> newChildren) {
+ setChildren(newChildren.toArray(new ElementDescriptor[newChildren.size()]));
+ }
+
+ /**
+ * Returns an optional tooltip. Will be null if not present.
+ * <p/>
+ * The tooltip is based on the Javadoc of the element and already processed via
+ * {@link DescriptorsUtils#formatTooltip(String)} to be displayed right away as
+ * a UI tooltip.
+ */
+ public String getTooltip() {
+ return mTooltip;
+ }
+
+ /** Returns an optional SKD URL. Will be null if not present. */
+ public String getSdkUrl() {
+ return mSdkUrl;
+ }
+
+ /** Sets the optional tooltip. Can be null or empty. */
+ public void setTooltip(String tooltip) {
+ mTooltip = tooltip;
+ }
+
+ /** Sets the optional SDK URL. Can be null or empty. */
+ public void setSdkUrl(String sdkUrl) {
+ mSdkUrl = sdkUrl;
+ }
+
+ /**
+ * @return A new {@link UiElementNode} linked to this descriptor.
+ */
+ public UiElementNode createUiNode() {
+ return new UiElementNode(this);
+ }
+
+ /**
+ * Returns the first children of this descriptor that describes the given XML element name.
+ * <p/>
+ * In recursive mode, searches the direct children first before descending in the hierarchy.
+ *
+ * @return The ElementDescriptor matching the requested XML node element name or null.
+ */
+ public ElementDescriptor findChildrenDescriptor(String element_name, boolean recursive) {
+ return findChildrenDescriptorInternal(element_name, recursive, null);
+ }
+
+ private ElementDescriptor findChildrenDescriptorInternal(String element_name,
+ boolean recursive,
+ Set<ElementDescriptor> visited) {
+ if (recursive && visited == null) {
+ visited = new HashSet<ElementDescriptor>();
+ }
+
+ for (ElementDescriptor e : getChildren()) {
+ if (e.getXmlName().equals(element_name)) {
+ return e;
+ }
+ }
+
+ if (visited != null) {
+ visited.add(this);
+ }
+
+ if (recursive) {
+ for (ElementDescriptor e : getChildren()) {
+ if (visited != null) {
+ if (!visited.add(e)) { // Set.add() returns false if element is already present
+ continue;
+ }
+ }
+ ElementDescriptor f = e.findChildrenDescriptorInternal(element_name,
+ recursive, visited);
+ if (f != null) {
+ return f;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Utility helper than pretty-formats an XML Name for the UI.
+ * This is used by the simplified constructor that takes only an XML element name.
+ *
+ * @param xml_name The XML name to convert.
+ * @return The XML name with dashes replaced by spaces and capitalized.
+ */
+ private static String prettyName(String xml_name) {
+ char c[] = xml_name.toCharArray();
+ if (c.length > 0) {
+ c[0] = Character.toUpperCase(c[0]);
+ }
+ return new String(c).replace("-", " "); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ /**
+ * Returns true if this node defines the given attribute
+ *
+ * @param namespaceUri the namespace URI of the target attribute
+ * @param attributeName the attribute name
+ * @return true if this element defines an attribute of the given name and namespace
+ */
+ public boolean definesAttribute(String namespaceUri, String attributeName) {
+ for (AttributeDescriptor desc : mAttributes) {
+ if (desc.getXmlLocalName().equals(attributeName) &&
+ desc.getNamespaceUri().equals(namespaceUri)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ // Implements Comparable<ElementDescriptor>:
+ @Override
+ public int compareTo(ElementDescriptor o) {
+ return mUiName.compareToIgnoreCase(o.mUiName);
+ }
+
+ /**
+ * Ensures that this view descriptor's attribute list is up to date. This is
+ * always the case for all the builtin descriptors, but for example for a
+ * custom view, it could be changing dynamically so caches may have to be
+ * recomputed. This method will return true if nothing changed, and false if
+ * it recomputed its info.
+ *
+ * @return true if the attributes are already up to date and nothing changed
+ */
+ public boolean syncAttributes() {
+ return true;
+ }
+}