From 5d9564e48be0d1601f7c9f0bb87ad2db907e112f Mon Sep 17 00:00:00 2001 From: nik Date: Fri, 2 Jul 2010 09:27:52 +0400 Subject: [PATCH] caching for OrderRootsEnumerator added --- .../impl/javaCompiler/ModuleChunk.java | 6 +- .../com/intellij/util/JarClasspathHelper.java | 5 +- .../intellij/roots/DependencyScopeTest.java | 47 +++-- .../intellij/roots/OrderEnumeratorTest.java | 51 +++++- .../roots/OrderEnumerationHandler.java | 5 +- .../openapi/roots/OrderEnumerator.java | 6 +- .../openapi/roots/OrderRootsEnumerator.java | 28 ++- .../intellij/find/impl/FindInProjectUtil.java | 4 +- .../roots/impl/ModuleOrderEnumerator.java | 4 +- .../roots/impl/ModuleRootManagerImpl.java | 21 +-- .../roots/impl/OrderEnumeratorBase.java | 53 +++++- .../openapi/roots/impl/OrderRootsCache.java | 105 +++++++++++ .../roots/impl/OrderRootsEnumeratorImpl.java | 167 +++++++++++++----- .../roots/impl/ProjectOrderEnumerator.java | 2 +- .../openapi/roots/impl/RootModelImpl.java | 2 +- .../MavenOrderEnumeratorHandler.java | 13 +- .../plugins/xpathView/search/SearchScope.java | 5 +- 17 files changed, 402 insertions(+), 122 deletions(-) create mode 100644 platform/lang-impl/src/com/intellij/openapi/roots/impl/OrderRootsCache.java diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/ModuleChunk.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/ModuleChunk.java index af0956d66e1e..d507c2b13dfb 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/ModuleChunk.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/ModuleChunk.java @@ -198,7 +198,7 @@ public class ModuleChunk extends Chunk { if ((mySourcesFilter & TEST_SOURCES) == 0) { enumerator = enumerator.productionOnly(); } - cpFiles.addAll(enumerator.recursively().exportedOnly().getClassesRoots()); + Collections.addAll(cpFiles, enumerator.recursively().exportedOnly().getClassesRoots()); } cpFiles = JarClasspathHelper.patchFiles(cpFiles, myContext.getProject()); @@ -218,8 +218,8 @@ public class ModuleChunk extends Chunk { if ((mySourcesFilter & TEST_SOURCES) == 0) { enumerator = enumerator.productionOnly(); } - cpFiles.addAll(enumerator.recursively().exportedOnly().getClassesRoots()); - jdkFiles.addAll(OrderEnumerator.orderEntries(module).sdkOnly().getClassesRoots()); + Collections.addAll(cpFiles, enumerator.recursively().exportedOnly().getClassesRoots()); + Collections.addAll(jdkFiles, OrderEnumerator.orderEntries(module).sdkOnly().getClassesRoots()); } cpFiles.addAll(jdkFiles); return cpFiles; diff --git a/java/java-impl/src/com/intellij/util/JarClasspathHelper.java b/java/java-impl/src/com/intellij/util/JarClasspathHelper.java index 11d0ba91531a..e2387850c6a1 100644 --- a/java/java-impl/src/com/intellij/util/JarClasspathHelper.java +++ b/java/java-impl/src/com/intellij/util/JarClasspathHelper.java @@ -25,7 +25,9 @@ import com.intellij.util.containers.OrderedSet; import gnu.trove.TObjectHashingStrategy; import org.jetbrains.annotations.Nullable; +import java.util.Arrays; import java.util.Collection; +import java.util.HashSet; /** * @author Dmitry Avdeev @@ -38,7 +40,8 @@ public class JarClasspathHelper { } String path = getJarsPath(project); final OrderedSet result = new OrderedSet(TObjectHashingStrategy.CANONICAL); - final Collection modulesOutputs = OrderEnumerator.orderEntries(project).withoutLibraries().withoutSdk().getClassesRoots(); + final Collection modulesOutputs = + new HashSet(Arrays.asList(OrderEnumerator.orderEntries(project).withoutLibraries().withoutSdk().getClassesRoots())); for (VirtualFile file : files) { VirtualFile jar = getJarFile(path, file.getName()); if (modulesOutputs.contains(file) && jar != null) { diff --git a/java/java-tests/testSrc/com/intellij/roots/DependencyScopeTest.java b/java/java-tests/testSrc/com/intellij/roots/DependencyScopeTest.java index 5bb5646847db..c519dbb33055 100644 --- a/java/java-tests/testSrc/com/intellij/roots/DependencyScopeTest.java +++ b/java/java-tests/testSrc/com/intellij/roots/DependencyScopeTest.java @@ -10,7 +10,6 @@ import com.intellij.testFramework.fixtures.impl.LightTempDirTestFixtureImpl; import com.intellij.util.PathsList; import java.io.IOException; -import java.util.Collection; /** * @author yole @@ -39,9 +38,9 @@ public class DependencyScopeTest extends ModuleTestCase { assertFalse(moduleA.getModuleWithDependenciesAndLibrariesScope(false).contains(classB)); assertFalse(moduleA.getModuleWithDependenciesAndLibrariesScope(false).isSearchInModuleContent(moduleB)); - final Collection compilationClasspath = getCompilationClasspath(moduleA); - assertEquals(1, compilationClasspath.size()); - final Collection productionCompilationClasspath = getProductionCompileClasspath(moduleA); + final VirtualFile[] compilationClasspath = getCompilationClasspath(moduleA); + assertEquals(1, compilationClasspath.length); + final VirtualFile[] productionCompilationClasspath = getProductionCompileClasspath(moduleA); assertEmpty(productionCompilationClasspath); final PathsList pathsList = OrderEnumerator.orderEntries(moduleA).recursively().getPathsList(); @@ -78,20 +77,20 @@ public class DependencyScopeTest extends ModuleTestCase { assertTrue(m.getModuleWithDependenciesAndLibrariesScope(true).contains(libraryClass)); assertFalse(m.getModuleWithDependenciesAndLibrariesScope(false).contains(libraryClass)); - final Collection compilationClasspath = getCompilationClasspath(m); - assertEquals(1, compilationClasspath.size()); - final Collection productionCompilationClasspath = getProductionCompileClasspath(m); + final VirtualFile[] compilationClasspath = getCompilationClasspath(m); + assertEquals(1, compilationClasspath.length); + final VirtualFile[] productionCompilationClasspath = getProductionCompileClasspath(m); assertEmpty(productionCompilationClasspath); } public void testRuntimeModuleDependency() throws IOException { Module moduleA = createModule("a.iml", StdModuleTypes.JAVA); addDependentModule(moduleA, DependencyScope.RUNTIME); - final Collection runtimeClasspath = getRuntimeClasspath(moduleA); - assertEquals(1, runtimeClasspath.size()); - final Collection compilationClasspath = getCompilationClasspath(moduleA); - assertEquals(1, compilationClasspath.size()); - Collection production = getProductionCompileClasspath(moduleA); + final VirtualFile[] runtimeClasspath = getRuntimeClasspath(moduleA); + assertEquals(1, runtimeClasspath.length); + final VirtualFile[] compilationClasspath = getCompilationClasspath(moduleA); + assertEquals(1, compilationClasspath.length); + VirtualFile[] production = getProductionCompileClasspath(moduleA); assertEmpty(production); } @@ -99,12 +98,12 @@ public class DependencyScopeTest extends ModuleTestCase { Module m = createModule("a.iml", StdModuleTypes.JAVA); VirtualFile libraryRoot = addLibrary(m, DependencyScope.RUNTIME); - final Collection runtimeClasspath = getRuntimeClasspath(m); + final VirtualFile[] runtimeClasspath = getRuntimeClasspath(m); assertOrderedEquals(runtimeClasspath, libraryRoot); - final Collection compilationClasspath = getCompilationClasspath(m); - assertEquals(1, compilationClasspath.size()); - Collection production = getProductionCompileClasspath(m); + final VirtualFile[] compilationClasspath = getCompilationClasspath(m); + assertEquals(1, compilationClasspath.length); + VirtualFile[] production = getProductionCompileClasspath(m); assertEmpty(production); VirtualFile libraryClass = myFixture.createFile("lib/Test.java", "public class Test { }"); @@ -115,20 +114,20 @@ public class DependencyScopeTest extends ModuleTestCase { public void testProvidedModuleDependency() throws IOException { Module moduleA = createModule("a.iml", StdModuleTypes.JAVA); addDependentModule(moduleA, DependencyScope.PROVIDED); - Collection runtimeClasspath = getRuntimeClasspath(moduleA); + VirtualFile[] runtimeClasspath = getRuntimeClasspath(moduleA); assertEmpty(runtimeClasspath); - final Collection compilationClasspath = getCompilationClasspath(moduleA); - assertEquals(1, compilationClasspath.size()); + final VirtualFile[] compilationClasspath = getCompilationClasspath(moduleA); + assertEquals(1, compilationClasspath.length); } public void testProvidedLibraryDependency() throws IOException { Module m = createModule("a.iml", StdModuleTypes.JAVA); VirtualFile libraryRoot = addLibrary(m, DependencyScope.PROVIDED); - final Collection runtimeClasspath = getRuntimeClasspath(m); + final VirtualFile[] runtimeClasspath = getRuntimeClasspath(m); assertEmpty(runtimeClasspath); - final Collection compilationClasspath = getCompilationClasspath(m); + final VirtualFile[] compilationClasspath = getCompilationClasspath(m); assertOrderedEquals(compilationClasspath, libraryRoot); VirtualFile libraryClass = myFixture.createFile("lib/Test.java", "public class Test { }"); @@ -136,15 +135,15 @@ public class DependencyScopeTest extends ModuleTestCase { assertTrue(m.getModuleWithDependenciesAndLibrariesScope(false).contains(libraryClass)); } - private static Collection getRuntimeClasspath(Module m) { + private static VirtualFile[] getRuntimeClasspath(Module m) { return ModuleRootManager.getInstance(m).orderEntries().productionOnly().runtimeOnly().recursively().getClassesRoots(); } - private static Collection getProductionCompileClasspath(Module moduleA) { + private static VirtualFile[] getProductionCompileClasspath(Module moduleA) { return ModuleRootManager.getInstance(moduleA).orderEntries().productionOnly().compileOnly().recursively().exportedOnly().getClassesRoots(); } - private static Collection getCompilationClasspath(Module m) { + private static VirtualFile[] getCompilationClasspath(Module m) { return ModuleRootManager.getInstance(m).orderEntries().recursively().exportedOnly().getClassesRoots(); } diff --git a/java/java-tests/testSrc/com/intellij/roots/OrderEnumeratorTest.java b/java/java-tests/testSrc/com/intellij/roots/OrderEnumeratorTest.java index b6db1a0ed3e3..91dbbe4e0630 100644 --- a/java/java-tests/testSrc/com/intellij/roots/OrderEnumeratorTest.java +++ b/java/java-tests/testSrc/com/intellij/roots/OrderEnumeratorTest.java @@ -3,9 +3,13 @@ package com.intellij.roots; import com.intellij.openapi.module.Module; import com.intellij.openapi.roots.DependencyScope; import com.intellij.openapi.roots.OrderEnumerator; +import com.intellij.openapi.roots.OrderRootsEnumerator; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.PathsList; +import com.intellij.util.ArrayUtil; + +import java.util.ArrayList; +import java.util.List; import static com.intellij.openapi.roots.OrderEnumerator.orderEntries; @@ -89,14 +93,51 @@ public class OrderEnumeratorTest extends ModuleRootManagerTestCase { assertClassRoots(orderEntries(myModule).exportedOnly()); } + public void testCaching() throws Exception { + final VirtualFile[] roots = orderEntries(myModule).classes().usingCache().getRoots(); + assertOrderedEquals(roots, getRtJar()); + assertSame(roots, orderEntries(myModule).classes().usingCache().getRoots()); + final VirtualFile[] rootsWithoutSdk = orderEntries(myModule).withoutSdk().classes().usingCache().getRoots(); + assertEmpty(rootsWithoutSdk); + assertSame(roots, orderEntries(myModule).classes().usingCache().getRoots()); + assertSame(rootsWithoutSdk, orderEntries(myModule).withoutSdk().classes().usingCache().getRoots()); + + addLibraryDependency(myModule, createJDomLibrary()); + + assertRoots(orderEntries(myModule).classes().usingCache().getPathsList(), getRtJar(), getJDomJar()); + assertRoots(orderEntries(myModule).withoutSdk().classes().usingCache().getPathsList(), getJDomJar()); + } + + public void testCachingUrls() throws Exception { + final String[] urls = orderEntries(myModule).classes().usingCache().getUrls(); + assertOrderedEquals(urls, getRtJar().getUrl()); + assertSame(urls, orderEntries(myModule).classes().usingCache().getUrls()); + + final String[] sourceUrls = orderEntries(myModule).sources().usingCache().getUrls(); + assertEmpty(sourceUrls); + assertSame(urls, orderEntries(myModule).classes().usingCache().getUrls()); + assertSame(sourceUrls, orderEntries(myModule).sources().usingCache().getUrls()); + + addLibraryDependency(myModule, createJDomLibrary()); + assertOrderedEquals(orderEntries(myModule).classes().usingCache().getUrls(), getRtJar().getUrl(), getJDomJar().getUrl()); + assertOrderedEquals(orderEntries(myModule).sources().usingCache().getUrls(), getJDomSources().getUrl()); + } + private static void assertClassRoots(final OrderEnumerator enumerator, VirtualFile... files) { - assertRoots(enumerator.getPathsList(), files); + assertEnumeratorRoots(enumerator.classes(), files); } private static void assertSourceRoots(final OrderEnumerator enumerator, VirtualFile... files) { - final PathsList result = new PathsList(); - enumerator.sources().collectPaths(result); - assertRoots(result, files); + assertEnumeratorRoots(enumerator.sources(), files); + } + + private static void assertEnumeratorRoots(OrderRootsEnumerator rootsEnumerator, VirtualFile... files) { + assertOrderedEquals(rootsEnumerator.getRoots(), files); + List expectedUrls = new ArrayList(); + for (VirtualFile file : files) { + expectedUrls.add(file.getUrl()); + } + assertOrderedEquals(rootsEnumerator.getUrls(), ArrayUtil.toStringArray(expectedUrls)); } } diff --git a/platform/lang-api/src/com/intellij/openapi/roots/OrderEnumerationHandler.java b/platform/lang-api/src/com/intellij/openapi/roots/OrderEnumerationHandler.java index 9c5378ab29c3..53aa45de4f55 100644 --- a/platform/lang-api/src/com/intellij/openapi/roots/OrderEnumerationHandler.java +++ b/platform/lang-api/src/com/intellij/openapi/roots/OrderEnumerationHandler.java @@ -18,10 +18,9 @@ package com.intellij.openapi.roots; import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; -import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; -import java.util.List; +import java.util.Collection; /** * @author nik @@ -37,7 +36,7 @@ public abstract class OrderEnumerationHandler { return true; } - public boolean addCustomOutput(@NotNull ModuleOrderEntry orderEntry, boolean productionOnly, @NotNull List result) { + public boolean addCustomOutput(@NotNull ModuleOrderEntry orderEntry, boolean productionOnly, @NotNull Collection urls) { return false; } } diff --git a/platform/lang-api/src/com/intellij/openapi/roots/OrderEnumerator.java b/platform/lang-api/src/com/intellij/openapi/roots/OrderEnumerator.java index dce115e7e22f..a477da23c6d4 100644 --- a/platform/lang-api/src/com/intellij/openapi/roots/OrderEnumerator.java +++ b/platform/lang-api/src/com/intellij/openapi/roots/OrderEnumerator.java @@ -25,8 +25,6 @@ import com.intellij.util.PathsList; import com.intellij.util.Processor; import org.jetbrains.annotations.NotNull; -import java.util.Collection; - /** * Interface for convenient processing dependencies of a module or a project. Allows to process {@link OrderEntry}s and collect classes * and source roots.

@@ -112,14 +110,14 @@ public abstract class OrderEnumerator { /** * @return classes roots for all entries processed by this enumerator */ - public Collection getClassesRoots() { + public VirtualFile[] getClassesRoots() { return classes().getRoots(); } /** * @return source roots for all entries processed by this enumerator */ - public Collection getSourceRoots() { + public VirtualFile[] getSourceRoots() { return sources().getRoots(); } diff --git a/platform/lang-api/src/com/intellij/openapi/roots/OrderRootsEnumerator.java b/platform/lang-api/src/com/intellij/openapi/roots/OrderRootsEnumerator.java index f363c4c1273b..5664b1ece922 100644 --- a/platform/lang-api/src/com/intellij/openapi/roots/OrderRootsEnumerator.java +++ b/platform/lang-api/src/com/intellij/openapi/roots/OrderRootsEnumerator.java @@ -19,10 +19,8 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.PathsList; import org.jetbrains.annotations.NotNull; -import java.util.Collection; - /** - * Interface for processing classes or source roots. + * Interface for processing roots of OrderEntry's from {@link OrderEnumerator}. * * @see OrderEnumerator#classes() * @see OrderEnumerator#sources() @@ -30,12 +28,34 @@ import java.util.Collection; * @author nik */ public interface OrderRootsEnumerator { + /** + * @return all roots processed by this enumerator + */ @NotNull - Collection getRoots(); + VirtualFile[] getRoots(); + /** + * @return urls of all roots processed by this enumerator + */ + @NotNull + String[] getUrls(); + + /** + * @return list of path to all roots processed by this enumerator + */ @NotNull PathsList getPathsList(); + /** + * Add all source roots processed by this enumerator to list + * @param list list + */ void collectPaths(@NotNull PathsList list); + /** + * If roots for this enumerator are already evaluated the cached result will be used. Otherwise roots will be evaluated and cached for + * subsequent calls + * @return this instance + */ + OrderRootsEnumerator usingCache(); } diff --git a/platform/lang-impl/src/com/intellij/find/impl/FindInProjectUtil.java b/platform/lang-impl/src/com/intellij/find/impl/FindInProjectUtil.java index ddb7de66e5ee..21108288998c 100644 --- a/platform/lang-impl/src/com/intellij/find/impl/FindInProjectUtil.java +++ b/platform/lang-impl/src/com/intellij/find/impl/FindInProjectUtil.java @@ -442,7 +442,7 @@ public class FindInProjectUtil { SearchScope customScope = findModel.getCustomScope(); if (success && customScope instanceof GlobalSearchScope && ((GlobalSearchScope)customScope).isSearchInLibraries()) { OrderEnumerator enumerator = module == null ? OrderEnumerator.orderEntries(project) : OrderEnumerator.orderEntries(module); - final Collection librarySources = enumerator.withoutModuleSourceEntries().withoutDepModules().getSourceRoots(); + final VirtualFile[] librarySources = enumerator.withoutModuleSourceEntries().withoutDepModules().getSourceRoots(); iterateAll(librarySources, (GlobalSearchScope)customScope, iterator); } } @@ -462,7 +462,7 @@ public class FindInProjectUtil { } } - private static boolean iterateAll(Collection files, final GlobalSearchScope searchScope, final ContentIterator iterator) { + private static boolean iterateAll(VirtualFile[] files, final GlobalSearchScope searchScope, final ContentIterator iterator) { final FileTypeManager fileTypeManager = FileTypeManager.getInstance(); final VirtualFileFilter contentFilter = new VirtualFileFilter() { public boolean accept(final VirtualFile file) { diff --git a/platform/lang-impl/src/com/intellij/openapi/roots/impl/ModuleOrderEnumerator.java b/platform/lang-impl/src/com/intellij/openapi/roots/impl/ModuleOrderEnumerator.java index 0d954cbc014c..4fe6590f41ee 100644 --- a/platform/lang-impl/src/com/intellij/openapi/roots/impl/ModuleOrderEnumerator.java +++ b/platform/lang-impl/src/com/intellij/openapi/roots/impl/ModuleOrderEnumerator.java @@ -27,8 +27,8 @@ import org.jetbrains.annotations.NotNull; public class ModuleOrderEnumerator extends OrderEnumeratorBase { private final RootModelImpl myRootModel; - public ModuleOrderEnumerator(RootModelImpl rootModel) { - super(rootModel.getModule(), rootModel.getProject()); + public ModuleOrderEnumerator(RootModelImpl rootModel, final OrderRootsCache cache) { + super(rootModel.getModule(), rootModel.getProject(), cache); myRootModel = rootModel; } diff --git a/platform/lang-impl/src/com/intellij/openapi/roots/impl/ModuleRootManagerImpl.java b/platform/lang-impl/src/com/intellij/openapi/roots/impl/ModuleRootManagerImpl.java index 2124f6d0e763..58f326159a20 100644 --- a/platform/lang-impl/src/com/intellij/openapi/roots/impl/ModuleRootManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/openapi/roots/impl/ModuleRootManagerImpl.java @@ -76,6 +76,7 @@ public class ModuleRootManagerImpl extends ModuleRootManager implements ModuleCo private final ModuleFileIndexImpl myFileIndex; private boolean myIsDisposed = false; private boolean isModuleAdded = false; + private final OrderRootsCache myOrderRootsCache; private final Map> myCachedFiles = new THashMap>(); private final Map> myCachedExportedFiles = new THashMap>(); private final Map myModelCreations = new THashMap(); @@ -92,6 +93,7 @@ public class ModuleRootManagerImpl extends ModuleRootManager implements ModuleCo myFileIndex = new ModuleFileIndexImpl(myModule, directoryIndex); myRootModel = new RootModelImpl(this, myProjectRootManager, myFilePointerManager); + myOrderRootsCache = new OrderRootsCache(module); } @NotNull @@ -223,19 +225,7 @@ public class ModuleRootManagerImpl extends ModuleRootManager implements ModuleCo } myCachedFiles.put(type, cachedFiles); } - return convertPointers(cachedFiles); - } - - private static VirtualFile[] convertPointers(final Set cachedFiles) { - final LinkedHashSet result = new LinkedHashSet(); - for (VirtualFilePointer cachedFile : cachedFiles) { - final VirtualFile virtualFile = cachedFile.getFile(); - if (virtualFile != null) { - result.add(virtualFile); - } - } - - return VfsUtil.toVirtualFileArray(result); + return VfsUtil.toVirtualFileArray(OrderRootsCache.convertPointersToFiles(cachedFiles)); } @NotNull @@ -360,7 +350,7 @@ public class ModuleRootManagerImpl extends ModuleRootManager implements ModuleCo @NotNull @Override public OrderEnumerator orderEntries() { - return myRootModel.orderEntries(); + return new ModuleOrderEnumerator(myRootModel, myOrderRootsCache); } List getUrlsForOtherModules(OrderRootType rootType, Set processed) { @@ -420,7 +410,7 @@ public class ModuleRootManagerImpl extends ModuleRootManager implements ModuleCo myCachedExportedFiles.put(rootType, filePointers); } - return convertPointers(filePointers); + return VfsUtil.toVirtualFileArray(OrderRootsCache.convertPointersToFiles(filePointers)); } @NotNull @@ -534,6 +524,7 @@ public class ModuleRootManagerImpl extends ModuleRootManager implements ModuleCo public void dropCaches() { myCachedFiles.clear(); myCachedExportedFiles.clear(); + myOrderRootsCache.clearCache(); } public ModuleRootManagerState getState() { diff --git a/platform/lang-impl/src/com/intellij/openapi/roots/impl/OrderEnumeratorBase.java b/platform/lang-impl/src/com/intellij/openapi/roots/impl/OrderEnumeratorBase.java index 6617b67f7695..1db9ab8a77d4 100644 --- a/platform/lang-impl/src/com/intellij/openapi/roots/impl/OrderEnumeratorBase.java +++ b/platform/lang-impl/src/com/intellij/openapi/roots/impl/OrderEnumeratorBase.java @@ -15,6 +15,7 @@ */ package com.intellij.openapi.roots.impl; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.*; @@ -22,11 +23,15 @@ import com.intellij.openapi.roots.libraries.Library; import com.intellij.openapi.roots.ui.configuration.ModulesProvider; import com.intellij.openapi.util.Condition; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.util.Processor; import com.intellij.util.SmartList; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Set; @@ -34,6 +39,7 @@ import java.util.Set; * @author nik */ abstract class OrderEnumeratorBase extends OrderEnumerator { + private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.roots.impl.OrderEnumeratorBase"); private boolean myProductionOnly; private boolean myCompileOnly; private boolean myRuntimeOnly; @@ -47,8 +53,10 @@ abstract class OrderEnumeratorBase extends OrderEnumerator { private Condition myCondition; private List myCustomHandlers; private ModulesProvider myModulesProvider; + private OrderRootsCache myCache; - public OrderEnumeratorBase(@Nullable Module module, @NotNull Project project) { + public OrderEnumeratorBase(@Nullable Module module, @NotNull Project project, @Nullable OrderRootsCache cache) { + myCache = cache; for (OrderEnumerationHandler handler : OrderEnumerationHandler.EP_NAME.getExtensions()) { if (handler.isApplicable(project) && (module == null || handler.isApplicable(module))) { if (myCustomHandlers == null) { @@ -147,6 +155,29 @@ abstract class OrderEnumeratorBase extends OrderEnumerator { return ModuleRootManager.getInstance(module); } + public OrderRootsCache getCache() { + LOG.assertTrue(myCache != null, "Caching supported only for OrderEnumerator obtained by using OrderEnumerator.orderEntries(module) or ModuleRootManager.getInstance(module).orderEntries() methods"); + LOG.assertTrue(myCondition == null, "Caching not supported for OrderEnumerator with 'satisfying(Condition)' option"); + LOG.assertTrue(myModulesProvider == null, "Caching not supported for OrderEnumerator with 'using(ModulesProvider)' option"); + return myCache; + } + + public int getFlags() { + int flags = 0; + int i = 0; + if (myProductionOnly) flags |= 1 << (i++); + if (myCompileOnly) flags |= 1 << (i++); + if (myRuntimeOnly) flags |= 1 << (i++); + if (myWithoutJdk) flags |= 1 << (i++); + if (myWithoutLibraries) flags |= 1 << (i++); + if (myWithoutDepModules) flags |= 1 << (i++); + if (myWithoutThisModuleContent) flags |= 1 << (i++); + if (myRecursively) flags |= 1 << (i++); + if (myRecursivelyExportedOnly) flags |= 1 << (i++); + if (myExportedOnly) flags |= 1 << i; + return flags; + } + protected void processEntries(final ModuleRootModel rootModel, Processor processor, Set processed, boolean firstLevel) { @@ -235,10 +266,26 @@ abstract class OrderEnumeratorBase extends OrderEnumerator { return processor.myValue; } - boolean addCustomOutput(ModuleOrderEntry moduleOrderEntry, List list) { + boolean addCustomOutput(ModuleOrderEntry moduleOrderEntry, Collection result) { if (myCustomHandlers != null) { for (OrderEnumerationHandler handler : myCustomHandlers) { - if (handler.addCustomOutput(moduleOrderEntry, myProductionOnly, list)) { + final List urls = new ArrayList(); + final boolean added = handler.addCustomOutput(moduleOrderEntry, myProductionOnly, urls); + for (String url : urls) { + ContainerUtil.addIfNotNull(VirtualFileManager.getInstance().findFileByUrl(url), result); + } + if (added) { + return true; + } + } + } + return false; + } + + boolean addCustomOutputUrls(ModuleOrderEntry moduleOrderEntry, Collection result) { + if (myCustomHandlers != null) { + for (OrderEnumerationHandler handler : myCustomHandlers) { + if (handler.addCustomOutput(moduleOrderEntry, myProductionOnly, result)) { return true; } } diff --git a/platform/lang-impl/src/com/intellij/openapi/roots/impl/OrderRootsCache.java b/platform/lang-impl/src/com/intellij/openapi/roots/impl/OrderRootsCache.java new file mode 100644 index 000000000000..8936896ce08c --- /dev/null +++ b/platform/lang-impl/src/com/intellij/openapi/roots/impl/OrderRootsCache.java @@ -0,0 +1,105 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.roots.impl; + +import com.intellij.openapi.Disposable; +import com.intellij.openapi.roots.OrderRootType; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.pointers.VirtualFilePointer; +import com.intellij.openapi.vfs.pointers.VirtualFilePointerContainer; +import com.intellij.openapi.vfs.pointers.VirtualFilePointerManager; +import com.intellij.util.containers.LockPoolSynchronizedMap; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.Set; + +/** + * @author nik + */ +public class OrderRootsCache { + private final LockPoolSynchronizedMap myRoots = new LockPoolSynchronizedMap(); + private final Disposable myParentDisposable; + + public OrderRootsCache(Disposable parentDisposable) { + myParentDisposable = parentDisposable; + } + + public VirtualFilePointerContainer setCachedRoots(OrderRootType rootType, int flags, Collection urls) { + final VirtualFilePointerContainer container = VirtualFilePointerManager.getInstance().createContainer(myParentDisposable); + for (String url : urls) { + container.add(url); + } + myRoots.put(new CacheKey(rootType, flags), container); + return container; + } + + @Nullable + public VirtualFile[] getCachedRoots(OrderRootType rootType, int flags) { + final VirtualFilePointerContainer cached = myRoots.get(new CacheKey(rootType, flags)); + return cached != null ? cached.getFiles() : null; + } + + @Nullable + public String[] getCachedUrls(OrderRootType rootType, int flags) { + final VirtualFilePointerContainer cached = myRoots.get(new CacheKey(rootType, flags)); + return cached != null ? cached.getUrls() : null; + } + + public void clearCache() { + for (VirtualFilePointerContainer container : myRoots.values()) { + container.killAll(); + } + myRoots.clear(); + } + + public static Set convertPointersToFiles(Set cachedFiles) { + final LinkedHashSet result = new LinkedHashSet(); + for (VirtualFilePointer cachedFile : cachedFiles) { + final VirtualFile virtualFile = cachedFile.getFile(); + if (virtualFile != null) { + result.add(virtualFile); + } + } + return result; + } + + private static final class CacheKey { + private final OrderRootType myRootType; + private int myFlags; + + private CacheKey(OrderRootType rootType, int flags) { + myRootType = rootType; + myFlags = flags; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + CacheKey cacheKey = (CacheKey)o; + return myFlags == cacheKey.myFlags && myRootType.equals(cacheKey.myRootType); + + } + + @Override + public int hashCode() { + return 31 * myRootType.hashCode() + myFlags; + } + } +} diff --git a/platform/lang-impl/src/com/intellij/openapi/roots/impl/OrderRootsEnumeratorImpl.java b/platform/lang-impl/src/com/intellij/openapi/roots/impl/OrderRootsEnumeratorImpl.java index 6ae3bf930099..49d66c7ca08a 100644 --- a/platform/lang-impl/src/com/intellij/openapi/roots/impl/OrderRootsEnumeratorImpl.java +++ b/platform/lang-impl/src/com/intellij/openapi/roots/impl/OrderRootsEnumeratorImpl.java @@ -17,15 +17,14 @@ package com.intellij.openapi.roots.impl; import com.intellij.openapi.module.Module; import com.intellij.openapi.roots.*; +import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.ArrayUtil; import com.intellij.util.PathsList; import com.intellij.util.Processor; import org.jetbrains.annotations.NotNull; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; +import java.util.*; /** * @author nik @@ -33,6 +32,7 @@ import java.util.List; public class OrderRootsEnumeratorImpl implements OrderRootsEnumerator { private final OrderEnumeratorBase myOrderEnumerator; private final OrderRootType myRootType; + private boolean myUsingCache; public OrderRootsEnumeratorImpl(OrderEnumeratorBase orderEnumerator, OrderRootType rootType) { myOrderEnumerator = orderEnumerator; @@ -41,9 +41,94 @@ public class OrderRootsEnumeratorImpl implements OrderRootsEnumerator { @NotNull @Override - public Collection getRoots() { - final List result = new ArrayList(); - collectRoots(result); + public VirtualFile[] getRoots() { + if (myUsingCache) { + final OrderRootsCache cache = myOrderEnumerator.getCache(); + if (cache != null) { + final int flags = myOrderEnumerator.getFlags(); + final VirtualFile[] cached = cache.getCachedRoots(myRootType, flags); + if (cached == null) { + return cache.setCachedRoots(myRootType, flags, computeRootsUrls()).getFiles(); + } + else { + return cached; + } + } + } + + return VfsUtil.toVirtualFileArray(computeRoots()); + } + + @NotNull + @Override + public String[] getUrls() { + if (myUsingCache) { + final OrderRootsCache cache = myOrderEnumerator.getCache(); + if (cache != null) { + final int flags = myOrderEnumerator.getFlags(); + String[] cached = cache.getCachedUrls(myRootType, flags); + if (cached == null) { + return cache.setCachedRoots(myRootType, flags, computeRootsUrls()).getUrls(); + } + else { + return cached; + } + } + } + return ArrayUtil.toStringArray(computeRootsUrls()); + } + + private Collection computeRoots() { + final Collection result = new LinkedHashSet(); + myOrderEnumerator.forEach(new Processor() { + @Override + public boolean process(OrderEntry orderEntry) { + if (orderEntry instanceof ModuleSourceOrderEntry) { + collectModuleRoots(((ModuleSourceOrderEntry)orderEntry).getRootModel(), result); + } + else if (orderEntry instanceof ModuleOrderEntry) { + ModuleOrderEntry moduleOrderEntry = (ModuleOrderEntry)orderEntry; + final Module module = moduleOrderEntry.getModule(); + if (module != null) { + if (myOrderEnumerator.addCustomOutput(moduleOrderEntry, result)) { + return true; + } + collectModuleRoots(myOrderEnumerator.getRootModel(module), result); + } + } + else { + Collections.addAll(result, orderEntry.getFiles(myRootType)); + } + return true; + } + }); + return result; + } + + private Collection computeRootsUrls() { + final Collection result = new LinkedHashSet(); + myOrderEnumerator.forEach(new Processor() { + @Override + public boolean process(OrderEntry orderEntry) { + if (orderEntry instanceof ModuleSourceOrderEntry) { + collectModuleRootsUrls(((ModuleSourceOrderEntry)orderEntry).getRootModel(), result); + } + else if (orderEntry instanceof ModuleOrderEntry) { + ModuleOrderEntry moduleOrderEntry = (ModuleOrderEntry)orderEntry; + final Module module = moduleOrderEntry.getModule(); + if (module != null) { + if (myOrderEnumerator.addCustomOutputUrls(moduleOrderEntry, result)) { + return true; + } + collectModuleRootsUrls(myOrderEnumerator.getRootModel(module), result); + } + } + else { + Collections.addAll(result, orderEntry.getUrls(myRootType)); + } + return true; + } + }); return result; } @@ -60,64 +145,56 @@ public class OrderRootsEnumeratorImpl implements OrderRootsEnumerator { list.addVirtualFiles(getRoots()); } - private void collectRoots(final List list) { - myOrderEnumerator.forEach(new Processor() { - @Override - public boolean process(OrderEntry orderEntry) { - if (orderEntry instanceof ModuleSourceOrderEntry) { - collectModulePaths(((ModuleSourceOrderEntry)orderEntry).getRootModel(), list); - } - else if (orderEntry instanceof ModuleOrderEntry) { - ModuleOrderEntry moduleOrderEntry = (ModuleOrderEntry)orderEntry; - final Module module = moduleOrderEntry.getModule(); - if (module != null) { - if (myOrderEnumerator.addCustomOutput(moduleOrderEntry, list)) { - return true; - } - collectModulePaths(myOrderEnumerator.getRootModel(module), list); - } - } - else { - Collections.addAll(list, orderEntry.getFiles(myRootType)); - } - return true; - } - }); + @Override + public OrderRootsEnumerator usingCache() { + myUsingCache = true; + return this; } - private void collectModulePaths(ModuleRootModel rootModel, List list) { + private void collectModuleRoots(ModuleRootModel rootModel, Collection result) { if (myRootType.equals(OrderRootType.SOURCES)) { if (myOrderEnumerator.isProductionOnly()) { - ContentEntry[] contentEntries = rootModel.getContentEntries(); - for (ContentEntry contentEntry : contentEntries) { + for (ContentEntry contentEntry : rootModel.getContentEntries()) { for (SourceFolder folder : contentEntry.getSourceFolders()) { VirtualFile root = folder.getFile(); if (root != null && !folder.isTestSource()) { - list.add(root); + result.add(root); } } } } else { - Collections.addAll(list, rootModel.getSourceRoots()); + Collections.addAll(result, rootModel.getSourceRoots()); } } else if (myRootType.equals(OrderRootType.CLASSES)) { final CompilerModuleExtension extension = rootModel.getModuleExtension(CompilerModuleExtension.class); if (extension != null) { - VirtualFile testOutput = extension.getCompilerOutputPathForTests(); - if (myOrderEnumerator.isProductionOnly()) { - testOutput = null; - } + Collections.addAll(result, extension.getOutputRoots(!myOrderEnumerator.isProductionOnly())); + } + } + } - if (testOutput != null) { - list.add(testOutput); - } - VirtualFile output = extension.getCompilerOutputPath(); - if (output != null && !output.equals(testOutput)) { - list.add(output); + private void collectModuleRootsUrls(ModuleRootModel rootModel, Collection result) { + if (myRootType.equals(OrderRootType.SOURCES)) { + if (myOrderEnumerator.isProductionOnly()) { + for (ContentEntry contentEntry : rootModel.getContentEntries()) { + for (SourceFolder folder : contentEntry.getSourceFolders()) { + if (!folder.isTestSource()) { + result.add(folder.getUrl()); + } + } } } + else { + Collections.addAll(result, rootModel.getSourceRootUrls()); + } + } + else if (myRootType.equals(OrderRootType.CLASSES)) { + final CompilerModuleExtension extension = rootModel.getModuleExtension(CompilerModuleExtension.class); + if (extension != null) { + Collections.addAll(result, extension.getOutputRootUrls(!myOrderEnumerator.isProductionOnly())); + } } } diff --git a/platform/lang-impl/src/com/intellij/openapi/roots/impl/ProjectOrderEnumerator.java b/platform/lang-impl/src/com/intellij/openapi/roots/impl/ProjectOrderEnumerator.java index 2d0582a394ef..9b31ead7d894 100644 --- a/platform/lang-impl/src/com/intellij/openapi/roots/impl/ProjectOrderEnumerator.java +++ b/platform/lang-impl/src/com/intellij/openapi/roots/impl/ProjectOrderEnumerator.java @@ -31,7 +31,7 @@ public class ProjectOrderEnumerator extends OrderEnumeratorBase { private Project myProject; public ProjectOrderEnumerator(Project project) { - super(null, project); + super(null, project, null); myProject = project; } diff --git a/platform/lang-impl/src/com/intellij/openapi/roots/impl/RootModelImpl.java b/platform/lang-impl/src/com/intellij/openapi/roots/impl/RootModelImpl.java index acaa1ec8ded0..8fc6eb058f81 100644 --- a/platform/lang-impl/src/com/intellij/openapi/roots/impl/RootModelImpl.java +++ b/platform/lang-impl/src/com/intellij/openapi/roots/impl/RootModelImpl.java @@ -521,7 +521,7 @@ public class RootModelImpl implements ModifiableRootModel { @NotNull @Override public OrderEnumerator orderEntries() { - return new ModuleOrderEnumerator(this); + return new ModuleOrderEnumerator(this, null); } public Project getProject() { diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/execution/MavenOrderEnumeratorHandler.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/execution/MavenOrderEnumeratorHandler.java index 9bde6f9599b4..ff6139e50130 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/execution/MavenOrderEnumeratorHandler.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/execution/MavenOrderEnumeratorHandler.java @@ -20,14 +20,13 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.CompilerModuleExtension; import com.intellij.openapi.roots.ModuleOrderEntry; import com.intellij.openapi.roots.OrderEnumerationHandler; -import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.idea.maven.model.MavenArtifact; import org.jetbrains.idea.maven.model.MavenConstants; import org.jetbrains.idea.maven.project.MavenProject; import org.jetbrains.idea.maven.project.MavenProjectsManager; -import java.util.List; +import java.util.Collection; /** * @author nik @@ -52,7 +51,7 @@ public class MavenOrderEnumeratorHandler extends OrderEnumerationHandler { @Override public boolean addCustomOutput(@NotNull ModuleOrderEntry orderEntry, boolean productionOnly, - @NotNull List result) { + @NotNull Collection urls) { final Module ownerModule = orderEntry.getOwnerModule(); final Module depModule = orderEntry.getModule(); if (depModule == null) return false; @@ -70,18 +69,18 @@ public class MavenOrderEnumeratorHandler extends OrderEnumerationHandler { if (productionOnly && MavenConstants.SCOPE_TEST.equals(each.getScope())) continue; boolean isTestJar = MavenConstants.TYPE_TEST_JAR.equals(each.getType()) || "tests".equals(each.getClassifier()); - addOutput(depModule, isTestJar, result); + addOutput(depModule, isTestJar, urls); } return true; } - private static void addOutput(Module module, boolean tests, List result) { + private static void addOutput(Module module, boolean tests, Collection urls) { CompilerModuleExtension ex = CompilerModuleExtension.getInstance(module); if (ex == null) return; - VirtualFile output = tests ? ex.getCompilerOutputPathForTests() : ex.getCompilerOutputPath(); + String output = tests ? ex.getCompilerOutputUrlForTests() : ex.getCompilerOutputUrl(); if (output != null) { - result.add(output); + urls.add(output); } } } diff --git a/plugins/xpath/xpath-view/src/org/intellij/plugins/xpathView/search/SearchScope.java b/plugins/xpath/xpath-view/src/org/intellij/plugins/xpathView/search/SearchScope.java index f3dcfda9c368..9f4db0768274 100644 --- a/plugins/xpath/xpath-view/src/org/intellij/plugins/xpathView/search/SearchScope.java +++ b/plugins/xpath/xpath-view/src/org/intellij/plugins/xpathView/search/SearchScope.java @@ -39,6 +39,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; +import java.util.Collections; public class SearchScope implements JDOMExternalizable { @@ -214,8 +215,8 @@ public class SearchScope implements JDOMExternalizable { if (searchScope.isSearchInLibraries()) { final OrderEnumerator enumerator = OrderEnumerator.orderEntries(project).withoutModuleSourceEntries().withoutDepModules(); final Collection libraryFiles = new THashSet(); - libraryFiles.addAll(enumerator.getClassesRoots()); - libraryFiles.addAll(enumerator.getSourceRoots()); + Collections.addAll(libraryFiles, enumerator.getClassesRoots()); + Collections.addAll(libraryFiles, enumerator.getSourceRoots()); final Processor adapter = new Processor() { public boolean process(VirtualFile virtualFile) { return iterator.processFile(virtualFile);