blob: a6dec653c44ed7777fc0f8e32b409d48414dd68e [file] [log] [blame]
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package git4idea.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;
private int myMaxWireNum;
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);
}
};
myMaxWireNum = 0;
}
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);
if (waiting.isMerge()) {
// put/update start event - now we know index so can create/update wire event
mySkeleton.addStartToEvent(waiting.myIdx, rowCount, waiting.getWire());
}
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);
}
}
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
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
mySeizedWires.put(wireNumber, rowCount);
commitI.setWireNumber(wireNumber);
myMaxWireNum = Math.max(myMaxWireNum, myRing.getMaxNumber());
if (parents.isEmpty()) {
// end event
mySkeleton.wireEnds(rowCount);
// free
myRing.back(wireNumber);
mySeizedWires.remove(wireNumber);
} else {
boolean selfUsed = false;
final List<Integer> parentWires = new ArrayList<Integer>();
for (AbstractHash parent : parents) {
WaitingItem item;
Collection<WaitingItem> existing = myAwaitingParents.get(parent);
if (existing != null && ! existing.isEmpty()) {
// use its wire!
final int wire = existing.iterator().next().getWire();
item = new WaitingItem(rowCount, wire, parents.size() > 1);
parentWires.add(wire);
} 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);
}
parentWires.add(parentWire);
myFutureSeizedWires.put(parentWire, parent);
item = new WaitingItem(rowCount, parentWire, parents.size() > 1);
}
myAwaitingParents.putValue(parent, item);
myBackIndex.putValue(item.myIdx, item);
}
if (parents.size() > 1) {
mySkeleton.setWireStartsNumber(rowCount, parentWires.toArray(new Integer[parentWires.size()]));
}
}
}
public int getMaxWireNum() {
return myMaxWireNum;
}
// 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;
}
}
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;
}
}