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