/* * Copyright 2000-2009 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.ConfigurationType; import com.intellij.execution.configurations.ConfigurationTypeUtil; import com.intellij.execution.configurations.RunConfiguration; import com.intellij.execution.junit.RuntimeConfigurationProducer; import com.intellij.ide.DataManager; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.actionSystem.LangDataKeys; import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Ref; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; import org.jetbrains.annotations.Nullable; import java.awt.*; import java.util.List; /** * Context for creating run configurations from a location in the source code. * * @see RunConfigurationProducer */ public class ConfigurationContext { private static final Logger LOG = Logger.getInstance("#com.intellij.execution.actions.ConfigurationContext"); private final Location<PsiElement> myLocation; private RunnerAndConfigurationSettings myConfiguration; private Ref<RunnerAndConfigurationSettings> myExistingConfiguration; private final Module myModule; private final RunConfiguration myRuntimeConfiguration; private final Component myContextComponent; public static Key<ConfigurationContext> SHARED_CONTEXT = Key.create("SHARED_CONTEXT"); private List<RuntimeConfigurationProducer> myPreferredProducers; private List<ConfigurationFromContext> myConfigurationsFromContext; public static ConfigurationContext getFromContext(DataContext dataContext) { final ConfigurationContext context = new ConfigurationContext(dataContext); final DataManager dataManager = DataManager.getInstance(); ConfigurationContext sharedContext = dataManager.loadFromDataContext(dataContext, SHARED_CONTEXT); if (sharedContext == null || sharedContext.getLocation() == null || context.getLocation() == null || !Comparing.equal(sharedContext.getLocation().getPsiElement(), context.getLocation().getPsiElement())) { sharedContext = context; dataManager.saveInDataContext(dataContext, SHARED_CONTEXT, sharedContext); } return sharedContext; } private ConfigurationContext(final DataContext dataContext) { myRuntimeConfiguration = RunConfiguration.DATA_KEY.getData(dataContext); myContextComponent = PlatformDataKeys.CONTEXT_COMPONENT.getData(dataContext); myModule = LangDataKeys.MODULE.getData(dataContext); @SuppressWarnings({"unchecked"}) final Location<PsiElement> location = (Location<PsiElement>)Location.DATA_KEY.getData(dataContext); if (location != null) { myLocation = location; return; } final Project project = CommonDataKeys.PROJECT.getData(dataContext); if (project == null) { myLocation = null; return; } final PsiElement element = getSelectedPsiElement(dataContext, project); if (element == null) { myLocation = null; return; } myLocation = new PsiLocation<PsiElement>(project, myModule, element); } /** * Returns the configuration created from this context. * * @return the configuration, or null if none of the producers were able to create a configuration from this context. */ @Nullable public RunnerAndConfigurationSettings getConfiguration() { if (myConfiguration == null) createConfiguration(); return myConfiguration; } private void createConfiguration() { LOG.assertTrue(myConfiguration == null); final Location location = getLocation(); myConfiguration = location != null ? PreferredProducerFind.createConfiguration(location, this) : null; } public void setConfiguration(RunnerAndConfigurationSettings configuration) { myConfiguration = configuration; } @Deprecated @Nullable public RunnerAndConfigurationSettings updateConfiguration(final RuntimeConfigurationProducer producer) { myConfiguration = producer.getConfiguration(); return myConfiguration; } /** * Returns the source code location for this context. * * @return the source code location, or null if no source code fragment is currently selected. */ @Nullable public Location getLocation() { return myLocation; } /** * Returns the PSI element at caret for this context. * * @return the PSI element, or null if no source code fragment is currently selected. */ @Nullable public PsiElement getPsiLocation() { return myLocation != null ? myLocation.getPsiElement() : null; } /** * Finds an existing run configuration matching the context. * * @return an existing configuration, or null if none was found. */ @Nullable public RunnerAndConfigurationSettings findExisting() { if (myExistingConfiguration != null) return myExistingConfiguration.get(); myExistingConfiguration = new Ref<RunnerAndConfigurationSettings>(); if (myLocation == null) { return null; } final PsiElement psiElement = myLocation.getPsiElement(); if (!psiElement.isValid()) { return null; } final List<RuntimeConfigurationProducer> producers = findPreferredProducers(); if (myRuntimeConfiguration != null) { if (producers != null) { for (RuntimeConfigurationProducer producer : producers) { final RunnerAndConfigurationSettings configuration = producer.findExistingConfiguration(myLocation, this); if (configuration != null && configuration.getConfiguration() == myRuntimeConfiguration) { myExistingConfiguration.set(configuration); } } } for (RunConfigurationProducer producer : Extensions.getExtensions(RunConfigurationProducer.EP_NAME)) { RunnerAndConfigurationSettings configuration = producer.findExistingConfiguration(this); if (configuration != null && configuration.getConfiguration() == myRuntimeConfiguration) { myExistingConfiguration.set(configuration); } } } if (producers != null) { for (RuntimeConfigurationProducer producer : producers) { final RunnerAndConfigurationSettings configuration = producer.findExistingConfiguration(myLocation, this); if (configuration != null) { myExistingConfiguration.set(configuration); } } } for (RunConfigurationProducer producer : Extensions.getExtensions(RunConfigurationProducer.EP_NAME)) { RunnerAndConfigurationSettings configuration = producer.findExistingConfiguration(this); if (configuration != null) { myExistingConfiguration.set(configuration); } } return myExistingConfiguration.get(); } @Nullable private static PsiElement getSelectedPsiElement(final DataContext dataContext, final Project project) { PsiElement element = null; final Editor editor = CommonDataKeys.EDITOR.getData(dataContext); if (editor != null){ final PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument()); if (psiFile != null) { final int offset = editor.getCaretModel().getOffset(); element = psiFile.findElementAt(offset); if (element == null && offset > 0 && offset == psiFile.getTextLength()) { element = psiFile.findElementAt(offset-1); } } } if (element == null) { final PsiElement[] elements = LangDataKeys.PSI_ELEMENT_ARRAY.getData(dataContext); element = elements != null && elements.length > 0 ? elements[0] : null; } if (element == null) { final VirtualFile[] files = CommonDataKeys.VIRTUAL_FILE_ARRAY.getData(dataContext); if (files != null && files.length > 0) { element = PsiManager.getInstance(project).findFile(files[0]); } } return element; } public RunManager getRunManager() { return RunManager.getInstance(getProject()); } public Project getProject() { return myLocation.getProject(); } public Module getModule() { return myModule; } public DataContext getDataContext() { return DataManager.getInstance().getDataContext(myContextComponent); } /** * Returns original {@link RunConfiguration} from this context. * For example, it could be some test framework runtime configuration that had been launched * and that had brought a result test tree on which a right-click action was performed. * * @param type {@link ConfigurationType} instance to filter original runtime configuration by its type * @return {@link RunConfiguration} instance, it could be null */ @Nullable public RunConfiguration getOriginalConfiguration(@Nullable ConfigurationType type) { if (type == null) { return myRuntimeConfiguration; } if (myRuntimeConfiguration != null && ConfigurationTypeUtil.equals(myRuntimeConfiguration.getType(), type)) { return myRuntimeConfiguration; } return null; } @Deprecated @Nullable public List<RuntimeConfigurationProducer> findPreferredProducers() { if (myPreferredProducers == null) { myPreferredProducers = PreferredProducerFind.findPreferredProducers(myLocation, this, true); } return myPreferredProducers; } public List<ConfigurationFromContext> getConfigurationsFromContext() { if (myConfigurationsFromContext == null) { myConfigurationsFromContext = PreferredProducerFind.getConfigurationsFromContext(myLocation, this, true); } return myConfigurationsFromContext; } }