blob: 690ec0e1fb13a7789ffec44d5ad5d1d9d363d259 [file] [log] [blame]
/*
* 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.jetbrains.idea.svn;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.FilePathImpl;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Processor;
import com.intellij.vcsUtil.VcsUtil;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.commandLine.SvnCommandLineStatusClient;
import org.jetbrains.idea.svn.portable.JavaHLSvnStatusClient;
import org.jetbrains.idea.svn.portable.SvnStatusClientI;
import org.jetbrains.idea.svn.portable.SvnkitSvnStatusClient;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNNodeKind;
import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;
import org.tmatesoft.svn.core.wc.*;
import java.io.File;
import java.util.LinkedList;
public class SvnRecursiveStatusWalker {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.SvnRecursiveStatusWalker");
private final StatusWalkerPartner myPartner;
private final SvnVcs myVcs;
private final Project myProject;
private final StatusReceiver myReceiver;
private final LinkedList<MyItem> myQueue;
private final MyHandler myHandler;
public SvnRecursiveStatusWalker(final SvnVcs vcs, final StatusReceiver receiver, final StatusWalkerPartner partner) {
myVcs = vcs;
myProject = vcs.getProject();
myReceiver = receiver;
myPartner = partner;
myQueue = new LinkedList<MyItem>();
myHandler = new MyHandler();
}
public void go(final FilePath rootPath, final SVNDepth depth) throws SVNException {
final MyItem root = new MyItem(myVcs, rootPath, depth, myPartner.createStatusClient(), false);
myQueue.add(root);
while (! myQueue.isEmpty()) {
myPartner.checkCanceled();
final MyItem item = myQueue.removeFirst();
final FilePath path = item.getPath();
final File ioFile = path.getIOFile();
if (path.isDirectory()) {
myHandler.setCurrentItem(item);
try {
final SvnStatusClientI client = item.getClient(ioFile);
client.doStatus(ioFile, SVNRevision.WORKING, item.getDepth(), false, false, true, true, myHandler, null);
myHandler.checkIfCopyRootWasReported(null, ioFile);
}
catch (SVNException e) {
handleStatusException(item, path, e);
}
} else {
try {
final SVNStatus status = item.getClient(ioFile).doStatus(ioFile, false, false);
myReceiver.process(path, status);
} catch (SVNException e) {
handleStatusException(item, path, e);
}
}
}
}
private void handleStatusException(MyItem item, FilePath path, SVNException e) throws SVNException {
final SVNErrorCode errorCode = e.getErrorMessage().getErrorCode();
if (SVNErrorCode.WC_NOT_DIRECTORY.equals(errorCode) || SVNErrorCode.WC_NOT_FILE.equals(errorCode)) {
final VirtualFile virtualFile = path.getVirtualFile();
if (virtualFile != null) {
if (! myPartner.isExcluded(virtualFile)) {
// self is unversioned
myReceiver.processUnversioned(virtualFile);
if (virtualFile.isDirectory()) {
processRecursively(virtualFile, item.getDepth());
}
}
}
} else {
throw e;
}
}
private static class MyItem {
private final Project myProject;
private final FilePath myPath;
private final SVNDepth myDepth;
private final SvnStatusClientI mySvnClient;
private final SvnStatusClientI myCommandLineClient;
private final boolean myIsInnerCopyRoot;
private final SvnConfiguration myConfiguration17;
private final SvnVcs myVcs;
private MyItem(SvnVcs vcs, FilePath path, SVNDepth depth, SVNStatusClient client, boolean isInnerCopyRoot) {
myVcs = vcs;
myProject = vcs.getProject();
myConfiguration17 = SvnConfiguration.getInstance(myProject);
myPath = path;
myDepth = depth;
mySvnClient = new SvnkitSvnStatusClient(myVcs, client);
myCommandLineClient = new SvnCommandLineStatusClient(myVcs);
myIsInnerCopyRoot = isInnerCopyRoot;
}
public FilePath getPath() {
return myPath;
}
public SVNDepth getDepth() {
return myDepth;
}
public SvnStatusClientI getClient(final File file) {
// TODO: refactor to ClientFactory usage but carefully save all parameters passed in myClient - fileProvider and
// TODO: event handler (for cancel support)
WorkingCopyFormat format = myVcs.getWorkingCopyFormat(file);
if (format == WorkingCopyFormat.ONE_DOT_EIGHT) {
return myCommandLineClient;
}
if (format == WorkingCopyFormat.ONE_DOT_SIX) {
return mySvnClient;
}
// check format
if (CheckJavaHL.isPresent() && SvnConfiguration.UseAcceleration.javaHL.equals(myConfiguration17.myUseAcceleration) &&
Svn17Detector.is17(myProject, file)) {
return new JavaHLSvnStatusClient(myProject);
} else if (myConfiguration17.isCommandLine()) {
// apply command line disregarding working copy format
return myCommandLineClient;
}
return mySvnClient;
}
public boolean isIsInnerCopyRoot() {
return myIsInnerCopyRoot;
}
}
private void processRecursively(final VirtualFile vFile, final SVNDepth prevDepth) {
if (SVNDepth.EMPTY.equals(prevDepth)) return;
if (myPartner.isIgnoredIdeaLevel(vFile)) {
myReceiver.processIgnored(vFile);
return;
}
final SVNDepth newDepth = SVNDepth.INFINITY.equals(prevDepth) ? SVNDepth.INFINITY : SVNDepth.EMPTY;
final File ioFile = new File(vFile.getPath());
final Processor<File> processor;
final Processor<File> directoryFilter;
final Ref<File> lastIgnored = new Ref<File>();
final Processor<File> checkDirProcessor = new Processor<File>() {
@Override
public boolean process(File file) {
final FilePathImpl path = new FilePathImpl(file, true);
path.refresh();
path.hardRefresh();
VirtualFile vf = path.getVirtualFile();
if (vf != null && myPartner.isIgnoredIdeaLevel(vf)) {
lastIgnored.set(file);
myReceiver.processIgnored(vf);
return true;
}
if (file.isDirectory() && new File(file, SVNFileUtil.getAdminDirectoryName()).exists()) {
final MyItem childItem = new MyItem(myVcs, path, newDepth, myPartner.createStatusClient(), true);
myQueue.add(childItem);
} else if (vf != null) {
myReceiver.processUnversioned(vf);
}
return true;
}
};
if (SVNDepth.EMPTY.equals(newDepth)) {
directoryFilter = Processor.TRUE;
processor = new Processor<File>() {
@Override
public boolean process(File file) {
// here we deal only with immediate children - so ignored on IDEA level for children is not important - we nevertheless do not go into
// other levels
if (! FileUtil.filesEqual(ioFile, file)) return true;
if (! FileUtil.filesEqual(ioFile, file.getParentFile())) return false;
return checkDirProcessor.process(file);
}
};
} else {
directoryFilter = new Processor<File>() {
@Override
public boolean process(File file) {
return ! Comparing.equal(lastIgnored, file) && (myQueue.isEmpty() || ! FileUtil.filesEqual(myQueue.getLast().getPath().getIOFile(), file));
}
};
processor = checkDirProcessor;
}
FileUtil.processFilesRecursively(ioFile, processor, directoryFilter);
}
private class MyHandler implements ISVNStatusHandler {
private MyItem myCurrentItem;
private boolean myMetCurrentItem;
public void setCurrentItem(MyItem currentItem) {
myCurrentItem = currentItem;
myMetCurrentItem = false;
}
public void checkIfCopyRootWasReported(@Nullable final SVNStatus ioFileStatus, final File ioFile) {
File itemFile = myCurrentItem.getPath().getIOFile();
if (! myMetCurrentItem && FileUtil.filesEqual(ioFile, itemFile)) {
myMetCurrentItem = true;
SVNStatus statusInner;
try {
statusInner = ioFileStatus != null ? ioFileStatus :
myCurrentItem.getClient(itemFile).doStatus(itemFile, false);
}
catch (SVNException e) {
LOG.info(e);
statusInner = null;
}
if (statusInner == null) return;
final SVNStatusType status = statusInner.getNodeStatus();
final VirtualFile vf = myCurrentItem.getPath().getVirtualFile();
if (SVNStatusType.STATUS_IGNORED.equals(status)) {
if (vf != null) {
myReceiver.processIgnored(vf);
}
return;
}
if (SVNStatusType.STATUS_UNVERSIONED.equals(status) || SVNStatusType.UNKNOWN.equals(status)) {
if (vf != null) {
myReceiver.processUnversioned(vf);
processRecursively(vf, myCurrentItem.getDepth());
}
return;
}
if (SVNStatusType.OBSTRUCTED.equals(status) || SVNStatusType.STATUS_NONE.equals(status)) {
return;
}
if (vf != null) {
if (myCurrentItem.isIsInnerCopyRoot()) {
myReceiver.processCopyRoot(vf, statusInner.getURL(),
WorkingCopyFormat.getInstance(statusInner.getWorkingCopyFormat()), statusInner.getRepositoryRootURL());
} else {
myReceiver.bewareRoot(vf, statusInner.getURL(), WorkingCopyFormat.getInstance(statusInner.getWorkingCopyFormat()));
}
}
}
}
public void handleStatus(final SVNStatus status) throws SVNException {
myPartner.checkCanceled();
final File ioFile = status.getFile();
checkIfCopyRootWasReported(status, ioFile);
final VirtualFile vFile = getVirtualFile(ioFile);
if (vFile != null) {
final Boolean excluded = ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() {
@Override
public Boolean compute() {
if (myProject.isDisposed()) return null;
return myPartner.isExcluded(vFile);
}
});
if (Boolean.TRUE.equals(excluded)) return;
}
if (myProject.isDisposed()) throw new ProcessCanceledException();
if ((vFile != null) && (SvnVcs.svnStatusIsUnversioned(status))) {
if (vFile.isDirectory()) {
if (FileUtil.filesEqual(myCurrentItem.getPath().getIOFile(), ioFile)) {
//myReceiver.processUnversioned(vFile);
//processRecursively(vFile, myCurrentItem.getDepth());
} else {
final MyItem childItem = new MyItem(myVcs, new FilePathImpl(vFile), SVNDepth.INFINITY,
myPartner.createStatusClient(), true);
myQueue.add(childItem);
}
} else {
myReceiver.processUnversioned(vFile);
}
} else {
final FilePath path = VcsUtil.getFilePath(ioFile, status.getKind().equals(SVNNodeKind.DIR));
myReceiver.process(path, status);
}
}
}
private VirtualFile getVirtualFile(File ioFile) {
final LocalFileSystem lfs = LocalFileSystem.getInstance();
VirtualFile vFile = lfs.findFileByIoFile(ioFile);
if (vFile == null) {
vFile = lfs.refreshAndFindFileByIoFile(ioFile);
}
return vFile;
}
}