• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.manifest.descriptors;
18 
19 import com.android.ide.common.api.IAttributeInfo;
20 import com.android.ide.common.api.IAttributeInfo.Format;
21 import com.android.ide.common.layout.LayoutConstants;
22 import com.android.ide.common.resources.platform.AttributeInfo;
23 import com.android.ide.common.resources.platform.AttrsXmlParser;
24 import com.android.ide.common.resources.platform.DeclareStyleableInfo;
25 import com.android.ide.eclipse.adt.AdtPlugin;
26 import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
27 import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils;
28 import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
29 import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor.Mandatory;
30 import com.android.ide.eclipse.adt.internal.editors.descriptors.IDescriptorProvider;
31 import com.android.ide.eclipse.adt.internal.editors.descriptors.ITextAttributeCreator;
32 import com.android.ide.eclipse.adt.internal.editors.descriptors.ListAttributeDescriptor;
33 import com.android.ide.eclipse.adt.internal.editors.descriptors.ReferenceAttributeDescriptor;
34 import com.android.ide.eclipse.adt.internal.editors.descriptors.TextAttributeDescriptor;
35 import com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor;
36 import com.android.sdklib.SdkConstants;
37 
38 import org.eclipse.core.runtime.IStatus;
39 
40 import java.util.ArrayList;
41 import java.util.HashMap;
42 import java.util.HashSet;
43 import java.util.Iterator;
44 import java.util.Map;
45 import java.util.Map.Entry;
46 import java.util.Set;
47 import java.util.TreeSet;
48 
49 
50 /**
51  * Complete description of the AndroidManifest.xml structure.
52  * <p/>
53  * The root element are static instances which always exists.
54  * However their sub-elements and attributes are created only when the SDK changes or is
55  * loaded the first time.
56  */
57 public final class AndroidManifestDescriptors implements IDescriptorProvider {
58     /** Name of the {@code <uses-permission>} */
59     public static final String USES_PERMISSION = "uses-permission";             //$NON-NLS-1$
60     private static final String MANIFEST_NODE_NAME = "manifest";                //$NON-NLS-1$
61     private static final String ANDROID_MANIFEST_STYLEABLE =
62         AttrsXmlParser.ANDROID_MANIFEST_STYLEABLE;
63 
64     // Public attributes names, attributes descriptors and elements descriptors
65 
66     public static final String ANDROID_LABEL_ATTR = "label";    //$NON-NLS-1$
67     public static final String ANDROID_NAME_ATTR  = "name";     //$NON-NLS-1$
68     public static final String PACKAGE_ATTR       = "package";  //$NON-NLS-1$
69 
70     /** The {@link ElementDescriptor} for the root Manifest element. */
71     private final ElementDescriptor MANIFEST_ELEMENT;
72     /** The {@link ElementDescriptor} for the root Application element. */
73     private final ElementDescriptor APPLICATION_ELEMENT;
74 
75     /** The {@link ElementDescriptor} for the root Instrumentation element. */
76     private final ElementDescriptor INTRUMENTATION_ELEMENT;
77     /** The {@link ElementDescriptor} for the root Permission element. */
78     private final ElementDescriptor PERMISSION_ELEMENT;
79     /** The {@link ElementDescriptor} for the root UsesPermission element. */
80     private final ElementDescriptor USES_PERMISSION_ELEMENT;
81     /** The {@link ElementDescriptor} for the root UsesSdk element. */
82     private final ElementDescriptor USES_SDK_ELEMENT;
83 
84     /** The {@link ElementDescriptor} for the root PermissionGroup element. */
85     private final ElementDescriptor PERMISSION_GROUP_ELEMENT;
86     /** The {@link ElementDescriptor} for the root PermissionTree element. */
87     private final ElementDescriptor PERMISSION_TREE_ELEMENT;
88 
89     /** Private package attribute for the manifest element. Needs to be handled manually. */
90     private final TextAttributeDescriptor PACKAGE_ATTR_DESC;
91 
AndroidManifestDescriptors()92     public AndroidManifestDescriptors() {
93         APPLICATION_ELEMENT = createElement("application", null, Mandatory.MANDATORY_LAST); //$NON-NLS-1$ + no child & mandatory
94         INTRUMENTATION_ELEMENT = createElement("instrumentation"); //$NON-NLS-1$
95 
96         PERMISSION_ELEMENT = createElement("permission"); //$NON-NLS-1$
97         USES_PERMISSION_ELEMENT = createElement(USES_PERMISSION);
98         USES_SDK_ELEMENT = createElement("uses-sdk", null, Mandatory.MANDATORY); //$NON-NLS-1$ + no child & mandatory
99 
100         PERMISSION_GROUP_ELEMENT = createElement("permission-group"); //$NON-NLS-1$
101         PERMISSION_TREE_ELEMENT = createElement("permission-tree"); //$NON-NLS-1$
102 
103         MANIFEST_ELEMENT = createElement(
104                         MANIFEST_NODE_NAME, // xml name
105                         new ElementDescriptor[] {
106                                         APPLICATION_ELEMENT,
107                                         INTRUMENTATION_ELEMENT,
108                                         PERMISSION_ELEMENT,
109                                         USES_PERMISSION_ELEMENT,
110                                         PERMISSION_GROUP_ELEMENT,
111                                         PERMISSION_TREE_ELEMENT,
112                                         USES_SDK_ELEMENT,
113                         },
114                         Mandatory.MANDATORY);
115 
116         // The "package" attribute is treated differently as it doesn't have the standard
117         // Android XML namespace.
118         PACKAGE_ATTR_DESC = new PackageAttributeDescriptor(PACKAGE_ATTR,
119                 null /* nsUri */,
120                 new AttributeInfo(PACKAGE_ATTR, Format.REFERENCE_SET)).setTooltip(
121                     "This attribute gives a unique name for the package, using a Java-style " +
122                     "naming convention to avoid name collisions.\nFor example, applications " +
123                     "published by Google could have names of the form com.google.app.appname");
124     }
125 
126     @Override
getRootElementDescriptors()127     public ElementDescriptor[] getRootElementDescriptors() {
128         return new ElementDescriptor[] { MANIFEST_ELEMENT };
129     }
130 
131     @Override
getDescriptor()132     public ElementDescriptor getDescriptor() {
133         return getManifestElement();
134     }
135 
getApplicationElement()136     public ElementDescriptor getApplicationElement() {
137         return APPLICATION_ELEMENT;
138     }
139 
getManifestElement()140     public ElementDescriptor getManifestElement() {
141         return MANIFEST_ELEMENT;
142     }
143 
getUsesSdkElement()144     public ElementDescriptor getUsesSdkElement() {
145         return USES_SDK_ELEMENT;
146     }
147 
getInstrumentationElement()148     public ElementDescriptor getInstrumentationElement() {
149         return INTRUMENTATION_ELEMENT;
150     }
151 
getPermissionElement()152     public ElementDescriptor getPermissionElement() {
153         return PERMISSION_ELEMENT;
154     }
155 
getUsesPermissionElement()156     public ElementDescriptor getUsesPermissionElement() {
157         return USES_PERMISSION_ELEMENT;
158     }
159 
getPermissionGroupElement()160     public ElementDescriptor getPermissionGroupElement() {
161         return PERMISSION_GROUP_ELEMENT;
162     }
163 
getPermissionTreeElement()164     public ElementDescriptor getPermissionTreeElement() {
165         return PERMISSION_TREE_ELEMENT;
166     }
167 
168     /**
169      * Updates the document descriptor.
170      * <p/>
171      * It first computes the new children of the descriptor and then updates them
172      * all at once.
173      *
174      * @param manifestMap The map style => attributes from the attrs_manifest.xml file
175      */
updateDescriptors( Map<String, DeclareStyleableInfo> manifestMap)176     public synchronized void updateDescriptors(
177             Map<String, DeclareStyleableInfo> manifestMap) {
178 
179         // -- setup the required attributes overrides --
180 
181         Set<String> required = new HashSet<String>();
182         required.add("provider/authorities");  //$NON-NLS-1$
183 
184         // -- setup the various attribute format overrides --
185 
186         // The key for each override is "element1,element2,.../attr-xml-local-name" or
187         // "*/attr-xml-local-name" to match the attribute in any element.
188 
189         Map<String, ITextAttributeCreator> overrides = new HashMap<String, ITextAttributeCreator>();
190 
191         overrides.put("*/icon",             ReferenceAttributeDescriptor.CREATOR);  //$NON-NLS-1$
192 
193         overrides.put("*/theme",            ThemeAttributeDescriptor.CREATOR);      //$NON-NLS-1$
194         overrides.put("*/permission",       ListAttributeDescriptor.CREATOR);       //$NON-NLS-1$
195         overrides.put("*/targetPackage",    ManifestPkgAttrDescriptor.CREATOR);     //$NON-NLS-1$
196 
197         overrides.put("uses-library/name",  ListAttributeDescriptor.CREATOR);       //$NON-NLS-1$
198         overrides.put("action,category,uses-permission/" + ANDROID_NAME_ATTR,       //$NON-NLS-1$
199                                             ListAttributeDescriptor.CREATOR);
200 
201         overrides.put("application/" + ANDROID_NAME_ATTR,                           //$NON-NLS-1$
202                                             ApplicationAttributeDescriptor.CREATOR);
203 
204         overrideClassName(overrides, "activity", SdkConstants.CLASS_ACTIVITY);           //$NON-NLS-1$
205         overrideClassName(overrides, "receiver", SdkConstants.CLASS_BROADCASTRECEIVER);  //$NON-NLS-1$
206         overrideClassName(overrides, "service",  SdkConstants.CLASS_SERVICE);            //$NON-NLS-1$
207         overrideClassName(overrides, "provider", SdkConstants.CLASS_CONTENTPROVIDER);    //$NON-NLS-1$
208         overrideClassName(overrides, "instrumentation", SdkConstants.CLASS_INSTRUMENTATION);    //$NON-NLS-1$
209 
210         // -- list element nodes already created --
211         // These elements are referenced by already opened editors, so we want to update them
212         // but not re-create them when reloading an SDK on the fly.
213 
214         HashMap<String, ElementDescriptor> elementDescs =
215             new HashMap<String, ElementDescriptor>();
216         elementDescs.put(MANIFEST_ELEMENT.getXmlLocalName(),         MANIFEST_ELEMENT);
217         elementDescs.put(APPLICATION_ELEMENT.getXmlLocalName(),      APPLICATION_ELEMENT);
218         elementDescs.put(INTRUMENTATION_ELEMENT.getXmlLocalName(),   INTRUMENTATION_ELEMENT);
219         elementDescs.put(PERMISSION_ELEMENT.getXmlLocalName(),       PERMISSION_ELEMENT);
220         elementDescs.put(USES_PERMISSION_ELEMENT.getXmlLocalName(),  USES_PERMISSION_ELEMENT);
221         elementDescs.put(USES_SDK_ELEMENT.getXmlLocalName(),         USES_SDK_ELEMENT);
222         elementDescs.put(PERMISSION_GROUP_ELEMENT.getXmlLocalName(), PERMISSION_GROUP_ELEMENT);
223         elementDescs.put(PERMISSION_TREE_ELEMENT.getXmlLocalName(),  PERMISSION_TREE_ELEMENT);
224 
225         // --
226 
227         inflateElement(manifestMap,
228                 overrides,
229                 required,
230                 elementDescs,
231                 MANIFEST_ELEMENT,
232                 "AndroidManifest"); //$NON-NLS-1$
233         insertAttribute(MANIFEST_ELEMENT, PACKAGE_ATTR_DESC);
234 
235         XmlnsAttributeDescriptor xmlns = new XmlnsAttributeDescriptor(
236                 LayoutConstants.ANDROID_NS_NAME, SdkConstants.NS_RESOURCES);
237         insertAttribute(MANIFEST_ELEMENT, xmlns);
238 
239         assert sanityCheck(manifestMap, MANIFEST_ELEMENT);
240     }
241 
242     /**
243      * Sets up an attribute override for ANDROID_NAME_ATTR using a ClassAttributeDescriptor
244      * with the specified class name.
245      */
overrideClassName( Map<String, ITextAttributeCreator> overrides, String elementName, final String className)246     private static void overrideClassName(
247             Map<String, ITextAttributeCreator> overrides,
248             String elementName,
249             final String className) {
250         overrides.put(elementName + "/" + ANDROID_NAME_ATTR,
251                 new ITextAttributeCreator() {
252             @Override
253             public TextAttributeDescriptor create(String xmlName, String nsUri,
254                     IAttributeInfo attrInfo) {
255                 if (attrInfo == null) {
256                     attrInfo = new AttributeInfo(xmlName, Format.STRING_SET );
257                 }
258 
259                 if (SdkConstants.CLASS_ACTIVITY.equals(className)) {
260                     return new ClassAttributeDescriptor(
261                             className,
262                             PostActivityCreationAction.getAction(),
263                             xmlName,
264                             nsUri,
265                             attrInfo,
266                             true /*mandatory */,
267                             true /*defaultToProjectOnly*/);
268                 } else if (SdkConstants.CLASS_BROADCASTRECEIVER.equals(className)) {
269                     return new ClassAttributeDescriptor(
270                             className,
271                             PostReceiverCreationAction.getAction(),
272                             xmlName,
273                             nsUri,
274                             attrInfo,
275                             true /*mandatory */,
276                             true /*defaultToProjectOnly*/);
277                 } else if (SdkConstants.CLASS_INSTRUMENTATION.equals(className)) {
278                     return new ClassAttributeDescriptor(
279                             className,
280                             null, // no post action
281                             xmlName,
282                             nsUri,
283                             attrInfo,
284                             true /*mandatory */,
285                             false /*defaultToProjectOnly*/);
286                 } else {
287                     return new ClassAttributeDescriptor(
288                             className,
289                             xmlName,
290                             nsUri,
291                             attrInfo,
292                             true /*mandatory */);
293                 }
294             }
295         });
296     }
297 
298     /**
299      * Returns a new ElementDescriptor constructed from the information given here
300      * and the javadoc & attributes extracted from the style map if any.
301      * <p/>
302      * Creates an element with no attribute overrides.
303      */
createElement( String xmlName, ElementDescriptor[] childrenElements, Mandatory mandatory)304     private ElementDescriptor createElement(
305             String xmlName,
306             ElementDescriptor[] childrenElements,
307             Mandatory mandatory) {
308         // Creates an element with no attribute overrides.
309         String styleName = guessStyleName(xmlName);
310         String sdkUrl = DescriptorsUtils.MANIFEST_SDK_URL + styleName;
311         String uiName = getUiName(xmlName);
312 
313         ElementDescriptor element = new ManifestElementDescriptor(xmlName, uiName, null, sdkUrl,
314                 null, childrenElements, mandatory);
315 
316         return element;
317     }
318 
319     /**
320      * Returns a new ElementDescriptor constructed from its XML local name.
321      * <p/>
322      * This version creates an element not mandatory.
323      */
createElement(String xmlName)324     private ElementDescriptor createElement(String xmlName) {
325         // Creates an element with no child and not mandatory
326         return createElement(xmlName, null, Mandatory.NOT_MANDATORY);
327     }
328 
329     /**
330      * Inserts an attribute in this element attribute list if it is not present there yet
331      * (based on the attribute XML name.)
332      * The attribute is inserted at the beginning of the attribute list.
333      */
insertAttribute(ElementDescriptor element, AttributeDescriptor newAttr)334     private void insertAttribute(ElementDescriptor element, AttributeDescriptor newAttr) {
335         AttributeDescriptor[] attributes = element.getAttributes();
336         for (AttributeDescriptor attr : attributes) {
337             if (attr.getXmlLocalName().equals(newAttr.getXmlLocalName())) {
338                 return;
339             }
340         }
341 
342         AttributeDescriptor[] newArray = new AttributeDescriptor[attributes.length + 1];
343         newArray[0] = newAttr;
344         System.arraycopy(attributes, 0, newArray, 1, attributes.length);
345         element.setAttributes(newArray);
346     }
347 
348     /**
349      * "Inflates" the properties of an {@link ElementDescriptor} from the styleable declaration.
350      * <p/>
351      * This first creates all the attributes for the given ElementDescriptor.
352      * It then finds all children of the descriptor, inflates them recursively and sets them
353      * as child to this ElementDescriptor.
354      *
355      * @param styleMap The input styleable map for manifest elements & attributes.
356      * @param overrides A list of attribute overrides (to customize the type of the attribute
357      *          descriptors).
358      * @param requiredAttributes Set of attributes to be marked as required.
359      * @param existingElementDescs A map of already created element descriptors, keyed by
360      *          XML local name. This is used to use the static elements created initially by this
361      *          class, which are referenced directly by editors (so that reloading an SDK won't
362      *          break these references).
363      * @param elemDesc The current {@link ElementDescriptor} to inflate.
364      * @param styleName The name of the {@link ElementDescriptor} to inflate. Its XML local name
365      *          will be guessed automatically from the style name.
366      */
inflateElement( Map<String, DeclareStyleableInfo> styleMap, Map<String, ITextAttributeCreator> overrides, Set<String> requiredAttributes, HashMap<String, ElementDescriptor> existingElementDescs, ElementDescriptor elemDesc, String styleName)367     private void inflateElement(
368             Map<String, DeclareStyleableInfo> styleMap,
369             Map<String, ITextAttributeCreator> overrides,
370             Set<String> requiredAttributes,
371             HashMap<String, ElementDescriptor> existingElementDescs,
372             ElementDescriptor elemDesc,
373             String styleName) {
374         assert elemDesc != null;
375         assert styleName != null;
376         assert styleMap != null;
377 
378         if (styleMap == null) {
379             return;
380         }
381 
382         // define attributes
383         DeclareStyleableInfo style = styleMap.get(styleName);
384         if (style != null) {
385             ArrayList<AttributeDescriptor> attrDescs = new ArrayList<AttributeDescriptor>();
386             DescriptorsUtils.appendAttributes(attrDescs,
387                     elemDesc.getXmlLocalName(),
388                     SdkConstants.NS_RESOURCES,
389                     style.getAttributes(),
390                     requiredAttributes,
391                     overrides);
392             elemDesc.setTooltip(style.getJavaDoc());
393             elemDesc.setAttributes(attrDescs.toArray(new AttributeDescriptor[attrDescs.size()]));
394         }
395 
396         // find all elements that have this one as parent
397         ArrayList<ElementDescriptor> children = new ArrayList<ElementDescriptor>();
398         for (Entry<String, DeclareStyleableInfo> entry : styleMap.entrySet()) {
399             DeclareStyleableInfo childStyle = entry.getValue();
400             boolean isParent = false;
401             String[] parents = childStyle.getParents();
402             if (parents != null) {
403                 for (String parent: parents) {
404                     if (styleName.equals(parent)) {
405                         isParent = true;
406                         break;
407                     }
408                 }
409             }
410             if (isParent) {
411                 String childStyleName = entry.getKey();
412                 String childXmlName = guessXmlName(childStyleName);
413 
414                 // create or re-use element
415                 ElementDescriptor child = existingElementDescs.get(childXmlName);
416                 if (child == null) {
417                     child = createElement(childXmlName);
418                     existingElementDescs.put(childXmlName, child);
419                 }
420                 children.add(child);
421 
422                 inflateElement(styleMap,
423                         overrides,
424                         requiredAttributes,
425                         existingElementDescs,
426                         child,
427                         childStyleName);
428             }
429         }
430         elemDesc.setChildren(children.toArray(new ElementDescriptor[children.size()]));
431     }
432 
433     /**
434      * Get an UI name from the element XML name.
435      * <p/>
436      * Capitalizes the first letter and replace non-alphabet by a space followed by a capital.
437      */
getUiName(String xmlName)438     private static String getUiName(String xmlName) {
439         StringBuilder sb = new StringBuilder();
440 
441         boolean capitalize = true;
442         for (char c : xmlName.toCharArray()) {
443             if (capitalize && c >= 'a' && c <= 'z') {
444                 sb.append((char)(c + 'A' - 'a'));
445                 capitalize = false;
446             } else if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z')) {
447                 sb.append(' ');
448                 capitalize = true;
449             } else {
450                 sb.append(c);
451             }
452         }
453 
454         return sb.toString();
455     }
456 
457     /**
458      * Guesses the style name for a given XML element name.
459      * <p/>
460      * The rules are:
461      * - capitalize the first letter:
462      * - if there's a dash, skip it and capitalize the next one
463      * - prefix AndroidManifest
464      * The exception is "manifest" which just becomes AndroidManifest.
465      * <p/>
466      * Examples:
467      * - manifest        => AndroidManifest
468      * - application     => AndroidManifestApplication
469      * - uses-permission => AndroidManifestUsesPermission
470      */
guessStyleName(String xmlName)471     private String guessStyleName(String xmlName) {
472         StringBuilder sb = new StringBuilder();
473 
474         if (!xmlName.equals(MANIFEST_NODE_NAME)) {
475             boolean capitalize = true;
476             for (char c : xmlName.toCharArray()) {
477                 if (capitalize && c >= 'a' && c <= 'z') {
478                     sb.append((char)(c + 'A' - 'a'));
479                     capitalize = false;
480                 } else if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z')) {
481                     // not a letter -- skip the character and capitalize the next one
482                     capitalize = true;
483                 } else {
484                     sb.append(c);
485                 }
486             }
487         }
488 
489         sb.insert(0, ANDROID_MANIFEST_STYLEABLE);
490         return sb.toString();
491     }
492 
493     /**
494      * This method performs a sanity check to make sure all the styles declared in the
495      * manifestMap are actually defined in the actual element descriptors and reachable from
496      * the manifestElement root node.
497      */
sanityCheck(Map<String, DeclareStyleableInfo> manifestMap, ElementDescriptor manifestElement)498     private boolean sanityCheck(Map<String, DeclareStyleableInfo> manifestMap,
499             ElementDescriptor manifestElement) {
500         TreeSet<String> elementsDeclared = new TreeSet<String>();
501         findAllElementNames(manifestElement, elementsDeclared);
502 
503         TreeSet<String> stylesDeclared = new TreeSet<String>();
504         for (String styleName : manifestMap.keySet()) {
505             if (styleName.startsWith(ANDROID_MANIFEST_STYLEABLE)) {
506                 stylesDeclared.add(styleName);
507             }
508         }
509 
510         for (Iterator<String> it = elementsDeclared.iterator(); it.hasNext();) {
511             String xmlName = it.next();
512             String styleName = guessStyleName(xmlName);
513             if (stylesDeclared.remove(styleName)) {
514                 it.remove();
515             }
516         }
517 
518         StringBuilder sb = new StringBuilder();
519         if (!stylesDeclared.isEmpty()) {
520             sb.append("Warning, ADT/SDK Mismatch! The following elements are declared by the SDK but unknown to ADT: ");
521             for (String name : stylesDeclared) {
522                 sb.append(guessXmlName(name));
523 
524                 if (!name.equals(stylesDeclared.last())) {
525                     sb.append(", ");    //$NON-NLS-1$
526                 }
527             }
528 
529             AdtPlugin.log(IStatus.WARNING, "%s", sb.toString());
530             AdtPlugin.printToConsole((String)null, sb);
531             sb.setLength(0);
532         }
533 
534         if (!elementsDeclared.isEmpty()) {
535             sb.append("Warning, ADT/SDK Mismatch! The following elements are declared by ADT but not by the SDK: ");
536             for (String name : elementsDeclared) {
537                 sb.append(name);
538                 if (!name.equals(elementsDeclared.last())) {
539                     sb.append(", ");    //$NON-NLS-1$
540                 }
541             }
542 
543             AdtPlugin.log(IStatus.WARNING, "%s", sb.toString());
544             AdtPlugin.printToConsole((String)null, sb);
545         }
546 
547         return true;
548     }
549 
550     /**
551      * Performs an approximate translation of the style name into a potential
552      * xml name. This is more or less the reverse from guessStyleName().
553      *
554      * @return The XML local name for a given style name.
555      */
guessXmlName(String name)556     private String guessXmlName(String name) {
557         StringBuilder sb = new StringBuilder();
558         if (ANDROID_MANIFEST_STYLEABLE.equals(name)) {
559             sb.append(MANIFEST_NODE_NAME);
560         } else {
561             name = name.replace(ANDROID_MANIFEST_STYLEABLE, "");                //$NON-NLS-1$
562             boolean first_char = true;
563             for (char c : name.toCharArray()) {
564                 if (c >= 'A' && c <= 'Z') {
565                     if (!first_char) {
566                         sb.append('-');
567                     }
568                     c = (char) (c - 'A' + 'a');
569                 }
570                 sb.append(c);
571                 first_char = false;
572             }
573         }
574         return sb.toString();
575     }
576 
577     /**
578      * Helper method used by {@link #sanityCheck(Map, ElementDescriptor)} to find all the
579      * {@link ElementDescriptor} names defined by the tree of descriptors.
580      * <p/>
581      * Note: this assumes no circular reference in the tree of {@link ElementDescriptor}s.
582      */
findAllElementNames(ElementDescriptor element, TreeSet<String> declared)583     private void findAllElementNames(ElementDescriptor element, TreeSet<String> declared) {
584         declared.add(element.getXmlName());
585         for (ElementDescriptor desc : element.getChildren()) {
586             findAllElementNames(desc, declared);
587         }
588     }
589 
590 
591 }
592