package jadex.rules.tools.reteviewer; import jadex.commons.ChangeEvent; import jadex.commons.IBreakpointPanel; import jadex.commons.IChangeListener; import jadex.commons.ICommand; import jadex.commons.ISteppable; import jadex.rules.rulesystem.Activation; import jadex.rules.rulesystem.IAgendaListener; import jadex.rules.rulesystem.IRule; import jadex.rules.rulesystem.RuleSystem; import jadex.rules.rulesystem.rete.RetePatternMatcherFunctionality; import jadex.rules.rulesystem.rete.RetePatternMatcherState; import jadex.rules.rulesystem.rete.nodes.AlphaNode; import jadex.rules.rulesystem.rete.nodes.BetaNode; import jadex.rules.rulesystem.rete.nodes.INode; import jadex.rules.rulesystem.rete.nodes.IObjectConsumerNode; import jadex.rules.rulesystem.rete.nodes.IObjectSourceNode; import jadex.rules.rulesystem.rete.nodes.ITupleConsumerNode; import jadex.rules.rulesystem.rete.nodes.ITupleSourceNode; import jadex.rules.rulesystem.rete.nodes.InitialFactNode; import jadex.rules.rulesystem.rete.nodes.LeftInputAdapterNode; import jadex.rules.rulesystem.rete.nodes.NotNode; import jadex.rules.rulesystem.rete.nodes.ReteMemory; import jadex.rules.rulesystem.rete.nodes.ReteNode; import jadex.rules.rulesystem.rete.nodes.RightInputAdapterNode; import jadex.rules.rulesystem.rete.nodes.SplitNode; import jadex.rules.rulesystem.rete.nodes.TerminalNode; import jadex.rules.rulesystem.rete.nodes.TestNode; import jadex.rules.rulesystem.rete.nodes.TypeNode; import java.awt.BasicStroke; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JPanel; import javax.swing.JSplitPane; import javax.swing.JTabbedPane; import javax.swing.SwingUtilities; import org.apache.commons.collections15.Transformer; import org.apache.commons.collections15.functors.ConstantTransformer; import edu.uci.ics.jung.graph.DirectedSparseGraph; import edu.uci.ics.jung.visualization.VisualizationViewer; import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse; import edu.uci.ics.jung.visualization.control.GraphMouseListener; import edu.uci.ics.jung.visualization.control.ModalGraphMouse; import edu.uci.ics.jung.visualization.decorators.EdgeShape; import edu.uci.ics.jung.visualization.decorators.ToStringLabeller; /** * Can be used to visualize a rete network. */ public class RetePanel extends JPanel { //-------- constants -------- /** The name of the node details panel. */ public static final String NODE_DETAILS_NAME = "Node Details"; /** The name of the agenda panel. */ public static final String AGENDA_NAME = "Agenda"; //-------- attributes -------- /** Flag if node text should be shown. */ protected boolean showtxt; /** The rulebase panel. */ protected IBreakpointPanel rulebasepanel; /** The agenda panel. */ protected AgendaPanel ap; /** The info panels on the right hand side. */ protected JTabbedPane infopanels; /** The currently removed nodes and edges. */ protected List remnodes; protected List remedges; /** The graph. */ protected DirectedSparseGraph g; /** The viewer. */ protected VisualizationViewer vv; /** The layout. */ protected ReteLayout layout; /** The agenda listener. */ protected IAgendaListener agendalistener; /** The rule system. */ protected RuleSystem system; //-------- constructors -------- /** * Create a new rete panel. * Set steppable to null for panel without breakpoints and step mode. */ public RetePanel(final RuleSystem system, final ISteppable steppable, final IBreakpointPanel rulebasepanel) { this.rulebasepanel = rulebasepanel; this.infopanels = new JTabbedPane(); this.system = system; final ReteNode root = ((RetePatternMatcherFunctionality)system.getMatcherFunctionality()).getReteNode(); final ReteMemory mem = ((RetePatternMatcherState)system.getMatcherState()).getReteMemory(); this.g = new DirectedSparseGraph(); buildGraph(g, root); hideMarkedNodes(Collections.EMPTY_SET); final NodePanel np = new NodePanel(null, mem, system.getState()); this.layout = new ReteLayout(g); this.vv = new VisualizationViewer(layout, new Dimension(600,600)); vv.getRenderContext().setEdgeShapeTransformer(new EdgeShape.Line()); vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller()); vv.getRenderContext().setVertexLabelTransformer(new Transformer() { public Object transform(Object arg0) { return showtxt? arg0.toString(): null; } }); vv.getRenderContext().setVertexStrokeTransformer(new Transformer() { public Object transform(Object node) { Collection coll = ((INode)node).getNodeMemory(mem); return (coll==null || coll.isEmpty()) ? new BasicStroke(1) : new BasicStroke(2); } }); vv.getRenderContext().setVertexFillPaintTransformer(new Transformer() { public Object transform(Object node) { Color fg; if(node instanceof ReteNode) fg = Color.WHITE; else if(node instanceof TypeNode) fg = Color.PINK; else if(node instanceof AlphaNode) fg = Color.RED; else if(node instanceof LeftInputAdapterNode) fg = Color.ORANGE; else if(node instanceof RightInputAdapterNode) fg = Color.ORANGE; else if(node instanceof NotNode) fg = Color.YELLOW; else if(node instanceof BetaNode) fg = Color.GREEN; else if(node instanceof SplitNode) fg = Color.MAGENTA; else if(node instanceof TerminalNode) fg = Color.CYAN; else if(node instanceof TestNode) fg = new Color(0, 255, 100); else if(node instanceof InitialFactNode) fg = new Color(255, 100, 0); else fg = Color.GRAY; return fg; // Collection coll = ((INode)node).getNodeMemory(mem); // return (coll==null || coll.isEmpty()) // ? fg : new LinearGradientPaint(0,0, 5,5, new float[]{0.8f, 1f}, new Color[]{fg,Color.BLACK}); } }); vv.getRenderContext().setEdgeDrawPaintTransformer(new Transformer() { public Object transform(Object edge) { if(edge instanceof ReteEdge) { if(((ReteEdge)edge).isTuple()) { return Color.GREEN; } else { return Color.RED; } } else { return Color.GRAY; } } }); vv.setVertexToolTipTransformer(new ToStringLabeller()); vv.getRenderContext().setArrowFillPaintTransformer(new ConstantTransformer(Color.lightGray)); DefaultModalGraphMouse gm = new DefaultModalGraphMouse(); gm.setMode(ModalGraphMouse.Mode.PICKING); vv.setGraphMouse(gm); vv.addGraphMouseListener(new GraphMouseListener() { public void graphClicked(Object arg0, MouseEvent arg1) { //System.out.println("clicked: "+arg0+" "+arg1); if(arg0 instanceof INode) { np.setNode((INode)arg0); infopanels.setSelectedComponent(getInfoPanel(NODE_DETAILS_NAME)); } } public void graphPressed(Object arg0, MouseEvent arg1) { // System.out.println("pressed: "+arg0+" "+arg1); } public void graphReleased(Object arg0, MouseEvent arg1) { // System.out.println("released: "+arg0+" "+arg1); } }); final JButton hidenodes = new JButton("Show Subgraph"); hidenodes.setMargin(new Insets(2,4,2,4)); hidenodes.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { Set subgraph = new HashSet(vv.getPickedVertexState().getPicked()); hideMarkedNodes(subgraph); layout.graphChanged(); vv.repaint(); } }); /*final JButton showdesc = new JButton("Show text"); showdesc.setMargin(new Insets(0,0,0,0)); showdesc.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { showtxt = !showtxt; vv.repaint(); showdesc.setText(showtxt? "Hide text": "Show text"); } });*/ JButton refresh = new JButton("Refresh"); // refresh.setMargin(new Insets(0,0,0,0)); refresh.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { np.refresh(); vv.repaint(); } }); // JButton remrule = new JButton("Remove rule"); // remrule.setMargin(new Insets(0,0,0,0)); /*remrule.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { Set subgraph = new HashSet(vv.getPickedVertexState().getPicked()); if(subgraph.isEmpty()) { System.out.println("Please select a terminal node(s) first."); } else { for(Iterator it=subgraph.iterator(); it.hasNext(); ) { INode node = (INode)it.next(); if(node instanceof TerminalNode) { TerminalNode tn = (TerminalNode)node; root.removeRule(tn.getRule()); buildGraph(g, root); } } vv.repaint(); } } });*/ int mw = (int)hidenodes.getMinimumSize().getWidth(); int pw = (int)hidenodes.getPreferredSize().getWidth(); int mh = (int)hidenodes.getMinimumSize().getHeight(); int ph = (int)hidenodes.getPreferredSize().getHeight(); hidenodes.setMinimumSize(new Dimension(mw, mh)); hidenodes.setPreferredSize(new Dimension(pw, ph)); // showdesc.setMinimumSize(new Dimension(mw, mh)); // showdesc.setPreferredSize(new Dimension(pw, ph)); refresh.setMinimumSize(new Dimension(mw, mh)); refresh.setPreferredSize(new Dimension(pw, ph)); // remrule.setMinimumSize(new Dimension(mw, mh)); // remrule.setPreferredSize(new Dimension(pw, ph)); JPanel buts = new JPanel(new GridBagLayout()); // buts.setBorder(BorderFactory.createTitledBorder(BorderFactory.createBevelBorder( // BevelBorder.LOWERED), "Node Settings")); buts.add(hidenodes, new GridBagConstraints(0,0,1,1,1,0,GridBagConstraints.EAST, GridBagConstraints.NONE,new Insets(2,4,4,2),0,0)); // buts.add(showdesc, new GridBagConstraints(1,0,1,1,0,0,GridBagConstraints.EAST, // GridBagConstraints.NONE,new Insets(2,4,4,2),0,0)); buts.add(refresh, new GridBagConstraints(0,1,1,1,1,0,GridBagConstraints.EAST, GridBagConstraints.NONE,new Insets(2,4,4,2),0,0)); // buts.add(remrule, new GridBagConstraints(0,3,1,1,0,0,GridBagConstraints.EAST, // GridBagConstraints.NONE,new Insets(2,4,4,2),0,0)); JPanel tmp = new JPanel(new BorderLayout()); tmp.add(np, BorderLayout.CENTER); tmp.add(buts, BorderLayout.SOUTH); this.ap = new AgendaPanel(system.getAgenda()); JPanel tmp3 = new JPanel(new BorderLayout()); final JCheckBox followact = new JCheckBox("Follow activation", true); followact.setToolTipText("Follow the selected activation by displaying the rule."); tmp3.add(ap, BorderLayout.CENTER); // tmp3.add(followact, BorderLayout.SOUTH); // The step action final JButton step = new JButton("Step"); if(steppable!=null) { JPanel tmp4 = new JPanel(new GridBagLayout()); step.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { steppable.doStep(); } }); step.setEnabled(steppable.isStepmode() && !system.getAgenda().isEmpty()); final JCheckBox stepmode = new JCheckBox("Step Mode", steppable.isStepmode()); stepmode.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { steppable.setStepmode(stepmode.isSelected()); step.setEnabled(steppable.isStepmode() && !system.getAgenda().isEmpty()); } }); steppable.addBreakpointCommand(new ICommand() { public void execute(Object args) { SwingUtilities.invokeLater(new Runnable() { public void run() { stepmode.setSelected(steppable.isStepmode()); step.setEnabled(steppable.isStepmode() && !system.getAgenda().isEmpty()); } }); } }); int row = 0; int col = 0; tmp4.add(followact, new GridBagConstraints(col, row++, 2, 1, 1,0, GridBagConstraints.LINE_END, GridBagConstraints.NONE, new Insets(1,1,1,1), 0,0)); tmp4.add(stepmode, new GridBagConstraints(col++, row, 1, 1, 1,0, GridBagConstraints.LINE_END, GridBagConstraints.NONE, new Insets(1,1,1,1), 0,0)); tmp4.add(step, new GridBagConstraints(col, row++, GridBagConstraints.REMAINDER, 1, 0,0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(1,1,1,1), 0,0)); tmp3.add(tmp4, BorderLayout.SOUTH); } addInfoPanel(NODE_DETAILS_NAME, tmp); addInfoPanel(AGENDA_NAME, tmp3); JSplitPane sp2 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); sp2.setOneTouchExpandable(true); sp2.setResizeWeight(1); sp2.setDividerLocation(500); sp2.add(vv); sp2.add(infopanels); this.setLayout(new BorderLayout()); this.add(sp2, BorderLayout.CENTER); vv.addComponentListener(new ComponentAdapter() { public void componentResized(ComponentEvent e) { layout.setSize(vv.getSize()); } }); rulebasepanel.addBreakpointListener(new IChangeListener() { public void changeOccurred(ChangeEvent e) { String[] rules = (String[])rulebasepanel.getSelectedBreakpoints(); // System.out.println("Selected: "+SUtil.arrayToString(rules)+" "+e); if(rules!=null && rules.length>0) { // Show all nodes if(remnodes!=null) showHiddenNodes(); // Build subgraph of selected rules (terminal nodes). Set subgraph = new HashSet(); for(int i=0; i<rules.length; i++) { INode node = root.getTerminalNode(system.getRulebase().getRule(rules[i])); subgraph.add(node); } // Hide all but selected nodes hideMarkedNodes(subgraph); if(np.getNode()==null || !subgraph.contains(np.getNode())) np.setNode(root.getTerminalNode(system.getRulebase().getRule(rules[0]))); } // Show no nodes at all. else { hideMarkedNodes(Collections.EMPTY_SET); } layout.graphChanged(); vv.repaint(); } }); this.agendalistener = new IAgendaListener() { boolean invoked = false; Activation next = null; public void agendaChanged() { synchronized(RetePanel.this) { next = system.getAgenda().getNextActivation(); } if(!invoked) { invoked = true; SwingUtilities.invokeLater(new Runnable() { public void run() { invoked = false; // System.out.println("Next activation: "+act); if(followact.isSelected()) { IRule rule; synchronized(RetePanel.this) { rule = next!=null ? next.getRule() : null; } if(rule!=null) { rulebasepanel.setSelectedBreakpoints(new String[]{rule.getName()}); } else { rulebasepanel.setSelectedBreakpoints(new String[0]); } } if(steppable!=null) { step.setEnabled(steppable.isStepmode() && !system.getAgenda().isEmpty()); } } }); } } }; system.getAgenda().addAgendaListener(agendalistener); followact.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if(followact.isSelected()) { Activation act = system.getAgenda().getNextActivation(); if(act!=null && followact.isSelected()) { rulebasepanel.setSelectedBreakpoints(new String[]{act.getRule().getName()}); } } else { rulebasepanel.setSelectedBreakpoints(new String[0]); } } }); if(followact.isSelected()) { Activation act = system.getAgenda().getNextActivation(); if(act!=null && followact.isSelected()) { rulebasepanel.setSelectedBreakpoints(new String[]{act.getRule().getName()}); } } /*ap.getActivationsList().addListSelectionListener(new ListSelectionListener() { public void valueChanged(ListSelectionEvent e) { if(!e.getValueIsAdjusting()) { Activation act = (Activation)ap.getActivationsList().getSelectedValue(); System.out.println("Sel activation: "+act); if(act!=null)// && followact) { rulebasepanel.getList().setSelectedValue(act.getRule(), true); } } } });*/ } /** * Dispose the panel and remove any listeners. */ public void dispose() { ap.dispose(); rulebasepanel.dispose(); system.getAgenda().removeAgendaListener(agendalistener); } /** * Build (or rebuild) the graph from the root node. * @param g The graph. * @param root The root node. */ protected void buildGraph(DirectedSparseGraph g, ReteNode root) { Object[] edges = g.getEdges().toArray(); for(int i=0; i<edges.length; i++) { g.removeEdge(edges[i]); } Object[] vers= g.getVertices().toArray(); for(int i=0; i<vers.length; i++) { g.removeVertex(vers[i]); } List todo = new ArrayList(); todo.add(root); for(int i=0; i<todo.size(); i++) { INode current = (INode)todo.get(i); g.addVertex(current); if(current instanceof IObjectSourceNode) { IObjectConsumerNode[] cons = ((IObjectSourceNode)current).getObjectConsumers(); for(int j=0; cons!=null && j<cons.length; j++) { if(!todo.contains(cons[j])) todo.add(cons[j]); g.addEdge(new ReteEdge(current, cons[j], false), current, cons[j]); } } if(current instanceof ITupleSourceNode) { ITupleConsumerNode[] cons = ((ITupleSourceNode)current).getTupleConsumers(); for(int j=0; cons!=null && j<cons.length; j++) { if(!todo.contains(cons[j])) todo.add(cons[j]); g.addEdge(new ReteEdge(current, cons[j], true), current, cons[j]); } } } } /** * Add an info panel (right hand side). */ public void addInfoPanel(String name, JComponent panel) { boolean found = false; for(int i=0; !found && i<infopanels.getTabCount(); i++) if(name.equals(infopanels.getTitleAt(i))) // claas - removed - @since 1.6 //infopanels.setTabComponentAt(i, panel); infopanels.setComponentAt(i, panel); if(!found) infopanels.addTab(name, panel); infopanels.setSelectedComponent(panel); } /** * Get an info panel with a given name. */ public JComponent getInfoPanel(String name) { JComponent ret = null; for(int i=0; ret==null && i<infopanels.getTabCount(); i++) if(name.equals(infopanels.getTitleAt(i))) ret = (JComponent)infopanels.getComponentAt(i); return ret; } /** * Hide the marked nodes. * @param subgraph The subgraph. */ protected void hideMarkedNodes(Set subgraph) { if(remnodes!=null || remedges!=null) showHiddenNodes(); // Build subgraph of connected nodes to retain. Set upnodes = new HashSet(subgraph); Set downnodes = new HashSet(subgraph); while(!upnodes.isEmpty() || !downnodes.isEmpty()) { if(upnodes.isEmpty()) { Object node = downnodes.iterator().next(); downnodes.remove(node); if(node instanceof IObjectSourceNode) { Object[] nexts = ((IObjectSourceNode)node).getObjectConsumers(); for(int j=0; j<nexts.length; j++) { if(!subgraph.contains(nexts[j])) { subgraph.add(nexts[j]); downnodes.add(nexts[j]); } } } if(node instanceof ITupleSourceNode) { Object[] nexts = ((ITupleSourceNode)node).getTupleConsumers(); for(int j=0; j<nexts.length; j++) { if(!subgraph.contains(nexts[j])) { subgraph.add(nexts[j]); downnodes.add(nexts[j]); } } } } else { Object node = upnodes.iterator().next(); upnodes.remove(node); if(node instanceof IObjectConsumerNode) { Object next = ((IObjectConsumerNode)node).getObjectSource(); if(!subgraph.contains(next)) { subgraph.add(next); upnodes.add(next); } } if(node instanceof ITupleConsumerNode) { Object next = ((ITupleConsumerNode)node).getTupleSource(); if(!subgraph.contains(next)) { subgraph.add(next); upnodes.add(next); } } } } // Remove all nodes not in subgraph remnodes = new ArrayList(); remedges = new ArrayList(); Object[] nodes = g.getVertices().toArray(); for(int n=0; n<nodes.length; n++) { if(!subgraph.contains(nodes[n])) { Object[] edges = g.getInEdges(nodes[n]).toArray(); for(int i=0; i<edges.length; i++) { remedges.add(edges[i]); g.removeEdge(edges[i]); } edges = g.getOutEdges(nodes[n]).toArray(); for(int i=0; i<edges.length; i++) { remedges.add(edges[i]); g.removeEdge(edges[i]); } remnodes.add(nodes[n]); g.removeVertex(nodes[n]); } } } /** * Show the hidden nodes. */ public void showHiddenNodes() { for(int i=0; remnodes!=null && i<remnodes.size(); i++) { g.addVertex(remnodes.get(i)); } for(int i=0; remedges!=null && i<remedges.size(); i++) { ReteEdge edge = (ReteEdge)remedges.get(i); g.addEdge(edge, edge.getStart(), edge.getEnd()); } remnodes = null; remedges = null; } /** * Get the rulebase panel. * / public RulebasePanel getRulebasePanel() { return rulebasepanel; }*/ /** * Main for testing. * / public static void main(String[] args) { RuleSystem s = OAVBlockMetamodel.createReteSystem(); createReteFrame("Blocksworld Test", ((RetePatternMatcherFunctionality)s.getMatcherFunctionality()).getReteNode(), ((RetePatternMatcherState)s.getMatcherState()).getReteMemory(), new Object()); }*/ /** * Create a frame for a rete structure. * @param title The title for the frame. * @param rs The rule system. * @return The frame. * / public static JFrame createReteFrame(String title, RuleSystem rs, Object monitor) { return createReteFrame(title, rs, monitor, null); } /** * Create a frame for a rete structure. * @param title The title for the frame. * @param rs The rule system. * @return The frame. * / public static JFrame createReteFrame(String title, RuleSystem rs, Object monitor, ISteppable steppable) { JFrame f = new JFrame(title); f.getContentPane().setLayout(new BorderLayout()); RetePanel rp = new RetePanel(rs, monitor, steppable); rp.showHiddenNodes(); f.add("Center", rp); f.pack(); f.setVisible(true); return f; }*/ }