Add local variable information to DB store.
[cfb.git] / prod / net / jaekl / cfb / store / DbStore.java
1 package net.jaekl.cfb.store;
2
3 import java.sql.Connection;
4 import java.sql.SQLException;
5 import java.util.ArrayList;
6 import java.util.List;
7
8 import net.jaekl.cfb.analyze.Analysis;
9 import net.jaekl.cfb.db.CfbSchema;
10 import net.jaekl.cfb.db.Column;
11 import net.jaekl.cfb.db.Condition;
12 import net.jaekl.cfb.db.Operation;
13 import net.jaekl.cfb.db.Row;
14 import net.jaekl.cfb.db.Table;
15 import net.jaekl.cfb.db.TypeMismatchException;
16 import net.jaekl.cfb.db.driver.DbDriver;
17 import net.jaekl.cfb.xml.BugClass;
18 import net.jaekl.cfb.xml.BugInstance;
19 import net.jaekl.cfb.xml.BugMethod;
20 import net.jaekl.cfb.xml.LocalVariable;
21 import net.jaekl.cfb.xml.SourceLine;
22 import net.jaekl.cfb.xml.messages.MessageCollection;
23
24 public class DbStore {
25         Connection m_con;
26         DbDriver m_driver;
27         MessageCollection m_msgColl;
28         
29         public DbStore(Connection con, DbDriver driver, MessageCollection msgColl) {
30                 m_con = con;
31                 m_driver = driver;
32                 m_msgColl = msgColl;
33         }
34         
35         public boolean put(Analysis analysis) throws SQLException, TypeMismatchException {
36                 if (null == analysis) {
37                         return false;
38                 }
39                 
40                 // ----------------------------------
41                 // Add a run record for this analysis
42                 
43                 long runId = m_driver.nextVal(m_con, CfbSchema.RUN_SEQ);
44                 Object[][] values = { 
45                                                                 {
46                                                                         Long.valueOf(runId),
47                                                                         analysis.getBuildNumber(),
48                                                                         analysis.getStart(),
49                                                                         analysis.getEnd() 
50                                                                 } 
51                                                         };
52                 int count = m_driver.insert(m_con, CfbSchema.RUNS, values);
53                 if (1 != count) {
54                         return false;
55                 }
56                 
57                 // -------------------------------------
58                 // Add a found record for each bug found
59                 
60                 List<BugInstance> bugs = analysis.getBugCollection().getBugs();
61                 values = new Object[bugs.size()][CfbSchema.FOUND.getNumColumns()];
62                 
63                 int row = 0;
64                 for (BugInstance bug : bugs)
65                 {
66                         Long foundId = Long.valueOf(m_driver.nextVal(m_con, CfbSchema.FOUND_SEQ));
67                         Long bugId = Long.valueOf(m_msgColl.getPattern(bug.getType()).getId());
68                         Long categoryId = Long.valueOf(m_msgColl.getCategory(bug.getCategory()).getId());
69                         Location[] locs = computeLocations(bug);
70                         Location firstLoc  = (locs.length > 0) ? locs[0] : null;
71                         Location secondLoc = (locs.length > 1) ? locs[1] : null;
72                         Location thirdLoc  = (locs.length > 2) ? locs[2] : null;
73                         
74                         values[row][0] = foundId;
75                         values[row][1] = bugId;
76                         values[row][2] = categoryId;
77                         values[row][3] = getLocId(firstLoc);
78                         values[row][4] = getLocId(secondLoc);
79                         values[row][5] = getLocId(thirdLoc);
80                         values[row][6] = getVarId(bug);
81                         row++;
82                 }
83                 
84                 count = m_driver.insert(m_con, CfbSchema.FOUND, values);
85                 return (bugs.size() == count);
86         }
87         
88         Location[] computeLocations(BugInstance bug)
89         {
90                 ArrayList<Location> locs = new ArrayList<Location>();
91                 
92                 /*
93                         Somewhat unfortunate special case.
94                         The primary "location" for a bug instance is split between tags.
95                         Most bugs have a pattern like this:
96                         <BugInstance>
97                                 ...
98                                 <Method>
99                                         <SourceLine .../>
100                                 </Method>
101                                 ...
102                                 <SourceLine .../>
103                         </BugInstance>
104                         
105                         The primary location for a bug is given by the <Method> with no role attribute, 
106                         but the <SourceLine/> inside that method describes the whole range of lines 
107                         covered by that Method, not the spot where the bug is located--that is given 
108                         by the <SourceLine/> that is a direct child fo the <BugInstance/>.
109                  */
110                 
111                 BugMethod primaryMethod = null;
112                 SourceLine primaryLine = null;
113                 
114                 for (BugMethod method : bug.getMethods()) {
115                         if (null != method.getRole()) {
116                                 primaryMethod = method;
117                                 break;
118                         }
119                 }
120                 if (bug.getLines().size() > 0) {
121                         primaryLine = bug.getLines().get(0);
122                 }
123                 
124                 if ((null != primaryMethod) && (null != primaryLine)) {
125                         locs.add(new Location(primaryMethod, primaryLine));
126                 }
127                 
128                 for (BugMethod method : bug.getMethods()) {
129                         if (primaryMethod != method) {
130                                 locs.add(new Location(method));                         
131                         }
132                 }
133                 for (SourceLine line : bug.getLines()) {
134                         if (primaryLine != line) {
135                                 locs.add(new Location(line));
136                         }
137                 }
138                 for (BugClass clazz : bug.getClasses()) {
139                         locs.add(new Location(clazz));
140                 }
141                 
142                 return locs.toArray(new Location[locs.size()]);
143         }
144         
145         Long getLocId(Location loc) throws SQLException, TypeMismatchException 
146         {
147                 if (null == loc) {
148                         return null;
149                 }
150                 Long locId = findLocId(loc);
151                 if (null != locId) {
152                         return locId;
153                 }
154
155                 return storeLoc(loc);
156         }
157         
158         Long findLocId(Location loc) throws SQLException, TypeMismatchException
159         {
160                 Column[] columns = { CfbSchema.LOCID };
161                 Table[] tables = { CfbSchema.LOCATIONS };
162                 
163                 Condition[] conditions = { 
164                                                 new Condition( CfbSchema.CLASSNAME,  loc.getClassName(),  Operation.EQUAL ),
165                                                 new Condition( CfbSchema.METHODNAME, loc.getMethodName(), Operation.EQUAL ),
166                                                 new Condition( CfbSchema.METHODROLE, loc.getMethodRole(), Operation.EQUAL ),
167                                                 new Condition( CfbSchema.STARTLINE,  loc.getStart(),      Operation.EQUAL ),
168                                                 new Condition( CfbSchema.ENDLINE,    loc.getEnd(),        Operation.EQUAL )
169                                         };
170                 List<Row> rows = m_driver.select(m_con, columns, tables, conditions);
171                 if (rows.size() > 0) {
172                         assert(1 == rows.size());       // should only have one match
173                         
174                         return rows.get(0).getLong(0);
175                 }
176                 
177                 return null;    // not found
178         }
179         
180         Long storeLoc(Location loc) throws SQLException
181         {
182                 long locId = m_driver.nextVal(m_con, CfbSchema.LOC_SEQ);
183                 
184                 Object[][] values = { { 
185                                                         Long.valueOf(locId),
186                                                         loc.getClassName(),
187                                                         loc.getMethodName(),
188                                                         loc.getMethodRole(),
189                                                         Long.valueOf(loc.getStart()),
190                                                         Long.valueOf(loc.getEnd())
191                                                 } };
192                 int count = m_driver.insert(m_con, CfbSchema.LOCATIONS, values);
193                 if (1 != count) {
194                         return null;
195                 }
196                 
197                 return Long.valueOf(locId);
198         }
199         
200         Long getVarId(BugInstance bug) throws SQLException, TypeMismatchException
201         {
202                 if (null == bug) {
203                         return null;
204                 }
205                 
206                 List<LocalVariable> vars = bug.getVariables();
207                 if ((null == vars) || (0 == vars.size())) {
208                         return null;
209                 }
210                 
211                 return getVarId(vars.get(0));
212         }
213         
214         Long getVarId(LocalVariable var) throws SQLException, TypeMismatchException
215         {
216                 if (null == var) {
217                         return null;
218                 }
219                 
220                 Long result = findVarId(var);
221                 
222                 if (null != result) {
223                         return result;
224                 }
225                 
226                 return storeVar(var);
227         }
228         
229         Long findVarId(LocalVariable var) throws SQLException, TypeMismatchException
230         {
231                 Column[] columns = { CfbSchema.VARID_PK };
232                 Table[] tables = { CfbSchema.VARIABLES };
233                 
234                 Condition[] conditions = { 
235                                                 new Condition( CfbSchema.NAME,    var.getName(), Operation.EQUAL ),
236                                                 new Condition( CfbSchema.VARROLE, var.getRole(), Operation.EQUAL )
237                                         };
238                 List<Row> rows = m_driver.select(m_con, columns, tables, conditions);
239                 if (rows.size() > 0) {
240                         assert(1 == rows.size());       // should only have one match
241                         
242                         return rows.get(0).getLong(0);
243                 }
244                 
245                 return null;    // not found
246         }
247         
248         Long storeVar(LocalVariable var) throws SQLException
249         {
250                 long varId = m_driver.nextVal(m_con, CfbSchema.VARIABLE_SEQ);
251                 
252                 Object[][] values = { { 
253                                                         Long.valueOf(varId),
254                                                         var.getName(),
255                                                         var.getRole()
256                                                 } };
257                 int count = m_driver.insert(m_con, CfbSchema.VARIABLES, values);
258                 if (1 != count) {
259                         return null;
260                 }
261                 
262                 return Long.valueOf(varId);
263         }
264 }