blob: 40cde8406cda68f03f90cf192ac962d647ccd0c0 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.netbeans.modules.subversion;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.logging.Level;
import javax.swing.AbstractAction;
import javax.swing.Action;
import org.netbeans.modules.subversion.client.SvnClient;
import org.netbeans.modules.subversion.client.SvnClientExceptionHandler;
import org.netbeans.modules.subversion.client.SvnProgressSupport;
import org.netbeans.modules.subversion.ui.history.SearchHistoryAction;
import org.netbeans.modules.subversion.util.SvnUtils;
import org.netbeans.modules.versioning.history.HistoryAction;
import org.netbeans.modules.versioning.spi.VCSHistoryProvider;
import org.netbeans.modules.versioning.util.FileUtils;
import org.openide.util.*;
import org.tigris.subversion.svnclientadapter.*;
/**
*
* @author Tomas Stupka
*/
public class HistoryProvider implements VCSHistoryProvider {
private final List<VCSHistoryProvider.HistoryChangeListener> listeners = new LinkedList<VCSHistoryProvider.HistoryChangeListener>();
private Action[] actions;
@Override
public void addHistoryChangeListener(VCSHistoryProvider.HistoryChangeListener l) {
synchronized(listeners) {
listeners.add(l);
}
}
@Override
public void removeHistoryChangeListener(VCSHistoryProvider.HistoryChangeListener l) {
synchronized(listeners) {
listeners.remove(l);
}
}
@Override
public HistoryEntry[] getHistory(File[] files, Date fromDate) {
try {
SvnClient client = Subversion.getInstance().getClient(files[0]);
List<HistoryEntry> ret = new LinkedList<HistoryEntry>();
Map<String, Set<File>> rev2FileMap = new HashMap<String, Set<File>>();
Map<String, ISVNLogMessage> rev2LMMap = new HashMap<String, ISVNLogMessage>();
Map<File, SVNUrl> file2Copy = new HashMap<File, SVNUrl>();
SVNUrl repoUrl = null;
for (File file : files) {
FileInformation fi = Subversion.getInstance().getStatusCache().getStatus(file);
if ((fi.getStatus() & FileInformation.STATUS_VERSIONED) == 0) {
continue;
}
ISVNLogMessage[] messages = null;
if ((fi.getStatus() == FileInformation.STATUS_VERSIONED_ADDEDLOCALLY) &&
fi.getEntry(file).isCopied())
{
ISVNInfo info = SvnUtils.getInfoFromWorkingCopy(client, file);
SVNUrl copyUrl = info.getCopyUrl();
repoUrl = info.getRepository();
if (copyUrl != null) {
messages =
client.getLogMessages(
copyUrl,
fromDate == null ?
new SVNRevision.Number(1) :
new SVNRevision.DateSpec(fromDate),
SVNRevision.HEAD
);
file2Copy.put(file, copyUrl);
}
}
if (messages == null) {
messages =
client.getLogMessages(
file,
fromDate == null ?
new SVNRevision.Number(1) :
new SVNRevision.DateSpec(fromDate),
SVNRevision.HEAD
);
}
for (ISVNLogMessage m : messages) {
String r = m.getRevision().toString();
rev2LMMap.put(r, m);
Set<File> s = rev2FileMap.get(r);
if(s == null) {
s = new HashSet<File>();
rev2FileMap.put(r, s);
}
s.add(file);
}
}
for(ISVNLogMessage m : rev2LMMap.values()) {
Set<File> s = rev2FileMap.get(m.getRevision().toString());
File[] involvedFiles = s.toArray(new File[s.size()]);
HistoryEntry e = new HistoryEntry(
involvedFiles,
m.getDate(),
m.getMessage(),
m.getAuthor(),
m.getAuthor(),
m.getRevision().toString(),
m.getRevision().toString(),
getActions(),
new RevisionProviderImpl(m.getRevision(), repoUrl, file2Copy));
ret.add(e);
}
return ret.toArray(new HistoryEntry[ret.size()]);
} catch (SVNClientException e) {
if (SvnClientExceptionHandler.isCancelledAction(e.getMessage())) {
Subversion.LOG.log(Level.FINE, null, e);
} else {
SvnClientExceptionHandler.notifyException(e, true, true);
}
}
return null;
}
@Override
public Action createShowHistoryAction(File[] files) {
return new OpenHistoryAction(files);
}
public void fireHistoryChange(final File[] files) {
final HistoryChangeListener[] la;
synchronized(listeners) {
la = listeners.toArray(new HistoryChangeListener[listeners.size()]);
}
Subversion.getInstance().getParallelRequestProcessor().post(new Runnable() {
@Override
public void run() {
for (HistoryChangeListener l : la) {
l.fireHistoryChanged(new HistoryEvent(HistoryProvider.this, files));
}
}
});
}
private static class RevisionProviderImpl implements RevisionProvider {
private final SVNRevision revision;
private final Map<File, SVNUrl> file2Copy;
private final SVNUrl repoUrl;
public RevisionProviderImpl(SVNRevision svnRevision, SVNUrl repoUrl, Map<File, SVNUrl> file2Copy) {
this.revision = svnRevision;
this.file2Copy = file2Copy;
this.repoUrl = repoUrl;
}
@Override
public void getRevisionFile(File originalFile, File revisionFile) {
try {
File file;
SVNUrl copyUrl = repoUrl != null ? file2Copy.get(originalFile) : null;
if(copyUrl != null) {
file = VersionsCache.getInstance().getFileRevision(repoUrl, copyUrl, revision.toString(), originalFile.getName());
} else {
file = VersionsCache.getInstance().getFileRevision(originalFile, revision.toString());
}
FileUtils.copyFile(file, revisionFile); // XXX lets be faster - LH should cache that somehow ...
} catch (IOException e) {
Exception ex = e;
if (e.getCause() != null && e.getCause() instanceof SVNClientException) {
ex = (SVNClientException) e.getCause();
}
if (SvnClientExceptionHandler.isCancelledAction(ex.getMessage())) {
Subversion.LOG.log(Level.FINE, null, e);
} else {
SvnClientExceptionHandler.notifyException(ex, true, true);
}
}
}
}
private static class OpenHistoryAction extends AbstractAction {
private final File[] files;
public OpenHistoryAction(File[] files) {
this.files = files;
}
@Override
public void actionPerformed(ActionEvent e) {
openHistory(files);
}
public void openHistory(final File[] files) {
if(files == null || files.length == 0) {
return;
}
if(!org.netbeans.modules.subversion.api.Subversion.isClientAvailable(true)) {
org.netbeans.modules.subversion.Subversion.LOG.log(Level.WARNING, "Subversion client is unavailable");
return;
}
/**
* Open in AWT
*/
EventQueue.invokeLater(new Runnable() {
public void run() {
SearchHistoryAction.openHistory(files);
}
});
}
}
private synchronized Action[] getActions() {
if(actions == null) {
actions = new Action[] {
new RollbackAction(),
new ViewAction(),
new AnnotateAction()
};
}
return actions;
}
private class ViewAction extends HistoryAction {
public ViewAction() {
super(NbBundle.getMessage(SearchHistoryAction.class, "CTL_SummaryView_View"));
}
@Override
protected void perform(HistoryEntry entry, Set<File> files) {
SVNRevision.Number svnRevision = new SVNRevision.Number(Long.parseLong(entry.getRevision()));
view(svnRevision, false, files.toArray(new File[files.size()]));
}
}
private class AnnotateAction extends HistoryAction {
public AnnotateAction() {
super(NbBundle.getMessage(SearchHistoryAction.class, "CTL_SummaryView_ShowAnnotations"));
}
@Override
protected void perform(HistoryEntry entry, Set<File> files) {
SVNRevision.Number svnRevision = new SVNRevision.Number(Long.parseLong(entry.getRevision()));
view(svnRevision, true, files.toArray(new File[files.size()]));
}
}
private class RollbackAction extends HistoryAction {
@Override
protected void perform(final HistoryEntry entry, final Set<File> files) {
final File file = files.iterator().next();
SVNUrl repository;
try {
repository = SvnUtils.getRepositoryRootUrl(file);
} catch (SVNClientException ex) {
SvnClientExceptionHandler.notifyException(ex, false, false);
return;
}
RequestProcessor rp = Subversion.getInstance().getRequestProcessor(repository);
SvnProgressSupport support = new SvnProgressSupport() {
@Override
public void perform() {
try {
SVNUrl repoUrl = SvnUtils.getRepositoryRootUrl(file);
for(File file : files) {
SvnClient client = Subversion.getInstance().getClient(false);
ISVNInfo info = client.getInfo(file);
SVNUrl fileUrl = info.getUrl();
SVNRevision.Number svnRevision = new SVNRevision.Number(Long.parseLong(entry.getRevision()));
SvnUtils.rollback(file, repoUrl, fileUrl, svnRevision, false, getLogger());
}
} catch (SVNClientException ex) {
SvnClientExceptionHandler.notifyException(ex, false, false);
}
}
};
support.start(rp, repository, NbBundle.getMessage(SearchHistoryAction.class, "MSG_Rollback_Progress")); // NOI18N
}
@Override
protected boolean isMultipleHistory() {
return false;
}
@Override
public String getName() {
String rev = getRevisionShort();
if(rev == null) {
rev = ""; // NOI18N
}
return NbBundle.getMessage(SearchHistoryAction.class, "CTL_SummaryView_RollbackTo", rev); // NOI18N
}
@Override
public HelpCtx getHelpCtx() {
return null;
}
}
private void view(final SVNRevision revision, final boolean showAnnotations, final File... files) {
if(files == null || files.length == 0) {
return;
}
Subversion.getInstance().getParallelRequestProcessor().post(new Runnable() {
@Override
public void run() {
try {
SVNUrl repoUrl = SvnUtils.getRepositoryRootUrl(files[0]);
for (File file : files) {
SvnClient client = Subversion.getInstance().getClient(false);
ISVNInfo info = client.getInfo(file);
SVNUrl fileUrl = info.getUrl();
SvnUtils.openInRevision(file, repoUrl, fileUrl, revision, revision, showAnnotations);
}
} catch (SVNClientException ex) {
SvnClientExceptionHandler.notifyException(ex, false, false);
}
}
});
}
}