package jetbrains.mps.ide.migration;
/*Generated by MPS */
import com.intellij.openapi.components.AbstractProjectComponent;
import org.apache.log4j.Logger;
import org.apache.log4j.LogManager;
import jetbrains.mps.project.Project;
import jetbrains.mps.migration.global.ProjectMigration;
import org.jetbrains.mps.openapi.module.SModule;
import jetbrains.mps.smodel.tempmodel.TempModuleOptions;
import jetbrains.mps.lang.migration.runtime.base.DataCollector;
import java.util.Map;
import org.jetbrains.mps.openapi.model.SNode;
import jetbrains.mps.lang.migration.runtime.base.MigrationScriptReference;
import jetbrains.mps.internal.collections.runtime.MapSequence;
import java.util.HashMap;
import jetbrains.mps.internal.collections.runtime.SetSequence;
import jetbrains.mps.migration.component.util.MigrationsUtil;
import jetbrains.mps.internal.collections.runtime.IVisitor;
import jetbrains.mps.migration.component.util.MigrationDataUtil;
import jetbrains.mps.baseLanguage.closures.runtime.Wrappers;
import jetbrains.mps.internal.collections.runtime.Sequence;
import jetbrains.mps.internal.collections.runtime.ListSequence;
import java.util.List;
import jetbrains.mps.migration.global.ProjectMigrationsRegistry;
import jetbrains.mps.internal.collections.runtime.IWhereFilter;
import jetbrains.mps.project.AbstractModule;
import jetbrains.mps.project.structure.modules.ModuleDescriptor;
import org.jetbrains.mps.openapi.module.SModuleReference;
import java.util.Collections;
import org.jetbrains.mps.openapi.language.SLanguage;
import java.util.Set;
import jetbrains.mps.smodel.SLanguageHierarchy;
import jetbrains.mps.smodel.language.LanguageRegistry;
import java.util.LinkedHashSet;
import java.util.Collection;
import jetbrains.mps.project.dependency.GlobalModuleDependenciesManager;
import org.jetbrains.mps.openapi.model.SModel;
import jetbrains.mps.smodel.SModelInternal;
import jetbrains.mps.internal.collections.runtime.CollectionSequence;
import org.apache.log4j.Level;
import jetbrains.mps.internal.collections.runtime.ITranslator2;
import java.util.ArrayList;
import jetbrains.mps.migration.global.CleanupProjectMigration;
import jetbrains.mps.migration.global.MigrationOptions;
import jetbrains.mps.migration.global.ProjectMigrationWithOptions;
import org.jetbrains.annotations.Nullable;
import jetbrains.mps.lang.migration.runtime.base.BaseScriptReference;
import jetbrains.mps.ide.project.ProjectHelper;
import jetbrains.mps.lang.migration.runtime.base.RefactoringScriptReference;
import jetbrains.mps.refactoring.participant.RefactoringSession;
import jetbrains.mps.lang.migration.runtime.base.MigrationScript;
import jetbrains.mps.lang.migration.runtime.base.MigrationModuleUtil;
import jetbrains.mps.refactoring.participant.RefactoringSessionImpl;
import jetbrains.mps.lang.migration.runtime.base.RefactoringScript;
import jetbrains.mps.baseLanguage.closures.runtime._FunctionTypes;
import jetbrains.mps.refactoring.participant.RefactoringUI;
import jetbrains.mps.refactoring.participant.RefactoringParticipant;
import jetbrains.mps.ide.platform.actions.core.RefactoringProcessor;
import jetbrains.mps.ide.findusages.model.scopes.ModulesScope;
public class MigrationManagerImpl extends AbstractProjectComponent implements MigrationManager {
private static final Logger LOG = LogManager.getLogger(MigrationManagerImpl.class);
private Project myMpsProject;
private ProjectMigration lastProjectMigration = null;
private SModule myDataModule;
private TempModuleOptions myDataModuleOptions;
private DataCollector myDataCollector = new DataCollector() {
public Map<SModule, SNode> collectData(SModule module, final MigrationScriptReference scriptReference) {
final Map<SModule, SNode> requiredData = MapSequence.fromMap(new HashMap<SModule, SNode>());
SetSequence.fromSet(MigrationsUtil.getModuleDependencies(module)).visitAll(new IVisitor<SModule>() {
public void visit(SModule it) {
SNode dataString = MigrationDataUtil.readData(it, scriptReference);
if (dataString != null) {
MapSequence.fromMap(requiredData).put(it, dataString);
}
}
});
return requiredData;
}
};
public MigrationManagerImpl(com.intellij.openapi.project.Project project, Project mpsProject) {
super(project);
myMpsProject = mpsProject;
}
@Override
public void initComponent() {
myMpsProject.getModelAccess().runWriteAction(new Runnable() {
public void run() {
myDataModuleOptions = TempModuleOptions.forDefaultModule();
myDataModule = myDataModuleOptions.createModule();
}
});
}
@Override
public void disposeComponent() {
myMpsProject.getModelAccess().runWriteAction(new Runnable() {
public void run() {
myDataModuleOptions.disposeModule();
}
});
}
public boolean isMigrationRequired() {
final Wrappers._boolean result = new Wrappers._boolean(false);
myMpsProject.getRepository().getModelAccess().runReadAction(new Runnable() {
public void run() {
Iterable<SModule> modules = MigrationsUtil.getMigrateableModulesFromProject(myMpsProject);
result.value = isMigrationRequired(modules);
}
});
return result.value;
}
public boolean isMigrationRequired(Iterable<SModule> modules) {
return Sequence.fromIterable(getProjectMigrationsToApply()).isNotEmpty() || ListSequence.fromList(getModuleMigrationsToApply(modules)).isNotEmpty();
}
public Iterable<ProjectMigration> getProjectMigrationsToApply() {
List<ProjectMigration> allProjectMigrations = ProjectMigrationsRegistry.getInstance().getMigrations();
return ListSequence.fromList(allProjectMigrations).where(new IWhereFilter<ProjectMigration>() {
public boolean accept(ProjectMigration it) {
return it.shouldBeExecuted(myMpsProject);
}
}).toListSequence();
}
public boolean importVersionsUpdateRequired(Iterable<SModule> modules) {
myMpsProject.getModelAccess().checkReadAccess();
for (SModule module : Sequence.fromIterable(modules)) {
AbstractModule abstractModule = (AbstractModule) module;
ModuleDescriptor md = abstractModule.getModuleDescriptor();
if (md == null) {
throw new IllegalStateException("Module " + modules + " has not module descriptor.");
}
Map<SModuleReference, Integer> oldDepVersions = Collections.unmodifiableMap(md.getDependencyVersions());
Map<SModuleReference, Integer> newDepVersions = collectDependencyVersions(abstractModule, oldDepVersions);
if (!(oldDepVersions.keySet().equals(newDepVersions.keySet()))) {
return true;
}
Map<SLanguage, Integer> oldLangVersions = Collections.unmodifiableMap(md.getLanguageVersions());
Map<SLanguage, Integer> newLangVersions = collectLanguageVersions(abstractModule, oldLangVersions);
checkModelVersionsAreValid(module, newLangVersions);
if (!(oldLangVersions.equals(newLangVersions))) {
return true;
}
}
return false;
}
public void doUpdateImportVersions(SModule module) {
module.getRepository().getModelAccess().checkWriteAccess();
AbstractModule abstractModule = (AbstractModule) module;
ModuleDescriptor md = abstractModule.getModuleDescriptor();
if (md == null) {
throw new IllegalStateException("Module " + module + " has not module descriptor.");
}
Map<SModuleReference, Integer> depVersions = md.getDependencyVersions();
Map<SModuleReference, Integer> newDepVersions = collectDependencyVersions(abstractModule, depVersions);
if (!(depVersions.equals(newDepVersions))) {
abstractModule.setChanged();
depVersions.clear();
depVersions.putAll(newDepVersions);
}
Map<SLanguage, Integer> langVersions = md.getLanguageVersions();
Map<SLanguage, Integer> newLangVersions = collectLanguageVersions(abstractModule, langVersions);
if (!(langVersions.equals(newLangVersions))) {
abstractModule.setChanged();
langVersions.clear();
langVersions.putAll(newLangVersions);
}
}
public Map<SLanguage, Integer> collectLanguageVersions(SModule module, Map<SLanguage, Integer> oldLangVersions) {
module.getRepository().getModelAccess().checkReadAccess();
Map<SLanguage, Integer> newLangVersions = new HashMap<SLanguage, Integer>();
Set<SLanguage> usedLanguages = module.getUsedLanguages();
SLanguageHierarchy languageHierarchy = new SLanguageHierarchy(LanguageRegistry.getInstance(myMpsProject.getRepository()), usedLanguages);
Set<SLanguage> extendingLangsClosure = languageHierarchy.getExtended();
for (SLanguage lang : extendingLangsClosure) {
if (oldLangVersions.containsKey(lang)) {
newLangVersions.put(lang, oldLangVersions.get(lang));
} else {
newLangVersions.put(lang, lang.getLanguageVersion());
}
}
return newLangVersions;
}
public Map<SModuleReference, Integer> collectDependencyVersions(SModule module, Map<SModuleReference, Integer> oldDepVersions) {
module.getRepository().getModelAccess().checkReadAccess();
Map<SModuleReference, Integer> newDepVersions = new HashMap<SModuleReference, Integer>();
Set<SModule> visible = new LinkedHashSet<SModule>();
visible.add(module);
Collection<SModule> dependentModules = new GlobalModuleDependenciesManager(module).getModules(GlobalModuleDependenciesManager.Deptype.VISIBLE);
visible.addAll(dependentModules);
for (SModule dep : visible) {
if (oldDepVersions.containsKey(dep.getModuleReference())) {
newDepVersions.put(dep.getModuleReference(), oldDepVersions.get(dep.getModuleReference()));
} else {
newDepVersions.put(dep.getModuleReference(), ((AbstractModule) dep).getModuleVersion());
}
}
return newDepVersions;
}
public void checkModelVersionsAreValid(SModule module, Map<SLanguage, Integer> langVersions) {
module.getRepository().getModelAccess().checkReadAccess();
for (SModel m : module.getModels()) {
SModelInternal modelInternal = (SModelInternal) m;
for (SLanguage lang : CollectionSequence.fromCollection(modelInternal.importedLanguageIds())) {
int currentVersion = langVersions.get(lang);
int modelVer = modelInternal.getLanguageImportVersion(lang);
if (modelVer != -1) {
if (modelVer != currentVersion) {
if (LOG.isEnabledFor(Level.ERROR)) {
LOG.error("Migration assistant detected inconsistecy in language versions. Module " + module + " uses language " + lang + " with version " + currentVersion + " while its model " + m.getName() + " uses this language with version " + modelVer);
}
}
}
}
}
}
public List<ScriptApplied> getModuleMigrationsToApply(Iterable<SModule> modules) {
return Sequence.fromIterable(modules).translate(new ITranslator2<SModule, ScriptApplied>() {
public Iterable<ScriptApplied> translate(SModule module) {
return MigrationsUtil.getAllSteps(module, false);
}
}).toListSequence();
}
public List<ScriptApplied> getMissingMigrations() {
final Wrappers._T<List<ScriptApplied>> result = new Wrappers._T<List<ScriptApplied>>(ListSequence.fromList(new ArrayList<ScriptApplied>()));
myMpsProject.getRepository().getModelAccess().runReadAction(new Runnable() {
public void run() {
result.value = ListSequence.fromList(getModuleMigrationsToApply(MigrationsUtil.getMigrateableModulesFromProject(myMpsProject))).where(new IWhereFilter<ScriptApplied>() {
public boolean accept(ScriptApplied it) {
return it.getScriptReference().resolve(false) == null;
}
}).toListSequence();
}
});
return result.value;
}
public int projectStepsCount(boolean isCleanup) {
List<ProjectMigration> migrations = ProjectMigrationsRegistry.getInstance().getMigrations();
int cleanupSize = ListSequence.fromList(migrations).ofType(CleanupProjectMigration.class).count();
return (isCleanup ? cleanupSize : ListSequence.fromList(migrations).count() - cleanupSize);
}
public ProjectMigration nextProjectStep(MigrationOptions options, boolean cleanup) {
ProjectMigration current = next(lastProjectMigration, cleanup);
while (current != null && !(current.shouldBeExecuted(myMpsProject))) {
current = next(current, cleanup);
}
if (current == null) {
return null;
}
lastProjectMigration = current;
if (current instanceof ProjectMigrationWithOptions) {
((ProjectMigrationWithOptions) current).setOptionValues(options);
}
return current;
}
private ProjectMigration next(ProjectMigration current, final boolean cleanup) {
List<ProjectMigration> mig = ProjectMigrationsRegistry.getInstance().getMigrations();
mig = ListSequence.fromList(mig).where(new IWhereFilter<ProjectMigration>() {
public boolean accept(ProjectMigration it) {
boolean isCleanup = it instanceof CleanupProjectMigration;
// this is xor, which is absent in bl
return (cleanup ? isCleanup : !(isCleanup));
}
}).toListSequence();
if (ListSequence.fromList(mig).isEmpty()) {
return null;
}
if (ListSequence.fromList(mig).indexOf(current) < 0) {
// was: cleanup, now: not cleanup
current = null;
}
if (current == null) {
return ListSequence.fromList(mig).getElement(0);
}
int index = ListSequence.fromList(mig).indexOf(current);
if (index == ListSequence.fromList(mig).count() - 1) {
return null;
}
return ListSequence.fromList(mig).getElement(index + 1);
}
public int moduleStepsCount() {
final Wrappers._int result = new Wrappers._int();
myMpsProject.getRepository().getModelAccess().runReadAction(new Runnable() {
public void run() {
result.value = ListSequence.fromList(getModuleMigrationsToApply(MigrationsUtil.getMigrateableModulesFromProject(myMpsProject))).count();
}
});
return result.value;
}
public ScriptApplied nextModuleStep(@Nullable final BaseScriptReference preferredId) {
final Wrappers._T<ScriptApplied> result = new Wrappers._T<ScriptApplied>(null);
myMpsProject.getRepository().getModelAccess().runReadAction(new Runnable() {
public void run() {
// .toList is important here, makes it not to perform calculation many times
Iterable<SModule> modules = MigrationsUtil.getMigrateableModulesFromProject(ProjectHelper.toMPSProject(myProject));
if (preferredId == null) {
result.value = Sequence.fromIterable(modules).translate(new ITranslator2<SModule, ScriptApplied>() {
public Iterable<ScriptApplied> translate(SModule module) {
return MigrationsUtil.getAllSteps(module, true);
}
}).findFirst(new IWhereFilter<ScriptApplied>() {
public boolean accept(ScriptApplied it) {
return canBeExecutedImmediately(it);
}
});
return;
}
if (preferredId instanceof MigrationScriptReference) {
final MigrationScriptReference mid = as_yvrsrz_a0a0a4a0a0a0a1a34(preferredId, MigrationScriptReference.class);
SModule byId = Sequence.fromIterable(modules).where(new IWhereFilter<SModule>() {
public boolean accept(SModule it) {
return SetSequence.fromSet(MigrationsUtil.getUsedLanguages(it)).contains(mid.getLanguage());
}
}).where(new IWhereFilter<SModule>() {
public boolean accept(SModule it) {
int ver = Math.max(0, ((AbstractModule) it).getUsedLanguageVersion(mid.getLanguage()));
return ver == mid.getFromVersion();
}
}).findFirst(new IWhereFilter<SModule>() {
public boolean accept(SModule it) {
return canBeExecutedImmediately(new ScriptApplied(it, mid));
}
});
if (byId != null) {
result.value = new ScriptApplied(byId, mid);
return;
}
} else if (preferredId instanceof RefactoringScriptReference) {
final RefactoringScriptReference rid = as_yvrsrz_a0a0a0e0a0a0a0b0rb(preferredId, RefactoringScriptReference.class);
SModule byId = Sequence.fromIterable(modules).where(new IWhereFilter<SModule>() {
public boolean accept(SModule it) {
return SetSequence.fromSet(MigrationsUtil.getModuleDependencies(it)).contains(rid.getModule());
}
}).where(new IWhereFilter<SModule>() {
public boolean accept(SModule it) {
int ver = Math.max(0, ((AbstractModule) it).getDependencyVersion(rid.getModule()));
return ver == rid.getFromVersion();
}
}).findFirst(new IWhereFilter<SModule>() {
public boolean accept(SModule it) {
return canBeExecutedImmediately(new ScriptApplied(it, rid));
}
});
if (byId != null) {
result.value = new ScriptApplied(byId, rid);
return;
}
} else {
// todo get rid of explicit class mention
throw new IllegalArgumentException();
}
// no applicable found by id
result.value = Sequence.fromIterable(modules).translate(new ITranslator2<SModule, ScriptApplied>() {
public Iterable<ScriptApplied> translate(SModule module) {
return MigrationsUtil.getAllSteps(module, true);
}
}).findFirst(new IWhereFilter<ScriptApplied>() {
public boolean accept(ScriptApplied it) {
return canBeExecutedImmediately(it);
}
});
}
});
return result.value;
}
public void executeScript(ScriptApplied s) {
// todo why don't we have executeProjectMigration method?
// todo remove explicit class mention (map<ref->script>?)
if (s.getScriptReference() instanceof MigrationScriptReference) {
executeMigrationScript(s);
} else if (s.getScriptReference() instanceof RefactoringScriptReference) {
executeRefactoringScript(s);
} else {
throw new IllegalArgumentException();
}
}
private static class RefactoringSessionTaskQueue {
private static final String myId = "refactoringSession.migrationAssistant.taskQueue";
private List<Runnable> myTasks = ListSequence.fromList(new ArrayList<Runnable>());
public static MigrationManagerImpl.RefactoringSessionTaskQueue getInstance(RefactoringSession session) {
MigrationManagerImpl.RefactoringSessionTaskQueue result = ((MigrationManagerImpl.RefactoringSessionTaskQueue) session.getObject(myId));
if (result == null) {
result = new MigrationManagerImpl.RefactoringSessionTaskQueue();
session.putObject(myId, result);
}
return result;
}
public void putTask(Runnable task) {
ListSequence.fromList(myTasks).addElement(task);
}
public void runAll() {
for (Runnable task : ListSequence.fromList(myTasks)) {
task.run();
}
}
}
private void executeMigrationScript(ScriptApplied<MigrationScriptReference> sa) {
MigrationScript script = sa.getScriptReference().resolve(true);
AbstractModule module = ((AbstractModule) sa.getModule());
SLanguage fromLanguage = script.getReference().getLanguage();
Integer usedVersion = module.getModuleDescriptor().getLanguageVersions().get(fromLanguage);
usedVersion = Math.max(usedVersion, 0);
assert usedVersion == script.getReference().getFromVersion();
script.setDataCollector(myDataCollector);
SNode data = script.execute(module);
if (data != null) {
MigrationDataUtil.addData(module, myDataModule, script.getReference(), data);
}
int toVersion = script.getReference().getFromVersion() + 1;
module.getModuleDescriptor().getLanguageVersions().put(fromLanguage, toVersion);
module.setChanged();
for (SModel model : ListSequence.fromList(module.getModels())) {
if (model.isReadOnly()) {
continue;
}
if (!((model instanceof SModelInternal))) {
continue;
}
if (!(((SModelInternal) model).importedLanguageIds().contains(fromLanguage))) {
continue;
}
((SModelInternal) model).setLanguageImportVersion(fromLanguage, toVersion);
}
}
private void executeRefactoringScript(ScriptApplied<RefactoringScriptReference> sa) {
RefactoringScriptReference rLog = sa.getScriptReference();
final AbstractModule module = ((AbstractModule) sa.getModule());
SModule fromModule = rLog.getModule();
int importedVersion = MigrationModuleUtil.getDependencyVersion(module, fromModule);
importedVersion = Math.max(importedVersion, 0);
assert importedVersion == rLog.getFromVersion();
final RefactoringSessionImpl refactoringSession = new RefactoringSessionImpl();
RefactoringScript ref = rLog.resolve(true);
ref.setSession(refactoringSession);
ref.setTaskExecutor(new _FunctionTypes._void_P1_E0<Runnable>() {
public void invoke(Runnable task) {
MigrationManagerImpl.RefactoringSessionTaskQueue.getInstance(refactoringSession).putTask(task);
}
});
ref.setRefactoringProcessor(new _FunctionTypes._void_P4_E0<RefactoringUI, RefactoringParticipant.PersistentRefactoringParticipant, Iterable<SNode>, Map<SNode, SNode>>() {
public void invoke(RefactoringUI ui, RefactoringParticipant.PersistentRefactoringParticipant p, Iterable<SNode> initialState, Map<SNode, SNode> initialToFinal) {
doRun(module, p, ui, initialState, initialToFinal, refactoringSession);
}
});
ref.execute(module);
MigrationManagerImpl.RefactoringSessionTaskQueue.getInstance(refactoringSession).runAll();
refactoringSession.performAllRegistered();
int toVersion = rLog.getFromVersion() + 1;
MigrationModuleUtil.setDepVersion(module, fromModule.getModuleReference(), toVersion);
// todo: versions in models
}
private <IP, FP> void doRun(AbstractModule module, RefactoringParticipant.PersistentRefactoringParticipant<?, ?, IP, FP> participant, RefactoringUI ui, Iterable<SNode> initialState, final Map<SNode, SNode> initialToFinal, RefactoringSessionImpl refactoringSession) {
RefactoringProcessor.<IP,FP,SNode,SNode>performRefactoring(new RefactoringParticipant.DeserializingParticipantStateFactory<IP, FP>(), ui, refactoringSession, module.getRepository(), new ModulesScope(module), null, ((Iterable<? extends RefactoringParticipant<?, ?, IP, FP>>) Sequence.<RefactoringParticipant<?, ?, IP, FP>>singleton(participant)), Sequence.fromIterable(initialState).toListSequence(), new _FunctionTypes._return_P1_E0<Map<SNode, SNode>, Iterable<RefactoringParticipant.ParticipantApplied<?, ?, IP, FP, SNode, SNode>>>() {
public Map<SNode, SNode> invoke(Iterable<RefactoringParticipant.ParticipantApplied<?, ?, IP, FP, SNode, SNode>> changes) {
return initialToFinal;
}
}, null);
}
private boolean canBeExecutedImmediately(ScriptApplied script) {
// todo remove explicit class mention
AbstractModule moduleToMigrate = (AbstractModule) script.getModule();
if (script.getScriptReference() instanceof MigrationScriptReference) {
MigrationScriptReference sr = (MigrationScriptReference) script.getScriptReference();
int v = Math.max(0, moduleToMigrate.getUsedLanguageVersion(sr.getLanguage(), false));
if (v != sr.getFromVersion()) {
return false;
}
for (MigrationScriptReference s : Sequence.fromIterable(sr.resolve(true).executeAfter())) {
if (needsToBeApplied(s, moduleToMigrate)) {
return false;
}
}
for (MigrationScriptReference s : Sequence.fromIterable(sr.resolve(true).requiresData())) {
for (SModule dep : SetSequence.fromSet(MigrationsUtil.getModuleDependencies(moduleToMigrate))) {
if (needsToBeApplied(s, dep)) {
return false;
}
}
}
return true;
}
if (script.getScriptReference() instanceof RefactoringScriptReference) {
RefactoringScriptReference sr = (RefactoringScriptReference) script.getScriptReference();
int v = Math.max(0, moduleToMigrate.getDependencyVersion(sr.getModule(), false));
if (v != sr.getFromVersion()) {
return false;
}
for (RefactoringScriptReference s : Sequence.fromIterable(sr.resolve(true).getExecuteAfter())) {
if (needsToBeApplied(s, moduleToMigrate)) {
return false;
}
}
return true;
}
throw new IllegalArgumentException();
}
private boolean needsToBeApplied(MigrationScriptReference ref, SModule m) {
if (!(SetSequence.fromSet(MigrationsUtil.getUsedLanguages(m)).contains(ref.getLanguage()))) {
return false;
}
int dv = Math.max(0, ((AbstractModule) m).getUsedLanguageVersion(ref.getLanguage(), false));
return dv <= ref.getFromVersion();
}
private boolean needsToBeApplied(RefactoringScriptReference ref, SModule m) {
if (!(SetSequence.fromSet(MigrationsUtil.getModuleDependencies(m)).contains(ref.getModule()))) {
return false;
}
int dv = Math.max(0, ((AbstractModule) m).getDependencyVersion(ref.getModule(), false));
return dv <= ref.getFromVersion();
}
private static <T> T as_yvrsrz_a0a0a4a0a0a0a1a34(Object o, Class<T> type) {
return (type.isInstance(o) ? (T) o : null);
}
private static <T> T as_yvrsrz_a0a0a0e0a0a0a0b0rb(Object o, Class<T> type) {
return (type.isInstance(o) ? (T) o : null);
}
}