import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeExpansionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;

@SuppressWarnings("serial")
/**
 *  JTree extremely slows down when having lots of nodes.
 *
 *  How to reproduce:
 *   - Start example
 *   - expand nodes
 *
 *  Problem:
 *  When expanding nodes in lower level (i.e. level == 2) it takes very long () to expand.
 *  -> "node 2:0" or "node 3:0"
 *
 *  Problem occurs on:
 *  macOS 13 and 14
 *  Hardware: MacBook Pro (Apple M2 Pro) and lower
 *  JDK: 11, 17, 21
 *
 *  With jstack I see that is is somewhere under sun.lwawt.macosx.CAccessibility.getChildrenAndRolesImpl()
 */
public class TestLargeTree
{
    class MyTreeNode extends DefaultMutableTreeNode
    {
        public MyTreeNode(String txt)
        {
            super(txt, true);
        }

        @Override
        public int getChildCount()
        {
            int anz =  super.getChildCount();
            if(anz==0)
            {
                // dynamically add some child nodes
                int level = getLevel();
                int num = 100;
                if(level>0)
                    num = 5000;
                for(int i=0;i<num; i++)
                {
                    insert(new MyTreeNode("node "+level+" :"+i), i);
                }
                anz =  super.getChildCount();
            }
            return anz;
        }

        @Override
        public boolean isLeaf()
        {
            return false;
        }
    }

    public TestLargeTree()
    {
        JFrame frame = new JFrame("TestLargeTree");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

        JTree tree = new JTree(new DefaultTreeModel(new MyTreeNode("root")));
        tree.setShowsRootHandles(true);
        tree.addTreeExpansionListener(new TreeExpansionListener()
        {
            @Override
            public void treeExpanded(TreeExpansionEvent event)
            {
                frame.setTitle("TestLargeTree rows: "+tree.getRowCount());
            }

            @Override
            public void treeCollapsed(TreeExpansionEvent event)
            {
                frame.setTitle("TestLargeTree rows: "+tree.getRowCount());
            }
        });
        frame.add(new JScrollPane(tree));

        frame.pack();
        frame.setSize(300, 300);
        frame.setVisible(true);
    }

    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            @Override
            public void run()
            {
                new TestLargeTree();
            }
        });


    }

}
