KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > edu > umd > cs > findbugs > workflow > MineBugHistory


1 /*
2  * FindBugs - Find Bugs in Java programs
3  * Copyright (C) 2005, University of Maryland
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18  */

19
20 package edu.umd.cs.findbugs.workflow;
21
22 import java.io.FileOutputStream JavaDoc;
23 import java.io.PrintStream JavaDoc;
24 import java.text.SimpleDateFormat JavaDoc;
25 import java.util.Date JavaDoc;
26 import java.util.HashMap JavaDoc;
27 import java.util.Iterator JavaDoc;
28 import java.util.Map JavaDoc;
29
30 import edu.umd.cs.findbugs.AppVersion;
31 import edu.umd.cs.findbugs.BugCollection;
32 import edu.umd.cs.findbugs.BugInstance;
33 import edu.umd.cs.findbugs.DetectorFactoryCollection;
34 import edu.umd.cs.findbugs.Project;
35 import edu.umd.cs.findbugs.SortedBugCollection;
36 import edu.umd.cs.findbugs.TigerSubstitutes;
37 import edu.umd.cs.findbugs.config.CommandLine;
38
39 /**
40  * Mine historical information from a BugCollection.
41  * The BugCollection should be built using UpdateBugCollection
42  * to record the history of analyzing all versions over time.
43  *
44  * @author David Hovemeyer
45  * @author William Pugh
46  */

47 public class MineBugHistory {
48     static final int ADDED = 0;
49     static final int NEWCODE = 1;
50     static final int REMOVED = 2;
51     static final int REMOVEDCODE= 3;
52     static final int RETAINED = 4;
53     static final int DEAD = 5;
54     static final int ACTIVE_NOW = 6;
55     static final int TUPLE_SIZE = 7;
56     
57     static final SimpleDateFormat JavaDoc dateFormat = new SimpleDateFormat JavaDoc("yyyy.MM.dd-HH:mm");
58
59     static class Version {
60         long sequence;
61         int tuple[] = new int[TUPLE_SIZE];
62         
63         Version(long sequence) {
64             this.sequence = sequence;
65         }
66         
67         /**
68          * @return Returns the sequence.
69          */

70         public long getSequence() {
71             return sequence;
72         }
73
74         void increment(int key) {
75             tuple[key]++;
76             if (key == ADDED || key == RETAINED || key == NEWCODE)
77                 tuple[ACTIVE_NOW]++;
78         }
79         
80         int get(int key) {
81             return tuple[key];
82         }
83     }
84     
85     BugCollection bugCollection;
86     Version[] versionList;
87     Map JavaDoc<Long JavaDoc, AppVersion> sequenceToAppVersionMap = new HashMap JavaDoc<Long JavaDoc, AppVersion>();
88     boolean formatDates = false;
89     boolean noTabs = false;
90     boolean summary = false;
91     
92     public MineBugHistory() {
93     }
94     public MineBugHistory(BugCollection bugCollection) {
95         this.bugCollection = bugCollection;
96     }
97
98     public void setBugCollection(BugCollection bugCollection) {
99         this.bugCollection = bugCollection;
100     }
101
102     public void setFormatDates(boolean value) {
103         this.formatDates = value;
104     }
105
106     public void setNoTabs() {
107         this.noTabs = true;
108         this.summary = false;
109     }
110     public void setSummary() {
111         this.summary = true;
112         this.noTabs = false;
113     }
114
115     
116     
117     public MineBugHistory execute() {
118         long sequenceNumber = bugCollection.getSequenceNumber();
119         int maxSequence = (int)sequenceNumber;
120         versionList = new Version[maxSequence + 1];
121         for (int i = 0; i <= maxSequence; ++i) {
122             versionList[i] = new Version(i);
123         }
124         
125         for (Iterator JavaDoc<AppVersion> i = bugCollection.appVersionIterator(); i.hasNext();) {
126             AppVersion appVersion = i.next();
127             long versionSequenceNumber = appVersion.getSequenceNumber();
128             sequenceToAppVersionMap.put(TigerSubstitutes.valueOf(versionSequenceNumber), appVersion);
129         }
130         
131         AppVersion currentAppVersion = bugCollection.getCurrentAppVersion();
132         sequenceToAppVersionMap.put(
133             TigerSubstitutes.valueOf(sequenceNumber),
134             currentAppVersion);
135         
136         for (Iterator JavaDoc<BugInstance> j = bugCollection.iterator(); j.hasNext();) {
137             BugInstance bugInstance = j.next();
138
139             for (int i = 0; i <= maxSequence; ++i) {
140                 if (bugInstance.getFirstVersion() > i) continue;
141                 boolean activePrevious = bugInstance.getFirstVersion() < i
142                     && (bugInstance.getLastVersion() == -1 || bugInstance.getLastVersion() >= i-1 );
143                 boolean activeCurrent = bugInstance.getLastVersion() == -1 || bugInstance.getLastVersion() >= i ;
144                 
145                 int key = getKey(activePrevious, activeCurrent);
146                 if (key == REMOVED && !bugInstance.isRemovedByChangeOfPersistingClass()) key = REMOVEDCODE;
147                 else if (key == ADDED && !bugInstance.isIntroducedByChangeOfExistingClass()) key = NEWCODE;
148                 versionList[i].increment(key);
149             }
150         }
151         
152         return this;
153     }
154
155     
156     
157
158     public void dump(PrintStream JavaDoc out) {
159         if (noTabs) dumpNoTabs(out);
160         else if (summary) dumpSummary(out);
161         else dumpOriginal(out);
162     }
163
164     public void dumpSummary(PrintStream JavaDoc out) {
165     
166         StringBuffer JavaDoc b = new StringBuffer JavaDoc();
167         
168         for (int i = Math.max(0,versionList.length - 10); i < versionList.length; ++i) {
169             Version version = versionList[i];
170             int added = version.get(ADDED) + version.get(NEWCODE);
171             int removed = version.get(REMOVED) + version.get(REMOVEDCODE);
172             b.append(" ");
173             if (added > 0) {
174                 b.append('+'); b.append(added);
175             }
176             if (removed > 0) {
177                 b.append('-'); b.append(removed);
178             }
179             if (added == 0 && removed == 0) b.append('0');
180             
181             int paddingNeeded = 8 - b.length() % 8;
182             if (paddingNeeded > 0) b.append(" ".substring(0,paddingNeeded));
183         }
184             
185         out.println(b.toString());
186     }
187     
188     /** This is how dump() was implemented up to and including version 0.9.5. */
189     public void dumpOriginal(PrintStream JavaDoc out) {
190         out.println("seq version time classes NCSS added newCode fixed removed retained dead active");
191         for (int i = 0; i < versionList.length; ++i) {
192             Version version = versionList[i];
193             AppVersion appVersion = sequenceToAppVersionMap.get(version.getSequence());
194             out.print(i);
195             out.print('\t');
196             out.print(appVersion != null ? appVersion.getReleaseName() : "");
197             out.print('\t');
198             if (formatDates)
199                 out.print("\"" + (appVersion != null ? new Date JavaDoc(appVersion.getTimestamp()).toString() : "") + "\"");
200             else out.print(appVersion != null ? appVersion.getTimestamp() : 0L);
201             out.print('\t');
202             if (appVersion != null) {
203                 out.print(appVersion.getNumClasses());
204                 out.print('\t');
205                 out.print(appVersion.getCodeSize());
206
207             } else out.print("\t0\t0");
208
209             for (int j = 0; j < TUPLE_SIZE; ++j) {
210                 out.print('\t');
211                 out.print(version.get(j));
212             }
213             out.println();
214         }
215     }
216
217     /** emit <code>width</code> space characters to <code>out</code> */
218     private static void pad(int width, PrintStream JavaDoc out) {
219         while (width-- > 0) out.print(' ');
220     }
221
222     /** equivalent to out.print(obj) except it may be padded on the left or right
223      * @param width padding will occur if the stringified oxj is shorter than this
224      * @param alignRight true to pad on the left, false to pad on the right
225      * @param out the PrintStream printed to
226      * @param obj the value to print (may be an auto-boxed primitive) */

227     private static void print(int width, boolean alignRight, PrintStream JavaDoc out, Object JavaDoc obj) {
228         String JavaDoc s = String.valueOf(obj);
229         int padLen = width - s.length();
230         if (alignRight) pad(padLen, out);
231         out.print(s); // doesn't truncate if (s.length() > width)
232
if (!alignRight) pad(padLen, out);
233     }
234
235     /** This implementation of dump() tries to better align columns (when viewed
236      * with a fixed-width font) by padding with spaces instead of using tabs.
237      * Also, timestamps are formatted more tersely (-formatDates option).
238      * The bad news is that it requires a minimum of 112 columns.
239      * @see #dumpOriginal(PrintStream) */

240     public void dumpNoTabs(PrintStream JavaDoc out) {
241         //out.println("seq version time classes NCSS added newCode fixed removed retained dead active");
242
print(3, true, out, "seq");
243         out.print(' ');
244         print(19, false, out, "version");
245         out.print(' ');
246         print(16, false, out, "time");
247         print(1+7, true, out, "classes");
248         print(1+7, true, out, "NCSS");
249         print(1+7, true, out, "added");
250         print(1+7, true, out, "newCode");
251         print(1+7, true, out, "fixed");
252         print(1+7, true, out, "removed");
253         print(1+8, true, out, "retained");
254         print(1+6, true, out, "dead");
255         print(1+7, true, out, "active");
256         out.println();
257         //note: if we were allowed to depend on JDK 1.5 we could use out.printf():
258
//Object line[] = { "seq", "version", "time", "classes", "NCSS", "added", "newCode", "fixed", "removed", "retained", "dead", "active" };
259
//out.printf("%3s %-19s %-16s %7s %7s %7s %7s %7s %7s %8s %6s %7s%n", line);
260
for (int i = 0; i < versionList.length; ++i) {
261             Version version = versionList[i];
262             AppVersion appVersion = sequenceToAppVersionMap.get(version.getSequence());
263             print(3, true, out, i); //out.print(i);
264
out.print(' '); // '\t'
265
print(19, false, out, appVersion != null ? appVersion.getReleaseName() : "");
266             out.print(' ');
267
268             long ts = (appVersion != null ? appVersion.getTimestamp(): 0L);
269             if (formatDates)
270                 print(16, false, out, dateFormat.format(ts));
271             else print(16, false, out, ts);
272             out.print(' ');
273
274             print(7, true, out, appVersion != null ? appVersion.getNumClasses() : 0);
275             out.print(' ');
276             print(7, true, out, appVersion != null ? appVersion.getCodeSize() : 0);
277
278             for (int j = 0; j < TUPLE_SIZE; ++j) {
279                 out.print(' ');
280                 print(7, true, out, version.get(j));
281             }
282             out.println();
283         }
284     }
285
286     /**
287      * Get key used to classify the presence and/or abscence of a BugInstance
288      * in successive versions in the history.
289      *
290      * @param activePrevious true if the bug was active in the previous version, false if not
291      * @param activeCurrent true if the bug is active in the current version, false if not
292      * @return the key: one of ADDED, RETAINED, REMOVED, and DEAD
293      */

294     private int getKey(boolean activePrevious, boolean activeCurrent) {
295         if (activePrevious)
296             return activeCurrent ? RETAINED : REMOVED;
297         else // !activePrevious
298
return activeCurrent ? ADDED : DEAD;
299     }
300
301      class MineBugHistoryCommandLine extends CommandLine {
302
303         MineBugHistoryCommandLine() {
304             addSwitch("-formatDates", "render dates in textual form");
305             addSwitch("-noTabs", "delimit columns with groups of spaces for better alignment");
306             addSwitch("-summary", "just summarize changes over the last ten entries");
307         }
308
309         @Override JavaDoc
310         public void handleOption(String JavaDoc option, String JavaDoc optionalExtraPart) {
311             if (option.equals("-formatDates"))
312                 setFormatDates(true);
313             else if (option.equals("-noTabs")) setNoTabs();
314             else if (option.equals("-summary")) setSummary();
315             else
316                 throw new IllegalArgumentException JavaDoc("unknown option: " + option);
317         }
318
319         @Override JavaDoc
320         public void handleOptionWithArgument(String JavaDoc option, String JavaDoc argument) {
321
322                 throw new IllegalArgumentException JavaDoc("unknown option: " + option);
323         }
324     }
325
326     public static void main(String JavaDoc[] args) throws Exception JavaDoc {
327         DetectorFactoryCollection.instance(); // load plugins
328

329         MineBugHistory mineBugHistory = new MineBugHistory();
330         MineBugHistoryCommandLine commandLine = mineBugHistory.new MineBugHistoryCommandLine();
331         int argCount = commandLine.parse(args, 0, 2, "Usage: " + MineBugHistory.class.getName()
332                 + " [options] [<xml results> [<history]] ");
333
334         SortedBugCollection bugCollection = new SortedBugCollection();
335         if (argCount < args.length)
336             bugCollection.readXML(args[argCount++], new Project());
337         else bugCollection.readXML(System.in, new Project());
338         mineBugHistory.setBugCollection(bugCollection);
339
340         mineBugHistory.execute();
341         PrintStream JavaDoc out = System.out;
342         
343         try {
344         if (argCount < args.length) {
345             out = new PrintStream JavaDoc(new FileOutputStream JavaDoc(args[argCount++]), true);
346             }
347         mineBugHistory.dump(out);
348         } finally {
349         out.close();
350         }
351         
352     }
353 }
354
Popular Tags