Add some unit testing of the CfbSchema.
[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.Sort;
15 import net.jaekl.cfb.db.Table;
16 import net.jaekl.cfb.db.TypeMismatchException;
17 import net.jaekl.cfb.db.driver.DbDriver;
18 import net.jaekl.cfb.xml.BugClass;
19 import net.jaekl.cfb.xml.BugCollection;
20 import net.jaekl.cfb.xml.BugInstance;
21 import net.jaekl.cfb.xml.BugMethod;
22 import net.jaekl.cfb.xml.LocalVariable;
23 import net.jaekl.cfb.xml.SourceLine;
24 import net.jaekl.cfb.xml.messages.MessageCollection;
25
26 public class DbStore {
27         Connection m_con;
28         DbDriver m_driver;
29         MessageCollection m_msgColl;
30         
31         public DbStore(Connection con, DbDriver driver, MessageCollection msgColl) {
32                 m_con = con;
33                 m_driver = driver;
34                 m_msgColl = msgColl;
35         }
36
37         public Analysis getPrior(Analysis analysis) throws SQLException, TypeMismatchException {
38                 if (null == analysis) {
39                         return null;
40                 }
41                 Long priorId = getPriorId(analysis);
42                 if (null == priorId) {
43                         return null;
44                 }
45                 
46                 return getAnalysis(priorId);
47         }
48         
49         public boolean put(Analysis analysis) throws SQLException, TypeMismatchException {
50                 if (null == analysis) {
51                         return false;
52                 }
53                 
54                 // ----------------------------------
55                 // Add a run record for this analysis
56                 
57                 long runId = m_driver.nextVal(m_con, CfbSchema.RUN_SEQ);
58                 Object[][] values = { 
59                                                                 {
60                                                                         Long.valueOf(runId),
61                                                                         analysis.getBuildNumber(),
62                                                                         analysis.getStart(),
63                                                                         analysis.getEnd() 
64                                                                 } 
65                                                         };
66                 int count = m_driver.insert(m_con, CfbSchema.RUNS, values);
67                 if (1 != count) {
68                         return false;
69                 }
70                 
71                 // -------------------------------------
72                 // Add a found record for each bug found
73                 
74                 List<BugInstance> bugs = analysis.getBugCollection().getBugs();
75                 values = new Object[bugs.size()][CfbSchema.FOUND.getNumColumns()];
76                 
77                 int row = 0;
78                 for (BugInstance bug : bugs)
79                 {
80                         Long foundId = Long.valueOf(m_driver.nextVal(m_con, CfbSchema.FOUND_SEQ));
81                         Long bugId = Long.valueOf(m_msgColl.getPattern(bug.getType()).getId());
82                         Long categoryId = Long.valueOf(m_msgColl.getCategory(bug.getCategory()).getId());
83                         Location[] locs = computeLocations(bug);
84                         Location firstLoc  = (locs.length > 0) ? locs[0] : null;
85                         Location secondLoc = (locs.length > 1) ? locs[1] : null;
86                         Location thirdLoc  = (locs.length > 2) ? locs[2] : null;
87                         
88                         values[row][0] = foundId;
89                         values[row][1] = bugId;
90                         values[row][2] = categoryId;
91                         values[row][3] = getLocId(firstLoc);
92                         values[row][4] = getLocId(secondLoc);
93                         values[row][5] = getLocId(thirdLoc);
94                         values[row][6] = getVarId(bug);
95                         row++;
96                 }
97                 
98                 count = m_driver.insert(m_con, CfbSchema.FOUND, values);
99                 return (bugs.size() == count);
100         }
101         
102         Location[] computeLocations(BugInstance bug)
103         {
104                 ArrayList<Location> locs = new ArrayList<Location>();
105                 
106                 /*
107                         Somewhat unfortunate special case.
108                         The primary "location" for a bug instance is split between tags.
109                         Most bugs have a pattern like this:
110                         <BugInstance>
111                                 ...
112                                 <Method>
113                                         <SourceLine .../>
114                                 </Method>
115                                 ...
116                                 <SourceLine .../>
117                         </BugInstance>
118                         
119                         The primary location for a bug is given by the <Method> with no role attribute, 
120                         but the <SourceLine/> inside that method describes the whole range of lines 
121                         covered by that Method, not the spot where the bug is located--that is given 
122                         by the <SourceLine/> that is a direct child fo the <BugInstance/>.
123                  */
124                 
125                 BugMethod primaryMethod = null;
126                 SourceLine primaryLine = null;
127                 
128                 for (BugMethod method : bug.getMethods()) {
129                         if (null != method.getRole()) {
130                                 primaryMethod = method;
131                                 break;
132                         }
133                 }
134                 if (bug.getLines().size() > 0) {
135                         primaryLine = bug.getLines().get(0);
136                 }
137                 
138                 if ((null != primaryMethod) && (null != primaryLine)) {
139                         locs.add(new Location(primaryMethod, primaryLine));
140                 }
141                 
142                 for (BugMethod method : bug.getMethods()) {
143                         if (primaryMethod != method) {
144                                 locs.add(new Location(method));                         
145                         }
146                 }
147                 for (SourceLine line : bug.getLines()) {
148                         if (primaryLine != line) {
149                                 locs.add(new Location(line));
150                         }
151                 }
152                 for (BugClass clazz : bug.getClasses()) {
153                         locs.add(new Location(clazz));
154                 }
155                 
156                 return locs.toArray(new Location[locs.size()]);
157         }
158         
159         Long getLocId(Location loc) throws SQLException, TypeMismatchException 
160         {
161                 if (null == loc) {
162                         return null;
163                 }
164                 Long locId = findLocId(loc);
165                 if (null != locId) {
166                         return locId;
167                 }
168
169                 return storeLoc(loc);
170         }
171         
172         Long findLocId(Location loc) throws SQLException, TypeMismatchException
173         {
174                 Column[] columns = { CfbSchema.LOCID };
175                 Table[] tables = { CfbSchema.LOCATIONS };
176                 
177                 Condition[] conditions = { 
178                                                 new Condition( CfbSchema.CLASSNAME,  loc.getClassName(),  Operation.EQUAL ),
179                                                 new Condition( CfbSchema.METHODNAME, loc.getMethodName(), Operation.EQUAL ),
180                                                 new Condition( CfbSchema.METHODROLE, loc.getMethodRole(), Operation.EQUAL ),
181                                                 new Condition( CfbSchema.STARTLINE,  loc.getStart(),      Operation.EQUAL ),
182                                                 new Condition( CfbSchema.ENDLINE,    loc.getEnd(),        Operation.EQUAL )
183                                         };
184                 List<Row> rows = m_driver.select(m_con, columns, tables, conditions);
185                 if (rows.size() > 0) {
186                         assert(1 == rows.size());       // should only have one match
187                         
188                         return rows.get(0).getLong(0);
189                 }
190                 
191                 return null;    // not found
192         }
193         
194         Long storeLoc(Location loc) throws SQLException
195         {
196                 long locId = m_driver.nextVal(m_con, CfbSchema.LOC_SEQ);
197                 
198                 Object[][] values = { { 
199                                                         Long.valueOf(locId),
200                                                         loc.getClassName(),
201                                                         loc.getMethodName(),
202                                                         loc.getMethodRole(),
203                                                         Long.valueOf(loc.getStart()),
204                                                         Long.valueOf(loc.getEnd())
205                                                 } };
206                 int count = m_driver.insert(m_con, CfbSchema.LOCATIONS, values);
207                 if (1 != count) {
208                         return null;
209                 }
210                 
211                 return Long.valueOf(locId);
212         }
213         
214         Long getVarId(BugInstance bug) throws SQLException, TypeMismatchException
215         {
216                 if (null == bug) {
217                         return null;
218                 }
219                 
220                 List<LocalVariable> vars = bug.getVariables();
221                 if ((null == vars) || (0 == vars.size())) {
222                         return null;
223                 }
224                 
225                 return getVarId(vars.get(0));
226         }
227         
228         Long getVarId(LocalVariable var) throws SQLException, TypeMismatchException
229         {
230                 if (null == var) {
231                         return null;
232                 }
233                 
234                 Long result = findVarId(var);
235                 
236                 if (null != result) {
237                         return result;
238                 }
239                 
240                 return storeVar(var);
241         }
242         
243         Long findVarId(LocalVariable var) throws SQLException, TypeMismatchException
244         {
245                 Column[] columns = { CfbSchema.VARID_PK };
246                 Table[] tables = { CfbSchema.VARIABLES };
247                 
248                 Condition[] conditions = { 
249                                                 new Condition( CfbSchema.NAME,    var.getName(), Operation.EQUAL ),
250                                                 new Condition( CfbSchema.VARROLE, var.getRole(), Operation.EQUAL )
251                                         };
252                 List<Row> rows = m_driver.select(m_con, columns, tables, conditions);
253                 if (rows.size() > 0) {
254                         assert(1 == rows.size());       // should only have one match
255                         
256                         return rows.get(0).getLong(0);
257                 }
258                 
259                 return null;    // not found
260         }
261         
262         Long storeVar(LocalVariable var) throws SQLException
263         {
264                 long varId = m_driver.nextVal(m_con, CfbSchema.VARIABLE_SEQ);
265                 
266                 Object[][] values = { { 
267                                                         Long.valueOf(varId),
268                                                         var.getName(),
269                                                         var.getRole()
270                                                 } };
271                 int count = m_driver.insert(m_con, CfbSchema.VARIABLES, values);
272                 if (1 != count) {
273                         return null;
274                 }
275                 
276                 return Long.valueOf(varId);
277         }
278         
279         Long getPriorId(Analysis analysis) throws SQLException, TypeMismatchException
280         {
281                 Column[] columns = { CfbSchema.RUNID };
282                 Table[] tables = { CfbSchema.RUNS };
283                 Condition[] conditions = { new Condition( CfbSchema.STARTTIME, analysis.getStart(), Operation.LESS_THAN ) };
284                 Sort[] sorts = { new Sort( CfbSchema.STARTTIME, Sort.Direction.DESCENDING ) };
285                 int limit = 1;
286                 
287                 List<Row> rows = m_driver.select(m_con, columns, tables, conditions, sorts, limit);
288                 if (rows.size() < 1) {
289                         return null;
290                 }
291                 return rows.get(0).getLong(0);
292         }
293         
294         Analysis getAnalysis(Long priorId) throws SQLException, TypeMismatchException
295         {
296                 Column[] columns = { CfbSchema.VERSION, CfbSchema.STARTTIME, CfbSchema.ENDTIME };
297                 Table[] tables = { CfbSchema.RUNS };
298                 Condition[] conditions = { new Condition( CfbSchema.RUNID, priorId, Operation.EQUAL ) };
299                 
300                 List<Row> rows = m_driver.select(m_con, columns, tables, conditions);
301                 if (rows.size() < 1) {
302                         return null;
303                 }
304                 
305                 Row row = rows.get(0);
306                 
307                 String version = row.getString(0);
308                 java.util.Date start= row.getDate(1);
309                 java.util.Date end = row.getDate(2);
310                 
311                 Analysis prior = new Analysis(version);
312                 prior.setId(priorId.longValue());
313                 prior.setStart(start);
314                 prior.setEnd(end);
315                 
316                 prior.setBugCollection(getBugCollection(priorId));
317                 
318                 return prior;
319         }
320         
321         BugCollection getBugCollection(Long runId) throws SQLException, TypeMismatchException 
322         {
323                 throw new UnsupportedOperationException("Not yet implemented");
324         }
325 }