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