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); } }