/* * 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.ide.projectPane.logicalview; import jetbrains.mps.ide.ui.tree.MPSTreeNode; import jetbrains.mps.ide.ui.tree.MPSTreeNodeEx; import jetbrains.mps.ide.ui.tree.module.AccessoriesModelTreeNode; import jetbrains.mps.ide.ui.tree.module.NamespaceTextNode; import jetbrains.mps.ide.ui.tree.module.ProjectLanguageTreeNode; import jetbrains.mps.ide.ui.tree.module.ProjectLanguageTreeNode.AllModelsTreeNode; import jetbrains.mps.ide.ui.tree.module.ProjectModuleTreeNode; import jetbrains.mps.ide.ui.tree.module.ProjectModulesPoolTreeNode; import jetbrains.mps.ide.ui.tree.module.SModelsSubtree.StubsTreeNode; import jetbrains.mps.ide.ui.tree.module.SModelsSubtree.TestsTreeNode; import jetbrains.mps.ide.ui.tree.smodel.PackageNode; import jetbrains.mps.ide.ui.tree.smodel.SModelTreeNode; import jetbrains.mps.ide.ui.tree.smodel.SNodeTreeNode; import jetbrains.mps.project.Project; import jetbrains.mps.smodel.Generator; import jetbrains.mps.smodel.Language; import jetbrains.mps.smodel.SNodeUtil; import jetbrains.mps.util.SNodeOperations; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.mps.openapi.model.SModel; import org.jetbrains.mps.openapi.model.SModelReference; import org.jetbrains.mps.openapi.model.SNode; import org.jetbrains.mps.openapi.module.SModule; import org.jetbrains.mps.openapi.module.SModuleReference; import org.jetbrains.mps.util.Condition; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.TreeNode; import java.util.ArrayDeque; import java.util.LinkedList; public final class ProjectTreeFindHelper { private ProjectTree myProjectTree; public ProjectTreeFindHelper(ProjectTree projectTree) { myProjectTree = projectTree; } public ProjectModuleTreeNode findMostSuitableModuleTreeNode(final @NotNull SModule module) { ProjectModuleTreeNode result = findModuleTreeNodeInProject(module); if (result != null) return result; ProjectModulesPoolTreeNode modulesPoolNode = getTree().getModulesPoolNode(); if (!modulesPoolNode.isInitialized()) { modulesPoolNode.init(); } return findModuleTreeNodeAnywhere(module); } protected ProjectModuleTreeNode findModuleTreeNodeInProject(final @NotNull SModule module) { return (ProjectModuleTreeNode) findTreeNode(getTree().getRootNode(), new ModuleInProjectCondition(), new NodeForModuleCondition(module)); } protected ProjectModuleTreeNode findModuleTreeNodeAnywhere(@NotNull SModule module) { return (ProjectModuleTreeNode) findTreeNode(getTree().getRootNode(), new ModuleEverywhereCondition(), new NodeForModuleCondition(module)); } public SModelTreeNode findMostSuitableModelTreeNode(@NotNull SModel model) { SModule module = getModuleForModel(getProject(), model); if (module == null) return findModelTreeNodeAnywhere(model, getTree().getRootNode()); ProjectModuleTreeNode moduleTreeNode = findMostSuitableModuleTreeNode(module); if (moduleTreeNode == null) return findModelTreeNodeAnywhere(model, getTree().getRootNode()); return findModelTreeNodeInModule(model, moduleTreeNode); } protected SModelTreeNode findModelTreeNodeInModule(final @NotNull SModel model, @NotNull ProjectModuleTreeNode moduleNode) { return (SModelTreeNode) findTreeNode(moduleNode, new ModelInModuleCondition(model), new NodeForModelCondition(model)); } protected SModelTreeNode findModelTreeNodeAnywhere(@NotNull SModel model, @NotNull MPSTreeNode parentNode) { return (SModelTreeNode) findTreeNode(parentNode, new ModelEverywhereCondition(model), new NodeForModelCondition(model)); } public MPSTreeNodeEx findMostSuitableSNodeTreeNode(@NotNull SNode node) { SModel model = node.getModel(); if (model == null) return null; SModelTreeNode modelNode = findMostSuitableModelTreeNode(model); if (modelNode == null) return null; return findSNodeTreeNodeInParent(node, modelNode); } //todo rewrite using findTreeNode protected MPSTreeNodeEx findSNodeTreeNodeInParent(@NotNull final SNode node, @NotNull final SModelTreeNode parent) { LinkedList<SNode> ancestors = new LinkedList<SNode>(); SNode current = node; while (current != null) { ancestors.addFirst(current); current = current.getParent(); } MPSTreeNode currentTreeNode = parent; for (final SNode anc : ancestors) { final MPSTreeNode finalCurrentTreeNode = currentTreeNode; if (!currentTreeNode.isInitialized() && !currentTreeNode.hasInfiniteSubtree()) currentTreeNode.init(); currentTreeNode = findTreeNode(finalCurrentTreeNode, new Condition<MPSTreeNode>() { @Override public boolean met(MPSTreeNode object) { if (object == finalCurrentTreeNode) return true; if (!(object instanceof PackageNode)) return false; String pack = ((PackageNode) object).getFullPackage(); String vp = node.getContainingRoot().getProperty(SNodeUtil.property_BaseConcept_virtualPackage); return vp != null && vp.startsWith(pack); } }, new Condition<MPSTreeNode>() { @Override public boolean met(MPSTreeNode tNode) { return (tNode instanceof SNodeTreeNode) && (((SNodeTreeNode) tNode).getSNode() == anc); } } ); if (currentTreeNode == null) return null; } return (MPSTreeNodeEx) currentTreeNode; } @Nullable protected MPSTreeNode findTreeNode(MPSTreeNode start, Condition<MPSTreeNode> descendCondition, Condition<MPSTreeNode> resultCondition) { // breadth-first to find top-most module (e.g. not the one under 'runtime' dependencies) ArrayDeque<MPSTreeNode> queue = new ArrayDeque<MPSTreeNode>(128); queue.add(start); while (!queue.isEmpty()) { MPSTreeNode tn = queue.removeFirst(); if (resultCondition.met(tn)) { return tn; } if (descendCondition.met(tn)) { if (!tn.isInitialized()) { tn.init(); } for (MPSTreeNode node : tn) { queue.addLast(node); } } } return null; } //----find next queries---- //todo: will work bad e.g. if operating with project data from modules pool public MPSTreeNode findNextTreeNode(SNode node) { MPSTreeNode foundNode = findMostSuitableSNodeTreeNode(node); if (foundNode == null) return null; DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) foundNode.getParent(); TreeNode result = parentNode.getChildAfter(foundNode); if (result == null) result = parentNode.getChildBefore(foundNode); if (result == null) result = parentNode; return (MPSTreeNode) result; } //todo: will work bad e.g. if operating with project data from modules pool public MPSTreeNode findNextTreeNode(SModel modelDescriptor) { SModelTreeNode sModelNode = findMostSuitableModelTreeNode(modelDescriptor); if (sModelNode == null) return null; DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) sModelNode.getParent(); TreeNode result = parentNode.getChildAfter(sModelNode); if (result == null) result = parentNode.getChildBefore(sModelNode); if (result == null) result = parentNode; return (MPSTreeNode) result; } //----node find conditions---- private static class ModuleInProjectCondition extends ModuleEverywhereCondition { @Override public boolean met(MPSTreeNode object) { if (!super.met(object)) return false; return !(object instanceof ProjectModulesPoolTreeNode); } } private static class ModuleEverywhereCondition implements Condition<MPSTreeNode> { @Override public boolean met(MPSTreeNode node) { if (node instanceof ProjectModuleTreeNode && !(node instanceof ProjectLanguageTreeNode)) return false; if (node instanceof SModelTreeNode) return false; /* todo: extract optimal module finding process. Used method only works when there is a single ability of selection //need to go into devkits if (node instanceof ProjectDevKitTreeNode) return true; */ return true; } } private static class ModelInModuleCondition extends ModelEverywhereCondition { private ModelInModuleCondition(SModel model) { super(model); } @Override public boolean met(MPSTreeNode node) { if (!super.met(node)) return false; if (node instanceof SModelTreeNode) { SModelTreeNode modelNode = (SModelTreeNode) node; if (!modelNode.hasModelsUnder()) return false; String outerName = SNodeOperations.getModelLongName(modelNode.getModel()); String innerName = SNodeOperations.getModelLongName(myModel); return innerName.startsWith(outerName + "."); } boolean descent = false; if (node instanceof ProjectModuleTreeNode) descent = true; if (node instanceof NamespaceTextNode) descent = true; if (node instanceof AccessoriesModelTreeNode) descent = true; if (node instanceof StubsTreeNode) descent = true; if (node instanceof AllModelsTreeNode) descent = true; if (node instanceof TestsTreeNode) descent = true; if (!descent) return false; if (!node.isInitialized() && !node.hasInfiniteSubtree()) { node.init(); return true; } return node.isInitialized(); } } private static class ModelEverywhereCondition implements Condition<MPSTreeNode> { protected SModel myModel; public ModelEverywhereCondition(SModel model) { myModel = model; } @Override public boolean met(MPSTreeNode node) { if (node instanceof SNodeTreeNode) return false; if (node instanceof SModelTreeNode) { SModelTreeNode modelNode = (SModelTreeNode) node; if (!modelNode.hasModelsUnder()) return false; String outerName = SNodeOperations.getModelLongName(modelNode.getModel()); String innerName = jetbrains.mps.util.SNodeOperations.getModelLongName(myModel); return innerName.startsWith(outerName + "."); } if (!node.isInitialized() && !node.hasInfiniteSubtree()) { node.init(); return true; } return node.isInitialized(); } } private static class NodeForModuleCondition implements Condition<MPSTreeNode> { private final SModule myModule; public NodeForModuleCondition(SModule module) { myModule = module; } @Override public boolean met(MPSTreeNode treeNode) { if (!(treeNode instanceof ProjectModuleTreeNode)) return false; return ((ProjectModuleTreeNode) treeNode).getModule() == myModule; } } private static class NodeForModelCondition implements Condition<MPSTreeNode> { private final SModel myModel; public NodeForModelCondition(SModel model) { myModel = model; } @Override public boolean met(MPSTreeNode node) { if (!(node instanceof SModelTreeNode)) return false; SModelTreeNode modelNode = (SModelTreeNode) node; SModel modelDescriptor = modelNode.getModel(); SModelReference modelReference = modelDescriptor.getReference(); return modelReference.equals(myModel.getReference()); } } //-----------getters---------- protected Project getProject() { return getTree().getProject(); } protected ProjectTree getTree() { return myProjectTree; } //-----------find module by model------------ private static SModule getModuleForModel(Project project, SModel model) { //language's and solution's own models (+generator models in language) SModule owner = model.getModule(); if (owner == null) return null; SModule mainModule = owner instanceof Generator ? ((Generator) owner).getSourceLanguage() : owner; if (project.isProjectModule(mainModule)) return owner; //accessories models in languages /* //with this enabled, alt-f1 does not work in case node is in non-owned accessory model to a project language for (Language l : project.getProjectLanguages()) { if (l.isAccessoryModel(model.getSModelReference())) return l; } */ //runtime models in languages for (Language l : project.getProjectModules(Language.class)) { for (SModuleReference depModule : l.getRuntimeModulesReferences()) { if (depModule.equals(mainModule.getModuleReference())) return owner; } } //accessories models in devkits //runtime models in devkits return owner; } }