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