1 package com.github.davidmoten.xuml;
2
3 import java.awt.Color;
4 import java.awt.Dimension;
5 import java.awt.EventQueue;
6 import java.awt.Graphics;
7 import java.awt.GridLayout;
8 import java.awt.Shape;
9 import java.awt.print.PrinterException;
10 import java.io.File;
11 import java.io.IOException;
12 import java.io.InputStream;
13 import java.util.List;
14
15 import javax.swing.JFileChooser;
16 import javax.swing.JFrame;
17 import javax.swing.JMenu;
18 import javax.swing.JMenuBar;
19 import javax.swing.JMenuItem;
20 import javax.swing.JPanel;
21 import javax.swing.filechooser.FileFilter;
22
23 import java.awt.Rectangle;
24
25 import com.google.common.base.Function;
26
27 import edu.uci.ics.jung.algorithms.layout.FRLayout;
28 import edu.uci.ics.jung.algorithms.layout.Layout;
29 import edu.uci.ics.jung.graph.DirectedSparseGraph;
30 import edu.uci.ics.jung.graph.Graph;
31 import edu.uci.ics.jung.visualization.VisualizationViewer;
32 import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse;
33 import edu.uci.ics.jung.visualization.control.ModalGraphMouse;
34 import edu.uci.ics.jung.visualization.decorators.EdgeShape;
35 import edu.uci.ics.jung.visualization.renderers.Renderer;
36 import xuml.tools.miuml.metamodel.jaxb.Class;
37 import xuml.tools.miuml.metamodel.jaxb.ModeledDomain;
38 import xuml.tools.model.compiler.Util;
39
40 public class StateDiagramViewer {
41
42 private final JFrame frame = new JFrame();
43 private final JPanel vvContainer = createVvContainer();
44 private volatile JMenu classMenu;
45
46 private final static ThreadLocal<DrawingState> state = new ThreadLocal<>();
47
48 private void open(ModeledDomain domain) {
49 if (classMenu == null)
50 throw new RuntimeException("must call start() before calling open(domain)");
51 classMenu.removeAll();
52 List<Class> classes = Util.getClasses(domain);
53 classes.stream().forEach(cls -> {
54 JMenuItem m = new JMenuItem(cls.getName());
55 m.setEnabled(cls.getLifecycle() != null);
56 m.addActionListener(evt -> show(cls));
57 classMenu.add(m);
58 });
59
60 classes.stream().filter(cls -> cls.getLifecycle() != null).limit(1)
61 .forEach(cls -> show(cls));
62 }
63
64 public void show(Class c) {
65 System.out.println("showing " + c.getName());
66 Graph<String, Edge> g = new DirectedSparseGraph<String, Edge>();
67 if (c.getLifecycle() != null) {
68 c.getLifecycle().getState().stream().forEach(state -> g.addVertex(state.getName()));
69 c.getLifecycle().getTransition().stream().forEach(transition -> {
70 String fromState = transition.getState();
71 String toState = transition.getDestination();
72 String eventName = c.getLifecycle().getEvent().stream().map(ev -> ev.getValue())
73 .filter(event -> event.getID().equals(transition.getEventID()))
74 .map(event -> event.getName()).findAny().get();
75 g.addEdge(new Edge(eventName), fromState, toState);
76 });
77 }
78 if (!g.getVertices().isEmpty()) {
79 VisualizationViewer<String, Edge> vv = createVisualizationViewer(g);
80 EventQueue.invokeLater(() -> {
81 vvContainer.removeAll();
82 vvContainer.add(vv);
83 vvContainer.setFocusable(true);
84 vvContainer.repaint();
85 vvContainer.revalidate();
86 System.out.println("added vv");
87 frame.setTitle(c.getName());
88 });
89 }
90 }
91
92 private void start() {
93 EventQueue.invokeLater(() -> {
94
95 JMenuBar menuBar = new JMenuBar();
96
97 frame.setJMenuBar(menuBar);
98
99 JMenu fileMenu = new JMenu("File");
100 classMenu = new JMenu("Class");
101 JMenu editMenu = new JMenu("Edit");
102 menuBar.add(fileMenu);
103 menuBar.add(editMenu);
104 menuBar.add(classMenu);
105 JMenuItem openMenuItem = new JMenuItem("Open");
106 JMenuItem printMenuItem = new JMenuItem("Print...");
107 JMenuItem saveAsImageMenuItem = new JMenuItem("Save as PNG...");
108 JMenuItem exitMenuItem = new JMenuItem("Exit");
109 fileMenu.add(openMenuItem);
110 fileMenu.add(printMenuItem);
111 fileMenu.add(saveAsImageMenuItem);
112 fileMenu.add(exitMenuItem);
113 openMenuItem.addActionListener(System.out::println);
114 exitMenuItem.addActionListener(e -> System.exit(0));
115 printMenuItem.addActionListener(e -> print());
116 saveAsImageMenuItem.addActionListener(e -> saveAsImage());
117
118 frame.getContentPane().add(vvContainer);
119 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
120 frame.setSize(new Dimension(1000, 800));
121 frame.setLocationRelativeTo(null);
122 frame.setVisible(true);
123 });
124
125 }
126
127 private void saveAsImage() {
128 JFileChooser fc = new JFileChooser();
129 fc.addChoosableFileFilter(new ImageFilter());
130 int returnVal = fc.showSaveDialog(vvContainer);
131 if (returnVal == JFileChooser.APPROVE_OPTION) {
132 File file = fc.getSelectedFile();
133 if (!file.getName().toUpperCase().endsWith(".PNG"))
134 file = new File(file.getAbsolutePath() + ".png");
135 Color bg = vvContainer.getBackground();
136 try {
137 vvContainer.setPreferredSize(vvContainer.getSize());
138 vvContainer.setBackground(Color.white);
139 Panels.saveImage(vvContainer, file);
140 } catch (RuntimeException e1) {
141 e1.printStackTrace();
142 } finally {
143 vvContainer.setBackground(bg);
144 }
145 }
146 }
147
148 private void print() {
149 Color bg = vvContainer.getBackground();
150 try {
151 vvContainer.setPreferredSize(vvContainer.getSize());
152 vvContainer.setBackground(Color.white);
153 Panels.print(vvContainer);
154 } catch (PrinterException e1) {
155 e1.printStackTrace();
156 } finally {
157 vvContainer.setBackground(bg);
158 }
159 }
160
161 private static JPanel createVvContainer() {
162 JPanel panel = new JPanel();
163 panel.setPreferredSize(new Dimension(800, 600));
164
165 panel.setLayout(new GridLayout(0, 1));
166 return panel;
167 }
168
169 public static final class Edge {
170 final String name;
171
172 Edge(String name) {
173 this.name = name;
174 }
175
176 public static Edge of(String name) {
177 return new Edge(name);
178 }
179 }
180
181 private static class DrawingState {
182
183 }
184
185 private static VisualizationViewer<String, Edge> createVisualizationViewer(
186 Graph<String, Edge> graph) {
187 FRLayout<String, Edge> layout = new FRLayout<String, Edge>(graph, new Dimension(800, 600));
188 while (!layout.done())
189 layout.step();
190
191 VisualizationViewer<String, Edge> vv = new VisualizationViewer<String, Edge>(layout,
192 new Dimension(800, 600)) {
193
194 private static final long serialVersionUID = -3546358007558213875L;
195
196 @Override
197 public void paintComponents(Graphics g) {
198 try {
199 state.set(new DrawingState());
200 super.paintComponents(g);
201 } finally {
202 state.remove();
203 }
204 }
205
206 };
207 vv.setBackground(Color.white);
208 vv.getRenderContext().setVertexLabelTransformer(s -> s);
209 vv.getRenderContext().setVertexFillPaintTransformer(vertex -> vertex.equals("Created")
210 ? Color.decode("#B5D9E6") : Color.decode("#FFF1BC"));
211 vv.getRenderContext().setVertexDrawPaintTransformer(vertex -> Color.black);
212 vv.getRenderContext().setVertexShapeTransformer(createVertexShapeTransformer(layout));
213 vv.getRenderer().getVertexLabelRenderer().setPosition(Renderer.VertexLabel.Position.CNTR);
214 vv.getRenderContext().setEdgeLabelTransformer(edge -> " " + edge.name + " ");
215 vv.getRenderContext().setEdgeShapeTransformer(EdgeShape.quadCurve(graph));
216 vv.getRenderer().setEdgeLabelRenderer(new MyEdgeLabelRenderer<>(-10, Color.white));
217
218
219
220 final DefaultModalGraphMouse<String, Number> graphMouse = new DefaultModalGraphMouse<String, Number>();
221 vv.setGraphMouse(graphMouse);
222 graphMouse.setMode(ModalGraphMouse.Mode.PICKING);
223 return vv;
224 }
225
226 private static Function<String, Shape> createVertexShapeTransformer(
227 Layout<String, Edge> layout) {
228 return vertex -> {
229 int w = 150;
230 int margin = 5;
231 int ascent = 12;
232 int descent = 5;
233 Shape shape = new Rectangle(-w / 2 - margin, -ascent - margin, w + 2 * margin,
234 (ascent + descent) + 2 * margin);
235 return shape;
236 };
237 }
238
239 private static class ImageFilter extends FileFilter {
240
241
242 @Override
243 public boolean accept(File f) {
244 if (f.isDirectory()) {
245 return true;
246 }
247 return f.getName().toUpperCase().endsWith(".PNG");
248 }
249
250
251 @Override
252 public String getDescription() {
253 return "PNG files";
254 }
255 }
256
257 public void start(String resource, String domainName) {
258 start(StateDiagramViewer.class.getResourceAsStream(resource), domainName);
259 }
260
261 public void start(InputStream input, String domainName) {
262 try (InputStream is = input) {
263 start();
264 ModeledDomain domain = Util.getModeledDomain(is, domainName);
265 open(domain);
266 } catch (IOException e) {
267 throw new RuntimeException(e);
268 }
269 }
270
271 }