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