| /* |
| * Copyright 2000-2014 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.actions; |
| |
| import com.intellij.execution.Location; |
| import com.intellij.execution.PsiLocation; |
| import com.intellij.execution.RunManager; |
| import com.intellij.execution.RunnerAndConfigurationSettings; |
| import com.intellij.execution.configurations.ConfigurationFactory; |
| import com.intellij.execution.configurations.ConfigurationType; |
| import com.intellij.execution.configurations.RunConfiguration; |
| import com.intellij.openapi.extensions.ExtensionPointName; |
| import com.intellij.openapi.extensions.Extensions; |
| import com.intellij.openapi.util.Ref; |
| import com.intellij.psi.PsiElement; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| /** |
| * Supports creating run configurations from context (by right-clicking a code element in the source editor or the project view). Typically, |
| * run configurations that can be created from context should extend the {@link com.intellij.execution.configurations.LocatableConfigurationBase} class. |
| * |
| * @since 13 |
| * @author yole |
| */ |
| public abstract class RunConfigurationProducer<T extends RunConfiguration> { |
| public static final ExtensionPointName<RunConfigurationProducer> EP_NAME = ExtensionPointName.create("com.intellij.runConfigurationProducer"); |
| private final ConfigurationFactory myConfigurationFactory; |
| |
| protected RunConfigurationProducer(final ConfigurationFactory configurationFactory) { |
| myConfigurationFactory = configurationFactory; |
| } |
| |
| protected RunConfigurationProducer(final ConfigurationType configurationType) { |
| myConfigurationFactory = configurationType.getConfigurationFactories()[0]; |
| } |
| |
| public ConfigurationFactory getConfigurationFactory() { |
| return myConfigurationFactory; |
| } |
| |
| public ConfigurationType getConfigurationType() { |
| return myConfigurationFactory.getType(); |
| } |
| |
| /** |
| * Creates a run configuration from the context. |
| * |
| * @param context contains the information about a location in the source code. |
| * @return a container with a prepared run configuration and the context element from which it was created, or null if the context is |
| * not applicable to this run configuration producer. |
| */ |
| @Nullable |
| public ConfigurationFromContext createConfigurationFromContext(ConfigurationContext context) { |
| final RunnerAndConfigurationSettings settings = cloneTemplateConfiguration(context); |
| final Ref<PsiElement> locationRef = new Ref<PsiElement>(context.getPsiLocation()); |
| if (!setupConfigurationFromContext((T)settings.getConfiguration(), context, locationRef)) { |
| return null; |
| } |
| return new ConfigurationFromContextImpl(this, settings, locationRef.get()); |
| } |
| |
| /** |
| * Sets up a configuration based on the specified context. |
| * |
| * @param configuration a clone of the template run configuration of the specified type |
| * @param context contains the information about a location in the source code. |
| * @param sourceElement a reference to the source element for the run configuration (by default contains the element at caret, |
| * can be updated by the producer to point to a higher-level element in the tree). |
| * |
| * @return true if the context is applicable to this run configuration producer, false if the context is not applicable and the |
| * configuration should be discarded. |
| */ |
| protected abstract boolean setupConfigurationFromContext(T configuration, ConfigurationContext context, Ref<PsiElement> sourceElement); |
| |
| /** |
| * Checks if the specified configuration was created from the specified context. |
| * @param configuration a configuration instance. |
| * @param context contains the information about a location in the source code. |
| * @return true if this configuration was created from the specified context, false otherwise. |
| */ |
| public abstract boolean isConfigurationFromContext(T configuration, ConfigurationContext context); |
| |
| /** |
| * When two configurations are created from the same context by two different producers, checks if the configuration created by |
| * this producer should be discarded in favor of the other one. |
| * |
| * @param self a configuration created by this producer. |
| * @param other a configuration created by another producer. |
| * @return true if the configuration created by this producer is at least as good as the other one; false if this configuration |
| * should be discarded and the other one should be used instead. |
| */ |
| public boolean isPreferredConfiguration(ConfigurationFromContext self, ConfigurationFromContext other) { |
| return true; |
| } |
| |
| /** |
| * Called before a configuration created from context by this producer is first executed. Can be used to show additional UI for |
| * customizing the created configuration. |
| * |
| * @param configuration a configuration created by this producer. |
| * @param context the context |
| * @param startRunnable the runnable that needs to be called after additional customization is complete. |
| */ |
| public void onFirstRun(ConfigurationFromContext configuration, ConfigurationContext context, Runnable startRunnable) { |
| startRunnable.run(); |
| } |
| |
| /** |
| * Searches the list of existing run configurations to find one created from this context. Returns one if found, or tries to create |
| * a new configuration from this context if not found. |
| * |
| * @param context contains the information about a location in the source code. |
| * @return a configuration (new or existing) matching the context, or null if the context is not applicable to this producer. |
| */ |
| @Nullable |
| public ConfigurationFromContext findOrCreateConfigurationFromContext(ConfigurationContext context) { |
| Location location = context.getLocation(); |
| if (location == null) { |
| return null; |
| } |
| |
| ConfigurationFromContext fromContext = createConfigurationFromContext(context); |
| if (fromContext != null) { |
| final PsiElement psiElement = fromContext.getSourceElement(); |
| final Location<PsiElement> _location = PsiLocation.fromPsiElement(psiElement, location.getModule()); |
| if (_location != null) { |
| // replace with existing configuration if any |
| final RunManager runManager = RunManager.getInstance(context.getProject()); |
| final ConfigurationType type = fromContext.getConfigurationType(); |
| final List<RunnerAndConfigurationSettings> configurations = runManager.getConfigurationSettingsList(type); |
| final RunnerAndConfigurationSettings settings = findExistingConfiguration(context); |
| if (settings != null) { |
| fromContext.setConfigurationSettings(settings); |
| } else { |
| final ArrayList<String> currentNames = new ArrayList<String>(); |
| for (RunnerAndConfigurationSettings configurationSettings : configurations) { |
| currentNames.add(configurationSettings.getName()); |
| } |
| RunConfiguration configuration = fromContext.getConfiguration(); |
| String name = configuration.getName(); |
| assert name != null : configuration; |
| configuration.setName(RunManager.suggestUniqueName(name, currentNames)); |
| } |
| } |
| } |
| |
| return fromContext; |
| } |
| |
| /** |
| * Searches the list of existing run configurations to find one created from this context. Returns one if found. |
| * |
| * @param context contains the information about a location in the source code. |
| * @return an existing configuration matching the context, or null if no such configuration is found. |
| */ |
| @Nullable |
| public RunnerAndConfigurationSettings findExistingConfiguration(ConfigurationContext context) { |
| final RunManager runManager = RunManager.getInstance(context.getProject()); |
| final List<RunnerAndConfigurationSettings> configurations = runManager.getConfigurationSettingsList(myConfigurationFactory.getType()); |
| for (RunnerAndConfigurationSettings configurationSettings : configurations) { |
| if (isConfigurationFromContext((T) configurationSettings.getConfiguration(), context)) { |
| return configurationSettings; |
| } |
| } |
| return null; |
| } |
| |
| protected RunnerAndConfigurationSettings cloneTemplateConfiguration(@NotNull final ConfigurationContext context) { |
| final RunConfiguration original = context.getOriginalConfiguration(myConfigurationFactory.getType()); |
| if (original != null) { |
| return RunManager.getInstance(context.getProject()).createConfiguration(original.clone(), myConfigurationFactory); |
| } |
| return RunManager.getInstance(context.getProject()).createRunConfiguration("", myConfigurationFactory); |
| } |
| |
| public static RunConfigurationProducer getInstance(Class<? extends RunConfigurationProducer> aClass) { |
| for (RunConfigurationProducer producer : Extensions.getExtensions(EP_NAME)) { |
| if (aClass.isInstance(producer)) { |
| return producer; |
| } |
| } |
| return null; |
| } |
| } |