我创建了以下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)
令人困惑的是,字符串被正确解析并且可以被程序正确使用,如:
[IF, A2=1, 0, IF, D2=D3, IF, C2=1, TRUE, FALSE, TRUE]
这是我的程序版本,可以在评论中使用input 2
(&#34;破坏&#34;输入)运行:
@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);
}
}
使用输入2执行代码时,我收到发布的错误。任何建议为什么会这样?我的猜测是字符串是相同的,即使这有点荒谬,因为树的叶子应该是唯一的。那么,如何处理这个问题?
感谢您的回答!
答案 0 :(得分:2)
问题是同一个顶点应该出现多次。
人们可以在解释细节方面走得很远,但只是提出这个想法:想象一下,JUNG中的Graph在内部被维护为一堆Map
。例如,Map<V, Set<E>>
将每个顶点映射到从该顶点开始的边集。现在,您将顶点的类型定义为String
。当有多个顶点应该由相同的String
标识时,那么特定的图形(应该是树)可能会变得不一致。
更具启发性:您想要创建一个如下所示的树:
tree.getNode("A").addChild("TRUE");
tree.getNode("B").addChild("TRUE");
这意味着对应于字符串"TRUE"
的节点将具有两个父节点,即节点"A"
和节点"B"
。 (你的案例中的实际情况不同,但我没有详细分析解析 - 无论如何,消息&#34;树必须不包含子D2 = D3&#34; 表示这是&#34;这种问题&#34;。
对此的解决方案相当简单,虽然可能有点不方便:您必须确保可能有多个具有相同String的顶点,这些顶点仍然被认为是不同的顶点。特别是:当对象实际上应该是不同的顶点时,这些顶点的equals
方法必须返回false
。
这可以通过引入一个简单的Vertex
类来实现。我在这里草拟了它,它似乎有效,但是又一次:我做了不分析这个解析器在那里做了什么,你应该仔细检查是否还有假定顶点的地方{ {1}}现在应该更改为使用String
对象(顺便说一句,代码可能需要清理)。但是,它应该显示解决这个问题的基本方法:
Vertex