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