org.refactoringminer.rm1.GitHistoryRefactoringMinerImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of refactoring-miner Show documentation
Show all versions of refactoring-miner Show documentation
RefactoringMiner is a library/API written in Java that can detect refactorings applied in the history of a Java project.
package org.refactoringminer.rm1;
import gr.uom.java.xmi.UMLModel;
import gr.uom.java.xmi.UMLModelASTReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringWriter;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.kohsuke.github.GHCommit;
import org.kohsuke.github.GHPullRequest;
import org.kohsuke.github.GHPullRequestCommitDetail;
import org.kohsuke.github.GHRepository;
import org.kohsuke.github.GHTree;
import org.kohsuke.github.GHTreeEntry;
import org.kohsuke.github.GitHub;
import org.kohsuke.github.PagedIterable;
import org.refactoringminer.api.Churn;
import org.refactoringminer.api.GitHistoryRefactoringMiner;
import org.refactoringminer.api.GitService;
import org.refactoringminer.api.Refactoring;
import org.refactoringminer.api.RefactoringHandler;
import org.refactoringminer.api.RefactoringMinerTimedOutException;
import org.refactoringminer.api.RefactoringType;
import org.refactoringminer.util.GitServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class GitHistoryRefactoringMinerImpl implements GitHistoryRefactoringMiner {
Logger logger = LoggerFactory.getLogger(GitHistoryRefactoringMinerImpl.class);
private Set refactoringTypesToConsider = null;
private GitHub gitHub;
public GitHistoryRefactoringMinerImpl() {
this.setRefactoringTypesToConsider(RefactoringType.ALL);
}
public void setRefactoringTypesToConsider(RefactoringType ... types) {
this.refactoringTypesToConsider = new HashSet();
for (RefactoringType type : types) {
this.refactoringTypesToConsider.add(type);
}
}
private void detect(GitService gitService, Repository repository, final RefactoringHandler handler, Iterator i) {
int commitsCount = 0;
int errorCommitsCount = 0;
int refactoringsCount = 0;
File metadataFolder = repository.getDirectory();
File projectFolder = metadataFolder.getParentFile();
String projectName = projectFolder.getName();
long time = System.currentTimeMillis();
while (i.hasNext()) {
RevCommit currentCommit = i.next();
try {
List refactoringsAtRevision = detectRefactorings(gitService, repository, handler, projectFolder, currentCommit);
refactoringsCount += refactoringsAtRevision.size();
} catch (Exception e) {
logger.warn(String.format("Ignored revision %s due to error", currentCommit.getId().getName()), e);
handler.handleException(currentCommit.getId().getName(),e);
errorCommitsCount++;
}
commitsCount++;
long time2 = System.currentTimeMillis();
if ((time2 - time) > 20000) {
time = time2;
logger.info(String.format("Processing %s [Commits: %d, Errors: %d, Refactorings: %d]", projectName, commitsCount, errorCommitsCount, refactoringsCount));
}
}
handler.onFinish(refactoringsCount, commitsCount, errorCommitsCount);
logger.info(String.format("Analyzed %s [Commits: %d, Errors: %d, Refactorings: %d]", projectName, commitsCount, errorCommitsCount, refactoringsCount));
}
protected List detectRefactorings(GitService gitService, Repository repository, final RefactoringHandler handler, File projectFolder, RevCommit currentCommit) throws Exception {
List refactoringsAtRevision;
String commitId = currentCommit.getId().getName();
List filePathsBefore = new ArrayList();
List filePathsCurrent = new ArrayList();
Map renamedFilesHint = new HashMap();
gitService.fileTreeDiff(repository, currentCommit, filePathsBefore, filePathsCurrent, renamedFilesHint);
Set repositoryDirectoriesBefore = new LinkedHashSet();
Set repositoryDirectoriesCurrent = new LinkedHashSet();
Map fileContentsBefore = new LinkedHashMap();
Map fileContentsCurrent = new LinkedHashMap();
try (RevWalk walk = new RevWalk(repository)) {
// If no java files changed, there is no refactoring. Also, if there are
// only ADD's or only REMOVE's there is no refactoring
if (!filePathsBefore.isEmpty() && !filePathsCurrent.isEmpty() && currentCommit.getParentCount() > 0) {
RevCommit parentCommit = currentCommit.getParent(0);
populateFileContents(repository, parentCommit, filePathsBefore, fileContentsBefore, repositoryDirectoriesBefore);
UMLModel parentUMLModel = createModel(fileContentsBefore, repositoryDirectoriesBefore);
populateFileContents(repository, currentCommit, filePathsCurrent, fileContentsCurrent, repositoryDirectoriesCurrent);
UMLModel currentUMLModel = createModel(fileContentsCurrent, repositoryDirectoriesCurrent);
refactoringsAtRevision = parentUMLModel.diff(currentUMLModel, renamedFilesHint).getRefactorings();
refactoringsAtRevision = filter(refactoringsAtRevision);
} else {
//logger.info(String.format("Ignored revision %s with no changes in java files", commitId));
refactoringsAtRevision = Collections.emptyList();
}
handler.handle(commitId, refactoringsAtRevision);
walk.dispose();
}
return refactoringsAtRevision;
}
private void populateFileContents(Repository repository, RevCommit commit,
List filePaths, Map fileContents, Set repositoryDirectories) throws Exception {
logger.info("Processing {} {} ...", repository.getDirectory().getParent().toString(), commit.getName());
RevTree parentTree = commit.getTree();
try (TreeWalk treeWalk = new TreeWalk(repository)) {
treeWalk.addTree(parentTree);
treeWalk.setRecursive(true);
while (treeWalk.next()) {
String pathString = treeWalk.getPathString();
if(filePaths.contains(pathString)) {
ObjectId objectId = treeWalk.getObjectId(0);
ObjectLoader loader = repository.open(objectId);
StringWriter writer = new StringWriter();
IOUtils.copy(loader.openStream(), writer);
fileContents.put(pathString, writer.toString());
}
if(pathString.endsWith(".java") && pathString.contains("/")) {
String directory = pathString.substring(0, pathString.lastIndexOf("/"));
repositoryDirectories.add(directory);
//include sub-directories
String subDirectory = new String(directory);
while(subDirectory.contains("/")) {
subDirectory = subDirectory.substring(0, subDirectory.lastIndexOf("/"));
repositoryDirectories.add(subDirectory);
}
}
}
}
}
protected List detectRefactorings(final RefactoringHandler handler, File projectFolder, String cloneURL, String currentCommitId) {
List refactoringsAtRevision = Collections.emptyList();
try {
List filesBefore = new ArrayList();
List filesCurrent = new ArrayList();
Map renamedFilesHint = new HashMap();
String parentCommitId = populateWithGitHubAPI(cloneURL, currentCommitId, filesBefore, filesCurrent, renamedFilesHint);
File currentFolder = new File(projectFolder.getParentFile(), projectFolder.getName() + "-" + currentCommitId);
File parentFolder = new File(projectFolder.getParentFile(), projectFolder.getName() + "-" + parentCommitId);
if (!currentFolder.exists()) {
downloadAndExtractZipFile(projectFolder, cloneURL, currentCommitId);
}
if (!parentFolder.exists()) {
downloadAndExtractZipFile(projectFolder, cloneURL, parentCommitId);
}
if (currentFolder.exists() && parentFolder.exists()) {
UMLModel currentUMLModel = createModel(currentFolder, filesCurrent);
UMLModel parentUMLModel = createModel(parentFolder, filesBefore);
// Diff between currentModel e parentModel
refactoringsAtRevision = parentUMLModel.diff(currentUMLModel, renamedFilesHint).getRefactorings();
refactoringsAtRevision = filter(refactoringsAtRevision);
}
else {
logger.warn(String.format("Folder %s not found", currentFolder.getPath()));
}
} catch (Exception e) {
logger.warn(String.format("Ignored revision %s due to error", currentCommitId), e);
handler.handleException(currentCommitId, e);
}
handler.handle(currentCommitId, refactoringsAtRevision);
return refactoringsAtRevision;
}
private void downloadAndExtractZipFile(File projectFolder, String cloneURL, String commitId)
throws IOException {
String downloadLink = extractDownloadLink(cloneURL, commitId);
File destinationFile = new File(projectFolder.getParentFile(), projectFolder.getName() + "-" + commitId + ".zip");
logger.info(String.format("Downloading archive %s", downloadLink));
FileUtils.copyURLToFile(new URL(downloadLink), destinationFile);
logger.info(String.format("Unzipping archive %s", downloadLink));
java.util.zip.ZipFile zipFile = new ZipFile(destinationFile);
try {
Enumeration extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
File entryDestination = new File(projectFolder.getParentFile(), entry.getName());
if (entry.isDirectory()) {
entryDestination.mkdirs();
} else {
entryDestination.getParentFile().mkdirs();
InputStream in = zipFile.getInputStream(entry);
OutputStream out = new FileOutputStream(entryDestination);
IOUtils.copy(in, out);
IOUtils.closeQuietly(in);
out.close();
}
}
} finally {
zipFile.close();
}
}
private String populateWithGitHubAPI(String cloneURL, String currentCommitId,
List filesBefore, List filesCurrent, Map renamedFilesHint) throws IOException {
logger.info("Processing {} {} ...", cloneURL, currentCommitId);
GitHub gitHub = connectToGitHub();
String repoName = extractRepositoryName(cloneURL);
GHRepository repository = gitHub.getRepository(repoName);
GHCommit commit = repository.getCommit(currentCommitId);
String parentCommitId = commit.getParents().get(0).getSHA1();
List commitFiles = commit.getFiles();
for (GHCommit.File commitFile : commitFiles) {
if (commitFile.getFileName().endsWith(".java")) {
if (commitFile.getStatus().equals("modified")) {
filesBefore.add(commitFile.getFileName());
filesCurrent.add(commitFile.getFileName());
}
else if (commitFile.getStatus().equals("added")) {
filesCurrent.add(commitFile.getFileName());
}
else if (commitFile.getStatus().equals("removed")) {
filesBefore.add(commitFile.getFileName());
}
else if (commitFile.getStatus().equals("renamed")) {
filesBefore.add(commitFile.getPreviousFilename());
filesCurrent.add(commitFile.getFileName());
renamedFilesHint.put(commitFile.getPreviousFilename(), commitFile.getFileName());
}
}
}
return parentCommitId;
}
private GitHub connectToGitHub() {
if(gitHub == null) {
try {
Properties prop = new Properties();
InputStream input = new FileInputStream("github-oauth.properties");
prop.load(input);
String oAuthToken = prop.getProperty("OAuthToken");
if (oAuthToken != null) {
gitHub = GitHub.connectUsingOAuth(oAuthToken);
if(gitHub.isCredentialValid()) {
logger.info("Connected to GitHub with OAuth token");
}
}
else {
gitHub = GitHub.connect();
}
} catch(FileNotFoundException e) {
logger.warn("File github-oauth.properties was not found in RefactoringMiner's execution directory", e);
} catch(IOException ioe) {
ioe.printStackTrace();
}
}
return gitHub;
}
protected List filter(List refactoringsAtRevision) {
if (this.refactoringTypesToConsider == null) {
return refactoringsAtRevision;
}
List filteredList = new ArrayList();
for (Refactoring ref : refactoringsAtRevision) {
if (this.refactoringTypesToConsider.contains(ref.getRefactoringType())) {
filteredList.add(ref);
}
}
return filteredList;
}
@Override
public void detectAll(Repository repository, String branch, final RefactoringHandler handler) throws Exception {
GitService gitService = new GitServiceImpl() {
@Override
public boolean isCommitAnalyzed(String sha1) {
return handler.skipCommit(sha1);
}
};
RevWalk walk = gitService.createAllRevsWalk(repository, branch);
try {
detect(gitService, repository, handler, walk.iterator());
} finally {
walk.dispose();
}
}
@Override
public void fetchAndDetectNew(Repository repository, final RefactoringHandler handler) throws Exception {
GitService gitService = new GitServiceImpl() {
@Override
public boolean isCommitAnalyzed(String sha1) {
return handler.skipCommit(sha1);
}
};
RevWalk walk = gitService.fetchAndCreateNewRevsWalk(repository);
try {
detect(gitService, repository, handler, walk.iterator());
} finally {
walk.dispose();
}
}
protected UMLModel createModel(Map fileContents, Set repositoryDirectories) throws Exception {
return new UMLModelASTReader(fileContents, repositoryDirectories).getUmlModel();
}
protected UMLModel createModel(File projectFolder, List filePaths) throws Exception {
return new UMLModelASTReader(projectFolder, filePaths).getUmlModel();
}
@Override
public void detectAtCommit(Repository repository, String commitId, RefactoringHandler handler) {
String cloneURL = repository.getConfig().getString("remote", "origin", "url");
File metadataFolder = repository.getDirectory();
File projectFolder = metadataFolder.getParentFile();
GitService gitService = new GitServiceImpl();
RevWalk walk = new RevWalk(repository);
try {
RevCommit commit = walk.parseCommit(repository.resolve(commitId));
if (commit.getParentCount() > 0) {
walk.parseCommit(commit.getParent(0));
this.detectRefactorings(gitService, repository, handler, projectFolder, commit);
}
else {
logger.warn(String.format("Ignored revision %s because it has no parent", commitId));
}
} catch (MissingObjectException moe) {
this.detectRefactorings(handler, projectFolder, cloneURL, commitId);
} catch (RefactoringMinerTimedOutException e) {
logger.warn(String.format("Ignored revision %s due to timeout", commitId), e);
} catch (Exception e) {
logger.warn(String.format("Ignored revision %s due to error", commitId), e);
handler.handleException(commitId, e);
} finally {
walk.close();
walk.dispose();
}
}
public void detectAtCommit(Repository repository, String commitId, RefactoringHandler handler, int timeout) {
ExecutorService service = Executors.newSingleThreadExecutor();
Future> f = null;
try {
Runnable r = () -> detectAtCommit(repository, commitId, handler);
f = service.submit(r);
f.get(timeout, TimeUnit.SECONDS);
} catch (TimeoutException e) {
f.cancel(true);
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
service.shutdown();
}
}
@Override
public String getConfigId() {
return "RM1";
}
@Override
public void detectBetweenTags(Repository repository, String startTag, String endTag, RefactoringHandler handler)
throws Exception {
GitService gitService = new GitServiceImpl() {
@Override
public boolean isCommitAnalyzed(String sha1) {
return handler.skipCommit(sha1);
}
};
Iterable walk = gitService.createRevsWalkBetweenTags(repository, startTag, endTag);
detect(gitService, repository, handler, walk.iterator());
}
@Override
public void detectBetweenCommits(Repository repository, String startCommitId, String endCommitId,
RefactoringHandler handler) throws Exception {
GitService gitService = new GitServiceImpl() {
@Override
public boolean isCommitAnalyzed(String sha1) {
return handler.skipCommit(sha1);
}
};
Iterable walk = gitService.createRevsWalkBetweenCommits(repository, startCommitId, endCommitId);
detect(gitService, repository, handler, walk.iterator());
}
@Override
public Churn churnAtCommit(Repository repository, String commitId, RefactoringHandler handler) {
GitService gitService = new GitServiceImpl();
RevWalk walk = new RevWalk(repository);
try {
RevCommit commit = walk.parseCommit(repository.resolve(commitId));
if (commit.getParentCount() > 0) {
walk.parseCommit(commit.getParent(0));
return gitService.churn(repository, commit);
}
else {
logger.warn(String.format("Ignored revision %s because it has no parent", commitId));
}
} catch (MissingObjectException moe) {
logger.warn(String.format("Ignored revision %s due to missing commit", commitId), moe);
} catch (Exception e) {
logger.warn(String.format("Ignored revision %s due to error", commitId), e);
handler.handleException(commitId, e);
} finally {
walk.close();
walk.dispose();
}
return null;
}
@Override
public void detectAtCommit(String gitURL, String commitId, RefactoringHandler handler, int timeout) {
ExecutorService service = Executors.newSingleThreadExecutor();
Future> f = null;
try {
Runnable r = () -> detectRefactorings(handler, gitURL, commitId);
f = service.submit(r);
f.get(timeout, TimeUnit.SECONDS);
} catch (TimeoutException e) {
f.cancel(true);
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
service.shutdown();
}
}
protected List detectRefactorings(final RefactoringHandler handler, String gitURL, String currentCommitId) {
List refactoringsAtRevision = Collections.emptyList();
try {
Set repositoryDirectoriesBefore = ConcurrentHashMap.newKeySet();
Set repositoryDirectoriesCurrent = ConcurrentHashMap.newKeySet();
Map fileContentsBefore = new ConcurrentHashMap();
Map fileContentsCurrent = new ConcurrentHashMap();
Map renamedFilesHint = new ConcurrentHashMap();
populateWithGitHubAPI(gitURL, currentCommitId, fileContentsBefore, fileContentsCurrent, renamedFilesHint, repositoryDirectoriesBefore, repositoryDirectoriesCurrent);
UMLModel currentUMLModel = createModel(fileContentsCurrent, repositoryDirectoriesCurrent);
UMLModel parentUMLModel = createModel(fileContentsBefore, repositoryDirectoriesBefore);
// Diff between currentModel e parentModel
refactoringsAtRevision = parentUMLModel.diff(currentUMLModel, renamedFilesHint).getRefactorings();
refactoringsAtRevision = filter(refactoringsAtRevision);
}
catch(RefactoringMinerTimedOutException e) {
logger.warn(String.format("Ignored revision %s due to timeout", currentCommitId), e);
handler.handleException(currentCommitId, e);
}
catch (Exception e) {
logger.warn(String.format("Ignored revision %s due to error", currentCommitId), e);
handler.handleException(currentCommitId, e);
}
handler.handle(currentCommitId, refactoringsAtRevision);
return refactoringsAtRevision;
}
private void populateWithGitHubAPI(String cloneURL, String currentCommitId,
Map filesBefore, Map filesCurrent, Map renamedFilesHint,
Set repositoryDirectoriesBefore, Set repositoryDirectoriesCurrent) throws IOException, InterruptedException {
logger.info("Processing {} {} ...", cloneURL, currentCommitId);
GitHub gitHub = connectToGitHub();
String repoName = extractRepositoryName(cloneURL);
GHRepository repository = gitHub.getRepository(repoName);
GHCommit currentCommit = repository.getCommit(currentCommitId);
final String parentCommitId = currentCommit.getParents().get(0).getSHA1();
Set deletedAndRenamedFileParentDirectories = ConcurrentHashMap.newKeySet();
List commitFiles = currentCommit.getFiles();
ExecutorService pool = Executors.newFixedThreadPool(commitFiles.size());
for (GHCommit.File commitFile : commitFiles) {
String fileName = commitFile.getFileName();
if (commitFile.getFileName().endsWith(".java")) {
if (commitFile.getStatus().equals("modified")) {
Runnable r = () -> {
try {
URL currentRawURL = commitFile.getRawUrl();
InputStream currentRawFileInputStream = currentRawURL.openStream();
String currentRawFile = IOUtils.toString(currentRawFileInputStream);
String rawURLInParentCommit = currentRawURL.toString().replace(currentCommitId, parentCommitId);
InputStream parentRawFileInputStream = new URL(rawURLInParentCommit).openStream();
String parentRawFile = IOUtils.toString(parentRawFileInputStream);
filesBefore.put(fileName, parentRawFile);
filesCurrent.put(fileName, currentRawFile);
}
catch(IOException e) {
e.printStackTrace();
}
};
pool.submit(r);
}
else if (commitFile.getStatus().equals("added")) {
Runnable r = () -> {
try {
URL currentRawURL = commitFile.getRawUrl();
InputStream currentRawFileInputStream = currentRawURL.openStream();
String currentRawFile = IOUtils.toString(currentRawFileInputStream);
filesCurrent.put(fileName, currentRawFile);
}
catch(IOException e) {
e.printStackTrace();
}
};
pool.submit(r);
}
else if (commitFile.getStatus().equals("removed")) {
Runnable r = () -> {
try {
URL rawURL = commitFile.getRawUrl();
InputStream rawFileInputStream = rawURL.openStream();
String rawFile = IOUtils.toString(rawFileInputStream);
filesBefore.put(fileName, rawFile);
if(fileName.contains("/")) {
deletedAndRenamedFileParentDirectories.add(fileName.substring(0, fileName.lastIndexOf("/")));
}
}
catch(IOException e) {
e.printStackTrace();
}
};
pool.submit(r);
}
else if (commitFile.getStatus().equals("renamed")) {
Runnable r = () -> {
try {
String previousFilename = commitFile.getPreviousFilename();
URL currentRawURL = commitFile.getRawUrl();
InputStream currentRawFileInputStream = currentRawURL.openStream();
String currentRawFile = IOUtils.toString(currentRawFileInputStream);
String rawURLInParentCommit = currentRawURL.toString().replace(currentCommitId, parentCommitId).replace(fileName, previousFilename);
InputStream parentRawFileInputStream = new URL(rawURLInParentCommit).openStream();
String parentRawFile = IOUtils.toString(parentRawFileInputStream);
filesBefore.put(previousFilename, parentRawFile);
filesCurrent.put(fileName, currentRawFile);
renamedFilesHint.put(previousFilename, fileName);
if(previousFilename.contains("/")) {
deletedAndRenamedFileParentDirectories.add(previousFilename.substring(0, previousFilename.lastIndexOf("/")));
}
}
catch(IOException e) {
e.printStackTrace();
}
};
pool.submit(r);
}
}
}
pool.shutdown();
pool.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
repositoryDirectories(currentCommit.getTree(), "", repositoryDirectoriesCurrent, deletedAndRenamedFileParentDirectories);
repositoryDirectoriesCurrent.addAll(deletedAndRenamedFileParentDirectories);
//allRepositoryDirectories(currentCommit.getTree(), "", repositoryDirectoriesCurrent);
//GHCommit parentCommit = repository.getCommit(parentCommitId);
//allRepositoryDirectories(parentCommit.getTree(), "", repositoryDirectoriesBefore);
}
private void repositoryDirectories(GHTree tree, String pathFromRoot, Set repositoryDirectories, Set targetPaths) throws IOException {
for(GHTreeEntry entry : tree.getTree()) {
String path = null;
if(pathFromRoot.equals("")) {
path = entry.getPath();
}
else {
path = pathFromRoot + "/" + entry.getPath();
}
if(atLeastOneStartsWith(targetPaths, path)) {
if(targetPaths.contains(path)) {
repositoryDirectories.add(path);
}
else {
repositoryDirectories.add(path);
GHTree asTree = entry.asTree();
if(asTree != null) {
repositoryDirectories(asTree, path, repositoryDirectories, targetPaths);
}
}
}
}
}
private boolean atLeastOneStartsWith(Set targetPaths, String path) {
for(String targetPath : targetPaths) {
if(path.endsWith("/") && targetPath.startsWith(path)) {
return true;
}
else if(!path.endsWith("/") && targetPath.startsWith(path + "/")) {
return true;
}
}
return false;
}
/*
private void allRepositoryDirectories(GHTree tree, String pathFromRoot, Set repositoryDirectories) throws IOException {
for(GHTreeEntry entry : tree.getTree()) {
String path = null;
if(pathFromRoot.equals("")) {
path = entry.getPath();
}
else {
path = pathFromRoot + "/" + entry.getPath();
}
GHTree asTree = entry.asTree();
if(asTree != null) {
allRepositoryDirectories(asTree, path, repositoryDirectories);
}
else if(path.endsWith(".java")) {
repositoryDirectories.add(path.substring(0, path.lastIndexOf("/")));
}
}
}
*/
@Override
public void detectAtPullRequest(String cloneURL, int pullRequestId, RefactoringHandler handler, int timeout) throws IOException {
GitHub gitHub = connectToGitHub();
String repoName = extractRepositoryName(cloneURL);
GHRepository repository = gitHub.getRepository(repoName);
GHPullRequest pullRequest = repository.getPullRequest(pullRequestId);
PagedIterable commits = pullRequest.listCommits();
for(GHPullRequestCommitDetail commit : commits) {
detectAtCommit(cloneURL, commit.getSha(), handler, timeout);
}
}
private static final String GITHUB_URL = "https://github.com/";
private static final String BITBUCKET_URL = "https://bitbucket.org/";
private static String extractRepositoryName(String cloneURL) {
int hostLength = 0;
if(cloneURL.startsWith(GITHUB_URL)) {
hostLength = GITHUB_URL.length();
}
else if(cloneURL.startsWith(BITBUCKET_URL)) {
hostLength = BITBUCKET_URL.length();
}
int indexOfDotGit = cloneURL.length();
if(cloneURL.endsWith(".git")) {
indexOfDotGit = cloneURL.indexOf(".git");
}
else if(cloneURL.endsWith("/")) {
indexOfDotGit = cloneURL.length() - 1;
}
String repoName = cloneURL.substring(hostLength, indexOfDotGit);
return repoName;
}
public static String extractCommitURL(String cloneURL, String commitId) {
int indexOfDotGit = cloneURL.length();
if(cloneURL.endsWith(".git")) {
indexOfDotGit = cloneURL.indexOf(".git");
}
else if(cloneURL.endsWith("/")) {
indexOfDotGit = cloneURL.length() - 1;
}
String commitResource = "/";
if(cloneURL.startsWith(GITHUB_URL)) {
commitResource = "/commit/";
}
else if(cloneURL.startsWith(BITBUCKET_URL)) {
commitResource = "/commits/";
}
String commitURL = cloneURL.substring(0, indexOfDotGit) + commitResource + commitId;
return commitURL;
}
private static String extractDownloadLink(String cloneURL, String commitId) {
int indexOfDotGit = cloneURL.length();
if(cloneURL.endsWith(".git")) {
indexOfDotGit = cloneURL.indexOf(".git");
}
else if(cloneURL.endsWith("/")) {
indexOfDotGit = cloneURL.length() - 1;
}
String downloadResource = "/";
if(cloneURL.startsWith(GITHUB_URL)) {
downloadResource = "/archive/";
}
else if(cloneURL.startsWith(BITBUCKET_URL)) {
downloadResource = "/get/";
}
String downloadLink = cloneURL.substring(0, indexOfDotGit) + downloadResource + commitId + ".zip";
return downloadLink;
}
}