package org.lrg.outcode.builder; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IField; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.IPackageFragment; import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.ASTParser; import org.eclipse.jdt.internal.core.JavaModelManager; import org.eclipse.jgit.diff.DiffEntry; import org.eclipse.jgit.diff.DiffEntry.ChangeType; import org.eclipse.jgit.diff.DiffFormatter; import org.eclipse.jgit.diff.RawTextComparator; import org.eclipse.jgit.lib.Repository; import org.lrg.outcode.activator.GraphDB; import org.lrg.outcode.builder.ast.OutCodeVisitor; import org.lrg.outcode.builder.db.GraphDatasource; import org.lrg.outcode.builder.detection.IDetectionStrategy; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Transaction; import org.osgi.framework.BundleException; public class ModelVistor { private String commit; private String commitID; public static int parsedLOC = 0; private Repository repository; public ModelVistor(int i, String commitID, Repository repository) { this.repository = repository; this.commit = new Integer(i).toString() + "000"; this.commitID = commitID; } private GraphDatasource db = GraphDatasource.INSTANCE; private ASTParser theParser = ASTParser.newParser(AST.JLS8); private HashMap<Integer, HashSet<IDetectionStrategy>> listeners = new HashMap<Integer, HashSet<IDetectionStrategy>>(); private OutCodeVisitor outCodeVisitor; private String onlyTrackThisFilePath; public void visitIJavaProject(IProject project, String onlyTrackThisFilePath) throws CoreException, JavaModelException { this.onlyTrackThisFilePath = onlyTrackThisFilePath; System.out.println("Working in project " + project.getName()); try { JavaCore.getPlugin().getBundle().stop(); JavaCore.getPlugin().getBundle().start(); } catch (BundleException e) { e.printStackTrace(); } // check if we have a Java project if (project.isNatureEnabled("org.eclipse.jdt.core.javanature")) { IJavaProject javaProject = JavaCore.create(project); visitIPackageFragments(javaProject); } } public void configureListener(Integer elementType, IDetectionStrategy iDetectionStrategy) { if (listeners.get(elementType) == null) listeners.put(elementType, new HashSet<IDetectionStrategy>()); listeners.get(elementType).add(iDetectionStrategy); } private void visitIPackageFragments(IJavaProject javaProject) throws JavaModelException { IPackageFragment[] packages = javaProject.getPackageFragments(); GraphDatabaseService dbService = GraphDB.instance.getDB(); Transaction tx = dbService.beginTx(); try { db.addProjectToDb(javaProject, commit, commitID); tx.success(); } catch (Exception e) { e.printStackTrace(); } finally { tx.close(); } for (IPackageFragment mypackage : packages) { if (mypackage.getKind() == IPackageFragmentRoot.K_SOURCE) { System.out.println("Package " + mypackage.getElementName()); tx = dbService.beginTx(); try { db.addNodeToDB(javaProject, mypackage, commit, commitID, 0, 0); tx.success(); } catch (Exception e) { e.printStackTrace(); } finally { tx.close(); } visitICompilationUnits(mypackage); } } } private void visitICompilationUnits(IPackageFragment mypackage) throws JavaModelException { for (ICompilationUnit unit : mypackage.getCompilationUnits()) { if (onlyTrackThisFilePath != null && onlyTrackThisFilePath.endsWith(unit.getResource().getProjectRelativePath().toPortableString())) { System.out.println(" parsing " + unit.getResource().getProjectRelativePath().toPortableString()); GraphDatabaseService dbService = GraphDB.instance.getDB(); Transaction tx = dbService.beginTx(); try { parseCompilationUnit(unit, 0, 0); db.addNodeToDB(mypackage, unit, commit, commitID, 0, 0); visitITypes(unit, 0, 0); tx.success(); } catch (Exception e) { e.printStackTrace(); } finally { tx.close(); } } else System.out.println(" ignoring " + unit.getResource().getProjectRelativePath().toPortableString()); } } private void parseCompilationUnit(ICompilationUnit unit, int added, int removed) { try { parsedLOC += unit.getSource().split("\\r?\\n").length; } catch (JavaModelException e) { e.printStackTrace(); } outCodeVisitor = new OutCodeVisitor(commit, commitID, added, removed); theParser.setSource(unit); theParser.setResolveBindings(true); ASTNode entireAST = theParser.createAST(new NullProgressMonitor()); entireAST.accept(outCodeVisitor); } private void visitNextVersionITypes(Node modifiedCompilationUnitNode, ICompilationUnit unit, String version, int addedLOC, int removedLOC) throws JavaModelException { IType[] allTypes = unit.getAllTypes(); for (IType type : allTypes) { Node latestVersionOfIType = db.addNextVersionNodeToDB(unit, type, version, commitID, addedLOC, removedLOC); addContanmentRel(modifiedCompilationUnitNode, latestVersionOfIType); visitIMethods(latestVersionOfIType, type, version, addedLOC, removedLOC); visitIFields(latestVersionOfIType, type, version, addedLOC, removedLOC); } } private void visitIMethods(Node latestVersionOfIType, IType type, String version, int addedLOC, int removedLOC) throws JavaModelException { IMethod[] methods = type.getMethods(); for (IMethod method : methods) { Node latestVersionOfIMethod = db.addNextVersionNodeToDB(type, method, version, commitID, addedLOC, removedLOC); addContanmentRel(latestVersionOfIType, latestVersionOfIMethod); } } private void addContanmentRel(Node parent, Node child) { if (child != null && parent != null) { parent.createRelationshipTo(child, RelTypes.CONTAINS); child.createRelationshipTo(parent, RelTypes.PARENT); } } private void visitIFields(Node latestVersionOfIType, IType type, String version, int addedLOC, int removedLOC) throws JavaModelException { IField[] methods = type.getFields(); for (IField field : methods) { Node latestVersionOfIField = db.addNextVersionNodeToDB(type, field, version, commitID, addedLOC, removedLOC); addContanmentRel(latestVersionOfIType, latestVersionOfIField); } } private void visitITypes(ICompilationUnit unit, int addedLOC, int removedLOC) throws JavaModelException { IType[] allTypes = unit.getAllTypes(); for (IType type : allTypes) { db.addNodeToDB(unit, type, commit, commitID, addedLOC, removedLOC); visitIMethods(type, addedLOC, removedLOC); visitIFields(type, addedLOC, removedLOC); } } private void visitIMethods(IType type, int addedLOC, int removedLOC) throws JavaModelException { IMethod[] methods = type.getMethods(); for (IMethod method : methods) { db.addNodeToDB(type, method, commit, commitID, addedLOC, removedLOC); } } private void visitIFields(IType type, int addedLOC, int removedLOC) throws JavaModelException { IField[] fileds = type.getFields(); for (IField field : fileds) { db.addNodeToDB(type, field, commit, commitID, addedLOC, removedLOC); } } @SuppressWarnings("restriction") public void visitIJavaProject(IProject iProject, String repoPath, List<DiffEntry> diffs, String version, String commitID, String onlyTrackThisFile) { GraphDatabaseService dbService = GraphDB.instance.getDB(); Transaction tx = dbService.beginTx(); try { JavaModelManager.getJavaModelManager().shutdown(); JavaModelManager.getJavaModelManager().startup(); Set<IPackageFragment> updateContainedElements = new HashSet<IPackageFragment>(); Set<IJavaElement> skipAlreadyAdded = new HashSet<IJavaElement>(); ByteArrayOutputStream out = new ByteArrayOutputStream(); for (DiffEntry diffEntry : diffs) { if (!onlyTrackThisFile.endsWith(diffEntry.getNewPath())) continue; String absolutePath = repoPath + diffEntry.getNewPath(); String pathFromProject = absolutePath.replaceAll("\\\\", "/").replaceFirst(iProject.getLocation().toPortableString(), ""); IFile file = iProject.getFile(new Path(pathFromProject)); file.refreshLocal(IResource.DEPTH_INFINITE, null); if (file.exists()) { IJavaProject javaProject = JavaCore.create(iProject); try { ICompilationUnit iCompilationUnit = findICompilationUnit(file, javaProject); if (iCompilationUnit != null) { DiffFormatter df = new DiffFormatter(out); df.setRepository(repository); df.setDiffComparator(RawTextComparator.WS_IGNORE_ALL); df.setDetectRenames(true); int addedLOC = 0; int removedLOC = 0; try { // Format a patch script for one file entry. df.setContext(0); df.format(diffEntry); String formattedDiff = out.toString(); addedLOC = countLinesThatStartWith("+", formattedDiff); removedLOC = countLinesThatStartWith("-", formattedDiff); out.reset(); } catch (IOException e) { e.printStackTrace(); } finally { df.close(); } if (diffEntry.getChangeType() == ChangeType.MODIFY) { System.out.println("---modify---- icompilationunint " + iCompilationUnit.getElementName()); Node latestVersionOfICompilationUnit = db.addModifiedVersion(iCompilationUnit, version); parseCompilationUnit(iCompilationUnit, addedLOC, removedLOC); visitNextVersionITypes(latestVersionOfICompilationUnit, iCompilationUnit, version, addedLOC, removedLOC); } else if (diffEntry.getChangeType() == ChangeType.ADD || diffEntry.getChangeType() == ChangeType.COPY) { /* * A new file is added with this commit. Make a new version of the package because now it's different. */ System.out.println("---add---- icompilationunint " + iCompilationUnit.getElementName()); IJavaElement iPackageFragment = iCompilationUnit.getParent(); db.addNextVersionNodeToDB(javaProject, iPackageFragment, version, commitID, addedLOC, removedLOC); parseCompilationUnit(iCompilationUnit, addedLOC, removedLOC); db.addNodeToDB(iPackageFragment, iCompilationUnit, version, commitID, addedLOC, removedLOC); visitITypes(iCompilationUnit, addedLOC, removedLOC); skipAlreadyAdded.add(iCompilationUnit); updateContainedElements.add((IPackageFragment) iPackageFragment); } else if (diffEntry.getChangeType() == ChangeType.DELETE) { IJavaElement iPackageFragment = iCompilationUnit.getParent(); updateContainedElements.add((IPackageFragment) iPackageFragment); } } } catch (JavaModelException e) { e.printStackTrace(); } catch (ClassCastException cce) { cce.printStackTrace(); } } else { System.out.println("file not found " + repoPath + diffEntry.getNewPath()); } } for (IPackageFragment iJavaElement : updateContainedElements) { IJavaElement[] children = iJavaElement.getChildren(); for (IJavaElement child : children) { if (skipAlreadyAdded.contains(child)) continue; Node parentNode = db.findLatestNodeForElementID(iJavaElement); Node childNode = db.findLatestNodeForElementID(child); addContanmentRel(parentNode, childNode); } } tx.success(); } catch (Exception e) { e.printStackTrace(); } finally { tx.close(); } } private int countLinesThatStartWith(String plusMinus, String formattedDiff) { Pattern pattern = Pattern.compile("\\n" + "\\" + plusMinus); Matcher matcher = pattern.matcher(formattedDiff); int count = 0; while (matcher.find()) count++; return count--; } private ICompilationUnit findICompilationUnit(IFile file, IJavaProject javaProject) throws JavaModelException { IPackageFragmentRoot[] allPackageFragmentRoots = javaProject.getAllPackageFragmentRoots(); for (IPackageFragmentRoot iPackageFragmentRoot : allPackageFragmentRoots) { if (file.getName().endsWith(".java")) { ICompilationUnit result = (ICompilationUnit) javaProject.findElement(file.getFullPath().makeRelativeTo(iPackageFragmentRoot.getPath())); if (result != null) return result; } } return null; } }