diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetParser.java')
-rw-r--r-- | eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetParser.java | 605 |
1 files changed, 605 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetParser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetParser.java new file mode 100644 index 000000000..9a1fd3dc9 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetParser.java @@ -0,0 +1,605 @@ +/* + * 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(); + } + +} |