package com.intellij.lang.javascript.flex.projectStructure; import com.intellij.flex.FlexCommonUtils; import com.intellij.flex.model.bc.LinkageType; import com.intellij.lang.javascript.flex.FlexModuleType; import com.intellij.lang.javascript.flex.library.FlexLibraryType; import com.intellij.lang.javascript.flex.projectStructure.model.BuildConfigurationEntry; import com.intellij.lang.javascript.flex.projectStructure.model.DependencyEntry; import com.intellij.lang.javascript.flex.projectStructure.model.FlexBuildConfiguration; import com.intellij.lang.javascript.flex.projectStructure.model.FlexBuildConfigurationManager; import com.intellij.lang.javascript.flex.projectStructure.options.BCUtils; import com.intellij.lang.javascript.flex.projectStructure.options.FlexProjectRootsUtil; import com.intellij.lang.javascript.flex.sdk.FlexSdkType2; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleType; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.roots.*; import com.intellij.openapi.roots.impl.libraries.LibraryEx; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.JarFileSystem; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; public class FlexOrderEnumerationHandler extends OrderEnumerationHandler { public static Key<FlexBuildConfiguration> FORCE_BC = Key.create(FlexOrderEnumerationHandler.class.getName() + ".forceBc"); public static class FactoryImpl extends Factory { @Override public boolean isApplicable(@NotNull Module module) { return ModuleType.get(module) == FlexModuleType.getInstance(); } @Override public OrderEnumerationHandler createHandler(@NotNull Module module) { return new FlexOrderEnumerationHandler(module); } } // TODO our special handling for myWithoutJdk, myWithoutLibraries private static class ModuleData { private Set<FlexBuildConfiguration> bcs = new HashSet<>(); private boolean accessibleInProduction = false; // true if this module accessible by non-test dependency types public void addBc(FlexBuildConfiguration bc, boolean production) { bcs.add(bc); accessibleInProduction |= production; } } @Nullable private final Map<Module, ModuleData> myActiveConfigurations; private final Module myRootModule; public FlexOrderEnumerationHandler(@NotNull Module module) { myRootModule = module; myActiveConfigurations = new HashMap<>(); // last argument can be whatever processModuleWithBuildConfiguration(module, null, myActiveConfigurations, new HashSet<>(), true); } // configuration is null for root module (one for which scope is being computed) private static void processModuleWithBuildConfiguration(@NotNull Module module, @Nullable FlexBuildConfiguration bc, Map<Module, ModuleData> modules2activeConfigurations, Set<FlexBuildConfiguration> processedConfigurations, boolean productionDependency) { if (ModuleType.get(module) != FlexModuleType.getInstance()) { return; } final boolean isRootModule = bc == null; if (isRootModule) { bc = getActiveConfiguration(module); } if (bc == null || !processedConfigurations.add(bc)) { return; } ModuleData moduleData = modules2activeConfigurations.get(module); if (moduleData == null) { modules2activeConfigurations.put(module, moduleData = new ModuleData()); } moduleData.addBc(bc, productionDependency); for (DependencyEntry entry : bc.getDependencies().getEntries()) { if (!(entry instanceof BuildConfigurationEntry)) { continue; } final LinkageType linkageType = entry.getDependencyType().getLinkageType(); if (linkageType == LinkageType.LoadInRuntime) { continue; } FlexBuildConfiguration dependencyBc = ((BuildConfigurationEntry)entry).findBuildConfiguration(); if (dependencyBc == null || !FlexCommonUtils.checkDependencyType(bc.getOutputType(), dependencyBc.getOutputType(), linkageType)) { continue; } if (!isRootModule && !BCUtils.isTransitiveDependency(linkageType)) { continue; } Module dependencyModule = ((BuildConfigurationEntry)entry).findModule(); if (dependencyModule == null || dependencyModule == module) { continue; } processModuleWithBuildConfiguration(dependencyModule, dependencyBc, modules2activeConfigurations, processedConfigurations, entry.getDependencyType().getLinkageType() != LinkageType.Test); } } private static FlexBuildConfiguration getActiveConfiguration(final Module module) { final FlexBuildConfiguration forced = FORCE_BC.get(module); return forced != null ? forced : FlexBuildConfigurationManager.getInstance(module).getActiveConfiguration(); } @NotNull @Override public AddDependencyType shouldAddDependency(@NotNull OrderEntry orderEntry, @NotNull OrderEnumeratorSettings settings) { Module module = orderEntry.getOwnerModule(); if (ModuleType.get(module) != FlexModuleType.getInstance()) { return super.shouldAddDependency(orderEntry, settings); } if (orderEntry instanceof ModuleSourceOrderEntry) { return AddDependencyType.DEFAULT; } if (orderEntry instanceof JdkOrderEntry) { if (module != myRootModule) { // never add transitive dependency to Flex SDK return AddDependencyType.DO_NOT_ADD; } if (myActiveConfigurations == null) { return AddDependencyType.DEFAULT; } ModuleData moduleData = myActiveConfigurations.get(module); for (FlexBuildConfiguration bc : moduleData.bcs) { if (bc.getSdk() != null) { return AddDependencyType.DEFAULT; } } return AddDependencyType.DO_NOT_ADD; } Collection<FlexBuildConfiguration> accessibleConfigurations; if (myActiveConfigurations != null) { ModuleData moduleData = myActiveConfigurations.get(module); accessibleConfigurations = moduleData != null ? moduleData.bcs : Collections.emptyList(); } else { // let all configurations be accessible in ProjectOrderEnumerator accessibleConfigurations = Arrays.asList(FlexBuildConfigurationManager.getInstance(module).getBuildConfigurations()); } if (orderEntry instanceof LibraryOrderEntry) { final LibraryEx library = (LibraryEx)((LibraryOrderEntry)orderEntry).getLibrary(); if (library == null) { return AddDependencyType.DEFAULT; } if (library.getKind() == FlexLibraryType.FLEX_LIBRARY) { return FlexProjectRootsUtil.dependOnLibrary(accessibleConfigurations, library, module != myRootModule, settings.isProductionOnly()) ? AddDependencyType.DEFAULT : AddDependencyType.DO_NOT_ADD; } else { // foreign library return AddDependencyType.DO_NOT_ADD; } } else if (orderEntry instanceof ModuleOrderEntry) { final Module dependencyModule = ((ModuleOrderEntry)orderEntry).getModule(); if (dependencyModule == null) { return AddDependencyType.DO_NOT_ADD; } if (myActiveConfigurations != null) { ModuleData moduleData = myActiveConfigurations.get(dependencyModule); return moduleData != null && (moduleData.accessibleInProduction || !settings.isProductionOnly()) ? AddDependencyType.DEFAULT : AddDependencyType.DO_NOT_ADD; } else { // let all modules dependencies be accessible in ProjectOrderEnumerator return AddDependencyType.DEFAULT; } } else { return AddDependencyType.DEFAULT; } } @Override public boolean addCustomRootsForLibrary(@NotNull final OrderEntry forOrderEntry, @NotNull final OrderRootType type, @NotNull final Collection<String> urls) { if (!(forOrderEntry instanceof JdkOrderEntry)) { return false; } if (myActiveConfigurations == null) { return false; } final Module forModule = forOrderEntry.getOwnerModule(); final FlexBuildConfiguration bc = getActiveConfiguration(forModule); final Sdk sdk = bc.getSdk(); if (sdk == null || sdk.getSdkType() != FlexSdkType2.getInstance()) { return false; } final String[] allUrls = sdk.getRootProvider().getUrls(type); if (type != OrderRootType.CLASSES) { urls.addAll(Arrays.asList(allUrls)); return true; } final List<String> themePaths = BCUtils.getThemes(forModule, bc); final List<String> allAccessibleUrls = ContainerUtil.filter(allUrls, s -> { s = VirtualFileManager.extractPath(StringUtil.trimEnd(s, JarFileSystem.JAR_SEPARATOR)); return BCUtils.getSdkEntryLinkageType(s, bc) != null || themePaths.contains(s); }); urls.addAll(new HashSet<>(allAccessibleUrls)); return true; } }