FileDocCategorySizeDatePackage
AndroidManifestDescriptors.javaAPI DocAndroid 1.5 API24775Wed May 06 22:41:10 BST 2009com.android.ide.eclipse.editors.manifest.descriptors

AndroidManifestDescriptors

public final class AndroidManifestDescriptors extends Object implements com.android.ide.eclipse.editors.descriptors.IDescriptorProvider
Complete description of the AndroidManifest.xml structure.

The root element are static instances which always exists. However their sub-elements and attributes are created only when the SDK changes or is loaded the first time.

Fields Summary
private static final String
MANIFEST_NODE_NAME
private static final String
ANDROID_MANIFEST_STYLEABLE
public static final String
ANDROID_LABEL_ATTR
public static final String
ANDROID_NAME_ATTR
public static final String
PACKAGE_ATTR
private final com.android.ide.eclipse.editors.descriptors.ElementDescriptor
MANIFEST_ELEMENT
The {@link ElementDescriptor} for the root Manifest element.
private final com.android.ide.eclipse.editors.descriptors.ElementDescriptor
APPLICATION_ELEMENT
The {@link ElementDescriptor} for the root Application element.
private final com.android.ide.eclipse.editors.descriptors.ElementDescriptor
INTRUMENTATION_ELEMENT
The {@link ElementDescriptor} for the root Instrumentation element.
private final com.android.ide.eclipse.editors.descriptors.ElementDescriptor
PERMISSION_ELEMENT
The {@link ElementDescriptor} for the root Permission element.
private final com.android.ide.eclipse.editors.descriptors.ElementDescriptor
USES_PERMISSION_ELEMENT
The {@link ElementDescriptor} for the root UsesPermission element.
private final com.android.ide.eclipse.editors.descriptors.ElementDescriptor
USES_SDK_ELEMENT
The {@link ElementDescriptor} for the root UsesSdk element.
private final com.android.ide.eclipse.editors.descriptors.ElementDescriptor
PERMISSION_GROUP_ELEMENT
The {@link ElementDescriptor} for the root PermissionGroup element.
private final com.android.ide.eclipse.editors.descriptors.ElementDescriptor
PERMISSION_TREE_ELEMENT
The {@link ElementDescriptor} for the root PermissionTree element.
private final com.android.ide.eclipse.editors.descriptors.TextAttributeDescriptor
PACKAGE_ATTR_DESC
Private package attribute for the manifest element. Needs to be handled manually.
Constructors Summary
public AndroidManifestDescriptors()

    
      
        APPLICATION_ELEMENT = createElement("application", null, true); //$NON-NLS-1$ + no child & mandatory
        INTRUMENTATION_ELEMENT = createElement("instrumentation"); //$NON-NLS-1$

        PERMISSION_ELEMENT = createElement("permission"); //$NON-NLS-1$
        USES_PERMISSION_ELEMENT = createElement("uses-permission"); //$NON-NLS-1$
        USES_SDK_ELEMENT = createElement("uses-sdk", null, true); //$NON-NLS-1$ + no child & mandatory

        PERMISSION_GROUP_ELEMENT = createElement("permission-group"); //$NON-NLS-1$
        PERMISSION_TREE_ELEMENT = createElement("permission-tree"); //$NON-NLS-1$

        MANIFEST_ELEMENT = createElement(
                        MANIFEST_NODE_NAME, // xml name
                        new ElementDescriptor[] {
                                        APPLICATION_ELEMENT,
                                        INTRUMENTATION_ELEMENT,
                                        PERMISSION_ELEMENT,
                                        USES_PERMISSION_ELEMENT,
                                        PERMISSION_GROUP_ELEMENT,
                                        PERMISSION_TREE_ELEMENT,
                                        USES_SDK_ELEMENT,
                        },
                        true /* mandatory */);

        // The "package" attribute is treated differently as it doesn't have the standard
        // Android XML namespace.
        PACKAGE_ATTR_DESC = new PackageAttributeDescriptor(PACKAGE_ATTR,
                "Package",
                null /* nsUri */,
                "This attribute gives a unique name for the package, using a Java-style naming convention to avoid name collisions.\nFor example, applications published by Google could have names of the form com.google.app.appname");
    
Methods Summary
private com.android.ide.eclipse.editors.descriptors.ElementDescriptorcreateElement(java.lang.String xmlName, com.android.ide.eclipse.editors.descriptors.ElementDescriptor[] childrenElements, boolean mandatory)
Returns a new ElementDescriptor constructed from the information given here and the javadoc & attributes extracted from the style map if any.

Creates an element with no attribute overrides.

        // Creates an element with no attribute overrides.
        String styleName = guessStyleName(xmlName);
        String sdkUrl = DescriptorsUtils.MANIFEST_SDK_URL + styleName; 
        String uiName = getUiName(xmlName);

        ElementDescriptor element = new ManifestElementDescriptor(xmlName, uiName, null, sdkUrl,
                null, childrenElements, mandatory);

        return element;
    
private com.android.ide.eclipse.editors.descriptors.ElementDescriptorcreateElement(java.lang.String xmlName)
Returns a new ElementDescriptor constructed from its XML local name.

This version creates an element not mandatory.

        // Creates an element with no child and not mandatory
        return createElement(xmlName, null, false);
    
private voidfindAllElementNames(com.android.ide.eclipse.editors.descriptors.ElementDescriptor element, java.util.TreeSet declared)
Helper method used by {@link #sanityCheck(Map, ElementDescriptor)} to find all the {@link ElementDescriptor} names defined by the tree of descriptors.

Note: this assumes no circular reference in the tree of {@link ElementDescriptor}s.

        declared.add(element.getXmlName());
        for (ElementDescriptor desc : element.getChildren()) {
            findAllElementNames(desc, declared);
        }
    
public com.android.ide.eclipse.editors.descriptors.ElementDescriptorgetApplicationElement()

        return APPLICATION_ELEMENT;
    
public com.android.ide.eclipse.editors.descriptors.ElementDescriptorgetDescriptor()

        return getManifestElement();
    
public com.android.ide.eclipse.editors.descriptors.ElementDescriptorgetInstrumentationElement()

        return INTRUMENTATION_ELEMENT;
    
public com.android.ide.eclipse.editors.descriptors.ElementDescriptorgetManifestElement()

        return MANIFEST_ELEMENT;
    
public com.android.ide.eclipse.editors.descriptors.ElementDescriptorgetPermissionElement()

        return PERMISSION_ELEMENT;
    
public com.android.ide.eclipse.editors.descriptors.ElementDescriptorgetPermissionGroupElement()

        return PERMISSION_GROUP_ELEMENT;
    
public com.android.ide.eclipse.editors.descriptors.ElementDescriptorgetPermissionTreeElement()

        return PERMISSION_TREE_ELEMENT;
    
public com.android.ide.eclipse.editors.descriptors.ElementDescriptor[]getRootElementDescriptors()

        return new ElementDescriptor[] { MANIFEST_ELEMENT };
    
private java.lang.StringgetUiName(java.lang.String xmlName)
Get an UI name from the element XML name.

Capitalizes the first letter and replace non-alphabet by a space followed by a capital.

        StringBuilder sb = new StringBuilder();

        boolean capitalize = true;
        for (char c : xmlName.toCharArray()) {
            if (capitalize && c >= 'a" && c <= 'z") {
                sb.append((char)(c + 'A" - 'a"));
                capitalize = false;
            } else if ((c < 'A" || c > 'Z") && (c < 'a" || c > 'z")) {
                sb.append(' ");
                capitalize = true;
            } else {
                sb.append(c);
            }
        }
        
        return sb.toString();
    
public com.android.ide.eclipse.editors.descriptors.ElementDescriptorgetUsesPermissionElement()

        return USES_PERMISSION_ELEMENT;
    
public com.android.ide.eclipse.editors.descriptors.ElementDescriptorgetUsesSdkElement()

        return USES_SDK_ELEMENT;
    
private java.lang.StringguessStyleName(java.lang.String xmlName)
Guesses the style name for a given XML element name.

The rules are: - capitalize the first letter: - if there's a dash, skip it and capitalize the next one - prefix AndroidManifest The exception is "manifest" which just becomes AndroidManifest.

Examples: - manifest => AndroidManifest - application => AndroidManifestApplication - uses-permission => AndroidManifestUsesPermission

        StringBuilder sb = new StringBuilder();

        if (!xmlName.equals(MANIFEST_NODE_NAME)) {
            boolean capitalize = true;
            for (char c : xmlName.toCharArray()) {
                if (capitalize && c >= 'a" && c <= 'z") {
                    sb.append((char)(c + 'A" - 'a"));
                    capitalize = false;
                } else if ((c < 'A" || c > 'Z") && (c < 'a" || c > 'z")) {
                    // not a letter -- skip the character and capitalize the next one
                    capitalize = true;
                } else {
                    sb.append(c);
                }
            }
        }
        
        sb.insert(0, ANDROID_MANIFEST_STYLEABLE);
        return sb.toString();
    
private java.lang.StringguessXmlName(java.lang.String name)
Performs an approximate translation of the style name into a potential xml name. This is more or less the reverse from guessStyleName().

return
The XML local name for a given style name.

        StringBuilder sb = new StringBuilder();
        if (ANDROID_MANIFEST_STYLEABLE.equals(name)) {
            sb.append(MANIFEST_NODE_NAME);
        } else {
            name = name.replace(ANDROID_MANIFEST_STYLEABLE, "");    //$NON-NLS-1$
            boolean first_char = true;
            for (char c : name.toCharArray()) {
                if (c >= 'A" && c <= 'Z") {
                    if (!first_char) {
                        sb.append('-");
                    }
                    c = (char) (c - 'A" + 'a");
                }
                sb.append(c);
                first_char = false;
            }
        }
        return sb.toString();
    
private voidinflateElement(java.util.Map styleMap, java.util.Map overrides, java.util.Set requiredAttributes, java.util.HashMap existingElementDescs, com.android.ide.eclipse.editors.descriptors.ElementDescriptor elemDesc, java.lang.String styleName)
"Inflates" the properties of an {@link ElementDescriptor} from the styleable declaration.

This first creates all the attributes for the given ElementDescriptor. It then finds all children of the descriptor, inflate them recursively and set them as child to this ElementDescriptor.

param
styleMap The input styleable map for manifest elements & attributes.
param
overrides A list of attribute overrides (to customize the type of the attribute descriptors).
param
requiredAttributes Set of attributes to be marked as required.
param
existingElementDescs A map of already created element descriptors, keyed by XML local name. This is used to use the static elements created initially by this class, which are referenced directly by editors (so that reloading an SDK won't break these references).
param
elemDesc The current {@link ElementDescriptor} to inflate.
param
styleName The name of the {@link ElementDescriptor} to inflate. Its XML local name will be guessed automatically from the style name.

        assert elemDesc != null;
        assert styleName != null;
        
        // define attributes
        DeclareStyleableInfo style = styleMap != null ? styleMap.get(styleName) : null;
        if (style != null) {
            ArrayList<AttributeDescriptor> attrDescs = new ArrayList<AttributeDescriptor>();
            DescriptorsUtils.appendAttributes(attrDescs,
                    elemDesc.getXmlLocalName(),
                    SdkConstants.NS_RESOURCES,
                    style.getAttributes(),
                    requiredAttributes,
                    overrides);
            elemDesc.setTooltip(style.getJavaDoc());
            elemDesc.setAttributes(attrDescs.toArray(new AttributeDescriptor[attrDescs.size()]));
        }
        
        // find all elements that have this one as parent
        ArrayList<ElementDescriptor> children = new ArrayList<ElementDescriptor>();
        for (Entry<String, DeclareStyleableInfo> entry : styleMap.entrySet()) {
            DeclareStyleableInfo childStyle = entry.getValue();
            boolean isParent = false;
            String[] parents = childStyle.getParents();
            if (parents != null) {
                for (String parent: parents) {
                    if (styleName.equals(parent)) {
                        isParent = true;
                        break;
                    }
                }
            }
            if (isParent) {
                String childStyleName = entry.getKey();
                String childXmlName = guessXmlName(childStyleName);
                
                // create or re-use element
                ElementDescriptor child = existingElementDescs.get(childXmlName);
                if (child == null) {
                    child = createElement(childXmlName);
                    existingElementDescs.put(childXmlName, child);
                }
                children.add(child);
                
                inflateElement(styleMap,
                        overrides,
                        requiredAttributes,
                        existingElementDescs,
                        child,
                        childStyleName);
            }
        }
        elemDesc.setChildren(children.toArray(new ElementDescriptor[children.size()]));
    
private voidinsertAttribute(com.android.ide.eclipse.editors.descriptors.ElementDescriptor element, com.android.ide.eclipse.editors.descriptors.AttributeDescriptor newAttr)
Inserts an attribute in this element attribute list if it is not present there yet (based on the attribute XML name.) The attribute is inserted at the beginning of the attribute list.

        AttributeDescriptor[] attributes = element.getAttributes();
        for (AttributeDescriptor attr : attributes) {
            if (attr.getXmlLocalName().equals(newAttr.getXmlLocalName())) {
                return;
            }
        }
        
        AttributeDescriptor[] newArray = new AttributeDescriptor[attributes.length + 1];
        newArray[0] = newAttr;
        System.arraycopy(attributes, 0, newArray, 1, attributes.length);
        element.setAttributes(newArray);
    
private static voidoverrideClassName(java.util.Map overrides, java.lang.String elementName, java.lang.String className)
Sets up an attribute override for ANDROID_NAME_ATTR using a ClassAttributeDescriptor with the specified class name.

        overrides.put(elementName + "/" + ANDROID_NAME_ATTR,
                new DescriptorsUtils.ITextAttributeCreator() {
            public TextAttributeDescriptor create(String xmlName, String uiName, String nsUri,
                    String tooltip) {
                uiName += "*";  //$NON-NLS-1$
                if (AndroidConstants.CLASS_ACTIVITY.equals(className)) {
                    return new ClassAttributeDescriptor(
                            className,
                            PostActivityCreationAction.getAction(),
                            xmlName,
                            uiName,
                            nsUri,
                            tooltip,
                            true /*mandatory */,
                            true /*defaultToProjectOnly*/);
                } else if (AndroidConstants.CLASS_BROADCASTRECEIVER.equals(className)) {
                    return new ClassAttributeDescriptor(
                            className,
                            PostReceiverCreationAction.getAction(),
                            xmlName,
                            uiName,
                            nsUri,
                            tooltip,
                            true /*mandatory */,
                            true /*defaultToProjectOnly*/);
                } else if (AndroidConstants.CLASS_INSTRUMENTATION.equals(className)) {
                    return new ClassAttributeDescriptor(
                            className,
                            null, // no post action
                            xmlName,
                            uiName,
                            nsUri,
                            tooltip,
                            true /*mandatory */,
                            false /*defaultToProjectOnly*/);
                } else {
                    return new ClassAttributeDescriptor(
                            className,
                            xmlName,
                            uiName,
                            nsUri,
                            tooltip,
                            true /*mandatory */);
                }
            }
        });
    
private voidsanityCheck(java.util.Map manifestMap, com.android.ide.eclipse.editors.descriptors.ElementDescriptor manifestElement)
This method performs a sanity check to make sure all the styles declared in the manifestMap are actually defined in the actual element descriptors and reachable from the manifestElement root node.

        TreeSet<String> elementsDeclared = new TreeSet<String>();
        findAllElementNames(manifestElement, elementsDeclared);

        TreeSet<String> stylesDeclared = new TreeSet<String>();
        for (String styleName : manifestMap.keySet()) {
            if (styleName.startsWith(ANDROID_MANIFEST_STYLEABLE)) {
                stylesDeclared.add(styleName);
            }
        }
        
        for (Iterator<String> it = elementsDeclared.iterator(); it.hasNext();) {
            String xmlName = it.next();
            String styleName = guessStyleName(xmlName);
            if (stylesDeclared.remove(styleName)) {
                it.remove();
            }
        }

        StringBuilder sb = new StringBuilder();
        if (!stylesDeclared.isEmpty()) {
            sb.append("Warning, ADT/SDK Mismatch! The following elements are declared by the SDK but unknown to ADT: ");
            for (String name : stylesDeclared) {
                name = guessXmlName(name);
                
                if (name != stylesDeclared.last()) {
                    sb.append(", ");    //$NON-NLS-1$
                }
            }
            
            AdtPlugin.log(IStatus.WARNING, "%s", sb.toString());
            AdtPlugin.printToConsole((String)null, sb);
            sb.setLength(0);
        }

        if (!elementsDeclared.isEmpty()) {
            sb.append("Warning, ADT/SDK Mismatch! The following elements are declared by ADT but not by the SDK: ");
            for (String name : elementsDeclared) {
                sb.append(name);
                if (name != elementsDeclared.last()) {
                    sb.append(", ");    //$NON-NLS-1$
                }
            }

            AdtPlugin.log(IStatus.WARNING, "%s", sb.toString());
            AdtPlugin.printToConsole((String)null, sb);
        }
    
public synchronized voidupdateDescriptors(java.util.Map manifestMap)
Updates the document descriptor.

It first computes the new children of the descriptor and then updates them all at once.

param
manifestMap The map style => attributes from the attrs_manifest.xml file


        XmlnsAttributeDescriptor xmlns = new XmlnsAttributeDescriptor(
                "android", //$NON-NLS-1$
                SdkConstants.NS_RESOURCES); 

        // -- setup the required attributes overrides --
        
        Set<String> required = new HashSet<String>();
        required.add("provider/authorities");  //$NON-NLS-1$
        
        // -- setup the various attribute format overrides --
        
        // The key for each override is "element1,element2,.../attr-xml-local-name" or
        // "*/attr-xml-local-name" to match the attribute in any element.
        
        Map<String, Object> overrides = new HashMap<String, Object>();

        overrides.put("*/icon", new DescriptorsUtils.ITextAttributeCreator() { //$NON-NLS-1$
            public TextAttributeDescriptor create(String xmlName, String uiName, String nsUri,
                    String tooltip) {
                return new ReferenceAttributeDescriptor(
                        ResourceType.DRAWABLE,
                        xmlName, uiName, nsUri,
                        tooltip);
            }
        });
        
        overrides.put("*/theme",         ThemeAttributeDescriptor.class);   //$NON-NLS-1$
        overrides.put("*/permission",    ListAttributeDescriptor.class);    //$NON-NLS-1$
        overrides.put("*/targetPackage", PackageAttributeDescriptor.class); //$NON-NLS-1$
        
        overrides.put("uses-library/name", ListAttributeDescriptor.class);       //$NON-NLS-1$

        overrides.put("action,category,uses-permission/" + ANDROID_NAME_ATTR,    //$NON-NLS-1$
                      ListAttributeDescriptor.class);
        overrides.put("application/" + ANDROID_NAME_ATTR, ApplicationAttributeDescriptor.class);  //$NON-NLS-1$

        overrideClassName(overrides, "activity", AndroidConstants.CLASS_ACTIVITY);           //$NON-NLS-1$
        overrideClassName(overrides, "receiver", AndroidConstants.CLASS_BROADCASTRECEIVER);  //$NON-NLS-1$
        overrideClassName(overrides, "service", AndroidConstants.CLASS_SERVICE);             //$NON-NLS-1$
        overrideClassName(overrides, "provider", AndroidConstants.CLASS_CONTENTPROVIDER);    //$NON-NLS-1$
        overrideClassName(overrides, "instrumentation", AndroidConstants.CLASS_INSTRUMENTATION);    //$NON-NLS-1$

        // -- list element nodes already created --
        // These elements are referenced by already opened editors, so we want to update them
        // but not re-create them when reloading an SDK on the fly.
        
        HashMap<String, ElementDescriptor> elementDescs =
            new HashMap<String, ElementDescriptor>();
        elementDescs.put(MANIFEST_ELEMENT.getXmlLocalName(),         MANIFEST_ELEMENT);
        elementDescs.put(APPLICATION_ELEMENT.getXmlLocalName(),      APPLICATION_ELEMENT);
        elementDescs.put(INTRUMENTATION_ELEMENT.getXmlLocalName(),   INTRUMENTATION_ELEMENT);
        elementDescs.put(PERMISSION_ELEMENT.getXmlLocalName(),       PERMISSION_ELEMENT);
        elementDescs.put(USES_PERMISSION_ELEMENT.getXmlLocalName(),  USES_PERMISSION_ELEMENT);
        elementDescs.put(USES_SDK_ELEMENT.getXmlLocalName(),         USES_SDK_ELEMENT);
        elementDescs.put(PERMISSION_GROUP_ELEMENT.getXmlLocalName(), PERMISSION_GROUP_ELEMENT);
        elementDescs.put(PERMISSION_TREE_ELEMENT.getXmlLocalName(),  PERMISSION_TREE_ELEMENT);

        // --

        inflateElement(manifestMap,
                overrides,
                required,
                elementDescs,
                MANIFEST_ELEMENT,
                "AndroidManifest"); //$NON-NLS-1$
        insertAttribute(MANIFEST_ELEMENT, PACKAGE_ATTR_DESC);
        
        sanityCheck(manifestMap, MANIFEST_ELEMENT);