diff --git a/java/java-impl/src/com/intellij/codeEditor/JavaEditorFileSwapper.java b/java/java-impl/src/com/intellij/codeEditor/JavaEditorFileSwapper.java index 345383ea0326..0847c5412e54 100644 --- a/java/java-impl/src/com/intellij/codeEditor/JavaEditorFileSwapper.java +++ b/java/java-impl/src/com/intellij/codeEditor/JavaEditorFileSwapper.java @@ -19,6 +19,7 @@ import com.intellij.openapi.fileEditor.impl.EditorFileSwapper; import com.intellij.openapi.fileEditor.impl.EditorWithProviderComposite; import com.intellij.openapi.fileEditor.impl.text.TextEditorImpl; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; @@ -66,7 +67,7 @@ public class JavaEditorFileSwapper extends EditorFileSwapper { if (member != null) { PsiElement navigationElement = member.getNavigationElement(); - if (navigationElement.getContainingFile().getVirtualFile() == sourceFile) { + if (Comparing.equal(navigationElement.getContainingFile().getVirtualFile(), sourceFile)) { position = navigationElement.getTextOffset(); } } diff --git a/java/java-impl/src/com/intellij/ide/favoritesTreeView/PsiClassFavoriteNodeProvider.java b/java/java-impl/src/com/intellij/ide/favoritesTreeView/PsiClassFavoriteNodeProvider.java index 723be0258738..14291be2775d 100644 --- a/java/java-impl/src/com/intellij/ide/favoritesTreeView/PsiClassFavoriteNodeProvider.java +++ b/java/java-impl/src/com/intellij/ide/favoritesTreeView/PsiClassFavoriteNodeProvider.java @@ -31,6 +31,7 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.module.ModuleUtil; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.JavaPsiFacade; import com.intellij.psi.PsiClass; @@ -77,7 +78,7 @@ public class PsiClassFavoriteNodeProvider extends FavoriteNodeProvider { public boolean elementContainsFile(final Object element, final VirtualFile vFile) { if (element instanceof PsiClass) { final PsiFile file = ((PsiClass)element).getContainingFile(); - if (file != null && file.getVirtualFile() == vFile) return true; + if (file != null && Comparing.equal(file.getVirtualFile(), vFile)) return true; } return false; } diff --git a/java/java-impl/src/com/intellij/openapi/roots/impl/ExcludeCompilerOutputPolicy.java b/java/java-impl/src/com/intellij/openapi/roots/impl/ExcludeCompilerOutputPolicy.java index dca26c3a8b7c..7dfb138567b4 100644 --- a/java/java-impl/src/com/intellij/openapi/roots/impl/ExcludeCompilerOutputPolicy.java +++ b/java/java-impl/src/com/intellij/openapi/roots/impl/ExcludeCompilerOutputPolicy.java @@ -21,6 +21,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.CompilerModuleExtension; import com.intellij.openapi.roots.CompilerProjectExtension; import com.intellij.openapi.roots.ModuleRootModel; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.pointers.VirtualFilePointer; import com.intellij.openapi.util.io.FileUtil; @@ -53,7 +54,8 @@ public class ExcludeCompilerOutputPolicy implements DirectoryIndexExcludePolicy @Override public boolean isExcludeRootForModule(final Module module, final VirtualFile excludeRoot) { final CompilerModuleExtension compilerModuleExtension = CompilerModuleExtension.getInstance(module); - return compilerModuleExtension.getCompilerOutputPath() == excludeRoot || compilerModuleExtension.getCompilerOutputPathForTests() == excludeRoot; + return Comparing.equal(compilerModuleExtension.getCompilerOutputPath(), excludeRoot) || + Comparing.equal(compilerModuleExtension.getCompilerOutputPathForTests(), excludeRoot); } @Override @@ -87,7 +89,7 @@ public class ExcludeCompilerOutputPolicy implements DirectoryIndexExcludePolicy private static boolean isEqualWithFileOrUrl(VirtualFile f, VirtualFile fileToCompareWith, String url) { if (fileToCompareWith != null) { - if (fileToCompareWith == f) return true; + if (Comparing.equal(fileToCompareWith, f)) return true; } else if (url != null) { if (FileUtil.pathsEqual(url, f.getUrl())) return true; diff --git a/java/java-impl/src/com/intellij/packageDependencies/ui/TreeModelBuilder.java b/java/java-impl/src/com/intellij/packageDependencies/ui/TreeModelBuilder.java index cae9eb2b3df5..e11d03a93de4 100644 --- a/java/java-impl/src/com/intellij/packageDependencies/ui/TreeModelBuilder.java +++ b/java/java-impl/src/com/intellij/packageDependencies/ui/TreeModelBuilder.java @@ -26,6 +26,7 @@ import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.*; import com.intellij.openapi.roots.libraries.LibraryUtil; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; @@ -172,7 +173,7 @@ public class TreeModelBuilder { VirtualFile dir = null; public boolean processFile(VirtualFile fileOrDir) { if (!fileOrDir.isDirectory()) { - if (lastParent != null && dir != fileOrDir.getParent()) { + if (lastParent != null && !Comparing.equal(dir, fileOrDir.getParent())) { lastParent = null; } lastParent = buildFileNode(fileOrDir, lastParent); diff --git a/java/java-impl/src/com/intellij/refactoring/extractSuperclass/JavaExtractSuperBaseDialog.java b/java/java-impl/src/com/intellij/refactoring/extractSuperclass/JavaExtractSuperBaseDialog.java index 674b6b16fe65..0fdb5959f16c 100644 --- a/java/java-impl/src/com/intellij/refactoring/extractSuperclass/JavaExtractSuperBaseDialog.java +++ b/java/java-impl/src/com/intellij/refactoring/extractSuperclass/JavaExtractSuperBaseDialog.java @@ -20,6 +20,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.ui.ComponentWithBrowseButton; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Pass; import com.intellij.openapi.vfs.VirtualFile; @@ -111,7 +112,7 @@ public abstract class JavaExtractSuperBaseDialog extends ExtractSuperBaseDialog< final VirtualFile sourceRoot = fileIndex.getSourceRootForFile(sourceFile); if (sourceRoot != null) { for (PsiDirectory dir : directories) { - if (fileIndex.getSourceRootForFile(dir.getVirtualFile()) == sourceRoot) { + if (Comparing.equal(fileIndex.getSourceRootForFile(dir.getVirtualFile()), sourceRoot)) { return dir; } } diff --git a/java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/DestinationFolderComboBox.java b/java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/DestinationFolderComboBox.java index 23feeb46c4a0..50c2c5fe1448 100644 --- a/java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/DestinationFolderComboBox.java +++ b/java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/DestinationFolderComboBox.java @@ -15,6 +15,8 @@ */ package com.intellij.refactoring.move.moveClassesOrPackages; +import com.intellij.openapi.util.Comparing; +import com.intellij.ui.ListCellRendererWrapper; import com.intellij.ide.util.DirectoryChooser; import com.intellij.openapi.editor.event.DocumentAdapter; import com.intellij.openapi.editor.event.DocumentEvent; @@ -122,7 +124,7 @@ public abstract class DestinationFolderComboBox extends ComboboxWithBrowseButton final ComboBoxModel model = getComboBox().getModel(); for (int i = 0; i < model.getSize(); i++) { DirectoryChooser.ItemWrapper item = (DirectoryChooser.ItemWrapper)model.getElementAt(i); - if (item != NULL_WRAPPER && fileIndex.getSourceRootForFile(item.getDirectory().getVirtualFile()) == root) { + if (item != NULL_WRAPPER && Comparing.equal(fileIndex.getSourceRootForFile(item.getDirectory().getVirtualFile()), root)) { getComboBox().setSelectedItem(item); getComboBox().repaint(); return; @@ -164,7 +166,9 @@ public abstract class DestinationFolderComboBox extends ComboboxWithBrowseButton } final PsiDirectory selectedPsiDirectory = selectedItem.getDirectory(); VirtualFile selectedDestination = selectedPsiDirectory.getVirtualFile(); - if (showChooserWhenDefault && selectedDestination == myInitialTargetDirectory.getVirtualFile() && mySourceRoots.length > 1) { + if (showChooserWhenDefault && + Comparing.equal(selectedDestination, myInitialTargetDirectory.getVirtualFile()) && + mySourceRoots.length > 1) { selectedDestination = MoveClassesOrPackagesUtil.chooseSourceRoot(targetPackage, mySourceRoots, myInitialTargetDirectory); } if (selectedDestination == null) return null; @@ -211,9 +215,10 @@ public abstract class DestinationFolderComboBox extends ComboboxWithBrowseButton DirectoryChooser.ItemWrapper itemWrapper = new DirectoryChooser.ItemWrapper(targetDirectory, pathsToCreate.get(targetDirectory)); items.add(itemWrapper); final VirtualFile sourceRootForFile = fileIndex.getSourceRootForFile(targetDirectory.getVirtualFile()); - if (sourceRootForFile == initialTargetDirectorySourceRoot) { + if (Comparing.equal(sourceRootForFile, initialTargetDirectorySourceRoot)) { initial = itemWrapper; - } else if (sourceRootForFile == oldSelection) { + } + else if (Comparing.equal(sourceRootForFile, oldSelection)) { oldOne = itemWrapper; } } diff --git a/java/java-impl/src/com/intellij/refactoring/move/moveInner/MoveInnerDialog.java b/java/java-impl/src/com/intellij/refactoring/move/moveInner/MoveInnerDialog.java index 1d4afb77e665..901cac555ffb 100644 --- a/java/java-impl/src/com/intellij/refactoring/move/moveInner/MoveInnerDialog.java +++ b/java/java-impl/src/com/intellij/refactoring/move/moveInner/MoveInnerDialog.java @@ -205,7 +205,7 @@ public class MoveInnerDialog extends RefactoringDialog { final PsiDirectory[] directories = oldPackage.getDirectories(); final VirtualFile root = projectRootManager.getFileIndex().getContentRootForFile(psiDirectory.getVirtualFile()); for(PsiDirectory dir: directories) { - if (projectRootManager.getFileIndex().getContentRootForFile(dir.getVirtualFile()) == root) { + if (Comparing.equal(projectRootManager.getFileIndex().getContentRootForFile(dir.getVirtualFile()), root)) { initialDir = dir; } } diff --git a/java/java-indexing-impl/src/com/intellij/psi/impl/file/impl/JavaFileManagerBase.java b/java/java-indexing-impl/src/com/intellij/psi/impl/file/impl/JavaFileManagerBase.java index 3bb06f50eee1..2c4e64e36ff8 100644 --- a/java/java-indexing-impl/src/com/intellij/psi/impl/file/impl/JavaFileManagerBase.java +++ b/java/java-indexing-impl/src/com/intellij/psi/impl/file/impl/JavaFileManagerBase.java @@ -20,6 +20,7 @@ import com.intellij.ide.highlighter.JavaClassFileType; import com.intellij.openapi.Disposable; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.roots.*; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; @@ -314,7 +315,7 @@ public abstract class JavaFileManagerBase implements JavaFileManager, Disposable final VirtualFile root = ProjectRootManager.getInstance(myManager.getProject()).getFileIndex().getClassRootForFile(vFile); VirtualFile parent = vFile.getParent(); final PsiNameHelper nameHelper = JavaPsiFacade.getInstance(myManager.getProject()).getNameHelper(); - while (parent != null && parent != root) { + while (parent != null && !Comparing.equal(parent, root)) { if (!nameHelper.isIdentifier(parent.getName())) return false; parent = parent.getParent(); } @@ -332,7 +333,7 @@ public abstract class JavaFileManagerBase implements JavaFileManager, Disposable final ProjectFileIndex fileIndex = rootManager.getFileIndex(); for (final VirtualFile sourceRoot : sourceRoots) { final String packageName = fileIndex.getPackageNameByDirectory(sourceRoot); - if (packageName != null && packageName.length() > 0) { + if (packageName != null && !packageName.isEmpty()) { names.add(packageName); } } diff --git a/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaDirectInheritorsSearcher.java b/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaDirectInheritorsSearcher.java index b88ac9e77309..049c6feb304b 100644 --- a/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaDirectInheritorsSearcher.java +++ b/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaDirectInheritorsSearcher.java @@ -1,9 +1,25 @@ +/* + * Copyright 2000-2012 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.psi.impl.search; import com.intellij.openapi.application.AccessToken; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.progress.ProgressIndicatorProvider; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; @@ -179,7 +195,7 @@ public class JavaDirectInheritorsSearcher implements QueryExecutor extends AbstractTreeNode im public boolean value(final VirtualFile virtualFile) { return contains(virtualFile) // in case of flattened packages, when package node a.b.c contains error file, node a.b might not. - && (getValue() instanceof PsiElement && PsiUtilBase.getVirtualFile((PsiElement)getValue()) == virtualFile || + && (getValue() instanceof PsiElement && Comparing.equal(PsiUtilBase.getVirtualFile((PsiElement)getValue()), virtualFile) || someChildContainsFile(virtualFile)); } }); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/FilePathCompletionContributor.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/FilePathCompletionContributor.java index 643422517170..fe911cdde33a 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/FilePathCompletionContributor.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/FilePathCompletionContributor.java @@ -1,371 +1,372 @@ -/* - * 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.codeInsight.completion; - -import com.intellij.codeInsight.CodeInsightBundle; -import com.intellij.codeInsight.lookup.LookupElement; -import com.intellij.codeInsight.lookup.LookupElementPresentation; -import com.intellij.navigation.ChooseByNameContributor; -import com.intellij.openapi.actionSystem.IdeActions; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.fileTypes.FileNameMatcher; -import com.intellij.openapi.fileTypes.FileType; -import com.intellij.openapi.fileTypes.FileTypeManager; -import com.intellij.openapi.module.Module; -import com.intellij.openapi.progress.ProcessCanceledException; -import com.intellij.openapi.progress.ProgressManager; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.roots.ProjectFileIndex; -import com.intellij.openapi.roots.ProjectRootManager; -import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiFileSystemItem; -import com.intellij.psi.PsiReference; -import com.intellij.psi.impl.source.resolve.reference.impl.PsiMultiReference; -import com.intellij.psi.impl.source.resolve.reference.impl.providers.*; -import com.intellij.psi.search.FilenameIndex; -import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.psi.search.ProjectScope; -import com.intellij.util.ArrayUtil; -import com.intellij.util.ProcessingContext; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; -import java.util.*; - -import static com.intellij.patterns.PlatformPatterns.psiElement; - -/** - * @author spleaner - */ -public class FilePathCompletionContributor extends CompletionContributor { - private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.FilePathCompletionContributor"); - - public FilePathCompletionContributor() { - extend(CompletionType.BASIC, psiElement(), new CompletionProvider() { - @Override - protected void addCompletions(@NotNull CompletionParameters parameters, - ProcessingContext context, - @NotNull CompletionResultSet result) { - final PsiReference psiReference = parameters.getPosition().getContainingFile().findReferenceAt(parameters.getOffset()); - if (getReference(psiReference) != null && parameters.getInvocationCount() == 1) { - final String shortcut = getActionShortcut(IdeActions.ACTION_CODE_COMPLETION); - if (shortcut != null) { - CompletionService.getCompletionService().setAdvertisementText(CodeInsightBundle.message("class.completion.file.path", shortcut)); - } - } - } - }); - - CompletionProvider provider = new CompletionProvider() { - @Override - protected void addCompletions(@NotNull final CompletionParameters parameters, - ProcessingContext context, - @NotNull final CompletionResultSet _result) { - if (!parameters.isExtendedCompletion()) { - return; - } - - @NotNull final CompletionResultSet result = _result.caseInsensitive(); - final PsiElement e = parameters.getPosition(); - final Project project = e.getProject(); - - final PsiReference psiReference = parameters.getPosition().getContainingFile().findReferenceAt(parameters.getOffset()); - - final Pair fileReferencePair = getReference(psiReference); - if (fileReferencePair != null) { - final FileReference first = fileReferencePair.getFirst(); - if (first == null) return; - - final FileReferenceSet set = first.getFileReferenceSet(); - String prefix = set.getPathString() - .substring(0, parameters.getOffset() - set.getElement().getTextRange().getStartOffset() - set.getStartInElement()); - final String textBeforePosition = e.getContainingFile().getText().substring(0, parameters.getOffset()); - if (!textBeforePosition.endsWith(prefix)) { - final int len = textBeforePosition.length(); - final String fragment = len > 100 ? textBeforePosition.substring(len - 100) : textBeforePosition; - throw new AssertionError("prefix should be some actual file string just before caret: " + - prefix + - "\n text=" + - fragment + - ";\npathString=" + - set.getPathString() + - ";\nelementText=" + - e.getParent().getText()); - } - - List pathPrefixParts = null; - int lastSlashIndex; - if ((lastSlashIndex = prefix.lastIndexOf('/')) != -1) { - pathPrefixParts = StringUtil.split(prefix.substring(0, lastSlashIndex), "/"); - prefix = prefix.substring(lastSlashIndex + 1); - } - - final CompletionResultSet __result = result.withPrefixMatcher(prefix).caseInsensitive(); - - final PsiFile originalFile = parameters.getOriginalFile(); - final VirtualFile contextFile = originalFile.getVirtualFile(); - if (contextFile != null) { - final String[] fileNames = getAllNames(project); - final Set resultNames = new TreeSet(); - for (String fileName : fileNames) { - if (filenameMatchesPrefixOrType(fileName, prefix, set.getSuitableFileTypes(), parameters.getInvocationCount())) { - resultNames.add(fileName); - } - } - - final ProjectFileIndex index = ProjectRootManager.getInstance(project).getFileIndex(); - - final Module contextModule = index.getModuleForFile(contextFile); - if (contextModule != null) { - final FileReferenceHelper contextHelper = FileReferenceHelperRegistrar.getNotNullHelper(originalFile); - - final GlobalSearchScope scope = ProjectScope.getProjectScope(project); - for (final String name : resultNames) { - ProgressManager.checkCanceled(); - - final PsiFile[] files = FilenameIndex.getFilesByName(project, name, scope); - - if (files.length > 0) { - for (final PsiFile file : files) { - ProgressManager.checkCanceled(); - - final VirtualFile virtualFile = file.getVirtualFile(); - if (virtualFile != null && virtualFile.isValid() && virtualFile != contextFile) { - if (contextHelper.isMine(project, virtualFile)) { - if (pathPrefixParts == null || - fileMatchesPathPrefix(contextHelper.getPsiFileSystemItem(project, virtualFile), pathPrefixParts)) { - __result.addElement(new FilePathLookupItem(file, contextHelper)); - } - } - } - } - } - } - } - } - - if (set.getSuitableFileTypes().length > 0 && parameters.getInvocationCount() == 1) { - final String shortcut = getActionShortcut(IdeActions.ACTION_CODE_COMPLETION); - if (shortcut != null) { - CompletionService.getCompletionService() - .setAdvertisementText(CodeInsightBundle.message("class.completion.file.path.all.variants", shortcut)); - } - } - - if (fileReferencePair.getSecond()) result.stopHere(); - } - } - }; - extend(CompletionType.BASIC, psiElement(), provider); - } - - private static boolean filenameMatchesPrefixOrType(final String fileName, final String prefix, final FileType[] suitableFileTypes, final int invocationCount) { - final boolean prefixMatched = prefix.length() == 0 || StringUtil.startsWithIgnoreCase(fileName, prefix); - if (prefixMatched && (suitableFileTypes.length == 0 || invocationCount > 2)) return true; - - if (prefixMatched) { - final String extension = FileUtil.getExtension(fileName); - if (extension.length() == 0) return false; - - for (final FileType fileType : suitableFileTypes) { - final List matchers = FileTypeManager.getInstance().getAssociations(fileType); - for (final FileNameMatcher matcher : matchers) { - if (matcher.accept(fileName)) return true; - } - } - } - - return false; - } - - private static boolean fileMatchesPathPrefix(@Nullable final PsiFileSystemItem file, @NotNull final List pathPrefix) { - if (file == null) return false; - - final List contextParts = new ArrayList(); - PsiFileSystemItem parentFile = file; - PsiFileSystemItem parent; - while ((parent = parentFile.getParent()) != null) { - if (parent.getName().length() > 0) contextParts.add(0, parent.getName().toLowerCase()); - parentFile = parent; - } - - final String path = StringUtil.join(contextParts, "/"); - - int nextIndex = 0; - for (final String s : pathPrefix) { - if ((nextIndex = path.indexOf(s.toLowerCase(), nextIndex)) == -1) return false; - } - - return true; - } - - private static String[] getAllNames(@NotNull final Project project) { - Set names = new HashSet(); - final ChooseByNameContributor[] nameContributors = ChooseByNameContributor.FILE_EP_NAME.getExtensions(); - for (final ChooseByNameContributor contributor : nameContributors) { - try { - names.addAll(Arrays.asList(contributor.getNames(project, false))); - } - catch (ProcessCanceledException ex) { - // index corruption detected, ignore - } - catch (Exception ex) { - LOG.error(ex); - } - } - - return ArrayUtil.toStringArray(names); - } - - @Nullable - private static Pair getReference(final PsiReference original) { - if (original == null) { - return null; - } - - if (original instanceof PsiMultiReference) { - final PsiMultiReference multiReference = (PsiMultiReference)original; - for (PsiReference reference : multiReference.getReferences()) { - if (reference instanceof FileReference) { - return Pair.create((FileReference) reference, false); - } - } - } - else if (original instanceof FileReferenceOwner) { - final FileReference fileReference = ((FileReferenceOwner)original).getLastFileReference(); - if (fileReference != null) { - return Pair.create(fileReference, true); - } - } - - return null; - } - - public class FilePathLookupItem extends LookupElement { - private final String myName; - private final String myPath; - private final String myInfo; - private final Icon myIcon; - private final PsiFile myFile; - private final FileReferenceHelper myReferenceHelper; - - public FilePathLookupItem(@NotNull final PsiFile file, @NotNull final FileReferenceHelper referenceHelper) { - myName = file.getName(); - myPath = file.getVirtualFile().getPath(); - - myReferenceHelper = referenceHelper; - - myInfo = FileInfoManager.getFileAdditionalInfo(file); - myIcon = file.getFileType().getIcon(); - - myFile = file; - } - - @SuppressWarnings({"HardCodedStringLiteral"}) - @Override - public String toString() { - return String.format("%s%s", myName, myInfo == null ? "" : " (" + myInfo + ")"); - } - - @NotNull - @Override - public Object getObject() { - return myFile; - } - - @NotNull - public String getLookupString() { - return myName; - } - - @Override - public void handleInsert(InsertionContext context) { - context.commitDocument(); - if (myFile.isValid()) { - final PsiReference psiReference = context.getFile().findReferenceAt(context.getStartOffset()); - final Pair fileReferencePair = getReference(psiReference); - LOG.assertTrue(fileReferencePair != null); - - FileReference ref = fileReferencePair.getFirst(); - context.setTailOffset(ref.getRangeInElement().getEndOffset() + ref.getElement().getTextRange().getStartOffset()); - ref.bindToElement(myFile, true); - } - } - - @Override - public void renderElement(LookupElementPresentation presentation) { - final VirtualFile virtualFile = myFile.getVirtualFile(); - LOG.assertTrue(virtualFile != null); - final PsiFileSystemItem root = myReferenceHelper.findRoot(myFile.getProject(), virtualFile); - final String relativePath = PsiFileSystemItemUtil.getRelativePath(root, myReferenceHelper.getPsiFileSystemItem(myFile.getProject(), virtualFile)); - - final StringBuilder sb = new StringBuilder(); - if (myInfo != null) { - sb.append(" (").append(myInfo); - } - - if (relativePath != null && !relativePath.equals(myName)) { - if (myInfo != null) { - sb.append(", "); - } - else { - sb.append(" ("); - } - - sb.append(relativePath); - } - - if (sb.length() > 0) { - sb.append(')'); - } - - presentation.setItemText(myName); - - if (sb.length() > 0) { - presentation.setTailText(sb.toString(), true); - } - - presentation.setIcon(myIcon); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - FilePathLookupItem that = (FilePathLookupItem)o; - - if (!myName.equals(that.myName)) return false; - if (!myPath.equals(that.myPath)) return false; - - return true; - } - - @Override - public int hashCode() { - int result = myName.hashCode(); - result = 31 * result + myPath.hashCode(); - return result; - } - } -} +/* + * 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.codeInsight.completion; + +import com.intellij.codeInsight.CodeInsightBundle; +import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.codeInsight.lookup.LookupElementPresentation; +import com.intellij.navigation.ChooseByNameContributor; +import com.intellij.openapi.actionSystem.IdeActions; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.fileTypes.FileNameMatcher; +import com.intellij.openapi.fileTypes.FileType; +import com.intellij.openapi.fileTypes.FileTypeManager; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.progress.ProcessCanceledException; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.ProjectFileIndex; +import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiFileSystemItem; +import com.intellij.psi.PsiReference; +import com.intellij.psi.impl.source.resolve.reference.impl.PsiMultiReference; +import com.intellij.psi.impl.source.resolve.reference.impl.providers.*; +import com.intellij.psi.search.FilenameIndex; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.search.ProjectScope; +import com.intellij.util.ArrayUtil; +import com.intellij.util.ProcessingContext; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.util.*; + +import static com.intellij.patterns.PlatformPatterns.psiElement; + +/** + * @author spleaner + */ +public class FilePathCompletionContributor extends CompletionContributor { + private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.FilePathCompletionContributor"); + + public FilePathCompletionContributor() { + extend(CompletionType.BASIC, psiElement(), new CompletionProvider() { + @Override + protected void addCompletions(@NotNull CompletionParameters parameters, + ProcessingContext context, + @NotNull CompletionResultSet result) { + final PsiReference psiReference = parameters.getPosition().getContainingFile().findReferenceAt(parameters.getOffset()); + if (getReference(psiReference) != null && parameters.getInvocationCount() == 1) { + final String shortcut = getActionShortcut(IdeActions.ACTION_CODE_COMPLETION); + if (shortcut != null) { + CompletionService.getCompletionService().setAdvertisementText(CodeInsightBundle.message("class.completion.file.path", shortcut)); + } + } + } + }); + + CompletionProvider provider = new CompletionProvider() { + @Override + protected void addCompletions(@NotNull final CompletionParameters parameters, + ProcessingContext context, + @NotNull final CompletionResultSet _result) { + if (!parameters.isExtendedCompletion()) { + return; + } + + @NotNull final CompletionResultSet result = _result.caseInsensitive(); + final PsiElement e = parameters.getPosition(); + final Project project = e.getProject(); + + final PsiReference psiReference = parameters.getPosition().getContainingFile().findReferenceAt(parameters.getOffset()); + + final Pair fileReferencePair = getReference(psiReference); + if (fileReferencePair != null) { + final FileReference first = fileReferencePair.getFirst(); + if (first == null) return; + + final FileReferenceSet set = first.getFileReferenceSet(); + String prefix = set.getPathString() + .substring(0, parameters.getOffset() - set.getElement().getTextRange().getStartOffset() - set.getStartInElement()); + final String textBeforePosition = e.getContainingFile().getText().substring(0, parameters.getOffset()); + if (!textBeforePosition.endsWith(prefix)) { + final int len = textBeforePosition.length(); + final String fragment = len > 100 ? textBeforePosition.substring(len - 100) : textBeforePosition; + throw new AssertionError("prefix should be some actual file string just before caret: " + + prefix + + "\n text=" + + fragment + + ";\npathString=" + + set.getPathString() + + ";\nelementText=" + + e.getParent().getText()); + } + + List pathPrefixParts = null; + int lastSlashIndex; + if ((lastSlashIndex = prefix.lastIndexOf('/')) != -1) { + pathPrefixParts = StringUtil.split(prefix.substring(0, lastSlashIndex), "/"); + prefix = prefix.substring(lastSlashIndex + 1); + } + + final CompletionResultSet __result = result.withPrefixMatcher(prefix).caseInsensitive(); + + final PsiFile originalFile = parameters.getOriginalFile(); + final VirtualFile contextFile = originalFile.getVirtualFile(); + if (contextFile != null) { + final String[] fileNames = getAllNames(project); + final Set resultNames = new TreeSet(); + for (String fileName : fileNames) { + if (filenameMatchesPrefixOrType(fileName, prefix, set.getSuitableFileTypes(), parameters.getInvocationCount())) { + resultNames.add(fileName); + } + } + + final ProjectFileIndex index = ProjectRootManager.getInstance(project).getFileIndex(); + + final Module contextModule = index.getModuleForFile(contextFile); + if (contextModule != null) { + final FileReferenceHelper contextHelper = FileReferenceHelperRegistrar.getNotNullHelper(originalFile); + + final GlobalSearchScope scope = ProjectScope.getProjectScope(project); + for (final String name : resultNames) { + ProgressManager.checkCanceled(); + + final PsiFile[] files = FilenameIndex.getFilesByName(project, name, scope); + + if (files.length > 0) { + for (final PsiFile file : files) { + ProgressManager.checkCanceled(); + + final VirtualFile virtualFile = file.getVirtualFile(); + if (virtualFile != null && virtualFile.isValid() && !Comparing.equal(virtualFile, contextFile)) { + if (contextHelper.isMine(project, virtualFile)) { + if (pathPrefixParts == null || + fileMatchesPathPrefix(contextHelper.getPsiFileSystemItem(project, virtualFile), pathPrefixParts)) { + __result.addElement(new FilePathLookupItem(file, contextHelper)); + } + } + } + } + } + } + } + } + + if (set.getSuitableFileTypes().length > 0 && parameters.getInvocationCount() == 1) { + final String shortcut = getActionShortcut(IdeActions.ACTION_CODE_COMPLETION); + if (shortcut != null) { + CompletionService.getCompletionService() + .setAdvertisementText(CodeInsightBundle.message("class.completion.file.path.all.variants", shortcut)); + } + } + + if (fileReferencePair.getSecond()) result.stopHere(); + } + } + }; + extend(CompletionType.BASIC, psiElement(), provider); + } + + private static boolean filenameMatchesPrefixOrType(final String fileName, final String prefix, final FileType[] suitableFileTypes, final int invocationCount) { + final boolean prefixMatched = prefix.length() == 0 || StringUtil.startsWithIgnoreCase(fileName, prefix); + if (prefixMatched && (suitableFileTypes.length == 0 || invocationCount > 2)) return true; + + if (prefixMatched) { + final String extension = FileUtil.getExtension(fileName); + if (extension.length() == 0) return false; + + for (final FileType fileType : suitableFileTypes) { + final List matchers = FileTypeManager.getInstance().getAssociations(fileType); + for (final FileNameMatcher matcher : matchers) { + if (matcher.accept(fileName)) return true; + } + } + } + + return false; + } + + private static boolean fileMatchesPathPrefix(@Nullable final PsiFileSystemItem file, @NotNull final List pathPrefix) { + if (file == null) return false; + + final List contextParts = new ArrayList(); + PsiFileSystemItem parentFile = file; + PsiFileSystemItem parent; + while ((parent = parentFile.getParent()) != null) { + if (parent.getName().length() > 0) contextParts.add(0, parent.getName().toLowerCase()); + parentFile = parent; + } + + final String path = StringUtil.join(contextParts, "/"); + + int nextIndex = 0; + for (final String s : pathPrefix) { + if ((nextIndex = path.indexOf(s.toLowerCase(), nextIndex)) == -1) return false; + } + + return true; + } + + private static String[] getAllNames(@NotNull final Project project) { + Set names = new HashSet(); + final ChooseByNameContributor[] nameContributors = ChooseByNameContributor.FILE_EP_NAME.getExtensions(); + for (final ChooseByNameContributor contributor : nameContributors) { + try { + names.addAll(Arrays.asList(contributor.getNames(project, false))); + } + catch (ProcessCanceledException ex) { + // index corruption detected, ignore + } + catch (Exception ex) { + LOG.error(ex); + } + } + + return ArrayUtil.toStringArray(names); + } + + @Nullable + private static Pair getReference(final PsiReference original) { + if (original == null) { + return null; + } + + if (original instanceof PsiMultiReference) { + final PsiMultiReference multiReference = (PsiMultiReference)original; + for (PsiReference reference : multiReference.getReferences()) { + if (reference instanceof FileReference) { + return Pair.create((FileReference) reference, false); + } + } + } + else if (original instanceof FileReferenceOwner) { + final FileReference fileReference = ((FileReferenceOwner)original).getLastFileReference(); + if (fileReference != null) { + return Pair.create(fileReference, true); + } + } + + return null; + } + + public class FilePathLookupItem extends LookupElement { + private final String myName; + private final String myPath; + private final String myInfo; + private final Icon myIcon; + private final PsiFile myFile; + private final FileReferenceHelper myReferenceHelper; + + public FilePathLookupItem(@NotNull final PsiFile file, @NotNull final FileReferenceHelper referenceHelper) { + myName = file.getName(); + myPath = file.getVirtualFile().getPath(); + + myReferenceHelper = referenceHelper; + + myInfo = FileInfoManager.getFileAdditionalInfo(file); + myIcon = file.getFileType().getIcon(); + + myFile = file; + } + + @SuppressWarnings({"HardCodedStringLiteral"}) + @Override + public String toString() { + return String.format("%s%s", myName, myInfo == null ? "" : " (" + myInfo + ")"); + } + + @NotNull + @Override + public Object getObject() { + return myFile; + } + + @NotNull + public String getLookupString() { + return myName; + } + + @Override + public void handleInsert(InsertionContext context) { + context.commitDocument(); + if (myFile.isValid()) { + final PsiReference psiReference = context.getFile().findReferenceAt(context.getStartOffset()); + final Pair fileReferencePair = getReference(psiReference); + LOG.assertTrue(fileReferencePair != null); + + FileReference ref = fileReferencePair.getFirst(); + context.setTailOffset(ref.getRangeInElement().getEndOffset() + ref.getElement().getTextRange().getStartOffset()); + ref.bindToElement(myFile, true); + } + } + + @Override + public void renderElement(LookupElementPresentation presentation) { + final VirtualFile virtualFile = myFile.getVirtualFile(); + LOG.assertTrue(virtualFile != null); + final PsiFileSystemItem root = myReferenceHelper.findRoot(myFile.getProject(), virtualFile); + final String relativePath = PsiFileSystemItemUtil.getRelativePath(root, myReferenceHelper.getPsiFileSystemItem(myFile.getProject(), virtualFile)); + + final StringBuilder sb = new StringBuilder(); + if (myInfo != null) { + sb.append(" (").append(myInfo); + } + + if (relativePath != null && !relativePath.equals(myName)) { + if (myInfo != null) { + sb.append(", "); + } + else { + sb.append(" ("); + } + + sb.append(relativePath); + } + + if (sb.length() > 0) { + sb.append(')'); + } + + presentation.setItemText(myName); + + if (sb.length() > 0) { + presentation.setTailText(sb.toString(), true); + } + + presentation.setIcon(myIcon); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + FilePathLookupItem that = (FilePathLookupItem)o; + + if (!myName.equals(that.myName)) return false; + if (!myPath.equals(that.myPath)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = myName.hashCode(); + result = 31 * result + myPath.hashCode(); + return result; + } + } +} diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/AnnotationHolderImpl.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/AnnotationHolderImpl.java index 4931b22a5d75..972109e5907d 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/AnnotationHolderImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/AnnotationHolderImpl.java @@ -22,6 +22,7 @@ import com.intellij.lang.annotation.AnnotationHolder; import com.intellij.lang.annotation.AnnotationSession; import com.intellij.lang.annotation.HighlightSeverity; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; @@ -130,7 +131,7 @@ public class AnnotationHolderImpl extends SmartList implements Annot LOG.assertTrue(containingFile != null, node); VirtualFile containingVFile = containingFile.getVirtualFile(); VirtualFile myVFile = myFile.getVirtualFile(); - if (containingVFile != myVFile) { + if (!Comparing.equal(containingVFile, myVFile)) { LOG.error( "Annotation must be registered for an element inside '" + myFile + "' which is in '" + myVFile + "'.\n" + "Element passed: '" + node + "' is inside the '" + containingFile + "' which is in '" + containingVFile + "'"); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/QuickEditHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/QuickEditHandler.java index 1a8b9ba856d2..6b957ac47126 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/QuickEditHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/QuickEditHandler.java @@ -200,7 +200,7 @@ public class QuickEditHandler extends DocumentAdapter implements Disposable { boolean unsplit = false; if (mySplittedWindow != null && !mySplittedWindow.isDisposed()) { final EditorWithProviderComposite[] editors = mySplittedWindow.getEditors(); - if (editors.length == 1 && editors[0].getFile() == myNewVirtualFile) { + if (editors.length == 1 && Comparing.equal(editors[0].getFile(), myNewVirtualFile)) { unsplit = true; } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/problems/WolfTheProblemSolverImpl.java b/platform/lang-impl/src/com/intellij/codeInsight/problems/WolfTheProblemSolverImpl.java index d505f8af1210..be1d11f253e5 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/problems/WolfTheProblemSolverImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/problems/WolfTheProblemSolverImpl.java @@ -33,10 +33,7 @@ import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Computable; -import com.intellij.openapi.util.Condition; -import com.intellij.openapi.util.Disposer; -import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.*; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.FileStatusListener; import com.intellij.openapi.vcs.FileStatusManager; @@ -333,7 +330,7 @@ public class WolfTheProblemSolverImpl extends WolfTheProblemSolver { Document document = ((TextEditor)editor).getEditor().getDocument(); PsiFile psiFile = PsiDocumentManager.getInstance(myProject).getCachedPsiFile(document); if (psiFile == null) continue; - if (file == psiFile.getVirtualFile()) return true; + if (Comparing.equal(file, psiFile.getVirtualFile())) return true; } return false; } diff --git a/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleImpl.java b/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleImpl.java index e16fa6bfa9ea..5bf76d099df1 100644 --- a/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleImpl.java @@ -51,10 +51,7 @@ import com.intellij.openapi.fileEditor.impl.FileDocumentManagerImpl; import com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl; import com.intellij.openapi.fileTypes.FileTypes; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Disposer; -import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.Ref; -import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.*; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; @@ -583,7 +580,7 @@ public class LanguageConsoleImpl implements Disposable, TypeSafeDataProvider { myProject.getMessageBus().connect(this).subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, new FileEditorManagerAdapter() { @Override public void fileOpened(FileEditorManager source, VirtualFile file) { - if (file != myFile.getVirtualFile()) return; + if (!Comparing.equal(file, myFile.getVirtualFile())) return; if (myConsoleEditor != null) { Editor selectedTextEditor = source.getSelectedTextEditor(); for (FileEditor fileEditor : source.getAllEditors(file)) { @@ -608,7 +605,7 @@ public class LanguageConsoleImpl implements Disposable, TypeSafeDataProvider { @Override public void fileClosed(FileEditorManager source, VirtualFile file) { - if (file != myFile.getVirtualFile()) return; + if (!Comparing.equal(file, myFile.getVirtualFile())) return; if (myUiUpdateRunnable != null && !Boolean.TRUE.equals(file.getUserData(FileEditorManagerImpl.CLOSING_TO_REOPEN))) { if (myCurrentEditor.isDisposed()) myCurrentEditor = null; ApplicationManager.getApplication().runReadAction(myUiUpdateRunnable); diff --git a/platform/lang-impl/src/com/intellij/find/impl/FindManagerImpl.java b/platform/lang-impl/src/com/intellij/find/impl/FindManagerImpl.java index 4b139de03afb..ded203b2c1e8 100644 --- a/platform/lang-impl/src/com/intellij/find/impl/FindManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/find/impl/FindManagerImpl.java @@ -1,4 +1,3 @@ - /* * Copyright 2000-2012 JetBrains s.r.o. * @@ -414,7 +413,7 @@ public class FindManagerImpl extends FindManager implements PersistentStateCompo if(lang == null) return NOT_FOUND_RESULT; CommentsLiteralsSearchData data = model.getUserData(ourCommentsLiteralsSearchDataKey); - if (data == null || data.lastFile != file) { + if (data == null || !Comparing.equal(data.lastFile, file)) { Lexer lexer = getLexer(file, lang); TokenSet tokensOfInterest = TokenSet.EMPTY; diff --git a/platform/lang-impl/src/com/intellij/ide/bookmarks/BookmarkManager.java b/platform/lang-impl/src/com/intellij/ide/bookmarks/BookmarkManager.java index b2b635d7496f..b3264e0daaaa 100644 --- a/platform/lang-impl/src/com/intellij/ide/bookmarks/BookmarkManager.java +++ b/platform/lang-impl/src/com/intellij/ide/bookmarks/BookmarkManager.java @@ -28,6 +28,7 @@ import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.project.DumbAwareRunnable; import com.intellij.openapi.project.Project; import com.intellij.openapi.startup.StartupManager; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; @@ -79,7 +80,7 @@ public class BookmarkManager extends AbstractProjectComponent implements Persist public void run() { if (myProject.isDisposed()) return; for (Bookmark bookmark : myBookmarks) { - if (bookmark.getFile() == file) { + if (Comparing.equal(bookmark.getFile(), file)) { bookmark.createHighlighter((MarkupModelEx)DocumentMarkupModel.forDocument(document, myProject, true)); } } @@ -165,7 +166,7 @@ public class BookmarkManager extends AbstractProjectComponent implements Persist @Nullable public Bookmark findFileBookmark(@NotNull VirtualFile file) { for (Bookmark bookmark : myBookmarks) { - if (bookmark.getFile() == file && bookmark.getLine() == -1) return bookmark; + if (Comparing.equal(bookmark.getFile(), file) && bookmark.getLine() == -1) return bookmark; } return null; diff --git a/platform/lang-impl/src/com/intellij/ide/impl/PatchProjectUtil.java b/platform/lang-impl/src/com/intellij/ide/impl/PatchProjectUtil.java index aa3f1174dca1..9c8ca5ae68e1 100644 --- a/platform/lang-impl/src/com/intellij/ide/impl/PatchProjectUtil.java +++ b/platform/lang-impl/src/com/intellij/ide/impl/PatchProjectUtil.java @@ -26,6 +26,7 @@ import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.roots.impl.ModifiableModelCommitter; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.*; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VfsUtil; @@ -104,7 +105,7 @@ public class PatchProjectUtil { if (included.isEmpty()) return; final Set parents = new HashSet(); for (VirtualFile file : included) { - if (file == contentEntry.getFile()) return; + if (Comparing.equal(file, contentEntry.getFile())) return; final VirtualFile parent = file.getParent(); if (parent == null || parents.contains(parent)) continue; parents.add(parent); diff --git a/platform/lang-impl/src/com/intellij/ide/impl/ProjectPaneSelectInTarget.java b/platform/lang-impl/src/com/intellij/ide/impl/ProjectPaneSelectInTarget.java index c7598d63b6d9..72f89bfaa6d0 100644 --- a/platform/lang-impl/src/com/intellij/ide/impl/ProjectPaneSelectInTarget.java +++ b/platform/lang-impl/src/com/intellij/ide/impl/ProjectPaneSelectInTarget.java @@ -24,6 +24,7 @@ import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFileSystemItem; @@ -57,7 +58,7 @@ public class ProjectPaneSelectInTarget extends ProjectViewSelectInTarget impleme return true; } - return vFile.getParent() == myProject.getBaseDir(); + return Comparing.equal(vFile.getParent(), myProject.getBaseDir()); } return false; diff --git a/platform/lang-impl/src/com/intellij/ide/projectView/actions/MarkRootAction.java b/platform/lang-impl/src/com/intellij/ide/projectView/actions/MarkRootAction.java index b821f227bbac..4216e5753b28 100644 --- a/platform/lang-impl/src/com/intellij/ide/projectView/actions/MarkRootAction.java +++ b/platform/lang-impl/src/com/intellij/ide/projectView/actions/MarkRootAction.java @@ -22,6 +22,7 @@ import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.module.Module; import com.intellij.openapi.roots.*; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Ref; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; @@ -65,7 +66,7 @@ public class MarkRootAction extends AnAction { if (entry != null) { final SourceFolder[] sourceFolders = entry.getSourceFolders(); for (SourceFolder sourceFolder : sourceFolders) { - if (sourceFolder.getFile() == vFile) { + if (Comparing.equal(sourceFolder.getFile(), vFile)) { entry.removeSourceFolder(sourceFolder); break; } @@ -126,7 +127,7 @@ public class MarkRootAction extends AnAction { if (!fileIndex.isInContent(vFile)) { return false; } - if (fileIndex.getSourceRootForFile(vFile) == vFile) { + if (Comparing.equal(fileIndex.getSourceRootForFile(vFile), vFile)) { boolean isTestSourceRoot = fileIndex.isInTestSourceContent(vFile); if (acceptSourceRoot && !isTestSourceRoot) { if (rootType != null) rootType.set(true); diff --git a/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectRootsUtil.java b/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectRootsUtil.java index 84109a989960..42dc2bd3954e 100644 --- a/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectRootsUtil.java +++ b/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectRootsUtil.java @@ -18,6 +18,7 @@ package com.intellij.ide.projectView.impl; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.*; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; @@ -63,7 +64,7 @@ public class ProjectRootsUtil { for (ContentEntry contentEntry : contentEntries) { final SourceFolder[] sourceFolders = contentEntry.getSourceFolders(); for (SourceFolder sourceFolder : sourceFolders) { - if (virtualFile == sourceFolder.getFile()) return true; + if (Comparing.equal(virtualFile, sourceFolder.getFile())) return true; } } return false; diff --git a/platform/lang-impl/src/com/intellij/ide/projectView/impl/nodes/ProjectViewDirectoryHelper.java b/platform/lang-impl/src/com/intellij/ide/projectView/impl/nodes/ProjectViewDirectoryHelper.java index e621f8dac500..c897b0d765fd 100644 --- a/platform/lang-impl/src/com/intellij/ide/projectView/impl/nodes/ProjectViewDirectoryHelper.java +++ b/platform/lang-impl/src/com/intellij/ide/projectView/impl/nodes/ProjectViewDirectoryHelper.java @@ -113,7 +113,7 @@ public class ProjectViewDirectoryHelper { public boolean canRepresent(Object element, PsiDirectory directory) { if (element instanceof VirtualFile) { VirtualFile vFile = (VirtualFile) element; - return directory.getVirtualFile() == vFile; + return Comparing.equal(directory.getVirtualFile(), vFile); } return false; } @@ -180,7 +180,7 @@ public class ProjectViewDirectoryHelper { while (current != null) { VirtualFile parent = current.getParent(); - if (parent == dir) { + if (Comparing.equal(parent, dir)) { final PsiDirectory psi = manager.findDirectory(current); if (psi != null) { directoriesOnTheWayToContentRoots.add(psi); diff --git a/platform/lang-impl/src/com/intellij/ide/projectView/impl/nodes/PsiDirectoryNode.java b/platform/lang-impl/src/com/intellij/ide/projectView/impl/nodes/PsiDirectoryNode.java index 79c443dd51aa..9a35f9b89676 100644 --- a/platform/lang-impl/src/com/intellij/ide/projectView/impl/nodes/PsiDirectoryNode.java +++ b/platform/lang-impl/src/com/intellij/ide/projectView/impl/nodes/PsiDirectoryNode.java @@ -298,7 +298,7 @@ public class PsiDirectoryNode extends BasePsiNode implements Navig if (ProjectAttachProcessor.canAttachToProject()) { // primary module is always on top; attached modules are sorted alphabetically final VirtualFile file = getVirtualFile(); - if (file == myProject.getBaseDir()) { + if (Comparing.equal(file, myProject.getBaseDir())) { return ""; // sorts before any other name } return getTitle(); diff --git a/platform/lang-impl/src/com/intellij/ide/projectView/impl/nodes/PsiFileNode.java b/platform/lang-impl/src/com/intellij/ide/projectView/impl/nodes/PsiFileNode.java index b53889ffb4d1..d7258fcd05a4 100644 --- a/platform/lang-impl/src/com/intellij/ide/projectView/impl/nodes/PsiFileNode.java +++ b/platform/lang-impl/src/com/intellij/ide/projectView/impl/nodes/PsiFileNode.java @@ -26,6 +26,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.OrderEntry; import com.intellij.openapi.roots.libraries.LibraryUtil; import com.intellij.openapi.roots.ui.configuration.ProjectSettingsService; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Iconable; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.JarFileSystem; @@ -175,6 +176,6 @@ public class PsiFileNode extends BasePsiNode implements NavigatableWith @Override public boolean contains(@NotNull VirtualFile file) { - return super.contains(file) || isArchive() && PathUtil.getLocalFile(file) == getVirtualFile(); + return super.contains(file) || isArchive() && Comparing.equal(PathUtil.getLocalFile(file), getVirtualFile()); } } diff --git a/platform/lang-impl/src/com/intellij/lang/LanguagePerFileMappings.java b/platform/lang-impl/src/com/intellij/lang/LanguagePerFileMappings.java index d5a4fa9703f8..a5aa44f17a8a 100644 --- a/platform/lang-impl/src/com/intellij/lang/LanguagePerFileMappings.java +++ b/platform/lang-impl/src/com/intellij/lang/LanguagePerFileMappings.java @@ -21,6 +21,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.impl.FilePropertyPusher; import com.intellij.openapi.roots.impl.PushedFilePropertiesUpdater; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.testFramework.LightVirtualFile; @@ -76,7 +77,7 @@ public abstract class LanguagePerFileMappings implements PersistentStateCompo file = window.getDelegate(); } VirtualFile originalFile = file instanceof LightVirtualFile ? ((LightVirtualFile)file).getOriginalFile() : null; - if (originalFile == file) originalFile = null; + if (Comparing.equal(originalFile, file)) originalFile = null; if (file != null) { final FilePropertyPusher pusher = getFilePropertyPusher(); diff --git a/platform/lang-impl/src/com/intellij/openapi/fileEditor/impl/PsiAwareFileEditorManagerImpl.java b/platform/lang-impl/src/com/intellij/openapi/fileEditor/impl/PsiAwareFileEditorManagerImpl.java index 5118581eb4ef..bd6b2dece58e 100644 --- a/platform/lang-impl/src/com/intellij/openapi/fileEditor/impl/PsiAwareFileEditorManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/openapi/fileEditor/impl/PsiAwareFileEditorManagerImpl.java @@ -1,168 +1,170 @@ -/* - * Copyright 2000-2012 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.fileEditor.impl; - -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.fileEditor.impl.text.TextEditorPsiDataProvider; -import com.intellij.openapi.module.Module; -import com.intellij.openapi.module.ModuleUtil; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.problems.WolfTheProblemSolver; -import com.intellij.psi.*; -import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil; -import com.intellij.ui.ColorUtil; -import com.intellij.ui.docking.DockManager; -import org.jetbrains.annotations.NotNull; - -import java.awt.*; - -/** - * @author yole - */ -public class PsiAwareFileEditorManagerImpl extends FileEditorManagerImpl { - private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.fileEditor.impl.text.PsiAwareFileEditorManagerImpl"); - - private final PsiManager myPsiManager; - private final WolfTheProblemSolver myProblemSolver; - - /** - * Updates icons for open files when project roots change - */ - private final MyPsiTreeChangeListener myPsiTreeChangeListener; - private final WolfTheProblemSolver.ProblemListener myProblemListener; - - public PsiAwareFileEditorManagerImpl(final Project project, final PsiManager psiManager, final WolfTheProblemSolver problemSolver, DockManager dockManager) { - super(project, dockManager); - myPsiManager = psiManager; - myProblemSolver = problemSolver; - myPsiTreeChangeListener = new MyPsiTreeChangeListener(); - myProblemListener = new MyProblemListener(); - registerExtraEditorDataProvider(new TextEditorPsiDataProvider(), null); - } - - @Override - public void projectOpened() { - super.projectOpened(); //To change body of overridden methods use File | Settings | File Templates. - myPsiManager.addPsiTreeChangeListener(myPsiTreeChangeListener); - myProblemSolver.addProblemListener(myProblemListener); - } - - @Override - public Color getFileColor(@NotNull final VirtualFile file) { - Color color = super.getFileColor(file); - if (myProblemSolver.isProblemFile(file)) { - return ColorUtil.toAlpha(color, WaverGraphicsDecorator.WAVE_ALPHA_KEY); - } - return color; - } - - public boolean isProblem(@NotNull final VirtualFile file) { - return myProblemSolver.isProblemFile(file); - } - - public String getFileTooltipText(final VirtualFile file) { - final StringBuilder tooltipText = new StringBuilder(); - final Module module = ModuleUtil.findModuleForFile(file, getProject()); - if (module != null) { - tooltipText.append("["); - tooltipText.append(module.getName()); - tooltipText.append("] "); - } - tooltipText.append(FileUtil.getLocationRelativeToUserHome(file.getPresentableUrl())); - return tooltipText.toString(); - } - - @Override - protected Editor getOpenedEditor(final Editor editor, final boolean focusEditor) { - PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getProject()); - Document document = editor.getDocument(); - PsiFile psiFile = documentManager.getPsiFile(document); - if (!focusEditor || documentManager.isUncommited(document)) { - return editor; - } - - return InjectedLanguageUtil.getEditorForInjectedLanguageNoCommit(editor, psiFile); - } - - /** - * Updates attribute of open files when roots change - */ - private final class MyPsiTreeChangeListener extends PsiTreeChangeAdapter { - public void propertyChanged(@NotNull final PsiTreeChangeEvent e) { - if (PsiTreeChangeEvent.PROP_ROOTS.equals(e.getPropertyName())) { - ApplicationManager.getApplication().assertIsDispatchThread(); - final VirtualFile[] openFiles = getOpenFiles(); - for (int i = openFiles.length - 1; i >= 0; i--) { - final VirtualFile file = openFiles[i]; - LOG.assertTrue(file != null); - updateFileIcon(file); - } - } - } - - public void childAdded(@NotNull PsiTreeChangeEvent event) { - doChange(event); - } - - public void childRemoved(@NotNull PsiTreeChangeEvent event) { - doChange(event); - } - - public void childReplaced(@NotNull PsiTreeChangeEvent event) { - doChange(event); - } - - public void childMoved(@NotNull PsiTreeChangeEvent event) { - doChange(event); - } - - public void childrenChanged(@NotNull PsiTreeChangeEvent event) { - doChange(event); - } - - private void doChange(final PsiTreeChangeEvent event) { - final PsiFile psiFile = event.getFile(); - final VirtualFile currentFile = getCurrentFile(); - if (currentFile != null && psiFile != null && psiFile.getVirtualFile() == currentFile) { - updateFileIcon(currentFile); - } - } - } - - private class MyProblemListener extends WolfTheProblemSolver.ProblemListener { - public void problemsAppeared(final VirtualFile file) { - updateFile(file); - } - - public void problemsDisappeared(VirtualFile file) { - updateFile(file); - } - - public void problemsChanged(VirtualFile file) { - updateFile(file); - } - - private void updateFile(final VirtualFile file) { - queueUpdateFile(file); - } - } -} +/* + * Copyright 2000-2012 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.fileEditor.impl; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.fileEditor.impl.text.TextEditorPsiDataProvider; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleUtil; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.io.FileUtil; + +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.problems.WolfTheProblemSolver; +import com.intellij.psi.*; +import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil; +import com.intellij.ui.ColorUtil; +import com.intellij.ui.docking.DockManager; +import org.jetbrains.annotations.NotNull; + +import java.awt.*; + +/** + * @author yole + */ +public class PsiAwareFileEditorManagerImpl extends FileEditorManagerImpl { + private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.fileEditor.impl.text.PsiAwareFileEditorManagerImpl"); + + private final PsiManager myPsiManager; + private final WolfTheProblemSolver myProblemSolver; + + /** + * Updates icons for open files when project roots change + */ + private final MyPsiTreeChangeListener myPsiTreeChangeListener; + private final WolfTheProblemSolver.ProblemListener myProblemListener; + + public PsiAwareFileEditorManagerImpl(final Project project, final PsiManager psiManager, final WolfTheProblemSolver problemSolver, DockManager dockManager) { + super(project, dockManager); + myPsiManager = psiManager; + myProblemSolver = problemSolver; + myPsiTreeChangeListener = new MyPsiTreeChangeListener(); + myProblemListener = new MyProblemListener(); + registerExtraEditorDataProvider(new TextEditorPsiDataProvider(), null); + } + + @Override + public void projectOpened() { + super.projectOpened(); //To change body of overridden methods use File | Settings | File Templates. + myPsiManager.addPsiTreeChangeListener(myPsiTreeChangeListener); + myProblemSolver.addProblemListener(myProblemListener); + } + + @Override + public Color getFileColor(@NotNull final VirtualFile file) { + Color color = super.getFileColor(file); + if (myProblemSolver.isProblemFile(file)) { + return ColorUtil.toAlpha(color, WaverGraphicsDecorator.WAVE_ALPHA_KEY); + } + return color; + } + + public boolean isProblem(@NotNull final VirtualFile file) { + return myProblemSolver.isProblemFile(file); + } + + public String getFileTooltipText(final VirtualFile file) { + final StringBuilder tooltipText = new StringBuilder(); + final Module module = ModuleUtil.findModuleForFile(file, getProject()); + if (module != null) { + tooltipText.append("["); + tooltipText.append(module.getName()); + tooltipText.append("] "); + } + tooltipText.append(FileUtil.getLocationRelativeToUserHome(file.getPresentableUrl())); + return tooltipText.toString(); + } + + @Override + protected Editor getOpenedEditor(final Editor editor, final boolean focusEditor) { + PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getProject()); + Document document = editor.getDocument(); + PsiFile psiFile = documentManager.getPsiFile(document); + if (!focusEditor || documentManager.isUncommited(document)) { + return editor; + } + + return InjectedLanguageUtil.getEditorForInjectedLanguageNoCommit(editor, psiFile); + } + + /** + * Updates attribute of open files when roots change + */ + private final class MyPsiTreeChangeListener extends PsiTreeChangeAdapter { + public void propertyChanged(@NotNull final PsiTreeChangeEvent e) { + if (PsiTreeChangeEvent.PROP_ROOTS.equals(e.getPropertyName())) { + ApplicationManager.getApplication().assertIsDispatchThread(); + final VirtualFile[] openFiles = getOpenFiles(); + for (int i = openFiles.length - 1; i >= 0; i--) { + final VirtualFile file = openFiles[i]; + LOG.assertTrue(file != null); + updateFileIcon(file); + } + } + } + + public void childAdded(@NotNull PsiTreeChangeEvent event) { + doChange(event); + } + + public void childRemoved(@NotNull PsiTreeChangeEvent event) { + doChange(event); + } + + public void childReplaced(@NotNull PsiTreeChangeEvent event) { + doChange(event); + } + + public void childMoved(@NotNull PsiTreeChangeEvent event) { + doChange(event); + } + + public void childrenChanged(@NotNull PsiTreeChangeEvent event) { + doChange(event); + } + + private void doChange(final PsiTreeChangeEvent event) { + final PsiFile psiFile = event.getFile(); + final VirtualFile currentFile = getCurrentFile(); + if (currentFile != null && psiFile != null && Comparing.equal(psiFile.getVirtualFile(), currentFile)) { + updateFileIcon(currentFile); + } + } + } + + private class MyProblemListener extends WolfTheProblemSolver.ProblemListener { + public void problemsAppeared(final VirtualFile file) { + updateFile(file); + } + + public void problemsDisappeared(VirtualFile file) { + updateFile(file); + } + + public void problemsChanged(VirtualFile file) { + updateFile(file); + } + + private void updateFile(final VirtualFile file) { + queueUpdateFile(file); + } + } +} diff --git a/platform/lang-impl/src/com/intellij/openapi/fileEditor/impl/TestEditorManagerImpl.java b/platform/lang-impl/src/com/intellij/openapi/fileEditor/impl/TestEditorManagerImpl.java index 26ac8a341b86..44b42d3dfcc8 100644 --- a/platform/lang-impl/src/com/intellij/openapi/fileEditor/impl/TestEditorManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/openapi/fileEditor/impl/TestEditorManagerImpl.java @@ -33,6 +33,7 @@ import com.intellij.openapi.fileEditor.impl.text.TextEditorPsiDataProvider; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.ActionCallback; import com.intellij.openapi.util.AsyncResult; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; @@ -331,7 +332,7 @@ import java.util.Map; if (editor != null){ EditorFactory.getInstance().releaseEditor(editor); } - if (file == myActiveFile) myActiveFile = null; + if (Comparing.equal(file, myActiveFile)) myActiveFile = null; } @Override diff --git a/platform/lang-impl/src/com/intellij/openapi/vcs/changes/patch/PsiPatchBaseDirectoryDetector.java b/platform/lang-impl/src/com/intellij/openapi/vcs/changes/patch/PsiPatchBaseDirectoryDetector.java index 5910887431b7..99520def4ac9 100644 --- a/platform/lang-impl/src/com/intellij/openapi/vcs/changes/patch/PsiPatchBaseDirectoryDetector.java +++ b/platform/lang-impl/src/com/intellij/openapi/vcs/changes/patch/PsiPatchBaseDirectoryDetector.java @@ -17,6 +17,7 @@ package com.intellij.openapi.vcs.changes.patch; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiFile; @@ -47,7 +48,7 @@ public class PsiPatchBaseDirectoryDetector extends PatchBaseDirectoryDetector { if (psiFiles.length == 1) { PsiDirectory parent = psiFiles [0].getContainingDirectory(); for(int i=nameComponents.length-2; i >= 0; i--) { - if (!parent.getName().equals(nameComponents [i]) || parent.getVirtualFile() == myProject.getBaseDir()) { + if (!parent.getName().equals(nameComponents[i]) || Comparing.equal(parent.getVirtualFile(), myProject.getBaseDir())) { return new Result(parent.getVirtualFile().getPresentableUrl(), i+1); } parent = parent.getParentDirectory(); diff --git a/platform/lang-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesModuleGroupingPolicy.java b/platform/lang-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesModuleGroupingPolicy.java index c44bf20793d9..2bde879b0205 100644 --- a/platform/lang-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesModuleGroupingPolicy.java +++ b/platform/lang-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesModuleGroupingPolicy.java @@ -20,6 +20,7 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.Nullable; @@ -48,7 +49,7 @@ public class ChangesModuleGroupingPolicy implements ChangesGroupingPolicy { ProjectFileIndex index = ProjectRootManager.getInstance(myProject).getFileIndex(); VirtualFile vFile = node.getVf(); - if (vFile != null && vFile == index.getContentRootForFile(vFile)) { + if (vFile != null && Comparing.equal(vFile, index.getContentRootForFile(vFile))) { Module module = index.getModuleForFile(vFile); return getNodeForModule(module, rootNode); } diff --git a/platform/lang-impl/src/com/intellij/packageDependencies/ui/DirectoryNode.java b/platform/lang-impl/src/com/intellij/packageDependencies/ui/DirectoryNode.java index ff4f11f5e5e0..21ec2f02fcf1 100644 --- a/platform/lang-impl/src/com/intellij/packageDependencies/ui/DirectoryNode.java +++ b/platform/lang-impl/src/com/intellij/packageDependencies/ui/DirectoryNode.java @@ -21,6 +21,7 @@ import com.intellij.ide.projectView.impl.nodes.ProjectViewDirectoryHelper; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; @@ -61,12 +62,12 @@ public class DirectoryNode extends PackageDependenciesNode { if (showFQName) { final VirtualFile contentRoot = index.getContentRootForFile(myVDirectory); if (contentRoot != null) { - if (myVDirectory == contentRoot) { + if (Comparing.equal(myVDirectory, contentRoot)) { myFQName = dirName; } else { final VirtualFile sourceRoot = index.getSourceRootForFile(myVDirectory); - if (myVDirectory == sourceRoot) { + if (Comparing.equal(myVDirectory, sourceRoot)) { myFQName = VfsUtilCore.getRelativePath(myVDirectory, contentRoot, '/'); } else if (sourceRoot != null) { @@ -96,10 +97,11 @@ public class DirectoryNode extends PackageDependenciesNode { private String getContentRootName(final VirtualFile baseDir, final String dirName) { if (baseDir != null) { - if (myVDirectory != baseDir) { + if (!Comparing.equal(myVDirectory, baseDir)) { if (VfsUtil.isAncestor(baseDir, myVDirectory, false)) { return VfsUtilCore.getRelativePath(myVDirectory, baseDir, '/'); - } else { + } + else { return myVDirectory.getPresentableUrl(); } } @@ -140,7 +142,7 @@ public class DirectoryNode extends PackageDependenciesNode { final ProjectFileIndex index = ProjectRootManager.getInstance(myProject).getFileIndex(); VirtualFile directory = myVDirectory; VirtualFile contentRoot = index.getContentRootForFile(directory); - if (directory == contentRoot) { + if (Comparing.equal(directory, contentRoot)) { return ""; } if (contentRoot == null) { diff --git a/platform/lang-impl/src/com/intellij/packageDependencies/ui/FileTreeModelBuilder.java b/platform/lang-impl/src/com/intellij/packageDependencies/ui/FileTreeModelBuilder.java index 2d5ad682d109..ce552ade2d90 100644 --- a/platform/lang-impl/src/com/intellij/packageDependencies/ui/FileTreeModelBuilder.java +++ b/platform/lang-impl/src/com/intellij/packageDependencies/ui/FileTreeModelBuilder.java @@ -30,6 +30,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ContentIterator; import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VfsUtil; @@ -431,7 +432,7 @@ public class FileTreeModelBuilder { else if (node instanceof FileNode) { //non java files psiFile = ((PsiFile)node.getPsiElement()); } - if (psiFile != null && psiFile.getVirtualFile() == ((PsiFile)element).getVirtualFile()) { + if (psiFile != null && Comparing.equal(psiFile.getVirtualFile(), ((PsiFile)element).getVirtualFile())) { result.add(node); } } @@ -497,7 +498,7 @@ public class FileTreeModelBuilder { final VirtualFile directory = virtualFile.getParent(); if (!myFlattenPackages && directory != null) { - if (myCompactEmptyMiddlePackages && sourceRoot != virtualFile && contentRoot != virtualFile) {//compact + if (myCompactEmptyMiddlePackages && !Comparing.equal(sourceRoot, virtualFile) && !Comparing.equal(contentRoot, virtualFile)) {//compact ((DirectoryNode)directoryNode).setCompactedDirNode(childNode); } if (fileIndex.getModuleForFile(directory) == module) { @@ -505,7 +506,7 @@ public class FileTreeModelBuilder { if (parentDirectoryNode != null || !myCompactEmptyMiddlePackages || (sourceRoot != null && VfsUtil.isAncestor(directory, sourceRoot, false) && fileIndex.getSourceRootForFile(directory) != null) - || directory == contentRoot) { + || Comparing.equal(directory, contentRoot)) { getModuleDirNode(directory, module, (DirectoryNode)directoryNode).add(directoryNode); } else { @@ -517,15 +518,18 @@ public class FileTreeModelBuilder { } } else { - if (contentRoot == virtualFile) { + if (Comparing.equal(contentRoot, virtualFile)) { getModuleNode(module).add(directoryNode); - } else { + } + else { final VirtualFile root; - if (sourceRoot != virtualFile && sourceRoot != null) { + if (!Comparing.equal(sourceRoot, virtualFile) && sourceRoot != null) { root = sourceRoot; - } else if (contentRoot != null) { + } + else if (contentRoot != null) { root = contentRoot; - } else { + } + else { root = null; } if (root != null) { @@ -586,7 +590,7 @@ public class FileTreeModelBuilder { public boolean processFile(VirtualFile fileOrDir) { if (!fileOrDir.isDirectory()) { - if (lastParent != null && dir != fileOrDir.getParent()) { + if (lastParent != null && !Comparing.equal(dir, fileOrDir.getParent())) { lastParent = null; } lastParent = buildFileNode(fileOrDir, lastParent); diff --git a/platform/lang-impl/src/com/intellij/platform/ModuleAttachProcessor.java b/platform/lang-impl/src/com/intellij/platform/ModuleAttachProcessor.java index 188c848c7d8e..94da68fb6c47 100644 --- a/platform/lang-impl/src/com/intellij/platform/ModuleAttachProcessor.java +++ b/platform/lang-impl/src/com/intellij/platform/ModuleAttachProcessor.java @@ -29,6 +29,7 @@ import com.intellij.openapi.project.ex.ProjectManagerEx; import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.roots.ModuleRootModificationUtil; import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vcs.AbstractVcs; @@ -160,7 +161,7 @@ public class ModuleAttachProcessor extends ProjectAttachProcessor { for (Module module : ModuleManager.getInstance(project).getModules()) { final VirtualFile[] roots = ModuleRootManager.getInstance(module).getContentRoots(); for (VirtualFile root : roots) { - if (root == project.getBaseDir()) { + if (Comparing.equal(root, project.getBaseDir())) { return module; } } diff --git a/platform/lang-impl/src/com/intellij/platform/PlatformProjectViewStructureProvider.java b/platform/lang-impl/src/com/intellij/platform/PlatformProjectViewStructureProvider.java index bd2681310c9a..f277acd8c552 100644 --- a/platform/lang-impl/src/com/intellij/platform/PlatformProjectViewStructureProvider.java +++ b/platform/lang-impl/src/com/intellij/platform/PlatformProjectViewStructureProvider.java @@ -23,6 +23,7 @@ import com.intellij.ide.util.treeView.AbstractTreeNode; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ProjectFileIndex; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; @@ -43,7 +44,7 @@ public class PlatformProjectViewStructureProvider implements TreeStructureProvid public Collection modify(final AbstractTreeNode parent, final Collection children, final ViewSettings settings) { if (parent instanceof PsiDirectoryNode) { final VirtualFile vFile = ((PsiDirectoryNode)parent).getVirtualFile(); - if (vFile != null && ProjectFileIndex.SERVICE.getInstance(myProject).getContentRootForFile(vFile) == vFile) { + if (vFile != null && Comparing.equal(ProjectFileIndex.SERVICE.getInstance(myProject).getContentRootForFile(vFile), vFile)) { final Collection moduleChildren = ((PsiDirectoryNode) parent).getChildren(); Collection result = new ArrayList(); for (AbstractTreeNode moduleChild : moduleChildren) { diff --git a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/FileElementInfo.java b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/FileElementInfo.java index 8fd306cb49b1..628dd65c6005 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/FileElementInfo.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/FileElementInfo.java @@ -71,7 +71,7 @@ class FileElementInfo implements SmartPointerElementInfo { @Override public boolean pointsToTheSameElementAs(@NotNull SmartPointerElementInfo other) { if (other instanceof FileElementInfo) { - return myVirtualFile == ((FileElementInfo)other).myVirtualFile; + return Comparing.equal(myVirtualFile, ((FileElementInfo)other).myVirtualFile); } return Comparing.equal(restoreElement(), other.restoreElement()); } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SelfElementInfo.java b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SelfElementInfo.java index c8d1b63fb577..423d187dff4f 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SelfElementInfo.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SelfElementInfo.java @@ -286,12 +286,12 @@ public class SelfElementInfo implements SmartPointerElementInfo { public boolean pointsToTheSameElementAs(@NotNull SmartPointerElementInfo other) { if (other instanceof SelfElementInfo) { SelfElementInfo otherInfo = (SelfElementInfo)other; - return myVirtualFile == otherInfo.myVirtualFile - && myType == otherInfo.myType - && mySyncMarkerIsValid - && otherInfo.mySyncMarkerIsValid - && mySyncStartOffset == otherInfo.mySyncStartOffset - && mySyncEndOffset == otherInfo.mySyncEndOffset + return Comparing.equal(myVirtualFile, otherInfo.myVirtualFile) + && myType == otherInfo.myType + && mySyncMarkerIsValid + && otherInfo.mySyncMarkerIsValid + && mySyncStartOffset == otherInfo.mySyncStartOffset + && mySyncEndOffset == otherInfo.mySyncEndOffset ; } return Comparing.equal(restoreElement(), other.restoreElement()); diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/PsiFileReferenceHelper.java b/platform/lang-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/PsiFileReferenceHelper.java index fb8b400ebbcf..70430ac59874 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/PsiFileReferenceHelper.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/PsiFileReferenceHelper.java @@ -23,6 +23,7 @@ import com.intellij.openapi.module.ModuleUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.*; import com.intellij.openapi.roots.impl.DirectoryIndex; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; @@ -84,7 +85,7 @@ public class PsiFileReferenceHelper extends FileReferenceHelper { if (orderEntry instanceof ModuleSourceOrderEntry) { for(ContentEntry e: ((ModuleSourceOrderEntry)orderEntry).getRootModel().getContentEntries()) { for(SourceFolder sf:e.getSourceFolders()) { - if (sf.getFile() == root) { + if (Comparing.equal(sf.getFile(), root)) { String s = sf.getPackagePrefix(); if (s.length() > 0) { path = s + "." + path; diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/MultiHostRegistrarImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/MultiHostRegistrarImpl.java index d1ee84f27989..615c58974177 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/MultiHostRegistrarImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/MultiHostRegistrarImpl.java @@ -345,7 +345,10 @@ public class MultiHostRegistrarImpl implements MultiHostRegistrar, ModificationT assert documentWindow.getText().equals(psiFile.getText()) : "Document window text mismatch"; assert injectedFileViewProvider.getDocument() == documentWindow : "Provider document mismatch"; assert documentManager.getCachedDocument(psiFile) == documentWindow : "Cached document mismatch"; - assert psiFile.getVirtualFile() == injectedFileViewProvider.getVirtualFile() : "Virtual file mismatch: "+psiFile.getVirtualFile()+"; "+injectedFileViewProvider.getVirtualFile(); + assert Comparing.equal(psiFile.getVirtualFile(), injectedFileViewProvider.getVirtualFile()) : "Virtual file mismatch: " + + psiFile.getVirtualFile() + + "; " + + injectedFileViewProvider.getVirtualFile(); PsiDocumentManagerImpl.checkConsistency(psiFile, documentWindow); } diff --git a/platform/lang-impl/src/com/intellij/refactoring/rename/DirectoryAsPackageRenameHandlerBase.java b/platform/lang-impl/src/com/intellij/refactoring/rename/DirectoryAsPackageRenameHandlerBase.java index 5e1d334817f7..d826950a525b 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/rename/DirectoryAsPackageRenameHandlerBase.java +++ b/platform/lang-impl/src/com/intellij/refactoring/rename/DirectoryAsPackageRenameHandlerBase.java @@ -29,6 +29,7 @@ import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; @@ -67,7 +68,7 @@ public abstract class DirectoryAsPackageRenameHandlerBase extends TreeTable { TreeNode child = root.getChildAt(i); VirtualFile file = ((FileNode)child).getObject(); if (VfsUtil.isAncestor(file, toSelect, false)) { - if (file == toSelect) { + if (Comparing.equal(file, toSelect)) { TreeUtil.selectNode(getTree(), child); getSelectionModel().clearSelection(); addSelectedPath(TreeUtil.getPathFromRoot(child)); diff --git a/platform/lvcs-impl/src/com/intellij/history/integration/IdeaGateway.java b/platform/lvcs-impl/src/com/intellij/history/integration/IdeaGateway.java index 4b13d2a3929c..18308cb9e751 100644 --- a/platform/lvcs-impl/src/com/intellij/history/integration/IdeaGateway.java +++ b/platform/lvcs-impl/src/com/intellij/history/integration/IdeaGateway.java @@ -32,6 +32,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.util.Clock; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.*; @@ -60,7 +61,7 @@ public class IdeaGateway { for (Project each : openProjects) { if (each.isDefault()) continue; if (!each.isInitialized()) continue; - if (each.getWorkspaceFile() == f) return false; + if (Comparing.equal(each.getWorkspaceFile(), f)) return false; if (ProjectRootManager.getInstance(each).getFileIndex().isIgnored(f)) return false; } diff --git a/platform/lvcs-impl/src/com/intellij/history/integration/revertion/UndoChangeRevertingVisitor.java b/platform/lvcs-impl/src/com/intellij/history/integration/revertion/UndoChangeRevertingVisitor.java index 8c6a3c7cdb78..3d2edf2f2988 100644 --- a/platform/lvcs-impl/src/com/intellij/history/integration/revertion/UndoChangeRevertingVisitor.java +++ b/platform/lvcs-impl/src/com/intellij/history/integration/revertion/UndoChangeRevertingVisitor.java @@ -25,6 +25,7 @@ import com.intellij.history.integration.IdeaGateway; import com.intellij.openapi.command.impl.DocumentUndoProvider; import com.intellij.openapi.editor.Document; import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.containers.HashSet; @@ -101,7 +102,7 @@ public class UndoChangeRevertingVisitor extends ChangeVisitor { if (f != null) { VirtualFile existing = f.getParent().findChild(c.getOldName()); try { - if (existing != null && existing != f) { + if (existing != null && !Comparing.equal(existing, f)) { existing.delete(LocalHistory.VFS_EVENT_REQUESTOR); } f.rename(LocalHistory.VFS_EVENT_REQUESTOR, c.getOldName()); diff --git a/platform/platform-api/src/com/intellij/openapi/editor/LazyRangeMarkerFactory.java b/platform/platform-api/src/com/intellij/openapi/editor/LazyRangeMarkerFactory.java index 331b18d35770..96826cd917de 100644 --- a/platform/platform-api/src/com/intellij/openapi/editor/LazyRangeMarkerFactory.java +++ b/platform/platform-api/src/com/intellij/openapi/editor/LazyRangeMarkerFactory.java @@ -22,6 +22,7 @@ import com.intellij.openapi.editor.event.DocumentAdapter; import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.UserDataHolderBase; import com.intellij.openapi.vfs.VirtualFile; @@ -53,7 +54,7 @@ public class LazyRangeMarkerFactory extends AbstractProjectComponent { List markers = lazyMarkers.toStrongList(); List markersToRemove = new ArrayList(); for (final LazyMarker marker : markers) { - if (marker.getFile() == docFile) { + if (Comparing.equal(marker.getFile(), docFile)) { marker.ensureDelegate(); markersToRemove.add(marker); } diff --git a/platform/platform-api/src/com/intellij/openapi/fileChooser/FileElement.java b/platform/platform-api/src/com/intellij/openapi/fileChooser/FileElement.java index 8e61b7327f80..6b15e4a3dad7 100644 --- a/platform/platform-api/src/com/intellij/openapi/fileChooser/FileElement.java +++ b/platform/platform-api/src/com/intellij/openapi/fileChooser/FileElement.java @@ -16,6 +16,7 @@ package com.intellij.openapi.fileChooser; import com.intellij.openapi.fileTypes.FileTypes; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.vfs.JarFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.StringBuilderSpinAllocator; @@ -84,7 +85,7 @@ public class FileElement { @Override public boolean equals(Object obj) { if (obj instanceof FileElement) { - if (((FileElement)obj).myFile == myFile) return true; + if (Comparing.equal(((FileElement)obj).myFile, myFile)) return true; } return false; } diff --git a/platform/platform-api/src/com/intellij/openapi/fileEditor/OpenFileDescriptor.java b/platform/platform-api/src/com/intellij/openapi/fileEditor/OpenFileDescriptor.java index 78464b10d14a..c15570283dae 100644 --- a/platform/platform-api/src/com/intellij/openapi/fileEditor/OpenFileDescriptor.java +++ b/platform/platform-api/src/com/intellij/openapi/fileEditor/OpenFileDescriptor.java @@ -1,264 +1,265 @@ -/* - * 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.fileEditor; - -import com.intellij.ide.*; -import com.intellij.ide.FileEditorProvider; -import com.intellij.openapi.actionSystem.DataContext; -import com.intellij.openapi.actionSystem.DataKey; -import com.intellij.openapi.editor.*; -import com.intellij.openapi.fileTypes.FileType; -import com.intellij.openapi.fileTypes.FileTypeManager; -import com.intellij.openapi.fileTypes.INativeFileType; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.TextRange; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.wm.IdeFocusManager; -import com.intellij.pom.Navigatable; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.List; - -public class OpenFileDescriptor implements Navigatable { - /** - * Tells descriptor to navigate in specific editor rather than file editor - * in main IDEA window. - * For example if you want to navigate in editor embedded into modal dialog, - * you should provide this data. - */ - public static final DataKey NAVIGATE_IN_EDITOR = DataKey.create("NAVIGATE_IN_EDITOR"); - - @NotNull - private final VirtualFile myFile; - private final int myOffset; - private final int myLogicalLine; - private final int myLogicalColumn; - private final RangeMarker myRangeMarker; - @NotNull - private final Project myProject; - - private boolean myUseCurrentWindow = false; - - public OpenFileDescriptor(@NotNull Project project, @NotNull VirtualFile file, int offset) { - this(project, file, -1, -1, offset, false); - } - - public OpenFileDescriptor(@NotNull Project project, @NotNull VirtualFile file, int logicalLine, int logicalColumn) { - this(project, file, logicalLine, logicalColumn, -1, false); - } - - public OpenFileDescriptor(@NotNull Project project, @NotNull VirtualFile file, - int logicalLine, int logicalColumn, boolean persistent) { - this(project, file, logicalLine, logicalColumn, -1, persistent); - } - - public OpenFileDescriptor(@NotNull Project project, @NotNull VirtualFile file) { - this(project, file, -1, -1, -1, false); - } - - private OpenFileDescriptor(@NotNull Project project, @NotNull VirtualFile file, - int logicalLine, int logicalColumn, int offset, boolean persistent) { - myProject = project; - - myFile = file; - myLogicalLine = logicalLine; - myLogicalColumn = logicalColumn; - myOffset = offset; - if (offset >= 0) { - myRangeMarker = LazyRangeMarkerFactory.getInstance(project).createRangeMarker(file, offset); - } - else if (logicalLine >= 0 ){ - myRangeMarker = LazyRangeMarkerFactory.getInstance(project).createRangeMarker(file, logicalLine, Math.max(0, logicalColumn), persistent); - } - else { - myRangeMarker = null; - } - } - - @NotNull - public VirtualFile getFile() { - return myFile; - } - - @Nullable - public RangeMarker getRangeMarker() { - return myRangeMarker; - } - - public int getOffset() { - return myRangeMarker != null && myRangeMarker.isValid() ? myRangeMarker.getStartOffset() : myOffset; - } - - public int getLine() { - return myLogicalLine; - } - - public int getColumn() { - return myLogicalColumn; - } - - @Override - public void navigate(boolean requestFocus) { - if (!canNavigate()) { - throw new IllegalStateException("Navigation is not possible with null project"); - } - - if (!myFile.isDirectory() && navigateInEditor(myProject, requestFocus)) return; - - navigateInProjectView(); - } - - private boolean navigateInEditor(@NotNull Project project, boolean requestFocus) { - FileType type = FileTypeManager.getInstance().getKnownFileTypeOrAssociate(myFile,project); - if (type == null || !myFile.isValid()) return false; - - if (type instanceof INativeFileType) { - return ((INativeFileType) type).openFileInAssociatedApplication(project, myFile); - } - - return navigateInRequestedEditor() || navigateInAnyFileEditor(project, requestFocus); - } - - private boolean navigateInRequestedEditor() { - DataContext ctx = DataManager.getInstance().getDataContext(); - Editor e = NAVIGATE_IN_EDITOR.getData(ctx); - if (e == null) return false; - if (FileDocumentManager.getInstance().getFile(e.getDocument()) != myFile) return false; - - navigateIn(e); - return true; - } - - private boolean navigateInAnyFileEditor(Project project, boolean focusEditor) { - List editors = FileEditorManager.getInstance(project).openEditor(this, focusEditor); - for (FileEditor editor : editors) { - if (editor instanceof TextEditor) { - Editor e = ((TextEditor)editor).getEditor(); - unfoldCurrentLine(e); - if (focusEditor) { - IdeFocusManager.getInstance(myProject).requestFocus(e.getContentComponent(), true); - } - } - } - return !editors.isEmpty(); - } - - private void navigateInProjectView() { - SelectInContext context = new SelectInContext() { - @Override - @NotNull - public Project getProject() { - return myProject; - } - - @Override - @NotNull - public VirtualFile getVirtualFile() { - return myFile; - } - - @Override - @Nullable - public Object getSelectorInFile() { - return null; - } - - @Override - @Nullable - public FileEditorProvider getFileEditorProvider() { - return null; - } - }; - - for (SelectInTarget target : SelectInManager.getInstance(myProject).getTargets()) { - if (target.canSelect(context)) { - target.selectIn(context, true); - return; - } - } - } - - public void navigateIn(@NotNull Editor e) { - final int offset = getOffset(); - CaretModel caretModel = e.getCaretModel(); - boolean caretMoved = false; - if (myLogicalLine >= 0) { - LogicalPosition pos = new LogicalPosition(myLogicalLine, Math.max(myLogicalColumn, 0)); - if (offset < 0 || offset == e.logicalPositionToOffset(pos)) { - caretModel.moveToLogicalPosition(pos); - caretMoved = true; - } - } - if (!caretMoved && offset >= 0) { - caretModel.moveToOffset(Math.min(offset, e.getDocument().getTextLength())); - caretMoved = true; - } - - if (caretMoved) { - e.getSelectionModel().removeSelection(); - scrollToCaret(e); - unfoldCurrentLine(e); - } - } - - private static void unfoldCurrentLine(@NotNull final Editor editor) { - final FoldRegion[] allRegions = editor.getFoldingModel().getAllFoldRegions(); - final int offset = editor.getCaretModel().getOffset(); - int line = editor.getDocument().getLineNumber(offset); - int start = editor.getDocument().getLineStartOffset(line); - int end = editor.getDocument().getLineEndOffset(line); - final TextRange range = new TextRange(start, end); - editor.getFoldingModel().runBatchFoldingOperation(new Runnable() { - @Override - public void run() { - for (FoldRegion region : allRegions) { - if (!region.isExpanded() && range.intersects(TextRange.create(region))) { - region.setExpanded(true); - } - } - } - }); - } - - private static void scrollToCaret(@NotNull Editor e) { - e.getScrollingModel().scrollToCaret(ScrollType.CENTER); - } - - @Override - public boolean canNavigate() { - return myFile.isValid(); - } - - @Override - public boolean canNavigateToSource() { - return canNavigate(); - } - - @NotNull - public Project getProject() { - return myProject; - } - - public OpenFileDescriptor setUseCurrentWindow(boolean search) { - myUseCurrentWindow = search; - return this; - } - - public boolean isUseCurrentWindow() { - return myUseCurrentWindow; - } -} +/* + * 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.fileEditor; + +import com.intellij.ide.*; +import com.intellij.ide.FileEditorProvider; +import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.actionSystem.DataKey; +import com.intellij.openapi.editor.*; +import com.intellij.openapi.fileTypes.FileType; +import com.intellij.openapi.fileTypes.FileTypeManager; +import com.intellij.openapi.fileTypes.INativeFileType; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.wm.IdeFocusManager; +import com.intellij.pom.Navigatable; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class OpenFileDescriptor implements Navigatable { + /** + * Tells descriptor to navigate in specific editor rather than file editor + * in main IDEA window. + * For example if you want to navigate in editor embedded into modal dialog, + * you should provide this data. + */ + public static final DataKey NAVIGATE_IN_EDITOR = DataKey.create("NAVIGATE_IN_EDITOR"); + + @NotNull + private final VirtualFile myFile; + private final int myOffset; + private final int myLogicalLine; + private final int myLogicalColumn; + private final RangeMarker myRangeMarker; + @NotNull + private final Project myProject; + + private boolean myUseCurrentWindow = false; + + public OpenFileDescriptor(@NotNull Project project, @NotNull VirtualFile file, int offset) { + this(project, file, -1, -1, offset, false); + } + + public OpenFileDescriptor(@NotNull Project project, @NotNull VirtualFile file, int logicalLine, int logicalColumn) { + this(project, file, logicalLine, logicalColumn, -1, false); + } + + public OpenFileDescriptor(@NotNull Project project, @NotNull VirtualFile file, + int logicalLine, int logicalColumn, boolean persistent) { + this(project, file, logicalLine, logicalColumn, -1, persistent); + } + + public OpenFileDescriptor(@NotNull Project project, @NotNull VirtualFile file) { + this(project, file, -1, -1, -1, false); + } + + private OpenFileDescriptor(@NotNull Project project, @NotNull VirtualFile file, + int logicalLine, int logicalColumn, int offset, boolean persistent) { + myProject = project; + + myFile = file; + myLogicalLine = logicalLine; + myLogicalColumn = logicalColumn; + myOffset = offset; + if (offset >= 0) { + myRangeMarker = LazyRangeMarkerFactory.getInstance(project).createRangeMarker(file, offset); + } + else if (logicalLine >= 0 ){ + myRangeMarker = LazyRangeMarkerFactory.getInstance(project).createRangeMarker(file, logicalLine, Math.max(0, logicalColumn), persistent); + } + else { + myRangeMarker = null; + } + } + + @NotNull + public VirtualFile getFile() { + return myFile; + } + + @Nullable + public RangeMarker getRangeMarker() { + return myRangeMarker; + } + + public int getOffset() { + return myRangeMarker != null && myRangeMarker.isValid() ? myRangeMarker.getStartOffset() : myOffset; + } + + public int getLine() { + return myLogicalLine; + } + + public int getColumn() { + return myLogicalColumn; + } + + @Override + public void navigate(boolean requestFocus) { + if (!canNavigate()) { + throw new IllegalStateException("Navigation is not possible with null project"); + } + + if (!myFile.isDirectory() && navigateInEditor(myProject, requestFocus)) return; + + navigateInProjectView(); + } + + private boolean navigateInEditor(@NotNull Project project, boolean requestFocus) { + FileType type = FileTypeManager.getInstance().getKnownFileTypeOrAssociate(myFile,project); + if (type == null || !myFile.isValid()) return false; + + if (type instanceof INativeFileType) { + return ((INativeFileType) type).openFileInAssociatedApplication(project, myFile); + } + + return navigateInRequestedEditor() || navigateInAnyFileEditor(project, requestFocus); + } + + private boolean navigateInRequestedEditor() { + DataContext ctx = DataManager.getInstance().getDataContext(); + Editor e = NAVIGATE_IN_EDITOR.getData(ctx); + if (e == null) return false; + if (!Comparing.equal(FileDocumentManager.getInstance().getFile(e.getDocument()), myFile)) return false; + + navigateIn(e); + return true; + } + + private boolean navigateInAnyFileEditor(Project project, boolean focusEditor) { + List editors = FileEditorManager.getInstance(project).openEditor(this, focusEditor); + for (FileEditor editor : editors) { + if (editor instanceof TextEditor) { + Editor e = ((TextEditor)editor).getEditor(); + unfoldCurrentLine(e); + if (focusEditor) { + IdeFocusManager.getInstance(myProject).requestFocus(e.getContentComponent(), true); + } + } + } + return !editors.isEmpty(); + } + + private void navigateInProjectView() { + SelectInContext context = new SelectInContext() { + @Override + @NotNull + public Project getProject() { + return myProject; + } + + @Override + @NotNull + public VirtualFile getVirtualFile() { + return myFile; + } + + @Override + @Nullable + public Object getSelectorInFile() { + return null; + } + + @Override + @Nullable + public FileEditorProvider getFileEditorProvider() { + return null; + } + }; + + for (SelectInTarget target : SelectInManager.getInstance(myProject).getTargets()) { + if (target.canSelect(context)) { + target.selectIn(context, true); + return; + } + } + } + + public void navigateIn(@NotNull Editor e) { + final int offset = getOffset(); + CaretModel caretModel = e.getCaretModel(); + boolean caretMoved = false; + if (myLogicalLine >= 0) { + LogicalPosition pos = new LogicalPosition(myLogicalLine, Math.max(myLogicalColumn, 0)); + if (offset < 0 || offset == e.logicalPositionToOffset(pos)) { + caretModel.moveToLogicalPosition(pos); + caretMoved = true; + } + } + if (!caretMoved && offset >= 0) { + caretModel.moveToOffset(Math.min(offset, e.getDocument().getTextLength())); + caretMoved = true; + } + + if (caretMoved) { + e.getSelectionModel().removeSelection(); + scrollToCaret(e); + unfoldCurrentLine(e); + } + } + + private static void unfoldCurrentLine(@NotNull final Editor editor) { + final FoldRegion[] allRegions = editor.getFoldingModel().getAllFoldRegions(); + final int offset = editor.getCaretModel().getOffset(); + int line = editor.getDocument().getLineNumber(offset); + int start = editor.getDocument().getLineStartOffset(line); + int end = editor.getDocument().getLineEndOffset(line); + final TextRange range = new TextRange(start, end); + editor.getFoldingModel().runBatchFoldingOperation(new Runnable() { + @Override + public void run() { + for (FoldRegion region : allRegions) { + if (!region.isExpanded() && range.intersects(TextRange.create(region))) { + region.setExpanded(true); + } + } + } + }); + } + + private static void scrollToCaret(@NotNull Editor e) { + e.getScrollingModel().scrollToCaret(ScrollType.CENTER); + } + + @Override + public boolean canNavigate() { + return myFile.isValid(); + } + + @Override + public boolean canNavigateToSource() { + return canNavigate(); + } + + @NotNull + public Project getProject() { + return myProject; + } + + public OpenFileDescriptor setUseCurrentWindow(boolean search) { + myUseCurrentWindow = search; + return this; + } + + public boolean isUseCurrentWindow() { + return myUseCurrentWindow; + } +} diff --git a/platform/platform-api/src/com/intellij/openapi/vfs/VfsUtil.java b/platform/platform-api/src/com/intellij/openapi/vfs/VfsUtil.java index 38675fe45e03..d81f23417e75 100644 --- a/platform/platform-api/src/com/intellij/openapi/vfs/VfsUtil.java +++ b/platform/platform-api/src/com/intellij/openapi/vfs/VfsUtil.java @@ -20,6 +20,7 @@ import com.intellij.openapi.application.WriteAction; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.fileTypes.FileTypeManager; import com.intellij.openapi.fileTypes.FileTypes; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; @@ -473,8 +474,8 @@ public class VfsUtil extends VfsUtilCore { final VirtualFile commonAncestor = getCommonAncestor(src, dst); if (commonAncestor != null) { StringBuilder buffer = new StringBuilder(); - if (src != commonAncestor) { - while (src.getParent() != commonAncestor) { + if (!Comparing.equal(src, commonAncestor)) { + while (!Comparing.equal(src.getParent(), commonAncestor)) { buffer.append("..").append(separatorChar); src = src.getParent(); } diff --git a/platform/platform-impl/src/com/intellij/ide/actions/CloseAllEditorsButActiveAction.java b/platform/platform-impl/src/com/intellij/ide/actions/CloseAllEditorsButActiveAction.java index 443a10030ec2..683e205d644e 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/CloseAllEditorsButActiveAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/CloseAllEditorsButActiveAction.java @@ -1,4 +1,3 @@ - /* * Copyright 2000-2009 JetBrains s.r.o. * @@ -24,6 +23,7 @@ import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx; import com.intellij.openapi.fileEditor.impl.EditorWindow; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.vfs.VirtualFile; public class CloseAllEditorsButActiveAction extends AnAction implements DumbAware { @@ -39,7 +39,7 @@ public class CloseAllEditorsButActiveAction extends AnAction implements DumbAwar selectedFile = fileEditorManager.getSelectedFiles()[0]; final VirtualFile[] siblings = fileEditorManager.getSiblings(selectedFile); for (final VirtualFile sibling : siblings) { - if (selectedFile != sibling) { + if (!Comparing.equal(selectedFile, sibling)) { fileEditorManager.closeFile(sibling); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/command/impl/DocumentReferenceManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/command/impl/DocumentReferenceManagerImpl.java index a53830f241b7..c5405f1e9dc3 100644 --- a/platform/platform-impl/src/com/intellij/openapi/command/impl/DocumentReferenceManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/command/impl/DocumentReferenceManagerImpl.java @@ -21,6 +21,7 @@ import com.intellij.openapi.command.undo.DocumentReferenceManager; import com.intellij.openapi.components.ApplicationComponent; import com.intellij.openapi.editor.Document; import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Key; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileAdapter; @@ -75,7 +76,7 @@ public class DocumentReferenceManagerImpl extends DocumentReferenceManager imple List files = f.getUserData(DELETED_FILES); f.putUserData(DELETED_FILES, null); - assert files != null; + assert files != null : f; for (VirtualFile each : files) { Reference r = new WeakReferenceWithEquals(each); DocumentReference ref = myFileToRef.remove(r); @@ -159,7 +160,7 @@ public class DocumentReferenceManagerImpl extends DocumentReferenceManager imple @Override public boolean equals(Object obj) { T doc = get(); - return doc != null && obj instanceof Reference && ((Reference)obj).get() == doc; + return doc != null && obj instanceof Reference && Comparing.equal(doc, ((Reference)obj).get()); } } } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorComposite.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorComposite.java index 33c8e1d104b5..bedb82bbca22 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorComposite.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorComposite.java @@ -181,7 +181,7 @@ public abstract class EditorComposite implements Disposable { public void selectionChanged(final FileEditorManagerEvent event) { final VirtualFile oldFile = event.getOldFile(); final VirtualFile newFile = event.getNewFile(); - if (oldFile == newFile && getFile() == newFile) { + if (Comparing.equal(oldFile, newFile) && Comparing.equal(getFile(), newFile)) { final FileEditor oldEditor = event.getOldEditor(); if (oldEditor != null) oldEditor.deselectNotify(); final FileEditor newEditor = event.getNewEditor(); diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorHistoryManager.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorHistoryManager.java index 03791c9477d5..5380ee9ec8fa 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorHistoryManager.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorHistoryManager.java @@ -26,6 +26,7 @@ import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.project.DumbAwareRunnable; import com.intellij.openapi.project.Project; import com.intellij.openapi.startup.StartupManager; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.JDOMExternalizable; import com.intellij.openapi.util.Pair; @@ -254,7 +255,7 @@ public final class EditorHistoryManager extends AbstractProjectComponent impleme public boolean hasBeenOpen(@NotNull VirtualFile f) { for (HistoryEntry each : myEntriesList) { - if (each.myFile == f) return true; + if (Comparing.equal(each.myFile, f)) return true; } return false; } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindow.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindow.java index c1cfae48f8ee..81d51d770538 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindow.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindow.java @@ -122,7 +122,7 @@ public class EditorWindow { public void closeAllExcept(final VirtualFile selectedFile) { final VirtualFile[] files = getFiles(); for (final VirtualFile file : files) { - if (file != selectedFile && !isFilePinned(file)) { + if (!Comparing.equal(file, selectedFile) && !isFilePinned(file)) { closeFile(file); } } @@ -1090,7 +1090,7 @@ public class EditorWindow { if (fileCanBeClosed(file, fileToIgnore)) { boolean found = false; for (int j = 0; j != histFiles.length; j++) { - if (histFiles[j] == file) { + if (Comparing.equal(histFiles[j], file)) { found = true; break; } @@ -1146,7 +1146,7 @@ public class EditorWindow { if (fileCanBeClosed(file, fileToIgnore)) { boolean found = false; for (int j = 0; j != histFiles.length; j++) { - if (histFiles[j] == file) { + if (Comparing.equal(histFiles[j], file)) { found = true; break; } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java index e5c2c5eb7991..1afc02766e6b 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java @@ -387,7 +387,7 @@ public class EditorsSplitters extends JPanel { final VirtualFile currentFile = getCurrentFile(); if (currentFile != null) { for (int i = 0; i != virtualFiles.length; ++i) { - if (virtualFiles[i] == currentFile) { + if (Comparing.equal(virtualFiles[i], currentFile)) { virtualFiles[i] = virtualFiles[0]; virtualFiles[0] = currentFile; break; @@ -572,7 +572,7 @@ public class EditorsSplitters extends JPanel { for (int i = 0; i != windows.length; ++i) { final VirtualFile[] files = windows[i].getFiles(); for (final VirtualFile fileAt : files) { - if (fileAt != file) { + if (!Comparing.equal(fileAt, file)) { return fileAt; } } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java index a12860e54b64..a050a9455940 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java @@ -1,1801 +1,1801 @@ -/* - * Copyright 2000-2012 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.fileEditor.impl; - -import com.intellij.ProjectTopics; -import com.intellij.ide.IdeBundle; -import com.intellij.ide.plugins.PluginManager; -import com.intellij.ide.ui.UISettings; -import com.intellij.ide.ui.UISettingsListener; -import com.intellij.injected.editor.VirtualFileWindow; -import com.intellij.openapi.Disposable; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.ModalityState; -import com.intellij.openapi.application.ex.ApplicationManagerEx; -import com.intellij.openapi.application.impl.LaterInvocator; -import com.intellij.openapi.command.CommandProcessor; -import com.intellij.openapi.components.ProjectComponent; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.ScrollType; -import com.intellij.openapi.editor.ex.EditorEx; -import com.intellij.openapi.editor.impl.EditorComponentImpl; -import com.intellij.openapi.extensions.Extensions; -import com.intellij.openapi.fileEditor.*; -import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx; -import com.intellij.openapi.fileEditor.ex.FileEditorProviderManager; -import com.intellij.openapi.fileEditor.ex.IdeDocumentHistory; -import com.intellij.openapi.fileEditor.impl.text.TextEditorImpl; -import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider; -import com.intellij.openapi.fileTypes.FileTypeEvent; -import com.intellij.openapi.fileTypes.FileTypeListener; -import com.intellij.openapi.fileTypes.FileTypeManager; -import com.intellij.openapi.project.DumbAwareRunnable; -import com.intellij.openapi.project.DumbService; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.project.impl.ProjectImpl; -import com.intellij.openapi.roots.ModuleRootAdapter; -import com.intellij.openapi.roots.ModuleRootEvent; -import com.intellij.openapi.startup.StartupManager; -import com.intellij.openapi.util.*; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.util.registry.Registry; -import com.intellij.openapi.vcs.FileStatus; -import com.intellij.openapi.vcs.FileStatusListener; -import com.intellij.openapi.vcs.FileStatusManager; -import com.intellij.openapi.vfs.*; -import com.intellij.openapi.wm.IdeFocusManager; -import com.intellij.openapi.wm.ToolWindowManager; -import com.intellij.openapi.wm.WindowManager; -import com.intellij.openapi.wm.ex.StatusBarEx; -import com.intellij.openapi.wm.impl.IdeFrameImpl; -import com.intellij.ui.FocusTrackback; -import com.intellij.ui.docking.DockContainer; -import com.intellij.ui.docking.DockManager; -import com.intellij.ui.tabs.impl.JBTabsImpl; -import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.messages.MessageBusConnection; -import com.intellij.util.messages.impl.MessageListenerList; -import com.intellij.util.ui.SameColor; -import com.intellij.util.ui.UIUtil; -import com.intellij.util.ui.update.MergingUpdateQueue; -import com.intellij.util.ui.update.Update; -import org.jdom.Element; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; -import javax.swing.border.Border; -import java.awt.*; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.lang.ref.WeakReference; -import java.util.*; -import java.util.List; - -/** - * @author Anton Katilin - * @author Eugene Belyaev - * @author Vladimir Kondratyev - */ -public class FileEditorManagerImpl extends FileEditorManagerEx implements ProjectComponent, JDOMExternalizable { - private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl"); - private static final Key WATCH_REQUEST_KEY = Key.create("WATCH_REQUEST_KEY"); - private static final Key DUMB_AWARE = Key.create("DUMB_AWARE"); - - private static final FileEditor[] EMPTY_EDITOR_ARRAY = {}; - private static final FileEditorProvider[] EMPTY_PROVIDER_ARRAY = {}; - public static final Key CLOSING_TO_REOPEN = Key.create("CLOSING_TO_REOPEN"); - - private volatile JPanel myPanels; - private EditorsSplitters mySplitters; - private final Project myProject; - private final List> mySelectionHistory = new ArrayList>(); - private WeakReference myLastSelectedComposite = new WeakReference(null); - - - private final MergingUpdateQueue myQueue = new MergingUpdateQueue("FileEditorManagerUpdateQueue", 50, true, null); - - private final BusyObject.Impl.Simple myBusyObject = new BusyObject.Impl.Simple(); - - /** - * Removes invalid myEditor and updates "modified" status. - */ - private final MyEditorPropertyChangeListener myEditorPropertyChangeListener = new MyEditorPropertyChangeListener(); - private final DockManager myDockManager; - private DockableEditorContainerFactory myContentFactory; - - public FileEditorManagerImpl(final Project project, DockManager dockManager) { -/* ApplicationManager.getApplication().assertIsDispatchThread(); */ - myProject = project; - myDockManager = dockManager; - myListenerList = - new MessageListenerList(myProject.getMessageBus(), FileEditorManagerListener.FILE_EDITOR_MANAGER); - - if (Extensions.getExtensions(FileEditorAssociateFinder.EP_NAME).length > 0) { - myListenerList.add(new FileEditorManagerAdapter() { - @Override - public void selectionChanged(FileEditorManagerEvent event) { - EditorsSplitters splitters = getSplitters(); - openAssociatedFile(event.getNewFile(), splitters.getCurrentWindow(), splitters); - } - }); - } - - myQueue.setTrackUiActivity(true); - } - - void initDockableContentFactory() { - if (myContentFactory != null) return; - - myContentFactory = new DockableEditorContainerFactory(myProject, this, myDockManager); - myDockManager.register(DockableEditorContainerFactory.TYPE, myContentFactory); - Disposer.register(myProject, myContentFactory); - } - - public static boolean isDumbAware(FileEditor editor) { - return Boolean.TRUE.equals(editor.getUserData(DUMB_AWARE)); - } - - //------------------------------------------------------------------------------- - - public JComponent getComponent() { - initUI(); - return myPanels; - } - - public EditorsSplitters getMainSplitters() { - initUI(); - - return mySplitters; - } - - public Set getAllSplitters() { - HashSet all = new HashSet(); - all.add(getMainSplitters()); - Set dockContainers = myDockManager.getContainers(); - for (DockContainer each : dockContainers) { - if (each instanceof DockableEditorTabbedContainer) { - all.add(((DockableEditorTabbedContainer)each).getSplitters()); - } - } - - return Collections.unmodifiableSet(all); - } - - private AsyncResult getActiveSplitters(boolean syncUsage) { - final boolean async = Registry.is("ide.windowSystem.asyncSplitters") && !syncUsage; - - final AsyncResult result = new AsyncResult(); - final IdeFocusManager fm = IdeFocusManager.getInstance(myProject); - Runnable run = new Runnable() { - @Override - public void run() { - if (myProject.isDisposed()) { - result.setRejected(); - return; - } - - Component focusOwner = fm.getFocusOwner(); - if (focusOwner == null && !async) { - focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); - } - - if (focusOwner == null && !async) { - focusOwner = fm.getLastFocusedFor(fm.getLastFocusedFrame()); - } - - DockContainer container = myDockManager.getContainerFor(focusOwner); - if (container == null && !async) { - focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow(); - container = myDockManager.getContainerFor(focusOwner); - } - - if (container instanceof DockableEditorTabbedContainer) { - result.setDone(((DockableEditorTabbedContainer)container).getSplitters()); - } - else { - result.setDone(getMainSplitters()); - } - } - }; - - if (async) { - fm.doWhenFocusSettlesDown(run); - } - else { - run.run(); - } - - return result; - } - - private final Object myInitLock = new Object(); - - private void initUI() { - if (myPanels == null) { - synchronized (myInitLock) { - if (myPanels == null) { - myPanels = new JPanel(new BorderLayout()) { - @Override - public Color getBackground() { - boolean navBar = UISettings.getInstance().SHOW_NAVIGATION_BAR; - if (navBar) { - return UIUtil.getSlightlyDarkerColor(UIUtil.isUnderAquaLookAndFeel() ? new SameColor(200) : UIUtil.getPanelBackground()); - } else { - return UIUtil.isUnderAquaLookAndFeel() ? new SameColor(189) : UIUtil.getPanelBackground(); - } - } - }; - myPanels.setBorder(new MyBorder()); - mySplitters = new EditorsSplitters(this, myDockManager, true); - myPanels.add(mySplitters, BorderLayout.CENTER); - } - } - } - } - - private static class MyBorder implements Border { - @Override - public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { - if (UIUtil.isUnderAquaLookAndFeel()) { - g.setColor(JBTabsImpl.MAC_AQUA_BG_COLOR); - final Insets insets = getBorderInsets(c); - if (insets.top > 0) { - g.fillRect(x, y, width, height + insets.top); - } - } - } - - @Override - public Insets getBorderInsets(Component c) { - return new Insets(0, 0, 0, 0); - } - - @Override - public boolean isBorderOpaque() { - return false; - } - } - - public JComponent getPreferredFocusedComponent() { - assertReadAccess(); - final EditorWindow window = getSplitters().getCurrentWindow(); - if (window != null) { - final EditorWithProviderComposite editor = window.getSelectedEditor(); - if (editor != null) { - return editor.getPreferredFocusedComponent(); - } - } - return null; - } - - //------------------------------------------------------- - - /** - * @return color of the file which corresponds to the - * file's status - */ - public Color getFileColor(@NotNull final VirtualFile file) { - final FileStatusManager fileStatusManager = FileStatusManager.getInstance(myProject); - Color statusColor = fileStatusManager != null ? fileStatusManager.getStatus(file).getColor() : Color.BLACK; - if (statusColor == null) statusColor = Color.BLACK; - return statusColor; - } - - public boolean isProblem(@NotNull final VirtualFile file) { - return false; - } - - public String getFileTooltipText(VirtualFile file) { - return FileUtil.getLocationRelativeToUserHome(file.getPresentableUrl()); - } - - public void updateFilePresentation(VirtualFile file) { - if (!isFileOpen(file)) return; - - updateFileColor(file); - updateFileIcon(file); - updateFileName(file); - updateFileBackgroundColor(file); - } - - /** - * Updates tab color for the specified file. The file - * should be opened in the myEditor, otherwise the method throws an assertion. - */ - private void updateFileColor(final VirtualFile file) { - Set all = getAllSplitters(); - for (EditorsSplitters each : all) { - each.updateFileColor(file); - } - } - - private void updateFileBackgroundColor(final VirtualFile file) { - Set all = getAllSplitters(); - for (EditorsSplitters each : all) { - each.updateFileBackgroundColor(file); - } - } - - /** - * Updates tab icon for the specified file. The file - * should be opened in the myEditor, otherwise the method throws an assertion. - */ - protected void updateFileIcon(final VirtualFile file) { - Set all = getAllSplitters(); - for (EditorsSplitters each : all) { - each.updateFileIcon(file); - } - } - - /** - * Updates tab title and tab tool tip for the specified file - */ - void updateFileName(@Nullable final VirtualFile file) { - // Queue here is to prevent title flickering when tab is being closed and two events arriving: with component==null and component==next focused tab - // only the last event makes sense to handle - myQueue.queue(new Update("UpdateFileName " + (file == null ? "" : file.getPath())) { - public boolean isExpired() { - return myProject.isDisposed() || !myProject.isOpen() || (file == null ? super.isExpired() : !file.isValid()); - } - - public void run() { - Set all = getAllSplitters(); - for (EditorsSplitters each : all) { - each.updateFileName(file); - } - } - }); - } - - //------------------------------------------------------- - - - public VirtualFile getFile(@NotNull final FileEditor editor) { - final EditorComposite editorComposite = getEditorComposite(editor); - if (editorComposite != null) { - return editorComposite.getFile(); - } - return null; - } - - public void unsplitWindow() { - final EditorWindow currentWindow = getActiveSplitters(true).getResult().getCurrentWindow(); - if (currentWindow != null) { - currentWindow.unsplit(true); - } - } - - public void unsplitAllWindow() { - final EditorWindow currentWindow = getActiveSplitters(true).getResult().getCurrentWindow(); - if (currentWindow != null) { - currentWindow.unsplitAll(); - } - } - - @Override - public int getWindowSplitCount() { - return getActiveSplitters(true).getResult().getSplitCount(); - } - - @Override - public boolean hasSplitOrUndockedWindows() { - Set splitters = getAllSplitters(); - if (splitters.size() > 1) return true; - return getWindowSplitCount() > 1; - } - - @NotNull - public EditorWindow[] getWindows() { - ArrayList windows = new ArrayList(); - Set all = getAllSplitters(); - for (EditorsSplitters each : all) { - EditorWindow[] eachList = each.getWindows(); - windows.addAll(Arrays.asList(eachList)); - } - - return windows.toArray(new EditorWindow[windows.size()]); - } - - public EditorWindow getNextWindow(@NotNull final EditorWindow window) { - final EditorWindow[] windows = getSplitters().getOrderedWindows(); - for (int i = 0; i != windows.length; ++i) { - if (windows[i].equals(window)) { - return windows[(i + 1) % windows.length]; - } - } - LOG.error("Not window found"); - return null; - } - - public EditorWindow getPrevWindow(@NotNull final EditorWindow window) { - final EditorWindow[] windows = getSplitters().getOrderedWindows(); - for (int i = 0; i != windows.length; ++i) { - if (windows[i].equals(window)) { - return windows[(i + windows.length - 1) % windows.length]; - } - } - LOG.error("Not window found"); - return null; - } - - public void createSplitter(final int orientation, @Nullable final EditorWindow window) { - // window was available from action event, for example when invoked from the tab menu of an editor that is not the 'current' - if (window != null) { - window.split(orientation, true, null, false); - } - // otherwise we'll split the current window, if any - else { - final EditorWindow currentWindow = getSplitters().getCurrentWindow(); - if (currentWindow != null) { - currentWindow.split(orientation, true, null, false); - } - } - } - - public void changeSplitterOrientation() { - final EditorWindow currentWindow = getSplitters().getCurrentWindow(); - if (currentWindow != null) { - currentWindow.changeOrientation(); - } - } - - - public void flipTabs() { - /* - if (myTabs == null) { - myTabs = new EditorTabs (this, UISettings.getInstance().EDITOR_TAB_PLACEMENT); - remove (mySplitters); - add (myTabs, BorderLayout.CENTER); - initTabs (); - } else { - remove (myTabs); - add (mySplitters, BorderLayout.CENTER); - myTabs.dispose (); - myTabs = null; - } - */ - myPanels.revalidate(); - } - - public boolean tabsMode() { - return false; - } - - private void setTabsMode(final boolean mode) { - if (tabsMode() != mode) { - flipTabs(); - } - //LOG.assertTrue (tabsMode () == mode); - } - - - public boolean isInSplitter() { - final EditorWindow currentWindow = getSplitters().getCurrentWindow(); - return currentWindow != null && currentWindow.inSplitter(); - } - - public boolean hasOpenedFile() { - final EditorWindow currentWindow = getSplitters().getCurrentWindow(); - return currentWindow != null && currentWindow.getSelectedEditor() != null; - } - - public VirtualFile getCurrentFile() { - return getActiveSplitters(true).getResult().getCurrentFile(); - } - - public AsyncResult getActiveWindow() { - return _getActiveWindow(false); - } - - private AsyncResult _getActiveWindow(boolean now) { - final AsyncResult result = new AsyncResult(); - getActiveSplitters(now).doWhenDone(new AsyncResult.Handler() { - @Override - public void run(EditorsSplitters editorsSplitters) { - result.setDone(editorsSplitters.getCurrentWindow()); - } - }); - - return result; - } - - public EditorWindow getCurrentWindow() { - return _getActiveWindow(true).getResult(); - } - - public void setCurrentWindow(final EditorWindow window) { - getActiveSplitters(true).getResult().setCurrentWindow(window, true); - } - - public void closeFile(@NotNull final VirtualFile file, @NotNull final EditorWindow window, final boolean transferFocus) { - assertDispatchThread(); - - CommandProcessor.getInstance().executeCommand(myProject, new Runnable() { - public void run() { - if (window.isFileOpen(file)) { - window.closeFile(file, true, transferFocus); - final List windows = window.getOwner().findWindows(file); - if (windows.isEmpty()) { // no more windows containing this file left - final LocalFileSystem.WatchRequest request = file.getUserData(WATCH_REQUEST_KEY); - if (request != null) { - LocalFileSystem.getInstance().removeWatchedRoot(request); - } - } - } - } - }, IdeBundle.message("command.close.active.editor"), null); - removeSelectionRecord(file, window); - } - - public void closeFile(@NotNull final VirtualFile file, @NotNull final EditorWindow window) { - closeFile(file, window, true); - } - - //============================= EditorManager methods ================================ - - public void closeFile(@NotNull final VirtualFile file) { - closeFile(file, true, false); - } - - public void closeFile(@NotNull final VirtualFile file, final boolean moveFocus, final boolean closeAllCopies) { - assertDispatchThread(); - - final LocalFileSystem.WatchRequest request = file.getUserData(WATCH_REQUEST_KEY); - if (request != null) { - LocalFileSystem.getInstance().removeWatchedRoot(request); - } - - CommandProcessor.getInstance().executeCommand(myProject, new Runnable() { - public void run() { - closeFileImpl(file, moveFocus, closeAllCopies); - } - }, "", null); - } - - - - private void closeFileImpl(@NotNull final VirtualFile file, final boolean moveFocus, boolean closeAllCopies) { - assertDispatchThread(); - runChange(new FileEditorManagerChange() { - public void run(EditorsSplitters splitters) { - splitters.closeFile(file, moveFocus); - } - }, closeAllCopies ? null : getActiveSplitters(true).getResult()); - } - - //-------------------------------------- Open File ---------------------------------------- - - @NotNull - public Pair openFileWithProviders(@NotNull final VirtualFile file, - final boolean focusEditor, - boolean searchForSplitter) { - if (!file.isValid()) { - throw new IllegalArgumentException("file is not valid: " + file); - } - assertDispatchThread(); - - EditorWindow wndToOpenIn = null; - if (searchForSplitter) { - Set all = getAllSplitters(); - EditorsSplitters active = getActiveSplitters(true).getResult(); - if (active.getCurrentWindow() != null && active.getCurrentWindow().isFileOpen(file)) { - wndToOpenIn = active.getCurrentWindow(); - } else { - for (EditorsSplitters splitters : all) { - final EditorWindow window = splitters.getCurrentWindow(); - if (window == null) continue; - - if (window.isFileOpen(file)) { - wndToOpenIn = window; - break; - } - } - } - } - else { - wndToOpenIn = getSplitters().getCurrentWindow(); - } - - EditorsSplitters splitters = getSplitters(); - - if (wndToOpenIn == null) { - wndToOpenIn = splitters.getOrCreateCurrentWindow(file); - } - - openAssociatedFile(file, wndToOpenIn, splitters); - return openFileImpl2(wndToOpenIn, file, focusEditor); - } - - private void openAssociatedFile(VirtualFile file, EditorWindow wndToOpenIn, EditorsSplitters splitters) { - EditorWindow[] windows = splitters.getWindows(); - - if (file != null && windows.length == 2) { - for (FileEditorAssociateFinder finder : Extensions.getExtensions(FileEditorAssociateFinder.EP_NAME)) { - VirtualFile associatedFile = finder.getAssociatedFileToOpen(myProject, file); - - if (associatedFile != null) { - EditorWindow currentWindow = splitters.getCurrentWindow(); - int idx = windows[0] == wndToOpenIn ? 1 : 0; - openFileImpl2(windows[idx], associatedFile, false); - - if (currentWindow != null) { - splitters.setCurrentWindow(currentWindow, false); - } - - break; - } - } - } - } - - @NotNull - @Override - public Pair openFileWithProviders(@NotNull VirtualFile file, - boolean focusEditor, - @NotNull EditorWindow window) { - if (!file.isValid()) { - throw new IllegalArgumentException("file is not valid: " + file); - } - assertDispatchThread(); - - return openFileImpl2(window, file, focusEditor); - } - - @NotNull - public Pair openFileImpl2(@NotNull final EditorWindow window, - @NotNull final VirtualFile file, - final boolean focusEditor) { - final Ref> result = new Ref>(); - CommandProcessor.getInstance().executeCommand(myProject, new Runnable() { - public void run() { - result.set(openFileImpl3(window, file, focusEditor, null, true)); - } - }, "", null); - return result.get(); - } - - /** - * @param file to be opened. Unlike openFile method, file can be - * invalid. For example, all file were invalidate and they are being - * removed one by one. If we have removed one invalid file, then another - * invalid file become selected. That's why we do not require that - * passed file is valid. - * @param entry map between FileEditorProvider and FileEditorState. If this parameter - * @param current - */ - @NotNull - Pair openFileImpl3(@NotNull final EditorWindow window, - @NotNull final VirtualFile file, - final boolean focusEditor, - @Nullable final HistoryEntry entry, - boolean current) { - return openFileImpl4(window, file, focusEditor, entry, current, -1); - } - - @NotNull - Pair openFileImpl4(@NotNull final EditorWindow window, - @NotNull final VirtualFile file, - final boolean focusEditor, - @Nullable final HistoryEntry entry, - boolean current, - int index) { - // Open file - FileEditor[] editors; - FileEditorProvider[] providers; - final EditorWithProviderComposite newSelectedComposite; - boolean newEditorCreated = false; - - final boolean open = window.isFileOpen(file); - if (open) { - // File is already opened. In this case we have to just select existing EditorComposite - newSelectedComposite = window.findFileComposite(file); - LOG.assertTrue(newSelectedComposite != null); - - editors = newSelectedComposite.getEditors(); - providers = newSelectedComposite.getProviders(); - } - else { - // File is not opened yet. In this case we have to create editors - // and select the created EditorComposite. - final FileEditorProviderManager editorProviderManager = FileEditorProviderManager.getInstance(); - providers = editorProviderManager.getProviders(myProject, file); - if (DumbService.getInstance(myProject).isDumb()) { - final List dumbAware = ContainerUtil.findAll(providers, new Condition() { - public boolean value(FileEditorProvider fileEditorProvider) { - return DumbService.isDumbAware(fileEditorProvider); - } - }); - providers = dumbAware.toArray(new FileEditorProvider[dumbAware.size()]); - } - - if (providers.length == 0) { - return Pair.create(EMPTY_EDITOR_ARRAY, EMPTY_PROVIDER_ARRAY); - } - newEditorCreated = true; - - getProject().getMessageBus().syncPublisher(FileEditorManagerListener.Before.FILE_EDITOR_MANAGER).beforeFileOpened(this, file); - - editors = new FileEditor[providers.length]; - for (int i = 0; i < providers.length; i++) { - try { - final FileEditorProvider provider = providers[i]; - LOG.assertTrue(provider != null); - LOG.assertTrue(provider.accept(myProject, file)); - final FileEditor editor = provider.createEditor(myProject, file); - LOG.assertTrue(editor != null); - LOG.assertTrue(editor.isValid()); - editors[i] = editor; - // Register PropertyChangeListener into editor - editor.addPropertyChangeListener(myEditorPropertyChangeListener); - editor.putUserData(DUMB_AWARE, DumbService.isDumbAware(provider)); - - if (current && editor instanceof TextEditorImpl) { - ((TextEditorImpl)editor).initFolding(); - } - } - catch (Exception e) { - LOG.error(e); - } - catch (AssertionError e) { - LOG.error(e); - } - } - - // Now we have to create EditorComposite and insert it into the TabbedEditorComponent. - // After that we have to select opened editor. - newSelectedComposite = new EditorWithProviderComposite(file, editors, providers, this); - - if (index >= 0) { - newSelectedComposite.getFile().putUserData(EditorWindow.INITIAL_INDEX_KEY, index); - } - } - - window.setEditor(newSelectedComposite, focusEditor); - - final EditorHistoryManager editorHistoryManager = EditorHistoryManager.getInstance(myProject); - for (int i = 0; i < editors.length; i++) { - final FileEditor editor = editors[i]; - if (editor instanceof TextEditor) { - // hack!!! - // This code prevents "jumping" on next repaint. - ((EditorEx)((TextEditor)editor).getEditor()).stopOptimizedScrolling(); - } - - final FileEditorProvider provider = providers[i];//getProvider(editor); - - // Restore editor state - FileEditorState state = null; - if (entry != null) { - state = entry.getState(provider); - } - if (state == null && !open) { - // We have to try to get state from the history only in case - // if editor is not opened. Otherwise history entry might have a state - // out of sync with the current editor state. - state = editorHistoryManager.getState(file, provider); - } - if (state != null) { - editor.setState(state); - } - } - - // Restore selected editor - final FileEditorProvider selectedProvider = editorHistoryManager.getSelectedProvider(file); - if (selectedProvider != null) { - final FileEditor[] _editors = newSelectedComposite.getEditors(); - final FileEditorProvider[] _providers = newSelectedComposite.getProviders(); - for (int i = _editors.length - 1; i >= 0; i--) { - final FileEditorProvider provider = _providers[i];//getProvider(_editors[i]); - if (provider.equals(selectedProvider)) { - newSelectedComposite.setSelectedEditor(i); - break; - } - } - } - - // Notify editors about selection changes - window.getOwner().setCurrentWindow(window, focusEditor); - window.getOwner().afterFileOpen(file); - - newSelectedComposite.getSelectedEditor().selectNotify(); - - final IdeFocusManager focusManager = IdeFocusManager.getInstance(myProject); - if (newEditorCreated) { - if (window.isShowing()) { - window.setPaintBlocked(true); - } - notifyPublisher(new Runnable() { - @Override - public void run() { - window.setPaintBlocked(false); - if (isFileOpen(file)) { - getProject().getMessageBus().syncPublisher(FileEditorManagerListener.FILE_EDITOR_MANAGER) - .fileOpened(FileEditorManagerImpl.this, file); - } - } - }); - - //Add request to watch this editor's virtual file - final VirtualFile parentDir = file.getParent(); - if (parentDir != null) { - final LocalFileSystem.WatchRequest request = LocalFileSystem.getInstance().addRootToWatch(parentDir.getPath(), false); - file.putUserData(WATCH_REQUEST_KEY, request); - } - } - - //[jeka] this is a hack to support back-forward navigation - // previously here was incorrect call to fireSelectionChanged() with a side-effect - ((IdeDocumentHistoryImpl)IdeDocumentHistory.getInstance(myProject)).onSelectionChanged(); - - // Transfer focus into editor - if (!ApplicationManagerEx.getApplicationEx().isUnitTestMode()) { - if (focusEditor) { - //myFirstIsActive = myTabbedContainer1.equals(tabbedContainer); - window.setAsCurrentWindow(true); - ToolWindowManager.getInstance(myProject).activateEditorComponent(); - focusManager.toFront(window.getOwner()); - } - } - - // Update frame and tab title - updateFileName(file); - - // Make back/forward work - IdeDocumentHistory.getInstance(myProject).includeCurrentCommandAsNavigation(); - - return Pair.create(editors, providers); - } - - @Override - public ActionCallback notifyPublisher(final Runnable runnable) { - final IdeFocusManager focusManager = IdeFocusManager.getInstance(myProject); - final ActionCallback done = new ActionCallback(); - return myBusyObject.execute(new ActiveRunnable() { - @Override - public ActionCallback run() { - focusManager.doWhenFocusSettlesDown(new ExpirableRunnable.ForProject(myProject) { - @Override - public void run() { - runnable.run(); - done.setDone(); - } - }); - return done; - } - }); - } - - public void setSelectedEditor(VirtualFile file, String fileEditorProviderId) { - EditorWithProviderComposite composite = getCurrentEditorWithProviderComposite(file); - if (composite == null) { - final List composites = getEditorComposites(file); - - if (composites.isEmpty()) return; - composite = composites.get(0); - } - - final FileEditorProvider[] editorProviders = composite.getProviders(); - final FileEditorProvider selectedProvider = composite.getSelectedEditorWithProvider().getSecond(); - - for (int i = 0; i < editorProviders.length; i++) { - if (editorProviders[i].getEditorTypeId().equals(fileEditorProviderId) && !selectedProvider.equals(editorProviders[i])) { - composite.setSelectedEditor(i); - composite.getSelectedEditor().selectNotify(); - } - } - } - - - @Nullable - EditorWithProviderComposite newEditorComposite(final VirtualFile file) { - if (file == null) { - return null; - } - - final FileEditorProviderManager editorProviderManager = FileEditorProviderManager.getInstance(); - final FileEditorProvider[] providers = editorProviderManager.getProviders(myProject, file); - final FileEditor[] editors = new FileEditor[providers.length]; - for (int i = 0; i < providers.length; i++) { - final FileEditorProvider provider = providers[i]; - LOG.assertTrue(provider != null); - LOG.assertTrue(provider.accept(myProject, file)); - final FileEditor editor = provider.createEditor(myProject, file); - editors[i] = editor; - LOG.assertTrue(editor.isValid()); - editor.addPropertyChangeListener(myEditorPropertyChangeListener); - } - - final EditorWithProviderComposite newComposite = new EditorWithProviderComposite(file, editors, providers, this); - final EditorHistoryManager editorHistoryManager = EditorHistoryManager.getInstance(myProject); - for (int i = 0; i < editors.length; i++) { - final FileEditor editor = editors[i]; - if (editor instanceof TextEditor) { - // hack!!! - // This code prevents "jumping" on next repaint. - //((EditorEx)((TextEditor)editor).getEditor()).stopOptimizedScrolling(); - } - - final FileEditorProvider provider = providers[i]; - -// Restore myEditor state - FileEditorState state = editorHistoryManager.getState(file, provider); - if (state != null) { - editor.setState(state); - } - } - return newComposite; - } - - @NotNull - public List openEditor(@NotNull final OpenFileDescriptor descriptor, final boolean focusEditor) { - assertDispatchThread(); - if (descriptor.getFile() instanceof VirtualFileWindow) { - VirtualFileWindow delegate = (VirtualFileWindow)descriptor.getFile(); - int hostOffset = delegate.getDocumentWindow().injectedToHost(descriptor.getOffset()); - OpenFileDescriptor realDescriptor = new OpenFileDescriptor(descriptor.getProject(), delegate.getDelegate(), hostOffset); - realDescriptor.setUseCurrentWindow(descriptor.isUseCurrentWindow()); - return openEditor(realDescriptor, focusEditor); - } - - final List result = new ArrayList(); - CommandProcessor.getInstance().executeCommand(myProject, new Runnable() { - public void run() { - VirtualFile file = descriptor.getFile(); - final FileEditor[] editors = openFile(file, focusEditor, !descriptor.isUseCurrentWindow()); - ContainerUtil.addAll(result, editors); - - boolean navigated = false; - for (final FileEditor editor : editors) { - if (editor instanceof NavigatableFileEditor && - getSelectedEditor(descriptor.getFile()) == editor) { // try to navigate opened editor - navigated = navigateAndSelectEditor((NavigatableFileEditor)editor, descriptor); - if (navigated) break; - } - } - - if (!navigated) { - for (final FileEditor editor : editors) { - if (editor instanceof NavigatableFileEditor && getSelectedEditor(descriptor.getFile()) != editor) { // try other editors - if (navigateAndSelectEditor((NavigatableFileEditor)editor, descriptor)) { - break; - } - } - } - } - } - }, "", null); - - return result; - } - - private boolean navigateAndSelectEditor(final NavigatableFileEditor editor, final OpenFileDescriptor descriptor) { - if (editor.canNavigateTo(descriptor)) { - setSelectedEditor(editor); - editor.navigateTo(descriptor); - return true; - } - - return false; - } - - private void setSelectedEditor(final FileEditor editor) { - final EditorWithProviderComposite composite = getEditorComposite(editor); - if (composite == null) return; - - final FileEditor[] editors = composite.getEditors(); - for (int i = 0; i < editors.length; i++) { - final FileEditor each = editors[i]; - if (editor == each) { - composite.setSelectedEditor(i); - composite.getSelectedEditor().selectNotify(); - break; - } - } - } - - @NotNull - public Project getProject() { - return myProject; - } - - @Nullable - public Editor openTextEditor(final OpenFileDescriptor descriptor, final boolean focusEditor) { - final Collection fileEditors = openEditor(descriptor, focusEditor); - for (FileEditor fileEditor : fileEditors) { - if (fileEditor instanceof TextEditor) { - setSelectedEditor(descriptor.getFile(), TextEditorProvider.getInstance().getEditorTypeId()); - Editor editor = ((TextEditor)fileEditor).getEditor(); - return getOpenedEditor(editor, focusEditor); - } - } - - return null; - } - - protected Editor getOpenedEditor(final Editor editor, final boolean focusEditor) { - return editor; - } - - public Editor getSelectedTextEditor() { - assertReadAccess(); - - final EditorWindow currentWindow = getSplitters().getCurrentWindow(); - if (currentWindow != null) { - final EditorWithProviderComposite selectedEditor = currentWindow.getSelectedEditor(); - if (selectedEditor != null && selectedEditor.getSelectedEditor() instanceof TextEditor) { - return ((TextEditor)selectedEditor.getSelectedEditor()).getEditor(); - } - } - - return null; - } - - - public boolean isFileOpen(@NotNull final VirtualFile file) { - return !getEditorComposites(file).isEmpty(); - } - - @NotNull - public VirtualFile[] getOpenFiles() { - HashSet openFiles = new HashSet(); - for (EditorsSplitters each : getAllSplitters()) { - openFiles.addAll(Arrays.asList(each.getOpenFiles())); - } - - return VfsUtilCore.toVirtualFileArray(openFiles); - } - - @NotNull - public VirtualFile[] getSelectedFiles() { - HashSet selectedFiles = new HashSet(); - for (EditorsSplitters each : getAllSplitters()) { - selectedFiles.addAll(Arrays.asList(each.getSelectedFiles())); - } - - return VfsUtilCore.toVirtualFileArray(selectedFiles); - } - - @NotNull - public FileEditor[] getSelectedEditors() { - HashSet selectedEditors = new HashSet(); - for (EditorsSplitters each : getAllSplitters()) { - selectedEditors.addAll(Arrays.asList(each.getSelectedEditors())); - } - - return selectedEditors.toArray(new FileEditor[selectedEditors.size()]); - } - - public EditorsSplitters getSplitters() { - EditorsSplitters active = getActiveSplitters(true).getResult(); - return active == null ? getMainSplitters() : active; - } - - @Nullable - public FileEditor getSelectedEditor(@NotNull final VirtualFile file) { - final Pair selectedEditorWithProvider = getSelectedEditorWithProvider(file); - return selectedEditorWithProvider == null ? null : selectedEditorWithProvider.getFirst(); - } - - - @Nullable - public Pair getSelectedEditorWithProvider(@NotNull VirtualFile file) { - if (file instanceof VirtualFileWindow) file = ((VirtualFileWindow)file).getDelegate(); - final EditorWithProviderComposite composite = getCurrentEditorWithProviderComposite(file); - if (composite != null) { - return composite.getSelectedEditorWithProvider(); - } - - final List composites = getEditorComposites(file); - return composites.isEmpty() ? null : composites.get(0).getSelectedEditorWithProvider(); - } - - @NotNull - public Pair getEditorsWithProviders(@NotNull final VirtualFile file) { - assertReadAccess(); - - final EditorWithProviderComposite composite = getCurrentEditorWithProviderComposite(file); - if (composite != null) { - return Pair.create(composite.getEditors(), composite.getProviders()); - } - - final List composites = getEditorComposites(file); - if (!composites.isEmpty()) { - return Pair.create(composites.get(0).getEditors(), composites.get(0).getProviders()); - } - else { - return Pair.create(EMPTY_EDITOR_ARRAY, EMPTY_PROVIDER_ARRAY); - } - } - - @NotNull - public FileEditor[] getEditors(@NotNull VirtualFile file) { - assertReadAccess(); - if (file instanceof VirtualFileWindow) file = ((VirtualFileWindow)file).getDelegate(); - - final EditorWithProviderComposite composite = getCurrentEditorWithProviderComposite(file); - if (composite != null) { - return composite.getEditors(); - } - - final List composites = getEditorComposites(file); - if (!composites.isEmpty()) { - return composites.get(0).getEditors(); - } - else { - return EMPTY_EDITOR_ARRAY; - } - } - - @NotNull - @Override - public FileEditor[] getAllEditors(@NotNull VirtualFile file) { - List editorComposites = getEditorComposites(file); - List editors = new ArrayList(); - for (EditorWithProviderComposite composite : editorComposites) { - ContainerUtil.addAll(editors, composite.getEditors()); - } - return editors.toArray(new FileEditor[editors.size()]); - } - - @Nullable - private EditorWithProviderComposite getCurrentEditorWithProviderComposite(@NotNull final VirtualFile virtualFile) { - final EditorWindow editorWindow = getSplitters().getCurrentWindow(); - if (editorWindow != null) { - return editorWindow.findFileComposite(virtualFile); - } - return null; - } - - @NotNull - public List getEditorComposites(final VirtualFile file) { - ArrayList result = new ArrayList(); - Set all = getAllSplitters(); - for (EditorsSplitters each : all) { - result.addAll(each.findEditorComposites(file)); - } - return result; - } - - @NotNull - public FileEditor[] getAllEditors() { - assertReadAccess(); - final ArrayList result = new ArrayList(); - final Set allSplitters = getAllSplitters(); - for (EditorsSplitters splitter : allSplitters) { - final EditorWithProviderComposite[] editorsComposites = splitter.getEditorsComposites(); - for (EditorWithProviderComposite editorsComposite : editorsComposites) { - final FileEditor[] editors = editorsComposite.getEditors(); - ContainerUtil.addAll(result, editors); - } - } - return result.toArray(new FileEditor[result.size()]); - } - - public void showEditorAnnotation(@NotNull FileEditor editor, @NotNull JComponent annotationComponent) { - addTopComponent(editor, annotationComponent); - } - - public void removeEditorAnnotation(@NotNull FileEditor editor, @NotNull JComponent annotationComponent) { - removeTopComponent(editor, annotationComponent); - } - - public void addTopComponent(@NotNull final FileEditor editor, @NotNull final JComponent component) { - final EditorComposite composite = getEditorComposite(editor); - if (composite != null) { - composite.addTopComponent(editor, component); - } - } - - public void removeTopComponent(@NotNull final FileEditor editor, @NotNull final JComponent component) { - final EditorComposite composite = getEditorComposite(editor); - if (composite != null) { - composite.removeTopComponent(editor, component); - } - } - - public void addBottomComponent(@NotNull final FileEditor editor, @NotNull final JComponent component) { - final EditorComposite composite = getEditorComposite(editor); - if (composite != null) { - composite.addBottomComponent(editor, component); - } - } - - public void removeBottomComponent(@NotNull final FileEditor editor, @NotNull final JComponent component) { - final EditorComposite composite = getEditorComposite(editor); - if (composite != null) { - composite.removeBottomComponent(editor, component); - } - } - - private final MessageListenerList myListenerList; - - public void addFileEditorManagerListener(@NotNull final FileEditorManagerListener listener) { - myListenerList.add(listener); - } - - public void addFileEditorManagerListener(@NotNull final FileEditorManagerListener listener, final Disposable parentDisposable) { - myListenerList.add(listener, parentDisposable); - } - - public void removeFileEditorManagerListener(@NotNull final FileEditorManagerListener listener) { - myListenerList.remove(listener); - } - -// ProjectComponent methods - - public void projectOpened() { - //myFocusWatcher.install(myWindows.getComponent ()); - getMainSplitters().startListeningFocus(); - - MessageBusConnection connection = myProject.getMessageBus().connect(myProject); - - final FileStatusManager fileStatusManager = FileStatusManager.getInstance(myProject); - if (fileStatusManager != null) { - /** - * Updates tabs colors - */ - final MyFileStatusListener myFileStatusListener = new MyFileStatusListener(); - fileStatusManager.addFileStatusListener(myFileStatusListener, myProject); - } - connection.subscribe(FileTypeManager.TOPIC, new MyFileTypeListener()); - connection.subscribe(ProjectTopics.PROJECT_ROOTS, new MyRootsListener()); - - /** - * Updates tabs names - */ - final MyVirtualFileListener myVirtualFileListener = new MyVirtualFileListener(); - VirtualFileManager.getInstance().addVirtualFileListener(myVirtualFileListener, myProject); - /** - * Extends/cuts number of opened tabs. Also updates location of tabs. - */ - final MyUISettingsListener myUISettingsListener = new MyUISettingsListener(); - UISettings.getInstance().addUISettingsListener(myUISettingsListener, myProject); - - StartupManager.getInstance(myProject).registerPostStartupActivity(new DumbAwareRunnable() { - public void run() { - - setTabsMode(UISettings.getInstance().EDITOR_TAB_PLACEMENT != UISettings.TABS_NONE); - - ToolWindowManager.getInstance(myProject).invokeLater(new Runnable() { - public void run() { - CommandProcessor.getInstance().executeCommand(myProject, new Runnable() { - public void run() { - - LaterInvocator.invokeLater(new Runnable() { - public void run() { - long currentTime = System.nanoTime(); - Long startTime = myProject.getUserData(ProjectImpl.CREATION_TIME); - if (startTime != null) { - LOG.info("Project opening took " + (currentTime - startTime.longValue()) / 1000000 + " ms"); - PluginManager.dumpPluginClassStatistics(); - } - } - }); -// group 1 - } - }, "", null); - } - }); - } - }); - } - - public void projectClosed() { - //myFocusWatcher.deinstall(myWindows.getComponent ()); - getMainSplitters().dispose(); - -// Dispose created editors. We do not use use closeEditor method because -// it fires event and changes history. - closeAllFiles(); - } - -// BaseCompomemnt methods - - @NotNull - public String getComponentName() { - return "FileEditorManager"; - } - - public void initComponent() { - - } - - public void disposeComponent() { /* really do nothing */ } - -//JDOMExternalizable methods - - public void writeExternal(final Element element) { - getMainSplitters().writeExternal(element); - } - - public void readExternal(final Element element) { - getMainSplitters().readExternal(element); - } - - @Nullable - private EditorWithProviderComposite getEditorComposite(@NotNull final FileEditor editor) { - for (EditorsSplitters splitters : getAllSplitters()) { - final EditorWithProviderComposite[] editorsComposites = splitters.getEditorsComposites(); - for (int i = editorsComposites.length - 1; i >= 0; i--) { - final EditorWithProviderComposite composite = editorsComposites[i]; - final FileEditor[] editors = composite.getEditors(); - for (int j = editors.length - 1; j >= 0; j--) { - final FileEditor _editor = editors[j]; - LOG.assertTrue(_editor != null); - if (editor.equals(_editor)) { - return composite; - } - } - } - } - return null; - } - -//======================= Misc ===================== - - private static void assertDispatchThread() { - ApplicationManager.getApplication().assertIsDispatchThread(); - } - - private static void assertReadAccess() { - ApplicationManager.getApplication().assertReadAccessAllowed(); - } - - public void fireSelectionChanged(final EditorComposite newSelectedComposite) { - final Trinity oldData = extract(myLastSelectedComposite.get()); - final Trinity newData = extract(newSelectedComposite); - myLastSelectedComposite = new WeakReference(newSelectedComposite); - final boolean filesEqual = oldData.first == null ? newData.first == null : oldData.first.equals(newData.first); - final boolean editorsEqual = oldData.second == null ? newData.second == null : oldData.second.equals(newData.second); - if (!filesEqual || !editorsEqual) { - if (oldData.first != null && newData.first != null) { - for (FileEditorAssociateFinder finder : Extensions.getExtensions(FileEditorAssociateFinder.EP_NAME)) { - VirtualFile associatedFile = finder.getAssociatedFileToOpen(myProject, oldData.first); - - if (associatedFile == newData.first) { - return; - } - } - } - - final FileEditorManagerEvent event = - new FileEditorManagerEvent(this, oldData.first, oldData.second, oldData.third, newData.first, newData.second, newData.third); - final FileEditorManagerListener publisher = getProject().getMessageBus().syncPublisher(FileEditorManagerListener.FILE_EDITOR_MANAGER); - - if (newData.first != null) { - final JComponent component = newData.second.getComponent(); - final EditorWindowHolder holder = UIUtil.getParentOfType(EditorWindowHolder.class, component); - if (holder != null) { - addSelectionRecord(newData.first, holder.getEditorWindow()); - } - } - notifyPublisher(new Runnable() { - @Override - public void run() { - publisher.selectionChanged(event); - } - }); - } - } - - @NotNull - private static Trinity extract(@Nullable EditorComposite composite) { - final VirtualFile file; - final FileEditor editor; - final FileEditorProvider provider; - if (composite == null || composite.isDisposed()) { - file = null; - editor = null; - provider = null; - } - else { - file = composite.getFile(); - final Pair pair = composite.getSelectedEditorWithProvider(); - editor = pair.first; - provider = pair.second; - } - return new Trinity(file, editor, provider); - } - - public boolean isChanged(@NotNull final EditorComposite editor) { - final FileStatusManager fileStatusManager = FileStatusManager.getInstance(myProject); - if (fileStatusManager != null) { - VirtualFile file = editor.getFile(); - FileStatus status = fileStatusManager.getStatus(file); - if (status == FileStatus.UNKNOWN && !file.isWritable()) { - return false; - } - if (!status.equals(FileStatus.NOT_CHANGED)) { - return true; - } - } - return false; - } - - public void disposeComposite(@NotNull EditorWithProviderComposite editor) { - if (getAllEditors().length == 0) { - setCurrentWindow(null); - } - - if (editor.equals(getLastSelected())) { - editor.getSelectedEditor().deselectNotify(); - getSplitters().setCurrentWindow(null, false); - } - - final FileEditor[] editors = editor.getEditors(); - final FileEditorProvider[] providers = editor.getProviders(); - - final FileEditor selectedEditor = editor.getSelectedEditor(); - for (int i = editors.length - 1; i >= 0; i--) { - final FileEditor editor1 = editors[i]; - final FileEditorProvider provider = providers[i]; - if (!editor.equals(selectedEditor)) { // we already notified the myEditor (when fire event) - if (selectedEditor.equals(editor1)) { - editor1.deselectNotify(); - } - } - editor1.removePropertyChangeListener(myEditorPropertyChangeListener); - provider.disposeEditor(editor1); - } - - Disposer.dispose(editor); - } - - @Nullable - EditorComposite getLastSelected() { - final EditorWindow currentWindow = getActiveSplitters(true).getResult().getCurrentWindow(); - if (currentWindow != null) { - return currentWindow.getSelectedEditor(); - } - return null; - } - - public void runChange(FileEditorManagerChange change, EditorsSplitters splitters) { - Set target = new HashSet(); - if (splitters == null) { - target.addAll(getAllSplitters()); - } else { - target.add(splitters); - } - - for (EditorsSplitters each : target) { - each.myInsideChange++; - try { - change.run(each); - } - finally { - each.myInsideChange--; - } - } - } - - //================== Listeners ===================== - - /** - * Closes deleted files. Closes file which are in the deleted directories. - */ - private final class MyVirtualFileListener extends VirtualFileAdapter { - public void beforeFileDeletion(VirtualFileEvent e) { - assertDispatchThread(); - - boolean moveFocus = moveFocusOnDelete(); - - final VirtualFile file = e.getFile(); - final VirtualFile[] openFiles = getOpenFiles(); - for (int i = openFiles.length - 1; i >= 0; i--) { - if (VfsUtilCore.isAncestor(file, openFiles[i], false)) { - closeFile(openFiles[i], moveFocus, true); - } - } - } - - public void propertyChanged(VirtualFilePropertyEvent e) { - if (VirtualFile.PROP_NAME.equals(e.getPropertyName())) { - assertDispatchThread(); - final VirtualFile file = e.getFile(); - if (isFileOpen(file)) { - updateFileName(file); - updateFileIcon(file); // file type can change after renaming - updateFileBackgroundColor(file); - } - } - else if (VirtualFile.PROP_WRITABLE.equals(e.getPropertyName()) || VirtualFile.PROP_ENCODING.equals(e.getPropertyName())) { - // TODO: message bus? - updateIconAndStatusBar(e); - } - } - - private void updateIconAndStatusBar(final VirtualFilePropertyEvent e) { - assertDispatchThread(); - final VirtualFile file = e.getFile(); - if (isFileOpen(file)) { - updateFileIcon(file); - if (file.equals(getSelectedFiles()[0])) { // update "write" status - final StatusBarEx statusBar = (StatusBarEx)WindowManager.getInstance().getStatusBar(myProject); - assert statusBar != null; - statusBar.updateWidgets(); - } - } - } - - public void fileMoved(VirtualFileMoveEvent e) { - final VirtualFile file = e.getFile(); - final VirtualFile[] openFiles = getOpenFiles(); - for (final VirtualFile openFile : openFiles) { - if (VfsUtilCore.isAncestor(file, openFile, false)) { - updateFileName(openFile); - updateFileBackgroundColor(openFile); - } - } - } - } - - private static boolean moveFocusOnDelete() { - final Window window = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusedWindow(); - if (window != null) { - final Component component = FocusTrackback.getFocusFor(window); - if (component != null) { - return component instanceof EditorComponentImpl; - } - return window instanceof IdeFrameImpl; - } - return true; - } - - public boolean isInsideChange() { - return getSplitters().isInsideChange(); - } - - private final class MyEditorPropertyChangeListener implements PropertyChangeListener { - public void propertyChange(final PropertyChangeEvent e) { - assertDispatchThread(); - - final String propertyName = e.getPropertyName(); - if (FileEditor.PROP_MODIFIED.equals(propertyName)) { - final FileEditor editor = (FileEditor)e.getSource(); - final EditorComposite composite = getEditorComposite(editor); - if (composite != null) { - updateFileIcon(composite.getFile()); - } - } - else if (FileEditor.PROP_VALID.equals(propertyName)) { - final boolean valid = ((Boolean)e.getNewValue()).booleanValue(); - if (!valid) { - final FileEditor editor = (FileEditor)e.getSource(); - LOG.assertTrue(editor != null); - final EditorComposite composite = getEditorComposite(editor); - if (composite != null) { - closeFile(composite.getFile()); - } - } - } - - } - } - - - /** - * Gets events from VCS and updates color of myEditor tabs - */ - private final class MyFileStatusListener implements FileStatusListener { - public void fileStatusesChanged() { // update color of all open files - assertDispatchThread(); - LOG.debug("FileEditorManagerImpl.MyFileStatusListener.fileStatusesChanged()"); - final VirtualFile[] openFiles = getOpenFiles(); - for (int i = openFiles.length - 1; i >= 0; i--) { - final VirtualFile file = openFiles[i]; - LOG.assertTrue(file != null); - ApplicationManager.getApplication().invokeLater(new Runnable() { - public void run() { - if (LOG.isDebugEnabled()) { - LOG.debug("updating file status in tab for " + file.getPath()); - } - updateFileStatus(file); - } - }, ModalityState.NON_MODAL, myProject.getDisposed()); - } - } - - public void fileStatusChanged(@NotNull final VirtualFile file) { // update color of the file (if necessary) - assertDispatchThread(); - if (isFileOpen(file)) { - updateFileStatus(file); - } - } - - private void updateFileStatus(final VirtualFile file) { - updateFileColor(file); - updateFileIcon(file); - } - } - - /** - * Gets events from FileTypeManager and updates icons on tabs - */ - private final class MyFileTypeListener implements FileTypeListener { - public void beforeFileTypesChanged(FileTypeEvent event) { - } - - public void fileTypesChanged(final FileTypeEvent event) { - assertDispatchThread(); - final VirtualFile[] openFiles = getOpenFiles(); - for (int i = openFiles.length - 1; i >= 0; i--) { - final VirtualFile file = openFiles[i]; - LOG.assertTrue(file != null); - updateFileIcon(file); - } - } - } - - private class MyRootsListener extends ModuleRootAdapter { - public void rootsChanged(ModuleRootEvent event) { - EditorFileSwapper[] swappers = Extensions.getExtensions(EditorFileSwapper.EP_NAME); - - for (EditorWindow eachWindow : getWindows()) { - EditorWithProviderComposite selected = eachWindow.getSelectedEditor(); - EditorWithProviderComposite[] editors = eachWindow.getEditors(); - for (int i = 0; i < editors.length; i++) { - EditorWithProviderComposite editor = editors[i]; - VirtualFile file = editor.getFile(); - if (!file.isValid()) continue; - - Pair newFilePair = null; - - for (EditorFileSwapper each : swappers) { - newFilePair = each.getFileToSwapTo(myProject, editor); - if (newFilePair != null) break; - } - - if (newFilePair == null) continue; - - VirtualFile newFile = newFilePair.first; - if (newFile == null) continue; - - // already open - if (eachWindow.findFileIndex(newFile) != -1) continue; - - try { - newFile.putUserData(EditorWindow.INITIAL_INDEX_KEY, i); - Pair pair = openFileImpl2(eachWindow, newFile, editor == selected); - - if (newFilePair.second != null) { - TextEditorImpl openedEditor = EditorFileSwapper.findSinglePsiAwareEditor(pair.first); - if (openedEditor != null) { - openedEditor.getEditor().getCaretModel().moveToOffset(newFilePair.second); - openedEditor.getEditor().getScrollingModel().scrollToCaret(ScrollType.CENTER); - } - } - } - finally { - newFile.putUserData(EditorWindow.INITIAL_INDEX_KEY, null); - } - closeFile(file, eachWindow); - } - } - } - } - - /** - * Gets notifications from UISetting component to track changes of RECENT_FILES_LIMIT - * and EDITOR_TAB_LIMIT, etc values. - */ - private final class MyUISettingsListener implements UISettingsListener { - public void uiSettingsChanged(final UISettings source) { - assertDispatchThread(); - setTabsMode(source.EDITOR_TAB_PLACEMENT != UISettings.TABS_NONE); - - for (EditorsSplitters each : getAllSplitters()) { - each.setTabsPlacement(source.EDITOR_TAB_PLACEMENT); - each.trimToSize(source.EDITOR_TAB_LIMIT); - - // Tab layout policy - if (source.SCROLL_TAB_LAYOUT_IN_EDITOR) { - each.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); - } - else { - each.setTabLayoutPolicy(JTabbedPane.WRAP_TAB_LAYOUT); - } - } - - // "Mark modified files with asterisk" - final VirtualFile[] openFiles = getOpenFiles(); - for (int i = openFiles.length - 1; i >= 0; i--) { - final VirtualFile file = openFiles[i]; - updateFileIcon(file); - updateFileName(file); - updateFileBackgroundColor(file); - } - } - } - - public void closeAllFiles() { - final VirtualFile[] openFiles = getSplitters().getOpenFiles(); - for (VirtualFile openFile : openFiles) { - closeFile(openFile); - } - } - - @NotNull - public VirtualFile[] getSiblings(VirtualFile file) { - return getOpenFiles(); - } - - protected void queueUpdateFile(final VirtualFile file) { - myQueue.queue(new Update(file) { - public void run() { - if (isFileOpen(file)) { - updateFileIcon(file); - updateFileColor(file); - updateFileBackgroundColor(file); - } - - } - }); - } - - public EditorsSplitters getSplittersFor(Component c) { - EditorsSplitters splitters = null; - DockContainer dockContainer = myDockManager.getContainerFor(c); - if (dockContainer instanceof DockableEditorTabbedContainer) { - splitters = ((DockableEditorTabbedContainer)dockContainer).getSplitters(); - } - - if (splitters == null) { - splitters = getMainSplitters(); - } - - return splitters; - } - - public List> getSelectionHistory() { - List> copy = new ArrayList>(); - for (Pair pair : mySelectionHistory) { - if (pair.second.getFiles().length == 0) { - final EditorWindow[] windows = pair.second.getOwner().getWindows(); - if (windows.length > 0 && windows[0] != null && windows[0].getFiles().length > 0) { - final Pair p = Pair.create(pair.first, windows[0]); - if (!copy.contains(p)) { - copy.add(p); - } - } - } else { - if (!copy.contains(pair)) { - copy.add(pair); - } - } - } - mySelectionHistory.clear(); - mySelectionHistory.addAll(copy); - return mySelectionHistory; - } - - public void addSelectionRecord(VirtualFile file, EditorWindow window) { - final Pair record = Pair.create(file, window); - mySelectionHistory.remove(record); - mySelectionHistory.add(0, record); - } - - public void removeSelectionRecord(VirtualFile file, EditorWindow window) { - mySelectionHistory.remove(Pair.create(file, window)); - } - - @Override - public ActionCallback getReady(@NotNull Object requestor) { - return myBusyObject.getReady(requestor); - } -} +/* + * Copyright 2000-2012 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.fileEditor.impl; + +import com.intellij.ProjectTopics; +import com.intellij.ide.IdeBundle; +import com.intellij.ide.plugins.PluginManager; +import com.intellij.ide.ui.UISettings; +import com.intellij.ide.ui.UISettingsListener; +import com.intellij.injected.editor.VirtualFileWindow; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.application.ex.ApplicationManagerEx; +import com.intellij.openapi.application.impl.LaterInvocator; +import com.intellij.openapi.command.CommandProcessor; +import com.intellij.openapi.components.ProjectComponent; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.ScrollType; +import com.intellij.openapi.editor.ex.EditorEx; +import com.intellij.openapi.editor.impl.EditorComponentImpl; +import com.intellij.openapi.extensions.Extensions; +import com.intellij.openapi.fileEditor.*; +import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx; +import com.intellij.openapi.fileEditor.ex.FileEditorProviderManager; +import com.intellij.openapi.fileEditor.ex.IdeDocumentHistory; +import com.intellij.openapi.fileEditor.impl.text.TextEditorImpl; +import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider; +import com.intellij.openapi.fileTypes.FileTypeEvent; +import com.intellij.openapi.fileTypes.FileTypeListener; +import com.intellij.openapi.fileTypes.FileTypeManager; +import com.intellij.openapi.project.DumbAwareRunnable; +import com.intellij.openapi.project.DumbService; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.impl.ProjectImpl; +import com.intellij.openapi.roots.ModuleRootAdapter; +import com.intellij.openapi.roots.ModuleRootEvent; +import com.intellij.openapi.startup.StartupManager; +import com.intellij.openapi.util.*; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.registry.Registry; +import com.intellij.openapi.vcs.FileStatus; +import com.intellij.openapi.vcs.FileStatusListener; +import com.intellij.openapi.vcs.FileStatusManager; +import com.intellij.openapi.vfs.*; +import com.intellij.openapi.wm.IdeFocusManager; +import com.intellij.openapi.wm.ToolWindowManager; +import com.intellij.openapi.wm.WindowManager; +import com.intellij.openapi.wm.ex.StatusBarEx; +import com.intellij.openapi.wm.impl.IdeFrameImpl; +import com.intellij.ui.FocusTrackback; +import com.intellij.ui.docking.DockContainer; +import com.intellij.ui.docking.DockManager; +import com.intellij.ui.tabs.impl.JBTabsImpl; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.messages.MessageBusConnection; +import com.intellij.util.messages.impl.MessageListenerList; +import com.intellij.util.ui.SameColor; +import com.intellij.util.ui.UIUtil; +import com.intellij.util.ui.update.MergingUpdateQueue; +import com.intellij.util.ui.update.Update; +import org.jdom.Element; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import javax.swing.border.Border; +import java.awt.*; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.lang.ref.WeakReference; +import java.util.*; +import java.util.List; + +/** + * @author Anton Katilin + * @author Eugene Belyaev + * @author Vladimir Kondratyev + */ +public class FileEditorManagerImpl extends FileEditorManagerEx implements ProjectComponent, JDOMExternalizable { + private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl"); + private static final Key WATCH_REQUEST_KEY = Key.create("WATCH_REQUEST_KEY"); + private static final Key DUMB_AWARE = Key.create("DUMB_AWARE"); + + private static final FileEditor[] EMPTY_EDITOR_ARRAY = {}; + private static final FileEditorProvider[] EMPTY_PROVIDER_ARRAY = {}; + public static final Key CLOSING_TO_REOPEN = Key.create("CLOSING_TO_REOPEN"); + + private volatile JPanel myPanels; + private EditorsSplitters mySplitters; + private final Project myProject; + private final List> mySelectionHistory = new ArrayList>(); + private WeakReference myLastSelectedComposite = new WeakReference(null); + + + private final MergingUpdateQueue myQueue = new MergingUpdateQueue("FileEditorManagerUpdateQueue", 50, true, null); + + private final BusyObject.Impl.Simple myBusyObject = new BusyObject.Impl.Simple(); + + /** + * Removes invalid myEditor and updates "modified" status. + */ + private final MyEditorPropertyChangeListener myEditorPropertyChangeListener = new MyEditorPropertyChangeListener(); + private final DockManager myDockManager; + private DockableEditorContainerFactory myContentFactory; + + public FileEditorManagerImpl(final Project project, DockManager dockManager) { +/* ApplicationManager.getApplication().assertIsDispatchThread(); */ + myProject = project; + myDockManager = dockManager; + myListenerList = + new MessageListenerList(myProject.getMessageBus(), FileEditorManagerListener.FILE_EDITOR_MANAGER); + + if (Extensions.getExtensions(FileEditorAssociateFinder.EP_NAME).length > 0) { + myListenerList.add(new FileEditorManagerAdapter() { + @Override + public void selectionChanged(FileEditorManagerEvent event) { + EditorsSplitters splitters = getSplitters(); + openAssociatedFile(event.getNewFile(), splitters.getCurrentWindow(), splitters); + } + }); + } + + myQueue.setTrackUiActivity(true); + } + + void initDockableContentFactory() { + if (myContentFactory != null) return; + + myContentFactory = new DockableEditorContainerFactory(myProject, this, myDockManager); + myDockManager.register(DockableEditorContainerFactory.TYPE, myContentFactory); + Disposer.register(myProject, myContentFactory); + } + + public static boolean isDumbAware(FileEditor editor) { + return Boolean.TRUE.equals(editor.getUserData(DUMB_AWARE)); + } + + //------------------------------------------------------------------------------- + + public JComponent getComponent() { + initUI(); + return myPanels; + } + + public EditorsSplitters getMainSplitters() { + initUI(); + + return mySplitters; + } + + public Set getAllSplitters() { + HashSet all = new HashSet(); + all.add(getMainSplitters()); + Set dockContainers = myDockManager.getContainers(); + for (DockContainer each : dockContainers) { + if (each instanceof DockableEditorTabbedContainer) { + all.add(((DockableEditorTabbedContainer)each).getSplitters()); + } + } + + return Collections.unmodifiableSet(all); + } + + private AsyncResult getActiveSplitters(boolean syncUsage) { + final boolean async = Registry.is("ide.windowSystem.asyncSplitters") && !syncUsage; + + final AsyncResult result = new AsyncResult(); + final IdeFocusManager fm = IdeFocusManager.getInstance(myProject); + Runnable run = new Runnable() { + @Override + public void run() { + if (myProject.isDisposed()) { + result.setRejected(); + return; + } + + Component focusOwner = fm.getFocusOwner(); + if (focusOwner == null && !async) { + focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); + } + + if (focusOwner == null && !async) { + focusOwner = fm.getLastFocusedFor(fm.getLastFocusedFrame()); + } + + DockContainer container = myDockManager.getContainerFor(focusOwner); + if (container == null && !async) { + focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow(); + container = myDockManager.getContainerFor(focusOwner); + } + + if (container instanceof DockableEditorTabbedContainer) { + result.setDone(((DockableEditorTabbedContainer)container).getSplitters()); + } + else { + result.setDone(getMainSplitters()); + } + } + }; + + if (async) { + fm.doWhenFocusSettlesDown(run); + } + else { + run.run(); + } + + return result; + } + + private final Object myInitLock = new Object(); + + private void initUI() { + if (myPanels == null) { + synchronized (myInitLock) { + if (myPanels == null) { + myPanels = new JPanel(new BorderLayout()) { + @Override + public Color getBackground() { + boolean navBar = UISettings.getInstance().SHOW_NAVIGATION_BAR; + if (navBar) { + return UIUtil.getSlightlyDarkerColor(UIUtil.isUnderAquaLookAndFeel() ? new SameColor(200) : UIUtil.getPanelBackground()); + } else { + return UIUtil.isUnderAquaLookAndFeel() ? new SameColor(189) : UIUtil.getPanelBackground(); + } + } + }; + myPanels.setBorder(new MyBorder()); + mySplitters = new EditorsSplitters(this, myDockManager, true); + myPanels.add(mySplitters, BorderLayout.CENTER); + } + } + } + } + + private static class MyBorder implements Border { + @Override + public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { + if (UIUtil.isUnderAquaLookAndFeel()) { + g.setColor(JBTabsImpl.MAC_AQUA_BG_COLOR); + final Insets insets = getBorderInsets(c); + if (insets.top > 0) { + g.fillRect(x, y, width, height + insets.top); + } + } + } + + @Override + public Insets getBorderInsets(Component c) { + return new Insets(0, 0, 0, 0); + } + + @Override + public boolean isBorderOpaque() { + return false; + } + } + + public JComponent getPreferredFocusedComponent() { + assertReadAccess(); + final EditorWindow window = getSplitters().getCurrentWindow(); + if (window != null) { + final EditorWithProviderComposite editor = window.getSelectedEditor(); + if (editor != null) { + return editor.getPreferredFocusedComponent(); + } + } + return null; + } + + //------------------------------------------------------- + + /** + * @return color of the file which corresponds to the + * file's status + */ + public Color getFileColor(@NotNull final VirtualFile file) { + final FileStatusManager fileStatusManager = FileStatusManager.getInstance(myProject); + Color statusColor = fileStatusManager != null ? fileStatusManager.getStatus(file).getColor() : Color.BLACK; + if (statusColor == null) statusColor = Color.BLACK; + return statusColor; + } + + public boolean isProblem(@NotNull final VirtualFile file) { + return false; + } + + public String getFileTooltipText(VirtualFile file) { + return FileUtil.getLocationRelativeToUserHome(file.getPresentableUrl()); + } + + public void updateFilePresentation(VirtualFile file) { + if (!isFileOpen(file)) return; + + updateFileColor(file); + updateFileIcon(file); + updateFileName(file); + updateFileBackgroundColor(file); + } + + /** + * Updates tab color for the specified file. The file + * should be opened in the myEditor, otherwise the method throws an assertion. + */ + private void updateFileColor(final VirtualFile file) { + Set all = getAllSplitters(); + for (EditorsSplitters each : all) { + each.updateFileColor(file); + } + } + + private void updateFileBackgroundColor(final VirtualFile file) { + Set all = getAllSplitters(); + for (EditorsSplitters each : all) { + each.updateFileBackgroundColor(file); + } + } + + /** + * Updates tab icon for the specified file. The file + * should be opened in the myEditor, otherwise the method throws an assertion. + */ + protected void updateFileIcon(final VirtualFile file) { + Set all = getAllSplitters(); + for (EditorsSplitters each : all) { + each.updateFileIcon(file); + } + } + + /** + * Updates tab title and tab tool tip for the specified file + */ + void updateFileName(@Nullable final VirtualFile file) { + // Queue here is to prevent title flickering when tab is being closed and two events arriving: with component==null and component==next focused tab + // only the last event makes sense to handle + myQueue.queue(new Update("UpdateFileName " + (file == null ? "" : file.getPath())) { + public boolean isExpired() { + return myProject.isDisposed() || !myProject.isOpen() || (file == null ? super.isExpired() : !file.isValid()); + } + + public void run() { + Set all = getAllSplitters(); + for (EditorsSplitters each : all) { + each.updateFileName(file); + } + } + }); + } + + //------------------------------------------------------- + + + public VirtualFile getFile(@NotNull final FileEditor editor) { + final EditorComposite editorComposite = getEditorComposite(editor); + if (editorComposite != null) { + return editorComposite.getFile(); + } + return null; + } + + public void unsplitWindow() { + final EditorWindow currentWindow = getActiveSplitters(true).getResult().getCurrentWindow(); + if (currentWindow != null) { + currentWindow.unsplit(true); + } + } + + public void unsplitAllWindow() { + final EditorWindow currentWindow = getActiveSplitters(true).getResult().getCurrentWindow(); + if (currentWindow != null) { + currentWindow.unsplitAll(); + } + } + + @Override + public int getWindowSplitCount() { + return getActiveSplitters(true).getResult().getSplitCount(); + } + + @Override + public boolean hasSplitOrUndockedWindows() { + Set splitters = getAllSplitters(); + if (splitters.size() > 1) return true; + return getWindowSplitCount() > 1; + } + + @NotNull + public EditorWindow[] getWindows() { + ArrayList windows = new ArrayList(); + Set all = getAllSplitters(); + for (EditorsSplitters each : all) { + EditorWindow[] eachList = each.getWindows(); + windows.addAll(Arrays.asList(eachList)); + } + + return windows.toArray(new EditorWindow[windows.size()]); + } + + public EditorWindow getNextWindow(@NotNull final EditorWindow window) { + final EditorWindow[] windows = getSplitters().getOrderedWindows(); + for (int i = 0; i != windows.length; ++i) { + if (windows[i].equals(window)) { + return windows[(i + 1) % windows.length]; + } + } + LOG.error("Not window found"); + return null; + } + + public EditorWindow getPrevWindow(@NotNull final EditorWindow window) { + final EditorWindow[] windows = getSplitters().getOrderedWindows(); + for (int i = 0; i != windows.length; ++i) { + if (windows[i].equals(window)) { + return windows[(i + windows.length - 1) % windows.length]; + } + } + LOG.error("Not window found"); + return null; + } + + public void createSplitter(final int orientation, @Nullable final EditorWindow window) { + // window was available from action event, for example when invoked from the tab menu of an editor that is not the 'current' + if (window != null) { + window.split(orientation, true, null, false); + } + // otherwise we'll split the current window, if any + else { + final EditorWindow currentWindow = getSplitters().getCurrentWindow(); + if (currentWindow != null) { + currentWindow.split(orientation, true, null, false); + } + } + } + + public void changeSplitterOrientation() { + final EditorWindow currentWindow = getSplitters().getCurrentWindow(); + if (currentWindow != null) { + currentWindow.changeOrientation(); + } + } + + + public void flipTabs() { + /* + if (myTabs == null) { + myTabs = new EditorTabs (this, UISettings.getInstance().EDITOR_TAB_PLACEMENT); + remove (mySplitters); + add (myTabs, BorderLayout.CENTER); + initTabs (); + } else { + remove (myTabs); + add (mySplitters, BorderLayout.CENTER); + myTabs.dispose (); + myTabs = null; + } + */ + myPanels.revalidate(); + } + + public boolean tabsMode() { + return false; + } + + private void setTabsMode(final boolean mode) { + if (tabsMode() != mode) { + flipTabs(); + } + //LOG.assertTrue (tabsMode () == mode); + } + + + public boolean isInSplitter() { + final EditorWindow currentWindow = getSplitters().getCurrentWindow(); + return currentWindow != null && currentWindow.inSplitter(); + } + + public boolean hasOpenedFile() { + final EditorWindow currentWindow = getSplitters().getCurrentWindow(); + return currentWindow != null && currentWindow.getSelectedEditor() != null; + } + + public VirtualFile getCurrentFile() { + return getActiveSplitters(true).getResult().getCurrentFile(); + } + + public AsyncResult getActiveWindow() { + return _getActiveWindow(false); + } + + private AsyncResult _getActiveWindow(boolean now) { + final AsyncResult result = new AsyncResult(); + getActiveSplitters(now).doWhenDone(new AsyncResult.Handler() { + @Override + public void run(EditorsSplitters editorsSplitters) { + result.setDone(editorsSplitters.getCurrentWindow()); + } + }); + + return result; + } + + public EditorWindow getCurrentWindow() { + return _getActiveWindow(true).getResult(); + } + + public void setCurrentWindow(final EditorWindow window) { + getActiveSplitters(true).getResult().setCurrentWindow(window, true); + } + + public void closeFile(@NotNull final VirtualFile file, @NotNull final EditorWindow window, final boolean transferFocus) { + assertDispatchThread(); + + CommandProcessor.getInstance().executeCommand(myProject, new Runnable() { + public void run() { + if (window.isFileOpen(file)) { + window.closeFile(file, true, transferFocus); + final List windows = window.getOwner().findWindows(file); + if (windows.isEmpty()) { // no more windows containing this file left + final LocalFileSystem.WatchRequest request = file.getUserData(WATCH_REQUEST_KEY); + if (request != null) { + LocalFileSystem.getInstance().removeWatchedRoot(request); + } + } + } + } + }, IdeBundle.message("command.close.active.editor"), null); + removeSelectionRecord(file, window); + } + + public void closeFile(@NotNull final VirtualFile file, @NotNull final EditorWindow window) { + closeFile(file, window, true); + } + + //============================= EditorManager methods ================================ + + public void closeFile(@NotNull final VirtualFile file) { + closeFile(file, true, false); + } + + public void closeFile(@NotNull final VirtualFile file, final boolean moveFocus, final boolean closeAllCopies) { + assertDispatchThread(); + + final LocalFileSystem.WatchRequest request = file.getUserData(WATCH_REQUEST_KEY); + if (request != null) { + LocalFileSystem.getInstance().removeWatchedRoot(request); + } + + CommandProcessor.getInstance().executeCommand(myProject, new Runnable() { + public void run() { + closeFileImpl(file, moveFocus, closeAllCopies); + } + }, "", null); + } + + + + private void closeFileImpl(@NotNull final VirtualFile file, final boolean moveFocus, boolean closeAllCopies) { + assertDispatchThread(); + runChange(new FileEditorManagerChange() { + public void run(EditorsSplitters splitters) { + splitters.closeFile(file, moveFocus); + } + }, closeAllCopies ? null : getActiveSplitters(true).getResult()); + } + + //-------------------------------------- Open File ---------------------------------------- + + @NotNull + public Pair openFileWithProviders(@NotNull final VirtualFile file, + final boolean focusEditor, + boolean searchForSplitter) { + if (!file.isValid()) { + throw new IllegalArgumentException("file is not valid: " + file); + } + assertDispatchThread(); + + EditorWindow wndToOpenIn = null; + if (searchForSplitter) { + Set all = getAllSplitters(); + EditorsSplitters active = getActiveSplitters(true).getResult(); + if (active.getCurrentWindow() != null && active.getCurrentWindow().isFileOpen(file)) { + wndToOpenIn = active.getCurrentWindow(); + } else { + for (EditorsSplitters splitters : all) { + final EditorWindow window = splitters.getCurrentWindow(); + if (window == null) continue; + + if (window.isFileOpen(file)) { + wndToOpenIn = window; + break; + } + } + } + } + else { + wndToOpenIn = getSplitters().getCurrentWindow(); + } + + EditorsSplitters splitters = getSplitters(); + + if (wndToOpenIn == null) { + wndToOpenIn = splitters.getOrCreateCurrentWindow(file); + } + + openAssociatedFile(file, wndToOpenIn, splitters); + return openFileImpl2(wndToOpenIn, file, focusEditor); + } + + private void openAssociatedFile(VirtualFile file, EditorWindow wndToOpenIn, EditorsSplitters splitters) { + EditorWindow[] windows = splitters.getWindows(); + + if (file != null && windows.length == 2) { + for (FileEditorAssociateFinder finder : Extensions.getExtensions(FileEditorAssociateFinder.EP_NAME)) { + VirtualFile associatedFile = finder.getAssociatedFileToOpen(myProject, file); + + if (associatedFile != null) { + EditorWindow currentWindow = splitters.getCurrentWindow(); + int idx = windows[0] == wndToOpenIn ? 1 : 0; + openFileImpl2(windows[idx], associatedFile, false); + + if (currentWindow != null) { + splitters.setCurrentWindow(currentWindow, false); + } + + break; + } + } + } + } + + @NotNull + @Override + public Pair openFileWithProviders(@NotNull VirtualFile file, + boolean focusEditor, + @NotNull EditorWindow window) { + if (!file.isValid()) { + throw new IllegalArgumentException("file is not valid: " + file); + } + assertDispatchThread(); + + return openFileImpl2(window, file, focusEditor); + } + + @NotNull + public Pair openFileImpl2(@NotNull final EditorWindow window, + @NotNull final VirtualFile file, + final boolean focusEditor) { + final Ref> result = new Ref>(); + CommandProcessor.getInstance().executeCommand(myProject, new Runnable() { + public void run() { + result.set(openFileImpl3(window, file, focusEditor, null, true)); + } + }, "", null); + return result.get(); + } + + /** + * @param file to be opened. Unlike openFile method, file can be + * invalid. For example, all file were invalidate and they are being + * removed one by one. If we have removed one invalid file, then another + * invalid file become selected. That's why we do not require that + * passed file is valid. + * @param entry map between FileEditorProvider and FileEditorState. If this parameter + * @param current + */ + @NotNull + Pair openFileImpl3(@NotNull final EditorWindow window, + @NotNull final VirtualFile file, + final boolean focusEditor, + @Nullable final HistoryEntry entry, + boolean current) { + return openFileImpl4(window, file, focusEditor, entry, current, -1); + } + + @NotNull + Pair openFileImpl4(@NotNull final EditorWindow window, + @NotNull final VirtualFile file, + final boolean focusEditor, + @Nullable final HistoryEntry entry, + boolean current, + int index) { + // Open file + FileEditor[] editors; + FileEditorProvider[] providers; + final EditorWithProviderComposite newSelectedComposite; + boolean newEditorCreated = false; + + final boolean open = window.isFileOpen(file); + if (open) { + // File is already opened. In this case we have to just select existing EditorComposite + newSelectedComposite = window.findFileComposite(file); + LOG.assertTrue(newSelectedComposite != null); + + editors = newSelectedComposite.getEditors(); + providers = newSelectedComposite.getProviders(); + } + else { + // File is not opened yet. In this case we have to create editors + // and select the created EditorComposite. + final FileEditorProviderManager editorProviderManager = FileEditorProviderManager.getInstance(); + providers = editorProviderManager.getProviders(myProject, file); + if (DumbService.getInstance(myProject).isDumb()) { + final List dumbAware = ContainerUtil.findAll(providers, new Condition() { + public boolean value(FileEditorProvider fileEditorProvider) { + return DumbService.isDumbAware(fileEditorProvider); + } + }); + providers = dumbAware.toArray(new FileEditorProvider[dumbAware.size()]); + } + + if (providers.length == 0) { + return Pair.create(EMPTY_EDITOR_ARRAY, EMPTY_PROVIDER_ARRAY); + } + newEditorCreated = true; + + getProject().getMessageBus().syncPublisher(FileEditorManagerListener.Before.FILE_EDITOR_MANAGER).beforeFileOpened(this, file); + + editors = new FileEditor[providers.length]; + for (int i = 0; i < providers.length; i++) { + try { + final FileEditorProvider provider = providers[i]; + LOG.assertTrue(provider != null); + LOG.assertTrue(provider.accept(myProject, file)); + final FileEditor editor = provider.createEditor(myProject, file); + LOG.assertTrue(editor != null); + LOG.assertTrue(editor.isValid()); + editors[i] = editor; + // Register PropertyChangeListener into editor + editor.addPropertyChangeListener(myEditorPropertyChangeListener); + editor.putUserData(DUMB_AWARE, DumbService.isDumbAware(provider)); + + if (current && editor instanceof TextEditorImpl) { + ((TextEditorImpl)editor).initFolding(); + } + } + catch (Exception e) { + LOG.error(e); + } + catch (AssertionError e) { + LOG.error(e); + } + } + + // Now we have to create EditorComposite and insert it into the TabbedEditorComponent. + // After that we have to select opened editor. + newSelectedComposite = new EditorWithProviderComposite(file, editors, providers, this); + + if (index >= 0) { + newSelectedComposite.getFile().putUserData(EditorWindow.INITIAL_INDEX_KEY, index); + } + } + + window.setEditor(newSelectedComposite, focusEditor); + + final EditorHistoryManager editorHistoryManager = EditorHistoryManager.getInstance(myProject); + for (int i = 0; i < editors.length; i++) { + final FileEditor editor = editors[i]; + if (editor instanceof TextEditor) { + // hack!!! + // This code prevents "jumping" on next repaint. + ((EditorEx)((TextEditor)editor).getEditor()).stopOptimizedScrolling(); + } + + final FileEditorProvider provider = providers[i];//getProvider(editor); + + // Restore editor state + FileEditorState state = null; + if (entry != null) { + state = entry.getState(provider); + } + if (state == null && !open) { + // We have to try to get state from the history only in case + // if editor is not opened. Otherwise history entry might have a state + // out of sync with the current editor state. + state = editorHistoryManager.getState(file, provider); + } + if (state != null) { + editor.setState(state); + } + } + + // Restore selected editor + final FileEditorProvider selectedProvider = editorHistoryManager.getSelectedProvider(file); + if (selectedProvider != null) { + final FileEditor[] _editors = newSelectedComposite.getEditors(); + final FileEditorProvider[] _providers = newSelectedComposite.getProviders(); + for (int i = _editors.length - 1; i >= 0; i--) { + final FileEditorProvider provider = _providers[i];//getProvider(_editors[i]); + if (provider.equals(selectedProvider)) { + newSelectedComposite.setSelectedEditor(i); + break; + } + } + } + + // Notify editors about selection changes + window.getOwner().setCurrentWindow(window, focusEditor); + window.getOwner().afterFileOpen(file); + + newSelectedComposite.getSelectedEditor().selectNotify(); + + final IdeFocusManager focusManager = IdeFocusManager.getInstance(myProject); + if (newEditorCreated) { + if (window.isShowing()) { + window.setPaintBlocked(true); + } + notifyPublisher(new Runnable() { + @Override + public void run() { + window.setPaintBlocked(false); + if (isFileOpen(file)) { + getProject().getMessageBus().syncPublisher(FileEditorManagerListener.FILE_EDITOR_MANAGER) + .fileOpened(FileEditorManagerImpl.this, file); + } + } + }); + + //Add request to watch this editor's virtual file + final VirtualFile parentDir = file.getParent(); + if (parentDir != null) { + final LocalFileSystem.WatchRequest request = LocalFileSystem.getInstance().addRootToWatch(parentDir.getPath(), false); + file.putUserData(WATCH_REQUEST_KEY, request); + } + } + + //[jeka] this is a hack to support back-forward navigation + // previously here was incorrect call to fireSelectionChanged() with a side-effect + ((IdeDocumentHistoryImpl)IdeDocumentHistory.getInstance(myProject)).onSelectionChanged(); + + // Transfer focus into editor + if (!ApplicationManagerEx.getApplicationEx().isUnitTestMode()) { + if (focusEditor) { + //myFirstIsActive = myTabbedContainer1.equals(tabbedContainer); + window.setAsCurrentWindow(true); + ToolWindowManager.getInstance(myProject).activateEditorComponent(); + focusManager.toFront(window.getOwner()); + } + } + + // Update frame and tab title + updateFileName(file); + + // Make back/forward work + IdeDocumentHistory.getInstance(myProject).includeCurrentCommandAsNavigation(); + + return Pair.create(editors, providers); + } + + @Override + public ActionCallback notifyPublisher(final Runnable runnable) { + final IdeFocusManager focusManager = IdeFocusManager.getInstance(myProject); + final ActionCallback done = new ActionCallback(); + return myBusyObject.execute(new ActiveRunnable() { + @Override + public ActionCallback run() { + focusManager.doWhenFocusSettlesDown(new ExpirableRunnable.ForProject(myProject) { + @Override + public void run() { + runnable.run(); + done.setDone(); + } + }); + return done; + } + }); + } + + public void setSelectedEditor(VirtualFile file, String fileEditorProviderId) { + EditorWithProviderComposite composite = getCurrentEditorWithProviderComposite(file); + if (composite == null) { + final List composites = getEditorComposites(file); + + if (composites.isEmpty()) return; + composite = composites.get(0); + } + + final FileEditorProvider[] editorProviders = composite.getProviders(); + final FileEditorProvider selectedProvider = composite.getSelectedEditorWithProvider().getSecond(); + + for (int i = 0; i < editorProviders.length; i++) { + if (editorProviders[i].getEditorTypeId().equals(fileEditorProviderId) && !selectedProvider.equals(editorProviders[i])) { + composite.setSelectedEditor(i); + composite.getSelectedEditor().selectNotify(); + } + } + } + + + @Nullable + EditorWithProviderComposite newEditorComposite(final VirtualFile file) { + if (file == null) { + return null; + } + + final FileEditorProviderManager editorProviderManager = FileEditorProviderManager.getInstance(); + final FileEditorProvider[] providers = editorProviderManager.getProviders(myProject, file); + final FileEditor[] editors = new FileEditor[providers.length]; + for (int i = 0; i < providers.length; i++) { + final FileEditorProvider provider = providers[i]; + LOG.assertTrue(provider != null); + LOG.assertTrue(provider.accept(myProject, file)); + final FileEditor editor = provider.createEditor(myProject, file); + editors[i] = editor; + LOG.assertTrue(editor.isValid()); + editor.addPropertyChangeListener(myEditorPropertyChangeListener); + } + + final EditorWithProviderComposite newComposite = new EditorWithProviderComposite(file, editors, providers, this); + final EditorHistoryManager editorHistoryManager = EditorHistoryManager.getInstance(myProject); + for (int i = 0; i < editors.length; i++) { + final FileEditor editor = editors[i]; + if (editor instanceof TextEditor) { + // hack!!! + // This code prevents "jumping" on next repaint. + //((EditorEx)((TextEditor)editor).getEditor()).stopOptimizedScrolling(); + } + + final FileEditorProvider provider = providers[i]; + +// Restore myEditor state + FileEditorState state = editorHistoryManager.getState(file, provider); + if (state != null) { + editor.setState(state); + } + } + return newComposite; + } + + @NotNull + public List openEditor(@NotNull final OpenFileDescriptor descriptor, final boolean focusEditor) { + assertDispatchThread(); + if (descriptor.getFile() instanceof VirtualFileWindow) { + VirtualFileWindow delegate = (VirtualFileWindow)descriptor.getFile(); + int hostOffset = delegate.getDocumentWindow().injectedToHost(descriptor.getOffset()); + OpenFileDescriptor realDescriptor = new OpenFileDescriptor(descriptor.getProject(), delegate.getDelegate(), hostOffset); + realDescriptor.setUseCurrentWindow(descriptor.isUseCurrentWindow()); + return openEditor(realDescriptor, focusEditor); + } + + final List result = new ArrayList(); + CommandProcessor.getInstance().executeCommand(myProject, new Runnable() { + public void run() { + VirtualFile file = descriptor.getFile(); + final FileEditor[] editors = openFile(file, focusEditor, !descriptor.isUseCurrentWindow()); + ContainerUtil.addAll(result, editors); + + boolean navigated = false; + for (final FileEditor editor : editors) { + if (editor instanceof NavigatableFileEditor && + getSelectedEditor(descriptor.getFile()) == editor) { // try to navigate opened editor + navigated = navigateAndSelectEditor((NavigatableFileEditor)editor, descriptor); + if (navigated) break; + } + } + + if (!navigated) { + for (final FileEditor editor : editors) { + if (editor instanceof NavigatableFileEditor && getSelectedEditor(descriptor.getFile()) != editor) { // try other editors + if (navigateAndSelectEditor((NavigatableFileEditor)editor, descriptor)) { + break; + } + } + } + } + } + }, "", null); + + return result; + } + + private boolean navigateAndSelectEditor(final NavigatableFileEditor editor, final OpenFileDescriptor descriptor) { + if (editor.canNavigateTo(descriptor)) { + setSelectedEditor(editor); + editor.navigateTo(descriptor); + return true; + } + + return false; + } + + private void setSelectedEditor(final FileEditor editor) { + final EditorWithProviderComposite composite = getEditorComposite(editor); + if (composite == null) return; + + final FileEditor[] editors = composite.getEditors(); + for (int i = 0; i < editors.length; i++) { + final FileEditor each = editors[i]; + if (editor == each) { + composite.setSelectedEditor(i); + composite.getSelectedEditor().selectNotify(); + break; + } + } + } + + @NotNull + public Project getProject() { + return myProject; + } + + @Nullable + public Editor openTextEditor(final OpenFileDescriptor descriptor, final boolean focusEditor) { + final Collection fileEditors = openEditor(descriptor, focusEditor); + for (FileEditor fileEditor : fileEditors) { + if (fileEditor instanceof TextEditor) { + setSelectedEditor(descriptor.getFile(), TextEditorProvider.getInstance().getEditorTypeId()); + Editor editor = ((TextEditor)fileEditor).getEditor(); + return getOpenedEditor(editor, focusEditor); + } + } + + return null; + } + + protected Editor getOpenedEditor(final Editor editor, final boolean focusEditor) { + return editor; + } + + public Editor getSelectedTextEditor() { + assertReadAccess(); + + final EditorWindow currentWindow = getSplitters().getCurrentWindow(); + if (currentWindow != null) { + final EditorWithProviderComposite selectedEditor = currentWindow.getSelectedEditor(); + if (selectedEditor != null && selectedEditor.getSelectedEditor() instanceof TextEditor) { + return ((TextEditor)selectedEditor.getSelectedEditor()).getEditor(); + } + } + + return null; + } + + + public boolean isFileOpen(@NotNull final VirtualFile file) { + return !getEditorComposites(file).isEmpty(); + } + + @NotNull + public VirtualFile[] getOpenFiles() { + HashSet openFiles = new HashSet(); + for (EditorsSplitters each : getAllSplitters()) { + openFiles.addAll(Arrays.asList(each.getOpenFiles())); + } + + return VfsUtilCore.toVirtualFileArray(openFiles); + } + + @NotNull + public VirtualFile[] getSelectedFiles() { + HashSet selectedFiles = new HashSet(); + for (EditorsSplitters each : getAllSplitters()) { + selectedFiles.addAll(Arrays.asList(each.getSelectedFiles())); + } + + return VfsUtilCore.toVirtualFileArray(selectedFiles); + } + + @NotNull + public FileEditor[] getSelectedEditors() { + HashSet selectedEditors = new HashSet(); + for (EditorsSplitters each : getAllSplitters()) { + selectedEditors.addAll(Arrays.asList(each.getSelectedEditors())); + } + + return selectedEditors.toArray(new FileEditor[selectedEditors.size()]); + } + + public EditorsSplitters getSplitters() { + EditorsSplitters active = getActiveSplitters(true).getResult(); + return active == null ? getMainSplitters() : active; + } + + @Nullable + public FileEditor getSelectedEditor(@NotNull final VirtualFile file) { + final Pair selectedEditorWithProvider = getSelectedEditorWithProvider(file); + return selectedEditorWithProvider == null ? null : selectedEditorWithProvider.getFirst(); + } + + + @Nullable + public Pair getSelectedEditorWithProvider(@NotNull VirtualFile file) { + if (file instanceof VirtualFileWindow) file = ((VirtualFileWindow)file).getDelegate(); + final EditorWithProviderComposite composite = getCurrentEditorWithProviderComposite(file); + if (composite != null) { + return composite.getSelectedEditorWithProvider(); + } + + final List composites = getEditorComposites(file); + return composites.isEmpty() ? null : composites.get(0).getSelectedEditorWithProvider(); + } + + @NotNull + public Pair getEditorsWithProviders(@NotNull final VirtualFile file) { + assertReadAccess(); + + final EditorWithProviderComposite composite = getCurrentEditorWithProviderComposite(file); + if (composite != null) { + return Pair.create(composite.getEditors(), composite.getProviders()); + } + + final List composites = getEditorComposites(file); + if (!composites.isEmpty()) { + return Pair.create(composites.get(0).getEditors(), composites.get(0).getProviders()); + } + else { + return Pair.create(EMPTY_EDITOR_ARRAY, EMPTY_PROVIDER_ARRAY); + } + } + + @NotNull + public FileEditor[] getEditors(@NotNull VirtualFile file) { + assertReadAccess(); + if (file instanceof VirtualFileWindow) file = ((VirtualFileWindow)file).getDelegate(); + + final EditorWithProviderComposite composite = getCurrentEditorWithProviderComposite(file); + if (composite != null) { + return composite.getEditors(); + } + + final List composites = getEditorComposites(file); + if (!composites.isEmpty()) { + return composites.get(0).getEditors(); + } + else { + return EMPTY_EDITOR_ARRAY; + } + } + + @NotNull + @Override + public FileEditor[] getAllEditors(@NotNull VirtualFile file) { + List editorComposites = getEditorComposites(file); + List editors = new ArrayList(); + for (EditorWithProviderComposite composite : editorComposites) { + ContainerUtil.addAll(editors, composite.getEditors()); + } + return editors.toArray(new FileEditor[editors.size()]); + } + + @Nullable + private EditorWithProviderComposite getCurrentEditorWithProviderComposite(@NotNull final VirtualFile virtualFile) { + final EditorWindow editorWindow = getSplitters().getCurrentWindow(); + if (editorWindow != null) { + return editorWindow.findFileComposite(virtualFile); + } + return null; + } + + @NotNull + public List getEditorComposites(final VirtualFile file) { + ArrayList result = new ArrayList(); + Set all = getAllSplitters(); + for (EditorsSplitters each : all) { + result.addAll(each.findEditorComposites(file)); + } + return result; + } + + @NotNull + public FileEditor[] getAllEditors() { + assertReadAccess(); + final ArrayList result = new ArrayList(); + final Set allSplitters = getAllSplitters(); + for (EditorsSplitters splitter : allSplitters) { + final EditorWithProviderComposite[] editorsComposites = splitter.getEditorsComposites(); + for (EditorWithProviderComposite editorsComposite : editorsComposites) { + final FileEditor[] editors = editorsComposite.getEditors(); + ContainerUtil.addAll(result, editors); + } + } + return result.toArray(new FileEditor[result.size()]); + } + + public void showEditorAnnotation(@NotNull FileEditor editor, @NotNull JComponent annotationComponent) { + addTopComponent(editor, annotationComponent); + } + + public void removeEditorAnnotation(@NotNull FileEditor editor, @NotNull JComponent annotationComponent) { + removeTopComponent(editor, annotationComponent); + } + + public void addTopComponent(@NotNull final FileEditor editor, @NotNull final JComponent component) { + final EditorComposite composite = getEditorComposite(editor); + if (composite != null) { + composite.addTopComponent(editor, component); + } + } + + public void removeTopComponent(@NotNull final FileEditor editor, @NotNull final JComponent component) { + final EditorComposite composite = getEditorComposite(editor); + if (composite != null) { + composite.removeTopComponent(editor, component); + } + } + + public void addBottomComponent(@NotNull final FileEditor editor, @NotNull final JComponent component) { + final EditorComposite composite = getEditorComposite(editor); + if (composite != null) { + composite.addBottomComponent(editor, component); + } + } + + public void removeBottomComponent(@NotNull final FileEditor editor, @NotNull final JComponent component) { + final EditorComposite composite = getEditorComposite(editor); + if (composite != null) { + composite.removeBottomComponent(editor, component); + } + } + + private final MessageListenerList myListenerList; + + public void addFileEditorManagerListener(@NotNull final FileEditorManagerListener listener) { + myListenerList.add(listener); + } + + public void addFileEditorManagerListener(@NotNull final FileEditorManagerListener listener, final Disposable parentDisposable) { + myListenerList.add(listener, parentDisposable); + } + + public void removeFileEditorManagerListener(@NotNull final FileEditorManagerListener listener) { + myListenerList.remove(listener); + } + +// ProjectComponent methods + + public void projectOpened() { + //myFocusWatcher.install(myWindows.getComponent ()); + getMainSplitters().startListeningFocus(); + + MessageBusConnection connection = myProject.getMessageBus().connect(myProject); + + final FileStatusManager fileStatusManager = FileStatusManager.getInstance(myProject); + if (fileStatusManager != null) { + /** + * Updates tabs colors + */ + final MyFileStatusListener myFileStatusListener = new MyFileStatusListener(); + fileStatusManager.addFileStatusListener(myFileStatusListener, myProject); + } + connection.subscribe(FileTypeManager.TOPIC, new MyFileTypeListener()); + connection.subscribe(ProjectTopics.PROJECT_ROOTS, new MyRootsListener()); + + /** + * Updates tabs names + */ + final MyVirtualFileListener myVirtualFileListener = new MyVirtualFileListener(); + VirtualFileManager.getInstance().addVirtualFileListener(myVirtualFileListener, myProject); + /** + * Extends/cuts number of opened tabs. Also updates location of tabs. + */ + final MyUISettingsListener myUISettingsListener = new MyUISettingsListener(); + UISettings.getInstance().addUISettingsListener(myUISettingsListener, myProject); + + StartupManager.getInstance(myProject).registerPostStartupActivity(new DumbAwareRunnable() { + public void run() { + + setTabsMode(UISettings.getInstance().EDITOR_TAB_PLACEMENT != UISettings.TABS_NONE); + + ToolWindowManager.getInstance(myProject).invokeLater(new Runnable() { + public void run() { + CommandProcessor.getInstance().executeCommand(myProject, new Runnable() { + public void run() { + + LaterInvocator.invokeLater(new Runnable() { + public void run() { + long currentTime = System.nanoTime(); + Long startTime = myProject.getUserData(ProjectImpl.CREATION_TIME); + if (startTime != null) { + LOG.info("Project opening took " + (currentTime - startTime.longValue()) / 1000000 + " ms"); + PluginManager.dumpPluginClassStatistics(); + } + } + }); +// group 1 + } + }, "", null); + } + }); + } + }); + } + + public void projectClosed() { + //myFocusWatcher.deinstall(myWindows.getComponent ()); + getMainSplitters().dispose(); + +// Dispose created editors. We do not use use closeEditor method because +// it fires event and changes history. + closeAllFiles(); + } + +// BaseCompomemnt methods + + @NotNull + public String getComponentName() { + return "FileEditorManager"; + } + + public void initComponent() { + + } + + public void disposeComponent() { /* really do nothing */ } + +//JDOMExternalizable methods + + public void writeExternal(final Element element) { + getMainSplitters().writeExternal(element); + } + + public void readExternal(final Element element) { + getMainSplitters().readExternal(element); + } + + @Nullable + private EditorWithProviderComposite getEditorComposite(@NotNull final FileEditor editor) { + for (EditorsSplitters splitters : getAllSplitters()) { + final EditorWithProviderComposite[] editorsComposites = splitters.getEditorsComposites(); + for (int i = editorsComposites.length - 1; i >= 0; i--) { + final EditorWithProviderComposite composite = editorsComposites[i]; + final FileEditor[] editors = composite.getEditors(); + for (int j = editors.length - 1; j >= 0; j--) { + final FileEditor _editor = editors[j]; + LOG.assertTrue(_editor != null); + if (editor.equals(_editor)) { + return composite; + } + } + } + } + return null; + } + +//======================= Misc ===================== + + private static void assertDispatchThread() { + ApplicationManager.getApplication().assertIsDispatchThread(); + } + + private static void assertReadAccess() { + ApplicationManager.getApplication().assertReadAccessAllowed(); + } + + public void fireSelectionChanged(final EditorComposite newSelectedComposite) { + final Trinity oldData = extract(myLastSelectedComposite.get()); + final Trinity newData = extract(newSelectedComposite); + myLastSelectedComposite = new WeakReference(newSelectedComposite); + final boolean filesEqual = oldData.first == null ? newData.first == null : oldData.first.equals(newData.first); + final boolean editorsEqual = oldData.second == null ? newData.second == null : oldData.second.equals(newData.second); + if (!filesEqual || !editorsEqual) { + if (oldData.first != null && newData.first != null) { + for (FileEditorAssociateFinder finder : Extensions.getExtensions(FileEditorAssociateFinder.EP_NAME)) { + VirtualFile associatedFile = finder.getAssociatedFileToOpen(myProject, oldData.first); + + if (Comparing.equal(associatedFile, newData.first)) { + return; + } + } + } + + final FileEditorManagerEvent event = + new FileEditorManagerEvent(this, oldData.first, oldData.second, oldData.third, newData.first, newData.second, newData.third); + final FileEditorManagerListener publisher = getProject().getMessageBus().syncPublisher(FileEditorManagerListener.FILE_EDITOR_MANAGER); + + if (newData.first != null) { + final JComponent component = newData.second.getComponent(); + final EditorWindowHolder holder = UIUtil.getParentOfType(EditorWindowHolder.class, component); + if (holder != null) { + addSelectionRecord(newData.first, holder.getEditorWindow()); + } + } + notifyPublisher(new Runnable() { + @Override + public void run() { + publisher.selectionChanged(event); + } + }); + } + } + + @NotNull + private static Trinity extract(@Nullable EditorComposite composite) { + final VirtualFile file; + final FileEditor editor; + final FileEditorProvider provider; + if (composite == null || composite.isDisposed()) { + file = null; + editor = null; + provider = null; + } + else { + file = composite.getFile(); + final Pair pair = composite.getSelectedEditorWithProvider(); + editor = pair.first; + provider = pair.second; + } + return new Trinity(file, editor, provider); + } + + public boolean isChanged(@NotNull final EditorComposite editor) { + final FileStatusManager fileStatusManager = FileStatusManager.getInstance(myProject); + if (fileStatusManager != null) { + VirtualFile file = editor.getFile(); + FileStatus status = fileStatusManager.getStatus(file); + if (status == FileStatus.UNKNOWN && !file.isWritable()) { + return false; + } + if (!status.equals(FileStatus.NOT_CHANGED)) { + return true; + } + } + return false; + } + + public void disposeComposite(@NotNull EditorWithProviderComposite editor) { + if (getAllEditors().length == 0) { + setCurrentWindow(null); + } + + if (editor.equals(getLastSelected())) { + editor.getSelectedEditor().deselectNotify(); + getSplitters().setCurrentWindow(null, false); + } + + final FileEditor[] editors = editor.getEditors(); + final FileEditorProvider[] providers = editor.getProviders(); + + final FileEditor selectedEditor = editor.getSelectedEditor(); + for (int i = editors.length - 1; i >= 0; i--) { + final FileEditor editor1 = editors[i]; + final FileEditorProvider provider = providers[i]; + if (!editor.equals(selectedEditor)) { // we already notified the myEditor (when fire event) + if (selectedEditor.equals(editor1)) { + editor1.deselectNotify(); + } + } + editor1.removePropertyChangeListener(myEditorPropertyChangeListener); + provider.disposeEditor(editor1); + } + + Disposer.dispose(editor); + } + + @Nullable + EditorComposite getLastSelected() { + final EditorWindow currentWindow = getActiveSplitters(true).getResult().getCurrentWindow(); + if (currentWindow != null) { + return currentWindow.getSelectedEditor(); + } + return null; + } + + public void runChange(FileEditorManagerChange change, EditorsSplitters splitters) { + Set target = new HashSet(); + if (splitters == null) { + target.addAll(getAllSplitters()); + } else { + target.add(splitters); + } + + for (EditorsSplitters each : target) { + each.myInsideChange++; + try { + change.run(each); + } + finally { + each.myInsideChange--; + } + } + } + + //================== Listeners ===================== + + /** + * Closes deleted files. Closes file which are in the deleted directories. + */ + private final class MyVirtualFileListener extends VirtualFileAdapter { + public void beforeFileDeletion(VirtualFileEvent e) { + assertDispatchThread(); + + boolean moveFocus = moveFocusOnDelete(); + + final VirtualFile file = e.getFile(); + final VirtualFile[] openFiles = getOpenFiles(); + for (int i = openFiles.length - 1; i >= 0; i--) { + if (VfsUtilCore.isAncestor(file, openFiles[i], false)) { + closeFile(openFiles[i], moveFocus, true); + } + } + } + + public void propertyChanged(VirtualFilePropertyEvent e) { + if (VirtualFile.PROP_NAME.equals(e.getPropertyName())) { + assertDispatchThread(); + final VirtualFile file = e.getFile(); + if (isFileOpen(file)) { + updateFileName(file); + updateFileIcon(file); // file type can change after renaming + updateFileBackgroundColor(file); + } + } + else if (VirtualFile.PROP_WRITABLE.equals(e.getPropertyName()) || VirtualFile.PROP_ENCODING.equals(e.getPropertyName())) { + // TODO: message bus? + updateIconAndStatusBar(e); + } + } + + private void updateIconAndStatusBar(final VirtualFilePropertyEvent e) { + assertDispatchThread(); + final VirtualFile file = e.getFile(); + if (isFileOpen(file)) { + updateFileIcon(file); + if (file.equals(getSelectedFiles()[0])) { // update "write" status + final StatusBarEx statusBar = (StatusBarEx)WindowManager.getInstance().getStatusBar(myProject); + assert statusBar != null; + statusBar.updateWidgets(); + } + } + } + + public void fileMoved(VirtualFileMoveEvent e) { + final VirtualFile file = e.getFile(); + final VirtualFile[] openFiles = getOpenFiles(); + for (final VirtualFile openFile : openFiles) { + if (VfsUtilCore.isAncestor(file, openFile, false)) { + updateFileName(openFile); + updateFileBackgroundColor(openFile); + } + } + } + } + + private static boolean moveFocusOnDelete() { + final Window window = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusedWindow(); + if (window != null) { + final Component component = FocusTrackback.getFocusFor(window); + if (component != null) { + return component instanceof EditorComponentImpl; + } + return window instanceof IdeFrameImpl; + } + return true; + } + + public boolean isInsideChange() { + return getSplitters().isInsideChange(); + } + + private final class MyEditorPropertyChangeListener implements PropertyChangeListener { + public void propertyChange(final PropertyChangeEvent e) { + assertDispatchThread(); + + final String propertyName = e.getPropertyName(); + if (FileEditor.PROP_MODIFIED.equals(propertyName)) { + final FileEditor editor = (FileEditor)e.getSource(); + final EditorComposite composite = getEditorComposite(editor); + if (composite != null) { + updateFileIcon(composite.getFile()); + } + } + else if (FileEditor.PROP_VALID.equals(propertyName)) { + final boolean valid = ((Boolean)e.getNewValue()).booleanValue(); + if (!valid) { + final FileEditor editor = (FileEditor)e.getSource(); + LOG.assertTrue(editor != null); + final EditorComposite composite = getEditorComposite(editor); + if (composite != null) { + closeFile(composite.getFile()); + } + } + } + + } + } + + + /** + * Gets events from VCS and updates color of myEditor tabs + */ + private final class MyFileStatusListener implements FileStatusListener { + public void fileStatusesChanged() { // update color of all open files + assertDispatchThread(); + LOG.debug("FileEditorManagerImpl.MyFileStatusListener.fileStatusesChanged()"); + final VirtualFile[] openFiles = getOpenFiles(); + for (int i = openFiles.length - 1; i >= 0; i--) { + final VirtualFile file = openFiles[i]; + LOG.assertTrue(file != null); + ApplicationManager.getApplication().invokeLater(new Runnable() { + public void run() { + if (LOG.isDebugEnabled()) { + LOG.debug("updating file status in tab for " + file.getPath()); + } + updateFileStatus(file); + } + }, ModalityState.NON_MODAL, myProject.getDisposed()); + } + } + + public void fileStatusChanged(@NotNull final VirtualFile file) { // update color of the file (if necessary) + assertDispatchThread(); + if (isFileOpen(file)) { + updateFileStatus(file); + } + } + + private void updateFileStatus(final VirtualFile file) { + updateFileColor(file); + updateFileIcon(file); + } + } + + /** + * Gets events from FileTypeManager and updates icons on tabs + */ + private final class MyFileTypeListener implements FileTypeListener { + public void beforeFileTypesChanged(FileTypeEvent event) { + } + + public void fileTypesChanged(final FileTypeEvent event) { + assertDispatchThread(); + final VirtualFile[] openFiles = getOpenFiles(); + for (int i = openFiles.length - 1; i >= 0; i--) { + final VirtualFile file = openFiles[i]; + LOG.assertTrue(file != null); + updateFileIcon(file); + } + } + } + + private class MyRootsListener extends ModuleRootAdapter { + public void rootsChanged(ModuleRootEvent event) { + EditorFileSwapper[] swappers = Extensions.getExtensions(EditorFileSwapper.EP_NAME); + + for (EditorWindow eachWindow : getWindows()) { + EditorWithProviderComposite selected = eachWindow.getSelectedEditor(); + EditorWithProviderComposite[] editors = eachWindow.getEditors(); + for (int i = 0; i < editors.length; i++) { + EditorWithProviderComposite editor = editors[i]; + VirtualFile file = editor.getFile(); + if (!file.isValid()) continue; + + Pair newFilePair = null; + + for (EditorFileSwapper each : swappers) { + newFilePair = each.getFileToSwapTo(myProject, editor); + if (newFilePair != null) break; + } + + if (newFilePair == null) continue; + + VirtualFile newFile = newFilePair.first; + if (newFile == null) continue; + + // already open + if (eachWindow.findFileIndex(newFile) != -1) continue; + + try { + newFile.putUserData(EditorWindow.INITIAL_INDEX_KEY, i); + Pair pair = openFileImpl2(eachWindow, newFile, editor == selected); + + if (newFilePair.second != null) { + TextEditorImpl openedEditor = EditorFileSwapper.findSinglePsiAwareEditor(pair.first); + if (openedEditor != null) { + openedEditor.getEditor().getCaretModel().moveToOffset(newFilePair.second); + openedEditor.getEditor().getScrollingModel().scrollToCaret(ScrollType.CENTER); + } + } + } + finally { + newFile.putUserData(EditorWindow.INITIAL_INDEX_KEY, null); + } + closeFile(file, eachWindow); + } + } + } + } + + /** + * Gets notifications from UISetting component to track changes of RECENT_FILES_LIMIT + * and EDITOR_TAB_LIMIT, etc values. + */ + private final class MyUISettingsListener implements UISettingsListener { + public void uiSettingsChanged(final UISettings source) { + assertDispatchThread(); + setTabsMode(source.EDITOR_TAB_PLACEMENT != UISettings.TABS_NONE); + + for (EditorsSplitters each : getAllSplitters()) { + each.setTabsPlacement(source.EDITOR_TAB_PLACEMENT); + each.trimToSize(source.EDITOR_TAB_LIMIT); + + // Tab layout policy + if (source.SCROLL_TAB_LAYOUT_IN_EDITOR) { + each.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); + } + else { + each.setTabLayoutPolicy(JTabbedPane.WRAP_TAB_LAYOUT); + } + } + + // "Mark modified files with asterisk" + final VirtualFile[] openFiles = getOpenFiles(); + for (int i = openFiles.length - 1; i >= 0; i--) { + final VirtualFile file = openFiles[i]; + updateFileIcon(file); + updateFileName(file); + updateFileBackgroundColor(file); + } + } + } + + public void closeAllFiles() { + final VirtualFile[] openFiles = getSplitters().getOpenFiles(); + for (VirtualFile openFile : openFiles) { + closeFile(openFile); + } + } + + @NotNull + public VirtualFile[] getSiblings(VirtualFile file) { + return getOpenFiles(); + } + + protected void queueUpdateFile(final VirtualFile file) { + myQueue.queue(new Update(file) { + public void run() { + if (isFileOpen(file)) { + updateFileIcon(file); + updateFileColor(file); + updateFileBackgroundColor(file); + } + + } + }); + } + + public EditorsSplitters getSplittersFor(Component c) { + EditorsSplitters splitters = null; + DockContainer dockContainer = myDockManager.getContainerFor(c); + if (dockContainer instanceof DockableEditorTabbedContainer) { + splitters = ((DockableEditorTabbedContainer)dockContainer).getSplitters(); + } + + if (splitters == null) { + splitters = getMainSplitters(); + } + + return splitters; + } + + public List> getSelectionHistory() { + List> copy = new ArrayList>(); + for (Pair pair : mySelectionHistory) { + if (pair.second.getFiles().length == 0) { + final EditorWindow[] windows = pair.second.getOwner().getWindows(); + if (windows.length > 0 && windows[0] != null && windows[0].getFiles().length > 0) { + final Pair p = Pair.create(pair.first, windows[0]); + if (!copy.contains(p)) { + copy.add(p); + } + } + } else { + if (!copy.contains(pair)) { + copy.add(pair); + } + } + } + mySelectionHistory.clear(); + mySelectionHistory.addAll(copy); + return mySelectionHistory; + } + + public void addSelectionRecord(VirtualFile file, EditorWindow window) { + final Pair record = Pair.create(file, window); + mySelectionHistory.remove(record); + mySelectionHistory.add(0, record); + } + + public void removeSelectionRecord(VirtualFile file, EditorWindow window) { + mySelectionHistory.remove(Pair.create(file, window)); + } + + @Override + public ActionCallback getReady(@NotNull Object requestor) { + return myBusyObject.getReady(requestor); + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/RefreshWorker.java b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/RefreshWorker.java index dd7f84436da4..e07854bc140c 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/RefreshWorker.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/RefreshWorker.java @@ -15,6 +15,7 @@ */ package com.intellij.openapi.vfs.newvfs.persistent; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VfsUtil; @@ -67,7 +68,7 @@ public class RefreshWorker { final VirtualFileSystemEntry file = (VirtualFileSystemEntry)myRefreshQueue.pullFirst(); if (!file.isDirty()) continue; - int attributes = file == root ? rootAttributes : delegate.getBooleanAttributes(file, -1); + int attributes = Comparing.equal(file, root) ? rootAttributes : delegate.getBooleanAttributes(file, -1); VirtualFileSystemEntry parent = file.getParent(); if (parent != null && checkAndScheduleAttributesChange(parent, file, delegate, attributes)) { // ignore everything else diff --git a/platform/platform-tests/testSrc/com/intellij/history/integration/IntegrationTestCase.java b/platform/platform-tests/testSrc/com/intellij/history/integration/IntegrationTestCase.java index 8c339dbfadc5..bca4cc9b34c0 100644 --- a/platform/platform-tests/testSrc/com/intellij/history/integration/IntegrationTestCase.java +++ b/platform/platform-tests/testSrc/com/intellij/history/integration/IntegrationTestCase.java @@ -30,6 +30,7 @@ import com.intellij.openapi.roots.ContentEntry; import com.intellij.openapi.roots.ModifiableRootModel; import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.util.Clock; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.*; @@ -177,7 +178,7 @@ public abstract class IntegrationTestCase extends PlatformTestCase { ModuleRootManager rm = ModuleRootManager.getInstance(myModule); ModifiableRootModel m = rm.getModifiableModel(); for (ContentEntry e : m.getContentEntries()) { - if (e.getFile() != myRoot) continue; + if (!Comparing.equal(e.getFile(), myRoot)) continue; e.addExcludeFolder(VfsUtilCore.pathToUrl(FileUtil.toSystemIndependentName(path))); } m.commit(); diff --git a/platform/testFramework/src/com/intellij/testFramework/PsiTestUtil.java b/platform/testFramework/src/com/intellij/testFramework/PsiTestUtil.java index d29cbdda2a1b..312bedd68b28 100644 --- a/platform/testFramework/src/com/intellij/testFramework/PsiTestUtil.java +++ b/platform/testFramework/src/com/intellij/testFramework/PsiTestUtil.java @@ -31,6 +31,7 @@ import com.intellij.openapi.roots.impl.ContentEntryImpl; import com.intellij.openapi.roots.impl.libraries.ProjectLibraryTable; import com.intellij.openapi.roots.libraries.Library; import com.intellij.openapi.roots.libraries.LibraryTable; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.*; @@ -189,7 +190,7 @@ public class PsiTestUtil { } }.execute().throwException(); for (ContentEntry entry : rootManager.getContentEntries()) { - if (entry.getFile() == vDir) { + if (Comparing.equal(entry.getFile(), vDir)) { Assert.assertFalse(((ContentEntryImpl)entry).isDisposed()); return entry; } diff --git a/platform/usageView/src/com/intellij/usages/UsageInfo2UsageAdapter.java b/platform/usageView/src/com/intellij/usages/UsageInfo2UsageAdapter.java index 6e84ee8c0c03..025217bdc4df 100644 --- a/platform/usageView/src/com/intellij/usages/UsageInfo2UsageAdapter.java +++ b/platform/usageView/src/com/intellij/usages/UsageInfo2UsageAdapter.java @@ -308,7 +308,7 @@ public class UsageInfo2UsageAdapter implements UsageInModule, if (!(other instanceof UsageInfo2UsageAdapter)) return false; UsageInfo2UsageAdapter u2 = (UsageInfo2UsageAdapter)other; assert u2 != this; - if (myLineNumber != u2.myLineNumber || getFile() != u2.getFile()) return false; + if (myLineNumber != u2.myLineNumber || !Comparing.equal(getFile(), u2.getFile())) return false; myMergedUsageInfos.addAll(u2.myMergedUsageInfos); Collections.sort(myMergedUsageInfos, new Comparator() { @Override diff --git a/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/formove/FilePathComparator.java b/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/formove/FilePathComparator.java index bda6f4c92805..090f88a3fe72 100644 --- a/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/formove/FilePathComparator.java +++ b/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/formove/FilePathComparator.java @@ -15,6 +15,7 @@ */ package com.intellij.openapi.diff.impl.patch.formove; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.vfs.VirtualFile; import java.util.Comparator; @@ -27,7 +28,7 @@ public final class FilePathComparator implements Comparator { } public int compare(final VirtualFile o1, final VirtualFile o2) { - if (o1 == o2) return 0; + if (Comparing.equal(o1, o2)) return 0; return o1.getPath().compareTo(o2.getPath()); } } diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/VcsVFSListener.java b/platform/vcs-api/src/com/intellij/openapi/vcs/VcsVFSListener.java index 2378efaec35f..2ab5790586fb 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/VcsVFSListener.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/VcsVFSListener.java @@ -269,7 +269,7 @@ public abstract class VcsVFSListener implements Disposable { final String newPath = newParentPath + "/" + newName; boolean foundExistingInfo = false; for (MovedFileInfo info : myMovedFiles) { - if (info.myFile == file) { + if (Comparing.equal(info.myFile, file)) { info.myNewPath = newPath; foundExistingInfo = true; break; diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/changes/IgnoredFileBean.java b/platform/vcs-api/src/com/intellij/openapi/vcs/changes/IgnoredFileBean.java index b59fa802abf5..0ed4bc41c12c 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/changes/IgnoredFileBean.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/changes/IgnoredFileBean.java @@ -23,6 +23,7 @@ package com.intellij.openapi.vcs.changes; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VfsUtil; @@ -105,15 +106,15 @@ public class IgnoredFileBean { } else { VirtualFile selector = resolve(); - if (selector == NullVirtualFile.INSTANCE) return false; + if (Comparing.equal(selector, NullVirtualFile.INSTANCE)) return false; if (myType == IgnoreSettingsType.FILE) { - return selector == file; + return Comparing.equal(selector, file); } else { if ("./".equals(myPath)) { // special case for ignoring the project base dir (IDEADEV-16056) - return !file.isDirectory() && file.getParent() == selector; + return !file.isDirectory() && Comparing.equal(file.getParent(), selector); } return VfsUtil.isAncestor(selector, file, false); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/annotate/VFSForAnnotationListener.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/annotate/VFSForAnnotationListener.java index 2dd1f6faefcc..5032b8f8cde6 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/annotate/VFSForAnnotationListener.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/annotate/VFSForAnnotationListener.java @@ -15,6 +15,7 @@ */ package com.intellij.openapi.vcs.annotate; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileAdapter; import com.intellij.openapi.vfs.VirtualFileEvent; @@ -32,7 +33,7 @@ public class VFSForAnnotationListener extends VirtualFileAdapter { } public void propertyChanged(VirtualFilePropertyEvent event) { - if (myFile != event.getFile()) return; + if (!Comparing.equal(myFile, event.getFile())) return; if (! event.isFromRefresh()) return; if (event.getPropertyName().equals(VirtualFile.PROP_WRITABLE)) { @@ -43,7 +44,7 @@ public class VFSForAnnotationListener extends VirtualFileAdapter { } public void contentsChanged(VirtualFileEvent event) { - if (myFile != event.getFile()) return; + if (!Comparing.equal(myFile, event.getFile())) return; if (! event.isFromRefresh()) return; if (! myFile.isWritable()) { fireAnnotationChanged(); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsDirtyScopeImpl.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsDirtyScopeImpl.java index fcdd21c64d16..210fda396d50 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsDirtyScopeImpl.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsDirtyScopeImpl.java @@ -18,6 +18,7 @@ package com.intellij.openapi.vcs.changes; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Computable; import com.intellij.openapi.vcs.*; import com.intellij.openapi.vfs.VfsUtil; @@ -224,7 +225,7 @@ public class VcsDirtyScopeImpl extends VcsModifiableDirtyScope { if (newcomer.isDirectory()) { final List files = new ArrayList(myDirtyFiles); for (FilePath oldBoy : files) { - if (!oldBoy.isDirectory() && oldBoy.getVirtualFileParent() == newcomer.getVirtualFile()) { + if (!oldBoy.isDirectory() && Comparing.equal(oldBoy.getVirtualFileParent(), newcomer.getVirtualFile())) { myDirtyFiles.remove(oldBoy); } } diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XBreakpointManagerImpl.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XBreakpointManagerImpl.java index 277facd9601b..0f7cd85cee45 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XBreakpointManagerImpl.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XBreakpointManagerImpl.java @@ -21,6 +21,7 @@ import com.intellij.openapi.components.PersistentStateComponent; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.startup.StartupManager; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.JDOMUtil; import com.intellij.openapi.util.MultiValuesMap; import com.intellij.openapi.vfs.VirtualFile; @@ -84,7 +85,7 @@ public class XBreakpointManagerImpl implements XBreakpointManager, PersistentSta XBreakpointBase[] breakpoints = getAllBreakpoints(); for (XBreakpointBase breakpoint : breakpoints) { XSourcePosition position = breakpoint.getSourcePosition(); - if (position != null && position.getFile() == file) { + if (position != null && Comparing.equal(position.getFile(), file)) { fireBreakpointChanged(breakpoint); } } diff --git a/plugins/android/src/org/jetbrains/android/compiler/AndroidAutogenerator.java b/plugins/android/src/org/jetbrains/android/compiler/AndroidAutogenerator.java index 3d0ce4836092..248fd15b45f3 100644 --- a/plugins/android/src/org/jetbrains/android/compiler/AndroidAutogenerator.java +++ b/plugins/android/src/org/jetbrains/android/compiler/AndroidAutogenerator.java @@ -13,6 +13,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.LocalFileSystem; @@ -375,7 +376,7 @@ public class AndroidAutogenerator { final List filesToDelete = new ArrayList(); for (final VirtualFile f : files) { - if (f != vFile && VfsUtilCore.isAncestor(genDir, f, true)) { + if (!Comparing.equal(f, vFile) && VfsUtilCore.isAncestor(genDir, f, true)) { filesToDelete.add(f); } } diff --git a/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java b/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java index b31a35ad3cf7..79c21de7a9ca 100644 --- a/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java +++ b/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java @@ -175,7 +175,7 @@ public class AndroidCompileUtil { } for (Map.Entry entry : presentableFilesMap.entrySet()) { - if (file == entry.getValue()) { + if (Comparing.equal(file, entry.getValue())) { return entry.getKey().getUrl(); } } @@ -185,7 +185,7 @@ public class AndroidCompileUtil { private static void collectChildrenRecursively(@NotNull VirtualFile root, @NotNull VirtualFile anchor, @NotNull Collection result) { - if (root == anchor) { + if (Comparing.equal(root, anchor)) { return; } @@ -194,11 +194,11 @@ public class AndroidCompileUtil { return; } for (VirtualFile child : parent.getChildren()) { - if (child != anchor) { + if (!Comparing.equal(child, anchor)) { result.add(child); } } - if (parent != root) { + if (!Comparing.equal(parent, root)) { collectChildrenRecursively(root, parent, result); } } @@ -219,7 +219,7 @@ public class AndroidCompileUtil { if (contentEntry != null) { ExcludeFolder excludedFolder = null; for (ExcludeFolder folder : contentEntry.getExcludeFolders()) { - if (folder.getFile() == excludedRoot) { + if (Comparing.equal(folder.getFile(), excludedRoot)) { excludedFolder = folder; break; } @@ -280,7 +280,7 @@ public class AndroidCompileUtil { boolean markedAsSource = false; for (VirtualFile existingRoot : manager.getSourceRoots()) { - if (existingRoot == root) { + if (Comparing.equal(existingRoot, root)) { markedAsSource = true; } } @@ -301,7 +301,7 @@ public class AndroidCompileUtil { ((CompilerConfigurationImpl)CompilerConfiguration.getInstance(project)).getExcludedEntriesConfiguration(); for (ExcludeEntryDescription description : configuration.getExcludeEntryDescriptions()) { - if (description.getVirtualFile() == dir) { + if (Comparing.equal(description.getVirtualFile(), dir)) { return; } } @@ -477,7 +477,7 @@ public class AndroidCompileUtil { PsiFile psiFile = c.getContainingFile(); if (className.equals(FileUtil.getNameWithoutExtension(psiFile.getName()))) { VirtualFile virtualFile = psiFile.getVirtualFile(); - if (virtualFile != null && projectFileIndex.getSourceRootForFile(virtualFile) == sourceRoot) { + if (virtualFile != null && Comparing.equal(projectFileIndex.getSourceRootForFile(virtualFile), sourceRoot)) { final String path = virtualFile.getPath(); File f = new File(path); diff --git a/plugins/android/src/org/jetbrains/android/compiler/AndroidIncludingCompiler.java b/plugins/android/src/org/jetbrains/android/compiler/AndroidIncludingCompiler.java index 396542a80caa..ac9d297ef759 100644 --- a/plugins/android/src/org/jetbrains/android/compiler/AndroidIncludingCompiler.java +++ b/plugins/android/src/org/jetbrains/android/compiler/AndroidIncludingCompiler.java @@ -23,6 +23,7 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.roots.ContentIterator; import com.intellij.openapi.roots.ModuleFileIndex; import com.intellij.openapi.roots.ModuleRootManager; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.LocalFileSystem; @@ -64,7 +65,7 @@ public class AndroidIncludingCompiler implements SourceGeneratingCompiler { VirtualFile[] srcRoots = ModuleRootManager.getInstance(depFacet.getModule()).getSourceRoots(); for (VirtualFile depSourceRoot : srcRoots) { - if (depSourceRoot != genSrcRoot) { + if (!Comparing.equal(depSourceRoot, genSrcRoot)) { VirtualFile file = depSourceRoot.findFileByRelativePath(generatedFileRelativePath); if (file != null) { return file; @@ -112,7 +113,7 @@ public class AndroidIncludingCompiler implements SourceGeneratingCompiler { VirtualFile[] srcRoots = ModuleRootManager.getInstance(depFacet.getModule()).getSourceRoots(); for (VirtualFile depSourceRoot : srcRoots) { - if (depSourceRoot != aptGenSrcRoot && depSourceRoot != aidlGenSrcRoot) { + if (!Comparing.equal(depSourceRoot, aptGenSrcRoot) && !Comparing.equal(depSourceRoot, aidlGenSrcRoot)) { collectCompilableFiles(module, depFacet.getModule(), context, depSourceRoot, qName2Item); } } diff --git a/plugins/android/src/org/jetbrains/android/compiler/AndroidPackagingCompiler.java b/plugins/android/src/org/jetbrains/android/compiler/AndroidPackagingCompiler.java index 3bc201520429..c100cdd996b4 100644 --- a/plugins/android/src/org/jetbrains/android/compiler/AndroidPackagingCompiler.java +++ b/plugins/android/src/org/jetbrains/android/compiler/AndroidPackagingCompiler.java @@ -27,6 +27,7 @@ import com.intellij.openapi.roots.DependencyScope; import com.intellij.openapi.roots.ModuleOrderEntry; import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.roots.OrderEntry; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.JarFileSystem; import com.intellij.openapi.vfs.VfsUtil; @@ -79,7 +80,7 @@ public class AndroidPackagingCompiler implements PackagingCompiler { VirtualFile resDir = facet != null ? AndroidRootUtil.getResourceDir(facet) : null; ModuleRootManager manager = ModuleRootManager.getInstance(module); for (VirtualFile sourceRoot : manager.getSourceRoots(includingTests)) { - if (resDir != sourceRoot) { + if (!Comparing.equal(resDir, sourceRoot)) { result.add(sourceRoot); } } diff --git a/plugins/android/src/org/jetbrains/android/compiler/AndroidRenderscriptCompiler.java b/plugins/android/src/org/jetbrains/android/compiler/AndroidRenderscriptCompiler.java index b1e6f16e6248..b554fa8de67f 100644 --- a/plugins/android/src/org/jetbrains/android/compiler/AndroidRenderscriptCompiler.java +++ b/plugins/android/src/org/jetbrains/android/compiler/AndroidRenderscriptCompiler.java @@ -11,6 +11,7 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VfsUtilCore; @@ -254,7 +255,7 @@ public class AndroidRenderscriptCompiler implements SourceGeneratingCompiler { } final VirtualFile parent = sourceFile.getParent(); - if (parent == sourceRoot) { + if (Comparing.equal(parent, sourceRoot)) { return genFolder.getPath(); } diff --git a/plugins/android/src/org/jetbrains/android/facet/AndroidResourceFilesListener.java b/plugins/android/src/org/jetbrains/android/facet/AndroidResourceFilesListener.java index d356350f68e3..829e32356f86 100644 --- a/plugins/android/src/org/jetbrains/android/facet/AndroidResourceFilesListener.java +++ b/plugins/android/src/org/jetbrains/android/facet/AndroidResourceFilesListener.java @@ -21,6 +21,7 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtil; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Computable; import com.intellij.openapi.vfs.*; import com.intellij.util.ui.update.MergingUpdateQueue; @@ -178,7 +179,7 @@ class AndroidResourceFilesListener extends VirtualFileAdapter { final VirtualFile resourceDir = AndroidRootUtil.getResourceDir(myFacet); - if (gp == resourceDir && + if (Comparing.equal(gp, resourceDir) && ResourceFolderType.VALUES.getName().equals(AndroidCommonUtils.getResourceTypeByDirName(parent.getName()))) { myFacet.getLocalResourceManager().invalidateAttributeDefinitions(); } @@ -186,7 +187,7 @@ class AndroidResourceFilesListener extends VirtualFileAdapter { final List modes = new ArrayList(); - if (AndroidAptCompiler.isToCompileModule(module, myFacet.getConfiguration()) && manifestFile == file) { + if (AndroidAptCompiler.isToCompileModule(module, myFacet.getConfiguration()) && Comparing.equal(manifestFile, file)) { final Manifest manifest = myFacet.getManifest(); final String aPackage = manifest != null ? manifest.getPackage().getValue() : null; @@ -200,19 +201,19 @@ class AndroidResourceFilesListener extends VirtualFileAdapter { if (file.getFileType() == AndroidIdlFileType.ourFileType) { VirtualFile sourceRoot = findSourceRoot(myModule, file); - if (sourceRoot != null && AndroidRootUtil.getAidlGenDir(myFacet) != sourceRoot) { + if (sourceRoot != null && !Comparing.equal(AndroidRootUtil.getAidlGenDir(myFacet), sourceRoot)) { modes.add(AndroidAutogeneratorMode.AIDL); } } if (file.getFileType() == AndroidRenderscriptFileType.INSTANCE) { final VirtualFile sourceRoot = findSourceRoot(myModule, file); - if (sourceRoot != null && AndroidRootUtil.getRenderscriptGenDir(myFacet) != sourceRoot) { + if (sourceRoot != null && !Comparing.equal(AndroidRootUtil.getRenderscriptGenDir(myFacet), sourceRoot)) { modes.add(AndroidAutogeneratorMode.RENDERSCRIPT); } } - if (manifestFile == file) { + if (Comparing.equal(manifestFile, file)) { modes.add(AndroidAutogeneratorMode.BUILDCONFIG); } return modes; @@ -223,8 +224,8 @@ class AndroidResourceFilesListener extends VirtualFileAdapter { if (update instanceof MyUpdate) { VirtualFile hisFile = ((MyUpdate)update).myEvent.getFile(); VirtualFile file = myEvent.getFile(); - - if (hisFile == file) { + + if (Comparing.equal(hisFile, file)) { return true; } diff --git a/plugins/android/src/org/jetbrains/android/facet/AndroidRootUtil.java b/plugins/android/src/org/jetbrains/android/facet/AndroidRootUtil.java index 15a04560ccb4..50dcad1b79ff 100644 --- a/plugins/android/src/org/jetbrains/android/facet/AndroidRootUtil.java +++ b/plugins/android/src/org/jetbrains/android/facet/AndroidRootUtil.java @@ -25,6 +25,7 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.roots.*; import com.intellij.openapi.roots.libraries.Library; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.FileUtil; @@ -101,7 +102,7 @@ public class AndroidRootUtil { final VirtualFile moduleFileParentDir = LocalFileSystem.getInstance().findFileByPath(moduleFileParentDirPath); if (moduleFileParentDir != null) { for (VirtualFile contentRoot : contentRoots) { - if (contentRoot == moduleFileParentDir) { + if (Comparing.equal(contentRoot, moduleFileParentDir)) { root = contentRoot; } } diff --git a/plugins/android/src/org/jetbrains/android/inspections/lint/AndroidLintExternalAnnotator.java b/plugins/android/src/org/jetbrains/android/inspections/lint/AndroidLintExternalAnnotator.java index dee646c5cbaf..53f95528fae4 100644 --- a/plugins/android/src/org/jetbrains/android/inspections/lint/AndroidLintExternalAnnotator.java +++ b/plugins/android/src/org/jetbrains/android/inspections/lint/AndroidLintExternalAnnotator.java @@ -68,7 +68,7 @@ public class AndroidLintExternalAnnotator extends ExternalAnnotator 0) { - final VirtualFile contentRoot = files[0]; - final AndroidFacet facet = AndroidUtils.addAndroidFacet(rootModel.getModule(), contentRoot, myProjectType == ProjectType.LIBRARY); - - if (myProjectType == null) { - ImportDependenciesUtil.importDependencies(rootModel.getModule(), true); - return; - } - - final Project project = rootModel.getProject(); - final VirtualFile sourceRoot = findSourceRoot(rootModel); - - if (myProjectType == ProjectType.TEST) { - assert myTestedModule != null; - facet.getConfiguration().PACK_TEST_CODE = true; - ModuleOrderEntry entry = rootModel.addModuleOrderEntry(myTestedModule); - entry.setScope(DependencyScope.PROVIDED); - } - - StartupManager.getInstance(project).runWhenProjectIsInitialized(new Runnable() { - public void run() { - ApplicationManager.getApplication().invokeLater(new Runnable() { - public void run() { - ApplicationManager.getApplication().runWriteAction(new Runnable() { - public void run() { - createProject(contentRoot, sourceRoot, facet); - } - }); - } - }); - } - }); - } - } - - private void createProject(VirtualFile contentRoot, VirtualFile sourceRoot, AndroidFacet facet) { - if (myProjectType == ProjectType.APPLICATION) { - createDirectoryStructure(contentRoot, sourceRoot, facet); - } - else { - createProjectByAndroidTool(contentRoot, sourceRoot, facet); - } - } - - private void createDirectoryStructure(VirtualFile contentRoot, VirtualFile sourceRoot, AndroidFacet facet) { - if (isHelloAndroid()) { - if (createProjectByAndroidTool(contentRoot, sourceRoot, facet)) { - return; - } - } - Project project = facet.getModule().getProject(); - createManifestFileAndAntFiles(project, contentRoot); - createResourcesAndLibs(project, contentRoot); - PsiDirectory sourceDir = sourceRoot != null ? PsiManager.getInstance(project).findDirectory(sourceRoot) : null; - createActivityAndSetupManifest(facet, sourceDir); - if (myTargetSelectionMode != null) { - addRunConfiguration(facet, myTargetSelectionMode, myPreferredAvd); - } - } - - @NotNull - private static String getAntProjectName(@NotNull String moduleName) { - StringBuilder result = new StringBuilder(); - for (int i = 0; i < moduleName.length(); i++) { - char c = moduleName.charAt(i); - if (!(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || Character.isDigit(c))) { - c = '_'; - } - result.append(c); - } - return result.toString(); - } - - private boolean createProjectByAndroidTool(final VirtualFile contentRoot, - final VirtualFile sourceRoot, - final AndroidFacet facet) { - - - File tempContentRoot = null; - - // todo: support custom non-empty source root - - if (sourceRoot != null && - sourceRoot.getChildren().length == 0 && - (sourceRoot.getParent() != contentRoot || !SdkConstants.FD_SOURCES.equals(sourceRoot.getName()))) { - try { - tempContentRoot = FileUtil.createTempDirectory("android_temp_content_root", "tmp"); - } - catch (IOException e) { - LOG.error(e); - } - } - - final Module module = facet.getModule(); - AndroidPlatform platform = AndroidPlatform.parse(mySdk); - - if (platform == null) { - Messages.showErrorDialog(module.getProject(), "Cannot parse Android SDK", CommonBundle.getErrorTitle()); - return true; - } - - final IAndroidTarget target = platform.getTarget(); - - final String androidToolPath = - platform.getSdkData().getLocation() + File.separator + AndroidCommonUtils.toolPath(SdkConstants.androidCmdName()); - - if (!new File(androidToolPath).exists()) { - return false; - } - - final GeneralCommandLine commandLine = new GeneralCommandLine(); - commandLine.setExePath(FileUtil.toSystemDependentName(androidToolPath)); - - commandLine.addParameter("create"); - - switch (myProjectType) { - case APPLICATION: - commandLine.addParameter("project"); - break; - case LIBRARY: - commandLine.addParameter("lib-project"); - break; - case TEST: - commandLine.addParameter("test-project"); - break; - } - - commandLine.addParameters("--name"); - commandLine.addParameter(getAntProjectName(module.getName())); - - commandLine.addParameters("--path"); - final String targetDirectoryPath = tempContentRoot != null ? tempContentRoot.getPath() : contentRoot.getPath(); - commandLine.addParameter(FileUtil.toSystemDependentName(targetDirectoryPath)); - - if (myProjectType == ProjectType.APPLICATION || myProjectType == ProjectType.LIBRARY) { - String apiLevel = target.hashString(); - commandLine.addParameter("--target"); - commandLine.addParameter(apiLevel); - commandLine.addParameter("--package"); - commandLine.addParameter(myPackageName); - } - - if (myProjectType == ProjectType.APPLICATION) { - commandLine.addParameter("--activity"); - commandLine.addParameter(myActivityName); - } - else if (myProjectType == ProjectType.TEST) { - String moduleDirPath = AndroidRootUtil.getModuleDirPath(myTestedModule); - assert moduleDirPath != null; - commandLine.addParameter("--main"); - commandLine.addParameter(FileUtil.toSystemDependentName(moduleDirPath)); - } - - final File finalTempContentRoot = tempContentRoot; - - ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { - @Override - public void run() { - final Project project = module.getProject(); - AndroidUtils.runExternalTool(commandLine, project); - - if (finalTempContentRoot != null) { - final File[] children = finalTempContentRoot.listFiles(); - - if (children != null) { - for (File child : children) { - if (SdkConstants.FD_SOURCES.equals(child.getName())) { - continue; - } - final File to = new File(contentRoot.getPath(), child.getName()); - - if (!FileUtil.moveDirWithContent(child, to)) { - LOG.error("Cannot move content from " + child.getPath() + " to " + to.getPath()); - } - } - } - - final File tempSourceRoot = new File(finalTempContentRoot, SdkConstants.FD_SOURCES); - if (tempSourceRoot.exists()) { - final File to = new File(sourceRoot.getPath()); - - if (!FileUtil.moveDirWithContent(tempSourceRoot, to)) { - LOG.error("Cannot move content from " + tempSourceRoot.getPath() + " to " + to.getPath()); - } - } - } - - StartupManager.getInstance(project).runWhenProjectIsInitialized(new Runnable() { - public void run() { - FileDocumentManager.getInstance().saveAllDocuments(); - } - }); - contentRoot.refresh(false, true); - - ApplicationManager.getApplication().invokeLater(new Runnable() { - @Override - public void run() { - - if (contentRoot.findChild(SdkConstants.FN_ANDROID_MANIFEST_XML) == null) { - AndroidUtils.printMessageToConsole(project, "The project wasn't generated by 'android' tool.", - ConsoleViewContentType.ERROR_OUTPUT); - return; - } - - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - try { - if (project.isDisposed()) { - return; - } - if (myProjectType == ProjectType.APPLICATION) { - assignApplicationName(facet); - configureManifest(facet, target); - createChildDirectoryIfNotExist(project, contentRoot, SdkConstants.FD_ASSETS); - createChildDirectoryIfNotExist(project, contentRoot, SdkConstants.FD_NATIVE_LIBS); - } - else if (myProjectType == ProjectType.LIBRARY && myPackageName != null) { - final String[] dirs = myPackageName.split("\\."); - VirtualFile file = sourceRoot; - - for (String dir : dirs) { - if (file == null || dir.length() == 0) { - break; - } - final VirtualFile childDir = file.findChild(dir); - file = childDir != null - ? childDir - : file.createChildDirectory(project, dir); - } - } - } - catch (IOException e) { - LOG.error(e); - } - } - }); - - ApplicationManager.getApplication().runReadAction(new Runnable() { - @Override - public void run() { - if (project.isDisposed() || facet.getModule().isDisposed()) { - return; - } - - if (myTargetSelectionMode != null) { - if (myProjectType == ProjectType.APPLICATION) { - addRunConfiguration(facet, myTargetSelectionMode, myPreferredAvd); - } - else if (myProjectType == ProjectType.TEST) { - addTestRunConfiguration(facet, myTargetSelectionMode, myPreferredAvd); - } - } - } - }); - - new ReformatCodeProcessor(project, module, false).run(); - } - }); - } - }); - return true; - } - - private static void configureManifest(@NotNull AndroidFacet facet, @NotNull IAndroidTarget target) { - final Manifest manifest = facet.getManifest(); - if (manifest == null) { - return; - } - - final XmlTag manifestTag = manifest.getXmlTag(); - if (manifestTag == null) { - return; - } - - XmlTag usesSdkTag = manifestTag.createChildTag("uses-sdk", "", null, false); - if (usesSdkTag != null) { - usesSdkTag = manifestTag.addSubTag(usesSdkTag, true); - usesSdkTag.setAttribute("minSdkVersion", SdkConstants.NS_RESOURCES, target.getVersion().getApiString()); - } - - final PsiFile manifestFile = manifestTag.getContainingFile(); - if (manifestFile != null) { - CodeStyleManager.getInstance(manifestFile.getProject()).reformat(manifestFile); - } - } - - private void assignApplicationName(AndroidFacet facet) { - if (myApplicationName == null || myApplicationName.length() == 0) { - return; - } - - final LocalResourceManager manager = facet.getLocalResourceManager(); - ResourceElement appNameResElement = null; - final String appNameResource = "app_name"; - - for (ResourceElement resElement : manager.getValueResources("string")) { - if (appNameResource.equals(resElement.getName().getValue())) { - appNameResElement = resElement; - } - } - - final String normalizedAppName = AndroidResourceUtil.normalizeXmlResourceValue(myApplicationName.replace("\\", "\\\\")); - - if (appNameResElement == null) { - final String fileName = AndroidResourceUtil.getDefaultResourceFileName(ResourceType.STRING); - assert fileName != null; - AndroidResourceUtil.createValueResource(facet.getModule(), appNameResource, ResourceType.STRING, fileName, Collections - .singletonList(AndroidConstants.FD_RES_VALUES), normalizedAppName); - } - else { - appNameResElement.setStringValue(normalizedAppName); - } - - final Manifest manifest = facet.getManifest(); - - if (manifest != null) { - manifest.getApplication().getLabel().setValue(ResourceValue.referenceTo('@', null, "string", appNameResource)); - } - } - - private void createManifestFileAndAntFiles(Project project, VirtualFile contentRoot) { - VirtualFile existingManifestFile = contentRoot.findChild(FN_ANDROID_MANIFEST_XML); - if (existingManifestFile != null) { - return; - } - try { - AndroidFileTemplateProvider - .createFromTemplate(project, contentRoot, AndroidFileTemplateProvider.ANDROID_MANIFEST_TEMPLATE, FN_ANDROID_MANIFEST_XML); - - AndroidPlatform platform = AndroidPlatform.parse(mySdk); - - if (platform == null) { - Messages.showErrorDialog(project, "Cannot parse Android SDK: 'default.properties' won't be generated", CommonBundle.getErrorTitle()); - return; - } - - Properties properties = FileTemplateManager.getInstance().getDefaultProperties(project); - properties.setProperty("TARGET", platform.getTarget().hashString()); - AndroidFileTemplateProvider.createFromTemplate(project, contentRoot, AndroidFileTemplateProvider.DEFAULT_PROPERTIES_TEMPLATE, - SdkConstants.FN_PROJECT_PROPERTIES, properties); - } - catch (Exception e) { - LOG.error(e); - } - } - - private void addRunConfiguration(@NotNull AndroidFacet facet, - @NotNull TargetSelectionMode targetSelectionMode, - @Nullable String targetAvd) { - String activityClass; - if (isHelloAndroid()) { - activityClass = myPackageName + '.' + myActivityName; - } - else { - activityClass = null; - } - Module module = facet.getModule(); - AndroidUtils.addRunConfiguration(facet, activityClass, false, targetSelectionMode, targetAvd); - } - - private static void addTestRunConfiguration(final AndroidFacet facet, @NotNull TargetSelectionMode mode, @Nullable String preferredAvd) { - Project project = facet.getModule().getProject(); - RunManagerEx runManager = RunManagerEx.getInstanceEx(project); - Module module = facet.getModule(); - RunnerAndConfigurationSettings settings = runManager - .createRunConfiguration(module.getName(), AndroidTestRunConfigurationType.getInstance().getFactory()); - - AndroidTestRunConfiguration configuration = (AndroidTestRunConfiguration)settings.getConfiguration(); - configuration.setModule(module); - configuration.setTargetSelectionMode(mode); - if (preferredAvd != null) { - configuration.PREFERRED_AVD = preferredAvd; - } - - runManager.addConfiguration(settings, false); - runManager.setActiveConfiguration(settings); - } - - private boolean isHelloAndroid() { - return myActivityName.length() > 0; - } - - @Nullable - private static VirtualFile findSourceRoot(ModifiableRootModel model) { - VirtualFile genSourceRoot = AndroidRootUtil.getStandartGenDir(model.getModule()); - for (VirtualFile root : model.getSourceRoots()) { - if (root != genSourceRoot) { - return root; - } - } - return null; - } - - @Nullable - private static PsiDirectory createPackageIfPossible(final PsiDirectory sourceDir, String packageName) { - if (sourceDir != null) { - final String[] ids = packageName.split("\\."); - return ApplicationManager.getApplication().runWriteAction(new Computable() { - public PsiDirectory compute() { - PsiDirectory dir = sourceDir; - for (String id : ids) { - PsiDirectory child = dir.findSubdirectory(id); - dir = child == null ? dir.createSubdirectory(id) : child; - } - return dir; - } - }); - } - return null; - } - - private void createActivityAndSetupManifest(final AndroidFacet facet, final PsiDirectory sourceDir) { - if (myPackageName != null) { - CommandProcessor.getInstance().executeCommand(facet.getModule().getProject(), new ExternalChangeAction() { - public void run() { - Runnable action = new Runnable() { - public void run() { - PsiDirectory packageDir = createPackageIfPossible(sourceDir, myPackageName); - if (packageDir == null) return; - final Manifest manifest = facet.getManifest(); - if (manifest != null) { - manifest.getPackage().setValue(myPackageName); - final Project project = facet.getModule().getProject(); - StartupManager.getInstance(project).runWhenProjectIsInitialized(new Runnable() { - public void run() { - FileDocumentManager.getInstance().saveAllDocuments(); - } - }); - - assignApplicationName(facet); - - final AndroidPlatform platform = AndroidPlatform.parse(mySdk); - if (platform != null) { - configureManifest(facet, platform.getTarget()); - } - } - } - }; - ApplicationManager.getApplication().runWriteAction(action); - } - }, AndroidBundle.message("build.android.module.process.title"), null); - } - } - - private void createResourcesAndLibs(final Project project, final VirtualFile rootDir) { - ApplicationManager.getApplication().runWriteAction(new Runnable() { - public void run() { - try { - createChildDirectoryIfNotExist(project, rootDir, SdkConstants.FD_ASSETS); - createChildDirectoryIfNotExist(project, rootDir, SdkConstants.FD_NATIVE_LIBS); - VirtualFile resDir = createChildDirectoryIfNotExist(project, rootDir, SdkConstants.FD_RES); - VirtualFile drawableDir = createChildDirectoryIfNotExist(project, resDir, AndroidConstants.FD_RES_DRAWABLE); - createFileFromResource(project, drawableDir, "icon.png", "/icons/androidLarge.png"); - if (isHelloAndroid()) { - VirtualFile valuesDir = createChildDirectoryIfNotExist(project, resDir, AndroidConstants.FD_RES_VALUES); - createFileFromResource(project, valuesDir, "strings.xml", "res/values/strings.xml"); - VirtualFile layoutDir = AndroidUtils.createChildDirectoryIfNotExist(project, resDir, AndroidConstants.FD_RES_LAYOUT); - createFileFromResource(project, layoutDir, "main.xml", "res/layout/main.xml"); - } - } - catch (IOException e) { - LOG.error(e); - } - } - }); - } - - private static void createFileFromResource(Project project, VirtualFile drawableDir, String name, String resourceFilePath) - throws IOException { - if (drawableDir.findChild(name) != null) { - return; - } - VirtualFile resFile = drawableDir.createChildData(project, name); - InputStream stream = AndroidModuleBuilder.class.getResourceAsStream(resourceFilePath); - try { - byte[] bytes = FileUtil.adaptiveLoadBytes(stream); - resFile.setBinaryContent(bytes); - } - finally { - stream.close(); - } - } - - public void setProjectType(ProjectType projectType) { - myProjectType = projectType; - } - - public void setActivityName(String activityName) { - myActivityName = activityName; - } - - public void setApplicationName(String applicationName) { - myApplicationName = applicationName; - } - - public void setPackageName(String packageName) { - myPackageName = packageName; - } - - public void setSdk(Sdk sdk) { - mySdk = sdk; - } - - public ModuleType getModuleType() { - return StdModuleTypes.JAVA; - } - - public void setTestedModule(Module module) { - myTestedModule = module; - } - - public void setTargetSelectionMode(TargetSelectionMode targetSelectionMode) { - myTargetSelectionMode = targetSelectionMode; - } - - public void setPreferredAvd(String preferredAvd) { - myPreferredAvd = preferredAvd; - } - - @Override - public ModuleWizardStep[] createWizardSteps(WizardContext wizardContext, ModulesProvider modulesProvider) { - List steps = new ArrayList(); - ProjectWizardStepFactory factory = ProjectWizardStepFactory.getInstance(); - steps.add(factory.createSourcePathsStep(wizardContext, this, null, "reference.dialogs.new.project.fromScratch.source")); - - if (!hasAppropriateJdk()) { - steps.add(new ProjectJdkForModuleStep(wizardContext, JavaSdk.getInstance()) { - @Override - public void updateDataModel() { - // do nothing - } - - @Override - public boolean validate() { - for (Object o : getAllJdks()) { - if (o instanceof Sdk) { - Sdk sdk = (Sdk)o; - if (AndroidSdkUtils.isApplicableJdk(sdk)) { - return true; - } - } - } - Messages.showErrorDialog(AndroidBundle.message("no.jdk.error"), CommonBundle.getErrorTitle()); - return false; - } - }); - } - - steps.add(new AndroidModuleWizardStep(this, wizardContext)); - return steps.toArray(new ModuleWizardStep[steps.size()]); - } - - public Icon getBigIcon() { - return AndroidUtils.ANDROID_ICON_24; - } - - public String getDescription() { - return AndroidBundle.message("android.module.type.description"); - } - - public String getPresentableName() { - return AndroidBundle.message("android.module.type.name"); - } - - @Override - public String getBuilderId() { - return getClass().getName(); - } - - private static boolean hasAppropriateJdk() { - for (Sdk sdk : ProjectJdkTable.getInstance().getAllJdks()) { - if (AndroidSdkUtils.isApplicableJdk(sdk)) { - return true; - } - } - return false; - } -} +/* + * Copyright 2000-2010 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.android.newProject; + +import com.android.AndroidConstants; +import com.android.resources.ResourceType; +import com.android.sdklib.IAndroidTarget; +import com.android.sdklib.SdkConstants; +import com.intellij.CommonBundle; +import com.intellij.codeInsight.actions.ReformatCodeProcessor; +import com.intellij.execution.RunManagerEx; +import com.intellij.execution.RunnerAndConfigurationSettings; +import com.intellij.execution.configurations.GeneralCommandLine; +import com.intellij.execution.ui.ConsoleViewContentType; +import com.intellij.ide.fileTemplates.FileTemplateManager; +import com.intellij.ide.util.projectWizard.*; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.command.CommandProcessor; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleType; +import com.intellij.openapi.module.StdModuleTypes; +import com.intellij.openapi.options.ConfigurationException; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.projectRoots.JavaSdk; +import com.intellij.openapi.projectRoots.ProjectJdkTable; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.roots.*; +import com.intellij.openapi.roots.ui.configuration.ModulesProvider; +import com.intellij.openapi.startup.StartupManager; +import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.pom.java.LanguageLevel; +import com.intellij.psi.ExternalChangeAction; +import com.intellij.psi.PsiDirectory; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiManager; +import com.intellij.psi.codeStyle.CodeStyleManager; +import com.intellij.psi.xml.XmlTag; +import org.jetbrains.android.AndroidFileTemplateProvider; +import org.jetbrains.android.dom.manifest.Manifest; +import org.jetbrains.android.dom.resources.ResourceElement; +import org.jetbrains.android.dom.resources.ResourceValue; +import org.jetbrains.android.facet.AndroidFacet; +import org.jetbrains.android.facet.AndroidRootUtil; +import org.jetbrains.android.importDependencies.ImportDependenciesUtil; +import org.jetbrains.android.resourceManagers.LocalResourceManager; +import org.jetbrains.android.run.TargetSelectionMode; +import org.jetbrains.android.run.testing.AndroidTestRunConfiguration; +import org.jetbrains.android.run.testing.AndroidTestRunConfigurationType; +import org.jetbrains.android.sdk.AndroidPlatform; +import org.jetbrains.android.sdk.AndroidSdkUtils; +import org.jetbrains.android.util.AndroidBundle; +import org.jetbrains.android.util.AndroidCommonUtils; +import org.jetbrains.android.util.AndroidResourceUtil; +import org.jetbrains.android.util.AndroidUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Properties; + +import static com.android.sdklib.SdkConstants.FN_ANDROID_MANIFEST_XML; +import static org.jetbrains.android.util.AndroidUtils.createChildDirectoryIfNotExist; + +/** + * @author Eugene.Kudelevsky + */ +public class AndroidModuleBuilder extends JavaModuleBuilder { + private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.newProject.AndroidModuleBuilder"); + + private String myPackageName; + private String myApplicationName; + private String myActivityName; + private ProjectType myProjectType; + private Module myTestedModule; + private Sdk mySdk; + private TargetSelectionMode myTargetSelectionMode; + private String myPreferredAvd; + + public void setupRootModel(final ModifiableRootModel rootModel) throws ConfigurationException { + super.setupRootModel(rootModel); + + rootModel.setSdk(mySdk); + + final LanguageLevelModuleExtension moduleExt = rootModel.getModuleExtension(LanguageLevelModuleExtension.class); + + if (moduleExt != null) { + LanguageLevel languageLevel = moduleExt.getLanguageLevel(); + if (languageLevel == null) { + final LanguageLevelProjectExtension projectExt = LanguageLevelProjectExtension.getInstance(rootModel.getProject()); + if (projectExt != null) { + languageLevel = projectExt.getLanguageLevel(); + } + } + if (languageLevel == LanguageLevel.JDK_1_3) { + moduleExt.setLanguageLevel(LanguageLevel.JDK_1_5); + } + } + + VirtualFile[] files = rootModel.getContentRoots(); + if (files.length > 0) { + final VirtualFile contentRoot = files[0]; + final AndroidFacet facet = AndroidUtils.addAndroidFacet(rootModel.getModule(), contentRoot, myProjectType == ProjectType.LIBRARY); + + if (myProjectType == null) { + ImportDependenciesUtil.importDependencies(rootModel.getModule(), true); + return; + } + + final Project project = rootModel.getProject(); + final VirtualFile sourceRoot = findSourceRoot(rootModel); + + if (myProjectType == ProjectType.TEST) { + assert myTestedModule != null; + facet.getConfiguration().PACK_TEST_CODE = true; + ModuleOrderEntry entry = rootModel.addModuleOrderEntry(myTestedModule); + entry.setScope(DependencyScope.PROVIDED); + } + + StartupManager.getInstance(project).runWhenProjectIsInitialized(new Runnable() { + public void run() { + ApplicationManager.getApplication().invokeLater(new Runnable() { + public void run() { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + public void run() { + createProject(contentRoot, sourceRoot, facet); + } + }); + } + }); + } + }); + } + } + + private void createProject(VirtualFile contentRoot, VirtualFile sourceRoot, AndroidFacet facet) { + if (myProjectType == ProjectType.APPLICATION) { + createDirectoryStructure(contentRoot, sourceRoot, facet); + } + else { + createProjectByAndroidTool(contentRoot, sourceRoot, facet); + } + } + + private void createDirectoryStructure(VirtualFile contentRoot, VirtualFile sourceRoot, AndroidFacet facet) { + if (isHelloAndroid()) { + if (createProjectByAndroidTool(contentRoot, sourceRoot, facet)) { + return; + } + } + Project project = facet.getModule().getProject(); + createManifestFileAndAntFiles(project, contentRoot); + createResourcesAndLibs(project, contentRoot); + PsiDirectory sourceDir = sourceRoot != null ? PsiManager.getInstance(project).findDirectory(sourceRoot) : null; + createActivityAndSetupManifest(facet, sourceDir); + if (myTargetSelectionMode != null) { + addRunConfiguration(facet, myTargetSelectionMode, myPreferredAvd); + } + } + + @NotNull + private static String getAntProjectName(@NotNull String moduleName) { + StringBuilder result = new StringBuilder(); + for (int i = 0; i < moduleName.length(); i++) { + char c = moduleName.charAt(i); + if (!(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || Character.isDigit(c))) { + c = '_'; + } + result.append(c); + } + return result.toString(); + } + + private boolean createProjectByAndroidTool(final VirtualFile contentRoot, + final VirtualFile sourceRoot, + final AndroidFacet facet) { + + + File tempContentRoot = null; + + // todo: support custom non-empty source root + + if (sourceRoot != null && + sourceRoot.getChildren().length == 0 && + (!Comparing.equal(sourceRoot.getParent(), contentRoot) || !SdkConstants.FD_SOURCES.equals(sourceRoot.getName()))) { + try { + tempContentRoot = FileUtil.createTempDirectory("android_temp_content_root", "tmp"); + } + catch (IOException e) { + LOG.error(e); + } + } + + final Module module = facet.getModule(); + AndroidPlatform platform = AndroidPlatform.parse(mySdk); + + if (platform == null) { + Messages.showErrorDialog(module.getProject(), "Cannot parse Android SDK", CommonBundle.getErrorTitle()); + return true; + } + + final IAndroidTarget target = platform.getTarget(); + + final String androidToolPath = + platform.getSdkData().getLocation() + File.separator + AndroidCommonUtils.toolPath(SdkConstants.androidCmdName()); + + if (!new File(androidToolPath).exists()) { + return false; + } + + final GeneralCommandLine commandLine = new GeneralCommandLine(); + commandLine.setExePath(FileUtil.toSystemDependentName(androidToolPath)); + + commandLine.addParameter("create"); + + switch (myProjectType) { + case APPLICATION: + commandLine.addParameter("project"); + break; + case LIBRARY: + commandLine.addParameter("lib-project"); + break; + case TEST: + commandLine.addParameter("test-project"); + break; + } + + commandLine.addParameters("--name"); + commandLine.addParameter(getAntProjectName(module.getName())); + + commandLine.addParameters("--path"); + final String targetDirectoryPath = tempContentRoot != null ? tempContentRoot.getPath() : contentRoot.getPath(); + commandLine.addParameter(FileUtil.toSystemDependentName(targetDirectoryPath)); + + if (myProjectType == ProjectType.APPLICATION || myProjectType == ProjectType.LIBRARY) { + String apiLevel = target.hashString(); + commandLine.addParameter("--target"); + commandLine.addParameter(apiLevel); + commandLine.addParameter("--package"); + commandLine.addParameter(myPackageName); + } + + if (myProjectType == ProjectType.APPLICATION) { + commandLine.addParameter("--activity"); + commandLine.addParameter(myActivityName); + } + else if (myProjectType == ProjectType.TEST) { + String moduleDirPath = AndroidRootUtil.getModuleDirPath(myTestedModule); + assert moduleDirPath != null; + commandLine.addParameter("--main"); + commandLine.addParameter(FileUtil.toSystemDependentName(moduleDirPath)); + } + + final File finalTempContentRoot = tempContentRoot; + + ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { + @Override + public void run() { + final Project project = module.getProject(); + AndroidUtils.runExternalTool(commandLine, project); + + if (finalTempContentRoot != null) { + final File[] children = finalTempContentRoot.listFiles(); + + if (children != null) { + for (File child : children) { + if (SdkConstants.FD_SOURCES.equals(child.getName())) { + continue; + } + final File to = new File(contentRoot.getPath(), child.getName()); + + if (!FileUtil.moveDirWithContent(child, to)) { + LOG.error("Cannot move content from " + child.getPath() + " to " + to.getPath()); + } + } + } + + final File tempSourceRoot = new File(finalTempContentRoot, SdkConstants.FD_SOURCES); + if (tempSourceRoot.exists()) { + final File to = new File(sourceRoot.getPath()); + + if (!FileUtil.moveDirWithContent(tempSourceRoot, to)) { + LOG.error("Cannot move content from " + tempSourceRoot.getPath() + " to " + to.getPath()); + } + } + } + + StartupManager.getInstance(project).runWhenProjectIsInitialized(new Runnable() { + public void run() { + FileDocumentManager.getInstance().saveAllDocuments(); + } + }); + contentRoot.refresh(false, true); + + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + + if (contentRoot.findChild(SdkConstants.FN_ANDROID_MANIFEST_XML) == null) { + AndroidUtils.printMessageToConsole(project, "The project wasn't generated by 'android' tool.", + ConsoleViewContentType.ERROR_OUTPUT); + return; + } + + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + try { + if (project.isDisposed()) { + return; + } + if (myProjectType == ProjectType.APPLICATION) { + assignApplicationName(facet); + configureManifest(facet, target); + createChildDirectoryIfNotExist(project, contentRoot, SdkConstants.FD_ASSETS); + createChildDirectoryIfNotExist(project, contentRoot, SdkConstants.FD_NATIVE_LIBS); + } + else if (myProjectType == ProjectType.LIBRARY && myPackageName != null) { + final String[] dirs = myPackageName.split("\\."); + VirtualFile file = sourceRoot; + + for (String dir : dirs) { + if (file == null || dir.length() == 0) { + break; + } + final VirtualFile childDir = file.findChild(dir); + file = childDir != null + ? childDir + : file.createChildDirectory(project, dir); + } + } + } + catch (IOException e) { + LOG.error(e); + } + } + }); + + ApplicationManager.getApplication().runReadAction(new Runnable() { + @Override + public void run() { + if (project.isDisposed() || facet.getModule().isDisposed()) { + return; + } + + if (myTargetSelectionMode != null) { + if (myProjectType == ProjectType.APPLICATION) { + addRunConfiguration(facet, myTargetSelectionMode, myPreferredAvd); + } + else if (myProjectType == ProjectType.TEST) { + addTestRunConfiguration(facet, myTargetSelectionMode, myPreferredAvd); + } + } + } + }); + + new ReformatCodeProcessor(project, module, false).run(); + } + }); + } + }); + return true; + } + + private static void configureManifest(@NotNull AndroidFacet facet, @NotNull IAndroidTarget target) { + final Manifest manifest = facet.getManifest(); + if (manifest == null) { + return; + } + + final XmlTag manifestTag = manifest.getXmlTag(); + if (manifestTag == null) { + return; + } + + XmlTag usesSdkTag = manifestTag.createChildTag("uses-sdk", "", null, false); + if (usesSdkTag != null) { + usesSdkTag = manifestTag.addSubTag(usesSdkTag, true); + usesSdkTag.setAttribute("minSdkVersion", SdkConstants.NS_RESOURCES, target.getVersion().getApiString()); + } + + final PsiFile manifestFile = manifestTag.getContainingFile(); + if (manifestFile != null) { + CodeStyleManager.getInstance(manifestFile.getProject()).reformat(manifestFile); + } + } + + private void assignApplicationName(AndroidFacet facet) { + if (myApplicationName == null || myApplicationName.length() == 0) { + return; + } + + final LocalResourceManager manager = facet.getLocalResourceManager(); + ResourceElement appNameResElement = null; + final String appNameResource = "app_name"; + + for (ResourceElement resElement : manager.getValueResources("string")) { + if (appNameResource.equals(resElement.getName().getValue())) { + appNameResElement = resElement; + } + } + + final String normalizedAppName = AndroidResourceUtil.normalizeXmlResourceValue(myApplicationName.replace("\\", "\\\\")); + + if (appNameResElement == null) { + final String fileName = AndroidResourceUtil.getDefaultResourceFileName(ResourceType.STRING); + assert fileName != null; + AndroidResourceUtil.createValueResource(facet.getModule(), appNameResource, ResourceType.STRING, fileName, Collections + .singletonList(AndroidConstants.FD_RES_VALUES), normalizedAppName); + } + else { + appNameResElement.setStringValue(normalizedAppName); + } + + final Manifest manifest = facet.getManifest(); + + if (manifest != null) { + manifest.getApplication().getLabel().setValue(ResourceValue.referenceTo('@', null, "string", appNameResource)); + } + } + + private void createManifestFileAndAntFiles(Project project, VirtualFile contentRoot) { + VirtualFile existingManifestFile = contentRoot.findChild(FN_ANDROID_MANIFEST_XML); + if (existingManifestFile != null) { + return; + } + try { + AndroidFileTemplateProvider + .createFromTemplate(project, contentRoot, AndroidFileTemplateProvider.ANDROID_MANIFEST_TEMPLATE, FN_ANDROID_MANIFEST_XML); + + AndroidPlatform platform = AndroidPlatform.parse(mySdk); + + if (platform == null) { + Messages.showErrorDialog(project, "Cannot parse Android SDK: 'default.properties' won't be generated", CommonBundle.getErrorTitle()); + return; + } + + Properties properties = FileTemplateManager.getInstance().getDefaultProperties(project); + properties.setProperty("TARGET", platform.getTarget().hashString()); + AndroidFileTemplateProvider.createFromTemplate(project, contentRoot, AndroidFileTemplateProvider.DEFAULT_PROPERTIES_TEMPLATE, + SdkConstants.FN_PROJECT_PROPERTIES, properties); + } + catch (Exception e) { + LOG.error(e); + } + } + + private void addRunConfiguration(@NotNull AndroidFacet facet, + @NotNull TargetSelectionMode targetSelectionMode, + @Nullable String targetAvd) { + String activityClass; + if (isHelloAndroid()) { + activityClass = myPackageName + '.' + myActivityName; + } + else { + activityClass = null; + } + Module module = facet.getModule(); + AndroidUtils.addRunConfiguration(facet, activityClass, false, targetSelectionMode, targetAvd); + } + + private static void addTestRunConfiguration(final AndroidFacet facet, @NotNull TargetSelectionMode mode, @Nullable String preferredAvd) { + Project project = facet.getModule().getProject(); + RunManagerEx runManager = RunManagerEx.getInstanceEx(project); + Module module = facet.getModule(); + RunnerAndConfigurationSettings settings = runManager + .createRunConfiguration(module.getName(), AndroidTestRunConfigurationType.getInstance().getFactory()); + + AndroidTestRunConfiguration configuration = (AndroidTestRunConfiguration)settings.getConfiguration(); + configuration.setModule(module); + configuration.setTargetSelectionMode(mode); + if (preferredAvd != null) { + configuration.PREFERRED_AVD = preferredAvd; + } + + runManager.addConfiguration(settings, false); + runManager.setActiveConfiguration(settings); + } + + private boolean isHelloAndroid() { + return myActivityName.length() > 0; + } + + @Nullable + private static VirtualFile findSourceRoot(ModifiableRootModel model) { + VirtualFile genSourceRoot = AndroidRootUtil.getStandartGenDir(model.getModule()); + for (VirtualFile root : model.getSourceRoots()) { + if (!Comparing.equal(root, genSourceRoot)) { + return root; + } + } + return null; + } + + @Nullable + private static PsiDirectory createPackageIfPossible(final PsiDirectory sourceDir, String packageName) { + if (sourceDir != null) { + final String[] ids = packageName.split("\\."); + return ApplicationManager.getApplication().runWriteAction(new Computable() { + public PsiDirectory compute() { + PsiDirectory dir = sourceDir; + for (String id : ids) { + PsiDirectory child = dir.findSubdirectory(id); + dir = child == null ? dir.createSubdirectory(id) : child; + } + return dir; + } + }); + } + return null; + } + + private void createActivityAndSetupManifest(final AndroidFacet facet, final PsiDirectory sourceDir) { + if (myPackageName != null) { + CommandProcessor.getInstance().executeCommand(facet.getModule().getProject(), new ExternalChangeAction() { + public void run() { + Runnable action = new Runnable() { + public void run() { + PsiDirectory packageDir = createPackageIfPossible(sourceDir, myPackageName); + if (packageDir == null) return; + final Manifest manifest = facet.getManifest(); + if (manifest != null) { + manifest.getPackage().setValue(myPackageName); + final Project project = facet.getModule().getProject(); + StartupManager.getInstance(project).runWhenProjectIsInitialized(new Runnable() { + public void run() { + FileDocumentManager.getInstance().saveAllDocuments(); + } + }); + + assignApplicationName(facet); + + final AndroidPlatform platform = AndroidPlatform.parse(mySdk); + if (platform != null) { + configureManifest(facet, platform.getTarget()); + } + } + } + }; + ApplicationManager.getApplication().runWriteAction(action); + } + }, AndroidBundle.message("build.android.module.process.title"), null); + } + } + + private void createResourcesAndLibs(final Project project, final VirtualFile rootDir) { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + public void run() { + try { + createChildDirectoryIfNotExist(project, rootDir, SdkConstants.FD_ASSETS); + createChildDirectoryIfNotExist(project, rootDir, SdkConstants.FD_NATIVE_LIBS); + VirtualFile resDir = createChildDirectoryIfNotExist(project, rootDir, SdkConstants.FD_RES); + VirtualFile drawableDir = createChildDirectoryIfNotExist(project, resDir, AndroidConstants.FD_RES_DRAWABLE); + createFileFromResource(project, drawableDir, "icon.png", "/icons/androidLarge.png"); + if (isHelloAndroid()) { + VirtualFile valuesDir = createChildDirectoryIfNotExist(project, resDir, AndroidConstants.FD_RES_VALUES); + createFileFromResource(project, valuesDir, "strings.xml", "res/values/strings.xml"); + VirtualFile layoutDir = AndroidUtils.createChildDirectoryIfNotExist(project, resDir, AndroidConstants.FD_RES_LAYOUT); + createFileFromResource(project, layoutDir, "main.xml", "res/layout/main.xml"); + } + } + catch (IOException e) { + LOG.error(e); + } + } + }); + } + + private static void createFileFromResource(Project project, VirtualFile drawableDir, String name, String resourceFilePath) + throws IOException { + if (drawableDir.findChild(name) != null) { + return; + } + VirtualFile resFile = drawableDir.createChildData(project, name); + InputStream stream = AndroidModuleBuilder.class.getResourceAsStream(resourceFilePath); + try { + byte[] bytes = FileUtil.adaptiveLoadBytes(stream); + resFile.setBinaryContent(bytes); + } + finally { + stream.close(); + } + } + + public void setProjectType(ProjectType projectType) { + myProjectType = projectType; + } + + public void setActivityName(String activityName) { + myActivityName = activityName; + } + + public void setApplicationName(String applicationName) { + myApplicationName = applicationName; + } + + public void setPackageName(String packageName) { + myPackageName = packageName; + } + + public void setSdk(Sdk sdk) { + mySdk = sdk; + } + + public ModuleType getModuleType() { + return StdModuleTypes.JAVA; + } + + public void setTestedModule(Module module) { + myTestedModule = module; + } + + public void setTargetSelectionMode(TargetSelectionMode targetSelectionMode) { + myTargetSelectionMode = targetSelectionMode; + } + + public void setPreferredAvd(String preferredAvd) { + myPreferredAvd = preferredAvd; + } + + @Override + public ModuleWizardStep[] createWizardSteps(WizardContext wizardContext, ModulesProvider modulesProvider) { + List steps = new ArrayList(); + ProjectWizardStepFactory factory = ProjectWizardStepFactory.getInstance(); + steps.add(factory.createSourcePathsStep(wizardContext, this, null, "reference.dialogs.new.project.fromScratch.source")); + + if (!hasAppropriateJdk()) { + steps.add(new ProjectJdkForModuleStep(wizardContext, JavaSdk.getInstance()) { + @Override + public void updateDataModel() { + // do nothing + } + + @Override + public boolean validate() { + for (Object o : getAllJdks()) { + if (o instanceof Sdk) { + Sdk sdk = (Sdk)o; + if (AndroidSdkUtils.isApplicableJdk(sdk)) { + return true; + } + } + } + Messages.showErrorDialog(AndroidBundle.message("no.jdk.error"), CommonBundle.getErrorTitle()); + return false; + } + }); + } + + steps.add(new AndroidModuleWizardStep(this, wizardContext)); + return steps.toArray(new ModuleWizardStep[steps.size()]); + } + + public Icon getBigIcon() { + return AndroidUtils.ANDROID_ICON_24; + } + + public String getDescription() { + return AndroidBundle.message("android.module.type.description"); + } + + public String getPresentableName() { + return AndroidBundle.message("android.module.type.name"); + } + + @Override + public String getBuilderId() { + return getClass().getName(); + } + + private static boolean hasAppropriateJdk() { + for (Sdk sdk : ProjectJdkTable.getInstance().getAllJdks()) { + if (AndroidSdkUtils.isApplicableJdk(sdk)) { + return true; + } + } + return false; + } +} diff --git a/plugins/android/src/org/jetbrains/android/uipreview/RenderUtil.java b/plugins/android/src/org/jetbrains/android/uipreview/RenderUtil.java index cfd48c23c2db..f34d62e9614c 100644 --- a/plugins/android/src/org/jetbrains/android/uipreview/RenderUtil.java +++ b/plugins/android/src/org/jetbrains/android/uipreview/RenderUtil.java @@ -29,6 +29,7 @@ import com.intellij.openapi.roots.ui.configuration.ClasspathEditor; import com.intellij.openapi.roots.ui.configuration.ModulesConfigurator; import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable; import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.FileUtil; @@ -498,7 +499,7 @@ public class RenderUtil { final String filePath = FileUtil.toSystemIndependentName(fileWrapper.getOsLocation()); vFile = LocalFileSystem.getInstance().findFileByPath(filePath); - if (vFile != null && vFile == layoutXmlFile && layoutXmlFileText != null) { + if (vFile != null && Comparing.equal(vFile, layoutXmlFile) && layoutXmlFileText != null) { resFolder.processFile(new MyFileWrapper(layoutXmlFileText, childRes), ResourceDeltaKind.ADDED, scanningContext); } else { diff --git a/plugins/android/src/org/jetbrains/android/util/AndroidUtils.java b/plugins/android/src/org/jetbrains/android/util/AndroidUtils.java index 9bfafc939fd3..82207c492b93 100644 --- a/plugins/android/src/org/jetbrains/android/util/AndroidUtils.java +++ b/plugins/android/src/org/jetbrains/android/util/AndroidUtils.java @@ -52,6 +52,7 @@ import com.intellij.openapi.roots.OrderEntry; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.IconLoader; import com.intellij.openapi.util.Key; @@ -183,7 +184,7 @@ public class AndroidUtils { final List packages = new ArrayList(); file = file.getParent(); - while (file != null && projectDir != file && !sourceRoots.contains(file)) { + while (file != null && !Comparing.equal(projectDir, file) && !sourceRoots.contains(file)) { packages.add(file.getName()); file = file.getParent(); } diff --git a/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/application/CvsInfo.java b/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/application/CvsInfo.java index ace6c475de99..d385be061481 100644 --- a/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/application/CvsInfo.java +++ b/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/application/CvsInfo.java @@ -27,6 +27,7 @@ import com.intellij.cvsSupport2.errorHandling.ErrorRegistry; import com.intellij.cvsSupport2.javacvsImpl.io.ReadWriteStatistics; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.MessageType; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.ThreeState; @@ -142,7 +143,7 @@ public class CvsInfo { } private void loadEntries() { - if (myParent != DUMMY_ROOT) { + if (!Comparing.equal(myParent, DUMMY_ROOT)) { myEntries = createEntriesFor(getParentFile()); } else { diff --git a/plugins/eclipse/src/org/jetbrains/idea/eclipse/conversion/EclipseClasspathWriter.java b/plugins/eclipse/src/org/jetbrains/idea/eclipse/conversion/EclipseClasspathWriter.java index 7e7559287839..46887aee7e49 100644 --- a/plugins/eclipse/src/org/jetbrains/idea/eclipse/conversion/EclipseClasspathWriter.java +++ b/plugins/eclipse/src/org/jetbrains/idea/eclipse/conversion/EclipseClasspathWriter.java @@ -97,7 +97,7 @@ public class EclipseClasspathWriter { for (SourceFolder sourceFolder : contentEntry.getSourceFolders()) { final String srcUrl = sourceFolder.getUrl(); String relativePath = EPathUtil.collapse2EclipsePath(srcUrl, myModel); - if (contentRoot != EPathUtil.getContentRoot(myModel)) { + if (!Comparing.equal(contentRoot, EPathUtil.getContentRoot(myModel))) { final String linkedPath = EclipseModuleManager.getInstance(entry.getOwnerModule()).getEclipseLinkedSrcVariablePath(srcUrl); if (linkedPath != null) { relativePath = linkedPath; diff --git a/plugins/git4idea/src/git4idea/annotate/GitFileAnnotation.java b/plugins/git4idea/src/git4idea/annotate/GitFileAnnotation.java index a18a99b85e89..ca4a9584975e 100644 --- a/plugins/git4idea/src/git4idea/annotate/GitFileAnnotation.java +++ b/plugins/git4idea/src/git4idea/annotate/GitFileAnnotation.java @@ -17,6 +17,7 @@ package git4idea.annotate; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.vcs.FileStatus; import com.intellij.openapi.vcs.FileStatusListener; import com.intellij.openapi.vcs.FileStatusManager; @@ -124,7 +125,7 @@ public class GitFileAnnotation implements FileAnnotation { myFileListener = new VirtualFileAdapter() { @Override public void contentsChanged(final VirtualFileEvent event) { - if (myFile != event.getFile()) return; + if (!Comparing.equal(myFile, event.getFile())) return; if (!event.isFromRefresh()) return; final VcsRevisionNumber currentRevision = myVcs.getDiffProvider().getCurrentRevision(myFile); if (currentRevision != null && currentRevision.equals(revision)) return; diff --git a/plugins/git4idea/src/git4idea/status/GitOldChangesCollector.java b/plugins/git4idea/src/git4idea/status/GitOldChangesCollector.java index 518efac8ca8d..8fbf943bb111 100644 --- a/plugins/git4idea/src/git4idea/status/GitOldChangesCollector.java +++ b/plugins/git4idea/src/git4idea/status/GitOldChangesCollector.java @@ -16,6 +16,7 @@ package git4idea.status; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.vcs.*; import com.intellij.openapi.vcs.changes.Change; import com.intellij.openapi.vcs.changes.ChangeListManager; @@ -226,7 +227,7 @@ class GitOldChangesCollector extends GitChangesCollector { sc.skipChars(2); if ('?' == status) { VirtualFile file = myVcsRoot.findFileByRelativePath(GitUtil.unescapePath(sc.line())); - if (GitUtil.gitRootOrNull(file) == myVcsRoot) { + if (Comparing.equal(GitUtil.gitRootOrNull(file), myVcsRoot)) { myUnversioned.add(file); } } @@ -235,7 +236,7 @@ class GitOldChangesCollector extends GitChangesCollector { sc.boundedToken('\t'); String file = GitUtil.unescapePath(sc.line()); VirtualFile vFile = myVcsRoot.findFileByRelativePath(file); - if (GitUtil.gitRootOrNull(vFile) != myVcsRoot) { + if (!Comparing.equal(GitUtil.gitRootOrNull(vFile), myVcsRoot)) { continue; } if (!myUnmergedNames.add(file)) { diff --git a/plugins/git4idea/src/git4idea/ui/GitRefspecPanel.java b/plugins/git4idea/src/git4idea/ui/GitRefspecPanel.java index b64fd47a42a1..52b128b0b3c0 100644 --- a/plugins/git4idea/src/git4idea/ui/GitRefspecPanel.java +++ b/plugins/git4idea/src/git4idea/ui/GitRefspecPanel.java @@ -17,6 +17,7 @@ package git4idea.ui; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.DocumentAdapter; @@ -308,7 +309,7 @@ public class GitRefspecPanel extends JPanel { * @param gitRoot a git root */ public void setGitRoot(final VirtualFile gitRoot) { - if (gitRoot == myGitRoot) { + if (Comparing.equal(gitRoot, myGitRoot)) { return; } myGitRoot = gitRoot; diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcModuleStructureUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcModuleStructureUtil.java index 61cbb2593255..fa8c9d30ac39 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcModuleStructureUtil.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcModuleStructureUtil.java @@ -70,7 +70,7 @@ public class MvcModuleStructureUtil { @Nullable public static ContentEntry findContentEntry(ModuleRootModel rootModel, VirtualFile root) { for (ContentEntry entry : rootModel.getContentEntries()) { - if (entry.getFile() == root) { + if (Comparing.equal(entry.getFile(), root)) { return entry; } } @@ -147,7 +147,7 @@ public class MvcModuleStructureUtil { public void consume(ContentEntry contentEntry) { SourceFolder[] folders = contentEntry.getSourceFolders(); for (SourceFolder folder : folders) { - if (folder.getFile() == file) { + if (Comparing.equal(folder.getFile(), file)) { contentEntry.removeSourceFolder(folder); } } @@ -218,7 +218,7 @@ public class MvcModuleStructureUtil { @Override public void consume(ContentEntry entry) { for (SourceFolder folder : entry.getSourceFolders()) { - if (folder.getFile() == src) { + if (Comparing.equal(folder.getFile(), src)) { entry.removeSourceFolder(folder); entry.addSourceFolder(src, isTest, ""); break; @@ -795,7 +795,7 @@ public class MvcModuleStructureUtil { } else { for (int i = 1; i < contentRoots.length; i++) { - if (parent != contentRoots[i].getParent()) { + if (!Comparing.equal(parent, contentRoots[i].getParent())) { parent = null; break; } diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/MavenDomUtil.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/MavenDomUtil.java index b1906ff44f2b..169080277b91 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/MavenDomUtil.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/MavenDomUtil.java @@ -22,6 +22,7 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; @@ -155,7 +156,7 @@ public class MavenDomUtil { result.getArtifactId().setStringValue(parentId.getArtifactId()); result.getVersion().setStringValue(parentId.getVersion()); - if (pomFile.getParent().getParent() != parentProject.getDirectoryFile()) { + if (!Comparing.equal(pomFile.getParent().getParent(), parentProject.getDirectoryFile())) { result.getRelativePath().setValue(PsiManager.getInstance(project).findFile(parentProject.getFile())); } diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/annotator/MavenDomAnnotator.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/annotator/MavenDomAnnotator.java index 1227c5e913bc..fedf17ea23ad 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/annotator/MavenDomAnnotator.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/annotator/MavenDomAnnotator.java @@ -20,6 +20,7 @@ import com.intellij.codeInspection.ProblemDescriptor; import com.intellij.lang.annotation.HighlightSeverity; import com.intellij.openapi.fileEditor.OpenFileDescriptor; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.xml.DomElement; @@ -59,7 +60,7 @@ public class MavenDomAnnotator implements DomElementsAnnotator { VirtualFile problemFile = LocalFileSystem.getInstance().findFileByPath(each.getPath()); LocalQuickFix[] fixes = LocalQuickFix.EMPTY_ARRAY; - if (problemFile != null && mavenProject.getFile() != problemFile) { + if (problemFile != null && !Comparing.equal(mavenProject.getFile(), problemFile)) { fixes = new LocalQuickFix[]{new OpenProblemFileFix(problemFile)}; } holder.createProblem(element, HighlightSeverity.ERROR, each.getDescription(), fixes); diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/references/MavenModulePsiReference.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/references/MavenModulePsiReference.java index 56efcf6de5df..2909cb467231 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/references/MavenModulePsiReference.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/references/MavenModulePsiReference.java @@ -20,6 +20,7 @@ import com.intellij.codeInspection.LocalQuickFix; import com.intellij.codeInspection.LocalQuickFixProvider; import com.intellij.codeInspection.ProblemDescriptor; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VfsUtil; @@ -64,7 +65,7 @@ public class MavenModulePsiReference extends MavenPsiReference implements LocalQ for (DomFileElement eachDomFile : files) { VirtualFile eachVFile = eachDomFile.getOriginalFile().getVirtualFile(); - if (eachVFile == myVirtualFile) continue; + if (Comparing.equal(eachVFile, myVirtualFile)) continue; PsiFile psiFile = eachDomFile.getFile(); String modulePath = calcRelativeModulePath(myVirtualFile, eachVFile); diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenParentProjectFileProcessor.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenParentProjectFileProcessor.java index 6c243cbaa20d..205a7de78aef 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenParentProjectFileProcessor.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenParentProjectFileProcessor.java @@ -15,6 +15,7 @@ */ package org.jetbrains.idea.maven.project; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; @@ -31,7 +32,7 @@ public abstract class MavenParentProjectFileProcessor { @NotNull VirtualFile projectFile, @Nullable MavenParentDesc parentDesc) { VirtualFile superPom = generalSettings.getEffectiveSuperPom(); - if (projectFile == superPom) return null; + if (Comparing.equal(projectFile, superPom)) return null; RESULT_TYPE result = null; diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsManagerWatcher.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsManagerWatcher.java index 2b5d4c68a5d4..694597e93d24 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsManagerWatcher.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsManagerWatcher.java @@ -346,7 +346,7 @@ public class MavenProjectsManagerWatcher { private boolean isSettingsFile(VirtualFile f) { for (VirtualFilePointer each : mySettingsFilesPointers) { - if (each.getFile() == f) return true; + if (Comparing.equal(each.getFile(), f)) return true; } return false; } diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenUtil.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenUtil.java index 0a044cd42563..925792a728d3 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenUtil.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenUtil.java @@ -1,723 +1,724 @@ -/* - * 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.maven.utils; - -import com.intellij.codeInsight.template.TemplateManager; -import com.intellij.codeInsight.template.impl.TemplateImpl; -import com.intellij.execution.configurations.ParametersList; -import com.intellij.ide.fileTemplates.FileTemplate; -import com.intellij.ide.fileTemplates.FileTemplateManager; -import com.intellij.notification.Notification; -import com.intellij.notification.NotificationType; -import com.intellij.notification.Notifications; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.ModalityState; -import com.intellij.openapi.application.PathManager; -import com.intellij.openapi.application.impl.LaterInvocator; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.fileEditor.FileEditorManager; -import com.intellij.openapi.fileEditor.OpenFileDescriptor; -import com.intellij.openapi.progress.ProcessCanceledException; -import com.intellij.openapi.progress.ProgressIndicator; -import com.intellij.openapi.progress.ProgressManager; -import com.intellij.openapi.progress.Task; -import com.intellij.openapi.project.DumbService; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.startup.StartupManager; -import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.SystemInfo; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vfs.JarFileSystem; -import com.intellij.openapi.vfs.LocalFileSystem; -import com.intellij.openapi.vfs.VfsUtil; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.Function; -import com.intellij.util.SystemProperties; -import com.intellij.util.containers.ContainerUtil; -import gnu.trove.THashSet; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.idea.maven.model.MavenConstants; -import org.jetbrains.idea.maven.model.MavenId; -import org.jetbrains.idea.maven.project.MavenProject; -import org.jetbrains.idea.maven.server.MavenServerManager; -import org.jetbrains.idea.maven.server.MavenServerUtil; - -import java.io.File; -import java.io.IOException; -import java.net.URL; -import java.util.*; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.regex.PatternSyntaxException; - -public class MavenUtil { - public static final String MAVEN_NOTIFICATION_GROUP = "Maven"; - public static final String SETTINGS_XML = "settings.xml"; - public static final String DOT_M2_DIR = ".m2"; - public static final String PROP_USER_HOME = "user.home"; - public static final String ENV_M2_HOME = "M2_HOME"; - public static final String M2_DIR = "m2"; - public static final String BIN_DIR = "bin"; - public static final String CONF_DIR = "conf"; - public static final String M2_CONF_FILE = "m2.conf"; - public static final String REPOSITORY_DIR = "repository"; - public static final String LIB_DIR = "lib"; - - @SuppressWarnings("unchecked") - private static final Pair[] SUPER_POM_PATHS = new Pair[]{ - Pair.create(Pattern.compile("maven-\\d+\\.\\d+\\.\\d+-uber\\.jar"), "org/apache/maven/project/" + MavenConstants.SUPER_POM_XML), - Pair.create(Pattern.compile("maven-model-builder-\\d+\\.\\d+\\.\\d+\\.jar"), "org/apache/maven/model/" + MavenConstants.SUPER_POM_XML) - }; - - private static volatile Map ourPropertiesFromMvnOpts; - - public static Map getPropertiesFromMavenOpts() { - Map res = ourPropertiesFromMvnOpts; - if (res == null) { - String mavenOpts = System.getenv("MAVEN_OPTS"); - if (mavenOpts != null) { - ParametersList mavenOptsList = new ParametersList(); - mavenOptsList.addParametersString(mavenOpts); - res = mavenOptsList.getProperties(); - } - else { - res = Collections.emptyMap(); - } - - ourPropertiesFromMvnOpts = res; - } - - return res; - } - - - public static void invokeLater(Project p, Runnable r) { - invokeLater(p, ModalityState.defaultModalityState(), r); - } - - public static void invokeLater(final Project p, final ModalityState state, final Runnable r) { - if (isNoBackgroundMode()) { - r.run(); - } - else { - ApplicationManager.getApplication().invokeLater(new Runnable() { - public void run() { - if (p.isDisposed()) return; - r.run(); - } - }, state); - } - } - - public static void invokeAndWait(Project p, Runnable r) { - invokeAndWait(p, ModalityState.defaultModalityState(), r); - } - - public static void invokeAndWait(final Project p, final ModalityState state, final Runnable r) { - if (isNoBackgroundMode()) { - r.run(); - } - else { - if (ApplicationManager.getApplication().isDispatchThread()) { - r.run(); - } - else { - ApplicationManager.getApplication().invokeAndWait(new Runnable() { - public void run() { - if (p.isDisposed()) return; - r.run(); - } - }, state); - } - } - } - - public static void invokeAndWaitWriteAction(Project p, final Runnable r) { - invokeAndWait(p, new Runnable() { - public void run() { - ApplicationManager.getApplication().runWriteAction(r); - } - }); - } - - public static void runDumbAware(final Project project, final Runnable r) { - if (DumbService.isDumbAware(r)) { - r.run(); - } - else { - DumbService.getInstance(project).runWhenSmart(new Runnable() { - public void run() { - if (project.isDisposed()) return; - r.run(); - } - }); - } - } - - public static void runWhenInitialized(final Project project, final Runnable r) { - if (project.isDisposed()) return; - - if (isNoBackgroundMode()) { - r.run(); - return; - } - - if (!project.isInitialized()) { - StartupManager.getInstance(project).registerPostStartupActivity(r); - return; - } - - runDumbAware(project, r); - } - - public static boolean isNoBackgroundMode() { - return (ApplicationManager.getApplication().isUnitTestMode() - || ApplicationManager.getApplication().isHeadlessEnvironment()); - } - - public static boolean isInModalContext() { - if (isNoBackgroundMode()) return false; - return LaterInvocator.isInModalContext(); - } - - public static void showError(Project project, String title, Throwable e) { - MavenLog.LOG.warn(title, e); - Notifications.Bus.notify(new Notification(MAVEN_NOTIFICATION_GROUP, title, e.getMessage(), NotificationType.ERROR), project); - } - - public static Properties getSystemProperties() { - Properties result = (Properties)System.getProperties().clone(); - for (String each : new THashSet((Set)result.keySet())) { - if (each.startsWith("idea.")) { - result.remove(each); - } - } - return result; - } - - public static Properties getEnvProperties() { - Properties reuslt = new Properties(); - for (Map.Entry each : System.getenv().entrySet()) { - if (isMagicalProperty(each.getKey())) continue; - reuslt.put(each.getKey(), each.getValue()); - } - return reuslt; - } - - private static boolean isMagicalProperty(String key) { - return key.startsWith("="); - } - - public static File getPluginSystemDir(String folder) { - // PathManager.getSystemPath() may return relative path - return new File(PathManager.getSystemPath(), "Maven" + "/" + folder).getAbsoluteFile(); - } - - public static VirtualFile findProfilesXmlFile(VirtualFile pomFile) { - return pomFile.getParent().findChild(MavenConstants.PROFILES_XML); - } - - public static File getProfilesXmlIoFile(VirtualFile pomFile) { - return new File(pomFile.getParent().getPath(), MavenConstants.PROFILES_XML); - } - - public static List collectFirsts(List> pairs) { - List result = new ArrayList(pairs.size()); - for (Pair each : pairs) { - result.add(each.first); - } - return result; - } - - public static List collectSeconds(List> pairs) { - List result = new ArrayList(pairs.size()); - for (Pair each : pairs) { - result.add(each.second); - } - return result; - } - - public static List collectPaths(List files) { - return ContainerUtil.map(files, new Function() { - public String fun(VirtualFile file) { - return file.getPath(); - } - }); - } - - public static List collectFiles(Collection projects) { - return ContainerUtil.map(projects, new Function() { - public VirtualFile fun(MavenProject project) { - return project.getFile(); - } - }); - } - - public static boolean equalAsSets(final Collection collection1, final Collection collection2) { - return toSet(collection1).equals(toSet(collection2)); - } - - private static Collection toSet(final Collection collection) { - return (collection instanceof Set ? collection : new THashSet(collection)); - } - - public static List> mapToList(Map map) { - return ContainerUtil.map2List(map.entrySet(), new Function, Pair>() { - public Pair fun(Map.Entry tuEntry) { - return Pair.create(tuEntry.getKey(), tuEntry.getValue()); - } - }); - } - - public static String formatHtmlImage(URL url) { - return " "; - } - - public static void runOrApplyMavenProjectFileTemplate(Project project, - VirtualFile file, - MavenId projectId, - boolean interactive) throws IOException { - runOrApplyMavenProjectFileTemplate(project, file, projectId, null, null, interactive); - } - - public static void runOrApplyMavenProjectFileTemplate(Project project, - VirtualFile file, - MavenId projectId, - MavenId parentId, - VirtualFile parentFile, - boolean interactive) throws IOException { - Properties properties = new Properties(); - Properties conditions = new Properties(); - properties.setProperty("GROUP_ID", projectId.getGroupId()); - properties.setProperty("ARTIFACT_ID", projectId.getArtifactId()); - properties.setProperty("VERSION", projectId.getVersion()); - if (parentId != null) { - conditions.setProperty("HAS_PARENT", "true"); - properties.setProperty("PARENT_GROUP_ID", parentId.getGroupId()); - properties.setProperty("PARENT_ARTIFACT_ID", parentId.getArtifactId()); - properties.setProperty("PARENT_VERSION", parentId.getVersion()); - - if (parentFile != null) { - VirtualFile modulePath = file.getParent(); - VirtualFile parentModulePath = parentFile.getParent(); - - if (modulePath.getParent() != parentModulePath) { - String relativePath = VfsUtil.getPath(file, parentModulePath, '/'); - if (relativePath != null) { - if (relativePath.endsWith("/")) relativePath = relativePath.substring(0, relativePath.length() - 1); - - conditions.setProperty("HAS_RELATIVE_PATH", "true"); - properties.setProperty("PARENT_RELATIVE_PATH", relativePath); - } - } - } - } - runOrApplyFileTemplate(project, file, MavenFileTemplateGroupFactory.MAVEN_PROJECT_XML_TEMPLATE, properties, conditions, interactive); - } - - public static void runFileTemplate(Project project, - VirtualFile file, - String templateName) throws IOException { - runOrApplyFileTemplate(project, file, templateName, new Properties(), new Properties(), true); - } - - private static void runOrApplyFileTemplate(Project project, - VirtualFile file, - String templateName, - Properties properties, - Properties conditions, - boolean interactive) throws IOException { - FileTemplateManager manager = FileTemplateManager.getInstance(); - FileTemplate fileTemplate = manager.getJ2eeTemplate(templateName); - Properties allProperties = manager.getDefaultProperties(project); - if (!interactive) { - allProperties.putAll(properties); - } - allProperties.putAll(conditions); - String text = fileTemplate.getText(allProperties); - Pattern pattern = Pattern.compile("\\$\\{(.*)\\}"); - Matcher matcher = pattern.matcher(text); - StringBuffer builder = new StringBuffer(); - while (matcher.find()) { - matcher.appendReplacement(builder, "\\$" + matcher.group(1).toUpperCase() + "\\$"); - } - matcher.appendTail(builder); - text = builder.toString(); - - TemplateImpl template = (TemplateImpl)TemplateManager.getInstance(project).createTemplate("", "", text); - for (int i = 0; i < template.getSegmentsCount(); i++) { - if (i == template.getEndSegmentNumber()) continue; - String name = template.getSegmentName(i); - String value = "\"" + properties.getProperty(name, "") + "\""; - template.addVariable(name, value, value, true); - } - - if (interactive) { - OpenFileDescriptor descriptor = new OpenFileDescriptor(project, file); - Editor editor = FileEditorManager.getInstance(project).openTextEditor(descriptor, true); - editor.getDocument().setText(""); - TemplateManager.getInstance(project).startTemplate(editor, template); - } - else { - VfsUtil.saveText(file, template.getTemplateText()); - } - } - - public static > T collectPattern(String text, T result) { - String antPattern = FileUtil.convertAntToRegexp(text.trim()); - try { - result.add(Pattern.compile(antPattern)); - } - catch (PatternSyntaxException ignore) { - } - return result; - } - - public static boolean isIncluded(String relativeName, List includes, List excludes) { - boolean result = false; - for (Pattern each : includes) { - if (each.matcher(relativeName).matches()) { - result = true; - break; - } - } - if (!result) return false; - for (Pattern each : excludes) { - if (each.matcher(relativeName).matches()) return false; - } - return true; - } - - public static void run(Project project, String title, final MavenTask task) throws MavenProcessCanceledException { - final Exception[] canceledEx = new Exception[1]; - final RuntimeException[] runtimeEx = new RuntimeException[1]; - final Error[] errorEx = new Error[1]; - - ProgressManager.getInstance().run(new Task.Modal(project, title, true) { - public void run(@NotNull ProgressIndicator i) { - try { - task.run(new MavenProgressIndicator(i)); - } - catch (MavenProcessCanceledException e) { - canceledEx[0] = e; - } - catch (ProcessCanceledException e) { - canceledEx[0] = e; - } - catch (RuntimeException e) { - runtimeEx[0] = e; - } - catch (Error e) { - errorEx[0] = e; - } - } - }); - if (canceledEx[0] instanceof MavenProcessCanceledException) throw (MavenProcessCanceledException)canceledEx[0]; - if (canceledEx[0] instanceof ProcessCanceledException) throw new MavenProcessCanceledException(); - - if (runtimeEx[0] != null) throw runtimeEx[0]; - if (errorEx[0] != null) throw errorEx[0]; - } - - public static MavenTaskHandler runInBackground(final Project project, - final String title, - final boolean cancellable, - final MavenTask task) { - final MavenProgressIndicator indicator = new MavenProgressIndicator(); - - Runnable runnable = new Runnable() { - public void run() { - try { - task.run(indicator); - } - catch (MavenProcessCanceledException ignore) { - indicator.cancel(); - } - catch (ProcessCanceledException ignore) { - indicator.cancel(); - } - } - }; - - if (isNoBackgroundMode()) { - runnable.run(); - return new MavenTaskHandler() { - public void waitFor() { - } - }; - } - else { - final Future future = ApplicationManager.getApplication().executeOnPooledThread(runnable); - final MavenTaskHandler handler = new MavenTaskHandler() { - public void waitFor() { - try { - future.get(); - } - catch (InterruptedException e) { - MavenLog.LOG.error(e); - } - catch (ExecutionException e) { - MavenLog.LOG.error(e); - } - } - }; - invokeLater(project, new Runnable() { - public void run() { - if (future.isDone()) return; - new Task.Backgroundable(project, title, cancellable) { - public void run(@NotNull ProgressIndicator i) { - indicator.setIndicator(i); - handler.waitFor(); - } - }.queue(); - } - }); - return handler; - } - } - - @Nullable - public static File resolveMavenHomeDirectory(@Nullable String overrideMavenHome) { - if (!isEmptyOrSpaces(overrideMavenHome)) { - return new File(overrideMavenHome); - } - - String m2home = System.getenv(ENV_M2_HOME); - if (!isEmptyOrSpaces(m2home)) { - final File homeFromEnv = new File(m2home); - if (isValidMavenHome(homeFromEnv)) { - return homeFromEnv; - } - } - - String userHome = SystemProperties.getUserHome(); - if (!isEmptyOrSpaces(userHome)) { - final File underUserHome = new File(userHome, M2_DIR); - if (isValidMavenHome(underUserHome)) { - return underUserHome; - } - } - - if (SystemInfo.isMac) { - File home = fromBrew(); - if (home != null) { - return home; - } - - if ((home = fromMacSystemJavaTools()) != null) { - return home; - } - } - else if (SystemInfo.isLinux) { - File home = new File("/usr/share/maven2"); - if (isValidMavenHome(home)) { - return home; - } - } - - return null; - } - - @Nullable - private static File fromMacSystemJavaTools() { - final File symlinkDir = new File("/usr/share/maven"); - if (isValidMavenHome(symlinkDir)) { - return symlinkDir; - } - - // well, try to search - final File dir = new File("/usr/share/java"); - final String[] list = dir.list(); - if (list == null || list.length == 0) { - return null; - } - - String home = null; - final String prefix = "maven-"; - final int versionIndex = prefix.length(); - for (String path : list) { - if (path.startsWith(prefix) && - (home == null || StringUtil.compareVersionNumbers(path.substring(versionIndex), home.substring(versionIndex)) > 0)) { - home = path; - } - } - - if (home != null) { - File file = new File(dir, home); - if (isValidMavenHome(file)) { - return file; - } - } - - return null; - } - - @Nullable - private static File fromBrew() { - final File brewDir = new File("/usr/local/Cellar/maven"); - final String[] list = brewDir.list(); - if (list == null || list.length == 0) { - return null; - } - - if (list.length > 1) { - Arrays.sort(list, new Comparator() { - @Override - public int compare(String o1, String o2) { - return StringUtil.compareVersionNumbers(o2, o1); - } - }); - } - - final File file = new File(brewDir, list[0] + "/libexec"); - return isValidMavenHome(file) ? file : null; - } - - public static boolean isEmptyOrSpaces(@Nullable String str) { - return str == null || str.length() == 0 || str.trim().length() == 0; - } - - public static boolean isValidMavenHome(File home) { - return getMavenConfFile(home).exists(); - } - - public static File getMavenConfFile(File mavenHome) { - return new File(new File(mavenHome, BIN_DIR), M2_CONF_FILE); - } - - @Nullable - public static File resolveGlobalSettingsFile(@Nullable String overriddenMavenHome) { - File directory = resolveMavenHomeDirectory(overriddenMavenHome); - if (directory == null) return null; - - return new File(new File(directory, CONF_DIR), SETTINGS_XML); - } - - @NotNull - public static File resolveUserSettingsFile(@Nullable String overriddenUserSettingsFile) { - if (!isEmptyOrSpaces(overriddenUserSettingsFile)) return new File(overriddenUserSettingsFile); - return new File(resolveM2Dir(), SETTINGS_XML); - } - - @NotNull - public static File resolveM2Dir() { - return new File(SystemProperties.getUserHome(), DOT_M2_DIR); - } - - @NotNull - public static File resolveLocalRepository(@Nullable String overriddenLocalRepository, - @Nullable String overriddenMavenHome, - @Nullable String overriddenUserSettingsFile) { - File result = null; - if (!isEmptyOrSpaces(overriddenLocalRepository)) result = new File(overriddenLocalRepository); - if (result == null) { - result = doResolveLocalRepository(resolveUserSettingsFile(overriddenUserSettingsFile), - resolveGlobalSettingsFile(overriddenMavenHome)); - } - try { - return result.getCanonicalFile(); - } - catch (IOException e) { - return result; - } - } - - @NotNull - public static File doResolveLocalRepository(@Nullable File userSettingsFile, @Nullable File globalSettingsFile) { - if (userSettingsFile != null) { - final String fromUserSettings = getRepositoryFromSettings(userSettingsFile); - if (!StringUtil.isEmpty(fromUserSettings)) { - return new File(fromUserSettings); - } - } - - if (globalSettingsFile != null) { - final String fromGlobalSettings = getRepositoryFromSettings(globalSettingsFile); - if (!StringUtil.isEmpty(fromGlobalSettings)) { - return new File(fromGlobalSettings); - } - } - - return new File(resolveM2Dir(), REPOSITORY_DIR); - } - - @Nullable - public static String getRepositoryFromSettings(final File file) { - try { - byte[] bytes = FileUtil.loadFileBytes(file); - return expandProperties(MavenJDOMUtil.findChildValueByPath(MavenJDOMUtil.read(bytes, null), "localRepository", null)); - } - catch (IOException e) { - return null; - } - } - - public static String expandProperties(String text) { - if (StringUtil.isEmptyOrSpaces(text)) return text; - Properties props = MavenServerUtil.collectSystemProperties(); - for (Map.Entry each : props.entrySet()) { - Object val = each.getValue(); - text = text.replace("${" + each.getKey() + "}", val instanceof CharSequence ? (CharSequence)val : val.toString()); - } - return text; - } - - @NotNull - public static VirtualFile resolveSuperPomFile(@Nullable File mavenHome) { - VirtualFile result = null; - if (mavenHome != null) { - result = doResolveSuperPomFile(new File(mavenHome, LIB_DIR)); - } - if (result == null) { - result = doResolveSuperPomFile(MavenServerManager.collectClassPathAndLibsFolder().second); - } - return result; - } - - @Nullable - public static VirtualFile doResolveSuperPomFile(@NotNull File mavenHome) { - File[] files = mavenHome.listFiles(); - if (files == null) return null; - - for (File library : files) { - - for (Pair path : SUPER_POM_PATHS) { - if (path.first.matcher(library.getName()).matches()) { - VirtualFile libraryVirtualFile = LocalFileSystem.getInstance().findFileByIoFile(library); - if (libraryVirtualFile == null) continue; - - VirtualFile root = JarFileSystem.getInstance().getJarRootForLocalFile(libraryVirtualFile); - if (root == null) continue; - - VirtualFile pomFile = root.findFileByRelativePath(path.second); - if (pomFile != null) { - return pomFile; - } - } - } - } - - return null; - } - - public interface MavenTaskHandler { - void waitFor(); - } -} +/* + * 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.maven.utils; + +import com.intellij.codeInsight.template.TemplateManager; +import com.intellij.codeInsight.template.impl.TemplateImpl; +import com.intellij.execution.configurations.ParametersList; +import com.intellij.ide.fileTemplates.FileTemplate; +import com.intellij.ide.fileTemplates.FileTemplateManager; +import com.intellij.notification.Notification; +import com.intellij.notification.NotificationType; +import com.intellij.notification.Notifications; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.application.PathManager; +import com.intellij.openapi.application.impl.LaterInvocator; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.fileEditor.OpenFileDescriptor; +import com.intellij.openapi.progress.ProcessCanceledException; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.Task; +import com.intellij.openapi.project.DumbService; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.startup.StartupManager; +import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.JarFileSystem; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.Function; +import com.intellij.util.SystemProperties; +import com.intellij.util.containers.ContainerUtil; +import gnu.trove.THashSet; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.maven.model.MavenConstants; +import org.jetbrains.idea.maven.model.MavenId; +import org.jetbrains.idea.maven.project.MavenProject; +import org.jetbrains.idea.maven.server.MavenServerManager; +import org.jetbrains.idea.maven.server.MavenServerUtil; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.util.*; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +public class MavenUtil { + public static final String MAVEN_NOTIFICATION_GROUP = "Maven"; + public static final String SETTINGS_XML = "settings.xml"; + public static final String DOT_M2_DIR = ".m2"; + public static final String PROP_USER_HOME = "user.home"; + public static final String ENV_M2_HOME = "M2_HOME"; + public static final String M2_DIR = "m2"; + public static final String BIN_DIR = "bin"; + public static final String CONF_DIR = "conf"; + public static final String M2_CONF_FILE = "m2.conf"; + public static final String REPOSITORY_DIR = "repository"; + public static final String LIB_DIR = "lib"; + + @SuppressWarnings("unchecked") + private static final Pair[] SUPER_POM_PATHS = new Pair[]{ + Pair.create(Pattern.compile("maven-\\d+\\.\\d+\\.\\d+-uber\\.jar"), "org/apache/maven/project/" + MavenConstants.SUPER_POM_XML), + Pair.create(Pattern.compile("maven-model-builder-\\d+\\.\\d+\\.\\d+\\.jar"), "org/apache/maven/model/" + MavenConstants.SUPER_POM_XML) + }; + + private static volatile Map ourPropertiesFromMvnOpts; + + public static Map getPropertiesFromMavenOpts() { + Map res = ourPropertiesFromMvnOpts; + if (res == null) { + String mavenOpts = System.getenv("MAVEN_OPTS"); + if (mavenOpts != null) { + ParametersList mavenOptsList = new ParametersList(); + mavenOptsList.addParametersString(mavenOpts); + res = mavenOptsList.getProperties(); + } + else { + res = Collections.emptyMap(); + } + + ourPropertiesFromMvnOpts = res; + } + + return res; + } + + + public static void invokeLater(Project p, Runnable r) { + invokeLater(p, ModalityState.defaultModalityState(), r); + } + + public static void invokeLater(final Project p, final ModalityState state, final Runnable r) { + if (isNoBackgroundMode()) { + r.run(); + } + else { + ApplicationManager.getApplication().invokeLater(new Runnable() { + public void run() { + if (p.isDisposed()) return; + r.run(); + } + }, state); + } + } + + public static void invokeAndWait(Project p, Runnable r) { + invokeAndWait(p, ModalityState.defaultModalityState(), r); + } + + public static void invokeAndWait(final Project p, final ModalityState state, final Runnable r) { + if (isNoBackgroundMode()) { + r.run(); + } + else { + if (ApplicationManager.getApplication().isDispatchThread()) { + r.run(); + } + else { + ApplicationManager.getApplication().invokeAndWait(new Runnable() { + public void run() { + if (p.isDisposed()) return; + r.run(); + } + }, state); + } + } + } + + public static void invokeAndWaitWriteAction(Project p, final Runnable r) { + invokeAndWait(p, new Runnable() { + public void run() { + ApplicationManager.getApplication().runWriteAction(r); + } + }); + } + + public static void runDumbAware(final Project project, final Runnable r) { + if (DumbService.isDumbAware(r)) { + r.run(); + } + else { + DumbService.getInstance(project).runWhenSmart(new Runnable() { + public void run() { + if (project.isDisposed()) return; + r.run(); + } + }); + } + } + + public static void runWhenInitialized(final Project project, final Runnable r) { + if (project.isDisposed()) return; + + if (isNoBackgroundMode()) { + r.run(); + return; + } + + if (!project.isInitialized()) { + StartupManager.getInstance(project).registerPostStartupActivity(r); + return; + } + + runDumbAware(project, r); + } + + public static boolean isNoBackgroundMode() { + return (ApplicationManager.getApplication().isUnitTestMode() + || ApplicationManager.getApplication().isHeadlessEnvironment()); + } + + public static boolean isInModalContext() { + if (isNoBackgroundMode()) return false; + return LaterInvocator.isInModalContext(); + } + + public static void showError(Project project, String title, Throwable e) { + MavenLog.LOG.warn(title, e); + Notifications.Bus.notify(new Notification(MAVEN_NOTIFICATION_GROUP, title, e.getMessage(), NotificationType.ERROR), project); + } + + public static Properties getSystemProperties() { + Properties result = (Properties)System.getProperties().clone(); + for (String each : new THashSet((Set)result.keySet())) { + if (each.startsWith("idea.")) { + result.remove(each); + } + } + return result; + } + + public static Properties getEnvProperties() { + Properties reuslt = new Properties(); + for (Map.Entry each : System.getenv().entrySet()) { + if (isMagicalProperty(each.getKey())) continue; + reuslt.put(each.getKey(), each.getValue()); + } + return reuslt; + } + + private static boolean isMagicalProperty(String key) { + return key.startsWith("="); + } + + public static File getPluginSystemDir(String folder) { + // PathManager.getSystemPath() may return relative path + return new File(PathManager.getSystemPath(), "Maven" + "/" + folder).getAbsoluteFile(); + } + + public static VirtualFile findProfilesXmlFile(VirtualFile pomFile) { + return pomFile.getParent().findChild(MavenConstants.PROFILES_XML); + } + + public static File getProfilesXmlIoFile(VirtualFile pomFile) { + return new File(pomFile.getParent().getPath(), MavenConstants.PROFILES_XML); + } + + public static List collectFirsts(List> pairs) { + List result = new ArrayList(pairs.size()); + for (Pair each : pairs) { + result.add(each.first); + } + return result; + } + + public static List collectSeconds(List> pairs) { + List result = new ArrayList(pairs.size()); + for (Pair each : pairs) { + result.add(each.second); + } + return result; + } + + public static List collectPaths(List files) { + return ContainerUtil.map(files, new Function() { + public String fun(VirtualFile file) { + return file.getPath(); + } + }); + } + + public static List collectFiles(Collection projects) { + return ContainerUtil.map(projects, new Function() { + public VirtualFile fun(MavenProject project) { + return project.getFile(); + } + }); + } + + public static boolean equalAsSets(final Collection collection1, final Collection collection2) { + return toSet(collection1).equals(toSet(collection2)); + } + + private static Collection toSet(final Collection collection) { + return (collection instanceof Set ? collection : new THashSet(collection)); + } + + public static List> mapToList(Map map) { + return ContainerUtil.map2List(map.entrySet(), new Function, Pair>() { + public Pair fun(Map.Entry tuEntry) { + return Pair.create(tuEntry.getKey(), tuEntry.getValue()); + } + }); + } + + public static String formatHtmlImage(URL url) { + return " "; + } + + public static void runOrApplyMavenProjectFileTemplate(Project project, + VirtualFile file, + MavenId projectId, + boolean interactive) throws IOException { + runOrApplyMavenProjectFileTemplate(project, file, projectId, null, null, interactive); + } + + public static void runOrApplyMavenProjectFileTemplate(Project project, + VirtualFile file, + MavenId projectId, + MavenId parentId, + VirtualFile parentFile, + boolean interactive) throws IOException { + Properties properties = new Properties(); + Properties conditions = new Properties(); + properties.setProperty("GROUP_ID", projectId.getGroupId()); + properties.setProperty("ARTIFACT_ID", projectId.getArtifactId()); + properties.setProperty("VERSION", projectId.getVersion()); + if (parentId != null) { + conditions.setProperty("HAS_PARENT", "true"); + properties.setProperty("PARENT_GROUP_ID", parentId.getGroupId()); + properties.setProperty("PARENT_ARTIFACT_ID", parentId.getArtifactId()); + properties.setProperty("PARENT_VERSION", parentId.getVersion()); + + if (parentFile != null) { + VirtualFile modulePath = file.getParent(); + VirtualFile parentModulePath = parentFile.getParent(); + + if (!Comparing.equal(modulePath.getParent(), parentModulePath)) { + String relativePath = VfsUtil.getPath(file, parentModulePath, '/'); + if (relativePath != null) { + if (relativePath.endsWith("/")) relativePath = relativePath.substring(0, relativePath.length() - 1); + + conditions.setProperty("HAS_RELATIVE_PATH", "true"); + properties.setProperty("PARENT_RELATIVE_PATH", relativePath); + } + } + } + } + runOrApplyFileTemplate(project, file, MavenFileTemplateGroupFactory.MAVEN_PROJECT_XML_TEMPLATE, properties, conditions, interactive); + } + + public static void runFileTemplate(Project project, + VirtualFile file, + String templateName) throws IOException { + runOrApplyFileTemplate(project, file, templateName, new Properties(), new Properties(), true); + } + + private static void runOrApplyFileTemplate(Project project, + VirtualFile file, + String templateName, + Properties properties, + Properties conditions, + boolean interactive) throws IOException { + FileTemplateManager manager = FileTemplateManager.getInstance(); + FileTemplate fileTemplate = manager.getJ2eeTemplate(templateName); + Properties allProperties = manager.getDefaultProperties(project); + if (!interactive) { + allProperties.putAll(properties); + } + allProperties.putAll(conditions); + String text = fileTemplate.getText(allProperties); + Pattern pattern = Pattern.compile("\\$\\{(.*)\\}"); + Matcher matcher = pattern.matcher(text); + StringBuffer builder = new StringBuffer(); + while (matcher.find()) { + matcher.appendReplacement(builder, "\\$" + matcher.group(1).toUpperCase() + "\\$"); + } + matcher.appendTail(builder); + text = builder.toString(); + + TemplateImpl template = (TemplateImpl)TemplateManager.getInstance(project).createTemplate("", "", text); + for (int i = 0; i < template.getSegmentsCount(); i++) { + if (i == template.getEndSegmentNumber()) continue; + String name = template.getSegmentName(i); + String value = "\"" + properties.getProperty(name, "") + "\""; + template.addVariable(name, value, value, true); + } + + if (interactive) { + OpenFileDescriptor descriptor = new OpenFileDescriptor(project, file); + Editor editor = FileEditorManager.getInstance(project).openTextEditor(descriptor, true); + editor.getDocument().setText(""); + TemplateManager.getInstance(project).startTemplate(editor, template); + } + else { + VfsUtil.saveText(file, template.getTemplateText()); + } + } + + public static > T collectPattern(String text, T result) { + String antPattern = FileUtil.convertAntToRegexp(text.trim()); + try { + result.add(Pattern.compile(antPattern)); + } + catch (PatternSyntaxException ignore) { + } + return result; + } + + public static boolean isIncluded(String relativeName, List includes, List excludes) { + boolean result = false; + for (Pattern each : includes) { + if (each.matcher(relativeName).matches()) { + result = true; + break; + } + } + if (!result) return false; + for (Pattern each : excludes) { + if (each.matcher(relativeName).matches()) return false; + } + return true; + } + + public static void run(Project project, String title, final MavenTask task) throws MavenProcessCanceledException { + final Exception[] canceledEx = new Exception[1]; + final RuntimeException[] runtimeEx = new RuntimeException[1]; + final Error[] errorEx = new Error[1]; + + ProgressManager.getInstance().run(new Task.Modal(project, title, true) { + public void run(@NotNull ProgressIndicator i) { + try { + task.run(new MavenProgressIndicator(i)); + } + catch (MavenProcessCanceledException e) { + canceledEx[0] = e; + } + catch (ProcessCanceledException e) { + canceledEx[0] = e; + } + catch (RuntimeException e) { + runtimeEx[0] = e; + } + catch (Error e) { + errorEx[0] = e; + } + } + }); + if (canceledEx[0] instanceof MavenProcessCanceledException) throw (MavenProcessCanceledException)canceledEx[0]; + if (canceledEx[0] instanceof ProcessCanceledException) throw new MavenProcessCanceledException(); + + if (runtimeEx[0] != null) throw runtimeEx[0]; + if (errorEx[0] != null) throw errorEx[0]; + } + + public static MavenTaskHandler runInBackground(final Project project, + final String title, + final boolean cancellable, + final MavenTask task) { + final MavenProgressIndicator indicator = new MavenProgressIndicator(); + + Runnable runnable = new Runnable() { + public void run() { + try { + task.run(indicator); + } + catch (MavenProcessCanceledException ignore) { + indicator.cancel(); + } + catch (ProcessCanceledException ignore) { + indicator.cancel(); + } + } + }; + + if (isNoBackgroundMode()) { + runnable.run(); + return new MavenTaskHandler() { + public void waitFor() { + } + }; + } + else { + final Future future = ApplicationManager.getApplication().executeOnPooledThread(runnable); + final MavenTaskHandler handler = new MavenTaskHandler() { + public void waitFor() { + try { + future.get(); + } + catch (InterruptedException e) { + MavenLog.LOG.error(e); + } + catch (ExecutionException e) { + MavenLog.LOG.error(e); + } + } + }; + invokeLater(project, new Runnable() { + public void run() { + if (future.isDone()) return; + new Task.Backgroundable(project, title, cancellable) { + public void run(@NotNull ProgressIndicator i) { + indicator.setIndicator(i); + handler.waitFor(); + } + }.queue(); + } + }); + return handler; + } + } + + @Nullable + public static File resolveMavenHomeDirectory(@Nullable String overrideMavenHome) { + if (!isEmptyOrSpaces(overrideMavenHome)) { + return new File(overrideMavenHome); + } + + String m2home = System.getenv(ENV_M2_HOME); + if (!isEmptyOrSpaces(m2home)) { + final File homeFromEnv = new File(m2home); + if (isValidMavenHome(homeFromEnv)) { + return homeFromEnv; + } + } + + String userHome = SystemProperties.getUserHome(); + if (!isEmptyOrSpaces(userHome)) { + final File underUserHome = new File(userHome, M2_DIR); + if (isValidMavenHome(underUserHome)) { + return underUserHome; + } + } + + if (SystemInfo.isMac) { + File home = fromBrew(); + if (home != null) { + return home; + } + + if ((home = fromMacSystemJavaTools()) != null) { + return home; + } + } + else if (SystemInfo.isLinux) { + File home = new File("/usr/share/maven2"); + if (isValidMavenHome(home)) { + return home; + } + } + + return null; + } + + @Nullable + private static File fromMacSystemJavaTools() { + final File symlinkDir = new File("/usr/share/maven"); + if (isValidMavenHome(symlinkDir)) { + return symlinkDir; + } + + // well, try to search + final File dir = new File("/usr/share/java"); + final String[] list = dir.list(); + if (list == null || list.length == 0) { + return null; + } + + String home = null; + final String prefix = "maven-"; + final int versionIndex = prefix.length(); + for (String path : list) { + if (path.startsWith(prefix) && + (home == null || StringUtil.compareVersionNumbers(path.substring(versionIndex), home.substring(versionIndex)) > 0)) { + home = path; + } + } + + if (home != null) { + File file = new File(dir, home); + if (isValidMavenHome(file)) { + return file; + } + } + + return null; + } + + @Nullable + private static File fromBrew() { + final File brewDir = new File("/usr/local/Cellar/maven"); + final String[] list = brewDir.list(); + if (list == null || list.length == 0) { + return null; + } + + if (list.length > 1) { + Arrays.sort(list, new Comparator() { + @Override + public int compare(String o1, String o2) { + return StringUtil.compareVersionNumbers(o2, o1); + } + }); + } + + final File file = new File(brewDir, list[0] + "/libexec"); + return isValidMavenHome(file) ? file : null; + } + + public static boolean isEmptyOrSpaces(@Nullable String str) { + return str == null || str.length() == 0 || str.trim().length() == 0; + } + + public static boolean isValidMavenHome(File home) { + return getMavenConfFile(home).exists(); + } + + public static File getMavenConfFile(File mavenHome) { + return new File(new File(mavenHome, BIN_DIR), M2_CONF_FILE); + } + + @Nullable + public static File resolveGlobalSettingsFile(@Nullable String overriddenMavenHome) { + File directory = resolveMavenHomeDirectory(overriddenMavenHome); + if (directory == null) return null; + + return new File(new File(directory, CONF_DIR), SETTINGS_XML); + } + + @NotNull + public static File resolveUserSettingsFile(@Nullable String overriddenUserSettingsFile) { + if (!isEmptyOrSpaces(overriddenUserSettingsFile)) return new File(overriddenUserSettingsFile); + return new File(resolveM2Dir(), SETTINGS_XML); + } + + @NotNull + public static File resolveM2Dir() { + return new File(SystemProperties.getUserHome(), DOT_M2_DIR); + } + + @NotNull + public static File resolveLocalRepository(@Nullable String overriddenLocalRepository, + @Nullable String overriddenMavenHome, + @Nullable String overriddenUserSettingsFile) { + File result = null; + if (!isEmptyOrSpaces(overriddenLocalRepository)) result = new File(overriddenLocalRepository); + if (result == null) { + result = doResolveLocalRepository(resolveUserSettingsFile(overriddenUserSettingsFile), + resolveGlobalSettingsFile(overriddenMavenHome)); + } + try { + return result.getCanonicalFile(); + } + catch (IOException e) { + return result; + } + } + + @NotNull + public static File doResolveLocalRepository(@Nullable File userSettingsFile, @Nullable File globalSettingsFile) { + if (userSettingsFile != null) { + final String fromUserSettings = getRepositoryFromSettings(userSettingsFile); + if (!StringUtil.isEmpty(fromUserSettings)) { + return new File(fromUserSettings); + } + } + + if (globalSettingsFile != null) { + final String fromGlobalSettings = getRepositoryFromSettings(globalSettingsFile); + if (!StringUtil.isEmpty(fromGlobalSettings)) { + return new File(fromGlobalSettings); + } + } + + return new File(resolveM2Dir(), REPOSITORY_DIR); + } + + @Nullable + public static String getRepositoryFromSettings(final File file) { + try { + byte[] bytes = FileUtil.loadFileBytes(file); + return expandProperties(MavenJDOMUtil.findChildValueByPath(MavenJDOMUtil.read(bytes, null), "localRepository", null)); + } + catch (IOException e) { + return null; + } + } + + public static String expandProperties(String text) { + if (StringUtil.isEmptyOrSpaces(text)) return text; + Properties props = MavenServerUtil.collectSystemProperties(); + for (Map.Entry each : props.entrySet()) { + Object val = each.getValue(); + text = text.replace("${" + each.getKey() + "}", val instanceof CharSequence ? (CharSequence)val : val.toString()); + } + return text; + } + + @NotNull + public static VirtualFile resolveSuperPomFile(@Nullable File mavenHome) { + VirtualFile result = null; + if (mavenHome != null) { + result = doResolveSuperPomFile(new File(mavenHome, LIB_DIR)); + } + if (result == null) { + result = doResolveSuperPomFile(MavenServerManager.collectClassPathAndLibsFolder().second); + } + return result; + } + + @Nullable + public static VirtualFile doResolveSuperPomFile(@NotNull File mavenHome) { + File[] files = mavenHome.listFiles(); + if (files == null) return null; + + for (File library : files) { + + for (Pair path : SUPER_POM_PATHS) { + if (path.first.matcher(library.getName()).matches()) { + VirtualFile libraryVirtualFile = LocalFileSystem.getInstance().findFileByIoFile(library); + if (libraryVirtualFile == null) continue; + + VirtualFile root = JarFileSystem.getInstance().getJarRootForLocalFile(libraryVirtualFile); + if (root == null) continue; + + VirtualFile pomFile = root.findFileByRelativePath(path.second); + if (pomFile != null) { + return pomFile; + } + } + } + } + + return null; + } + + public interface MavenTaskHandler { + void waitFor(); + } +} diff --git a/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleEditor.java b/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleEditor.java index 7ddb4d84ba25..5da5eaa7c22b 100644 --- a/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleEditor.java +++ b/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleEditor.java @@ -248,7 +248,7 @@ public class ResourceBundleEditor extends UserDataHolderBase implements FileEdit @Override public void fileDeleted(VirtualFileEvent event) { for (PropertiesFile file : myEditors.keySet()) { - if (file.getVirtualFile() == event.getFile()) { + if (Comparing.equal(file.getVirtualFile(), event.getFile())) { recreateEditorsPanel(); return; } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileSystemListener.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileSystemListener.java index b814fad64a2c..f6b94374e688 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileSystemListener.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileSystemListener.java @@ -1,974 +1,975 @@ -/* - * 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.command.CommandAdapter; -import com.intellij.openapi.command.CommandEvent; -import com.intellij.openapi.command.undo.UndoManager; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.progress.ProgressManager; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.project.ProjectManager; -import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.vcs.*; -import com.intellij.openapi.vcs.actions.VcsContextFactory; -import com.intellij.openapi.vcs.changes.ChangeListManager; -import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager; -import com.intellij.openapi.vfs.LocalFileOperationsHandler; -import com.intellij.openapi.vfs.LocalFileSystem; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.newvfs.RefreshQueue; -import com.intellij.openapi.vfs.newvfs.RefreshSession; -import com.intellij.util.ThrowableConsumer; -import com.intellij.util.containers.MultiMap; -import com.intellij.vcsUtil.ActionWithTempFile; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -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.io.IOException; -import java.util.*; - -public class SvnFileSystemListener extends CommandAdapter implements LocalFileOperationsHandler { - private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.SvnFileSystemListener"); - private final LocalFileSystem myLfs; - - private static class AddedFileInfo { - private final VirtualFile myDir; - private final String myName; - @Nullable private final File myCopyFrom; - private final boolean myRecursive; - - public AddedFileInfo(final VirtualFile dir, final String name, @Nullable final File copyFrom, boolean recursive) { - myDir = dir; - myName = name; - myCopyFrom = copyFrom; - myRecursive = recursive; - } - } - - private static class MovedFileInfo { - private final Project myProject; - private final File mySrc; - private final File myDst; - - private MovedFileInfo(final Project project, final File src, final File dst) { - myProject = project; - mySrc = src; - myDst = dst; - } - } - - private final MultiMap myAddedFiles = new MultiMap(); - private final MultiMap myDeletedFiles = new MultiMap(); - private final List myMovedFiles = new ArrayList(); - private final Map> myMoveExceptions = new HashMap>(); - private final List myFilesToRefresh = new ArrayList(); - @Nullable private File myStorageForUndo; - private final List> myUndoStorageContents = new ArrayList>(); - private boolean myUndoingMove = false; - - public SvnFileSystemListener() { - myLfs = LocalFileSystem.getInstance(); - } - - private void addToMoveExceptions(final Project project, final SVNException e) { - List exceptionList = myMoveExceptions.get(project); - if (exceptionList == null) { - exceptionList = new ArrayList(); - myMoveExceptions.put(project, exceptionList); - } - VcsException vcsException; - if (SVNErrorCode.ENTRY_EXISTS.equals(e.getErrorMessage().getErrorCode())) { - vcsException = new VcsException(Arrays.asList("Target of move operation is already under version control.", - "Subversion move had not been performed. ", e.getMessage())); - } else { - vcsException = new VcsException(e); - } - exceptionList.add(vcsException); - } - - @Nullable - public File copy(final VirtualFile file, final VirtualFile toDir, final String copyName) throws IOException { - SvnVcs vcs = getVCS(toDir); - if (vcs == null) { - vcs = getVCS(file); - } - if (vcs == null) { - return null; - } - - File srcFile = new File(file.getPath()); - File destFile = new File(new File(toDir.getPath()), copyName); - final boolean dstDirUnderControl = SvnUtil.isSvnVersioned(vcs.getProject(), destFile.getParentFile()); - if (! dstDirUnderControl && !isPendingAdd(vcs.getProject(), toDir)) { - return null; - } - - if (! SvnUtil.isSvnVersioned(vcs.getProject(), srcFile.getParentFile())) { - myAddedFiles.putValue(vcs.getProject(), new AddedFileInfo(toDir, copyName, null, false)); - return null; - } - - final SVNStatus fileStatus = getFileStatus(vcs, srcFile); - if (fileStatus != null && SvnVcs.svnStatusIs(fileStatus, SVNStatusType.STATUS_ADDED)) { - myAddedFiles.putValue(vcs.getProject(), new AddedFileInfo(toDir, copyName, null, false)); - return null; - } - - if (sameRoot(vcs, file.getParent(), toDir)) { - myAddedFiles.putValue(vcs.getProject(), new AddedFileInfo(toDir, copyName, srcFile, false)); - return null; - } - - myAddedFiles.putValue(vcs.getProject(), new AddedFileInfo(toDir, copyName, null, false)); - return null; - } - - private boolean sameRoot(final SvnVcs vcs, final VirtualFile srcDir, final VirtualFile dstDir) { - final UUIDHelper helper = new UUIDHelper(vcs); - final String srcUUID = helper.getRepositoryUUID(vcs.getProject(), srcDir); - final String dstUUID = helper.getRepositoryUUID(vcs.getProject(), dstDir); - - return srcUUID != null && dstUUID != null && srcUUID.equals(dstUUID); - } - - private class UUIDHelper { - private final SVNWCClient myWcClient; - - private UUIDHelper(final SvnVcs vcs) { - myWcClient = vcs.createWCClient(); - } - - /** - * passed dir must be under VC control (it is assumed) - */ - @Nullable - public String getRepositoryUUID(final Project project, final VirtualFile dir) { - try { - final SVNInfo info1 = new RepeatSvnActionThroughBusy() { - @Override - protected void executeImpl() throws SVNException { - myT = myWcClient.doInfo(new File(dir.getPath()), SVNRevision.UNDEFINED); - } - }.compute(); - if (info1 == null || info1.getRepositoryUUID() == null) { - // go deeper if current parent was added (if parent was added, it theoretically could NOT know its repo UUID) - final VirtualFile parent = dir.getParent(); - if (parent == null) { - return null; - } - if (isPendingAdd(project, parent)) { - return getRepositoryUUID(project, parent); - } - } else { - return info1.getRepositoryUUID(); - } - } catch (SVNException e) { - // go to return default - } - return null; - } - } - - public boolean move(VirtualFile file, VirtualFile toDir) throws IOException { - File srcFile = getIOFile(file); - File dstFile = new File(getIOFile(toDir), file.getName()); - - final SvnVcs vcs = getVCS(toDir); - final SvnVcs sourceVcs = getVCS(file); - if (vcs == null && sourceVcs == null) return false; - - if (vcs == null) { - return false; - } - if (sourceVcs == null) { - return createItem(toDir, file.getName(), file.isDirectory(), true); - } - - if (isPendingAdd(vcs.getProject(), toDir)) { - - myMovedFiles.add(new MovedFileInfo(sourceVcs.getProject(), srcFile, dstFile)); - return true; - } - else { - final VirtualFile oldParent = file.getParent(); - myFilesToRefresh.add(oldParent); - myFilesToRefresh.add(toDir); - return doMove(sourceVcs, srcFile, dstFile); - } - } - - public boolean rename(VirtualFile file, String newName) throws IOException { - File srcFile = getIOFile(file); - File dstFile = new File(srcFile.getParentFile(), newName); - SvnVcs vcs = getVCS(file); - if (vcs != null) { - myFilesToRefresh.add(file.getParent()); - return doMove(vcs, srcFile, dstFile); - } - return false; - } - - private boolean doMove(@NotNull SvnVcs vcs, final File src, final File dst) { - long srcTime = src.lastModified(); - try { - final boolean isUndo = isUndo(vcs); - final String list = isUndo ? null : SvnChangelistListener.getCurrentMapping(vcs.getProject(), src); - - final boolean is17 = SvnUtil.is17CopyPart(src); - if (is17) { - if (for17move(vcs, src, dst, isUndo)) return false; - } else { - if (for16move(vcs, src, dst, isUndo)) return false; - } - - if (! isUndo && list != null) { - SvnChangelistListener.putUnderList(vcs.getProject(), list, dst); - } - dst.setLastModified(srcTime); - } - catch (SVNException e) { - addToMoveExceptions(vcs.getProject(), e); - return false; - } - return true; - } - - private boolean for17move(SvnVcs vcs, final File src, final File dst, boolean undo) throws SVNException { - if (undo) { - final SVNWCClient wcClient = vcs.createWCClient(); - myUndoingMove = true; - new RepeatSvnActionThroughBusy() { - @Override - protected void executeImpl() throws SVNException { - wcClient.doRevert(dst, true); - } - }.execute(); - new RepeatSvnActionThroughBusy() { - @Override - protected void executeImpl() throws SVNException { - wcClient.doRevert(src, true); - } - }.execute(); - restoreFromUndoStorage(dst); - } else { - if (doUsualMove(vcs, src)) return true; - final SVNCopyClient copyClient = vcs.createCopyClient(); - final SVNCopySource svnCopySource = new SVNCopySource(SVNRevision.UNDEFINED, SVNRevision.WORKING, src); - new RepeatSvnActionThroughBusy() { - @Override - protected void executeImpl() throws SVNException { - copyClient.doCopy(new SVNCopySource[]{svnCopySource}, dst, true, false, true); - } - }.execute(); - } - return false; - } - - private boolean doUsualMove(SvnVcs vcs, File src) { - // if src is not under version control, do usual move. - SVNStatus srcStatus = getFileStatus(vcs, src); - if (srcStatus == null || SvnVcs.svnStatusIsUnversioned(srcStatus) || - SvnVcs.svnStatusIs(srcStatus, SVNStatusType.STATUS_OBSTRUCTED) || - SvnVcs.svnStatusIs(srcStatus, SVNStatusType.STATUS_MISSING) || - SvnVcs.svnStatusIs(srcStatus, SVNStatusType.STATUS_EXTERNAL)) { - return true; - } - return false; - } - - private boolean for16move(SvnVcs vcs, final File src, final File dst, boolean undo) throws SVNException { - final SVNMoveClient mover = vcs.createMoveClient(); - if (undo) { - myUndoingMove = true; - restoreFromUndoStorage(dst); - new RepeatSvnActionThroughBusy() { - @Override - protected void executeImpl() throws SVNException { - mover.undoMove(src, dst); - } - }.execute(); - } - else { - // if src is not under version control, do usual move. - if (doUsualMove(vcs, src)) return true; - new RepeatSvnActionThroughBusy() { - @Override - protected void executeImpl() throws SVNException { - mover.doMove(src, dst); - } - }.execute(); - } - return false; - } - - private void restoreFromUndoStorage(final File dst) { - String normPath = FileUtil.toSystemIndependentName(dst.getPath()); - for (Iterator> it = myUndoStorageContents.iterator(); it.hasNext();) { - Pair e = it.next(); - final String p = FileUtil.toSystemIndependentName(e.first.getPath()); - if (p.startsWith(normPath)) { - try { - FileUtil.rename(e.second, e.first); - } - catch (IOException ex) { - LOG.error(ex); - FileUtil.asyncDelete(e.second); - } - it.remove(); - } - } - if (myStorageForUndo != null) { - final File[] files = myStorageForUndo.listFiles(); - if (files == null || files.length == 0) { - FileUtil.asyncDelete(myStorageForUndo); - myStorageForUndo = null; - } - } - } - - - public boolean createFile(VirtualFile dir, String name) throws IOException { - return createItem(dir, name, false, false); - } - - public boolean createDirectory(VirtualFile dir, String name) throws IOException { - return createItem(dir, name, true, false); - } - - /** - * delete file or directory (both 'undo' and 'do' modes) - * unversioned: do nothing, return false - * obstructed: do nothing, return false - * external or wc root: do nothing, return false - * missing: do nothing, return false - *

- * versioned: schedule for deletion, return true - * added: schedule for deletion (make unversioned), return true - * copied, but not scheduled: schedule for deletion, return true - * replaced: schedule for deletion, return true - *

- * deleted: do nothing, return true (strange) - */ - public boolean delete(VirtualFile file) throws IOException { - SvnVcs vcs = getVCS(file); - if (vcs != null && SvnUtil.isAdminDirectory(file)) { - return true; - } - if (vcs == null) return false; - final File ioFile = getIOFile(file); - if (! SvnUtil.isSvnVersioned(vcs.getProject(), ioFile.getParentFile())) { - return false; - } - try { - if (SVNWCUtil.isWorkingCopyRoot(ioFile)) { - return false; - } - } catch (SVNException e) { - // - } - - SVNStatus status = getFileStatus(ioFile); - - if (status == null || - SvnVcs.svnStatusIsUnversioned(status) || - SvnVcs.svnStatusIs(status, SVNStatusType.STATUS_OBSTRUCTED) || - SvnVcs.svnStatusIs(status, SVNStatusType.STATUS_MISSING) || - SvnVcs.svnStatusIs(status, SVNStatusType.STATUS_EXTERNAL) || - SvnVcs.svnStatusIs(status, SVNStatusType.STATUS_IGNORED)) { - return false; - } else if (SvnVcs.svnStatusIs(status, SVNStatusType.STATUS_DELETED)) { - if (isUndo(vcs)) { - moveToUndoStorage(file); - } - return true; - } - else { - if (vcs != null) { - if (isAboveSourceOfCopyOrMove(vcs.getProject(), ioFile)) { - myDeletedFiles.putValue(vcs.getProject(), ioFile); - return true; - } - if (SvnVcs.svnStatusIs(status, SVNStatusType.STATUS_ADDED)) { - try { - final SVNWCClient wcClient = vcs.createWCClient(); - new RepeatSvnActionThroughBusy() { - @Override - protected void executeImpl() throws SVNException { - wcClient.doRevert(ioFile, false); - } - }.execute(); - } - catch (SVNException e) { - // ignore - } - } - else { - myDeletedFiles.putValue(vcs.getProject(), ioFile); - // packages deleted from disk should not be deleted from svn (IDEADEV-16066) - if (file.isDirectory() || isUndo(vcs)) return true; - } - } - return false; - } - } - - private boolean isAboveSourceOfCopyOrMove(final Project p, File ioFile) { - for (MovedFileInfo file : myMovedFiles) { - if (FileUtil.isAncestor(ioFile, file.mySrc, false)) return true; - } - for (AddedFileInfo info : myAddedFiles.get(p)) { - if (info.myCopyFrom != null && FileUtil.isAncestor(ioFile, info.myCopyFrom, false)) return true; - } - return false; - } - - private void moveToUndoStorage(final VirtualFile file) { - if (myStorageForUndo == null) { - try { - myStorageForUndo = FileUtil.createTempDirectory("svnUndoStorage", ""); - } - catch (IOException e) { - LOG.error(e); - return; - } - } - final File tmpFile = FileUtil.findSequentNonexistentFile(myStorageForUndo, "tmp", ""); - myUndoStorageContents.add(0, new Pair(new File(file.getPath()), tmpFile)); - new File(file.getPath()).renameTo(tmpFile); - } - - /** - * add file or directory: - *

- * parent directory is: - * unversioned: do nothing, return false - * versioned: - * entry is: - * null: create entry, schedule for addition - * missing: do nothing, return false - * deleted, 'do' mode: try to create entry and it schedule for addition if kind is the same, otherwise do nothing, return false. - * deleted: 'undo' mode: try to revert non-recursively, if kind is the same, otherwise do nothing, return false. - * anything else: return false. - */ - private boolean createItem(VirtualFile dir, String name, boolean directory, final boolean recursive) { - SvnVcs vcs = getVCS(dir); - if (vcs == null) { - return false; - } - if (isUndo(vcs) && SvnUtil.isAdminDirectory(dir, name)) { - return false; - } - File ioDir = getIOFile(dir); - boolean pendingAdd = isPendingAdd(vcs.getProject(), dir); - if (! SvnUtil.isSvnVersioned(vcs.getProject(), ioDir) && ! pendingAdd) { - return false; - } - final SVNWCClient wcClient = vcs.createWCClient(); - final File targetFile = new File(ioDir, name); - SVNStatus status = getFileStatus(vcs, targetFile); - - if (status == null || status.getContentsStatus() == SVNStatusType.STATUS_NONE || - status.getContentsStatus() == SVNStatusType.STATUS_UNVERSIONED) { - myAddedFiles.putValue(vcs.getProject(), new AddedFileInfo(dir, name, null, recursive)); - return false; - } - else if (SvnVcs.svnStatusIs(status, SVNStatusType.STATUS_MISSING)) { - return false; - } - else if (SvnVcs.svnStatusIs(status, SVNStatusType.STATUS_DELETED)) { - SVNNodeKind kind = status.getKind(); - // kind differs. - if (directory && kind != SVNNodeKind.DIR || !directory && kind != SVNNodeKind.FILE) { - return false; - } - try { - if (isUndo(vcs)) { - new RepeatSvnActionThroughBusy() { - @Override - protected void executeImpl() throws SVNException { - wcClient.doRevert(targetFile, false); - } - }.execute(); - return true; - } - myAddedFiles.putValue(vcs.getProject(), new AddedFileInfo(dir, name, null, recursive)); - return false; - } - catch (SVNException e) { - SVNFileUtil.deleteAll(targetFile, true); - return false; - } - } - return false; - } - - private boolean isPendingAdd(final Project project, final VirtualFile dir) { - final Collection addedFileInfos = myAddedFiles.get(project); - for(AddedFileInfo i: addedFileInfos) { - if (i.myDir == dir.getParent() && i.myName.equals(dir.getName())) { - return true; - } - } - return false; - } - - public void commandStarted(CommandEvent event) { - myUndoingMove = false; - final Project project = event.getProject(); - if (project == null) return; - commandStarted(project); - } - - void commandStarted(final Project project) { - myUndoingMove = false; - myMoveExceptions.remove(project); - } - - public void commandFinished(CommandEvent event) { - final Project project = event.getProject(); - if (project == null) return; - commandFinished(project); - } - - void commandFinished(final Project project) { - checkOverwrites(project); - if (myAddedFiles.containsKey(project)) { - processAddedFiles(project); - } - processMovedFiles(project); - if (myDeletedFiles.containsKey(project)) { - processDeletedFiles(project); - } - - final List exceptionList = myMoveExceptions.get(project); - if (exceptionList != null && ! exceptionList.isEmpty()) { - AbstractVcsHelper.getInstance(project).showErrors(exceptionList, SvnBundle.message("move.files.errors.title")); - } - - if (!myFilesToRefresh.isEmpty()) { - refreshFiles(project); - } - } - - private void checkOverwrites(final Project project) { - final Collection addedFileInfos = myAddedFiles.get(project); - final Collection deletedFiles = myDeletedFiles.get(project); - if (addedFileInfos.isEmpty() || deletedFiles.isEmpty()) return; - final Iterator iterator = addedFileInfos.iterator(); - while (iterator.hasNext()) { - AddedFileInfo addedFileInfo = iterator.next(); - final File ioFile = new File(addedFileInfo.myDir.getPath(), addedFileInfo.myName); - if (deletedFiles.remove(ioFile)) { - iterator.remove(); - } - } - } - - private void refreshFiles(final Project project) { - final List toRefreshFiles = new ArrayList(); - final List toRefreshDirs = new ArrayList(); - for (VirtualFile file : myFilesToRefresh) { - if (file == null) continue; - if (file.isDirectory()) { - toRefreshDirs.add(file); - } else { - toRefreshFiles.add(file); - } - } - // if refresh asynchronously, local changes would also be notified that they are dirty asynchronously, - // and commit could be executed while not all changes are visible - final RefreshSession session = RefreshQueue.getInstance().createSession(true, true, new Runnable() { - public void run() { - if (project.isDisposed()) return; - filterOutInvalid(toRefreshFiles); - filterOutInvalid(toRefreshDirs); - - final VcsDirtyScopeManager vcsDirtyScopeManager = VcsDirtyScopeManager.getInstance(project); - vcsDirtyScopeManager.filesDirty(toRefreshFiles, toRefreshDirs); - } - }); - filterOutInvalid(myFilesToRefresh); - session.addAllFiles(myFilesToRefresh); - session.launch(); - myFilesToRefresh.clear(); - } - - private static void filterOutInvalid(final Collection files) { - for (Iterator iterator = files.iterator(); iterator.hasNext();) { - final VirtualFile file = iterator.next(); - if (! file.isValid() || ! file.exists()) { - LOG.info("Refresh root is not valid: " + file.getPath()); - iterator.remove(); - } - } - } - - private void processAddedFiles(Project project) { - SvnVcs vcs = SvnVcs.getInstance(project); - List addedVFiles = new ArrayList(); - Map copyFromMap = new HashMap(); - final Set recursiveItems = new HashSet(); - fillAddedFiles(project, vcs, addedVFiles, copyFromMap, recursiveItems); - if (addedVFiles.isEmpty()) return; - final VcsShowConfirmationOption.Value value = vcs.getAddConfirmation().getValue(); - if (value != VcsShowConfirmationOption.Value.DO_NOTHING_SILENTLY) { - final AbstractVcsHelper vcsHelper = AbstractVcsHelper.getInstance(project); - final Collection filesToProcess = promptAboutAddition(vcs, addedVFiles, value, vcsHelper); - if (filesToProcess != null && !filesToProcess.isEmpty()) { - final List exceptions = new ArrayList(); - runInBackground(project, "Adding files to Subversion", - createAdditionRunnable(project, vcs, copyFromMap, filesToProcess, exceptions)); - if (!exceptions.isEmpty()) { - vcsHelper.showErrors(exceptions, SvnBundle.message("add.files.errors.title")); - } - } - } - } - - private void runInBackground(final Project project, final String name, final Runnable runnable) { - if (ApplicationManager.getApplication().isDispatchThread()) { - ProgressManager.getInstance().runProcessWithProgressSynchronously(runnable, name, false, project); - } else { - runnable.run(); - } - } - - private Runnable createAdditionRunnable(final Project project, - final SvnVcs vcs, - final Map copyFromMap, - final Collection filesToProcess, - final List exceptions) { - return new Runnable() { - @Override - public void run() { - final SVNWCClient wcClient = vcs.createWCClient(); - final SVNCopyClient copyClient = vcs.createCopyClient(); - for(VirtualFile file: filesToProcess) { - final File ioFile = new File(file.getPath()); - try { - final File copyFrom = copyFromMap.get(file); - if (copyFrom != null) { - try { - new ActionWithTempFile(ioFile) { - protected void executeInternal() throws VcsException { - try { - // not recursive - final SVNCopySource[] copySource = {new SVNCopySource(SVNRevision.WORKING, SVNRevision.WORKING, copyFrom)}; - new RepeatSvnActionThroughBusy() { - @Override - protected void executeImpl() throws SVNException { - copyClient.doCopy(copySource, ioFile, false, true, true); - } - }.execute(); - } - catch (SVNException e) { - throw new VcsException(e); - } - } - }.execute(); - } - catch (VcsException e) { - exceptions.add(e); - } - } - else { - new RepeatSvnActionThroughBusy() { - @Override - protected void executeImpl() throws SVNException { - wcClient.doAdd(ioFile, true, false, false, true); - } - }.execute(); - } - VcsDirtyScopeManager.getInstance(project).fileDirty(file); - } - catch (SVNException e) { - exceptions.add(new VcsException(e)); - } - } - } - }; - } - - private Collection promptAboutAddition(SvnVcs vcs, - List addedVFiles, - VcsShowConfirmationOption.Value value, - AbstractVcsHelper vcsHelper) { - Collection filesToProcess; - if (value == VcsShowConfirmationOption.Value.DO_ACTION_SILENTLY) { - filesToProcess = addedVFiles; - } - else { - final String singleFilePrompt; - if (addedVFiles.size() == 1 && addedVFiles.get(0).isDirectory()) { - singleFilePrompt = SvnBundle.getString("confirmation.text.add.dir"); - } - else { - singleFilePrompt = SvnBundle.getString("confirmation.text.add.file"); - } - filesToProcess = vcsHelper.selectFilesToProcess(addedVFiles, SvnBundle.message("confirmation.title.add.multiple.files"), - null, - SvnBundle.message("confirmation.title.add.file"), singleFilePrompt, - vcs.getAddConfirmation()); - } - return filesToProcess; - } - - private void fillAddedFiles(Project project, - SvnVcs vcs, - List addedVFiles, - Map copyFromMap, - Set recursiveItems) { - final Collection addedFileInfos = myAddedFiles.remove(project); - final ChangeListManager changeListManager = ChangeListManager.getInstance(project); - - for (AddedFileInfo addedFileInfo : addedFileInfos) { - final File ioFile = new File(getIOFile(addedFileInfo.myDir), addedFileInfo.myName); - VirtualFile addedFile = addedFileInfo.myDir.findChild(addedFileInfo.myName); - if (addedFile == null) { - addedFile = myLfs.refreshAndFindFileByIoFile(ioFile); - } - if (addedFile != null) { - final SVNStatus fileStatus = getFileStatus(vcs, ioFile); - if (fileStatus == null || ! SvnVcs.svnStatusIs(fileStatus, SVNStatusType.STATUS_IGNORED)) { - boolean isIgnored = changeListManager.isIgnoredFile(addedFile); - if (!isIgnored) { - addedVFiles.add(addedFile); - copyFromMap.put(addedFile, addedFileInfo.myCopyFrom); - if (addedFileInfo.myRecursive) { - recursiveItems.add(addedFile); - } - } - } - } - } - } - - private void processDeletedFiles(Project project) { - final List deletedFiles = new ArrayList(); - final Collection filesToProcess = new ArrayList(); - fillDeletedFiles(project, deletedFiles, filesToProcess); - if (deletedFiles.isEmpty() && filesToProcess.isEmpty() || myUndoingMove) return; - SvnVcs vcs = SvnVcs.getInstance(project); - final VcsShowConfirmationOption.Value value = vcs.getDeleteConfirmation().getValue(); - if (value != VcsShowConfirmationOption.Value.DO_NOTHING_SILENTLY) { - final AbstractVcsHelper vcsHelper = AbstractVcsHelper.getInstance(project); - if (! deletedFiles.isEmpty()) { - final Collection confirmed = promptAboutDeletion(deletedFiles, vcs, value, vcsHelper); - if (confirmed != null) { - filesToProcess.addAll(confirmed); - } - } - if (filesToProcess != null && !filesToProcess.isEmpty()) { - List exceptions = new ArrayList(); - runInBackground(project, "Deleting files from Subversion", createDeleteRunnable(project, vcs, filesToProcess, exceptions)); - if (!exceptions.isEmpty()) { - vcsHelper.showErrors(exceptions, SvnBundle.message("delete.files.errors.title")); - } - } - for (FilePath file : deletedFiles) { - final FilePath parent = file.getParentPath(); - if (parent != null) { - myFilesToRefresh.add(parent.getVirtualFile()); - } - } - if (filesToProcess != null) { - deletedFiles.removeAll(filesToProcess); - } - for (FilePath file : deletedFiles) { - FileUtil.delete(file.getIOFile()); - } - } - } - - private Runnable createDeleteRunnable(final Project project, - final SvnVcs vcs, - final Collection filesToProcess, - final List exceptions) { - return new Runnable() { - public void run() { - final SVNWCClient wcClient = vcs.createWCClient(); - for(FilePath file: filesToProcess) { - VirtualFile vFile = file.getVirtualFile(); // for deleted directories - final File ioFile = new File(file.getPath()); - try { - new RepeatSvnActionThroughBusy() { - @Override - protected void executeImpl() throws SVNException { - wcClient.doDelete(ioFile, true, false); - } - }.execute(); - if (vFile != null && vFile.isValid() && vFile.isDirectory()) { - vFile.refresh(true, true); - VcsDirtyScopeManager.getInstance(project).dirDirtyRecursively(vFile); - } - else { - VcsDirtyScopeManager.getInstance(project).fileDirty(file); - } - } - catch (SVNException e) { - exceptions.add(new VcsException(e)); - } - } - } - }; - } - - private Collection promptAboutDeletion(List deletedFiles, - SvnVcs vcs, - VcsShowConfirmationOption.Value value, - AbstractVcsHelper vcsHelper) { - Collection filesToProcess; - if (value == VcsShowConfirmationOption.Value.DO_ACTION_SILENTLY) { - filesToProcess = new ArrayList(deletedFiles); - } - else { - - final String singleFilePrompt; - if (deletedFiles.size() == 1 && deletedFiles.get(0).isDirectory()) { - singleFilePrompt = SvnBundle.getString("confirmation.text.delete.dir"); - } - else { - singleFilePrompt = SvnBundle.getString("confirmation.text.delete.file"); - } - final Collection files = vcsHelper - .selectFilePathsToProcess(deletedFiles, SvnBundle.message("confirmation.title.delete.multiple.files"), null, - SvnBundle.message("confirmation.title.delete.file"), singleFilePrompt, vcs.getDeleteConfirmation()); - filesToProcess = files == null ? null : new ArrayList(files); - } - return filesToProcess; - } - - private void fillDeletedFiles(Project project, List deletedFiles, Collection deleteAnyway) { - final SvnVcs vcs = SvnVcs.getInstance(project); - final SVNStatusClient sc = vcs.createStatusClient(); - final Collection files = myDeletedFiles.remove(project); - for (final File file : files) { - boolean isAdded = false; - try { - final SVNStatus status = new RepeatSvnActionThroughBusy() { - @Override - protected void executeImpl() throws SVNException { - myT = sc.doStatus(file, false); - } - }.compute(); - isAdded = SVNStatusType.STATUS_ADDED.equals(status.getNodeStatus()); - } - catch (SVNException e) { - // - } - final FilePath filePath = VcsContextFactory.SERVICE.getInstance().createFilePathOn(file); - if (isAdded) { - deleteAnyway.add(filePath); - } else { - deletedFiles.add(filePath); - } - } - } - - private void processMovedFiles(final Project project) { - if (myMovedFiles.isEmpty()) return; - - final Runnable runnable = new Runnable() { - public void run() { - for (Iterator iterator = myMovedFiles.iterator(); iterator.hasNext();) { - MovedFileInfo movedFileInfo = iterator.next(); - if (movedFileInfo.myProject == project) { - doMove(SvnVcs.getInstance(project), movedFileInfo.mySrc, movedFileInfo.myDst); - iterator.remove(); - } - } - } - }; - runInBackground(project, "Moving files in Subversion", runnable); - } - - @Nullable - private static SvnVcs getVCS(VirtualFile file) { - Project[] projects = ProjectManager.getInstance().getOpenProjects(); - for (Project project : projects) { - AbstractVcs vcs = ProjectLevelVcsManager.getInstance(project).getVcsFor(file); - if (vcs instanceof SvnVcs) { - return (SvnVcs)vcs; - } - } - return null; - } - - - private static File getIOFile(VirtualFile vf) { - return new File(vf.getPath()).getAbsoluteFile(); - } - - @Nullable - private static SVNStatus getFileStatus(File file) { - final SVNClientManager clientManager = SVNClientManager.newInstance(); - try { - SVNStatusClient stClient = clientManager.getStatusClient(); - return getFileStatus(file, stClient); - } - finally { - clientManager.dispose(); - } - } - - @Nullable - private static SVNStatus getFileStatus(SvnVcs vcs, File file) { - SVNStatusClient stClient = vcs.createStatusClient(); - return getFileStatus(file, stClient); - } - - @Nullable - private static SVNStatus getFileStatus(final File file, final SVNStatusClient stClient) { - try { - return new RepeatSvnActionThroughBusy() { - @Override - protected void executeImpl() throws SVNException { - myT = stClient.doStatus(file, false); - } - }.compute(); - } - catch (SVNException e) { - return null; - } - } - - private static boolean isUndoOrRedo(@NotNull final Project project) { - final UndoManager undoManager = UndoManager.getInstance(project); - return undoManager.isUndoInProgress() || undoManager.isRedoInProgress(); - } - - private static boolean isUndo(SvnVcs vcs) { - if (vcs == null || vcs.getProject() == null) { - return false; - } - Project p = vcs.getProject(); - return UndoManager.getInstance(p).isUndoInProgress(); - } - - public void afterDone(final ThrowableConsumer invoker) { - } -} +/* + * 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.command.CommandAdapter; +import com.intellij.openapi.command.CommandEvent; +import com.intellij.openapi.command.undo.UndoManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.ProjectManager; +import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vcs.*; +import com.intellij.openapi.vcs.actions.VcsContextFactory; +import com.intellij.openapi.vcs.changes.ChangeListManager; +import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager; +import com.intellij.openapi.vfs.LocalFileOperationsHandler; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.newvfs.RefreshQueue; +import com.intellij.openapi.vfs.newvfs.RefreshSession; +import com.intellij.util.ThrowableConsumer; +import com.intellij.util.containers.MultiMap; +import com.intellij.vcsUtil.ActionWithTempFile; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +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.io.IOException; +import java.util.*; + +public class SvnFileSystemListener extends CommandAdapter implements LocalFileOperationsHandler { + private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.SvnFileSystemListener"); + private final LocalFileSystem myLfs; + + private static class AddedFileInfo { + private final VirtualFile myDir; + private final String myName; + @Nullable private final File myCopyFrom; + private final boolean myRecursive; + + public AddedFileInfo(final VirtualFile dir, final String name, @Nullable final File copyFrom, boolean recursive) { + myDir = dir; + myName = name; + myCopyFrom = copyFrom; + myRecursive = recursive; + } + } + + private static class MovedFileInfo { + private final Project myProject; + private final File mySrc; + private final File myDst; + + private MovedFileInfo(final Project project, final File src, final File dst) { + myProject = project; + mySrc = src; + myDst = dst; + } + } + + private final MultiMap myAddedFiles = new MultiMap(); + private final MultiMap myDeletedFiles = new MultiMap(); + private final List myMovedFiles = new ArrayList(); + private final Map> myMoveExceptions = new HashMap>(); + private final List myFilesToRefresh = new ArrayList(); + @Nullable private File myStorageForUndo; + private final List> myUndoStorageContents = new ArrayList>(); + private boolean myUndoingMove = false; + + public SvnFileSystemListener() { + myLfs = LocalFileSystem.getInstance(); + } + + private void addToMoveExceptions(final Project project, final SVNException e) { + List exceptionList = myMoveExceptions.get(project); + if (exceptionList == null) { + exceptionList = new ArrayList(); + myMoveExceptions.put(project, exceptionList); + } + VcsException vcsException; + if (SVNErrorCode.ENTRY_EXISTS.equals(e.getErrorMessage().getErrorCode())) { + vcsException = new VcsException(Arrays.asList("Target of move operation is already under version control.", + "Subversion move had not been performed. ", e.getMessage())); + } else { + vcsException = new VcsException(e); + } + exceptionList.add(vcsException); + } + + @Nullable + public File copy(final VirtualFile file, final VirtualFile toDir, final String copyName) throws IOException { + SvnVcs vcs = getVCS(toDir); + if (vcs == null) { + vcs = getVCS(file); + } + if (vcs == null) { + return null; + } + + File srcFile = new File(file.getPath()); + File destFile = new File(new File(toDir.getPath()), copyName); + final boolean dstDirUnderControl = SvnUtil.isSvnVersioned(vcs.getProject(), destFile.getParentFile()); + if (! dstDirUnderControl && !isPendingAdd(vcs.getProject(), toDir)) { + return null; + } + + if (! SvnUtil.isSvnVersioned(vcs.getProject(), srcFile.getParentFile())) { + myAddedFiles.putValue(vcs.getProject(), new AddedFileInfo(toDir, copyName, null, false)); + return null; + } + + final SVNStatus fileStatus = getFileStatus(vcs, srcFile); + if (fileStatus != null && SvnVcs.svnStatusIs(fileStatus, SVNStatusType.STATUS_ADDED)) { + myAddedFiles.putValue(vcs.getProject(), new AddedFileInfo(toDir, copyName, null, false)); + return null; + } + + if (sameRoot(vcs, file.getParent(), toDir)) { + myAddedFiles.putValue(vcs.getProject(), new AddedFileInfo(toDir, copyName, srcFile, false)); + return null; + } + + myAddedFiles.putValue(vcs.getProject(), new AddedFileInfo(toDir, copyName, null, false)); + return null; + } + + private boolean sameRoot(final SvnVcs vcs, final VirtualFile srcDir, final VirtualFile dstDir) { + final UUIDHelper helper = new UUIDHelper(vcs); + final String srcUUID = helper.getRepositoryUUID(vcs.getProject(), srcDir); + final String dstUUID = helper.getRepositoryUUID(vcs.getProject(), dstDir); + + return srcUUID != null && dstUUID != null && srcUUID.equals(dstUUID); + } + + private class UUIDHelper { + private final SVNWCClient myWcClient; + + private UUIDHelper(final SvnVcs vcs) { + myWcClient = vcs.createWCClient(); + } + + /** + * passed dir must be under VC control (it is assumed) + */ + @Nullable + public String getRepositoryUUID(final Project project, final VirtualFile dir) { + try { + final SVNInfo info1 = new RepeatSvnActionThroughBusy() { + @Override + protected void executeImpl() throws SVNException { + myT = myWcClient.doInfo(new File(dir.getPath()), SVNRevision.UNDEFINED); + } + }.compute(); + if (info1 == null || info1.getRepositoryUUID() == null) { + // go deeper if current parent was added (if parent was added, it theoretically could NOT know its repo UUID) + final VirtualFile parent = dir.getParent(); + if (parent == null) { + return null; + } + if (isPendingAdd(project, parent)) { + return getRepositoryUUID(project, parent); + } + } else { + return info1.getRepositoryUUID(); + } + } catch (SVNException e) { + // go to return default + } + return null; + } + } + + public boolean move(VirtualFile file, VirtualFile toDir) throws IOException { + File srcFile = getIOFile(file); + File dstFile = new File(getIOFile(toDir), file.getName()); + + final SvnVcs vcs = getVCS(toDir); + final SvnVcs sourceVcs = getVCS(file); + if (vcs == null && sourceVcs == null) return false; + + if (vcs == null) { + return false; + } + if (sourceVcs == null) { + return createItem(toDir, file.getName(), file.isDirectory(), true); + } + + if (isPendingAdd(vcs.getProject(), toDir)) { + + myMovedFiles.add(new MovedFileInfo(sourceVcs.getProject(), srcFile, dstFile)); + return true; + } + else { + final VirtualFile oldParent = file.getParent(); + myFilesToRefresh.add(oldParent); + myFilesToRefresh.add(toDir); + return doMove(sourceVcs, srcFile, dstFile); + } + } + + public boolean rename(VirtualFile file, String newName) throws IOException { + File srcFile = getIOFile(file); + File dstFile = new File(srcFile.getParentFile(), newName); + SvnVcs vcs = getVCS(file); + if (vcs != null) { + myFilesToRefresh.add(file.getParent()); + return doMove(vcs, srcFile, dstFile); + } + return false; + } + + private boolean doMove(@NotNull SvnVcs vcs, final File src, final File dst) { + long srcTime = src.lastModified(); + try { + final boolean isUndo = isUndo(vcs); + final String list = isUndo ? null : SvnChangelistListener.getCurrentMapping(vcs.getProject(), src); + + final boolean is17 = SvnUtil.is17CopyPart(src); + if (is17) { + if (for17move(vcs, src, dst, isUndo)) return false; + } else { + if (for16move(vcs, src, dst, isUndo)) return false; + } + + if (! isUndo && list != null) { + SvnChangelistListener.putUnderList(vcs.getProject(), list, dst); + } + dst.setLastModified(srcTime); + } + catch (SVNException e) { + addToMoveExceptions(vcs.getProject(), e); + return false; + } + return true; + } + + private boolean for17move(SvnVcs vcs, final File src, final File dst, boolean undo) throws SVNException { + if (undo) { + final SVNWCClient wcClient = vcs.createWCClient(); + myUndoingMove = true; + new RepeatSvnActionThroughBusy() { + @Override + protected void executeImpl() throws SVNException { + wcClient.doRevert(dst, true); + } + }.execute(); + new RepeatSvnActionThroughBusy() { + @Override + protected void executeImpl() throws SVNException { + wcClient.doRevert(src, true); + } + }.execute(); + restoreFromUndoStorage(dst); + } else { + if (doUsualMove(vcs, src)) return true; + final SVNCopyClient copyClient = vcs.createCopyClient(); + final SVNCopySource svnCopySource = new SVNCopySource(SVNRevision.UNDEFINED, SVNRevision.WORKING, src); + new RepeatSvnActionThroughBusy() { + @Override + protected void executeImpl() throws SVNException { + copyClient.doCopy(new SVNCopySource[]{svnCopySource}, dst, true, false, true); + } + }.execute(); + } + return false; + } + + private boolean doUsualMove(SvnVcs vcs, File src) { + // if src is not under version control, do usual move. + SVNStatus srcStatus = getFileStatus(vcs, src); + if (srcStatus == null || SvnVcs.svnStatusIsUnversioned(srcStatus) || + SvnVcs.svnStatusIs(srcStatus, SVNStatusType.STATUS_OBSTRUCTED) || + SvnVcs.svnStatusIs(srcStatus, SVNStatusType.STATUS_MISSING) || + SvnVcs.svnStatusIs(srcStatus, SVNStatusType.STATUS_EXTERNAL)) { + return true; + } + return false; + } + + private boolean for16move(SvnVcs vcs, final File src, final File dst, boolean undo) throws SVNException { + final SVNMoveClient mover = vcs.createMoveClient(); + if (undo) { + myUndoingMove = true; + restoreFromUndoStorage(dst); + new RepeatSvnActionThroughBusy() { + @Override + protected void executeImpl() throws SVNException { + mover.undoMove(src, dst); + } + }.execute(); + } + else { + // if src is not under version control, do usual move. + if (doUsualMove(vcs, src)) return true; + new RepeatSvnActionThroughBusy() { + @Override + protected void executeImpl() throws SVNException { + mover.doMove(src, dst); + } + }.execute(); + } + return false; + } + + private void restoreFromUndoStorage(final File dst) { + String normPath = FileUtil.toSystemIndependentName(dst.getPath()); + for (Iterator> it = myUndoStorageContents.iterator(); it.hasNext();) { + Pair e = it.next(); + final String p = FileUtil.toSystemIndependentName(e.first.getPath()); + if (p.startsWith(normPath)) { + try { + FileUtil.rename(e.second, e.first); + } + catch (IOException ex) { + LOG.error(ex); + FileUtil.asyncDelete(e.second); + } + it.remove(); + } + } + if (myStorageForUndo != null) { + final File[] files = myStorageForUndo.listFiles(); + if (files == null || files.length == 0) { + FileUtil.asyncDelete(myStorageForUndo); + myStorageForUndo = null; + } + } + } + + + public boolean createFile(VirtualFile dir, String name) throws IOException { + return createItem(dir, name, false, false); + } + + public boolean createDirectory(VirtualFile dir, String name) throws IOException { + return createItem(dir, name, true, false); + } + + /** + * delete file or directory (both 'undo' and 'do' modes) + * unversioned: do nothing, return false + * obstructed: do nothing, return false + * external or wc root: do nothing, return false + * missing: do nothing, return false + *

+ * versioned: schedule for deletion, return true + * added: schedule for deletion (make unversioned), return true + * copied, but not scheduled: schedule for deletion, return true + * replaced: schedule for deletion, return true + *

+ * deleted: do nothing, return true (strange) + */ + public boolean delete(VirtualFile file) throws IOException { + SvnVcs vcs = getVCS(file); + if (vcs != null && SvnUtil.isAdminDirectory(file)) { + return true; + } + if (vcs == null) return false; + final File ioFile = getIOFile(file); + if (! SvnUtil.isSvnVersioned(vcs.getProject(), ioFile.getParentFile())) { + return false; + } + try { + if (SVNWCUtil.isWorkingCopyRoot(ioFile)) { + return false; + } + } catch (SVNException e) { + // + } + + SVNStatus status = getFileStatus(ioFile); + + if (status == null || + SvnVcs.svnStatusIsUnversioned(status) || + SvnVcs.svnStatusIs(status, SVNStatusType.STATUS_OBSTRUCTED) || + SvnVcs.svnStatusIs(status, SVNStatusType.STATUS_MISSING) || + SvnVcs.svnStatusIs(status, SVNStatusType.STATUS_EXTERNAL) || + SvnVcs.svnStatusIs(status, SVNStatusType.STATUS_IGNORED)) { + return false; + } else if (SvnVcs.svnStatusIs(status, SVNStatusType.STATUS_DELETED)) { + if (isUndo(vcs)) { + moveToUndoStorage(file); + } + return true; + } + else { + if (vcs != null) { + if (isAboveSourceOfCopyOrMove(vcs.getProject(), ioFile)) { + myDeletedFiles.putValue(vcs.getProject(), ioFile); + return true; + } + if (SvnVcs.svnStatusIs(status, SVNStatusType.STATUS_ADDED)) { + try { + final SVNWCClient wcClient = vcs.createWCClient(); + new RepeatSvnActionThroughBusy() { + @Override + protected void executeImpl() throws SVNException { + wcClient.doRevert(ioFile, false); + } + }.execute(); + } + catch (SVNException e) { + // ignore + } + } + else { + myDeletedFiles.putValue(vcs.getProject(), ioFile); + // packages deleted from disk should not be deleted from svn (IDEADEV-16066) + if (file.isDirectory() || isUndo(vcs)) return true; + } + } + return false; + } + } + + private boolean isAboveSourceOfCopyOrMove(final Project p, File ioFile) { + for (MovedFileInfo file : myMovedFiles) { + if (FileUtil.isAncestor(ioFile, file.mySrc, false)) return true; + } + for (AddedFileInfo info : myAddedFiles.get(p)) { + if (info.myCopyFrom != null && FileUtil.isAncestor(ioFile, info.myCopyFrom, false)) return true; + } + return false; + } + + private void moveToUndoStorage(final VirtualFile file) { + if (myStorageForUndo == null) { + try { + myStorageForUndo = FileUtil.createTempDirectory("svnUndoStorage", ""); + } + catch (IOException e) { + LOG.error(e); + return; + } + } + final File tmpFile = FileUtil.findSequentNonexistentFile(myStorageForUndo, "tmp", ""); + myUndoStorageContents.add(0, new Pair(new File(file.getPath()), tmpFile)); + new File(file.getPath()).renameTo(tmpFile); + } + + /** + * add file or directory: + *

+ * parent directory is: + * unversioned: do nothing, return false + * versioned: + * entry is: + * null: create entry, schedule for addition + * missing: do nothing, return false + * deleted, 'do' mode: try to create entry and it schedule for addition if kind is the same, otherwise do nothing, return false. + * deleted: 'undo' mode: try to revert non-recursively, if kind is the same, otherwise do nothing, return false. + * anything else: return false. + */ + private boolean createItem(VirtualFile dir, String name, boolean directory, final boolean recursive) { + SvnVcs vcs = getVCS(dir); + if (vcs == null) { + return false; + } + if (isUndo(vcs) && SvnUtil.isAdminDirectory(dir, name)) { + return false; + } + File ioDir = getIOFile(dir); + boolean pendingAdd = isPendingAdd(vcs.getProject(), dir); + if (! SvnUtil.isSvnVersioned(vcs.getProject(), ioDir) && ! pendingAdd) { + return false; + } + final SVNWCClient wcClient = vcs.createWCClient(); + final File targetFile = new File(ioDir, name); + SVNStatus status = getFileStatus(vcs, targetFile); + + if (status == null || status.getContentsStatus() == SVNStatusType.STATUS_NONE || + status.getContentsStatus() == SVNStatusType.STATUS_UNVERSIONED) { + myAddedFiles.putValue(vcs.getProject(), new AddedFileInfo(dir, name, null, recursive)); + return false; + } + else if (SvnVcs.svnStatusIs(status, SVNStatusType.STATUS_MISSING)) { + return false; + } + else if (SvnVcs.svnStatusIs(status, SVNStatusType.STATUS_DELETED)) { + SVNNodeKind kind = status.getKind(); + // kind differs. + if (directory && kind != SVNNodeKind.DIR || !directory && kind != SVNNodeKind.FILE) { + return false; + } + try { + if (isUndo(vcs)) { + new RepeatSvnActionThroughBusy() { + @Override + protected void executeImpl() throws SVNException { + wcClient.doRevert(targetFile, false); + } + }.execute(); + return true; + } + myAddedFiles.putValue(vcs.getProject(), new AddedFileInfo(dir, name, null, recursive)); + return false; + } + catch (SVNException e) { + SVNFileUtil.deleteAll(targetFile, true); + return false; + } + } + return false; + } + + private boolean isPendingAdd(final Project project, final VirtualFile dir) { + final Collection addedFileInfos = myAddedFiles.get(project); + for(AddedFileInfo i: addedFileInfos) { + if (Comparing.equal(i.myDir, dir.getParent()) && i.myName.equals(dir.getName())) { + return true; + } + } + return false; + } + + public void commandStarted(CommandEvent event) { + myUndoingMove = false; + final Project project = event.getProject(); + if (project == null) return; + commandStarted(project); + } + + void commandStarted(final Project project) { + myUndoingMove = false; + myMoveExceptions.remove(project); + } + + public void commandFinished(CommandEvent event) { + final Project project = event.getProject(); + if (project == null) return; + commandFinished(project); + } + + void commandFinished(final Project project) { + checkOverwrites(project); + if (myAddedFiles.containsKey(project)) { + processAddedFiles(project); + } + processMovedFiles(project); + if (myDeletedFiles.containsKey(project)) { + processDeletedFiles(project); + } + + final List exceptionList = myMoveExceptions.get(project); + if (exceptionList != null && ! exceptionList.isEmpty()) { + AbstractVcsHelper.getInstance(project).showErrors(exceptionList, SvnBundle.message("move.files.errors.title")); + } + + if (!myFilesToRefresh.isEmpty()) { + refreshFiles(project); + } + } + + private void checkOverwrites(final Project project) { + final Collection addedFileInfos = myAddedFiles.get(project); + final Collection deletedFiles = myDeletedFiles.get(project); + if (addedFileInfos.isEmpty() || deletedFiles.isEmpty()) return; + final Iterator iterator = addedFileInfos.iterator(); + while (iterator.hasNext()) { + AddedFileInfo addedFileInfo = iterator.next(); + final File ioFile = new File(addedFileInfo.myDir.getPath(), addedFileInfo.myName); + if (deletedFiles.remove(ioFile)) { + iterator.remove(); + } + } + } + + private void refreshFiles(final Project project) { + final List toRefreshFiles = new ArrayList(); + final List toRefreshDirs = new ArrayList(); + for (VirtualFile file : myFilesToRefresh) { + if (file == null) continue; + if (file.isDirectory()) { + toRefreshDirs.add(file); + } else { + toRefreshFiles.add(file); + } + } + // if refresh asynchronously, local changes would also be notified that they are dirty asynchronously, + // and commit could be executed while not all changes are visible + final RefreshSession session = RefreshQueue.getInstance().createSession(true, true, new Runnable() { + public void run() { + if (project.isDisposed()) return; + filterOutInvalid(toRefreshFiles); + filterOutInvalid(toRefreshDirs); + + final VcsDirtyScopeManager vcsDirtyScopeManager = VcsDirtyScopeManager.getInstance(project); + vcsDirtyScopeManager.filesDirty(toRefreshFiles, toRefreshDirs); + } + }); + filterOutInvalid(myFilesToRefresh); + session.addAllFiles(myFilesToRefresh); + session.launch(); + myFilesToRefresh.clear(); + } + + private static void filterOutInvalid(final Collection files) { + for (Iterator iterator = files.iterator(); iterator.hasNext();) { + final VirtualFile file = iterator.next(); + if (! file.isValid() || ! file.exists()) { + LOG.info("Refresh root is not valid: " + file.getPath()); + iterator.remove(); + } + } + } + + private void processAddedFiles(Project project) { + SvnVcs vcs = SvnVcs.getInstance(project); + List addedVFiles = new ArrayList(); + Map copyFromMap = new HashMap(); + final Set recursiveItems = new HashSet(); + fillAddedFiles(project, vcs, addedVFiles, copyFromMap, recursiveItems); + if (addedVFiles.isEmpty()) return; + final VcsShowConfirmationOption.Value value = vcs.getAddConfirmation().getValue(); + if (value != VcsShowConfirmationOption.Value.DO_NOTHING_SILENTLY) { + final AbstractVcsHelper vcsHelper = AbstractVcsHelper.getInstance(project); + final Collection filesToProcess = promptAboutAddition(vcs, addedVFiles, value, vcsHelper); + if (filesToProcess != null && !filesToProcess.isEmpty()) { + final List exceptions = new ArrayList(); + runInBackground(project, "Adding files to Subversion", + createAdditionRunnable(project, vcs, copyFromMap, filesToProcess, exceptions)); + if (!exceptions.isEmpty()) { + vcsHelper.showErrors(exceptions, SvnBundle.message("add.files.errors.title")); + } + } + } + } + + private void runInBackground(final Project project, final String name, final Runnable runnable) { + if (ApplicationManager.getApplication().isDispatchThread()) { + ProgressManager.getInstance().runProcessWithProgressSynchronously(runnable, name, false, project); + } else { + runnable.run(); + } + } + + private Runnable createAdditionRunnable(final Project project, + final SvnVcs vcs, + final Map copyFromMap, + final Collection filesToProcess, + final List exceptions) { + return new Runnable() { + @Override + public void run() { + final SVNWCClient wcClient = vcs.createWCClient(); + final SVNCopyClient copyClient = vcs.createCopyClient(); + for(VirtualFile file: filesToProcess) { + final File ioFile = new File(file.getPath()); + try { + final File copyFrom = copyFromMap.get(file); + if (copyFrom != null) { + try { + new ActionWithTempFile(ioFile) { + protected void executeInternal() throws VcsException { + try { + // not recursive + final SVNCopySource[] copySource = {new SVNCopySource(SVNRevision.WORKING, SVNRevision.WORKING, copyFrom)}; + new RepeatSvnActionThroughBusy() { + @Override + protected void executeImpl() throws SVNException { + copyClient.doCopy(copySource, ioFile, false, true, true); + } + }.execute(); + } + catch (SVNException e) { + throw new VcsException(e); + } + } + }.execute(); + } + catch (VcsException e) { + exceptions.add(e); + } + } + else { + new RepeatSvnActionThroughBusy() { + @Override + protected void executeImpl() throws SVNException { + wcClient.doAdd(ioFile, true, false, false, true); + } + }.execute(); + } + VcsDirtyScopeManager.getInstance(project).fileDirty(file); + } + catch (SVNException e) { + exceptions.add(new VcsException(e)); + } + } + } + }; + } + + private Collection promptAboutAddition(SvnVcs vcs, + List addedVFiles, + VcsShowConfirmationOption.Value value, + AbstractVcsHelper vcsHelper) { + Collection filesToProcess; + if (value == VcsShowConfirmationOption.Value.DO_ACTION_SILENTLY) { + filesToProcess = addedVFiles; + } + else { + final String singleFilePrompt; + if (addedVFiles.size() == 1 && addedVFiles.get(0).isDirectory()) { + singleFilePrompt = SvnBundle.getString("confirmation.text.add.dir"); + } + else { + singleFilePrompt = SvnBundle.getString("confirmation.text.add.file"); + } + filesToProcess = vcsHelper.selectFilesToProcess(addedVFiles, SvnBundle.message("confirmation.title.add.multiple.files"), + null, + SvnBundle.message("confirmation.title.add.file"), singleFilePrompt, + vcs.getAddConfirmation()); + } + return filesToProcess; + } + + private void fillAddedFiles(Project project, + SvnVcs vcs, + List addedVFiles, + Map copyFromMap, + Set recursiveItems) { + final Collection addedFileInfos = myAddedFiles.remove(project); + final ChangeListManager changeListManager = ChangeListManager.getInstance(project); + + for (AddedFileInfo addedFileInfo : addedFileInfos) { + final File ioFile = new File(getIOFile(addedFileInfo.myDir), addedFileInfo.myName); + VirtualFile addedFile = addedFileInfo.myDir.findChild(addedFileInfo.myName); + if (addedFile == null) { + addedFile = myLfs.refreshAndFindFileByIoFile(ioFile); + } + if (addedFile != null) { + final SVNStatus fileStatus = getFileStatus(vcs, ioFile); + if (fileStatus == null || ! SvnVcs.svnStatusIs(fileStatus, SVNStatusType.STATUS_IGNORED)) { + boolean isIgnored = changeListManager.isIgnoredFile(addedFile); + if (!isIgnored) { + addedVFiles.add(addedFile); + copyFromMap.put(addedFile, addedFileInfo.myCopyFrom); + if (addedFileInfo.myRecursive) { + recursiveItems.add(addedFile); + } + } + } + } + } + } + + private void processDeletedFiles(Project project) { + final List deletedFiles = new ArrayList(); + final Collection filesToProcess = new ArrayList(); + fillDeletedFiles(project, deletedFiles, filesToProcess); + if (deletedFiles.isEmpty() && filesToProcess.isEmpty() || myUndoingMove) return; + SvnVcs vcs = SvnVcs.getInstance(project); + final VcsShowConfirmationOption.Value value = vcs.getDeleteConfirmation().getValue(); + if (value != VcsShowConfirmationOption.Value.DO_NOTHING_SILENTLY) { + final AbstractVcsHelper vcsHelper = AbstractVcsHelper.getInstance(project); + if (! deletedFiles.isEmpty()) { + final Collection confirmed = promptAboutDeletion(deletedFiles, vcs, value, vcsHelper); + if (confirmed != null) { + filesToProcess.addAll(confirmed); + } + } + if (filesToProcess != null && !filesToProcess.isEmpty()) { + List exceptions = new ArrayList(); + runInBackground(project, "Deleting files from Subversion", createDeleteRunnable(project, vcs, filesToProcess, exceptions)); + if (!exceptions.isEmpty()) { + vcsHelper.showErrors(exceptions, SvnBundle.message("delete.files.errors.title")); + } + } + for (FilePath file : deletedFiles) { + final FilePath parent = file.getParentPath(); + if (parent != null) { + myFilesToRefresh.add(parent.getVirtualFile()); + } + } + if (filesToProcess != null) { + deletedFiles.removeAll(filesToProcess); + } + for (FilePath file : deletedFiles) { + FileUtil.delete(file.getIOFile()); + } + } + } + + private Runnable createDeleteRunnable(final Project project, + final SvnVcs vcs, + final Collection filesToProcess, + final List exceptions) { + return new Runnable() { + public void run() { + final SVNWCClient wcClient = vcs.createWCClient(); + for(FilePath file: filesToProcess) { + VirtualFile vFile = file.getVirtualFile(); // for deleted directories + final File ioFile = new File(file.getPath()); + try { + new RepeatSvnActionThroughBusy() { + @Override + protected void executeImpl() throws SVNException { + wcClient.doDelete(ioFile, true, false); + } + }.execute(); + if (vFile != null && vFile.isValid() && vFile.isDirectory()) { + vFile.refresh(true, true); + VcsDirtyScopeManager.getInstance(project).dirDirtyRecursively(vFile); + } + else { + VcsDirtyScopeManager.getInstance(project).fileDirty(file); + } + } + catch (SVNException e) { + exceptions.add(new VcsException(e)); + } + } + } + }; + } + + private Collection promptAboutDeletion(List deletedFiles, + SvnVcs vcs, + VcsShowConfirmationOption.Value value, + AbstractVcsHelper vcsHelper) { + Collection filesToProcess; + if (value == VcsShowConfirmationOption.Value.DO_ACTION_SILENTLY) { + filesToProcess = new ArrayList(deletedFiles); + } + else { + + final String singleFilePrompt; + if (deletedFiles.size() == 1 && deletedFiles.get(0).isDirectory()) { + singleFilePrompt = SvnBundle.getString("confirmation.text.delete.dir"); + } + else { + singleFilePrompt = SvnBundle.getString("confirmation.text.delete.file"); + } + final Collection files = vcsHelper + .selectFilePathsToProcess(deletedFiles, SvnBundle.message("confirmation.title.delete.multiple.files"), null, + SvnBundle.message("confirmation.title.delete.file"), singleFilePrompt, vcs.getDeleteConfirmation()); + filesToProcess = files == null ? null : new ArrayList(files); + } + return filesToProcess; + } + + private void fillDeletedFiles(Project project, List deletedFiles, Collection deleteAnyway) { + final SvnVcs vcs = SvnVcs.getInstance(project); + final SVNStatusClient sc = vcs.createStatusClient(); + final Collection files = myDeletedFiles.remove(project); + for (final File file : files) { + boolean isAdded = false; + try { + final SVNStatus status = new RepeatSvnActionThroughBusy() { + @Override + protected void executeImpl() throws SVNException { + myT = sc.doStatus(file, false); + } + }.compute(); + isAdded = SVNStatusType.STATUS_ADDED.equals(status.getNodeStatus()); + } + catch (SVNException e) { + // + } + final FilePath filePath = VcsContextFactory.SERVICE.getInstance().createFilePathOn(file); + if (isAdded) { + deleteAnyway.add(filePath); + } else { + deletedFiles.add(filePath); + } + } + } + + private void processMovedFiles(final Project project) { + if (myMovedFiles.isEmpty()) return; + + final Runnable runnable = new Runnable() { + public void run() { + for (Iterator iterator = myMovedFiles.iterator(); iterator.hasNext();) { + MovedFileInfo movedFileInfo = iterator.next(); + if (movedFileInfo.myProject == project) { + doMove(SvnVcs.getInstance(project), movedFileInfo.mySrc, movedFileInfo.myDst); + iterator.remove(); + } + } + } + }; + runInBackground(project, "Moving files in Subversion", runnable); + } + + @Nullable + private static SvnVcs getVCS(VirtualFile file) { + Project[] projects = ProjectManager.getInstance().getOpenProjects(); + for (Project project : projects) { + AbstractVcs vcs = ProjectLevelVcsManager.getInstance(project).getVcsFor(file); + if (vcs instanceof SvnVcs) { + return (SvnVcs)vcs; + } + } + return null; + } + + + private static File getIOFile(VirtualFile vf) { + return new File(vf.getPath()).getAbsoluteFile(); + } + + @Nullable + private static SVNStatus getFileStatus(File file) { + final SVNClientManager clientManager = SVNClientManager.newInstance(); + try { + SVNStatusClient stClient = clientManager.getStatusClient(); + return getFileStatus(file, stClient); + } + finally { + clientManager.dispose(); + } + } + + @Nullable + private static SVNStatus getFileStatus(SvnVcs vcs, File file) { + SVNStatusClient stClient = vcs.createStatusClient(); + return getFileStatus(file, stClient); + } + + @Nullable + private static SVNStatus getFileStatus(final File file, final SVNStatusClient stClient) { + try { + return new RepeatSvnActionThroughBusy() { + @Override + protected void executeImpl() throws SVNException { + myT = stClient.doStatus(file, false); + } + }.compute(); + } + catch (SVNException e) { + return null; + } + } + + private static boolean isUndoOrRedo(@NotNull final Project project) { + final UndoManager undoManager = UndoManager.getInstance(project); + return undoManager.isUndoInProgress() || undoManager.isRedoInProgress(); + } + + private static boolean isUndo(SvnVcs vcs) { + if (vcs == null || vcs.getProject() == null) { + return false; + } + Project p = vcs.getProject(); + return UndoManager.getInstance(p).isUndoInProgress(); + } + + public void afterDone(final ThrowableConsumer invoker) { + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/SvnFileAnnotation.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/SvnFileAnnotation.java index fcff85f5a40a..65571b3ecb0d 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/SvnFileAnnotation.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/SvnFileAnnotation.java @@ -15,6 +15,7 @@ */ package org.jetbrains.idea.svn.annotate; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.vcs.annotate.AnnotationListener; import com.intellij.openapi.vcs.annotate.ShowAllAffectedGenericAction; import com.intellij.openapi.vcs.history.VcsRevisionNumber; @@ -27,7 +28,7 @@ public class SvnFileAnnotation extends BaseSvnFileAnnotation { private final VirtualFile myFile; private final SvnEntriesListener myListener = new SvnEntriesListener() { public void onEntriesChanged(VirtualFile directory) { - if (directory != myFile.getParent()) return; + if (!Comparing.equal(directory, myFile.getParent())) return; final VcsRevisionNumber currentRevision = myVcs.getDiffProvider().getCurrentRevision(myFile); if (currentRevision != null && currentRevision.equals(myBaseRevision)) return; diff --git a/plugins/svn4ideaOld/src/org/jetbrains/idea/svn/SvnFileSystemListener.java b/plugins/svn4ideaOld/src/org/jetbrains/idea/svn/SvnFileSystemListener.java index e2f7fb97f110..5732651ab052 100644 --- a/plugins/svn4ideaOld/src/org/jetbrains/idea/svn/SvnFileSystemListener.java +++ b/plugins/svn4ideaOld/src/org/jetbrains/idea/svn/SvnFileSystemListener.java @@ -25,6 +25,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vcs.*; @@ -439,7 +440,7 @@ public class SvnFileSystemListener extends CommandAdapter implements LocalFileOp private boolean isPendingAdd(final Project project, final VirtualFile dir) { final Collection addedFileInfos = myAddedFiles.get(project); for(AddedFileInfo i: addedFileInfos) { - if (i.myDir == dir.getParent() && i.myName.equals(dir.getName())) { + if (Comparing.equal(i.myDir, dir.getParent()) && i.myName.equals(dir.getName())) { return true; } } diff --git a/plugins/svn4ideaOld/src/org/jetbrains/idea/svn/annotate/SvnFileAnnotation.java b/plugins/svn4ideaOld/src/org/jetbrains/idea/svn/annotate/SvnFileAnnotation.java index e4329a259887..a94fb2b1db22 100644 --- a/plugins/svn4ideaOld/src/org/jetbrains/idea/svn/annotate/SvnFileAnnotation.java +++ b/plugins/svn4ideaOld/src/org/jetbrains/idea/svn/annotate/SvnFileAnnotation.java @@ -15,6 +15,7 @@ */ package org.jetbrains.idea.svn.annotate; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.vcs.annotate.*; import com.intellij.openapi.vcs.history.VcsFileRevision; import com.intellij.openapi.vcs.history.VcsRevisionNumber; @@ -104,7 +105,7 @@ public class SvnFileAnnotation implements FileAnnotation { }; private final SvnEntriesListener myListener = new SvnEntriesListener() { public void onEntriesChanged(VirtualFile directory) { - if (directory != myFile.getParent()) return; + if (!Comparing.equal(directory, myFile.getParent())) return; final VcsRevisionNumber currentRevision = myVcs.getDiffProvider().getCurrentRevision(myFile); if (currentRevision != null && currentRevision.equals(myBaseRevision)) return; diff --git a/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/impl/XsltIncludeIndex.java b/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/impl/XsltIncludeIndex.java index b15d485771ea..1c3866c13162 100644 --- a/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/impl/XsltIncludeIndex.java +++ b/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/impl/XsltIncludeIndex.java @@ -17,6 +17,7 @@ package org.intellij.lang.xpath.xslt.impl; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; @@ -46,7 +47,7 @@ public class XsltIncludeIndex { //noinspection ForLoopReplaceableByForEach for (int i = 0; i < which.length; i++) { final VirtualFile file = which[i]; - if (file == from) { + if (Comparing.equal(file, from)) { return true; } } diff --git a/xml/impl/src/com/intellij/codeInsight/daemon/impl/analysis/CreateNSDeclarationIntentionFix.java b/xml/impl/src/com/intellij/codeInsight/daemon/impl/analysis/CreateNSDeclarationIntentionFix.java index 8d98f7fe61f9..21c8b0dbb5f6 100644 --- a/xml/impl/src/com/intellij/codeInsight/daemon/impl/analysis/CreateNSDeclarationIntentionFix.java +++ b/xml/impl/src/com/intellij/codeInsight/daemon/impl/analysis/CreateNSDeclarationIntentionFix.java @@ -37,6 +37,7 @@ import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.PopupChooserBuilder; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiAnchor; import com.intellij.psi.PsiDocumentManager; @@ -129,7 +130,7 @@ public class CreateNSDeclarationIntentionFix implements HintAction, LocalQuickFi final PsiFile containingFile = descriptor.getPsiElement().getContainingFile(); Editor editor = FileEditorManager.getInstance(project).getSelectedTextEditor(); final PsiFile file = editor != null ? PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument()):null; - if (file == null || file.getVirtualFile() != containingFile.getVirtualFile()) return; + if (file == null || !Comparing.equal(file.getVirtualFile(), containingFile.getVirtualFile())) return; try { invoke(project, editor, containingFile); } catch (IncorrectOperationException ex) { LOG.error(ex); diff --git a/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsXmlWrapper.java b/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsXmlWrapper.java index d1b16c25325c..56b9b063d118 100644 --- a/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsXmlWrapper.java +++ b/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsXmlWrapper.java @@ -29,6 +29,7 @@ import com.intellij.openapi.editor.event.CaretListener; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.TextRange; @@ -119,7 +120,7 @@ public class BreadcrumbsXmlWrapper implements BreadcrumbsItemListener