aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/descriptors/AndroidManifestDescriptors.java
diff options
context:
space:
mode:
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/descriptors/AndroidManifestDescriptors.java')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/descriptors/AndroidManifestDescriptors.java628
1 files changed, 628 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/descriptors/AndroidManifestDescriptors.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/descriptors/AndroidManifestDescriptors.java
new file mode 100644
index 000000000..3429e43a0
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/descriptors/AndroidManifestDescriptors.java
@@ -0,0 +1,628 @@
+/*
+ * 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.manifest.descriptors;
+
+import com.android.SdkConstants;
+import com.android.ide.common.api.IAttributeInfo;
+import com.android.ide.common.api.IAttributeInfo.Format;
+import com.android.ide.common.resources.platform.AttributeInfo;
+import com.android.ide.common.resources.platform.AttrsXmlParser;
+import com.android.ide.common.resources.platform.DeclareStyleableInfo;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor.Mandatory;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.IDescriptorProvider;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.ITextAttributeCreator;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.ListAttributeDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.ReferenceAttributeDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.TextAttributeDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor;
+
+import org.eclipse.core.runtime.IStatus;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeSet;
+
+
+/**
+ * Complete description of the AndroidManifest.xml structure.
+ * <p/>
+ * 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.
+ */
+public final class AndroidManifestDescriptors implements IDescriptorProvider {
+ /** Name of the {@code <uses-permission>} */
+ public static final String USES_PERMISSION = "uses-permission"; //$NON-NLS-1$
+ private static final String MANIFEST_NODE_NAME = "manifest"; //$NON-NLS-1$
+ private static final String ANDROID_MANIFEST_STYLEABLE =
+ AttrsXmlParser.ANDROID_MANIFEST_STYLEABLE;
+
+ // Public attributes names, attributes descriptors and elements descriptors
+
+ public static final String ANDROID_LABEL_ATTR = "label"; //$NON-NLS-1$
+ public static final String ANDROID_NAME_ATTR = "name"; //$NON-NLS-1$
+ public static final String PACKAGE_ATTR = "package"; //$NON-NLS-1$
+
+ /** The {@link ElementDescriptor} for the root Manifest element. */
+ private final ElementDescriptor MANIFEST_ELEMENT;
+ /** The {@link ElementDescriptor} for the root Application element. */
+ private final ElementDescriptor APPLICATION_ELEMENT;
+
+ /** The {@link ElementDescriptor} for the root Instrumentation element. */
+ private final ElementDescriptor INTRUMENTATION_ELEMENT;
+ /** The {@link ElementDescriptor} for the root Permission element. */
+ private final ElementDescriptor PERMISSION_ELEMENT;
+ /** The {@link ElementDescriptor} for the root UsesPermission element. */
+ private final ElementDescriptor USES_PERMISSION_ELEMENT;
+ /** The {@link ElementDescriptor} for the root UsesSdk element. */
+ private final ElementDescriptor USES_SDK_ELEMENT;
+
+ /** The {@link ElementDescriptor} for the root PermissionGroup element. */
+ private final ElementDescriptor PERMISSION_GROUP_ELEMENT;
+ /** The {@link ElementDescriptor} for the root PermissionTree element. */
+ private final ElementDescriptor PERMISSION_TREE_ELEMENT;
+
+ /** Private package attribute for the manifest element. Needs to be handled manually. */
+ private final TextAttributeDescriptor PACKAGE_ATTR_DESC;
+
+ public AndroidManifestDescriptors() {
+ APPLICATION_ELEMENT = createElement("application", null, Mandatory.MANDATORY_LAST); //$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);
+ USES_SDK_ELEMENT = createElement("uses-sdk", null, Mandatory.MANDATORY); //$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,
+ },
+ Mandatory.MANDATORY);
+
+ // The "package" attribute is treated differently as it doesn't have the standard
+ // Android XML namespace.
+ PACKAGE_ATTR_DESC = new PackageAttributeDescriptor(PACKAGE_ATTR,
+ null /* nsUri */,
+ new AttributeInfo(PACKAGE_ATTR, Format.REFERENCE_SET)).setTooltip(
+ "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");
+ }
+
+ @Override
+ public ElementDescriptor[] getRootElementDescriptors() {
+ return new ElementDescriptor[] { MANIFEST_ELEMENT };
+ }
+
+ @Override
+ public ElementDescriptor getDescriptor() {
+ return getManifestElement();
+ }
+
+ public ElementDescriptor getApplicationElement() {
+ return APPLICATION_ELEMENT;
+ }
+
+ public ElementDescriptor getManifestElement() {
+ return MANIFEST_ELEMENT;
+ }
+
+ public ElementDescriptor getUsesSdkElement() {
+ return USES_SDK_ELEMENT;
+ }
+
+ public ElementDescriptor getInstrumentationElement() {
+ return INTRUMENTATION_ELEMENT;
+ }
+
+ public ElementDescriptor getPermissionElement() {
+ return PERMISSION_ELEMENT;
+ }
+
+ public ElementDescriptor getUsesPermissionElement() {
+ return USES_PERMISSION_ELEMENT;
+ }
+
+ public ElementDescriptor getPermissionGroupElement() {
+ return PERMISSION_GROUP_ELEMENT;
+ }
+
+ public ElementDescriptor getPermissionTreeElement() {
+ return PERMISSION_TREE_ELEMENT;
+ }
+
+ /**
+ * Updates the document descriptor.
+ * <p/>
+ * 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
+ */
+ public synchronized void updateDescriptors(
+ Map<String, DeclareStyleableInfo> manifestMap) {
+
+ // -- 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, ITextAttributeCreator> overrides = new HashMap<String, ITextAttributeCreator>();
+
+ overrides.put("*/icon", ReferenceAttributeDescriptor.CREATOR); //$NON-NLS-1$
+
+ overrides.put("*/theme", ThemeAttributeDescriptor.CREATOR); //$NON-NLS-1$
+ overrides.put("*/permission", ListAttributeDescriptor.CREATOR); //$NON-NLS-1$
+ overrides.put("*/targetPackage", ManifestPkgAttrDescriptor.CREATOR); //$NON-NLS-1$
+
+ overrides.put("uses-library/name", ListAttributeDescriptor.CREATOR); //$NON-NLS-1$
+ overrides.put("action,category,uses-permission/" + ANDROID_NAME_ATTR, //$NON-NLS-1$
+ ListAttributeDescriptor.CREATOR);
+
+ overrideClassName(overrides, "application", //$NON-NLS-1$
+ SdkConstants.CLASS_APPLICATION,
+ false /*mandatory*/);
+ overrideClassName(overrides, "application/backupAgent", //$NON-NLS-1$
+ "android.app.backup.BackupAgent", //$NON-NLS-1$
+ false /*mandatory*/);
+ overrideClassName(overrides, "activity", SdkConstants.CLASS_ACTIVITY); //$NON-NLS-1$
+ overrideClassName(overrides, "receiver", SdkConstants.CLASS_BROADCASTRECEIVER);//$NON-NLS-1$
+ overrideClassName(overrides, "service", SdkConstants.CLASS_SERVICE); //$NON-NLS-1$
+ overrideClassName(overrides, "provider", SdkConstants.CLASS_CONTENTPROVIDER); //$NON-NLS-1$
+ overrideClassName(overrides, "instrumentation",
+ SdkConstants.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);
+
+ XmlnsAttributeDescriptor xmlns = new XmlnsAttributeDescriptor(
+ SdkConstants.ANDROID_NS_NAME, SdkConstants.ANDROID_URI);
+ insertAttribute(MANIFEST_ELEMENT, xmlns);
+
+ /*
+ *
+ *
+ */
+ assert sanityCheck(manifestMap, MANIFEST_ELEMENT);
+ }
+
+ /**
+ * Sets up a mandatory attribute override using a ClassAttributeDescriptor
+ * with the specified class name.
+ *
+ * @param overrides The current map of overrides.
+ * @param elementName The element name to override, e.g. "application".
+ * If this name does NOT have a slash (/), the ANDROID_NAME_ATTR attribute will be overriden.
+ * Otherwise, if it contains a (/) the format is "element/attribute", for example
+ * "application/name" vs "application/backupAgent".
+ * @param className The fully qualified name of the base class of the attribute.
+ */
+ private static void overrideClassName(
+ Map<String, ITextAttributeCreator> overrides,
+ String elementName,
+ final String className) {
+ overrideClassName(overrides, elementName, className, true);
+ }
+
+ /**
+ * Sets up an attribute override using a ClassAttributeDescriptor
+ * with the specified class name.
+ *
+ * @param overrides The current map of overrides.
+ * @param elementName The element name to override, e.g. "application".
+ * If this name does NOT have a slash (/), the ANDROID_NAME_ATTR attribute will be overriden.
+ * Otherwise, if it contains a (/) the format is "element/attribute", for example
+ * "application/name" vs "application/backupAgent".
+ * @param className The fully qualified name of the base class of the attribute.
+ * @param mandatory True if this attribute is mandatory, false if optional.
+ */
+ private static void overrideClassName(
+ Map<String, ITextAttributeCreator> overrides,
+ String elementName,
+ final String className,
+ final boolean mandatory) {
+ if (elementName.indexOf('/') == -1) {
+ elementName = elementName + '/' + ANDROID_NAME_ATTR;
+ }
+ overrides.put(elementName,
+ new ITextAttributeCreator() {
+ @Override
+ public TextAttributeDescriptor create(String xmlName, String nsUri,
+ IAttributeInfo attrInfo) {
+ if (attrInfo == null) {
+ attrInfo = new AttributeInfo(xmlName, Format.STRING_SET );
+ }
+
+ if (SdkConstants.CLASS_ACTIVITY.equals(className)) {
+ return new ClassAttributeDescriptor(
+ className,
+ PostActivityCreationAction.getAction(),
+ xmlName,
+ nsUri,
+ attrInfo,
+ mandatory,
+ true /*defaultToProjectOnly*/);
+ } else if (SdkConstants.CLASS_BROADCASTRECEIVER.equals(className)) {
+ return new ClassAttributeDescriptor(
+ className,
+ PostReceiverCreationAction.getAction(),
+ xmlName,
+ nsUri,
+ attrInfo,
+ mandatory,
+ true /*defaultToProjectOnly*/);
+ } else if (SdkConstants.CLASS_INSTRUMENTATION.equals(className)) {
+ return new ClassAttributeDescriptor(
+ className,
+ null, // no post action
+ xmlName,
+ nsUri,
+ attrInfo,
+ mandatory,
+ false /*defaultToProjectOnly*/);
+ } else {
+ return new ClassAttributeDescriptor(
+ className,
+ xmlName,
+ nsUri,
+ attrInfo,
+ mandatory);
+ }
+ }
+ });
+ }
+
+ /**
+ * Returns a new ElementDescriptor constructed from the information given here
+ * and the javadoc & attributes extracted from the style map if any.
+ * <p/>
+ * Creates an element with no attribute overrides.
+ */
+ private ElementDescriptor createElement(
+ String xmlName,
+ ElementDescriptor[] childrenElements,
+ Mandatory mandatory) {
+ // 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;
+ }
+
+ /**
+ * Returns a new ElementDescriptor constructed from its XML local name.
+ * <p/>
+ * This version creates an element not mandatory.
+ */
+ private ElementDescriptor createElement(String xmlName) {
+ // Creates an element with no child and not mandatory
+ return createElement(xmlName, null, Mandatory.NOT_MANDATORY);
+ }
+
+ /**
+ * 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.
+ */
+ private void insertAttribute(ElementDescriptor element, AttributeDescriptor newAttr) {
+ 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);
+ }
+
+ /**
+ * "Inflates" the properties of an {@link ElementDescriptor} from the styleable declaration.
+ * <p/>
+ * This first creates all the attributes for the given ElementDescriptor.
+ * It then finds all children of the descriptor, inflates them recursively and sets 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.
+ */
+ private void inflateElement(
+ Map<String, DeclareStyleableInfo> styleMap,
+ Map<String, ITextAttributeCreator> overrides,
+ Set<String> requiredAttributes,
+ HashMap<String, ElementDescriptor> existingElementDescs,
+ ElementDescriptor elemDesc,
+ String styleName) {
+ assert elemDesc != null;
+ assert styleName != null;
+ assert styleMap != null;
+
+ if (styleMap == null) {
+ return;
+ }
+
+ // define attributes
+ DeclareStyleableInfo style = styleMap.get(styleName);
+ 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()]));
+ }
+
+ /**
+ * Get an UI name from the element XML name.
+ * <p/>
+ * Capitalizes the first letter and replace non-alphabet by a space followed by a capital.
+ */
+ private static String getUiName(String xmlName) {
+ 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();
+ }
+
+ /**
+ * Guesses the style name for a given XML element name.
+ * <p/>
+ * 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.
+ * <p/>
+ * Examples:
+ * - manifest => AndroidManifest
+ * - application => AndroidManifestApplication
+ * - uses-permission => AndroidManifestUsesPermission
+ */
+ private String guessStyleName(String xmlName) {
+ 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();
+ }
+
+ /**
+ * 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.
+ */
+ private boolean sanityCheck(Map<String, DeclareStyleableInfo> manifestMap,
+ ElementDescriptor manifestElement) {
+ 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) {
+ sb.append(guessXmlName(name));
+
+ if (!name.equals(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.equals(elementsDeclared.last())) {
+ sb.append(", "); //$NON-NLS-1$
+ }
+ }
+
+ AdtPlugin.log(IStatus.WARNING, "%s", sb.toString());
+ AdtPlugin.printToConsole((String)null, sb);
+ }
+
+ return true;
+ }
+
+ /**
+ * 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.
+ */
+ private String guessXmlName(String 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();
+ }
+
+ /**
+ * Helper method used by {@link #sanityCheck(Map, ElementDescriptor)} to find all the
+ * {@link ElementDescriptor} names defined by the tree of descriptors.
+ * <p/>
+ * Note: this assumes no circular reference in the tree of {@link ElementDescriptor}s.
+ */
+ private void findAllElementNames(ElementDescriptor element, TreeSet<String> declared) {
+ declared.add(element.getXmlName());
+ for (ElementDescriptor desc : element.getChildren()) {
+ findAllElementNames(desc, declared);
+ }
+ }
+
+
+}