From 8e5bdf0294e85b4419ee1e582d5677cccd3f736e Mon Sep 17 00:00:00 2001 From: Chris Jaekl Date: Sat, 3 Oct 2015 21:51:41 +0900 Subject: [PATCH] Add ability to load previously found bugs back out of the database. --- prod/net/jaekl/cfb/db/driver/DbDriver.java | 35 +++++ prod/net/jaekl/cfb/store/DbStore.java | 170 +++++++++++++-------- prod/net/jaekl/cfb/store/Location.java | 20 ++- prod/net/jaekl/cfb/xml/BugInstance.java | 120 ++++++++++++--- prod/net/jaekl/cfb/xml/BugMethod.java | 9 ++ prod/net/jaekl/cfb/xml/LocalVariable.java | 11 ++ prod/net/jaekl/qd/xml/ParseResult.java | 4 +- 7 files changed, 280 insertions(+), 89 deletions(-) diff --git a/prod/net/jaekl/cfb/db/driver/DbDriver.java b/prod/net/jaekl/cfb/db/driver/DbDriver.java index eea3e22..a756ab2 100644 --- a/prod/net/jaekl/cfb/db/driver/DbDriver.java +++ b/prod/net/jaekl/cfb/db/driver/DbDriver.java @@ -80,6 +80,22 @@ public abstract class DbDriver { } } + public Row selectExactlyOne(Connection con, Column[] columns, Table[] tables, Condition[] conditions) + throws SQLException + { + Sort[] sorts = new Sort[0]; + int limit = 2; + List rows = select(con, columns, tables, conditions, sorts, limit); + if (rows.size() < 1) { + throw new SQLException("Expected one result, but found none: ", selectSql(columns, tables, conditions, sorts, limit)); + } + if (rows.size() > 1) { + throw new SQLException("Expected one result, but found more than one: " + selectSql(columns, tables, conditions, sorts, limit)); + } + + return rows.get(0); + } + public List select(Connection con, Column[] columns, Table[] tables, Condition[] conditions) throws SQLException { @@ -131,6 +147,8 @@ public abstract class DbDriver { int count = 0; int pendingValues = 0; + assert( isValidInsert(table, values)); + String sql = insertSql(table); try (PreparedStatement ps = con.prepareStatement(sql)) @@ -372,4 +390,21 @@ public abstract class DbDriver { } abstract protected String nextValSql(Sequence seq); + + boolean isValidInsert(Table table, Object[][] values) + { + if (null == table) return false; + if (null == values) return false; + + for (Object[] rowValues : values) { + if (rowValues.length != table.getNumColumns()) { + return false; + } + for (int idx = 0; idx < rowValues.length; ++idx) { + + } + } + + return true; + } } diff --git a/prod/net/jaekl/cfb/store/DbStore.java b/prod/net/jaekl/cfb/store/DbStore.java index fed1f9d..69ea8bb 100644 --- a/prod/net/jaekl/cfb/store/DbStore.java +++ b/prod/net/jaekl/cfb/store/DbStore.java @@ -2,7 +2,6 @@ package net.jaekl.cfb.store; import java.sql.Connection; import java.sql.SQLException; -import java.util.ArrayList; import java.util.List; import net.jaekl.cfb.analyze.Analysis; @@ -15,12 +14,9 @@ import net.jaekl.cfb.db.Sort; import net.jaekl.cfb.db.Table; import net.jaekl.cfb.db.TypeMismatchException; import net.jaekl.cfb.db.driver.DbDriver; -import net.jaekl.cfb.xml.BugClass; import net.jaekl.cfb.xml.BugCollection; import net.jaekl.cfb.xml.BugInstance; -import net.jaekl.cfb.xml.BugMethod; import net.jaekl.cfb.xml.LocalVariable; -import net.jaekl.cfb.xml.SourceLine; import net.jaekl.cfb.xml.messages.MessageCollection; public class DbStore { @@ -80,18 +76,19 @@ public class DbStore { Long foundId = Long.valueOf(m_driver.nextVal(m_con, CfbSchema.FOUND_SEQ)); Long bugId = Long.valueOf(m_msgColl.getPattern(bug.getType()).getId()); Long categoryId = Long.valueOf(m_msgColl.getCategory(bug.getCategory()).getId()); - Location[] locs = computeLocations(bug); - Location firstLoc = (locs.length > 0) ? locs[0] : null; - Location secondLoc = (locs.length > 1) ? locs[1] : null; - Location thirdLoc = (locs.length > 2) ? locs[2] : null; + List locs = bug.getLocations(); + Location firstLoc = (locs.size() > 0) ? locs.get(0) : null; + Location secondLoc = (locs.size() > 1) ? locs.get(1) : null; + Location thirdLoc = (locs.size() > 2) ? locs.get(2) : null; values[row][0] = foundId; - values[row][1] = bugId; - values[row][2] = categoryId; - values[row][3] = getLocId(firstLoc); - values[row][4] = getLocId(secondLoc); - values[row][5] = getLocId(thirdLoc); - values[row][6] = getVarId(bug); + values[row][1] = runId; + values[row][2] = bugId; + values[row][3] = categoryId; + values[row][4] = getLocId(firstLoc); + values[row][5] = getLocId(secondLoc); + values[row][6] = getLocId(thirdLoc); + values[row][7] = getVarId(bug); row++; } @@ -99,61 +96,43 @@ public class DbStore { return (bugs.size() == count); } - Location[] computeLocations(BugInstance bug) + String getBugType(Long bugPatternId) throws SQLException, TypeMismatchException { - ArrayList locs = new ArrayList(); - - /* - Somewhat unfortunate special case. - The primary "location" for a bug instance is split between tags. - Most bugs have a pattern like this: - - ... - - - - ... - - - - The primary location for a bug is given by the with no role attribute, - but the inside that method describes the whole range of lines - covered by that Method, not the spot where the bug is located--that is given - by the that is a direct child fo the . - */ - - BugMethod primaryMethod = null; - SourceLine primaryLine = null; - - for (BugMethod method : bug.getMethods()) { - if (null != method.getRole()) { - primaryMethod = method; - break; - } - } - if (bug.getLines().size() > 0) { - primaryLine = bug.getLines().get(0); - } + Column[] columns = { CfbSchema.TYPE }; + Table[] tables = { CfbSchema.BUGS }; + Condition[] conditions = { new Condition(CfbSchema.BUGID, bugPatternId, Operation.EQUAL) }; + Row row = m_driver.selectExactlyOne(m_con, columns, tables, conditions); + String type = row.getString(0); + return type; + } + + String getCategoryName(Long categoryId) throws SQLException, TypeMismatchException + { + Column[] columns = { CfbSchema.CATEGORY }; + Table[] tables = { CfbSchema.CATEGORIES }; + Condition[] conditions = { new Condition(CfbSchema.CATEGORYID, categoryId, Operation.EQUAL) }; + Row row = m_driver.selectExactlyOne(m_con, columns, tables, conditions); + String name = row.getString(0); + return name; + } + + + Location getLoc(Long locId) throws SQLException, TypeMismatchException + { + Column[] columns = { CfbSchema.CLASSNAME, CfbSchema.METHODNAME, CfbSchema.METHODROLE, CfbSchema.STARTLINE, CfbSchema.ENDLINE }; + Table[] tables = { CfbSchema.LOCATIONS }; + Condition[] conditions = { new Condition(CfbSchema.LOCID, locId, Operation.EQUAL) }; - if ((null != primaryMethod) && (null != primaryLine)) { - locs.add(new Location(primaryMethod, primaryLine)); - } + Row row = m_driver.selectExactlyOne(m_con, columns, tables, conditions); - for (BugMethod method : bug.getMethods()) { - if (primaryMethod != method) { - locs.add(new Location(method)); - } - } - for (SourceLine line : bug.getLines()) { - if (primaryLine != line) { - locs.add(new Location(line)); - } - } - for (BugClass clazz : bug.getClasses()) { - locs.add(new Location(clazz)); - } + String className = row.getString(0); + String methodName = row.getString(1); + String methodRole = row.getString(2); + long startLine = row.getLong(3); + long endLine = row.getLong(4); - return locs.toArray(new Location[locs.size()]); + Location loc = new Location(locId, className, methodName, methodRole, startLine, endLine); + return loc; } Long getLocId(Location loc) throws SQLException, TypeMismatchException @@ -225,6 +204,26 @@ public class DbStore { return getVarId(vars.get(0)); } + LocalVariable getVar(Long varId) throws SQLException, TypeMismatchException + { + Column[] columns = { CfbSchema.NAME, CfbSchema.VARROLE }; + Table[] tables = { CfbSchema.VARIABLES }; + Condition[] conditions = { new Condition(CfbSchema.VARID_PK, varId, Operation.EQUAL) }; + + List result = m_driver.select(m_con, columns, tables, conditions); + if (result.size() < 1) { + throw new SQLException("No variable found for ID " + varId); + } + if (result.size() > 1) { + throw new SQLException("Too many matches (" + result.size() + ") found for variable ID " + varId); + } + + String varName = result.get(0).getString(0); + String varRole = result.get(0).getString(1); + + return new LocalVariable(varId, varName, varRole); + } + Long getVarId(LocalVariable var) throws SQLException, TypeMismatchException { if (null == var) { @@ -320,6 +319,45 @@ public class DbStore { BugCollection getBugCollection(Long runId) throws SQLException, TypeMismatchException { - throw new UnsupportedOperationException("Not yet implemented"); + Column[] columns = { + CfbSchema.FOUNDID, + CfbSchema.BUGID, + CfbSchema.CATEGORYID, + CfbSchema.FIRSTLOCID, + CfbSchema.SECONDLOCID, + CfbSchema.THIRDLOCID, + CfbSchema.VARID_FK + }; + Table[] tables = { + CfbSchema.FOUND + }; + Condition[] conditions = { + new Condition(CfbSchema.RUNID, runId, Operation.EQUAL) + }; + + BugCollection coll = new BugCollection(); + + List rows = m_driver.select(m_con, columns, tables, conditions); + + for (Row row : rows) { + // long foundId = row.getLong(0); + long bugId = row.getLong(1); + long categoryId = row.getLong(2); + long firstLocId = row.getLong(3); + long secondLocId = row.getLong(4); + long thirdLocId = row.getLong(5); + long varId = row.getLong(6); + + String bugType = getBugType(bugId); + String category = getCategoryName(categoryId); + Location[] locations = { getLoc(firstLocId), getLoc(secondLocId), getLoc(thirdLocId) }; + LocalVariable[] vars = { getVar(Long.valueOf(varId)) }; + + + BugInstance bug = new BugInstance(bugId, category, bugType, locations, vars); + coll.getBugs().add(bug); + } + + return coll; } } diff --git a/prod/net/jaekl/cfb/store/Location.java b/prod/net/jaekl/cfb/store/Location.java index 1ffc732..8755fe6 100644 --- a/prod/net/jaekl/cfb/store/Location.java +++ b/prod/net/jaekl/cfb/store/Location.java @@ -5,6 +5,7 @@ import net.jaekl.cfb.xml.BugMethod; import net.jaekl.cfb.xml.SourceLine; public class Location { + Long m_id; String m_className; String m_methodName; String m_methodRole; @@ -38,6 +39,11 @@ public class Location { m_className = bugClass.getClassName(); } + public Location(Long id, String className, String methodName, String methodRole, long startLine, long endLine) + { + + } + public String getClassName() { return m_className; } public String getMethodName() { return m_methodName; } public String getMethodRole() { return m_methodRole; } @@ -51,13 +57,19 @@ public class Location { init(sourceLines[0]); } } + + private void init(SourceLine sourceLine) + { + init(sourceLine.getClassName(), sourceLine.getStart(), sourceLine.getEnd()); + } - private void init(SourceLine sourceLine) + private void init(String className, int startLine, int endLine) { - m_className = sourceLine.getClassName(); + m_id = null; + m_className = className; m_methodName = null; m_methodRole = null; - m_startLine = sourceLine.getStart(); - m_endLine = sourceLine.getEnd(); + m_startLine = startLine; + m_endLine = endLine; } } diff --git a/prod/net/jaekl/cfb/xml/BugInstance.java b/prod/net/jaekl/cfb/xml/BugInstance.java index 100251f..96a815e 100644 --- a/prod/net/jaekl/cfb/xml/BugInstance.java +++ b/prod/net/jaekl/cfb/xml/BugInstance.java @@ -2,11 +2,13 @@ package net.jaekl.cfb.xml; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import org.xml.sax.Attributes; +import net.jaekl.cfb.store.Location; import net.jaekl.cfb.util.Util; import net.jaekl.qd.xml.MissingAttributeException; import net.jaekl.qd.xml.ParseResult; @@ -23,35 +25,61 @@ public class BugInstance extends ParseResult { static final String CATEGORY = "category"; static final String TYPE = "type"; + Long m_id; String m_category; String m_type; ArrayList m_classes; ArrayList m_methods; ArrayList m_locals; ArrayList m_lines; + ArrayList m_locations; public BugInstance() { super(TAG, INTERNAL, EXTERNAL); + m_id = null; m_category = m_type = null; m_classes = new ArrayList(); m_methods = new ArrayList(); m_locals = new ArrayList(); m_lines = new ArrayList(); + m_locations = new ArrayList(); + } + + public BugInstance(Long id, + String category, + String type, + Location[] locations, + LocalVariable[] variables) + { + super(TAG, INTERNAL, EXTERNAL); + + m_id = id; + m_category = category; + m_type = type; + m_classes = null; + m_methods = null; + m_lines = null; + m_locations = new ArrayList(Arrays.asList(locations)); + m_locals = new ArrayList(Arrays.asList(variables)); } public String getCategory() { return m_category; } public String getType() { return m_type; } - public List getClasses() { return Collections.unmodifiableList(m_classes); } - public List getMethods() { return Collections.unmodifiableList(m_methods); } - public List getLines() { return Collections.unmodifiableList(m_lines); } public List getVariables() { return Collections.unmodifiableList(m_locals); } + public List getLocations() { return Collections.unmodifiableList(m_locations); } @Override public void endContents(String uri, String localName, String qName, String chars) throws XmlParseException { - // no-op + // no operation + } + + @Override + public void complete() + { + computeLocations(); } @Override @@ -144,18 +172,18 @@ public class BugInstance extends ParseResult { return false; } - BugMethod thisMethod = this.getPrincipalMethod(); - BugMethod thatMethod = that.getPrincipalMethod(); - if (null == thisMethod) { - if (null == thatMethod) { + Location thisLoc = this.getPrincipalLocation(); + Location thatLoc = that.getPrincipalLocation(); + if (null == thisLoc) { + if (null == thatLoc) { return false; } } else { - if (! Util.objsAreEqual(thisMethod.getClassName(), thatMethod.getClassName())) { + if (! Util.objsAreEqual(thisLoc.getClassName(), thatLoc.getClassName())) { return false; } - if (! Util.objsAreEqual(thisMethod.getMethodName(), thatMethod.getMethodName())) { + if (! Util.objsAreEqual(thisLoc.getMethodName(), thatLoc.getMethodName())) { return false; } } @@ -174,19 +202,77 @@ public class BugInstance extends ParseResult { { int code = Util.objHashCode(m_type) * Util.objHashCode(m_category) - * Util.objHashCode(getPrincipalMethod()) + * Util.objHashCode(getPrincipalLocation()) * Util.objHashCode(getVariables()); return code; } - // Get the "principal" method. + // Get the "principal" Location. // This should be the place where the bug is reported. - BugMethod getPrincipalMethod() + Location getPrincipalLocation() { - List bugMethods = getMethods(); - if ((null == bugMethods) || (0 == bugMethods.size())) { - return null; + if (null != m_locations && m_locations.size() > 0) { + return m_locations.get(0); + } + return null; + } + + private void computeLocations() + { + assert(null != m_classes); + assert(null != m_methods); + assert(null != m_lines); + + m_locations.clear(); + + /* + Somewhat unfortunate special case. + The primary "location" for a bug instance is split between tags. + Most bugs have a pattern like this: + + ... + + + + ... + + + + The primary location for a bug is given by the with no role attribute, + but the inside that method describes the whole range of lines + covered by that Method, not the spot where the bug is located--that is given + by the that is a direct child fo the . + */ + + BugMethod primaryMethod = null; + SourceLine primaryLine = null; + + for (BugMethod method : m_methods) { + if (null != method.getRole()) { + primaryMethod = method; + break; + } + } + if (m_lines.size() > 0) { + primaryLine = m_lines.get(0); + } + + if ((null != primaryMethod) && (null != primaryLine)) { + m_locations.add(new Location(primaryMethod, primaryLine)); + } + + for (BugMethod method : m_methods) { + if (primaryMethod != method) { + m_locations.add(new Location(method)); + } + } + for (SourceLine line : m_lines) { + if (primaryLine != line) { + m_locations.add(new Location(line)); + } + } + for (BugClass clazz : m_classes) { + m_locations.add(new Location(clazz)); } - return bugMethods.get(0); } } diff --git a/prod/net/jaekl/cfb/xml/BugMethod.java b/prod/net/jaekl/cfb/xml/BugMethod.java index 4e5fc47..f05c510 100644 --- a/prod/net/jaekl/cfb/xml/BugMethod.java +++ b/prod/net/jaekl/cfb/xml/BugMethod.java @@ -37,6 +37,15 @@ public class BugMethod extends ParseResult { m_sourceLines = new ArrayList(); } + public BugMethod(Long id, String methodName, String methodRole) { + super(TAG, INTERNAL, EXTERNAL); + m_className = null; + m_methodName = methodName; + m_role = methodRole; + m_isStatic = false; + m_sourceLines = new ArrayList(); + } + public String getClassName() { return m_className; } public String getMethodName() { return m_methodName; } public String getRole() { return m_role; } diff --git a/prod/net/jaekl/cfb/xml/LocalVariable.java b/prod/net/jaekl/cfb/xml/LocalVariable.java index 42c282a..8043821 100644 --- a/prod/net/jaekl/cfb/xml/LocalVariable.java +++ b/prod/net/jaekl/cfb/xml/LocalVariable.java @@ -18,15 +18,26 @@ public class LocalVariable extends ParseResult { static final String NAME = "name"; static final String ROLE = "role"; + Long m_id; String m_name; String m_role; public LocalVariable() { super(TAG, INTERNAL, EXTERNAL); + m_id = null; m_name = m_role = null; } + public LocalVariable(Long id, String name, String role) { + super(TAG, INTERNAL, EXTERNAL); + + m_id = id; + m_name = name; + m_role = role; + } + + public Long getId() { return m_id; } public String getName() { return m_name; } public String getRole() { return m_role; } diff --git a/prod/net/jaekl/qd/xml/ParseResult.java b/prod/net/jaekl/qd/xml/ParseResult.java index f653b36..68f1486 100644 --- a/prod/net/jaekl/qd/xml/ParseResult.java +++ b/prod/net/jaekl/qd/xml/ParseResult.java @@ -125,7 +125,7 @@ public abstract class ParseResult } if (m_tagName.equals(localName)) { - validate(); + complete(); return true; } @@ -197,7 +197,7 @@ public abstract class ParseResult return this; } - public void validate() throws XmlParseException + public void complete() throws XmlParseException { // Default implementation is a no-op. // Override if you want to validate on endElement() -- 2.30.2