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