blob: b13df0560292d5045db5ee9ac15d6e104a99d5a7 [file] [log] [blame]
/*
* 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;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.*;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.util.NotNullFunction;
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.*;
/**
* @author nik
*/
abstract class OrderEnumeratorBase extends OrderEnumerator implements OrderEnumeratorSettings {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.roots.impl.OrderEnumeratorBase");
private boolean myProductionOnly;
private boolean myCompileOnly;
private boolean myRuntimeOnly;
private boolean myWithoutJdk;
private boolean myWithoutLibraries;
protected boolean myWithoutDepModules;
private boolean myWithoutModuleSourceEntries;
protected boolean myRecursively;
protected boolean myRecursivelyExportedOnly;
private boolean myExportedOnly;
private Condition<OrderEntry> myCondition;
private final List<OrderEnumerationHandler> myCustomHandlers;
protected RootModelProvider myModulesProvider;
private final OrderRootsCache myCache;
public OrderEnumeratorBase(@Nullable Module module, @NotNull Project project, @Nullable OrderRootsCache cache) {
myCache = cache;
List<OrderEnumerationHandler> customHandlers = null;
for (OrderEnumerationHandler.Factory handlerFactory : OrderEnumerationHandler.EP_NAME.getExtensions()) {
if (handlerFactory.isApplicable(project) && (module == null || handlerFactory.isApplicable(module))) {
if (customHandlers == null) {
customHandlers = new SmartList<OrderEnumerationHandler>();
}
customHandlers.add(handlerFactory.createHandler(module));
}
}
this.myCustomHandlers = customHandlers == null ? Collections.<OrderEnumerationHandler>emptyList() : customHandlers;
}
@Override
public OrderEnumerator productionOnly() {
myProductionOnly = true;
return this;
}
@Override
public OrderEnumerator compileOnly() {
myCompileOnly = true;
return this;
}
@Override
public OrderEnumerator runtimeOnly() {
myRuntimeOnly = true;
return this;
}
@Override
public OrderEnumerator withoutSdk() {
myWithoutJdk = true;
return this;
}
@Override
public OrderEnumerator withoutLibraries() {
myWithoutLibraries = true;
return this;
}
@Override
public OrderEnumerator withoutDepModules() {
myWithoutDepModules = true;
return this;
}
@Override
public OrderEnumerator withoutModuleSourceEntries() {
myWithoutModuleSourceEntries = true;
return this;
}
@Override
public OrderEnumerator recursively() {
myRecursively = true;
return this;
}
@Override
public OrderEnumerator exportedOnly() {
if (myRecursively) {
myRecursivelyExportedOnly = true;
}
else {
myExportedOnly = true;
}
return this;
}
@Override
public OrderEnumerator satisfying(Condition<OrderEntry> condition) {
myCondition = condition;
return this;
}
@Override
public OrderEnumerator using(@NotNull RootModelProvider provider) {
myModulesProvider = provider;
return this;
}
@Override
public OrderRootsEnumerator classes() {
return new OrderRootsEnumeratorImpl(this, OrderRootType.CLASSES);
}
@Override
public OrderRootsEnumerator sources() {
return new OrderRootsEnumeratorImpl(this, OrderRootType.SOURCES);
}
@Override
public OrderRootsEnumerator roots(@NotNull OrderRootType rootType) {
return new OrderRootsEnumeratorImpl(this, rootType);
}
@Override
public OrderRootsEnumerator roots(@NotNull NotNullFunction<OrderEntry, OrderRootType> rootTypeProvider) {
return new OrderRootsEnumeratorImpl(this, rootTypeProvider);
}
ModuleRootModel getRootModel(Module module) {
if (myModulesProvider != null) {
return myModulesProvider.getRootModel(module);
}
return ModuleRootManager.getInstance(module);
}
public OrderRootsCache getCache() {
LOG.assertTrue(myCache != null, "Caching is not supported for ModifiableRootModel");
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;
if (myProductionOnly) flags |= 1;
flags <<= 1;
if (myCompileOnly) flags |= 1;
flags <<= 1;
if (myRuntimeOnly) flags |= 1;
flags <<= 1;
if (myWithoutJdk) flags |= 1;
flags <<= 1;
if (myWithoutLibraries) flags |= 1;
flags <<= 1;
if (myWithoutDepModules) flags |= 1;
flags <<= 1;
if (myWithoutModuleSourceEntries) flags |= 1;
flags <<= 1;
if (myRecursively) flags |= 1;
flags <<= 1;
if (myRecursivelyExportedOnly) flags |= 1;
flags <<= 1;
if (myExportedOnly) flags |= 1;
return flags;
}
protected void processEntries(final ModuleRootModel rootModel,
Processor<OrderEntry> processor,
Set<Module> processed, boolean firstLevel) {
if (processed != null && !processed.add(rootModel.getModule())) return;
for (OrderEntry entry : rootModel.getOrderEntries()) {
if (myCondition != null && !myCondition.value(entry)) continue;
if (myWithoutJdk && entry instanceof JdkOrderEntry) continue;
if (myWithoutLibraries && entry instanceof LibraryOrderEntry) continue;
if (myWithoutDepModules) {
if (!myRecursively && entry instanceof ModuleOrderEntry) continue;
if (entry instanceof ModuleSourceOrderEntry && !isRootModuleModel(((ModuleSourceOrderEntry)entry).getRootModel())) continue;
}
if (myWithoutModuleSourceEntries && entry instanceof ModuleSourceOrderEntry) continue;
OrderEnumerationHandler.AddDependencyType shouldAdd = OrderEnumerationHandler.AddDependencyType.DEFAULT;
for (OrderEnumerationHandler handler : myCustomHandlers) {
shouldAdd = handler.shouldAddDependency(entry, this);
if (shouldAdd != OrderEnumerationHandler.AddDependencyType.DEFAULT) break;
}
if (shouldAdd == OrderEnumerationHandler.AddDependencyType.DO_NOT_ADD) continue;
boolean exported = !(entry instanceof JdkOrderEntry);
if (entry instanceof ExportableOrderEntry) {
ExportableOrderEntry exportableEntry = (ExportableOrderEntry)entry;
if (shouldAdd == OrderEnumerationHandler.AddDependencyType.DEFAULT) {
final DependencyScope scope = exportableEntry.getScope();
boolean forTestCompile = scope.isForTestCompile() || scope == DependencyScope.RUNTIME && shouldAddRuntimeDependenciesToTestCompilationClasspath();
if (myCompileOnly && !scope.isForProductionCompile() && !forTestCompile) continue;
if (myRuntimeOnly && !scope.isForProductionRuntime() && !scope.isForTestRuntime()) continue;
if (myProductionOnly) {
if (!scope.isForProductionCompile() && !scope.isForProductionRuntime()
|| myCompileOnly && !scope.isForProductionCompile()
|| myRuntimeOnly && !scope.isForProductionRuntime()) {
continue;
}
}
}
exported = exportableEntry.isExported();
}
if (!exported) {
if (myExportedOnly) continue;
if (myRecursivelyExportedOnly && !firstLevel) continue;
}
if (myRecursively && entry instanceof ModuleOrderEntry) {
ModuleOrderEntry moduleOrderEntry = (ModuleOrderEntry)entry;
final Module module = moduleOrderEntry.getModule();
if (module != null && shouldProcessRecursively()) {
processEntries(getRootModel(module), processor, processed, false);
continue;
}
}
if (myWithoutDepModules && entry instanceof ModuleOrderEntry) continue;
if (!processor.process(entry)) {
return;
}
}
}
private boolean shouldAddRuntimeDependenciesToTestCompilationClasspath() {
for (OrderEnumerationHandler handler : myCustomHandlers) {
if (handler.shouldAddRuntimeDependenciesToTestCompilationClasspath()) {
return true;
}
}
return false;
}
private boolean shouldProcessRecursively() {
for (OrderEnumerationHandler handler : myCustomHandlers) {
if (!handler.shouldProcessDependenciesRecursively()) {
return false;
}
}
return true;
}
@Override
public void forEachLibrary(@NotNull final Processor<Library> processor) {
forEach(new Processor<OrderEntry>() {
@Override
public boolean process(OrderEntry orderEntry) {
if (orderEntry instanceof LibraryOrderEntry) {
final Library library = ((LibraryOrderEntry)orderEntry).getLibrary();
if (library != null) {
return processor.process(library);
}
}
return true;
}
});
}
@Override
public void forEachModule(@NotNull final Processor<Module> processor) {
forEach(new Processor<OrderEntry>() {
@Override
public boolean process(OrderEntry orderEntry) {
if (myRecursively && orderEntry instanceof ModuleSourceOrderEntry) {
final Module module = ((ModuleSourceOrderEntry)orderEntry).getRootModel().getModule();
return processor.process(module);
}
else if (orderEntry instanceof ModuleOrderEntry && (!myRecursively || !shouldProcessRecursively())) {
final Module module = ((ModuleOrderEntry)orderEntry).getModule();
if (module != null) {
return processor.process(module);
}
}
return true;
}
});
}
@Override
public <R> R process(@NotNull final RootPolicy<R> policy, final R initialValue) {
final OrderEntryProcessor<R> processor = new OrderEntryProcessor<R>(policy, initialValue);
forEach(processor);
return processor.myValue;
}
boolean shouldIncludeTestsFromDependentModulesToTestClasspath() {
for (OrderEnumerationHandler handler : myCustomHandlers) {
if (!handler.shouldIncludeTestsFromDependentModulesToTestClasspath()) {
return false;
}
}
return true;
}
boolean addCustomRootsForLibrary(OrderEntry forOrderEntry, OrderRootType type, Collection<VirtualFile> result) {
for (OrderEnumerationHandler handler : myCustomHandlers) {
final List<String> urls = new ArrayList<String>();
final boolean added =
handler.addCustomRootsForLibrary(forOrderEntry, type, urls);
for (String url : urls) {
ContainerUtil.addIfNotNull(VirtualFileManager.getInstance().findFileByUrl(url), result);
}
if (added) {
return true;
}
}
return false;
}
boolean addCustomRootUrlsForLibrary(OrderEntry forOrderEntry, OrderRootType type, Collection<String> result) {
for (OrderEnumerationHandler handler : myCustomHandlers) {
final List<String> urls = new ArrayList<String>();
final boolean added =
handler.addCustomRootsForLibrary(forOrderEntry, type, urls);
result.addAll(urls);
if (added) {
return true;
}
}
return false;
}
boolean addCustomRootsForModule(OrderRootType type,
ModuleRootModel rootModel,
Collection<VirtualFile> result,
boolean includeProduction,
boolean includeTests) {
for (OrderEnumerationHandler handler : myCustomHandlers) {
final List<String> urls = new ArrayList<String>();
final boolean added = handler.addCustomModuleRoots(type, rootModel, urls, includeProduction, includeTests);
for (String url : urls) {
ContainerUtil.addIfNotNull(VirtualFileManager.getInstance().findFileByUrl(url), result);
}
if (added) return true;
}
return false;
}
boolean addCustomRootUrlsForModule(OrderRootType type,
ModuleRootModel rootModel,
Collection<String> result,
boolean includeProduction,
boolean includeTests) {
for (OrderEnumerationHandler handler : myCustomHandlers) {
final List<String> urls = new ArrayList<String>();
final boolean added = handler.addCustomModuleRoots(type, rootModel, urls, includeProduction, includeTests);
result.addAll(urls);
if (added) return true;
}
return false;
}
@Override
public boolean isRuntimeOnly() {
return myRuntimeOnly;
}
@Override
public boolean isCompileOnly() {
return myCompileOnly;
}
@Override
public boolean isProductionOnly() {
return myProductionOnly;
}
public boolean isRootModuleModel(@NotNull ModuleRootModel rootModel) {
return false;
}
/**
* Runs processor on each module that this enumerator was created on.
*
* @param processor processor
*/
public abstract void processRootModules(@NotNull Processor<Module> processor);
private class OrderEntryProcessor<R> implements Processor<OrderEntry> {
private R myValue;
private final RootPolicy<R> myPolicy;
public OrderEntryProcessor(RootPolicy<R> policy, R initialValue) {
myPolicy = policy;
myValue = initialValue;
}
@Override
public boolean process(OrderEntry orderEntry) {
myValue = orderEntry.accept(myPolicy, myValue);
return true;
}
}
}