java.lang.IllegalArgumentException:树不能已经包含子

java.lang.IllegalArgumentException:树不能已经包含子,java,algorithm,jung,jung2,Java,Algorithm,Jung,Jung2,我创建了以下jung2可视化。我的代码将文本可视化为树。基本上我使用两个输入,一次一个: /* * 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;

我创建了以下
jung2
可视化。我的代码将文本可视化为树。基本上我使用两个输入,一次一个:

        /*
         * 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))";
我的问题是,第一个输入按预期工作,但当使用
input 2
时,我得到的错误是:最后一个元素
4
TRUE
不同:

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)
当使用输入2执行代码时,我得到了发布的错误。有什么建议可以解释这种情况吗?我猜字符串是一样的,尽管这有点可笑,因为树的叶子应该是独一无二的。因此,如何处理这个问题呢


谢谢你的回答

问题是同一个顶点应该出现多次

我们可以对细节进行深入的解释,但只是给出一个想法:假设JUNG中的图形在内部维护为一组
Map
s。例如,将每个顶点映射到从该顶点开始的边集的
贴图。现在,您将顶点的类型定义为
String
。当有多个顶点应该由相同的
字符串
标识时,特定的图形(应该是)可能会变得不一致

更具启发性的是:您希望创建一个如下所示的树:

tree.getNode("A").addChild("TRUE");
tree.getNode("B").addChild("TRUE");
这意味着与字符串
“TRUE”
对应的节点将有两个父节点,即节点
“A”
和节点
“B”
。(您案例中的实际情况有所不同,但我没有详细分析解析过程——在任何情况下,“Tree不得已包含child D2=D3”消息表示这是“此类问题”

解决方案相当简单,尽管可能有点不方便:您必须确保可能有多个具有相同字符串的顶点,这些顶点仍然被视为不同的顶点。特别是:当对象实际应为不同的顶点时,这些顶点的
equals
方法必须返回
false
不同的顶点

这可以通过引入一个简单的
Vertex
类来实现。我在这里画了一个草图,它似乎很有效,但再次强调:我确实分析了而不是解析器在那里做了什么,您应该仔细检查是否还有一些地方的顶点被假定为
字符串,现在应该进行更改使用
顶点
对象(顺便说一句,代码可能需要清理)。但是,它应该显示解决此问题的基本方法:

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);
    }
}
包堆栈溢出;
导入java.awt.BorderLayout;
导入java.awt.Color;
导入java.awt.Container;
导入java.awt.Dimension;
导入java.awt.Graphics;
导入java.awt.Graphics2D;
导入java.awt.GridLayout;
导入java.awt.Shape;
导入java.awt.event.ActionEvent;
导入java.awt.event.ActionListener;
导入java.awt.event.ItemEvent;
导入java.awt.event.ItemListener;
导入java.awt.geom.Ellipse2D;
导入java.awt.geom.Point2D;
导入java.util.array;
导入java.util.Collection;
导入java.util.HashSet;
导入java.util.Map;
导入java.util.Set;
导入javax.swing.BorderFactory;
导入javax.swing.JApplet;
导入javax.swing.JButton;
导入javax.swing.JComboBox;
导入javax.swing.JFrame;
导入javax.swing.JPanel;
导入javax.swing.JToggleButton;
导入org.apache.commons.collections15.Factory;
导入org.apache.commons.collections15.Transformer;
导入org.apache.commons.collections15.functors.constantttransformer;
导入edu.uci.ics.jung.algorithms.layout.FRLayout;
导入edu.uci.ics.jung.algorithms.layout.PolarPoint;
导入edu.uci.ics.jung.algorithms.layout.RadialTreeLayout;
导入edu.uci.ics.jung.algorithms.layout;
导入edu.uci.ics.jung.graph.DelegateForest;
导入edu.uci.ics.jung.graph.DelegateTree;
导入edu.uci.ics.jung.graph.DirectedGraph;
导入edu.uci.ics.jung.graph.DirectedSparseMultraph;
导入edu.uci.ics.jung.graph.Forest;
导入edu.uci.ics.jung.graph.graph;
导入edu.uci.ics.jung.graph.Tree;
导入edu.uci.ics.jung.visualization.GraphZoomScrollPane;
导入edu.uci.ics.jung.visualization.Layer;
导入edu.uci.ics.jung.visualization.VisualizationServer;
导入edu.uci.ics.jung.visualization.VisualizationViewer;
导入edu.uci.ics.jung.visualization.control.CrossoverScalingControl;
导入edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse;
导入edu.uci.ics.jung.visualization.control.ModalGraphMouse;
导入edu.uci.ics.jung.visualization.control.ScalingControl;
导入edu.uci.ics.jung.visualization.decorators.EdgeShape;
导入edu.uci.ics.jung.visualization.decorators.EllipseVertexShapeTransformer;
导入edu.uci.ics.jung.visualization.decorators.ToStringLabeller;
导入edu.uci.ics.jung.visualization.subLayout.TreeCollapser;
类顶点
{
私有静态int IDCOUNTER=0;
私有最终字符串名;
私有最终int id;
顶点(字符串名称)
{
this.name=名称;
this.id=IDCOUNTER++;
}
字符串getName()
{
返回名称;
}
@凌驾
公共字符串toString()
{
返回名称;
}
@凌驾
公共int hashCode()
{
最终整数素数=31;
int结果=1;
结果=素数*结果+id;
result=prime*result+((name==null)?0:name.hashCode();
返回结果;
}
@凌驾
公共布尔等于(对象obj)
{
if(this==obj)
返回true;
if(obj==null)
返回false;
如果(getClass()!=obj.getClass())
返回false;
顶点其他=(顶点)obj;
if(id!=其他.id)
返回false;
if(name==null)
{
if(other.name!=null)
返回false;
}
如果(!name.equals(other.name))
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);
    }
}