-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathExampleFinder.java
More file actions
188 lines (165 loc) · 8.64 KB
/
ExampleFinder.java
File metadata and controls
188 lines (165 loc) · 8.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
package org.variantsync.diffdetective.examplesearch;
import org.tinylog.Logger;
import org.variantsync.diffdetective.analysis.Analysis;
import org.variantsync.diffdetective.datasets.PatchDiffParseOptions;
import org.variantsync.diffdetective.datasets.Repository;
import org.variantsync.diffdetective.diff.git.GitPatch;
import org.variantsync.diffdetective.diff.result.DiffParseException;
import org.variantsync.diffdetective.diff.text.TextBasedDiff;
import org.variantsync.diffdetective.feature.AnnotationParser;
import org.variantsync.diffdetective.show.Show;
import org.variantsync.diffdetective.util.Assert;
import org.variantsync.diffdetective.util.IO;
import org.variantsync.diffdetective.util.Source;
import org.variantsync.diffdetective.util.StringUtils;
import org.variantsync.diffdetective.variation.DiffLinesLabel;
import org.variantsync.diffdetective.variation.diff.Time;
import org.variantsync.diffdetective.variation.diff.VariationDiff;
import org.variantsync.diffdetective.variation.diff.filter.ExplainedFilter;
import org.variantsync.diffdetective.variation.diff.parse.VariationDiffParseOptions;
import org.variantsync.diffdetective.variation.diff.render.PatchDiffRenderer;
import org.variantsync.diffdetective.variation.diff.render.RenderOptions;
import org.variantsync.diffdetective.variation.diff.render.VariationDiffRenderer;
import org.variantsync.diffdetective.variation.diff.serialize.GraphFormat;
import org.variantsync.diffdetective.variation.diff.serialize.edgeformat.DefaultEdgeLabelFormat;
import org.variantsync.diffdetective.variation.diff.serialize.nodeformat.MappingsDiffNodeFormat;
import org.variantsync.diffdetective.variation.diff.serialize.treeformat.CommitDiffVariationDiffLabelFormat;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
/**
* Helper class to find suitable running examples.
* An example finder inspects each VariationDiff, checks some requirements relevant for the
* desired running example and writes candidates to a file and renders them.
* An example finder should be side-effect free (i.e., it does not alter or transform the given VariationDiff, just observes it).
*/
public class ExampleFinder implements Analysis.Hooks {
/**
* Default render options for exporting example candidates.
*/
public static final RenderOptions<DiffLinesLabel> ExportOptions = new RenderOptions<>(
GraphFormat.VARIATION_DIFF,
new CommitDiffVariationDiffLabelFormat(),
new MappingsDiffNodeFormat<>(),
new DefaultEdgeLabelFormat<>(),
false,
1000,
RenderOptions.DEFAULT().nodesize() / 3,
0.5 * RenderOptions.DEFAULT().edgesize(),
RenderOptions.DEFAULT().arrowsize() / 2,
2,
true,
List.of()
);
private final ExplainedFilter<VariationDiff<? extends DiffLinesLabel>> isGoodExample;
private final PatchDiffRenderer exampleExport;
/**
* Creates a new ExampleFinder.
*
* @param isGoodExample Function that decides whether a VariationDiff is an example candidate or not.
* Should return {@link Optional#empty()} when the given tree is not a good example and thus, should not be considered.
* Should return a VariationDiff when the given tree is a good example candidate and should be exported.
* The returned VariationDiff might be the exact same VariationDiff or a subtree (e.g., to only export a certain subtree that is relevant).
* @param renderer The renderer to use for rendering example candidates.
*/
public ExampleFinder(final ExplainedFilter<VariationDiff<? extends DiffLinesLabel>> isGoodExample, VariationDiffRenderer renderer) {
this.isGoodExample = isGoodExample;
this.exampleExport = new PatchDiffRenderer(renderer, ExportOptions);
}
public static <T> List<List<T>> split(List<T> elements, Predicate<T> split) {
final List<List<T>> result = new ArrayList<>();
List<T> current = new ArrayList<>();
for (final T t : elements) {
if (split.test(t)) {
if (!current.isEmpty()) {
result.add(current);
current = new ArrayList<>();
}
} else {
current.add(t);
}
}
if (!current.isEmpty()) {
result.add(current);
}
return result;
}
private boolean checkIfExample(Analysis analysis, String localDiff) {
// Logger.info(localDiff);
final VariationDiff<DiffLinesLabel> variationDiff = analysis.getCurrentVariationDiff();
final AnnotationParser annotationParser = analysis.getRepository().getParseOptions().variationDiffParseOptions().annotationParser();
// We do not want a variationDiff for the entire file but only for the local change to have a small example.
final VariationDiff<DiffLinesLabel> localTree;
try {
localTree = VariationDiff.fromDiff(localDiff, Source.findFirst(variationDiff, GitPatch.class), new VariationDiffParseOptions(annotationParser, true, true));
// Not every local diff can be parsed to a VariationDiff because diffs are unaware of the underlying language (i.e., CPP).
// We want only running examples whose diffs describe entire diff trees for easier understanding.
if (!isGoodExample.test(localTree)) {
return false;
}
} catch (DiffParseException e) {
return false;
}
exportExample(
analysis,
localDiff,
localTree,
analysis.getOutputDir()
);
return true;
}
@Override
public void initializeResults(Analysis analysis) {
Assert.assertEquals(analysis.getRepository().getParseOptions().diffStoragePolicy(), PatchDiffParseOptions.DiffStoragePolicy.REMEMBER_STRIPPED_DIFF);
}
@Override
public boolean analyzeVariationDiff(Analysis analysis) {
// We do not want a VariationDiff for the entire file but only for the local change to have a small example.
// Analyze all patches individually
final String localDiffs = getDiff(analysis.getCurrentVariationDiff());
boolean exampleFound = false;
for (List<String> diffLines : split(localDiffs.lines().toList(), s -> s.startsWith("@"))) {
exampleFound |= checkIfExample(analysis, String.join(StringUtils.LINEBREAK, diffLines));
}
return exampleFound;
}
private void exportExample(final Analysis analysis, final String tdiff, final VariationDiff<DiffLinesLabel> vdiff, Path outputDir) {
final Repository repo = analysis.getRepository();
final GitPatch patch = Source.findFirst(vdiff, GitPatch.class);
Assert.assertNotNull(patch);
outputDir = outputDir.resolve(Path.of(repo.getRepositoryName() + "_" + patch.getCommitHash()));
final String filename = patch.getFileName(Time.AFTER);
Logger.info("Exporting example candidate: {}", patch);
// export metadata
String metadata = "";
metadata += "Repository Name: " + repo.getRepositoryName() + StringUtils.LINEBREAK;
metadata += "Repository URL: " + repo.getRemoteURI() + StringUtils.LINEBREAK;
metadata += "Child commit: " + patch.getCommitHash() + StringUtils.LINEBREAK;
metadata += "Parent commit: " + patch.getParentCommitHash() + StringUtils.LINEBREAK;
metadata += "File: " + patch.getFileName(Time.AFTER) + StringUtils.LINEBREAK;
String githubLink = repo.getRemoteURI().toString();
if (githubLink.endsWith(".git")) {
githubLink = githubLink.substring(0, githubLink.length() - ".git".length());
}
githubLink += "/commit/" + patch.getCommitHash();
metadata += "Github: " + githubLink + StringUtils.LINEBREAK;
IO.tryWrite(outputDir.resolve(filename + ".metadata.txt"), metadata);
// export tdiff
IO.tryWrite(outputDir.resolve(filename + ".diff"), tdiff);
// export vdiff
//exampleExport.render(example, patch, treeDir);
try {
Show.diff(vdiff).dontShowButRenderToTexture().saveAsPng(outputDir.resolve(patch.getFileName(Time.AFTER) + ".png").toFile());
} catch (IOException e) {
Logger.error("Could not render example");
}
}
static String getDiff(final VariationDiff<?> tree) {
TextBasedDiff textBasedDiff = Source.findFirst(tree, TextBasedDiff.class);
Assert.assertNotNull(textBasedDiff);
return textBasedDiff.getDiff();
}
}