package org.community.intellij.plugins.communitycase.history.wholeTree; import com.intellij.openapi.vcs.Ring; import com.intellij.util.SmartList; import com.intellij.util.containers.BidirectionalMap; import com.intellij.util.containers.Convertor; import com.intellij.util.containers.MultiMap; import com.intellij.util.containers.ReadonlyList; import java.util.*; /** * @author irengrig */ public class SkeletonBuilder { private final WireEventsListener mySkeleton; private final MultiMap<AbstractHash, WaitingItem> myAwaitingParents; private final MultiMap<Integer, WaitingItem> myBackIndex; // next available idx private final Ring.IntegerRing myRing; private final BidirectionalMap<Integer, Integer> mySeizedWires; // !! can intersect with existing, but own lifecycle private final BidirectionalMap<Integer, AbstractHash> myFutureSeizedWires; private final Convertor<Integer,List<Integer>> myFutureConvertor; public SkeletonBuilder(WireEventsListener treeNavigation) { mySkeleton = treeNavigation; myAwaitingParents = new MultiMap<AbstractHash, WaitingItem>(); myBackIndex = new MultiMap<Integer, WaitingItem>(); myRing = new Ring.IntegerRing(); // wire number -> last commit on that wire mySeizedWires = new BidirectionalMap<Integer, Integer>(); myFutureSeizedWires = new BidirectionalMap<Integer, AbstractHash>(); myFutureConvertor = new Convertor<Integer, List<Integer>>() { @Override public List<Integer> convert(Integer o) { return getFutureWireStarts(o); } }; } public void consume(final CommitI commitI, final List<AbstractHash> parents, final ReadonlyList<CommitI> commits, final int rowCount) { int wireNumber = -1; // will become real! // todo: superflous information both in waiting item and in future map myFutureSeizedWires.removeValue(commitI.getHash()); final Collection<WaitingItem> awaiting = myAwaitingParents.remove(commitI.getHash()); if (awaiting != null) { final List<WaitingItem> awaitingList = (List<WaitingItem>) awaiting; if (awaitingList.size() > 1) { Collections.sort(awaitingList, CommitsComparator.getInstance()); } final List<WaitingItem> willReturnTheirWires = new SmartList<WaitingItem>(); for (final WaitingItem waiting : awaitingList) { Collection<WaitingItem> waitingCommits = myBackIndex.get(waiting.myIdx); waitingCommits.remove(waiting); if (waitingCommits.isEmpty()) { myBackIndex.remove(waiting.myIdx); } if (wireNumber == -1) { wireNumber = waiting.getWire(); } else { assert wireNumber == waiting.getWire(); } final CommitI waitingI = commits.get(waiting.myIdx); //waiting.parentFound(); if (waiting.isMerge()) { // put/update start event - now we know index so can create/update wire event mySkeleton.addStartToEvent(waiting.myIdx, rowCount); } final Integer seized = mySeizedWires.get(waitingI.getWireNumber()); AbstractHash something = myFutureSeizedWires.get(waitingI.getWireNumber()); if (seized != null && seized == waiting.myIdx && waitingI.getWireNumber() != wireNumber && something == null) { // return willReturnTheirWires.add(waiting); /*if (wireNumber == -1) { // if this commit still doesn't have wire // there is no other commits on the wire after parent -> use it wireNumber = waitingI.getWireNumber(); } else { // if there are no other children of that commit. wire dies if (waiting.allParentsFound()) { myRing.back(waitingI.getWireNumber()); // end of waiting commits' wire mySkeleton.parentWireEnds(rowCount, waiting.myIdx); } }*/ } } for (WaitingItem waitingItem : willReturnTheirWires) { final CommitI waitingI = commits.get(waitingItem.myIdx); myRing.back(waitingI.getWireNumber()); mySeizedWires.remove(waitingI.getWireNumber()); mySkeleton.parentWireEnds(rowCount, waitingItem.myIdx); } // event about branch! if (awaitingList.size() > 1) { // merge event //mySkeleton.parentWireEnds(); // fix? final int[] ends = new int[awaitingList.size()]; for (int i = 0; i < awaitingList.size(); i++) { final WaitingItem waiting = awaitingList.get(i); ends[i] = waiting.myIdx; } mySkeleton.setEnds(rowCount, ends); } } else { // a start (head): no children. Use new wire wireNumber = myRing.getFree(); // this is start mySkeleton.wireStarts(rowCount); mySkeleton.setEnds(rowCount, new int[] {-1}); } // register what we choose if (mySeizedWires.containsValue(rowCount) && mySeizedWires.getKeysByValue(rowCount).iterator().next() != wireNumber) { System.out.println("caught on adding!"); } mySeizedWires.put(wireNumber, rowCount); commitI.setWireNumber(wireNumber); if (parents.isEmpty()) { // end event mySkeleton.wireEnds(rowCount); // free myRing.back(wireNumber); mySeizedWires.remove(wireNumber); } else { boolean selfUsed = false; for (AbstractHash parent : parents) { WaitingItem item; Collection<WaitingItem> existing = myAwaitingParents.get(parent); if (existing != null && ! existing.isEmpty()) { // use its wire! item = new WaitingItem(rowCount, existing.iterator().next().getWire(), parents.size() > 1); } else { // a start (head): no children. Use new wire Integer parentWire; if (! selfUsed) { parentWire = wireNumber; selfUsed = true; } else { // this is start parentWire = myRing.getFree(); mySkeleton.wireStarts(rowCount); } myFutureSeizedWires.put(parentWire, parent); item = new WaitingItem(rowCount, parentWire, parents.size() > 1); } myAwaitingParents.putValue(parent, item); myBackIndex.putValue(item.myIdx, item); } } } public void oldBecameNew(final Map<Integer, Integer> map) { final MultiMap<Integer, WaitingItem> backCopy = new MultiMap<Integer, WaitingItem>(); backCopy.putAllValues(myBackIndex); myBackIndex.clear(); for (Map.Entry<Integer, Collection<WaitingItem>> entry : backCopy.entrySet()) { Integer key = map.get(entry.getKey()); Collection<WaitingItem> items = entry.getValue(); if (key == null) { key = entry.getKey(); } else { for (WaitingItem item : items) { item.myIdx = key; } } myBackIndex.put(key, items); } // seized BidirectionalMap<Integer, Integer> copy = new BidirectionalMap<Integer, Integer>(); copy.putAll(mySeizedWires); mySeizedWires.clear(); for (Integer oldIdx : copy.values()) { List<Integer> wires = copy.getKeysByValue(oldIdx); if (wires == null || wires.size() != 1) { System.out.println("www"); } assert (wires != null && wires.size() == 1); Integer newIdx = map.get(oldIdx); newIdx = newIdx == null ? oldIdx : newIdx; mySeizedWires.put(wires.get(0), newIdx); } } /*public void oldBecameNew(int was, int is) { Collection<WaitingItem> removed = myBackIndex.remove(was); if (removed != null) { for (WaitingItem commit : removed) { commit.myIdx = is; } myBackIndex.put(is, removed); } List<Integer> keysByValue = mySeizedWires.getKeysByValue(was); if (keysByValue != null && ! keysByValue.isEmpty()) { if (keysByValue.size() > 1) { System.out.println("Ooops!"); } assert keysByValue.size() == 1; // each commit only on one wire mySeizedWires.remove(was); int value = keysByValue.get(0); mySeizedWires.put(value, is); } }*/ // just some order private static class CommitsComparator implements Comparator<WaitingItem> { private final static CommitsComparator ourInstance = new CommitsComparator(); public static CommitsComparator getInstance() { return ourInstance; } @Override public int compare(WaitingItem wc1, WaitingItem wc2) { return new Integer(wc1.getWire()).compareTo(wc2.getWire()); } } private static class WaitingItem { private int myIdx; private int myWire; private boolean myIsMerge; private WaitingItem(int idx, int wire, boolean isMerge) { myIdx = idx; myWire = wire; myIsMerge = isMerge; } public boolean isMerge() { return myIsMerge; } public int getIdx() { return myIdx; } public int getWire() { return myWire; } } private static class WaitingCommit { private int myIdx; // id of commit that's wait for parents (self) private int myNumParents; // i.e. a start private final boolean myIsMerge; private int myWire; private WaitingCommit(int idx, int numParents) { myIdx = idx; myNumParents = numParents; myIsMerge = myNumParents > 1; } public boolean isMerge() { return myIsMerge; } public void parentFound() { -- myNumParents; } public boolean allParentsFound() { return myNumParents == 0; } public int getWire() { return myWire; } public void setWire(int wire) { myWire = wire; } } public List<Integer> getFutureWireStarts(final int idx) { Collection<WaitingItem> waitingItems = myBackIndex.get(idx); if (waitingItems == null || waitingItems.isEmpty()) return Collections.emptyList(); final List<Integer> result = new ArrayList<Integer>(); for (WaitingItem item : waitingItems) { result.add(item.getWire()); } return result; } public Convertor<Integer, List<Integer>> getFutureConvertor() { return myFutureConvertor; } }