package org.community.intellij.plugins.communitycase.history.wholeTree;
import com.intellij.openapi.diff.impl.patch.formove.FilePathComparator;
import com.intellij.openapi.vcs.BigArray;
import com.intellij.openapi.vcs.GroupingMerger;
import com.intellij.openapi.vcs.changes.committed.DateChangeListGroupingStrategy;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.containers.MultiMap;
import com.intellij.util.containers.ReadonlyList;
import com.intellij.util.containers.StepList;
import com.intellij.util.ui.ColumnInfo;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.table.AbstractTableModel;
import java.util.*;
/**
* @author irengrig
*/
public class BigTableTableModel extends AbstractTableModel {
public final static Object LOADING = new Object();
// should be grouped
@Nullable
private Map<VirtualFile, SkeletonBuilder> mySkeletonBuilder;
@Nullable
private Map<VirtualFile, TreeNavigationImpl> myNavigation;
@Nullable
private List<VirtualFile> myOrder;
private Map<VirtualFile, Integer> myAdditions;
// end group
@NotNull
private final List<ColumnInfo> myColumns;
private RootsHolder myRootsHolder;
@Nullable
private StepList<CommitI> myLines;
private int myCutCount;
private DetailsCache myCache;
private Runnable myInit;
private CommitGroupingStrategy myStrategy;
private Comparator<CommitI> myCurrentComparator;
private int myCommitIdxInterval;
private int myNumEventsInGroup;
public BigTableTableModel(@NotNull final List<ColumnInfo> columns, Runnable init) {
myColumns = columns;
myInit = init;
myCurrentComparator = CommitIReorderingInsideOneRepoComparator.getInstance();
final DateChangeListGroupingStrategy delegate = new DateChangeListGroupingStrategy();
myStrategy = new CommitGroupingStrategy() {
@Override
public void beforeStart() {
delegate.beforeStart();
}
@Override
public String getGroupName(CommitI commit) {
return delegate.getGroupName(new Date(commit.getTime()));
}
};
myLines = new BigArray<CommitI>(10);
myCutCount = -1;
myCommitIdxInterval = 50;
myNumEventsInGroup = 20;
}
public void setCommitIdxInterval(int commitIdxInterval) {
myCommitIdxInterval = commitIdxInterval;
}
public void setNumEventsInGroup(int numEventsInGroup) {
myNumEventsInGroup = numEventsInGroup;
}
public ColumnInfo getColumnInfo(final int column) {
return myColumns.get(column);
}
@Override
public String getColumnName(int column) {
return myColumns.get(column).getName();
}
@Override
public int getColumnCount() {
return myColumns.size();
}
int getTrueCount() {
return myLines == null ? 0 : myLines.getSize();
}
@Override
public int getRowCount() {
if (myInit != null) {
final Runnable init = myInit;
myInit = null;
init.run();
}
if (myCutCount > 0) {
return myCutCount;
}
return myLines == null ? 0 : myLines.getSize();
}
public CommitI getCommitAt(final int row) {
if (myLines == null) return null;
if (row >= myLines.getSize()) return null;
return myLines.get(row);
}
public int getTotalWires() {
if (mySkeletonBuilder == null) return -1;
int wires = 0;
for (TreeNavigationImpl navigation : myNavigation.values()) {
wires += navigation.getMaximumWires();
}
return wires;
}
@Nullable
public List<Integer> getWiresGroups() {
if (mySkeletonBuilder == null) return null;
final List<Integer> result = new ArrayList<Integer>(myOrder.size());
for (VirtualFile file : myOrder) {
result.add(myNavigation.get(file).getMaximumWires());
}
return result;
}
public int getCorrectedWire(final CommitI commitI) {
if (mySkeletonBuilder == null) return -1;
final VirtualFile file = commitI.selectRepository(myRootsHolder.getRoots());
return myAdditions.get(file) + commitI.getWireNumber();
}
public WiresGroupIterator getGroupIterator(final int firstRow) {
return new WiresGroupIterator(firstRow);
}
class WiresGroupIterator {
private final int myFirstIdx;
private List<Integer> myFirstUsed;
WiresGroupIterator(int firstIdx) {
myFirstIdx = firstIdx;
myFirstUsed = new ArrayList<Integer>();
for (VirtualFile file : myOrder) {
TreeNavigationImpl navigation = myNavigation.get(file);
final List<Integer> used = navigation.getUsedWires(firstIdx, myLines, mySkeletonBuilder.get(file).getFutureConvertor()).getUsed();
myFirstUsed.addAll(used);
}
}
public List<Integer> getFirstUsed() {
return myFirstUsed;
}
public WireEvent getEventForRow(final int row) {
assert row >= myFirstIdx;
return myNavigation.get(getCommitAt(row).selectRepository(myRootsHolder.getRoots())).getEventForRow(row);
}
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
final ColumnInfo column = myColumns.get(columnIndex);
if (myLines == null) return column.getPreferredStringValue();
final CommitI commitI = myLines.get(rowIndex);
if (commitI == null) return column.getPreferredStringValue();
if (commitI.holdsDecoration()) return columnIndex == 0 ? commitI.getDecorationString() : "";
final org.community.intellij.plugins.communitycase.history.browser.Commit details =
myCache.convert(commitI.selectRepository(myRootsHolder.getRoots()), commitI.getHash());
if (details == null) return LOADING;
return column.valueOf(details);
}
public MultiMap<VirtualFile, AbstractHash> getMissing(final int startRow, final int endRow) {
if (myLines == null || myRootsHolder == null) return MultiMap.emptyInstance();
final MultiMap<VirtualFile, AbstractHash> result = new MultiMap<VirtualFile, AbstractHash>();
for (int i = startRow; i <= endRow; i++) {
final CommitI commitI = myLines.get(i);
if (commitI.holdsDecoration()) continue;
final AbstractHash hash = commitI.getHash();
final VirtualFile root = commitI.selectRepository(myRootsHolder.getRoots());
if (myCache.convert(root, commitI.getHash()) == null) {
result.putValue(root, hash);
}
}
return result;
}
public void clear(boolean noFilters) {
// todo uncomment for git log tree
/*if (noFilters) {
myCurrentComparator = CommitIComparator.getInstance();
myNavigation = new HashMap<VirtualFile, TreeNavigationImpl>();
mySkeletonBuilder = new HashMap<VirtualFile, SkeletonBuilder>();
myAdditions = new HashMap<VirtualFile, Integer>();
myOrder = new ArrayList<VirtualFile>(myRootsHolder.getRoots());
Collections.sort(myOrder, FilePathComparator.getInstance());
for (VirtualFile vf : myOrder) {
final TreeNavigationImpl navigation = new TreeNavigationImpl(myCommitIdxInterval, myNumEventsInGroup);// try to adjust numbers
final SkeletonBuilder skeletonBuilder = new SkeletonBuilder(navigation);
myNavigation.put(vf, navigation);
mySkeletonBuilder.put(vf, skeletonBuilder);
myAdditions.put(vf, 0);
}
} else {
myCurrentComparator = CommitIReorderingInsideOneRepoComparator.getInstance();
*/
myAdditions = null;
mySkeletonBuilder = null;
myNavigation = null;
myOrder = null;
//}
myLines = new BigArray<CommitI>(10);
myCutCount = -1;
}
public void cutAt(final int lastShownItemIdx) {
myCutCount = lastShownItemIdx + 1;
}
public void restore() {
myCutCount = -1;
}
public void appendData(final List<CommitI> lines, final List<List<AbstractHash>> parents) {
if (mySkeletonBuilder == null) {
Collections.sort(lines, myCurrentComparator);
}
final Integer[] parentsIdx = new Integer[1];
parentsIdx[0] = 0;
final Set<Integer> whatToRecount = mySkeletonBuilder == null ? null : new HashSet<Integer>();
final Map<Integer, Integer> indexRecalculation = new HashMap<Integer, Integer>();
myStrategy.beforeStart();
// find those ..... long awaited start idx by stupid long iteration since
// items can NOT be ordered by simple rule
int idxFrom = findIdx(lines);
int recountFrom = new GroupingMerger<CommitI, String>() {
@Override
protected void willBeRecountFrom(int idx, int wasSize) {
/* see community.git commit 60210d79199f6add144945978410abc5ab99aada
if (mySkeletonBuilder != null) {
for (int i = idx; i < wasSize; i++) {
//myIdxMap.removeValue(i);
for (VirtualFile root : myOrder) {
myRepoIdxMap.get(root).remove(i);
}
}
}
*/
}
@Override
protected CommitI wrapItem(CommitI commitI) {
if (mySkeletonBuilder != null && ! commitI.holdsDecoration()) {
return new WireNumberCommitDecoration(commitI);
}
return super.wrapItem(commitI);
}
@Override
protected void afterConsumed(CommitI commitI, int i) {
if (mySkeletonBuilder != null && ! commitI.holdsDecoration()) {
whatToRecount.add(i);
//mySkeletonBuilder.get(commitI.selectRepository(myRootsHolder.getRoots())).consume(commitI, parents.get(parentsIdx[0]), myLines, i);
//++parentsIdx[0];
}
}
@Override
protected boolean filter(CommitI commitI) {
return !commitI.holdsDecoration();
}
@Override
protected String getGroup(CommitI commitI) {
return mySkeletonBuilder != null ? "" : myStrategy.getGroupName(commitI);
}
@Override
protected CommitI wrapGroup(String s, CommitI item) {
return new GroupHeaderDatePseudoCommit(s, item.getTime() - 1);
}
@Override
protected void oldBecame(int was, int is) {
if (mySkeletonBuilder != null && was != is) {
indexRecalculation.put(was, is);
/*CommitI commitI = myLines.get(is);
if (! commitI.holdsDecoration()) {
mySkeletonBuilder.get(commitI.selectRepository(myRootsHolder.getRoots())).oldBecameNew(was, is);
}*/
}
// todo
//System.out.println("old: " + was + " became: " + is);
}
}.firstPlusSecond(myLines, new ReadonlyList.ArrayListWrapper<CommitI>(lines), myCurrentComparator, mySkeletonBuilder == null ? -1 : idxFrom);
if (mySkeletonBuilder != null) {
for (SkeletonBuilder skeletonBuilder : mySkeletonBuilder.values()) {
skeletonBuilder.oldBecameNew(indexRecalculation);
}
for (int i = recountFrom; i < myLines.getSize(); i++) {
final CommitI commitI = myLines.get(i);
if (mySkeletonBuilder != null && ! commitI.holdsDecoration() && whatToRecount.contains(i)) {
mySkeletonBuilder.get(commitI.selectRepository(myRootsHolder.getRoots())).consume(commitI, parents.get(parentsIdx[0]), myLines, i);
++parentsIdx[0];
}
}
for (Map.Entry<VirtualFile, TreeNavigationImpl> entry : myNavigation.entrySet()) {
final TreeNavigationImpl navigation = myNavigation.get(entry.getKey());
navigation.recalcIndex(myLines, mySkeletonBuilder.get(entry.getKey()).getFutureConvertor());
}
int size = 0;
for (VirtualFile file : myOrder) {
myAdditions.put(file, size);
size += myNavigation.get(file).getMaximumWires();
}
}
}
private int findIdx(List<CommitI> lines) {
final VirtualFile targetRepo = lines.get(0).selectRepository(myRootsHolder.getRoots());
final long time = lines.get(0).getTime();
for (int i = myLines.getSize() - 1; i >= 0; i--) {
final CommitI current = myLines.get(i);
if (current.selectRepository(myRootsHolder.getRoots()).equals(targetRepo)) {
return i + 1; // will be equal to list size sometimes, is that ok?
} else {
if (current.getTime() > time) {
return i + 1;
}
}
}
return 0;
}
public void setCache(DetailsCache cache) {
myCache = cache;
}
public void setRootsHolder(RootsHolder rootsHolder) {
myRootsHolder = rootsHolder;
}
public void setStrategy(CommitGroupingStrategy strategy) {
myStrategy = strategy;
}
// todo test
public void printNavigation() {
for (Map.Entry<VirtualFile, TreeNavigationImpl> entry : myNavigation.entrySet()) {
if (entry.getKey().getPath().contains("inner")) {
entry.getValue().printSelf();
}
}
}
}