1   /****************************************************************************
2    * This demo file is part of yFiles for Java 2.14.
3    * Copyright (c) 2000-2017 by yWorks GmbH, Vor dem Kreuzberg 28,
4    * 72070 Tuebingen, Germany. All rights reserved.
5    * 
6    * yFiles demo files exhibit yFiles for Java functionalities. Any redistribution
7    * of demo files in source code or binary form, with or without
8    * modification, is not permitted.
9    * 
10   * Owners of a valid software license for a yFiles for Java version that this
11   * demo is shipped with are allowed to use the demo source code as basis
12   * for their own yFiles for Java powered applications. Use of such programs is
13   * governed by the rights and conditions as set out in the yFiles for Java
14   * license agreement.
15   * 
16   * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESS OR IMPLIED
17   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18   * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
19   * NO EVENT SHALL yWorks BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
21   * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23   * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24   * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26   *
27   ***************************************************************************/
28  package demo.layout.orthogonal;
29  
30  import demo.view.DemoBase;
31  import demo.view.DemoDefaults;
32  
33  import y.base.DataProvider;
34  import y.base.Edge;
35  import y.base.EdgeCursor;
36  import y.base.Node;
37  import y.layout.LayoutOrientation;
38  import y.layout.Layouter;
39  import y.layout.PortConstraintKeys;
40  import y.layout.orthogonal.DirectedOrthogonalLayouter;
41  import y.layout.router.EdgeGroupRouterStage;
42  import y.layout.router.polyline.EdgeRouter;
43  import y.layout.router.polyline.Grid;
44  import y.util.DataProviderAdapter;
45  import y.view.Arrow;
46  import y.view.BridgeCalculator;
47  import y.view.DefaultGraph2DRenderer;
48  import y.view.EdgeRealizer;
49  import y.view.Graph2D;
50  import y.view.LineType;
51  import y.view.NodeRealizer;
52  import y.view.PolyLineEdgeRealizer;
53  
54  import javax.swing.AbstractAction;
55  import javax.swing.JComboBox;
56  import javax.swing.JComponent;
57  import javax.swing.JList;
58  import javax.swing.JToolBar;
59  import javax.swing.ListCellRenderer;
60  import java.awt.Color;
61  import java.awt.Component;
62  import java.awt.Cursor;
63  import java.awt.Dimension;
64  import java.awt.Graphics;
65  import java.awt.Graphics2D;
66  import java.awt.EventQueue;
67  import java.awt.event.ActionEvent;
68  import java.awt.event.ItemEvent;
69  import java.awt.event.ItemListener;
70  import java.util.Locale;
71  
72  /**
73   * <p>
74   * This demo shows how {@link DirectedOrthogonalLayouter} and {@link EdgeRouter} can be used to
75   * nicely layout UML Class Diagrams in an orthogonal layout style.
76   * <p>
77   * Usually, there are different kind of relationships between the classes of an UML diagram.
78   * Some of the relationships are undirected (e.g. associations) while others are directed
79   * (e.g. generalizations and realizations). This demo arranges a diagram in a way that
80   * directed relationships point in a main layout direction(here bottom-to-top), while the
81   * undirected relationships will be arranged without such a direction constraint.
82   * Furthermore, all directed relationships of the same type sharing a common target node
83   * will be routed in a bus-like style. 
84   * <p>
85   * The toolbar of this demo offers four additional items:
86   * <ol>
87   * <li>A combobox that selects the type of relationship to be used: association (no arrow),
88   * generalization (arrow and solid line), and realization (arrow and dashed line).
89   * </li>
90   * <li>Layout button - to layout the diagram</li>
91   * <li>Layout From Sketch button - to layout the diagram, while obeying the layout of the given diagram</li>
92   * <li>Route Edges button - to route all edges of the diagram, while preserving the coordinates of the nodes</li>
93   * </ol>
94   *
95   * @see <a href="http://docs.yworks.com/yfiles/doc/api/index.html#/dguide/directed_orthogonal_layouter" target="_blank">Section Directed Orthogonal Layout</a> in the yFiles for Java Developer's Guide
96   * @see <a href="http://docs.yworks.com/yfiles/doc/api/index.html#/dguide/polyline_edge_router" target="_blank">Section Polyline Edge Routing</a> in the yFiles for Java Developer's Guide
97   */
98  public class UMLClassDiagramLayouterDemo extends DemoBase {
99  
100   DirectedOrthogonalLayouter doLayouter;
101   Layouter layouter, router;
102 
103   public UMLClassDiagramLayouterDemo() {
104     final Graph2D graph = view.getGraph2D();
105 
106     //configure default node realizer
107     NodeRealizer nr = graph.getDefaultNodeRealizer();
108     nr.setSize(80.0, 50.0);
109     nr.setLabelText("<html><b>Class</b><br><hr>doit():void");
110     nr.setFillColor(new Color(189, 185, 146));
111 
112     //activate grid mode
113     view.setGridMode(true);
114     view.setGridResolution(15.0);
115 
116     //activate bridge style painting of edge crossings
117     DefaultGraph2DRenderer r = (DefaultGraph2DRenderer) view.getGraph2DRenderer();
118     BridgeCalculator bc = new BridgeCalculator();
119     bc.setCrossingMode(BridgeCalculator.CROSSING_MODE_VERTICAL_CROSSES_HORIZONTAL);
120     r.setBridgeCalculator(bc);
121 
122     configureLayout();
123 
124     loadGraph(getClass(), "resource/classdiagram01.graphml");
125     DemoDefaults.applyRealizerDefaults(graph, false, false);
126   }
127 
128 
129   /**
130    * Configures layout algorithm and adds layout-specific data providers to the graph
131    */
132   private void configureLayout() {
133     final Graph2D graph = view.getGraph2D();
134 
135     doLayouter = new DirectedOrthogonalLayouter();
136     doLayouter.setLayoutOrientation(LayoutOrientation.BOTTOM_TO_TOP);
137     doLayouter.setGrid(30);
138     layouter = doLayouter;
139 
140     DataProvider directedDP = new DataProviderAdapter() {
141       public boolean getBool(Object obj) {
142         return graph.getRealizer((Edge) obj).getTargetArrow() != Arrow.NONE;
143       }
144     };
145     graph.addDataProvider(DirectedOrthogonalLayouter.DIRECTED_EDGE_DPKEY, directedDP);
146 
147     DataProvider targetGroupDP = new DataProviderAdapter() {
148       public Object get(Object obj) {
149         EdgeRealizer er = graph.getRealizer((Edge) obj);
150         if (er.getTargetArrow() == Arrow.NONE) {
151           return null;
152         } else {
153           return er.getLineType();
154         }
155       }
156     };
157     graph.addDataProvider(PortConstraintKeys.TARGET_GROUPID_KEY, targetGroupDP);
158 
159     EdgeRouter edgeRouter = new EdgeRouter();
160     edgeRouter.setGrid(new Grid(0, 0, 10));
161     edgeRouter.setReroutingEnabled(true);
162     router = edgeRouter;
163   }
164 
165   /**
166    * Run a layout algorithm
167    */
168   private void runLayout(Layouter layouter) {
169     Cursor oldCursor = view.getViewCursor();
170     try {
171       contentPane.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
172       view.applyLayoutAnimated(layouter);
173     } finally {
174       contentPane.setCursor(oldCursor);
175     }
176   }
177 
178   /**
179    * Add a layout button and a combobox for edge realizer selection to the ToolBar
180    */
181   protected JToolBar createToolBar() {
182     final JToolBar toolBar = super.createToolBar();
183 
184     toolBar.addSeparator();
185     toolBar.add(createEdgeRealizerComboBox());
186 
187     toolBar.addSeparator();
188     toolBar.add(createActionControl(new AbstractAction(
189             "Layout", SHARED_LAYOUT_ICON) {
190       public void actionPerformed(ActionEvent e) {
191         doLayouter.setUseSketchDrawing(false);
192         runLayout(doLayouter);
193       }
194     }));
195 
196     toolBar.add(createActionControl(new AbstractAction(
197             "Layout From Sketch", SHARED_LAYOUT_ICON) {
198       public void actionPerformed(ActionEvent e) {
199         doLayouter.setUseSketchDrawing(true);
200         runLayout(doLayouter);
201       }
202     }));
203 
204     toolBar.add(createActionControl(new AbstractAction(
205             "Route Edges", SHARED_LAYOUT_ICON) {
206       public void actionPerformed(ActionEvent e) {
207         runLayout(router);
208       }
209     }));
210 
211     return toolBar;
212   }
213 
214   JComboBox createEdgeRealizerComboBox() {
215     final EdgeRealizer association = new PolyLineEdgeRealizer();
216     final EdgeRealizer generalization = new PolyLineEdgeRealizer();
217     generalization.setTargetArrow(Arrow.WHITE_DELTA);
218     generalization.setLineType(LineType.LINE_2);
219     generalization.setLineColor(new Color(51, 51, 153));
220     final EdgeRealizer realization = new PolyLineEdgeRealizer();
221     realization.setReversedPathRenderingEnabled(true);
222     realization.setTargetArrow(Arrow.WHITE_DELTA);
223     realization.setLineType(LineType.DASHED_2);
224     realization.setLineColor(new Color(51, 51, 153));
225 
226     final Object[] items = {
227         association,
228         generalization,
229         realization
230     };
231 
232     final JComboBox box = new JComboBox(items);
233     box.setRenderer(new EdgeRealizerCellRenderer());
234     box.setMaximumSize(new Dimension(box.getMinimumSize().width, box.getMaximumSize().height));
235     box.addItemListener(new ItemListener() {
236       public void itemStateChanged(ItemEvent ev) {
237         if (ev.getStateChange() == ItemEvent.SELECTED) {
238           EdgeRealizer r = (EdgeRealizer) box.getSelectedItem();
239           if (r != null) {
240             for (EdgeCursor ec = view.getGraph2D().selectedEdges(); ec.ok(); ec.next()) {
241               EdgeRealizer ser = view.getGraph2D().getRealizer(ec.edge());
242               ser.setLineColor(r.getLineColor());
243               ser.setTargetArrow(r.getTargetArrow());
244               ser.setLineType(r.getLineType());
245             }
246             view.getGraph2D().setDefaultEdgeRealizer(r.createCopy());
247           }
248         }
249       }
250     });
251     box.setSelectedIndex(0);
252 
253     return box;
254   }
255 
256   static class EdgeRealizerCellRenderer extends JComponent implements ListCellRenderer {
257     private Graph2D graph;
258     private EdgeRealizer er;
259 
260     public EdgeRealizerCellRenderer() {
261       graph = new Graph2D();
262       Node s = graph.createNode(0.0, 12.0, 1.0, 1.0, "");
263       Node t = graph.createNode(60.0, 12.0, 1.0, 1.0, "");
264       graph.createEdge(s, t);
265     }
266 
267     public Component getListCellRendererComponent(
268         JList list,
269         Object value,
270         int index,
271         boolean isSelected,
272         boolean cellHasFocus) {
273 
274       setPreferredSize(new Dimension(60, 24));
275 
276       er = (EdgeRealizer) value;
277       graph.setRealizer(graph.firstEdge(), er);
278       return this;
279     }
280 
281     public void paint(Graphics g) {
282       Graphics2D gfx = (Graphics2D) g;
283       er.paint(gfx);
284     }
285   }
286 
287   public static void main(String[] args) {
288     EventQueue.invokeLater(new Runnable() {
289       public void run() {
290         Locale.setDefault(Locale.ENGLISH);
291         initLnF();
292         (new UMLClassDiagramLayouterDemo()).start();
293       }
294     });
295   }
296 }
297