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

      return element.getNext((List) parent.getChildren());
    }
 @NotNull
 SliceNode copy() {
   SliceUsage newUsage = getValue().copy();
   SliceNode newNode = new SliceNode(getProject(), newUsage, targetEqualUsages);
   newNode.dupNodeCalculated = dupNodeCalculated;
   newNode.duplicate = duplicate;
   return newNode;
 }
 private static void expandNodesTo(final SliceNode node, List<SliceNode> to) {
   node.update();
   node.calculateDupNode();
   to.add(node);
   Collection<? extends AbstractTreeNode> nodes = node.getChildren();
   for (AbstractTreeNode child : nodes) {
     expandNodesTo((SliceNode) child, to);
   }
 }
  private static void groupByValues(
      @NotNull Collection<PsiElement> leaves,
      @NotNull SliceRootNode oldRoot,
      @NotNull Map<SliceNode, Collection<PsiElement>> map) {
    assert oldRoot.myCachedChildren.size() == 1;
    SliceRootNode root = createTreeGroupedByValues(leaves, oldRoot, map);

    SliceNode oldRootStart = oldRoot.myCachedChildren.get(0);
    SliceUsage rootUsage = oldRootStart.getValue();
    String description =
        SliceManager.getElementDescription(null, rootUsage.getElement(), " (grouped by value)");
    SliceManager.getInstance(root.getProject()).createToolWindow(true, root, true, description);
  }
 @Nullable
 private List<UsageInfo> getSelectedUsageInfos() {
   TreePath[] paths = myTree.getSelectionPaths();
   if (paths == null) return null;
   final ArrayList<UsageInfo> result = new ArrayList<UsageInfo>();
   for (TreePath path : paths) {
     SliceNode sliceNode = fromPath(path);
     if (sliceNode != null) {
       result.add(sliceNode.getValue().getUsageInfo());
     }
   }
   if (result.isEmpty()) return null;
   return result;
 }
  public void testTypingDoesNotInterfereWithDuplicates() throws Exception {
    SliceTreeStructure treeStructure = configureTree("DupSlice");
    SliceNode root = (SliceNode) treeStructure.getRootElement();
    List<SliceNode> nodes = new ArrayList<SliceNode>();
    expandNodesTo(root, nodes);

    for (int i = 0; i < nodes.size() - 1; i++) {
      SliceNode node = nodes.get(i);
      assertNull(node.getDuplicate());
    }
    SliceNode last = nodes.get(nodes.size() - 1);
    assertNotNull(last.getDuplicate());

    type("   xx");
    PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
    backspace();
    backspace();
    PsiDocumentManager.getInstance(getProject()).commitAllDocuments();

    nodes.clear();
    expandNodesTo(root, nodes);
    for (int i = 0; i < nodes.size() - 1; i++) {
      SliceNode node = nodes.get(i);
      assertNull(node.getDuplicate());
    }
    assertNotNull(last.getDuplicate());
  }
  static SliceNode filterTree(
      SliceNode oldRoot,
      NullableFunction<SliceNode, SliceNode> filter,
      PairProcessor<SliceNode, List<SliceNode>> postProcessor) {
    SliceNode filtered = filter.fun(oldRoot);
    if (filtered == null) return null;

    List<SliceNode> childrenFiltered = new ArrayList<SliceNode>();
    if (oldRoot.myCachedChildren != null) {
      for (SliceNode child : oldRoot.myCachedChildren) {
        SliceNode childFiltered = filterTree(child, filter, postProcessor);
        if (childFiltered != null) {
          childrenFiltered.add(childFiltered);
        }
      }
    }
    boolean success = postProcessor == null || postProcessor.process(filtered, childrenFiltered);
    if (!success) return null;
    filtered.myCachedChildren = new ArrayList<SliceNode>(childrenFiltered);
    return filtered;
  }
  public void testGroupByValuesCorrectLeaves() throws Exception {
    SliceTreeStructure treeStructure = configureTree("DuplicateLeaves");
    SliceRootNode root = (SliceRootNode) treeStructure.getRootElement();
    Map<SliceNode, Collection<PsiElement>> map = SliceLeafAnalyzer.createMap();
    Collection<PsiElement> leaves = SliceLeafAnalyzer.calcLeafExpressions(root, treeStructure, map);
    assertNotNull(leaves);
    assertEquals(1, leaves.size());
    PsiElement leaf = leaves.iterator().next();
    assertTrue(leaf instanceof PsiLiteralExpression);
    assertEquals("\"oo\"", leaf.getText());

    SliceRootNode newRoot = SliceLeafAnalyzer.createTreeGroupedByValues(leaves, root, map);
    Collection<? extends AbstractTreeNode> children = newRoot.getChildren();
    assertEquals(1, children.size());
    SliceNode child = (SliceNode) children.iterator().next();
    assertTrue(child instanceof SliceLeafValueRootNode);

    children = child.getChildren();
    assertEquals(1, children.size());
    child = (SliceNode) children.iterator().next();
    assertTrue(child.getValue().getElement() instanceof PsiField);

    children = child.getChildren();
    assertEquals(1, children.size());
    child = (SliceNode) children.iterator().next();
    assertTrue(child.getValue().getElement() instanceof PsiReferenceExpression);

    children = child.getChildren();
    assertEquals(1, children.size());
    child = (SliceNode) children.iterator().next();
    assertTrue(child.getValue().getElement() instanceof PsiReferenceExpression);

    children = child.getChildren();
    assertEquals(1, children.size());
    child = (SliceNode) children.iterator().next();
    assertTrue(child.getValue().getElement() instanceof PsiLiteralExpression);
    assertEquals(child.getValue().getElement(), leaf);
  }
  private static void checkStructure(final SliceNode root, @NonNls String dataExpected) {
    List<SliceNode> actualNodes = new ArrayList<SliceNode>((Collection) root.getChildren());
    Collections.sort(actualNodes, SliceTreeBuilder.SLICE_NODE_COMPARATOR);

    Object[] actualStrings =
        ContainerUtil.map2Array(
            actualNodes,
            new Function<SliceNode, Object>() {
              @Override
              public Object fun(SliceNode node) {
                return node.toString();
              }
            });

    String[] childrenExpected =
        dataExpected.length() == 0 ? ArrayUtil.EMPTY_STRING_ARRAY : dataExpected.split("\n");
    String curChildren = "";
    String curNode = null;
    int iactual = 0;
    for (int iexp = 0; iexp <= childrenExpected.length; iexp++) {
      String e = iexp == childrenExpected.length ? null : childrenExpected[iexp];
      boolean isTopLevel = e == null || e.charAt(0) != ' ';
      if (isTopLevel) {
        if (curNode != null) {
          assertTrue(iactual < actualStrings.length);
          Object actual = actualStrings[iactual];
          assertEquals(curNode, actual);
          checkStructure(actualNodes.get(iactual), curChildren);
          iactual++;
        }

        curNode = e;
        curChildren = "";
      } else {
        curChildren += StringUtil.trimStart(e, "  ") + "\n";
      }
    }
    assertEquals(actualNodes.size(), iactual);
  }
 @Override
 public final void actionPerformed(final AnActionEvent e) {
   SliceNode rootNode = (SliceNode) myBuilder.getRootNode().getUserObject();
   rootNode.setChanged();
   myBuilder.addSubtreeToUpdate(myBuilder.getRootNode());
 }
 @Override
 public SliceNode getParent(@NotNull SliceNode element) {
   AbstractTreeNode parent = element.getParent();
   return parent instanceof SliceNode ? (SliceNode) parent : null;
 }