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 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 124 public ElementDescriptor[] getRootElementDescriptors() { 125 return new ElementDescriptor[] { MANIFEST_ELEMENT }; 126 } 127 128 public ElementDescriptor getDescriptor() { 129 return getManifestElement(); 130 } 131 132 public ElementDescriptor getApplicationElement() { 133 return APPLICATION_ELEMENT; 134 } 135 136 public ElementDescriptor getManifestElement() { 137 return MANIFEST_ELEMENT; 138 } 139 140 public ElementDescriptor getUsesSdkElement() { 141 return USES_SDK_ELEMENT; 142 } 143 144 public ElementDescriptor getInstrumentationElement() { 145 return INTRUMENTATION_ELEMENT; 146 } 147 148 public ElementDescriptor getPermissionElement() { 149 return PERMISSION_ELEMENT; 150 } 151 152 public ElementDescriptor getUsesPermissionElement() { 153 return USES_PERMISSION_ELEMENT; 154 } 155 156 public ElementDescriptor getPermissionGroupElement() { 157 return PERMISSION_GROUP_ELEMENT; 158 } 159 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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