package jetbrains.mps.ide.depanalyzer; /*Generated by MPS */ import jetbrains.mps.ide.ui.tree.MPSTree; import com.intellij.openapi.actionSystem.DataProvider; import jetbrains.mps.project.Project; import org.jetbrains.mps.openapi.module.SModule; import jetbrains.mps.ide.ui.tree.MPSTreeNode; import jetbrains.mps.ide.ui.tree.TextTreeNode; import jetbrains.mps.ide.icons.IconManager; import jetbrains.mps.ide.ui.tree.TreeMessage; import java.awt.Color; import org.jetbrains.mps.openapi.module.SModuleReference; import jetbrains.mps.internal.collections.runtime.Sequence; import jetbrains.mps.internal.collections.runtime.ISelector; import jetbrains.mps.internal.collections.runtime.IWhereFilter; import org.jetbrains.mps.util.Condition; import java.util.List; import jetbrains.mps.internal.collections.runtime.ITranslator2; import jetbrains.mps.internal.collections.runtime.ListSequence; import com.intellij.openapi.actionSystem.ActionGroup; import jetbrains.mps.workbench.action.ActionUtils; import jetbrains.mps.workbench.action.BaseAction; import com.intellij.openapi.actionSystem.ActionManager; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.NonNls; import jetbrains.mps.ide.actions.MPSCommonDataKeys; import jetbrains.mps.smodel.ModelReadRunnable; public class DependencyTree extends MPSTree implements DataProvider { private Project myProject; private SModule myModule; private boolean myShowRuntime; private boolean myShowUsedLanguage = true; public DependencyTree(Project project) { myProject = project; } /*package*/ Project getProject() { return myProject; } public void setModules(SModule module) { myModule = module; } public SModule getModule() { return myModule; } public boolean isShowRuntime() { return myShowRuntime; } public void setShowRuntime(boolean showRuntime) { myShowRuntime = showRuntime; } public boolean isShowUsedLanguage() { return myShowUsedLanguage; } public void setShowUsedLanguage(boolean showUsedLanguage) { myShowUsedLanguage = showUsedLanguage; } @Override protected MPSTreeNode rebuild() { if (myModule == null) { return new TextTreeNode("No Content"); } DepLink deps = new DependencyUtil(myModule.getRepository()).trackRuntime(isShowRuntime()).build(myModule); TextTreeNode root = new TextTreeNode(myModule.getModuleName()); root.setIcon(IconManager.getIconFor(myModule)); populate(root, deps.allDependencies()); return root; } private void populate(MPSTreeNode root, Iterable<DepLink> allDependencies) { final TreeMessage HAS_CYCLE = new TreeMessage(Color.RED, "module with dependency cycle", null); final TreeMessage BOOTSTRAP_DEPENDENCY = new TreeMessage(Color.RED, "language with bootstrap dependency", null); Iterable<SModuleReference> sortedModules = Sequence.fromIterable(allDependencies).select(new ISelector<DepLink, SModuleReference>() { public SModuleReference select(DepLink it) { return it.module; } }).distinct().sort(new ISelector<SModuleReference, String>() { public String select(SModuleReference it) { return it.getModuleName(); } }, true); for (final SModuleReference module : Sequence.fromIterable(sortedModules)) { Iterable<DepLink> moduleDeps = Sequence.fromIterable(allDependencies).where(new IWhereFilter<DepLink>() { public boolean accept(DepLink it) { return module.equals(it.module) && it.role.isDependency(); } }).distinct(); if (Sequence.fromIterable(moduleDeps).isEmpty()) { continue; } ModuleDependencyNode n = new ModuleDependencyNode(module, moduleDeps, false); n.updateIcon(myModule.getRepository()); final CycleBuilder cb = new CycleBuilder(new Condition<DepLink>() { public boolean met(DepLink dl) { return dl.role.isDependency(); } }); // if there's any dependency with loop to itself, and role of each element of this path isDependency, then it's dependency cycle // NOTE, selectMany ends up as TranslatingSequence, it we don't want cycles to be recalculated again and again on any // ModuleDependencyNode.isLeaf call, shall keep it calcualted in a collection once and for all (e.g. with toList) List<DepPath> cycles = Sequence.fromIterable(moduleDeps).translate(new ITranslator2<DepLink, DepPath>() { public Iterable<DepPath> translate(DepLink dep) { return cb.cyclePaths(dep); } }).toListSequence(); if (ListSequence.fromList(cycles).isNotEmpty()) { n.setCycles(cycles); n.addTreeMessage(HAS_CYCLE); } root.add(n); } if (isShowUsedLanguage()) { MPSTreeNode usedlanguages = new TextTreeNode("Used Languages"); boolean hasBootstrapDep = false; for (final SModuleReference module : Sequence.fromIterable(sortedModules)) { Iterable<DepLink> usedLangDeps = Sequence.fromIterable(allDependencies).where(new IWhereFilter<DepLink>() { public boolean accept(DepLink it) { return it.module == module && it.role.isUsedLanguage(); } }); if (Sequence.fromIterable(usedLangDeps).isEmpty()) { continue; } ModuleDependencyNode n = new ModuleDependencyNode(module, usedLangDeps, true); n.updateIcon(myModule.getRepository()); final CycleBuilder cb = new CycleBuilder(new Condition<DepLink>() { public boolean met(DepLink dl) { return dl.role.isUsedLanguage(); } }); Iterable<DepPath> cycles = Sequence.fromIterable(usedLangDeps).translate(new ITranslator2<DepLink, DepPath>() { public Iterable<DepPath> translate(DepLink dep) { return cb.cyclePaths(dep); } }); if (Sequence.fromIterable(cycles).isNotEmpty()) { hasBootstrapDep = true; n.setCycles(cycles); n.addTreeMessage(BOOTSTRAP_DEPENDENCY); } usedlanguages.add(n); } if (hasBootstrapDep) { usedlanguages.addTreeMessage(BOOTSTRAP_DEPENDENCY); } if (usedlanguages.getChildCount() > 0) { root.add(usedlanguages); } } } @Override protected ActionGroup createPopupActionGroup(MPSTreeNode treeNode) { return ActionUtils.groupFromActions(((BaseAction) ActionManager.getInstance().getAction("jetbrains.mps.ide.actions.ShowDependenciesInViewer_Action")), ((BaseAction) ActionManager.getInstance().getAction("jetbrains.mps.ide.actions.AnalyzeModuleDependencies_Action")), ((BaseAction) ActionManager.getInstance().getAction("jetbrains.mps.ide.actions.ModuleProperties_Action"))); } @Nullable @Override public Object getData(@NonNls String id) { if (!(getCurrentNode() instanceof ModuleDependencyNode)) { return null; } ModuleDependencyNode current = (ModuleDependencyNode) getCurrentNode(); if (id.equals(MPSCommonDataKeys.TREE_NODE.getName())) { return current; } if (id.equals(MPSCommonDataKeys.MODULE.getName())) { return current.getModule().resolve(myModule.getRepository()); } return null; } @Override protected void doInit(MPSTreeNode node, Runnable runnable) { super.doInit(node, new ModelReadRunnable(myProject.getModelAccess(), runnable)); } }