package org.community.intellij.plugins.communitycase.history.wholeTree;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.vcs.Ring;
import com.intellij.util.Consumer;
import com.intellij.util.containers.BidirectionalMap;
import com.intellij.util.containers.Convertor;
import com.intellij.util.containers.ReadonlyList;
import java.util.*;
/**
* @author irengrig
*/
public class TreeNavigationImpl implements TreeNavigation, WireEventsListener {
public static final int[] MARKER = new int[]{-1};
private final TreeMap<Integer, WireEvent> myWireEvents;
// created for some of wire events; idx of _commit_
private final TreeMap<Integer, RingIndex> myRingIndex;
// maximum number of commits, after which index entry should be written
private final int myCommitIndexInterval;
// maximum number of wire events, after which index entry should be written
private final int myNumWiresInGroup;
private int myMaximumWires;
public TreeNavigationImpl(final int commitIndexInterval, final int numWiresInGroup) {
myCommitIndexInterval = commitIndexInterval;
myNumWiresInGroup = numWiresInGroup;
myWireEvents = new TreeMap<Integer, WireEvent>();
myRingIndex = new TreeMap<Integer, RingIndex>();
myMaximumWires = 0;
}
public void recalcIndex(final ReadonlyList<CommitI> commits, final Convertor<Integer, List<Integer>> future) {
if (myWireEvents.isEmpty()) return;
Integer lastIndexKey = myRingIndex.isEmpty() ? null : myRingIndex.lastKey();
//System.out.println("=== recalc index from: " + lastIndexKey + " ====");
final SortedMap<Integer, WireEvent> tail;
final Ring<Integer> ring;
if (lastIndexKey != null) {
//tail = myWireEvents.tailMap(lastIndexKey, false); // was like that
tail = myWireEvents.tailMap(lastIndexKey, true);
final int size = tail.size();
if (size == 1) return;
final Integer lastKey = myWireEvents.lastKey();
if (lastKey - lastIndexKey < myCommitIndexInterval || size < myNumWiresInGroup) {
return;
}
ring = myRingIndex.lastEntry().getValue().getUsedInRing();
} else {
lastIndexKey = myWireEvents.firstKey();
ring = new Ring.IntegerRing();
final List<Integer> used = ring.getUsed();
myRingIndex.put(lastIndexKey, new RingIndex(used.toArray(new Integer[used.size()])));
WireEvent firstEvent = myWireEvents.firstEntry().getValue();
performOnRing(ring, firstEvent, commits, future.convert(firstEvent.getCommitIdx()));
tail = myWireEvents.tailMap(lastIndexKey, false);
}
int cnt = 0;
int recordCommitIdx = myCommitIndexInterval + myWireEvents.floorKey(lastIndexKey);
for (Integer integer : tail.keySet()) {
++ cnt;
if ((cnt >= myNumWiresInGroup) || (integer >= recordCommitIdx)) {
final List<Integer> used = ring.getUsed();
myRingIndex.put(integer, new RingIndex(used.toArray(new Integer[used.size()])));
cnt = 0;
recordCommitIdx += myCommitIndexInterval;
}
WireEvent event = tail.get(integer);
performOnRing(ring, event, commits, future.convert(event.getCommitIdx()));
}
}
// todo write start immediately in event in form of hash
// todo to be able to get it here, when building index
// todo: problem: when doing index, we don't have "future commits" wires!
private void performOnRing(final Ring<Integer> ring, final WireEvent event, final ReadonlyList<CommitI> convertor,
List<Integer> futureWireStarts) {
final int[] wireEnds = event.getWireEnds();
if (wireEnds != null) {
for (int wireEnd : wireEnds) {
int wireNumber = convertor.get(wireEnd).getWireNumber();
if (ring.haveInFree(wireNumber)) {
System.out.println("assertion will rise here, commits size: " + convertor.getSize() + " event idx: " + event.getCommitIdx());
}
//System.out.println("back(1): " + wireNumber + " from: " + event.getCommitIdx());
ring.back(wireNumber);
}
}
if (event.isStart()) {
final int commitWire = convertor.get(event.getCommitIdx()).getWireNumber();
//System.out.println("use(start): " + commitWire);
ring.minus(commitWire);
}
if (event.isEnd()) {
final int commitWire = convertor.get(event.getCommitIdx()).getWireNumber();
//System.out.println("back(2): " + commitWire + " from: " + event.getCommitIdx());
ring.back(commitWire);
} else {
final int[] commitsStarts = event.getCommitsStarts();
for (int commitStart : commitsStarts) {
final int commitWire = convertor.get(commitStart).getWireNumber();
//System.out.println("use(merge commit): " + commitWire);
ring.minus(commitWire);
}
}
for (Integer wireStart : futureWireStarts) {
ring.minus(wireStart);
}
myMaximumWires = Math.max(myMaximumWires, ring.getMaxNumber());
}
public Collection<WireEvent> getTail(int rowInclusive) {
return myWireEvents.tailMap(rowInclusive).values();
}
public WireEvent getEventForRow(int row) {
return myWireEvents.get(row);
}
@Override
public Iterator<WireEvent> createWireEventsIterator(int rowInclusive) {
return myWireEvents.tailMap(rowInclusive).values().iterator();
}
private Iterator<WireEvent> createWireEventsBackIterator(int rowExclusive) {
return myWireEvents.headMap(rowExclusive, false).descendingMap().values().iterator();
}
@Override
public Ring<Integer> getUsedWires(int row, ReadonlyList<CommitI> commits, final Convertor<Integer, List<Integer>> future) {
final Map.Entry<Integer, RingIndex> entry = myRingIndex.floorEntry(row);
if (entry == null) return new Ring.IntegerRing();
final Ring<Integer> ring = entry.getValue().getUsedInRing();
if (entry.getKey() == row) {
return ring;
}
System.out.println("-----------------> row = " + row);
final Iterator<WireEvent> iterator = createWireEventsIterator(entry.getKey());
while (iterator.hasNext()) {
final WireEvent event = iterator.next();
if (event.getCommitIdx() >= row) {
return ring;
}
System.out.println("event: " + event.toString());
System.out.println("ring before: " + ring.toString());
performOnRing(ring, event, commits, future.convert(event.getCommitIdx()));
}
return ring;
}
@Override
public void addStartToEvent(int row, final int parentRow) {
modify(row, new Consumer<WireEvent>() {
@Override
public void consume(WireEvent wireEvent) {
wireEvent.addStart(parentRow);
}
});
}
@Override
public void wireStarts(int row) {
modify(row, new Consumer<WireEvent>() {
@Override
public void consume(WireEvent wireEvent) {
/*if (wireEvent.getCommitsEnds() == null) {
wireEvent.setCommitEnds(MARKER);
}*/
}
});
//myWireEvents.put(row, new WireEvent(row, MARKER));
}
@Override
public void wireEnds(int row) {
modify(row, new Consumer<WireEvent>() {
@Override
public void consume(WireEvent wireEvent) {
wireEvent.addStart(-1);
}
});
}
@Override
public void setEnds(int row, final int[] commitEnds) {
modify(row, new Consumer<WireEvent>() {
@Override
public void consume(WireEvent wireEvent) {
wireEvent.setCommitEnds(commitEnds);
}
});
}
/*@Override
public void addWireEvent(int row, int[] branched) {
final WireEvent wireEvent = new WireEvent(row, branched);
myWireEvents.put(row, wireEvent);
}*/
@Override
public void parentWireEnds(int row, final int parentRow) {
modify(row, new Consumer<WireEvent>() {
@Override
public void consume(WireEvent wireEvent) {
wireEvent.addWireEnd(parentRow);
}
});
}
private void modify(final int row, final Consumer<WireEvent> consumer) {
WireEvent event = myWireEvents.get(row);
if (event == null) {
event = new WireEvent(row, null);
myWireEvents.put(row, event);
}
consumer.consume(event);
}
public void printSelf() {
System.out.println("============== EVENTS =================");
for (WireEvent event : myWireEvents.values()) {
System.out.println(event.toString());
}
System.out.println("==============********=================");
}
private static class RingIndex {
private final Integer[] myWireNumbers;
private RingIndex(Integer[] wireNumbers) {
myWireNumbers = wireNumbers;
}
public Ring<Integer> getUsedInRing() {
return new Ring.IntegerRing(Arrays.<Integer>asList(myWireNumbers));
}
}
/*public void recountWires(final int fromIdx, final ReadonlyList<CommitI> commits) {
final Map<Integer, Integer> recalculateMap = new HashMap<Integer, Integer>();
//final Iterator<WireEvent> backIterator = createWireEventsBackIterator(fromIdx);
//int runningCommitNumber = backIterator.hasNext() ? backIterator.next().getCommitIdx() : 0; // next after previous event
// t_odo: group two iterators to optimize!
Ring<Integer> usedWires = getUsedWires(fromIdx, commits);
final Ring.IntegerRing ring = new Ring.IntegerRing(usedWires.getUsed());
int runningCommitNumber = 0;
final Iterator<WireEvent> iterator = createWireEventsIterator(fromIdx);
for (; iterator.hasNext(); ) {
final WireEvent we = iterator.next();
recountFragmentZwichem(commits, recalculateMap, runningCommitNumber, we.getCommitIdx());
runningCommitNumber = we.getCommitIdx() + 1;
final int[] wireEnds = we.getWireEnds();
if (wireEnds != null) {
for (int wireEnd : wireEnds) {
ring.back(wireEnd);
}
}
if (we.isStart()) {
final CommitI thisCommit = commits.get(we.getCommitIdx());
final int thisWireNum = thisCommit.getWireNumber();
final Integer newNum = ring.getFree();
if (newNum != thisWireNum) {
recalculateMap.put(thisWireNum, newNum);
// if self is start, recalculate self here
thisCommit.setWireNumber(newNum);
}
}
if (we.isEnd()) {
ring.back(commits.get(we.getCommitIdx()).getWireNumber());
}
final int[] commitsStarts = we.getCommitsStarts();
if (commitsStarts.length > 0 && (! we.isEnd())) {
for (int commitStart : commitsStarts) {
// wire number
Integer corrected = recalculateMap.get(commitStart);
int wasWireNumber = commits.get(commitStart).getWireNumber();
corrected = (corrected == null) ? wasWireNumber : corrected;
if (! ring.isNumUsed(corrected)) {
final Integer newNum = ring.getFree();
recalculateMap.put(wasWireNumber, newNum);
}
}
}
}
recountFragmentZwichem(commits, recalculateMap, runningCommitNumber, commits.getSize() - 1);
// todo is not called any more
myMaximumWires = Math.max(myMaximumWires, ring.getMaxNumber());
}*/
public int getMaximumWires() {
return myMaximumWires;
}
private void recountFragmentZwichem(ReadonlyList<CommitI> commits,
Map<Integer, Integer> recalculateMap,
int runningCommitNumber,
int inclusive) {
if (! recalculateMap.isEmpty()) {
for (int i = runningCommitNumber; i <= inclusive; i++) {
final CommitI commit = commits.get(i);
final Integer newWire = recalculateMap.get(commit.getWireNumber());
if (newWire != null) {
commit.setWireNumber(newWire);
}
}
}
}
}