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