import net.jaekl.cfb.analyze.Analyzer;
import net.jaekl.cfb.analyze.MessageMap;
import net.jaekl.cfb.db.CfbSchema;
+import net.jaekl.cfb.db.TypeMismatchException;
import net.jaekl.cfb.db.driver.DbDriver;
import net.jaekl.cfb.db.driver.PostgresqlDriver;
import net.jaekl.cfb.store.DbStore;
}
}
- void doMain(PrintWriter pw, String[] args) throws SQLException, IOException, XmlParseException, SAXException {
+ void doMain(PrintWriter pw, String[] args) throws SQLException, IOException, XmlParseException, SAXException, TypeMismatchException {
initArgs(); // read environment and system properties
if ( ! parseArgs(pw, args) ) {
return;
m_schema.purge(con);
return;
}
- m_schema.ensureDbInitialized(con);
+ m_schema.ensureDbInitialized(con);
+ messageMap.loadIds(con, m_driver);
}
catch (SQLException exc) {
reportUnableToConnect(pw, exc);
}
try (Connection con = m_driver.connect(m_host, m_port, m_dbName, m_user, m_pass)) {
- DbStore store = new DbStore(con);
+ DbStore store = new DbStore(con, m_driver, messageMap.getColl());
store.put(analysis);
}
try (PrintWriter pw = new PrintWriter(System.out)){
cfb.doMain(pw, args);
pw.flush();
- } catch (SQLException | IOException | XmlParseException | SAXException exc) {
+ } catch (SQLException | IOException | XmlParseException | SAXException | TypeMismatchException exc) {
exc.printStackTrace();
}
}
public class Analysis {
BugCollection m_bugCollection;
String m_buildNumber;
- Date m_date; // Date/time when analysis was started
+ Date m_start; // Date/time when analysis was started
+ Date m_end;
public Analysis(String buildNumber) {
m_bugCollection = null;
m_buildNumber = buildNumber;
- m_date = new Date();
+ m_start = new Date();
+ m_end = null;
}
public BugCollection getBugCollection() { return m_bugCollection; }
+ public String getBuildNumber() { return m_buildNumber; }
+ public Date getStart() { return m_start; }
+ public Date getEnd() { return m_end; }
public void parse(InputSource xml) throws FileNotFoundException, IOException, SAXException
{
reader.parse(xml);
}
+ // Set the end time (when FindBugs was done analyzing)
+ public void setEnd(Date date)
+ {
+ m_end = date;
+ }
+
public void dump(PrintWriter pw)
{
if (null != m_bugCollection) {
import java.io.IOException;
import java.io.PrintWriter;
import java.text.MessageFormat;
+import java.util.Date;
import java.util.Locale;
import java.util.Locale.Category;
return null;
}
+ result.setEnd(new Date());
result.parse(new InputSource(fbOutput.getAbsolutePath()));
result.dump(pw);
return result;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.List;
import java.util.Locale;
+import net.jaekl.cfb.db.CfbSchema;
+import net.jaekl.cfb.db.Column;
+import net.jaekl.cfb.db.Condition;
+import net.jaekl.cfb.db.Row;
+import net.jaekl.cfb.db.Table;
+import net.jaekl.cfb.db.TypeMismatchException;
+import net.jaekl.cfb.db.driver.DbDriver;
+import net.jaekl.cfb.xml.messages.BugCategory;
+import net.jaekl.cfb.xml.messages.BugPattern;
import net.jaekl.cfb.xml.messages.MessageCollection;
import net.jaekl.qd.xml.ParseErrorHandler;
import net.jaekl.qd.xml.ParseHandler;
public MessageCollection getColl() { return m_msgColl; }
public File getFindBugsDir() { return m_findBugsDir; }
+ // Load the list of primary keys (sequence numbers) from the database
+ public void loadIds(Connection con, DbDriver driver) throws SQLException, TypeMismatchException
+ {
+ loadCategoryIds(con, driver);
+ loadBugPatternIds(con, driver);
+
+ }
+
+ void loadCategoryIds(Connection con, DbDriver driver) throws SQLException, TypeMismatchException
+ {
+ Column[] columns = { CfbSchema.CATEGORIES.getColumn(CfbSchema.CATEGORYID),
+ CfbSchema.CATEGORIES.getColumn(CfbSchema.CATEGORY) };
+ Table[] tables = { CfbSchema.CATEGORIES };
+ Condition[] conditions = { };
+ List<Row> rows = driver.select(con, columns, tables, conditions);
+
+ for (Row row : rows) {
+ long catId = row.getLong(0);
+ String catName = row.getString(1);
+
+ BugCategory cat = getColl().getCategory(catName);
+ if (null == cat) {
+ throw new SQLException("Database result (" + catId + ", \"" + catName + "\") not found in messages.xml. "
+ + "Perhaps your database and findbugs versions are out of sync?");
+ }
+
+ cat.setId(catId);
+ }
+ }
+
+ void loadBugPatternIds(Connection con, DbDriver driver) throws SQLException, TypeMismatchException
+ {
+ Column[] columns = { CfbSchema.BUGS.getColumn(CfbSchema.BUGID),
+ CfbSchema.BUGS.getColumn(CfbSchema.TYPE) };
+ Table[] tables = { CfbSchema.BUGS };
+ Condition[] conditions = { };
+ List<Row> rows = driver.select(con, columns, tables, conditions);
+
+ for (Row row: rows) {
+ long bugId = row.getLong(0);
+ String type = row.getString(1);
+
+ BugPattern bug = getColl().getPattern(type);
+ if (null == bug) {
+ throw new SQLException("Database result (" + bugId + ", \"" + type + "\") not found in messages.xml. "
+ + "Perhaps your database and findbugs versions are out of sync?");
+ }
+
+ bug.setId(bugId);
+ }
+ }
+
+ // Load the list of bug patterns and categories from the FindBugs messages.xml file.
public void load(File findBugsDir, Locale locale) throws FileNotFoundException, IOException, SAXException
{
m_findBugsDir = findBugsDir;
}
+ // Parse the FindBugs messages.xml file
void parse(InputSource xml) throws FileNotFoundException, IOException, SAXException
{
m_msgColl = new MessageCollection();
public static final Sequence RUN_SEQ = new Sequence("RUN_SEQ");
public static final String BUGID = "BUGID";
+ public static final String CATEGORY = "CATEGORY";
public static final String CATEGORYID = "CATEGORYID";
+ public static final String CLASSNAME = "CLASSNAME";
+ public static final String ENDLINE = "ENDLINE";
+ public static final String FIRSTLOCID = "FIRSTLOCID";
public static final String FOUNDID = "FOUNDID";
public static final String LOCID = "LOCID";
+ public static final String METHODNAME = "METHODNAME";
public static final String RUNID = "RUNID";
+ public static final String SECONDLOCID = "SECONDLOCID";
+ public static final String STARTLINE = "STARTLINE";
+ public static final String THIRDLOCID = "THIRDLOCID";
+ public static final String TYPE = "TYPE";
// Define each table as follows:
// {
// Description of each possible bug
{ "BUG" },
{ BUGID, INTEGER, -1, NOT_NULL },
- { "TYPE", VARCHAR, 80, NOT_NULL }
+ { TYPE, VARCHAR, 80, NOT_NULL },
+ { "CATEGORYID", INTEGER, -1, NOT_NULL }
};
private static final Object[][] CATEGORIES_DEFN =
{
// Description of each possible bug category
- { "CATEGORY" },
+ { "CATEGORIES" },
{ CATEGORYID, INTEGER, -1, NOT_NULL },
- { "CATEGORY", VARCHAR, 80, NOT_NULL }
+ { CATEGORY, VARCHAR, 80, NOT_NULL }
};
private static final Object[][] FOUND_DEFN =
{
// One BugInstance, found during an analysis
{ "FOUND" },
{ FOUNDID, INTEGER, -1, NOT_NULL },
- { "BUGID", INTEGER, -1, NOT_NULL },
- { "CATEGORYID", INTEGER, -1, NOT_NULL },
- { "FIRSTLOCID", INTEGER, -1, NOT_NULL },
- { "SECONDLOCID", INTEGER, -1, NULL },
- { "THIRDLOCID", INTEGER, -1, NULL }
+ { BUGID, INTEGER, -1, NOT_NULL },
+ { FIRSTLOCID, INTEGER, -1, NOT_NULL },
+ { SECONDLOCID, INTEGER, -1, NULL },
+ { THIRDLOCID, INTEGER, -1, NULL }
};
private static final Object[][] LOCATIONS_DEFN =
{
// Location in the source code referenced by a BugInstance
{ "LOCATION" },
{ LOCID, INTEGER, -1, NOT_NULL },
- { "CLASSNAME", VARCHAR, 256, NOT_NULL },
- { "STARTLINE", INTEGER, -1, NULL },
- { "ENDLINE", INTEGER, -1, NULL }
+ { CLASSNAME, VARCHAR, 256, NOT_NULL },
+ { METHODNAME, VARCHAR, 256, NULL },
+ { STARTLINE, INTEGER, -1, NULL },
+ { ENDLINE, INTEGER, -1, NULL }
};
private static final Object[][] RUNS_DEFN =
{
int row = 0;
for (BugCategory cat : categories) {
long categoryId = m_driver.nextVal(con, CATEGORY_SEQ);
+ cat.setId(categoryId);
values[row][0] = Long.valueOf(categoryId);
values[row][1] = cat.getCategory();
int row = 0;
for (BugPattern bug : patterns) {
long bugId = m_driver.nextVal(con, BUG_SEQ);
+ bug.setId(bugId);
values[row][0] = Long.valueOf(bugId);
values[row][1] = bug.getType();
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
public class Table {
String m_name;
+ HashMap<String, Column> m_columnsByName;
ArrayList<Column> m_columns;
public Table(String name, Column[] columns) {
m_name = name;
- m_columns = new ArrayList<Column>(Arrays.asList(columns));
+
+ m_columns = new ArrayList<Column>();
+ m_columns.addAll(Arrays.asList(columns));
+
+ m_columnsByName = new HashMap<String, Column>();
+ for (int i = 0; i < columns.length; ++i) {
+ m_columnsByName.put(columns[i].getName(), columns[i]);
+ }
}
public String getName() { return m_name; }
public int getNumColumns() { return m_columns.size(); }
public Column getColumn(int idx) { return m_columns.get(idx); }
-
+ public Column getColumn(String name) { return m_columnsByName.get(name); }
+
// Construct a table from an array of objects like this:
// {
// { table_name },
package net.jaekl.cfb.store;
import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.List;
import net.jaekl.cfb.analyze.Analysis;
+import net.jaekl.cfb.db.CfbSchema;
+import net.jaekl.cfb.db.Column;
+import net.jaekl.cfb.db.Condition;
+import net.jaekl.cfb.db.Operation;
+import net.jaekl.cfb.db.Row;
+import net.jaekl.cfb.db.Table;
+import net.jaekl.cfb.db.TypeMismatchException;
+import net.jaekl.cfb.db.driver.DbDriver;
+import net.jaekl.cfb.xml.BugInstance;
+import net.jaekl.cfb.xml.SourceLine;
+import net.jaekl.cfb.xml.messages.MessageCollection;
public class DbStore {
- Connection m_conn;
+ Connection m_con;
+ DbDriver m_driver;
+ MessageCollection m_msgColl;
- public DbStore(Connection conn) {
- m_conn = conn;
+ public DbStore(Connection con, DbDriver driver, MessageCollection msgColl) {
+ m_con = con;
+ m_driver = driver;
+ m_msgColl = msgColl;
}
- public boolean put(Analysis analysis) {
+ public boolean put(Analysis analysis) throws SQLException {
if (null == analysis) {
return false;
}
+ // ----------------------------------
+ // Add a run record for this analysis
+ long runId = m_driver.nextVal(m_con, CfbSchema.RUN_SEQ);
+ Object[][] values = {
+ {
+ Long.valueOf(runId),
+ analysis.getBuildNumber(),
+ analysis.getStart(),
+ analysis.getEnd()
+ }
+ };
+ int count = m_driver.insert(m_con, CfbSchema.RUNS, values);
+ if (1 != count) {
+ return false;
+ }
+
+ // -------------------------------------
+ // Add a found record for each bug found
+
+ List<BugInstance> bugs = analysis.getBugCollection().getBugs();
+ values = new Object[bugs.size()][CfbSchema.FOUND.getNumColumns()];
+
+ int row = 0;
+ for (BugInstance bug : bugs)
+ {
+ long foundId = m_driver.nextVal(m_con, CfbSchema.FOUND_SEQ);
+ long bugId = m_msgColl.getPattern(bug.getType()).getId();
+ // values[row] = { foundId, bugId, firstLocId, secondLocId, thirdLocId };
+ row++;
+ }
return true;
}
+
+ Location[] computeLocations(BugInstance bug)
+ {
+ throw new UnsupportedOperationException("UNIMPLEMENTED");
+ }
+
+ long getLocId(SourceLine loc) throws SQLException, TypeMismatchException
+ {
+ long locId = findLocId(loc);
+ if (locId >= 0) {
+ return locId;
+ }
+
+ return storeLoc(loc);
+ }
+
+ long findLocId(SourceLine loc) throws SQLException, TypeMismatchException
+ {
+ Column[] columns = { CfbSchema.LOCATIONS.getColumn(CfbSchema.LOCID) };
+ Table[] tables = { CfbSchema.LOCATIONS };
+ Condition[] conditions = {
+ new Condition( CfbSchema.LOCATIONS.getColumn(CfbSchema.CLASSNAME),
+ loc.getClassName(),
+ Operation.EQUAL
+ ),
+ new Condition( CfbSchema.LOCATIONS.getColumn(CfbSchema.STARTLINE),
+ loc.getStart(),
+ Operation.EQUAL
+ ),
+ new Condition( CfbSchema.LOCATIONS.getColumn(CfbSchema.ENDLINE),
+ loc.getEnd(),
+ Operation.EQUAL
+ )
+ };
+ List<Row> rows = m_driver.select(m_con, columns, tables, conditions);
+ if (rows.size() > 0) {
+ assert(1 == rows.size()); // should only have one match
+
+ return rows.get(0).getLong(0);
+ }
+
+ return (-1); // not found
+ }
+
+ long storeLoc(SourceLine loc) throws SQLException
+ {
+ long locId = m_driver.nextVal(m_con, CfbSchema.LOC_SEQ);
+
+ Object[][] values = { {
+ locId,
+ loc.getClassName(),
+ Long.valueOf(loc.getStart()),
+ Long.valueOf(loc.getEnd())
+ } };
+ m_driver.insert(m_con, CfbSchema.LOCATIONS, values);
+
+ return locId;
+ }
}
--- /dev/null
+package net.jaekl.cfb.store;
+
+import net.jaekl.cfb.xml.BugClass;
+import net.jaekl.cfb.xml.BugMethod;
+import net.jaekl.cfb.xml.SourceLine;
+
+public class Location {
+ String m_className;
+ String m_methodName;
+ int m_startLine;
+ int m_endLine;
+
+ public Location(SourceLine sourceLine)
+ {
+ init(sourceLine);
+ }
+
+ public Location(BugMethod method)
+ {
+ init(method.getSourceLines());
+ m_className = method.getClassName();
+ m_methodName = method.getMethodName();
+ }
+
+ public Location(BugClass bugClass)
+ {
+ init(bugClass.getSourceLines());
+ m_className = bugClass.getClassName();
+ }
+
+ private void init(SourceLine[] sourceLines)
+ {
+ if (sourceLines.length > 0) {
+ assert(null != sourceLines[0]);
+ init(sourceLines[0]);
+ }
+ }
+
+ private void init(SourceLine sourceLine)
+ {
+ m_className = sourceLine.getClassName();
+ m_methodName = null;
+ m_startLine = sourceLine.getStart();
+ m_endLine = sourceLine.getEnd();
+ }
+}
m_className = "";
m_lines = new ArrayList<SourceLine>();
}
+
+ public String getClassName() { return m_className; }
+ public SourceLine[] getSourceLines() { return m_lines.toArray(new SourceLine[m_lines.size()]); }
@Override
public void handleMainAttributes(Attributes attr) throws MissingAttributeException
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
import net.jaekl.qd.xml.ParseResult;
import net.jaekl.qd.xml.XmlParseException;
m_bugs = new ArrayList<BugInstance>();
}
- public int size() { return m_bugs.size(); }
- public BugInstance get(int idx) { return m_bugs.get(idx); }
+ public List<BugInstance> getBugs() { return Collections.unmodifiableList(m_bugs); }
@Override
public void endContents(String uri, String localName, String qName, String chars)
import java.io.PrintWriter;
import java.util.ArrayList;
+import javax.tools.JavaFileManager.Location;
+
import org.xml.sax.Attributes;
import net.jaekl.qd.xml.MissingAttributeException;
m_sourceLines = new ArrayList<SourceLine>();
}
+ public String getClassName() { return m_className; }
+ public String getMethodName() { return m_methodName; }
+ public SourceLine[] getSourceLines() { return m_sourceLines.toArray(new SourceLine[m_sourceLines.size()]); }
+
@Override
public void handleMainAttributes(Attributes attr) throws MissingAttributeException
{
m_start = m_end = (-1);
}
+ public String getClassName() { return m_className; }
+ public int getStart() { return m_start; }
+ public int getEnd() { return m_end; }
+
@Override
public void handleMainAttributes(Attributes attr) throws MissingAttributeException {
String scratch;
String m_descr;
String m_abbrev;
String m_details;
+ long m_id;
public BugCategory()
{
public String getAbbrev() { return m_abbrev; }
public String getDetails() { return m_details; }
+ public void setId(long id) { m_id = id; }
+ public long getId() { return m_id; }
+
@Override
public void endContents(String uri, String localName, String qName, String chars) throws XmlParseException
{
String m_short;
String m_long;
String m_details;
+ long m_id;
public BugPattern()
{
public String getLong() { return m_long; }
public String getDetails() { return m_details; }
+ public void setId(long id) { m_id = id; }
+ public long getId() { return m_id; }
+
@Override
public void endContents(String uri, String localName, String qName, String chars) throws XmlParseException
{
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.HashMap;
+import java.util.List;
import net.jaekl.cfb.xml.BugCollection;
import net.jaekl.cfb.xml.BugInstance;
assertNotNull(analysis);
BugCollection bugColl = analysis.getBugCollection();
+ List<BugInstance> bugs = bugColl.getBugs();
assertNotNull(bugColl);
- assertEquals(2, bugColl.size());
+ assertEquals(2, bugs.size());
HashMap<String, BugInstance> typeMap = new HashMap<String, BugInstance>();
- for (int idx = 0; idx < bugColl.size(); ++idx) {
- inst = bugColl.get(idx);
- typeMap.put(inst.getType(), inst);
+ for (BugInstance bug : bugs) {
+ typeMap.put(bug.getType(), bug);
}
inst = typeMap.get("DM_DEFAULT_ENCODING");