forked from reposense/RepoSense
-
Notifications
You must be signed in to change notification settings - Fork 0
/
FileInfoAnalyzer.java
133 lines (115 loc) · 5.44 KB
/
FileInfoAnalyzer.java
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
package reposense.authorship;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import reposense.authorship.analyzer.AnnotatorAnalyzer;
import reposense.authorship.analyzer.CheckStyleParser;
import reposense.authorship.model.FileInfo;
import reposense.authorship.model.FileResult;
import reposense.authorship.model.LineInfo;
import reposense.git.GitBlame;
import reposense.model.Author;
import reposense.model.CommitHash;
import reposense.model.RepoConfiguration;
import reposense.system.LogsManager;
/**
* Analyzes the target and information given in the {@code FileInfo}.
*/
public class FileInfoAnalyzer {
private static final Logger logger = LogsManager.getLogger(FileInfoAnalyzer.class);
private static final String REUSED_TAG = "//@reused";
private static final int AUTHOR_NAME_OFFSET = "author ".length();
private static final int AUTHOR_EMAIL_OFFSET = "author-mail ".length();
private static final int FULL_COMMIT_HASH_LENGTH = 40;
/**
* Analyzes the lines of the file, given in the {@code fileInfo}, that has changed in the time period provided
* by {@code config}.
* Returns null if the file contains the reused tag, or none of the {@code Author} specified in
* {@code config} contributed to the file in {@code fileInfo}.
*/
public static FileResult analyzeFile(RepoConfiguration config, FileInfo fileInfo) {
String relativePath = fileInfo.getPath();
if (isReused(config.getRepoRoot(), relativePath)) {
return null;
}
aggregateBlameAuthorInfo(config, fileInfo);
if (config.isNeedCheckStyle()) {
CheckStyleParser.aggregateStyleIssue(fileInfo, config.getRepoRoot());
}
if (config.isAnnotationOverwrite()) {
AnnotatorAnalyzer.aggregateAnnotationAuthorInfo(fileInfo, config.getAuthorEmailsAndAliasesMap());
}
if (!config.getAuthorList().isEmpty() && fileInfo.isAllAuthorsIgnored(config.getAuthorList())) {
return null;
}
return generateFileResult(fileInfo);
}
/**
* Generates and returns a {@code FileResult} with the authorship results from {@code fileInfo} consolidated.
*/
private static FileResult generateFileResult(FileInfo fileInfo) {
HashMap<Author, Integer> authorContributionMap = new HashMap<>();
for (LineInfo line : fileInfo.getLines()) {
Author author = line.getAuthor();
authorContributionMap.put(author, authorContributionMap.getOrDefault(author, 0) + 1);
}
return new FileResult(fileInfo.getPath(), fileInfo.getLines(), authorContributionMap);
}
/**
* Sets the {@code Author} for each line in {@code fileInfo} based on the git blame analysis on the file.
*/
private static void aggregateBlameAuthorInfo(RepoConfiguration config, FileInfo fileInfo) {
Map<String, Author> authorEmailsAndAliasesMap = config.getAuthorEmailsAndAliasesMap();
String blameResults = getGitBlameResult(config, fileInfo.getPath());
String[] blameResultLines = blameResults.split("\n");
Path filePath = Paths.get(fileInfo.getPath());
for (int lineCount = 0; lineCount < blameResultLines.length; lineCount += 3) {
String commitHash = blameResultLines[lineCount].substring(0, FULL_COMMIT_HASH_LENGTH);
String authorName = blameResultLines[lineCount + 1].substring(AUTHOR_NAME_OFFSET);
String authorEmail = blameResultLines[lineCount + 2]
.substring(AUTHOR_EMAIL_OFFSET).replaceAll("<|>", "");
Author author = authorEmailsAndAliasesMap.getOrDefault(authorName,
authorEmailsAndAliasesMap.getOrDefault(authorEmail, Author.UNKNOWN_AUTHOR));
if (!fileInfo.isFileLineTracked(lineCount / 3) || isAuthorIgnoringFile(author, filePath)
|| CommitHash.isInsideCommitList(commitHash, config.getIgnoreCommitList())) {
author = Author.UNKNOWN_AUTHOR;
}
fileInfo.setLineAuthor(lineCount / 3, author);
}
}
/**
* Returns the analysis result from running git blame on {@code filePath}.
*/
private static String getGitBlameResult(RepoConfiguration config, String filePath) {
return GitBlame.blame(config.getRepoRoot(), filePath);
}
/**
* Returns true if the first line in the file at {@code repoRoot}'s {@code relativePath} contains the reused tag.
*/
private static boolean isReused(String repoRoot, String relativePath) {
Path path = Paths.get(repoRoot, relativePath);
try (BufferedReader br = new BufferedReader(new FileReader(path.toFile()))) {
String firstLine = br.readLine();
if (firstLine == null || firstLine.contains(REUSED_TAG)) {
return true;
}
} catch (IOException ioe) {
logger.log(Level.WARNING, ioe.getMessage(), ioe);
}
return false;
}
/**
* Returns true if the {@code author} is ignoring the {@code filePath} based on its ignore glob list.
*/
private static boolean isAuthorIgnoringFile(Author author, Path filePath) {
PathMatcher ignoreGlobMatcher = author.getIgnoreGlobMatcher();
return ignoreGlobMatcher.matches(filePath);
}
}