Search code examples
javaalgorithmjungjung2

java.lang.IllegalArgumentException: Tree must not already contain child


I have created the following jung2 visualization. My code visualizes a text into a tree. Basically I am using two inputs, one at a time:

        /*
         * INPUT 1
         */
        String text = "=IF(A2=1;0;IF(D2=D3;IF(C2=1;TRUE;FALSE);4))";

        /*
         * INPUT 2
         */
        String text = "=IF(A2=1;0;IF(D2=D3;IF(C2=1;TRUE;FALSE);TRUE))";

My problem is, the first input works as expected, but when using the Input 2, which is just different in the last element 4 instead of TRUE, I get the error:

Exception in thread "main" java.lang.IllegalArgumentException: Tree must not already contain child D2=D3
    at edu.uci.ics.jung.graph.DelegateTree.addChild(DelegateTree.java:182)
    at edu.uci.ics.jung.graph.DelegateTree.addEdge(DelegateTree.java:102)
    at edu.uci.ics.jung.graph.DelegateTree.addEdge(DelegateTree.java:346)
    at edu.uci.ics.jung.graph.util.TreeUtils.growSubTree(TreeUtils.java:76)
    at edu.uci.ics.jung.graph.util.TreeUtils.growSubTree(TreeUtils.java:80)
    at edu.uci.ics.jung.graph.DelegateForest.getTrees(DelegateForest.java:295)
    at edu.uci.ics.jung.graph.util.TreeUtils.getRoots(TreeUtils.java:34)
    at edu.uci.ics.jung.algorithms.layout.TreeLayout.buildTree(TreeLayout.java:102)
    at edu.uci.ics.jung.algorithms.layout.TreeLayout.<init>(TreeLayout.java:97)
    at edu.uci.ics.jung.algorithms.layout.TreeLayout.<init>(TreeLayout.java:75)
    at justDelete.me.codeTestingPackage.Test.<init>(Test.java:131)
    at justDelete.me.codeTestingPackage.Test.main(Test.java:396)

The confusing thing is that the string is parsed correctly and could be used by the program correctly like:

[IF, A2=1, 0, IF, D2=D3, IF, C2=1, TRUE, FALSE, TRUE]

Here is the version of my program which is runnable with input 2(the "broken" input) in comments:

@SuppressWarnings("serial")
public class Test extends JApplet {

    /**
     * the graph
     */
    Forest<String,Integer> graph;


    private static final Logger log = Logger.getLogger(Test.class);


    Factory<DirectedGraph<String,Integer>> graphFactory = 
            new Factory<DirectedGraph<String,Integer>>() {

        public DirectedGraph<String, Integer> create() {
            return new DirectedSparseMultigraph<String,Integer>();
        }
    };

    Factory<Tree<String,Integer>> treeFactory =
            new Factory<Tree<String,Integer>> () {

        public Tree<String, Integer> create() {
            return new DelegateTree<String,Integer>(graphFactory);
        }
    };



    Factory<Integer> edgeFactory = new Factory<Integer>() {
        int i=0;
        public Integer create() {
            return i++;
        }};

        Factory<String> vertexFactory = new Factory<String>() {
            int i=0;
            public String create() {
                return "V"+i++;
            }};

            /**
             * the visual component and renderer for the graph
             */
            VisualizationViewer<String,Integer> vv;

            VisualizationServer.Paintable rings;

            String root;

            TreeLayout<String,Integer> layout;
            @SuppressWarnings("unchecked")
            FRLayout layout1;

            TreeCollapser collapser;

            RadialTreeLayout<String,Integer> radialLayout;

            @SuppressWarnings("unchecked")
            public Test() {

                // create a simple graph for the demo
                graph = new DelegateForest<String,Integer>();

                createTree();

                layout = new TreeLayout<String,Integer>(graph);
                collapser = new TreeCollapser();

                radialLayout = new RadialTreeLayout<String,Integer>(graph);
                radialLayout.setSize(new Dimension(600,600));
                vv =  new VisualizationViewer<String,Integer>(layout, new Dimension(600,600));
                vv.setBackground(Color.white);
                vv.getRenderContext().setEdgeShapeTransformer(new EdgeShape.Line());
                vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller());
                vv.getRenderContext().setVertexShapeTransformer(new ClusterVertexShapeFunction());
                // add a listener for ToolTips
                vv.setVertexToolTipTransformer(new ToStringLabeller());
                vv.getRenderContext().setArrowFillPaintTransformer(new ConstantTransformer(Color.lightGray));
                rings = new Rings();

                Container content = getContentPane();
                final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv);
                content.add(panel);

                final DefaultModalGraphMouse graphMouse = new DefaultModalGraphMouse();

                vv.setGraphMouse(graphMouse);

                JComboBox modeBox = graphMouse.getModeComboBox();
                modeBox.addItemListener(graphMouse.getModeListener());
                graphMouse.setMode(ModalGraphMouse.Mode.TRANSFORMING);

                final ScalingControl scaler = new CrossoverScalingControl();

                JButton plus = new JButton("+");
                plus.addActionListener(new ActionListener() {
                    public void actionPerformed(ActionEvent e) {
                        scaler.scale(vv, 1.1f, vv.getCenter());
                    }
                });
                JButton minus = new JButton("-");
                minus.addActionListener(new ActionListener() {
                    public void actionPerformed(ActionEvent e) {
                        scaler.scale(vv, 1/1.1f, vv.getCenter());
                    }
                });

                JToggleButton radial = new JToggleButton("Radial");
                radial.addItemListener(new ItemListener() {

                    public void itemStateChanged(ItemEvent e) {
                        if(e.getStateChange() == ItemEvent.SELECTED) {
                            //                  layout.setRadial(true);
                            vv.setGraphLayout(radialLayout);
                            vv.getRenderContext().getMultiLayerTransformer().setToIdentity();
                            vv.addPreRenderPaintable(rings);
                        } else {
                            //                  layout.setRadial(false);
                            vv.setGraphLayout(layout);
                            vv.getRenderContext().getMultiLayerTransformer().setToIdentity();
                            vv.removePreRenderPaintable(rings);
                        }
                        vv.repaint();
                    }});

                JButton collapse = new JButton("Collapse");
                collapse.addActionListener(new ActionListener() {

                    public void actionPerformed(ActionEvent e) {
                        Collection picked =new HashSet(vv.getPickedVertexState().getPicked());
                        if(picked.size() == 1) {
                            Object root = picked.iterator().next();
                            Forest inGraph = (Forest)layout.getGraph();

                            try {
                                collapser.collapse(vv.getGraphLayout(), inGraph, root);
                            } catch (InstantiationException e1) {
                                // TODO Auto-generated catch block
                                e1.printStackTrace();
                            } catch (IllegalAccessException e1) {
                                // TODO Auto-generated catch block
                                e1.printStackTrace();
                            }

                            vv.getPickedVertexState().clear();
                            vv.repaint();
                        }
                    }});

                JButton expand = new JButton("Expand");
                expand.addActionListener(new ActionListener() {

                    public void actionPerformed(ActionEvent e) {
                        Collection picked = vv.getPickedVertexState().getPicked();
                        for(Object v : picked) {
                            if(v instanceof Forest) {
                                Forest inGraph = (Forest)layout.getGraph();
                                collapser.expand(inGraph, (Forest)v);
                            }
                            vv.getPickedVertexState().clear();
                            vv.repaint();
                        }
                    }});

                JPanel scaleGrid = new JPanel(new GridLayout(1,0));
                scaleGrid.setBorder(BorderFactory.createTitledBorder("Zoom"));

                JPanel controls = new JPanel();
                scaleGrid.add(plus);
                scaleGrid.add(minus);
                controls.add(radial);
                controls.add(scaleGrid);
                controls.add(modeBox);
                controls.add(collapse);
                controls.add(expand);
                content.add(controls, BorderLayout.SOUTH);
            }

            class Rings implements VisualizationServer.Paintable {

                Collection<Double> depths;

                public Rings() {
                    depths = getDepths();
                }

                private Collection<Double> getDepths() {
                    Set<Double> depths = new HashSet<Double>();
                    Map<String,PolarPoint> polarLocations = radialLayout.getPolarLocations();
                    for(String v : graph.getVertices()) {
                        PolarPoint pp = polarLocations.get(v);
                        depths.add(pp.getRadius());
                    }
                    return depths;
                }

                public void paint(Graphics g) {
                    g.setColor(Color.lightGray);

                    Graphics2D g2d = (Graphics2D)g;
                    Point2D center = radialLayout.getCenter();

                    Ellipse2D ellipse = new Ellipse2D.Double();
                    for(double d : depths) {
                        ellipse.setFrameFromDiagonal(center.getX()-d, center.getY()-d, 
                                center.getX()+d, center.getY()+d);
                        Shape shape = vv.getRenderContext().
                                getMultiLayerTransformer().getTransformer(Layer.LAYOUT).transform(ellipse);
                        g2d.draw(shape);
                    }
                }

                public boolean useTransform() {
                    return true;
                }
            }

            /**
             * create Tree
             */
            private void createTree() {

                /*
                 * INPUT 1
                 */
//              String text = "=IF(A2=1;0;IF(D2=D3;IF(C2=1;TRUE;FALSE);4))";

                /*
                 * INPUT 2
                 */
                String text = "=IF(A2=1;0;IF(D2=D3;IF(C2=1;TRUE;FALSE);TRUE))";


                text.toUpperCase();

                //START
                String[] operands = text.substring(1, text.length()).split("[;()]+");

                System.out.println(Arrays.toString(operands));
                int numIfs = operands.length / 3; // actually (operands.length - 1) / 3 but int division makes it the same 
                String[] nodes = new String[numIfs]; // stores the nodes (test strings)
                int[] operandNos = new int[numIfs]; // stores the number of operands the if currently has
                int nodesIndex = -1; // the index of the if node currently parsed
                for (String s : operands) {
                    if (s.equals("IF")) {
                        // new if found -> increase position in the "stack" (nodes)
                        operandNos[++nodesIndex] = 0;
                    } else {
                        //                      addVertex(s);
                        graph.addVertex(s);
                        switch (operandNos[nodesIndex]++) {
                        case 0:
                            // first operand = node name
                            nodes[nodesIndex] = s;
                            break;
                        case 1:
                            // second operand found -> add edge
                            graph.addEdge(edgeFactory.create(), s, nodes[nodesIndex]);
                            break;
                        case 2:
                            // last operand found -> add edge and go back
                            do {
                                graph.addEdge(edgeFactory.create(), s, nodes[nodesIndex]);
                                s = nodes[nodesIndex--];
                            } while (nodesIndex >= 0 && operandNos[nodesIndex]++ == 2);
                            if (nodesIndex >= 0) {
                                // was not the last operand of the IF
                                graph.addEdge(edgeFactory.create(), s, nodes[nodesIndex]);
                            }
                        }
                    }
                }       
                //END

            }

            class ClusterVertexShapeFunction<V> extends EllipseVertexShapeTransformer<V>
            {

                ClusterVertexShapeFunction() {
                    setSizeTransformer(new ClusterVertexSizeFunction<V>(20));
                }
                @SuppressWarnings("unchecked")
                @Override
                public Shape transform(V v) {
                    if(v instanceof Graph) {
                        int size = ((Graph)v).getVertexCount();
                        if (size < 8) {   
                            int sides = Math.max(size, 3);
                            return factory.getRegularPolygon(v, sides);
                        }
                        else {
                            return factory.getRegularStar(v, size);
                        }
                    }
                    return super.transform(v);
                }
            }

            /**
             * A demo class that will make vertices larger if they represent
             * a collapsed collection of original vertices
             * @author Tom Nelson
             *
             * @param <V>
             */
            class ClusterVertexSizeFunction<V> implements Transformer<V,Integer> {
                int size;
                public ClusterVertexSizeFunction(Integer size) {
                    this.size = size;
                }

                public Integer transform(V v) {
                    if(v instanceof Graph) {
                        return 30;
                    }
                    return size;
                }
            }



            /**
             * a driver for this demo
             */
            public static void main(String[] args) {
                JFrame frame = new JFrame();
                Container content = frame.getContentPane();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

                content.add(new Test());
                frame.pack();
                frame.setVisible(true);
            }
}

When executing the code with input 2, I get the posted error. Any recommendations why this is the case? My guess is that the strings are the same, even though this would be kind of ridiculous because the leaves of a tree should be unique. Hence, how to deal with this problem?

I appreciate your answer!


Solution

  • The problem is that the same vertex should appear multiple times.

    One could go very far with explaining the details, but just to give the idea: Imagine that the Graph in JUNG is internally maintained as a bunch of Maps. For example, a Map<V, Set<E>> that maps each vertex to the set of edges that start at this vertex. Now, you defined the type of the vertex to be String. And when there are multiple vertices that should be identified by the same String, then the particular Graph (which should be a Tree) may become inconsistent.

    Even more suggestive: You want to create a tree that looks like this:

    tree.getNode("A").addChild("TRUE");
    tree.getNode("B").addChild("TRUE");
    

    This would mean that the node that corresponds to the String "TRUE" would have two parents, namely the node "A" and the node "B". (The actual situation in your case is different, but I did not analyze the parsing in detail - in any case, the message "Tree must not already contain child D2=D3" indicates that it is "this kind of problem".

    The solution for that is rather simple, although it may be a bit inconvenient: You have to make sure that there may be multiple vertices with the same String, which are still considered as being different vertices. In particular: The equals method of these vertices must return false when the objects should actually be distinct vertices.

    This can be achieved by introducing a simple Vertex class. I sketched this here, and it seems to work, but again: I did not analyze what this parser is doing there, and you should carefully check whether there are still places where the vertices are assumed to be Strings and that now should be changed to use Vertex objects instead (and the code could need a cleanup, by the way). However, it should show the basic approach for solving this:

    package stackoverflow;
    
    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Container;
    import java.awt.Dimension;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.GridLayout;
    import java.awt.Shape;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.event.ItemEvent;
    import java.awt.event.ItemListener;
    import java.awt.geom.Ellipse2D;
    import java.awt.geom.Point2D;
    import java.util.Arrays;
    import java.util.Collection;
    import java.util.HashSet;
    import java.util.Map;
    import java.util.Set;
    
    import javax.swing.BorderFactory;
    import javax.swing.JApplet;
    import javax.swing.JButton;
    import javax.swing.JComboBox;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JToggleButton;
    
    import org.apache.commons.collections15.Factory;
    import org.apache.commons.collections15.Transformer;
    import org.apache.commons.collections15.functors.ConstantTransformer;
    
    import edu.uci.ics.jung.algorithms.layout.FRLayout;
    import edu.uci.ics.jung.algorithms.layout.PolarPoint;
    import edu.uci.ics.jung.algorithms.layout.RadialTreeLayout;
    import edu.uci.ics.jung.algorithms.layout.TreeLayout;
    import edu.uci.ics.jung.graph.DelegateForest;
    import edu.uci.ics.jung.graph.DelegateTree;
    import edu.uci.ics.jung.graph.DirectedGraph;
    import edu.uci.ics.jung.graph.DirectedSparseMultigraph;
    import edu.uci.ics.jung.graph.Forest;
    import edu.uci.ics.jung.graph.Graph;
    import edu.uci.ics.jung.graph.Tree;
    import edu.uci.ics.jung.visualization.GraphZoomScrollPane;
    import edu.uci.ics.jung.visualization.Layer;
    import edu.uci.ics.jung.visualization.VisualizationServer;
    import edu.uci.ics.jung.visualization.VisualizationViewer;
    import edu.uci.ics.jung.visualization.control.CrossoverScalingControl;
    import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse;
    import edu.uci.ics.jung.visualization.control.ModalGraphMouse;
    import edu.uci.ics.jung.visualization.control.ScalingControl;
    import edu.uci.ics.jung.visualization.decorators.EdgeShape;
    import edu.uci.ics.jung.visualization.decorators.EllipseVertexShapeTransformer;
    import edu.uci.ics.jung.visualization.decorators.ToStringLabeller;
    import edu.uci.ics.jung.visualization.subLayout.TreeCollapser;
    
    class Vertex
    {
        private static int IDCOUNTER = 0;
        private final String name;
        private final int id;
    
        Vertex(String name)
        {
            this.name = name;
            this.id = IDCOUNTER++;
        }
    
        String getName()
        {
            return name;
        }
    
        @Override
        public String toString()
        {
            return name;
        }
    
        @Override
        public int hashCode()
        {
            final int prime = 31;
            int result = 1;
            result = prime * result + id;
            result = prime * result + ((name == null) ? 0 : name.hashCode());
            return result;
        }
    
        @Override
        public boolean equals(Object obj)
        {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            Vertex other = (Vertex) obj;
            if (id != other.id)
                return false;
            if (name == null)
            {
                if (other.name != null)
                    return false;
            }
            else if (!name.equals(other.name))
                return false;
            return true;
        }
    
    }
    
    
    @SuppressWarnings("serial")
    public class JUNGTree extends JApplet
    {
    
        /**
         * the graph
         */
        Forest<Vertex, Integer> graph;
    
        Factory<DirectedGraph<Vertex, Integer>> graphFactory =
            new Factory<DirectedGraph<Vertex, Integer>>()
            {
    
                public DirectedGraph<Vertex, Integer> create()
                {
                    return new DirectedSparseMultigraph<Vertex, Integer>();
                }
            };
    
        Factory<Tree<Vertex, Integer>> treeFactory =
            new Factory<Tree<Vertex, Integer>>()
            {
    
                public Tree<Vertex, Integer> create()
                {
                    return new DelegateTree<Vertex, Integer>(graphFactory);
                }
            };
    
        Factory<Integer> edgeFactory = new Factory<Integer>()
        {
            int i = 0;
    
            public Integer create()
            {
                return i++;
            }
        };
    
        Factory<Vertex> vertexFactory = new Factory<Vertex>()
        {
            int i = 0;
    
            public Vertex create()
            {
                return new Vertex("V" + i++);
            }
        };
    
        /**
         * the visual component and renderer for the graph
         */
        VisualizationViewer<Vertex, Integer> vv;
    
        VisualizationServer.Paintable rings;
    
        String root;
    
        TreeLayout<Vertex, Integer> layout;
        @SuppressWarnings("unchecked")
        FRLayout layout1;
    
        TreeCollapser collapser;
    
        RadialTreeLayout<Vertex, Integer> radialLayout;
    
        @SuppressWarnings("unchecked")
        public JUNGTree()
        {
    
            // create a simple graph for the demo
            graph = new DelegateForest<Vertex, Integer>();
    
            createTree();
    
            layout = new TreeLayout<Vertex, Integer>(graph);
            collapser = new TreeCollapser();
    
            radialLayout = new RadialTreeLayout<Vertex, Integer>(graph);
            radialLayout.setSize(new Dimension(600, 600));
            vv =
                new VisualizationViewer<Vertex, Integer>(layout, new Dimension(600,
                    600));
            vv.setBackground(Color.white);
            vv.getRenderContext().setEdgeShapeTransformer(new EdgeShape.Line());
            vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller());
            vv.getRenderContext().setVertexShapeTransformer(
                new ClusterVertexShapeFunction());
            // add a listener for ToolTips
            vv.setVertexToolTipTransformer(new ToStringLabeller());
            vv.getRenderContext().setArrowFillPaintTransformer(
                new ConstantTransformer(Color.lightGray));
            rings = new Rings();
    
            Container content = getContentPane();
            final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv);
            content.add(panel);
    
            final DefaultModalGraphMouse graphMouse = new DefaultModalGraphMouse();
    
            vv.setGraphMouse(graphMouse);
    
            JComboBox modeBox = graphMouse.getModeComboBox();
            modeBox.addItemListener(graphMouse.getModeListener());
            graphMouse.setMode(ModalGraphMouse.Mode.TRANSFORMING);
    
            final ScalingControl scaler = new CrossoverScalingControl();
    
            JButton plus = new JButton("+");
            plus.addActionListener(new ActionListener()
            {
                public void actionPerformed(ActionEvent e)
                {
                    scaler.scale(vv, 1.1f, vv.getCenter());
                }
            });
            JButton minus = new JButton("-");
            minus.addActionListener(new ActionListener()
            {
                public void actionPerformed(ActionEvent e)
                {
                    scaler.scale(vv, 1 / 1.1f, vv.getCenter());
                }
            });
    
            JToggleButton radial = new JToggleButton("Radial");
            radial.addItemListener(new ItemListener()
            {
    
                public void itemStateChanged(ItemEvent e)
                {
                    if (e.getStateChange() == ItemEvent.SELECTED)
                    {
                        // layout.setRadial(true);
                        vv.setGraphLayout(radialLayout);
                        vv.getRenderContext().getMultiLayerTransformer()
                            .setToIdentity();
                        vv.addPreRenderPaintable(rings);
                    }
                    else
                    {
                        // layout.setRadial(false);
                        vv.setGraphLayout(layout);
                        vv.getRenderContext().getMultiLayerTransformer()
                            .setToIdentity();
                        vv.removePreRenderPaintable(rings);
                    }
                    vv.repaint();
                }
            });
    
            JButton collapse = new JButton("Collapse");
            collapse.addActionListener(new ActionListener()
            {
    
                public void actionPerformed(ActionEvent e)
                {
                    Collection picked =
                        new HashSet(vv.getPickedVertexState().getPicked());
                    if (picked.size() == 1)
                    {
                        Object root = picked.iterator().next();
                        Forest inGraph = (Forest) layout.getGraph();
    
                        try
                        {
                            collapser.collapse(vv.getGraphLayout(), inGraph, root);
                        }
                        catch (InstantiationException e1)
                        {
                            // TODO Auto-generated catch block
                            e1.printStackTrace();
                        }
                        catch (IllegalAccessException e1)
                        {
                            // TODO Auto-generated catch block
                            e1.printStackTrace();
                        }
    
                        vv.getPickedVertexState().clear();
                        vv.repaint();
                    }
                }
            });
    
            JButton expand = new JButton("Expand");
            expand.addActionListener(new ActionListener()
            {
    
                public void actionPerformed(ActionEvent e)
                {
                    Collection picked = vv.getPickedVertexState().getPicked();
                    for (Object v : picked)
                    {
                        if (v instanceof Forest)
                        {
                            Forest inGraph = (Forest) layout.getGraph();
                            collapser.expand(inGraph, (Forest) v);
                        }
                        vv.getPickedVertexState().clear();
                        vv.repaint();
                    }
                }
            });
    
            JPanel scaleGrid = new JPanel(new GridLayout(1, 0));
            scaleGrid.setBorder(BorderFactory.createTitledBorder("Zoom"));
    
            JPanel controls = new JPanel();
            scaleGrid.add(plus);
            scaleGrid.add(minus);
            controls.add(radial);
            controls.add(scaleGrid);
            controls.add(modeBox);
            controls.add(collapse);
            controls.add(expand);
            content.add(controls, BorderLayout.SOUTH);
        }
    
        class Rings implements VisualizationServer.Paintable
        {
    
            Collection<Double> depths;
    
            public Rings()
            {
                depths = getDepths();
            }
    
            private Collection<Double> getDepths()
            {
                Set<Double> depths = new HashSet<Double>();
                Map<Vertex, PolarPoint> polarLocations =
                    radialLayout.getPolarLocations();
                for (Vertex v : graph.getVertices())
                {
                    PolarPoint pp = polarLocations.get(v);
                    depths.add(pp.getRadius());
                }
                return depths;
            }
    
            public void paint(Graphics g)
            {
                g.setColor(Color.lightGray);
    
                Graphics2D g2d = (Graphics2D) g;
                Point2D center = radialLayout.getCenter();
    
                Ellipse2D ellipse = new Ellipse2D.Double();
                for (double d : depths)
                {
                    ellipse.setFrameFromDiagonal(center.getX() - d, center.getY()
                        - d, center.getX() + d, center.getY() + d);
                    Shape shape =
                        vv.getRenderContext().getMultiLayerTransformer()
                            .getTransformer(Layer.LAYOUT).transform(ellipse);
                    g2d.draw(shape);
                }
            }
    
            public boolean useTransform()
            {
                return true;
            }
        }
    
        /**
         * create Tree
         */
        private void createTree()
        {
    
            /*
             * INPUT 1
             */
            //String text = "=IF(A2=1;0;IF(D2=D3;IF(C2=1;TRUE;FALSE);4))";
    
            /*
             * INPUT 2
             */
            String text = "=IF(A2=1;0;IF(D2=D3;IF(C2=1;TRUE;FALSE);TRUE))";
    
            text.toUpperCase();
    
            // START
            String[] operandStrings = text.substring(1, text.length()).split("[;()]+");
    
            Vertex[] operands = new Vertex[operandStrings.length];
            for (int i=0; i<operandStrings.length; i++)
            {
                operands[i] = new Vertex(operandStrings[i]);
            }
    
            System.out.println(Arrays.toString(operands));
            int numIfs = operands.length / 3; // actually (operands.length - 1) / 3
                                              // but int division makes it the same
            Vertex[] nodes = new Vertex[numIfs]; // stores the nodes (test strings)
            int[] operandNos = new int[numIfs]; // stores the number of operands the
                                                // if currently has
            int nodesIndex = -1; // the index of the if node currently parsed
            for (Vertex s : operands)
            {
                if (s.getName().equals("IF"))
                {
                    // new if found -> increase position in the "stack" (nodes)
                    operandNos[++nodesIndex] = 0;
                }
                else
                {
                    // addVertex(s);
                    graph.addVertex(s);
                    switch (operandNos[nodesIndex]++)
                    {
                        case 0:
                            // first operand = node name
                            nodes[nodesIndex] = s;
                            break;
                        case 1:
                            // second operand found -> add edge
                            graph.addEdge(edgeFactory.create(), s,
                                nodes[nodesIndex]);
                            break;
                        case 2:
                            // last operand found -> add edge and go back
                            do
                            {
                                graph.addEdge(edgeFactory.create(), s,
                                    nodes[nodesIndex]);
                                s = nodes[nodesIndex--];
                            } while (nodesIndex >= 0
                                && operandNos[nodesIndex]++ == 2);
                            if (nodesIndex >= 0)
                            {
                                // was not the last operand of the IF
                                graph.addEdge(edgeFactory.create(), s,
                                    nodes[nodesIndex]);
                            }
                    }
                }
            }
            // END
    
        }
    
        class ClusterVertexShapeFunction<V> extends
            EllipseVertexShapeTransformer<V>
        {
    
            ClusterVertexShapeFunction()
            {
                setSizeTransformer(new ClusterVertexSizeFunction<V>(20));
            }
    
            @SuppressWarnings("unchecked")
            @Override
            public Shape transform(V v)
            {
                if (v instanceof Graph)
                {
                    int size = ((Graph) v).getVertexCount();
                    if (size < 8)
                    {
                        int sides = Math.max(size, 3);
                        return factory.getRegularPolygon(v, sides);
                    }
                    else
                    {
                        return factory.getRegularStar(v, size);
                    }
                }
                return super.transform(v);
            }
        }
    
        /**
         * A demo class that will make vertices larger if they represent a collapsed
         * collection of original vertices
         * 
         * @author Tom Nelson
         * 
         * @param <V>
         */
        class ClusterVertexSizeFunction<V> implements Transformer<V, Integer>
        {
            int size;
    
            public ClusterVertexSizeFunction(Integer size)
            {
                this.size = size;
            }
    
            public Integer transform(V v)
            {
                if (v instanceof Graph)
                {
                    return 30;
                }
                return size;
            }
        }
    
        /**
         * a driver for this demo
         */
        public static void main(String[] args)
        {
            JFrame frame = new JFrame();
            Container content = frame.getContentPane();
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
            content.add(new JUNGTree());
            frame.pack();
            frame.setVisible(true);
        }
    }