/*
* FindBugs - Find bugs in Java programs
* Copyright (C) 2003-2005 William Pugh
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package edu.umd.cs.findbugs.workflow;
import java.io.BufferedReader;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import org.dom4j.DocumentException;
import edu.umd.cs.findbugs.AppVersion;
import edu.umd.cs.findbugs.BugCategory;
import edu.umd.cs.findbugs.BugCollection;
import edu.umd.cs.findbugs.BugInstance;
import edu.umd.cs.findbugs.BugPattern;
import edu.umd.cs.findbugs.BugRanker;
import edu.umd.cs.findbugs.DetectorFactoryCollection;
import edu.umd.cs.findbugs.ExcludingHashesBugReporter;
import edu.umd.cs.findbugs.FieldAnnotation;
import edu.umd.cs.findbugs.FindBugs;
import edu.umd.cs.findbugs.I18N;
import edu.umd.cs.findbugs.MethodAnnotation;
import edu.umd.cs.findbugs.PackageStats;
import edu.umd.cs.findbugs.PackageStats.ClassStats;
import edu.umd.cs.findbugs.Project;
import edu.umd.cs.findbugs.ProjectStats;
import edu.umd.cs.findbugs.SloppyBugComparator;
import edu.umd.cs.findbugs.SortedBugCollection;
import edu.umd.cs.findbugs.SourceLineAnnotation;
import edu.umd.cs.findbugs.charsets.UTF8;
import edu.umd.cs.findbugs.cloud.Cloud;
import edu.umd.cs.findbugs.cloud.Cloud.SigninState;
import edu.umd.cs.findbugs.config.CommandLine;
import edu.umd.cs.findbugs.filter.FilterException;
import edu.umd.cs.findbugs.filter.Matcher;
import edu.umd.cs.findbugs.util.Util;
/**
* Java main application to filter/transform an XML bug collection or bug
* history collection.
*
* @author William Pugh
*/
public class Filter {
static class FilterCommandLine extends CommandLine {
/**
*
*/
public static final long MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000L;
Pattern classPattern, bugPattern, callsPattern;
public boolean notSpecified = false;
public boolean not = false;
int duration;
long first;
String firstAsString;
long after;
String afterAsString;
long before;
String beforeAsString;
int maxRank = Integer.MAX_VALUE;
long maybeMutated;
String maybeMutatedAsString;
long last;
String lastAsString;
String trimToVersionAsString;
String fixedAsString; // alternate way to specify 'last'
long present;
String presentAsString;
long absent;
String absentAsString;
String annotation;
HashSet<String> hashesFromFile;
public boolean sloppyUniqueSpecified = false;
public boolean sloppyUnique = false;
public boolean purgeHistorySpecified = false;
public boolean purgeHistory = false;
public boolean activeSpecified = false;
public boolean active = false;
public boolean notAProblem = false;
public boolean notAProblemSpecified = false;
public boolean shouldFix = false;
public boolean shouldFixSpecified = false;
public boolean hasField = false;
public boolean hasFieldSpecified = false;
public boolean hasLocal = false;
public boolean hasLocalSpecified = false;
public boolean applySuppression = false;
public boolean applySuppressionSpecified = false;
public boolean withSource = false;
public boolean withSourceSpecified = false;
public boolean knownSource = false;
public boolean knownSourceSpecified = false;
public boolean introducedByChange = false;
public boolean introducedByChangeSpecified = false;
public boolean removedByChange = false;
public boolean removedByChangeSpecified = false;
public boolean newCode = false;
public boolean newCodeSpecified = false;
public boolean hashChanged = false;
public boolean hashChangedSpecified = false;
public boolean removedCode = false;
public boolean removedCodeSpecified = false;
public boolean dontUpdateStats = false;
public boolean dontUpdateStatsSpecified = false;
public int maxAge = 0;
public boolean maxAgeSpecified = false;
public boolean withMessagesSpecified = false;
public boolean withMessages = false;
private final List<Matcher> includeFilter = new LinkedList<Matcher>();
private final List<Matcher> excludeFilter = new LinkedList<Matcher>();
HashSet<String> excludedInstanceHashes = new HashSet<String>();
Set<String> designationKey = new HashSet<String>();
Set<String> categoryKey = new HashSet<String>();
SortedSet<BugInstance> uniqueSloppy;
int priority = 3;
FilterCommandLine() {
addSwitch("-not", "reverse (all) switches for the filter");
addSwitchWithOptionalExtraPart("-knownSource", "trurh", "Only issues that have known source locations");
addSwitchWithOptionalExtraPart("-withSource", "truth", "only warnings for which source is available");
addSwitchWithOptionalExtraPart("-hashChanged", "truth",
"only warnings for which the stored hash is not the same as the calculated hash");
addOption("-excludeBugs", "baseline bug collection", "exclude bugs already contained in the baseline bug collection");
addOption("-exclude", "filter file", "exclude bugs matching given filter");
addOption("-include", "filter file", "include only bugs matching given filter");
addOption("-annotation", "text", "allow only warnings containing this text in a user annotation");
addSwitchWithOptionalExtraPart("-withMessages", "truth", "generated XML should contain textual messages");
addOption("-maxDuration", "# versions", "only issues present in at most this many versions");
addOption("-after", "when", "allow only warnings that first occurred after this version");
addOption("-before", "when", "allow only warnings that first occurred before this version");
addOption("-first", "when", "allow only warnings that first occurred in this version");
addOption("-last", "when", "allow only warnings that last occurred in this version");
addOption("-trimToVersion", "when", "trim bug collection to exclude information about versions after this one");
addOption("-fixed", "when", "allow only warnings that last occurred in the previous version (clobbers last)");
addOption("-present", "when", "allow only warnings present in this version");
addOption("-absent", "when", "allow only warnings absent in this version");
addOption("-maybeMutated", "when", "allow only warnings that might have mutated/fixed/born in this version");
addSwitchWithOptionalExtraPart("-hasField", "truth", "allow only warnings that are annotated with a field");
addSwitchWithOptionalExtraPart("-hasLocal", "truth", "allow only warnings that are annotated with a local variable");
addSwitchWithOptionalExtraPart("-active", "truth", "allow only warnings alive in the last sequence number");
addSwitch("-applySuppression", "exclude warnings that match the suppression filter");
addSwitch("-purgeHistory", "remove all version history");
addSwitchWithOptionalExtraPart("-sloppyUnique", "truth", "select only issues thought to be unique by the sloppy bug comparator ");
makeOptionUnlisted("-sloppyUnique");
addSwitchWithOptionalExtraPart("-introducedByChange", "truth",
"allow only warnings introduced by a change of an existing class");
addSwitchWithOptionalExtraPart("-removedByChange", "truth",
"allow only warnings removed by a change of a persisting class");
addSwitchWithOptionalExtraPart("-newCode", "truth", "allow only warnings introduced by the addition of a new class");
addSwitchWithOptionalExtraPart("-removedCode", "truth", "allow only warnings removed by removal of a class");
addOption("-priority", "level", "allow only warnings with this priority or higher");
makeOptionUnlisted("-priority");
addOption("-confidence", "level", "allow only warnings with this confidence or higher");
addOption("-maxRank", "rank", "allow only warnings with this rank or lower");
addOption("-maxAge", "days", "Only issues that and in the cloud and weren't first seen more than this many days ago");
addSwitchWithOptionalExtraPart("-notAProblem", "truth",
"Only issues with a consensus view that they are not a problem");
addSwitchWithOptionalExtraPart("-shouldFix", "truth", "Only issues with a consensus view that they should be fixed");
addOption("-class", "pattern", "allow only bugs whose primary class name matches this pattern");
addOption("-calls", "pattern", "allow only bugs that involve a call to a method that matches this pattern (matches with method class or name)");
addOption("-bugPattern", "pattern", "allow only bugs whose type matches this pattern");
addOption("-category", "category", "allow only warnings with a category that starts with this string");
addOption("-designation", "designation",
"allow only warnings with this designation (e.g., -designation SHOULD_FIX,MUST_FIX)");
addSwitch("-dontUpdateStats",
"used when withSource is specified to only update bugs, not the class and package stats");
addOption("-hashes", "hash file", "only bugs with instance hashes contained in the hash file");
}
public static long getVersionNum(BugCollection collection, String val,
boolean roundToLaterVersion) {
if (val == null) {
return -1;
}
Map<String, AppVersion> versions = new HashMap<String, AppVersion>();
SortedMap<Long, AppVersion> timeStamps = new TreeMap<Long, AppVersion>();
for (Iterator<AppVersion> i = collection.appVersionIterator(); i.hasNext();) {
AppVersion v = i.next();
versions.put(v.getReleaseName(), v);
timeStamps.put(v.getTimestamp(), v);
}
// add current version to the maps
AppVersion v = collection.getCurrentAppVersion();
versions.put(v.getReleaseName(), v);
timeStamps.put(v.getTimestamp(), v);
return getVersionNum(versions, timeStamps, val,
roundToLaterVersion, v.getSequenceNumber());
}
public static long getVersionNum(Map<String, AppVersion> versions, SortedMap<Long, AppVersion> timeStamps, String val,
boolean roundToLaterVersion, long currentSeqNum) {
if (val == null) {
return -1;
}
long numVersions = currentSeqNum + 1;
if (val.equals("last") || val.equals("lastVersion")) {
return numVersions - 1;
}
AppVersion v = versions.get(val);
if (v != null) {
return v.getSequenceNumber();
}
try {
long time = 0;
if (val.endsWith("daysAgo")) {
time = System.currentTimeMillis() - MILLISECONDS_PER_DAY
* Integer.parseInt(val.substring(0, val.length() - 7));
} else {
time = Date.parse(val);
}
return getAppropriateSeq(timeStamps, time, roundToLaterVersion);
} catch (Exception e) {
try {
long version = Long.parseLong(val);
if (version < 0) {
version = numVersions + version;
}
return version;
} catch (NumberFormatException e1) {
throw new IllegalArgumentException("Could not interpret version specification of '" + val + "'");
}
}
}
// timeStamps contains 0 10 20 30
// if roundToLater == true, ..0 = 0, 1..10 = 1, 11..20 = 2, 21..30 = 3,
// 31.. = Long.MAX
// if roundToLater == false, ..-1 = Long.MIN, 0..9 = 0, 10..19 = 1,
// 20..29 = 2, 30..39 = 3, 40 .. = 4
static private long getAppropriateSeq(SortedMap<Long, AppVersion> timeStamps, long when, boolean roundToLaterVersion) {
if (roundToLaterVersion) {
SortedMap<Long, AppVersion> geq = timeStamps.tailMap(when);
if (geq.isEmpty()) {
return Long.MAX_VALUE;
}
return geq.get(geq.firstKey()).getSequenceNumber();
} else {
SortedMap<Long, AppVersion> leq = timeStamps.headMap(when);
if (leq.isEmpty()) {
return Long.MIN_VALUE;
}
return leq.get(leq.lastKey()).getSequenceNumber();
}
}
private long minFirstSeen;
edu.umd.cs.findbugs.filter.Filter suppressionFilter;
void adjustFilter(Project project, BugCollection collection) {
suppressionFilter = project.getSuppressionFilter();
if (maxAgeSpecified) {
minFirstSeen = collection.getAnalysisTimestamp() - maxAge * MILLISECONDS_PER_DAY;
}
first = getVersionNum(collection, firstAsString, true);
maybeMutated = getVersionNum(collection, maybeMutatedAsString, true);
last = getVersionNum(collection, lastAsString, true);
before = getVersionNum(collection, beforeAsString, true);
after = getVersionNum(collection, afterAsString, false);
present = getVersionNum(collection, presentAsString, true);
absent = getVersionNum(collection, absentAsString, true);
if (sloppyUniqueSpecified) {
uniqueSloppy = new TreeSet<BugInstance>(new SloppyBugComparator());
}
long fixed = getVersionNum(collection, fixedAsString, true);
if (fixed >= 0)
{
last = fixed - 1; // fixed means last on previous sequence (ok
// if -1)
}
}
boolean accept(BugCollection collection, BugInstance bug) {
boolean result = evaluate(collection, bug);
if (not) {
return !result;
}
return result;
}
boolean evaluate(BugCollection collection, BugInstance bug) {
for (Matcher m : includeFilter) {
if (!m.match(bug)) {
return false;
}
}
for (Matcher m : excludeFilter) {
if (m.match(bug)) {
return false;
}
}
if (excludedInstanceHashes.contains(bug.getInstanceHash())) {
return false;
}
if (annotation != null && bug.getAnnotationText().indexOf(annotation) == -1) {
return false;
}
if (bug.getPriority() > priority) {
return false;
}
if (firstAsString != null && bug.getFirstVersion() != first) {
return false;
}
if (afterAsString != null && bug.getFirstVersion() <= after) {
return false;
}
if (beforeAsString != null && bug.getFirstVersion() >= before) {
return false;
}
if (hashesFromFile != null && !hashesFromFile.contains(bug.getInstanceHash())) {
return false;
}
long lastSeen = bug.getLastVersion();
if (lastSeen < 0) {
lastSeen = collection.getSequenceNumber();
}
long thisDuration = lastSeen - bug.getFirstVersion();
if (duration > 0 && thisDuration > duration) {
return false;
}
if ((lastAsString != null || fixedAsString != null) && (last < 0 || bug.getLastVersion() != last)) {
return false;
}
if (presentAsString != null && !bugLiveAt(bug, present)) {
return false;
}
if (absentAsString != null && bugLiveAt(bug, absent)) {
return false;
}
if (hasFieldSpecified && (hasField != (bug.getPrimaryField() != null))) {
return false;
}
if (hasLocalSpecified && (hasLocal != (bug.getPrimaryLocalVariableAnnotation() != null))) {
return false;
}
if (maxRank < Integer.MAX_VALUE && BugRanker.findRank(bug) > maxRank) {
return false;
}
if (activeSpecified && active == bug.isDead()) {
return false;
}
if (removedByChangeSpecified && bug.isRemovedByChangeOfPersistingClass() != removedByChange) {
return false;
}
if (introducedByChangeSpecified && bug.isIntroducedByChangeOfExistingClass() != introducedByChange) {
return false;
}
if (newCodeSpecified && newCode != (!bug.isIntroducedByChangeOfExistingClass() && bug.getFirstVersion() != 0)) {
return false;
}
if (removedCodeSpecified && removedCode != (!bug.isRemovedByChangeOfPersistingClass() && bug.isDead())) {
return false;
}
if (bugPattern != null && !bugPattern.matcher(bug.getType()).find()) {
return false;
}
if (classPattern != null && !classPattern.matcher(bug.getPrimaryClass().getClassName()).find()) {
return false;
}
if (callsPattern != null) {
MethodAnnotation m = bug.getAnnotationWithRole(MethodAnnotation.class, MethodAnnotation.METHOD_CALLED);
if (m == null) {
return false;
}
if (!callsPattern.matcher(m.getClassName()).find() && !callsPattern.matcher(m.getMethodName()).find()) {
return false;
}
}
if (maybeMutatedAsString != null && !(atMutationPoint(bug) && mutationPoints.contains(getBugLocation(bug)))) {
return false;
}
BugPattern thisBugPattern = bug.getBugPattern();
if (!categoryKey.isEmpty() && thisBugPattern != null && !categoryKey.contains(thisBugPattern.getCategory())) {
return false;
}
if (!designationKey.isEmpty() && !designationKey.contains(bug.getUserDesignationKey())) {
return false;
}
if (hashChangedSpecified) {
if (bug.isInstanceHashConsistent() == hashChanged) {
return false;
}
}
if (applySuppressionSpecified && applySuppression && suppressionFilter.match(bug)) {
return false;
}
SourceLineAnnotation primarySourceLineAnnotation = bug.getPrimarySourceLineAnnotation();
if (knownSourceSpecified) {
if (primarySourceLineAnnotation.isUnknown() == knownSource) {
return false;
}
}
if (withSourceSpecified) {
if (sourceSearcher.findSource(primarySourceLineAnnotation) != withSource) {
return false;
}
}
Cloud cloud = collection.getCloud();
if (maxAgeSpecified) {
long firstSeen = cloud.getFirstSeen(bug);
if (!cloud.isInCloud(bug)) {
return false;
}
if (firstSeen < minFirstSeen) {
return false;
}
}
if (notAProblemSpecified && notAProblem != (cloud.getConsensusDesignation(bug).score() < 0)) {
return false;
}
if (shouldFixSpecified && shouldFix != (cloud.getConsensusDesignation(bug).score() > 0)) {
return false;
}
if (sloppyUniqueSpecified) {
boolean unique = uniqueSloppy.add(bug);
if (unique != sloppyUnique) {
return false;
}
}
return true;
}
private void addDesignationKey(String argument) {
I18N i18n = I18N.instance();
for (String x : argument.split("[,|]")) {
for (String designationKey : i18n.getUserDesignationKeys()) {
if (designationKey.equals(x) || i18n.getUserDesignation(designationKey).equals(x)) {
this.designationKey.add(designationKey);
break;
}
}
}
}
private void addCategoryKey(String argument) {
DetectorFactoryCollection i18n = DetectorFactoryCollection.instance();
for (String x : argument.split("[,|]")) {
for (BugCategory category : i18n.getBugCategoryObjects()) {
if (category.getAbbrev().equals(x) || category.getCategory().equals(x)) {
this.categoryKey.add(category.getCategory());
break;
}
}
}
}
private boolean bugLiveAt(BugInstance bug, long now) {
if (now < bug.getFirstVersion()) {
return false;
}
if (bug.isDead() && bug.getLastVersion() < now) {
return false;
}
return true;
}
@Override
protected void handleOption(String option, String optionExtraPart) throws IOException {
option = option.substring(1);
if (optionExtraPart.length() == 0) {
setField(option, true);
} else {
setField(option, Boolean.parseBoolean(optionExtraPart));
}
setField(option + "Specified", true);
}
private void setField(String fieldName, boolean value) {
try {
Field f = FilterCommandLine.class.getField(fieldName);
f.setBoolean(this, value);
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
protected void handleOptionWithArgument(String option, String argument) throws IOException {
if (option.equals("-priority") || option.equals("-confidence")) {
priority = parsePriority(argument);
}
else if (option.equals("-maxRank")) {
maxRank = Integer.parseInt(argument);
} else if (option.equals("-first")) {
firstAsString = argument;
} else if (option.equals("-maybeMutated")) {
maybeMutatedAsString = argument;
} else if (option.equals("-last")) {
lastAsString = argument;
} else if (option.equals("-trimToVersion")) {
trimToVersionAsString = argument;
} else if (option.equals("-maxDuration")) {
duration = Integer.parseInt(argument);
} else if (option.equals("-fixed")) {
fixedAsString = argument;
} else if (option.equals("-after")) {
afterAsString = argument;
} else if (option.equals("-before")) {
beforeAsString = argument;
} else if (option.equals("-present")) {
presentAsString = argument;
} else if (option.equals("-absent")) {
absentAsString = argument;
} else if (option.equals("-category")) {
addCategoryKey(argument);
} else if (option.equals("-designation")) {
addDesignationKey(argument);
} else if (option.equals("-class")) {
classPattern = Pattern.compile(argument.replace(',', '|'));
} else if (option.equals("-calls")) {
callsPattern = Pattern.compile(argument.replace(',', '|'));
} else if (option.equals("-bugPattern")) {
bugPattern = Pattern.compile(argument);
} else if (option.equals("-annotation")) {
annotation = argument;
} else if (option.equals("-excludeBugs")) {
try {
ExcludingHashesBugReporter.addToExcludedInstanceHashes(excludedInstanceHashes, argument);
} catch (DocumentException e) {
throw new IllegalArgumentException("Error processing include file: " + argument, e);
}
} else if (option.equals("-include")) {
try {
includeFilter.add(new edu.umd.cs.findbugs.filter.Filter(argument));
} catch (FilterException e) {
throw new IllegalArgumentException("Error processing include file: " + argument, e);
}
} else if (option.equals("-exclude")) {
try {
excludeFilter.add(new edu.umd.cs.findbugs.filter.Filter(argument));
} catch (FilterException e) {
throw new IllegalArgumentException("Error processing include file: " + argument, e);
}
} else if (option.equals("-maxAge")) {
maxAge = Integer.parseInt(argument);
maxAgeSpecified = true;
} else if (option.equals("-hashes")) {
hashesFromFile = new HashSet<String>();
BufferedReader in = null;
try {
in = new BufferedReader(UTF8.fileReader(argument));
while (true) {
String h = in.readLine();
if (h == null) {
break;
}
hashesFromFile.add(h);
}
} catch (IOException e) {
throw new RuntimeException("Error reading hashes from " + argument, e);
} finally {
Util.closeSilently(in);
}
} else {
throw new IllegalArgumentException("can't handle command line argument of " + option);
}
}
HashSet<String> mutationPoints;
/**
* Do any prep work needed to perform bug filtering
*
* @param origCollection
*/
public void getReady(SortedBugCollection origCollection) {
if (maybeMutatedAsString != null) {
HashSet<String> addedIssues = new HashSet<String>();
HashSet<String> removedIssues = new HashSet<String>();
for (BugInstance b : origCollection) {
if (b.getFirstVersion() == maybeMutated) {
addedIssues.add(getBugLocation(b));
} else if (b.getLastVersion() == maybeMutated - 1) {
removedIssues.add(getBugLocation(b));
}
}
addedIssues.remove(null);
addedIssues.retainAll(removedIssues);
mutationPoints = addedIssues;
}
}
/**
* @param b
* @return
*/
private boolean atMutationPoint(BugInstance b) {
return b.getFirstVersion() == maybeMutated || b.getLastVersion() == maybeMutated - 1;
}
/**
* @param b
* @return
*/
private String getBugLocation(BugInstance b) {
String point;
MethodAnnotation m = b.getPrimaryMethod();
FieldAnnotation f = b.getPrimaryField();
if (m != null) {
point = m.toString();
} else if (f != null) {
point = f.toString();
} else {
point = null;
}
return point;
}
}
public static int parsePriority(String argument) {
int i = " HMLE".indexOf(argument);
if (i == -1) {
i = " 1234".indexOf(argument);
}
if (i == -1) {
throw new IllegalArgumentException("Bad priority: " + argument);
}
return i;
}
static SourceSearcher sourceSearcher;
public static void main(String[] args) throws Exception {
FindBugs.setNoAnalysis();
DetectorFactoryCollection.instance();
final FilterCommandLine commandLine = new FilterCommandLine();
int argCount = commandLine.parse(args, 0, 2, "Usage: " + Filter.class.getName()
+ " [options] [<orig results> [<new results]] ");
SortedBugCollection origCollection = new SortedBugCollection();
if (argCount == args.length) {
origCollection.readXML(System.in);
} else {
origCollection.readXML(args[argCount++]);
}
boolean verbose = argCount < args.length;
SortedBugCollection resultCollection = origCollection.createEmptyCollectionWithMetadata();
Project project = resultCollection.getProject();
int passed = 0;
int dropped = 0;
resultCollection.setWithMessages(commandLine.withMessages);
if (commandLine.hashChangedSpecified) {
origCollection.computeBugHashes();
}
commandLine.adjustFilter(project, resultCollection);
ProjectStats projectStats = resultCollection.getProjectStats();
projectStats.clearBugCounts();
if (commandLine.classPattern != null) {
projectStats.purgeClassesThatDontMatch(commandLine.classPattern);
}
sourceSearcher = new SourceSearcher(project);
long trimToVersion = -1;
if (commandLine.trimToVersionAsString != null) {
Map<String, AppVersion> versions = new HashMap<String, AppVersion>();
SortedMap<Long, AppVersion> timeStamps = new TreeMap<Long, AppVersion>();
for (Iterator<AppVersion> i = origCollection.appVersionIterator(); i.hasNext();) {
AppVersion v = i.next();
versions.put(v.getReleaseName(), v);
timeStamps.put(v.getTimestamp(), v);
}
// add current version to the maps
AppVersion v = resultCollection.getCurrentAppVersion();
versions.put(v.getReleaseName(), v);
timeStamps.put(v.getTimestamp(), v);
trimToVersion = edu.umd.cs.findbugs.workflow.Filter.FilterCommandLine.getVersionNum(versions, timeStamps,
commandLine.trimToVersionAsString, true, v.getSequenceNumber());
if (trimToVersion < origCollection.getSequenceNumber()) {
String name = resultCollection.getAppVersionFromSequenceNumber(trimToVersion).getReleaseName();
long timestamp = resultCollection.getAppVersionFromSequenceNumber(trimToVersion).getTimestamp();
resultCollection.setReleaseName(name);
resultCollection.setTimestamp(timestamp);
resultCollection.trimAppVersions(trimToVersion);
}
}
if (commandLine.maxAgeSpecified || commandLine.notAProblemSpecified || commandLine.shouldFixSpecified) {
Cloud cloud = origCollection.getCloud();
SigninState signinState = cloud.getSigninState();
if (!signinState.canDownload()) {
disconnect(verbose, commandLine, resultCollection, cloud.getCloudName() + " state is " + signinState
+ "; ignoring filtering options that require cloud access");
} else if (!cloud.waitUntilIssueDataDownloaded(20, TimeUnit.SECONDS)) {
if (verbose) {
System.out.println("Waiting for cloud information required for filtering");
}
if (!cloud.waitUntilIssueDataDownloaded(60, TimeUnit.SECONDS)) {
disconnect(verbose, commandLine, resultCollection,
"Unable to connect to cloud; ignoring filtering options that require cloud access");
}
}
}
commandLine.getReady(origCollection);
for (BugInstance bug : origCollection.getCollection()) {
if (commandLine.accept(origCollection, bug)) {
if (trimToVersion >= 0) {
if (bug.getFirstVersion() > trimToVersion) {
dropped++;
continue;
} else if (bug.getLastVersion() >= trimToVersion) {
bug.setLastVersion(-1);
bug.setRemovedByChangeOfPersistingClass(false);
}
}
resultCollection.add(bug, false);
passed++;
} else {
dropped++;
}
}
if (commandLine.purgeHistorySpecified && commandLine.purgeHistory) {
resultCollection.clearAppVersions();
for (BugInstance bug : resultCollection.getCollection()) {
bug.clearHistory();
}
}
if (verbose) {
System.out.println(passed + " warnings passed through, " + dropped + " warnings dropped");
}
if (commandLine.withSourceSpecified && commandLine.withSource && !commandLine.dontUpdateStats
&& projectStats.hasClassStats()) {
for (PackageStats stats : projectStats.getPackageStats()) {
Iterator<ClassStats> i = stats.getClassStats().iterator();
while (i.hasNext()) {
String className = i.next().getName();
if (sourceSearcher.sourceNotFound.contains(className) || !sourceSearcher.sourceFound.contains(className)
&& !sourceSearcher.findSource(SourceLineAnnotation.createReallyUnknown(className))) {
i.remove();
}
}
}
}
projectStats.recomputeFromComponents();
if (argCount == args.length) {
assert !verbose;
resultCollection.writeXML(System.out);
} else {
resultCollection.writeXML(args[argCount++]);
}
}
private static void disconnect(boolean verbose, final FilterCommandLine commandLine, SortedBugCollection resultCollection,
String msg) {
if (verbose) {
System.out.println(msg);
}
resultCollection.addError(msg);
commandLine.maxAgeSpecified = commandLine.notAProblemSpecified = commandLine.shouldFixSpecified = false;
}
}