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 5702cf351133..961ca84f71db 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,7 +21,6 @@ import com.intellij.util.ArrayUtil; import com.intellij.util.containers.ConcurrentList; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.io.URLUtil; -import gnu.trove.THashSet; import org.jdom.Element; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -61,11 +60,7 @@ public class VirtualFilePointerContainerImpl extends TraceableDisposable impleme @Override 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); - } + addAll(ContainerUtil.map(urls, url -> url.getAttributeValue(URL_ATTR))); if (externalizeJarDirectories) { List jarDirs = rootChild.getChildren(JAR_DIRECTORY_ELEMENT); for (Element jarDir : jarDirs) { @@ -86,24 +81,22 @@ public class VirtualFilePointerContainerImpl extends TraceableDisposable impleme 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); - } + writeJarDirs(myJarDirectories, element, false); + writeJarDirs(myJarRecursiveDirectories, element, true); + } + } + + private static void writeJarDirs(@NotNull List myJarDirectories, + @NotNull Element element, + boolean recursive) { + List jarDirectories = new ArrayList<>(myJarDirectories); + Collections.sort(jarDirectories, 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(recursive)); + element.addContent(jarDirElement); } } @@ -173,19 +166,24 @@ public class VirtualFilePointerContainerImpl extends TraceableDisposable impleme assert !myDisposed; dropCaches(); - Set set = new THashSet<>(myList); - List thatList = that.getList(); - List toAddList = new ArrayList<>(thatList.size()); - for (final VirtualFilePointer pointer : thatList) { - if (!set.contains(pointer)) toAddList.add(duplicate(pointer)); - } - myList.addAll(toAddList); + addAll(Arrays.asList(that.getUrls())); + List jarDups = ContainerUtil.map(((VirtualFilePointerContainerImpl)that).myJarDirectories, this::duplicate); List jarRecursiveDups = ContainerUtil.map(((VirtualFilePointerContainerImpl)that).myJarRecursiveDirectories, this::duplicate); myJarDirectories.addAll(jarDups); myJarRecursiveDirectories.addAll(jarRecursiveDups); } + public void addAll(@NotNull Collection urls) { + Set existing = ContainerUtil.map2Set(myList, p->p.getUrl()); + List toAddList = new ArrayList<>(urls.size()); + for (String url : urls) { + if (!existing.contains(url)) toAddList.add(create(url)); + } + // optimization: faster than calling .add() one by one + myList.addAll(toAddList); + } + private void dropCaches() { myTimeStampOfCachedThings = -1; // make it never equal to myVirtualFilePointerManager.getModificationCount() myCachedThings = EMPTY; @@ -396,14 +394,20 @@ public class VirtualFilePointerContainerImpl extends TraceableDisposable impleme /** optimization: faster than calling {@link #addJarDirectory(String, boolean)} one by one */ public void addAllJarDirectories(@NotNull Collection directoryUrls, boolean recursively) { if (directoryUrls.isEmpty()) return; - Set jars = new THashSet<>(recursively ? myJarRecursiveDirectories : myJarDirectories); - Set list = new THashSet<>(myList); + Set jarUrls = ContainerUtil.map2Set(recursively ? myJarRecursiveDirectories : myJarDirectories, p->p.getUrl()); + Set listUrls = ContainerUtil.map2Set(myList, p->p.getUrl()); List toAddJars = new ArrayList<>(directoryUrls.size()); List toAddList = new ArrayList<>(directoryUrls.size()); directoryUrls.forEach(url -> { - VirtualFilePointer pointer = myVirtualFilePointerManager.createDirectoryPointer(url, recursively, myParent, myListener); - if (!jars.contains(pointer)) toAddJars.add(pointer); - if (!list.contains(pointer)) toAddList.add(pointer); + VirtualFilePointer pointer = null; + if (!jarUrls.contains(url)) { + pointer = myVirtualFilePointerManager.createDirectoryPointer(url, recursively, myParent, myListener); + toAddJars.add(pointer); + } + if (!listUrls.contains(url)) { + if (pointer == null) pointer = myVirtualFilePointerManager.createDirectoryPointer(url, recursively, myParent, myListener); + toAddList.add(pointer); + } }); (recursively ? myJarRecursiveDirectories : myJarDirectories).addAll(toAddJars); myList.addAll(toAddList); // hack. jar directories need to be contained in class roots too (for externalization compatibility) but be ignored in getFiles() diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/OrderRootsCache.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/OrderRootsCache.java index afe8f80d1bfd..ac7383d27146 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/OrderRootsCache.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/OrderRootsCache.java @@ -20,6 +20,7 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.roots.OrderRootType; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.impl.VirtualFilePointerContainerImpl; import com.intellij.openapi.vfs.pointers.VirtualFilePointerContainer; import com.intellij.openapi.vfs.pointers.VirtualFilePointerManager; import com.intellij.util.ArrayUtil; @@ -58,9 +59,13 @@ class OrderRootsCache { private static final VirtualFilePointerContainer EMPTY = ObjectUtils.sentinel("Empty roots container", VirtualFilePointerContainer.class); private VirtualFilePointerContainer setCachedRoots(@NotNull CacheKey key, @NotNull Collection urls) { // optimization: avoid creating heavy container for empty list, use 'EMPTY' stub for that case - VirtualFilePointerContainer container = urls.isEmpty() ? EMPTY : VirtualFilePointerManager.getInstance().createContainer(myRootsDisposable); - for (String url : urls) { - container.add(url); + VirtualFilePointerContainer container; + if (urls.isEmpty()) { + container = EMPTY; + } + else { + container = VirtualFilePointerManager.getInstance().createContainer(myRootsDisposable); + ((VirtualFilePointerContainerImpl)container).addAll(urls); } Map map = myRoots.get(); if (map == null) map = ConcurrencyUtil.cacheOrGet(myRoots, ContainerUtil.newConcurrentMap());