javaswing

Common parent of DefaultMutableTreeNode collection


Can I find a common parent node of a collection of DefualtMutableTreeNodes?

OOB ways to achieve that, if any, are preferred. That includes standard as well as external libraries.

If it's not already available, the solution should be as readable and concise as possible (reducing the maintenance overhead). It shouldn't be too expensive too, if possible.

Swing, Java 8.

    @Test
    // it should be split into at least two tests, but it'll do
    void findCommonParent_returnsExpectedNode() {
        /*
        a____b
        |____c____d
        |____e____f
        |    |____g____h____i
        |         |____j
        |____k
         */
        JTree tree = new JTree();
        DefaultMutableTreeNode rootNodeA = new DefaultMutableTreeNode("a");
        DefaultMutableTreeNode nodeB = new DefaultMutableTreeNode("b");
        DefaultMutableTreeNode nodeC = new DefaultMutableTreeNode("c");
        DefaultMutableTreeNode nodeD = new DefaultMutableTreeNode("d");
        DefaultMutableTreeNode nodeE = new DefaultMutableTreeNode("e");
        DefaultMutableTreeNode nodeF = new DefaultMutableTreeNode("f");
        DefaultMutableTreeNode nodeG = new DefaultMutableTreeNode("g");
        DefaultMutableTreeNode nodeH = new DefaultMutableTreeNode("h");
        DefaultMutableTreeNode nodeI = new DefaultMutableTreeNode("i");
        DefaultMutableTreeNode nodeJ = new DefaultMutableTreeNode("j");
        DefaultMutableTreeNode nodeK = new DefaultMutableTreeNode("k");
        nodeC.add(nodeD);
        nodeE.add(nodeF);
        nodeE.add(nodeG);
        nodeG.add(nodeH);
        nodeG.add(nodeJ);
        nodeH.add(nodeI);
        rootNodeA.add(nodeB);
        rootNodeA.add(nodeC);
        rootNodeA.add(nodeE);
        rootNodeA.add(nodeK);
        DefaultTreeModel model = new DefaultTreeModel(rootNodeA);
        tree.setModel(model);

        Optional<DefaultMutableTreeNode> commonParentOptional = findCommonParent(Arrays.asList(nodeF, nodeJ));
        assertTrue(commonParentOptional.isPresent());
        assertEquals(nodeE, commonParentOptional.get());

        Optional<DefaultMutableTreeNode> anotherCommonParentOptional = findCommonParent(Arrays.asList(nodeF, new DefaultMutableTreeNode()));
        assertFalse(anotherCommonParentOptional.isPresent());
    }

    Optional<DefaultMutableTreeNode> findCommonParent(List<DefaultMutableTreeNode> nodes) {
        // your implementation goes here
    }

Solution

  • I think this is the best complete solution, based on the two previous answers:

        Optional<DefaultMutableTreeNode> findCommonParent(List<DefaultMutableTreeNode> nodes) {
            if (nodes.isEmpty()) {
                return Optional.empty();
            }
            Iterator<DefaultMutableTreeNode> iterator = nodes.iterator();
            DefaultMutableTreeNode common = iterator.next();
            while (iterator.hasNext() && common != null) {
                DefaultMutableTreeNode node = iterator.next();
                common = (DefaultMutableTreeNode) findCommonParent(common, node);
            }
            return Optional.ofNullable(common);
        }
    
        private TreeNode findCommonParent(TreeNode a, TreeNode b) {
            return parentsOf(a)
                    .filter(parent -> parentsOf(b).anyMatch(parent::equals))                    
                    .findFirst().orElse(null);
        }
    
        private static Stream<TreeNode> parentsOf(TreeNode a) {
            return Stream.iterate(a, Objects::nonNull, TreeNode::getParent);
        }