1 18 package edu.umd.cs.findbugs.workflow; 19 20 import java.io.File ; 21 import java.io.IOException ; 22 import java.util.Comparator ; 23 import java.util.HashMap ; 24 import java.util.HashSet ; 25 import java.util.Iterator ; 26 import java.util.LinkedList ; 27 import java.util.TreeMap ; 28 29 import org.dom4j.DocumentException; 30 31 import edu.umd.cs.findbugs.AppVersion; 32 import edu.umd.cs.findbugs.BugDesignation; 33 import edu.umd.cs.findbugs.TigerSubstitutes; 34 import edu.umd.cs.findbugs.BugCollection; 35 import edu.umd.cs.findbugs.BugInstance; 36 import edu.umd.cs.findbugs.ClassAnnotation; 37 import edu.umd.cs.findbugs.DetectorFactoryCollection; 38 import edu.umd.cs.findbugs.Project; 39 import edu.umd.cs.findbugs.SortedBugCollection; 40 import edu.umd.cs.findbugs.VersionInsensitiveBugComparator; 41 import edu.umd.cs.findbugs.config.CommandLine; 42 import edu.umd.cs.findbugs.model.MovedClassMap; 43 44 50 51 public class Update { 52 53 56 private static final String USAGE = "Usage: " + Update.class.getName() 57 + " [options] data1File data2File data3File ... "; 58 59 private HashMap <BugInstance, BugInstance> mapFromNewToOldBug = new HashMap <BugInstance, BugInstance>(); 60 61 private HashSet <BugInstance> matchedOldBugs = new HashSet <BugInstance>(); 62 63 boolean noPackageMoves = false; 64 65 boolean preciseMatch = false; 66 boolean precisePriorityMatch = false; 67 68 class UpdateCommandLine extends CommandLine { 69 boolean overrideRevisionNames = false; 70 71 String outputFilename; 72 73 UpdateCommandLine() { 74 addSwitch("-overrideRevisionNames", 75 "override revision names for each version with names computed filenames"); 76 addSwitch( 77 "-noPackageMoves", 78 "if a class seems to have moved from one package to another, treat warnings in that class as two seperate warnings"); 79 addSwitch("-preciseMatch", 80 "require bug patterns to match precisely"); 81 addSwitch("-precisePriorityMatch", 82 "only consider two warnings to be the same if their priorities match exactly"); 83 addOption("-output", "output file", 84 "explicit filename for merged results (standard out used if not specified)"); 85 addSwitch("-quiet", 86 "don't generate any outout to standard out unless there is an error"); 87 88 } 89 90 @Override 91 protected void handleOption(String option, String optionExtraPart) 92 throws IOException { 93 if (option.equals("-overrideRevisionNames")) { 94 if (optionExtraPart.length() == 0) 95 overrideRevisionNames = true; 96 else 97 overrideRevisionNames = TigerSubstitutes 98 .parseBoolean(optionExtraPart); 99 } else if (option.equals("-noPackageMoves")) { 100 if (optionExtraPart.length() == 0) 101 noPackageMoves = true; 102 else 103 noPackageMoves = TigerSubstitutes 104 .parseBoolean(optionExtraPart); 105 } else if (option.equals("-preciseMatch")) { 106 preciseMatch = true; 107 } else if (option.equals("-precisePriorityMatch")) { 108 versionInsensitiveBugComparator.setComparePriorities(true); 109 fuzzyBugPatternMatcher.setComparePriorities(true); 110 precisePriorityMatch = true; 111 } else if (option.equals("-quiet")) 112 verbose = false; 113 else 114 throw new IllegalArgumentException ("no option " + option); 115 116 } 117 118 @Override 119 protected void handleOptionWithArgument(String option, String argument) 120 throws IOException { 121 if (option.equals("-output")) 122 outputFilename = argument; 123 else 124 throw new IllegalArgumentException ("Can't handle option " 125 + option); 126 127 } 128 129 } 130 131 VersionInsensitiveBugComparator versionInsensitiveBugComparator = new VersionInsensitiveBugComparator(); 132 133 VersionInsensitiveBugComparator fuzzyBugPatternMatcher = new VersionInsensitiveBugComparator(); 134 { 135 fuzzyBugPatternMatcher.setExactBugPatternMatch(false); 136 } 137 138 public BugCollection mergeCollections(BugCollection origCollection, 139 BugCollection newCollection, boolean copyDeadBugs) { 140 141 mapFromNewToOldBug.clear(); 142 143 matchedOldBugs.clear(); 144 BugCollection resultCollection = newCollection 145 .createEmptyCollectionWithMetadata(); 146 long lastSequence = origCollection.getSequenceNumber(); 148 resultCollection.clearAppVersions(); 152 for (Iterator <AppVersion> i = origCollection.appVersionIterator(); i 153 .hasNext();) { 154 AppVersion appVersion = i.next(); 155 resultCollection.addAppVersion((AppVersion) appVersion.clone()); 156 } 157 AppVersion origCollectionVersion = new AppVersion(lastSequence); 160 origCollectionVersion.setTimestamp(origCollection 161 .getCurrentAppVersion().getTimestamp()); 162 origCollectionVersion.setReleaseName(origCollection 163 .getCurrentAppVersion().getReleaseName()); 164 origCollectionVersion.setNumClasses(origCollection.getProjectStats() 165 .getNumClasses()); 166 origCollectionVersion.setCodeSize(origCollection.getProjectStats() 167 .getCodeSize()); 168 169 resultCollection.addAppVersion(origCollectionVersion); 170 171 long currentSequence = origCollection.getSequenceNumber() + 1; 174 resultCollection.setSequenceNumber(currentSequence); 175 176 int oldBugs = 0; 177 if (copyDeadBugs) 179 for (BugInstance bug : origCollection.getCollection()) 180 if (bug.getLastVersion() != -1) { 181 oldBugs++; 182 BugInstance newBug = (BugInstance) bug.clone(); 183 resultCollection.add(newBug, false); 184 } 185 186 matchBugs(SortedBugCollection.BugInstanceComparator.instance, 187 origCollection, newCollection); 188 matchBugs(versionInsensitiveBugComparator, origCollection, 189 newCollection); 190 if (!preciseMatch) { 191 matchBugs(fuzzyBugPatternMatcher, origCollection, newCollection); 192 } 193 if (!noPackageMoves) { 194 VersionInsensitiveBugComparator movedBugComparator = new VersionInsensitiveBugComparator(); 195 MovedClassMap movedClassMap = new MovedClassMap( 196 origCollection, newCollection).execute(); 197 if (!movedClassMap.isEmpty()) { 198 movedBugComparator.setClassNameRewriter(movedClassMap); 199 movedBugComparator.setComparePriorities(precisePriorityMatch); 200 matchBugs(movedBugComparator, origCollection, newCollection); 201 if (!preciseMatch) { 202 movedBugComparator.setExactBugPatternMatch(false); 203 matchBugs(movedBugComparator, origCollection, newCollection); 204 } 205 } 206 } 207 208 210 int newlyDeadBugs = 0; 211 int persistantBugs = 0; 212 int addedBugs = 0; 213 int addedInNewCode = 0; 214 int deadBugInDeadCode = 0; 215 216 if (copyDeadBugs) 218 for (BugInstance bug : origCollection.getCollection()) 219 if (!matchedOldBugs.contains(bug)) { 220 if (bug.getLastVersion() == -1) 221 newlyDeadBugs++; 222 else 223 oldBugs++; 224 BugInstance newBug = (BugInstance) bug.clone(); 225 226 if (newBug.getLastVersion() == -1) { 227 newBug.setLastVersion(lastSequence); 228 ClassAnnotation classBugFoundIn = bug.getPrimaryClass(); 229 String className = classBugFoundIn.getClassName(); 230 if (newCollection.getProjectStats().getClassStats( 231 className) != null) 232 newBug.setRemovedByChangeOfPersistingClass(true); 233 else 234 deadBugInDeadCode++; 235 } 236 237 if (newBug.getFirstVersion() > newBug.getLastVersion()) 238 throw new IllegalStateException ( 239 "Illegal Version range: " 240 + newBug.getFirstVersion() + ".." 241 + newBug.getLastVersion()); 242 resultCollection.add(newBug, false); 243 } 244 for (BugInstance bug : newCollection.getCollection()) { 246 BugInstance newBug = (BugInstance) bug.clone(); 247 if (mapFromNewToOldBug.containsKey(bug)) { 248 BugInstance origWarning = mapFromNewToOldBug.get(bug); 249 assert origWarning.getLastVersion() == -1; 250 251 copyBugHistory(origWarning, newBug); 252 BugDesignation designation = newBug.getUserDesignation(); 255 if (designation != null) 256 designation.merge(origWarning.getUserDesignation()); 257 else 258 newBug.setUserDesignation(origWarning.getUserDesignation()); 260 persistantBugs++; 261 } else { 262 newBug.setFirstVersion(lastSequence + 1); 263 addedBugs++; 264 265 ClassAnnotation classBugFoundIn = bug.getPrimaryClass(); 266 267 String className = classBugFoundIn.getClassName(); 268 if (origCollection.getProjectStats().getClassStats(className) != null) { 269 newBug.setIntroducedByChangeOfExistingClass(true); 270 } else 274 addedInNewCode++; 275 } 276 assert newBug.getLastVersion() == -1; 277 if (newBug.getLastVersion() != -1) 278 throw new IllegalStateException ("Illegal Version range: " 279 + newBug.getFirstVersion() + ".." 280 + newBug.getLastVersion()); 281 int oldSize = resultCollection.getCollection().size(); 282 resultCollection.add(newBug, false); 283 int newSize = resultCollection.getCollection().size(); 284 if (newSize != oldSize + 1) { 285 System.out.println("Failed to add bug #" + newBug.getUniqueId() 286 + " : " + newBug.getMessage()); 287 } 288 } 289 if (false && verbose) { 290 System.out.println(origCollection.getCollection().size() 291 + " orig bugs, " + newCollection.getCollection().size() 292 + " new bugs"); 293 System.out.println("Bugs: " + oldBugs + " old, " 294 + deadBugInDeadCode + " in removed code, " 295 + (newlyDeadBugs - deadBugInDeadCode) + " died, " 296 + persistantBugs + " persist, " + addedInNewCode 297 + " in new code, " + (addedBugs - addedInNewCode) 298 + " added"); 299 System.out.println(resultCollection.getCollection().size() 300 + " resulting bugs"); 301 } 302 return resultCollection; 303 304 } 305 306 boolean verbose = true; 307 308 public static String [] getFilePathParts(String filePath) { 309 String regex = (File.separatorChar=='\\' ? "\\\\" : File.separator); 310 return filePath.split(regex); 311 } 312 313 public static void main(String [] args) throws IOException , 314 DocumentException { 315 new Update().doit(args); 316 } 317 318 public void doit(String [] args) throws IOException , DocumentException { 319 320 DetectorFactoryCollection.instance(); 321 UpdateCommandLine commandLine = new UpdateCommandLine(); 322 int argCount = commandLine.parse(args, 2, Integer.MAX_VALUE, USAGE); 323 324 if (commandLine.outputFilename == null) 325 verbose = false; 326 String [] firstPathParts = getFilePathParts(args[argCount]); 327 int commonPrefix = firstPathParts.length; 328 for (int i = argCount + 1; i <= (args.length - 1); i++) { 329 330 commonPrefix = Math.min(commonPrefix, lengthCommonPrefix( 331 firstPathParts, getFilePathParts(args[i]))); 332 } 333 334 String origFilename = args[argCount++]; 335 Project project = new Project(); 336 BugCollection origCollection; 337 origCollection = new SortedBugCollection(); 338 if (verbose) 339 System.out.println("Starting with " + origFilename); 340 341 origCollection.readXML(origFilename, project); 342 343 if (commandLine.overrideRevisionNames 344 || origCollection.getReleaseName() == null 345 || origCollection.getReleaseName().length() == 0) 346 origCollection.setReleaseName(firstPathParts[commonPrefix]); 347 for (BugInstance bug : origCollection.getCollection()) 348 if (bug.getLastVersion() >= 0 349 && bug.getFirstVersion() > bug.getLastVersion()) 350 throw new IllegalStateException ("Illegal Version range: " 351 + bug.getFirstVersion() + ".." + bug.getLastVersion()); 352 353 while (argCount <= (args.length - 1)) { 354 355 BugCollection newCollection = new SortedBugCollection(); 356 357 String newFilename = args[argCount++]; 358 if (verbose) 359 System.out.println("Merging " + newFilename); 360 project = new Project(); 361 try { 362 File f = new File (newFilename); 363 if (f.length() == 0) { 364 if (verbose) 365 System.out.println("Empty input file: " + f); 366 continue; 367 } 368 newCollection.readXML(newFilename, project); 369 370 if (commandLine.overrideRevisionNames 371 || newCollection.getReleaseName() == null 372 || newCollection.getReleaseName().length() == 0) 373 newCollection 374 .setReleaseName(getFilePathParts(newFilename)[commonPrefix]); 375 376 origCollection = mergeCollections(origCollection, 377 newCollection, true); 378 } catch (IOException e) { 379 if (verbose) 380 System.out.println(e); 381 else 382 throw e; 383 } 384 } 385 386 if (commandLine.outputFilename != null) 387 origCollection.writeXML(commandLine.outputFilename, project); 388 else 389 origCollection.writeXML(System.out, project); 390 391 } 392 393 private static int lengthCommonPrefix(String [] string, String [] string2) { 394 int maxLength = Math.min(string.length, string2.length); 395 for (int result = 0; result < maxLength; result++) 396 if (!string[result].equals(string2[result])) 397 return result; 398 return maxLength; 399 } 400 401 private static void copyBugHistory(BugInstance src, BugInstance dest) { 402 403 dest.setFirstVersion(src.getFirstVersion()); 404 dest.setLastVersion(src.getLastVersion()); 405 dest.setIntroducedByChangeOfExistingClass(src 406 .isIntroducedByChangeOfExistingClass()); 407 dest.setRemovedByChangeOfPersistingClass(src 408 .isRemovedByChangeOfPersistingClass()); 409 } 410 411 private void matchBugs(Comparator <BugInstance> bugInstanceComparator, 412 BugCollection origCollection, BugCollection newCollection) { 413 414 TreeMap <BugInstance, LinkedList <BugInstance>> set = new TreeMap <BugInstance, LinkedList <BugInstance>>( 415 bugInstanceComparator); 416 int oldBugs = 0; 417 int newBugs = 0; 418 int matchedBugs = 0; 419 for (BugInstance bug : origCollection.getCollection()) 420 if (bug.getLastVersion() == -1 && !matchedOldBugs.contains(bug)) { 421 oldBugs++; 422 LinkedList <BugInstance> q = set.get(bug); 423 if (q == null) { 424 q = new LinkedList <BugInstance>(); 425 set.put(bug, q); 426 } 427 q.add(bug); 428 } 429 for (BugInstance bug : newCollection.getCollection()) 430 if (!mapFromNewToOldBug.containsKey(bug)) { 431 newBugs++; 432 LinkedList <BugInstance> q = set.get(bug); 433 if (q != null && !q.isEmpty()) { 434 matchedBugs++; 435 BugInstance matchedBug = q.removeFirst(); 436 mapFromNewToOldBug.put(bug, matchedBug); 437 matchedOldBugs.add(matchedBug); 438 } 439 } 440 if (false && verbose) 441 System.out.println("matched " + matchedBugs + " of " + oldBugs 442 + "o/" + newBugs + "n bugs using " 443 + bugInstanceComparator.getClass().getName()); 444 } 445 446 } 447 | Popular Tags |