1 /* 2 * Copyright (C) 2008 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.layout.descriptors; 18 19 import static com.android.ide.common.layout.LayoutConstants.ANDROID_URI; 20 import static com.android.ide.common.layout.LayoutConstants.ATTR_CLASS; 21 import static com.android.ide.common.layout.LayoutConstants.ATTR_NAME; 22 import static com.android.ide.common.layout.LayoutConstants.ATTR_TAG; 23 import static com.android.ide.common.layout.LayoutConstants.FQCN_GESTURE_OVERLAY_VIEW; 24 25 import com.android.ide.common.api.IAttributeInfo.Format; 26 import com.android.ide.common.resources.platform.AttributeInfo; 27 import com.android.ide.common.resources.platform.DeclareStyleableInfo; 28 import com.android.ide.common.resources.platform.ViewClassInfo; 29 import com.android.ide.common.resources.platform.ViewClassInfo.LayoutParamsInfo; 30 import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor; 31 import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils; 32 import com.android.ide.eclipse.adt.internal.editors.descriptors.DocumentDescriptor; 33 import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor; 34 import com.android.ide.eclipse.adt.internal.editors.descriptors.IDescriptorProvider; 35 import com.android.ide.eclipse.adt.internal.editors.descriptors.SeparatorAttributeDescriptor; 36 import com.android.ide.eclipse.adt.internal.editors.manifest.descriptors.ClassAttributeDescriptor; 37 import com.android.sdklib.IAndroidTarget; 38 import com.android.sdklib.SdkConstants; 39 40 import java.util.ArrayList; 41 import java.util.Collection; 42 import java.util.Collections; 43 import java.util.HashMap; 44 import java.util.List; 45 import java.util.Map; 46 import java.util.Map.Entry; 47 48 49 /** 50 * Complete description of the layout structure. 51 */ 52 public final class LayoutDescriptors implements IDescriptorProvider { 53 54 /** 55 * The XML name of the special {@code <include>} layout tag. 56 * A synthetic element with that name is created as part of the view descriptors list 57 * returned by {@link #getViewDescriptors()}. 58 */ 59 public static final String VIEW_INCLUDE = "include"; //$NON-NLS-1$ 60 61 /** 62 * The XML name of the special {@code <merge>} layout tag. 63 * A synthetic element with that name is created as part of the view descriptors list 64 * returned by {@link #getViewDescriptors()}. 65 */ 66 public static final String VIEW_MERGE = "merge"; //$NON-NLS-1$ 67 68 /** 69 * The XML name of the special {@code <fragment>} layout tag. 70 * A synthetic element with that name is created as part of the view descriptors list 71 * returned by {@link #getViewDescriptors()}. 72 */ 73 public static final String VIEW_FRAGMENT = "fragment"; //$NON-NLS-1$ 74 75 /** 76 * The XML name of the special {@code <view>} layout tag. This is used to add generic 77 * views with a class attribute to specify the view. 78 * <p> 79 * TODO: We should add a synthetic descriptor for this, similar to our descriptors for 80 * include, merge and requestFocus. 81 */ 82 public static final String VIEW_VIEWTAG = "view"; //$NON-NLS-1$ 83 84 /** 85 * The XML name of the special {@code <requestFocus>} layout tag. 86 * A synthetic element with that name is created as part of the view descriptors list 87 * returned by {@link #getViewDescriptors()}. 88 */ 89 public static final String REQUEST_FOCUS = "requestFocus";//$NON-NLS-1$ 90 91 /** 92 * The attribute name of the include tag's url naming the resource to be inserted 93 * <p> 94 * <b>NOTE</b>: The layout attribute is NOT in the Android namespace! 95 */ 96 public static final String ATTR_LAYOUT = "layout"; //$NON-NLS-1$ 97 98 // Public attributes names, attributes descriptors and elements descriptors 99 public static final String ID_ATTR = "id"; //$NON-NLS-1$ 100 101 /** The document descriptor. Contains all layouts and views linked together. */ 102 private DocumentDescriptor mRootDescriptor = 103 new DocumentDescriptor("layout_doc", null); //$NON-NLS-1$ 104 105 /** The list of all known ViewLayout descriptors. */ 106 private List<ViewElementDescriptor> mLayoutDescriptors = 107 new ArrayList<ViewElementDescriptor>(); 108 109 /** Read-Only list of View Descriptors. */ 110 private List<ViewElementDescriptor> mROLayoutDescriptors; 111 112 /** The list of all known View (not ViewLayout) descriptors. */ 113 private List<ViewElementDescriptor> mViewDescriptors = new ArrayList<ViewElementDescriptor>(); 114 115 /** Read-Only list of View Descriptors. */ 116 private List<ViewElementDescriptor> mROViewDescriptors; 117 118 /** The descriptor matching android.view.View. */ 119 private ViewElementDescriptor mBaseViewDescriptor; 120 121 /** Map from view full class name to view descriptor */ 122 private Map<String, ViewElementDescriptor> mFqcnToDescriptor = 123 // As of 3.1 there are 58 items in this map 124 new HashMap<String, ViewElementDescriptor>(80); 125 126 /** Returns the document descriptor. Contains all layouts and views linked together. */ 127 public DocumentDescriptor getDescriptor() { 128 return mRootDescriptor; 129 } 130 131 /** Returns the read-only list of all known ViewLayout descriptors. */ 132 public List<ViewElementDescriptor> getLayoutDescriptors() { 133 return mROLayoutDescriptors; 134 } 135 136 /** Returns the read-only list of all known View (not ViewLayout) descriptors. */ 137 public List<ViewElementDescriptor> getViewDescriptors() { 138 return mROViewDescriptors; 139 } 140 141 public ElementDescriptor[] getRootElementDescriptors() { 142 return mRootDescriptor.getChildren(); 143 } 144 145 /** 146 * Returns the descriptor matching android.view.View, which is guaranteed 147 * to be a {@link ViewElementDescriptor}. 148 */ 149 public ViewElementDescriptor getBaseViewDescriptor() { 150 if (mBaseViewDescriptor == null) { 151 mBaseViewDescriptor = findDescriptorByClass(SdkConstants.CLASS_VIEW); 152 } 153 return mBaseViewDescriptor; 154 } 155 156 /** 157 * Updates the document descriptor. 158 * <p/> 159 * It first computes the new children of the descriptor and then update them 160 * all at once. 161 * <p/> 162 * TODO: differentiate groups from views in the tree UI? => rely on icons 163 * <p/> 164 * 165 * @param views The list of views in the framework. 166 * @param layouts The list of layouts in the framework. 167 * @param styleMap A map from style names to style information provided by the SDK 168 * @param target The android target being initialized 169 */ 170 public synchronized void updateDescriptors(ViewClassInfo[] views, ViewClassInfo[] layouts, 171 Map<String, DeclareStyleableInfo> styleMap, IAndroidTarget target) { 172 173 // This map links every ViewClassInfo to the ElementDescriptor we created. 174 // It is filled by convertView() and used later to fix the super-class hierarchy. 175 HashMap<ViewClassInfo, ViewElementDescriptor> infoDescMap = 176 new HashMap<ViewClassInfo, ViewElementDescriptor>(); 177 178 ArrayList<ViewElementDescriptor> newViews = new ArrayList<ViewElementDescriptor>(40); 179 if (views != null) { 180 for (ViewClassInfo info : views) { 181 ViewElementDescriptor desc = convertView(info, infoDescMap); 182 newViews.add(desc); 183 mFqcnToDescriptor.put(desc.getFullClassName(), desc); 184 } 185 } 186 187 // Create <include> as a synthetic regular view. 188 // Note: ViewStub is already described by attrs.xml 189 insertInclude(newViews); 190 191 List<ViewElementDescriptor> newLayouts = new ArrayList<ViewElementDescriptor>(30); 192 if (layouts != null) { 193 for (ViewClassInfo info : layouts) { 194 ViewElementDescriptor desc = convertView(info, infoDescMap); 195 newLayouts.add(desc); 196 mFqcnToDescriptor.put(desc.getFullClassName(), desc); 197 } 198 } 199 200 // Find View and inherit all its layout attributes 201 AttributeDescriptor[] frameLayoutAttrs = findViewLayoutAttributes( 202 SdkConstants.CLASS_FRAMELAYOUT); 203 204 if (target.getVersion().getApiLevel() >= 4) { 205 ViewElementDescriptor fragmentTag = createFragment(frameLayoutAttrs, styleMap); 206 newViews.add(fragmentTag); 207 } 208 209 List<ElementDescriptor> newDescriptors = new ArrayList<ElementDescriptor>(80); 210 newDescriptors.addAll(newLayouts); 211 newDescriptors.addAll(newViews); 212 213 // Link all layouts to everything else here.. recursively 214 for (ViewElementDescriptor layoutDesc : newLayouts) { 215 layoutDesc.setChildren(newDescriptors); 216 } 217 218 // The gesture overlay descriptor is really a layout but not included in the layouts list 219 // so handle it specially 220 ViewElementDescriptor gestureView = findDescriptorByClass(FQCN_GESTURE_OVERLAY_VIEW); 221 if (gestureView != null) { 222 gestureView.setChildren(newDescriptors); 223 // Inherit layout attributes from FrameLayout 224 gestureView.setLayoutAttributes(frameLayoutAttrs); 225 } 226 227 fixSuperClasses(infoDescMap); 228 229 ViewElementDescriptor requestFocus = createRequestFocus(); 230 newViews.add(requestFocus); 231 newDescriptors.add(requestFocus); 232 233 // The <merge> tag can only be a root tag, so it is added at the end. 234 // It gets everything else as children but it is not made a child itself. 235 ViewElementDescriptor mergeTag = createMerge(frameLayoutAttrs); 236 mergeTag.setChildren(newDescriptors); // mergeTag makes a copy of the list 237 newDescriptors.add(mergeTag); 238 newLayouts.add(mergeTag); 239 240 // Sort palette contents 241 Collections.sort(newViews); 242 Collections.sort(newLayouts); 243 244 mViewDescriptors = newViews; 245 mLayoutDescriptors = newLayouts; 246 mRootDescriptor.setChildren(newDescriptors); 247 248 mBaseViewDescriptor = null; 249 mROLayoutDescriptors = Collections.unmodifiableList(mLayoutDescriptors); 250 mROViewDescriptors = Collections.unmodifiableList(mViewDescriptors); 251 } 252 253 /** 254 * Creates an element descriptor from a given {@link ViewClassInfo}. 255 * 256 * @param info The {@link ViewClassInfo} to convert into a new {@link ViewElementDescriptor}. 257 * @param infoDescMap This map links every ViewClassInfo to the ElementDescriptor it created. 258 * It is filled by here and used later to fix the super-class hierarchy. 259 */ 260 private ViewElementDescriptor convertView( 261 ViewClassInfo info, 262 HashMap<ViewClassInfo, ViewElementDescriptor> infoDescMap) { 263 String xmlName = info.getShortClassName(); 264 String uiName = xmlName; 265 String fqcn = info.getFullClassName(); 266 if (ViewElementDescriptor.viewNeedsPackage(fqcn)) { 267 xmlName = fqcn; 268 } 269 String tooltip = info.getJavaDoc(); 270 271 // Average is around 90, max (in 3.2) is 145 272 ArrayList<AttributeDescriptor> attributes = new ArrayList<AttributeDescriptor>(120); 273 274 // All views and groups have an implicit "style" attribute which is a reference. 275 AttributeInfo styleInfo = new AttributeInfo( 276 "style", //$NON-NLS-1$ xmlLocalName 277 new Format[] { Format.REFERENCE }); 278 styleInfo.setJavaDoc("A reference to a custom style"); //tooltip 279 DescriptorsUtils.appendAttribute(attributes, 280 "style", //$NON-NLS-1$ 281 null, //nsUri 282 styleInfo, 283 false, //required 284 null); // overrides 285 styleInfo.setDefinedBy(SdkConstants.CLASS_VIEW); 286 287 // Process all View attributes 288 DescriptorsUtils.appendAttributes(attributes, 289 null, // elementName 290 SdkConstants.NS_RESOURCES, 291 info.getAttributes(), 292 null, // requiredAttributes 293 null /* overrides */); 294 295 List<String> attributeSources = new ArrayList<String>(); 296 if (info.getAttributes() != null && info.getAttributes().length > 0) { 297 attributeSources.add(fqcn); 298 } 299 300 for (ViewClassInfo link = info.getSuperClass(); 301 link != null; 302 link = link.getSuperClass()) { 303 AttributeInfo[] attrList = link.getAttributes(); 304 if (attrList.length > 0) { 305 attributeSources.add(link.getFullClassName()); 306 attributes.add(new SeparatorAttributeDescriptor( 307 String.format("Attributes from %1$s", link.getShortClassName()))); 308 DescriptorsUtils.appendAttributes(attributes, 309 null, // elementName 310 SdkConstants.NS_RESOURCES, 311 attrList, 312 null, // requiredAttributes 313 null /* overrides */); 314 } 315 } 316 317 // Process all LayoutParams attributes 318 ArrayList<AttributeDescriptor> layoutAttributes = new ArrayList<AttributeDescriptor>(); 319 LayoutParamsInfo layoutParams = info.getLayoutData(); 320 321 for(; layoutParams != null; layoutParams = layoutParams.getSuperClass()) { 322 boolean needSeparator = true; 323 for (AttributeInfo attrInfo : layoutParams.getAttributes()) { 324 if (DescriptorsUtils.containsAttribute(layoutAttributes, 325 SdkConstants.NS_RESOURCES, attrInfo)) { 326 continue; 327 } 328 if (needSeparator) { 329 ViewClassInfo viewLayoutClass = layoutParams.getViewLayoutClass(); 330 String title; 331 String shortClassName = viewLayoutClass.getShortClassName(); 332 if (layoutParams.getShortClassName().equals( 333 SdkConstants.CLASS_NAME_LAYOUTPARAMS)) { 334 title = String.format("Layout Attributes from %1$s", 335 shortClassName); 336 } else { 337 title = String.format("Layout Attributes from %1$s (%2$s)", 338 shortClassName, 339 layoutParams.getShortClassName()); 340 } 341 layoutAttributes.add(new SeparatorAttributeDescriptor(title)); 342 needSeparator = false; 343 } 344 DescriptorsUtils.appendAttribute(layoutAttributes, 345 null, // elementName 346 SdkConstants.NS_RESOURCES, 347 attrInfo, 348 false, // required 349 null /* overrides */); 350 } 351 } 352 353 ViewElementDescriptor desc = new ViewElementDescriptor( 354 xmlName, 355 uiName, 356 fqcn, 357 tooltip, 358 null, // sdk_url 359 attributes.toArray(new AttributeDescriptor[attributes.size()]), 360 layoutAttributes.toArray(new AttributeDescriptor[layoutAttributes.size()]), 361 null, // children 362 false /* mandatory */); 363 desc.setAttributeSources(Collections.unmodifiableList(attributeSources)); 364 infoDescMap.put(info, desc); 365 return desc; 366 } 367 368 /** 369 * Creates a new <include> descriptor and adds it to the list of view descriptors. 370 * 371 * @param knownViews A list of view descriptors being populated. Also used to find the 372 * View descriptor and extract its layout attributes. 373 */ 374 private void insertInclude(List<ViewElementDescriptor> knownViews) { 375 String xmlName = VIEW_INCLUDE; 376 377 // Create the include custom attributes 378 ArrayList<AttributeDescriptor> attributes = new ArrayList<AttributeDescriptor>(); 379 380 // Note that the "layout" attribute does NOT have the Android namespace 381 DescriptorsUtils.appendAttribute(attributes, 382 null, //elementXmlName 383 null, //nsUri 384 new AttributeInfo( 385 ATTR_LAYOUT, 386 new Format[] { Format.REFERENCE } ), 387 true, //required 388 null); //overrides 389 390 DescriptorsUtils.appendAttribute(attributes, 391 null, //elementXmlName 392 SdkConstants.NS_RESOURCES, //nsUri 393 new AttributeInfo( 394 "id", //$NON-NLS-1$ 395 new Format[] { Format.REFERENCE } ), 396 true, //required 397 null); //overrides 398 399 // Find View and inherit all its layout attributes 400 AttributeDescriptor[] viewLayoutAttribs = findViewLayoutAttributes( 401 SdkConstants.CLASS_VIEW); 402 403 // Create the include descriptor 404 ViewElementDescriptor desc = new ViewElementDescriptor(xmlName, 405 xmlName, // ui_name 406 VIEW_INCLUDE, // "class name"; the GLE only treats this as an element tag 407 "Lets you statically include XML layouts inside other XML layouts.", // tooltip 408 null, // sdk_url 409 attributes.toArray(new AttributeDescriptor[attributes.size()]), 410 viewLayoutAttribs, // layout attributes 411 null, // children 412 false /* mandatory */); 413 414 knownViews.add(desc); 415 } 416 417 /** 418 * Creates and returns a new {@code <merge>} descriptor. 419 * @param viewLayoutAttribs The layout attributes to use for the new descriptor 420 */ 421 private ViewElementDescriptor createMerge(AttributeDescriptor[] viewLayoutAttribs) { 422 String xmlName = VIEW_MERGE; 423 424 // Create the include descriptor 425 ViewElementDescriptor desc = new ViewElementDescriptor(xmlName, 426 xmlName, // ui_name 427 VIEW_MERGE, // "class name"; the GLE only treats this as an element tag 428 "A root tag useful for XML layouts inflated using a ViewStub.", // tooltip 429 null, // sdk_url 430 null, // attributes 431 viewLayoutAttribs, // layout attributes 432 null, // children 433 false /* mandatory */); 434 435 return desc; 436 } 437 438 /** 439 * Creates and returns a new {@code <fragment>} descriptor. 440 * @param viewLayoutAttribs The layout attributes to use for the new descriptor 441 * @param styleMap The style map provided by the SDK 442 */ 443 private ViewElementDescriptor createFragment(AttributeDescriptor[] viewLayoutAttribs, 444 Map<String, DeclareStyleableInfo> styleMap) { 445 String xmlName = VIEW_FRAGMENT; 446 final ViewElementDescriptor descriptor; 447 448 // First try to create the descriptor from metadata in attrs.xml: 449 DeclareStyleableInfo style = styleMap.get("Fragment"); //$NON-NLS-1$ 450 String fragmentTooltip = 451 "A Fragment is a piece of an application's user interface or behavior that " 452 + "can be placed in an Activity"; 453 String sdkUrl = "http://developer.android.com/guide/topics/fundamentals/fragments.html"; 454 ClassAttributeDescriptor classAttribute = new ClassAttributeDescriptor( 455 // Should accept both CLASS_V4_FRAGMENT and CLASS_FRAGMENT 456 null /*superClassName*/, 457 ATTR_CLASS, ATTR_CLASS, null /* namespace */, 458 "Supply the name of the fragment class to instantiate", 459 new AttributeInfo(ATTR_CLASS, new Format[] { Format.STRING}), 460 true /*mandatory*/); 461 462 if (style != null) { 463 descriptor = new ViewElementDescriptor( 464 VIEW_FRAGMENT, VIEW_FRAGMENT, VIEW_FRAGMENT, 465 fragmentTooltip, // tooltip 466 sdkUrl, //, 467 null /* attributes */, 468 viewLayoutAttribs, // layout attributes 469 null /*childrenElements*/, 470 false /*mandatory*/); 471 ArrayList<AttributeDescriptor> descs = new ArrayList<AttributeDescriptor>(); 472 // The class attribute is not included in the attrs.xml 473 descs.add(classAttribute); 474 DescriptorsUtils.appendAttributes(descs, 475 null, // elementName 476 SdkConstants.NS_RESOURCES, 477 style.getAttributes(), 478 null, // requiredAttributes 479 null); // overrides 480 //descriptor.setTooltip(style.getJavaDoc()); 481 descriptor.setAttributes(descs.toArray(new AttributeDescriptor[descs.size()])); 482 } else { 483 // The above will only work on API 11 and up. However, fragments are *also* available 484 // on older platforms, via the fragment support library, so add in a manual 485 // entry if necessary. 486 descriptor = new ViewElementDescriptor(xmlName, 487 xmlName, // ui_name 488 xmlName, // "class name"; the GLE only treats this as an element tag 489 fragmentTooltip, 490 sdkUrl, 491 new AttributeDescriptor[] { 492 new ClassAttributeDescriptor( 493 null /*superClassName*/, 494 ATTR_NAME, ATTR_NAME, ANDROID_URI, 495 "Supply the name of the fragment class to instantiate", 496 new AttributeInfo(ATTR_NAME, new Format[] { Format.STRING}), 497 true /*mandatory*/), 498 classAttribute, 499 new ClassAttributeDescriptor( 500 null /*superClassName*/, 501 ATTR_TAG, ATTR_TAG, ANDROID_URI, 502 "Supply a tag for the top-level view containing a String", 503 new AttributeInfo(ATTR_TAG, new Format[] { Format.STRING}), 504 true /*mandatory*/), 505 }, // attributes 506 viewLayoutAttribs, // layout attributes 507 null, // children 508 false /* mandatory */); 509 } 510 511 return descriptor; 512 } 513 514 /** 515 * Creates and returns a new {@code <requestFocus>} descriptor. 516 */ 517 private ViewElementDescriptor createRequestFocus() { 518 String xmlName = REQUEST_FOCUS; 519 520 // Create the include descriptor 521 return new ViewElementDescriptor( 522 xmlName, // xml_name 523 xmlName, // ui_name 524 xmlName, // "class name"; the GLE only treats this as an element tag 525 "Requests focus for the parent element or one of its descendants", // tooltip 526 null, // sdk_url 527 null, // attributes 528 null, // layout attributes 529 null, // children 530 false /* mandatory */); 531 } 532 533 /** 534 * Finds the descriptor and retrieves all its layout attributes. 535 */ 536 private AttributeDescriptor[] findViewLayoutAttributes( 537 String viewFqcn) { 538 ViewElementDescriptor viewDesc = findDescriptorByClass(viewFqcn); 539 if (viewDesc != null) { 540 return viewDesc.getLayoutAttributes(); 541 } 542 543 return null; 544 } 545 546 /** 547 * Set the super-class of each {@link ViewElementDescriptor} by using the super-class 548 * information available in the {@link ViewClassInfo}. 549 */ 550 private void fixSuperClasses(Map<ViewClassInfo, ViewElementDescriptor> infoDescMap) { 551 552 for (Entry<ViewClassInfo, ViewElementDescriptor> entry : infoDescMap.entrySet()) { 553 ViewClassInfo info = entry.getKey(); 554 ViewElementDescriptor desc = entry.getValue(); 555 556 ViewClassInfo sup = info.getSuperClass(); 557 if (sup != null) { 558 ViewElementDescriptor supDesc = infoDescMap.get(sup); 559 while (supDesc == null && sup != null) { 560 // We don't have a descriptor for the super-class. That means the class is 561 // probably abstract, so we just need to walk up the super-class chain till 562 // we find one we have. All views derive from android.view.View so we should 563 // surely find that eventually. 564 sup = sup.getSuperClass(); 565 if (sup != null) { 566 supDesc = infoDescMap.get(sup); 567 } 568 } 569 if (supDesc != null) { 570 desc.setSuperClass(supDesc); 571 } 572 } 573 } 574 } 575 576 /** 577 * Returns the {@link ViewElementDescriptor} with the given fully qualified class 578 * name, or null if not found. This is a quick map lookup. 579 * 580 * @param fqcn the fully qualified class name 581 * @return the corresponding {@link ViewElementDescriptor} or null 582 */ 583 public ViewElementDescriptor findDescriptorByClass(String fqcn) { 584 return mFqcnToDescriptor.get(fqcn); 585 } 586 587 /** 588 * Returns the {@link ViewElementDescriptor} with the given XML tag name, 589 * which usually does not include the package (depending on the 590 * value of {@link ViewElementDescriptor#viewNeedsPackage(String)}). 591 * 592 * @param tag the XML tag name 593 * @return the corresponding {@link ViewElementDescriptor} or null 594 */ 595 public ViewElementDescriptor findDescriptorByTag(String tag) { 596 // TODO: Consider whether we need to add a direct map lookup for this as well. 597 // Currently not done since this is not frequently needed (only needed for 598 // exploded rendering which was already performing list iteration.) 599 for (ViewElementDescriptor descriptor : mLayoutDescriptors) { 600 if (tag.equals(descriptor.getXmlLocalName())) { 601 return descriptor; 602 } 603 } 604 605 return null; 606 } 607 608 /** 609 * Returns a collection of all the view class names, including layouts 610 * 611 * @return a collection of all the view class names, never null 612 */ 613 public Collection<String> getAllViewClassNames() { 614 return mFqcnToDescriptor.keySet(); 615 } 616 } 617