2 * Copyright 2000-2015 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.execution.actions;
18 import com.intellij.execution.*;
19 import com.intellij.execution.configurations.ConfigurationFactory;
20 import com.intellij.execution.configurations.ConfigurationType;
21 import com.intellij.execution.configurations.RunConfiguration;
22 import com.intellij.openapi.diagnostic.Logger;
23 import com.intellij.openapi.extensions.ExtensionPointName;
24 import com.intellij.openapi.extensions.Extensions;
25 import com.intellij.openapi.project.Project;
26 import com.intellij.openapi.util.Ref;
27 import com.intellij.psi.PsiElement;
28 import com.intellij.util.containers.ContainerUtil;
29 import org.jetbrains.annotations.NotNull;
30 import org.jetbrains.annotations.Nullable;
32 import java.util.List;
35 * Supports creating run configurations from context (by right-clicking a code element in the source editor or the project view). Typically,
36 * run configurations that can be created from context should extend the {@link com.intellij.execution.configurations.LocatableConfigurationBase} class.
41 public abstract class RunConfigurationProducer<T extends RunConfiguration> {
42 public static final ExtensionPointName<RunConfigurationProducer> EP_NAME = ExtensionPointName.create("com.intellij.runConfigurationProducer");
43 private static final Logger LOG = Logger.getInstance("#" + RunConfigurationProducer.class.getName());
46 public static List<RunConfigurationProducer<?>> getProducers(@NotNull Project project) {
47 RunConfigurationProducerService runConfigurationProducerService = RunConfigurationProducerService.getInstance(project);
48 RunConfigurationProducer[] allProducers = Extensions.getExtensions(EP_NAME);
50 List<RunConfigurationProducer<?>> result = ContainerUtil.newArrayListWithCapacity(allProducers.length);
51 for (RunConfigurationProducer producer : allProducers) {
52 if (!runConfigurationProducerService.isIgnored(producer)) {
60 private final ConfigurationFactory myConfigurationFactory;
62 protected RunConfigurationProducer(final ConfigurationFactory configurationFactory) {
63 myConfigurationFactory = configurationFactory;
66 protected RunConfigurationProducer(final ConfigurationType configurationType) {
67 myConfigurationFactory = configurationType.getConfigurationFactories()[0];
70 public ConfigurationFactory getConfigurationFactory() {
71 return myConfigurationFactory;
74 public ConfigurationType getConfigurationType() {
75 return myConfigurationFactory.getType();
79 * Creates a run configuration from the context.
81 * @param context contains the information about a location in the source code.
82 * @return a container with a prepared run configuration and the context element from which it was created, or null if the context is
83 * not applicable to this run configuration producer.
86 public ConfigurationFromContext createConfigurationFromContext(ConfigurationContext context) {
87 final RunnerAndConfigurationSettings settings = cloneTemplateConfiguration(context);
88 Ref<PsiElement> ref = new Ref<PsiElement>(context.getPsiLocation());
90 if (!setupConfigurationFromContext((T)settings.getConfiguration(), context, ref)) {
94 catch (ClassCastException e) {
95 LOG.error(myConfigurationFactory + " produced wrong type", e);
98 return new ConfigurationFromContextImpl(this, settings, ref.get());
102 * Sets up a configuration based on the specified context.
104 * @param configuration a clone of the template run configuration of the specified type
105 * @param context contains the information about a location in the source code.
106 * @param sourceElement a reference to the source element for the run configuration (by default contains the element at caret,
107 * can be updated by the producer to point to a higher-level element in the tree).
109 * @return true if the context is applicable to this run configuration producer, false if the context is not applicable and the
110 * configuration should be discarded.
112 protected abstract boolean setupConfigurationFromContext(T configuration, ConfigurationContext context, Ref<PsiElement> sourceElement);
115 * Checks if the specified configuration was created from the specified context.
116 * @param configuration a configuration instance.
117 * @param context contains the information about a location in the source code.
118 * @return true if this configuration was created from the specified context, false otherwise.
120 public abstract boolean isConfigurationFromContext(T configuration, ConfigurationContext context);
123 * When two configurations are created from the same context by two different producers, checks if the configuration created by
124 * this producer should be discarded in favor of the other one.
126 * @param self a configuration created by this producer.
127 * @param other a configuration created by another producer.
128 * @return true if the configuration created by this producer is at least as good as the other one; false if this configuration
129 * should be discarded and the other one should be used instead.
130 * @see #shouldReplace(ConfigurationFromContext, ConfigurationFromContext)
132 public boolean isPreferredConfiguration(ConfigurationFromContext self, ConfigurationFromContext other) {
137 * When two configurations are created from the same context by two different producers, checks if the configuration created by
138 * this producer should replace the other one, that is if the other one should be discarded.
140 * <p>This is the same relationship as {@link #isPreferredConfiguration(ConfigurationFromContext, ConfigurationFromContext)} but
141 * specified from the "replacement" side.
143 * @param self a configuration created by this producer.
144 * @param other a configuration created by another producer.
145 * @return true if the other configuration should be discarded, false otherwise.
146 * @see #isPreferredConfiguration(ConfigurationFromContext, ConfigurationFromContext)
148 public boolean shouldReplace(ConfigurationFromContext self, ConfigurationFromContext other) {
153 * Called before a configuration created from context by this producer is first executed. Can be used to show additional UI for
154 * customizing the created configuration.
156 * @param configuration a configuration created by this producer.
157 * @param context the context
158 * @param startRunnable the runnable that needs to be called after additional customization is complete.
160 public void onFirstRun(ConfigurationFromContext configuration, ConfigurationContext context, Runnable startRunnable) {
165 * Searches the list of existing run configurations to find one created from this context. Returns one if found, or tries to create
166 * a new configuration from this context if not found.
168 * @param context contains the information about a location in the source code.
169 * @return a configuration (new or existing) matching the context, or null if the context is not applicable to this producer.
172 public ConfigurationFromContext findOrCreateConfigurationFromContext(ConfigurationContext context) {
173 Location location = context.getLocation();
174 if (location == null) {
178 ConfigurationFromContext fromContext = createConfigurationFromContext(context);
179 if (fromContext != null) {
180 final PsiElement psiElement = fromContext.getSourceElement();
181 final Location<PsiElement> _location = PsiLocation.fromPsiElement(psiElement, location.getModule());
182 if (_location != null) {
183 // replace with existing configuration if any
184 final RunManager runManager = RunManager.getInstance(context.getProject());
185 final ConfigurationType type = fromContext.getConfigurationType();
186 final RunnerAndConfigurationSettings settings = findExistingConfiguration(context);
187 if (settings != null) {
188 fromContext.setConfigurationSettings(settings);
190 runManager.setUniqueNameIfNeed(fromContext.getConfiguration());
199 * Searches the list of existing run configurations to find one created from this context. Returns one if found.
201 * @param context contains the information about a location in the source code.
202 * @return an existing configuration matching the context, or null if no such configuration is found.
205 public RunnerAndConfigurationSettings findExistingConfiguration(ConfigurationContext context) {
206 final RunManager runManager = RunManager.getInstance(context.getProject());
207 final List<RunnerAndConfigurationSettings> configurations = runManager.getConfigurationSettingsList(myConfigurationFactory.getType());
208 for (RunnerAndConfigurationSettings configurationSettings : configurations) {
209 if (isConfigurationFromContext((T) configurationSettings.getConfiguration(), context)) {
210 return configurationSettings;
216 protected RunnerAndConfigurationSettings cloneTemplateConfiguration(@NotNull final ConfigurationContext context) {
217 final RunConfiguration original = context.getOriginalConfiguration(myConfigurationFactory.getType());
218 if (original != null) {
219 return RunManager.getInstance(context.getProject()).createConfiguration(original.clone(), myConfigurationFactory);
221 return RunManager.getInstance(context.getProject()).createRunConfiguration("", myConfigurationFactory);
225 public static <T extends RunConfigurationProducer> T getInstance(Class<? extends T> aClass) {
226 for (RunConfigurationProducer producer : Extensions.getExtensions(EP_NAME)) {
227 if (aClass.isInstance(producer)) {
231 assert false : aClass;
236 public RunConfiguration createLightConfiguration(@NotNull final ConfigurationContext context) {
237 RunConfiguration configuration = myConfigurationFactory.createTemplateConfiguration(context.getProject());
238 final Ref<PsiElement> ref = new Ref<PsiElement>(context.getPsiLocation());
240 if (!setupConfigurationFromContext((T)configuration, context, ref)) {
244 catch (ClassCastException e) {
245 LOG.error(myConfigurationFactory + " produced wrong type", e);
248 return configuration;