/*
* Copyright 2003-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 jetbrains.mps.idea.core.actions;
import com.intellij.ide.projectView.ProjectView;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.DataKey;
import com.intellij.openapi.actionSystem.LangDataKeys;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiElement;
import jetbrains.mps.extapi.model.SModelBase;
import jetbrains.mps.extapi.module.SModuleBase;
import jetbrains.mps.extapi.persistence.ModelFactoryService;
import jetbrains.mps.extapi.persistence.SourceRoot;
import jetbrains.mps.extapi.persistence.datasource.DataSourceFactoryFromName;
import jetbrains.mps.extapi.persistence.datasource.PreinstalledDataSourceTypes;
import jetbrains.mps.ide.actions.MPSCommonDataKeys;
import jetbrains.mps.ide.icons.IdeIcons;
import jetbrains.mps.ide.project.ProjectHelper;
import jetbrains.mps.ide.vfs.VirtualFileUtils;
import jetbrains.mps.persistence.FilePerRootDataSource;
import jetbrains.mps.persistence.ModelCannotBeCreatedException;
import jetbrains.mps.persistence.PreinstalledModelFactoryTypes;
import jetbrains.mps.project.LanguageImportHelper;
import jetbrains.mps.project.MPSProject;
import jetbrains.mps.smodel.ModelAccessHelper;
import jetbrains.mps.smodel.SModelInternal;
import jetbrains.mps.util.Computable;
import jetbrains.mps.vfs.IFile;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.mps.openapi.model.EditableSModel;
import org.jetbrains.mps.openapi.model.SModelName;
import org.jetbrains.mps.openapi.module.ModelAccess;
import org.jetbrains.mps.openapi.module.SRepository;
import org.jetbrains.mps.openapi.persistence.DataSource;
import org.jetbrains.mps.openapi.persistence.ModelFactory;
import org.jetbrains.mps.openapi.persistence.ModelRoot;
import org.jetbrains.mps.openapi.persistence.datasource.DataSourceType;
/**
* Created by danilla on 28/10/15.
*/
public class MakeDirAModel extends NewModelActionBase {
private static Logger LOG = LogManager.getLogger(MakeDirAModel.class);
public static final DataKey<LanguageImportHelper.Interaction> LANGUAGE_IMPORT_INTERACTION = DataKey.create("languageImportInteraction");
public MakeDirAModel() {
super("Use MPS language here", null, IdeIcons.LANGUAGE_ICON);
}
@Override
public void actionPerformed(AnActionEvent anActionEvent) {
EditableSModel model = createModel(anActionEvent);
if (model == null) {
return;
}
MPSProject mpsProject = ProjectHelper.fromIdeaProject(myProject);
SRepository repository = ProjectHelper.getProjectRepository(myProject);
assert mpsProject != null;
assert repository != null;
LanguageImportHelper importHelper = ApplicationManager.getApplication().isUnitTestMode() ?
new LanguageImportHelper(mpsProject, LANGUAGE_IMPORT_INTERACTION.getData(anActionEvent.getDataContext())) :
new LanguageImportHelper(mpsProject);
importHelper.setOnCloseActivity(
new Runnable() {
@Override
public void run() {
ModelAccess modelAccess = repository.getModelAccess();
boolean noImportHasBeenAdded = new ModelAccessHelper(modelAccess).runReadAction(new Computable<Boolean>() {
@Override
public Boolean compute() {
return ((SModelInternal) model).importedLanguageIds().isEmpty();
}
});
if (noImportHasBeenAdded) {
// was cancelled
// todo have better way to signal cancellation
// fixme DefaultModelRoot currently registers model when created, which is bad
// hence, here we have to deregister it in case of cancellation
// another solution is to extend api of LanguageImportHelper and have means to choose language
// _before_ creating the model
modelAccess.runWriteAction(new Runnable() {
@Override
public void run() {
((SModuleBase) model.getModule()).unregisterModel((SModelBase) model);
}
});
return;
}
// writing file only now, this way VCS dialog doesn't get in the way and make the language chooser disappear
modelAccess.runWriteAction(new Runnable() {
@Override
public void run() {
model.save();
}
});
ProjectView.getInstance(myProject).refresh();
}
}
).addUsedLanguage(model);
}
private EditableSModel createModel(final AnActionEvent e) {
return new ModelAccessHelper(ProjectHelper.getModelAccess(myProject)).executeCommand(() -> {
EditableSModel model = null;
try {
SModelName newModelName = new SModelName(myModelPrefix);
PsiDirectory psiDir = (PsiDirectory) e.getData(LangDataKeys.PSI_ELEMENT);
if (psiDir == null) {
throw new IllegalStateException("Could not find psi directory in the context");
}
VirtualFile targetFile = psiDir.getVirtualFile();
DataSourceFactoryFromName dataSourceFactory = createDataSourceFactory(targetFile);
ModelFactory modelFactory = ModelFactoryService.getInstance().getFactoryByType(PreinstalledModelFactoryTypes.PER_ROOT_XML);
model = (EditableSModel) myModelRoot.createModel(newModelName, mySourceRoot, dataSourceFactory, modelFactory);
} catch (ModelCannotBeCreatedException ex) {
LOG.error("", ex);
return null;
}
model.setChanged(true);
model.load();
model.save();
//TODO: This methods are from SModuleOperations.createModelWithAdjustments. Need to check them really needed.
// ModelsAutoImportsManager.doAutoImport(myModelRoot.getModule(), model);
// new MissingDependenciesFixer(model).fixModuleDependencies();
return model;
});
}
@NotNull
private DataSourceFactoryFromName createDataSourceFactory(VirtualFile targetFile) {
return new DataSourceFactoryFromName() {
@NotNull
@Override
public DataSourceType getType() {
return PreinstalledDataSourceTypes.MODEL_ROOT;
}
@NotNull
@Override
public DataSource create(@NotNull SModelName modelName, @NotNull SourceRoot sourceRoot, @Nullable ModelRoot modelRoot) {
IFile folder = VirtualFileUtils.toIFile(targetFile);
return new FilePerRootDataSource(folder, modelRoot);
}
};
}
@Override
protected boolean isEnabled(AnActionEvent e) {
if (!super.isEnabled(e)) {
return false;
}
PsiElement psiElement = e.getData(LangDataKeys.PSI_ELEMENT);
if (psiElement == null || !(psiElement instanceof PsiDirectory)) {
return false;
}
boolean modelExists = MPSCommonDataKeys.CONTEXT_MODEL.getData(e.getDataContext()) != null;
return myModelPrefix != null && !myModelPrefix.isEmpty() && !modelExists;
}
}