• 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.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          */
handleError(Exception exception, int lineNumber)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          */
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 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          */
ManifestHandler(IAbstractFile manifestFile, ManifestData manifestData, ManifestErrorHandler errorHandler)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
setDocumentLocator(Locator locator)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
startElement(String uri, String localName, String name, Attributes attributes)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
endElement(String uri, String localName, String name)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
error(SAXParseException e)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
fatalError(SAXParseException e)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
warning(SAXParseException e)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          */
processActivityNode(Attributes attributes)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          */
processNode(Attributes attributes, String superClassName)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          */
processInstrumentationNode(Attributes attributes)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          */
processSupportsScreensNode(Attributes attributes)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          */
processUsesConfiguration(Attributes attributes)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          */
getAttributeValue(Attributes attributes, String attributeName, boolean hasNamespace)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          */
getAttributeBooleanValue(Attributes attributes, String attributeName, boolean hasNamespace)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      */
parse( IAbstractFile manifestFile, boolean gatherData, ManifestErrorHandler errorHandler)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      */
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 = 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      */
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     /**
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      */
getManifest(IAbstractFolder projectFolder)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