From: Chris Jaekl Date: Wed, 23 Sep 2015 12:56:56 +0000 (+0900) Subject: Add code to load bug categories and patterns from the FindBugs messages.xml file. X-Git-Url: https://jaekl.net/gitweb/?a=commitdiff_plain;h=3c10b6100c6035a65ce37dea846b027135289f67;p=cfb.git Add code to load bug categories and patterns from the FindBugs messages.xml file. --- diff --git a/prod/cfb.properties b/prod/cfb.properties index d897971..78c3352 100644 --- a/prod/cfb.properties +++ b/prod/cfb.properties @@ -1,4 +1,5 @@ +analysis.failed=Attempt to analyze source code failed. Will now stop. cannot.connect.to.db=Unable to connect to database {2} on {0}:{1} as user {3}. cannot.exec=Got result code {1} when attempting to execute command-line: {0} stderr.was=-----8<------ Error (stderr) output was: ------8<----- -stdout.was=-----8<----- Console (stdout) output was: -----8<----- \ No newline at end of file +stdout.was=-----8<----- Console (stdout) output was: -----8<----- diff --git a/prod/net/jaekl/cfb/CFB.java b/prod/net/jaekl/cfb/CFB.java index d587c18..28d2b1c 100644 --- a/prod/net/jaekl/cfb/CFB.java +++ b/prod/net/jaekl/cfb/CFB.java @@ -14,12 +14,15 @@ import java.sql.Connection; import java.sql.SQLException; import java.text.MessageFormat; import java.util.Locale; +import java.util.Locale.Category; import net.jaekl.cfb.analyze.Analysis; import net.jaekl.cfb.analyze.Analyzer; +import net.jaekl.cfb.analyze.MessageMap; import net.jaekl.cfb.db.CfbSchema; import net.jaekl.cfb.db.driver.DbDriver; import net.jaekl.cfb.db.driver.PostgresqlDriver; +import net.jaekl.cfb.store.DbStore; import net.jaekl.qd.xml.XmlParseException; import org.apache.commons.cli.CommandLine; @@ -27,6 +30,7 @@ import org.apache.commons.cli.GnuParser; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; +import org.xml.sax.SAXException; public class CFB { DbDriver m_driver; @@ -42,6 +46,7 @@ public class CFB { int m_port; // db port String m_user; // db user String m_pass; // db password + String m_buildNum; // build number (version) CFB(Locale locale) { m_driver = new PostgresqlDriver(); @@ -56,6 +61,7 @@ public class CFB { m_port = 5432; m_pass = ""; m_user = "user"; + m_buildNum = null; } Options createOptions() { @@ -64,6 +70,7 @@ public class CFB { opt.addOption("d", "dbname", true, "DB name"); opt.addOption("f", "fbp", true, "FindBugsProject file"); opt.addOption("h", "host", true, "DB hostname"); + opt.addOption("n", "number", true, "Build number (version)"); opt.addOption("p", "pass", true, "DB password"); opt.addOption("t", "port", true, "DB port"); opt.addOption("u", "user", true, "DB username"); @@ -85,6 +92,9 @@ public class CFB { if (line.hasOption("h")) { m_host = line.getOptionValue("h"); } + if (line.hasOption("n")) { + m_buildNum = line.getOptionValue("n"); + } if (line.hasOption("p")) { m_pass = line.getOptionValue("p"); } @@ -122,6 +132,10 @@ public class CFB { return System.getProperty(propName); } + File getFindBugsDir() { + return (null != m_fbDir) ? m_fbDir : new File("."); + } + void initArgs() { String findBugsDir = getenv("FINDBUGS_HOME"); if (null != findBugsDir) { @@ -133,7 +147,7 @@ public class CFB { } } - void doMain(PrintWriter pw, String[] args) throws SQLException, IOException, XmlParseException { + void doMain(PrintWriter pw, String[] args) throws SQLException, IOException, XmlParseException, SAXException { initArgs(); // read environment and system properties if ( ! parseArgs(pw, args) ) { return; @@ -143,21 +157,38 @@ public class CFB { m_schema.ensureDbInitialized(con); } catch (SQLException exc) { - String cannotConnectFormat = trans(CfbBundle.CANNOT_CONNECT); - String cannotConnect = MessageFormat.format(cannotConnectFormat, m_host, ""+m_port, m_dbName, m_user); - exc.printStackTrace(pw); - pw.println(cannotConnect); + reportUnableToConnect(pw, exc); return; } - File findBugsDir = (null != m_fbDir) ? m_fbDir : new File("."); + File findBugsDir = getFindBugsDir(); File workDir = new File("."); - Analyzer analyzer = new Analyzer(findBugsDir); - Analysis analysis = analyzer.analyze(pw, workDir, m_fbp); - if (null != analysis) { - // TODO + MessageMap messageMap = new MessageMap(); + messageMap.load(findBugsDir, Locale.getDefault(Category.DISPLAY)); + Analyzer analyzer = new Analyzer(messageMap); + Analysis analysis = analyzer.analyze(pw, workDir, m_fbp, m_buildNum); + if (null == analysis) { + pw.println(trans(CfbBundle.ANALYSIS_FAILED)); + return; + } + + try (Connection con = m_driver.connect(m_host, m_port, m_dbName, m_user, m_pass)) { + DbStore store = new DbStore(con); + + store.put(analysis); + } + catch (SQLException exc) { + reportUnableToConnect(pw, exc); + return; } } + + private void reportUnableToConnect(PrintWriter pw, SQLException exc) { + String cannotConnectFormat = trans(CfbBundle.CANNOT_CONNECT); + String cannotConnect = MessageFormat.format(cannotConnectFormat, m_host, ""+m_port, m_dbName, m_user); + exc.printStackTrace(pw); + pw.println(cannotConnect); + } public static void main(String[] args) { CFB cfb = new CFB(Locale.getDefault()); @@ -165,7 +196,7 @@ public class CFB { try (PrintWriter pw = new PrintWriter(System.out)){ cfb.doMain(pw, args); pw.flush(); - } catch (SQLException | IOException | XmlParseException exc) { + } catch (SQLException | IOException | XmlParseException | SAXException exc) { exc.printStackTrace(); } } diff --git a/prod/net/jaekl/cfb/CfbBundle.java b/prod/net/jaekl/cfb/CfbBundle.java index c83182c..335a68d 100644 --- a/prod/net/jaekl/cfb/CfbBundle.java +++ b/prod/net/jaekl/cfb/CfbBundle.java @@ -10,6 +10,7 @@ import java.util.concurrent.ConcurrentHashMap; import net.jaekl.qd.QDBundleFactory; public class CfbBundle { + public static final String ANALYSIS_FAILED = "analysis.failed"; public static final String CANNOT_CONNECT = "cannot.connect.to.db"; public static final String CANNOT_EXEC = "cannot.exec"; public static final String STDERR_WAS = "stderr.was"; diff --git a/prod/net/jaekl/cfb/analyze/Analysis.java b/prod/net/jaekl/cfb/analyze/Analysis.java index ab9fffb..e2f090f 100644 --- a/prod/net/jaekl/cfb/analyze/Analysis.java +++ b/prod/net/jaekl/cfb/analyze/Analysis.java @@ -5,6 +5,7 @@ package net.jaekl.cfb.analyze; import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintWriter; +import java.util.Date; import net.jaekl.cfb.xml.BugCollection; import net.jaekl.qd.xml.ParseErrorHandler; @@ -17,9 +18,13 @@ import org.xml.sax.helpers.XMLReaderFactory; public class Analysis { BugCollection m_bugCollection; + String m_buildNumber; + Date m_date; // Date/time when analysis was started - public Analysis() { + public Analysis(String buildNumber) { m_bugCollection = null; + m_buildNumber = buildNumber; + m_date = new Date(); } public BugCollection getBugCollection() { return m_bugCollection; } diff --git a/prod/net/jaekl/cfb/analyze/Analyzer.java b/prod/net/jaekl/cfb/analyze/Analyzer.java index 5e72fe0..03aeb1c 100644 --- a/prod/net/jaekl/cfb/analyze/Analyzer.java +++ b/prod/net/jaekl/cfb/analyze/Analyzer.java @@ -17,14 +17,15 @@ import net.jaekl.cfb.util.Command; import net.jaekl.qd.xml.XmlParseException; public class Analyzer { - File m_findbugsDir; + MessageMap m_msgMap; - public Analyzer(File findbugsDir) { - m_findbugsDir = findbugsDir; + public Analyzer(MessageMap msgMap) { + m_msgMap = msgMap; } - public Analysis analyze(PrintWriter pw, File workDir, File fbp) throws IOException, XmlParseException { - Analysis result = new Analysis(); + public Analysis analyze(PrintWriter pw, File workDir, File fbp, String buildNumber) throws IOException, XmlParseException, SAXException + { + Analysis result = new Analysis(buildNumber); File fbOutput = outputWorkFile(workDir, fbp); @@ -45,7 +46,7 @@ public class Analyzer { return null; } - result = parseFbOutput(new InputSource(fbOutput.getAbsolutePath())); + result.parse(new InputSource(fbOutput.getAbsolutePath())); result.dump(pw); return result; } @@ -62,7 +63,7 @@ public class Analyzer { StringBuilder sb = new StringBuilder(); - sb.append(m_findbugsDir.getAbsolutePath()) + sb.append(m_msgMap.getFindBugsDir().getAbsolutePath()) .append(File.separator) .append("bin") .append(File.separator) @@ -95,17 +96,4 @@ public class Analyzer { return new File(workPath + File.separator + projName + ".xml"); } - - // Parse the output.xml that resulted from a FindBugs run, - // and store its findings into an Analysis object. - Analysis parseFbOutput(InputSource fbOutput) throws XmlParseException - { - Analysis result = new Analysis(); - try { - result.parse(fbOutput); - } catch (IOException | SAXException exc) { - throw new XmlParseException(exc); - } - return result; - } } diff --git a/prod/net/jaekl/cfb/analyze/MessageMap.java b/prod/net/jaekl/cfb/analyze/MessageMap.java new file mode 100644 index 0000000..ac5288f --- /dev/null +++ b/prod/net/jaekl/cfb/analyze/MessageMap.java @@ -0,0 +1,63 @@ +package net.jaekl.cfb.analyze; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Locale; + +import net.jaekl.cfb.xml.messages.MessageCollection; +import net.jaekl.qd.xml.ParseErrorHandler; +import net.jaekl.qd.xml.ParseHandler; + +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.XMLReaderFactory; + +public class MessageMap { + static final String MESSAGES = "messages"; + static final String XML = "xml"; + + MessageCollection m_msgColl; + File m_findBugsDir; + + public MessageMap() { + m_msgColl = null; + m_findBugsDir = null; + } + + public MessageCollection getColl() { return m_msgColl; } + public File getFindBugsDir() { return m_findBugsDir; } + + public void load(File findBugsDir, Locale locale) throws FileNotFoundException, IOException, SAXException + { + m_findBugsDir = findBugsDir; + + String langName = locale.getLanguage(); + + File msgXml = new File(findBugsDir.getAbsolutePath() + File.separator + MESSAGES + "_" + langName + "." + XML); + if (! msgXml.canRead()) { + msgXml = new File(findBugsDir.getAbsolutePath() + File.separator + MESSAGES + "." + XML); + } + + if (! msgXml.canRead()) { + throw new FileNotFoundException(msgXml.getAbsolutePath()); + } + + parse(new InputSource(new FileInputStream(msgXml))); + + } + + void parse(InputSource xml) throws FileNotFoundException, IOException, SAXException + { + m_msgColl = new MessageCollection(); + + XMLReader reader = XMLReaderFactory.createXMLReader(); + ParseHandler ph = new ParseHandler(m_msgColl); + ParseErrorHandler peh = new ParseErrorHandler(); + reader.setContentHandler(ph); + reader.setErrorHandler(peh); + reader.parse(xml); + } +} diff --git a/prod/net/jaekl/cfb/db/CfbSchema.java b/prod/net/jaekl/cfb/db/CfbSchema.java index d2333fa..e214b1b 100644 --- a/prod/net/jaekl/cfb/db/CfbSchema.java +++ b/prod/net/jaekl/cfb/db/CfbSchema.java @@ -15,21 +15,19 @@ public class CfbSchema extends Schema { // } private static final Object[][][] TABLES = { { - { "BUGS" }, + // Description of each possible bug + { "BUG" }, { "BUGID", INTEGER, -1, NOT_NULL }, - { "TYPE", VARCHAR, 80, NOT_NULL }, - { "SHORTDESCR", VARCHAR, 128, NOT_NULL }, - { "LONGDESCR", VARCHAR, 128, NOT_NULL }, - { "DETAILS", VARCHAR, 4096, NOT_NULL } + { "TYPE", VARCHAR, 80, NOT_NULL } }, { - { "CATEGORIES" }, + // Description of each possible bug category + { "CATEGORY" }, { "CATEGORYID", INTEGER, -1, NOT_NULL }, - { "DESCRIPTION", VARCHAR, 128, NOT_NULL }, - { "ABBREVIATION", CHAR, 1, NOT_NULL }, - { "DETAILS", VARCHAR, 4096, NOT_NULL } + { "CATEGORY", VARCHAR, 80, NOT_NULL } }, { + // One BugInstance, found during an analysis { "FOUND" }, { "FOUNDID", INTEGER, -1, NOT_NULL }, { "BUGID", INTEGER, -1, NOT_NULL }, @@ -39,6 +37,7 @@ public class CfbSchema extends Schema { { "THIRDLOCID", INTEGER, -1, NULL } }, { + // Location in the source code referenced by a BugInstance { "LOCATION" }, { "LOCID", INTEGER, -1, NOT_NULL }, { "CLASSNAME", VARCHAR, 256, NOT_NULL }, @@ -47,7 +46,7 @@ public class CfbSchema extends Schema { }, { // Runs of FindBugs, normally one per build version - { "RUNS" }, + { "RUN" }, { "RUNID", INTEGER, -1, NOT_NULL }, { "VERSION", VARCHAR, 32, NULL }, { "STARTTIME", TIMESTAMPTZ, -1, NOT_NULL }, diff --git a/prod/net/jaekl/cfb/db/Condition.java b/prod/net/jaekl/cfb/db/Condition.java new file mode 100644 index 0000000..b221551 --- /dev/null +++ b/prod/net/jaekl/cfb/db/Condition.java @@ -0,0 +1,23 @@ +package net.jaekl.cfb.db; + +public class Condition { + + Column m_column; + Object m_value; + Operation m_operation; + + public Condition(Column column, Object value, Operation operation) + { + m_column = column; + m_value = value; + m_operation = operation; + } + + public Condition(Column column, Object value) { + this(column, value, Operation.EQUAL); + } + + public Column getColumn() { return m_column; } + public Object getValue() { return m_value; } + public Operation getOperation() { return m_operation; } +} diff --git a/prod/net/jaekl/cfb/db/Operation.java b/prod/net/jaekl/cfb/db/Operation.java new file mode 100644 index 0000000..1751c31 --- /dev/null +++ b/prod/net/jaekl/cfb/db/Operation.java @@ -0,0 +1,20 @@ +package net.jaekl.cfb.db; + +public enum Operation { + EQUAL(" = ? ", true), + LESS_THAN(" < ? ", true), + GREATER_THAN(" > ? ", true), + NULL(" is null ", false), + NOT_NULL(" is not null ", false); + + String m_sql; + boolean m_hasParam; + + Operation(String sql, boolean hasParam) { + m_sql = sql; + m_hasParam = hasParam; + } + + public String getSql() { return m_sql; } + public boolean hasParam() { return m_hasParam; } +} diff --git a/prod/net/jaekl/cfb/db/Row.java b/prod/net/jaekl/cfb/db/Row.java new file mode 100644 index 0000000..c5d5478 --- /dev/null +++ b/prod/net/jaekl/cfb/db/Row.java @@ -0,0 +1,46 @@ +package net.jaekl.cfb.db; + + +public class Row { + Column[] m_columns; + Object[] m_values; + + public Row(Column[] columns, Object[] values) + { + m_columns = columns.clone(); + m_values = values.clone(); + } + + public int getNumColumns() { return m_columns.length; } + + public String getString(int index) throws TypeMismatchException { + checkType(index, Column.Type.VARCHAR); + return (String)m_values[index]; + } + + public int getInt(int index) throws TypeMismatchException + { + checkType(index, Column.Type.INTEGER); + Number num = (Number)m_values[index]; + return num.intValue(); + } + + public long getLong(int index) throws TypeMismatchException + { + checkType(index, Column.Type.INTEGER); + Number num = (Number)m_values[index]; + return num.longValue(); + } + + protected void checkType(int index, Column.Type type) throws TypeMismatchException { + Column column = m_columns[index]; + if (column.getType().equals(type)) { + return; + } + + String msg = "Column " + column.getName() + + " is of type " + column.getType().name() + + " which cannot be coerced to type " + type.name() + "."; + throw new TypeMismatchException(msg); + } +} diff --git a/prod/net/jaekl/cfb/db/Schema.java b/prod/net/jaekl/cfb/db/Schema.java index b6dc601..f802b58 100644 --- a/prod/net/jaekl/cfb/db/Schema.java +++ b/prod/net/jaekl/cfb/db/Schema.java @@ -16,11 +16,13 @@ public class Schema { String m_name; DbDriver m_driver; ArrayList m_tables; + ArrayList m_sequences; public Schema(String name, DbDriver driver) { m_name = name; m_driver = driver; m_tables = new ArrayList
(); + m_sequences = new ArrayList(); } public boolean ensureDbInitialized(Connection con) throws SQLException { @@ -34,6 +36,10 @@ public class Schema { return false; } + if (!createAllSequences(con)) { + return false; + } + return true; } @@ -73,6 +79,15 @@ public class Schema { return true; } + boolean createAllSequences(Connection con) throws SQLException { + for (Sequence seq : m_sequences) { + if (!m_driver.createSequence(con, seq)) { + return false; + } + } + return true; + } + void addTable(Table table) { m_tables.add(table); } @@ -88,5 +103,9 @@ public class Schema { for (Object[][] table : tables) { addTable(Table.construct(table)); } - } + } + + void addSequence(Sequence seq) { + m_sequences.add(seq); + } } diff --git a/prod/net/jaekl/cfb/db/Sequence.java b/prod/net/jaekl/cfb/db/Sequence.java new file mode 100644 index 0000000..55c3449 --- /dev/null +++ b/prod/net/jaekl/cfb/db/Sequence.java @@ -0,0 +1,11 @@ +package net.jaekl.cfb.db; + +public class Sequence { + String m_name; + + public Sequence(String name) { + m_name = name; + } + + public String getName() { return m_name; } +} diff --git a/prod/net/jaekl/cfb/db/TypeMismatchException.java b/prod/net/jaekl/cfb/db/TypeMismatchException.java new file mode 100644 index 0000000..db7a303 --- /dev/null +++ b/prod/net/jaekl/cfb/db/TypeMismatchException.java @@ -0,0 +1,9 @@ +package net.jaekl.cfb.db; + +public class TypeMismatchException extends Exception { + private static final long serialVersionUID = 1L; + + public TypeMismatchException(String msg) { + super(msg); + } +} diff --git a/prod/net/jaekl/cfb/db/driver/DbDriver.java b/prod/net/jaekl/cfb/db/driver/DbDriver.java index c864957..525ca70 100644 --- a/prod/net/jaekl/cfb/db/driver/DbDriver.java +++ b/prod/net/jaekl/cfb/db/driver/DbDriver.java @@ -8,9 +8,14 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; import net.jaekl.cfb.db.Column; import net.jaekl.cfb.db.Column.Type; +import net.jaekl.cfb.db.Condition; +import net.jaekl.cfb.db.Row; +import net.jaekl.cfb.db.Sequence; import net.jaekl.cfb.db.Table; public abstract class DbDriver { @@ -35,7 +40,97 @@ public abstract class DbDriver { return true; } - public abstract ResultSet selectColumnsFromWhere(Column[] columns, Table[] tables, String where); + public boolean createSequence(Connection con, Sequence seq) throws SQLException + { + String sql = createSequenceSql(seq); + try (PreparedStatement ps = con.prepareStatement(sql)) { + ps.executeUpdate(); + } + catch (SQLException exc) { + throw new SQLException("Failed to executeUpdate: " + sql, exc); + } + + return true; + } + + public List select(Connection con, Column[] columns, Table[] tables, Condition[] conditions) + throws SQLException + { + String sql = selectSql(columns, tables, conditions); + ArrayList result = new ArrayList(); + + try (PreparedStatement ps = con.prepareStatement(sql)) { + int index = 0; + for (Condition condition : conditions) { + if (condition.getOperation().hasParam()) { + index++; + ps.setObject(index, condition.getValue()); + } + } + + try (ResultSet rs = ps.executeQuery()) { + while (rs.next()) { + Object[] values = new Object[columns.length]; + for (index = 0; index < columns.length; ++index) { + values[index] = rs.getObject(index); + } + Row row = new Row(columns, values); + result.add(row); + } + } + } + + return result; + } + + protected String selectSql(Column[] columns, Table[] tables, Condition[] conditions) + { + StringBuilder sb = new StringBuilder("SELECT "); + + boolean firstColumn = true; + for (Column column : columns) { + if (firstColumn) { + firstColumn = false; + } + else { + sb.append(", "); + } + sb.append(column.getName()); + } + + sb.append(" FROM "); + + boolean firstTable = true; + for (Table table : tables) { + if (firstTable) { + firstTable = false; + } + else { + sb.append(", "); + } + sb.append(table.getName()); + } + + if (null != conditions && conditions.length > 0) { + sb.append(" WHERE "); + + boolean firstCondition = true; + + for (Condition condition : conditions) { + if (firstCondition) { + firstCondition = false; + } + else { + sb.append(" AND "); + } + + sb.append(condition.getColumn().getName()) + .append(condition.getOperation().getSql()); + } + } + + return sb.toString(); + } protected String typeName(Type type) { return type.toString(); @@ -81,4 +176,11 @@ public abstract class DbDriver { return sb.toString(); } + + protected String createSequenceSql(Sequence seq) { + assert(null != seq); + assert(null != seq.getName()); + + return "CREATE SEQUENCE " + seq.getName(); + } } diff --git a/prod/net/jaekl/cfb/db/driver/PostgresqlDriver.java b/prod/net/jaekl/cfb/db/driver/PostgresqlDriver.java index 0fe1f08..a8d28f9 100644 --- a/prod/net/jaekl/cfb/db/driver/PostgresqlDriver.java +++ b/prod/net/jaekl/cfb/db/driver/PostgresqlDriver.java @@ -4,13 +4,9 @@ package net.jaekl.cfb.db.driver; import java.sql.Connection; import java.sql.DriverManager; -import java.sql.ResultSet; import java.sql.SQLException; import java.util.Properties; -import net.jaekl.cfb.db.Column; -import net.jaekl.cfb.db.Table; - public class PostgresqlDriver extends DbDriver { @Override @@ -30,11 +26,4 @@ public class PostgresqlDriver extends DbDriver { //props.setProperty("ssl", "true"); return DriverManager.getConnection(url, props); } - - @Override - public ResultSet selectColumnsFromWhere(Column[] columns, Table[] tables, String where) { - // TODO Auto-generated method stub - return null; - } - } diff --git a/prod/net/jaekl/cfb/store/DbStore.java b/prod/net/jaekl/cfb/store/DbStore.java new file mode 100644 index 0000000..7a1e610 --- /dev/null +++ b/prod/net/jaekl/cfb/store/DbStore.java @@ -0,0 +1,23 @@ +package net.jaekl.cfb.store; + +import java.sql.Connection; + +import net.jaekl.cfb.analyze.Analysis; + +public class DbStore { + Connection m_conn; + + public DbStore(Connection conn) { + m_conn = conn; + } + + public boolean put(Analysis analysis) { + if (null == analysis) { + return false; + } + + + + return true; + } +} diff --git a/prod/net/jaekl/cfb/xml/messages/BugCategory.java b/prod/net/jaekl/cfb/xml/messages/BugCategory.java new file mode 100644 index 0000000..6b5b492 --- /dev/null +++ b/prod/net/jaekl/cfb/xml/messages/BugCategory.java @@ -0,0 +1,62 @@ +package net.jaekl.cfb.xml.messages; + +import org.xml.sax.Attributes; + +import net.jaekl.qd.xml.MissingAttributeException; +import net.jaekl.qd.xml.ParseResult; +import net.jaekl.qd.xml.XmlParseException; + +public class BugCategory extends ParseResult { + static final String CATEGORY = "category"; // attribute name + static final String DESCRIPTION = "Description"; + static final String ABBREVIATION = "Abbreviation"; + static final String DETAILS = "Details"; + + static final String TAG = "BugCategory"; + static final String[] INTERNAL = { DESCRIPTION, ABBREVIATION, DETAILS }; + static final Object[][] EXTERNAL = { }; + + String m_category; + String m_descr; + String m_abbrev; + String m_details; + + public BugCategory(String tagName, String[] internalMemberTags, Object[][] externalParserTags) + { + super(tagName, internalMemberTags, externalParserTags); + m_category = m_descr = m_abbrev = m_details = ""; + } + + public String getCategory() { return m_category; } + public String getDescr() { return m_descr; } + public String getAbbrev() { return m_abbrev; } + public String getDetails() { return m_details; } + + @Override + public void endContents(String uri, String localName, String qName, String chars) throws XmlParseException + { + if (DESCRIPTION.equals(localName)) { + m_descr = chars; + } + else if (ABBREVIATION.equals(localName)) { + m_abbrev = chars; + } + else if (DETAILS.equals(localName)) { + m_details = chars; + } + } + + @Override + public void endExternal(String uri, String localName, String qName) + throws XmlParseException + { + // nothing to do + } + + // Called once for this tag itself + @Override + public void handleMainAttributes(Attributes attr) throws MissingAttributeException { + m_category = this.getRequiredAttr(TAG, attr, CATEGORY); + } + +} diff --git a/prod/net/jaekl/cfb/xml/messages/BugPattern.java b/prod/net/jaekl/cfb/xml/messages/BugPattern.java new file mode 100644 index 0000000..82ada92 --- /dev/null +++ b/prod/net/jaekl/cfb/xml/messages/BugPattern.java @@ -0,0 +1,60 @@ +package net.jaekl.cfb.xml.messages; + +import net.jaekl.qd.xml.MissingAttributeException; +import net.jaekl.qd.xml.ParseResult; +import net.jaekl.qd.xml.XmlParseException; + +import org.xml.sax.Attributes; + +public class BugPattern extends ParseResult { + static final String TYPE = "type"; + static final String SHORT = "ShortDescription"; + static final String LONG = "LongDescription"; + static final String DETAILS = "Details"; + + static final String TAG = "BugPattern"; + static final String[] INTERNAL = { SHORT, LONG, DETAILS }; + static final Object[][] EXTERNAL = { }; + + String m_type; + String m_short; + String m_long; + String m_details; + + public BugPattern(String tagName, String[] internalMemberTags, Object[][] externalParserTags) + { + super(tagName, internalMemberTags, externalParserTags); + m_type = m_short = m_long = m_details = ""; + } + + public String getType() { return m_type; } + public String getShort() { return m_short; } + public String getLong() { return m_long; } + public String getDetails() { return m_details; } + + @Override + public void endContents(String uri, String localName, String qName, String chars) throws XmlParseException + { + if (SHORT.equals(localName)) { + m_short = chars; + } + else if (LONG.equals(localName)) { + m_long = chars; + } + else if (DETAILS.equals(localName)) { + m_details = chars; + } + } + + @Override + public void endExternal(String uri, String localName, String qName) throws XmlParseException + { + // nothing to do + } + + // Called once for this tag itself + @Override + public void handleMainAttributes(Attributes attr) throws MissingAttributeException { + m_type = this.getRequiredAttr(TAG, attr, TYPE); + } +} diff --git a/prod/net/jaekl/cfb/xml/messages/MessageCollection.java b/prod/net/jaekl/cfb/xml/messages/MessageCollection.java new file mode 100644 index 0000000..cf16e23 --- /dev/null +++ b/prod/net/jaekl/cfb/xml/messages/MessageCollection.java @@ -0,0 +1,55 @@ +package net.jaekl.cfb.xml.messages; + +import java.util.HashMap; + +import net.jaekl.qd.xml.ParseResult; +import net.jaekl.qd.xml.XmlParseException; + +public class MessageCollection extends ParseResult { + static final String TAG = "MessageCollection"; + static final String[] INTERNAL = { }; + static final Object[][] EXTERNAL = { { BugCategory.TAG, BugCategory.class }, + { BugPattern.TAG, BugPattern.class } }; + + HashMap m_categories; + HashMap m_patterns; + + public MessageCollection() + { + super(TAG, INTERNAL, EXTERNAL); + m_categories = new HashMap(); + m_patterns = new HashMap(); + } + + @Override + public void endContents(String uri, String localName, String qName, String chars) + throws XmlParseException + { + // Nothing to do + } + + @Override + public void endExternal(String uri, String localName, String qName) throws XmlParseException + { + ParseResult[] prs; + + prs = collectParsedChildren(BugCategory.class); + if (null != prs && prs.length > 0) { + for (ParseResult pr : prs) { + assert(pr instanceof BugCategory); + BugCategory bc = (BugCategory)pr; + m_categories.put(bc.getCategory(), bc); + } + } + + prs = collectParsedChildren(BugPattern.class); + if (null != prs && prs.length > 0) { + for (ParseResult pr : prs) { + assert(pr instanceof BugPattern); + BugPattern bp = (BugPattern) pr; + m_patterns.put(bp.getType(), bp); + } + } + } + +} diff --git a/test/net/jaekl/cfb/analyze/AnalysisTest.java b/test/net/jaekl/cfb/analyze/AnalysisTest.java new file mode 100644 index 0000000..f0b28c7 --- /dev/null +++ b/test/net/jaekl/cfb/analyze/AnalysisTest.java @@ -0,0 +1,86 @@ +package net.jaekl.cfb.analyze; + +import static org.junit.Assert.*; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.HashMap; + +import net.jaekl.cfb.xml.BugCollection; +import net.jaekl.cfb.xml.BugInstance; +import net.jaekl.qd.xml.XmlParseException; + +import org.junit.Test; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +public class AnalysisTest { + private static final String UTF8 = "utf-8"; + + private static final String SAMPLE1_XML = "" + + "\n" + + "\n" + + "\n" + + "/home/chris/prog/././cfb/bin\n" + + "/home/chris/prog/././cfb/src\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n"; + + @Test + public void testParseSample1() throws IOException, XmlParseException, SAXException { + Charset utf8 = Charset.forName(UTF8); + BugInstance inst = null; + + try ( ByteArrayInputStream bais = new ByteArrayInputStream(SAMPLE1_XML.getBytes(utf8))) + { + InputSource inputSource = new InputSource(bais); + Analysis analysis = new Analysis(null); + analysis.parse(inputSource); + + assertNotNull(analysis); + + BugCollection bugColl = analysis.getBugCollection(); + + assertNotNull(bugColl); + assertEquals(2, bugColl.size()); + + HashMap typeMap = new HashMap(); + for (int idx = 0; idx < bugColl.size(); ++idx) { + inst = bugColl.get(idx); + typeMap.put(inst.getType(), inst); + } + + inst = typeMap.get("DM_DEFAULT_ENCODING"); + assertNotNull(inst); + + inst = typeMap.get("RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE"); + assertNotNull(inst); + } + } +} diff --git a/test/net/jaekl/cfb/analyze/AnalyzerTest.java b/test/net/jaekl/cfb/analyze/AnalyzerTest.java index 9691efa..85bd3d2 100644 --- a/test/net/jaekl/cfb/analyze/AnalyzerTest.java +++ b/test/net/jaekl/cfb/analyze/AnalyzerTest.java @@ -1,57 +1,12 @@ package net.jaekl.cfb.analyze; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; -import java.io.ByteArrayInputStream; import java.io.File; -import java.io.IOException; -import java.nio.charset.Charset; -import java.util.HashMap; - -import net.jaekl.cfb.xml.BugCollection; -import net.jaekl.cfb.xml.BugInstance; -import net.jaekl.qd.xml.XmlParseException; import org.junit.Test; -import org.xml.sax.InputSource; public class AnalyzerTest { - private static final String UTF8 = "utf-8"; - - private static final String SAMPLE1_XML = "" - + "\n" - + "\n" - + "\n" - + "/home/chris/prog/././cfb/bin\n" - + "/home/chris/prog/././cfb/src\n" - + "\n" - + "\n" - + "\n" - + "\n" - + "\n" - + "\n" - + "\n" - + "\n" - + "\n" - + "\n" - + "\n" - + "\n" - + "\n" - + "\n" - + "\n" - + "\n" - + "\n" - + "\n" - + "\n" - + "\n" - + "\n" - + "\n" - + "\n" - + "\n" - + "\n" - + "\n" - + "\n"; - @Test public void testOutputWorkFile() { final String[][] DATA = { @@ -71,41 +26,13 @@ public class AnalyzerTest { File fbp = new File(datum[2]); File expected = new File(datum[3]); - Analyzer analyzer = new Analyzer(findBugsDir); + MessageMapMock mmm = new MessageMapMock(); + mmm.mock_setFindBugsDir(findBugsDir); + + Analyzer analyzer = new Analyzer(mmm); File actual = analyzer.outputWorkFile(workDir, fbp); assertEquals(expected.getAbsolutePath(), actual.getAbsolutePath()); } } - @Test - public void testParseSample1() throws IOException, XmlParseException { - Charset utf8 = Charset.forName(UTF8); - BugInstance inst = null; - - try ( ByteArrayInputStream bais = new ByteArrayInputStream(SAMPLE1_XML.getBytes(utf8))) - { - InputSource inputSource = new InputSource(bais); - Analyzer analyzer = new Analyzer(new File(".")); - Analysis analysis = analyzer.parseFbOutput(inputSource); - - assertNotNull(analysis); - - BugCollection bugColl = analysis.getBugCollection(); - - assertNotNull(bugColl); - assertEquals(2, bugColl.size()); - - HashMap typeMap = new HashMap(); - for (int idx = 0; idx < bugColl.size(); ++idx) { - inst = bugColl.get(idx); - typeMap.put(inst.getType(), inst); - } - - inst = typeMap.get("DM_DEFAULT_ENCODING"); - assertNotNull(inst); - - inst = typeMap.get("RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE"); - assertNotNull(inst); - } - } } diff --git a/test/net/jaekl/cfb/analyze/MessageMapMock.java b/test/net/jaekl/cfb/analyze/MessageMapMock.java new file mode 100644 index 0000000..41956c2 --- /dev/null +++ b/test/net/jaekl/cfb/analyze/MessageMapMock.java @@ -0,0 +1,9 @@ +package net.jaekl.cfb.analyze; + +import java.io.File; + +public class MessageMapMock extends MessageMap { + void mock_setFindBugsDir(File fbd) { + this.m_findBugsDir = fbd; + } +}