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