package org.outcode.git; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Date; import java.util.Iterator; import java.util.List; import org.eclipse.core.commands.AbstractHandler; import org.eclipse.core.commands.ExecutionEvent; import org.eclipse.core.commands.ExecutionException; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubMonitor; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jgit.api.CheckoutCommand; import org.eclipse.jgit.api.CheckoutResult; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.api.errors.NoHeadException; import org.eclipse.jgit.diff.DiffEntry; import org.eclipse.jgit.diff.DiffFormatter; import org.eclipse.jgit.diff.RawTextComparator; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.RepositoryBuilder; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevTree; import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.util.io.NullOutputStream; import org.lrg.outcode.CountdownTimer; import org.lrg.outcode.IHindsight; import org.lrg.outcode.activator.GraphDB; import org.lrg.outcode.builder.ModelVistor; import org.lrg.outcode.visitors.FeatureEnvy; import org.lrg.outcode.visitors.GodClass; import org.lrg.outcode.visitors.IntensiveCoupling; import org.lrg.outcode.visitors.RefusedParentBequest; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Label; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.ResourceIterator; import org.neo4j.graphdb.Transaction; public class Giterator extends AbstractHandler { private DiffFormatter df = new DiffFormatter(NullOutputStream.INSTANCE);; public static final class NullProgressMonitorExtension extends NullProgressMonitor { private boolean isDone = false; @Override public void done() { isDone = true; super.done(); } public boolean isDone() { return isDone; } } @Override public Object execute(ExecutionEvent event) throws ExecutionException { Job j = new Job("j") { @Override protected IStatus run(IProgressMonitor monitor) { walkRepo(monitor); return Status.OK_STATUS; } }; j.schedule(); return Status.OK_STATUS; } private void walkRepo(IProgressMonitor moni) { File gitDir = findRepoDir(); if (gitDir == null) return; RepositoryBuilder builder = new RepositoryBuilder(); Repository repository; try { ModelVistor.parsedLOC = 0; CountdownTimer.start("iterating"); repository = builder.setGitDir(gitDir).readEnvironment().findGitDir().build(); Git git = new Git(repository); RevWalk walk = new RevWalk(repository); RevCommit commit = null; Iterable<RevCommit> logs = git.log().call(); Iterator<RevCommit> i = logs.iterator(); ArrayList<RevCommit> revCommits = new ArrayList<RevCommit>(); while (i.hasNext()) { RevCommit next = i.next(); revCommits.add(0, next); } int k = 0; int noK = 0; boolean isEclipseProject = false; SubMonitor monitor = SubMonitor.convert(moni, revCommits.size()); for (RevCommit revCommit : revCommits) { CountdownTimer.start("parseCommit"); commit = walk.parseCommit(revCommit); CountdownTimer.stop("parseCommit"); System.out.println("parsing commit with message " + revCommit.getFullMessage() + new Date(revCommit.getCommitTime()).toString()); if (!isEclipseProject && addedDotProjectFile(repository, walk, commit)) isEclipseProject = true; if (isEclipseProject) { CheckoutCommand checkout = git.checkout(); try { CountdownTimer.start("call"); checkout.setName(commit.getName()).call(); CountdownTimer.stop("call"); CheckoutResult result = checkout.getResult(); if (result.getStatus() == CheckoutResult.Status.OK) { refreshWs(); if (k == 0) { RevTree tree = commit.getTree(); System.out.println("modified " + tree); System.out.println("commit name " + commit.getName() + " " + commit.getId().name()); new ModelVistor(commit.getCommitTime(), commit.getName(), repository).visitIJavaProject(ResourcesPlugin.getWorkspace().getRoot().getProjects()[0], null); runMethodDetectionStrategies(commit.getCommitTime() + "000", true); runClassDetectionStrategies(commit.getCommitTime() + "000"); } else { if (commit.getParentCount() > 0) { RevCommit parent = walk.parseCommit(commit.getParent(0).getId()); ByteArrayOutputStream out = new ByteArrayOutputStream(); df = new DiffFormatter(out); df.setRepository(repository); df.setDiffComparator(RawTextComparator.WS_IGNORE_ALL); df.setDetectRenames(true); List<DiffEntry> diffs = df.scan(parent.getTree(), commit.getTree()); String version = commit.getCommitTime() + "000"; System.out.println("------- version " + new Date(Long.parseLong(version)).toString() + " -------"); new ModelVistor(commit.getCommitTime(), commit.getName(), repository).visitIJavaProject(ResourcesPlugin.getWorkspace().getRoot().getProjects()[0], gitDir.getAbsolutePath().replace("/.git", "/"), diffs, version, commit.getName(), null); runMethodDetectionStrategies(version, false); runClassDetectionStrategies(version); } } k++; } else { System.err.println("checkout failed " + result.getStatus()); } } catch (Exception ex) { CountdownTimer.stop("call"); ex.printStackTrace(); } } else noK++; if (monitor.isCanceled()) break; monitor.worked(1); } git.close(); monitor.done(); System.out.println("ka = " + k); System.out.println("noK = " + noK); CountdownTimer.stop("iterating"); CountdownTimer.printAndReset("call"); CountdownTimer.printAndReset("parseCommit"); CountdownTimer.printAndReset("iterating"); System.out.println("ka = " + k); System.out.println("noK = " + noK); System.out.println("total loc parsed " + ModelVistor.parsedLOC); } catch (IOException e) { e.printStackTrace(); } catch (NoHeadException e) { e.printStackTrace(); } catch (GitAPIException e) { e.printStackTrace(); } } private File findRepoDir() { IProject iProject = ResourcesPlugin.getWorkspace().getRoot().getProjects()[0]; IPath location = iProject.getLocation(); if (location.append("/.git").toFile().exists()) return location.append(".git").toFile(); while (location.segmentCount() > 0 && !location.append("/.git").toFile().exists()) location = location.removeLastSegments(1); if (location.append("/.git").toFile().exists()) { IPath finalLocation = location.append("/.git"); System.out.println("repo found " + finalLocation.toPortableString()); return finalLocation.toFile(); } return null; } private boolean addedDotProjectFile(Repository repository, RevWalk walk, RevCommit commit) { try { ObjectId parentTree = repository.resolve("HEAD@{0}"); if (commit.getParentCount() > 0) { RevCommit parent = walk.parseCommit(commit.getParent(0).getId()); parentTree = parent.getTree(); } df.setRepository(repository); df.setDiffComparator(RawTextComparator.WS_IGNORE_ALL); df.setDetectRenames(true); List<DiffEntry> diffs = df.scan(null, commit.getTree()); for (DiffEntry diff : diffs) { String replaceFirst = diff.getNewPath();// .substring(0, // diff.getNewPath().length() // ); // removes replaceFirst = replaceFirst.substring(replaceFirst.indexOf("/") + 1, replaceFirst.length()); if (replaceFirst.endsWith(".project")) return true; } } catch (IOException e) { e.printStackTrace(); } return false; } private void refreshWs() throws CoreException { // System.out.println("starting refresh"); NullProgressMonitorExtension nullProgressMonitorExtension = new NullProgressMonitorExtension(); ResourcesPlugin.getWorkspace().getRoot().refreshLocal(IResource.DEPTH_INFINITE, nullProgressMonitorExtension); while (!nullProgressMonitorExtension.isDone()) { System.out.println("sleeping a sec"); try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } // System.out.println("refresh done"); } public static void runMethodDetectionStrategies(String commit, boolean b) { GraphDatabaseService dbService = GraphDB.instance.getDB(); Transaction tx = dbService.beginTx(); try { FeatureEnvy featureEnvy = new FeatureEnvy(b); IntensiveCoupling intensiveCoupling = new IntensiveCoupling(); ResourceIterator<Node> allArticles = dbService.findNodes(Label.label("Method"), IHindsight.COMMIT, commit); while (allArticles.hasNext()) { Node node = allArticles.next(); featureEnvy.findFeatureEnvy(node); intensiveCoupling.findIntensiveCoupling(node); } allArticles.close(); tx.success(); } catch (Exception e) { e.printStackTrace(); } finally { tx.close(); } } public static void runClassDetectionStrategies(String commit) { GraphDatabaseService dbService = GraphDB.instance.getDB(); Transaction tx = dbService.beginTx(); try { GodClass godClass = new GodClass(); RefusedParentBequest refusedParentBequest = new RefusedParentBequest(); ResourceIterator<Node> allArticles = dbService.findNodes(Label.label("Type"), IHindsight.COMMIT, commit); while (allArticles.hasNext()) { Node node = allArticles.next(); godClass.findGodClasses(node); refusedParentBequest.findRefusedParentBequest(node); } allArticles.close(); tx.success(); } catch (Exception e) { e.printStackTrace(); } finally { tx.close(); } } }