blob: 5de3880acf0cb5a7aa197de9c191566d1a83cc9c [file] [log] [blame]
/*
* Copyright 2000-2014 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 com.intellij.cvsSupport2.history;
import com.intellij.CvsBundle;
import com.intellij.cvsSupport2.CvsFilePath;
import com.intellij.cvsSupport2.CvsUtil;
import com.intellij.cvsSupport2.application.CvsEntriesManager;
import com.intellij.cvsSupport2.changeBrowser.CvsChangeList;
import com.intellij.cvsSupport2.changeBrowser.CvsRepositoryLocation;
import com.intellij.cvsSupport2.connections.CvsConnectionSettings;
import com.intellij.cvsSupport2.connections.CvsEnvironment;
import com.intellij.cvsSupport2.cvsExecution.CvsOperationExecutor;
import com.intellij.cvsSupport2.cvsExecution.DefaultCvsOperationExecutorCallback;
import com.intellij.cvsSupport2.cvshandlers.CommandCvsHandler;
import com.intellij.cvsSupport2.cvsoperations.cvsLog.LocalPathIndifferentLogOperation;
import com.intellij.cvsSupport2.cvsoperations.cvsTagOrBranch.ui.TagsPanel;
import com.intellij.cvsSupport2.util.CvsVfsUtil;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.annotate.ShowAllAffectedGenericAction;
import com.intellij.openapi.vcs.history.*;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.TreeItem;
import com.intellij.util.ui.AbstractTableCellEditor;
import com.intellij.util.ui.ColumnInfo;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.netbeans.lib.cvsclient.admin.Entry;
import org.netbeans.lib.cvsclient.command.log.LogInformation;
import org.netbeans.lib.cvsclient.command.log.Revision;
import javax.swing.*;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import java.awt.*;
import java.io.File;
import java.util.*;
import java.util.List;
public class CvsHistoryProvider implements VcsHistoryProvider {
public static final ColumnInfo<VcsFileRevision, String> STATE = new ColumnInfo<VcsFileRevision, String>(
CvsBundle.message("file.history.state.column.name")) {
public String valueOf(VcsFileRevision vcsFileRevision) {
if (!(vcsFileRevision instanceof CvsFileRevision)) return "";
return ((CvsFileRevision)vcsFileRevision).getState();
}
public Comparator<VcsFileRevision> getComparator() {
return new Comparator<VcsFileRevision>() {
public int compare(VcsFileRevision r1, VcsFileRevision r2) {
if (!(r1 instanceof CvsFileRevision)) return 1;
if (!(r2 instanceof CvsFileRevision)) return -1;
return ((CvsFileRevision)r1).getState().compareTo(((CvsFileRevision)r2).getState());
}
};
}
};
abstract class TagOrBranchColumn extends ColumnInfo {
public TagOrBranchColumn(final String name) {
super(name);
}
public TableCellRenderer getRenderer(Object object) {
final TableCellRenderer rendererFromSuper = super.getRenderer(object);
if (!(object instanceof CvsFileRevision)) return rendererFromSuper;
final Collection tags = getValues((CvsFileRevision)object);
if (tags.size() < 2) return rendererFromSuper;
return new TagsPanel(getName());
}
public boolean isCellEditable(Object object) {
if (!(object instanceof CvsFileRevision)) return false;
return getValues(((CvsFileRevision)object)).size() > 1;
}
public TableCellEditor getEditor(final Object object) {
if (!(object instanceof CvsFileRevision)) return null;
return new AbstractTableCellEditor() {
public Object getCellEditorValue() {
return "";
}
public Component getTableCellEditorComponent(JTable table,
Object value,
boolean isSelected,
int row,
int column) {
final TagsPanel result = new TagsPanel(getName());
result.setTags(getValues((CvsFileRevision)object));
result.setSelected(true, table);
return result;
}
};
}
protected abstract Collection<String> getValues(CvsFileRevision revision);
public Object valueOf(Object object) {
if (!(object instanceof CvsFileRevision)) return "";
final Collection values = getValues(((CvsFileRevision)object));
if (values.isEmpty()) return "";
if (values.size() == 1) return values.iterator().next().toString();
return values;
}
}
private final ColumnInfo TAG = new TagOrBranchColumn(CvsBundle.message("file.history.tag.column.name")) {
protected Collection<String> getValues(CvsFileRevision revision) {
return revision.getTags();
}
};
public final ColumnInfo BRANCHES = new TagOrBranchColumn(CvsBundle.message("file.history.branches.column.name")) {
protected Collection<String> getValues(CvsFileRevision revision) {
return revision.getBranches();
}
};
private final Project myProject;
public CvsHistoryProvider(Project project) {
myProject = project;
}
public boolean isDateOmittable() {
return false;
}
public VcsDependentHistoryComponents getUICustomization(final VcsHistorySession session, JComponent forShortcutRegistration) {
return VcsDependentHistoryComponents.createOnlyColumns(new ColumnInfo[]{
STATE, TAG, BRANCHES
});
}
public String getHelpId() {
return null;
}
@Nullable
public VcsHistorySession createSessionFor(final FilePath filePath) {
final List<VcsFileRevision> fileRevisionList = createRevisions(filePath);
if (fileRevisionList == null) return null;
return new MyHistorySession(fileRevisionList, filePath);
}
private static class MyHistorySession extends VcsAbstractHistorySession {
private final FilePath myFilePath;
private MyHistorySession(List<? extends VcsFileRevision> revisions, FilePath filePath) {
super(revisions);
myFilePath = filePath;
}
@Nullable
public VcsRevisionNumber calcCurrentRevisionNumber() {
return myFilePath == null ? null : getCurrentRevision(myFilePath);
}
@Override
public synchronized boolean shouldBeRefreshed() {
//noinspection SimplifiableIfStatement
if (!CvsEntriesManager.getInstance().isActive()) {
return false;
}
return super.shouldBeRefreshed();
}
public boolean isContentAvailable(final VcsFileRevision revision) {
if (revision instanceof CvsFileRevision) {
final CvsFileRevision cvsFileRevision = (CvsFileRevision)revision;
return !cvsFileRevision.getState().equals(CvsChangeList.DEAD_STATE);
}
return super.isContentAvailable(revision);
}
public HistoryAsTreeProvider getHistoryAsTreeProvider() {
return MyHistoryAsTreeProvider.getInstance();
}
@Override
public VcsHistorySession copy() {
return new MyHistorySession(getRevisionList(), myFilePath);
}
}
public void reportAppendableHistory(FilePath path, VcsAppendableHistorySessionPartner partner) throws VcsException {
final VcsHistorySession session;
if (path instanceof CvsFilePath) {
final CvsRepositoryLocation location = ((CvsFilePath)path).getRepositoryLocation();
final List<VcsFileRevision> fileRevisionList = createRevisions(location.getEnvironment(), path.getIOFile());
if (fileRevisionList == null) return;
session = new MyHistorySession(fileRevisionList, path);
}
else {
session = createSessionFor(path);
}
partner.reportCreatedEmptySession((VcsAbstractHistorySession)session);
}
private static VcsRevisionNumber getCurrentRevision(FilePath filePath) {
final Entry entryFor = CvsEntriesManager.getInstance().getEntryFor(filePath.getVirtualFileParent(), filePath.getName());
if (entryFor == null) {
return new CvsRevisionNumber("0");
}
else {
return new CvsRevisionNumber(entryFor.getRevision());
}
}
@Nullable
public List<VcsFileRevision> createRevisions(final FilePath filePath) {
final File file = filePath.getIOFile();
final VirtualFile root = CvsVfsUtil.refreshAndFindFileByIoFile(file.getParentFile());
// check if we have a history pane open for a file in a package which has just been deleted
if (root == null) return null;
final CvsConnectionSettings env = CvsEntriesManager.getInstance().getCvsConnectionSettingsFor(filePath.getVirtualFileParent());
final File lightweightFileForFile = CvsUtil.getCvsLightweightFileForFile(file);
return createRevisions(env, lightweightFileForFile);
}
private List<VcsFileRevision> createRevisions(final CvsEnvironment connectionSettings, final File lightweightFileForFile) {
final LocalPathIndifferentLogOperation logOperation = new LocalPathIndifferentLogOperation(connectionSettings);
logOperation.addFile(lightweightFileForFile);
final CvsOperationExecutor executor = new CvsOperationExecutor(myProject);
final ArrayList<VcsFileRevision> result = new ArrayList<VcsFileRevision>();
executor.performActionSync(new CommandCvsHandler(CvsBundle.message("operation.name.load.file.content"), logOperation),
new DefaultCvsOperationExecutorCallback() {
@Override
public void executionFinishedSuccessfully() {
final LogInformation firstLogInformation = logOperation.getFirstLogInformation();
if (firstLogInformation != null) {
final List<Revision> revisionList = firstLogInformation.getRevisionList();
for (Revision revision : revisionList) {
result.add(new CvsFileRevisionImpl(revision, lightweightFileForFile,
firstLogInformation, connectionSettings, myProject));
}
}
}
});
Collections.sort(result, Collections.reverseOrder(VcsFileRevisionComparator.INSTANCE));
return result;
}
public AnAction[] getAdditionalActions(final Runnable refresher) {
return new AnAction[]{ ShowAllAffectedGenericAction.getInstance() };
}
public boolean supportsHistoryForDirectories() {
return false;
}
@Override
public DiffFromHistoryHandler getHistoryDiffHandler() {
return null;
}
@Override
public boolean canShowHistoryFor(@NotNull VirtualFile file) {
return true;
}
private static class MyHistoryAsTreeProvider implements HistoryAsTreeProvider {
private static final MyHistoryAsTreeProvider ourInstance = new MyHistoryAsTreeProvider();
public static MyHistoryAsTreeProvider getInstance() {
return ourInstance;
}
public List<TreeItem<VcsFileRevision>> createTreeOn(List<VcsFileRevision> allRevisions) {
Collections.sort(allRevisions, VcsFileRevisionComparator.INSTANCE);
final List<TreeItem<VcsFileRevision>> result = new ArrayList<TreeItem<VcsFileRevision>>();
TreeItem<VcsFileRevision> prevRevision = null;
for (final VcsFileRevision sortedRevision : allRevisions) {
final CvsFileRevisionImpl cvsFileRevision = (CvsFileRevisionImpl)sortedRevision;
final TreeItem<VcsFileRevision> treeItem = new TreeItem<VcsFileRevision>(cvsFileRevision);
final TreeItem<VcsFileRevision> commonParent = getCommonParent(prevRevision, treeItem);
if (commonParent != null) {
commonParent.addChild(treeItem);
}
else {
result.add(treeItem);
}
prevRevision = treeItem;
}
return result;
}
@Nullable
private static TreeItem<VcsFileRevision> getCommonParent(TreeItem<VcsFileRevision> prevRevision, TreeItem<VcsFileRevision> cvsFileRevision) {
if (prevRevision == null) return null;
while (!isParent(prevRevision, cvsFileRevision)) {
prevRevision = prevRevision.getParent();
}
return prevRevision;
}
private static boolean isParent(TreeItem<VcsFileRevision> prevRevision, TreeItem<VcsFileRevision> cvsFileRevision) {
if (prevRevision == null) return true;
final CvsFileRevisionImpl prevData = (CvsFileRevisionImpl)prevRevision.getData();
final CvsFileRevisionImpl data = (CvsFileRevisionImpl)cvsFileRevision.getData();
return data.getRevisionNumber().asString().startsWith(prevData.getRevisionNumber().asString());
}
}
private static class VcsFileRevisionComparator implements Comparator<VcsFileRevision> {
public static final VcsFileRevisionComparator INSTANCE = new VcsFileRevisionComparator();
private VcsFileRevisionComparator() {}
public int compare(VcsFileRevision rev1, VcsFileRevision rev2) {
return VcsHistoryUtil.compare(rev1, rev2);
}
}
}