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.SdkConstants; 20 import com.android.ide.common.rendering.LayoutLibrary; 21 import com.android.ide.common.resources.ResourceRepository; 22 import com.android.ide.common.resources.platform.AttrsXmlParser; 23 import com.android.ide.common.resources.platform.DeclareStyleableInfo; 24 import com.android.ide.common.resources.platform.ViewClassInfo; 25 import com.android.ide.eclipse.adt.AdtPlugin; 26 import com.android.ide.eclipse.adt.internal.editors.animator.AnimDescriptors; 27 import com.android.ide.eclipse.adt.internal.editors.animator.AnimatorDescriptors; 28 import com.android.ide.eclipse.adt.internal.editors.color.ColorDescriptors; 29 import com.android.ide.eclipse.adt.internal.editors.drawable.DrawableDescriptors; 30 import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors; 31 import com.android.ide.eclipse.adt.internal.editors.manifest.descriptors.AndroidManifestDescriptors; 32 import com.android.ide.eclipse.adt.internal.editors.menu.descriptors.MenuDescriptors; 33 import com.android.ide.eclipse.adt.internal.editors.otherxml.descriptors.OtherXmlDescriptors; 34 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager; 35 import com.android.sdklib.IAndroidTarget; 36 37 import org.eclipse.core.runtime.IProgressMonitor; 38 import org.eclipse.core.runtime.IStatus; 39 import org.eclipse.core.runtime.Status; 40 import org.eclipse.core.runtime.SubMonitor; 41 42 import java.io.BufferedReader; 43 import java.io.FileNotFoundException; 44 import java.io.FileReader; 45 import java.io.IOException; 46 import java.lang.reflect.Field; 47 import java.lang.reflect.Modifier; 48 import java.util.ArrayList; 49 import java.util.Collection; 50 import java.util.Collections; 51 import java.util.HashMap; 52 import java.util.List; 53 import java.util.Map; 54 55 import javax.management.InvalidAttributeValueException; 56 57 /** 58 * Parser for the platform data in an SDK. 59 * <p/> 60 * This gather the following information: 61 * <ul> 62 * <li>Resource ID from <code>android.R</code></li> 63 * <li>The list of permissions values from <code>android.Manifest$permission</code></li> 64 * <li></li> 65 * </ul> 66 */ 67 public final class AndroidTargetParser { 68 69 private static final String TAG = "Framework Resource Parser"; 70 private final IAndroidTarget mAndroidTarget; 71 72 /** 73 * Creates a platform data parser. 74 */ 75 public AndroidTargetParser(IAndroidTarget platformTarget) { 76 mAndroidTarget = platformTarget; 77 } 78 79 /** 80 * Parses the framework, collects all interesting information and stores them in the 81 * {@link IAndroidTarget} given to the constructor. 82 * 83 * @param monitor A progress monitor. Can be null. Caller is responsible for calling done. 84 * @return True if the SDK path was valid and parsing has been attempted. 85 */ 86 public IStatus run(IProgressMonitor monitor) { 87 try { 88 SubMonitor progress = SubMonitor.convert(monitor, 89 String.format("Parsing SDK %1$s", mAndroidTarget.getName()), 90 16); 91 92 AndroidTargetData targetData = new AndroidTargetData(mAndroidTarget); 93 94 // parse the rest of the data. 95 96 AndroidJarLoader classLoader = 97 new AndroidJarLoader(mAndroidTarget.getPath(IAndroidTarget.ANDROID_JAR)); 98 99 preload(classLoader, progress.newChild(40, SubMonitor.SUPPRESS_NONE)); 100 101 if (progress.isCanceled()) { 102 return Status.CANCEL_STATUS; 103 } 104 105 // get the permissions 106 progress.subTask("Permissions"); 107 String[] permissionValues = collectPermissions(classLoader); 108 progress.worked(1); 109 110 if (progress.isCanceled()) { 111 return Status.CANCEL_STATUS; 112 } 113 114 // get the action and category values for the Intents. 115 progress.subTask("Intents"); 116 ArrayList<String> activity_actions = new ArrayList<String>(); 117 ArrayList<String> broadcast_actions = new ArrayList<String>(); 118 ArrayList<String> service_actions = new ArrayList<String>(); 119 ArrayList<String> categories = new ArrayList<String>(); 120 collectIntentFilterActionsAndCategories(activity_actions, broadcast_actions, 121 service_actions, categories); 122 progress.worked(1); 123 124 if (progress.isCanceled()) { 125 return Status.CANCEL_STATUS; 126 } 127 128 // gather the attribute definition 129 progress.subTask("Attributes definitions"); 130 AttrsXmlParser attrsXmlParser = new AttrsXmlParser( 131 mAndroidTarget.getPath(IAndroidTarget.ATTRIBUTES), 132 AdtPlugin.getDefault(), 133 1000); 134 attrsXmlParser.preload(); 135 136 progress.worked(1); 137 138 progress.subTask("Manifest definitions"); 139 AttrsXmlParser attrsManifestXmlParser = new AttrsXmlParser( 140 mAndroidTarget.getPath(IAndroidTarget.MANIFEST_ATTRIBUTES), 141 attrsXmlParser, 142 AdtPlugin.getDefault(), 1100); 143 attrsManifestXmlParser.preload(); 144 progress.worked(1); 145 146 Collection<ViewClassInfo> mainList = new ArrayList<ViewClassInfo>(); 147 Collection<ViewClassInfo> groupList = new ArrayList<ViewClassInfo>(); 148 149 // collect the layout/widgets classes 150 progress.subTask("Widgets and layouts"); 151 collectLayoutClasses(classLoader, attrsXmlParser, mainList, groupList, 152 progress.newChild(1)); 153 154 if (progress.isCanceled()) { 155 return Status.CANCEL_STATUS; 156 } 157 158 ViewClassInfo[] layoutViewsInfo = mainList.toArray( 159 new ViewClassInfo[mainList.size()]); 160 ViewClassInfo[] layoutGroupsInfo = groupList.toArray( 161 new ViewClassInfo[groupList.size()]); 162 mainList.clear(); 163 groupList.clear(); 164 165 // collect the preferences classes. 166 collectPreferenceClasses(classLoader, attrsXmlParser, mainList, groupList, 167 progress.newChild(1)); 168 169 if (progress.isCanceled()) { 170 return Status.CANCEL_STATUS; 171 } 172 173 ViewClassInfo[] preferencesInfo = mainList.toArray(new ViewClassInfo[mainList.size()]); 174 ViewClassInfo[] preferenceGroupsInfo = groupList.toArray( 175 new ViewClassInfo[groupList.size()]); 176 177 Map<String, DeclareStyleableInfo> xmlMenuMap = collectMenuDefinitions(attrsXmlParser); 178 Map<String, DeclareStyleableInfo> xmlSearchableMap = collectSearchableDefinitions( 179 attrsXmlParser); 180 Map<String, DeclareStyleableInfo> manifestMap = collectManifestDefinitions( 181 attrsManifestXmlParser); 182 Map<String, Map<String, Integer>> enumValueMap = attrsXmlParser.getEnumFlagValues(); 183 184 Map<String, DeclareStyleableInfo> xmlAppWidgetMap = null; 185 if (mAndroidTarget.getVersion().getApiLevel() >= 3) { 186 xmlAppWidgetMap = collectAppWidgetDefinitions(attrsXmlParser); 187 } 188 189 if (progress.isCanceled()) { 190 return Status.CANCEL_STATUS; 191 } 192 193 // From the information that was collected, create the pieces that will be put in 194 // the PlatformData object. 195 AndroidManifestDescriptors manifestDescriptors = new AndroidManifestDescriptors(); 196 manifestDescriptors.updateDescriptors(manifestMap); 197 progress.worked(1); 198 199 if (progress.isCanceled()) { 200 return Status.CANCEL_STATUS; 201 } 202 203 LayoutDescriptors layoutDescriptors = new LayoutDescriptors(); 204 layoutDescriptors.updateDescriptors(layoutViewsInfo, layoutGroupsInfo, 205 attrsXmlParser.getDeclareStyleableList(), mAndroidTarget); 206 progress.worked(1); 207 208 if (progress.isCanceled()) { 209 return Status.CANCEL_STATUS; 210 } 211 212 MenuDescriptors menuDescriptors = new MenuDescriptors(); 213 menuDescriptors.updateDescriptors(xmlMenuMap); 214 progress.worked(1); 215 216 if (progress.isCanceled()) { 217 return Status.CANCEL_STATUS; 218 } 219 220 OtherXmlDescriptors otherXmlDescriptors = new OtherXmlDescriptors(); 221 otherXmlDescriptors.updateDescriptors( 222 xmlSearchableMap, 223 xmlAppWidgetMap, 224 preferencesInfo, 225 preferenceGroupsInfo); 226 progress.worked(1); 227 228 if (progress.isCanceled()) { 229 return Status.CANCEL_STATUS; 230 } 231 232 DrawableDescriptors drawableDescriptors = new DrawableDescriptors(); 233 Map<String, DeclareStyleableInfo> map = attrsXmlParser.getDeclareStyleableList(); 234 drawableDescriptors.updateDescriptors(map); 235 progress.worked(1); 236 237 if (progress.isCanceled()) { 238 return Status.CANCEL_STATUS; 239 } 240 241 AnimatorDescriptors animatorDescriptors = new AnimatorDescriptors(); 242 animatorDescriptors.updateDescriptors(map); 243 progress.worked(1); 244 245 if (progress.isCanceled()) { 246 return Status.CANCEL_STATUS; 247 } 248 249 AnimDescriptors animDescriptors = new AnimDescriptors(); 250 animDescriptors.updateDescriptors(map); 251 progress.worked(1); 252 253 if (progress.isCanceled()) { 254 return Status.CANCEL_STATUS; 255 } 256 257 ColorDescriptors colorDescriptors = new ColorDescriptors(); 258 colorDescriptors.updateDescriptors(map); 259 progress.worked(1); 260 261 // load the framework resources. 262 ResourceRepository frameworkResources = 263 ResourceManager.getInstance().loadFrameworkResources(mAndroidTarget); 264 progress.worked(1); 265 266 // now load the layout lib bridge 267 LayoutLibrary layoutBridge = LayoutLibrary.load( 268 mAndroidTarget.getPath(IAndroidTarget.LAYOUT_LIB), 269 AdtPlugin.getDefault(), 270 "ADT plug-in"); 271 272 progress.worked(1); 273 274 // and finally create the PlatformData with all that we loaded. 275 targetData.setExtraData( 276 manifestDescriptors, 277 layoutDescriptors, 278 menuDescriptors, 279 otherXmlDescriptors, 280 drawableDescriptors, 281 animatorDescriptors, 282 animDescriptors, 283 colorDescriptors, 284 enumValueMap, 285 permissionValues, 286 activity_actions.toArray(new String[activity_actions.size()]), 287 broadcast_actions.toArray(new String[broadcast_actions.size()]), 288 service_actions.toArray(new String[service_actions.size()]), 289 categories.toArray(new String[categories.size()]), 290 mAndroidTarget.getPlatformLibraries(), 291 mAndroidTarget.getOptionalLibraries(), 292 frameworkResources, 293 layoutBridge); 294 295 targetData.setAttributeMap(attrsXmlParser.getAttributeMap()); 296 297 Sdk.getCurrent().setTargetData(mAndroidTarget, targetData); 298 299 return Status.OK_STATUS; 300 } catch (Exception e) { 301 AdtPlugin.logAndPrintError(e, TAG, "SDK parser failed"); //$NON-NLS-1$ 302 AdtPlugin.printToConsole("SDK parser failed", e.getMessage()); 303 return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, "SDK parser failed", e); 304 } 305 } 306 307 /** 308 * Preloads all "interesting" classes from the framework SDK jar. 309 * <p/> 310 * Currently this preloads all classes from the framework jar 311 * 312 * @param classLoader The framework SDK jar classloader 313 * @param monitor A progress monitor. Can be null. Caller is responsible for calling done. 314 */ 315 private void preload(AndroidJarLoader classLoader, IProgressMonitor monitor) { 316 try { 317 classLoader.preLoadClasses("" /* all classes */, //$NON-NLS-1$ 318 mAndroidTarget.getName(), // monitor task label 319 monitor); 320 } catch (InvalidAttributeValueException e) { 321 AdtPlugin.log(e, "Problem preloading classes"); //$NON-NLS-1$ 322 } catch (IOException e) { 323 AdtPlugin.log(e, "Problem preloading classes"); //$NON-NLS-1$ 324 } 325 } 326 327 /** 328 * Loads, collects and returns the list of default permissions from the framework. 329 * 330 * @param classLoader The framework SDK jar classloader 331 * @return a non null (but possibly empty) array containing the permission values. 332 */ 333 private String[] collectPermissions(AndroidJarLoader classLoader) { 334 try { 335 Class<?> permissionClass = 336 classLoader.loadClass(SdkConstants.CLASS_MANIFEST_PERMISSION); 337 338 if (permissionClass != null) { 339 ArrayList<String> list = new ArrayList<String>(); 340 341 Field[] fields = permissionClass.getFields(); 342 343 for (Field f : fields) { 344 int modifiers = f.getModifiers(); 345 if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers) && 346 Modifier.isPublic(modifiers)) { 347 try { 348 Object value = f.get(null); 349 if (value instanceof String) { 350 list.add((String)value); 351 } 352 } catch (IllegalArgumentException e) { 353 // since we provide null this should not happen 354 } catch (IllegalAccessException e) { 355 // if the field is inaccessible we ignore it. 356 } catch (NullPointerException npe) { 357 // looks like this is not a static field. we can ignore. 358 } catch (ExceptionInInitializerError eiie) { 359 // lets just ignore the field again 360 } 361 } 362 } 363 364 return list.toArray(new String[list.size()]); 365 } 366 } catch (ClassNotFoundException e) { 367 AdtPlugin.logAndPrintError(e, TAG, 368 "Collect permissions failed, class %1$s not found in %2$s", //$NON-NLS-1$ 369 SdkConstants.CLASS_MANIFEST_PERMISSION, 370 mAndroidTarget.getPath(IAndroidTarget.ANDROID_JAR)); 371 } 372 373 return new String[0]; 374 } 375 376 /** 377 * Loads and collects the action and category default values from the framework. 378 * The values are added to the <code>actions</code> and <code>categories</code> lists. 379 * 380 * @param activityActions the list which will receive the activity action values. 381 * @param broadcastActions the list which will receive the broadcast action values. 382 * @param serviceActions the list which will receive the service action values. 383 * @param categories the list which will receive the category values. 384 */ 385 private void collectIntentFilterActionsAndCategories(ArrayList<String> activityActions, 386 ArrayList<String> broadcastActions, 387 ArrayList<String> serviceActions, ArrayList<String> categories) { 388 collectValues(mAndroidTarget.getPath(IAndroidTarget.ACTIONS_ACTIVITY), 389 activityActions); 390 collectValues(mAndroidTarget.getPath(IAndroidTarget.ACTIONS_BROADCAST), 391 broadcastActions); 392 collectValues(mAndroidTarget.getPath(IAndroidTarget.ACTIONS_SERVICE), 393 serviceActions); 394 collectValues(mAndroidTarget.getPath(IAndroidTarget.CATEGORIES), 395 categories); 396 } 397 398 /** 399 * Collects values from a text file located in the SDK 400 * @param osFilePath The path to the text file. 401 * @param values the {@link ArrayList} to fill with the values. 402 */ 403 private void collectValues(String osFilePath, ArrayList<String> values) { 404 FileReader fr = null; 405 BufferedReader reader = null; 406 try { 407 fr = new FileReader(osFilePath); 408 reader = new BufferedReader(fr); 409 410 String line; 411 while ((line = reader.readLine()) != null) { 412 line = line.trim(); 413 if (line.length() > 0 && line.startsWith("#") == false) { //$NON-NLS-1$ 414 values.add(line); 415 } 416 } 417 } catch (IOException e) { 418 AdtPlugin.log(e, "Failed to read SDK values"); //$NON-NLS-1$ 419 } finally { 420 try { 421 if (reader != null) { 422 reader.close(); 423 } 424 } catch (IOException e) { 425 AdtPlugin.log(e, "Failed to read SDK values"); //$NON-NLS-1$ 426 } 427 428 try { 429 if (fr != null) { 430 fr.close(); 431 } 432 } catch (IOException e) { 433 AdtPlugin.log(e, "Failed to read SDK values"); //$NON-NLS-1$ 434 } 435 } 436 } 437 438 /** 439 * Collects all layout classes information from the class loader and the 440 * attrs.xml and sets the corresponding structures in the resource manager. 441 * 442 * @param classLoader The framework SDK jar classloader in case we cannot get the widget from 443 * the platform directly 444 * @param attrsXmlParser The parser of the attrs.xml file 445 * @param mainList the Collection to receive the main list of {@link ViewClassInfo}. 446 * @param groupList the Collection to receive the group list of {@link ViewClassInfo}. 447 * @param monitor A progress monitor. Can be null. Caller is responsible for calling done. 448 */ 449 private void collectLayoutClasses(AndroidJarLoader classLoader, 450 AttrsXmlParser attrsXmlParser, 451 Collection<ViewClassInfo> mainList, 452 Collection<ViewClassInfo> groupList, 453 IProgressMonitor monitor) { 454 LayoutParamsParser ldp = null; 455 try { 456 WidgetClassLoader loader = new WidgetClassLoader( 457 mAndroidTarget.getPath(IAndroidTarget.WIDGETS)); 458 if (loader.parseWidgetList(monitor)) { 459 ldp = new LayoutParamsParser(loader, attrsXmlParser); 460 } 461 // if the parsing failed, we'll use the old loader below. 462 } catch (FileNotFoundException e) { 463 AdtPlugin.log(e, "Android Framework Parser"); //$NON-NLS-1$ 464 // the file does not exist, we'll use the old loader below. 465 } 466 467 if (ldp == null) { 468 ldp = new LayoutParamsParser(classLoader, attrsXmlParser); 469 } 470 ldp.parseLayoutClasses(monitor); 471 472 List<ViewClassInfo> views = ldp.getViews(); 473 List<ViewClassInfo> groups = ldp.getGroups(); 474 475 if (views != null && groups != null) { 476 mainList.addAll(views); 477 groupList.addAll(groups); 478 } 479 } 480 481 /** 482 * Collects all preferences definition information from the attrs.xml and 483 * sets the corresponding structures in the resource manager. 484 * 485 * @param classLoader The framework SDK jar classloader 486 * @param attrsXmlParser The parser of the attrs.xml file 487 * @param mainList the Collection to receive the main list of {@link ViewClassInfo}. 488 * @param groupList the Collection to receive the group list of {@link ViewClassInfo}. 489 * @param monitor A progress monitor. Can be null. Caller is responsible for calling done. 490 */ 491 private void collectPreferenceClasses(AndroidJarLoader classLoader, 492 AttrsXmlParser attrsXmlParser, Collection<ViewClassInfo> mainList, 493 Collection<ViewClassInfo> groupList, IProgressMonitor monitor) { 494 LayoutParamsParser ldp = new LayoutParamsParser(classLoader, attrsXmlParser); 495 496 try { 497 ldp.parsePreferencesClasses(monitor); 498 499 List<ViewClassInfo> prefs = ldp.getViews(); 500 List<ViewClassInfo> groups = ldp.getGroups(); 501 502 if (prefs != null && groups != null) { 503 mainList.addAll(prefs); 504 groupList.addAll(groups); 505 } 506 } catch (NoClassDefFoundError e) { 507 AdtPlugin.logAndPrintError(e, TAG, 508 "Collect preferences failed, class %1$s not found in %2$s", 509 e.getMessage(), 510 classLoader.getSource()); 511 } catch (Throwable e) { 512 AdtPlugin.log(e, "Android Framework Parser: failed to collect preference classes"); //$NON-NLS-1$ 513 AdtPlugin.printErrorToConsole("Android Framework Parser", 514 "failed to collect preference classes"); 515 } 516 } 517 518 /** 519 * Collects all menu definition information from the attrs.xml and returns it. 520 * 521 * @param attrsXmlParser The parser of the attrs.xml file 522 */ 523 private Map<String, DeclareStyleableInfo> collectMenuDefinitions( 524 AttrsXmlParser attrsXmlParser) { 525 Map<String, DeclareStyleableInfo> map = attrsXmlParser.getDeclareStyleableList(); 526 Map<String, DeclareStyleableInfo> map2 = new HashMap<String, DeclareStyleableInfo>(); 527 for (String key : new String[] { "Menu", //$NON-NLS-1$ 528 "MenuItem", //$NON-NLS-1$ 529 "MenuGroup" }) { //$NON-NLS-1$ 530 if (map.containsKey(key)) { 531 map2.put(key, map.get(key)); 532 } else { 533 AdtPlugin.log(IStatus.WARNING, 534 "Menu declare-styleable %1$s not found in file %2$s", //$NON-NLS-1$ 535 key, attrsXmlParser.getOsAttrsXmlPath()); 536 AdtPlugin.printErrorToConsole("Android Framework Parser", 537 String.format("Menu declare-styleable %1$s not found in file %2$s", //$NON-NLS-1$ 538 key, attrsXmlParser.getOsAttrsXmlPath())); 539 } 540 } 541 542 return Collections.unmodifiableMap(map2); 543 } 544 545 /** 546 * Collects all searchable definition information from the attrs.xml and returns it. 547 * 548 * @param attrsXmlParser The parser of the attrs.xml file 549 */ 550 private Map<String, DeclareStyleableInfo> collectSearchableDefinitions( 551 AttrsXmlParser attrsXmlParser) { 552 Map<String, DeclareStyleableInfo> map = attrsXmlParser.getDeclareStyleableList(); 553 Map<String, DeclareStyleableInfo> map2 = new HashMap<String, DeclareStyleableInfo>(); 554 for (String key : new String[] { "Searchable", //$NON-NLS-1$ 555 "SearchableActionKey" }) { //$NON-NLS-1$ 556 if (map.containsKey(key)) { 557 map2.put(key, map.get(key)); 558 } else { 559 AdtPlugin.log(IStatus.WARNING, 560 "Searchable declare-styleable %1$s not found in file %2$s", //$NON-NLS-1$ 561 key, attrsXmlParser.getOsAttrsXmlPath()); 562 AdtPlugin.printErrorToConsole("Android Framework Parser", 563 String.format("Searchable declare-styleable %1$s not found in file %2$s", //$NON-NLS-1$ 564 key, attrsXmlParser.getOsAttrsXmlPath())); 565 } 566 } 567 568 return Collections.unmodifiableMap(map2); 569 } 570 571 /** 572 * Collects all appWidgetProviderInfo definition information from the attrs.xml and returns it. 573 * 574 * @param attrsXmlParser The parser of the attrs.xml file 575 */ 576 private Map<String, DeclareStyleableInfo> collectAppWidgetDefinitions( 577 AttrsXmlParser attrsXmlParser) { 578 Map<String, DeclareStyleableInfo> map = attrsXmlParser.getDeclareStyleableList(); 579 Map<String, DeclareStyleableInfo> map2 = new HashMap<String, DeclareStyleableInfo>(); 580 for (String key : new String[] { "AppWidgetProviderInfo" }) { //$NON-NLS-1$ 581 if (map.containsKey(key)) { 582 map2.put(key, map.get(key)); 583 } else { 584 AdtPlugin.log(IStatus.WARNING, 585 "AppWidget declare-styleable %1$s not found in file %2$s", //$NON-NLS-1$ 586 key, attrsXmlParser.getOsAttrsXmlPath()); 587 AdtPlugin.printErrorToConsole("Android Framework Parser", 588 String.format("AppWidget declare-styleable %1$s not found in file %2$s", //$NON-NLS-1$ 589 key, attrsXmlParser.getOsAttrsXmlPath())); 590 } 591 } 592 593 return Collections.unmodifiableMap(map2); 594 } 595 596 /** 597 * Collects all manifest definition information from the attrs_manifest.xml and returns it. 598 */ 599 private Map<String, DeclareStyleableInfo> collectManifestDefinitions( 600 AttrsXmlParser attrsXmlParser) { 601 602 return attrsXmlParser.getDeclareStyleableList(); 603 } 604 605 } 606