1 /* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php 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.ide.eclipse.adt.internal.build; 18 19 import com.android.ide.eclipse.adt.AdtConstants; 20 import com.android.ide.eclipse.adt.AdtPlugin; 21 import com.android.ide.eclipse.adt.AndroidConstants; 22 import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper; 23 import com.android.ide.eclipse.adt.internal.project.ProjectHelper; 24 import com.android.ide.eclipse.adt.internal.project.XmlErrorHandler; 25 import com.android.ide.eclipse.adt.internal.project.XmlErrorHandler.XmlErrorListener; 26 import com.android.ide.eclipse.adt.internal.sdk.LoadStatus; 27 28 import org.eclipse.core.resources.IContainer; 29 import org.eclipse.core.resources.IFile; 30 import org.eclipse.core.resources.IFolder; 31 import org.eclipse.core.resources.IMarker; 32 import org.eclipse.core.resources.IProject; 33 import org.eclipse.core.resources.IResource; 34 import org.eclipse.core.resources.IWorkspaceRoot; 35 import org.eclipse.core.resources.IncrementalProjectBuilder; 36 import org.eclipse.core.resources.ResourcesPlugin; 37 import org.eclipse.core.runtime.CoreException; 38 import org.eclipse.core.runtime.IPath; 39 import org.eclipse.core.runtime.IProgressMonitor; 40 import org.eclipse.core.runtime.IStatus; 41 import org.eclipse.core.runtime.Status; 42 import org.eclipse.core.runtime.SubProgressMonitor; 43 import org.eclipse.jdt.core.IClasspathEntry; 44 import org.eclipse.jdt.core.IJavaProject; 45 import org.eclipse.jdt.core.JavaCore; 46 import org.xml.sax.SAXException; 47 48 import java.io.BufferedReader; 49 import java.io.File; 50 import java.io.IOException; 51 import java.io.InputStreamReader; 52 import java.util.ArrayList; 53 import java.util.regex.Matcher; 54 import java.util.regex.Pattern; 55 56 import javax.xml.parsers.ParserConfigurationException; 57 import javax.xml.parsers.SAXParser; 58 import javax.xml.parsers.SAXParserFactory; 59 60 /** 61 * Base builder for XML files. This class allows for basic XML parsing with 62 * error checking and marking the files for errors/warnings. 63 */ 64 abstract class BaseBuilder extends IncrementalProjectBuilder { 65 66 // TODO: rename the pattern to something that makes sense + javadoc comments. 67 68 /** 69 * Single line aapt warning for skipping files.<br> 70 * " (skipping hidden file '<file path>'" 71 */ 72 private final static Pattern sPattern0Line1 = Pattern.compile( 73 "^\\s+\\(skipping hidden file\\s'(.*)'\\)$"); //$NON-NLS-1$ 74 75 /** 76 * First line of dual line aapt error.<br> 77 * "ERROR at line <line>: <error>"<br> 78 * " (Occurred while parsing <path>)" 79 */ 80 private final static Pattern sPattern1Line1 = Pattern.compile( 81 "^ERROR\\s+at\\s+line\\s+(\\d+):\\s+(.*)$"); //$NON-NLS-1$ 82 /** 83 * Second line of dual line aapt error.<br> 84 * "ERROR at line <line>: <error>"<br> 85 * " (Occurred while parsing <path>)"<br> 86 * @see #sPattern1Line1 87 */ 88 private final static Pattern sPattern1Line2 = Pattern.compile( 89 "^\\s+\\(Occurred while parsing\\s+(.*)\\)$"); //$NON-NLS-1$ 90 /** 91 * First line of dual line aapt error.<br> 92 * "ERROR: <error>"<br> 93 * "Defined at file <path> line <line>" 94 */ 95 private final static Pattern sPattern2Line1 = Pattern.compile( 96 "^ERROR:\\s+(.+)$"); //$NON-NLS-1$ 97 /** 98 * Second line of dual line aapt error.<br> 99 * "ERROR: <error>"<br> 100 * "Defined at file <path> line <line>"<br> 101 * @see #sPattern2Line1 102 */ 103 private final static Pattern sPattern2Line2 = Pattern.compile( 104 "Defined\\s+at\\s+file\\s+(.+)\\s+line\\s+(\\d+)"); //$NON-NLS-1$ 105 /** 106 * Single line aapt error<br> 107 * "<path> line <line>: <error>" 108 */ 109 private final static Pattern sPattern3Line1 = Pattern.compile( 110 "^(.+)\\sline\\s(\\d+):\\s(.+)$"); //$NON-NLS-1$ 111 /** 112 * First line of dual line aapt error.<br> 113 * "ERROR parsing XML file <path>"<br> 114 * "<error> at line <line>" 115 */ 116 private final static Pattern sPattern4Line1 = Pattern.compile( 117 "^Error\\s+parsing\\s+XML\\s+file\\s(.+)$"); //$NON-NLS-1$ 118 /** 119 * Second line of dual line aapt error.<br> 120 * "ERROR parsing XML file <path>"<br> 121 * "<error> at line <line>"<br> 122 * @see #sPattern4Line1 123 */ 124 private final static Pattern sPattern4Line2 = Pattern.compile( 125 "^(.+)\\s+at\\s+line\\s+(\\d+)$"); //$NON-NLS-1$ 126 127 /** 128 * Single line aapt warning<br> 129 * "<path>:<line>: <error>" 130 */ 131 private final static Pattern sPattern5Line1 = Pattern.compile( 132 "^(.+?):(\\d+):\\s+WARNING:(.+)$"); //$NON-NLS-1$ 133 134 /** 135 * Single line aapt error<br> 136 * "<path>:<line>: <error>" 137 */ 138 private final static Pattern sPattern6Line1 = Pattern.compile( 139 "^(.+?):(\\d+):\\s+(.+)$"); //$NON-NLS-1$ 140 141 /** 142 * 4 line aapt error<br> 143 * "ERROR: 9-path image <path> malformed"<br> 144 * Line 2 and 3 are taken as-is while line 4 is ignored (it repeats with<br> 145 * 'ERROR: failure processing <path>) 146 */ 147 private final static Pattern sPattern7Line1 = Pattern.compile( 148 "^ERROR:\\s+9-patch\\s+image\\s+(.+)\\s+malformed\\.$"); //$NON-NLS-1$ 149 150 private final static Pattern sPattern8Line1 = Pattern.compile( 151 "^(invalid resource directory name): (.*)$"); //$NON-NLS-1$ 152 153 /** 154 * 2 line aapt error<br> 155 * "ERROR: Invalid configuration: foo"<br> 156 * " ^^^"<br> 157 * There's no need to parse the 2nd line. 158 */ 159 private final static Pattern sPattern9Line1 = Pattern.compile( 160 "^Invalid configuration: (.+)$"); //$NON-NLS-1$ 161 162 /** SAX Parser factory. */ 163 private SAXParserFactory mParserFactory; 164 165 /** 166 * Base Resource Delta Visitor to handle XML error 167 */ 168 protected static class BaseDeltaVisitor implements XmlErrorListener { 169 170 /** The Xml builder used to validate XML correctness. */ 171 protected BaseBuilder mBuilder; 172 173 /** 174 * XML error flag. if true, we keep parsing the ResourceDelta but the 175 * compilation will not happen (we're putting markers) 176 */ 177 public boolean mXmlError = false; 178 BaseDeltaVisitor(BaseBuilder builder)179 public BaseDeltaVisitor(BaseBuilder builder) { 180 mBuilder = builder; 181 } 182 183 /** 184 * Finds a matching Source folder for the current path. This checkds if the current path 185 * leads to, or is a source folder. 186 * @param sourceFolders The list of source folders 187 * @param pathSegments The segments of the current path 188 * @return The segments of the source folder, or null if no match was found 189 */ findMatchingSourceFolder(ArrayList<IPath> sourceFolders, String[] pathSegments)190 protected static String[] findMatchingSourceFolder(ArrayList<IPath> sourceFolders, 191 String[] pathSegments) { 192 193 for (IPath p : sourceFolders) { 194 // check if we are inside one of those source class path 195 196 // get the segments 197 String[] srcSegments = p.segments(); 198 199 // compare segments. We want the path of the resource 200 // we're visiting to be 201 boolean valid = true; 202 int segmentCount = pathSegments.length; 203 204 for (int i = 0 ; i < segmentCount; i++) { 205 String s1 = pathSegments[i]; 206 String s2 = srcSegments[i]; 207 208 if (s1.equalsIgnoreCase(s2) == false) { 209 valid = false; 210 break; 211 } 212 } 213 214 if (valid) { 215 // this folder, or one of this children is a source 216 // folder! 217 // we return its segments 218 return srcSegments; 219 } 220 } 221 222 return null; 223 } 224 225 /** 226 * Sent when an XML error is detected. 227 * @see XmlErrorListener 228 */ errorFound()229 public void errorFound() { 230 mXmlError = true; 231 } 232 } 233 BaseBuilder()234 public BaseBuilder() { 235 super(); 236 mParserFactory = SAXParserFactory.newInstance(); 237 238 // FIXME when the compiled XML support for namespace is in, set this to true. 239 mParserFactory.setNamespaceAware(false); 240 } 241 242 /** 243 * Checks an Xml file for validity. Errors/warnings will be marked on the 244 * file 245 * @param resource the resource to check 246 * @param visitor a valid resource delta visitor 247 */ checkXML(IResource resource, BaseDeltaVisitor visitor)248 protected final void checkXML(IResource resource, BaseDeltaVisitor visitor) { 249 250 // first make sure this is an xml file 251 if (resource instanceof IFile) { 252 IFile file = (IFile)resource; 253 254 // remove previous markers 255 removeMarkersFromFile(file, AndroidConstants.MARKER_XML); 256 257 // create the error handler 258 XmlErrorHandler reporter = new XmlErrorHandler(file, visitor); 259 try { 260 // parse 261 getParser().parse(file.getContents(), reporter); 262 } catch (Exception e1) { 263 } 264 } 265 } 266 267 /** 268 * Returns the SAXParserFactory, instantiating it first if it's not already 269 * created. 270 * @return the SAXParserFactory object 271 * @throws ParserConfigurationException 272 * @throws SAXException 273 */ getParser()274 protected final SAXParser getParser() throws ParserConfigurationException, 275 SAXException { 276 return mParserFactory.newSAXParser(); 277 } 278 279 /** 280 * Adds a marker to the current project. 281 * 282 * @param markerId The id of the marker to add. 283 * @param message the message associated with the mark 284 * @param severity the severity of the marker. 285 */ markProject(String markerId, String message, int severity)286 protected final void markProject(String markerId, String message, int severity) { 287 BaseProjectHelper.addMarker(getProject(), markerId, message, severity); 288 } 289 290 291 /** 292 * Removes markers from a file. 293 * @param file The file from which to delete the markers. 294 * @param markerId The id of the markers to remove. If null, all marker of 295 * type <code>IMarker.PROBLEM</code> will be removed. 296 */ removeMarkersFromFile(IFile file, String markerId)297 protected final void removeMarkersFromFile(IFile file, String markerId) { 298 try { 299 if (file.exists()) { 300 file.deleteMarkers(markerId, true, IResource.DEPTH_ZERO); 301 } 302 } catch (CoreException ce) { 303 String msg = String.format(Messages.Marker_Delete_Error, markerId, file.toString()); 304 AdtPlugin.printErrorToConsole(getProject(), msg); 305 } 306 } 307 308 /** 309 * Removes markers from a container and its children. 310 * @param folder The container from which to delete the markers. 311 * @param markerId The id of the markers to remove. If null, all marker of 312 * type <code>IMarker.PROBLEM</code> will be removed. 313 */ removeMarkersFromContainer(IContainer folder, String markerId)314 protected final void removeMarkersFromContainer(IContainer folder, String markerId) { 315 try { 316 if (folder.exists()) { 317 folder.deleteMarkers(markerId, true, IResource.DEPTH_INFINITE); 318 } 319 } catch (CoreException ce) { 320 String msg = String.format(Messages.Marker_Delete_Error, markerId, folder.toString()); 321 AdtPlugin.printErrorToConsole(getProject(), msg); 322 } 323 } 324 325 /** 326 * Removes markers from a project and its children. 327 * @param project The project from which to delete the markers 328 * @param markerId The id of the markers to remove. If null, all marker of 329 * type <code>IMarker.PROBLEM</code> will be removed. 330 */ removeMarkersFromProject(IProject project, String markerId)331 protected final static void removeMarkersFromProject(IProject project, 332 String markerId) { 333 try { 334 if (project.exists()) { 335 project.deleteMarkers(markerId, true, IResource.DEPTH_INFINITE); 336 } 337 } catch (CoreException ce) { 338 String msg = String.format(Messages.Marker_Delete_Error, markerId, project.getName()); 339 AdtPlugin.printErrorToConsole(project, msg); 340 } 341 } 342 343 /** 344 * Get the stderr output of a process and return when the process is done. 345 * @param process The process to get the ouput from 346 * @param results The array to store the stderr output 347 * @return the process return code. 348 * @throws InterruptedException 349 */ grabProcessOutput(final Process process, final ArrayList<String> results)350 protected final int grabProcessOutput(final Process process, 351 final ArrayList<String> results) 352 throws InterruptedException { 353 // Due to the limited buffer size on windows for the standard io (stderr, stdout), we 354 // *need* to read both stdout and stderr all the time. If we don't and a process output 355 // a large amount, this could deadlock the process. 356 357 // read the lines as they come. if null is returned, it's 358 // because the process finished 359 new Thread("") { //$NON-NLS-1$ 360 @Override 361 public void run() { 362 // create a buffer to read the stderr output 363 InputStreamReader is = new InputStreamReader(process.getErrorStream()); 364 BufferedReader errReader = new BufferedReader(is); 365 366 try { 367 while (true) { 368 String line = errReader.readLine(); 369 if (line != null) { 370 results.add(line); 371 } else { 372 break; 373 } 374 } 375 } catch (IOException e) { 376 // do nothing. 377 } 378 } 379 }.start(); 380 381 new Thread("") { //$NON-NLS-1$ 382 @Override 383 public void run() { 384 InputStreamReader is = new InputStreamReader(process.getInputStream()); 385 BufferedReader outReader = new BufferedReader(is); 386 387 IProject project = getProject(); 388 389 try { 390 while (true) { 391 String line = outReader.readLine(); 392 if (line != null) { 393 AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, 394 project, line); 395 } else { 396 break; 397 } 398 } 399 } catch (IOException e) { 400 // do nothing. 401 } 402 } 403 404 }.start(); 405 406 // get the return code from the process 407 return process.waitFor(); 408 } 409 410 /** 411 * Parse the output of aapt and mark the incorrect file with error markers 412 * 413 * @param results the output of aapt 414 * @param project the project containing the file to mark 415 * @return true if the parsing failed, false if success. 416 */ parseAaptOutput(ArrayList<String> results, IProject project)417 protected final boolean parseAaptOutput(ArrayList<String> results, 418 IProject project) { 419 // nothing to parse? just return false; 420 if (results.size() == 0) { 421 return false; 422 } 423 424 // get the root of the project so that we can make IFile from full 425 // file path 426 String osRoot = project.getLocation().toOSString(); 427 428 Matcher m; 429 430 for (int i = 0; i < results.size(); i++) { 431 String p = results.get(i); 432 433 m = sPattern0Line1.matcher(p); 434 if (m.matches()) { 435 // we ignore those (as this is an ignore message from aapt) 436 continue; 437 } 438 439 m = sPattern1Line1.matcher(p); 440 if (m.matches()) { 441 String lineStr = m.group(1); 442 String msg = m.group(2); 443 444 // get the matcher for the next line. 445 m = getNextLineMatcher(results, ++i, sPattern1Line2); 446 if (m == null) { 447 return true; 448 } 449 450 String location = m.group(1); 451 452 // check the values and attempt to mark the file. 453 if (checkAndMark(location, lineStr, msg, osRoot, project, 454 AndroidConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) { 455 return true; 456 } 457 continue; 458 } 459 460 // this needs to be tested before Pattern2 since they both start with 'ERROR:' 461 m = sPattern7Line1.matcher(p); 462 if (m.matches()) { 463 String location = m.group(1); 464 String msg = p; // default msg is the line in case we don't find anything else 465 466 if (++i < results.size()) { 467 msg = results.get(i).trim(); 468 if (++i < results.size()) { 469 msg = msg + " - " + results.get(i).trim(); //$NON-NLS-1$ 470 471 // skip the next line 472 i++; 473 } 474 } 475 476 // display the error 477 if (checkAndMark(location, null, msg, osRoot, project, 478 AndroidConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) { 479 return true; 480 } 481 482 // success, go to the next line 483 continue; 484 } 485 486 m = sPattern2Line1.matcher(p); 487 if (m.matches()) { 488 // get the msg 489 String msg = m.group(1); 490 491 // get the matcher for the next line. 492 m = getNextLineMatcher(results, ++i, sPattern2Line2); 493 if (m == null) { 494 return true; 495 } 496 497 String location = m.group(1); 498 String lineStr = m.group(2); 499 500 // check the values and attempt to mark the file. 501 if (checkAndMark(location, lineStr, msg, osRoot, project, 502 AndroidConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) { 503 return true; 504 } 505 continue; 506 } 507 508 m = sPattern3Line1.matcher(p); 509 if (m.matches()) { 510 String location = m.group(1); 511 String lineStr = m.group(2); 512 String msg = m.group(3); 513 514 // check the values and attempt to mark the file. 515 if (checkAndMark(location, lineStr, msg, osRoot, project, 516 AndroidConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) { 517 return true; 518 } 519 520 // success, go to the next line 521 continue; 522 } 523 524 m = sPattern4Line1.matcher(p); 525 if (m.matches()) { 526 // get the filename. 527 String location = m.group(1); 528 529 // get the matcher for the next line. 530 m = getNextLineMatcher(results, ++i, sPattern4Line2); 531 if (m == null) { 532 return true; 533 } 534 535 String msg = m.group(1); 536 String lineStr = m.group(2); 537 538 // check the values and attempt to mark the file. 539 if (checkAndMark(location, lineStr, msg, osRoot, project, 540 AndroidConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) { 541 return true; 542 } 543 544 // success, go to the next line 545 continue; 546 } 547 548 m = sPattern5Line1.matcher(p); 549 if (m.matches()) { 550 String location = m.group(1); 551 String lineStr = m.group(2); 552 String msg = m.group(3); 553 554 // check the values and attempt to mark the file. 555 if (checkAndMark(location, lineStr, msg, osRoot, project, 556 AndroidConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_WARNING) == false) { 557 return true; 558 } 559 560 // success, go to the next line 561 continue; 562 } 563 564 m = sPattern6Line1.matcher(p); 565 if (m.matches()) { 566 String location = m.group(1); 567 String lineStr = m.group(2); 568 String msg = m.group(3); 569 570 // check the values and attempt to mark the file. 571 if (checkAndMark(location, lineStr, msg, osRoot, project, 572 AndroidConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) { 573 return true; 574 } 575 576 // success, go to the next line 577 continue; 578 } 579 580 m = sPattern8Line1.matcher(p); 581 if (m.matches()) { 582 String location = m.group(2); 583 String msg = m.group(1); 584 585 // check the values and attempt to mark the file. 586 if (checkAndMark(location, null, msg, osRoot, project, 587 AndroidConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) { 588 return true; 589 } 590 591 // success, go to the next line 592 continue; 593 } 594 595 m = sPattern9Line1.matcher(p); 596 if (m.matches()) { 597 String badConfig = m.group(1); 598 String msg = String.format("APK Configuration filter '%1$s' is invalid", badConfig); 599 600 // skip the next line 601 i++; 602 603 // check the values and attempt to mark the file. 604 if (checkAndMark(null /*location*/, null, msg, osRoot, project, 605 AndroidConstants.MARKER_AAPT_PACKAGE, IMarker.SEVERITY_ERROR) == false) { 606 return true; 607 } 608 609 // success, go to the next line 610 continue; 611 } 612 613 // invalid line format, flag as error, and bail 614 return true; 615 } 616 617 return false; 618 } 619 620 621 622 /** 623 * Saves a String property into the persistent storage of the project. 624 * @param propertyName the name of the property. The id of the plugin is added to this string. 625 * @param value the value to save 626 * @return true if the save succeeded. 627 */ saveProjectStringProperty(String propertyName, String value)628 protected boolean saveProjectStringProperty(String propertyName, String value) { 629 IProject project = getProject(); 630 return ProjectHelper.saveStringProperty(project, propertyName, value); 631 } 632 633 634 /** 635 * Loads a String property from the persistent storage of the project. 636 * @param propertyName the name of the property. The id of the plugin is added to this string. 637 * @return the property value or null if it was not found. 638 */ loadProjectStringProperty(String propertyName)639 protected String loadProjectStringProperty(String propertyName) { 640 IProject project = getProject(); 641 return ProjectHelper.loadStringProperty(project, propertyName); 642 } 643 644 /** 645 * Saves a property into the persistent storage of the project. 646 * @param propertyName the name of the property. The id of the plugin is added to this string. 647 * @param value the value to save 648 * @return true if the save succeeded. 649 */ saveProjectBooleanProperty(String propertyName, boolean value)650 protected boolean saveProjectBooleanProperty(String propertyName, boolean value) { 651 IProject project = getProject(); 652 return ProjectHelper.saveStringProperty(project, propertyName, Boolean.toString(value)); 653 } 654 655 /** 656 * Loads a boolean property from the persistent storage of the project. 657 * @param propertyName the name of the property. The id of the plugin is added to this string. 658 * @param defaultValue The default value to return if the property was not found. 659 * @return the property value or the default value if the property was not found. 660 */ loadProjectBooleanProperty(String propertyName, boolean defaultValue)661 protected boolean loadProjectBooleanProperty(String propertyName, boolean defaultValue) { 662 IProject project = getProject(); 663 return ProjectHelper.loadBooleanProperty(project, propertyName, defaultValue); 664 } 665 666 /** 667 * Saves the path of a resource into the persistent storate of the project. 668 * @param propertyName the name of the property. The id of the plugin is added to this string. 669 * @param resource the resource which path is saved. 670 * @return true if the save succeeded 671 */ saveProjectResourceProperty(String propertyName, IResource resource)672 protected boolean saveProjectResourceProperty(String propertyName, IResource resource) { 673 return ProjectHelper.saveResourceProperty(getProject(), propertyName, resource); 674 } 675 676 /** 677 * Loads the path of a resource from the persistent storage of the project, and returns the 678 * corresponding IResource object. 679 * @param propertyName the name of the property. The id of the plugin is added to this string. 680 * @return The corresponding IResource object (or children interface) or null 681 */ loadProjectResourceProperty(String propertyName)682 protected IResource loadProjectResourceProperty(String propertyName) { 683 IProject project = getProject(); 684 return ProjectHelper.loadResourceProperty(project, propertyName); 685 } 686 687 /** 688 * Check if the parameters gotten from the error output are valid, and mark 689 * the file with an AAPT marker. 690 * @param location the full OS path of the error file. If null, the project is marked 691 * @param lineStr 692 * @param message 693 * @param root The root directory of the project, in OS specific format. 694 * @param project 695 * @param markerId The marker id to put. 696 * @param severity The severity of the marker to put (IMarker.SEVERITY_*) 697 * @return true if the parameters were valid and the file was marked successfully. 698 * 699 * @see IMarker 700 */ checkAndMark(String location, String lineStr, String message, String root, IProject project, String markerId, int severity)701 private final boolean checkAndMark(String location, String lineStr, 702 String message, String root, IProject project, String markerId, int severity) { 703 // check this is in fact a file 704 if (location != null) { 705 File f = new File(location); 706 if (f.exists() == false) { 707 return false; 708 } 709 } 710 711 // get the line number 712 int line = -1; // default value for error with no line. 713 714 if (lineStr != null) { 715 try { 716 line = Integer.parseInt(lineStr); 717 } catch (NumberFormatException e) { 718 // looks like the string we extracted wasn't a valid 719 // file number. Parsing failed and we return true 720 return false; 721 } 722 } 723 724 // add the marker 725 IResource f2 = project; 726 if (location != null) { 727 f2 = getResourceFromFullPath(location, root, project); 728 if (f2 == null) { 729 return false; 730 } 731 } 732 733 // check if there's a similar marker already, since aapt is launched twice 734 boolean markerAlreadyExists = false; 735 try { 736 IMarker[] markers = f2.findMarkers(markerId, true, IResource.DEPTH_ZERO); 737 738 for (IMarker marker : markers) { 739 int tmpLine = marker.getAttribute(IMarker.LINE_NUMBER, -1); 740 if (tmpLine != line) { 741 break; 742 } 743 744 int tmpSeverity = marker.getAttribute(IMarker.SEVERITY, -1); 745 if (tmpSeverity != severity) { 746 break; 747 } 748 749 String tmpMsg = marker.getAttribute(IMarker.MESSAGE, null); 750 if (tmpMsg == null || tmpMsg.equals(message) == false) { 751 break; 752 } 753 754 // if we're here, all the marker attributes are equals, we found it 755 // and exit 756 markerAlreadyExists = true; 757 break; 758 } 759 760 } catch (CoreException e) { 761 // if we couldn't get the markers, then we just mark the file again 762 // (since markerAlreadyExists is initialized to false, we do nothing) 763 } 764 765 if (markerAlreadyExists == false) { 766 if (line != -1) { 767 BaseProjectHelper.addMarker(f2, markerId, message, line, 768 severity); 769 } else { 770 BaseProjectHelper.addMarker(f2, markerId, message, severity); 771 } 772 } 773 774 return true; 775 } 776 777 /** 778 * Returns a matching matcher for the next line 779 * @param lines The array of lines 780 * @param nextIndex The index of the next line 781 * @param pattern The pattern to match 782 * @return null if error or no match, the matcher otherwise. 783 */ getNextLineMatcher(ArrayList<String> lines, int nextIndex, Pattern pattern)784 private final Matcher getNextLineMatcher(ArrayList<String> lines, 785 int nextIndex, Pattern pattern) { 786 // unless we can't, because we reached the last line 787 if (nextIndex == lines.size()) { 788 // we expected a 2nd line, so we flag as error 789 // and we bail 790 return null; 791 } 792 793 Matcher m = pattern.matcher(lines.get(nextIndex)); 794 if (m.matches()) { 795 return m; 796 } 797 798 return null; 799 } 800 getResourceFromFullPath(String filename, String root, IProject project)801 private IResource getResourceFromFullPath(String filename, String root, 802 IProject project) { 803 if (filename.startsWith(root)) { 804 String file = filename.substring(root.length()); 805 806 // get the resource 807 IResource r = project.findMember(file); 808 809 // if the resource is valid, we add the marker 810 if (r.exists()) { 811 return r; 812 } 813 } 814 815 return null; 816 } 817 818 /** 819 * Returns an array of external jar files used by the project. 820 * @return an array of OS-specific absolute file paths 821 */ getExternalJars()822 protected final String[] getExternalJars() { 823 // get the current project 824 IProject project = getProject(); 825 826 // get a java project from it 827 IJavaProject javaProject = JavaCore.create(project); 828 829 IWorkspaceRoot wsRoot = ResourcesPlugin.getWorkspace().getRoot(); 830 831 ArrayList<String> oslibraryList = new ArrayList<String>(); 832 IClasspathEntry[] classpaths = javaProject.readRawClasspath(); 833 if (classpaths != null) { 834 for (IClasspathEntry e : classpaths) { 835 if (e.getEntryKind() == IClasspathEntry.CPE_LIBRARY || 836 e.getEntryKind() == IClasspathEntry.CPE_VARIABLE) { 837 // if this is a classpath variable reference, we resolve it. 838 if (e.getEntryKind() == IClasspathEntry.CPE_VARIABLE) { 839 e = JavaCore.getResolvedClasspathEntry(e); 840 } 841 842 // get the IPath 843 IPath path = e.getPath(); 844 845 // check the name ends with .jar 846 if (AndroidConstants.EXT_JAR.equalsIgnoreCase(path.getFileExtension())) { 847 boolean local = false; 848 IResource resource = wsRoot.findMember(path); 849 if (resource != null && resource.exists() && 850 resource.getType() == IResource.FILE) { 851 local = true; 852 oslibraryList.add(resource.getLocation().toOSString()); 853 } 854 855 if (local == false) { 856 // if the jar path doesn't match a workspace resource, 857 // then we get an OSString and check if this links to a valid file. 858 String osFullPath = path.toOSString(); 859 860 File f = new File(osFullPath); 861 if (f.exists()) { 862 oslibraryList.add(osFullPath); 863 } else { 864 String message = String.format( Messages.Couldnt_Locate_s_Error, 865 path); 866 AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, 867 project, message); 868 869 // Also put a warning marker on the project 870 markProject(AdtConstants.MARKER_ADT, message, 871 IMarker.SEVERITY_WARNING); 872 } 873 } 874 } 875 } 876 } 877 } 878 879 return oslibraryList.toArray(new String[oslibraryList.size()]); 880 } 881 882 /** 883 * Aborts the build if the SDK/project setups are broken. This does not 884 * display any errors. 885 * 886 * @param project The {@link IJavaProject} being compiled. 887 * @throws CoreException 888 */ abortOnBadSetup(IProject project)889 protected void abortOnBadSetup(IProject project) throws CoreException { 890 // check if we have finished loading the SDK. 891 if (AdtPlugin.getDefault().getSdkLoadStatus() != LoadStatus.LOADED) { 892 // we exit silently 893 stopBuild("SDK is not loaded yet"); 894 } 895 896 // abort if there are TARGET or ADT type markers 897 IMarker[] markers = project.findMarkers(AdtConstants.MARKER_TARGET, 898 false /*includeSubtypes*/, IResource.DEPTH_ZERO); 899 900 if (markers.length > 0) { 901 stopBuild(""); 902 } 903 904 markers = project.findMarkers(AdtConstants.MARKER_ADT, false /*includeSubtypes*/, 905 IResource.DEPTH_ZERO); 906 907 if (markers.length > 0) { 908 stopBuild(""); 909 } 910 } 911 912 /** 913 * Throws an exception to cancel the build. 914 * 915 * @param error the error message 916 * @param args the printf-style arguments to the error message. 917 * @throws CoreException 918 */ stopBuild(String error, Object... args)919 protected final void stopBuild(String error, Object... args) throws CoreException { 920 throw new CoreException(new Status(IStatus.CANCEL, AdtPlugin.PLUGIN_ID, 921 String.format(error, args))); 922 } 923 924 /** 925 * Recursively delete all the derived resources. 926 */ removeDerivedResources(IResource resource, IProgressMonitor monitor)927 protected void removeDerivedResources(IResource resource, IProgressMonitor monitor) 928 throws CoreException { 929 if (resource.exists()) { 930 if (resource.isDerived()) { 931 resource.delete(true, new SubProgressMonitor(monitor, 10)); 932 } else if (resource.getType() == IResource.FOLDER) { 933 IFolder folder = (IFolder)resource; 934 IResource[] members = folder.members(); 935 for (IResource member : members) { 936 removeDerivedResources(member, monitor); 937 } 938 } 939 } 940 } 941 } 942