-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathGroundTruthExtraction.java
More file actions
254 lines (229 loc) · 10.2 KB
/
GroundTruthExtraction.java
File metadata and controls
254 lines (229 loc) · 10.2 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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
package org.variantsync.vevos.extraction;
import org.tinylog.Logger;
import org.variantsync.diffdetective.AnalysisRunner;
import org.variantsync.diffdetective.datasets.PatchDiffParseOptions;
import org.variantsync.diffdetective.datasets.Repository;
import org.variantsync.diffdetective.diff.git.DiffFilter;
import org.variantsync.diffdetective.feature.AnnotationParser;
import org.variantsync.diffdetective.feature.PreprocessorAnnotationParser;
import org.variantsync.diffdetective.variation.diff.parse.VariationDiffParseOptions;
import org.variantsync.vevos.extraction.gt.GroundTruth;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Path;
import java.util.Properties;
import java.util.function.BiConsumer;
import static org.variantsync.vevos.extraction.ConfigProperties.*;
/**
* Base class for ground truth extractions. This class offers basic utilities for any ground truth
* extraction and expects the implementation of an extraction runner that is provided in form of a
* supplier method.
*
* Each GroundTruthExtraction must be initialized with a set of properties that configure the
* extraction.
*/
public abstract class GroundTruthExtraction {
protected final Properties properties;
/**
* Initialize the basic GroundTruth extraction with a set of extraction properties.
*/
protected GroundTruthExtraction(Properties properties) {
this.properties = properties;
}
/**
* Main method to start the extraction. The method first loads the properties from the specified
* file and then intializes the specified extraction class with those properties. Lastly, it
* starts the ground truth extraction with the configuration specified in the properties.
*
* @param args Two arguments are expected: First, a path to a properties file in which the
* extraction is configured, and second, the full specifier of a GroundTruthExtraction
* subclass. The subclass' constructor must match the constructor of
* GroundTruthExtraction.
* @throws IOException When loading the properties fails.
*/
public static void main(String[] args) throws IOException {
checkOS();
// Load the configuration
Properties properties = getProperties(getPropertiesFile(args));
//
Class<?> extractionClass;
try {
extractionClass = determineExtractionClass(args);
} catch (ClassNotFoundException e) {
Logger.error("The class " + args[1] + " provided as program argument was not found.");
throw new RuntimeException(e);
}
GroundTruthExtraction extraction;
try {
extraction = initializeExtraction(extractionClass, properties);
} catch (NoSuchMethodException e) {
throw new RuntimeException(
"The required constructor does not exist for the specified class " + args[1]);
} catch (InvocationTargetException | InstantiationException | IllegalAccessException e) {
Logger.error("Was not able to instantiate extraction class with the propterties "
+ args[0] + " and the class name " + args[1]);
throw new RuntimeException(e);
}
var options = diffdetectiveOptions(properties);
Logger.info("Starting SPL history analysis.");
extraction.run(options);
}
private static Class<?> determineExtractionClass(String... args) throws ClassNotFoundException {
if (args.length > 1) {
return Class.forName(args[1]);
} else {
Logger.error(
"The second program argument must specify a valid GroundTruthExtraction class.");
throw new IllegalArgumentException(
"The second program argument must specify a valid GroundTruthExtraction class.");
}
}
private static GroundTruthExtraction initializeExtraction(Class<?> extractionClass,
Properties properties) throws NoSuchMethodException, InvocationTargetException,
InstantiationException, IllegalAccessException {
Constructor<?> constructor = extractionClass.getDeclaredConstructor(Properties.class);
constructor.setAccessible(true); // If the constructor is not public
return (GroundTruthExtraction) constructor.newInstance(properties);
}
/**
* Loads the properties in the given file.
*
* @param propertiesFile The file to load
* @return The loaded properties
*/
public static Properties getProperties(File propertiesFile) {
Properties props = new Properties();
try (FileInputStream input = new FileInputStream(propertiesFile)) {
props.load(input);
} catch (IOException e) {
Logger.error("problem while loading properties");
Logger.error(e);
quitOnError();
}
return props;
}
/**
* Options for the execution of DiffDetective
*
* @param properties The properties loaded by main()
* @return The options instance
*/
public static AnalysisRunner.Options diffdetectiveOptions(Properties properties) {
final String[] allowedFileExtensions;
String propertyValue = properties.getProperty(FILE_EXTENSIONS);
if (propertyValue == null) {
final String[] defaultExtensions = {"h", "hpp", "c", "cpp"};
allowedFileExtensions = defaultExtensions;
} else {
allowedFileExtensions = propertyValue.split("\\w*,\\w*");
}
AnnotationParser parser;
switch (properties.getProperty(PARSER)) {
case "jpp" -> parser = PreprocessorAnnotationParser.JPPAnnotationParser;
case "cpp" -> parser = PreprocessorAnnotationParser.CPPAnnotationParser;
default -> throw new IllegalArgumentException("The parser " + properties.getProperty(PARSER) +
" is not supported. Choose between 'jpp' and 'cpp'");
}
return new AnalysisRunner.Options(Path.of(properties.getProperty(REPO_SAVE_DIR)),
Path.of(properties.getProperty(DD_OUTPUT_DIR)),
Path.of(properties.getProperty(DATASET_FILE)), repo -> {
final PatchDiffParseOptions repoDefault = repo.getParseOptions();
return new PatchDiffParseOptions(
PatchDiffParseOptions.DiffStoragePolicy.DO_NOT_REMEMBER,
new VariationDiffParseOptions(
repoDefault.variationDiffParseOptions().annotationParser(),
false, false)).withAnnotationParser(parser);
}, repo -> new DiffFilter.Builder().allowMerge(true)
.allowedFileExtensions(allowedFileExtensions).build(),
true, false);
}
/**
* Throws an error if the host OS is Windows.
*/
public static void checkOS() {
boolean isWindows = System.getProperty("os.name").toLowerCase().startsWith("windows");
if (isWindows) {
Logger.error(
"Running the analysis under Windows is not supported as the Linux/BusyBox sources are not"
+ "checked out correctly.");
quitOnError();
}
}
/**
* Logs an error message and quits the extraction with an exception.
*/
public static void quitOnError() {
Logger.error("An error occurred and the program has to quit.");
throw new IllegalStateException("Not able to continue analysis due to previous error");
}
/**
* Parses the file in which the properties are located from the arguments.
*
* @param args the arguments to parse
* @return the properties file
*/
public static File getPropertiesFile(String[] args) {
File propertiesFile = null;
if (args.length > 0) {
propertiesFile = new File(args[0]);
}
if (propertiesFile == null) {
Logger.error("You must specify a .properties file as first argument");
quitOnError();
}
return propertiesFile;
}
/**
* Prints the given ground truth to console.
*
* @param groundTruth GT to print
* @param commitName The id of the commit for which the GT has been calculated
*/
public static void print(GroundTruth groundTruth, String commitName) {
System.out.println();
System.out.printf("***************** %s ******************", commitName);
System.out.println();
for (String file : groundTruth.fileGTs().keySet()) {
System.out.println(groundTruth.get(file));
}
}
/**
* Starts the extraction.
*
* @param options The options for DiffDetective
* @throws IOException If an IO error occurs in DiffDetective
*/
public void run(AnalysisRunner.Options options) throws IOException {
AnalysisRunner.run(options, extractionRunner());
}
protected int numProcessors() {
final int availableProcessors;
String numThreads = this.properties.getProperty(NUM_THREADS);
if (numThreads == null || numThreads.trim().isEmpty() || numThreads.trim().equals("0")) {
availableProcessors = Runtime.getRuntime().availableProcessors();
} else {
availableProcessors = Integer.parseInt(numThreads);
}
return availableProcessors;
}
protected int diffDetectiveBatchSize() {
final int batchSize;
String configuredSize = this.properties.getProperty(BATCH_SIZE);
if (configuredSize == null || configuredSize.trim().isEmpty()
|| configuredSize.trim().equals("0")) {
batchSize = 256;
} else {
batchSize = Integer.parseInt(configuredSize);
}
return batchSize;
}
/**
* Return a runner for the ground truth extraction. The runner receives pairs of repositories
* and paths to result output directories and then starts a DiffDetective analysis. See
* {@link FastGroundTruthExtraction} and {@link FullGroundTruthExtraction} for examples.
*/
protected abstract BiConsumer<Repository, Path> extractionRunner();
}