package jetbrains.mps.execution.api.configurations;
/*Generated by MPS */
import com.intellij.execution.junit.RuntimeConfigurationProducer;
import org.apache.log4j.Logger;
import org.apache.log4j.LogManager;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.Nullable;
import com.intellij.execution.actions.ConfigurationContext;
import com.intellij.execution.configurations.ConfigurationType;
import com.intellij.execution.configurations.ConfigurationFactory;
import org.jetbrains.annotations.NotNull;
import jetbrains.mps.project.MPSProject;
import com.intellij.openapi.project.Project;
import jetbrains.mps.ide.project.ProjectHelper;
import com.intellij.execution.RunnerAndConfigurationSettings;
import com.intellij.execution.Location;
import java.util.List;
import java.util.Objects;
import com.intellij.execution.configurations.RunConfiguration;
import jetbrains.mps.plugins.runconfigs.MPSLocation;
import jetbrains.mps.plugins.runconfigs.MPSPsiElement;
import jetbrains.mps.smodel.ModelAccessHelper;
import jetbrains.mps.util.Computable;
import com.intellij.execution.impl.RunnerAndConfigurationSettingsImpl;
import com.intellij.execution.impl.RunManagerImpl;
import jetbrains.mps.util.EqualUtil;
import org.jetbrains.annotations.NonNls;
import jetbrains.mps.internal.collections.runtime.Sequence;
import org.apache.log4j.Level;
/**
* Currently extends the deprecated RuntimeConfigurationProducer
* To be migrated in 3.5 to the {@link com.intellij.execution.actions.RunConfigurationProducer }
*
* @param <T> denotes the MpsPsiElement's item which is the 'key' of the subclassing producer
*/
public abstract class BaseMpsProducer<T> extends RuntimeConfigurationProducer {
private static final Logger LOG = LogManager.getLogger(BaseMpsProducer.class);
private PsiElement mySourceElement;
@Nullable
private ConfigurationContext myContext;
public BaseMpsProducer(ConfigurationType configurationType, String factoryClassName) {
super(BaseMpsProducer.findFactory(configurationType, factoryClassName));
}
public BaseMpsProducer(ConfigurationFactory configurationFactory) {
super(configurationFactory);
}
public void setSourceElement(PsiElement sourceElement) {
mySourceElement = sourceElement;
}
@Override
public PsiElement getSourceElement() {
return mySourceElement;
}
@NotNull
protected final MPSProject getMpsProject() {
if (myContext == null) {
throw new IllegalStateException("Context is not set");
}
Project project = myContext.getProject();
return ProjectHelper.fromIdeaProject(project);
}
@Nullable
protected ConfigurationContext getContext() {
return myContext;
}
/**
* Here we are making resolve by type and name (!), however it is not legal in some scenarios (consider main1 and main2, user creates configuration
* named main2, which runs the main method from the main1 class. If now user switches to the main2 and presses ctrl-shift-f10 he will be still
* running the main method from the main1 class)
* The proper way is to implement this method in each producer obligatory.
* However runconfigurations generator must be fully updated with new platform
* RunConfigurationProducer (instead of RuntimeConfigurationProducer).
*/
@Override
@Nullable
protected RunnerAndConfigurationSettings findExistingByElement(Location location, @NotNull List<RunnerAndConfigurationSettings> existingConfigurations, ConfigurationContext context) {
RunnerAndConfigurationSettings given = getConfiguration();
for (RunnerAndConfigurationSettings existing : existingConfigurations) {
if (Objects.equals(existing.getType(), given.getType())) {
if (isConfigurationFromContext(existing.getConfiguration(), context)) {
return existing;
}
}
}
return null;
}
/**
* Supposed to return whether the given location is created from the given context
* By default compares by names
*
* TODO keep it after migration to the {@link com.intellij.execution.actions.RunConfigurationProducer }
*/
protected boolean isConfigurationFromContext(@NotNull RunConfiguration configuration, @NotNull ConfigurationContext context) {
RunnerAndConfigurationSettings given = getConfiguration();
return Objects.equals(configuration.getName(), given.getName());
}
@Nullable
@Override
protected RunnerAndConfigurationSettings createConfigurationByElement(Location location, ConfigurationContext context) {
myContext = context;
if (!((location instanceof MPSLocation))) {
return null;
}
MPSLocation mpsLocation = (MPSLocation) location;
final MPSPsiElement psiElement = mpsLocation.getPsiElement();
MPSProject mpsProject = mpsLocation.getMPSProject();
if (psiElement.isTransientElement()) {
// Generally, no run configurations for transient elements might be too much - one may desire
// e.g. to run a main class for a transient model. Perhaps, each producer shall decide itself (in isApplicable) whether
// to ignore tranient models, however, at the moment I decided to prevent any code execution for transient models for
// the sake of change simplicity.
return null;
}
RunConfiguration config = new ModelAccessHelper(mpsProject.getRepository()).runReadAction(new Computable<RunConfiguration>() {
@Override
public RunConfiguration compute() {
Object mpsItem = psiElement.getMPSItem();
if (mpsItem == null) {
return null;
}
if (!(isApplicable(mpsItem))) {
return null;
}
return doCreateConfiguration((T) mpsItem);
}
});
if (config == null) {
return null;
}
return new RunnerAndConfigurationSettingsImpl(RunManagerImpl.getInstanceImpl(location.getProject()), config, false);
}
@Nullable
protected abstract RunConfiguration doCreateConfiguration(T node);
protected abstract boolean isApplicable(Object element);
@Override
public int compareTo(Object o) {
return RuntimeConfigurationProducer.PREFERED;
}
@Override
public int hashCode() {
return ((myContext == null ? 0 : myContext.hashCode())) + 10 * ((mySourceElement == null ? 0 : mySourceElement.hashCode())) + 20 * getClass().getName().hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj == null || !((obj instanceof BaseMpsProducer))) {
return false;
}
BaseMpsProducer configCreator = (BaseMpsProducer) obj;
return EqualUtil.equals(configCreator.myContext, myContext) && EqualUtil.equals(configCreator.mySourceElement, mySourceElement) && EqualUtil.equals(configCreator.getClass().getName(), getClass().getName());
}
@NotNull
protected static ConfigurationFactory findFactory(ConfigurationType configurationType, @NonNls String configurationFactoryClassName) {
for (ConfigurationFactory factory : Sequence.fromIterable(Sequence.fromArray(configurationType.getConfigurationFactories()))) {
if (factory.getClass().getName().equals(configurationFactoryClassName)) {
return factory;
}
}
if (LOG.isEnabledFor(Level.WARN)) {
LOG.warn("Cound not find configuration factory for " + configurationFactoryClassName + " in type " + configurationType.getDisplayName() + ".");
}
return configurationType.getConfigurationFactories()[0];
}
}