• 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.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. */
getDescriptor()127     public DocumentDescriptor getDescriptor() {
128         return mRootDescriptor;
129     }
130 
131     /** Returns the read-only list of all known ViewLayout descriptors. */
getLayoutDescriptors()132     public List<ViewElementDescriptor> getLayoutDescriptors() {
133         return mROLayoutDescriptors;
134     }
135 
136     /** Returns the read-only list of all known View (not ViewLayout) descriptors. */
getViewDescriptors()137     public List<ViewElementDescriptor> getViewDescriptors() {
138         return mROViewDescriptors;
139     }
140 
getRootElementDescriptors()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      */
getBaseViewDescriptor()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      */
updateDescriptors(ViewClassInfo[] views, ViewClassInfo[] layouts, Map<String, DeclareStyleableInfo> styleMap, IAndroidTarget target)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      */
convertView( ViewClassInfo info, HashMap<ViewClassInfo, ViewElementDescriptor> infoDescMap)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      */
insertInclude(List<ViewElementDescriptor> knownViews)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      */
createMerge(AttributeDescriptor[] viewLayoutAttribs)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      */
createFragment(AttributeDescriptor[] viewLayoutAttribs, Map<String, DeclareStyleableInfo> styleMap)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      */
createRequestFocus()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      */
findViewLayoutAttributes( String viewFqcn)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      */
fixSuperClasses(Map<ViewClassInfo, ViewElementDescriptor> infoDescMap)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      */
findDescriptorByClass(String fqcn)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      */
findDescriptorByTag(String tag)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      */
getAllViewClassNames()613     public Collection<String> getAllViewClassNames() {
614         return mFqcnToDescriptor.keySet();
615     }
616 }
617