Home | History | Annotate | Download | only in descriptors
      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