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