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