| /* |
| * 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; |
| } |
| } |