Home | History | Annotate | Download | only in sdk
      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.sdk;
     18 
     19 import com.android.ide.common.resources.platform.AttrsXmlParser;
     20 import com.android.ide.common.resources.platform.ViewClassInfo;
     21 import com.android.ide.common.resources.platform.ViewClassInfo.LayoutParamsInfo;
     22 import com.android.ide.eclipse.adt.AdtPlugin;
     23 import com.android.ide.eclipse.adt.internal.sdk.IAndroidClassLoader.IClassDescriptor;
     24 import com.android.sdklib.SdkConstants;
     25 
     26 import org.eclipse.core.runtime.IProgressMonitor;
     27 import org.eclipse.core.runtime.SubMonitor;
     28 
     29 import java.io.IOException;
     30 import java.util.ArrayList;
     31 import java.util.Collection;
     32 import java.util.HashMap;
     33 import java.util.List;
     34 import java.util.SortedMap;
     35 import java.util.TreeMap;
     36 
     37 import javax.management.InvalidAttributeValueException;
     38 
     39 /*
     40  * TODO: refactor this. Could use some cleanup.
     41  */
     42 
     43 /**
     44  * Parser for the framework library.
     45  * <p/>
     46  * This gather the following information:
     47  * <ul>
     48  * <li>Resource ID from <code>android.R</code></li>
     49  * <li>The list of permissions values from <code>android.Manifest$permission</code></li>
     50  * <li></li>
     51  * </ul>
     52  */
     53 public class LayoutParamsParser {
     54 
     55     /**
     56      * Class extending {@link ViewClassInfo} by adding the notion of instantiability.
     57      * {@link LayoutParamsParser#getViews()} and {@link LayoutParamsParser#getGroups()} should
     58      * only return classes that can be instantiated.
     59      */
     60     final static class ExtViewClassInfo extends ViewClassInfo {
     61 
     62         private boolean mIsInstantiable;
     63 
     64         ExtViewClassInfo(boolean instantiable, boolean isLayout, String canonicalClassName,
     65                 String shortClassName) {
     66             super(isLayout, canonicalClassName, shortClassName);
     67             mIsInstantiable = instantiable;
     68         }
     69 
     70         boolean isInstantiable() {
     71             return mIsInstantiable;
     72         }
     73     }
     74 
     75     /* Note: protected members/methods are overridden in unit tests */
     76 
     77     /** Reference to android.view.View */
     78     protected IClassDescriptor mTopViewClass;
     79     /** Reference to android.view.ViewGroup */
     80     protected IClassDescriptor mTopGroupClass;
     81     /** Reference to android.view.ViewGroup$LayoutParams */
     82     protected IClassDescriptor mTopLayoutParamsClass;
     83 
     84     /** Input list of all classes deriving from android.view.View */
     85     protected ArrayList<IClassDescriptor> mViewList;
     86     /** Input list of all classes deriving from android.view.ViewGroup */
     87     protected ArrayList<IClassDescriptor> mGroupList;
     88 
     89     /** Output map of FQCN => info on View classes */
     90     protected TreeMap<String, ExtViewClassInfo> mViewMap;
     91     /** Output map of FQCN => info on ViewGroup classes */
     92     protected TreeMap<String, ExtViewClassInfo> mGroupMap;
     93     /** Output map of FQCN => info on LayoutParams classes */
     94     protected HashMap<String, LayoutParamsInfo> mLayoutParamsMap;
     95 
     96     /** The attrs.xml parser */
     97     protected AttrsXmlParser mAttrsXmlParser;
     98 
     99     /** The android.jar class loader */
    100     protected IAndroidClassLoader mClassLoader;
    101 
    102     /**
    103      * Instantiate a new LayoutParamsParser.
    104      * @param classLoader The android.jar class loader
    105      * @param attrsXmlParser The parser of the attrs.xml file
    106      */
    107     public LayoutParamsParser(IAndroidClassLoader classLoader,
    108             AttrsXmlParser attrsXmlParser) {
    109         mClassLoader = classLoader;
    110         mAttrsXmlParser = attrsXmlParser;
    111     }
    112 
    113     /** Returns the map of FQCN => info on View classes */
    114     public List<ViewClassInfo> getViews() {
    115         return getInstantiables(mViewMap);
    116     }
    117 
    118     /** Returns the map of FQCN => info on ViewGroup classes */
    119     public List<ViewClassInfo> getGroups() {
    120         return getInstantiables(mGroupMap);
    121     }
    122 
    123     /**
    124      * TODO: doc here.
    125      * <p/>
    126      * Note: on output we should have NO dependency on {@link IClassDescriptor},
    127      * otherwise we wouldn't be able to unload the class loader later.
    128      * <p/>
    129      * Note on Vocabulary: FQCN=Fully Qualified Class Name (e.g. "my.package.class$innerClass")
    130      * @param monitor A progress monitor. Can be null. Caller is responsible for calling done.
    131      */
    132     public void parseLayoutClasses(IProgressMonitor monitor) {
    133         parseClasses(monitor,
    134                 SdkConstants.CLASS_VIEW,
    135                 SdkConstants.CLASS_VIEWGROUP,
    136                 SdkConstants.CLASS_VIEWGROUP_LAYOUTPARAMS);
    137     }
    138 
    139     public void parsePreferencesClasses(IProgressMonitor monitor) {
    140         parseClasses(monitor,
    141                 SdkConstants.CLASS_PREFERENCE,
    142                 SdkConstants.CLASS_PREFERENCEGROUP,
    143                 null /* paramsClassName */ );
    144     }
    145 
    146     private void parseClasses(IProgressMonitor monitor,
    147             String rootClassName,
    148             String groupClassName,
    149             String paramsClassName) {
    150         try {
    151             SubMonitor progress = SubMonitor.convert(monitor, 100);
    152 
    153             String[] superClasses = new String[2 + (paramsClassName == null ? 0 : 1)];
    154             superClasses[0] = groupClassName;
    155             superClasses[1] = rootClassName;
    156             if (paramsClassName != null) {
    157                 superClasses[2] = paramsClassName;
    158             }
    159             HashMap<String, ArrayList<IClassDescriptor>> found =
    160                     mClassLoader.findClassesDerivingFrom("android.", superClasses);  //$NON-NLS-1$
    161             mTopViewClass = mClassLoader.getClass(rootClassName);
    162             mTopGroupClass = mClassLoader.getClass(groupClassName);
    163             if (paramsClassName != null) {
    164                 mTopLayoutParamsClass = mClassLoader.getClass(paramsClassName);
    165             }
    166 
    167             mViewList = found.get(rootClassName);
    168             mGroupList = found.get(groupClassName);
    169 
    170             mViewMap = new TreeMap<String, ExtViewClassInfo>();
    171             mGroupMap = new TreeMap<String, ExtViewClassInfo>();
    172             if (mTopLayoutParamsClass != null) {
    173                 mLayoutParamsMap = new HashMap<String, LayoutParamsInfo>();
    174             }
    175 
    176             // Add top classes to the maps since by design they are not listed in classes deriving
    177             // from themselves.
    178             if (mTopGroupClass != null) {
    179                 addGroup(mTopGroupClass);
    180             }
    181             if (mTopViewClass != null) {
    182                 addView(mTopViewClass);
    183             }
    184 
    185             // ViewGroup derives from View
    186             ExtViewClassInfo vg = mGroupMap.get(groupClassName);
    187             if (vg != null) {
    188                 vg.setSuperClass(mViewMap.get(rootClassName));
    189             }
    190 
    191             progress.setWorkRemaining(mGroupList.size() + mViewList.size());
    192 
    193             for (IClassDescriptor groupChild : mGroupList) {
    194                 addGroup(groupChild);
    195                 progress.worked(1);
    196             }
    197 
    198             for (IClassDescriptor viewChild : mViewList) {
    199                 if (viewChild != mTopGroupClass) {
    200                     addView(viewChild);
    201                 }
    202                 progress.worked(1);
    203             }
    204         } catch (ClassNotFoundException e) {
    205             AdtPlugin.log(e, "Problem loading class %1$s or %2$s",  //$NON-NLS-1$
    206                     rootClassName, groupClassName);
    207         } catch (InvalidAttributeValueException e) {
    208             AdtPlugin.log(e, "Problem loading classes"); //$NON-NLS-1$
    209         } catch (ClassFormatError e) {
    210             AdtPlugin.log(e, "Problem loading classes"); //$NON-NLS-1$
    211         } catch (IOException e) {
    212             AdtPlugin.log(e, "Problem loading classes"); //$NON-NLS-1$
    213         }
    214     }
    215 
    216     /**
    217      * Parses a View class and adds a ExtViewClassInfo for it in mViewMap.
    218      * It calls itself recursively to handle super classes which are also Views.
    219      */
    220     private ExtViewClassInfo addView(IClassDescriptor viewClass) {
    221         String fqcn = viewClass.getFullClassName();
    222         if (mViewMap.containsKey(fqcn)) {
    223             return mViewMap.get(fqcn);
    224         } else if (mGroupMap.containsKey(fqcn)) {
    225             return mGroupMap.get(fqcn);
    226         }
    227 
    228         ExtViewClassInfo info = new ExtViewClassInfo(viewClass.isInstantiable(),
    229                 false /* layout */, fqcn, viewClass.getSimpleName());
    230         mViewMap.put(fqcn, info);
    231 
    232         // All view classes derive from mTopViewClass by design.
    233         // Do not lookup the super class for mTopViewClass itself.
    234         if (viewClass.equals(mTopViewClass) == false) {
    235             IClassDescriptor superClass = viewClass.getSuperclass();
    236             ExtViewClassInfo superClassInfo = addView(superClass);
    237             info.setSuperClass(superClassInfo);
    238         }
    239 
    240         mAttrsXmlParser.loadViewAttributes(info);
    241         return info;
    242     }
    243 
    244     /**
    245      * Parses a ViewGroup class and adds a ExtViewClassInfo for it in mGroupMap.
    246      * It calls itself recursively to handle super classes which are also ViewGroups.
    247      */
    248     private ExtViewClassInfo addGroup(IClassDescriptor groupClass) {
    249         String fqcn = groupClass.getFullClassName();
    250         if (mGroupMap.containsKey(fqcn)) {
    251             return mGroupMap.get(fqcn);
    252         }
    253 
    254         ExtViewClassInfo info = new ExtViewClassInfo(groupClass.isInstantiable(),
    255                 true /* layout */, fqcn, groupClass.getSimpleName());
    256         mGroupMap.put(fqcn, info);
    257 
    258         // All groups derive from android.view.ViewGroup, which in turns derives from
    259         // android.view.View (i.e. mTopViewClass here). So the only group that can have View as
    260         // its super class is the ViewGroup base class and we don't try to resolve it since groups
    261         // are loaded before views.
    262         IClassDescriptor superClass = groupClass.getSuperclass();
    263 
    264         // Assertion: at this point, we should have
    265         //   superClass != mTopViewClass || fqcn.equals(AndroidConstants.CLASS_VIEWGROUP);
    266 
    267         if (superClass != null && superClass.equals(mTopViewClass) == false) {
    268             ExtViewClassInfo superClassInfo = addGroup(superClass);
    269 
    270             // Assertion: we should have superClassInfo != null && superClassInfo != info;
    271             if (superClassInfo != null && superClassInfo != info) {
    272                 info.setSuperClass(superClassInfo);
    273             }
    274         }
    275 
    276         mAttrsXmlParser.loadViewAttributes(info);
    277         if (mTopLayoutParamsClass != null) {
    278             info.setLayoutParams(addLayoutParams(groupClass));
    279         }
    280         return info;
    281     }
    282 
    283     /**
    284      * Parses a ViewGroup class and returns an info object on its inner LayoutParams.
    285      *
    286      * @return The {@link LayoutParamsInfo} for the ViewGroup class or null.
    287      */
    288     private LayoutParamsInfo addLayoutParams(IClassDescriptor groupClass) {
    289 
    290         // Is there a LayoutParams in this group class?
    291         IClassDescriptor layoutParamsClass = findLayoutParams(groupClass);
    292 
    293         // if there's no layout data in the group class, link to the one from the
    294         // super class.
    295         if (layoutParamsClass == null) {
    296             for (IClassDescriptor superClass = groupClass.getSuperclass();
    297                     layoutParamsClass == null &&
    298                         superClass != null &&
    299                         superClass.equals(mTopViewClass) == false;
    300                     superClass = superClass.getSuperclass()) {
    301                 layoutParamsClass = findLayoutParams(superClass);
    302             }
    303         }
    304 
    305         if (layoutParamsClass != null) {
    306             return getLayoutParamsInfo(layoutParamsClass);
    307         }
    308 
    309         return null;
    310     }
    311 
    312     /**
    313      * Parses a LayoutParams class and returns a LayoutParamsInfo object for it.
    314      * It calls itself recursively to handle the super class of the LayoutParams.
    315      */
    316     private LayoutParamsInfo getLayoutParamsInfo(IClassDescriptor layoutParamsClass) {
    317         String fqcn = layoutParamsClass.getFullClassName();
    318         LayoutParamsInfo layoutParamsInfo = mLayoutParamsMap.get(fqcn);
    319 
    320         if (layoutParamsInfo != null) {
    321             return layoutParamsInfo;
    322         }
    323 
    324         // Find the link on the LayoutParams super class
    325         LayoutParamsInfo superClassInfo = null;
    326         if (layoutParamsClass.equals(mTopLayoutParamsClass) == false) {
    327             IClassDescriptor superClass = layoutParamsClass.getSuperclass();
    328             superClassInfo = getLayoutParamsInfo(superClass);
    329         }
    330 
    331         // Find the link on the enclosing ViewGroup
    332         ExtViewClassInfo enclosingGroupInfo = addGroup(layoutParamsClass.getEnclosingClass());
    333 
    334         layoutParamsInfo = new ExtViewClassInfo.LayoutParamsInfo(
    335                 enclosingGroupInfo, layoutParamsClass.getSimpleName(), superClassInfo);
    336         mLayoutParamsMap.put(fqcn, layoutParamsInfo);
    337 
    338         mAttrsXmlParser.loadLayoutParamsAttributes(layoutParamsInfo);
    339 
    340         return layoutParamsInfo;
    341     }
    342 
    343     /**
    344      * Given a ViewGroup-derived class, looks for an inner class named LayoutParams
    345      * and if found returns its class definition.
    346      * <p/>
    347      * This uses the actual defined inner classes and does not look at inherited classes.
    348      *
    349      * @param groupClass The ViewGroup derived class
    350      * @return The Class of the inner LayoutParams or null if none is declared.
    351      */
    352     private IClassDescriptor findLayoutParams(IClassDescriptor groupClass) {
    353         IClassDescriptor[] innerClasses = groupClass.getDeclaredClasses();
    354         for (IClassDescriptor innerClass : innerClasses) {
    355             if (innerClass.getSimpleName().equals(SdkConstants.CLASS_NAME_LAYOUTPARAMS)) {
    356                 return innerClass;
    357             }
    358         }
    359         return null;
    360     }
    361 
    362     /**
    363      * Computes and return a list of ViewClassInfo from a map by filtering out the class that
    364      * cannot be instantiated.
    365      */
    366     private List<ViewClassInfo> getInstantiables(SortedMap<String, ExtViewClassInfo> map) {
    367         Collection<ExtViewClassInfo> values = map.values();
    368         ArrayList<ViewClassInfo> list = new ArrayList<ViewClassInfo>();
    369 
    370         for (ExtViewClassInfo info : values) {
    371             if (info.isInstantiable()) {
    372                 list.add(info);
    373             }
    374         }
    375 
    376         return list;
    377     }
    378 }
    379