blob: 7fce03978be72004bfa93736f6898dea1bd25076 [file] [log] [blame]
/*
* Copyright 2000-2011 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.cvsoperations.common;
import com.intellij.CvsBundle;
import com.intellij.cvsSupport2.CvsUtil;
import com.intellij.cvsSupport2.application.CvsEntriesManager;
import com.intellij.cvsSupport2.config.CvsApplicationLevelConfiguration;
import com.intellij.cvsSupport2.connections.CvsEnvironment;
import com.intellij.cvsSupport2.connections.CvsRootProvider;
import com.intellij.cvsSupport2.connections.pserver.PServerCvsSettings;
import com.intellij.cvsSupport2.cvsIgnore.IgnoreFileFilterBasedOnCvsEntriesManager;
import com.intellij.cvsSupport2.cvsoperations.cvsMessages.CvsMessagesListener;
import com.intellij.cvsSupport2.cvsoperations.cvsMessages.CvsMessagesTranslator;
import com.intellij.cvsSupport2.cvsoperations.cvsMessages.FileMessage;
import com.intellij.cvsSupport2.cvsoperations.javacvsSpecificImpls.AdminReaderOnCache;
import com.intellij.cvsSupport2.cvsoperations.javacvsSpecificImpls.AdminWriterOnCache;
import com.intellij.cvsSupport2.errorHandling.CannotFindCvsRootException;
import com.intellij.cvsSupport2.errorHandling.CvsException;
import com.intellij.cvsSupport2.javacvsImpl.FileReadOnlyHandler;
import com.intellij.cvsSupport2.javacvsImpl.ProjectContentInfoProvider;
import com.intellij.cvsSupport2.javacvsImpl.io.*;
import com.intellij.cvsSupport2.util.CvsVfsUtil;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.netbeans.lib.cvsclient.ClientEnvironment;
import org.netbeans.lib.cvsclient.IClientEnvironment;
import org.netbeans.lib.cvsclient.IRequestProcessor;
import org.netbeans.lib.cvsclient.RequestProcessor;
import org.netbeans.lib.cvsclient.admin.Entry;
import org.netbeans.lib.cvsclient.admin.IAdminReader;
import org.netbeans.lib.cvsclient.admin.IAdminWriter;
import org.netbeans.lib.cvsclient.command.*;
import org.netbeans.lib.cvsclient.command.update.UpdateFileInfo;
import org.netbeans.lib.cvsclient.connection.AuthenticationException;
import org.netbeans.lib.cvsclient.connection.IConnection;
import org.netbeans.lib.cvsclient.event.*;
import org.netbeans.lib.cvsclient.file.FileObject;
import org.netbeans.lib.cvsclient.file.IFileSystem;
import org.netbeans.lib.cvsclient.file.ILocalFileReader;
import org.netbeans.lib.cvsclient.file.ILocalFileWriter;
import org.netbeans.lib.cvsclient.progress.IProgressViewer;
import org.netbeans.lib.cvsclient.progress.RangeProgressViewer;
import org.netbeans.lib.cvsclient.util.IIgnoreFileFilter;
import java.io.File;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
public abstract class CvsCommandOperation extends CvsOperation implements IFileInfoListener,
IMessageListener,
IEntryListener,
IModuleExpansionListener,
ProjectContentInfoProvider {
private static final IAdminReader DEFAULT_ADMIN_READER = new AdminReaderOnCache();
protected final IAdminReader myAdminReader;
protected final IAdminWriter myAdminWriter;
protected static final Logger LOG = Logger.getInstance("#com.intellij.cvsSupport2.cvsoperations.common.CvsCommandOperation");
private String myLastProcessedCvsRoot;
private final UpdatedFilesManager myUpdatedFilesManager = new UpdatedFilesManager();
protected CvsCommandOperation() {
this(DEFAULT_ADMIN_READER);
}
protected CvsCommandOperation(IAdminReader adminReader, IAdminWriter adminWriter) {
myAdminReader = adminReader;
myAdminWriter = adminWriter;
}
protected CvsCommandOperation(IAdminReader adminReader) {
myAdminReader = adminReader;
myAdminWriter = new AdminWriterOnCache(myUpdatedFilesManager, this);
}
abstract protected Command createCommand(CvsRootProvider root,
CvsExecutionEnvironment cvsExecutionEnvironment);
@Override
public void execute(CvsExecutionEnvironment executionEnvironment, boolean underReadAction) throws VcsException, CommandAbortedException {
if (runInExclusiveLock()) {
synchronized (CvsOperation.class) {
doExecute(executionEnvironment, underReadAction);
}
}
else {
doExecute(executionEnvironment, underReadAction);
}
}
@Override
public void appendSelfCvsRootProvider(@NotNull Collection<CvsEnvironment> roots) throws CannotFindCvsRootException {
roots.addAll(getAllCvsRoots());
}
private void doExecute(final CvsExecutionEnvironment executionEnvironment, boolean underReadAction) throws VcsException {
final VcsException[] exc = new VcsException[1];
final Runnable action = new Runnable() {
@Override
public void run() {
try {
final ReadWriteStatistics statistics = executionEnvironment.getReadWriteStatistics();
final Collection<CvsRootProvider> allCvsRoots;
try {
allCvsRoots = getAllCvsRoots();
}
catch (CannotFindCvsRootException e) {
throw createVcsExceptionOn(e, null);
}
final IProgressViewer progressViewer = new IProgressViewer() {
@Override
public void setProgress(double value) {
final ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator();
if (progressIndicator != null) progressIndicator.setFraction(value);
}
};
int count = 0;
final double step = 1.0 / allCvsRoots.size();
for (CvsRootProvider cvsRootProvider : allCvsRoots) {
try {
final double lowerBound = step * count;
final RangeProgressViewer partialProgress = new RangeProgressViewer(progressViewer, lowerBound, lowerBound + step);
myLastProcessedCvsRoot = cvsRootProvider.getCvsRootAsString();
execute(cvsRootProvider, executionEnvironment, statistics, partialProgress);
count++;
}
catch (IOCommandException e) {
LOG.info(e);
throw createVcsExceptionOn(e.getIOException(), cvsRootProvider.getCvsRootAsString());
}
catch (CommandException e) {
LOG.info(e);
final Exception underlyingException = e.getUnderlyingException();
if (underlyingException != null) {
LOG.info(underlyingException);
}
throw createVcsExceptionOn(underlyingException == null ? e : underlyingException, cvsRootProvider.getCvsRootAsString());
}
}
}
catch (VcsException e) {
exc[0] = e;
}
}
};
if (underReadAction) {
ApplicationManager.getApplication().runReadAction(action);
} else {
action.run();
}
if (exc[0] != null) throw exc[0];
}
private static VcsException createVcsExceptionOn(Exception e, String cvsRoot) {
LOG.debug(e);
final String message = getMessageFrom(null, e);
if (message == null) {
return new CvsException(CvsBundle.message("exception.text.unknown.error"), e, cvsRoot);
}
return new CvsException(message, e, cvsRoot);
}
private static String getMessageFrom(String initialMessage, Throwable e) {
if (e == null) return initialMessage;
String result = initialMessage;
if (result != null && !result.isEmpty()) return result;
result = e.getLocalizedMessage();
if (result == null || result.isEmpty()) result = e.getMessage();
return getMessageFrom(result, e.getCause());
}
protected abstract Collection<CvsRootProvider> getAllCvsRoots() throws CannotFindCvsRootException;
protected void execute(CvsRootProvider root,
final CvsExecutionEnvironment executionEnvironment,
ReadWriteStatistics statistics, IProgressViewer progressViewer)
throws CommandException, VcsException {
final IConnection connection = root.createConnection(statistics);
execute(root, executionEnvironment, connection, progressViewer);
}
public void execute(final CvsRootProvider root,
final CvsExecutionEnvironment executionEnvironment,
IConnection connection, IProgressViewer progressViewer) throws CommandException {
final Command command = createCommand(root, executionEnvironment);
if (command == null) return;
LOG.assertTrue(connection != null, root.getCvsRootAsString());
final CvsMessagesListener cvsMessagesListener = executionEnvironment.getCvsMessagesListener();
final long start = System.currentTimeMillis();
try {
final IClientEnvironment clientEnvironment = createEnvironment(connection, root,
myUpdatedFilesManager,
executionEnvironment);
myUpdatedFilesManager.setCvsFileSystem(clientEnvironment.getCvsFileSystem());
final EventManager eventManager = new EventManager(CvsApplicationLevelConfiguration.getCharset());
final IGlobalOptions globalOptions = command.getGlobalOptions();
final IRequestProcessor requestProcessor = new RequestProcessor(clientEnvironment,
globalOptions,
eventManager,
new StreamLogger(),
executionEnvironment.getCvsCommandStopper(),
PServerCvsSettings.getTimeoutMillis());
eventManager.addFileInfoListener(this);
eventManager.addEntryListener(this);
eventManager.addMessageListener(this);
eventManager.addModuleExpansionListener(this);
final CvsMessagesTranslator cvsMessagesTranslator = new CvsMessagesTranslator(cvsMessagesListener,
clientEnvironment.getCvsFileSystem(),
myUpdatedFilesManager,
root.getCvsRootAsString());
cvsMessagesTranslator.registerTo(eventManager);
final CvsEntriesManager cvsEntriesManager = CvsEntriesManager.getInstance();
if (shouldMakeChangesOnTheLocalFileSystem()) {
eventManager.addEntryListener(new MergeSupportingEntryListener(clientEnvironment, cvsEntriesManager, myUpdatedFilesManager));
eventManager.addMessageListener(myUpdatedFilesManager);
}
modifyOptions(command.getGlobalOptions());
final String commandString = composeCommandString(root, command);
cvsMessagesListener.commandStarted(commandString);
setProgressText(CvsBundle.message("progress.text.command.running.for.file", getOperationName(), root.getCvsRootAsString()));
try {
command.execute(requestProcessor, eventManager, eventManager, clientEnvironment, progressViewer);
}
catch (AuthenticationException e) {
throw root.processException(new CommandException(e, "Authentication problem"));
}
cvsMessagesTranslator.operationCompleted();
}
catch (CommandException t) {
throw root.processException(t);
}
finally {
cvsMessagesListener.commandFinished(composeCommandString(root, command),
System.currentTimeMillis() - start);
executeFinishActions();
}
}
@NonNls protected abstract String getOperationName();
@SuppressWarnings({"HardCodedStringLiteral"})
private static String composeCommandString(CvsRootProvider root, Command command) {
final StringBuilder result = new StringBuilder();
result.append(root.getLocalRoot());
result.append(" cvs ");
final GlobalOptions globalOptions = command.getGlobalOptions();
if (globalOptions.isCheckedOutFilesReadOnly()) result.append("-r ");
if (globalOptions.isDoNoChanges()) result.append("-n ");
if (globalOptions.isNoHistoryLogging()) result.append("-l ");
if (globalOptions.isSomeQuiet()) result.append("-q ");
result.append(command.getCvsCommandLine());
return result.toString();
}
protected boolean shouldMakeChangesOnTheLocalFileSystem() {
return true;
}
protected boolean runInExclusiveLock() {
return true;
}
private static void setProgressText(String text) {
final ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator();
if (progressIndicator != null) progressIndicator.setText(text);
}
private IClientEnvironment createEnvironment(IConnection connection,
final CvsRootProvider root,
UpdatedFilesManager mergedFilesCollector,
CvsExecutionEnvironment cvsExecutionEnv) {
final File localRoot = getLocalRootFor(root);
final File adminRoot = getAdminRootFor(root);
LOG.assertTrue(localRoot != null, getClass().getName());
LOG.assertTrue(adminRoot != null, getClass().getName());
return new ClientEnvironment(connection,
localRoot, adminRoot, root.getCvsRoot(),
createLocalFileReader(),
createLocalFileWriter(root.getCvsRootAsString(),
mergedFilesCollector,
cvsExecutionEnv),
myAdminReader, myAdminWriter,
getIgnoreFileFilter(), new FileReadOnlyHandler(),
CvsApplicationLevelConfiguration.getCharset());
}
protected ILocalFileWriter createLocalFileWriter(String cvsRoot,
UpdatedFilesManager mergedFilesCollector,
CvsExecutionEnvironment cvsExecutionEnvironment) {
return new StoringLineSeparatorsLocalFileWriter(new ReceiveTextFilePreprocessor(createReceivedFileProcessor(mergedFilesCollector,
cvsExecutionEnvironment.getPostCvsActivity())),
cvsExecutionEnvironment.getErrorProcessor(),
myUpdatedFilesManager,
cvsRoot,
this);
}
protected ReceivedFileProcessor createReceivedFileProcessor(UpdatedFilesManager mergedFilesCollector, PostCvsActivity postCvsActivity) {
return ReceivedFileProcessor.DEFAULT;
}
protected ILocalFileReader createLocalFileReader() {
return new LocalFileReaderBasedOnVFS(new SendTextFilePreprocessor(), this);
}
protected IIgnoreFileFilter getIgnoreFileFilter() {
return new IgnoreFileFilterBasedOnCvsEntriesManager();
}
protected File getAdminRootFor(CvsRootProvider root) {
return root.getAdminRoot();
}
protected File getLocalRootFor(CvsRootProvider root) {
return root.getLocalRoot();
}
@Override
public void fileInfoGenerated(Object info) {
if (info instanceof UpdateFileInfo) {
final UpdateFileInfo updateFileInfo = ((UpdateFileInfo)info);
if (FileMessage.CONFLICT.equals(updateFileInfo.getType())) {
CvsUtil.addConflict(updateFileInfo.getFile());
}
}
}
@Override
public void gotEntry(FileObject fileObject, Entry entry) {
}
@Override
public void messageSent(String message, final byte[] byteMessage, boolean error, boolean tagged) {
}
@Override
public void moduleExpanded(String module) {
}
@Override
public boolean fileIsUnderProject(final VirtualFile file) {
return true;
}
@Override
public boolean fileIsUnderProject(File file) {
return true;
}
@Override
public String getLastProcessedCvsRoot() {
return myLastProcessedCvsRoot;
}
private static class MergeSupportingEntryListener implements IEntryListener {
private final IClientEnvironment myClientEnvironment;
private final CvsEntriesManager myCvsEntriesManager;
private final UpdatedFilesManager myUpdatedFilesManager;
private final Map<File, Entry> myFileToPreviousEntryMap = new HashMap<File, Entry>();
public MergeSupportingEntryListener(IClientEnvironment clientEnvironment,
CvsEntriesManager cvsEntriesManager,
UpdatedFilesManager mergedFilesCollector) {
myClientEnvironment = clientEnvironment;
myCvsEntriesManager = cvsEntriesManager;
myUpdatedFilesManager = mergedFilesCollector;
}
@Override
public void gotEntry(FileObject fileObject, Entry entry) {
final IFileSystem localFileSystem = myClientEnvironment.getCvsFileSystem().getLocalFileSystem();
final File file = localFileSystem.getFile(fileObject);
if (myUpdatedFilesManager.fileIsNotUpdated(file)) {
return;
}
final File parent = file.getParentFile();
final VirtualFile virtualParent = CvsVfsUtil.findFileByIoFile(parent);
if (entry != null) {
final Entry previousEntry = myFileToPreviousEntryMap.containsKey(file)
?
myFileToPreviousEntryMap.get(file)
: CvsEntriesManager.getInstance().getCachedEntry(virtualParent, entry.getFileName());
if (previousEntry != null) {
myFileToPreviousEntryMap.put(file, previousEntry);
if (entry.isResultOfMerge()) {
final UpdatedFilesManager.CurrentMergedFileInfo info = myUpdatedFilesManager.getInfo(file);
info.registerNewRevision(previousEntry);
CvsUtil.saveRevisionForMergedFile(virtualParent,
info.getOriginalEntry(),
info.getRevisions());
}
}
}
else {
myCvsEntriesManager.removeEntryForFile(parent, fileObject.getName());
}
if (entry != null) {
myCvsEntriesManager.setEntryForFile(virtualParent, entry);
}
if (entry == null || !entry.isResultOfMerge()) {
CvsUtil.removeConflict(file);
}
}
}
@Override
public void binaryMessageSent(final byte[] bytes) {
}
}