1 18 19 package edu.umd.cs.findbugs.workflow; 20 21 import java.beans.DesignMode ; 22 import java.io.IOException ; 23 import java.io.InputStream ; 24 import java.lang.reflect.Field ; 25 import java.util.Date ; 26 import java.util.HashMap ; 27 import java.util.HashSet ; 28 import java.util.Iterator ; 29 import java.util.Map ; 30 import java.util.Set ; 31 import java.util.SortedMap ; 32 import java.util.TreeMap ; 33 import java.util.regex.Pattern ; 34 35 import edu.umd.cs.findbugs.AppVersion; 36 import edu.umd.cs.findbugs.BugCategory; 37 import edu.umd.cs.findbugs.BugCollection; 38 import edu.umd.cs.findbugs.BugInstance; 39 import edu.umd.cs.findbugs.BugPattern; 40 import edu.umd.cs.findbugs.DetectorFactoryCollection; 41 import edu.umd.cs.findbugs.I18N; 42 import edu.umd.cs.findbugs.Project; 43 import edu.umd.cs.findbugs.SortedBugCollection; 44 import edu.umd.cs.findbugs.SourceLineAnnotation; 45 import edu.umd.cs.findbugs.TigerSubstitutes; 46 import edu.umd.cs.findbugs.ba.SourceFinder; 47 import edu.umd.cs.findbugs.config.CommandLine; 48 import edu.umd.cs.findbugs.filter.FilterException; 49 import edu.umd.cs.findbugs.filter.Matcher; 50 51 57 public class Filter { 58 static class FilterCommandLine extends CommandLine { 59 62 public static final long MILLISECONDS_PER_DAY = 24*60*60*1000L; 63 Pattern className,bugPattern; 64 public boolean notSpecified = false; 65 public boolean not = false; 66 long first; 67 String firstAsString; 68 long after; 69 String afterAsString; 70 long before; 71 String beforeAsString; 72 73 long last; 74 String lastAsString; 75 String fixedAsString; long present; 77 String presentAsString; 78 long absent; 79 String absentAsString; 80 String annotation; 81 public boolean activeSpecified = false; 82 public boolean active = false; 83 84 public boolean withSource = false; 85 public boolean withSourceSpecified = false; 86 public boolean introducedByChange = false; 87 public boolean introducedByChangeSpecified = false; 88 89 public boolean removedByChange = false; 90 public boolean removedByChangeSpecified = false; 91 92 public boolean newCode = false; 93 public boolean newCodeSpecified = false; 94 95 public boolean hashChanged = false; 96 public boolean hashChangedSpecified = false; 97 98 public boolean removedCode = false; 99 public boolean removedCodeSpecified = false; 100 101 public boolean classified = false; 102 public boolean classifiedSpecified = false; 103 104 105 public boolean withMessagesSpecified = false; 106 public boolean withMessages = false; 107 public boolean serious = false; 108 public boolean seriousSpecified = false; 109 110 private Matcher includeFilter, excludeFilter; 111 String designationString; 112 String designationKey; 113 String categoryString; 114 String categoryKey; 115 int priority = 3; 116 117 FilterCommandLine() { 118 119 addSwitch("-not", "reverse (all) switches for the filter"); 120 addSwitchWithOptionalExtraPart("-withSource", "truth", "only warnings for switch source is available"); 121 addSwitchWithOptionalExtraPart("-hashChanged", "truth", "only warnings for which the stored hash is not the same as the calculated hash"); 122 addOption("-exclude", "filter file", "exclude bugs matching given filter"); 123 addOption("-include", "filter file", "include only bugs matching given filter"); 124 125 addOption("-annotation", "text", "allow only warnings containing this text in an annotation"); 126 addSwitchWithOptionalExtraPart("-classified", "truth", "allow only classified warnings"); 127 addSwitchWithOptionalExtraPart("-withMessages", "truth", "generated XML should contain textual messages"); 128 129 addSwitchWithOptionalExtraPart("-serious", "truth", "allow only warnings classified as serious"); 130 131 addOption("-after", "when", "allow only warnings that first occurred after this version"); 132 addOption("-before", "when", "allow only warnings that first occurred before this version"); 133 addOption("-first", "when", "allow only warnings that first occurred in this version"); 134 addOption("-last", "when", "allow only warnings that last occurred in this version"); 135 addOption("-fixed", "when", "allow only warnings that last occurred in the previous version (clobbers last)"); 136 addOption("-present", "when", "allow only warnings present in this version"); 137 addOption("-absent", "when", "allow only warnings absent in this version"); 138 addSwitchWithOptionalExtraPart("-active", "truth", "allow only warnings alive in the last sequence number"); 139 140 addSwitchWithOptionalExtraPart("-introducedByChange", "truth", 141 "allow only warnings introduced by a change of an existing class"); 142 addSwitchWithOptionalExtraPart("-removedByChange", "truth", 143 "allow only warnings removed by a change of a persisting class"); 144 addSwitchWithOptionalExtraPart("-newCode", "truth", 145 "allow only warnings introduced by the addition of a new class"); 146 addSwitchWithOptionalExtraPart("-removedCode", "truth", 147 "allow only warnings removed by removal of a class"); 148 addOption("-priority", "level", "allow only warnings with this priority or higher"); 149 addOption("-class", "pattern", "allow only bugs whose primary class name matches this pattern"); 150 addOption("-bugPattern", "pattern", "allow only bugs whose type matches this pattern"); 151 addOption("-category", "category", "allow only warnings with a category that starts with this string"); 152 addOption("-designation", "designation", "allow only warnings with this designation (e.g., -designation SHOULD_FIX)"); 153 154 } 155 156 static long getVersionNum(Map <String , AppVersion> versions, 157 SortedMap <Long , AppVersion> timeStamps , String val, 158 boolean roundToLaterVersion, long numVersions) { 159 if (val == null) return -1; 160 if (val.equals("last") || val.equals("lastVersion")) return numVersions -1; 161 162 AppVersion v = versions.get(val); 163 if (v != null) return v.getSequenceNumber(); 164 try { 165 long time = 0; 166 if (val.endsWith("daysAgo")) 167 time = System.currentTimeMillis() - MILLISECONDS_PER_DAY * Integer.parseInt(val.substring(0, val.length() - 7)); 168 else time = Date.parse(val); 169 return getAppropriateSeq(timeStamps, time, roundToLaterVersion); 170 } catch (Exception e) { 171 try { 172 long version = Long.parseLong(val); 173 if (version < 0) { 174 version = numVersions + version; 175 } 176 return version; 177 } 178 catch (NumberFormatException e1) { 179 throw new IllegalArgumentException ("Could not interpret version specification of '" + val + "'"); 180 } 181 } 182 } 183 184 static private long getAppropriateSeq(SortedMap <Long , AppVersion> timeStamps, long when, boolean roundToLaterVersion) { 188 if (roundToLaterVersion) { 189 SortedMap <Long , AppVersion> geq = timeStamps.tailMap(when); 190 if (geq.isEmpty()) return Long.MAX_VALUE; 191 return geq.get(geq.firstKey()).getSequenceNumber(); 192 } else { 193 SortedMap <Long , AppVersion> leq = timeStamps.headMap(when); 194 if (leq.isEmpty()) return Long.MIN_VALUE; 195 return leq.get(leq.lastKey()).getSequenceNumber(); 196 } 197 } 198 199 void adjustFilter(BugCollection collection) { 200 Map <String , AppVersion> versions = new HashMap <String , AppVersion>(); 201 SortedMap <Long , AppVersion> timeStamps = new TreeMap <Long , AppVersion>(); 202 203 for(Iterator <AppVersion> i = collection.appVersionIterator(); i.hasNext(); ) { 204 AppVersion v = i.next(); 205 versions.put(v.getReleaseName(), v); 206 timeStamps.put(v.getTimestamp(), v); 207 } 208 AppVersion v = collection.getCurrentAppVersion(); 210 versions.put(v.getReleaseName(), v); 211 timeStamps.put(v.getTimestamp(), v); 212 213 first = getVersionNum(versions, timeStamps, firstAsString, true, v.getSequenceNumber()); 214 last = getVersionNum(versions, timeStamps, lastAsString, true, v.getSequenceNumber()); 215 before = getVersionNum(versions, timeStamps, beforeAsString, true, v.getSequenceNumber()); 216 after = getVersionNum(versions, timeStamps, afterAsString, false, v.getSequenceNumber()); 217 present = getVersionNum(versions, timeStamps, presentAsString, true, v.getSequenceNumber()); 218 absent = getVersionNum(versions, timeStamps, absentAsString, true, v.getSequenceNumber()); 219 220 long fixed = getVersionNum(versions, timeStamps, fixedAsString, true, v.getSequenceNumber()); 221 if (fixed >= 0) last = fixed - 1; } 223 224 boolean accept(BugInstance bug) { 225 boolean result = evaluate(bug); 226 if (not) return !result; 227 return result; 228 } 229 boolean evaluate(BugInstance bug) { 230 231 if (includeFilter != null && !includeFilter.match(bug)) return false; 232 if (excludeFilter != null && excludeFilter.match(bug)) return false; 233 if (annotation != null && bug.getAnnotationText().indexOf(annotation) == -1) 234 return false; 235 if (bug.getPriority() > priority) 236 return false; 237 if (firstAsString != null && bug.getFirstVersion() != first) 238 return false; 239 if (afterAsString != null && bug.getFirstVersion() <= after) 240 return false; 241 if (beforeAsString != null && bug.getFirstVersion() >= before) 242 return false; 243 if ((lastAsString != null || fixedAsString != null) && (last < 0 || bug.getLastVersion() != last)) 244 return false; 245 if (presentAsString != null && !bugLiveAt(bug, present)) 246 return false; 247 if (absentAsString != null && bugLiveAt(bug, absent)) 248 return false; 249 250 if (activeSpecified && active != (bug.getLastVersion() == -1)) 251 return false; 252 if (removedByChangeSpecified 253 && bug.isRemovedByChangeOfPersistingClass() != removedByChange) 254 return false; 255 if (introducedByChangeSpecified 256 && bug.isIntroducedByChangeOfExistingClass() != introducedByChange) 257 return false; 258 if (newCodeSpecified && newCode != (!bug.isIntroducedByChangeOfExistingClass() && bug.getFirstVersion() != 0)) 259 return false; 260 if (removedCodeSpecified && removedCode != (!bug.isRemovedByChangeOfPersistingClass() && bug.getLastVersion() != -1)) 261 return false; 262 263 if (bugPattern != null && !bugPattern.matcher(bug.getType()).find()) 264 return false; 265 if (className != null && !className.matcher(bug.getPrimaryClass().getClassName()).find()) 266 return false; 267 268 BugPattern thisBugPattern = bug.getBugPattern(); 269 if (categoryKey != null && thisBugPattern != null && !categoryKey.equals(thisBugPattern.getCategory())) 270 return false; 271 if (designationKey != null && !designationKey.equals(bug.getUserDesignationKey())) 272 return false; 273 274 if (withSourceSpecified) { 275 if (sourceSearcher.findSource(bug.getPrimarySourceLineAnnotation()) != withSource) 276 return false; 277 } 278 if (hashChangedSpecified) { 279 if (bug.isInstanceHashConsistent() == hashChanged) 280 return false; 281 } 282 283 if (classifiedSpecified && classified != isClassified(bug)) { 284 return false; 285 } 286 287 if (seriousSpecified) { 288 Set <String > words = bug.getTextAnnotationWords(); 289 boolean thisOneIsSerious = words.contains("BUG") 290 && !(words.contains("NOT_BUG") || words.contains("HARMLESS")); 291 if (serious != thisOneIsSerious) return false; 292 } 293 294 return true; 295 } 296 297 private boolean isClassified(BugInstance bug) { 298 Set <String > words = bug.getTextAnnotationWords(); 299 return words.contains("BUG") || words.contains("NOT_BUG"); 300 } 301 302 private boolean bugLiveAt(BugInstance bug, long now) { 303 if (now < bug.getFirstVersion()) 304 return false; 305 if (bug.getLastVersion() != -1 && bug.getLastVersion() < now) 306 return false; 307 return true; 308 } 309 310 @Override 311 protected void handleOption(String option, String optionExtraPart) throws IOException { 312 option = option.substring(1); 313 if (optionExtraPart.length() == 0) 314 setField(option, true); 315 else 316 setField(option, TigerSubstitutes.parseBoolean(optionExtraPart)); 317 setField(option+"Specified", true); 318 } 319 320 private void setField(String fieldName, boolean value) { 321 try { 322 Field f = FilterCommandLine.class.getField(fieldName); 323 f.setBoolean(this, value); 324 } catch (RuntimeException e) { 325 throw e; 326 } catch (Exception e) { 327 throw new RuntimeException (e); 328 } 329 330 } 331 @Override 332 protected void handleOptionWithArgument(String option, String argument) throws IOException { 333 334 if (option.equals("-priority")) { 335 priority = parsePriority(argument); 336 } 337 338 339 else if (option.equals("-first")) 340 firstAsString = argument; 341 else if (option.equals("-last")) 342 lastAsString = argument; 343 else if (option.equals("-fixed")) 344 fixedAsString = argument; 345 else if (option.equals("-after")) 346 afterAsString = argument; 347 else if (option.equals("-before")) 348 beforeAsString = argument; 349 else if (option.equals("-present")) 350 presentAsString = argument; 351 else if (option.equals("-absent")) 352 absentAsString = argument; 353 354 else if (option.equals("-category")) 355 categoryString = argument; 356 else if (option.equals("-designation")) 357 designationString = argument; 358 else if (option.equals("-class")) 359 className = Pattern.compile(argument); 360 else if (option.equals("-bugPattern")) 361 bugPattern = Pattern.compile(argument); 362 else if (option.equals("-annotation")) 363 annotation = argument; 364 else if (option.equals("-include")) { 365 try { 366 includeFilter = new edu.umd.cs.findbugs.filter.Filter(argument); 367 } catch (FilterException e) { 368 throw new IllegalArgumentException ("Error processing include file: " + argument, e); 369 } 370 } else if (option.equals("-exclude")) { 371 try { 372 excludeFilter = new edu.umd.cs.findbugs.filter.Filter(argument); 373 } catch (FilterException e) { 374 throw new IllegalArgumentException ("Error processing include file: " + argument, e); 375 } 376 } else throw new IllegalArgumentException ("can't handle command line argument of " + option); 377 } 378 379 380 381 } 382 public static int parsePriority(String argument) { 383 int i = " HMLE".indexOf(argument); 384 if (i == -1) 385 i = " 1234".indexOf(argument); 386 if (i == -1) 387 throw new IllegalArgumentException ("Bad priority: " + argument); 388 return i; 389 } 390 391 static SourceSearcher sourceSearcher; 392 393 public static void main(String [] args) throws Exception { 394 DetectorFactoryCollection.instance(); 395 final FilterCommandLine commandLine = new FilterCommandLine(); 396 397 int argCount = commandLine.parse(args, 0, 2, "Usage: " + Filter.class.getName() 398 + " [options] [<orig results> [<new results]] "); 399 Project project = new Project(); 400 SortedBugCollection origCollection = new SortedBugCollection(); 401 402 if (argCount == args.length) 403 origCollection.readXML(System.in, project); 404 else 405 origCollection.readXML(args[argCount++], project); 406 boolean verbose = argCount < args.length; 407 I18N i18n = I18N.instance(); 408 if (commandLine.categoryString != null) { 409 for (BugCategory bugCategory : i18n 410 .getBugCategoryObjects()) 411 if (bugCategory.getAbbrev().equals(commandLine.categoryString)) { 412 commandLine.categoryKey = bugCategory.getCategory(); 413 break; 414 } 415 if (commandLine.categoryKey == null) 416 for (BugCategory bugCategory : i18n 417 .getBugCategoryObjects()) 418 if (bugCategory.getAbbrev().startsWith( 419 commandLine.categoryString)) { 420 commandLine.categoryKey = bugCategory.getCategory(); 421 break; 422 } 423 } 424 425 if (commandLine.designationString != null) { 426 for (String designationKey : i18n.getUserDesignationKeys()) { 427 if (designationKey.startsWith(commandLine.designationString) 428 || i18n.getUserDesignation(designationKey).startsWith(commandLine.designationString)) { 429 commandLine.designationKey = designationKey; 430 break; 431 } 432 433 } 434 } 435 SortedBugCollection resultCollection = origCollection.createEmptyCollectionWithMetadata(); 436 int passed = 0; 437 int dropped = 0; 438 resultCollection.setWithMessages(commandLine.withMessages); 439 if (commandLine.hashChangedSpecified) 440 origCollection.computeBugHashes(); 441 commandLine.adjustFilter(resultCollection); 442 resultCollection.getProjectStats().clearBugCounts(); 443 sourceSearcher = new SourceSearcher(project); 444 for (BugInstance bug : origCollection.getCollection()) 445 if (commandLine.accept(bug)) { 446 resultCollection.add(bug, false); 447 if (bug.getLastVersion() == -1 ) 448 resultCollection.getProjectStats().addBug(bug); 449 passed++; 450 } else 451 dropped++; 452 453 if (verbose) 454 System.out.println(passed + " warnings passed through, " + dropped 455 + " warnings dropped"); 456 if (argCount == args.length) { 457 assert !verbose; 458 resultCollection.writeXML(System.out, project); 459 } 460 else { 461 resultCollection.writeXML(args[argCount++], project); 462 463 } 464 465 } 466 467 } 468 469 | Popular Tags |