blob: 05b0ef34be7e940b906b5cc50c945aead8b7a0b1 [file] [log] [blame]
/*
* Copyright 2000-2014 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.module.impl.scopes;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.roots.*;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiBundle;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.NotNullFunction;
import com.intellij.util.Processor;
import com.intellij.util.containers.ContainerUtil;
import gnu.trove.THashSet;
import gnu.trove.TObjectIntHashMap;
import org.intellij.lang.annotations.MagicConstant;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
import java.util.*;
public class ModuleWithDependenciesScope extends GlobalSearchScope {
public static final int COMPILE = 0x01;
public static final int LIBRARIES = 0x02;
public static final int MODULES = 0x04;
public static final int TESTS = 0x08;
public static final int RUNTIME = 0x10;
public static final int CONTENT = 0x20;
@MagicConstant(flags = {COMPILE, LIBRARIES, MODULES, TESTS, RUNTIME, CONTENT})
public @interface ScopeConstant {}
private final Module myModule;
@ScopeConstant
private final int myOptions;
private final ProjectFileIndex myProjectFileIndex;
private final Set<Module> myModules;
private final TObjectIntHashMap<VirtualFile> myRoots = new TObjectIntHashMap<VirtualFile>();
public ModuleWithDependenciesScope(@NotNull Module module, @ScopeConstant int options) {
super(module.getProject());
myModule = module;
myOptions = options;
myProjectFileIndex = ProjectRootManager.getInstance(module.getProject()).getFileIndex();
OrderEnumerator en = ModuleRootManager.getInstance(module).orderEntries();
en.recursively();
if (hasOption(COMPILE)) {
en.exportedOnly().compileOnly();
}
if (hasOption(RUNTIME)) {
en.runtimeOnly();
}
if (!hasOption(LIBRARIES)) en.withoutLibraries().withoutSdk();
if (!hasOption(MODULES)) en.withoutDepModules();
if (!hasOption(TESTS)) en.productionOnly();
final LinkedHashSet<Module> modules = ContainerUtil.newLinkedHashSet();
en.forEach(new Processor<OrderEntry>() {
@Override
public boolean process(OrderEntry each) {
if (each instanceof ModuleOrderEntry) {
ContainerUtil.addIfNotNull(modules, ((ModuleOrderEntry)each).getModule());
}
else if (each instanceof ModuleSourceOrderEntry) {
ContainerUtil.addIfNotNull(modules, each.getOwnerModule());
}
return true;
}
});
myModules = new THashSet<Module>(modules);
final LinkedHashSet<VirtualFile> roots = ContainerUtil.newLinkedHashSet();
if (hasOption(CONTENT)) {
for (Module m : modules) {
for (ContentEntry entry : ModuleRootManager.getInstance(m).getContentEntries()) {
ContainerUtil.addIfNotNull(entry.getFile(), roots);
}
}
}
else {
Collections.addAll(roots, en.roots(new NotNullFunction<OrderEntry, OrderRootType>() {
@NotNull
@Override
public OrderRootType fun(OrderEntry entry) {
if (entry instanceof ModuleOrderEntry || entry instanceof ModuleSourceOrderEntry) return OrderRootType.SOURCES;
return OrderRootType.CLASSES;
}
}).getRoots());
}
int i = 1;
for (VirtualFile root : roots) {
myRoots.put(root, i++);
}
}
@NotNull
public Module getModule() {
return myModule;
}
private boolean hasOption(@ScopeConstant int option) {
return (myOptions & option) != 0;
}
@NotNull
@Override
public String getDisplayName() {
return hasOption(COMPILE) ? PsiBundle.message("search.scope.module", myModule.getName())
: PsiBundle.message("search.scope.module.runtime", myModule.getName());
}
@Override
public boolean isSearchInModuleContent(@NotNull Module aModule) {
return myModules.contains(aModule);
}
@Override
public boolean isSearchInModuleContent(@NotNull Module aModule, boolean testSources) {
return isSearchInModuleContent(aModule) && (hasOption(TESTS) || !testSources);
}
@Override
public boolean isSearchInLibraries() {
return hasOption(LIBRARIES);
}
@Override
public boolean contains(@NotNull VirtualFile file) {
if (hasOption(CONTENT)) {
return myRoots.contains(myProjectFileIndex.getContentRootForFile(file));
}
if (myProjectFileIndex.isInContent(file) && myRoots.contains(myProjectFileIndex.getSourceRootForFile(file))) {
return true;
}
return myRoots.contains(myProjectFileIndex.getClassRootForFile(file));
}
@Override
public int compare(@NotNull VirtualFile file1, @NotNull VirtualFile file2) {
VirtualFile r1 = getFileRoot(file1);
VirtualFile r2 = getFileRoot(file2);
if (Comparing.equal(r1, r2)) return 0;
if (r1 == null) return -1;
if (r2 == null) return 1;
int i1 = myRoots.get(r1);
int i2 = myRoots.get(r2);
if (i1 == 0 && i2 == 0) return 0;
if (i1 > 0 && i2 > 0) return i2 - i1;
return i1 > 0 ? 1 : -1;
}
@Nullable
private VirtualFile getFileRoot(@NotNull VirtualFile file) {
if (myProjectFileIndex.isInContent(file)) {
return myProjectFileIndex.getSourceRootForFile(file);
}
return myProjectFileIndex.getClassRootForFile(file);
}
@TestOnly
public Collection<VirtualFile> getRoots() {
//noinspection unchecked
List<VirtualFile> result = (List)ContainerUtil.newArrayList(myRoots.keys());
Collections.sort(result, new Comparator<VirtualFile>() {
@Override
public int compare(VirtualFile o1, VirtualFile o2) {
return myRoots.get(o1) - myRoots.get(o2);
}
});
return result;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ModuleWithDependenciesScope that = (ModuleWithDependenciesScope)o;
return myOptions == that.myOptions && myModule.equals(that.myModule);
}
@Override
public int hashCode() {
return 31 * myModule.hashCode() + myOptions;
}
@Override
public String toString() {
return "Module with dependencies:" + myModule.getName() +
" compile:" + hasOption(COMPILE) +
" include libraries:" + hasOption(LIBRARIES) +
" include other modules:" + hasOption(MODULES) +
" include tests:" + hasOption(TESTS);
}
}