| /* |
| * Copyright (C) 2008 The Android Open Source Project |
| * |
| * Licensed under the Eclipse Public License, Version 1.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.eclipse.org/org/documents/epl-v10.php |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.ide.eclipse.adt.internal.sdk; |
| |
| import com.android.SdkConstants; |
| import com.android.ide.common.rendering.LayoutLibrary; |
| import com.android.ide.common.resources.ResourceRepository; |
| import com.android.ide.common.resources.platform.AttrsXmlParser; |
| import com.android.ide.common.resources.platform.DeclareStyleableInfo; |
| import com.android.ide.common.resources.platform.ViewClassInfo; |
| import com.android.ide.eclipse.adt.AdtPlugin; |
| import com.android.ide.eclipse.adt.internal.editors.animator.AnimDescriptors; |
| import com.android.ide.eclipse.adt.internal.editors.animator.AnimatorDescriptors; |
| import com.android.ide.eclipse.adt.internal.editors.color.ColorDescriptors; |
| import com.android.ide.eclipse.adt.internal.editors.drawable.DrawableDescriptors; |
| import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors; |
| import com.android.ide.eclipse.adt.internal.editors.manifest.descriptors.AndroidManifestDescriptors; |
| import com.android.ide.eclipse.adt.internal.editors.menu.descriptors.MenuDescriptors; |
| import com.android.ide.eclipse.adt.internal.editors.otherxml.descriptors.OtherXmlDescriptors; |
| import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager; |
| import com.android.sdklib.IAndroidTarget; |
| |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.SubMonitor; |
| |
| import java.io.BufferedReader; |
| import java.io.FileNotFoundException; |
| import java.io.FileReader; |
| import java.io.IOException; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.Modifier; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import javax.management.InvalidAttributeValueException; |
| |
| /** |
| * Parser for the platform data in an SDK. |
| * <p/> |
| * This gather the following information: |
| * <ul> |
| * <li>Resource ID from <code>android.R</code></li> |
| * <li>The list of permissions values from <code>android.Manifest$permission</code></li> |
| * <li></li> |
| * </ul> |
| */ |
| public final class AndroidTargetParser { |
| |
| private static final String TAG = "Framework Resource Parser"; |
| private final IAndroidTarget mAndroidTarget; |
| |
| /** |
| * Creates a platform data parser. |
| */ |
| public AndroidTargetParser(IAndroidTarget platformTarget) { |
| mAndroidTarget = platformTarget; |
| } |
| |
| /** |
| * Parses the framework, collects all interesting information and stores them in the |
| * {@link IAndroidTarget} given to the constructor. |
| * |
| * @param monitor A progress monitor. Can be null. Caller is responsible for calling done. |
| * @return True if the SDK path was valid and parsing has been attempted. |
| */ |
| public IStatus run(IProgressMonitor monitor) { |
| try { |
| SubMonitor progress = SubMonitor.convert(monitor, |
| String.format("Parsing SDK %1$s", mAndroidTarget.getName()), |
| 16); |
| |
| AndroidTargetData targetData = new AndroidTargetData(mAndroidTarget); |
| |
| // parse the rest of the data. |
| |
| AndroidJarLoader classLoader = |
| new AndroidJarLoader(mAndroidTarget.getPath(IAndroidTarget.ANDROID_JAR)); |
| |
| preload(classLoader, progress.newChild(40, SubMonitor.SUPPRESS_NONE)); |
| |
| if (progress.isCanceled()) { |
| return Status.CANCEL_STATUS; |
| } |
| |
| // get the permissions |
| progress.subTask("Permissions"); |
| String[] permissionValues = collectPermissions(classLoader); |
| progress.worked(1); |
| |
| if (progress.isCanceled()) { |
| return Status.CANCEL_STATUS; |
| } |
| |
| // get the action and category values for the Intents. |
| progress.subTask("Intents"); |
| ArrayList<String> activity_actions = new ArrayList<String>(); |
| ArrayList<String> broadcast_actions = new ArrayList<String>(); |
| ArrayList<String> service_actions = new ArrayList<String>(); |
| ArrayList<String> categories = new ArrayList<String>(); |
| collectIntentFilterActionsAndCategories(activity_actions, broadcast_actions, |
| service_actions, categories); |
| progress.worked(1); |
| |
| if (progress.isCanceled()) { |
| return Status.CANCEL_STATUS; |
| } |
| |
| // gather the attribute definition |
| progress.subTask("Attributes definitions"); |
| AttrsXmlParser attrsXmlParser = new AttrsXmlParser( |
| mAndroidTarget.getPath(IAndroidTarget.ATTRIBUTES), |
| AdtPlugin.getDefault(), |
| 1000); |
| attrsXmlParser.preload(); |
| |
| progress.worked(1); |
| |
| progress.subTask("Manifest definitions"); |
| AttrsXmlParser attrsManifestXmlParser = new AttrsXmlParser( |
| mAndroidTarget.getPath(IAndroidTarget.MANIFEST_ATTRIBUTES), |
| attrsXmlParser, |
| AdtPlugin.getDefault(), 1100); |
| attrsManifestXmlParser.preload(); |
| progress.worked(1); |
| |
| Collection<ViewClassInfo> mainList = new ArrayList<ViewClassInfo>(); |
| Collection<ViewClassInfo> groupList = new ArrayList<ViewClassInfo>(); |
| |
| // collect the layout/widgets classes |
| progress.subTask("Widgets and layouts"); |
| collectLayoutClasses(classLoader, attrsXmlParser, mainList, groupList, |
| progress.newChild(1)); |
| |
| if (progress.isCanceled()) { |
| return Status.CANCEL_STATUS; |
| } |
| |
| ViewClassInfo[] layoutViewsInfo = mainList.toArray( |
| new ViewClassInfo[mainList.size()]); |
| ViewClassInfo[] layoutGroupsInfo = groupList.toArray( |
| new ViewClassInfo[groupList.size()]); |
| mainList.clear(); |
| groupList.clear(); |
| |
| // collect the preferences classes. |
| collectPreferenceClasses(classLoader, attrsXmlParser, mainList, groupList, |
| progress.newChild(1)); |
| |
| if (progress.isCanceled()) { |
| return Status.CANCEL_STATUS; |
| } |
| |
| ViewClassInfo[] preferencesInfo = mainList.toArray(new ViewClassInfo[mainList.size()]); |
| ViewClassInfo[] preferenceGroupsInfo = groupList.toArray( |
| new ViewClassInfo[groupList.size()]); |
| |
| Map<String, DeclareStyleableInfo> xmlMenuMap = collectMenuDefinitions(attrsXmlParser); |
| Map<String, DeclareStyleableInfo> xmlSearchableMap = collectSearchableDefinitions( |
| attrsXmlParser); |
| Map<String, DeclareStyleableInfo> manifestMap = collectManifestDefinitions( |
| attrsManifestXmlParser); |
| Map<String, Map<String, Integer>> enumValueMap = attrsXmlParser.getEnumFlagValues(); |
| |
| Map<String, DeclareStyleableInfo> xmlAppWidgetMap = null; |
| if (mAndroidTarget.getVersion().getApiLevel() >= 3) { |
| xmlAppWidgetMap = collectAppWidgetDefinitions(attrsXmlParser); |
| } |
| |
| if (progress.isCanceled()) { |
| return Status.CANCEL_STATUS; |
| } |
| |
| // From the information that was collected, create the pieces that will be put in |
| // the PlatformData object. |
| AndroidManifestDescriptors manifestDescriptors = new AndroidManifestDescriptors(); |
| manifestDescriptors.updateDescriptors(manifestMap); |
| progress.worked(1); |
| |
| if (progress.isCanceled()) { |
| return Status.CANCEL_STATUS; |
| } |
| |
| LayoutDescriptors layoutDescriptors = new LayoutDescriptors(); |
| layoutDescriptors.updateDescriptors(layoutViewsInfo, layoutGroupsInfo, |
| attrsXmlParser.getDeclareStyleableList(), mAndroidTarget); |
| progress.worked(1); |
| |
| if (progress.isCanceled()) { |
| return Status.CANCEL_STATUS; |
| } |
| |
| MenuDescriptors menuDescriptors = new MenuDescriptors(); |
| menuDescriptors.updateDescriptors(xmlMenuMap); |
| progress.worked(1); |
| |
| if (progress.isCanceled()) { |
| return Status.CANCEL_STATUS; |
| } |
| |
| OtherXmlDescriptors otherXmlDescriptors = new OtherXmlDescriptors(); |
| otherXmlDescriptors.updateDescriptors( |
| xmlSearchableMap, |
| xmlAppWidgetMap, |
| preferencesInfo, |
| preferenceGroupsInfo); |
| progress.worked(1); |
| |
| if (progress.isCanceled()) { |
| return Status.CANCEL_STATUS; |
| } |
| |
| DrawableDescriptors drawableDescriptors = new DrawableDescriptors(); |
| Map<String, DeclareStyleableInfo> map = attrsXmlParser.getDeclareStyleableList(); |
| drawableDescriptors.updateDescriptors(map); |
| progress.worked(1); |
| |
| if (progress.isCanceled()) { |
| return Status.CANCEL_STATUS; |
| } |
| |
| AnimatorDescriptors animatorDescriptors = new AnimatorDescriptors(); |
| animatorDescriptors.updateDescriptors(map); |
| progress.worked(1); |
| |
| if (progress.isCanceled()) { |
| return Status.CANCEL_STATUS; |
| } |
| |
| AnimDescriptors animDescriptors = new AnimDescriptors(); |
| animDescriptors.updateDescriptors(map); |
| progress.worked(1); |
| |
| if (progress.isCanceled()) { |
| return Status.CANCEL_STATUS; |
| } |
| |
| ColorDescriptors colorDescriptors = new ColorDescriptors(); |
| colorDescriptors.updateDescriptors(map); |
| progress.worked(1); |
| |
| // load the framework resources. |
| ResourceRepository frameworkResources = |
| ResourceManager.getInstance().loadFrameworkResources(mAndroidTarget); |
| progress.worked(1); |
| |
| // now load the layout lib bridge |
| LayoutLibrary layoutBridge = LayoutLibrary.load( |
| mAndroidTarget.getPath(IAndroidTarget.LAYOUT_LIB), |
| AdtPlugin.getDefault(), |
| "ADT plug-in"); |
| |
| progress.worked(1); |
| |
| // and finally create the PlatformData with all that we loaded. |
| targetData.setExtraData( |
| manifestDescriptors, |
| layoutDescriptors, |
| menuDescriptors, |
| otherXmlDescriptors, |
| drawableDescriptors, |
| animatorDescriptors, |
| animDescriptors, |
| colorDescriptors, |
| enumValueMap, |
| permissionValues, |
| activity_actions.toArray(new String[activity_actions.size()]), |
| broadcast_actions.toArray(new String[broadcast_actions.size()]), |
| service_actions.toArray(new String[service_actions.size()]), |
| categories.toArray(new String[categories.size()]), |
| mAndroidTarget.getPlatformLibraries(), |
| mAndroidTarget.getOptionalLibraries(), |
| frameworkResources, |
| layoutBridge); |
| |
| targetData.setAttributeMap(attrsXmlParser.getAttributeMap()); |
| |
| Sdk.getCurrent().setTargetData(mAndroidTarget, targetData); |
| |
| return Status.OK_STATUS; |
| } catch (Exception e) { |
| AdtPlugin.logAndPrintError(e, TAG, "SDK parser failed"); //$NON-NLS-1$ |
| AdtPlugin.printToConsole("SDK parser failed", e.getMessage()); |
| return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, "SDK parser failed", e); |
| } |
| } |
| |
| /** |
| * Preloads all "interesting" classes from the framework SDK jar. |
| * <p/> |
| * Currently this preloads all classes from the framework jar |
| * |
| * @param classLoader The framework SDK jar classloader |
| * @param monitor A progress monitor. Can be null. Caller is responsible for calling done. |
| */ |
| private void preload(AndroidJarLoader classLoader, IProgressMonitor monitor) { |
| try { |
| classLoader.preLoadClasses("" /* all classes */, //$NON-NLS-1$ |
| mAndroidTarget.getName(), // monitor task label |
| monitor); |
| } catch (InvalidAttributeValueException e) { |
| AdtPlugin.log(e, "Problem preloading classes"); //$NON-NLS-1$ |
| } catch (IOException e) { |
| AdtPlugin.log(e, "Problem preloading classes"); //$NON-NLS-1$ |
| } |
| } |
| |
| /** |
| * Loads, collects and returns the list of default permissions from the framework. |
| * |
| * @param classLoader The framework SDK jar classloader |
| * @return a non null (but possibly empty) array containing the permission values. |
| */ |
| private String[] collectPermissions(AndroidJarLoader classLoader) { |
| try { |
| Class<?> permissionClass = |
| classLoader.loadClass(SdkConstants.CLASS_MANIFEST_PERMISSION); |
| |
| if (permissionClass != null) { |
| ArrayList<String> list = new ArrayList<String>(); |
| |
| Field[] fields = permissionClass.getFields(); |
| |
| for (Field f : fields) { |
| int modifiers = f.getModifiers(); |
| if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers) && |
| Modifier.isPublic(modifiers)) { |
| try { |
| Object value = f.get(null); |
| if (value instanceof String) { |
| list.add((String)value); |
| } |
| } catch (IllegalArgumentException e) { |
| // since we provide null this should not happen |
| } catch (IllegalAccessException e) { |
| // if the field is inaccessible we ignore it. |
| } catch (NullPointerException npe) { |
| // looks like this is not a static field. we can ignore. |
| } catch (ExceptionInInitializerError eiie) { |
| // lets just ignore the field again |
| } |
| } |
| } |
| |
| return list.toArray(new String[list.size()]); |
| } |
| } catch (ClassNotFoundException e) { |
| AdtPlugin.logAndPrintError(e, TAG, |
| "Collect permissions failed, class %1$s not found in %2$s", //$NON-NLS-1$ |
| SdkConstants.CLASS_MANIFEST_PERMISSION, |
| mAndroidTarget.getPath(IAndroidTarget.ANDROID_JAR)); |
| } |
| |
| return new String[0]; |
| } |
| |
| /** |
| * Loads and collects the action and category default values from the framework. |
| * The values are added to the <code>actions</code> and <code>categories</code> lists. |
| * |
| * @param activityActions the list which will receive the activity action values. |
| * @param broadcastActions the list which will receive the broadcast action values. |
| * @param serviceActions the list which will receive the service action values. |
| * @param categories the list which will receive the category values. |
| */ |
| private void collectIntentFilterActionsAndCategories(ArrayList<String> activityActions, |
| ArrayList<String> broadcastActions, |
| ArrayList<String> serviceActions, ArrayList<String> categories) { |
| collectValues(mAndroidTarget.getPath(IAndroidTarget.ACTIONS_ACTIVITY), |
| activityActions); |
| collectValues(mAndroidTarget.getPath(IAndroidTarget.ACTIONS_BROADCAST), |
| broadcastActions); |
| collectValues(mAndroidTarget.getPath(IAndroidTarget.ACTIONS_SERVICE), |
| serviceActions); |
| collectValues(mAndroidTarget.getPath(IAndroidTarget.CATEGORIES), |
| categories); |
| } |
| |
| /** |
| * Collects values from a text file located in the SDK |
| * @param osFilePath The path to the text file. |
| * @param values the {@link ArrayList} to fill with the values. |
| */ |
| private void collectValues(String osFilePath, ArrayList<String> values) { |
| FileReader fr = null; |
| BufferedReader reader = null; |
| try { |
| fr = new FileReader(osFilePath); |
| reader = new BufferedReader(fr); |
| |
| String line; |
| while ((line = reader.readLine()) != null) { |
| line = line.trim(); |
| if (line.length() > 0 && line.startsWith("#") == false) { //$NON-NLS-1$ |
| values.add(line); |
| } |
| } |
| } catch (IOException e) { |
| AdtPlugin.log(e, "Failed to read SDK values"); //$NON-NLS-1$ |
| } finally { |
| try { |
| if (reader != null) { |
| reader.close(); |
| } |
| } catch (IOException e) { |
| AdtPlugin.log(e, "Failed to read SDK values"); //$NON-NLS-1$ |
| } |
| |
| try { |
| if (fr != null) { |
| fr.close(); |
| } |
| } catch (IOException e) { |
| AdtPlugin.log(e, "Failed to read SDK values"); //$NON-NLS-1$ |
| } |
| } |
| } |
| |
| /** |
| * Collects all layout classes information from the class loader and the |
| * attrs.xml and sets the corresponding structures in the resource manager. |
| * |
| * @param classLoader The framework SDK jar classloader in case we cannot get the widget from |
| * the platform directly |
| * @param attrsXmlParser The parser of the attrs.xml file |
| * @param mainList the Collection to receive the main list of {@link ViewClassInfo}. |
| * @param groupList the Collection to receive the group list of {@link ViewClassInfo}. |
| * @param monitor A progress monitor. Can be null. Caller is responsible for calling done. |
| */ |
| private void collectLayoutClasses(AndroidJarLoader classLoader, |
| AttrsXmlParser attrsXmlParser, |
| Collection<ViewClassInfo> mainList, |
| Collection<ViewClassInfo> groupList, |
| IProgressMonitor monitor) { |
| LayoutParamsParser ldp = null; |
| try { |
| WidgetClassLoader loader = new WidgetClassLoader( |
| mAndroidTarget.getPath(IAndroidTarget.WIDGETS)); |
| if (loader.parseWidgetList(monitor)) { |
| ldp = new LayoutParamsParser(loader, attrsXmlParser); |
| } |
| // if the parsing failed, we'll use the old loader below. |
| } catch (FileNotFoundException e) { |
| AdtPlugin.log(e, "Android Framework Parser"); //$NON-NLS-1$ |
| // the file does not exist, we'll use the old loader below. |
| } |
| |
| if (ldp == null) { |
| ldp = new LayoutParamsParser(classLoader, attrsXmlParser); |
| } |
| ldp.parseLayoutClasses(monitor); |
| |
| List<ViewClassInfo> views = ldp.getViews(); |
| List<ViewClassInfo> groups = ldp.getGroups(); |
| |
| if (views != null && groups != null) { |
| mainList.addAll(views); |
| groupList.addAll(groups); |
| } |
| } |
| |
| /** |
| * Collects all preferences definition information from the attrs.xml and |
| * sets the corresponding structures in the resource manager. |
| * |
| * @param classLoader The framework SDK jar classloader |
| * @param attrsXmlParser The parser of the attrs.xml file |
| * @param mainList the Collection to receive the main list of {@link ViewClassInfo}. |
| * @param groupList the Collection to receive the group list of {@link ViewClassInfo}. |
| * @param monitor A progress monitor. Can be null. Caller is responsible for calling done. |
| */ |
| private void collectPreferenceClasses(AndroidJarLoader classLoader, |
| AttrsXmlParser attrsXmlParser, Collection<ViewClassInfo> mainList, |
| Collection<ViewClassInfo> groupList, IProgressMonitor monitor) { |
| LayoutParamsParser ldp = new LayoutParamsParser(classLoader, attrsXmlParser); |
| |
| try { |
| ldp.parsePreferencesClasses(monitor); |
| |
| List<ViewClassInfo> prefs = ldp.getViews(); |
| List<ViewClassInfo> groups = ldp.getGroups(); |
| |
| if (prefs != null && groups != null) { |
| mainList.addAll(prefs); |
| groupList.addAll(groups); |
| } |
| } catch (NoClassDefFoundError e) { |
| AdtPlugin.logAndPrintError(e, TAG, |
| "Collect preferences failed, class %1$s not found in %2$s", |
| e.getMessage(), |
| classLoader.getSource()); |
| } catch (Throwable e) { |
| AdtPlugin.log(e, "Android Framework Parser: failed to collect preference classes"); //$NON-NLS-1$ |
| AdtPlugin.printErrorToConsole("Android Framework Parser", |
| "failed to collect preference classes"); |
| } |
| } |
| |
| /** |
| * Collects all menu definition information from the attrs.xml and returns it. |
| * |
| * @param attrsXmlParser The parser of the attrs.xml file |
| */ |
| private Map<String, DeclareStyleableInfo> collectMenuDefinitions( |
| AttrsXmlParser attrsXmlParser) { |
| Map<String, DeclareStyleableInfo> map = attrsXmlParser.getDeclareStyleableList(); |
| Map<String, DeclareStyleableInfo> map2 = new HashMap<String, DeclareStyleableInfo>(); |
| for (String key : new String[] { "Menu", //$NON-NLS-1$ |
| "MenuItem", //$NON-NLS-1$ |
| "MenuGroup" }) { //$NON-NLS-1$ |
| if (map.containsKey(key)) { |
| map2.put(key, map.get(key)); |
| } else { |
| AdtPlugin.log(IStatus.WARNING, |
| "Menu declare-styleable %1$s not found in file %2$s", //$NON-NLS-1$ |
| key, attrsXmlParser.getOsAttrsXmlPath()); |
| AdtPlugin.printErrorToConsole("Android Framework Parser", |
| String.format("Menu declare-styleable %1$s not found in file %2$s", //$NON-NLS-1$ |
| key, attrsXmlParser.getOsAttrsXmlPath())); |
| } |
| } |
| |
| return Collections.unmodifiableMap(map2); |
| } |
| |
| /** |
| * Collects all searchable definition information from the attrs.xml and returns it. |
| * |
| * @param attrsXmlParser The parser of the attrs.xml file |
| */ |
| private Map<String, DeclareStyleableInfo> collectSearchableDefinitions( |
| AttrsXmlParser attrsXmlParser) { |
| Map<String, DeclareStyleableInfo> map = attrsXmlParser.getDeclareStyleableList(); |
| Map<String, DeclareStyleableInfo> map2 = new HashMap<String, DeclareStyleableInfo>(); |
| for (String key : new String[] { "Searchable", //$NON-NLS-1$ |
| "SearchableActionKey" }) { //$NON-NLS-1$ |
| if (map.containsKey(key)) { |
| map2.put(key, map.get(key)); |
| } else { |
| AdtPlugin.log(IStatus.WARNING, |
| "Searchable declare-styleable %1$s not found in file %2$s", //$NON-NLS-1$ |
| key, attrsXmlParser.getOsAttrsXmlPath()); |
| AdtPlugin.printErrorToConsole("Android Framework Parser", |
| String.format("Searchable declare-styleable %1$s not found in file %2$s", //$NON-NLS-1$ |
| key, attrsXmlParser.getOsAttrsXmlPath())); |
| } |
| } |
| |
| return Collections.unmodifiableMap(map2); |
| } |
| |
| /** |
| * Collects all appWidgetProviderInfo definition information from the attrs.xml and returns it. |
| * |
| * @param attrsXmlParser The parser of the attrs.xml file |
| */ |
| private Map<String, DeclareStyleableInfo> collectAppWidgetDefinitions( |
| AttrsXmlParser attrsXmlParser) { |
| Map<String, DeclareStyleableInfo> map = attrsXmlParser.getDeclareStyleableList(); |
| Map<String, DeclareStyleableInfo> map2 = new HashMap<String, DeclareStyleableInfo>(); |
| for (String key : new String[] { "AppWidgetProviderInfo" }) { //$NON-NLS-1$ |
| if (map.containsKey(key)) { |
| map2.put(key, map.get(key)); |
| } else { |
| AdtPlugin.log(IStatus.WARNING, |
| "AppWidget declare-styleable %1$s not found in file %2$s", //$NON-NLS-1$ |
| key, attrsXmlParser.getOsAttrsXmlPath()); |
| AdtPlugin.printErrorToConsole("Android Framework Parser", |
| String.format("AppWidget declare-styleable %1$s not found in file %2$s", //$NON-NLS-1$ |
| key, attrsXmlParser.getOsAttrsXmlPath())); |
| } |
| } |
| |
| return Collections.unmodifiableMap(map2); |
| } |
| |
| /** |
| * Collects all manifest definition information from the attrs_manifest.xml and returns it. |
| */ |
| private Map<String, DeclareStyleableInfo> collectManifestDefinitions( |
| AttrsXmlParser attrsXmlParser) { |
| |
| return attrsXmlParser.getDeclareStyleableList(); |
| } |
| |
| } |