package com.intellij.lang.javascript.flex.projectStructure.model.impl; import com.intellij.flex.FlexCommonUtils; import com.intellij.flex.model.bc.BuildConfigurationNature; import com.intellij.flex.model.bc.OutputType; import com.intellij.flex.model.bc.TargetPlatform; import com.intellij.lang.javascript.flex.FlexModuleType; import com.intellij.lang.javascript.flex.library.FlexLibraryProperties; import com.intellij.lang.javascript.flex.library.FlexLibraryType; import com.intellij.lang.javascript.flex.projectStructure.FlexBCConfigurator; import com.intellij.lang.javascript.flex.projectStructure.FlexBuildConfigurationsExtension; import com.intellij.lang.javascript.flex.projectStructure.FlexCompositeSdk; import com.intellij.lang.javascript.flex.projectStructure.model.*; import com.intellij.lang.javascript.flex.projectStructure.options.BCUtils; import com.intellij.lang.javascript.flex.projectStructure.options.FlexProjectRootsUtil; import com.intellij.lang.javascript.flex.projectStructure.ui.FlexBCConfigurable; import com.intellij.lang.javascript.flex.sdk.FlexSdkUtils; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleType; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.roots.*; import com.intellij.openapi.roots.impl.libraries.LibraryEx; import com.intellij.openapi.roots.libraries.Library; import com.intellij.openapi.roots.libraries.LibraryTable; import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar; import com.intellij.openapi.roots.ui.configuration.libraries.LibraryEditingUtil; import com.intellij.openapi.util.Pair; import com.intellij.util.ArrayUtil; import com.intellij.util.Consumer; import com.intellij.util.EventDispatcher; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.HashMap; import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.event.ChangeListener; import java.util.*; public class FlexProjectConfigurationEditor implements Disposable { private static final Logger LOG = Logger.getInstance(FlexProjectConfigurationEditor.class.getName()); private static class Editor extends FlexBuildConfigurationImpl { private final Module myModule; private final FlexBuildConfigurationImpl myOrigin; @Nullable private final String myOriginalName; Editor(Module module, @Nullable FlexBuildConfigurationImpl origin, boolean storeOriginalName) { myOrigin = origin != null ? origin : new FlexBuildConfigurationImpl(); myOriginalName = storeOriginalName && origin != null ? origin.getName() : null; myModule = module; myOrigin.applyTo(this); } public FlexBuildConfigurationImpl commit() { applyTo(myOrigin); return myOrigin; } public boolean isModified() { return !isEqual(myOrigin); } @Nullable public String getOriginalName() { return myOriginalName; } } public interface ProjectModifiableModelProvider { Module[] getModules(); ModifiableRootModel getModuleModifiableModel(Module module); void addListener(FlexBCConfigurator.Listener listener, Disposable parentDisposable); void commitModifiableModels() throws ConfigurationException; @Nullable Library findSourceLibraryForLiveName(String name, String level); @Nullable Library findSourceLibrary(String name, String level); } public interface ModulesModelChangeListener extends EventListener { void modulesModelsChanged(Collection<Module> modules); } private boolean myDisposed; private final ProjectModifiableModelProvider myProvider; @Nullable private final Project myProject; private final Map<Module, List<Editor>> myModule2Editors = new HashMap<>(); //private final FlexSdksEditor mySdksEditor; private final EventDispatcher<ModulesModelChangeListener> myModulesModelChangeEventDispatcher = EventDispatcher.create(ModulesModelChangeListener.class); public FlexProjectConfigurationEditor(@Nullable Project project, ProjectModifiableModelProvider provider) { myProject = project; myProvider = provider; //mySdksEditor = new FlexSdksEditor(this); provider.addListener(new FlexBCConfigurator.Listener() { @Override public void moduleRemoved(final Module module) { if (!isFlex(module)) { return; } LOG.assertTrue(myModule2Editors.containsKey(module), "Unknown module: " + module); myModule2Editors.remove(module); //Condition<OrderEntry> c = new Condition<OrderEntry>() { // @Override // public boolean value(OrderEntry orderEntry) { // return orderEntry instanceof ModuleOrderEntry && ((ModuleOrderEntry)orderEntry).getModule() == module; // } //}; //for (Module m : myModule2Editors.keySet()) { // ModifiableRootModel modifiableModel = myProvider.getModuleModifiableModel(m); // List<OrderEntry> orderEntriesToRemove = ContainerUtil.findAll(modifiableModel.getOrderEntries(), c); // for (OrderEntry orderEntry : orderEntriesToRemove) { // modifiableModel.removeOrderEntry(orderEntry); // } //} } @Override public void buildConfigurationRemoved(FlexBCConfigurable configurable) { configurationRemoved(configurable.getEditableObject()); } @Override public void natureChanged(final FlexBCConfigurable configurable) { } @Override public void buildConfigurationRenamed(final FlexBCConfigurable configurable) { } }, this); for (Module module : provider.getModules()) { if (!isFlex(module)) { continue; } addEditorsForModule(module); } } /** * Clients are responsible to commit or dispose returned <code>FlexProjectConfigurationEditor</code> as well as passed <code>projectLibrariesModel</code> and <code>globalLibrariesModel</code>.<br><br> * If <code>null</code> is given as <code>projectLibrariesModel</code> or <code>globalLibrariesModel</code> then the client must not set dependency on libraries of respective level.<br><br> * Removing of modules and build configurations is not allowed while returned <code>FlexProjectConfigurationEditor</code> exists. */ public static FlexProjectConfigurationEditor createEditor(final Project project, final Map<Module, ModifiableRootModel> moduleToModifiableModel, final @Nullable LibraryTable.ModifiableModel projectLibrariesModel, final @Nullable LibraryTable.ModifiableModel globalLibrariesModel) { LOG.assertTrue(FlexBuildConfigurationsExtension.getInstance().getConfigurator().getConfigEditor() == null, "Don't create FlexProjectConfigurationEditor when Project Structure dialog is open. Use FlexBCConfigurator.getConfigEditor()"); final ProjectModifiableModelProvider provider = createModelProvider(moduleToModifiableModel, projectLibrariesModel, globalLibrariesModel); return new FlexProjectConfigurationEditor(project, provider); } public static ProjectModifiableModelProvider createModelProvider(final Map<Module, ModifiableRootModel> moduleToModifiableModel, final @Nullable LibraryTable.ModifiableModel projectLibrariesModel, final @Nullable LibraryTable.ModifiableModel globalLibrariesModel) { return new ProjectModifiableModelProvider() { public Module[] getModules() { final Set<Module> modules = moduleToModifiableModel.keySet(); return modules.toArray(new Module[modules.size()]); } public ModifiableRootModel getModuleModifiableModel(final Module module) { final ModifiableRootModel model = moduleToModifiableModel.get(module); LOG.assertTrue(model != null, "No model for module " + module.getName()); return model; } public void addListener(final FlexBCConfigurator.Listener listener, final Disposable parentDisposable) { // modules and BCs must not be removed } public void commitModifiableModels() throws ConfigurationException { // commit must be performed somewhere else } @Nullable public Library findSourceLibrary(final String name, final String level) { if (LibraryTablesRegistrar.APPLICATION_LEVEL.equals(level)) { return globalLibrariesModel.getLibraryByName(name); } else if (LibraryTablesRegistrar.PROJECT_LEVEL.equals(level)) { LOG.assertTrue(projectLibrariesModel != null); return projectLibrariesModel.getLibraryByName(name); } LOG.error("Unexpected argument: " + level); return null; } public Library findSourceLibraryForLiveName(final String name, final String level) { return findSourceLibrary(name, level); } }; } public void configurationRemoved(@NotNull final ModifiableFlexBuildConfiguration configuration) { assertAlive(); Editor editor = (Editor)configuration; List<Editor> editors = myModule2Editors.get(editor.myModule); boolean contained = editors.remove(editor); LOG.assertTrue(contained); } public void addModulesModelChangeListener(ModulesModelChangeListener listener, Disposable parentDisposable) { myModulesModelChangeEventDispatcher.addListener(listener, parentDisposable); } private void addEditorsForModule(Module module) { FlexBuildConfiguration[] buildConfigurations = FlexBuildConfigurationManager.getInstance(module).getBuildConfigurations(); List<Editor> configEditors = new ArrayList<>(buildConfigurations.length); for (FlexBuildConfiguration buildConfiguration : buildConfigurations) { configEditors.add(new Editor(module, (FlexBuildConfigurationImpl)buildConfiguration, true)); } myModule2Editors.put(module, configEditors); } @Override public void dispose() { myDisposed = true; myModule2Editors.clear(); } public ModifiableFlexBuildConfiguration[] getConfigurations(Module module) { assertAlive(); List<Editor> editors = myModule2Editors.get(module); if (editors == null) { // module was just created addEditorsForModule(module); editors = myModule2Editors.get(module); } return editors.toArray(new ModifiableFlexBuildConfiguration[editors.size()]); } private void assertAlive() { LOG.assertTrue(!myDisposed, "Already disposed"); } public ModifiableFlexBuildConfiguration createConfiguration(Module module) { assertAlive(); List<Editor> editors = myModule2Editors.get(module); Editor newConfig = new Editor(module, null, false); editors.add(newConfig); return newConfig; } public ModifiableFlexBuildConfiguration copyConfiguration(ModifiableFlexBuildConfiguration configuration, BuildConfigurationNature newNature) { assertAlive(); Module module = ((Editor)configuration).myModule; List<Editor> editors = myModule2Editors.get(module); FlexBuildConfigurationImpl copy = ((Editor)configuration).getCopy(); DependencyEntry[] entries = copy.getDependencies().getEntries(); ModifiableRootModel modifiableModel = myProvider.getModuleModifiableModel(module); for (int i = 0; i < entries.length; i++) { if (entries[i] instanceof ModuleLibraryEntryImpl) { ModuleLibraryEntryImpl e = (ModuleLibraryEntryImpl)entries[i]; LibraryEx library = findLibrary(modifiableModel, e.getLibraryId()); if (library != null) { LibraryEx libraryCopy = copyModuleLibrary(modifiableModel, library); ModuleLibraryEntryImpl entryCopy = new ModuleLibraryEntryImpl(FlexProjectRootsUtil.getLibraryId(libraryCopy)); e.getDependencyType().applyTo(entryCopy.getDependencyType()); copy.getDependencies().getModifiableEntries().set(i, entryCopy); } } } Editor newConfig = new Editor(module, copy, false); newConfig.setNature(newNature); // just to simplify serialized view resetNonApplicableValuesToDefaults(newConfig); editors.add(newConfig); return newConfig; } private static LibraryEx copyModuleLibrary(final ModifiableRootModel modifiableModel, final LibraryEx library) { LibraryTable.ModifiableModel librariesModifiableModel = getTableModifiableModel(modifiableModel); LibraryEx libraryCopy = (LibraryEx)librariesModifiableModel.createLibrary(library.getName(), library.getKind()); LibraryEx.ModifiableModelEx libraryCopyModel = libraryCopy.getModifiableModel(); LibraryEditingUtil.copyLibrary(library, Collections.emptyMap(), libraryCopyModel); // will overwrite library id libraryCopyModel.setProperties(new FlexLibraryProperties(FlexLibraryIdGenerator.generateId())); // do assign unique library id libraryCopyModel.commit(); return libraryCopy; } public static void resetNonApplicableValuesToDefaults(final ModifiableFlexBuildConfiguration bc) { final FlexBuildConfiguration defaultConfiguration = new FlexBuildConfigurationImpl(); final BuildConfigurationNature nature = bc.getNature(); if (bc.getOutputType() != OutputType.RuntimeLoadedModule) { bc.setOptimizeFor(defaultConfiguration.getOptimizeFor()); } if (nature.isLib()) { bc.setMainClass(defaultConfiguration.getMainClass()); } if (!nature.isWebPlatform() || !nature.isApp()) { bc.setUseHtmlWrapper(defaultConfiguration.isUseHtmlWrapper()); bc.setWrapperTemplatePath(defaultConfiguration.getWrapperTemplatePath()); } if (!BCUtils.canHaveRLMsAndRuntimeStylesheets(bc)) { bc.setRLMs(defaultConfiguration.getRLMs()); bc.setCssFilesToCompile(defaultConfiguration.getCssFilesToCompile()); } if (!ArrayUtil.contains(bc.getDependencies().getFrameworkLinkage(), BCUtils.getSuitableFrameworkLinkages(nature))) { bc.getDependencies().setFrameworkLinkage(defaultConfiguration.getDependencies().getFrameworkLinkage()); } if (!nature.isWebPlatform()) { bc.getDependencies().setTargetPlayer(defaultConfiguration.getDependencies().getTargetPlayer()); } if (nature.isMobilePlatform() || bc.isPureAs()) { bc.getDependencies().setComponentSet(defaultConfiguration.getDependencies().getComponentSet()); } for (Iterator<ModifiableDependencyEntry> i = bc.getDependencies().getModifiableEntries().iterator(); i.hasNext(); ) { final ModifiableDependencyEntry entry = i.next(); if (entry instanceof BuildConfigurationEntry) { final FlexBuildConfiguration dependencyBC = ((BuildConfigurationEntry)entry).findBuildConfiguration(); if (dependencyBC == null || !FlexCommonUtils.checkDependencyType(bc.getOutputType(), dependencyBC.getOutputType(), entry.getDependencyType().getLinkageType())) { i.remove(); } } } if (bc.getTargetPlatform() != TargetPlatform.Desktop || bc.getOutputType() != OutputType.Application) { ((AirDesktopPackagingOptionsImpl)defaultConfiguration.getAirDesktopPackagingOptions()) .applyTo(((AirDesktopPackagingOptionsImpl)bc.getAirDesktopPackagingOptions())); } if (bc.getTargetPlatform() != TargetPlatform.Mobile || bc.getOutputType() != OutputType.Application) { ((AndroidPackagingOptionsImpl)defaultConfiguration.getAndroidPackagingOptions()) .applyTo(((AndroidPackagingOptionsImpl)bc.getAndroidPackagingOptions())); ((IosPackagingOptionsImpl)defaultConfiguration.getIosPackagingOptions()) .applyTo(((IosPackagingOptionsImpl)bc.getIosPackagingOptions())); } if (!nature.isLib()) { bc.getCompilerOptions().setFilesToIncludeInSWC(Collections.emptyList()); } } public Module getModule(ModifiableFlexBuildConfiguration configuration) { assertAlive(); return ((Editor)configuration).myModule; } @Nullable public Project getProject() { return myProject; } public void checkCanCommit() throws ConfigurationException { for (Module module : myModule2Editors.keySet()) { List<Editor> editors = myModule2Editors.get(module); Set<String> names = new HashSet<>(); for (Editor editor : editors) { if (!names.add(editor.getName())) { throw new ConfigurationException( "Duplicate build configuration name '" + editor.getName() + "' in module '" + module.getName() + "'"); } } } } public void commit() throws ConfigurationException { final Map<Pair<String, String>, String> renamedConfigs = new HashMap<>(); for (Module module : myModule2Editors.keySet()) { ModifiableRootModel modifiableModel = myProvider.getModuleModifiableModel(module); Collection<String> usedModulesLibrariesIds = new ArrayList<>(); // ---------------- SDK and shared libraries entries ---------------------- Map<Library, Boolean> librariesToAdd = new LinkedHashMap<>(); // Library -> add_library_entry_flag final Collection<String> sdkNames = new HashSet<>(); for (Editor editor : myModule2Editors.get(module)) { final SdkEntry sdkEntry = editor.getDependencies().getSdkEntry(); if (sdkEntry != null) { sdkNames.add(sdkEntry.getName()); } for (DependencyEntry dependencyEntry : editor.getDependencies().getEntries()) { if (dependencyEntry instanceof ModuleLibraryEntry) { ModuleLibraryEntry moduleLibraryEntry = (ModuleLibraryEntry)dependencyEntry; usedModulesLibrariesIds.add(moduleLibraryEntry.getLibraryId()); } if (dependencyEntry instanceof SharedLibraryEntry) { SharedLibraryEntry sharedLibraryEntry = (SharedLibraryEntry)dependencyEntry; Library library = myProvider.findSourceLibraryForLiveName(sharedLibraryEntry.getLibraryName(), sharedLibraryEntry.getLibraryLevel()); if (library != null) { librariesToAdd.put(library, true); } } } String originalName = editor.getOriginalName(); if (originalName != null && !originalName.equals(editor.getName())) { renamedConfigs.put(Pair.create(module.getName(), originalName), editor.getName()); } } final Sdk sdk; if (sdkNames.isEmpty()) { sdk = null; } else if (sdkNames.size() == 1) { sdk = FlexSdkUtils.findFlexOrFlexmojosSdk(sdkNames.iterator().next()); } else { sdk = new FlexCompositeSdk(ArrayUtil.toStringArray(sdkNames)); } modifiableModel.setSdk(sdk); Collection<OrderEntry> entriesToRemove = new ArrayList<>(); for (OrderEntry orderEntry : modifiableModel.getOrderEntries()) { if (orderEntry instanceof LibraryOrderEntry) { if (((LibraryOrderEntry)orderEntry).isModuleLevel()) { LibraryEx library = (LibraryEx)((LibraryOrderEntry)orderEntry).getLibrary(); if (FlexProjectRootsUtil.isFlexLibrary(library) && !usedModulesLibrariesIds.contains(FlexProjectRootsUtil.getLibraryId(library))) { entriesToRemove.add(orderEntry); } } else { LibraryEx library = (LibraryEx)((LibraryOrderEntry)orderEntry).getLibrary(); if (librariesToAdd.containsKey(library)) { librariesToAdd.put(library, false); // entry already exists for this library } else if (library != null && FlexProjectRootsUtil.isFlexLibrary(library)) { entriesToRemove.add(orderEntry); } } } } for (OrderEntry e : entriesToRemove) { modifiableModel.removeOrderEntry(e); } for (Library library : librariesToAdd.keySet()) { if (!((LibraryEx)library).isDisposed() && librariesToAdd.get(library) && myProvider.findSourceLibrary(library.getName(), library.getTable().getTableLevel()) != null) { modifiableModel.addLibraryEntry(library); } } // ---------------- modules entries ---------------------- final Set<Module> modulesToAdd = new THashSet<>(); for (Editor editor : myModule2Editors.get(module)) { for (DependencyEntry dependencyEntry : editor.getDependencies().getEntries()) { if (dependencyEntry instanceof BuildConfigurationEntry) { final Module dependencyModule = findModuleWithBC((BuildConfigurationEntry)dependencyEntry); if (dependencyModule != null && dependencyModule != module) { modulesToAdd.add(dependencyModule); } } } } List<OrderEntry> moduleOrderEntriesToRemove = ContainerUtil.filter(modifiableModel.getOrderEntries(), orderEntry -> orderEntry instanceof ModuleOrderEntry && !modulesToAdd.remove(((ModuleOrderEntry)orderEntry).getModule())); for (OrderEntry orderEntry : moduleOrderEntriesToRemove) { modifiableModel.removeOrderEntry(orderEntry); } for (Module moduleToAdd : modulesToAdd) { modifiableModel.addModuleOrderEntry(moduleToAdd); } for (OrderEntry entry : modifiableModel.getOrderEntries()) { if (entry instanceof ExportableOrderEntry) { // transitiveness will be filtered out in FlexOrderEnumeratorHandler if needed ((ExportableOrderEntry)entry).setExported(true); } } } // ---------------- do commit ---------------------- Collection<Module> modulesWithChangedModifiableModel = ContainerUtil.findAll(myModule2Editors.keySet(), module -> myProvider.getModuleModifiableModel(module).isChanged()); if (!modulesWithChangedModifiableModel.isEmpty()) { myProvider.commitModifiableModels(); myModulesModelChangeEventDispatcher.getMulticaster().modulesModelsChanged(modulesWithChangedModifiableModel); } ApplicationManager.getApplication().runWriteAction(() -> { for (Module module : myModule2Editors.keySet()) { Function<Editor, FlexBuildConfigurationImpl> f = editor -> editor.commit(); FlexBuildConfigurationImpl[] current = ContainerUtil.map2Array(myModule2Editors.get(module), FlexBuildConfigurationImpl.class, f); ((FlexBuildConfigurationManagerImpl)FlexBuildConfigurationManager.getInstance(module)).setBuildConfigurations(current); } //if (mySdksEditor.isModified()) { // mySdksEditor.commit(); //} if (myProject != null) { FlexBuildConfigurationManagerImpl.resetHighlighting(myProject); if (!renamedConfigs.isEmpty()) { myProject.getMessageBus().syncPublisher(FlexBuildConfigurationChangeListener.TOPIC).buildConfigurationsRenamed(renamedConfigs); } } }); } public boolean isModified() { if (myDisposed) { return false; } for (Module module : myModule2Editors.keySet()) { if (myProvider.getModuleModifiableModel(module).isChanged()) { return true; } FlexBuildConfiguration[] originalConfigurations = FlexBuildConfigurationManager.getInstance(module).getBuildConfigurations(); List<Editor> currentConfigurations = myModule2Editors.get(module); if (originalConfigurations.length != currentConfigurations.size()) { return true; } for (Editor currentConfiguration : currentConfigurations) { if (currentConfiguration.isModified()) { return true; } } } //if (mySdksEditor.isModified()) { // return true; //} return false; } private static boolean isFlex(Module module) { return ModuleType.get(module) == FlexModuleType.getInstance(); } public ModifiableBuildConfigurationEntry createBcEntry(ModifiableDependencies dependant, ModifiableFlexBuildConfiguration dependency, @Nullable String dependencyCurrentName) { assertAlive(); Module dependencyModule = ((Editor)dependency).myModule; ModifiableBuildConfigurationEntry e = new BuildConfigurationEntryImpl(dependencyModule, dependencyCurrentName != null ? dependencyCurrentName : dependency.getName()); //Module dependantModule = getEditor(dependant).myModule; //if (dependantModule != dependencyModule) { // ModifiableRootModel modifiableModel = myProvider.getModuleModifiableModel(dependantModule); // if (!ArrayUtil.contains(dependencyModule, modifiableModel.getModuleDependencies())) { // modifiableModel.addModuleOrderEntry(dependencyModule); // } //} return e; } public ModifiableBuildConfigurationEntry createBcEntry(ModifiableDependencies dependant, String moduleName, String bcName) { assertAlive(); Module dependantModule = getEditor(dependant).myModule; return new BuildConfigurationEntryImpl(dependantModule.getProject(), moduleName, bcName); } public ModifiableModuleLibraryEntry createModuleLibraryEntry(ModifiableDependencies dependant, String dependencyLibraryId) { assertAlive(); ModuleLibraryEntryImpl e = new ModuleLibraryEntryImpl(dependencyLibraryId); Module dependantModule = getEditor(dependant).myModule; ModifiableRootModel modifiableModel = myProvider.getModuleModifiableModel(dependantModule); if (findLibrary(modifiableModel, dependencyLibraryId) == null) { LOG.warn("Module library used in build configuration is missing"); } return e; } public ModifiableDependencyEntry createSharedLibraryEntry(final ModifiableDependencies dependencies, final String libraryName, final String libraryLevel) { assertAlive(); return new SharedLibraryEntryImpl(libraryName, libraryLevel); } @Nullable private static LibraryEx findLibrary(ModifiableRootModel modifiableModel, String libraryId) { for (Library library : modifiableModel.getModuleLibraryTable().getLibraries()) { if (((LibraryEx)library).getKind() == FlexLibraryType.FLEX_LIBRARY) { // allow subclasses if (libraryId.equals(FlexProjectRootsUtil.getLibraryId(library))) { return (LibraryEx)library; } } } return null; } public void setEntries(ModifiableDependencies dependant, List<? extends ModifiableDependencyEntry> newEntries) { assertAlive(); Map<String, ModifiableDependencyEntry> existingModuleLibrariesEntries = new HashMap<>(); Map<Pair<String, String>, ModifiableBuildConfigurationEntry> existingBcEntries = new HashMap<>(); Map<Pair<String, String>, ModifiableSharedLibraryEntry> existingSharedLibrariesEntries = new HashMap<>(); for (ModifiableDependencyEntry entry : dependant.getModifiableEntries()) { if (entry instanceof ModuleLibraryEntry) { existingModuleLibrariesEntries.put(((ModuleLibraryEntry)entry).getLibraryId(), entry); } else if (entry instanceof ModifiableSharedLibraryEntry) { final ModifiableSharedLibraryEntry e = (ModifiableSharedLibraryEntry)entry; existingSharedLibrariesEntries.put(Pair.create(e.getLibraryLevel(), e.getLibraryName()), e); } else if (entry instanceof ModifiableBuildConfigurationEntry) { final ModifiableBuildConfigurationEntry e = (ModifiableBuildConfigurationEntry)entry; existingBcEntries.put(Pair.create(e.getModuleName(), e.getBcName()), e); } else { assert false : entry; } } List<ModifiableDependencyEntry> entriesToRemove = new ArrayList<>(dependant.getModifiableEntries()); for (Iterator<? extends ModifiableDependencyEntry> i = newEntries.iterator(); i.hasNext(); ) { ModifiableDependencyEntry newEntry = i.next(); ModifiableDependencyEntry existingEntry = null; if (newEntry instanceof ModuleLibraryEntry) { existingEntry = existingModuleLibrariesEntries.get(((ModuleLibraryEntry)newEntry).getLibraryId()); } else if (newEntry instanceof SharedLibraryEntry) { final SharedLibraryEntry e = (SharedLibraryEntry)newEntry; existingEntry = existingSharedLibrariesEntries.get(Pair.create(e.getLibraryLevel(), e.getLibraryName())); } else if (newEntry instanceof BuildConfigurationEntry) { final BuildConfigurationEntry bcEntry = (BuildConfigurationEntry)newEntry; existingEntry = existingBcEntries.get(Pair.create(bcEntry.getModuleName(), bcEntry.getBcName())); } else { assert false : newEntry; } if (existingEntry != null) { entriesToRemove.remove(existingEntry); existingEntry.getDependencyType().copyFrom(newEntry.getDependencyType()); i.remove(); } } Editor dependantEditor = getEditor(dependant); ModifiableRootModel dependantModifiableModel = myProvider.getModuleModifiableModel(dependantEditor.myModule); for (DependencyEntry entry : entriesToRemove) { if (entry instanceof ModuleLibraryEntry) { ModuleLibraryEntry libraryEntry = (ModuleLibraryEntry)entry; Library dependencyLibrary = findLibrary(dependantModifiableModel, libraryEntry.getLibraryId()); if (dependencyLibrary != null) { List<Editor> otherEditors = new ArrayList<>(myModule2Editors.get(dependantEditor.myModule)); otherEditors.remove(dependantEditor); if (!libraryIsUsed(libraryEntry.getLibraryId(), otherEditors)) { LibraryOrderEntry orderEntry = dependantModifiableModel.findLibraryOrderEntry(dependencyLibrary); LOG.assertTrue(orderEntry != null); // TODO should we explicitly delete library as well? dependantModifiableModel.removeOrderEntry(orderEntry); } } } } dependant.getModifiableEntries().removeAll(entriesToRemove); dependant.getModifiableEntries().addAll(newEntries); } private static boolean libraryIsUsed(final String libraryId, final List<Editor> editors) { for (Editor editor : editors) { for (ModifiableDependencyEntry entry : editor.getDependencies().getModifiableEntries()) { if (entry instanceof ModuleLibraryEntry && libraryId.equals(((ModuleLibraryEntry)entry).getLibraryId())) { return true; } } } return false; } @Nullable protected Module findModuleWithBC(final BuildConfigurationEntry bcEntry) { final Module dependencyModule = ContainerUtil.find(myModule2Editors.keySet(), module -> bcEntry.getModuleName().equals(module.getName())); if (dependencyModule == null) { return null; } final Editor dependencyBC = ContainerUtil.find(myModule2Editors.get(dependencyModule), editor -> editor.getName().equals(bcEntry.getBcName())); return dependencyBC == null ? null : dependencyModule; } @Nullable public LibraryOrderEntry findLibraryOrderEntry(ModifiableDependencies dependencies, ModuleLibraryEntry moduleLibraryEntry) { assertAlive(); ModifiableRootModel modifiableModel = myProvider.getModuleModifiableModel(getEditor(dependencies).myModule); Library library = findLibrary(modifiableModel, moduleLibraryEntry.getLibraryId()); return library != null ? modifiableModel.findLibraryOrderEntry(library) : null; } public LibraryOrderEntry findLibraryOrderEntry(ModifiableDependencies dependencies, Library library) { ModifiableRootModel modifiableModel = myProvider.getModuleModifiableModel(getEditor(dependencies).myModule); return modifiableModel.findLibraryOrderEntry(library); } private Editor getEditor(ModifiableDependencies dependencies) { for (List<Editor> editors : myModule2Editors.values()) { for (Editor editor : editors) { if (editor.getDependencies() == dependencies) { return editor; } } } throw new IllegalArgumentException("unknown dependencies instance"); } public Module getModule(ModifiableDependencies dependencies) { assertAlive(); return getEditor(dependencies).myModule; } public ModifiableRootModel getModifiableRootModel(final Module module) { return myProvider.getModuleModifiableModel(module); } public LibraryTable.ModifiableModel getLibraryModel(ModifiableDependencies dependencies) { assertAlive(); ModifiableRootModel modifiableModel = myProvider.getModuleModifiableModel(getEditor(dependencies).myModule); return getTableModifiableModel(modifiableModel); } private static LibraryTable.ModifiableModel getTableModifiableModel(final ModifiableRootModel modifiableModel) { return modifiableModel.getModuleLibraryTable().getModifiableModel(); } public void addSdkListListener(ChangeListener changeListener, Disposable parentDisposable) { //mySdksEditor.addSdkListListener(changeListener, parentDisposable); } @Nullable public FlexBuildConfiguration findCurrentConfiguration(final Module module, final String originalBCName) { return ContainerUtil.find(myModule2Editors.get(module), editor -> editor.myOrigin.getName().equals(originalBCName)); } public static void makeNonStructuralModification(final FlexBuildConfiguration bc, final Consumer<NonStructuralModifiableBuildConfiguration> consumer) { consumer.consume(new NonStructuralModifiableBuildConfiguration((FlexBuildConfigurationImpl)bc)); } }