package com.intellij.lang.javascript.flex.projectStructure.model.impl;
import com.intellij.ProjectTopics;
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
import com.intellij.lang.javascript.flex.projectStructure.model.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.State;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.ModuleListener;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ex.ProjectRootManagerEx;
import com.intellij.openapi.util.EmptyRunnable;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.xmlb.annotations.AbstractCollection;
import com.intellij.util.xmlb.annotations.Attribute;
import com.intellij.util.xmlb.annotations.Property;
import com.intellij.util.xmlb.annotations.Tag;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.text.MessageFormat;
import java.util.*;
@State(name = FlexBuildConfigurationManagerImpl.COMPONENT_NAME)
public class FlexBuildConfigurationManagerImpl extends FlexBuildConfigurationManager
implements PersistentStateComponent<FlexBuildConfigurationManagerImpl.State> {
private static final Logger LOG = Logger.getInstance(FlexBuildConfigurationManagerImpl.class.getName());
public static final String COMPONENT_NAME = "FlexBuildConfigurationManager";
@Nullable
private final Module myModule;
private FlexBuildConfigurationImpl[] myConfigurations = new FlexBuildConfigurationImpl[]{new FlexBuildConfigurationImpl()};
private final CompilerOptionsImpl myModuleLevelCompilerOptions;
private FlexBuildConfigurationImpl myActiveConfiguration = myConfigurations[0];
public FlexBuildConfigurationManagerImpl(@Nullable final Module module) {
myModule = module;
myModuleLevelCompilerOptions = module == null ? new CompilerOptionsImpl() : new CompilerOptionsImpl(module.getProject(), true);
if (myModule != null) {
myModule.getProject().getMessageBus().connect(myModule).subscribe(ProjectTopics.MODULES, new ModuleListener() {
@Override
public void beforeModuleRemoved(@NotNull Project project, @NotNull Module module) {
if (module != myModule) {
removeDependenciesOn(module);
}
}
});
}
}
private void removeDependenciesOn(Module module) {
for (ModifiableFlexBuildConfiguration configuration : myConfigurations) {
// TODO remove 'optimize for' links
for (Iterator<ModifiableDependencyEntry> i = configuration.getDependencies().getModifiableEntries().iterator(); i.hasNext(); ) {
DependencyEntry entry = i.next();
if (entry instanceof BuildConfigurationEntry && ((BuildConfigurationEntry)entry).findModule() == module) {
i.remove();
}
}
}
}
@Override
@Nullable
public FlexBuildConfiguration findConfigurationByName(final String name) {
for (ModifiableFlexBuildConfiguration configuration : myConfigurations) {
if (configuration.getName().equals(name)) {
return configuration;
}
}
return null;
}
@Override
public FlexBuildConfiguration getActiveConfiguration() {
return myActiveConfiguration;
}
@Override
public void setActiveBuildConfiguration(final FlexBuildConfiguration buildConfiguration) {
if (myActiveConfiguration == buildConfiguration) {
return;
}
if (!ArrayUtil.contains(buildConfiguration, myConfigurations)) {
throw new IllegalArgumentException(
"Build configuration " + buildConfiguration.getName() + " does not belong to module " +
(myModule != null ? myModule.getName() : "(dummy)"));
}
if (myModule != null) {
ApplicationManager.getApplication().runWriteAction(() -> {
myActiveConfiguration = (FlexBuildConfigurationImpl)buildConfiguration;
resetHighlighting(myModule.getProject());
});
}
else {
myActiveConfiguration = (FlexBuildConfigurationImpl)buildConfiguration;
}
}
public FlexBuildConfiguration[] getBuildConfigurations() {
return Arrays.copyOf(myConfigurations, myConfigurations.length);
}
FlexBuildConfigurationImpl[] doGetBuildConfigurations() {
return myConfigurations;
}
// TODO should be getModifiableModel()!
@Override
public ModuleOrProjectCompilerOptions getModuleLevelCompilerOptions() {
return myModuleLevelCompilerOptions;
}
void setBuildConfigurations(FlexBuildConfigurationImpl[] configurations) {
final String activeName = myActiveConfiguration != null ? myActiveConfiguration.getName() : null;
ApplicationManager.getApplication().assertWriteAccessAllowed();
FlexBuildConfigurationImpl[] validatedConfigurations = getValidatedConfigurations(Arrays.asList(configurations));
doSetBuildConfigurations(validatedConfigurations);
updateActiveConfiguration(activeName);
}
void doSetBuildConfigurations(FlexBuildConfigurationImpl[] configurations) {
myConfigurations = configurations;
}
private void updateActiveConfiguration(@Nullable final String activeName) {
if (myConfigurations.length > 0) {
myActiveConfiguration =
activeName != null ? ContainerUtil.find(myConfigurations, bc -> bc.getName().equals(activeName)) : null;
if (myActiveConfiguration == null) {
myActiveConfiguration = myConfigurations[0];
}
}
else {
myActiveConfiguration = null;
}
}
public State getState() {
final State state = new State();
for (FlexBuildConfigurationImpl configuration : myConfigurations) {
state.CONFIGURATIONS.add(configuration.getState(myModule));
}
state.myModuleLevelCompilerOptions = myModuleLevelCompilerOptions.getState(myModule);
state.myActiveConfigurationName = myActiveConfiguration != null ? myActiveConfiguration.getName() : null;
return state;
}
public void loadState(final State state) {
if (myModule == null) {
throw new IllegalStateException("Cannot load state of a dummy config manager instance");
}
Collection<FlexBuildConfigurationImpl> configurations = new ArrayList<>(state.CONFIGURATIONS.size());
for (FlexBuildConfigurationState configurationState : state.CONFIGURATIONS) {
FlexBuildConfigurationImpl configuration = new FlexBuildConfigurationImpl();
configuration.loadState(configurationState, myModule.getProject());
configurations.add(configuration);
}
doSetBuildConfigurations(getValidatedConfigurations(configurations));
updateActiveConfiguration(state.myActiveConfigurationName);
myModuleLevelCompilerOptions.loadState(state.myModuleLevelCompilerOptions);
}
static void resetHighlighting(Project project) {
ProjectRootManagerEx.getInstanceEx(project).makeRootsChange(EmptyRunnable.getInstance(), false, true);
}
private FlexBuildConfigurationImpl[] getValidatedConfigurations(Collection<? extends FlexBuildConfigurationImpl> configurations) {
if (configurations.isEmpty()) {
LOG.warn("No Flash build configurations found");
return new FlexBuildConfigurationImpl[]{new FlexBuildConfigurationImpl()};
}
List<FlexBuildConfigurationImpl> configList = new ArrayList<>(configurations);
for (FlexBuildConfigurationImpl configuration : configList) {
if (StringUtil.isEmpty(configuration.getName())) {
LOG.warn("Empty build configuration name");
configuration.setName(myModule.getName());
}
}
Set<String> names = new HashSet<>();
String duplicateName = null;
for (FlexBuildConfiguration c : configList) {
if (StringUtil.isEmpty(c.getName())) {
LOG.warn("Empty build configuration name");
continue;
}
if (!names.add(c.getName())) {
duplicateName = c.getName();
break;
}
}
if (duplicateName != null) {
LOG.warn("Duplicate build configuration name: " + duplicateName);
List<String> uniqueNames =
generateUniqueNames(ContainerUtil.map2List(configList, bc -> bc.getName()));
for (int i = 0; i < configList.size(); i++) {
configList.get(i).setName(uniqueNames.get(i));
}
}
return configList.toArray(new FlexBuildConfigurationImpl[configList.size()]);
}
public static List<String> generateUniqueNames(List<String> names) {
List<String> result = new ArrayList<>(names.size());
Set<String> namesBefore = new HashSet<>();
for (int i = 0; i < names.size(); i++) {
String name = names.get(i);
String newName = name;
if (namesBefore.contains(newName)) {
Set<String> otherNames = new HashSet<>(namesBefore);
otherNames.addAll(names.subList(i + 1, names.size()));
int index = 1;
while (true) {
newName = MessageFormat.format("{0} ({1})", name, index++);
if (!otherNames.contains(newName)) break;
}
}
result.add(newName);
namesBefore.add(newName);
}
return result;
}
public static class State {
@Tag("configurations")
@AbstractCollection(surroundWithTag = false, elementTag = "configuration")
public List<FlexBuildConfigurationState> CONFIGURATIONS = new ArrayList<>();
@Property(surroundWithTag = false)
public CompilerOptionsImpl.State myModuleLevelCompilerOptions = new CompilerOptionsImpl.State();
@Attribute("active")
public String myActiveConfigurationName;
}
}