AndroidManifestDescriptorspublic final class AndroidManifestDescriptors extends Object implements com.android.ide.eclipse.editors.descriptors.IDescriptorProviderComplete 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_ELEMENTThe {@link ElementDescriptor} for the root Manifest element. | private final com.android.ide.eclipse.editors.descriptors.ElementDescriptor | APPLICATION_ELEMENTThe {@link ElementDescriptor} for the root Application element. | private final com.android.ide.eclipse.editors.descriptors.ElementDescriptor | INTRUMENTATION_ELEMENTThe {@link ElementDescriptor} for the root Instrumentation element. | private final com.android.ide.eclipse.editors.descriptors.ElementDescriptor | PERMISSION_ELEMENTThe {@link ElementDescriptor} for the root Permission element. | private final com.android.ide.eclipse.editors.descriptors.ElementDescriptor | USES_PERMISSION_ELEMENTThe {@link ElementDescriptor} for the root UsesPermission element. | private final com.android.ide.eclipse.editors.descriptors.ElementDescriptor | USES_SDK_ELEMENTThe {@link ElementDescriptor} for the root UsesSdk element. | private final com.android.ide.eclipse.editors.descriptors.ElementDescriptor | PERMISSION_GROUP_ELEMENTThe {@link ElementDescriptor} for the root PermissionGroup element. | private final com.android.ide.eclipse.editors.descriptors.ElementDescriptor | PERMISSION_TREE_ELEMENTThe {@link ElementDescriptor} for the root PermissionTree element. | private final com.android.ide.eclipse.editors.descriptors.TextAttributeDescriptor | PACKAGE_ATTR_DESCPrivate 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.ElementDescriptor | createElement(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.ElementDescriptor | createElement(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 void | findAllElementNames(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.ElementDescriptor | getApplicationElement()
return APPLICATION_ELEMENT;
| public com.android.ide.eclipse.editors.descriptors.ElementDescriptor | getDescriptor()
return getManifestElement();
| public com.android.ide.eclipse.editors.descriptors.ElementDescriptor | getInstrumentationElement()
return INTRUMENTATION_ELEMENT;
| public com.android.ide.eclipse.editors.descriptors.ElementDescriptor | getManifestElement()
return MANIFEST_ELEMENT;
| public com.android.ide.eclipse.editors.descriptors.ElementDescriptor | getPermissionElement()
return PERMISSION_ELEMENT;
| public com.android.ide.eclipse.editors.descriptors.ElementDescriptor | getPermissionGroupElement()
return PERMISSION_GROUP_ELEMENT;
| public com.android.ide.eclipse.editors.descriptors.ElementDescriptor | getPermissionTreeElement()
return PERMISSION_TREE_ELEMENT;
| public com.android.ide.eclipse.editors.descriptors.ElementDescriptor[] | getRootElementDescriptors()
return new ElementDescriptor[] { MANIFEST_ELEMENT };
| private java.lang.String | getUiName(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.ElementDescriptor | getUsesPermissionElement()
return USES_PERMISSION_ELEMENT;
| public com.android.ide.eclipse.editors.descriptors.ElementDescriptor | getUsesSdkElement()
return USES_SDK_ELEMENT;
| private java.lang.String | guessStyleName(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.String | guessXmlName(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().
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 void | inflateElement(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.
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 void | insertAttribute(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 void | overrideClassName(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 void | sanityCheck(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 void | updateDescriptors(java.util.Map manifestMap)Updates the document descriptor.
It first computes the new children of the descriptor and then updates them
all at once.
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);
|
|