/* * Copyright 2000-2016 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.execution.impl; import com.intellij.ProjectTopics; import com.intellij.execution.*; import com.intellij.execution.configurations.*; import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.ide.util.PropertiesComponent; import com.intellij.openapi.Disposable; import com.intellij.openapi.components.*; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.IndexNotReadyException; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ModuleRootAdapter; import com.intellij.openapi.roots.ModuleRootEvent; import com.intellij.openapi.updateSettings.impl.pluginsAdvertisement.UnknownFeaturesCollector; import com.intellij.openapi.util.*; import com.intellij.openapi.util.registry.Registry; import com.intellij.ui.IconDeferrer; import com.intellij.util.EventDispatcher; import com.intellij.util.SmartList; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.HashMap; import com.intellij.util.containers.WeakHashMap; import gnu.trove.THashMap; import gnu.trove.THashSet; import org.jdom.Element; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.util.*; @State( name = "RunManager", storages = @Storage(file = StoragePathMacros.WORKSPACE_FILE) ) public class RunManagerImpl extends RunManagerEx implements PersistentStateComponent<Element>, NamedComponent, Disposable { private static final Logger LOG = Logger.getInstance(RunManagerImpl.class); private final Project myProject; private final Map<String, ConfigurationType> myTypesByName = new LinkedHashMap<>(); private final Map<String, RunnerAndConfigurationSettings> myTemplateConfigurationsMap = new TreeMap<>(); private final Map<String, RunnerAndConfigurationSettings> myConfigurations = new LinkedHashMap<>(); // template configurations are not included here private final Map<String, Boolean> mySharedConfigurations = new THashMap<>(); private final Map<RunConfiguration, List<BeforeRunTask>> myConfigurationToBeforeTasksMap = new WeakHashMap<>(); // When readExternal not all configuration may be loaded, so we need to remember the selected configuration // so that when it is eventually loaded, we can mark is as a selected. @Nullable private String myLoadedSelectedConfigurationUniqueName = null; @Nullable private String mySelectedConfigurationId = null; private final Map<String, Icon> myIdToIcon = new HashMap<>(); private final Map<String, Long> myIconCheckTimes = new HashMap<>(); private final Map<String, Long> myIconCalcTime = Collections.synchronizedMap(new HashMap<String, Long>()); @NonNls protected static final String CONFIGURATION = "configuration"; protected static final String RECENT = "recent_temporary"; private ConfigurationType[] myTypes; private final RunManagerConfig myConfig; @NonNls protected static final String NAME_ATTR = "name"; @NonNls protected static final String SELECTED_ATTR = "selected"; @NonNls private static final String METHOD = "method"; @NonNls private static final String OPTION = "option"; private List<Element> myUnknownElements = null; private final JDOMExternalizableStringList myOrder = new JDOMExternalizableStringList(); private final ArrayList<RunConfiguration> myRecentlyUsedTemporaries = new ArrayList<>(); private boolean myOrdered = true; private final EventDispatcher<RunManagerListener> myDispatcher = EventDispatcher.create(RunManagerListener.class); public RunManagerImpl(@NotNull Project project, @NotNull PropertiesComponent propertiesComponent) { myConfig = new RunManagerConfig(propertiesComponent); myProject = project; initializeConfigurationTypes(ConfigurationType.CONFIGURATION_TYPE_EP.getExtensions()); myProject.getMessageBus().connect(myProject).subscribe(ProjectTopics.PROJECT_ROOTS, new ModuleRootAdapter() { @Override public void rootsChanged(ModuleRootEvent event) { RunnerAndConfigurationSettings configuration = getSelectedConfiguration(); if (configuration != null) { myIconCheckTimes.remove(configuration.getUniqueID());//cache will be expired } } }); } // separate method needed for tests public final void initializeConfigurationTypes(@NotNull final ConfigurationType[] factories) { Arrays.sort(factories, (o1, o2) -> o1.getDisplayName().compareTo(o2.getDisplayName())); final ArrayList<ConfigurationType> types = new ArrayList<>(Arrays.asList(factories)); types.add(UnknownConfigurationType.INSTANCE); myTypes = types.toArray(new ConfigurationType[types.size()]); for (final ConfigurationType type : factories) { myTypesByName.put(type.getId(), type); } final UnknownConfigurationType broken = UnknownConfigurationType.INSTANCE; myTypesByName.put(broken.getId(), broken); } @Override @NotNull public RunnerAndConfigurationSettings createConfiguration(@NotNull final String name, @NotNull final ConfigurationFactory factory) { return createConfiguration(doCreateConfiguration(name, factory, true), factory); } protected RunConfiguration doCreateConfiguration(@NotNull String name, @NotNull ConfigurationFactory factory, final boolean fromTemplate) { if (fromTemplate) { return factory.createConfiguration(name, getConfigurationTemplate(factory).getConfiguration()); } else { final RunConfiguration configuration = factory.createTemplateConfiguration(myProject, this); configuration.setName(name); return configuration; } } @Override @NotNull public RunnerAndConfigurationSettings createConfiguration(@NotNull final RunConfiguration runConfiguration, @NotNull final ConfigurationFactory factory) { RunnerAndConfigurationSettings template = getConfigurationTemplate(factory); RunnerAndConfigurationSettingsImpl settings = new RunnerAndConfigurationSettingsImpl(this, runConfiguration, false); settings.importRunnerAndConfigurationSettings((RunnerAndConfigurationSettingsImpl)template); if (!mySharedConfigurations.containsKey(settings.getUniqueID())) { shareConfiguration(settings, isConfigurationShared(template)); } return settings; } @Override public void dispose() { myTemplateConfigurationsMap.clear(); } @Override public RunManagerConfig getConfig() { return myConfig; } @Override @NotNull public ConfigurationType[] getConfigurationFactories() { return myTypes.clone(); } public ConfigurationType[] getConfigurationFactories(final boolean includeUnknown) { final ConfigurationType[] configurationTypes = myTypes.clone(); if (!includeUnknown) { final List<ConfigurationType> types = new ArrayList<>(); for (ConfigurationType configurationType : configurationTypes) { if (!(configurationType instanceof UnknownConfigurationType)) { types.add(configurationType); } } return types.toArray(new ConfigurationType[types.size()]); } return configurationTypes; } /** * Template configuration is not included */ @Override @NotNull public List<RunConfiguration> getConfigurationsList(@NotNull ConfigurationType type) { List<RunConfiguration> result = null; for (RunnerAndConfigurationSettings settings : getSortedConfigurations()) { RunConfiguration configuration = settings.getConfiguration(); if (type.getId().equals(configuration.getType().getId())) { if (result == null) { result = new SmartList<>(); } result.add(configuration); } } return ContainerUtil.notNullize(result); } @Override @NotNull public List<RunConfiguration> getAllConfigurationsList() { Collection<RunnerAndConfigurationSettings> sortedConfigurations = getSortedConfigurations(); if (sortedConfigurations.isEmpty()) { return Collections.emptyList(); } List<RunConfiguration> result = new ArrayList<>(sortedConfigurations.size()); for (RunnerAndConfigurationSettings settings : sortedConfigurations) { result.add(settings.getConfiguration()); } return result; } @NotNull @Override public RunConfiguration[] getAllConfigurations() { List<RunConfiguration> list = getAllConfigurationsList(); return list.toArray(new RunConfiguration[list.size()]); } @NotNull @Override public List<RunnerAndConfigurationSettings> getAllSettings() { return new ArrayList<>(getSortedConfigurations()); } @Nullable public RunnerAndConfigurationSettings getSettings(@Nullable RunConfiguration configuration) { if (configuration == null) { return null; } for (RunnerAndConfigurationSettings settings : getSortedConfigurations()) { if (settings.getConfiguration() == configuration) { return settings; } } return null; } /** * Template configuration is not included */ @Override @NotNull public List<RunnerAndConfigurationSettings> getConfigurationSettingsList(@NotNull ConfigurationType type) { List<RunnerAndConfigurationSettings> result = new SmartList<>(); for (RunnerAndConfigurationSettings configuration : getSortedConfigurations()) { ConfigurationType configurationType = configuration.getType(); if (configurationType != null && type.getId().equals(configurationType.getId())) { result.add(configuration); } } return result; } @NotNull @Override public RunnerAndConfigurationSettings[] getConfigurationSettings(@NotNull ConfigurationType type) { List<RunnerAndConfigurationSettings> list = getConfigurationSettingsList(type); return list.toArray(new RunnerAndConfigurationSettings[list.size()]); } @NotNull @Override public RunConfiguration[] getConfigurations(@NotNull ConfigurationType type) { RunnerAndConfigurationSettings[] settings = getConfigurationSettings(type); RunConfiguration[] result = new RunConfiguration[settings.length]; for (int i = 0; i < settings.length; i++) { result[i] = settings[i].getConfiguration(); } return result; } @NotNull @Override public Map<String, List<RunnerAndConfigurationSettings>> getStructure(@NotNull ConfigurationType type) { LinkedHashMap<String, List<RunnerAndConfigurationSettings>> map = new LinkedHashMap<>(); List<RunnerAndConfigurationSettings> typeList = new ArrayList<>(); List<RunnerAndConfigurationSettings> settings = getConfigurationSettingsList(type); for (RunnerAndConfigurationSettings setting : settings) { String folderName = setting.getFolderName(); if (folderName == null) { typeList.add(setting); } else { List<RunnerAndConfigurationSettings> list = map.get(folderName); if (list == null) { map.put(folderName, list = new ArrayList<>()); } list.add(setting); } } LinkedHashMap<String, List<RunnerAndConfigurationSettings>> result = new LinkedHashMap<>(); for (Map.Entry<String, List<RunnerAndConfigurationSettings>> entry : map.entrySet()) { result.put(entry.getKey(), Collections.unmodifiableList(entry.getValue())); } result.put(null, Collections.unmodifiableList(typeList)); return Collections.unmodifiableMap(result); } @Override @NotNull public RunnerAndConfigurationSettings getConfigurationTemplate(@NotNull ConfigurationFactory factory) { RunnerAndConfigurationSettings template = myTemplateConfigurationsMap.get(factory.getType().getId() + "." + factory.getName()); if (template == null) { template = new RunnerAndConfigurationSettingsImpl(this, factory.createTemplateConfiguration(myProject, this), true); template.setSingleton(factory.isConfigurationSingletonByDefault()); if (template.getConfiguration() instanceof UnknownRunConfiguration) { ((UnknownRunConfiguration)template.getConfiguration()).setDoNotStore(true); } myTemplateConfigurationsMap.put(factory.getType().getId() + "." + factory.getName(), template); } return template; } @Override public void addConfiguration(RunnerAndConfigurationSettings settings, boolean shared, List<BeforeRunTask> tasks, boolean addEnabledTemplateTasksIfAbsent) { String existingId = findExistingConfigurationId(settings); String newId = settings.getUniqueID(); RunnerAndConfigurationSettings existingSettings = null; if (existingId != null) { existingSettings = myConfigurations.remove(existingId); mySharedConfigurations.remove(existingId); } if (mySelectedConfigurationId != null && mySelectedConfigurationId.equals(existingId)) { setSelectedConfigurationId(newId); } myConfigurations.put(newId, settings); RunConfiguration configuration = settings.getConfiguration(); if (existingId == null) { refreshUsagesList(configuration); } checkRecentsLimit(); mySharedConfigurations.put(newId, shared); setBeforeRunTasks(configuration, tasks, addEnabledTemplateTasksIfAbsent); if (existingSettings == settings) { myDispatcher.getMulticaster().runConfigurationChanged(settings, existingId); } else { myDispatcher.getMulticaster().runConfigurationAdded(settings); } } @Override public void refreshUsagesList(RunProfile profile) { if (!(profile instanceof RunConfiguration)) return; RunnerAndConfigurationSettings settings = getSettings((RunConfiguration)profile); if (settings != null && settings.isTemporary()) { myRecentlyUsedTemporaries.remove((RunConfiguration)profile); myRecentlyUsedTemporaries.add(0, (RunConfiguration)profile); trimUsagesListToLimit(); } } @Override public boolean hasSettings(RunnerAndConfigurationSettings settings) { return myConfigurations.get(settings.getUniqueID()) != null; } private void trimUsagesListToLimit() { while(myRecentlyUsedTemporaries.size() > getConfig().getRecentsLimit()) { myRecentlyUsedTemporaries.remove(myRecentlyUsedTemporaries.size() - 1); } } void checkRecentsLimit() { trimUsagesListToLimit(); List<RunnerAndConfigurationSettings> removed = new SmartList<>(); while (getTempConfigurationsList().size() > getConfig().getRecentsLimit()) { for (Iterator<RunnerAndConfigurationSettings> it = myConfigurations.values().iterator(); it.hasNext(); ) { RunnerAndConfigurationSettings configuration = it.next(); if (configuration.isTemporary() && !myRecentlyUsedTemporaries.contains(configuration.getConfiguration())) { removed.add(configuration); it.remove(); break; } } } fireRunConfigurationsRemoved(removed); } public void setOrdered(boolean ordered) { myOrdered = ordered; } public void saveOrder() { setOrder(null); } private void doSaveOrder(@Nullable Comparator<RunnerAndConfigurationSettings> comparator) { List<RunnerAndConfigurationSettings> sorted = new ArrayList<>( ContainerUtil.filter(myConfigurations.values(), o -> !(o.getType() instanceof UnknownConfigurationType))); if (comparator != null) sorted.sort(comparator); myOrder.clear(); for (RunnerAndConfigurationSettings each : sorted) { myOrder.add(each.getUniqueID()); } } public void setOrder(@Nullable Comparator<RunnerAndConfigurationSettings> comparator) { doSaveOrder(comparator); setOrdered(false);// force recache of configurations list } @Override public void removeConfiguration(@Nullable RunnerAndConfigurationSettings settings) { if (settings == null) return; for (Iterator<RunnerAndConfigurationSettings> it = getSortedConfigurations().iterator(); it.hasNext(); ) { final RunnerAndConfigurationSettings configuration = it.next(); if (configuration.equals(settings)) { if (mySelectedConfigurationId != null && mySelectedConfigurationId.equals(settings.getUniqueID())) { setSelectedConfiguration(null); } it.remove(); mySharedConfigurations.remove(settings.getUniqueID()); myConfigurationToBeforeTasksMap.remove(settings.getConfiguration()); myRecentlyUsedTemporaries.remove(settings.getConfiguration()); myDispatcher.getMulticaster().runConfigurationRemoved(configuration); break; } } for (Map.Entry<RunConfiguration, List<BeforeRunTask>> entry : myConfigurationToBeforeTasksMap.entrySet()) { for (Iterator<BeforeRunTask> iterator = entry.getValue().iterator(); iterator.hasNext(); ) { BeforeRunTask task = iterator.next(); if (task instanceof RunConfigurationBeforeRunProvider.RunConfigurableBeforeRunTask && settings.equals(((RunConfigurationBeforeRunProvider.RunConfigurableBeforeRunTask)task).getSettings())) { iterator.remove(); RunnerAndConfigurationSettings changedSettings = getSettings(entry.getKey()); if (changedSettings != null) { myDispatcher.getMulticaster().runConfigurationChanged(changedSettings, null); } } } } } @Override @Nullable public RunnerAndConfigurationSettings getSelectedConfiguration() { if (mySelectedConfigurationId == null && myLoadedSelectedConfigurationUniqueName != null) { setSelectedConfigurationId(myLoadedSelectedConfigurationUniqueName); } return mySelectedConfigurationId == null ? null : myConfigurations.get(mySelectedConfigurationId); } @Override public void setSelectedConfiguration(@Nullable RunnerAndConfigurationSettings settings) { setSelectedConfigurationId(settings == null ? null : settings.getUniqueID()); fireRunConfigurationSelected(); } private void setSelectedConfigurationId(@Nullable String id) { mySelectedConfigurationId = id; if (mySelectedConfigurationId != null) myLoadedSelectedConfigurationUniqueName = null; } @Override @NotNull public Collection<RunnerAndConfigurationSettings> getSortedConfigurations() { if (myOrdered) { return myConfigurations.values(); } List<Pair<String, RunnerAndConfigurationSettings>> order = new ArrayList<>(myConfigurations.size()); final List<String> folderNames = new SmartList<>(); for (RunnerAndConfigurationSettings each : myConfigurations.values()) { order.add(Pair.create(each.getUniqueID(), each)); String folderName = each.getFolderName(); if (folderName != null && !folderNames.contains(folderName)) { folderNames.add(folderName); } } folderNames.add(null); myConfigurations.clear(); if (myOrder.isEmpty()) { // IDEA-63663 Sort run configurations alphabetically if clean checkout Collections.sort(order, (o1, o2) -> { boolean temporary1 = o1.getSecond().isTemporary(); boolean temporary2 = o2.getSecond().isTemporary(); if (temporary1 == temporary2) { return o1.first.compareTo(o2.first); } else { return temporary1 ? 1 : -1; } }); } else { Collections.sort(order, (o1, o2) -> { int i1 = folderNames.indexOf(o1.getSecond().getFolderName()); int i2 = folderNames.indexOf(o2.getSecond().getFolderName()); if (i1 != i2) { return i1 - i2; } boolean temporary1 = o1.getSecond().isTemporary(); boolean temporary2 = o2.getSecond().isTemporary(); if (temporary1 == temporary2) { int index1 = myOrder.indexOf(o1.first); int index2 = myOrder.indexOf(o2.first); if (index1 == -1 && index2 == -1) { return o1.second.getName().compareTo(o2.second.getName()); } return index1 - index2; } else { return temporary1 ? 1 : -1; } }); } for (Pair<String, RunnerAndConfigurationSettings> each : order) { RunnerAndConfigurationSettings setting = each.second; myConfigurations.put(setting.getUniqueID(), setting); } myOrdered = true; return myConfigurations.values(); } public static boolean canRunConfiguration(@NotNull ExecutionEnvironment environment) { RunnerAndConfigurationSettings runnerAndConfigurationSettings = environment.getRunnerAndConfigurationSettings(); return runnerAndConfigurationSettings != null && canRunConfiguration(runnerAndConfigurationSettings, environment.getExecutor()); } public static boolean canRunConfiguration(@NotNull RunnerAndConfigurationSettings configuration, @NotNull Executor executor) { try { configuration.checkSettings(executor); } catch (IndexNotReadyException ignored) { return Registry.is("dumb.aware.run.configurations"); } catch (RuntimeConfigurationError ignored) { return false; } catch (RuntimeConfigurationException ignored) { return true; } return true; } @Nullable @Override public Element getState() { Element parentNode = new Element("state"); // writes temporary configurations here writeContext(parentNode); for (RunnerAndConfigurationSettings configuration : myTemplateConfigurationsMap.values()) { if (configuration.getConfiguration() instanceof UnknownRunConfiguration && ((UnknownRunConfiguration)configuration.getConfiguration()).isDoNotStore()) { continue; } addConfigurationElement(parentNode, configuration); } for (RunnerAndConfigurationSettings configuration : getStableConfigurations(false)) { addConfigurationElement(parentNode, configuration); } JDOMExternalizableStringList order = null; for (RunnerAndConfigurationSettings each : myConfigurations.values()) { if (each.getType() instanceof UnknownConfigurationType) { continue; } if (order == null) { order = new JDOMExternalizableStringList(); } order.add(each.getUniqueID()); } if (order != null) { order.writeExternal(parentNode); } final JDOMExternalizableStringList recentList = new JDOMExternalizableStringList(); for (RunConfiguration each : myRecentlyUsedTemporaries) { if (each.getType() instanceof UnknownConfigurationType) { continue; } RunnerAndConfigurationSettings settings = getSettings(each); if (settings == null) { continue; } recentList.add(settings.getUniqueID()); } if (!recentList.isEmpty()) { Element recent = new Element(RECENT); parentNode.addContent(recent); recentList.writeExternal(recent); } if (myUnknownElements != null) { for (Element unloadedElement : myUnknownElements) { parentNode.addContent(unloadedElement.clone()); } } return parentNode; } public void writeContext(@NotNull Element parentNode) { Collection<RunnerAndConfigurationSettings> values = new ArrayList<>(myConfigurations.values()); for (RunnerAndConfigurationSettings configurationSettings : values) { if (configurationSettings.isTemporary()) { addConfigurationElement(parentNode, configurationSettings, CONFIGURATION); } } RunnerAndConfigurationSettings selected = getSelectedConfiguration(); if (selected != null) { parentNode.setAttribute(SELECTED_ATTR, selected.getUniqueID()); } } void addConfigurationElement(@NotNull Element parentNode, RunnerAndConfigurationSettings template) { addConfigurationElement(parentNode, template, CONFIGURATION); } private void addConfigurationElement(@NotNull Element parentNode, RunnerAndConfigurationSettings settings, String elementType) { Element configurationElement = new Element(elementType); parentNode.addContent(configurationElement); try { ((RunnerAndConfigurationSettingsImpl)settings).writeExternal(configurationElement); } catch (WriteExternalException e) { throw new RuntimeException(e); } if (settings.getConfiguration() instanceof UnknownRunConfiguration) { return; } List<BeforeRunTask> tasks = new ArrayList<>(getBeforeRunTasks(settings.getConfiguration())); Map<Key<BeforeRunTask>, BeforeRunTask> templateTasks = new THashMap<>(); List<BeforeRunTask> beforeRunTasks = settings.isTemplate() ? getHardcodedBeforeRunTasks(settings.getConfiguration()) : getBeforeRunTasks(getConfigurationTemplate(settings.getFactory()).getConfiguration()); for (BeforeRunTask templateTask : beforeRunTasks) { templateTasks.put(templateTask.getProviderId(), templateTask); if (templateTask.isEnabled()) { boolean found = false; for (BeforeRunTask realTask : tasks) { if (realTask.getProviderId() == templateTask.getProviderId()) { found = true; break; } } if (!found) { BeforeRunTask clone = templateTask.clone(); clone.setEnabled(false); tasks.add(0, clone); } } } Element methodsElement = new Element(METHOD); for (int i = 0, size = tasks.size(); i < size; i++) { BeforeRunTask task = tasks.get(i); int j = 0; BeforeRunTask templateTask = null; for (Map.Entry<Key<BeforeRunTask>, BeforeRunTask> entry : templateTasks.entrySet()) { if (entry.getKey() == task.getProviderId()) { templateTask = entry.getValue(); break; } j++; } if (task.equals(templateTask) && i == j) { // not necessary saving if the task is the same as template and on the same place continue; } Element child = new Element(OPTION); child.setAttribute(NAME_ATTR, task.getProviderId().toString()); task.writeExternal(child); methodsElement.addContent(child); } configurationElement.addContent(methodsElement); } @Override public void loadState(Element parentNode) { clear(false); List<Element> children = parentNode.getChildren(CONFIGURATION); Element[] sortedElements = children.toArray(new Element[children.size()]); // ensure templates are loaded first Arrays.sort(sortedElements, (a, b) -> { final boolean aDefault = Boolean.valueOf(a.getAttributeValue("default", "false")); final boolean bDefault = Boolean.valueOf(b.getAttributeValue("default", "false")); return aDefault == bDefault ? 0 : aDefault ? -1 : 1; }); // element could be detached, so, we must not use for each //noinspection ForLoopReplaceableByForEach for (int i = 0, length = sortedElements.length; i < length; i++) { Element element = sortedElements[i]; RunnerAndConfigurationSettings configurationSettings; try { configurationSettings = loadConfiguration(element, false); } catch (ProcessCanceledException e) { configurationSettings = null; } catch (Throwable e) { LOG.error(e); continue; } if (configurationSettings == null) { if (myUnknownElements == null) { myUnknownElements = new SmartList<>(); } myUnknownElements.add((Element)element.detach()); } } myOrder.readExternal(parentNode); // migration (old ids to UUIDs) readList(myOrder); myRecentlyUsedTemporaries.clear(); Element recentNode = parentNode.getChild(RECENT); if (recentNode != null) { JDOMExternalizableStringList list = new JDOMExternalizableStringList(); list.readExternal(recentNode); readList(list); for (String name : list) { RunnerAndConfigurationSettings settings = myConfigurations.get(name); if (settings != null) { myRecentlyUsedTemporaries.add(settings.getConfiguration()); } } } myOrdered = false; myLoadedSelectedConfigurationUniqueName = parentNode.getAttributeValue(SELECTED_ATTR); setSelectedConfigurationId(myLoadedSelectedConfigurationUniqueName); fireBeforeRunTasksUpdated(); fireRunConfigurationSelected(); } private void readList(@NotNull JDOMExternalizableStringList list) { for (int i = 0; i < list.size(); i++) { for (RunnerAndConfigurationSettings settings : myConfigurations.values()) { RunConfiguration configuration = settings.getConfiguration(); //noinspection deprecation if (configuration != null && list.get(i).equals(configuration.getType().getDisplayName() + "." + configuration.getName() + (configuration instanceof UnknownRunConfiguration ? configuration.getUniqueID() : ""))) { list.set(i, settings.getUniqueID()); break; } } } } public void readContext(Element parentNode) throws InvalidDataException { myLoadedSelectedConfigurationUniqueName = parentNode.getAttributeValue(SELECTED_ATTR); for (Object aChildren : parentNode.getChildren()) { Element element = (Element)aChildren; RunnerAndConfigurationSettings config = loadConfiguration(element, false); if (myLoadedSelectedConfigurationUniqueName == null && config != null && Boolean.valueOf(element.getAttributeValue(SELECTED_ATTR)).booleanValue()) { myLoadedSelectedConfigurationUniqueName = config.getUniqueID(); } } setSelectedConfigurationId(myLoadedSelectedConfigurationUniqueName); fireRunConfigurationSelected(); } @Nullable private String findExistingConfigurationId(@Nullable RunnerAndConfigurationSettings settings) { if (settings != null) { for (Map.Entry<String, RunnerAndConfigurationSettings> entry : myConfigurations.entrySet()) { if (entry.getValue() == settings) { return entry.getKey(); } } } return null; } // used by MPS, don't delete public void clearAll() { clear(true); myTypesByName.clear(); initializeConfigurationTypes(new ConfigurationType[0]); } private void clear(boolean allConfigurations) { List<RunnerAndConfigurationSettings> configurations; if (allConfigurations) { myConfigurations.clear(); mySharedConfigurations.clear(); myConfigurationToBeforeTasksMap.clear(); mySelectedConfigurationId = null; configurations = new ArrayList<>(myConfigurations.values()); } else { configurations = new SmartList<>(); for (Iterator<RunnerAndConfigurationSettings> iterator = myConfigurations.values().iterator(); iterator.hasNext(); ) { RunnerAndConfigurationSettings configuration = iterator.next(); if (configuration.isTemporary() || !isConfigurationShared(configuration)) { iterator.remove(); mySharedConfigurations.remove(configuration.getUniqueID()); myConfigurationToBeforeTasksMap.remove(configuration.getConfiguration()); configurations.add(configuration); } } if (mySelectedConfigurationId != null && myConfigurations.containsKey(mySelectedConfigurationId)) { mySelectedConfigurationId = null; } } myUnknownElements = null; myTemplateConfigurationsMap.clear(); myLoadedSelectedConfigurationUniqueName = null; myIdToIcon.clear(); myIconCheckTimes.clear(); myIconCalcTime.clear(); myRecentlyUsedTemporaries.clear(); fireRunConfigurationsRemoved(configurations); } @Nullable public RunnerAndConfigurationSettings loadConfiguration(@NotNull Element element, boolean isShared) { RunnerAndConfigurationSettingsImpl settings = new RunnerAndConfigurationSettingsImpl(this); try { settings.readExternal(element); } catch (InvalidDataException e) { LOG.error(e); } ConfigurationFactory factory = settings.getFactory(); if (factory == null) { return null; } List<BeforeRunTask> tasks = readStepsBeforeRun(element.getChild(METHOD), settings); if (settings.isTemplate()) { myTemplateConfigurationsMap.put(factory.getType().getId() + "." + factory.getName(), settings); setBeforeRunTasks(settings.getConfiguration(), tasks, true); } else { addConfiguration(settings, isShared, tasks, true); if (Boolean.valueOf(element.getAttributeValue(SELECTED_ATTR)).booleanValue()) { //to support old style setSelectedConfiguration(settings); } } return settings; } @NotNull private List<BeforeRunTask> readStepsBeforeRun(@Nullable Element child, @NotNull RunnerAndConfigurationSettings settings) { List<BeforeRunTask> result = null; if (child != null) { for (Element methodElement : child.getChildren(OPTION)) { Key<? extends BeforeRunTask> id = getProviderKey(methodElement.getAttributeValue(NAME_ATTR)); BeforeRunTask beforeRunTask = getProvider(id).createTask(settings.getConfiguration()); if (beforeRunTask != null) { beforeRunTask.readExternal(methodElement); if (result == null) { result = new SmartList<>(); } result.add(beforeRunTask); } } } return ContainerUtil.notNullize(result); } @Nullable public ConfigurationType getConfigurationType(final String typeName) { return myTypesByName.get(typeName); } @Nullable public ConfigurationFactory getFactory(final String typeName, String factoryName) { return getFactory(typeName, factoryName, false); } @Nullable public ConfigurationFactory getFactory(final String typeName, String factoryName, boolean checkUnknown) { final ConfigurationType type = myTypesByName.get(typeName); if (type == null && checkUnknown && typeName != null) { UnknownFeaturesCollector.getInstance(myProject).registerUnknownFeature(ConfigurationType.CONFIGURATION_TYPE_EP.getName(), typeName); } if (factoryName == null) { factoryName = type != null ? type.getConfigurationFactories()[0].getName() : null; } return findFactoryOfTypeNameByName(typeName, factoryName); } @Nullable private ConfigurationFactory findFactoryOfTypeNameByName(final String typeName, final String factoryName) { ConfigurationType type = myTypesByName.get(typeName); if (type == null) { type = myTypesByName.get(UnknownConfigurationType.NAME); } return findFactoryOfTypeByName(type, factoryName); } @Nullable private static ConfigurationFactory findFactoryOfTypeByName(final ConfigurationType type, final String factoryName) { if (factoryName == null) return null; if (type instanceof UnknownConfigurationType) { return type.getConfigurationFactories()[0]; } final ConfigurationFactory[] factories = type.getConfigurationFactories(); for (final ConfigurationFactory factory : factories) { if (factoryName.equals(factory.getName())) return factory; } return null; } @Override @NotNull public String getComponentName() { return "RunManager"; } @Override public void setTemporaryConfiguration(@Nullable final RunnerAndConfigurationSettings tempConfiguration) { if (tempConfiguration == null) return; tempConfiguration.setTemporary(true); addConfiguration(tempConfiguration, isConfigurationShared(tempConfiguration), getBeforeRunTasks(tempConfiguration.getConfiguration()), false); if (Registry.is("select.run.configuration.from.context")) { setSelectedConfiguration(tempConfiguration); } } @NotNull Collection<RunnerAndConfigurationSettings> getStableConfigurations(boolean shared) { List<RunnerAndConfigurationSettings> result = null; for (RunnerAndConfigurationSettings configuration : myConfigurations.values()) { if (!configuration.isTemporary() && isConfigurationShared(configuration) == shared) { if (result == null) { result = new SmartList<>(); } result.add(configuration); } } return ContainerUtil.notNullize(result); } @NotNull Collection<? extends RunnerAndConfigurationSettings> getConfigurationSettings() { return myConfigurations.values(); } @Override public boolean isTemporary(@NotNull final RunConfiguration configuration) { return Arrays.asList(getTempConfigurations()).contains(configuration); } @Override @NotNull public List<RunnerAndConfigurationSettings> getTempConfigurationsList() { List<RunnerAndConfigurationSettings> configurations = ContainerUtil.filter(myConfigurations.values(), RunnerAndConfigurationSettings::isTemporary); return Collections.unmodifiableList(configurations); } @NotNull @Override public RunConfiguration[] getTempConfigurations() { List<RunnerAndConfigurationSettings> list = getTempConfigurationsList(); RunConfiguration[] result = new RunConfiguration[list.size()]; for (int i = 0; i < list.size(); i++) { result[i] = list.get(i).getConfiguration(); } return result; } @Override public void makeStable(@NotNull RunnerAndConfigurationSettings settings) { settings.setTemporary(false); myRecentlyUsedTemporaries.remove(settings.getConfiguration()); if (!myOrder.isEmpty()) { setOrdered(false); } fireRunConfigurationChanged(settings); } @Override public void makeStable(@NotNull RunConfiguration configuration) { RunnerAndConfigurationSettings settings = getSettings(configuration); if (settings != null) { makeStable(settings); } } @Override @NotNull public RunnerAndConfigurationSettings createRunConfiguration(@NotNull String name, @NotNull ConfigurationFactory type) { return createConfiguration(name, type); } @Override public boolean isConfigurationShared(final RunnerAndConfigurationSettings settings) { Boolean shared = mySharedConfigurations.get(settings.getUniqueID()); if (shared == null) { final RunnerAndConfigurationSettings template = getConfigurationTemplate(settings.getFactory()); shared = mySharedConfigurations.get(template.getUniqueID()); } return shared != null && shared.booleanValue(); } @Override @NotNull public <T extends BeforeRunTask> List<T> getBeforeRunTasks(Key<T> taskProviderID) { final List<T> tasks = new ArrayList<>(); final List<RunnerAndConfigurationSettings> checkedTemplates = new ArrayList<>(); List<RunnerAndConfigurationSettings> settingsList = new ArrayList<>(myConfigurations.values()); for (RunnerAndConfigurationSettings settings : settingsList) { final List<BeforeRunTask> runTasks = getBeforeRunTasks(settings.getConfiguration()); for (BeforeRunTask task : runTasks) { if (task != null && task.isEnabled() && task.getProviderId() == taskProviderID) { tasks.add((T)task); } else { final RunnerAndConfigurationSettings template = getConfigurationTemplate(settings.getFactory()); if (!checkedTemplates.contains(template)) { checkedTemplates.add(template); final List<BeforeRunTask> templateTasks = getBeforeRunTasks(template.getConfiguration()); for (BeforeRunTask templateTask : templateTasks) { if (templateTask != null && templateTask.isEnabled() && templateTask.getProviderId() == taskProviderID) { tasks.add((T)templateTask); } } } } } } return tasks; } @Override public Icon getConfigurationIcon(@NotNull final RunnerAndConfigurationSettings settings) { final String uniqueID = settings.getUniqueID(); RunnerAndConfigurationSettings selectedConfiguration = getSelectedConfiguration(); String selectedId = selectedConfiguration != null ? selectedConfiguration.getUniqueID() : ""; if (selectedId.equals(uniqueID)) { Long lastCheckTime = myIconCheckTimes.get(uniqueID); Long calcTime = myIconCalcTime.get(uniqueID); if (calcTime == null || calcTime<150) calcTime = 150L; if (lastCheckTime == null || System.currentTimeMillis() - lastCheckTime > calcTime*10) { myIdToIcon.remove(uniqueID);//cache has expired } } Icon icon = myIdToIcon.get(uniqueID); if (icon == null) { icon = IconDeferrer.getInstance().deferAutoUpdatable(settings.getConfiguration().getIcon(), myProject.hashCode() ^ settings.hashCode(), param -> { if (myProject.isDisposed()) return null; myIconCalcTime.remove(uniqueID); long startTime = System.currentTimeMillis(); Icon icon1; if (DumbService.isDumb(myProject) && !Registry.is("dumb.aware.run.configurations")) { icon1 = IconLoader.getDisabledIcon(ProgramRunnerUtil.getRawIcon(settings)); if (settings.isTemporary()) { icon1 = ProgramRunnerUtil.getTemporaryIcon(icon1); } } else { try { DumbService.getInstance(myProject).setAlternativeResolveEnabled(true); settings.checkSettings(); icon1 = ProgramRunnerUtil.getConfigurationIcon(settings, false); } catch (IndexNotReadyException e) { icon1 = ProgramRunnerUtil.getConfigurationIcon(settings, !Registry.is("dumb.aware.run.configurations")); } catch (RuntimeConfigurationException ignored) { icon1 = ProgramRunnerUtil.getConfigurationIcon(settings, true); } finally { DumbService.getInstance(myProject).setAlternativeResolveEnabled(false); } } myIconCalcTime.put(uniqueID, System.currentTimeMillis() - startTime); return icon1; }); myIdToIcon.put(uniqueID, icon); myIconCheckTimes.put(uniqueID, System.currentTimeMillis()); } return icon; } public RunnerAndConfigurationSettings getConfigurationById(@NotNull final String id) { return myConfigurations.get(id); } @Override @Nullable public RunnerAndConfigurationSettings findConfigurationByName(@Nullable String name) { if (name == null) return null; for (RunnerAndConfigurationSettings each : myConfigurations.values()) { if (name.equals(each.getName())) return each; } return null; } @Nullable public RunnerAndConfigurationSettings findConfigurationByTypeAndName(@NotNull String typeId, @NotNull String name) { for (RunnerAndConfigurationSettings settings : getSortedConfigurations()) { ConfigurationType t = settings.getType(); if (t != null && typeId.equals(t.getId()) && name.equals(settings.getName())) { return settings; } } return null; } @NotNull @Override public <T extends BeforeRunTask> List<T> getBeforeRunTasks(RunConfiguration settings, Key<T> taskProviderID) { if (settings instanceof WrappingRunConfiguration) { return getBeforeRunTasks(((WrappingRunConfiguration)settings).getPeer(), taskProviderID); } List<BeforeRunTask> tasks = myConfigurationToBeforeTasksMap.get(settings); if (tasks == null) { tasks = getBeforeRunTasks(settings); myConfigurationToBeforeTasksMap.put(settings, tasks); } List<T> result = new SmartList<>(); for (BeforeRunTask task : tasks) { if (task.getProviderId() == taskProviderID) { //noinspection unchecked result.add((T)task); } } return result; } @Override @NotNull public List<BeforeRunTask> getBeforeRunTasks(final RunConfiguration settings) { if (settings instanceof WrappingRunConfiguration) { return getBeforeRunTasks(((WrappingRunConfiguration)settings).getPeer()); } List<BeforeRunTask> tasks = myConfigurationToBeforeTasksMap.get(settings); return tasks == null ? getTemplateBeforeRunTasks(settings) : getCopies(tasks); } private List<BeforeRunTask> getTemplateBeforeRunTasks(@NotNull RunConfiguration settings) { final RunnerAndConfigurationSettings template = getConfigurationTemplate(settings.getFactory()); final List<BeforeRunTask> templateTasks = myConfigurationToBeforeTasksMap.get(template.getConfiguration()); return templateTasks == null ? getHardcodedBeforeRunTasks(settings) : getCopies(templateTasks); } @NotNull private List<BeforeRunTask> getHardcodedBeforeRunTasks(@NotNull RunConfiguration settings) { List<BeforeRunTask> _tasks = new SmartList<>(); for (BeforeRunTaskProvider<? extends BeforeRunTask> provider : Extensions .getExtensions(BeforeRunTaskProvider.EXTENSION_POINT_NAME, myProject)) { BeforeRunTask task = provider.createTask(settings); if (task != null && task.isEnabled()) { Key<? extends BeforeRunTask> providerID = provider.getId(); settings.getFactory().configureBeforeRunTaskDefaults(providerID, task); if (task.isEnabled()) { _tasks.add(task); } } } return _tasks; } @NotNull private static List<BeforeRunTask> getCopies(@NotNull List<BeforeRunTask> original) { List<BeforeRunTask> result = new SmartList<>(); for (BeforeRunTask task : original) { if (task.isEnabled()) { result.add(task.clone()); } } return result; } public void shareConfiguration(final RunnerAndConfigurationSettings settings, final boolean shareConfiguration) { boolean shouldFire = settings != null && isConfigurationShared(settings) != shareConfiguration; if (shareConfiguration && settings.isTemporary()) { makeStable(settings); } mySharedConfigurations.put(settings.getUniqueID(), shareConfiguration); if (shouldFire) { fireRunConfigurationChanged(settings); } } @Override public final void setBeforeRunTasks(final RunConfiguration runConfiguration, @NotNull List<BeforeRunTask> tasks, boolean addEnabledTemplateTasksIfAbsent) { List<BeforeRunTask> result = new SmartList<>(tasks); if (addEnabledTemplateTasksIfAbsent) { List<BeforeRunTask> templates = getTemplateBeforeRunTasks(runConfiguration); Set<Key<BeforeRunTask>> idsToSet = new THashSet<>(); for (BeforeRunTask task : tasks) { idsToSet.add(task.getProviderId()); } int i = 0; for (BeforeRunTask template : templates) { if (!idsToSet.contains(template.getProviderId())) { result.add(i, template); i++; } } } myConfigurationToBeforeTasksMap.put(runConfiguration, ContainerUtil.notNullize(result)); fireBeforeRunTasksUpdated(); } public final void resetBeforeRunTasks(final RunConfiguration runConfiguration) { myConfigurationToBeforeTasksMap.remove(runConfiguration); fireBeforeRunTasksUpdated(); } @Override public void addConfiguration(final RunnerAndConfigurationSettings settings, final boolean isShared) { addConfiguration(settings, isShared, getTemplateBeforeRunTasks(settings.getConfiguration()), false); } public static RunManagerImpl getInstanceImpl(final Project project) { return (RunManagerImpl)RunManager.getInstance(project); } void removeNotExistingSharedConfigurations(@NotNull Set<String> existing) { List<RunnerAndConfigurationSettings> removed = null; for (Iterator<Map.Entry<String, RunnerAndConfigurationSettings>> it = myConfigurations.entrySet().iterator(); it.hasNext(); ) { Map.Entry<String, RunnerAndConfigurationSettings> entry = it.next(); final RunnerAndConfigurationSettings settings = entry.getValue(); if (!settings.isTemplate() && isConfigurationShared(settings) && !existing.contains(settings.getUniqueID())) { if (removed == null) { removed = new SmartList<>(); } removed.add(settings); it.remove(); } } fireRunConfigurationsRemoved(removed); } public void fireBeginUpdate() { myDispatcher.getMulticaster().beginUpdate(); } public void fireEndUpdate() { myDispatcher.getMulticaster().endUpdate(); } public void fireRunConfigurationChanged(@NotNull RunnerAndConfigurationSettings settings) { myDispatcher.getMulticaster().runConfigurationChanged(settings, null); } private void fireRunConfigurationsRemoved(@Nullable List<RunnerAndConfigurationSettings> removed) { if (!ContainerUtil.isEmpty(removed)) { myRecentlyUsedTemporaries.removeAll(removed); for (RunnerAndConfigurationSettings settings : removed) { myDispatcher.getMulticaster().runConfigurationRemoved(settings); } } } private void fireRunConfigurationSelected() { myDispatcher.getMulticaster().runConfigurationSelected(); } @Override public void addRunManagerListener(RunManagerListener listener) { myDispatcher.addListener(listener); } @Override public void removeRunManagerListener(RunManagerListener listener) { myDispatcher.removeListener(listener); } public void fireBeforeRunTasksUpdated() { myDispatcher.getMulticaster().beforeRunTasksChanged(); } private Map<Key<? extends BeforeRunTask>, BeforeRunTaskProvider> myBeforeStepsMap; private Map<String, Key<? extends BeforeRunTask>> myProviderKeysMap; @NotNull private synchronized BeforeRunTaskProvider getProvider(Key<? extends BeforeRunTask> providerId) { if (myBeforeStepsMap == null) { initProviderMaps(); } return myBeforeStepsMap.get(providerId); } @NotNull private synchronized Key<? extends BeforeRunTask> getProviderKey(String keyString) { if (myProviderKeysMap == null) { initProviderMaps(); } Key<? extends BeforeRunTask> id = myProviderKeysMap.get(keyString); if (id == null) { final UnknownBeforeRunTaskProvider provider = new UnknownBeforeRunTaskProvider(keyString); id = provider.getId(); myProviderKeysMap.put(keyString, id); myBeforeStepsMap.put(id, provider); } return id; } private void initProviderMaps() { myBeforeStepsMap = new LinkedHashMap<>(); myProviderKeysMap = new LinkedHashMap<>(); for (BeforeRunTaskProvider<? extends BeforeRunTask> provider : Extensions .getExtensions(BeforeRunTaskProvider.EXTENSION_POINT_NAME, myProject)) { final Key<? extends BeforeRunTask> id = provider.getId(); myBeforeStepsMap.put(id, provider); myProviderKeysMap.put(id.toString(), id); } } }