/* Copyright or (C) or Copr. GET / ENST, Telecom-Paris, Ludovic Apvrille * * ludovic.apvrille AT enst.fr * * This software is a computer program whose purpose is to allow the * edition of TURTLE analysis, design and deployment diagrams, to * allow the generation of RT-LOTOS or Java code from this diagram, * and at last to allow the analysis of formal validation traces * obtained from external tools, e.g. RTL from LAAS-CNRS and CADP * from INRIA Rhone-Alpes. * * This software is governed by the CeCILL license under French law and * abiding by the rules of distribution of free software. You can use, * modify and/ or redistribute the software under the terms of the CeCILL * license as circulated by CEA, CNRS and INRIA at the following URL * "http://www.cecill.info". * * As a counterpart to the access to the source code and rights to copy, * modify and redistribute granted by the license, users are provided only * with a limited warranty and the software's author, the holder of the * economic rights, and the successive licensors have only limited * liability. * * In this respect, the user's attention is drawn to the risks associated * with loading, using, modifying and/or developing or reproducing the * software by the user in light of its specific status of free software, * that may mean that it is complicated to manipulate, and that also * therefore means that it is reserved for developers and experienced * professionals having in-depth computer knowledge. Users are therefore * encouraged to load and test the software's suitability as regards their * requirements in conditions enabling the security of their systems and/or * data to be ensured and, more generally, to use and operate it in the * same conditions as regards security. * * The fact that you are presently reading this means that you have had * knowledge of the CeCILL license and that you accept its terms. */ package graph; import common.ConfigurationTTool; import common.SpecConfigTTool; import myutil.TraceManager; import org.graphstream.graph.implementations.AbstractEdge; import org.graphstream.graph.implementations.MultiGraph; import org.graphstream.graph.implementations.MultiNode; import org.graphstream.ui.view.Viewer; import org.graphstream.ui.view.ViewerListener; import org.graphstream.ui.view.ViewerPipe; import ui.file.PNGFilter; import ui.util.IconManager; import javax.imageio.ImageIO; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.image.BufferedImage; import java.io.File; import java.util.ArrayList; import java.util.logging.Logger; /** * Class AUTGraphDisplay * Creation : 01/12/2016 * * @version 1.0 01/12/2016 * * @author Ludovic APVRILLE */ public class AUTGraphDisplay implements MouseListener, ViewerListener, Runnable { protected AUTGraph graph; protected Viewer viewer; protected MultiGraph vGraph; protected boolean loop; protected MultiNode firstNode; protected ArrayList<AbstractEdge> edges; protected boolean exitOnClose = false; public static String STYLE_SHEET = "node {" + " fill-color: #B1CAF1; text-color: black; size: 11px, 11px;" + "} " + // "edge.defaultedge {" + // " shape: cubic-curve;" + // "}" + // "edge {shape: cubic-curve}" + "edge.external {" + " text-style: bold;" + "} " + "node.deadlock {" + " fill-color: red; text-color: white; size: 15px, 15px;" + "} " + "node.init {" + " fill-color: green; text-color: black; size: 15px, 15px;" + "} "; public AUTGraphDisplay(AUTGraph _graph, boolean _exitOnClose) { graph = _graph; exitOnClose = _exitOnClose; } public void display() { MultiNode node; AbstractEdge edge; Logger l0 = Logger.getLogger(""); try { if (l0 != null) { l0.removeHandler(l0.getHandlers()[0]); } } catch (Exception e) { } System.setProperty("org.graphstream.ui.renderer", "org.graphstream.ui.j2dviewer.J2DGraphRenderer"); vGraph = new MultiGraph("TTool graph"); if ((ConfigurationTTool.RGStyleSheet != null) && (ConfigurationTTool.RGStyleSheet.trim().length() > 0)) { TraceManager.addDev("Adding stylesheet:" + ConfigurationTTool.RGStyleSheet+ "\n\nvs default:" + STYLE_SHEET); vGraph.addAttribute("ui.stylesheet", ConfigurationTTool.RGStyleSheet ); } else { vGraph.addAttribute("ui.stylesheet", STYLE_SHEET); } //vGraph.addAttribute("layout.weight", 0.5); int cpt = 0; graph.computeStates(); for (AUTState state : graph.getStates()) { node = vGraph.addNode("" + state.id); node.addAttribute("ui.label", "" + state.id); if (state.getNbOutTransitions() == 0) { node.addAttribute("ui.class", "deadlock"); } if (cpt == 0) { node.addAttribute("ui.class", "init"); firstNode = node; } cpt++; } cpt = 0; edges = new ArrayList<AbstractEdge>(graph.getTransitions().size()); for (AUTTransition transition : graph.getTransitions()) { edge = vGraph.addEdge("" + cpt, "" + transition.origin, "" + transition.destination, true); /*TraceManager.addDev("Transition=" + transition.transition); String tmp = Conversion.replaceAllChar(transition.transition, '(', "$"); tmp = Conversion.replaceAllChar(tmp, ')', "$"); TraceManager.addDev("Transition=" + tmp);*/ edge.addAttribute("ui.label", transition.transition); edge.addAttribute("ui.class", "defaultedge"); edge.addAttribute("layout.weight", 0.4); if (!(transition.transition.startsWith("i("))) { edge.addAttribute("ui.class", "external"); } edges.add(edge); cpt++; } //viewer = vGraph.display(); //viewer = new Viewer(vGraph, Viewer.ThreadingModel.GRAPH_IN_SWING_THREAD); viewer = new Viewer(vGraph, Viewer.ThreadingModel.GRAPH_IN_ANOTHER_THREAD); //SwingUtilities.invokeLater(new InitializeApplication(viewer, vGraph)); viewer.enableAutoLayout(); //View vi = viewer.addDefaultView(true); viewer.setCloseFramePolicy(Viewer.CloseFramePolicy.CLOSE_VIEWER); BasicFrame bf = new BasicFrame(this, viewer, vGraph, graph, edges, exitOnClose); //vi.addMouseListener(this); loop = true; Thread t = new Thread(this); t.start(); } public void run() { ViewerPipe fromViewer = viewer.newViewerPipe(); fromViewer.addViewerListener(this); fromViewer.addSink(vGraph); // Then we need a loop to do our work and to wait for events. // In this loop we will need to call the // pump() method before each use of the graph to copy back events // that have already occurred in the viewer thread inside // our thread int cpt = 0; //TraceManager.addDev("Starting loop:" + cpt); while (loop) { try { //TraceManager.addDev("beg of loop:" + cpt); fromViewer.blockingPump(); // or fromViewer.blockingPump(); in the nightly builds } catch (Exception e) {//TraceManager.addDev("Exception in pump:" + e); } if (vGraph.hasAttribute("ui.viewClosed")) { TraceManager.addDev("View was closed"); loop = false; if (exitOnClose) { System.exit(1); } } /*else if (firstNode.hasAttribute("ui.clicked")) { TraceManager.addDev("Init node was clicked"); firstNode.removeAttribute("ui.clicked"); }*/ // here your simulation code. //TraceManager.addDev("End of loop" + cpt); //cpt ++; // You do not necessarily need to use a loop, this is only an example. // as long as you call pump() before using the graph. pump() is non // blocking. If you only use the loop to look at event, use blockingPump() // to avoid 100% CPU usage. The blockingPump() method is only available from // the nightly builds. } //viewPipe = null; } public void buttonPushed(String id) { TraceManager.addDev("Button pushed on node " + id); } public void buttonReleased(String id) { TraceManager.addDev("Button released on node " + id); } public void viewClosed(String id) { TraceManager.addDev("View closed and closed !"); loop = false; if (viewer != null) { viewer.close(); viewer.disableAutoLayout(); } viewer = null; vGraph.clear(); if (exitOnClose) { System.exit(1); } } public void displaySwing() { vGraph = new MultiGraph("mg"); vGraph.addAttribute("ui.stylesheet", STYLE_SHEET); viewer = new Viewer(vGraph, Viewer.ThreadingModel.GRAPH_IN_GUI_THREAD); SwingUtilities.invokeLater(new InitializeApplication(viewer, vGraph, graph)); } public void mousePressed(MouseEvent e) { TraceManager.addDev("Mouse pressed; # of clicks: " + e.getClickCount()); } public void mouseReleased(MouseEvent e) { TraceManager.addDev("Mouse released; # of clicks: " + e.getClickCount()); } public void mouseEntered(MouseEvent e) { TraceManager.addDev("Mouse entered"); } public void mouseExited(MouseEvent e) { TraceManager.addDev("Mouse exited"); } public void mouseClicked(MouseEvent e) { TraceManager.addDev("Mouse clicked (# of clicks: " + e.getClickCount() + ")"); } class InitializeApplication extends JFrame implements Runnable { private static final long serialVersionUID = -804177406404724792L; protected MultiGraph vGraph; protected Viewer viewer; protected AUTGraph graph; protected MultiNode firstNode; public InitializeApplication(Viewer viewer, MultiGraph vGraph, AUTGraph autgraph) { this.viewer = viewer; this.vGraph = vGraph; this.graph = autgraph; } public void run() { int cpt = 0; MultiNode node; AbstractEdge edge; for (AUTState state : graph.getStates()) { node = vGraph.addNode("" + state.id); node.addAttribute("ui.label", "" + state.id); if (state.getNbOutTransitions() == 0) { node.addAttribute("ui.class", "deadlock"); } if (cpt == 0) { node.addAttribute("ui.class", "init"); firstNode = node; } cpt++; } cpt = 0; for (AUTTransition transition : graph.getTransitions()) { edge = vGraph.addEdge("" + cpt, "" + transition.origin, "" + transition.destination, true); /*TraceManager.addDev("Transition=" + transition.transition); String tmp = Conversion.replaceAllChar(transition.transition, '(', "$"); tmp = Conversion.replaceAllChar(tmp, ')', "$"); TraceManager.addDev("Transition=" + tmp);*/ edge.addAttribute("ui.label", transition.transition); edge.addAttribute("ui.class", "defaultedge"); if (!(transition.transition.startsWith("i("))) { edge.addAttribute("ui.class", "external"); } cpt++; } viewer.enableAutoLayout(); add(viewer.addDefaultView(true), BorderLayout.CENTER); setDefaultCloseOperation(EXIT_ON_CLOSE); setSize(800, 600); setVisible(true); } } class BasicFrame extends JFrame implements ActionListener { protected MultiGraph vGraph; protected Viewer viewer; protected JPanel viewerPanel; protected AUTGraph graph; protected ArrayList<AbstractEdge> edges; protected JButton close; protected JButton screenshot; protected JCheckBox internalActions; protected JCheckBox readActions; protected JCheckBox higherQuality, antialiasing; protected JLabel help, info; protected boolean exitOnClose; private AUTGraphDisplay autD; public BasicFrame(AUTGraphDisplay autD, Viewer viewer, MultiGraph vGraph, AUTGraph autgraph, ArrayList<AbstractEdge> _edges, boolean _exitOnClose) { this.autD = autD; this.viewer = viewer; this.vGraph = vGraph; this.graph = autgraph; edges = _edges; exitOnClose = _exitOnClose; makeComponents(); if (exitOnClose) { setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); } } public void makeComponents() { viewerPanel = viewer.addDefaultView(false); add(viewerPanel, BorderLayout.CENTER); //add(viewer, BorderLayout.CENTER ); close = new JButton("Close", IconManager.imgic27); close.addActionListener(this); screenshot = new JButton("Save in png", IconManager.imgic28); screenshot.addActionListener(this); close.addActionListener(this); help = new JLabel("Zoom with PageUp/PageDown, move with cursor keys"); info = new JLabel("Graph: " + graph.getNbOfStates() + " states, " + graph.getNbOfTransitions() + " transitions"); internalActions = new JCheckBox("Display internal actions", true); internalActions.addActionListener(this); readActions = new JCheckBox("Display read/write actions", true); readActions.addActionListener(this); higherQuality = new JCheckBox("Higher drawing quality", false); higherQuality.addActionListener(this); antialiasing = new JCheckBox("Anti aliasing", false); antialiasing.addActionListener(this); JPanel jp01 = new JPanel(); GridBagLayout gridbag01 = new GridBagLayout(); GridBagConstraints c01 = new GridBagConstraints(); jp01.setLayout(gridbag01); jp01.setBorder(new javax.swing.border.TitledBorder("Options")); //c01.gridwidth = 1; c01.gridheight = 1; c01.weighty = 1.0; c01.weightx = 1.0; c01.fill = GridBagConstraints.HORIZONTAL; c01.gridwidth = GridBagConstraints.REMAINDER; //end row jp01.add(screenshot); jp01.add(internalActions); jp01.add(readActions); jp01.add(higherQuality); jp01.add(antialiasing); JPanel infoPanel = new JPanel(new BorderLayout()); JPanel labelPanel = new JPanel(new BorderLayout()); labelPanel.add(help, BorderLayout.EAST); labelPanel.add(info, BorderLayout.WEST); infoPanel.add(labelPanel, BorderLayout.NORTH); infoPanel.add(close, BorderLayout.SOUTH); infoPanel.add(jp01, BorderLayout.CENTER); add(infoPanel, BorderLayout.SOUTH); //setDefaultCloseOperation(EXIT_ON_CLOSE); setSize(1000, 700); setVisible(true); } public void actionPerformed(ActionEvent evt) { if (evt.getSource() == close) { closeFrame(); } else if (evt.getSource() == screenshot) { screenshot(); } else if (evt.getSource() == internalActions) { manageInternalActions(); } else if (evt.getSource() == readActions) { manageReadActions(); } else if (evt.getSource() == higherQuality) { manageHigherQuality(); } else if (evt.getSource() == antialiasing) { manageAntialiasing(); } } public void closeFrame() { if (autD != null) { autD.viewClosed("closed pushed"); } if (exitOnClose) { System.exit(1); } dispose(); } public void takeScreenshot(Component component, File file) { BufferedImage image = new BufferedImage( component.getWidth(), component.getHeight(), BufferedImage.TYPE_INT_RGB ); // call the Component's paint method, using // the Graphics object of the image. component.paint( image.getGraphics() ); // alternately use .printAll(..) try { // save captured image to PNG file ImageIO.write(image, "png", file); } catch (Exception e) { } } public void screenshot() { TraceManager.addDev("Screenshot"); JFileChooser jfcggraph; if (SpecConfigTTool.GGraphPath.length() > 0) { jfcggraph = new JFileChooser(SpecConfigTTool.GGraphPath); } else { jfcggraph = new JFileChooser(); } PNGFilter filter = new PNGFilter(); jfcggraph.setFileFilter(filter); int returnVal = jfcggraph.showDialog(this, "Graph capture (in png)"); if (returnVal != JFileChooser.APPROVE_OPTION) { return; } File pngFile = jfcggraph.getSelectedFile(); TraceManager.addDev("Making the screenshot in " + pngFile.getAbsolutePath()); //vGraph.addAttribute("ui.screenshot", pngFile.getAbsolutePath()); //vGraph.addAttribute("ui.screenshot", "/homes/apvrille/tmp/toto.png"); takeScreenshot(viewerPanel, pngFile); /*FileSinkImages pic = new FileSinkImages(OutputType.PNG, Resolutions.UXGA); //pic.setQuality(); //pic.setLayoutPolicy(LayoutPolicy.COMPUTED_FULLY_AT_NEW_IMAGE); pic.setLayoutPolicy(LayoutPolicy.COMPUTED_IN_LAYOUT_RUNNER); try { pic.writeAll(vGraph, pngFile.getAbsolutePath()); } catch (IOException e) { TraceManager.addDev("Capture could not be performed: " + e.getMessage()); }*/ //vGraph.addAttribute("ui.screenshot", "/tmp/toto.png"); TraceManager.addDev("Screenshot performed"); } public void manageInternalActions() { if (edges == null) { return; } int cpt = 0; for (AUTTransition transition : graph.getTransitions()) { if (transition.transition.startsWith("i(")) { if (internalActions.isSelected()) { edges.get(cpt).addAttribute("ui.label", transition.transition); } else { edges.get(cpt).addAttribute("ui.label", ""); } } cpt++; } } public void manageReadActions() { if (edges == null) { return; } int cpt = 0; for (AUTTransition transition : graph.getTransitions()) { if (transition.transition.contains("?")) { if (readActions.isSelected()) { edges.get(cpt).addAttribute("ui.label", transition.transition); } else { edges.get(cpt).addAttribute("ui.label", ""); } } cpt++; } } public void manageHigherQuality() { viewer.disableAutoLayout(); if (higherQuality.isSelected()) { vGraph.addAttribute("ui.quality"); } else { vGraph.removeAttribute("ui.quality"); } try { viewer.enableAutoLayout(); } catch (Exception e) { } } public void manageAntialiasing() { viewer.disableAutoLayout(); if (antialiasing.isSelected()) { vGraph.addAttribute("ui.antialias"); } else { vGraph.removeAttribute("ui.antialias"); } try { viewer.enableAutoLayout(); } catch (Exception e) { } } } // Basic Frame } // Main class