package jetbrains.mps.ide.hierarchy; /*Generated by MPS */ import jetbrains.mps.ide.ui.tree.MPSTree; import org.jetbrains.mps.openapi.model.SNode; import org.jetbrains.mps.openapi.module.SRepository; import javax.swing.Icon; import jetbrains.mps.ide.ui.tree.TextTreeNode; import javax.swing.tree.DefaultTreeModel; import org.jetbrains.annotations.Nullable; import jetbrains.mps.ide.ui.tree.MPSTreeNode; import jetbrains.mps.smodel.ModelReadRunnable; import jetbrains.mps.smodel.ModelAccessHelper; import jetbrains.mps.util.Computable; import java.util.Set; import jetbrains.mps.util.CollectionUtil; import org.jetbrains.mps.util.Condition; import jetbrains.mps.lang.smodel.generator.smodelAdapter.SNodeOperations; import java.util.ArrayList; import java.util.HashSet; import org.jetbrains.annotations.NotNull; import jetbrains.mps.smodel.SModelStereotype; import com.intellij.openapi.actionSystem.ActionGroup; import jetbrains.mps.workbench.action.BaseAction; import com.intellij.openapi.actionSystem.AnActionEvent; import java.util.Map; import jetbrains.mps.workbench.action.ActionUtils; public abstract class AbstractHierarchyTree extends MPSTree { private AbstractHierarchyView myHierarchyView; private SNode myHierarchyNode; protected boolean myIsParentHierarchy; protected boolean myOnlyInOneModel; protected boolean myShowGeneratorModels; private final SRepository myRepostitory; private HierarchyTreeNode myTreeNode; public AbstractHierarchyTree(SRepository repostitory) { myRepostitory = repostitory; } /** * Tree shall not depend on hierarchy view, and all uses of myHierarchyView/getHierarchyView() shall get refactored and removed. */ public void setHierarchyView(AbstractHierarchyView hv) { myHierarchyView = hv; } /** * Change text and icon of current root, if any. Only reasonable use is part of tree rebuild */ public void setRootNodeText(String text, Icon icon) { if (getRootNode() instanceof TextTreeNode) { TextTreeNode rv = ((TextTreeNode) getRootNode()); rv.setText(text); rv.setIcon(icon); if (getModel() instanceof DefaultTreeModel) { ((DefaultTreeModel) getModel()).nodeChanged(rv); } } } @Nullable public AbstractHierarchyView getHierarchyView() { return myHierarchyView; } public void setHierarchyNode(SNode modelNode) { myHierarchyNode = modelNode; } public boolean overridesNodeIdentifierCalculation() { return false; } public String calculateNodeIdentifier(HierarchyTreeNode node) { throw new UnsupportedOperationException(); } public boolean isParentHierarchy() { return myIsParentHierarchy; } /*package*/ void setParentHierarchy(boolean isParentHierarchy) { myIsParentHierarchy = isParentHierarchy; } public boolean isOnlyInOneModel() { return myOnlyInOneModel; } /*package*/ void setIsOnlyInOneModel(boolean isOnlyInOneModel) { boolean oldOnlyInOneModel = myOnlyInOneModel; myOnlyInOneModel = isOnlyInOneModel; if (oldOnlyInOneModel != myOnlyInOneModel) { rebuildNow(); } } public boolean isShowGeneratorModels() { return myShowGeneratorModels; } public void setShowGeneratorModels(boolean showGeneratorModels) { boolean oldShowGeneratorModels = myShowGeneratorModels; myShowGeneratorModels = showGeneratorModels; if (oldShowGeneratorModels != myShowGeneratorModels) { rebuildNow(); } } @Override protected void doInit(MPSTreeNode node, Runnable runnable) { super.doInit(node, new ModelReadRunnable(myRepostitory.getModelAccess(), runnable)); } @Override protected MPSTreeNode rebuild() { if (myHierarchyNode == null) { return new TextTreeNode(noNodeString()); } return new ModelAccessHelper(myRepostitory.getModelAccess()).runReadAction(new Computable<MPSTreeNode>() { @Override public MPSTreeNode compute() { return rebuildParentHierarchy(); } }); } protected abstract String noNodeString(); protected abstract SNode getParent(SNode node); protected abstract Set<SNode> getParents(SNode node, Set<SNode> visited) throws CircularHierarchyException; protected abstract Set<SNode> getDescendants(SNode node, Set<SNode> visited) throws CircularHierarchyException; protected Set<SNode> getAbstractChildren(final SNode node, Set<SNode> visited) throws CircularHierarchyException { Set<SNode> result; if (myIsParentHierarchy) { result = getParents(node, visited); } else { result = getDescendants(node, visited); } if (myOnlyInOneModel) { result = CollectionUtil.filter(result, new Condition<SNode>() { @Override public boolean met(SNode n) { if (n == null) { return false; } return SNodeOperations.getModel(n) == SNodeOperations.getModel(node); } }); } if (!(myShowGeneratorModels)) { result = CollectionUtil.filter(result, new Condition<SNode>() { @Override public boolean met(SNode n) { if (n == null) { return false; } return !(isInGeneratorModel(n)); } }); } return result; } protected SNode getAbstractParent(SNode node) { if (myIsParentHierarchy) { return null; } SNode result = getParent(node); if (result == null) { return null; } if (!(myShowGeneratorModels)) { while (isInGeneratorModel(result)) { result = getParent(result); if (result == null) { return null; } } } if (myOnlyInOneModel && (SNodeOperations.getModel(result) != SNodeOperations.getModel(node))) { return null; } return result; } protected MPSTreeNode rebuildParentHierarchy() { ArrayList<SNode> parentHierarchy = new ArrayList<SNode>(); SNode parentDeclaration = myHierarchyNode; while (parentDeclaration != null) { parentHierarchy.add(parentDeclaration); parentDeclaration = getAbstractParent(parentDeclaration); } HierarchyTreeNode parentTreeNode = null; HierarchyTreeNode hierarchyTreeNode = null; HierarchyTreeNode rootNode = null; Set<SNode> visited = new HashSet<SNode>(); for (int i = parentHierarchy.size() - 1; i >= 0; i--) { hierarchyTreeNode = (i > 0 ? (new HierarchyTreeNode(parentHierarchy.get(i), this)) : new ChildHierarchyTreeNode(parentHierarchy.get(i), this, visited)); if (i == parentHierarchy.size() - 1) { rootNode = hierarchyTreeNode; } visited.add(parentHierarchy.get(i)); if (parentTreeNode != null) { parentTreeNode.add(hierarchyTreeNode); } parentTreeNode = hierarchyTreeNode; } myTreeNode = hierarchyTreeNode; MPSTreeNode topRootNode = new TextTreeNode("Hierarchy"); topRootNode.add(rootNode); return topRootNode; } /*package*/ HierarchyTreeNode getActiveTreeNode() { // I've got no idea what's the need behind this, and 'active' is just a quess here. // I merely moved this field from HierarchyView, where it's read, here, where it's modified. return myTreeNode; } @Override protected void doubleClick(@NotNull MPSTreeNode node) { if (node instanceof HierarchyTreeNode && myHierarchyView != null) { myHierarchyView.openNode(((HierarchyTreeNode) node).getNodeReference()); } else { super.doubleClick(node); } } private boolean isInGeneratorModel(SNode n) { return SNodeOperations.getModel(n) != null && SModelStereotype.isGeneratorModel(SNodeOperations.getModel(n)); } @Override protected ActionGroup createPopupActionGroup(final MPSTreeNode treeNode) { if (!(treeNode instanceof HierarchyTreeNode)) { return null; } final AbstractHierarchyView hierarchyView = getHierarchyView(); if (hierarchyView == null) { return null; } BaseAction hierarchyAction = new BaseAction("Show Hierarchy For This Node") { @Override protected void doExecute(AnActionEvent e, Map<String, Object> _params) { final SNode node = ((HierarchyTreeNode) treeNode).getNode(); hierarchyView.showItemInHierarchy(node); } }; return ActionUtils.groupFromActions(hierarchyAction); } }