/*
* FindBugs - Find bugs in Java programs
* Copyright (C) 2003-2005 University of Maryland
*
* 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
*/
/*
* Project.java
*
* Created on March 30, 2003, 2:22 PM
*/
package edu.umd.cs.findbugs;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
import org.dom4j.DocumentException;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.ba.SourceFinder;
import edu.umd.cs.findbugs.ba.URLClassPath;
import edu.umd.cs.findbugs.filter.Filter;
import edu.umd.cs.findbugs.util.Util;
import edu.umd.cs.findbugs.xml.OutputStreamXMLOutput;
import edu.umd.cs.findbugs.xml.XMLAttributeList;
import edu.umd.cs.findbugs.xml.XMLOutput;
import edu.umd.cs.findbugs.xml.XMLOutputUtil;
import edu.umd.cs.findbugs.xml.XMLWriteable;
/**
* A project in the GUI.
* This consists of some number of Jar files to analyze for bugs, and optionally
* <p/>
* <ul>
* <li> some number of source directories, for locating the program's
* source code
* <li> some number of auxiliary classpath entries, for locating classes
* referenced by the program which the user doesn't want to analyze
* <li> some number of boolean options
* </ul>
*
* @author David Hovemeyer
*/
public class Project implements XMLWriteable {
private static final boolean DEBUG = SystemProperties.getBoolean("findbugs.project.debug");
private List<File> currentWorkingDirectoryList;
private String projectName;
/**
* List of jars/directories to analyze
*/
private List<String> analysisTargets;
/**
* The list of source directories.
*/
private List<String> srcDirList;
/**
* The list of auxiliary classpath entries.
*/
private List<String> auxClasspathEntryList;
/**
* Flag to indicate that this Project has been modified.
*/
private boolean isModified;
private String cloudId;
/**
* @return Returns the cloudId.
*/
public @CheckForNull String getCloudId() {
return cloudId;
}
/**
* @param cloudId The cloudId to set.
*/
public void setCloudId(String cloudId) {
this.cloudId = cloudId;
}
private Properties cloudProperties = new Properties();
/**
* @return Returns the cloudProperties.
*/
public Properties getCloudProperties() {
return cloudProperties;
}
/**
* @param cloudProperties The cloudProperties to set.
*/
public void setCloudProperties(Properties cloudProperties) {
this.cloudProperties = cloudProperties;
}
/**
* Constant used to name anonymous projects.
*/
public static final String UNNAMED_PROJECT = "<<unnamed project>>";
private long timestampForAnalyzedClasses = 0L;
private IGuiCallback guiCallback;
@NonNull private Filter suppressionFilter = new Filter();
private SourceFinder sourceFinder;
/**
* Create an anonymous project.
*/
public Project() {
analysisTargets = new LinkedList<String>();
srcDirList = new LinkedList<String>();
auxClasspathEntryList = new LinkedList<String>();
isModified = false;
currentWorkingDirectoryList = new ArrayList<File>();
}
/**
* Return an exact copy of this Project.
*/
public Project duplicate() {
Project dup = new Project();
dup.currentWorkingDirectoryList.addAll(this.currentWorkingDirectoryList);
dup.projectName = this.projectName;
dup.analysisTargets.addAll(this.analysisTargets);
dup.srcDirList.addAll(this.srcDirList);
dup.auxClasspathEntryList.addAll(this.auxClasspathEntryList);
dup.timestampForAnalyzedClasses = timestampForAnalyzedClasses;
dup.guiCallback = guiCallback;
dup.cloudId = cloudId;
dup.cloudProperties.putAll(cloudProperties);
return dup;
}
public SourceFinder getSourceFinder() {
if (sourceFinder == null) {
sourceFinder = new SourceFinder(this);
}
return sourceFinder;
}
public boolean isGuiAvaliable(){
return guiCallback != null;
}
/**
* add information from project2 to this project
*/
public void add(Project project2) {
analysisTargets = appendWithoutDuplicates(analysisTargets, project2.analysisTargets);
srcDirList = appendWithoutDuplicates(srcDirList, project2.srcDirList);
auxClasspathEntryList = appendWithoutDuplicates(auxClasspathEntryList, project2.auxClasspathEntryList);
}
public static <T> List<T> appendWithoutDuplicates(List<T> lst1, List<T> lst2) {
LinkedHashSet<T> joined = new LinkedHashSet<T>(lst1);
joined.addAll(lst2);
return new ArrayList<T>(joined);
}
public void setCurrentWorkingDirectory(File f) {
if (f != null)
addWorkingDir(f.toString());
}
/**
* Return whether or not this Project has unsaved modifications.
*/
public boolean isModified() {
return isModified;
}
/**
* Set whether or not this Project has unsaved modifications.
*/
public void setModified(boolean isModified) {
this.isModified = isModified;
}
/**
* Add a file to the project.
*
* @param fileName the file to add
* @return true if the file was added, or false if the
* file was already present
*/
public boolean addFile(String fileName) {
return addToListInternal(analysisTargets, makeAbsoluteCWD(fileName));
}
/**
* Add a source directory to the project.
* @param dirName the directory to add
* @return true if the source directory was added, or false if the
* source directory was already present
*/
public boolean addSourceDir(String dirName) {
boolean isNew = false;
for (String dir : makeAbsoluteCwdCandidates(dirName)) {
isNew = addToListInternal(srcDirList, dir) || isNew;
}
sourceFinder = new SourceFinder(this);
return isNew;
}
/**
* Add a working directory to the project.
* @param dirName the directory to add
* @return true if the working directory was added, or false if the
* working directory was already present
*/
public boolean addWorkingDir(String dirName) {
if (dirName == null)
throw new NullPointerException();
return addToListInternal(currentWorkingDirectoryList, new File(dirName));
}
/**
* Get the number of files in the project.
*
* @return the number of files in the project
*/
public int getFileCount() {
return analysisTargets.size();
}
/**
* Get the given file in the list of project files.
*
* @param num the number of the file in the list of project files
* @return the name of the file
*/
public String getFile(int num) {
return analysisTargets.get(num);
}
/**
* Remove file at the given index in the list of project files
*
* @param num index of the file to remove in the list of project files
*/
public void removeFile(int num) {
analysisTargets.remove(num);
isModified = true;
}
/**
* Get the list of files, directories, and zip files in the project.
*/
public List<String> getFileList() {
return analysisTargets;
}
/**
* Get the number of source directories in the project.
*
* @return the number of source directories in the project
*/
public int getNumSourceDirs() {
return srcDirList.size();
}
/**
* Get the given source directory.
*
* @param num the number of the source directory
* @return the source directory
*/
public String getSourceDir(int num) {
return srcDirList.get(num);
}
/**
* Remove source directory at given index.
*
* @param num index of the source directory to remove
*/
public void removeSourceDir(int num) {
srcDirList.remove(num);
sourceFinder = new SourceFinder(this);
isModified = true;
}
/**
* Get project files as an array of Strings.
*/
public String[] getFileArray() {
return analysisTargets.toArray(new String[analysisTargets.size()]);
}
/**
* Get source dirs as an array of Strings.
*/
public String[] getSourceDirArray() {
return srcDirList.toArray(new String[srcDirList.size()]);
}
/**
* Get the source dir list.
*/
public List<String> getSourceDirList() {
return srcDirList;
}
/**
* Add an auxiliary classpath entry
*
* @param auxClasspathEntry the entry
* @return true if the entry was added successfully, or false
* if the given entry is already in the list
*/
public boolean addAuxClasspathEntry(String auxClasspathEntry) {
return addToListInternal(auxClasspathEntryList, makeAbsoluteCWD(auxClasspathEntry));
}
/**
* Get the number of auxiliary classpath entries.
*/
public int getNumAuxClasspathEntries() {
return auxClasspathEntryList.size();
}
/**
* Get the n'th auxiliary classpath entry.
*/
public String getAuxClasspathEntry(int n) {
return auxClasspathEntryList.get(n);
}
/**
* Remove the n'th auxiliary classpath entry.
*/
public void removeAuxClasspathEntry(int n) {
auxClasspathEntryList.remove(n);
isModified = true;
}
/**
* Return the list of aux classpath entries.
*/
public List<String> getAuxClasspathEntryList() {
return auxClasspathEntryList;
}
/**
* Worklist item for finding implicit classpath entries.
*/
private static class WorkListItem {
private URL url;
/**
* Constructor.
*
* @param url the URL of the Jar or Zip file
*/
public WorkListItem(URL url) {
this.url = url;
}
/**
* Get URL of Jar/Zip file.
*/
public URL getURL() {
return this.url;
}
}
/**
* Worklist for finding implicit classpath entries.
*/
private static class WorkList {
private LinkedList<WorkListItem> itemList;
private HashSet<String> addedSet;
/**
* Constructor.
* Creates an empty worklist.
*/
public WorkList() {
this.itemList = new LinkedList<WorkListItem>();
this.addedSet = new HashSet<String>();
}
/**
* Create a URL from a filename specified in the project file.
*/
public URL createURL(String fileName) throws MalformedURLException {
String protocol = URLClassPath.getURLProtocol(fileName);
if (protocol == null) {
fileName = "file:" + fileName;
}
return new URL(fileName);
}
/**
* Create a URL of a file relative to another URL.
*/
public URL createRelativeURL(URL base, String fileName) throws MalformedURLException {
return new URL(base, fileName);
}
/**
* Add a worklist item.
*
* @param item the WorkListItem representing a zip/jar file to be examined
* @return true if the item was added, false if not (because it was
* examined already)
*/
public boolean add(WorkListItem item) {
if (DEBUG) {
System.out.println("Adding " + item.getURL().toString());
}
if (!addedSet.add(item.getURL().toString())) {
if (DEBUG) {
System.out.println("\t==> Already processed");
}
return false;
}
itemList.add(item);
return true;
}
/**
* Return whether or not the worklist is empty.
*/
public boolean isEmpty() {
return itemList.isEmpty();
}
/**
* Get the next item in the worklist.
*/
public WorkListItem getNextItem() {
return itemList.removeFirst();
}
}
/**
* Return the list of implicit classpath entries. The implicit
* classpath is computed from the closure of the set of jar files
* that are referenced by the <code>"Class-Path"</code> attribute
* of the manifest of the any jar file that is part of this project
* or by the <code>"Class-Path"</code> attribute of any directly or
* indirectly referenced jar. The referenced jar files that exist
* are the list of implicit classpath entries.
*
* @deprecated FindBugs2 and ClassPathBuilder take care of this automatically
*/
@Deprecated
public List<String> getImplicitClasspathEntryList() {
final LinkedList<String> implicitClasspath = new LinkedList<String>();
WorkList workList = new WorkList();
// Prime the worklist by adding the zip/jar files
// in the project.
for (String fileName : analysisTargets) {
try {
URL url = workList.createURL(fileName);
WorkListItem item = new WorkListItem(url);
workList.add(item);
} catch (MalformedURLException ignore) {
// Ignore
}
}
// Scan recursively.
while (!workList.isEmpty()) {
WorkListItem item = workList.getNextItem();
processComponentJar(item.getURL(), workList, implicitClasspath);
}
return implicitClasspath;
}
/**
* Examine the manifest of a single zip/jar file for implicit
* classapth entries.
*
* @param jarFileURL URL of the zip/jar file
* @param workList worklist of zip/jar files to examine
* @param implicitClasspath list of implicit classpath entries found
*/
private void processComponentJar(URL jarFileURL, WorkList workList,
List<String> implicitClasspath) {
if (DEBUG) {
System.out.println("Processing " + jarFileURL.toString());
}
if (!jarFileURL.toString().endsWith(".zip") && !jarFileURL.toString().endsWith(".jar")) {
return;
}
try {
URL manifestURL = new URL("jar:" + jarFileURL.toString() + "!/META-INF/MANIFEST.MF");
InputStream in = null;
try {
in = manifestURL.openStream();
Manifest manifest = new Manifest(in);
Attributes mainAttrs = manifest.getMainAttributes();
String classPath = mainAttrs.getValue("Class-Path");
if (classPath != null) {
String[] fileList = classPath.split("\\s+");
for (String jarFile : fileList) {
URL referencedURL = workList.createRelativeURL(jarFileURL, jarFile);
if (workList.add(new WorkListItem(referencedURL))) {
implicitClasspath.add(referencedURL.toString());
if (DEBUG) {
System.out.println("Implicit jar: " + referencedURL.toString());
}
}
}
}
} finally {
if (in != null) {
in.close();
}
}
} catch (IOException ignore) {
// Ignore
}
}
private static final String OPTIONS_KEY = "[Options]";
private static final String JAR_FILES_KEY = "[Jar files]";
private static final String SRC_DIRS_KEY = "[Source dirs]";
private static final String AUX_CLASSPATH_ENTRIES_KEY = "[Aux classpath entries]";
// Option keys
public static final String RELATIVE_PATHS = "relative_paths";
/**
* Save the project to an output file.
*
* @param outputFile name of output file
* @param useRelativePaths true if the project should be written
* using only relative paths
* @param relativeBase if useRelativePaths is true,
* this file is taken as the base directory in terms of which
* all files should be made relative
* @throws IOException if an error occurs while writing
*/
@Deprecated
public void write(String outputFile, boolean useRelativePaths, String relativeBase)
throws IOException {
PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(outputFile)));
try {
writer.println(JAR_FILES_KEY);
for (String jarFile : analysisTargets) {
if (useRelativePaths) {
jarFile = convertToRelative(jarFile, relativeBase);
}
writer.println(jarFile);
}
writer.println(SRC_DIRS_KEY);
for (String srcDir : srcDirList) {
if (useRelativePaths) {
srcDir = convertToRelative(srcDir, relativeBase);
}
writer.println(srcDir);
}
writer.println(AUX_CLASSPATH_ENTRIES_KEY);
for (String auxClasspathEntry : auxClasspathEntryList) {
if (useRelativePaths) {
auxClasspathEntry = convertToRelative(auxClasspathEntry, relativeBase);
}
writer.println(auxClasspathEntry);
}
if (useRelativePaths) {
writer.println(OPTIONS_KEY);
writer.println(RELATIVE_PATHS + "=true");
}
} finally {
writer.close();
}
// Project successfully saved
isModified = false;
}
public static Project readXML(File f) throws IOException, DocumentException, SAXException {
InputStream in = new BufferedInputStream(new FileInputStream(f));
Project project = new Project();
try {
String tag = Util.getXMLType(in);
SAXBugCollectionHandler handler;
if (tag.equals("Project")) {
handler = new SAXBugCollectionHandler(project, f);
} else if (tag.equals("BugCollection")) {
SortedBugCollection bugs = new SortedBugCollection(project);
handler = new SAXBugCollectionHandler(bugs, f);
} else {
throw new IOException("Can't load a project from a " + tag + " file");
}
XMLReader xr = XMLReaderFactory.createXMLReader();
xr.setContentHandler(handler);
xr.setErrorHandler(handler);
Reader reader = Util.getReader(in);
xr.parse(new InputSource(reader));
} finally {
in.close();
}
// Presumably, project is now up-to-date
project.setModified(false);
return project;
}
public void writeXML(File f) throws IOException {
OutputStream out = new FileOutputStream(f);
XMLOutput xmlOutput = new OutputStreamXMLOutput(out);
try {
writeXML(xmlOutput);
} finally {
xmlOutput.finish();
}
}
/**
* Read Project from named file.
*
* @param argument command line argument containing project file name
* @return the Project
* @throws IOException
*/
public static Project readProject(String argument) throws IOException {
String projectFileName = argument;
File projectFile = new File(projectFileName);
if (projectFileName.endsWith(".xml") || projectFileName.endsWith(".fbp")) {
try {
return Project.readXML(projectFile);
} catch (DocumentException e) {
IOException ioe = new IOException("Couldn't read saved FindBugs project");
ioe.initCause(e);
throw ioe;
}
catch (SAXException e) {
IOException ioe = new IOException("Couldn't read saved FindBugs project");
ioe.initCause(e);
throw ioe;
}
}
throw new IllegalArgumentException("Can't read project from " + argument);
}
/**
* Read a line from a BufferedReader, ignoring blank lines
* and comments.
*/
private static String getLine(BufferedReader reader) throws IOException {
String line;
while ((line = reader.readLine()) != null) {
line = line.trim();
if (!line.equals("") && !line.startsWith("#")) {
break;
}
}
return line;
}
/**
* Convert to a string in a nice (displayable) format.
*/
@Override
public String toString() {
if(projectName != null) {
return projectName;
}
return UNNAMED_PROJECT;
}
/**
* Transform a user-entered filename into a proper filename,
* by adding the ".fb" file extension if it isn't already present.
*/
public static String transformFilename(String fileName) {
if (!fileName.endsWith(".fb")) {
fileName = fileName + ".fb";
}
return fileName;
}
static final String JAR_ELEMENT_NAME = "Jar";
static final String AUX_CLASSPATH_ENTRY_ELEMENT_NAME = "AuxClasspathEntry";
static final String SRC_DIR_ELEMENT_NAME = "SrcDir";
static final String WRK_DIR_ELEMENT_NAME = "WrkDir";
static final String FILENAME_ATTRIBUTE_NAME = "filename";
static final String PROJECTNAME_ATTRIBUTE_NAME = "projectName";
static final String CLOUD_ELEMENT_NAME = "Cloud";
static final String CLOUD_ID_ATTRIBUTE_NAME = "id";
static final String CLOUD_PROPERTY_ELEMENT_NAME = "Property";
public void writeXML(XMLOutput xmlOutput) throws IOException {
writeXML(xmlOutput, null);
}
public void writeXML(XMLOutput xmlOutput, @CheckForNull Object destination) throws IOException {
XMLAttributeList attributeList = new XMLAttributeList();
if (getProjectName() != null) {
attributeList = attributeList.addAttribute(PROJECTNAME_ATTRIBUTE_NAME, getProjectName());
}
xmlOutput.openTag(
BugCollection.PROJECT_ELEMENT_NAME,
attributeList
);
XMLOutputUtil.writeElementList(xmlOutput, JAR_ELEMENT_NAME, analysisTargets);
XMLOutputUtil.writeElementList(xmlOutput, AUX_CLASSPATH_ENTRY_ELEMENT_NAME, auxClasspathEntryList);
XMLOutputUtil.writeElementList(xmlOutput, SRC_DIR_ELEMENT_NAME, srcDirList);
XMLOutputUtil.writeFileList(xmlOutput, WRK_DIR_ELEMENT_NAME, currentWorkingDirectoryList);
if (suppressionFilter != null && !suppressionFilter.isEmpty()) {
xmlOutput.openTag("SuppressionFilter");
suppressionFilter.writeBodyAsXML(xmlOutput);
xmlOutput.closeTag("SuppressionFilter");
}
if (cloudId != null) {
xmlOutput.startTag(CLOUD_ELEMENT_NAME);
xmlOutput.addAttribute(CLOUD_ID_ATTRIBUTE_NAME, cloudId);
xmlOutput.stopTag(false);
for(Map.Entry e : cloudProperties.entrySet()) {
xmlOutput.startTag(CLOUD_PROPERTY_ELEMENT_NAME);
xmlOutput.addAttribute("key", e.getKey().toString());
xmlOutput.stopTag(false);
Object value = e.getValue();
xmlOutput.writeText(value.toString());
xmlOutput.closeTag(CLOUD_PROPERTY_ELEMENT_NAME);
}
xmlOutput.closeTag(CLOUD_ELEMENT_NAME);
}
xmlOutput.closeTag(BugCollection.PROJECT_ELEMENT_NAME);
}
List<String> makeRelative(List<String> files, @CheckForNull Object destination) {
if (destination == null) {
return files;
}
if (currentWorkingDirectoryList.isEmpty()) {
return files;
}
if (destination instanceof File) {
File where = (File)destination;
if (where.getParentFile().equals(currentWorkingDirectoryList.get(0))) {
List<String> result = new ArrayList<String>(files.size());
String root = where.getParent();
for(String s : files) {
if (s.startsWith(root)) {
result.add(s.substring(root.length()));
} else {
result.add(s);
}
}
return result;
}
}
return files;
}
/**
* Hack for whether files are case insensitive.
* For now, we'll assume that Windows is the only
* case insensitive OS. (OpenVMS users,
* feel free to submit a patch :-)
*/
private static final boolean FILE_IGNORE_CASE =
SystemProperties.getProperty("os.name", "unknown").startsWith("Windows");
/**
* Converts a full path to a relative path if possible
*
* @param srcFile path to convert
* @return the converted filename
*/
private String convertToRelative(String srcFile, String base) {
String slash = SystemProperties.getProperty("file.separator");
if (FILE_IGNORE_CASE) {
srcFile = srcFile.toLowerCase();
base = base.toLowerCase();
}
if (base.equals(srcFile)) {
return ".";
}
if (!base.endsWith(slash)) {
base = base + slash;
}
if (base.length() <= srcFile.length()) {
String root = srcFile.substring(0, base.length());
if (root.equals(base)) {
// Strip off the base directory, make relative
return "." + SystemProperties.getProperty("file.separator") + srcFile.substring(base.length());
}
}
//See if we can build a relative path above the base using .. notation
int slashPos = srcFile.indexOf(slash);
int branchPoint;
if (slashPos >= 0) {
String subPath = srcFile.substring(0, slashPos);
if ((subPath.length() == 0) || base.startsWith(subPath)) {
branchPoint = slashPos + 1;
slashPos = srcFile.indexOf(slash, branchPoint);
while (slashPos >= 0) {
subPath = srcFile.substring(0, slashPos);
if (base.startsWith(subPath)) {
branchPoint = slashPos + 1;
} else {
break;
}
slashPos = srcFile.indexOf(slash, branchPoint);
}
int slashCount = 0;
slashPos = base.indexOf(slash, branchPoint);
while (slashPos >= 0) {
slashCount++;
slashPos = base.indexOf(slash, slashPos + 1);
}
StringBuilder path = new StringBuilder();
String upDir = ".." + slash;
for (int i = 0; i < slashCount; i++) {
path.append(upDir);
}
path.append(srcFile.substring(branchPoint));
return path.toString();
}
}
return srcFile;
}
/**
* Converts a relative path to an absolute path if possible.
*
* @param fileName path to convert
* @return the converted filename
*/
private String convertToAbsolute(String fileName) throws IOException {
// At present relative paths are only calculated if the fileName is
// below the project file. This need not be the case, and we could use ..
// syntax to move up the tree. (To Be Added)
File file = new File(fileName);
if (!file.isAbsolute()) {
for(File cwd : currentWorkingDirectoryList) {
File test = new File(cwd,fileName);
if (test.canRead())
return test.getAbsolutePath();
}
return file.getAbsolutePath();
}
return fileName;
}
/**
* Make the given filename absolute relative to the
* current working directory.
*/
private String makeAbsoluteCWD(String fileName) {
List<String> candidates = makeAbsoluteCwdCandidates(fileName);
return candidates.get(0);
}
/**
* Make the given filename absolute relative to the current working directory candidates.
*
* If the given filename exists in more than one of the working directories, a list of
* these existing absolute paths is returned.
*
* The returned list is guaranteed to be non-empty.
* The returned paths might exist or not exist and might be relative or absolute.
*
* @return A list of at least one candidate path for the given filename.
*/
private List<String> makeAbsoluteCwdCandidates(String fileName) {
List<String> candidates = new ArrayList<String>();
boolean hasProtocol = (URLClassPath.getURLProtocol(fileName) != null);
if (hasProtocol) {
candidates.add(fileName);
return candidates;
}
if (new File(fileName).isAbsolute()) {
candidates.add(fileName);
return candidates;
}
for (File currentWorkingDirectory : currentWorkingDirectoryList) {
File relativeToCurrent = new File(currentWorkingDirectory, fileName);
if (relativeToCurrent.exists()) {
candidates.add(relativeToCurrent.toString());
}
}
if (candidates.isEmpty()) {
candidates.add(fileName);
}
return candidates;
}
/**
* Add a value to given list, making the Project modified
* if the value is not already present in the list.
*
* @param list the list
* @param value the value to be added
* @return true if the value was not already present in the list,
* false otherwise
*/
private <T> boolean addToListInternal(Collection<T> list, T value) {
if (!list.contains(value)) {
list.add(value);
isModified = true;
return true;
} else {
return false;
}
}
/**
* Make the given list of pathnames absolute relative
* to the absolute path of the project file.
*/
private void makeListAbsoluteProject(List<String> list) throws IOException {
List<String> replace = new LinkedList<String>();
for (String fileName : list) {
fileName = convertToAbsolute(fileName);
replace.add(fileName);
}
list.clear();
list.addAll(replace);
}
/**
* @param timestamp The timestamp to set.
*/
public void setTimestamp(long timestamp) {
this.timestampForAnalyzedClasses = timestamp;
}
public void addTimestamp(long timestamp) {
if (this.timestampForAnalyzedClasses < timestamp && FindBugs.validTimestamp(timestamp)) {
this.timestampForAnalyzedClasses = timestamp;
}
}
/**
* @return Returns the timestamp.
*/
public long getTimestamp() {
return timestampForAnalyzedClasses;
}
/**
* @param projectName The projectName to set.
*/
public void setProjectName(String projectName) {
this.projectName = projectName;
}
/**
* @return Returns the projectName.
*/
public String getProjectName() {
return projectName;
}
/**
* @param suppressionFilter The suppressionFilter to set.
*/
public void setSuppressionFilter(Filter suppressionFilter) {
this.suppressionFilter = suppressionFilter;
}
/**
* @return Returns the suppressionFilter.
*/
public Filter getSuppressionFilter() {
if (suppressionFilter == null) {
suppressionFilter = new Filter();
}
return suppressionFilter;
}
public void setGuiCallback(IGuiCallback guiCallback) {
this.guiCallback = guiCallback;
}
public IGuiCallback getGuiCallback() {
if (guiCallback == null)
guiCallback = new CommandLineUiCallback();
return guiCallback;
}
/**
* @return
*/
public Iterable<String> getResolvedSourcePaths() {
List<String> result = new ArrayList<String>();
for(String s : srcDirList) {
boolean hasProtocol = (URLClassPath.getURLProtocol(s) != null);
if (hasProtocol) {
result.add(s);
continue;
}
File f = new File(s);
if (f.isAbsolute() || currentWorkingDirectoryList.isEmpty()) {
if (f.canRead())
result.add(s);
continue;
}
for(File d : currentWorkingDirectoryList)
if (d.canRead() && d.isDirectory()) {
File a = new File(d, s);
if (a.canRead())
result.add(a.getAbsolutePath());
}
}
return result;
}
}
// vim:ts=4