| 1 19 20 25 26 package edu.umd.cs.findbugs; 27 28 import java.io.BufferedReader ; 29 import java.io.BufferedWriter ; 30 import java.io.File ; 31 import java.io.FileReader ; 32 import java.io.FileWriter ; 33 import java.io.IOException ; 34 import java.io.InputStream ; 35 import java.io.PrintWriter ; 36 import java.net.MalformedURLException ; 37 import java.net.URL ; 38 import java.util.HashMap ; 39 import java.util.HashSet ; 40 import java.util.LinkedList ; 41 import java.util.List ; 42 import java.util.Map ; 43 import java.util.jar.Attributes ; 44 import java.util.jar.Manifest ; 45 46 import edu.umd.cs.findbugs.ba.URLClassPath; 47 import edu.umd.cs.findbugs.xml.XMLAttributeList; 48 import edu.umd.cs.findbugs.xml.XMLOutput; 49 import edu.umd.cs.findbugs.xml.XMLOutputUtil; 50 import edu.umd.cs.findbugs.xml.XMLWriteable; 51 52 66 public class Project implements XMLWriteable { 67 private static final boolean DEBUG = SystemProperties.getBoolean("findbugs.project.debug"); 68 69 72 private String projectFileName; 73 74 77 private Map <String , Boolean > optionsMap; 78 79 82 private LinkedList <String > fileList; 83 84 87 private LinkedList <String > srcDirList; 88 89 92 private LinkedList <String > auxClasspathEntryList; 93 94 97 private boolean isModified; 98 99 102 public static final String UNNAMED_PROJECT = "<<unnamed project>>"; 103 104 private long timestamp = 0L; 105 106 109 public Project() { 110 this.projectFileName = UNNAMED_PROJECT; 111 optionsMap = new HashMap <String , Boolean >(); 112 optionsMap.put(RELATIVE_PATHS, Boolean.FALSE); 113 fileList = new LinkedList <String >(); 114 srcDirList = new LinkedList <String >(); 115 auxClasspathEntryList = new LinkedList <String >(); 116 isModified = false; 117 } 118 119 122 public Project duplicate() { 123 Project dup = new Project(); 124 dup.projectFileName = this.projectFileName; 125 dup.optionsMap.clear(); 126 dup.optionsMap.putAll(this.optionsMap); 127 dup.fileList.addAll(this.fileList); 128 dup.srcDirList.addAll(this.srcDirList); 129 dup.auxClasspathEntryList.addAll(this.auxClasspathEntryList); 130 dup.timestamp = timestamp; 131 132 return dup; 133 } 134 135 138 public boolean isModified() { 139 return isModified; 140 } 141 142 145 public void setModified(boolean isModified) { 146 this.isModified = isModified; 147 } 148 149 152 public String getProjectFileName() { 153 return projectFileName; 154 } 155 156 161 public void setProjectFileName(String projectFileName) { 162 this.projectFileName = projectFileName; 163 } 164 165 172 public boolean addFile(String fileName) { 173 return addToListInternal(fileList, makeAbsoluteCWD(fileName)); 174 } 175 176 182 public boolean addSourceDir(String dirName) { 183 return addToListInternal(srcDirList, makeAbsoluteCWD(dirName)); 184 } 185 186 192 public boolean getOption(String option) { 193 Boolean value = optionsMap.get(option); 194 return value != null && value.booleanValue(); 195 } 196 197 202 public int getFileCount() { 203 return fileList.size(); 204 } 205 206 212 public String getFile(int num) { 213 return fileList.get(num); 214 } 215 216 221 public void removeFile(int num) { 222 fileList.remove(num); 223 isModified = true; 224 } 225 226 229 public List <String > getFileList() { 230 return fileList; 231 } 232 233 238 public int getNumSourceDirs() { 239 return srcDirList.size(); 240 } 241 242 248 public String getSourceDir(int num) { 249 return srcDirList.get(num); 250 } 251 252 257 public void removeSourceDir(int num) { 258 srcDirList.remove(num); 259 isModified = true; 260 } 261 262 265 public String [] getFileArray() { 266 return fileList.toArray(new String [fileList.size()]); 267 } 268 269 272 public String [] getSourceDirArray() { 273 return srcDirList.toArray(new String [srcDirList.size()]); 274 } 275 276 279 public List <String > getSourceDirList() { 280 return srcDirList; 281 } 282 283 290 public boolean addAuxClasspathEntry(String auxClasspathEntry) { 291 return addToListInternal(auxClasspathEntryList, makeAbsoluteCWD(auxClasspathEntry)); 292 } 293 294 297 public int getNumAuxClasspathEntries() { 298 return auxClasspathEntryList.size(); 299 } 300 301 304 public String getAuxClasspathEntry(int n) { 305 return auxClasspathEntryList.get(n); 306 } 307 308 311 public void removeAuxClasspathEntry(int n) { 312 auxClasspathEntryList.remove(n); 313 isModified = true; 314 } 315 316 319 public List <String > getAuxClasspathEntryList() { 320 return auxClasspathEntryList; 321 } 322 323 326 private static class WorkListItem { 327 private URL url; 328 329 334 public WorkListItem(URL url) { 335 this.url = url; 336 } 337 338 341 public URL getURL() { 342 return this.url; 343 } 344 } 345 346 349 private static class WorkList { 350 private LinkedList <WorkListItem> itemList; 351 private HashSet <String > addedSet; 352 353 357 public WorkList() { 358 this.itemList = new LinkedList <WorkListItem>(); 359 this.addedSet = new HashSet <String >(); 360 } 361 362 365 public URL createURL(String fileName) throws MalformedURLException { 366 String protocol = URLClassPath.getURLProtocol(fileName); 367 if (protocol == null) { 368 fileName = "file:" + fileName; 369 } 370 return new URL (fileName); 371 } 372 373 376 public URL createRelativeURL(URL base, String fileName) throws MalformedURLException { 377 return new URL (base, fileName); 378 } 379 380 387 public boolean add(WorkListItem item) { 388 if (DEBUG) System.out.println("Adding " + item.getURL().toString()); 389 if (!addedSet.add(item.getURL().toString())) { 390 if (DEBUG) System.out.println("\t==> Already processed"); 391 return false; 392 } 393 394 itemList.add(item); 395 return true; 396 } 397 398 401 public boolean isEmpty() { 402 return itemList.isEmpty(); 403 } 404 405 408 public WorkListItem getNextItem() { 409 return itemList.removeFirst(); 410 } 411 } 412 413 424 public List <String > getImplicitClasspathEntryList() { 425 final LinkedList <String > implicitClasspath = new LinkedList <String >(); 426 WorkList workList = new WorkList(); 427 428 for (String fileName : fileList) { 431 try { 432 URL url = workList.createURL(fileName); 433 WorkListItem item = new WorkListItem(url); 434 workList.add(item); 435 } catch (MalformedURLException ignore) { 436 } 438 } 439 440 while (!workList.isEmpty()) { 442 WorkListItem item = workList.getNextItem(); 443 processComponentJar(item.getURL(), workList, implicitClasspath); 444 } 445 446 return implicitClasspath; 447 } 448 449 457 private void processComponentJar(URL jarFileURL, WorkList workList, 458 List <String > implicitClasspath) { 459 460 if (DEBUG) System.out.println("Processing " + jarFileURL.toString()); 461 462 if (!jarFileURL.toString().endsWith(".zip") && !jarFileURL.toString().endsWith(".jar")) 463 return; 464 465 try { 466 URL manifestURL = new URL ("jar:" + jarFileURL.toString() + "!/META-INF/MANIFEST.MF"); 467 468 InputStream in = null; 469 try { 470 in = manifestURL.openStream(); 471 Manifest manifest = new Manifest (in); 472 473 Attributes mainAttrs = manifest.getMainAttributes(); 474 String classPath = mainAttrs.getValue("Class-Path"); 475 if (classPath != null) { 476 String [] fileList = classPath.split("\\s+"); 477 478 for (String jarFile : fileList) { 479 URL referencedURL = workList.createRelativeURL(jarFileURL, jarFile); 480 if (workList.add(new WorkListItem(referencedURL))) { 481 implicitClasspath.add(referencedURL.toString()); 482 if (DEBUG) System.out.println("Implicit jar: " + referencedURL.toString()); 483 } 484 } 485 } 486 } finally { 487 if (in != null) { 488 in.close(); 489 } 490 } 491 } catch (IOException ignore) { 492 } 494 } 495 496 private static final String OPTIONS_KEY = "[Options]"; 497 private static final String JAR_FILES_KEY = "[Jar files]"; 498 private static final String SRC_DIRS_KEY = "[Source dirs]"; 499 private static final String AUX_CLASSPATH_ENTRIES_KEY = "[Aux classpath entries]"; 500 501 public static final String RELATIVE_PATHS = "relative_paths"; 503 504 515 public void write(String outputFile, boolean useRelativePaths, String relativeBase) 516 throws IOException { 517 PrintWriter writer = new PrintWriter (new BufferedWriter (new FileWriter (outputFile))); 518 try { 519 writer.println(JAR_FILES_KEY); 520 for (String jarFile : fileList) { 521 if (useRelativePaths) 522 jarFile = convertToRelative(jarFile, relativeBase); 523 writer.println(jarFile); 524 } 525 526 writer.println(SRC_DIRS_KEY); 527 for (String srcDir : srcDirList) { 528 if (useRelativePaths) 529 srcDir = convertToRelative(srcDir, relativeBase); 530 writer.println(srcDir); 531 } 532 533 writer.println(AUX_CLASSPATH_ENTRIES_KEY); 534 for (String auxClasspathEntry : auxClasspathEntryList) { 535 if (useRelativePaths) 536 auxClasspathEntry = convertToRelative(auxClasspathEntry, relativeBase); 537 writer.println(auxClasspathEntry); 538 } 539 540 if (useRelativePaths) { 541 writer.println(OPTIONS_KEY); 542 writer.println(RELATIVE_PATHS + "=true"); 543 } 544 } finally { 545 writer.close(); 546 } 547 548 isModified = false; 550 } 551 552 560 public void read(String inputFile) throws IOException { 561 if (isModified) 562 throw new IllegalStateException ("Reading into a modified Project!"); 563 564 File file = new File (inputFile); 566 if (!file.isAbsolute()) 567 inputFile = file.getAbsolutePath(); 568 569 setProjectFileName(inputFile); 571 572 BufferedReader reader = null; 573 574 try { 575 reader = new BufferedReader (new FileReader (inputFile)); 576 String line; 577 line = getLine(reader); 578 579 if (line == null || !line.equals(JAR_FILES_KEY)) 580 throw new IOException ("Bad format: missing jar files key"); 581 while ((line = getLine(reader)) != null && !line.equals(SRC_DIRS_KEY)) { 582 addToListInternal(fileList, line); 583 } 584 585 if (line == null) 586 throw new IOException ("Bad format: missing source dirs key"); 587 while ((line = getLine(reader)) != null && !line.equals(AUX_CLASSPATH_ENTRIES_KEY)) { 588 addToListInternal(srcDirList, line); 589 } 590 591 if (line != null) { 593 while ((line = getLine(reader)) != null) { 594 if (line.equals(OPTIONS_KEY)) 595 break; 596 addToListInternal(auxClasspathEntryList, line); 597 } 598 } 599 600 if (line != null && line.equals(OPTIONS_KEY)) { 602 while ((line = getLine(reader)) != null && !line.equals(JAR_FILES_KEY)) 603 parseOption(line); 604 } 605 606 if (getOption(RELATIVE_PATHS)) { 611 makeListAbsoluteProject(fileList); 612 makeListAbsoluteProject(srcDirList); 613 makeListAbsoluteProject(auxClasspathEntryList); 614 } 615 616 isModified = false; 618 } finally { 619 if (reader != null) 620 reader.close(); 621 } 622 } 623 624 628 private static String getLine(BufferedReader reader) throws IOException { 629 String line; 630 while ((line = reader.readLine()) != null) { 631 line = line.trim(); 632 if (!line.equals("") && !line.startsWith("#")) 633 break; 634 } 635 return line; 636 } 637 638 641 @Override  642 public String toString() { 643 String name = projectFileName; 644 int lastSep = name.lastIndexOf(File.separatorChar); 645 if (lastSep >= 0) 646 name = name.substring(lastSep + 1); 647 int dot = (name.endsWith(".fb") ? name.length()-3 : -1); 650 if (dot >= 0) 651 name = name.substring(0, dot); 652 return name; 653 } 654 655 659 public static String transformFilename(String fileName) { 660 if (!fileName.endsWith(".fb")) 661 fileName = fileName + ".fb"; 662 return fileName; 663 } 664 665 static final String JAR_ELEMENT_NAME = "Jar"; 666 static final String AUX_CLASSPATH_ENTRY_ELEMENT_NAME = "AuxClasspathEntry"; 667 static final String SRC_DIR_ELEMENT_NAME = "SrcDir"; 668 static final String FILENAME_ATTRIBUTE_NAME = "filename"; 669 670 public void writeXML(XMLOutput xmlOutput) throws IOException { 671 xmlOutput.openTag( 672 BugCollection.PROJECT_ELEMENT_NAME, 673 new XMLAttributeList().addAttribute(FILENAME_ATTRIBUTE_NAME, getProjectFileName())); 674 675 XMLOutputUtil.writeElementList(xmlOutput, JAR_ELEMENT_NAME, fileList); 676 XMLOutputUtil.writeElementList(xmlOutput, AUX_CLASSPATH_ENTRY_ELEMENT_NAME, auxClasspathEntryList); 677 XMLOutputUtil.writeElementList(xmlOutput, SRC_DIR_ELEMENT_NAME, srcDirList); 678 679 xmlOutput.closeTag(BugCollection.PROJECT_ELEMENT_NAME); 680 } 681 682 687 private void parseOption(String option) throws IOException { 688 int equalPos = option.indexOf("="); 689 if (equalPos < 0) 690 throw new IOException ("Bad format: invalid option format"); 691 String name = option.substring(0, equalPos); 692 String value = option.substring(equalPos + 1); 693 optionsMap.put(name, Boolean.valueOf(value)); 694 } 695 696 702 private static final boolean FILE_IGNORE_CASE = 703 SystemProperties.getProperty("os.name", "unknown").startsWith("Windows"); 704 705 711 private String convertToRelative(String srcFile, String base) { 712 String slash = SystemProperties.getProperty("file.separator"); 713 714 if (FILE_IGNORE_CASE) { 715 srcFile = srcFile.toLowerCase(); 716 base = base.toLowerCase(); 717 } 718 719 if (base.equals(srcFile)) 720 return "."; 721 722 if (!base.endsWith(slash)) 723 base = base + slash; 724 725 if (base.length() <= srcFile.length()) { 726 String root = srcFile.substring(0, base.length()); 727 if (root.equals(base)) { 728 return "." + SystemProperties.getProperty("file.separator") + srcFile.substring(base.length()); 730 } 731 } 732 733 int slashPos = srcFile.indexOf(slash); 735 int branchPoint; 736 if (slashPos >= 0) { 737 String subPath = srcFile.substring(0, slashPos); 738 if ((subPath.length() == 0) || base.startsWith(subPath)) { 739 branchPoint = slashPos + 1; 740 slashPos = srcFile.indexOf(slash, branchPoint); 741 while (slashPos >= 0) { 742 subPath = srcFile.substring(0, slashPos); 743 if (base.startsWith(subPath)) 744 branchPoint = slashPos + 1; 745 else 746 break; 747 slashPos = srcFile.indexOf(slash, branchPoint); 748 } 749 750 int slashCount = 0; 751 slashPos = base.indexOf(slash, branchPoint); 752 while (slashPos >= 0) { 753 slashCount++; 754 slashPos = base.indexOf(slash, slashPos + 1); 755 } 756 757 StringBuffer path = new StringBuffer (); 758 String upDir = ".." + slash; 759 for (int i = 0; i < slashCount; i++) 760 path.append(upDir); 761 path.append(srcFile.substring(branchPoint)); 762 return path.toString(); 763 } 764 } 765 766 767 return srcFile; 768 769 } 770 771 777 private String convertToAbsolute(String fileName) throws IOException { 778 782 File file = new File (fileName); 783 784 if (!file.isAbsolute()) { 785 File projectFile = new File (projectFileName); 788 if (projectFile.isAbsolute()) { 789 String base = new File (projectFileName).getParent(); 791 792 fileName = new File (base, fileName).getCanonicalPath(); 794 } 795 } 796 return fileName; 797 } 798 799 803 private static String makeAbsoluteCWD(String fileName) { 804 File file = new File (fileName); 805 boolean hasProtocol = (URLClassPath.getURLProtocol(fileName) != null); 806 if (!hasProtocol && !file.isAbsolute()) 807 fileName = file.getAbsolutePath(); 808 return fileName; 809 } 810 811 820 private boolean addToListInternal(List <String > list, String value) { 821 if (!list.contains(value)) { 822 list.add(value); 823 isModified = true; 824 return true; 825 } else 826 return false; 827 } 828 829 833 private void makeListAbsoluteProject(List <String > list) throws IOException { 834 List <String > replace = new LinkedList <String >(); 835 for (String fileName : list) { 836 fileName = convertToAbsolute(fileName); 837 replace.add(fileName); 838 } 839 840 list.clear(); 841 list.addAll(replace); 842 } 843 844 847 public void setTimestamp(long timestamp) { 848 this.timestamp = timestamp; 849 } 850 851 public void addTimestamp(long timestamp) { 852 if (this.timestamp < timestamp) 853 this.timestamp = timestamp; 854 } 855 858 public long getTimestamp() { 859 return timestamp; 860 } 861 } 862 863 | Popular Tags |