Add ability to load previously found bugs back out of the database.
authorChris Jaekl <cejaekl@yahoo.com>
Sat, 3 Oct 2015 12:51:41 +0000 (21:51 +0900)
committerChris Jaekl <cejaekl@yahoo.com>
Sat, 3 Oct 2015 12:51:41 +0000 (21:51 +0900)
prod/net/jaekl/cfb/db/driver/DbDriver.java
prod/net/jaekl/cfb/store/DbStore.java
prod/net/jaekl/cfb/store/Location.java
prod/net/jaekl/cfb/xml/BugInstance.java
prod/net/jaekl/cfb/xml/BugMethod.java
prod/net/jaekl/cfb/xml/LocalVariable.java
prod/net/jaekl/qd/xml/ParseResult.java

index eea3e22c44c8d89f8fa79d21a52089b87e05555b..a756ab2d39f673b225eee46a4adcfef17bd2b219 100644 (file)
@@ -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<Row> 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<Row> 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;
+       }
 }
index fed1f9d9f8b7ab7f8f4700791a8b7b223e09c83e..69ea8bbafaeb405ce06ce3387e2f9caa3f490678 100644 (file)
@@ -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<Location> 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<Location> locs = new ArrayList<Location>();
-               
-               /*
-                       Somewhat unfortunate special case.
-                       The primary "location" for a bug instance is split between tags.
-                       Most bugs have a pattern like this:
-                       <BugInstance>
-                               ...
-                               <Method>
-                                       <SourceLine .../>
-                               </Method>
-                               ...
-                               <SourceLine .../>
-                       </BugInstance>
-                       
-                       The primary location for a bug is given by the <Method> with no role attribute, 
-                       but the <SourceLine/> 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 <SourceLine/> that is a direct child fo the <BugInstance/>.
-                */
-               
-               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<Row> 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<Row> 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;
        }
 }
index 1ffc7322522635999440f45ce8a10920884173f1..8755fe6cc5945a7e18d298f6bb3f09bcad450de5 100644 (file)
@@ -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;
        }
 }
index 100251f9e9a37fce9235b30beb1492ebec24adc3..96a815e5aaa41089beafd6599878a83d0e922e3a 100644 (file)
@@ -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<BugClass> m_classes;
        ArrayList<BugMethod> m_methods;
        ArrayList<LocalVariable> m_locals;
        ArrayList<SourceLine> m_lines;
+       ArrayList<Location> m_locations;
        
        public BugInstance() {
                super(TAG, INTERNAL, EXTERNAL);
                
+               m_id = null;
                m_category = m_type = null;
                m_classes = new ArrayList<BugClass>();
                m_methods = new ArrayList<BugMethod>();
                m_locals = new ArrayList<LocalVariable>();
                m_lines = new ArrayList<SourceLine>();
+               m_locations = new ArrayList<Location>();
+       }
+       
+       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<Location>(Arrays.asList(locations));
+               m_locals = new ArrayList<LocalVariable>(Arrays.asList(variables));
        }
        
        public String getCategory() { return m_category; }
        public String getType() { return m_type; }
-       public List<BugClass> getClasses() { return Collections.unmodifiableList(m_classes); }
-       public List<BugMethod> getMethods() { return Collections.unmodifiableList(m_methods); }
-       public List<SourceLine> getLines() { return Collections.unmodifiableList(m_lines); }
        public List<LocalVariable> getVariables() { return Collections.unmodifiableList(m_locals); }
+       public List<Location> 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<BugMethod> 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:
+                       <BugInstance>
+                               ...
+                               <Method>
+                                       <SourceLine .../>
+                               </Method>
+                               ...
+                               <SourceLine .../>
+                       </BugInstance>
+                       
+                       The primary location for a bug is given by the <Method> with no role attribute, 
+                       but the <SourceLine/> 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 <SourceLine/> that is a direct child fo the <BugInstance/>.
+                */
+               
+               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);
        }
 }
index 4e5fc47ceb6f1c6c4c61fdcf45975cb9a0f449ef..f05c510af39ddd412df0744439cb3edcb445b032 100644 (file)
@@ -37,6 +37,15 @@ public class BugMethod extends ParseResult {
                m_sourceLines = new ArrayList<SourceLine>();
        }
        
+       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<SourceLine>();
+       }
+       
        public String getClassName() { return m_className; }
        public String getMethodName() { return m_methodName; }
        public String getRole() { return m_role; }
index 42c282acf7bdd33893f83a176d79494c719ffbd1..8043821844c89f0085292dae231058a6a5bce847 100644 (file)
@@ -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; }
        
index f653b368c81f17f4dee94e8211410e334050f058..68f14860687d07ef26968a0385409ac2c4c9f1d1 100644 (file)
@@ -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()