package de.hub.srcrepo; import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.Collection; import java.util.HashSet; import java.util.Stack; import java.util.concurrent.TimeUnit; import org.eclipse.jgit.diff.DiffEntry.ChangeType; import com.google.common.base.Preconditions; import de.hub.jstattrack.Statistics; import de.hub.jstattrack.TimeStatistic; import de.hub.jstattrack.TimeStatistic.Timer; import de.hub.jstattrack.ValueStatistic; import de.hub.jstattrack.services.BatchedPlot; import de.hub.jstattrack.services.Histogram; import de.hub.jstattrack.services.Summary; import de.hub.jstattrack.services.WindowedPlot; import de.hub.srcrepo.repositorymodel.Diff; import de.hub.srcrepo.repositorymodel.ParentRelation; import de.hub.srcrepo.repositorymodel.RepositoryModel; import de.hub.srcrepo.repositorymodel.Rev; public class RepositoryModelTraversal { private class Branch { private final Rev lastCommonRev; private final Rev firstBranchRev; public Branch(Rev lastCommonRev, Rev firstBranchRev) { super(); this.lastCommonRev = lastCommonRev; this.firstBranchRev = firstBranchRev; } } public final static TimeStatistic visitFullETStat = new TimeStatistic(TimeUnit.MICROSECONDS) .with(Summary.class).with(BatchedPlot.class).with(new WindowedPlot(100)).with(Histogram.class) .register(RepositoryModelTraversal.class, "Visit time"); public final static ValueStatistic usedMemoryStat = new ValueStatistic("b") .with(BatchedPlot.class).register(RepositoryModelTraversal.class, "Used heap memory."); private final RepositoryModel repositoryModel; private final IRepositoryModelVisitor visitor; private Stack<Branch> openBranches = new Stack<Branch>(); private Collection<Rev> traversedRevs = new HashSet<Rev>(); public RepositoryModelTraversal(RepositoryModel repositoryModel, IRepositoryModelVisitor visitor) { super(); this.repositoryModel = repositoryModel; this.visitor = visitor; } private Stats stats = new Stats(); public class Stats { public int importedRevsCounter = 0; public int mergeCounter = 0; public int branchCounter = 0; void reset() { importedRevsCounter = 0; mergeCounter = 0; branchCounter = 0; } } public Stats run() { openBranches.clear(); traversedRevs.clear(); int count = 0; stats.reset(); stats.importedRevsCounter = 0; for (Rev rootRev: repositoryModel.getRootRevs()) { openBranches.add(new Branch(null, rootRev)); stats.branchCounter++; } while(!openBranches.isEmpty()) { Branch branch = openBranches.pop(); visitor.onBranch(branch.lastCommonRev, branch.firstBranchRev); Rev rev = branch.firstBranchRev; Rev lastRev = branch.lastCommonRev; branchLoop: while(true) { int children = rev.getChildRelations().size(); int parents = parentCount(rev); // is merge? if (parents > 1) { if (traversedRevs.contains(rev)) { stats.mergeCounter++; visitor.onMerge(rev, lastRev); break branchLoop; } } // import current rev visitRev(rev, lastRev, ++stats.importedRevsCounter); traversedRevs.add(rev); count++; // print performance data if (count % 1000 == 0 && count != 0) { // run gc, after that measure memory for (int i = 0; i < 2; i++) { Object obj = new Object(); WeakReference<?> ref = new WeakReference<Object>(obj); obj = null; while (ref.get() != null) { System.gc(); } System.runFinalization(); } long usedMemory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory(); usedMemoryStat.track(usedMemory); } if (count % 1000 == 0 && count != 0) { try { String json = Statistics.reportToJSON().toString(); PrintWriter out = new PrintWriter("/tmp/srcrepo.json"); out.print(json); out.close(); } catch (Exception e) { SrcRepoActivator.INSTANCE.warning("Could not write json! " + e.getMessage(), e); } } // is branch or branch complete: determine next rev lastRev = rev; if (children == 1) { rev = rev.getChildRelations().get(0).getChild(); } else if (children > 1) { Rev firstChildRev = rev.getChildRelations().get(0).getChild(); for (ParentRelation childRelation : rev.getChildRelations()) { Rev childRev = childRelation.getChild(); if (childRev != firstChildRev) { openBranches.add(new Branch(rev, childRev)); } else { visitor.onBranch(rev, childRev); } stats.branchCounter++; } rev = firstChildRev; } else { // branch completed break branchLoop; } } } return stats; } private int parentCount(Rev rev) { int result = 0; for(ParentRelation parentRelation: rev.getParentRelations()) { if (parentRelation.getParent() != null) { result++; } } return result; } private void visitRev(Rev rev, Rev traverseParentRev, int number) { Preconditions.checkArgument(!traversedRevs.contains(rev)); Timer visitAllTimer = visitFullETStat.timer(); visitor.onStartRev(rev,traverseParentRev, number); for (ParentRelation parentRelation: rev.getParentRelations()) { for (Diff diff : parentRelation.getDiffs()) { if (diff.getType() == ChangeType.ADD) { visitor.onAddedFile(diff); } else if (diff.getType() == ChangeType.COPY) { visitor.onCopiedFile(diff); } else if (diff.getType() == ChangeType.DELETE) { visitor.onDeletedFile(diff); } else if (diff.getType() == ChangeType.MODIFY) { visitor.onModifiedFile(diff); } else if (diff.getType() == ChangeType.RENAME) { visitor.onRenamedFile(diff); } } } visitor.onCompleteRev(rev, traverseParentRev); visitAllTimer.track(); } public static Stats traverse(RepositoryModel repositoryModel, IRepositoryModelVisitor visitor) { return new RepositoryModelTraversal(repositoryModel, visitor).run(); } }