package com.intellij.vcs.log.ui.tables;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.EmptyRunnable;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.NotNullFunction;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.text.DateFormatUtil;
import com.intellij.vcs.log.*;
import com.intellij.vcs.log.data.*;
import com.intellij.vcs.log.impl.VcsLogUtil;
import com.intellij.vcs.log.ui.VcsLogUiImpl;
import com.intellij.vcs.log.ui.render.GraphCommitCell;
import com.intellij.vcs.log.util.VcsUserUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.table.AbstractTableModel;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;
public class GraphTableModel extends AbstractTableModel {
public static final int ROOT_COLUMN = 0;
public static final int COMMIT_COLUMN = 1;
public static final int AUTHOR_COLUMN = 2;
public static final int DATE_COLUMN = 3;
private static final int COLUMN_COUNT = DATE_COLUMN + 1;
private static final String[] COLUMN_NAMES = {"", "Subject", "Author", "Date"};
private static final int UP_PRELOAD_COUNT = 20;
private static final int DOWN_PRELOAD_COUNT = 40;
@NotNull private final VcsLogData myLogData;
@NotNull protected final VcsLogUiImpl myUi;
@NotNull protected VisiblePack myDataPack;
private boolean myMoreRequested;
public GraphTableModel(@NotNull VisiblePack dataPack, @NotNull VcsLogData logData, @NotNull VcsLogUiImpl ui) {
myLogData = logData;
myUi = ui;
myDataPack = dataPack;
}
@Override
public int getRowCount() {
return myDataPack.getVisibleGraph().getVisibleCommitCount();
}
@NotNull
public VirtualFile getRoot(int rowIndex) {
return myDataPack.getRoot(rowIndex);
}
@NotNull
public Integer getIdAtRow(int row) {
return myDataPack.getVisibleGraph().getRowInfo(row).getCommit();
}
@Nullable
public CommitId getCommitIdAtRow(int row) {
return myLogData.getCommitId(getIdAtRow(row));
}
public int getRowOfCommit(@NotNull final Hash hash, @NotNull VirtualFile root) {
final int commitIndex = myLogData.getCommitIndex(hash, root);
return ContainerUtil.indexOf(VcsLogUtil.getVisibleCommits(myDataPack.getVisibleGraph()), (Condition<Integer>)i -> i == commitIndex);
}
public int getRowOfCommitByPartOfHash(@NotNull String partialHash) {
final CommitIdByStringCondition hashByString = new CommitIdByStringCondition(partialHash);
CommitId commitId = myLogData.getHashMap().findCommitId(
commitId1 -> hashByString.value(commitId1) && getRowOfCommit(commitId1.getHash(), commitId1.getRoot()) != -1);
return commitId != null ? getRowOfCommit(commitId.getHash(), commitId.getRoot()) : -1;
}
@Override
public final int getColumnCount() {
return COLUMN_COUNT;
}
/**
* Requests the proper data provider to load more data from the log & recreate the model.
*
* @param onLoaded will be called upon task completion on the EDT.
*/
public void requestToLoadMore(@NotNull Runnable onLoaded) {
myMoreRequested = true;
myUi.getFilterer().moreCommitsNeeded(onLoaded);
myUi.getTable().setPaintBusy(true);
}
@NotNull
@Override
public final Object getValueAt(int rowIndex, int columnIndex) {
if (rowIndex >= getRowCount() - 1 && canRequestMore()) {
requestToLoadMore(EmptyRunnable.INSTANCE);
}
VcsShortCommitDetails data = getShortDetails(rowIndex);
switch (columnIndex) {
case ROOT_COLUMN:
return getRoot(rowIndex);
case COMMIT_COLUMN:
return new GraphCommitCell(data.getSubject(), getRefsAtRow(rowIndex),
myDataPack.getVisibleGraph().getRowInfo(rowIndex).getPrintElements());
case AUTHOR_COLUMN:
String authorString = VcsUserUtil.getShortPresentation(data.getAuthor());
return authorString + (VcsUserUtil.isSamePerson(data.getAuthor(), data.getCommitter()) ? "" : "*");
case DATE_COLUMN:
if (data.getAuthorTime() < 0) {
return "";
}
else {
return DateFormatUtil.formatDateTime(data.getAuthorTime());
}
default:
throw new IllegalArgumentException("columnIndex is " + columnIndex + " > " + (COLUMN_COUNT - 1));
}
}
/**
* Returns true if not all data has been loaded, i.e. there is sense to {@link #requestToLoadMore(Runnable) request more data}.
*/
public boolean canRequestMore() {
return !myMoreRequested && myDataPack.canRequestMore();
}
@Override
public Class<?> getColumnClass(int column) {
switch (column) {
case ROOT_COLUMN:
return VirtualFile.class;
case COMMIT_COLUMN:
return GraphCommitCell.class;
case AUTHOR_COLUMN:
return String.class;
case DATE_COLUMN:
return String.class;
default:
throw new IllegalArgumentException("columnIndex is " + column + " > " + (COLUMN_COUNT - 1));
}
}
@Override
public String getColumnName(int column) {
return COLUMN_NAMES[column];
}
public void setVisiblePack(@NotNull VisiblePack visiblePack) {
myDataPack = visiblePack;
myMoreRequested = false;
fireTableDataChanged();
}
@NotNull
public VisiblePack getVisiblePack() {
return myDataPack;
}
@NotNull
public VcsFullCommitDetails getFullDetails(int row) {
return getDetails(row, myLogData.getCommitDetailsGetter());
}
@NotNull
public VcsShortCommitDetails getShortDetails(int row) {
return getDetails(row, myLogData.getMiniDetailsGetter());
}
@NotNull
private <T extends VcsShortCommitDetails> T getDetails(int row, @NotNull DataGetter<T> dataGetter) {
Iterable<Integer> iterable = createRowsIterable(row, UP_PRELOAD_COUNT, DOWN_PRELOAD_COUNT, getRowCount());
return dataGetter.getCommitData(getIdAtRow(row), iterable);
}
@NotNull
public Collection<VcsRef> getRefsAtRow(int row) {
return ((RefsModel)myDataPack.getRefs()).refsToCommit(getIdAtRow(row));
}
@NotNull
public List<VcsRef> getBranchesAtRow(int row) {
return getRefsAtRow(row).stream().filter(ref -> ref.getType().isBranch()).collect(Collectors.toList());
}
@NotNull
private Iterable<Integer> createRowsIterable(final int row, final int above, final int below, final int maxRows) {
return () -> new Iterator<Integer>() {
private int myRowIndex = Math.max(0, row - above);
@Override
public boolean hasNext() {
return myRowIndex < row + below && myRowIndex < maxRows;
}
@Override
public Integer next() {
int nextRow = myRowIndex;
myRowIndex++;
return getIdAtRow(nextRow);
}
@Override
public void remove() {
throw new UnsupportedOperationException("Removing elements is not supported.");
}
};
}
@NotNull
public List<Integer> convertToCommitIds(@NotNull List<Integer> rows) {
return ContainerUtil.map(rows, (NotNullFunction<Integer, Integer>)this::getIdAtRow);
}
}