(Finally) reach the point where we have some useful, if basic, functionality.
authorChris Jaekl <cejaekl@yahoo.com>
Sat, 3 Oct 2015 14:35:55 +0000 (23:35 +0900)
committerChris Jaekl <cejaekl@yahoo.com>
Sat, 3 Oct 2015 14:35:55 +0000 (23:35 +0900)
Delta now computes difference between previous run and the current one,
and supports a text dump of the new, fixed, and old bugs.

prod/net/jaekl/cfb/CFB.java
prod/net/jaekl/cfb/analyze/Analyzer.java
prod/net/jaekl/cfb/analyze/Delta.java
prod/net/jaekl/cfb/analyze/MessageMap.java
prod/net/jaekl/cfb/db/Column.java
prod/net/jaekl/cfb/db/Row.java
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/BugCollection.java
prod/net/jaekl/cfb/xml/BugInstance.java

index 81c30ea7a39ac86aaa9ee90838775953ecc74189..d202d6fcb518b1ee54697588fc6c0204fd92e4b7 100644 (file)
@@ -18,6 +18,7 @@ import java.util.Locale.Category;
 
 import net.jaekl.cfb.analyze.Analysis;
 import net.jaekl.cfb.analyze.Analyzer;
+import net.jaekl.cfb.analyze.Delta;
 import net.jaekl.cfb.analyze.MessageMap;
 import net.jaekl.cfb.db.CfbSchema;
 import net.jaekl.cfb.db.TypeMismatchException;
@@ -189,6 +190,9 @@ public class CFB {
                        DbStore store = new DbStore(con, m_driver, messageMap.getColl());
                        
                        store.put(analysis);
+                       Analysis prior = store.getPrior(analysis);
+                       Delta delta = new Delta(prior, analysis);
+                       delta.dump(pw);
                }
                catch (SQLException exc) {
                        reportUnableToConnect(pw, exc);
index 1ac5677abb50da33a7e85cfaf0882b26af31edc7..3f3c4cfa1395c548f2066486d737f06f9637f9fd 100644 (file)
@@ -51,7 +51,7 @@ public class Analyzer {
                
                result.setEnd(new Date());
                result.parse(new InputSource(fbOutput.getAbsolutePath()));
-               result.dump(pw);                        
+               // result.dump(pw);                     
                return result;
        }
        
index e2853e0b445cd6294f2c9667f9fb94d0630681f4..51971e06c7589a01336a4bfce8b8ba0785f9a04b 100644 (file)
@@ -1,5 +1,6 @@
 package net.jaekl.cfb.analyze;
 
+import java.io.PrintWriter;
 import java.util.HashSet;
 
 import net.jaekl.cfb.xml.BugInstance;
@@ -29,6 +30,29 @@ public class Delta {
        public BugInstance[] getNew() { return m_new.toArray(new BugInstance[m_new.size()]); }
        public int getNumNew() { return m_new.size(); }
        
+       public void dump(PrintWriter pw) {
+               pw.println("=========================");
+               pw.println("  NEW BUGS (" + m_new.size() + ")");
+               pw.println("-------------------------");
+               for (BugInstance bug : m_new) {
+                       bug.dump(pw, 2);
+               }
+               
+               pw.println("=========================");
+               pw.println("  FIXED BUGS (" + m_fixed.size() + ")");
+               pw.println("-------------------------");
+               for (BugInstance bug : m_fixed) {
+                       bug.dump(pw, 2);
+               }
+               
+               pw.println("=========================");
+               pw.println("  OLD BUGS (" + m_common.size() + ")");
+               pw.println("-------------------------");
+               for (BugInstance bug : m_common) {
+                       bug.dump(pw, 2);
+               }
+       }
+       
        void computeDelta(Analysis before, Analysis after)
        {
                m_fixed.clear();
@@ -37,7 +61,9 @@ public class Delta {
                
                HashSet<BugInstance> beforeBugs = new HashSet<BugInstance>();
                
-               beforeBugs.addAll(before.getBugCollection().getBugs());
+               if (null != before) {
+                       beforeBugs.addAll(before.getBugCollection().getBugs());
+               }
                
                for (BugInstance bug : after.getBugCollection().getBugs()) {
                        if (beforeBugs.contains(bug)) {
@@ -48,6 +74,10 @@ public class Delta {
                        }
                }
                
+               if (null == before) {
+                       return;
+               }
+               
                for (BugInstance bug : before.getBugCollection().getBugs()) {
                        if (! m_common.contains(bug)) {
                                m_fixed.add(bug);
index ae389ad845496a9fd79c9623bc34e1777d6cf4f7..f4936b004c1b7617a7a52d604b7de6fe7867118f 100644 (file)
@@ -59,7 +59,7 @@ public class MessageMap {
                List<Row> rows = driver.select(con, columns, tables, conditions);
                
                for (Row row : rows) {
-                       long catId = row.getLong(0);
+                       Long catId = row.getLong(0);
                        String catName = row.getString(1);
                        
                        BugCategory cat = getColl().getCategory(catName);
@@ -80,7 +80,7 @@ public class MessageMap {
                List<Row> rows = driver.select(con, columns, tables, conditions);
                
                for (Row row: rows) {
-                       long bugId = row.getLong(0);
+                       Long bugId = row.getLong(0);
                        String type = row.getString(1);
                        
                        BugPattern bug = getColl().getPattern(type);
index 8fc723ded7b00521bbb8fd103aa9c054437e961e..be9fb65729e7501e23d8fc25932f07700c4096d2 100644 (file)
@@ -1,5 +1,9 @@
 package net.jaekl.cfb.db;
 
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.Date;
+
 import net.jaekl.cfb.util.Util;
 
 // Copyright (C) 2015 Christian Jaekl
@@ -48,6 +52,22 @@ public class Column {
                return new Column(name, type, width.intValue(), canBeNull);
        }
        
+       // Wrapper around PreparedStatement.setObject().
+       // Note that indices start at 1, not at zero.
+       public void setObject(PreparedStatement ps, int idx, Object obj) throws SQLException
+       {
+               if (this.getType().equals(Type.TIMESTAMPTZ)) {
+                       // Special case:  because there's no good way to read a TIMESTAMPTZ from 
+                       // the database using JDBC, we store it as an integer (milliseconds since
+                       // the epoch, 01.01.1970 00:00:00.000 UTC).
+                       Date date = (Date)obj;
+                       ps.setLong(idx, date.getTime());
+               }
+               else {
+                       ps.setObject(idx, obj);
+               }
+       }
+       
        @Override 
        public boolean equals(Object obj) 
        {
index fa3de9948ebb721a18da45230caaadd0131fc02b..35bf1215c28da38aac5b237b815e5e6c8d5136c0 100644 (file)
@@ -26,24 +26,37 @@ public class Row {
                return num.intValue();
        }
        
-       public long getLong(int index) throws TypeMismatchException
+       public Long getLong(int index) throws TypeMismatchException
        {
                checkType(index, Column.Type.INTEGER);
+               if (null == m_values[index]) {
+                       return null;
+               }
+               
                Number num = (Number)m_values[index];
                return num.longValue();
        }
        
        public java.util.Date getDate(int index) throws TypeMismatchException
        {
-               checkType(index, Column.Type.INTEGER);
-               long milliseconds = (Long)m_values[index];
-               java.util.Date date = new java.util.Date(milliseconds);
-               return date;
+               checkType(index, Column.Type.TIMESTAMPTZ);
+               return (java.util.Date)m_values[index];
        }
        
        protected void checkType(int index, Column.Type type) throws TypeMismatchException {
                Column column = m_columns[index];
-               if (column.getType().equals(type)) {
+               Column.Type columnType = column.getType();
+               
+               if (columnType.equals(Column.Type.TIMESTAMPTZ)) {
+                       // Special case:  TIMESTAMPTZ is stored as an INTEGER
+                       if (   Column.Type.TIMESTAMPTZ.equals(type)
+                               || Column.Type.INTEGER.equals(type)     )
+                       {
+                               return;
+                       }
+               }
+               
+               if (columnType.equals(type)) {
                        return;
                }
                
index a756ab2d39f673b225eee46a4adcfef17bd2b219..bbe38f8cc0d9f23c4b3a24430bd747c1027a1354 100644 (file)
@@ -9,7 +9,6 @@ import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.util.ArrayList;
-import java.util.Date;
 import java.util.List;
 
 import net.jaekl.cfb.db.Column;
@@ -115,8 +114,9 @@ public abstract class DbDriver {
                        int index = 0;
                        for (Condition condition : conditions) {
                                if (condition.getOperation().hasParam()) {
+                                       Column column = condition.getColumn();
                                        index++;
-                                       ps.setObject(index, condition.getValue());
+                                       column.setObject(ps, index, condition.getValue());
                                }
                        }
                        
@@ -137,6 +137,9 @@ public abstract class DbDriver {
                                }
                        }
                }
+               catch (SQLException se) {
+                       throw new SQLException("Error with SQL:  " + sql, se);
+               }
                
                return result;
        }
@@ -159,19 +162,10 @@ public abstract class DbDriver {
                                assert(null != data);
                                assert(data.length == table.getNumColumns());
                                
-                               for (int col = 0; col < data.length; ++col) {
-                                       Object obj = data[col];
-                                       Column column = table.getColumn(col);
-                                       if (column.getType().equals(Type.TIMESTAMPTZ)) {
-                                               // Special case:  because there's no good way to read a TIMESTAMPTZ from 
-                                               // the database using JDBC, we store it as an integer (milliseconds since
-                                               // the epoch, 01.01.1970 00:00:00.000 UTC).
-                                               Date date = (Date)obj;
-                                               ps.setLong(col + 1, date.getTime());
-                                       }
-                                       else {
-                                               ps.setObject(col + 1, data[col]);
-                                       }
+                               for (int idx = 0; idx < data.length; ++idx) {
+                                       Object obj = data[idx];
+                                       Column column = table.getColumn(idx);
+                                       column.setObject(ps, idx + 1, obj);
                                        pendingValues++;
                                }
                                ps.addBatch();
@@ -300,10 +294,10 @@ public abstract class DbDriver {
                                sb.append(sort.getColumn().getName());
                                
                                if (sort.getDirection().equals(Sort.Direction.ASCENDING)) {
-                                       sb.append(" ASCENDING ");
+                                       sb.append(" ASC ");
                                }
                                else {
-                                       sb.append(" DESCENDING ");
+                                       sb.append(" DESC ");
                                }
                        }
                }
index 69ea8bbafaeb405ce06ce3387e2f9caa3f490678..6c2097925f3d78a3c14fb91e6325643d66578be1 100644 (file)
@@ -119,6 +119,10 @@ public class DbStore {
        
        Location getLoc(Long locId) throws SQLException, TypeMismatchException
        {
+               if (null == locId) {
+                       return null;
+               }
+               
                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) };
@@ -128,8 +132,8 @@ public class DbStore {
                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);
+               Integer startLine = row.getInt(3);
+               Integer endLine = row.getInt(4);
                
                Location loc = new Location(locId, className, methodName, methodRole, startLine, endLine);
                return loc;
@@ -206,6 +210,10 @@ public class DbStore {
        
        LocalVariable getVar(Long varId) throws SQLException, TypeMismatchException
        {
+               if (null == varId) {
+                       return null;
+               }
+               
                Column[] columns = { CfbSchema.NAME, CfbSchema.VARROLE };
                Table[] tables = { CfbSchema.VARIABLES };
                Condition[] conditions = { new Condition(CfbSchema.VARID_PK, varId, Operation.EQUAL) };
@@ -290,11 +298,11 @@ public class DbStore {
                return rows.get(0).getLong(0);
        }
        
-       Analysis getAnalysis(Long priorId) throws SQLException, TypeMismatchException
+       Analysis getAnalysis(Long analysisId) throws SQLException, TypeMismatchException
        {
                Column[] columns = { CfbSchema.VERSION, CfbSchema.STARTTIME, CfbSchema.ENDTIME };
                Table[] tables = { CfbSchema.RUNS };
-               Condition[] conditions = { new Condition( CfbSchema.RUNID, priorId, Operation.EQUAL ) };
+               Condition[] conditions = { new Condition( CfbSchema.RUNID, analysisId, Operation.EQUAL ) };
                
                List<Row> rows = m_driver.select(m_con, columns, tables, conditions);
                if (rows.size() < 1) {
@@ -308,11 +316,11 @@ public class DbStore {
                java.util.Date end = row.getDate(2);
                
                Analysis prior = new Analysis(version);
-               prior.setId(priorId.longValue());
+               prior.setId(analysisId.longValue());
                prior.setStart(start);
                prior.setEnd(end);
                
-               prior.setBugCollection(getBugCollection(priorId));
+               prior.setBugCollection(getBugCollection(analysisId));
                
                return prior;
        }
@@ -341,21 +349,21 @@ public class DbStore {
                
                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);
+                       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)) };
+                       LocalVariable[] vars = { getVar(varId) };
 
                        
                        BugInstance bug = new BugInstance(bugId, category, bugType, locations, vars);
-                       coll.getBugs().add(bug);
+                       coll.add(bug);
                }
                
                return coll;
index 8755fe6cc5945a7e18d298f6bb3f09bcad450de5..51857eb48c2289ca6aa94991dcec7b2ed4716927 100644 (file)
@@ -1,5 +1,8 @@
 package net.jaekl.cfb.store;
 
+import java.io.PrintWriter;
+
+import net.jaekl.cfb.util.Util;
 import net.jaekl.cfb.xml.BugClass;
 import net.jaekl.cfb.xml.BugMethod;
 import net.jaekl.cfb.xml.SourceLine;
@@ -9,8 +12,8 @@ public class Location {
        String m_className;
        String m_methodName;
        String m_methodRole;
-       int m_startLine;
-       int m_endLine;
+       Integer m_startLine;
+       Integer m_endLine;
        
        public Location(SourceLine sourceLine)
        {
@@ -39,9 +42,14 @@ public class Location {
                m_className = bugClass.getClassName();
        }
        
-       public Location(Long id, String className, String methodName, String methodRole, long startLine, long endLine)
+       public Location(Long id, String className, String methodName, String methodRole, Integer startLine, Integer endLine)
        {
-               
+               m_id = id;
+               m_className = className;
+               m_methodName = methodName;
+               m_methodRole = methodRole;
+               m_startLine = startLine;
+               m_endLine = endLine;
        }
        
        public String getClassName() { return m_className; }
@@ -50,6 +58,68 @@ public class Location {
        public int getStart() { return m_startLine; }
        public int getEnd() { return m_endLine; }
        
+       public void dump(PrintWriter pw, int indent) 
+       {
+               String margin = String.format("%" + indent + "s", "");
+               String tab = margin + "  ";
+               pw.println(margin + "Location");
+               if (null != m_className) {
+                       pw.println(tab + "classname = " + m_className);
+               }
+               if (null != m_methodName) {
+                       if (null != m_methodRole) {
+                               pw.println(tab + "method = " + m_methodName + " (" + m_methodRole + ")");
+                       }
+                       else {
+                               pw.println(tab + "method = " + m_methodName);
+                       }
+               }
+               if (null != m_startLine) {
+                       pw.println(tab + "lines = " + m_startLine + " .. " + m_endLine);
+               }
+       }
+
+       public boolean fuzzyEquals(Location that)
+       {
+               if (null == that) {
+                       return false;
+               }
+               
+               if (! Util.objsAreEqual(this.m_className, that.m_className)) {
+                       return false;
+               }
+               
+               if (! Util.objsAreEqual(this.m_methodName, that.m_methodName)) {
+                       return false;
+               }
+               
+               if (! Util.objsAreEqual(this.m_methodRole, that.m_methodRole)) {
+                       return false;
+               }
+               
+               return true;
+       }
+       
+       @Override
+       public boolean equals(Object other)
+       {
+               if (null == other) {
+                       return false;
+               }
+               if (other instanceof Location) {
+                       return fuzzyEquals((Location)other);
+               }
+               return false;
+       }
+       
+       @Override
+       public int hashCode()
+       {
+               return   Util.objHashCode(m_className)
+                          ^ Util.objHashCode(m_methodName)
+                          ^ Util.objHashCode(m_methodRole);
+       }
+       
        private void init(SourceLine[] sourceLines) 
        {
                if (sourceLines.length > 0) {
index 6fbf4460ec43d0b7105ae06d3b16b3bd7f68cac9..98ea93b422446de826716cbcce8f0da8511a3518 100644 (file)
@@ -22,6 +22,7 @@ public class BugCollection extends ParseResult {
        }
        
        public List<BugInstance> getBugs() { return Collections.unmodifiableList(m_bugs); }
+       public void add(BugInstance bug) { m_bugs.add(bug); }
        
        @Override
        public void endContents(String uri, String localName, String qName,     String chars) 
index 96a815e5aaa41089beafd6599878a83d0e922e3a..0683a7d23c83dd1437b08ebe77bd545d01a830f4 100644 (file)
@@ -57,9 +57,11 @@ public class BugInstance extends ParseResult {
                m_id = id;
                m_category = category;
                m_type = type;
-               m_classes = null;
-               m_methods = null;
-               m_lines = null;
+               
+               m_classes = new ArrayList<BugClass>();
+               m_methods = new ArrayList<BugMethod>();
+               m_lines = new ArrayList<SourceLine>();
+
                m_locations = new ArrayList<Location>(Arrays.asList(locations));
                m_locals = new ArrayList<LocalVariable>(Arrays.asList(variables));
        }
@@ -138,11 +140,18 @@ public class BugInstance extends ParseResult {
                        bm.dump(pw, childIndent);
                }
                for (LocalVariable lv : m_locals) {
-                       lv.dump(pw, childIndent);
+                       if (null != lv) {
+                               lv.dump(pw, childIndent);
+                       }
                }
                for (SourceLine sl : m_lines) {
                        sl.dump(pw, childIndent);
                }
+               for (Location loc : m_locations) {
+                       if (null != loc) {
+                               loc.dump(pw, childIndent);
+                       }
+               }
        }
        
        // Note that this is a heuristic, "fuzzy", equals.
@@ -180,18 +189,11 @@ public class BugInstance extends ParseResult {
                                }
                        }
                        else {
-                               if (! Util.objsAreEqual(thisLoc.getClassName(), thatLoc.getClassName())) {
-                                       return false;
-                               }
-                               if (! Util.objsAreEqual(thisLoc.getMethodName(), thatLoc.getMethodName())) {
+                               if (! thisLoc.fuzzyEquals(thatLoc)) {
                                        return false;
                                }
                        }
                        
-                       if (! Util.objsAreEqual(this.getVariables(), that.getVariables())) {
-                               return false;
-                       }
-                       
                        return true;
                }
                return false;
@@ -201,9 +203,8 @@ public class BugInstance extends ParseResult {
        public int hashCode() 
        {
                int code = Util.objHashCode(m_type)
-                                * Util.objHashCode(m_category)
-                                * Util.objHashCode(getPrincipalLocation())
-                                * Util.objHashCode(getVariables());
+                                ^ Util.objHashCode(m_category)
+                                ^ Util.objHashCode(getPrincipalLocation());
                return code;
        }