From dd8d43edaea1d928c79b1b52e2c6356f1cbbf7df Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Wed, 27 Sep 2017 18:23:00 +0300 Subject: [PATCH] IDEA-164682 Removing sdk leads to exception Rewritten SDK roots storage to use virtual file pointers to fix exceptions on load. Corresponding virtual file pointer's listener is used for informing ProjectRootManagerComponent of changed roots directly, eliminating need for all roots enumeration on each vfp change to determine if the "rootsChanged" event should be fired, which is good for performance. --- .../libraryEditor/NewLibraryEditor.java | 65 ++-- .../openapi/projectRoots/ProjectJdkTest.java | 61 ++++ .../com/intellij/roots/RootsChangedTest.java | 31 +- .../intellij/roots/libraries/LibraryTest.java | 13 +- .../pointers/VirtualFilePointerContainer.java | 4 +- .../impl/VirtualFilePointerContainerImpl.java | 73 +++-- .../impl/ProjectJdkTableImpl.java | 5 + .../impl/ProjectRootManagerComponent.java | 71 ++-- .../JarDirectoryWatcherFactoryImpl.java | 28 -- .../libraries/JarDirectoryWatcherImpl.java | 147 --------- .../openapi/vfs/impl/FilePointerPartNode.java | 9 +- .../src/META-INF/LangExtensions.xml | 3 - .../openapi/projectRoots/ex/ProjectRoot.java | 37 --- .../projectRoots/ex/ProjectRootContainer.java | 42 --- .../impl/CompositeProjectRoot.java | 162 ---------- .../projectRoots/impl/ProjectJdkImpl.java | 107 ++++--- .../impl/ProjectRootContainerImpl.java | 270 ---------------- .../impl/RootsAsVirtualFilePointers.java | 189 +++++++++++ .../projectRoots/impl/SimpleProjectRoot.java | 131 -------- .../openapi/roots/impl/ContentEntryImpl.java | 2 +- .../roots/impl/ContentFolderBaseImpl.java | 6 +- .../impl/InheritedJdkOrderEntryImpl.java | 4 +- .../impl/JavaModuleExternalPathsImpl.java | 4 +- .../roots/impl/ModuleJdkOrderEntryImpl.java | 16 +- .../roots/impl/ProjectRootManagerImpl.java | 37 ++- .../openapi/roots/impl/RootModelImpl.java | 6 + .../roots/impl/libraries/JarDirectories.java | 158 --------- .../libraries/JarDirectoryWatcherFactory.java | 43 --- .../roots/impl/libraries/LibraryImpl.java | 302 +++++++++--------- .../impl/FileAssociationsManagerImpl.java | 4 +- 30 files changed, 663 insertions(+), 1367 deletions(-) create mode 100644 java/java-tests/testSrc/com/intellij/java/openapi/projectRoots/ProjectJdkTest.java delete mode 100644 platform/lang-impl/src/com/intellij/openapi/roots/impl/libraries/JarDirectoryWatcherFactoryImpl.java delete mode 100644 platform/lang-impl/src/com/intellij/openapi/roots/impl/libraries/JarDirectoryWatcherImpl.java delete mode 100644 platform/projectModel-impl/src/com/intellij/openapi/projectRoots/ex/ProjectRoot.java delete mode 100644 platform/projectModel-impl/src/com/intellij/openapi/projectRoots/ex/ProjectRootContainer.java delete mode 100644 platform/projectModel-impl/src/com/intellij/openapi/projectRoots/impl/CompositeProjectRoot.java delete mode 100644 platform/projectModel-impl/src/com/intellij/openapi/projectRoots/impl/ProjectRootContainerImpl.java create mode 100644 platform/projectModel-impl/src/com/intellij/openapi/projectRoots/impl/RootsAsVirtualFilePointers.java delete mode 100644 platform/projectModel-impl/src/com/intellij/openapi/projectRoots/impl/SimpleProjectRoot.java delete mode 100644 platform/projectModel-impl/src/com/intellij/openapi/roots/impl/libraries/JarDirectories.java delete mode 100644 platform/projectModel-impl/src/com/intellij/openapi/roots/impl/libraries/JarDirectoryWatcherFactory.java diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/NewLibraryEditor.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/NewLibraryEditor.java index fff76877dd47..a7d64b5e0a59 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/NewLibraryEditor.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/NewLibraryEditor.java @@ -15,17 +15,20 @@ */ package com.intellij.openapi.roots.ui.configuration.libraryEditor; +import com.intellij.ide.highlighter.ArchiveFileType; +import com.intellij.openapi.fileTypes.FileTypeRegistry; import com.intellij.openapi.roots.OrderRootType; -import com.intellij.openapi.roots.impl.libraries.JarDirectories; import com.intellij.openapi.roots.impl.libraries.LibraryEx; -import com.intellij.openapi.roots.impl.libraries.LibraryImpl; import com.intellij.openapi.roots.libraries.LibraryProperties; import com.intellij.openapi.roots.libraries.LibraryType; +import com.intellij.openapi.vfs.StandardFileSystems; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.VirtualFileVisitor; import com.intellij.openapi.vfs.impl.LightFilePointer; import com.intellij.util.ArrayUtil; import com.intellij.util.containers.MultiMap; +import com.intellij.util.io.URLUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -41,7 +44,8 @@ public class NewLibraryEditor extends LibraryEditorBase { private String myLibraryName; private final MultiMap myRoots; private final Set myExcludedRoots; - private final JarDirectories myJarDirectories = new JarDirectories(); + private final MultiMap myJarDirectoryUrls = new MultiMap<>(); + private final MultiMap myJarDirectoryRecursiveUrls = new MultiMap<>(); private LibraryType myType; private LibraryProperties myProperties; private boolean myKeepInvalidUrls = true; @@ -57,10 +61,6 @@ public class NewLibraryEditor extends LibraryEditorBase { myExcludedRoots = new LinkedHashSet<>(); } - public boolean isKeepInvalidUrls() { - return myKeepInvalidUrls; - } - public void setKeepInvalidUrls(boolean keepInvalidUrls) { myKeepInvalidUrls = keepInvalidUrls; } @@ -122,8 +122,9 @@ public class NewLibraryEditor extends LibraryEditorBase { if (file.isDirectory()) { final String url = file.getUrl(); - if (myJarDirectories.contains(rootType, url)) { - LibraryImpl.collectJarFiles(file, result, myJarDirectories.isRecursive(rootType, url)); + if (isJarDirectory(url, rootType)) { + boolean recursive = myJarDirectoryRecursiveUrls.get(rootType).contains(url); + collectJarFiles(file, result, recursive); continue; } } @@ -171,20 +172,15 @@ public class NewLibraryEditor extends LibraryEditorBase { @Override public void addJarDirectory(@NotNull final String url, boolean recursive, @NotNull OrderRootType rootType) { addRoot(url, rootType); - myJarDirectories.add(rootType, url, recursive); + (recursive ? myJarDirectoryRecursiveUrls : myJarDirectoryUrls).putValue(rootType, url); } @Override public void removeRoot(@NotNull String url, @NotNull OrderRootType rootType) { myRoots.remove(rootType, new LightFilePointer(url)); - Iterator iterator = myExcludedRoots.iterator(); - while (iterator.hasNext()) { - LightFilePointer pointer = iterator.next(); - if (!isUnderRoots(pointer.getUrl())) { - iterator.remove(); - } - } - myJarDirectories.remove(rootType, url); + myExcludedRoots.removeIf(pointer -> !isUnderRoots(pointer.getUrl())); + myJarDirectoryUrls.remove(rootType, url); + myJarDirectoryRecursiveUrls.remove(rootType, url); } private boolean isUnderRoots(@NotNull String url) { @@ -203,7 +199,7 @@ public class NewLibraryEditor extends LibraryEditorBase { @Override public boolean isJarDirectory(@NotNull String url, @NotNull OrderRootType rootType) { - return myJarDirectories.contains(rootType, url); + return myJarDirectoryUrls.get(rootType).contains(url) || myJarDirectoryRecursiveUrls.get(rootType).contains(url); } @Override @@ -247,16 +243,39 @@ public class NewLibraryEditor extends LibraryEditorBase { // apply editor's state to the target container for (OrderRootType type : myRoots.keySet()) { for (LightFilePointer pointer : myRoots.get(type)) { - if (!myJarDirectories.contains(type, pointer.getUrl())) { + if (!isJarDirectory(pointer.getUrl(), type)) { addRoot.accept(pointer.getUrl(), type); } } } - for (OrderRootType type : myJarDirectories.getRootTypes()) { - for (String url : myJarDirectories.getDirectories(type)) { - addJarDir.accept(url, myJarDirectories.isRecursive(type, url), type); + for (Map.Entry> entry : myJarDirectoryUrls.entrySet()) { + OrderRootType type = entry.getKey(); + for (String url : entry.getValue()) { + addJarDir.accept(url, false, type); } } + for (Map.Entry> entry : myJarDirectoryRecursiveUrls.entrySet()) { + OrderRootType type = entry.getKey(); + for (String url : entry.getValue()) { + addJarDir.accept(url, true, type); + } + } + } + + private static void collectJarFiles(@NotNull VirtualFile dir, @NotNull List container, final boolean recursively) { + VfsUtilCore.visitChildrenRecursively(dir, new VirtualFileVisitor(VirtualFileVisitor.SKIP_ROOT, recursively ? null : VirtualFileVisitor.ONE_LEVEL_DEEP) { + @Override + public boolean visitFile(@NotNull VirtualFile file) { + if (!file.isDirectory() && FileTypeRegistry.getInstance().getFileTypeByFileName(file.getName()) == ArchiveFileType.INSTANCE) { + VirtualFile jarRoot = StandardFileSystems.jar().findFileByPath(file.getPath() + URLUtil.JAR_SEPARATOR); + if (jarRoot != null) { + container.add(jarRoot); + return false; + } + } + return true; + } + }); } @FunctionalInterface diff --git a/java/java-tests/testSrc/com/intellij/java/openapi/projectRoots/ProjectJdkTest.java b/java/java-tests/testSrc/com/intellij/java/openapi/projectRoots/ProjectJdkTest.java new file mode 100644 index 000000000000..882ef681fda4 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/java/openapi/projectRoots/ProjectJdkTest.java @@ -0,0 +1,61 @@ +// Copyright 2000-2017 JetBrains s.r.o. +// Use of this source code is governed by the Apache 2.0 license that can be +// found in the LICENSE file. +package com.intellij.java.openapi.projectRoots; + +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.projectRoots.JavaSdk; +import com.intellij.openapi.projectRoots.ProjectJdkTable; +import com.intellij.openapi.projectRoots.impl.ProjectJdkImpl; +import com.intellij.openapi.roots.OrderRootType; +import com.intellij.openapi.util.JDOMUtil; +import com.intellij.openapi.util.ThrowableComputable; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.testFramework.PlatformTestCase; +import org.jdom.Element; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class ProjectJdkTest extends PlatformTestCase { + public void testDoesntCrashOnJdkRootDisappearance() throws Exception { + VirtualFile nDir = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(createTempDir("nroot", true)); + String nUrl = nDir.getUrl(); + ProjectJdkImpl jdk = WriteCommandAction.runWriteCommandAction(getProject(), (ThrowableComputable)()->{ + ProjectJdkImpl myJdk = (ProjectJdkImpl)ProjectJdkTable.getInstance().createSdk("my", JavaSdk.getInstance()); + Element element = JDOMUtil.load("\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + ); + myJdk.readExternal(element); + return myJdk; + }); + + try { + List urls = Arrays.stream(jdk.getRoots(OrderRootType.CLASSES)).peek(v -> assertTrue(v.isValid())).map(VirtualFile::getUrl).collect(Collectors.toList()); + assertOrderedEquals(urls, nUrl); + + delete(nDir); + assertFalse(nDir.isValid()); + + urls = Arrays.stream(jdk.getRoots(OrderRootType.CLASSES)).peek(v -> assertTrue(v.isValid())).map(VirtualFile::getUrl).collect(Collectors.toList()); + assertEmpty(urls); + } + finally { + WriteCommandAction.runWriteCommandAction(getProject(), ()->ProjectJdkTable.getInstance().removeJdk(jdk)); + } + } +} diff --git a/java/java-tests/testSrc/com/intellij/roots/RootsChangedTest.java b/java/java-tests/testSrc/com/intellij/roots/RootsChangedTest.java index b17bdadd9f74..814f63eb4115 100644 --- a/java/java-tests/testSrc/com/intellij/roots/RootsChangedTest.java +++ b/java/java-tests/testSrc/com/intellij/roots/RootsChangedTest.java @@ -31,8 +31,10 @@ import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.pointers.VirtualFilePointerManager; import com.intellij.testFramework.IdeaTestUtil; import com.intellij.testFramework.ModuleTestCase; +import com.intellij.testFramework.PlatformTestUtil; import com.intellij.testFramework.VfsTestUtil; import com.intellij.util.messages.MessageBusConnection; import com.intellij.util.ui.UIUtil; @@ -215,9 +217,7 @@ public class RootsChangedTest extends ModuleTestCase { rootModelA.inheritSdk(); rootModelB.inheritSdk(); ModifiableRootModel[] rootModels = {rootModelA, rootModelB}; - if (rootModels.length > 0) { - ModifiableModelCommitter.multiCommit(rootModels, ModuleManager.getInstance(rootModels[0].getProject()).getModifiableModel()); - } + ModifiableModelCommitter.multiCommit(rootModels, ModuleManager.getInstance(rootModels[0].getProject()).getModifiableModel()); assertEventsCount(1); ProjectRootManager.getInstance(myProject).setProjectSdk(jdk); @@ -249,9 +249,7 @@ public class RootsChangedTest extends ModuleTestCase { rootModelA.addInvalidLibrary("Q", libraryTable.getTableLevel()); rootModelB.addInvalidLibrary("Q", libraryTable.getTableLevel()); ModifiableRootModel[] rootModels = {rootModelA, rootModelB}; - if (rootModels.length > 0) { - ModifiableModelCommitter.multiCommit(rootModels, ModuleManager.getInstance(rootModels[0].getProject()).getModifiableModel()); - } + ModifiableModelCommitter.multiCommit(rootModels, ModuleManager.getInstance(rootModels[0].getProject()).getModifiableModel()); assertEventsCount(1); final Library.ModifiableModel libraryModifiableModel2 = libraryA.getModifiableModel(); @@ -318,9 +316,7 @@ public class RootsChangedTest extends ModuleTestCase { assertEventsCount(0); ModifiableRootModel[] rootModels = {rootModelA, rootModelB}; - if (rootModels.length > 0) { - ModifiableModelCommitter.multiCommit(rootModels, ModuleManager.getInstance(rootModels[0].getProject()).getModifiableModel()); - } + ModifiableModelCommitter.multiCommit(rootModels, ModuleManager.getInstance(rootModels[0].getProject()).getModifiableModel()); assertEventsCount(1); libraryTable.removeLibrary(libraryQ); @@ -355,4 +351,21 @@ public class RootsChangedTest extends ModuleTestCase { afterCount = 0; } } + + public void testRootsChangedPerformanceInPresenceOfManyVirtualFilePointers() throws Exception { + VirtualFile temp = LocalFileSystem.getInstance().findFileByIoFile(createTempDirectory()); + String dirName = "xxx"; + for (int i = 0; i < 10_000; i++) { + VirtualFilePointerManager.getInstance().create(temp.getUrl() + "/" + dirName + "/" + i, getTestRootDisposable(), null); + } + + VirtualFile xxx = createChildDirectory(temp, dirName); + + PlatformTestUtil.startPerformanceTest("time wasted in ProjectRootManagerComponent.before/afterValidityChanged()", 10000, ()->{ + for (int i = 0; i < 100; i++) { + rename(xxx, "yyy"); + rename(xxx, dirName); + } + }).assertTiming(); + } } diff --git a/java/java-tests/testSrc/com/intellij/roots/libraries/LibraryTest.java b/java/java-tests/testSrc/com/intellij/roots/libraries/LibraryTest.java index ff37e7f3734b..d2720efffdb7 100644 --- a/java/java-tests/testSrc/com/intellij/roots/libraries/LibraryTest.java +++ b/java/java-tests/testSrc/com/intellij/roots/libraries/LibraryTest.java @@ -226,22 +226,21 @@ public class LibraryTest extends ModuleRootManagerTestCase { Library library = WriteAction.compute(() -> table.createLibrary("jarDirs")); Library.ModifiableModel model = library.getModifiableModel(); model.addJarDirectory("file://jar-dir", false, OrderRootType.CLASSES); + model.addJarDirectory("file://jar-dir-rec", true, OrderRootType.CLASSES); model.addJarDirectory("file://jar-dir-src", false, OrderRootType.SOURCES); commit(model); assertThat(serialize(library)).isEqualTo("\n" + " \n" + - " \n" + - " \n" + - " \n" + + " \n" + " \n" + - " \n" + - " \n" + - " \n" + + " \n" + " \n" + + " \n" + " \n" + " \n" + - ""); + "" + ); } private static Element serialize(Library library) { diff --git a/platform/core-api/src/com/intellij/openapi/vfs/pointers/VirtualFilePointerContainer.java b/platform/core-api/src/com/intellij/openapi/vfs/pointers/VirtualFilePointerContainer.java index e66de2874b1f..9401a6420de1 100644 --- a/platform/core-api/src/com/intellij/openapi/vfs/pointers/VirtualFilePointerContainer.java +++ b/platform/core-api/src/com/intellij/openapi/vfs/pointers/VirtualFilePointerContainer.java @@ -67,9 +67,9 @@ public interface VirtualFilePointerContainer { * * } */ - void readExternal(@NotNull Element rootChild, @NotNull String childElementName) throws InvalidDataException; + void readExternal(@NotNull Element rootChild, @NotNull String childElementName, boolean externalizeJarDirectories) throws InvalidDataException; - void writeExternal(@NotNull Element element, @NotNull String childElementName); + void writeExternal(@NotNull Element element, @NotNull String childElementName, boolean externalizeJarDirectories); void moveUp(@NotNull String url); diff --git a/platform/core-impl/src/com/intellij/openapi/vfs/impl/VirtualFilePointerContainerImpl.java b/platform/core-impl/src/com/intellij/openapi/vfs/impl/VirtualFilePointerContainerImpl.java index 388cac2dd9a7..c4596787dc67 100644 --- a/platform/core-impl/src/com/intellij/openapi/vfs/impl/VirtualFilePointerContainerImpl.java +++ b/platform/core-impl/src/com/intellij/openapi/vfs/impl/VirtualFilePointerContainerImpl.java @@ -21,10 +21,7 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.impl.ApplicationInfoImpl; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.fileTypes.FileTypeRegistry; -import com.intellij.openapi.util.InvalidDataException; -import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.TraceableDisposable; -import com.intellij.openapi.util.Trinity; +import com.intellij.openapi.util.*; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.StandardFileSystems; import com.intellij.openapi.vfs.VfsUtilCore; @@ -35,6 +32,7 @@ import com.intellij.openapi.vfs.pointers.VirtualFilePointerContainer; import com.intellij.openapi.vfs.pointers.VirtualFilePointerListener; import com.intellij.openapi.vfs.pointers.VirtualFilePointerManager; import com.intellij.util.ArrayUtil; +import com.intellij.util.containers.ConcurrentList; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.io.URLUtil; import org.jdom.Element; @@ -44,24 +42,28 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.List; /** * @author dsl */ -class VirtualFilePointerContainerImpl extends TraceableDisposable implements VirtualFilePointerContainer, Disposable { +public class VirtualFilePointerContainerImpl extends TraceableDisposable implements VirtualFilePointerContainer, Disposable { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vfs.pointers.VirtualFilePointerContainer"); - @NotNull private final List myList = ContainerUtil.createLockFreeCopyOnWriteList(); - @NotNull private final List myJarDirectories = ContainerUtil.createLockFreeCopyOnWriteList(); - @NotNull private final List myJarRecursiveDirectories = ContainerUtil.createLockFreeCopyOnWriteList(); + @NotNull private final ConcurrentList myList = ContainerUtil.createConcurrentList(); + @NotNull private final ConcurrentList myJarDirectories = ContainerUtil.createConcurrentList(); + @NotNull private final ConcurrentList myJarRecursiveDirectories = ContainerUtil.createConcurrentList(); @NotNull private final VirtualFilePointerManager myVirtualFilePointerManager; @NotNull private final Disposable myParent; private final VirtualFilePointerListener myListener; private volatile Trinity myCachedThings; private volatile long myTimeStampOfCachedThings = -1; - @NonNls private static final String URL_ATTR = "url"; + @NonNls public static final String URL_ATTR = "url"; private boolean myDisposed; private static final boolean TRACE_CREATION = LOG.isDebugEnabled() || ApplicationManager.getApplication().isUnitTestMode(); + @NonNls public static final String JAR_DIRECTORY_ELEMENT = "jarDirectory"; + @NonNls public static final String RECURSIVE_ATTR = "recursive"; + VirtualFilePointerContainerImpl(@NotNull VirtualFilePointerManager manager, @NotNull Disposable parentDisposable, @Nullable VirtualFilePointerListener listener) { @@ -72,23 +74,52 @@ class VirtualFilePointerContainerImpl extends TraceableDisposable implements Vir } @Override - public void readExternal(@NotNull final Element rootChild, @NotNull final String childName) throws InvalidDataException { + public void readExternal(@NotNull final Element rootChild, @NotNull final String childName, boolean externalizeJarDirectories) throws InvalidDataException { final List urls = rootChild.getChildren(childName); for (Element url : urls) { final String urlAttribute = url.getAttributeValue(URL_ATTR); if (urlAttribute == null) throw new InvalidDataException("path element without url"); add(urlAttribute); } + if (externalizeJarDirectories) { + List jarDirs = rootChild.getChildren(JAR_DIRECTORY_ELEMENT); + for (Element jarDir : jarDirs) { + String url = jarDir.getAttributeValue(URL_ATTR); + if (url == null) throw new InvalidDataException("path element without url: " + JDOMUtil.getValue(jarDir)); + boolean recursive = Boolean.valueOf(jarDir.getAttributeValue(RECURSIVE_ATTR, "false")); + addJarDirectory(url, recursive); + } + } } @Override - public void writeExternal(@NotNull final Element element, @NotNull final String childElementName) { + public void writeExternal(@NotNull final Element element, @NotNull final String childElementName, boolean externalizeJarDirectories) { for (VirtualFilePointer pointer : myList) { String url = pointer.getUrl(); final Element rootPathElement = new Element(childElementName); rootPathElement.setAttribute(URL_ATTR, url); element.addContent(rootPathElement); } + if (externalizeJarDirectories) { + List jarDirectories = new ArrayList<>(myJarDirectories); + Collections.sort(jarDirectories, Comparator.comparing(VirtualFilePointer::getUrl, String.CASE_INSENSITIVE_ORDER)); + List jarRecursiveDirectories = new ArrayList<>(myJarRecursiveDirectories); + Collections.sort(jarRecursiveDirectories, Comparator.comparing(VirtualFilePointer::getUrl, String.CASE_INSENSITIVE_ORDER)); + for (VirtualFilePointer pointer : jarDirectories) { + String url = pointer.getUrl(); + final Element jarDirElement = new Element(JAR_DIRECTORY_ELEMENT); + jarDirElement.setAttribute(URL_ATTR, url); + jarDirElement.setAttribute(RECURSIVE_ATTR, Boolean.toString(false)); + element.addContent(jarDirElement); + } + for (VirtualFilePointer pointer : jarRecursiveDirectories) { + String url = pointer.getUrl(); + final Element jarDirElement = new Element(JAR_DIRECTORY_ELEMENT); + jarDirElement.setAttribute(URL_ATTR, url); + jarDirElement.setAttribute(RECURSIVE_ATTR, Boolean.toString(true)); + element.addContent(jarDirElement); + } + } } @Override @@ -127,14 +158,14 @@ class VirtualFilePointerContainerImpl extends TraceableDisposable implements Vir public void add(@NotNull VirtualFile file) { assert !myDisposed; dropCaches(); - myList.add(create(file)); + myList.addIfAbsent(create(file)); } @Override public void add(@NotNull String url) { assert !myDisposed; dropCaches(); - myList.add(create(url)); + myList.addIfAbsent(create(url)); } @Override @@ -158,7 +189,7 @@ class VirtualFilePointerContainerImpl extends TraceableDisposable implements Vir dropCaches(); for (final VirtualFilePointer pointer : that.getList()) { - myList.add(duplicate(pointer)); + myList.addIfAbsent(duplicate(pointer)); } for (VirtualFilePointer jarDirectory : ((VirtualFilePointerContainerImpl)that).myJarDirectories) { myJarDirectories.add(duplicate(jarDirectory)); @@ -218,13 +249,14 @@ class VirtualFilePointerContainerImpl extends TraceableDisposable implements Vir for (VirtualFilePointer jarDirectoryPtr : myJarDirectories) { VirtualFile jarDirectory = jarDirectoryPtr.getFile(); if (jarDirectory != null) { + cachedUrls.add(jarDirectory.getUrl()); VirtualFile[] children = jarDirectory.getChildren(); for (VirtualFile file : children) { if (!file.isDirectory() && FileTypeRegistry.getInstance().getFileTypeByFileName(file.getName()) == ArchiveFileType.INSTANCE) { VirtualFile jarRoot = StandardFileSystems.jar().findFileByPath(file.getPath() + URLUtil.JAR_SEPARATOR); if (jarRoot != null) { cachedFiles.add(jarRoot); - cachedDirectories.add(file); + cachedDirectories.add(jarRoot); } } } @@ -233,6 +265,7 @@ class VirtualFilePointerContainerImpl extends TraceableDisposable implements Vir for (VirtualFilePointer jarDirectoryPtr : myJarRecursiveDirectories) { VirtualFile jarDirectory = jarDirectoryPtr.getFile(); if (jarDirectory != null) { + cachedUrls.add(jarDirectory.getUrl()); VfsUtilCore.visitChildrenRecursively(jarDirectory, new VirtualFileVisitor() { @Override public boolean visitFile(@NotNull VirtualFile file) { @@ -240,7 +273,7 @@ class VirtualFilePointerContainerImpl extends TraceableDisposable implements Vir VirtualFile jarRoot = StandardFileSystems.jar().findFileByPath(file.getPath() + URLUtil.JAR_SEPARATOR); if (jarRoot != null) { cachedFiles.add(jarRoot); - cachedDirectories.add(file); + cachedDirectories.add(jarRoot); return false; } } @@ -365,16 +398,16 @@ class VirtualFilePointerContainerImpl extends TraceableDisposable implements Vir @Override public void addJarDirectory(@NotNull String directoryUrl, boolean recursively) { VirtualFilePointer pointer = myVirtualFilePointerManager.createDirectoryPointer(directoryUrl, recursively, myParent, myListener); - (recursively ? myJarRecursiveDirectories : myJarDirectories).add(pointer); + (recursively ? myJarRecursiveDirectories : myJarDirectories).addIfAbsent(pointer); dropCaches(); } @Override public boolean removeJarDirectory(@NotNull String directoryUrl) { dropCaches(); - //noinspection NonShortCircuitBooleanExpression - return myJarDirectories.removeIf(ptr-> FileUtil.pathsEqual(ptr.getUrl(), directoryUrl)) - | myJarRecursiveDirectories.removeIf(ptr-> FileUtil.pathsEqual(ptr.getUrl(), directoryUrl)); + boolean removed1 = myJarDirectories.removeIf(ptr -> FileUtil.pathsEqual(ptr.getUrl(), directoryUrl)); + boolean removed2 = myJarRecursiveDirectories.removeIf(ptr -> FileUtil.pathsEqual(ptr.getUrl(), directoryUrl)); + return removed1 || removed2; } @NotNull diff --git a/platform/lang-impl/src/com/intellij/openapi/projectRoots/impl/ProjectJdkTableImpl.java b/platform/lang-impl/src/com/intellij/openapi/projectRoots/impl/ProjectJdkTableImpl.java index aa617981fd9f..7030a467e5fd 100644 --- a/platform/lang-impl/src/com/intellij/openapi/projectRoots/impl/ProjectJdkTableImpl.java +++ b/platform/lang-impl/src/com/intellij/openapi/projectRoots/impl/ProjectJdkTableImpl.java @@ -15,6 +15,7 @@ */ package com.intellij.openapi.projectRoots.impl; +import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.PathManager; import com.intellij.openapi.components.*; @@ -23,6 +24,7 @@ import com.intellij.openapi.fileTypes.FileTypes; import com.intellij.openapi.project.ProjectBundle; import com.intellij.openapi.projectRoots.*; import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VfsUtil; @@ -206,6 +208,9 @@ public class ProjectJdkTableImpl extends ProjectJdkTable implements ExportableCo ApplicationManager.getApplication().assertWriteAccessAllowed(); myMessageBus.syncPublisher(JDK_TABLE_TOPIC).jdkRemoved(jdk); mySdks.remove(jdk); + if (jdk instanceof Disposable) { + Disposer.dispose((Disposable)jdk); + } } @Override diff --git a/platform/lang-impl/src/com/intellij/openapi/roots/impl/ProjectRootManagerComponent.java b/platform/lang-impl/src/com/intellij/openapi/roots/impl/ProjectRootManagerComponent.java index 782583ae00a4..cebc2f5c02d2 100644 --- a/platform/lang-impl/src/com/intellij/openapi/roots/impl/ProjectRootManagerComponent.java +++ b/platform/lang-impl/src/com/intellij/openapi/roots/impl/ProjectRootManagerComponent.java @@ -37,16 +37,16 @@ import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.roots.OrderRootType; import com.intellij.openapi.roots.WatchedRootsProvider; import com.intellij.openapi.startup.StartupManager; +import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.vfs.LocalFileSystem; -import com.intellij.openapi.vfs.StandardFileSystems; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileManager; +import com.intellij.openapi.vfs.*; import com.intellij.openapi.vfs.ex.VirtualFileManagerAdapter; import com.intellij.openapi.vfs.newvfs.NewVirtualFile; import com.intellij.openapi.vfs.pointers.VirtualFilePointer; +import com.intellij.openapi.vfs.pointers.VirtualFilePointerContainer; import com.intellij.openapi.vfs.pointers.VirtualFilePointerListener; +import com.intellij.openapi.vfs.pointers.VirtualFilePointerManager; import com.intellij.project.ProjectKt; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.indexing.FileBasedIndex; @@ -74,6 +74,7 @@ public class ProjectRootManagerComponent extends ProjectRootManagerImpl implemen private Set myRootsToWatch = new THashSet<>(); private final boolean myDoLogCachesUpdate; + private Disposable myRootPointersDisposable = Disposer.newDisposable(); // accessed in EDT public ProjectRootManagerComponent(Project project, StartupManager startupManager) { super(project); @@ -116,7 +117,6 @@ public class ProjectRootManagerComponent extends ProjectRootManagerImpl implemen } }; - myConnection.subscribe(VirtualFilePointerListener.TOPIC, new MyVirtualFilePointerListener()); myDoLogCachesUpdate = ApplicationManager.getApplication().isInternal() && !ApplicationManager.getApplication().isUnitTestMode(); } @@ -138,7 +138,7 @@ public class ProjectRootManagerComponent extends ProjectRootManagerImpl implemen @Override protected void addRootsToWatch() { - final Pair, Set> roots = getAllRoots(false); + final Pair, Set> roots = getAllRoots(); if (roots == null) return; myRootsToWatch = LocalFileSystem.getInstance().replaceWatchedRoots(myRootsToWatch, roots.first, roots.second); } @@ -168,18 +168,6 @@ public class ProjectRootManagerComponent extends ProjectRootManagerImpl implemen } } - private boolean affectsRoots(@NotNull VirtualFilePointer[] pointers) { - Pair, Set> roots = getAllRoots(true); - if (roots == null) return false; - - for (VirtualFilePointer pointer : pointers) { - String path = extractLocalPath(pointer.getUrl()); - if (roots.first.contains(path) || roots.second.contains(path)) return true; - } - - return false; - } - @Override protected void fireBeforeRootsChangeEvent(boolean fileTypes) { isFiringEvent = true; @@ -207,7 +195,8 @@ public class ProjectRootManagerComponent extends ProjectRootManagerImpl implemen } @Nullable - private Pair, Set> getAllRoots(boolean includeSourceRoots) { + private Pair, Set> getAllRoots() { + ApplicationManager.getApplication().assertIsDispatchThread(); if (myProject.isDefault()) return null; final Set recursive = new THashSet<>(FileUtil.PATH_HASHING_STRATEGY); @@ -232,12 +221,22 @@ public class ProjectRootManagerComponent extends ProjectRootManagerImpl implemen recursive.addAll(extension.getRootsToWatch()); } - addRootsFromModules(includeSourceRoots, recursive, flat); + Disposable oldDisposable = myRootPointersDisposable; + myRootPointersDisposable = Disposer.newDisposable(); + Disposer.register(this, myRootPointersDisposable); + // create container with these urls with the sole purpose to get events to getRootsValidityChangedListener() when these roots change + VirtualFilePointerContainer container = VirtualFilePointerManager.getInstance().createContainer(myRootPointersDisposable, getRootsValidityChangedListener()); + recursive.forEach(path -> container.addJarDirectory(VfsUtilCore.pathToUrl(path), true)); + flat.forEach(path -> container.add(VfsUtilCore.pathToUrl(path))); + Disposer.dispose(oldDisposable); // dispose after the re-creating container to keep virtual file pointers from disposing and re-creating back + + // module roots already fire validity change events + addRootsFromModulesTo(recursive, flat); return Pair.create(recursive, flat); } - private void addRootsFromModules(boolean includeSourceRoots, Set recursive, Set flat) { + private void addRootsFromModulesTo(@NotNull Set recursive, @NotNull Set flat) { Set urls = ContainerUtil.newTroveSet(FileUtil.PATH_HASHING_STRATEGY); for (Module module : ModuleManager.getInstance(myProject).getModules()) { @@ -245,10 +244,6 @@ public class ProjectRootManagerComponent extends ProjectRootManagerImpl implemen ContainerUtil.addAll(urls, rootManager.getContentRootUrls()); - if (includeSourceRoots) { - ContainerUtil.addAll(urls, rootManager.getSourceRootUrls()); - } - rootManager.orderEntries().withoutModuleSourceEntries().withoutDepModules().forEach(entry -> { for (OrderRootType type : OrderRootType.getAllTypes()) { ContainerUtil.addAll(urls, entry.getUrls(type)); @@ -302,7 +297,7 @@ public class ProjectRootManagerComponent extends ProjectRootManagerImpl implemen @Override public void markRootsForRefresh() { Set paths = ContainerUtil.newTroveSet(FileUtil.PATH_HASHING_STRATEGY); - addRootsFromModules(false, paths, paths); + addRootsFromModulesTo(paths, paths); LocalFileSystem fs = LocalFileSystem.getInstance(); for (String path : paths) { @@ -339,7 +334,7 @@ public class ProjectRootManagerComponent extends ProjectRootManagerImpl implemen } } - private class MyVirtualFilePointerListener implements VirtualFilePointerListener { + private final VirtualFilePointerListener myRootsChangedListener = new VirtualFilePointerListener() { @Override public void beforeValidityChanged(@NotNull VirtualFilePointer[] pointers) { if (myProject.isDisposed()) { @@ -347,18 +342,14 @@ public class ProjectRootManagerComponent extends ProjectRootManagerImpl implemen } if (myInsideRefresh == 0) { - if (affectsRoots(pointers)) { - beforeRootsChange(false); - if (myDoLogCachesUpdate) LOG.debug(new Throwable(pointers.length > 0 ? pointers[0].getPresentableUrl():"")); - } + beforeRootsChange(false); + if (myDoLogCachesUpdate) LOG.debug(new Throwable(pointers.length > 0 ? pointers[0].getPresentableUrl():"")); } else if (!myPointerChangesDetected) { //this is the first pointer changing validity - if (affectsRoots(pointers)) { - myPointerChangesDetected = true; - myProject.getMessageBus().syncPublisher(ProjectTopics.PROJECT_ROOTS).beforeRootsChange(new ModuleRootEventImpl(myProject, false)); - if (myDoLogCachesUpdate) LOG.debug(new Throwable(pointers.length > 0 ? pointers[0].getPresentableUrl():"")); - } + myPointerChangesDetected = true; + myProject.getMessageBus().syncPublisher(ProjectTopics.PROJECT_ROOTS).beforeRootsChange(new ModuleRootEventImpl(myProject, false)); + if (myDoLogCachesUpdate) LOG.debug(new Throwable(pointers.length > 0 ? pointers[0].getPresentableUrl():"")); } } @@ -371,9 +362,15 @@ public class ProjectRootManagerComponent extends ProjectRootManagerImpl implemen if (myInsideRefresh > 0) { clearScopesCaches(); } - else if (affectsRoots(pointers)) { + else { rootsChanged(false); } } + }; + + @NotNull + @Override + public VirtualFilePointerListener getRootsValidityChangedListener() { + return myRootsChangedListener; } } diff --git a/platform/lang-impl/src/com/intellij/openapi/roots/impl/libraries/JarDirectoryWatcherFactoryImpl.java b/platform/lang-impl/src/com/intellij/openapi/roots/impl/libraries/JarDirectoryWatcherFactoryImpl.java deleted file mode 100644 index 8df3683ec528..000000000000 --- a/platform/lang-impl/src/com/intellij/openapi/roots/impl/libraries/JarDirectoryWatcherFactoryImpl.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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.roots.impl.libraries; - -import com.intellij.openapi.roots.impl.RootProviderBaseImpl; - -/** - * @author yole - */ -public class JarDirectoryWatcherFactoryImpl extends JarDirectoryWatcherFactory { - @Override - public JarDirectoryWatcher createWatcher(JarDirectories jarDirectories, RootProviderBaseImpl rootProvider) { - return new JarDirectoryWatcherImpl(jarDirectories, rootProvider); - } -} diff --git a/platform/lang-impl/src/com/intellij/openapi/roots/impl/libraries/JarDirectoryWatcherImpl.java b/platform/lang-impl/src/com/intellij/openapi/roots/impl/libraries/JarDirectoryWatcherImpl.java deleted file mode 100644 index 9f00c2bd22ea..000000000000 --- a/platform/lang-impl/src/com/intellij/openapi/roots/impl/libraries/JarDirectoryWatcherImpl.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2000-2017 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.roots.impl.libraries; - -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.roots.OrderRootType; -import com.intellij.openapi.roots.impl.RootProviderBaseImpl; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.vfs.LocalFileSystem; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileManager; -import com.intellij.openapi.vfs.newvfs.BulkFileListener; -import com.intellij.openapi.vfs.newvfs.events.*; -import com.intellij.util.messages.MessageBusConnection; -import org.jetbrains.annotations.NotNull; - -import java.util.*; - -/** - * @author ksafonov - */ -public class JarDirectoryWatcherImpl implements JarDirectoryWatcher { - private final JarDirectories myJarDirectories; - private final RootProviderBaseImpl myRootProvider; - private MessageBusConnection myBusConnection = null; - private Collection myWatchRequests = Collections.emptySet(); - - public JarDirectoryWatcherImpl(JarDirectories jarDirectories, RootProviderBaseImpl rootProvider) { - myJarDirectories = jarDirectories; - myRootProvider = rootProvider; - } - - @Override - public void updateWatchedRoots() { - final LocalFileSystem fs = LocalFileSystem.getInstance(); - if (!myJarDirectories.isEmpty()) { - final Set recursiveRoots = new HashSet<>(); - final Set flatRoots = new HashSet<>(); - final VirtualFileManager fm = VirtualFileManager.getInstance(); - for (OrderRootType rootType : myJarDirectories.getRootTypes()) { - for (String url : myJarDirectories.getDirectories(rootType)) { - if (fm.getFileSystem(VirtualFileManager.extractProtocol(url)) instanceof LocalFileSystem) { - final boolean watchRecursively = myJarDirectories.isRecursive(rootType, url); - final String path = VirtualFileManager.extractPath(url); - (watchRecursively ? recursiveRoots : flatRoots).add(path); - } - } - } - - myWatchRequests = fs.replaceWatchedRoots(myWatchRequests, recursiveRoots, flatRoots); - - if (myBusConnection == null) { - myBusConnection = ApplicationManager.getApplication().getMessageBus().connect(); - myBusConnection.subscribe(VirtualFileManager.VFS_CHANGES, new BulkFileListener() { - @Override - public void after(@NotNull final List events) { - boolean changesDetected = false; - for (VFileEvent event : events) { - if (event instanceof VFileCopyEvent) { - final VFileCopyEvent copyEvent = (VFileCopyEvent)event; - final VirtualFile file = copyEvent.getFile(); - if (isUnderJarDirectory(copyEvent.getNewParent().getUrl() + "/" + copyEvent.getNewChildName()) || - isUnderJarDirectory(file.getUrl())) { - changesDetected = true; - break; - } - } - else if (event instanceof VFileMoveEvent) { - final VFileMoveEvent moveEvent = (VFileMoveEvent)event; - final VirtualFile file = moveEvent.getFile(); - if (isUnderJarDirectory(file.getUrl()) || isUnderJarDirectory(moveEvent.getOldParent().getUrl() + "/" + file.getName())) { - changesDetected = true; - break; - } - } - else if (event instanceof VFileDeleteEvent) { - final VFileDeleteEvent deleteEvent = (VFileDeleteEvent)event; - if (isUnderJarDirectory(deleteEvent.getFile().getUrl())) { - changesDetected = true; - break; - } - } - else if (event instanceof VFileCreateEvent) { - final VFileCreateEvent createEvent = (VFileCreateEvent)event; - if (isUnderJarDirectory(createEvent.getParent().getUrl() + "/" + createEvent.getChildName())) { - changesDetected = true; - break; - } - } - } - - if (changesDetected) { - fireRootSetChanged(); - } - } - - private boolean isUnderJarDirectory(String url) { - for (String rootUrl : myJarDirectories.getAllDirectories()) { - if (FileUtil.startsWith(url, rootUrl)) { - return true; - } - } - return false; - } - }); - } - } - else { - cleanup(); - } - } - - protected void fireRootSetChanged() { - myRootProvider.fireRootSetChanged(); - } - - @Override - public void dispose() { - cleanup(); - } - - private void cleanup() { - if (!myWatchRequests.isEmpty()) { - LocalFileSystem.getInstance().removeWatchedRoots(myWatchRequests); - myWatchRequests = Collections.emptySet(); - } - - final MessageBusConnection connection = myBusConnection; - if (connection != null) { - myBusConnection = null; - connection.disconnect(); - } - } -} diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/FilePointerPartNode.java b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/FilePointerPartNode.java index 4bf453ee96a5..163d41b202c4 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/FilePointerPartNode.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/FilePointerPartNode.java @@ -73,7 +73,7 @@ class FilePointerPartNode { boolean separator, @NotNull CharSequence childName, int childStart, int childEnd, @NotNull FilePointerPartNode[] outNode, - @NotNull List outDirs) { + @Nullable List outDirs) { int partStart; if (parent == null) { partStart = 0; @@ -110,7 +110,8 @@ class FilePointerPartNode { if (partStart + index-childStart == found.part.length()) { // go to children for (FilePointerPartNode child : found.children) { - int childPos = child.position(null, null, childSeparator, childName, index, childEnd, outNode, outDirs); + // do not accidentally modify outDirs + int childPos = child.position(null, null, childSeparator, childName, index, childEnd, outNode, null); if (childPos != -1) { addRecursiveDirectoryPtr(outDirs); @@ -122,8 +123,8 @@ class FilePointerPartNode { return -1; } - private void addRecursiveDirectoryPtr(@NotNull List dirs) { - if(hasRecursiveDirectoryPointer() && (dirs.isEmpty() || dirs.get(dirs.size()-1) != this)) { + private void addRecursiveDirectoryPtr(@Nullable List dirs) { + if(dirs != null && hasRecursiveDirectoryPointer() && (dirs.isEmpty() || dirs.get(dirs.size()-1) != this)) { dirs.add(this); } } diff --git a/platform/platform-resources/src/META-INF/LangExtensions.xml b/platform/platform-resources/src/META-INF/LangExtensions.xml index 2901e6ffde61..367768b4aa5e 100644 --- a/platform/platform-resources/src/META-INF/LangExtensions.xml +++ b/platform/platform-resources/src/META-INF/LangExtensions.xml @@ -188,9 +188,6 @@ - - diff --git a/platform/projectModel-impl/src/com/intellij/openapi/projectRoots/ex/ProjectRoot.java b/platform/projectModel-impl/src/com/intellij/openapi/projectRoots/ex/ProjectRoot.java deleted file mode 100644 index 4ac61761c9c7..000000000000 --- a/platform/projectModel-impl/src/com/intellij/openapi/projectRoots/ex/ProjectRoot.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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.projectRoots.ex; - -import com.intellij.openapi.vfs.VirtualFile; -import org.jetbrains.annotations.NotNull; - -/** - * @author mike - */ -public interface ProjectRoot { - boolean isValid(); - @NotNull - VirtualFile[] getVirtualFiles(); - - @NotNull - String[] getUrls(); - - @NotNull - String getPresentableString(); - - void update(); -} diff --git a/platform/projectModel-impl/src/com/intellij/openapi/projectRoots/ex/ProjectRootContainer.java b/platform/projectModel-impl/src/com/intellij/openapi/projectRoots/ex/ProjectRootContainer.java deleted file mode 100644 index 07c7b2c1fa85..000000000000 --- a/platform/projectModel-impl/src/com/intellij/openapi/projectRoots/ex/ProjectRootContainer.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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.projectRoots.ex; - -import com.intellij.openapi.roots.OrderRootType; -import com.intellij.openapi.vfs.VirtualFile; -import org.jetbrains.annotations.NotNull; - -public interface ProjectRootContainer { - @NotNull - VirtualFile[] getRootFiles(@NotNull OrderRootType type); - @NotNull ProjectRoot[] getRoots(@NotNull OrderRootType type); - - // must execute modifications inside this method only - void changeRoots(@NotNull Runnable change); - - @NotNull - ProjectRoot addRoot(@NotNull VirtualFile virtualFile, @NotNull OrderRootType type); - void addRoot(@NotNull ProjectRoot root, @NotNull OrderRootType type); - void removeRoot(@NotNull ProjectRoot root, @NotNull OrderRootType type); - void removeAllRoots(@NotNull OrderRootType type); - - void removeAllRoots(); - - void removeRoot(@NotNull VirtualFile root, @NotNull OrderRootType type); - - void update(); -} diff --git a/platform/projectModel-impl/src/com/intellij/openapi/projectRoots/impl/CompositeProjectRoot.java b/platform/projectModel-impl/src/com/intellij/openapi/projectRoots/impl/CompositeProjectRoot.java deleted file mode 100644 index 082da1b0ed04..000000000000 --- a/platform/projectModel-impl/src/com/intellij/openapi/projectRoots/impl/CompositeProjectRoot.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright 2000-2015 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.projectRoots.impl; - -import com.intellij.openapi.projectRoots.ex.ProjectRoot; -import com.intellij.openapi.vfs.VfsUtilCore; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.ArrayUtil; -import com.intellij.util.containers.ContainerUtil; -import org.jdom.Element; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -/** - * @author mike - */ -class CompositeProjectRoot implements ProjectRoot { - @NonNls private static final String SIMPLE_ROOT = "simple"; - @NonNls private static final String COMPOSITE_ROOT = "composite"; - @NonNls private static final String ATTRIBUTE_TYPE = "type"; - @NonNls private static final String ELEMENT_ROOT = "root"; - private final List myRoots = new ArrayList<>(); - - @NotNull - ProjectRoot[] getProjectRoots() { - return myRoots.toArray(new ProjectRoot[myRoots.size()]); - } - - @Override - @NotNull - public String getPresentableString() { - throw new UnsupportedOperationException(); - } - - @Override - @NotNull - public VirtualFile[] getVirtualFiles() { - List result = new ArrayList<>(); - for (ProjectRoot root : myRoots) { - ContainerUtil.addAll(result, root.getVirtualFiles()); - } - - return VfsUtilCore.toVirtualFileArray(result); - } - - @Override - @NotNull - public String[] getUrls() { - final List result = new ArrayList<>(); - for (ProjectRoot root : myRoots) { - ContainerUtil.addAll(result, root.getUrls()); - } - return ArrayUtil.toStringArray(result); - } - - @Override - public boolean isValid() { - return true; - } - - void remove(@NotNull ProjectRoot root) { - myRoots.remove(root); - } - - @NotNull - ProjectRoot add(@NotNull VirtualFile virtualFile) { - final SimpleProjectRoot root = new SimpleProjectRoot(virtualFile); - myRoots.add(root); - return root; - } - - void add(@NotNull ProjectRoot root) { - myRoots.add(root); - } - - void remove(@NotNull VirtualFile root) { - for (Iterator iterator = myRoots.iterator(); iterator.hasNext();) { - ProjectRoot projectRoot = iterator.next(); - if (projectRoot instanceof SimpleProjectRoot) { - SimpleProjectRoot r = (SimpleProjectRoot)projectRoot; - if (root.equals(r.getFile())) { - iterator.remove(); - } - } - } - } - - void clear() { - myRoots.clear(); - } - - public void readExternal(Element element) { - for (Element child : element.getChildren()) { - myRoots.add(read(child)); - } - } - - public void writeExternal(Element element) { - for (ProjectRoot root : myRoots) { - Element e = write(root); - element.addContent(e); - } - } - - @Override - public void update() { - for (ProjectRoot root : myRoots) { - root.update(); - } - } - - @NotNull - static ProjectRoot read(Element element) { - final String type = element.getAttributeValue(ATTRIBUTE_TYPE); - - if (type.equals(SIMPLE_ROOT)) { - return new SimpleProjectRoot(element); - } - if (type.equals(COMPOSITE_ROOT)) { - CompositeProjectRoot root = new CompositeProjectRoot(); - root.readExternal(element); - return root; - } - throw new IllegalArgumentException("Wrong type: " + type); - } - - @NotNull - static Element write(ProjectRoot projectRoot) { - Element element = new Element(ELEMENT_ROOT); - if (projectRoot instanceof SimpleProjectRoot) { - element.setAttribute(ATTRIBUTE_TYPE, SIMPLE_ROOT); - ((SimpleProjectRoot)projectRoot).writeExternal(element); - } - else if (projectRoot instanceof CompositeProjectRoot) { - element.setAttribute(ATTRIBUTE_TYPE, COMPOSITE_ROOT); - ((CompositeProjectRoot)projectRoot).writeExternal(element); - } - else { - throw new IllegalArgumentException("Wrong root: " + projectRoot); - } - - return element; - } -} diff --git a/platform/projectModel-impl/src/com/intellij/openapi/projectRoots/impl/ProjectJdkImpl.java b/platform/projectModel-impl/src/com/intellij/openapi/projectRoots/impl/ProjectJdkImpl.java index 490cc1bb7cc5..7a112f073d1b 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/projectRoots/impl/ProjectJdkImpl.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/projectRoots/impl/ProjectJdkImpl.java @@ -18,35 +18,36 @@ package com.intellij.openapi.projectRoots.impl; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.projectRoots.*; -import com.intellij.openapi.projectRoots.ex.ProjectRoot; import com.intellij.openapi.roots.OrderRootType; +import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.roots.RootProvider; +import com.intellij.openapi.roots.impl.ProjectRootManagerImpl; import com.intellij.openapi.roots.impl.RootProviderBaseImpl; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.UserDataHolderBase; import com.intellij.openapi.vfs.StandardFileSystems; -import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.ArrayUtil; +import com.intellij.openapi.vfs.pointers.VirtualFilePointer; +import com.intellij.openapi.vfs.pointers.VirtualFilePointerListener; import com.intellij.util.containers.ContainerUtil; import org.jdom.Element; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; import java.util.List; -public class ProjectJdkImpl extends UserDataHolderBase implements Sdk, SdkModificator { +public class ProjectJdkImpl extends UserDataHolderBase implements Sdk, SdkModificator, Disposable { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.projectRoots.impl.ProjectJdkImpl"); - private final ProjectRootContainerImpl myRootContainer; private String myName; private String myVersionString; private boolean myVersionDefined; private String myHomePath = ""; - private final MyRootProvider myRootProvider = new MyRootProvider(); + private final RootsAsVirtualFilePointers myRoots; private ProjectJdkImpl myOrigin; private SdkAdditionalData myAdditionalData; private SdkTypeId mySdkType; @@ -57,12 +58,37 @@ public class ProjectJdkImpl extends UserDataHolderBase implements Sdk, SdkModifi @NonNls private static final String ELEMENT_ROOTS = "roots"; @NonNls private static final String ELEMENT_HOMEPATH = "homePath"; @NonNls private static final String ELEMENT_ADDITIONAL = "additional"; + private final MyRootProvider myRootProvider = new MyRootProvider(); public ProjectJdkImpl(String name, SdkTypeId sdkType) { mySdkType = sdkType; - myRootContainer = new ProjectRootContainerImpl(true); myName = name; - myRootContainer.addProjectRootContainerListener(myRootProvider); + + VirtualFilePointerListener listener = new VirtualFilePointerListener() { + @Override + public void beforeValidityChanged(@NotNull VirtualFilePointer[] pointers) { + //todo check if this sdk is really used in the project + for (Project project : ProjectManager.getInstance().getOpenProjects()) { + VirtualFilePointerListener listener = ((ProjectRootManagerImpl)ProjectRootManager.getInstance(project)).getRootsValidityChangedListener(); + listener.beforeValidityChanged(pointers); + } + } + + @Override + public void validityChanged(@NotNull VirtualFilePointer[] pointers) { + //todo check if this sdk is really used in the project + for (Project project : ProjectManager.getInstance().getOpenProjects()) { + VirtualFilePointerListener listener = ((ProjectRootManagerImpl)ProjectRootManager.getInstance(project)).getRootsValidityChangedListener(); + listener.validityChanged(pointers); + } + } + }; + myRoots = new RootsAsVirtualFilePointers(true, listener, this); + Disposer.register(ApplicationManager.getApplication(), this); + } + + @Override + public void dispose() { } public ProjectJdkImpl(String name, SdkTypeId sdkType, String homePath, String version) { @@ -151,14 +177,12 @@ public class ProjectJdkImpl extends UserDataHolderBase implements Sdk, SdkModifi myVersionDefined = false; } - myRootContainer.changeRoots(() -> { - String versionValue = element.getAttributeValue(ELEMENT_VERSION, ""); - if (versionValue.isEmpty() || !"2".equals(versionValue)) { - throw new InvalidDataException("Too old version is not supported: " + versionValue); - } - myHomePath = element.getChild(ELEMENT_HOMEPATH).getAttributeValue(ATTRIBUTE_VALUE); - myRootContainer.readExternal(element.getChild(ELEMENT_ROOTS)); - }); + String versionValue = element.getAttributeValue(ELEMENT_VERSION, ""); + if (versionValue.isEmpty() || !"2".equals(versionValue)) { + throw new InvalidDataException("Too old version is not supported: " + versionValue); + } + myHomePath = element.getChild(ELEMENT_HOMEPATH).getAttributeValue(ATTRIBUTE_VALUE); + myRoots.readExternal(element.getChild(ELEMENT_ROOTS)); final Element additional = element.getChild(ELEMENT_ADDITIONAL); if (additional != null) { @@ -170,7 +194,7 @@ public class ProjectJdkImpl extends UserDataHolderBase implements Sdk, SdkModifi } } - public void writeExternal(Element element) { + public void writeExternal(@NotNull Element element) { element.setAttribute(ELEMENT_VERSION, "2"); final Element name = new Element(ELEMENT_NAME); @@ -194,7 +218,7 @@ public class ProjectJdkImpl extends UserDataHolderBase implements Sdk, SdkModifi element.addContent(home); Element roots = new Element(ELEMENT_ROOTS); - myRootContainer.writeExternal(roots); + myRoots.writeExternal(roots); element.addContent(roots); Element additional = new Element(ELEMENT_ADDITIONAL); @@ -236,29 +260,25 @@ public class ProjectJdkImpl extends UserDataHolderBase implements Sdk, SdkModifi dest.myVersionDefined = myVersionDefined; dest.myVersionString = myVersionString; dest.setSdkAdditionalData(getSdkAdditionalData()); - dest.copyRootsFrom(myRootContainer); + dest.copyRootsFrom(myRoots); + dest.myRootProvider.rootsChanged(); } - void copyRootsFrom(@NotNull ProjectRootContainerImpl rootContainer) { - myRootContainer.copyRootsFrom(rootContainer); + private void copyRootsFrom(@NotNull RootProvider rootContainer) { + myRoots.copyRootsFrom(rootContainer); } private class MyRootProvider extends RootProviderBaseImpl implements ProjectRootListener { @Override @NotNull public String[] getUrls(@NotNull OrderRootType rootType) { - final ProjectRoot[] rootFiles = myRootContainer.getRoots(rootType); - final ArrayList result = new ArrayList<>(); - for (ProjectRoot rootFile : rootFiles) { - ContainerUtil.addAll(result, rootFile.getUrls()); - } - return ArrayUtil.toStringArray(result); + return myRoots.getUrls(rootType); } @Override @NotNull public VirtualFile[] getFiles(@NotNull final OrderRootType rootType) { - return myRootContainer.getRootFiles(rootType); + return myRoots.getFiles(rootType); } private final List myListeners = ContainerUtil.createLockFreeCopyOnWriteList(); @@ -298,8 +318,6 @@ public class ProjectJdkImpl extends UserDataHolderBase implements Sdk, SdkModifi public SdkModificator getSdkModificator() { ProjectJdkImpl sdk = clone(); sdk.myOrigin = this; - sdk.myRootContainer.startChange(); - sdk.update(); return sdk; } @@ -309,6 +327,7 @@ public class ProjectJdkImpl extends UserDataHolderBase implements Sdk, SdkModifi copyTo(myOrigin); myOrigin = null; + Disposer.dispose(this); } @Override @@ -323,33 +342,28 @@ public class ProjectJdkImpl extends UserDataHolderBase implements Sdk, SdkModifi @NotNull @Override - public VirtualFile[] getRoots(OrderRootType rootType) { - final ProjectRoot[] roots = myRootContainer.getRoots(rootType); // use getRoots() cause the data is most up-to-date there - final List files = new ArrayList<>(roots.length); - for (ProjectRoot root : roots) { - ContainerUtil.addAll(files, root.getVirtualFiles()); - } - return VfsUtilCore.toVirtualFileArray(files); + public VirtualFile[] getRoots(@NotNull OrderRootType rootType) { + return myRoots.getFiles(rootType); } @Override public void addRoot(@NotNull VirtualFile root, @NotNull OrderRootType rootType) { - myRootContainer.addRoot(root, rootType); + myRoots.addRoot(root, rootType); } @Override public void removeRoot(@NotNull VirtualFile root, @NotNull OrderRootType rootType) { - myRootContainer.removeRoot(root, rootType); + myRoots.removeRoot(root, rootType); } @Override public void removeRoots(@NotNull OrderRootType rootType) { - myRootContainer.removeAllRoots(rootType); + myRoots.removeAllRoots(rootType); } @Override public void removeAllRoots() { - myRootContainer.removeAllRoots(); + myRoots.removeAllRoots(); } @Override @@ -357,15 +371,6 @@ public class ProjectJdkImpl extends UserDataHolderBase implements Sdk, SdkModifi return myOrigin != null; } - public void update() { - try { - myRootContainer.update(); - } - finally { - resetVersionString(); - } - } - @Override public String toString() { return myName + (myVersionDefined ? ": " + myVersionString : "") + " (" + myHomePath + ")"; diff --git a/platform/projectModel-impl/src/com/intellij/openapi/projectRoots/impl/ProjectRootContainerImpl.java b/platform/projectModel-impl/src/com/intellij/openapi/projectRoots/impl/ProjectRootContainerImpl.java deleted file mode 100644 index 30c7c47cf003..000000000000 --- a/platform/projectModel-impl/src/com/intellij/openapi/projectRoots/impl/ProjectRootContainerImpl.java +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright 2000-2015 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.projectRoots.impl; - -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.projectRoots.ProjectRootListener; -import com.intellij.openapi.projectRoots.ex.ProjectRoot; -import com.intellij.openapi.projectRoots.ex.ProjectRootContainer; -import com.intellij.openapi.roots.OrderRootType; -import com.intellij.openapi.roots.PersistentOrderRootType; -import com.intellij.openapi.util.Comparing; -import com.intellij.openapi.util.JDOMExternalizable; -import com.intellij.openapi.vfs.*; -import com.intellij.util.ObjectUtils; -import com.intellij.util.containers.ContainerUtil; -import gnu.trove.THashMap; -import org.jdom.Element; -import org.jetbrains.annotations.NotNull; - -import java.util.List; -import java.util.Map; - -/** - * @author mike - */ -public class ProjectRootContainerImpl implements JDOMExternalizable, ProjectRootContainer { - private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.projectRoots.impl.ProjectRootContainerImpl"); - private final Map myRoots = new THashMap<>(); - private final Map myCachedFiles = new THashMap<>(); - - private boolean myInsideChange; - private final List myListeners = ContainerUtil.createLockFreeCopyOnWriteList(); - - private final boolean myNoCopyJars; - - ProjectRootContainerImpl(boolean noCopyJars) { - myNoCopyJars = noCopyJars; - - for (OrderRootType rootType : OrderRootType.getAllTypes()) { - myRoots.put(rootType, new CompositeProjectRoot()); - myCachedFiles.put(rootType, VirtualFile.EMPTY_ARRAY); - } - } - - @Override - @NotNull - public VirtualFile[] getRootFiles(@NotNull OrderRootType type) { - return ObjectUtils.chooseNotNull(myCachedFiles.get(type), VirtualFile.EMPTY_ARRAY); - } - - @Override - @NotNull - public ProjectRoot[] getRoots(@NotNull OrderRootType type) { - return myRoots.get(type).getProjectRoots(); - } - - void startChange() { - myInsideChange = true; // argh!! has to have this abomination just because of horrible Sdk.getSdkModificator()/commitChanges() are separated - } - - private void assertNotInsideChange() { - if (myInsideChange) throw new IllegalStateException(); - } - private void assertInsideChange() { - if (!myInsideChange) throw new IllegalStateException(); - } - - @Override - public void changeRoots(@NotNull Runnable change) { - assertNotInsideChange(); - myInsideChange = true; - Map oldRoots = new THashMap<>(myCachedFiles); - - try { - change.run(); - } - finally { - myInsideChange = false; - - if (cacheFiles(oldRoots)) { - fireRootsChanged(); - } - } - } - - - private boolean cacheFiles(@NotNull Map oldRoots) { - myCachedFiles.clear(); - - boolean changed = false; - for (OrderRootType orderRootType : OrderRootType.getAllTypes()) { - final VirtualFile[] roots = myRoots.get(orderRootType).getVirtualFiles(); - changed |= !Comparing.equal(roots, oldRoots.get(orderRootType)); - myCachedFiles.put(orderRootType, roots); - } - return changed; - } - - void addProjectRootContainerListener(@NotNull ProjectRootListener listener) { - myListeners.add(listener); - } - - public void removeProjectRootContainerListener(@NotNull ProjectRootListener listener) { - myListeners.remove(listener); - } - - private void fireRootsChanged() { - for (final ProjectRootListener listener : myListeners) { - listener.rootsChanged(); - } - } - - @Override - public void removeRoot(@NotNull ProjectRoot root, @NotNull OrderRootType type) { - assertInsideChange(); - myRoots.get(type).remove(root); - } - - @Override - @NotNull - public ProjectRoot addRoot(@NotNull VirtualFile virtualFile, @NotNull OrderRootType type) { - assertInsideChange(); - return myRoots.get(type).add(virtualFile); - } - - @Override - public void addRoot(@NotNull ProjectRoot root, @NotNull OrderRootType type) { - assertInsideChange(); - myRoots.get(type).add(root); - } - - @Override - public void removeAllRoots(@NotNull OrderRootType type) { - assertInsideChange(); - myRoots.get(type).clear(); - } - - @Override - public void removeRoot(@NotNull VirtualFile root, @NotNull OrderRootType type) { - assertInsideChange(); - myRoots.get(type).remove(root); - } - - @Override - public void removeAllRoots() { - assertInsideChange(); - for (CompositeProjectRoot myRoot : myRoots.values()) { - myRoot.clear(); - } - } - - @Override - public void update() { - assertInsideChange(); - for (CompositeProjectRoot myRoot : myRoots.values()) { - myRoot.update(); - } - } - - @Override - public void readExternal(Element element) { - assertInsideChange(); - for (PersistentOrderRootType type : OrderRootType.getAllPersistentTypes()) { - read(element, type); - } - - ApplicationManager.getApplication().runReadAction(() -> { - myRoots.values().forEach(root -> { - if (myNoCopyJars) { - setNoCopyJars(root); - } - }); - cacheFiles(new THashMap<>(myCachedFiles)); - }); - - for (OrderRootType type : OrderRootType.getAllTypes()) { - if (myRoots.get(type) == null) { - LOG.error(type + " wasn't serialized"); - myRoots.put(type, new CompositeProjectRoot()); - } - - final VirtualFile[] newRoots = getRootFiles(type); - final VirtualFile[] oldRoots = VirtualFile.EMPTY_ARRAY; - if (!Comparing.equal(oldRoots, newRoots)) { - fireRootsChanged(); - break; - } - } - } - - @Override - public void writeExternal(Element element) { - List allTypes = OrderRootType.getSortedRootTypes(); - for (PersistentOrderRootType type : allTypes) { - write(element, type); - } - } - - void copyRootsFrom(@NotNull ProjectRootContainerImpl rootContainer) { - changeRoots(() -> { - removeAllRoots(); - for (OrderRootType rootType : OrderRootType.getAllTypes()) { - final ProjectRoot[] newRoots = rootContainer.getRoots(rootType); - for (ProjectRoot newRoot : newRoots) { - addRoot(newRoot, rootType); - } - } - }); - } - - private static void setNoCopyJars(ProjectRoot root) { - if (root instanceof SimpleProjectRoot) { - String url = ((SimpleProjectRoot)root).getUrl(); - if (StandardFileSystems.JAR_PROTOCOL.equals(VirtualFileManager.extractProtocol(url))) { - String path = VirtualFileManager.extractPath(url); - final VirtualFileSystem fileSystem = StandardFileSystems.jar(); - if (fileSystem instanceof JarCopyingFileSystem) { - ((JarCopyingFileSystem)fileSystem).setNoCopyJarForPath(path); - } - } - } - else if (root instanceof CompositeProjectRoot) { - ProjectRoot[] roots = ((CompositeProjectRoot)root).getProjectRoots(); - for (ProjectRoot root1 : roots) { - setNoCopyJars(root1); - } - } - } - - private void read(Element element, PersistentOrderRootType type) { - String sdkRootName = type.getSdkRootName(); - Element child = sdkRootName != null ? element.getChild(sdkRootName) : null; - if (child == null) { - myRoots.put(type, new CompositeProjectRoot()); - return; - } - - List children = child.getChildren(); - if (children.size() != 1) { - LOG.error(children); - } - CompositeProjectRoot root = (CompositeProjectRoot)CompositeProjectRoot.read(children.get(0)); - myRoots.put(type, root); - } - - private void write(Element roots, PersistentOrderRootType type) { - String sdkRootName = type.getSdkRootName(); - if (sdkRootName != null) { - Element e = new Element(sdkRootName); - roots.addContent(e); - final Element root = CompositeProjectRoot.write(myRoots.get(type)); - e.addContent(root); - } - } -} diff --git a/platform/projectModel-impl/src/com/intellij/openapi/projectRoots/impl/RootsAsVirtualFilePointers.java b/platform/projectModel-impl/src/com/intellij/openapi/projectRoots/impl/RootsAsVirtualFilePointers.java new file mode 100644 index 000000000000..24fc58d17a6a --- /dev/null +++ b/platform/projectModel-impl/src/com/intellij/openapi/projectRoots/impl/RootsAsVirtualFilePointers.java @@ -0,0 +1,189 @@ +// Copyright 2000-2017 JetBrains s.r.o. +// Use of this source code is governed by the Apache 2.0 license that can be +// found in the LICENSE file. + +package com.intellij.openapi.projectRoots.impl; + +import com.intellij.openapi.Disposable; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.roots.OrderRootType; +import com.intellij.openapi.roots.PersistentOrderRootType; +import com.intellij.openapi.roots.RootProvider; +import com.intellij.openapi.vfs.*; +import com.intellij.openapi.vfs.pointers.VirtualFilePointer; +import com.intellij.openapi.vfs.pointers.VirtualFilePointerContainer; +import com.intellij.openapi.vfs.pointers.VirtualFilePointerListener; +import com.intellij.openapi.vfs.pointers.VirtualFilePointerManager; +import gnu.trove.THashMap; +import org.jdom.Element; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Map; + +/** + * @author mike + */ +public class RootsAsVirtualFilePointers implements RootProvider { + private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.projectRoots.impl.ProjectRootContainerImpl"); + private final Map myRoots = new THashMap<>(); + + private final boolean myNoCopyJars; + + RootsAsVirtualFilePointers(boolean noCopyJars, VirtualFilePointerListener listener, @NotNull Disposable parent) { + myNoCopyJars = noCopyJars; + + for (OrderRootType rootType : OrderRootType.getAllTypes()) { + myRoots.put(rootType, VirtualFilePointerManager.getInstance().createContainer(parent, listener)); + } + } + + @Override + @NotNull + public VirtualFile[] getFiles(@NotNull OrderRootType type) { + return myRoots.get(type).getFiles(); + } + + @Override + @NotNull + public String[] getUrls(@NotNull OrderRootType type) { + return myRoots.get(type).getUrls(); + } + + public void addRoot(@NotNull VirtualFile virtualFile, @NotNull OrderRootType type) { + myRoots.get(type).add(virtualFile); + } + + public void removeAllRoots(@NotNull OrderRootType type) { + myRoots.get(type).clear(); + } + + public void removeRoot(@NotNull VirtualFile root, @NotNull OrderRootType type) { + VirtualFilePointerContainer container = myRoots.get(type); + VirtualFilePointer pointer = container.findByUrl(root.getUrl()); + if (pointer != null) { + container.remove(pointer); + } + } + + public void removeAllRoots() { + for (VirtualFilePointerContainer myRoot : myRoots.values()) { + myRoot.clear(); + } + } + + public void readExternal(@NotNull Element element) { + for (PersistentOrderRootType type : OrderRootType.getAllPersistentTypes()) { + read(element, type); + } + + ApplicationManager.getApplication().runReadAction(() -> myRoots.values().forEach(container -> { + if (myNoCopyJars) { + for (String root : container.getUrls()) { + setNoCopyJars(root); + } + } + })); + + for (OrderRootType type : OrderRootType.getAllTypes()) { + if (myRoots.get(type) == null) { + LOG.error(type + " wasn't serialized"); + } + } + } + + public void writeExternal(@NotNull Element element) { + List allTypes = OrderRootType.getSortedRootTypes(); + for (PersistentOrderRootType type : allTypes) { + write(element, type); + } + } + + void copyRootsFrom(@NotNull RootProvider rootContainer) { + removeAllRoots(); + for (OrderRootType rootType : OrderRootType.getAllTypes()) { + final VirtualFile[] newRoots = rootContainer.getFiles(rootType); + for (VirtualFile newRoot : newRoots) { + addRoot(newRoot, rootType); + } + } + } + + private static void setNoCopyJars(@NotNull String url) { + if (StandardFileSystems.JAR_PROTOCOL.equals(VirtualFileManager.extractProtocol(url))) { + String path = VirtualFileManager.extractPath(url); + final VirtualFileSystem fileSystem = StandardFileSystems.jar(); + if (fileSystem instanceof JarCopyingFileSystem) { + ((JarCopyingFileSystem)fileSystem).setNoCopyJarForPath(path); + } + } + } + + /** + + + + + + + + + */ + private void read(@NotNull Element roots, @NotNull PersistentOrderRootType type) { + String sdkRootName = type.getSdkRootName(); + Element child = sdkRootName != null ? roots.getChild(sdkRootName) : null; + if (child == null) { + return; + } + + List composites = child.getChildren(); + if (composites.size() != 1) { + LOG.error(composites); + } + Element composite = composites.get(0); + + myRoots.get(type).readExternal(composite, "root", false); + } + + /** + + + + + + + + + */ + private void write(@NotNull Element roots, @NotNull PersistentOrderRootType type) { + String sdkRootName = type.getSdkRootName(); + if (sdkRootName == null) { + return; + } + Element e = new Element(sdkRootName); + roots.addContent(e); + Element composite = new Element("root"); + composite.setAttribute("type", "composite"); + e.addContent(composite); + myRoots.get(type).writeExternal(composite, "root", false); + for (Element root : composite.getChildren()) { + root.setAttribute("type", "simple"); + } + } + + @Override + public void addRootSetChangedListener(@NotNull RootSetChangedListener listener) { + throw new RuntimeException(); + } + + @Override + public void addRootSetChangedListener(@NotNull RootSetChangedListener listener, @NotNull Disposable parentDisposable) { + throw new RuntimeException(); + } + + @Override + public void removeRootSetChangedListener(@NotNull RootSetChangedListener listener) { + throw new RuntimeException(); + } +} diff --git a/platform/projectModel-impl/src/com/intellij/openapi/projectRoots/impl/SimpleProjectRoot.java b/platform/projectModel-impl/src/com/intellij/openapi/projectRoots/impl/SimpleProjectRoot.java deleted file mode 100644 index f3cc5910a4dd..000000000000 --- a/platform/projectModel-impl/src/com/intellij/openapi/projectRoots/impl/SimpleProjectRoot.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2000-2015 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.projectRoots.impl; - -import com.intellij.openapi.projectRoots.ex.ProjectRoot; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileManager; -import com.intellij.util.io.URLUtil; -import org.jdom.Element; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.NotNull; - -import java.io.File; - -/** - * @author mike - */ -public class SimpleProjectRoot implements ProjectRoot { - @NotNull - private final String myUrl; - private VirtualFile myFile; - private final VirtualFile[] myFileArray = new VirtualFile[1]; - private boolean myInitialized; - @NonNls private static final String ATTRIBUTE_URL = "url"; - - public SimpleProjectRoot(@NotNull VirtualFile file) { - myFile = file; - myUrl = myFile.getUrl(); - } - - public SimpleProjectRoot(@NotNull String url) { - myUrl = url; - } - - SimpleProjectRoot(@NotNull Element element) { - myUrl = readUrl(element); - } - - public VirtualFile getFile() { - return myFile; - } - - @Override - @NotNull - public String getPresentableString() { - String path = VirtualFileManager.extractPath(myUrl); - path = StringUtil.trimEnd(path, URLUtil.JAR_SEPARATOR); - return path.replace('/', File.separatorChar); - } - - @Override - @NotNull - public VirtualFile[] getVirtualFiles() { - if (!myInitialized) initialize(); - - if (myFile == null) { - return VirtualFile.EMPTY_ARRAY; - } - - myFileArray[0] = myFile; - return myFileArray; - } - - @Override - @NotNull - public String[] getUrls() { - return new String[]{getUrl()}; - } - - @Override - public boolean isValid() { - if (!myInitialized) { - initialize(); - } - - return myFile != null && myFile.isValid(); - } - - @Override - public void update() { - initialize(); - } - - private void initialize() { - myInitialized = true; - - if (myFile == null || !myFile.isValid()) { - myFile = VirtualFileManager.getInstance().findFileByUrl(myUrl); - if (myFile != null && !canHaveChildren()) { - myFile = null; - } - } - } - - private boolean canHaveChildren() { - return myFile.getFileSystem().getProtocol().equals(URLUtil.HTTP_PROTOCOL) || myFile.isDirectory(); - } - - @NotNull - public String getUrl() { - return myUrl; - } - - @NotNull - private static String readUrl(Element element) { - return element.getAttributeValue(ATTRIBUTE_URL); - } - - public void writeExternal(Element element) { - if (!myInitialized) { - initialize(); - } - - element.setAttribute(ATTRIBUTE_URL, getUrl()); - } -} diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ContentEntryImpl.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ContentEntryImpl.java index 0ac492e49946..4ccecf4452a8 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ContentEntryImpl.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ContentEntryImpl.java @@ -63,7 +63,7 @@ public class ContentEntryImpl extends RootModelComponentBase implements ContentE ContentEntryImpl(@NotNull String url, @NotNull RootModelImpl m) { super(m); - myRoot = VirtualFilePointerManager.getInstance().create(url, this, null); + myRoot = VirtualFilePointerManager.getInstance().create(url, this, m.getRootsChangedListener()); } ContentEntryImpl(@NotNull Element e, @NotNull RootModelImpl m) throws InvalidDataException { diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ContentFolderBaseImpl.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ContentFolderBaseImpl.java index 677ce415f923..e0306e21e975 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ContentFolderBaseImpl.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ContentFolderBaseImpl.java @@ -43,13 +43,13 @@ public abstract class ContentFolderBaseImpl extends RootModelComponentBase imple ContentFolderBaseImpl(@NotNull VirtualFile file, @NotNull ContentEntryImpl contentEntry) { super(contentEntry.getRootModel()); myContentEntry = contentEntry; - myFilePointer = VirtualFilePointerManager.getInstance().create(file, this, null); + myFilePointer = VirtualFilePointerManager.getInstance().create(file, this, getRootModel().getRootsChangedListener()); } ContentFolderBaseImpl(@NotNull String url, @NotNull ContentEntryImpl contentEntry) { super(contentEntry.getRootModel()); myContentEntry = contentEntry; - myFilePointer = VirtualFilePointerManager.getInstance().create(url, this, null); + myFilePointer = VirtualFilePointerManager.getInstance().create(url, this, getRootModel().getRootsChangedListener()); } protected ContentFolderBaseImpl(@NotNull ContentFolderBaseImpl that, @NotNull ContentEntryImpl contentEntry) { @@ -63,7 +63,7 @@ public abstract class ContentFolderBaseImpl extends RootModelComponentBase imple protected ContentFolderBaseImpl(@NotNull VirtualFilePointer filePointer, @NotNull ContentEntryImpl contentEntry) { super(contentEntry.getRootModel()); myContentEntry = contentEntry; - myFilePointer = VirtualFilePointerManager.getInstance().duplicate(filePointer,this, null); + myFilePointer = VirtualFilePointerManager.getInstance().duplicate(filePointer,this, getRootModel().getRootsChangedListener()); } private static String getUrlFrom(Element element) throws InvalidDataException { diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/InheritedJdkOrderEntryImpl.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/InheritedJdkOrderEntryImpl.java index ebb4ffce685a..3f4ca25df8f4 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/InheritedJdkOrderEntryImpl.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/InheritedJdkOrderEntryImpl.java @@ -34,13 +34,12 @@ import org.jetbrains.jps.model.serialization.module.JpsModuleRootModelSerializer */ public class InheritedJdkOrderEntryImpl extends LibraryOrderEntryBaseImpl implements InheritedJdkOrderEntry, ClonableOrderEntry, WritableOrderEntry { @NonNls public static final String ENTRY_TYPE = JpsModuleRootModelSerializer.INHERITED_JDK_TYPE; - private final MyJdkTableListener myJdkTableListener = new MyJdkTableListener(); private final MyProjectJdkListener myListener = new MyProjectJdkListener(); InheritedJdkOrderEntryImpl(@NotNull RootModelImpl rootModel, @NotNull ProjectRootManagerImpl projectRootManager) { super(rootModel, projectRootManager); myProjectRootManagerImpl.addProjectJdkListener(myListener); - myProjectRootManagerImpl.addJdkTableListener(myJdkTableListener); + myProjectRootManagerImpl.addJdkTableListener(new MyJdkTableListener(), this); init(); } @@ -107,7 +106,6 @@ public class InheritedJdkOrderEntryImpl extends LibraryOrderEntryBaseImpl implem @Override public void dispose() { super.dispose(); - myProjectRootManagerImpl.removeJdkTableListener(myJdkTableListener); myProjectRootManagerImpl.removeProjectJdkListener(myListener); } diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/JavaModuleExternalPathsImpl.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/JavaModuleExternalPathsImpl.java index dc01925c852a..9f1488a712a4 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/JavaModuleExternalPathsImpl.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/JavaModuleExternalPathsImpl.java @@ -119,7 +119,7 @@ public class JavaModuleExternalPathsImpl extends JavaModuleExternalPaths { if (pathsElement != null) { VirtualFilePointerContainer container = VirtualFilePointerManager.getInstance().createContainer(this, null); myOrderRootPointerContainers.put(orderRootType, container); - container.readExternal(pathsElement, ROOT_ELEMENT); + container.readExternal(pathsElement, ROOT_ELEMENT, false); } } } @@ -131,7 +131,7 @@ public class JavaModuleExternalPathsImpl extends JavaModuleExternalPaths { VirtualFilePointerContainer container = myOrderRootPointerContainers.get(orderRootType); if (container != null && container.size() > 0) { final Element javaDocPaths = new Element(((PersistentOrderRootType)orderRootType).getModulePathsName()); - container.writeExternal(javaDocPaths, ROOT_ELEMENT); + container.writeExternal(javaDocPaths, ROOT_ELEMENT, false); element.addContent(javaDocPaths); } } diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ModuleJdkOrderEntryImpl.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ModuleJdkOrderEntryImpl.java index f304ea7d7881..f9d334325f7d 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ModuleJdkOrderEntryImpl.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ModuleJdkOrderEntryImpl.java @@ -40,8 +40,8 @@ public class ModuleJdkOrderEntryImpl extends LibraryOrderEntryBaseImpl implement ModuleJdkOrderEntry, ProjectJdkTable.Listener { @NonNls public static final String ENTRY_TYPE = JpsModuleRootModelSerializer.JDK_TYPE; - @NonNls public static final String JDK_NAME_ATTR = JpsModuleRootModelSerializer.JDK_NAME_ATTRIBUTE; - @NonNls public static final String JDK_TYPE_ATTR = JpsModuleRootModelSerializer.JDK_TYPE_ATTRIBUTE; + @NonNls private static final String JDK_NAME_ATTR = JpsModuleRootModelSerializer.JDK_NAME_ATTRIBUTE; + @NonNls private static final String JDK_TYPE_ATTR = JpsModuleRootModelSerializer.JDK_TYPE_ATTRIBUTE; @Nullable private Sdk myJdk; private String myJdkName; @@ -103,7 +103,7 @@ public class ModuleJdkOrderEntryImpl extends LibraryOrderEntryBaseImpl implement myJdk = jdk; setJdkName(jdkName); setJdkType(jdkType); - addListener(); + myProjectRootManagerImpl.addJdkTableListener(this, this); init(); } @@ -114,10 +114,6 @@ public class ModuleJdkOrderEntryImpl extends LibraryOrderEntryBaseImpl implement return myJdkType; } - private void addListener() { - myProjectRootManagerImpl.addJdkTableListener(this); - } - @Override protected RootProvider getRootProvider() { return myJdk == null ? null : myJdk.getRootProvider(); @@ -214,12 +210,6 @@ public class ModuleJdkOrderEntryImpl extends LibraryOrderEntryBaseImpl implement return new ModuleJdkOrderEntryImpl(this, rootModel, ProjectRootManagerImpl.getInstanceImpl(getRootModel().getModule().getProject())); } - @Override - public void dispose() { - super.dispose(); - myProjectRootManagerImpl.removeJdkTableListener(this); - } - private void setJdkName(String jdkName) { myJdkName = jdkName; } diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ProjectRootManagerImpl.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ProjectRootManagerImpl.java index a081a7076281..6ec2bb55f58b 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ProjectRootManagerImpl.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ProjectRootManagerImpl.java @@ -16,6 +16,7 @@ package com.intellij.openapi.roots.impl; +import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.WriteAction; import com.intellij.openapi.components.PersistentStateComponent; @@ -31,9 +32,11 @@ import com.intellij.openapi.roots.*; import com.intellij.openapi.roots.ex.ProjectRootManagerEx; import com.intellij.openapi.roots.libraries.Library; import com.intellij.openapi.roots.libraries.LibraryTable; +import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.EmptyRunnable; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.pointers.VirtualFilePointerListener; import com.intellij.psi.PsiManager; import com.intellij.util.EventDispatcher; import com.intellij.util.containers.ContainerUtil; @@ -53,8 +56,8 @@ import java.util.*; public class ProjectRootManagerImpl extends ProjectRootManagerEx implements PersistentStateComponent { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.projectRoots.impl.ProjectRootManagerImpl"); - @NonNls public static final String PROJECT_JDK_NAME_ATTR = "project-jdk-name"; - @NonNls public static final String PROJECT_JDK_TYPE_ATTR = "project-jdk-type"; + @NonNls private static final String PROJECT_JDK_NAME_ATTR = "project-jdk-name"; + @NonNls private static final String PROJECT_JDK_TYPE_ATTR = "project-jdk-type"; protected final Project myProject; @@ -92,7 +95,7 @@ public class ProjectRootManagerImpl extends ProjectRootManagerEx implements Pers myBatchLevel -= 1; if (myChanged && myBatchLevel == 0) { try { - WriteAction.run(this::fireChange); + WriteAction.run(() -> fireRootsChanged(myFileTypes)); } finally { myChanged = false; @@ -100,10 +103,6 @@ public class ProjectRootManagerImpl extends ProjectRootManagerEx implements Pers } } - private boolean fireChange() { - return fireRootsChanged(myFileTypes); - } - protected void beforeRootsChanged() { if (myBatchLevel == 0 || !myChanged) { if (fireBeforeRootsChanged(myFileTypes)) { @@ -114,7 +113,7 @@ public class ProjectRootManagerImpl extends ProjectRootManagerEx implements Pers protected void rootsChanged() { if (myBatchLevel == 0) { - if (fireChange()) { + if (fireRootsChanged(myFileTypes)) { myChanged = false; } } @@ -123,6 +122,7 @@ public class ProjectRootManagerImpl extends ProjectRootManagerEx implements Pers protected final BatchSession myRootsChanged = new BatchSession(false); protected final BatchSession myFileTypesChanged = new BatchSession(true); + private final VirtualFilePointerListener myRootsValidityChangedListener = new VirtualFilePointerListener(){}; public static ProjectRootManagerImpl getInstanceImpl(Project project) { return (ProjectRootManagerImpl)getInstance(project); @@ -253,12 +253,12 @@ public class ProjectRootManagerImpl extends ProjectRootManagerEx implements Pers } @Override - public void addProjectJdkListener(ProjectJdkListener listener) { + public void addProjectJdkListener(@NotNull ProjectJdkListener listener) { myProjectJdkEventDispatcher.addListener(listener); } @Override - public void removeProjectJdkListener(ProjectJdkListener listener) { + public void removeProjectJdkListener(@NotNull ProjectJdkListener listener) { myProjectJdkEventDispatcher.removeListener(listener); } @@ -419,6 +419,7 @@ public class ProjectRootManagerImpl extends ProjectRootManagerEx implements Pers protected void addRootsToWatch() { } + @NotNull public Project getProject() { return myProject; } @@ -610,7 +611,7 @@ public class ProjectRootManagerImpl extends ProjectRootManagerEx implements Pers } }); String currentName = getProjectSdkName(); - if (previousName != null && previousName.equals(currentName)) { + if (previousName.equals(currentName)) { // if already had jdk name and that name was the name of the jdk just changed myProjectSdkName = jdk.getName(); myProjectSdkType = jdk.getSdkType().getName(); @@ -618,14 +619,11 @@ public class ProjectRootManagerImpl extends ProjectRootManagerEx implements Pers } } - private final Map> myRegisteredRootProviders = new HashMap<>(); + private final Map> myRegisteredRootProviders = ContainerUtil.newIdentityTroveMap(); - void addJdkTableListener(ProjectJdkTable.Listener jdkTableListener) { + void addJdkTableListener(@NotNull ProjectJdkTable.Listener jdkTableListener, @NotNull Disposable parent) { myJdkTableMultiListener.addListener(jdkTableListener); - } - - void removeJdkTableListener(ProjectJdkTable.Listener jdkTableListener) { - myJdkTableMultiListener.removeListener(jdkTableListener); + Disposer.register(parent, ()->myJdkTableMultiListener.removeListener(jdkTableListener)); } void assertListenersAreDisposed() { @@ -663,4 +661,9 @@ public class ProjectRootManagerImpl extends ProjectRootManagerEx implements Pers } public void markRootsForRefresh() { } + + @NotNull + public VirtualFilePointerListener getRootsValidityChangedListener() { + return myRootsValidityChangedListener; + } } \ No newline at end of file diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/RootModelImpl.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/RootModelImpl.java index 8ab6286d7478..82f10e20d81a 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/RootModelImpl.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/RootModelImpl.java @@ -34,6 +34,7 @@ import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.pointers.VirtualFilePointerListener; import com.intellij.openapi.vfs.pointers.VirtualFilePointerManager; import com.intellij.util.ArrayUtil; import com.intellij.util.ObjectUtils; @@ -831,4 +832,9 @@ public class RootModelImpl extends RootModelBase implements ModifiableRootModel } } } + + @NotNull + public VirtualFilePointerListener getRootsChangedListener() { + return myProjectRootManager.getRootsValidityChangedListener(); + } } diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/libraries/JarDirectories.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/libraries/JarDirectories.java deleted file mode 100644 index fb636fc4ba00..000000000000 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/libraries/JarDirectories.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright 2000-2011 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.openapi.roots.impl.libraries; - -import com.intellij.openapi.roots.OrderRootType; -import com.intellij.openapi.roots.PersistentOrderRootType; -import com.intellij.openapi.util.InvalidDataException; -import com.intellij.openapi.util.JDOMExternalizable; -import com.intellij.util.containers.MultiMap; -import org.jdom.Element; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -/** - * @author nik - */ -public class JarDirectories implements JDOMExternalizable { - private final MultiMap myDirectories = new MultiMap<>(); - private final MultiMap myRecursivelyIncluded = new MultiMap<>(); - - @NonNls private static final String JAR_DIRECTORY_ELEMENT = "jarDirectory"; - @NonNls private static final String URL_ATTR = "url"; - @NonNls private static final String RECURSIVE_ATTR = "recursive"; - @NonNls private static final String ROOT_TYPE_ATTR = "type"; - public static final OrderRootType DEFAULT_JAR_DIRECTORY_TYPE = OrderRootType.CLASSES; - - public void copyFrom(@NotNull JarDirectories other) { - myDirectories.clear(); - myDirectories.putAllValues(other.myDirectories); - myRecursivelyIncluded.clear(); - myRecursivelyIncluded.putAllValues(other.myRecursivelyIncluded); - } - - public boolean contains(@NotNull OrderRootType rootType, @NotNull String url) { - return myDirectories.get(rootType).contains(url); - } - - public boolean isRecursive(@NotNull OrderRootType rootType, @NotNull String url) { - return myRecursivelyIncluded.get(rootType).contains(url); - } - - public void add(@NotNull OrderRootType rootType, @NotNull String url, boolean recursively) { - myDirectories.putValue(rootType, url); - if (recursively) { - myRecursivelyIncluded.putValue(rootType, url); - } - } - - public void remove(@NotNull OrderRootType rootType, @NotNull String url) { - myDirectories.remove(rootType, url); - myRecursivelyIncluded.remove(rootType, url); - } - - public void clear() { - myDirectories.clear(); - myRecursivelyIncluded.clear(); - } - - @NotNull - public Collection getRootTypes() { - return myDirectories.keySet(); - } - - @NotNull - public Collection getDirectories(@NotNull OrderRootType rootType) { - return myDirectories.get(rootType); - } - - @NotNull - public Collection getAllDirectories() { - return myDirectories.values(); - } - - public boolean isEmpty() { - return myDirectories.isEmpty(); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof JarDirectories)) return false; - - JarDirectories that = (JarDirectories)o; - return myDirectories.equals(that.myDirectories) && myRecursivelyIncluded.equals(that.myRecursivelyIncluded); - } - - @Override - public int hashCode() { - return 31 * myDirectories.hashCode() + myRecursivelyIncluded.hashCode(); - } - - @Override - public String toString() { - return "JAR dirs: " + myDirectories.values(); - } - - - @Override - public void readExternal(Element element) throws InvalidDataException { - clear(); - final List jarDirs = element.getChildren(JAR_DIRECTORY_ELEMENT); - for (Element jarDir : jarDirs) { - final String url = jarDir.getAttributeValue(URL_ATTR); - final String recursive = jarDir.getAttributeValue(RECURSIVE_ATTR); - final OrderRootType rootType = getJarDirectoryRootType(jarDir.getAttributeValue(ROOT_TYPE_ATTR)); - if (url != null) { - add(rootType, url, Boolean.valueOf(Boolean.parseBoolean(recursive))); - } - } - } - - private static OrderRootType getJarDirectoryRootType(@Nullable String type) { - for (PersistentOrderRootType rootType : OrderRootType.getAllPersistentTypes()) { - if (rootType.name().equals(type)) { - return rootType; - } - } - return DEFAULT_JAR_DIRECTORY_TYPE; - } - - @Override - public void writeExternal(Element element) { - final List rootTypes = LibraryImpl.sortRootTypes(getRootTypes()); - for (OrderRootType rootType : rootTypes) { - final List urls = new ArrayList<>(getDirectories(rootType)); - Collections.sort(urls, String.CASE_INSENSITIVE_ORDER); - for (String url : urls) { - final Element jarDirElement = new Element(JAR_DIRECTORY_ELEMENT); - jarDirElement.setAttribute(URL_ATTR, url); - jarDirElement.setAttribute(RECURSIVE_ATTR, Boolean.toString(isRecursive(rootType, url))); - if (!rootType.equals(DEFAULT_JAR_DIRECTORY_TYPE)) { - jarDirElement.setAttribute(ROOT_TYPE_ATTR, rootType.name()); - } - element.addContent(jarDirElement); - } - } - } - -} diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/libraries/JarDirectoryWatcherFactory.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/libraries/JarDirectoryWatcherFactory.java deleted file mode 100644 index c197e8061499..000000000000 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/libraries/JarDirectoryWatcherFactory.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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.roots.impl.libraries; - -import com.intellij.openapi.components.ServiceManager; -import com.intellij.openapi.roots.impl.RootProviderBaseImpl; - -/** - * @author yole - */ -public class JarDirectoryWatcherFactory { - private static final JarDirectoryWatcherFactory Instance = new JarDirectoryWatcherFactory(); - - public static JarDirectoryWatcherFactory getInstance() { - final JarDirectoryWatcherFactory factory = ServiceManager.getService(JarDirectoryWatcherFactory.class); - return factory != null ? factory : Instance; - } - - public JarDirectoryWatcher createWatcher(JarDirectories jarDirectories, RootProviderBaseImpl rootProvider) { - return new JarDirectoryWatcher() { - @Override - public void updateWatchedRoots() { - } - - @Override - public void dispose() { - } - }; - } -} diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/libraries/LibraryImpl.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/libraries/LibraryImpl.java index 655f46c6cdb5..3ee1bc16f7be 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/libraries/LibraryImpl.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/libraries/LibraryImpl.java @@ -15,33 +15,32 @@ */ package com.intellij.openapi.roots.impl.libraries; -import com.intellij.ide.highlighter.ArchiveFileType; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.ComponentSerializationUtil; +import com.intellij.openapi.components.StateSplitterEx; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.fileTypes.FileTypeRegistry; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectUtilCore; import com.intellij.openapi.roots.*; +import com.intellij.openapi.roots.impl.ProjectRootManagerImpl; import com.intellij.openapi.roots.impl.RootModelImpl; -import com.intellij.openapi.roots.impl.RootProviderBaseImpl; import com.intellij.openapi.roots.libraries.*; import com.intellij.openapi.util.*; -import com.intellij.openapi.vfs.StandardFileSystems; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileVisitor; +import com.intellij.openapi.vfs.impl.VirtualFilePointerContainerImpl; import com.intellij.openapi.vfs.pointers.VirtualFilePointer; import com.intellij.openapi.vfs.pointers.VirtualFilePointerContainer; +import com.intellij.openapi.vfs.pointers.VirtualFilePointerListener; import com.intellij.openapi.vfs.pointers.VirtualFilePointerManager; import com.intellij.util.ArrayUtil; import com.intellij.util.ArrayUtilRt; +import com.intellij.util.EventDispatcher; import com.intellij.util.SmartList; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.HashMap; -import com.intellij.util.io.URLUtil; import com.intellij.util.xmlb.SkipDefaultValuesSerializationFilters; import com.intellij.util.xmlb.XmlSerializer; import gnu.trove.THashSet; @@ -52,62 +51,36 @@ import org.jetbrains.annotations.Nullable; import java.util.*; -import static com.intellij.openapi.components.StateSplitterEx.EXTERNAL_SYSTEM_ID_ATTRIBUTE; -import static com.intellij.openapi.vfs.VirtualFileVisitor.ONE_LEVEL_DEEP; -import static com.intellij.openapi.vfs.VirtualFileVisitor.SKIP_ROOT; - /** * @author dsl */ -public class LibraryImpl extends TraceableDisposable implements LibraryEx.ModifiableModelEx, LibraryEx { +public class LibraryImpl extends TraceableDisposable implements LibraryEx.ModifiableModelEx, LibraryEx, RootProvider { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.roots.impl.impl.LibraryImpl"); @NonNls public static final String LIBRARY_NAME_ATTR = "name"; - @NonNls public static final String LIBRARY_TYPE_ATTR = "type"; - @NonNls public static final String ROOT_PATH_ELEMENT = "root"; + @NonNls private static final String LIBRARY_TYPE_ATTR = "type"; + @NonNls private static final String ROOT_PATH_ELEMENT = "root"; @NonNls public static final String ELEMENT = "library"; - @NonNls public static final String PROPERTIES_ELEMENT = "properties"; + @NonNls private static final String PROPERTIES_ELEMENT = "properties"; private static final SkipDefaultValuesSerializationFilters SERIALIZATION_FILTERS = new SkipDefaultValuesSerializationFilters(); private static final String EXCLUDED_ROOTS_TAG = "excluded"; private String myName; private final LibraryTable myLibraryTable; private final Map myRoots; @Nullable private VirtualFilePointerContainer myExcludedRoots; - private final JarDirectories myJarDirectories = new JarDirectories(); private final LibraryImpl mySource; private PersistentLibraryKind myKind; private LibraryProperties myProperties; - private final MyRootProviderImpl myRootProvider = new MyRootProviderImpl(); @Nullable private final ModifiableRootModel myRootModel; private boolean myDisposed; private final Disposable myPointersDisposable = Disposer.newDisposable(); - private final JarDirectoryWatcher myRootsWatcher = JarDirectoryWatcherFactory.getInstance().createWatcher(myJarDirectories, myRootProvider); private final ProjectModelExternalSource myExternalSource; + private final EventDispatcher myDispatcher = EventDispatcher.create(RootSetChangedListener.class); LibraryImpl(LibraryTable table, @NotNull Element element, ModifiableRootModel rootModel) throws InvalidDataException { - this(table, rootModel, null, element.getAttributeValue(LIBRARY_NAME_ATTR), findPersistentLibraryKind(element), findExternalSource(element)); - readProperties(element); - myJarDirectories.readExternal(element); - readRoots(element); - myRootsWatcher.updateWatchedRoots(); - } - - @Nullable - private static ProjectModelExternalSource findExternalSource(Element element) { - @Nullable String externalSourceId = element.getAttributeValue(EXTERNAL_SYSTEM_ID_ATTRIBUTE); - return externalSourceId != null ? ExternalProjectSystemRegistry.getInstance().getSourceById(externalSourceId) : null; - } - - @Nullable - private static PersistentLibraryKind findPersistentLibraryKind(@NotNull Element element) { - String typeString = element.getAttributeValue(LIBRARY_TYPE_ATTR); - LibraryKind kind = LibraryKind.findById(typeString); - if (kind != null && !(kind instanceof PersistentLibraryKind)) { - LOG.error("Cannot load non-persistable library kind: " + typeString); - return null; - } - return (PersistentLibraryKind)kind; + this(table, rootModel, null, null, findPersistentLibraryKind(element), findExternalSource(element)); + readExternal(element); } LibraryImpl(String name, @Nullable final PersistentLibraryKind kind, LibraryTable table, ModifiableRootModel rootModel, @@ -135,12 +108,11 @@ public class LibraryImpl extends TraceableDisposable implements LibraryEx.Modifi if (from.myExcludedRoots != null) { myExcludedRoots = from.myExcludedRoots.clone(myPointersDisposable); } - myJarDirectories.copyFrom(from.myJarDirectories); } // primary private LibraryImpl(LibraryTable table, @Nullable ModifiableRootModel rootModel, LibraryImpl newSource, String name, - @Nullable final PersistentLibraryKind kind, @Nullable ProjectModelExternalSource externalSource) { + @Nullable PersistentLibraryKind kind, @Nullable ProjectModelExternalSource externalSource) { super(true); myLibraryTable = table; myRootModel = rootModel; @@ -150,9 +122,26 @@ public class LibraryImpl extends TraceableDisposable implements LibraryEx.Modifi myExternalSource = externalSource; //init roots depends on my myKind myRoots = initRoots(); - Disposer.register(this, myRootsWatcher); } + @Nullable + private static ProjectModelExternalSource findExternalSource(Element element) { + @Nullable String externalSourceId = element.getAttributeValue(StateSplitterEx.EXTERNAL_SYSTEM_ID_ATTRIBUTE); + return externalSourceId != null ? ExternalProjectSystemRegistry.getInstance().getSourceById(externalSourceId) : null; + } + + @Nullable + private static PersistentLibraryKind findPersistentLibraryKind(@NotNull Element element) { + String typeString = element.getAttributeValue(LIBRARY_TYPE_ATTR); + LibraryKind kind = LibraryKind.findById(typeString); + if (kind != null && !(kind instanceof PersistentLibraryKind)) { + LOG.error("Cannot load non-persistable library kind: " + typeString); + return null; + } + return (PersistentLibraryKind)kind; + } + + @NotNull private Set getAllRootTypes() { Set rootTypes = new HashSet<>(Arrays.asList(OrderRootType.getAllTypes())); if (myKind != null) { @@ -171,7 +160,7 @@ public class LibraryImpl extends TraceableDisposable implements LibraryEx.Modifi private void checkDisposed() { if (isDisposed()) { - throwDisposalError("'" + myName + "' already disposed:"); + throwDisposalError("'" + myName + "' already disposed: " + getStackTrace()); } } @@ -200,37 +189,7 @@ public class LibraryImpl extends TraceableDisposable implements LibraryEx.Modifi checkDisposed(); VirtualFilePointerContainer container = myRoots.get(rootType); - if (container == null) { - return VirtualFile.EMPTY_ARRAY; - } - - List expanded = new SmartList<>(); - for (VirtualFile file : container.getFiles()) { - if (file.isDirectory()) { - if (myJarDirectories.contains(rootType, file.getUrl())) { - collectJarFiles(file, expanded, myJarDirectories.isRecursive(rootType, file.getUrl())); - continue; - } - } - expanded.add(file); - } - return VfsUtilCore.toVirtualFileArray(expanded); - } - - public static void collectJarFiles(VirtualFile dir, List container, boolean recursively) { - VfsUtilCore.visitChildrenRecursively(dir, new VirtualFileVisitor(SKIP_ROOT, recursively ? null : ONE_LEVEL_DEEP) { - @Override - public boolean visitFile(@NotNull VirtualFile file) { - if (!file.isDirectory() && FileTypeRegistry.getInstance().getFileTypeByFileName(file.getName()) == ArchiveFileType.INSTANCE) { - VirtualFile jarRoot = StandardFileSystems.jar().findFileByPath(file.getPath() + URLUtil.JAR_SEPARATOR); - if (jarRoot != null) { - container.add(jarRoot); - return false; - } - } - return true; - } - }); + return container == null ? VirtualFile.EMPTY_ARRAY : container.getFiles(); } @Override @@ -247,15 +206,15 @@ public class LibraryImpl extends TraceableDisposable implements LibraryEx.Modifi return new LibraryImpl(this, this, myRootModel); } - public Library cloneLibrary(RootModelImpl rootModel) { + @NotNull + public Library cloneLibrary(@NotNull RootModelImpl rootModel) { LOG.assertTrue(myLibraryTable == null); - final LibraryImpl clone = new LibraryImpl(this, null, rootModel); - clone.myRootsWatcher.updateWatchedRoots(); - return clone; + return new LibraryImpl(this, null, rootModel); } + @NotNull @Override - public List getInvalidRootUrls(OrderRootType type) { + public List getInvalidRootUrls(@NotNull OrderRootType type) { if (myDisposed) return Collections.emptyList(); final List pointers = myRoots.get(type).getList(); @@ -280,21 +239,32 @@ public class LibraryImpl extends TraceableDisposable implements LibraryEx.Modifi @Override @NotNull public RootProvider getRootProvider() { - return myRootProvider; + return this; } + @NotNull private Map initRoots() { Disposer.register(this, myPointersDisposable); Map result = new HashMap<>(4); + VirtualFilePointerListener listener = getListener(); + for (OrderRootType rootType : getAllRootTypes()) { - result.put(rootType, VirtualFilePointerManager.getInstance().createContainer(myPointersDisposable)); + VirtualFilePointerContainer container = VirtualFilePointerManager.getInstance().createContainer(myPointersDisposable, listener); + result.put(rootType, container); } return result; } + @Nullable + private VirtualFilePointerListener getListener() { + Project project = myLibraryTable instanceof ProjectLibraryTable ? ((ProjectLibraryTable)myLibraryTable).getProject() : null; + return myRootModel != null ? ((RootModelImpl)myRootModel).getRootsChangedListener() : project != null ? ProjectRootManagerImpl + .getInstanceImpl(project).getRootsValidityChangedListener() : null; + } + @Nullable @Override public ProjectModelExternalSource getExternalSource() { @@ -306,8 +276,36 @@ public class LibraryImpl extends TraceableDisposable implements LibraryEx.Modifi readName(element); readProperties(element); readRoots(element); - myJarDirectories.readExternal(element); - myRootsWatcher.updateWatchedRoots(); + readJarDirectories(element); + } + + @NonNls private static final String ROOT_TYPE_ATTR = "type"; + private static final OrderRootType DEFAULT_JAR_DIRECTORY_TYPE = OrderRootType.CLASSES; + + // just to maintain .xml compatibility + // VirtualFilePointerContainerImpl does the same but stores its jar dirs attributes inside element + @Deprecated // todo to remove sometime later + private void readJarDirectories(Element element) { + final List jarDirs = element.getChildren(VirtualFilePointerContainerImpl.JAR_DIRECTORY_ELEMENT); + for (Element jarDir : jarDirs) { + final String url = jarDir.getAttributeValue(VirtualFilePointerContainerImpl.URL_ATTR); + if (url != null) { + final String recursive = jarDir.getAttributeValue(VirtualFilePointerContainerImpl.RECURSIVE_ATTR); + final OrderRootType rootType = getJarDirectoryRootType(jarDir.getAttributeValue(ROOT_TYPE_ATTR)); + VirtualFilePointerContainer roots = myRoots.get(rootType); + boolean recursively = Boolean.parseBoolean(recursive); + roots.addJarDirectory(url, recursively); + } + } + } + + private static OrderRootType getJarDirectoryRootType(@Nullable String type) { + for (PersistentOrderRootType rootType : OrderRootType.getAllPersistentTypes()) { + if (rootType.name().equals(type)) { + return rootType; + } + } + return DEFAULT_JAR_DIRECTORY_TYPE; } private void readProperties(Element element) { @@ -324,25 +322,26 @@ public class LibraryImpl extends TraceableDisposable implements LibraryEx.Modifi } } - private void readName(Element element) { + private void readName(@NotNull Element element) { myName = element.getAttributeValue(LIBRARY_NAME_ATTR); } - private void readRoots(Element element) throws InvalidDataException { + private void readRoots(@NotNull Element element) throws InvalidDataException { for (OrderRootType rootType : getAllRootTypes()) { final Element rootChild = element.getChild(rootType.name()); if (rootChild == null) { continue; } VirtualFilePointerContainer roots = myRoots.get(rootType); - roots.readExternal(rootChild, ROOT_PATH_ELEMENT); + roots.readExternal(rootChild, ROOT_PATH_ELEMENT, false); } Element excludedRoot = element.getChild(EXCLUDED_ROOTS_TAG); if (excludedRoot != null) { - getOrCreateExcludedRoots().readExternal(excludedRoot, ROOT_PATH_ELEMENT); + getOrCreateExcludedRoots().readExternal(excludedRoot, ROOT_PATH_ELEMENT, false); } } + @NotNull private VirtualFilePointerContainer getOrCreateExcludedRoots() { if (myExcludedRoots == null) { myExcludedRoots = VirtualFilePointerManager.getInstance().createContainer(myPointersDisposable); @@ -352,7 +351,8 @@ public class LibraryImpl extends TraceableDisposable implements LibraryEx.Modifi //TODO Remove the next two methods as a temporary solution. Sort in OrderRootType. // - public static List sortRootTypes(Collection rootTypes) { + @NotNull + private static List sortRootTypes(@NotNull Collection rootTypes) { List allTypes = new ArrayList<>(rootTypes); Collections.sort(allTypes, (o1, o2) -> o1.name().compareToIgnoreCase(o2.name())); return allTypes; @@ -389,11 +389,11 @@ public class LibraryImpl extends TraceableDisposable implements LibraryEx.Modifi } if (ProjectUtilCore.isExternalStorageEnabled(project)) { //we can add this attribute only if the library configuration will be stored separately, otherwise we will get modified files in .idea/libraries. - element.setAttribute(EXTERNAL_SYSTEM_ID_ATTRIBUTE, myExternalSource.getId()); + element.setAttribute(StateSplitterEx.EXTERNAL_SYSTEM_ID_ATTRIBUTE, myExternalSource.getId()); } } - ArrayList storableRootTypes = new ArrayList<>(Arrays.asList(OrderRootType.getAllTypes())); + List storableRootTypes = new ArrayList<>(Arrays.asList(OrderRootType.getAllTypes())); if (myKind != null) { storableRootTypes.addAll(Arrays.asList(myKind.getAdditionalRootTypes())); } @@ -405,18 +405,41 @@ public class LibraryImpl extends TraceableDisposable implements LibraryEx.Modifi } final Element rootTypeElement = new Element(rootType.name()); - roots.writeExternal(rootTypeElement, ROOT_PATH_ELEMENT); + roots.writeExternal(rootTypeElement, ROOT_PATH_ELEMENT, false); element.addContent(rootTypeElement); } if (myExcludedRoots != null && myExcludedRoots.size() > 0) { Element excluded = new Element(EXCLUDED_ROOTS_TAG); - myExcludedRoots.writeExternal(excluded, ROOT_PATH_ELEMENT); + myExcludedRoots.writeExternal(excluded, ROOT_PATH_ELEMENT, false); element.addContent(excluded); } - myJarDirectories.writeExternal(element); + writeJarDirectories(element); rootElement.addContent(element); } + // just to maintain .xml compatibility + // VirtualFilePointerContainerImpl does the same but stores its jar dirs attributes inside element + @Deprecated // todo to remove sometime later + private void writeJarDirectories(Element element) { + final List rootTypes = sortRootTypes(myRoots.keySet()); + for (OrderRootType rootType : rootTypes) { + VirtualFilePointerContainer container = myRoots.get(rootType); + List> jarDirectories = new ArrayList<>(container.getJarDirectories()); + Collections.sort(jarDirectories, Comparator.comparing(p->p.getFirst(), String.CASE_INSENSITIVE_ORDER)); + for (Pair pair : jarDirectories) { + String url = pair.getFirst(); + boolean isRecursive = pair.getSecond(); + final Element jarDirElement = new Element(VirtualFilePointerContainerImpl.JAR_DIRECTORY_ELEMENT); + jarDirElement.setAttribute(VirtualFilePointerContainerImpl.URL_ATTR, url); + jarDirElement.setAttribute(VirtualFilePointerContainerImpl.RECURSIVE_ATTR, Boolean.toString(isRecursive)); + if (!rootType.equals(DEFAULT_JAR_DIRECTORY_TYPE)) { + jarDirElement.setAttribute(ROOT_TYPE_ATTR, rootType.name()); + } + element.addContent(jarDirElement); + } + } + } + private boolean isWritable() { return mySource != null; } @@ -465,7 +488,7 @@ public class LibraryImpl extends TraceableDisposable implements LibraryEx.Modifi } @Override - public void setKind(PersistentLibraryKind kind) { + public void setKind(@NotNull PersistentLibraryKind kind) { LOG.assertTrue(isWritable()); LOG.assertTrue(myKind == null || myKind == kind, "Library kind cannot be changed from " + myKind + " to " + kind); myKind = kind; @@ -492,12 +515,12 @@ public class LibraryImpl extends TraceableDisposable implements LibraryEx.Modifi @Override public void addJarDirectory(@NotNull final String url, final boolean recursive) { - addJarDirectory(url, recursive, JarDirectories.DEFAULT_JAR_DIRECTORY_TYPE); + addJarDirectory(url, recursive, OrderRootType.CLASSES); } @Override public void addJarDirectory(@NotNull final VirtualFile file, final boolean recursive) { - addJarDirectory(file, recursive, JarDirectories.DEFAULT_JAR_DIRECTORY_TYPE); + addJarDirectory(file, recursive, OrderRootType.CLASSES); } @Override @@ -506,8 +529,7 @@ public class LibraryImpl extends TraceableDisposable implements LibraryEx.Modifi LOG.assertTrue(isWritable()); final VirtualFilePointerContainer container = myRoots.get(rootType); - container.add(url); - myJarDirectories.add(rootType, url, recursive); + container.addJarDirectory(url, recursive); } @Override @@ -516,18 +538,19 @@ public class LibraryImpl extends TraceableDisposable implements LibraryEx.Modifi LOG.assertTrue(isWritable()); final VirtualFilePointerContainer container = myRoots.get(rootType); - container.add(file); - myJarDirectories.add(rootType, file.getUrl(), recursive); + container.addJarDirectory(file.getUrl(), recursive); } @Override public boolean isJarDirectory(@NotNull final String url) { - return isJarDirectory(url, JarDirectories.DEFAULT_JAR_DIRECTORY_TYPE); + return isJarDirectory(url, OrderRootType.CLASSES); } @Override public boolean isJarDirectory(@NotNull final String url, @NotNull final OrderRootType rootType) { - return myJarDirectories.contains(rootType, url); + VirtualFilePointerContainer container = myRoots.get(rootType); + List> jarDirectories = container.getJarDirectories(); + return jarDirectories.contains(Pair.create(url, false)) || jarDirectories.contains(Pair.create(url, true)); } @Override @@ -556,10 +579,9 @@ public class LibraryImpl extends TraceableDisposable implements LibraryEx.Modifi } } } - myJarDirectories.remove(rootType, url); return true; } - return false; + return container.removeJarDirectory(url); } private boolean isUnderRoots(@NotNull String url) { @@ -594,29 +616,8 @@ public class LibraryImpl extends TraceableDisposable implements LibraryEx.Modifi return !mySource.equals(this); } - private boolean areRootsChanged(final LibraryImpl that) { + private boolean areRootsChanged(@NotNull LibraryImpl that) { return !that.equals(this); - //final OrderRootType[] allTypes = OrderRootType.getAllTypes(); - //for (OrderRootType type : allTypes) { - // final String[] urls = getUrls(type); - // final String[] thatUrls = that.getUrls(type); - // if (urls.length != thatUrls.length) { - // return true; - // } - // for (int idx = 0; idx < urls.length; idx++) { - // final String url = urls[idx]; - // final String thatUrl = thatUrls[idx]; - // if (!Comparing.equal(url, thatUrl)) { - // return true; - // } - // final Boolean jarDirRecursive = myJarDirectories.get(url); - // final Boolean sourceJarDirRecursive = that.myJarDirectories.get(thatUrl); - // if (jarDirRecursive == null ? sourceJarDirRecursive != null : !jarDirRecursive.equals(sourceJarDirRecursive)) { - // return true; - // } - // } - //} - //return false; } public Library getSource() { @@ -648,18 +649,16 @@ public class LibraryImpl extends TraceableDisposable implements LibraryEx.Modifi if (areRootsChanged(fromModel)) { disposeMyPointers(); copyRootsFrom(fromModel); - myJarDirectories.copyFrom(fromModel.myJarDirectories); - myRootsWatcher.updateWatchedRoots(); - myRootProvider.fireRootSetChanged(); + fireRootSetChanged(); } } - private void copyRootsFrom(LibraryImpl fromModel) { + private void copyRootsFrom(@NotNull LibraryImpl fromModel) { Map clonedRoots = ContainerUtil.newHashMap(); for (Map.Entry entry : fromModel.myRoots.entrySet()) { OrderRootType rootType = entry.getKey(); VirtualFilePointerContainer container = entry.getValue(); - VirtualFilePointerContainer clone = container.clone(myPointersDisposable); + VirtualFilePointerContainer clone = container.clone(myPointersDisposable, getListener()); clonedRoots.put(rootType, clone); } myRoots.clear(); @@ -680,24 +679,6 @@ public class LibraryImpl extends TraceableDisposable implements LibraryEx.Modifi Disposer.register(this, myPointersDisposable); } - private class MyRootProviderImpl extends RootProviderBaseImpl { - @Override - @NotNull - public String[] getUrls(@NotNull OrderRootType rootType) { - Set originalUrls = new LinkedHashSet<>(Arrays.asList(LibraryImpl.this.getUrls(rootType))); - for (VirtualFile file : getFiles(rootType)) { // Add those expanded with jar directories. - originalUrls.add(file.getUrl()); - } - return ArrayUtil.toStringArray(originalUrls); - } - - @Override - @NotNull - public VirtualFile[] getFiles(@NotNull final OrderRootType rootType) { - return LibraryImpl.this.getFiles(rootType); - } - } - @Override public LibraryTable getTable() { return myLibraryTable; @@ -709,7 +690,6 @@ public class LibraryImpl extends TraceableDisposable implements LibraryEx.Modifi final LibraryImpl library = (LibraryImpl)o; - if (!myJarDirectories.equals(library.myJarDirectories)) return false; if (myName != null ? !myName.equals(library.myName) : library.myName != null) return false; if (myRoots != null ? !myRoots.equals(library.myRoots) : library.myRoots != null) return false; if (myKind != null ? !myKind.equals(library.myKind) : library.myKind != null) return false; @@ -722,18 +702,36 @@ public class LibraryImpl extends TraceableDisposable implements LibraryEx.Modifi public int hashCode() { int result = myName != null ? myName.hashCode() : 0; result = 31 * result + (myRoots != null ? myRoots.hashCode() : 0); - result = 31 * result + myJarDirectories.hashCode(); return result; } @NonNls @Override public String toString() { - return "Library: name:" + myName + "; jars:" + myJarDirectories + "; roots:" + myRoots.values(); + return "Library: name:" + myName + "; roots:" + myRoots.values(); } @Nullable("will return non-null value only for module level libraries") public Module getModule() { return myRootModel == null ? null : myRootModel.getModule(); } + + @Override + public void addRootSetChangedListener(@NotNull RootSetChangedListener listener) { + myDispatcher.addListener(listener); + } + + @Override + public void removeRootSetChangedListener(@NotNull RootSetChangedListener listener) { + myDispatcher.removeListener(listener); + } + + @Override + public void addRootSetChangedListener(@NotNull RootSetChangedListener listener, @NotNull Disposable parentDisposable) { + myDispatcher.addListener(listener, parentDisposable); + } + + private void fireRootSetChanged() { + myDispatcher.getMulticaster().rootSetChanged(this); + } } diff --git a/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/associations/impl/FileAssociationsManagerImpl.java b/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/associations/impl/FileAssociationsManagerImpl.java index 09fffb734310..446ebc34a97c 100644 --- a/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/associations/impl/FileAssociationsManagerImpl.java +++ b/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/associations/impl/FileAssociationsManagerImpl.java @@ -65,7 +65,7 @@ class FileAssociationsManagerImpl extends FileAssociationsManager implements Pro if (url != null) { final VirtualFilePointer pointer = myFilePointerManager.create(url, myProject, null); final VirtualFilePointerContainer container = myFilePointerManager.createContainer(myProject); - container.readExternal(child, "association"); + container.readExternal(child, "association", false); myAssociations.put(pointer, container); } } @@ -77,7 +77,7 @@ class FileAssociationsManagerImpl extends FileAssociationsManager implements Pro final Element e = new Element("file"); e.setAttribute("url", pointer.getUrl()); final VirtualFilePointerContainer container = myAssociations.get(pointer); - container.writeExternal(e, "association"); + container.writeExternal(e, "association", false); element.addContent(e); } }