@Override
  public Collection<AbstractTreeNode> modify(
      AbstractTreeNode parent, Collection<AbstractTreeNode> children, ViewSettings settings) {
    List<AbstractTreeNode> result = new ArrayList<AbstractTreeNode>();

    for (AbstractTreeNode child : children) {
      Object childValue = child.getValue();

      if (childValue instanceof JetFile) {
        JetFile file = (JetFile) childValue;
        List<JetDeclaration> declarations = file.getDeclarations();

        JetClassOrObject mainClass = JetIconProvider.getMainClass(file);
        if (mainClass != null && declarations.size() == 1) {
          result.add(new JetClassOrObjectTreeNode(file.getProject(), mainClass, settings));
        } else {
          result.add(new JetFileTreeNode(file.getProject(), file, settings));
        }
      } else {
        result.add(child);
      }
    }

    return result;
  }
 private Pair<AbstractUrl, String> createPairForNode(AbstractTreeNode node) {
   final String className = node.getClass().getName();
   final Object value = node.getValue();
   final AbstractUrl url = createUrlByElement(value, myProject);
   if (url == null) return null;
   return Pair.create(url, className);
 }
  private static boolean addToPath(
      AbstractTreeNode<?> rootElement,
      Object element,
      ArrayList<AbstractTreeNode> result,
      Collection<Object> processedElements) {
    Object value = rootElement.getValue();
    if (value instanceof StructureViewTreeElement) {
      value = ((StructureViewTreeElement) value).getValue();
    }
    if (!processedElements.add(value)) {
      return false;
    }

    if (Comparing.equal(value, element)) {
      result.add(0, rootElement);
      return true;
    }

    Collection<? extends AbstractTreeNode> children = rootElement.getChildren();
    for (AbstractTreeNode child : children) {
      if (addToPath(child, element, result, processedElements)) {
        result.add(0, rootElement);
        return true;
      }
    }

    return false;
  }
    @Override
    public SliceNode getNextSibling(@NotNull SliceNode element) {
      AbstractTreeNode parent = element.getParent();
      if (parent == null) return null;

      return element.getNext((List) parent.getChildren());
    }
 public static Object extractUserObject(DefaultMutableTreeNode node) {
   Object userObject = node.getUserObject();
   Object element = null;
   if (userObject instanceof AbstractTreeNode) {
     AbstractTreeNode descriptor = (AbstractTreeNode) userObject;
     element = descriptor.getValue();
   } else if (userObject instanceof NodeDescriptor) {
     NodeDescriptor descriptor = (NodeDescriptor) userObject;
     element = descriptor.getElement();
     if (element instanceof AbstractTreeNode) {
       element = ((AbstractTreeNode) element).getValue();
     }
   } else if (userObject != null) {
     element = userObject;
   }
   return element;
 }
  @NotNull
  @Override
  public Collection<AbstractTreeNode> modify(
      @NotNull final AbstractTreeNode node,
      @NotNull Collection<AbstractTreeNode> collection,
      ViewSettings settings) {
    final Project project = node.getProject();
    if (node instanceof PsiDirectoryNode && project != null) {
      final Module module =
          ModuleUtil.findModuleForPsiElement(((PsiDirectoryNode) node).getValue());
      final Optional<String> buildPath =
          module != null
              ? PantsUtil.getPathFromAddress(
                  module, ExternalSystemConstants.LINKED_PROJECT_PATH_KEY)
              : Optional.empty();
      if (buildPath.isPresent()) {
        final Optional<VirtualFile> buildFile =
            PantsUtil.findFileRelativeToBuildRoot(project, buildPath.get());

        boolean isModuleRoot =
            ArrayUtil.indexOf(
                    ModuleRootManager.getInstance(module).getContentRoots(),
                    ((PsiDirectoryNode) node).getVirtualFile())
                >= 0;
        if (buildFile.isPresent() && isModuleRoot) {
          // Check if there's already a BUILD file in the directory; if so, we don't add another
          final AbstractTreeNode existingBuildFile =
              ContainerUtil.find(
                  collection.iterator(),
                  new Condition<AbstractTreeNode>() {
                    @Override
                    public boolean value(AbstractTreeNode node) {
                      return node instanceof PsiFileNode
                          && buildFile.get().equals(((PsiFileNode) node).getVirtualFile());
                    }
                  });
          if (existingBuildFile == null) {
            final PsiFile buildPsiFile = PsiManager.getInstance(project).findFile(buildFile.get());
            final PsiFileNode buildNode =
                new PsiFileNode(project, buildPsiFile, settings) {
                  @Override
                  protected void updateImpl(PresentationData data) {
                    super.updateImpl(data);
                    data.setIcon(PantsIcons.Icon);
                  }
                };
            final List<AbstractTreeNode> modifiedCollection =
                new ArrayList<AbstractTreeNode>(collection);
            modifiedCollection.add(buildNode);
            return modifiedCollection;
          }
        }
      }
    }
    return collection;
  }
 @NotNull
 @Override
 public Collection<AbstractTreeNode> modify(
     @NotNull AbstractTreeNode parent,
     @NotNull Collection<AbstractTreeNode> children,
     ViewSettings settings) {
   if (!isCourseBasedProject(parent)) {
     return children;
   }
   Collection<AbstractTreeNode> nodes = new ArrayList<AbstractTreeNode>();
   for (AbstractTreeNode node : children) {
     final Project project = node.getProject();
     if (project != null) {
       if (node.getValue() instanceof PsiDirectory) {
         final PsiDirectory nodeValue = (PsiDirectory) node.getValue();
         if (!nodeValue.getName().contains(EduNames.USER_TESTS)
             && !nodeValue.getName().equals(".idea")) {
           StudyDirectoryNode newNode = new StudyDirectoryNode(project, nodeValue, settings);
           nodes.add(newNode);
         }
       } else {
         if (parent instanceof StudyDirectoryNode && node instanceof PsiFileNode) {
           final PsiFileNode psiFileNode = (PsiFileNode) node;
           final VirtualFile virtualFile = psiFileNode.getVirtualFile();
           if (virtualFile == null) {
             return nodes;
           }
           final TaskFile taskFile = StudyUtils.getTaskFile(project, virtualFile);
           if (taskFile != null) {
             nodes.add(node);
           }
           final String parentName = parent.getName();
           if (parentName != null) {
             if (parentName.equals(EduNames.SANDBOX_DIR)) {
               nodes.add(node);
             }
           }
         }
       }
     }
   }
   return nodes;
 }
 protected boolean isCourseBasedProject(@NotNull final AbstractTreeNode parent) {
   final Project project = parent.getProject();
   if (project != null) {
     final StudyTaskManager studyTaskManager = StudyTaskManager.getInstance(project);
     if (studyTaskManager.getCourse() == null) {
       return false;
     }
   }
   return true;
 }
 private void appendChildNodes(
     AbstractTreeNode node, TreeItem<Pair<AbstractUrl, String>> treeItem) {
   final Collection<? extends AbstractTreeNode> children = node.getChildren();
   for (AbstractTreeNode child : children) {
     final TreeItem<Pair<AbstractUrl, String>> childTreeItem =
         new TreeItem<Pair<AbstractUrl, String>>(createPairForNode(child));
     treeItem.addChild(childTreeItem);
     appendChildNodes(child, childTreeItem);
   }
 }
 public Object getData(String dataId) {
   if (LangDataKeys.PSI_ELEMENT.is(dataId)) {
     TreePath path = getSelectedUniquePath();
     if (path == null) return null;
     DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
     Object userObject = node.getUserObject();
     if (!(userObject instanceof AbstractTreeNode)) return null;
     AbstractTreeNode descriptor = (AbstractTreeNode) userObject;
     Object element = descriptor.getValue();
     if (element instanceof StructureViewTreeElement) {
       element = ((StructureViewTreeElement) element).getValue();
     }
     if (!(element instanceof PsiElement)) return null;
     if (!((PsiElement) element).isValid()) return null;
     return element;
   }
   if (LangDataKeys.PSI_ELEMENT_ARRAY.is(dataId)) {
     return convertToPsiElementsArray(getSelectedElements());
   }
   if (PlatformDataKeys.FILE_EDITOR.is(dataId)) {
     return myFileEditor;
   }
   if (PlatformDataKeys.CUT_PROVIDER.is(dataId)) {
     return myCopyPasteDelegator.getCutProvider();
   }
   if (PlatformDataKeys.COPY_PROVIDER.is(dataId)) {
     return myCopyPasteDelegator.getCopyProvider();
   }
   if (PlatformDataKeys.PASTE_PROVIDER.is(dataId)) {
     return myCopyPasteDelegator.getPasteProvider();
   }
   if (PlatformDataKeys.NAVIGATABLE.is(dataId)) {
     Object[] selectedElements = getSelectedTreeElements();
     if (selectedElements == null || selectedElements.length == 0) return null;
     if (selectedElements[0] instanceof Navigatable) {
       return selectedElements[0];
     }
   }
   if (PlatformDataKeys.HELP_ID.is(dataId)) {
     return getHelpID();
   }
   return super.getData(dataId);
 }
 public synchronized boolean editRoot(
     @NotNull String name,
     @NotNull List<Integer> elementsIndexes,
     final AbstractTreeNode newElement) {
   List<TreeItem<Pair<AbstractUrl, String>>> list = getFavoritesListRootUrls(name);
   assert list != null;
   for (Integer index : elementsIndexes.subList(0, elementsIndexes.size() - 1)) {
     assert index >= 0 && index < list.size();
     final TreeItem<Pair<AbstractUrl, String>> item = list.get(index);
     list = item.getChildren();
   }
   assert list != null && !list.isEmpty();
   final Object value = newElement.getValue();
   final AbstractUrl urlByElement = createUrlByElement(value, myProject);
   if (urlByElement == null) return false;
   list.set(
       elementsIndexes.get(elementsIndexes.size() - 1).intValue(),
       new TreeItem<Pair<AbstractUrl, String>>(
           Pair.create(urlByElement, newElement.getClass().getName())));
   return true;
 }
  private static boolean processValuesFlownTo(
      @NotNull final PsiExpression argument,
      PsiElement scope,
      @NotNull final Processor<PsiExpression> processor) {
    SliceAnalysisParams params = new SliceAnalysisParams();
    params.dataFlowToThis = true;
    params.scope = new AnalysisScope(new LocalSearchScope(scope), argument.getProject());

    SliceRootNode rootNode =
        new SliceRootNode(
            scope.getProject(), new DuplicateMap(), SliceManager.createRootUsage(argument, params));

    Collection<? extends AbstractTreeNode> children =
        rootNode.getChildren().iterator().next().getChildren();
    for (AbstractTreeNode child : children) {
      SliceUsage usage = (SliceUsage) child.getValue();
      PsiElement element = usage.getElement();
      if (element instanceof PsiExpression && !processor.process((PsiExpression) element))
        return false;
    }

    return !children.isEmpty();
  }
  private <T> T getProvider(
      Collection<AbstractTreeNode> selected, ProviderFactory<T> createProvider) {
    if (selected.size() == 0) return null;

    List<SNodeReference> selectedNodePointers = new ArrayList<SNodeReference>();
    Project project = null;
    EditableSModel modelDescriptor = null;

    for (AbstractTreeNode treeNode : selected) {
      if (!(treeNode instanceof MPSPsiElementTreeNode)) return null; // only root nodes please

      MPSPsiRootNode mpsPsiNode = ((MPSPsiElementTreeNode) treeNode).getValue();
      if (!mpsPsiNode.isValid()) return null;

      selectedNodePointers.add(mpsPsiNode.getSNodeReference());

      if (project == null) {
        project = treeNode.getProject();
      } else if (project != treeNode.getProject()) {
        return null; // only same project
      }

      if (modelDescriptor == null) {
        modelDescriptor = getModel(mpsPsiNode);
      } else if (modelDescriptor != getModel(mpsPsiNode)) {
        return null; // only same model
      }
    }
    jetbrains.mps.project.Project mpsProject = ProjectHelper.toMPSProject(project);
    if (mpsProject == null || modelDescriptor == null) return null;

    SModel sModel = modelDescriptor;
    if (sModel == null) return null;

    return createProvider.create(selectedNodePointers, modelDescriptor, sModel, mpsProject);
  }
  private <T> T getModelProvider(
      AbstractTreeNode treeNode, ModelProviderFactory<T> createProvider) {
    if (!(treeNode instanceof MPSPsiModelTreeNode)) return null; // only model

    MPSPsiModel psiModel = ((MPSPsiModelTreeNode) treeNode).getModel();
    if (psiModel == null || !psiModel.isValid()) return null;

    Project project = treeNode.getProject();
    EditableSModel modelDescriptor = getModel(treeNode);

    jetbrains.mps.project.Project mpsProject = ProjectHelper.toMPSProject(project);
    if (mpsProject == null || modelDescriptor == null) return null;

    SModel sModel = modelDescriptor;
    if (sModel == null) return null;

    return createProvider.create(modelDescriptor, sModel, mpsProject);
  }
 private SNode getNode(AbstractTreeNode treeNode) {
   if (!(treeNode instanceof MPSPsiElementTreeNode)) {
     return null;
   }
   MPSPsiNodeBase psiNode = ((MPSPsiElementTreeNode) treeNode).getValue();
   if (!(psiNode instanceof MPSPsiRealNode)) {
     return null;
   }
   final SNodeReference nodeRef = ((MPSPsiRealNode) psiNode).getSNodeReference();
   final SRepository repository = ProjectHelper.getProjectRepository(treeNode.getProject());
   // TODO remove read action from here once SModelFileTracker stops doing the same (creating read
   // action if not already in one)
   return new ModelComputeRunnable<SNode>(
           new Computable<SNode>() {
             @Override
             public SNode compute() {
               return nodeRef.resolve(repository);
             }
           })
       .runRead(repository.getModelAccess());
 }
  @Override
  public Collection<AbstractTreeNode> modify(
      final AbstractTreeNode treeNode,
      final Collection<AbstractTreeNode> children,
      final ViewSettings settings) {
    final Ref<Collection<AbstractTreeNode>> result =
        new Ref<Collection<AbstractTreeNode>>(children);

    // we're actually in EDT here, but we work with SModels, and various routines assert that we can
    // read, thus read action
    ProjectHelper.getProjectRepository(treeNode.getProject())
        .getModelAccess()
        .runReadAction(
            new Runnable() {
              @Override
              public void run() {
                List<AbstractTreeNode> updatedChildren = null;
                final MPSPsiProvider mpsPsiProvider =
                    MPSPsiProvider.getInstance(treeNode.getProject());

                // if current dir is data source from some model
                FolderDataSource currentDirectoryDataSource = null;

                if (treeNode instanceof PsiDirectoryNode) {
                  // let's see if we have a model built from this dir, e.g. in per-root persistence
                  SModel sModel =
                      SModelFileTracker.getInstance()
                          .findModel(
                              VirtualFileUtils.toIFile(
                                  ((PsiDirectoryNode) treeNode).getVirtualFile()));
                  if (sModel != null) {
                    // adding root nodes (removing their corresponding files' nodes from the tree is
                    // further below)
                    List<MPSPsiElementTreeNode> rootsTreeNodes =
                        new ArrayList<MPSPsiElementTreeNode>();
                    for (SNode root : sModel.getRootNodes()) {
                      rootsTreeNodes.add(
                          new MPSPsiElementTreeNode(
                              treeNode.getProject(),
                              (MPSPsiRootNode) mpsPsiProvider.getPsi(root).getContainingFile(),
                              settings));
                    }
                    if (!rootsTreeNodes.isEmpty() && updatedChildren == null) {
                      updatedChildren = new ArrayList<AbstractTreeNode>(children);
                      updatedChildren.addAll(rootsTreeNodes);
                    }

                    DataSource dataSource = sModel.getSource();
                    if (dataSource instanceof FolderDataSource) {
                      // could be assert as currently SModelFileTracker only tracks FileDataSource
                      // and FolderDataSource
                      currentDirectoryDataSource = (FolderDataSource) dataSource;
                    }
                  }
                } else if (treeNode instanceof MPSPsiModelTreeNode) {
                  MPSPsiModel psiModel = ((MPSPsiModelTreeNode) treeNode).extractPsiFromValue();
                  updatedChildren = new ArrayList<AbstractTreeNode>();
                  for (PsiElement psiElement : psiModel.getChildren()) {
                    updatedChildren.add(
                        new MPSPsiElementTreeNode(
                            treeNode.getProject(), (MPSPsiRootNode) psiElement, settings));
                  }
                }

                for (final AbstractTreeNode child : children) {
                  if (child instanceof PsiFileNode) {
                    VirtualFile vFile = ((PsiFileNode) child).getVirtualFile();
                    if (vFile == null) {
                      continue;
                    }

                    // check if it's a single file model
                    final SModel sModel =
                        SModelFileTracker.getInstance().findModel(VirtualFileUtils.toIFile(vFile));
                    if (sModel != null) {
                      if (updatedChildren == null)
                        updatedChildren = new ArrayList<AbstractTreeNode>(children);
                      int idx = updatedChildren.indexOf(child);
                      updatedChildren.remove(idx);
                      updatedChildren.add(
                          idx,
                          new MPSPsiModelTreeNode(
                              treeNode.getProject(), mpsPsiProvider.getPsi(sModel), settings));
                      continue;
                    }

                    if (currentDirectoryDataSource != null
                        && currentDirectoryDataSource.isIncluded(VirtualFileUtils.toIFile(vFile))) {
                      // it's a file that constitutes a FolderDataSource-backed model, remove it
                      // from the tree (root nodes are shown instead)
                      if (updatedChildren == null)
                        updatedChildren = new ArrayList<AbstractTreeNode>(children);
                      int idx = updatedChildren.indexOf(child);
                      updatedChildren.remove(idx);
                    }

                  } else if (child instanceof PsiDirectoryNode) {
                    // below code only attaches our action to the directory and makes it show added
                    // children - our root nodes

                    final SModel perRootModel =
                        SModelFileTracker.getInstance()
                            .findModel(
                                VirtualFileUtils.toIFile(
                                    ((PsiDirectoryNode) child).getVirtualFile()));
                    if (perRootModel != null) {
                      if (updatedChildren == null)
                        updatedChildren = new ArrayList<AbstractTreeNode>(children);

                      int idx = updatedChildren.indexOf(child);
                      updatedChildren.remove(idx);
                      updatedChildren.add(
                          idx,
                          new PsiDirectoryNode(
                              treeNode.getProject(),
                              ((PsiDirectoryNode) child).getValue(),
                              settings) {
                            @Override
                            public boolean canNavigate() {
                              return true;
                            }

                            @Override
                            public String getNavigateActionText(boolean focusEditor) {
                              return MPSBundle.message("open.model.properties.action");
                            }

                            @Override
                            public void navigate(boolean requestFocus) {
                              MPSPropertiesConfigurable configurable =
                                  new ModelPropertiesConfigurable(
                                      perRootModel, ProjectHelper.toMPSProject(myProject), true);
                              final SingleConfigurableEditor dialog =
                                  new SingleConfigurableEditor(myProject, configurable);
                              configurable.setParentForCallBack(dialog);
                              SwingUtilities.invokeLater(
                                  new Runnable() {
                                    @Override
                                    public void run() {
                                      dialog.show();
                                    }
                                  });
                            }
                          });
                    }
                  }
                }

                if (updatedChildren != null) {
                  result.set(updatedChildren);
                }
              }
            });

    return result.get();
  }
 private Module getIdeaModule(AbstractTreeNode treeNode) {
   VirtualFile modelVFile = getVirtualFile(treeNode);
   if (modelVFile == null) return null;
   return ModuleUtilCore.findModuleForFile(modelVFile, treeNode.getProject());
 }
  public synchronized boolean addRoot(
      @NotNull String name,
      @NotNull List<AbstractTreeNode> parentElements,
      final AbstractTreeNode newElement,
      @Nullable AbstractTreeNode sibling) {
    final List<TreeItem<Pair<AbstractUrl, String>>> items = myName2FavoritesRoots.get(name);
    if (items == null) return false;
    AbstractUrl url = createUrlByElement(newElement.getValue(), myProject);
    if (url == null) return false;
    final TreeItem<Pair<AbstractUrl, String>> newItem =
        new TreeItem<Pair<AbstractUrl, String>>(Pair.create(url, newElement.getClass().getName()));

    if (parentElements.isEmpty()) {
      // directly to list
      if (sibling != null) {
        TreeItem<Pair<AbstractUrl, String>> after = null;
        AbstractUrl siblingUrl = createUrlByElement(sibling.getValue(), myProject);
        int idx = -1;
        for (int i = 0; i < items.size(); i++) {
          TreeItem<Pair<AbstractUrl, String>> item = items.get(i);
          if (item.getData().getFirst().equals(siblingUrl)) {
            idx = i;
            break;
          }
        }
        if (idx != -1) {
          items.add(idx, newItem);
        } else {
          items.add(newItem);
        }
      } else {
        items.add(newItem);
      }

      fireListeners.rootsChanged(name);
      return true;
    }

    Collection<TreeItem<Pair<AbstractUrl, String>>> list = items;
    TreeItem<Pair<AbstractUrl, String>> item = null;
    for (AbstractTreeNode obj : parentElements) {
      AbstractUrl objUrl = createUrlByElement(obj.getValue(), myProject);
      item = findNextItem(objUrl, list);
      if (item == null) return false;
      list = item.getChildren();
    }

    if (sibling != null) {
      TreeItem<Pair<AbstractUrl, String>> after = null;
      AbstractUrl siblingUrl = createUrlByElement(sibling.getValue(), myProject);
      for (TreeItem<Pair<AbstractUrl, String>> treeItem : list) {
        if (treeItem.getData().getFirst().equals(siblingUrl)) {
          after = treeItem;
          break;
        }
      }
      if (after == null) {
        item.addChild(newItem);
      } else {
        item.addChildAfter(newItem, after);
      }
    } else {
      item.addChild(newItem);
    }
    fireListeners.rootsChanged(name);
    return true;
  }