| /* |
| * 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 com.intellij.openapi.vcs.changes; |
| |
| import com.intellij.openapi.application.Application; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.fileEditor.OpenFileDescriptor; |
| import com.intellij.openapi.progress.ProcessCanceledException; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.Computable; |
| import com.intellij.openapi.util.Key; |
| import com.intellij.openapi.util.io.FileUtil; |
| import com.intellij.openapi.vcs.*; |
| import com.intellij.openapi.vcs.actions.VcsContextFactory; |
| import com.intellij.openapi.vfs.LocalFileSystem; |
| import com.intellij.openapi.vfs.VfsUtilCore; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.pom.Navigatable; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.io.File; |
| import java.util.*; |
| |
| /** |
| * @author max |
| */ |
| public class ChangesUtil { |
| private static final Key<Boolean> INTERNAL_OPERATION_KEY = Key.create("internal vcs operation"); |
| |
| private ChangesUtil() {} |
| |
| @NotNull |
| public static FilePath getFilePath(@NotNull final Change change) { |
| ContentRevision revision = change.getAfterRevision(); |
| if (revision == null) { |
| revision = change.getBeforeRevision(); |
| assert revision != null; |
| } |
| |
| return revision.getFile(); |
| } |
| |
| @Nullable |
| public static FilePath getBeforePath(@NotNull final Change change) { |
| ContentRevision revision = change.getBeforeRevision(); |
| return revision == null ? null : revision.getFile(); |
| } |
| |
| @Nullable |
| public static FilePath getAfterPath(@NotNull final Change change) { |
| ContentRevision revision = change.getAfterRevision(); |
| return revision == null ? null : revision.getFile(); |
| } |
| |
| public static AbstractVcs getVcsForChange(Change change, final Project project) { |
| return ProjectLevelVcsManager.getInstance(project).getVcsFor(getFilePath(change)); |
| } |
| |
| public static AbstractVcs getVcsForFile(VirtualFile file, Project project) { |
| return ProjectLevelVcsManager.getInstance(project).getVcsFor(file); |
| } |
| |
| public static AbstractVcs getVcsForFile(File file, Project project) { |
| return ProjectLevelVcsManager.getInstance(project).getVcsFor(VcsContextFactory.SERVICE.getInstance().createFilePathOn(file)); |
| } |
| |
| private static class Adder { |
| private final List<FilePath> myResult = new ArrayList<FilePath>(); |
| private final Set<String> myDuplicatesControlSet = new HashSet<String>(); |
| |
| public void add(final FilePath file) { |
| final String path = file.getIOFile().getAbsolutePath(); |
| if (! myDuplicatesControlSet.contains(path)) { |
| myResult.add(file); |
| myDuplicatesControlSet.add(path); |
| } |
| } |
| |
| public List<FilePath> getResult() { |
| return myResult; |
| } |
| } |
| |
| public static List<FilePath> getPaths(final Collection<Change> changes) { |
| final Adder adder = new Adder(); |
| for (Change change : changes) { |
| ContentRevision beforeRevision = change.getBeforeRevision(); |
| if (beforeRevision != null) { |
| adder.add(beforeRevision.getFile()); |
| } |
| ContentRevision afterRevision = change.getAfterRevision(); |
| if (afterRevision != null) { |
| adder.add(afterRevision.getFile()); |
| } |
| } |
| return adder.getResult(); |
| } |
| |
| public static List<File> getIoFilesFromChanges(final Collection<Change> changes) { |
| // further should contain paths |
| final List<File> result = new ArrayList<File>(); |
| for (Change change : changes) { |
| if (change.getAfterRevision() != null) { |
| final File ioFile = change.getAfterRevision().getFile().getIOFile(); |
| if (! result.contains(ioFile)) { |
| result.add(ioFile); |
| } |
| } |
| if (change.getBeforeRevision() != null) { |
| final File ioFile = change.getBeforeRevision().getFile().getIOFile(); |
| if (! result.contains(ioFile)) { |
| result.add(ioFile); |
| } |
| } |
| } |
| return result; |
| } |
| |
| public static VirtualFile[] getFilesFromChanges(final Collection<Change> changes) { |
| ArrayList<VirtualFile> files = new ArrayList<VirtualFile>(); |
| for (Change change : changes) { |
| final ContentRevision afterRevision = change.getAfterRevision(); |
| if (afterRevision != null) { |
| VirtualFile file = afterRevision.getFile().getVirtualFile(); |
| if (file != null && file.isValid()) { |
| files.add(file); |
| } else { |
| afterRevision.getFile().hardRefresh(); |
| file = afterRevision.getFile().getVirtualFile(); |
| if (file != null && file.isValid()) { |
| files.add(file); |
| } |
| } |
| } |
| } |
| return VfsUtilCore.toVirtualFileArray(files); |
| } |
| |
| public static Navigatable[] getNavigatableArray(final Project project, final VirtualFile[] selectedFiles) { |
| List<Navigatable> result = new ArrayList<Navigatable>(); |
| for (VirtualFile selectedFile : selectedFiles) { |
| if (!selectedFile.isDirectory()) { |
| result.add(new OpenFileDescriptor(project, selectedFile)); |
| } |
| } |
| return result.toArray(new Navigatable[result.size()]); |
| } |
| |
| public static boolean allChangesInOneListOrWholeListsSelected(@NotNull final Project project, @Nullable Change[] changes) { |
| final ChangeListManager clManager = ChangeListManager.getInstance(project); |
| if (clManager.getChangeListNameIfOnlyOne(changes) != null) return true; |
| final List<LocalChangeList> list = clManager.getChangeListsCopy(); |
| |
| final HashSet<Change> checkSet = new HashSet<Change>(); |
| checkSet.addAll(Arrays.asList(changes)); |
| for (LocalChangeList localChangeList : list) { |
| final Collection<Change> listChanges = localChangeList.getChanges(); |
| boolean first = true; |
| for (Change listChange : listChanges) { |
| if (! checkSet.contains(listChange)) { |
| if (! first) return false; |
| break; |
| } |
| first = false; |
| } |
| } |
| return true; |
| } |
| |
| @Nullable |
| public static ChangeList getChangeListIfOnlyOne(@NotNull final Project project, @Nullable Change[] changes) { |
| final ChangeListManager clManager = ChangeListManager.getInstance(project); |
| |
| final String name = clManager.getChangeListNameIfOnlyOne(changes); |
| return (name == null) ? null : clManager.findChangeList(name); |
| } |
| |
| public static FilePath getCommittedPath(final Project project, FilePath filePath) { |
| // check if the file has just been renamed (IDEADEV-15494) |
| Change change = ChangeListManager.getInstance(project).getChange(filePath); |
| if (change != null) { |
| final ContentRevision beforeRevision = change.getBeforeRevision(); |
| final ContentRevision afterRevision = change.getAfterRevision(); |
| if (beforeRevision != null && afterRevision != null && !beforeRevision.getFile().equals(afterRevision.getFile()) && |
| afterRevision.getFile().equals(filePath)) { |
| filePath = beforeRevision.getFile(); |
| } |
| } |
| return filePath; |
| } |
| |
| public static FilePath getLocalPath(final Project project, final FilePath filePath) { |
| // check if the file has just been renamed (IDEADEV-15494) |
| Change change = ApplicationManager.getApplication().runReadAction(new Computable<Change>() { |
| @Override |
| @Nullable |
| public Change compute() { |
| if (project.isDisposed()) throw new ProcessCanceledException(); |
| return ChangeListManager.getInstance(project).getChange(filePath); |
| } |
| }); |
| if (change != null) { |
| final ContentRevision beforeRevision = change.getBeforeRevision(); |
| final ContentRevision afterRevision = change.getAfterRevision(); |
| if (beforeRevision != null && afterRevision != null && !beforeRevision.getFile().equals(afterRevision.getFile()) && |
| beforeRevision.getFile().equals(filePath)) { |
| return afterRevision.getFile(); |
| } |
| } |
| return filePath; |
| } |
| |
| @Nullable |
| public static VirtualFile findValidParentUnderReadAction(final FilePath file) { |
| if (file.getVirtualFile() != null) return file.getVirtualFile(); |
| final Computable<VirtualFile> computable = new Computable<VirtualFile>() { |
| @Override |
| public VirtualFile compute() { |
| return findValidParent(file); |
| } |
| }; |
| final Application application = ApplicationManager.getApplication(); |
| if (application.isReadAccessAllowed()) { |
| return computable.compute(); |
| } else { |
| return application.runReadAction(computable); |
| } |
| } |
| |
| public static VirtualFile findValidParentAccurately(final FilePath filePath) { |
| if (filePath.getVirtualFile() != null) return filePath.getVirtualFile(); |
| final LocalFileSystem lfs = LocalFileSystem.getInstance(); |
| VirtualFile result = lfs.findFileByIoFile(filePath.getIOFile()); |
| if (result != null) return result; |
| if (! ApplicationManager.getApplication().isReadAccessAllowed()) { |
| result = lfs.refreshAndFindFileByIoFile(filePath.getIOFile()); |
| if (result != null) return result; |
| } |
| return getValidParentUnderReadAction(filePath); |
| } |
| |
| private static VirtualFile getValidParentUnderReadAction(final FilePath filePath) { |
| return ApplicationManager.getApplication().runReadAction(new Computable<VirtualFile>() { |
| @Override |
| public VirtualFile compute() { |
| return findValidParent(filePath); |
| } |
| }); |
| } |
| |
| /** |
| * @deprecated use {@link #findValidParentAccurately(com.intellij.openapi.vcs.FilePath)} |
| */ |
| @Nullable |
| @Deprecated |
| public static VirtualFile findValidParent(FilePath file) { |
| ApplicationManager.getApplication().assertReadAccessAllowed(); |
| VirtualFile parent = file.getVirtualFile(); |
| if (parent == null) { |
| parent = file.getVirtualFileParent(); |
| } |
| if (parent == null) { |
| File ioFile = file.getIOFile(); |
| final LocalFileSystem lfs = LocalFileSystem.getInstance(); |
| do { |
| parent = lfs.findFileByIoFile(ioFile); |
| if (parent != null) break; |
| ioFile = ioFile.getParentFile(); |
| if (ioFile == null) return null; |
| } |
| while (true); |
| } |
| return parent; |
| } |
| |
| @Nullable |
| public static String getProjectRelativePath(final Project project, @Nullable final File fileName) { |
| if (fileName == null) return null; |
| VirtualFile baseDir = project.getBaseDir(); |
| if (baseDir == null) return fileName.toString(); |
| String relativePath = FileUtil.getRelativePath(new File(baseDir.getPath()), fileName); |
| if (relativePath != null) return relativePath; |
| return fileName.toString(); |
| } |
| |
| public static boolean isBinaryContentRevision(final ContentRevision revision) { |
| return revision != null && !revision.getFile().isDirectory() && revision instanceof BinaryContentRevision; |
| } |
| |
| public static boolean isBinaryChange(final Change change) { |
| return isBinaryContentRevision(change.getBeforeRevision()) || isBinaryContentRevision(change.getAfterRevision()); |
| } |
| |
| public static boolean isTextConflictingChange(final Change change) { |
| final FileStatus status = change.getFileStatus(); |
| return FileStatus.MERGED_WITH_CONFLICTS.equals(status) || FileStatus.MERGED_WITH_BOTH_CONFLICTS.equals(status); |
| } |
| |
| public static boolean isPropertyConflictingChange(final Change change) { |
| final FileStatus status = change.getFileStatus(); |
| return FileStatus.MERGED_WITH_PROPERTY_CONFLICTS.equals(status) || FileStatus.MERGED_WITH_BOTH_CONFLICTS.equals(status); |
| } |
| |
| public interface PerVcsProcessor<T> { |
| void process(AbstractVcs vcs, List<T> items); |
| } |
| |
| public interface VcsSeparator<T> { |
| AbstractVcs getVcsFor(T item); |
| } |
| |
| public static <T> void processItemsByVcs(final Collection<T> items, final VcsSeparator<T> separator, PerVcsProcessor<T> processor) { |
| final Map<AbstractVcs, List<T>> changesByVcs = new HashMap<AbstractVcs, List<T>>(); |
| |
| ApplicationManager.getApplication().runReadAction(new Runnable() { |
| @Override |
| public void run() { |
| for (T item : items) { |
| final AbstractVcs vcs = separator.getVcsFor(item); |
| if (vcs != null) { |
| List<T> vcsChanges = changesByVcs.get(vcs); |
| if (vcsChanges == null) { |
| vcsChanges = new ArrayList<T>(); |
| changesByVcs.put(vcs, vcsChanges); |
| } |
| vcsChanges.add(item); |
| } |
| } |
| } |
| }); |
| |
| for (Map.Entry<AbstractVcs, List<T>> entry : changesByVcs.entrySet()) { |
| processor.process(entry.getKey(), entry.getValue()); |
| } |
| } |
| |
| public static void processChangesByVcs(final Project project, Collection<Change> changes, PerVcsProcessor<Change> processor) { |
| processItemsByVcs(changes, new VcsSeparator<Change>() { |
| @Override |
| public AbstractVcs getVcsFor(final Change item) { |
| return getVcsForChange(item, project); |
| } |
| }, processor); |
| } |
| |
| public static void processVirtualFilesByVcs(final Project project, Collection<VirtualFile> files, PerVcsProcessor<VirtualFile> processor) { |
| processItemsByVcs(files, new VcsSeparator<VirtualFile>() { |
| @Override |
| public AbstractVcs getVcsFor(final VirtualFile item) { |
| return getVcsForFile(item, project); |
| } |
| }, processor); |
| } |
| |
| public static void processFilePathsByVcs(final Project project, Collection<FilePath> files, PerVcsProcessor<FilePath> processor) { |
| processItemsByVcs(files, new VcsSeparator<FilePath>() { |
| @Override |
| public AbstractVcs getVcsFor(final FilePath item) { |
| return getVcsForFile(item.getIOFile(), project); |
| } |
| }, processor); |
| } |
| |
| public static List<File> filePathsToFiles(Collection<FilePath> filePaths) { |
| List<File> ioFiles = new ArrayList<File>(); |
| for(FilePath filePath: filePaths) { |
| ioFiles.add(filePath.getIOFile()); |
| } |
| return ioFiles; |
| } |
| |
| public static boolean hasFileChanges(final Collection<Change> changes) { |
| for(Change change: changes) { |
| FilePath path = getFilePath(change); |
| if (!path.isDirectory()) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| public static void markInternalOperation(Iterable<Change> changes, boolean set) { |
| for (Change change : changes) { |
| VirtualFile file = change.getVirtualFile(); |
| if (file != null) { |
| file.putUserData(INTERNAL_OPERATION_KEY, set); |
| } |
| } |
| } |
| |
| public static void markInternalOperation(VirtualFile file, boolean set) { |
| file.putUserData(INTERNAL_OPERATION_KEY, set); |
| } |
| |
| public static boolean isInternalOperation(VirtualFile file) { |
| Boolean data = file.getUserData(INTERNAL_OPERATION_KEY); |
| return data != null && data.booleanValue(); |
| } |
| |
| public static String getDefaultChangeListName() { |
| return VcsBundle.message("changes.default.changelist.name"); |
| } |
| } |