]> git.uio.no Git - ifi-stolz-refaktor.git/blob - case-study/refaktor-after/src/no/uio/ifi/refaktor/statistics/StatisticsAspect.aj
Case Study: adding data and statistics
[ifi-stolz-refaktor.git] / case-study / refaktor-after / src / no / uio / ifi / refaktor / statistics / StatisticsAspect.aj
1 package no.uio.ifi.refaktor.statistics;
2
3 import java.io.BufferedOutputStream;
4 import java.io.File;
5 import java.io.FileNotFoundException;
6 import java.io.FileOutputStream;
7 import java.io.PrintWriter;
8 import java.util.Date;
9 import java.util.HashMap;
10 import java.util.LinkedList;
11 import java.util.List;
12 import java.util.Map;
13
14 import no.uio.ifi.refaktor.analyze.ExtractAndMoveMethodCandidate;
15 import no.uio.ifi.refaktor.analyze.analyzers.CompilationUnitWideExtractAndMoveMethodAnalyzer;
16 import no.uio.ifi.refaktor.analyze.analyzers.ExtractAndMoveMethodAnalyzer;
17 import no.uio.ifi.refaktor.analyze.analyzers.PackageWideExtractAndMoveMethodAnalyzer;
18 import no.uio.ifi.refaktor.analyze.analyzers.SearchBasedExtractAndMoveMethodAnalyzer;
19 import no.uio.ifi.refaktor.analyze.analyzers.TypeWideExtractAndMoveMethodAnalyzer;
20 import no.uio.ifi.refaktor.change.executors.ExtractAndMoveMethodExecutor;
21 import no.uio.ifi.refaktor.change.executors.ExtractMethodPostExecutionResources;
22 import no.uio.ifi.refaktor.change.performers.RefactoringPerformer;
23 import no.uio.ifi.refaktor.debugging.RefaktorDebug;
24 import no.uio.ifi.refaktor.exceptions.RefaktorException;
25 import no.uio.ifi.refaktor.statistics.reports.Report;
26 import no.uio.ifi.refaktor.statistics.reports.SimpleReport;
27
28 import org.eclipse.jdt.core.IMethod;
29 import org.eclipse.jdt.core.JavaModelException;
30 import org.eclipse.ltk.core.refactoring.Refactoring;
31
32 privileged public aspect StatisticsAspect {
33
34         public class Statistics {
35
36                 private final String projectName;
37                 private final Map<String, Integer> successfullyPerformedRefactorings;
38                 private final Map<String, Integer> notPerformedRefactorings;
39                 private final long snapshotTimeStamp;
40
41                 private long startOfStatisticsTimeStamp;
42                 private long analysisTimeStamp;
43                 private long totalAnalysisTimeInMillis;
44                 private long totalChangeTimeInMillis;
45                 private int methodCount;
46                 private int typeCount;
47                 private int compilationUnitCount;
48                 private int packageCount;
49                 private int extractAndMoveMethodExecutedCount;
50                 private int extractAndMoveMethodNotExecutedCount;
51                 private int extractAndMoveMethodResultFoundCount;
52                 private int extractAndMoveMethodResultNotFoundCount;
53                 private int extractAndMoveMethodResultGeneratedCount;
54                 private int selectionsAnalyzedCount;
55                 private List<ExtractAndMoveMethodExecutionResult> extractAndMoveMethodExecutionResults;
56                 private List<ExtractAndMoveMethodExecutionResult> extractAndMoveMethodNotFullyExecutedExecutionResults;
57
58                 private Statistics(String projectName) {
59                         this.projectName = projectName;
60                         startOfStatisticsTimeStamp = timeStamp();
61                         methodCount = typeCount = compilationUnitCount = packageCount = 0;
62                         successfullyPerformedRefactorings = new HashMap<String, Integer>();
63                         notPerformedRefactorings = new HashMap<String, Integer>();
64                         extractAndMoveMethodExecutedCount = extractAndMoveMethodNotExecutedCount = 0;
65                         extractAndMoveMethodResultFoundCount = extractAndMoveMethodResultNotFoundCount = 0;
66                         extractAndMoveMethodResultGeneratedCount = selectionsAnalyzedCount = 0;
67                         snapshotTimeStamp = timeStamp();
68                         analysisTimeStamp = timeStamp();
69                         totalAnalysisTimeInMillis = 0;
70                         totalChangeTimeInMillis = 0;
71                         extractAndMoveMethodExecutionResults = new LinkedList<ExtractAndMoveMethodExecutionResult>();
72                         extractAndMoveMethodNotFullyExecutedExecutionResults = new LinkedList<ExtractAndMoveMethodExecutionResult>();
73                 }
74
75                 private Statistics(Statistics statistics) {
76                         this.projectName = statistics.projectName;
77                         this.startOfStatisticsTimeStamp = statistics.startOfStatisticsTimeStamp;
78                         this.successfullyPerformedRefactorings = new HashMap<String, Integer>(statistics.successfullyPerformedRefactorings);
79                         this.notPerformedRefactorings = new HashMap<String, Integer>(statistics.notPerformedRefactorings);
80                         this.methodCount = statistics.methodCount;
81                         this.typeCount = statistics.typeCount;
82                         this.compilationUnitCount = statistics.compilationUnitCount;
83                         this.packageCount = statistics.packageCount;
84                         this.extractAndMoveMethodExecutedCount = statistics.extractAndMoveMethodExecutedCount;
85                         this.extractAndMoveMethodNotExecutedCount = statistics.extractAndMoveMethodNotExecutedCount;
86                         this.extractAndMoveMethodResultFoundCount = statistics.extractAndMoveMethodResultFoundCount;
87                         this.extractAndMoveMethodResultNotFoundCount = statistics.extractAndMoveMethodResultNotFoundCount;
88                         this.extractAndMoveMethodResultGeneratedCount = statistics.extractAndMoveMethodResultGeneratedCount;
89                         this.selectionsAnalyzedCount = statistics.selectionsAnalyzedCount;
90                         this.snapshotTimeStamp = timeStamp();
91                         this.analysisTimeStamp = statistics.analysisTimeStamp;
92                         this.totalAnalysisTimeInMillis = statistics.totalAnalysisTimeInMillis;
93                         this.totalChangeTimeInMillis = statistics.totalChangeTimeInMillis;
94                         this.extractAndMoveMethodExecutionResults = 
95                                         new LinkedList<ExtractAndMoveMethodExecutionResult>(statistics.extractAndMoveMethodExecutionResults);
96                         this.extractAndMoveMethodNotFullyExecutedExecutionResults = 
97                                         new LinkedList<ExtractAndMoveMethodExecutionResult>(statistics.extractAndMoveMethodNotFullyExecutedExecutionResults);
98                 }
99
100                 private void noteLastAnalysisTime() {
101                         analysisTimeStamp = timeStamp();
102                 }
103
104                 private String timeSinceStart(long timeStamp) {
105                         return timeBetween(startOfStatisticsTimeStamp, timeStamp);
106                 }
107
108                 private String timeBetween(long startTimeStamp, long endTimeStamp) {
109                         long durationInMillis = endTimeStamp - startTimeStamp;
110
111                         if (durationInMillis > 999) {
112                                 long secondsSinceAnalysisStart = Math.round(durationInMillis/1000.0);
113                                 return secondsSinceAnalysisStart/60 + "m" + secondsSinceAnalysisStart % 60 + "s";
114                         } else {
115                                 return durationInMillis + "ms";
116                         }
117                 }
118
119                 private String timeUsed(long timeInMillis) {
120                         return timeBetween(0, timeInMillis);
121                 }
122
123                 private Statistics generateSnapshot() {
124                         return new Statistics(this);
125                 }
126
127                 @Override
128                 public String toString() {
129                         return generateReportString(new SimpleReport());
130                 }
131
132                 public void generateReportFile() {
133                         PrintWriter printWriter = null;
134                         try {
135                                 File reportFile = createReportFile(SimpleReport.FILENAME_EXTENSION);
136                                 printWriter = new PrintWriter(new BufferedOutputStream(new FileOutputStream(reportFile)));
137                                 printWriter.print(generateReportString(new SimpleReport()));
138                         } catch (FileNotFoundException e) {
139                                 throw new RefaktorException(e);
140                         } finally {
141                                 if (printWriter != null)
142                                         printWriter.close();
143                         }
144                 }
145
146                 private File createReportFile(String filenameExtension) {
147                         File reportDir = new File(System.getProperty("refaktor.reports", "/tmp"));
148
149                         if (!reportDir.exists()) {
150                                 boolean success = reportDir.mkdirs();
151                                 if (!success) {
152                                         RefaktorDebug.log("Could not create directory: " + reportDir);
153                                 }
154                         }
155
156                         String filenamePrefix = System.getProperty("refaktor.reportPrefix", "refaktor_statistics_report");
157                         File reportFile;
158                         int serialNumber = 0;
159
160                         do {
161                                 reportFile = new File(reportDir, filenamePrefix + (serialNumber == 0 ? "" : "_" + serialNumber) + filenameExtension);
162                                 serialNumber++;
163                         } while(reportFile.exists());
164                         return reportFile;
165                 }
166
167                 private String generateReportString(Report report) {
168                         report.setHeading("Statistics (Report created " + new Date().toString() + ".)");
169
170                         report.addData("Project name", projectName);
171                         report.addData("Total time used", timeSinceStart(snapshotTimeStamp));
172
173                         long totalTimeInMillis = snapshotTimeStamp - startOfStatisticsTimeStamp;
174                         long miscellaneousTimeInMillis = totalTimeInMillis - totalAnalysisTimeInMillis - totalChangeTimeInMillis;
175
176                         report.addData("Analysis time used", timeUsed(totalAnalysisTimeInMillis) + percentOf(totalAnalysisTimeInMillis, totalTimeInMillis));
177                         report.addData("Change time used", timeUsed(totalChangeTimeInMillis) + percentOf(totalChangeTimeInMillis, totalTimeInMillis));
178                         report.addData("Time used on miscellaneous tasks", timeUsed(miscellaneousTimeInMillis) + percentOf(miscellaneousTimeInMillis, totalTimeInMillis));
179
180
181                         // Section
182                         report.addSection("Analysis (numbers of each category analyzed)");
183
184                         if (packageCount > 0)
185                                 report.addData("Packages", packageCount);
186
187                         if (compilationUnitCount > 0)
188                                 report.addData("Compilation units", compilationUnitCount);
189
190                         if (typeCount > 0)
191                                 report.addData("Types", typeCount);
192
193                         report.addData("Methods", methodCount);
194                         report.addData("Text selections", selectionsAnalyzedCount);
195
196                         // Section
197                         report.addSection("Analysis results for the Extract and Move Method refactoring");
198
199                         report.addData("Number of methods chosen as candidates", extractAndMoveMethodResultFoundCount);
200                         report.addData("Number of methods NOT chosen as candidates", extractAndMoveMethodResultNotFoundCount);
201                         report.addData("Number of possible candidate selections (multiple per method)", extractAndMoveMethodResultGeneratedCount);
202
203                         // Section
204                         if (successfullyPerformedRefactorings.size() > 0) {
205                                 report.addSection("Primitive refactorings executed");
206
207                                 for (String refactoringName: successfullyPerformedRefactorings.keySet()) {
208                                         // Subsection
209                                         report.addSubsection(refactoringName);
210
211                                         Integer successCount = successfullyPerformedRefactorings.get(refactoringName);
212                                         report.addData("Performed", successCount);
213
214                                         Integer notPerformedCount = notPerformedRefactorings.get(refactoringName);
215                                         if (notPerformedCount != null) {
216                                                 report.addData("Not performed", notPerformedCount);
217                                                 report.addData("Total", successCount + notPerformedCount);
218                                         }
219                                 }
220                         }
221
222                         // Section
223                         if (extractAndMoveMethodExecutedCount > 0 || extractAndMoveMethodNotExecutedCount > 0) {
224                                 report.addSection("Extract and Move Method refactorings");
225
226                                 if (extractAndMoveMethodExecutedCount > 0)
227                                         report.addData("Fully executed", extractAndMoveMethodExecutedCount);
228
229                                 if (extractAndMoveMethodNotExecutedCount > 0)
230                                         report.addData("Not fully executed", extractAndMoveMethodNotExecutedCount);
231
232                                 report.addData("Total attempts", extractAndMoveMethodExecutedCount + extractAndMoveMethodNotExecutedCount);
233                         }
234
235                         // Section
236                         if (extractAndMoveMethodExecutionResults.size() > 0) {
237                                 report.addSection("Fully executed Extract and Move Methods");
238                                 for (ExtractAndMoveMethodExecutionResult result: extractAndMoveMethodExecutionResults)
239                                         result.addToReport(report);
240                         }
241
242                         // Section
243                         if (extractAndMoveMethodNotFullyExecutedExecutionResults.size() > 0) {
244                                 report.addSection("Not fully executed Extract and Move Methods");
245                                 for (ExtractAndMoveMethodExecutionResult result: extractAndMoveMethodNotFullyExecutedExecutionResults)
246                                         result.addToReport(report);
247                         }
248
249                         return report.generateReport();
250                 }
251
252                 private String percentOf(long timeInMillis, long totalTimeInMillis) {
253                         return " (" + Math.round(((timeInMillis*100.0)/totalTimeInMillis)) + "%)";
254                 }
255         }
256
257         private Statistics statistics;
258         private Map<String, Long> methodAnalysisTimeStamps;
259         private Map<Refactoring, Long> changeTimeStamps;
260
261         private long timeStamp() {
262                 return System.currentTimeMillis();
263         }
264
265         public StatisticsAspect() {
266                 this.statistics = new Statistics("no name");
267                 this.methodAnalysisTimeStamps = new HashMap<String, Long>();
268                 this.changeTimeStamps = new HashMap<Refactoring, Long>();
269         }
270
271         public static void init() {
272                 init("no name");
273         }
274         
275         public static void init(String projectName) {
276                 aspectOf().privateInit(projectName);
277         }
278
279         private void privateInit(String projectName) {
280                 this.statistics = new Statistics(projectName);
281                 this.methodAnalysisTimeStamps = new HashMap<String, Long>();
282                 this.changeTimeStamps = new HashMap<Refactoring, Long>();
283         }
284
285         public static Statistics getSnapshot() {
286                 return aspectOf().statistics.generateSnapshot();
287         }
288
289         pointcut selectionAnalyze() :
290                 call(* ExtractAndMoveMethodAnalyzer.analyze()) ;
291
292         pointcut methodAnalyze(SearchBasedExtractAndMoveMethodAnalyzer analyzer) : 
293                 call(* SearchBasedExtractAndMoveMethodAnalyzer.analyze()) && target(analyzer);
294
295         pointcut typeAnalyze() : 
296                 call(* TypeWideExtractAndMoveMethodAnalyzer.analyze());
297
298         pointcut compilationUnitAnalyze() : 
299                 call(* CompilationUnitWideExtractAndMoveMethodAnalyzer.analyze());
300
301         pointcut packageAnalyze() :
302                 call(* PackageWideExtractAndMoveMethodAnalyzer.analyze());
303
304         pointcut performRefactoring(Refactoring refactoring) : 
305                 call(* RefactoringPerformer+.performRefactoring(Refactoring)) && args(refactoring);
306
307         pointcut extractAndMoveExecuted(ExtractAndMoveMethodExecutor executor) : 
308                 call(* ExtractAndMoveMethodExecutor.execute(..)) && target(executor);
309
310         pointcut analysisResultCreated() : 
311                 call(public ExtractAndMoveMethodCandidate.new(..));
312
313         after() : selectionAnalyze() {
314                 statistics.selectionsAnalyzedCount++;
315         }
316
317         before(SearchBasedExtractAndMoveMethodAnalyzer analyzer) : methodAnalyze(analyzer) {
318                 noteStartOfAnalysisForMethod(analyzer.method);
319         }
320
321         after(SearchBasedExtractAndMoveMethodAnalyzer analyzer) : methodAnalyze(analyzer) {
322                 statistics.methodCount++;
323                 noteEndOfAnalysisForMethod(analyzer.method);
324                 statistics.noteLastAnalysisTime();
325                 debugPrintMethodAnalysisProgress(analyzer.method);
326         }
327
328         private void noteStartOfAnalysisForMethod(IMethod method) {
329                 methodAnalysisTimeStamps.put(method.getKey(), timeStamp());
330         }
331
332         private void noteEndOfAnalysisForMethod(IMethod method) {
333                 generated_5229367356361043229(method);
334         }
335
336         public void generated_5229367356361043229(IMethod method) {
337                 long methodTimeStamp = methodAnalysisTimeStamps.remove(method.getKey());
338                 statistics.totalAnalysisTimeInMillis += timeStamp() - methodTimeStamp;
339         }
340
341         private void debugPrintMethodAnalysisProgress(IMethod method) {
342                 generated_8558438568074782975(method);
343         }
344
345         public void generated_8558438568074782975(IMethod method) {
346                 try {
347                         RefaktorDebug.println("#" + statistics.methodCount + " (" + statistics.timeSinceStart(timeStamp()) + "): " 
348                                         + method.getDeclaringType().getElementName() + "." + method.getElementName() + " (" + "offset: "
349                                         + method.getSourceRange().getOffset() + ", length: " + method.getSourceRange().getLength() + ")");
350                 } catch (JavaModelException e) {
351                         RefaktorDebug.println("No info about " + method.getElementName());
352                 }
353         }
354
355         after(SearchBasedExtractAndMoveMethodAnalyzer analyzer) returning(): methodAnalyze(analyzer) {
356                 statistics.extractAndMoveMethodResultFoundCount++;
357         }
358
359         after(SearchBasedExtractAndMoveMethodAnalyzer analyzer) throwing(): methodAnalyze(analyzer) {
360                 statistics.extractAndMoveMethodResultNotFoundCount++;
361         }
362
363         after(): typeAnalyze() {
364                 statistics.typeCount++;
365         }
366
367         after(): compilationUnitAnalyze() {
368                 statistics.compilationUnitCount++;
369         }
370
371         after(): packageAnalyze() {
372                 statistics.packageCount++;
373         }
374
375         before(Refactoring refactoring) : performRefactoring(refactoring) {
376                 noteStartOfRefactoring(refactoring);
377         }
378
379         after(Refactoring refactoring) : performRefactoring(refactoring) {
380                 noteEndOfChange(refactoring);
381         }
382
383         private void noteStartOfRefactoring(Refactoring refactoring) {
384                 changeTimeStamps.put(refactoring, timeStamp());
385         }
386
387         private void noteEndOfChange(Refactoring refactoring) {
388                 generated_3462337828748833964(refactoring);
389         }
390
391         public void generated_3462337828748833964(Refactoring refactoring) {
392                 long changeTimeStamp = changeTimeStamps.remove(refactoring);
393                 statistics.totalChangeTimeInMillis += timeStamp() - changeTimeStamp;
394         }
395
396         after(Refactoring refactoring) returning() : performRefactoring(refactoring) {
397                 Integer count = statistics.successfullyPerformedRefactorings.get(refactoring.getName());
398                 if (count == null)
399                         statistics.successfullyPerformedRefactorings.put(refactoring.getName(), 1);
400                 else
401                         statistics.successfullyPerformedRefactorings.put(refactoring.getName(), count + 1);
402         }
403
404         after(Refactoring refactoring) throwing() : performRefactoring(refactoring) {
405                 Integer count = statistics.notPerformedRefactorings.get(refactoring.getName());
406                 if (count == null)
407                         statistics.notPerformedRefactorings.put(refactoring.getName(), 1);
408                 else
409                         statistics.notPerformedRefactorings.put(refactoring.getName(), count + 1);
410         }
411
412         after(ExtractAndMoveMethodExecutor executor) returning() : extractAndMoveExecuted(executor) {
413                 statistics.extractAndMoveMethodExecutedCount++;
414                 statistics.extractAndMoveMethodExecutionResults.add(new ExtractAndMoveMethodExecutionResult(
415                                 statistics.extractAndMoveMethodExecutedCount,
416                                 executor.analysisResult, 
417                                 executor.postExecutionResources.getSignatureOfExtractedMethod(), 
418                                 executor.originalTarget));
419         }
420
421         after(ExtractAndMoveMethodExecutor executor) throwing() : extractAndMoveExecuted(executor) {
422                 statistics.extractAndMoveMethodNotExecutedCount++;
423                 ExtractMethodPostExecutionResources postExecutionResources = executor.postExecutionResources;
424                 statistics.extractAndMoveMethodNotFullyExecutedExecutionResults.add(new ExtractAndMoveMethodExecutionResult(
425                                 statistics.extractAndMoveMethodNotExecutedCount,
426                                 executor.analysisResult, 
427                                 postExecutionResources == null ? "<extract method not executed>" : postExecutionResources.getSignatureOfExtractedMethod(), 
428                                                 executor.originalTarget));
429         }
430
431         after() returning() : analysisResultCreated() {
432                 statistics.extractAndMoveMethodResultGeneratedCount++;
433         }
434
435 }