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); } }