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.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
     86     public DocumentDescriptor getDescriptor() {
     87         return mRootDescriptor;
     88     }
     89 
     90     /** Returns the read-only list of all known ViewLayout descriptors. */
     91     public List<ViewElementDescriptor> getLayoutDescriptors() {
     92         return mROLayoutDescriptors;
     93     }
     94 
     95     /** Returns the read-only list of all known View (not ViewLayout) descriptors. */
     96     public List<ViewElementDescriptor> getViewDescriptors() {
     97         return mROViewDescriptors;
     98     }
     99 
    100     @Override
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    594     public Collection<String> getAllViewClassNames() {
    595         return mFqcnToDescriptor.keySet();
    596     }
    597 }
    598