/*
* Copyright 2000-2009 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 org.community.intellij.plugins.communitycase.annotate;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.annotate.FileAnnotation;
import com.intellij.openapi.vcs.history.VcsFileRevision;
import com.intellij.openapi.vcs.history.VcsRevisionNumber;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.vcsUtil.VcsUtil;
import org.community.intellij.plugins.communitycase.FileRevision;
import org.community.intellij.plugins.communitycase.Util;
import org.community.intellij.plugins.communitycase.commands.Command;
import org.community.intellij.plugins.communitycase.commands.SimpleHandler;
import org.community.intellij.plugins.communitycase.commands.StringScanner;
import org.community.intellij.plugins.communitycase.history.HistoryUtils;
import org.community.intellij.plugins.communitycase.i18n.Bundle;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
/**
* annotation provider implementation.
* <p/>
* Based on the JetBrains SVNAnnotationProvider.
*/
public class IntellijAnnotationProvider implements com.intellij.openapi.vcs.annotate.AnnotationProvider {
/**
* the context project
*/
private final Project myProject;
/**
* The author key for annotations
*/
@NonNls private static final String AUTHOR_KEY = "author";
/**
* The committer time key for annotations
*/
@NonNls private static final String COMMITTER_TIME_KEY = "committer-time";
/**
* A constructor
*
* @param project a context project
*/
public IntellijAnnotationProvider(@NotNull Project project) {
myProject = project;
}
/**
* {@inheritDoc}
*/
public FileAnnotation annotate(@NotNull VirtualFile file) throws VcsException {
return annotate(file, null);
}
/**
* {@inheritDoc}
*/
public FileAnnotation annotate(@NotNull final VirtualFile file, final VcsFileRevision revision) throws VcsException {
if (file.isDirectory()) {
throw new VcsException("Cannot annotate a directory");
}
final FileAnnotation[] annotation = new FileAnnotation[1];
final Exception[] exception = new Exception[1];
Runnable command = new Runnable() {
public void run() {
final ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator();
try {
final FilePath currentFilePath = VcsUtil.getFilePath(file.getPath());
final FilePath realFilePath;
if (progress != null) {
progress.setText(Bundle.message("getting.history", file.getName()));
}
final List<VcsFileRevision> revisions = HistoryUtils.history(myProject, currentFilePath);
if (revision == null) {
realFilePath = HistoryUtils.getLastCommitName(myProject, currentFilePath);
}
else {
realFilePath = ((FileRevision)revision).getPath();
}
if (progress != null) {
progress.setText(Bundle.message("computing.annotation", file.getName()));
}
final com.intellij.openapi.vcs.annotate.FileAnnotation result = annotate(realFilePath, revision, revisions, file);
annotation[0] = result;
}
catch (Exception e) {
exception[0] = e;
}
}
};
if (ApplicationManager.getApplication().isDispatchThread()) {
ProgressManager.getInstance()
.runProcessWithProgressSynchronously(command, Bundle.getString("annotate.action.name"), false, myProject);
}
else {
command.run();
}
if (exception[0] != null) {
throw new VcsException("Failed to annotate: " + exception[0], exception[0]);
}
return annotation[0];
}
/**
* Calculate annotations
*
* @param repositoryFilePath the file path in the repository
* @param revision the revision to checkout
* @param revisions the revision list from history
* @param file a virtual file for the action
* @return a file annotation object
* @throws VcsException if there is a problem with running
*/
private FileAnnotation annotate(final FilePath repositoryFilePath,
final VcsFileRevision revision,
final List<VcsFileRevision> revisions,
final VirtualFile file) throws VcsException {
SimpleHandler h = new SimpleHandler(myProject, Util.getRoot(repositoryFilePath), Command.ANNOTATE);
h.setRemote(true);
h.setStdoutSuppressed(true);
h.setCharset(file.getCharset());
h.addParameters("-p", "-l", "-t", "-M");
if (revision == null) {
h.addParameters("HEAD");
}
else {
h.addParameters(revision.getRevisionNumber().asString());
}
h.endOptions();
h.addRelativePaths(repositoryFilePath);
String output = h.run();
IntellijFileAnnotation annotation = new IntellijFileAnnotation(myProject, file, revision == null);
class CommitInfo {
Date date;
String author;
VcsRevisionNumber revision;
}
HashMap<String, CommitInfo> commits = new HashMap<String, CommitInfo>();
for (StringScanner s = new StringScanner(output); s.hasMoreData();) {
// parse header line
String commitHash = s.spaceToken();
if (commitHash.equals(HistoryUtils.createUnvalidatedRevisionNumber("NOT_COMMITTED_HASH"))) {
commitHash = null;
}
s.spaceToken(); // skip revision line number
int lineNum = Integer.parseInt(s.spaceToken());
s.nextLine();
// parse commit information
CommitInfo commit = commits.get(commitHash);
if (commit != null) {
while (s.hasMoreData() && !s.startsWith('\t')) {
s.nextLine();
}
}
else {
commit = new CommitInfo();
while (s.hasMoreData() && !s.startsWith('\t')) {
String key = s.spaceToken();
String value = s.line();
if (commitHash != null && AUTHOR_KEY.equals(key)) {
commit.author = value;
}
if (commitHash != null && COMMITTER_TIME_KEY.equals(key)) {
commit.date = Util.parseTimestamp(value);
commit.revision=HistoryUtils.createUnvalidatedRevisionNumber(commitHash);
}
}
commits.put(commitHash, commit);
}
// parse line
if (!s.hasMoreData()) {
// if the file is empty, the next line will not start with tab and it will be
// empty.
continue;
}
s.skipChars(1);
String line = s.line(true);
annotation.appendLineInfo(commit.date, commit.revision, commit.author, line, lineNum);
}
annotation.addLogEntries(revisions);
return annotation;
}
/**
* {@inheritDoc}
*/
public boolean isAnnotationValid(VcsFileRevision rev) {
return true;
}
}