Avoid unit test failures when cfb.properties is not found in the classpath.
[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.List;
6
7 import net.jaekl.cfb.analyze.Analysis;
8 import net.jaekl.cfb.db.CfbSchema;
9 import net.jaekl.cfb.db.Column;
10 import net.jaekl.cfb.db.Condition;
11 import net.jaekl.cfb.db.Operation;
12 import net.jaekl.cfb.db.Row;
13 import net.jaekl.cfb.db.Sort;
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.BugCollection;
18 import net.jaekl.cfb.xml.BugInstance;
19 import net.jaekl.cfb.xml.LocalVariable;
20 import net.jaekl.cfb.xml.messages.BugCategory;
21 import net.jaekl.cfb.xml.messages.BugPattern;
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 Analysis getPrior(Analysis analysis) throws SQLException, TypeMismatchException {
36                 if (null == analysis) {
37                         return null;
38                 }
39                 Long priorId = getPriorId(analysis);
40                 if (null == priorId) {
41                         return null;
42                 }
43                 
44                 return getAnalysis(priorId);
45         }
46         
47         public boolean put(Analysis analysis) throws SQLException, TypeMismatchException, StoreException {
48                 if (null == analysis) {
49                         return false;
50                 }
51                 
52                 // ----------------------------------
53                 // Add a run record for this analysis
54                 
55                 long runId = m_driver.nextVal(m_con, CfbSchema.RUN_SEQ);
56                 Object[][] values = { 
57                                                                 {
58                                                                         Long.valueOf(runId),
59                                                                         analysis.getProjectName(),
60                                                                         analysis.getBuildNumber(),
61                                                                         analysis.getStart(),
62                                                                         analysis.getEnd() 
63                                                                 } 
64                                                         };
65                 int count = m_driver.insert(m_con, CfbSchema.RUNS, values);
66                 if (1 != count) {
67                         return false;
68                 }
69                 
70                 // -------------------------------------
71                 // Add a found record for each bug found
72                 
73                 List<BugInstance> bugs = analysis.getBugCollection().getBugs();
74                 values = new Object[bugs.size()][CfbSchema.FOUND.getNumColumns()];
75                 
76                 int row = 0;
77                 for (BugInstance bug : bugs)
78                 {
79                         Long foundId = Long.valueOf(m_driver.nextVal(m_con, CfbSchema.FOUND_SEQ));
80                         Long bugId = Long.valueOf(m_msgColl.getPattern(bug.getType()).getId());
81                         Long categoryId = Long.valueOf(m_msgColl.getCategory(bug.getCategory()).getId());
82                         List<Location> locs = bug.getLocations();
83                         Location firstLoc  = (locs.size() > 0) ? locs.get(0) : null;
84                         Location secondLoc = (locs.size() > 1) ? locs.get(1) : null;
85                         Location thirdLoc  = (locs.size() > 2) ? locs.get(2) : null;
86                         
87                         if (BugPattern.UNKNOWN.getId() == bugId) {
88                                 throw new StoreException(StoreException.Type.UNKNOWN_PATTERN, ""+bug.getType());
89                         }
90                         if (BugCategory.UNKNOWN.getId() == categoryId) {
91                                 throw new StoreException(StoreException.Type.UNKNOWN_CATEGORY, ""+bug.getCategory());
92                         }
93                         
94                         values[row][0] = foundId;
95                         values[row][1] = runId;
96                         values[row][2] = bugId;
97                         values[row][3] = categoryId;
98                         values[row][4] = getLocId(firstLoc);
99                         values[row][5] = getLocId(secondLoc);
100                         values[row][6] = getLocId(thirdLoc);
101                         values[row][7] = getVarId(bug);
102                         row++;
103                 }
104                 
105                 count = m_driver.insert(m_con, CfbSchema.FOUND, values);
106                 return (bugs.size() == count);
107         }
108         
109         String getBugType(Long bugPatternId) throws SQLException, TypeMismatchException
110         {
111                 Column[] columns = { CfbSchema.TYPE };
112                 Table[] tables = { CfbSchema.BUGS };
113                 Condition[] conditions = { new Condition(CfbSchema.BUGID, bugPatternId, Operation.EQUAL) }; 
114                 Row row = m_driver.selectExactlyOne(m_con, columns, tables, conditions);
115                 String type = row.getString(0);
116                 return type;
117         }
118         
119         String getCategoryName(Long categoryId) throws SQLException, TypeMismatchException
120         {
121                 Column[] columns = { CfbSchema.CATEGORY };
122                 Table[] tables = { CfbSchema.CATEGORIES };
123                 Condition[] conditions = { new Condition(CfbSchema.CATEGORYID, categoryId, Operation.EQUAL) };
124                 Row row = m_driver.selectExactlyOne(m_con, columns, tables, conditions);
125                 String name = row.getString(0);
126                 return name;
127         }
128         
129         
130         Location getLoc(Long locId) throws SQLException, TypeMismatchException
131         {
132                 if (null == locId) {
133                         return null;
134                 }
135                 
136                 Column[] columns = { CfbSchema.CLASSNAME, CfbSchema.METHODNAME, CfbSchema.METHODROLE, CfbSchema.STARTLINE, CfbSchema.ENDLINE };
137                 Table[] tables = { CfbSchema.LOCATIONS };
138                 Condition[] conditions = { new Condition(CfbSchema.LOCID, locId, Operation.EQUAL) };
139                 
140                 Row row = m_driver.selectExactlyOne(m_con, columns, tables, conditions);
141                 
142                 String className = row.getString(0);
143                 String methodName = row.getString(1);
144                 String methodRole = row.getString(2);
145                 Integer startLine = row.getInt(3);
146                 Integer endLine = row.getInt(4);
147                 
148                 Location loc = new Location(locId, className, methodName, methodRole, startLine, endLine);
149                 return loc;
150         }
151         
152         Long getLocId(Location loc) throws SQLException, TypeMismatchException 
153         {
154                 if (null == loc) {
155                         return null;
156                 }
157                 Long locId = findLocId(loc);
158                 if (null != locId) {
159                         return locId;
160                 }
161
162                 return storeLoc(loc);
163         }
164         
165         Long findLocId(Location loc) throws SQLException, TypeMismatchException
166         {
167                 Column[] columns = { CfbSchema.LOCID };
168                 Table[] tables = { CfbSchema.LOCATIONS };
169                 
170                 Condition[] conditions = { 
171                                                 new Condition( CfbSchema.CLASSNAME,  loc.getClassName(),  Operation.EQUAL ),
172                                                 new Condition( CfbSchema.METHODNAME, loc.getMethodName(), Operation.EQUAL ),
173                                                 new Condition( CfbSchema.METHODROLE, loc.getMethodRole(), Operation.EQUAL ),
174                                                 new Condition( CfbSchema.STARTLINE,  loc.getStart(),      Operation.EQUAL ),
175                                                 new Condition( CfbSchema.ENDLINE,    loc.getEnd(),        Operation.EQUAL )
176                                         };
177                 List<Row> rows = m_driver.select(m_con, columns, tables, conditions);
178                 if (rows.size() > 0) {
179                         assert(1 == rows.size());       // should only have one match
180                         
181                         return rows.get(0).getLong(0);
182                 }
183                 
184                 return null;    // not found
185         }
186         
187         Long storeLoc(Location loc) throws SQLException
188         {
189                 long locId = m_driver.nextVal(m_con, CfbSchema.LOC_SEQ);
190                 
191                 Object[][] values = { { 
192                                                         Long.valueOf(locId),
193                                                         loc.getClassName(),
194                                                         loc.getMethodName(),
195                                                         loc.getMethodRole(),
196                                                         Long.valueOf(loc.getStart()),
197                                                         Long.valueOf(loc.getEnd())
198                                                 } };
199                 int count = m_driver.insert(m_con, CfbSchema.LOCATIONS, values);
200                 if (1 != count) {
201                         return null;
202                 }
203                 
204                 return Long.valueOf(locId);
205         }
206         
207         Long getVarId(BugInstance bug) throws SQLException, TypeMismatchException
208         {
209                 if (null == bug) {
210                         return null;
211                 }
212                 
213                 List<LocalVariable> vars = bug.getVariables();
214                 if ((null == vars) || (0 == vars.size())) {
215                         return null;
216                 }
217                 
218                 return getVarId(vars.get(0));
219         }
220         
221         LocalVariable getVar(Long varId) throws SQLException, TypeMismatchException
222         {
223                 if (null == varId) {
224                         return null;
225                 }
226                 
227                 Column[] columns = { CfbSchema.NAME, CfbSchema.VARROLE };
228                 Table[] tables = { CfbSchema.VARIABLES };
229                 Condition[] conditions = { new Condition(CfbSchema.VARID_PK, varId, Operation.EQUAL) };
230                 
231                 List<Row> result = m_driver.select(m_con, columns, tables, conditions);
232                 if (result.size() < 1) {
233                         throw new SQLException("No variable found for ID " + varId);
234                 }
235                 if (result.size() > 1) {
236                         throw new SQLException("Too many matches (" + result.size() + ") found for variable ID " + varId);
237                 }
238                 
239                 String varName = result.get(0).getString(0);
240                 String varRole = result.get(0).getString(1);
241                 
242                 return new LocalVariable(varId, varName, varRole);
243         }
244         
245         Long getVarId(LocalVariable var) throws SQLException, TypeMismatchException
246         {
247                 if (null == var) {
248                         return null;
249                 }
250                 
251                 Long result = findVarId(var);
252                 
253                 if (null != result) {
254                         return result;
255                 }
256                 
257                 return storeVar(var);
258         }
259         
260         Long findVarId(LocalVariable var) throws SQLException, TypeMismatchException
261         {
262                 Column[] columns = { CfbSchema.VARID_PK };
263                 Table[] tables = { CfbSchema.VARIABLES };
264                 
265                 Condition[] conditions = { 
266                                                 new Condition( CfbSchema.NAME,    var.getName(), Operation.EQUAL ),
267                                                 new Condition( CfbSchema.VARROLE, var.getRole(), Operation.EQUAL )
268                                         };
269                 List<Row> rows = m_driver.select(m_con, columns, tables, conditions);
270                 if (rows.size() > 0) {
271                         assert(1 == rows.size());       // should only have one match
272                         
273                         return rows.get(0).getLong(0);
274                 }
275                 
276                 return null;    // not found
277         }
278         
279         Long storeVar(LocalVariable var) throws SQLException
280         {
281                 long varId = m_driver.nextVal(m_con, CfbSchema.VARIABLE_SEQ);
282                 
283                 Object[][] values = { { 
284                                                         Long.valueOf(varId),
285                                                         var.getName(),
286                                                         var.getRole()
287                                                 } };
288                 int count = m_driver.insert(m_con, CfbSchema.VARIABLES, values);
289                 if (1 != count) {
290                         return null;
291                 }
292                 
293                 return Long.valueOf(varId);
294         }
295         
296         Long getPriorId(Analysis analysis) throws SQLException, TypeMismatchException
297         {
298                 Column[] columns = { CfbSchema.RUNID };
299                 Table[] tables = { CfbSchema.RUNS };
300                 Condition[] conditions = {
301                         new Condition( CfbSchema.PROJNAME, analysis.getProjectName(), Operation.EQUAL ),
302                         new Condition( CfbSchema.STARTTIME, analysis.getStart(), Operation.LESS_THAN ) 
303                 };
304                 Sort[] sorts = { new Sort( CfbSchema.STARTTIME, Sort.Direction.DESCENDING ) };
305                 int limit = 1;
306                 
307                 List<Row> rows = m_driver.select(m_con, columns, tables, conditions, sorts, limit);
308                 if (rows.size() < 1) {
309                         return null;
310                 }
311                 return rows.get(0).getLong(0);
312         }
313         
314         Analysis getAnalysis(Long analysisId) throws SQLException, TypeMismatchException
315         {
316                 Column[] columns = { CfbSchema.PROJNAME, CfbSchema.VERSION, CfbSchema.STARTTIME, CfbSchema.ENDTIME };
317                 Table[] tables = { CfbSchema.RUNS };
318                 Condition[] conditions = { new Condition( CfbSchema.RUNID, analysisId, Operation.EQUAL ) };
319                 
320                 List<Row> rows = m_driver.select(m_con, columns, tables, conditions);
321                 if (rows.size() < 1) {
322                         return null;
323                 }
324                 
325                 Row row = rows.get(0);
326                 
327                 String projName = row.getString(0);
328                 String version = row.getString(1);
329                 java.util.Date start= row.getDate(2);
330                 java.util.Date end = row.getDate(3);
331                 
332                 Analysis prior = new Analysis(projName, version);
333                 prior.setId(analysisId.longValue());
334                 prior.setStart(start);
335                 prior.setEnd(end);
336                 
337                 prior.setBugCollection(getBugCollection(analysisId));
338                 
339                 return prior;
340         }
341         
342         BugCollection getBugCollection(Long runId) throws SQLException, TypeMismatchException 
343         {
344                 Column[] columns = {
345                                 CfbSchema.FOUNDID,
346                                 CfbSchema.BUGID,
347                                 CfbSchema.CATEGORYID,
348                                 CfbSchema.FIRSTLOCID,
349                                 CfbSchema.SECONDLOCID,
350                                 CfbSchema.THIRDLOCID,
351                                 CfbSchema.VARID_FK 
352                 };
353                 Table[] tables = {
354                                 CfbSchema.FOUND
355                 };
356                 Condition[] conditions = {
357                                 new Condition(CfbSchema.RUNID, runId, Operation.EQUAL)
358                 };
359                 
360                 BugCollection coll = new BugCollection();
361                 
362                 List<Row> rows = m_driver.select(m_con, columns, tables, conditions);
363                 
364                 for (Row row : rows) {
365                         // long foundId = row.getLong(0);
366                         Long bugId = row.getLong(1);
367                         Long categoryId = row.getLong(2);
368                         Long firstLocId = row.getLong(3);
369                         Long secondLocId = row.getLong(4);
370                         Long thirdLocId = row.getLong(5);
371                         Long varId = row.getLong(6);
372                         
373                         String bugType = getBugType(bugId);
374                         String category = getCategoryName(categoryId);
375                         Location[] locations = { getLoc(firstLocId), getLoc(secondLocId), getLoc(thirdLocId) };
376                         LocalVariable[] vars = { getVar(varId) };
377
378                         
379                         BugInstance bug = new BugInstance(bugId, category, bugType, locations, vars);
380                         coll.add(bug);
381                 }
382                 
383                 return coll;
384         }
385 }