Home | History | Annotate | Download | only in xml
      1 /*
      2  * Copyright (C) 2007 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
      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.sdklib.xml;
     18 
     19 import com.android.sdklib.SdkConstants;
     20 import com.android.sdklib.io.IAbstractFile;
     21 import com.android.sdklib.io.IAbstractFolder;
     22 import com.android.sdklib.io.StreamException;
     23 import com.android.sdklib.resources.Keyboard;
     24 import com.android.sdklib.resources.Navigation;
     25 import com.android.sdklib.resources.TouchScreen;
     26 import com.android.sdklib.xml.ManifestData.Activity;
     27 import com.android.sdklib.xml.ManifestData.Instrumentation;
     28 import com.android.sdklib.xml.ManifestData.SupportsScreens;
     29 import com.android.sdklib.xml.ManifestData.UsesConfiguration;
     30 import com.android.sdklib.xml.ManifestData.UsesFeature;
     31 import com.android.sdklib.xml.ManifestData.UsesLibrary;
     32 
     33 import org.xml.sax.Attributes;
     34 import org.xml.sax.ErrorHandler;
     35 import org.xml.sax.InputSource;
     36 import org.xml.sax.Locator;
     37 import org.xml.sax.SAXException;
     38 import org.xml.sax.SAXParseException;
     39 import org.xml.sax.helpers.DefaultHandler;
     40 
     41 import java.io.FileNotFoundException;
     42 import java.io.IOException;
     43 import java.io.InputStream;
     44 
     45 import javax.xml.parsers.ParserConfigurationException;
     46 import javax.xml.parsers.SAXParser;
     47 import javax.xml.parsers.SAXParserFactory;
     48 
     49 public class AndroidManifestParser {
     50 
     51     private final static int LEVEL_TOP = 0;
     52     private final static int LEVEL_INSIDE_MANIFEST = 1;
     53     private final static int LEVEL_INSIDE_APPLICATION = 2;
     54     private final static int LEVEL_INSIDE_APP_COMPONENT = 3;
     55     private final static int LEVEL_INSIDE_INTENT_FILTER = 4;
     56 
     57     private final static String ACTION_MAIN = "android.intent.action.MAIN"; //$NON-NLS-1$
     58     private final static String CATEGORY_LAUNCHER = "android.intent.category.LAUNCHER"; //$NON-NLS-1$
     59 
     60     public interface ManifestErrorHandler extends ErrorHandler {
     61         /**
     62          * Handles a parsing error and an optional line number.
     63          * @param exception
     64          * @param lineNumber
     65          */
     66         void handleError(Exception exception, int lineNumber);
     67 
     68         /**
     69          * Checks that a class is valid and can be used in the Android Manifest.
     70          * <p/>
     71          * Errors are put as {@link IMarker} on the manifest file.
     72          * @param locator
     73          * @param className the fully qualified name of the class to test.
     74          * @param superClassName the fully qualified name of the class it is supposed to extend.
     75          * @param testVisibility if <code>true</code>, the method will check the visibility of
     76          * the class or of its constructors.
     77          */
     78         void checkClass(Locator locator, String className, String superClassName,
     79                 boolean testVisibility);
     80     }
     81 
     82     /**
     83      * XML error & data handler used when parsing the AndroidManifest.xml file.
     84      * <p/>
     85      * During parsing this will fill up the {@link ManifestData} object given to the constructor
     86      * and call out errors to the given {@link ManifestErrorHandler}.
     87      */
     88     private static class ManifestHandler extends DefaultHandler {
     89 
     90         //--- temporary data/flags used during parsing
     91         private final ManifestData mManifestData;
     92         private final ManifestErrorHandler mErrorHandler;
     93         private int mCurrentLevel = 0;
     94         private int mValidLevel = 0;
     95         private Activity mCurrentActivity = null;
     96         private Locator mLocator;
     97 
     98         /**
     99          * Creates a new {@link ManifestHandler}.
    100          *
    101          * @param manifestFile The manifest file being parsed. Can be null.
    102          * @param errorListener An optional error listener.
    103          * @param gatherData True if data should be gathered.
    104          * @param javaProject The java project holding the manifest file. Can be null.
    105          * @param markErrors True if errors should be marked as Eclipse Markers on the resource.
    106          */
    107         ManifestHandler(IAbstractFile manifestFile, ManifestData manifestData,
    108                 ManifestErrorHandler errorHandler) {
    109             super();
    110             mManifestData = manifestData;
    111             mErrorHandler = errorHandler;
    112         }
    113 
    114         /* (non-Javadoc)
    115          * @see org.xml.sax.helpers.DefaultHandler#setDocumentLocator(org.xml.sax.Locator)
    116          */
    117         @Override
    118         public void setDocumentLocator(Locator locator) {
    119             mLocator = locator;
    120             super.setDocumentLocator(locator);
    121         }
    122 
    123         /* (non-Javadoc)
    124          * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String,
    125          * java.lang.String, org.xml.sax.Attributes)
    126          */
    127         @Override
    128         public void startElement(String uri, String localName, String name, Attributes attributes)
    129                 throws SAXException {
    130             try {
    131                 if (mManifestData == null) {
    132                     return;
    133                 }
    134 
    135                 // if we're at a valid level
    136                 if (mValidLevel == mCurrentLevel) {
    137                     String value;
    138                     switch (mValidLevel) {
    139                         case LEVEL_TOP:
    140                             if (AndroidManifest.NODE_MANIFEST.equals(localName)) {
    141                                 // lets get the package name.
    142                                 mManifestData.mPackage = getAttributeValue(attributes,
    143                                         AndroidManifest.ATTRIBUTE_PACKAGE,
    144                                         false /* hasNamespace */);
    145 
    146                                 // and the versionCode
    147                                 String tmp = getAttributeValue(attributes,
    148                                         AndroidManifest.ATTRIBUTE_VERSIONCODE, true);
    149                                 if (tmp != null) {
    150                                     try {
    151                                         mManifestData.mVersionCode = Integer.valueOf(tmp);
    152                                     } catch (NumberFormatException e) {
    153                                         // keep null in the field.
    154                                     }
    155                                 }
    156                                 mValidLevel++;
    157                             }
    158                             break;
    159                         case LEVEL_INSIDE_MANIFEST:
    160                             if (AndroidManifest.NODE_APPLICATION.equals(localName)) {
    161                                 value = getAttributeValue(attributes,
    162                                         AndroidManifest.ATTRIBUTE_PROCESS,
    163                                         true /* hasNamespace */);
    164                                 if (value != null) {
    165                                     mManifestData.addProcessName(value);
    166                                 }
    167 
    168                                 value = getAttributeValue(attributes,
    169                                         AndroidManifest.ATTRIBUTE_DEBUGGABLE,
    170                                         true /* hasNamespace*/);
    171                                 if (value != null) {
    172                                     mManifestData.mDebuggable = Boolean.parseBoolean(value);
    173                                 }
    174 
    175                                 mValidLevel++;
    176                             } else if (AndroidManifest.NODE_USES_SDK.equals(localName)) {
    177                                 mManifestData.setMinSdkVersionString(getAttributeValue(attributes,
    178                                         AndroidManifest.ATTRIBUTE_MIN_SDK_VERSION,
    179                                         true /* hasNamespace */));
    180                                 mManifestData.setTargetSdkVersionString(getAttributeValue(attributes,
    181                                         AndroidManifest.ATTRIBUTE_TARGET_SDK_VERSION,
    182                                         true /* hasNamespace */));
    183                             } else if (AndroidManifest.NODE_INSTRUMENTATION.equals(localName)) {
    184                                 processInstrumentationNode(attributes);
    185 
    186                             } else if (AndroidManifest.NODE_SUPPORTS_SCREENS.equals(localName)) {
    187                                 processSupportsScreensNode(attributes);
    188 
    189                             } else if (AndroidManifest.NODE_USES_CONFIGURATION.equals(localName)) {
    190                                 processUsesConfiguration(attributes);
    191 
    192                             } else if (AndroidManifest.NODE_USES_FEATURE.equals(localName)) {
    193                                 UsesFeature feature = new UsesFeature();
    194 
    195                                 // get the name
    196                                 value = getAttributeValue(attributes,
    197                                         AndroidManifest.ATTRIBUTE_NAME,
    198                                         true /* hasNamespace */);
    199                                 if (value != null) {
    200                                     feature.mName = value;
    201                                 }
    202 
    203                                 // read the required attribute
    204                                 value = getAttributeValue(attributes,
    205                                         AndroidManifest.ATTRIBUTE_REQUIRED,
    206                                         true /*hasNamespace*/);
    207                                 if (value != null) {
    208                                     Boolean b = Boolean.valueOf(value);
    209                                     if (b != null) {
    210                                         feature.mRequired = b;
    211                                     }
    212                                 }
    213 
    214                                 // read the gl es attribute
    215                                 value = getAttributeValue(attributes,
    216                                         AndroidManifest.ATTRIBUTE_GLESVERSION,
    217                                         true /*hasNamespace*/);
    218                                 if (value != null) {
    219                                     try {
    220                                         int version = Integer.decode(value);
    221                                         feature.mGlEsVersion = version;
    222                                     } catch (NumberFormatException e) {
    223                                         // ignore
    224                                     }
    225 
    226                                 }
    227 
    228                                 mManifestData.mFeatures.add(feature);
    229                             }
    230                             break;
    231                         case LEVEL_INSIDE_APPLICATION:
    232                             if (AndroidManifest.NODE_ACTIVITY.equals(localName)) {
    233                                 processActivityNode(attributes);
    234                                 mValidLevel++;
    235                             } else if (AndroidManifest.NODE_SERVICE.equals(localName)) {
    236                                 processNode(attributes, SdkConstants.CLASS_SERVICE);
    237                                 mValidLevel++;
    238                             } else if (AndroidManifest.NODE_RECEIVER.equals(localName)) {
    239                                 processNode(attributes, SdkConstants.CLASS_BROADCASTRECEIVER);
    240                                 mValidLevel++;
    241                             } else if (AndroidManifest.NODE_PROVIDER.equals(localName)) {
    242                                 processNode(attributes, SdkConstants.CLASS_CONTENTPROVIDER);
    243                                 mValidLevel++;
    244                             } else if (AndroidManifest.NODE_USES_LIBRARY.equals(localName)) {
    245                                 value = getAttributeValue(attributes,
    246                                         AndroidManifest.ATTRIBUTE_NAME,
    247                                         true /* hasNamespace */);
    248                                 if (value != null) {
    249                                     UsesLibrary library = new UsesLibrary();
    250                                     library.mName = value;
    251 
    252                                     // read the required attribute
    253                                     value = getAttributeValue(attributes,
    254                                             AndroidManifest.ATTRIBUTE_REQUIRED,
    255                                             true /*hasNamespace*/);
    256                                     if (value != null) {
    257                                         Boolean b = Boolean.valueOf(value);
    258                                         if (b != null) {
    259                                             library.mRequired = b;
    260                                         }
    261                                     }
    262 
    263                                     mManifestData.mLibraries.add(library);
    264                                 }
    265                             }
    266                             break;
    267                         case LEVEL_INSIDE_APP_COMPONENT:
    268                             // only process this level if we are in an activity
    269                             if (mCurrentActivity != null &&
    270                                     AndroidManifest.NODE_INTENT.equals(localName)) {
    271                                 mCurrentActivity.resetIntentFilter();
    272                                 mValidLevel++;
    273                             }
    274                             break;
    275                         case LEVEL_INSIDE_INTENT_FILTER:
    276                             if (mCurrentActivity != null) {
    277                                 if (AndroidManifest.NODE_ACTION.equals(localName)) {
    278                                     // get the name attribute
    279                                     String action = getAttributeValue(attributes,
    280                                             AndroidManifest.ATTRIBUTE_NAME,
    281                                             true /* hasNamespace */);
    282                                     if (action != null) {
    283                                         mCurrentActivity.setHasAction(true);
    284                                         mCurrentActivity.setHasMainAction(
    285                                                 ACTION_MAIN.equals(action));
    286                                     }
    287                                 } else if (AndroidManifest.NODE_CATEGORY.equals(localName)) {
    288                                     String category = getAttributeValue(attributes,
    289                                             AndroidManifest.ATTRIBUTE_NAME,
    290                                             true /* hasNamespace */);
    291                                     if (CATEGORY_LAUNCHER.equals(category)) {
    292                                         mCurrentActivity.setHasLauncherCategory(true);
    293                                     }
    294                                 }
    295 
    296                                 // no need to increase mValidLevel as we don't process anything
    297                                 // below this level.
    298                             }
    299                             break;
    300                     }
    301                 }
    302 
    303                 mCurrentLevel++;
    304             } finally {
    305                 super.startElement(uri, localName, name, attributes);
    306             }
    307         }
    308 
    309         /* (non-Javadoc)
    310          * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String,
    311          * java.lang.String)
    312          */
    313         @Override
    314         public void endElement(String uri, String localName, String name) throws SAXException {
    315             try {
    316                 if (mManifestData == null) {
    317                     return;
    318                 }
    319 
    320                 // decrement the levels.
    321                 if (mValidLevel == mCurrentLevel) {
    322                     mValidLevel--;
    323                 }
    324                 mCurrentLevel--;
    325 
    326                 // if we're at a valid level
    327                 // process the end of the element
    328                 if (mValidLevel == mCurrentLevel) {
    329                     switch (mValidLevel) {
    330                         case LEVEL_INSIDE_APPLICATION:
    331                             mCurrentActivity = null;
    332                             break;
    333                         case LEVEL_INSIDE_APP_COMPONENT:
    334                             // if we found both a main action and a launcher category, this is our
    335                             // launcher activity!
    336                             if (mManifestData.mLauncherActivity == null &&
    337                                     mCurrentActivity != null &&
    338                                     mCurrentActivity.isHomeActivity() &&
    339                                     mCurrentActivity.isExported()) {
    340                                 mManifestData.mLauncherActivity = mCurrentActivity;
    341                             }
    342                             break;
    343                         default:
    344                             break;
    345                     }
    346 
    347                 }
    348             } finally {
    349                 super.endElement(uri, localName, name);
    350             }
    351         }
    352 
    353         /* (non-Javadoc)
    354          * @see org.xml.sax.helpers.DefaultHandler#error(org.xml.sax.SAXParseException)
    355          */
    356         @Override
    357         public void error(SAXParseException e) {
    358             if (mErrorHandler != null) {
    359                 mErrorHandler.handleError(e, e.getLineNumber());
    360             }
    361         }
    362 
    363         /* (non-Javadoc)
    364          * @see org.xml.sax.helpers.DefaultHandler#fatalError(org.xml.sax.SAXParseException)
    365          */
    366         @Override
    367         public void fatalError(SAXParseException e) {
    368             if (mErrorHandler != null) {
    369                 mErrorHandler.handleError(e, e.getLineNumber());
    370             }
    371         }
    372 
    373         /* (non-Javadoc)
    374          * @see org.xml.sax.helpers.DefaultHandler#warning(org.xml.sax.SAXParseException)
    375          */
    376         @Override
    377         public void warning(SAXParseException e) throws SAXException {
    378             if (mErrorHandler != null) {
    379                 mErrorHandler.warning(e);
    380             }
    381         }
    382 
    383         /**
    384          * Processes the activity node.
    385          * @param attributes the attributes for the activity node.
    386          */
    387         private void processActivityNode(Attributes attributes) {
    388             // lets get the activity name, and add it to the list
    389             String activityName = getAttributeValue(attributes, AndroidManifest.ATTRIBUTE_NAME,
    390                     true /* hasNamespace */);
    391             if (activityName != null) {
    392                 activityName = AndroidManifest.combinePackageAndClassName(mManifestData.mPackage,
    393                         activityName);
    394 
    395                 // get the exported flag.
    396                 String exportedStr = getAttributeValue(attributes,
    397                         AndroidManifest.ATTRIBUTE_EXPORTED, true);
    398                 boolean exported = exportedStr == null ||
    399                         exportedStr.toLowerCase().equals("true"); // $NON-NLS-1$
    400                 mCurrentActivity = new Activity(activityName, exported);
    401                 mManifestData.mActivities.add(mCurrentActivity);
    402 
    403                 if (mErrorHandler != null) {
    404                     mErrorHandler.checkClass(mLocator, activityName, SdkConstants.CLASS_ACTIVITY,
    405                             true /* testVisibility */);
    406                 }
    407             } else {
    408                 // no activity found! Aapt will output an error,
    409                 // so we don't have to do anything
    410                 mCurrentActivity = null;
    411             }
    412 
    413             String processName = getAttributeValue(attributes, AndroidManifest.ATTRIBUTE_PROCESS,
    414                     true /* hasNamespace */);
    415             if (processName != null) {
    416                 mManifestData.addProcessName(processName);
    417             }
    418         }
    419 
    420         /**
    421          * Processes the service/receiver/provider nodes.
    422          * @param attributes the attributes for the activity node.
    423          * @param superClassName the fully qualified name of the super class that this
    424          * node is representing
    425          */
    426         private void processNode(Attributes attributes, String superClassName) {
    427             // lets get the class name, and check it if required.
    428             String serviceName = getAttributeValue(attributes, AndroidManifest.ATTRIBUTE_NAME,
    429                     true /* hasNamespace */);
    430             if (serviceName != null) {
    431                 serviceName = AndroidManifest.combinePackageAndClassName(mManifestData.mPackage,
    432                         serviceName);
    433 
    434                 if (mErrorHandler != null) {
    435                     mErrorHandler.checkClass(mLocator, serviceName, superClassName,
    436                             false /* testVisibility */);
    437                 }
    438             }
    439 
    440             String processName = getAttributeValue(attributes, AndroidManifest.ATTRIBUTE_PROCESS,
    441                     true /* hasNamespace */);
    442             if (processName != null) {
    443                 mManifestData.addProcessName(processName);
    444             }
    445         }
    446 
    447         /**
    448          * Processes the instrumentation node.
    449          * @param attributes the attributes for the instrumentation node.
    450          */
    451         private void processInstrumentationNode(Attributes attributes) {
    452             // lets get the class name, and check it if required.
    453             String instrumentationName = getAttributeValue(attributes,
    454                     AndroidManifest.ATTRIBUTE_NAME,
    455                     true /* hasNamespace */);
    456             if (instrumentationName != null) {
    457                 String instrClassName = AndroidManifest.combinePackageAndClassName(
    458                         mManifestData.mPackage, instrumentationName);
    459                 String targetPackage = getAttributeValue(attributes,
    460                         AndroidManifest.ATTRIBUTE_TARGET_PACKAGE,
    461                         true /* hasNamespace */);
    462                 mManifestData.mInstrumentations.add(
    463                         new Instrumentation(instrClassName, targetPackage));
    464                 if (mErrorHandler != null) {
    465                     mErrorHandler.checkClass(mLocator, instrClassName,
    466                             SdkConstants.CLASS_INSTRUMENTATION, true /* testVisibility */);
    467                 }
    468             }
    469         }
    470 
    471         /**
    472          * Processes the supports-screens node.
    473          * @param attributes the attributes for the supports-screens node.
    474          */
    475         private void processSupportsScreensNode(Attributes attributes) {
    476             mManifestData.mSupportsScreensFromManifest = new SupportsScreens();
    477 
    478             mManifestData.mSupportsScreensFromManifest.setResizeable(getAttributeBooleanValue(
    479                     attributes, AndroidManifest.ATTRIBUTE_RESIZEABLE, true /*hasNamespace*/));
    480 
    481             mManifestData.mSupportsScreensFromManifest.setAnyDensity(getAttributeBooleanValue(
    482                     attributes, AndroidManifest.ATTRIBUTE_ANYDENSITY, true /*hasNamespace*/));
    483 
    484             mManifestData.mSupportsScreensFromManifest.setSmallScreens(getAttributeBooleanValue(
    485                     attributes, AndroidManifest.ATTRIBUTE_SMALLSCREENS, true /*hasNamespace*/));
    486 
    487             mManifestData.mSupportsScreensFromManifest.setNormalScreens(getAttributeBooleanValue(
    488                     attributes, AndroidManifest.ATTRIBUTE_NORMALSCREENS, true /*hasNamespace*/));
    489 
    490             mManifestData.mSupportsScreensFromManifest.setLargeScreens(getAttributeBooleanValue(
    491                     attributes, AndroidManifest.ATTRIBUTE_LARGESCREENS, true /*hasNamespace*/));
    492         }
    493 
    494         /**
    495          * Processes the supports-screens node.
    496          * @param attributes the attributes for the supports-screens node.
    497          */
    498         private void processUsesConfiguration(Attributes attributes) {
    499             mManifestData.mUsesConfiguration = new UsesConfiguration();
    500 
    501             mManifestData.mUsesConfiguration.mReqFiveWayNav = getAttributeBooleanValue(
    502                     attributes,
    503                     AndroidManifest.ATTRIBUTE_REQ_5WAYNAV, true /*hasNamespace*/);
    504             mManifestData.mUsesConfiguration.mReqNavigation = Navigation.getEnum(
    505                     getAttributeValue(attributes,
    506                             AndroidManifest.ATTRIBUTE_REQ_NAVIGATION, true /*hasNamespace*/));
    507             mManifestData.mUsesConfiguration.mReqHardKeyboard = getAttributeBooleanValue(
    508                     attributes,
    509                     AndroidManifest.ATTRIBUTE_REQ_HARDKEYBOARD, true /*hasNamespace*/);
    510             mManifestData.mUsesConfiguration.mReqKeyboardType = Keyboard.getEnum(
    511                     getAttributeValue(attributes,
    512                             AndroidManifest.ATTRIBUTE_REQ_KEYBOARDTYPE, true /*hasNamespace*/));
    513             mManifestData.mUsesConfiguration.mReqTouchScreen = TouchScreen.getEnum(
    514                     getAttributeValue(attributes,
    515                             AndroidManifest.ATTRIBUTE_REQ_TOUCHSCREEN, true /*hasNamespace*/));
    516         }
    517 
    518         /**
    519          * Searches through the attributes list for a particular one and returns its value.
    520          * @param attributes the attribute list to search through
    521          * @param attributeName the name of the attribute to look for.
    522          * @param hasNamespace Indicates whether the attribute has an android namespace.
    523          * @return a String with the value or null if the attribute was not found.
    524          * @see SdkConstants#NS_RESOURCES
    525          */
    526         private String getAttributeValue(Attributes attributes, String attributeName,
    527                 boolean hasNamespace) {
    528             int count = attributes.getLength();
    529             for (int i = 0 ; i < count ; i++) {
    530                 if (attributeName.equals(attributes.getLocalName(i)) &&
    531                         ((hasNamespace &&
    532                                 SdkConstants.NS_RESOURCES.equals(attributes.getURI(i))) ||
    533                                 (hasNamespace == false && attributes.getURI(i).length() == 0))) {
    534                     return attributes.getValue(i);
    535                 }
    536             }
    537 
    538             return null;
    539         }
    540 
    541         /**
    542          * Searches through the attributes list for a particular one and returns its value as a
    543          * Boolean. If the attribute is not present, this will return null.
    544          * @param attributes the attribute list to search through
    545          * @param attributeName the name of the attribute to look for.
    546          * @param hasNamespace Indicates whether the attribute has an android namespace.
    547          * @return a String with the value or null if the attribute was not found.
    548          * @see SdkConstants#NS_RESOURCES
    549          */
    550         private Boolean getAttributeBooleanValue(Attributes attributes, String attributeName,
    551                 boolean hasNamespace) {
    552             int count = attributes.getLength();
    553             for (int i = 0 ; i < count ; i++) {
    554                 if (attributeName.equals(attributes.getLocalName(i)) &&
    555                         ((hasNamespace &&
    556                                 SdkConstants.NS_RESOURCES.equals(attributes.getURI(i))) ||
    557                                 (hasNamespace == false && attributes.getURI(i).length() == 0))) {
    558                     String attr = attributes.getValue(i);
    559                     if (attr != null) {
    560                         return Boolean.valueOf(attr);
    561                     } else {
    562                         return null;
    563                     }
    564                 }
    565             }
    566 
    567             return null;
    568         }
    569 
    570     }
    571 
    572     private final static SAXParserFactory sParserFactory;
    573 
    574     static {
    575         sParserFactory = SAXParserFactory.newInstance();
    576         sParserFactory.setNamespaceAware(true);
    577     }
    578 
    579     /**
    580      * Parses the Android Manifest, and returns a {@link ManifestData} object containing the
    581      * result of the parsing.
    582      *
    583      * @param manifestFile the {@link IAbstractFile} representing the manifest file.
    584      * @param gatherData indicates whether the parsing will extract data from the manifest. If false
    585      * the method will always return null.
    586      * @param errorHandler an optional errorHandler.
    587      * @return
    588      * @throws StreamException
    589      * @throws IOException
    590      * @throws SAXException
    591      * @throws ParserConfigurationException
    592      */
    593     public static ManifestData parse(
    594             IAbstractFile manifestFile,
    595             boolean gatherData,
    596             ManifestErrorHandler errorHandler)
    597             throws SAXException, IOException, StreamException, ParserConfigurationException {
    598         if (manifestFile != null) {
    599             SAXParser parser = sParserFactory.newSAXParser();
    600 
    601             ManifestData data = null;
    602             if (gatherData) {
    603                 data = new ManifestData();
    604             }
    605 
    606             ManifestHandler manifestHandler = new ManifestHandler(manifestFile,
    607                     data, errorHandler);
    608             parser.parse(new InputSource(manifestFile.getContents()), manifestHandler);
    609 
    610             return data;
    611         }
    612 
    613         return null;
    614     }
    615 
    616     /**
    617      * Parses the Android Manifest, and returns an object containing the result of the parsing.
    618      *
    619      * <p/>
    620      * This is the equivalent of calling <pre>parse(manifestFile, true, null)</pre>
    621      *
    622      * @param manifestFile the manifest file to parse.
    623      * @throws ParserConfigurationException
    624      * @throws StreamException
    625      * @throws IOException
    626      * @throws SAXException
    627      */
    628     public static ManifestData parse(IAbstractFile manifestFile)
    629             throws SAXException, IOException, StreamException, ParserConfigurationException {
    630         return parse(manifestFile, true, null);
    631     }
    632 
    633     public static ManifestData parse(IAbstractFolder projectFolder)
    634             throws SAXException, IOException, StreamException, ParserConfigurationException {
    635         IAbstractFile manifestFile = getManifest(projectFolder);
    636         if (manifestFile == null) {
    637             throw new FileNotFoundException();
    638         }
    639 
    640         return parse(manifestFile, true, null);
    641     }
    642 
    643     /**
    644      * Parses the Android Manifest from an {@link InputStream}, and returns a {@link ManifestData}
    645      * object containing the result of the parsing.
    646      *
    647      * @param manifestFileStream the {@link InputStream} representing the manifest file.
    648      * @return
    649      * @throws StreamException
    650      * @throws IOException
    651      * @throws SAXException
    652      * @throws ParserConfigurationException
    653      */
    654     public static ManifestData parse(InputStream manifestFileStream)
    655             throws SAXException, IOException, StreamException, ParserConfigurationException {
    656         if (manifestFileStream != null) {
    657             SAXParser parser = sParserFactory.newSAXParser();
    658 
    659             ManifestData data = new ManifestData();
    660 
    661             ManifestHandler manifestHandler = new ManifestHandler(null, data, null);
    662             parser.parse(new InputSource(manifestFileStream), manifestHandler);
    663 
    664             return data;
    665         }
    666 
    667         return null;
    668     }
    669 
    670     /**
    671      * Returns an {@link IAbstractFile} object representing the manifest for the given project.
    672      *
    673      * @param project The project containing the manifest file.
    674      * @return An IAbstractFile object pointing to the manifest or null if the manifest
    675      *         is missing.
    676      */
    677     public static IAbstractFile getManifest(IAbstractFolder projectFolder) {
    678         IAbstractFile file = projectFolder.getFile(SdkConstants.FN_ANDROID_MANIFEST_XML);
    679         if (file.exists()) {
    680             return file;
    681         }
    682 
    683         return null;
    684     }
    685 }
    686