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.tree;
29  
30  import y.algo.Trees;
31  import y.base.DataProvider;
32  import y.base.Edge;
33  import y.base.Graph;
34  import y.base.Node;
35  import y.base.NodeMap;
36  import y.layout.LayoutGraph;
37  import y.layout.Layouter;
38  import y.layout.tree.AbstractRotatableNodePlacer;
39  import y.layout.tree.AbstractRotatableNodePlacer.Matrix;
40  import y.layout.tree.AbstractRotatableNodePlacer.RootAlignment;
41  import y.layout.tree.BusPlacer;
42  import y.layout.tree.DefaultNodePlacer;
43  import y.layout.tree.DelegatingNodePlacer;
44  import y.layout.tree.DoubleLinePlacer;
45  import y.layout.tree.GenericTreeLayouter;
46  import y.layout.tree.LayeredNodePlacer;
47  import y.layout.tree.NodePlacer;
48  import y.layout.tree.SimpleNodePlacer;
49  import y.util.DataProviderAdapter;
50  
51  /**
52   * This class demonstrates the configuration for the {@link y.layout.tree.GenericTreeLayouter}.
53   * There are several implementations that offer more or less complex configurations and compositions
54   * of NodePlacers.
55   * <p>
56   * The configurations can be used as follows:
57   * <code>
58   * TreeLayoutConfiguration.PLAYOFFS.layout( new GenericTreeLayouter(), graph );
59   * </code>
60   *
61   * @see <a href="http://docs.yworks.com/yfiles/doc/api/index.html#/dguide/cls_GenericTreeLayouter" target="_blank">Section Generic Tree Layout</a> in the yFiles for Java Developer's Guide
62   **/
63  public abstract class TreeLayoutConfiguration implements Layouter {
64  
65    /**
66     * Very basic configuration that creates a "default" layered tree with orthogonal style
67     * and the root alignment set to center.
68     */
69    public static final TreeLayoutConfiguration LAYERED_TREE = new TreeLayoutConfiguration() {
70      protected void prepare() {
71        super.prepare();
72        LayeredNodePlacer layeredNodePlacer = new LayeredNodePlacer();
73        layeredNodePlacer.setRootAlignment( RootAlignment.CENTER );
74        layeredNodePlacer.setRoutingStyle( LayeredNodePlacer.ORTHOGONAL_STYLE );
75        setNodePlacers( Trees.getRoot( graph ), layeredNodePlacer );
76      }
77    };
78  
79    /**
80     * Very basic configuration that creates a rotated layered tree.
81     */
82    public static final TreeLayoutConfiguration LAYERED_TREE_90 = new TreeLayoutConfiguration() {
83      protected void prepare() {
84        super.prepare();
85        LayeredNodePlacer layeredNodePlacer = new LayeredNodePlacer( Matrix.ROT90, Matrix.ROT90 );
86        layeredNodePlacer.setRootAlignment( RootAlignment.CENTER );
87        layeredNodePlacer.setRoutingStyle( LayeredNodePlacer.ORTHOGONAL_STYLE );
88        setNodePlacers( Trees.getRoot( graph ), layeredNodePlacer );
89      }
90    };
91  
92    /**
93     * Very basic configuration with custom spacing.
94     */
95    public static final TreeLayoutConfiguration DOUBLE_LINE = new TreeLayoutConfiguration() {
96      protected void prepare() {
97        super.prepare();
98        DoubleLinePlacer nodePlacer = new DoubleLinePlacer();
99        nodePlacer.setSpacing( 20 );
100       layouter.setDefaultNodePlacer( nodePlacer );
101     }
102   };
103 
104   /**
105    * Configuration that demonstrates how to use the DelegatingNodePlacer.
106    */
107   public static final TreeLayoutConfiguration DEFAULT_DELEGATING = new TreeLayoutConfiguration() {
108     protected void prepare() {
109       super.prepare();
110 
111       Node root = Trees.getRoot( graph );
112 
113       //First layer
114       SimpleNodePlacer placerNorth = new SimpleNodePlacer( Matrix.ROT180 );
115       placerNorth.setRootAlignment( RootAlignment.CENTER );
116 
117       SimpleNodePlacer placerSouth = new SimpleNodePlacer();
118       placerSouth.setRootAlignment( RootAlignment.CENTER );
119 
120       setNodePlacer( root, new DelegatingNodePlacer( Matrix.DEFAULT, placerNorth, placerSouth ) );
121 
122       //Second layer
123       int upperCount = root.outDegree() / 2;
124 
125       graph = ( LayoutGraph ) root.getGraph();
126       int counter = 0;
127       for ( Edge edge = root.firstOutEdge(); edge != null; edge = edge.nextOutEdge() ) {
128         Node child = edge.target();
129 
130         if ( counter < upperCount ) {
131           setNodePlacers( child, placerNorth );
132         } else {
133           setNodePlacers( child, placerSouth );
134         }
135         counter++;
136       }
137     }
138   };
139 
140   /**
141    * Configuration that can be used to layout *binary trees*. It generates something like a "playoff tree".
142    * This configuration uses DelegatingNodePlacers for the first two layers.
143    */
144   public static final TreeLayoutConfiguration PLAYOFFS_DOUBLE = new TreeLayoutConfiguration() {
145     protected void prepare() {
146       super.prepare();
147 
148       //Root
149       Node root = Trees.getRoot( graph );
150 
151       SimpleNodePlacer placerNorth = new SimpleNodePlacer( Matrix.MIR_HOR );
152       placerNorth.setRootAlignment( RootAlignment.MEDIAN );
153 
154       SimpleNodePlacer placerSouth = new SimpleNodePlacer();
155       placerSouth.setRootAlignment( RootAlignment.MEDIAN );
156 
157       DelegatingNodePlacer rootPlacer = new DelegatingNodePlacer( Matrix.DEFAULT, placerNorth, placerSouth );
158       setNodePlacer( root, rootPlacer );
159 
160       if ( root.outDegree() != 2 ) {
161         throw new IllegalStateException( "May only be used with a binary tree." );
162       }
163 
164       //2nd layer
165       Node upperChild = root.firstOutEdge().target();
166       Node lowerChild = root.firstOutEdge().nextOutEdge().target();
167 
168       SimpleNodePlacer placerLeft = new SimpleNodePlacer( Matrix.ROT90 );
169       placerLeft.setRootAlignment( RootAlignment.MEDIAN );
170 
171       SimpleNodePlacer placerRight = new SimpleNodePlacer( Matrix.ROT270 );
172       placerRight.setRootAlignment( RootAlignment.MEDIAN );
173 
174       DelegatingNodePlacer placer2ndLayer = new DelegatingNodePlacer( Matrix.ROT180, placerLeft, placerRight );
175       setNodePlacer( upperChild, placer2ndLayer );
176       setNodePlacer( lowerChild, placer2ndLayer );
177 
178       //3rd layer+
179       LayeredNodePlacer leftPlacer = new LayeredNodePlacer( Matrix.ROT90, Matrix.ROT90 );
180       leftPlacer.setRootAlignment( RootAlignment.MEDIAN );
181       leftPlacer.setRoutingStyle( LayeredNodePlacer.ORTHOGONAL_STYLE );
182 
183       LayeredNodePlacer rightPlacer = new LayeredNodePlacer( Matrix.ROT270, Matrix.ROT270 );
184       rightPlacer.setRootAlignment( RootAlignment.MEDIAN );
185       rightPlacer.setRoutingStyle( LayeredNodePlacer.ORTHOGONAL_STYLE );
186 
187       if ( upperChild.outDegree() != 2 ) {
188         throw new IllegalStateException( "May only be used with a binary tree." );
189       }
190       if ( lowerChild.outDegree() != 2 ) {
191         throw new IllegalStateException( "May only be used with a binary tree." );
192       }
193 
194       Node upperLeft = upperChild.firstOutEdge().target();
195       Node upperRight = upperChild.firstOutEdge().nextOutEdge().target();
196       setNodePlacers( upperLeft, leftPlacer );
197       setNodePlacers( upperRight, rightPlacer );
198 
199       Node lowerLeft = lowerChild.firstOutEdge().target();
200       Node lowerRight = lowerChild.firstOutEdge().nextOutEdge().target();
201       setNodePlacers( lowerLeft, leftPlacer );
202       setNodePlacers( lowerRight, rightPlacer );
203     }
204   };
205 
206   /**
207    * Configuration that can be used to layout *binary trees*. It generates something like a "playoff tree".
208    */
209   public static final TreeLayoutConfiguration PLAYOFFS = new TreeLayoutConfiguration() {
210     protected void prepare() {
211       super.prepare();
212 
213       //Root
214       Node root = Trees.getRoot( graph );
215 
216       LayeredNodePlacer placerLeft = new LayeredNodePlacer( Matrix.ROT270, Matrix.ROT270 );
217       placerLeft.setRootAlignment( RootAlignment.MEDIAN );
218       placerLeft.setRoutingStyle( LayeredNodePlacer.ORTHOGONAL_STYLE );
219 
220       LayeredNodePlacer placerRight = new LayeredNodePlacer( Matrix.ROT90, Matrix.ROT90 );
221       placerRight.setRootAlignment( RootAlignment.MEDIAN );
222       placerRight.setRoutingStyle( LayeredNodePlacer.ORTHOGONAL_STYLE );
223 
224       DelegatingNodePlacer rootPlacer = new DelegatingNodePlacer( Matrix.DEFAULT, placerLeft, placerRight );
225       setNodePlacer( root, rootPlacer );
226 
227       if ( root.outDegree() != 2 ) {
228         throw new IllegalStateException( "May only be used with a binary tree." );
229       }
230 
231       //2nd layer
232       Node firstChild = root.firstOutEdge().target();
233       Node secondChild = root.firstOutEdge().nextOutEdge().target();
234 
235       setNodePlacers( firstChild, placerLeft );
236       setNodePlacers( secondChild, placerRight );
237     }
238   };
239 
240   /**
241    * A special bus configuration that uses
242    */
243   public static final TreeLayoutConfiguration BUS = new TreeLayoutConfiguration() {
244     protected void prepare() {
245       super.prepare();
246 
247       Node root = Trees.getRoot( graph );
248       setNodePlacer( root, new BusPlacer() );
249 
250       DoubleLinePlacer northDouble = new DoubleLinePlacer( Matrix.ROT180 );
251       DoubleLinePlacer southDouble = new DoubleLinePlacer();
252 
253       SimpleNodePlacer north = new SimpleNodePlacer( Matrix.ROT180 );
254       north.setRootAlignment( RootAlignment.CENTER );
255       SimpleNodePlacer south = new SimpleNodePlacer();
256       south.setRootAlignment( RootAlignment.CENTER );
257 
258       int upperCount = root.outDegree() / 2;
259 
260       graph = ( LayoutGraph ) root.getGraph();
261       int counter = 0;
262       for ( Edge edge = root.firstOutEdge(); edge != null; edge = edge.nextOutEdge() ) {
263         Node child = edge.target();
264 
265         if ( counter < upperCount ) {
266           setNodePlacer( child, north );
267           setNodePlacerForChildren( child, northDouble );
268         } else {
269           setNodePlacer( child, south );
270           setNodePlacerForChildren( child, southDouble );
271         }
272         counter++;
273       }
274     }
275   };
276 
277 
278   protected LayoutGraph graph;
279   protected GenericTreeLayouter layouter;
280 
281   protected NodeMap nodePlacerMap;
282 
283   protected TreeLayoutConfiguration() {
284   }
285 
286   protected void setNodePlacer( Node node, NodePlacer nodePlacer ) {
287     nodePlacerMap.set( node, nodePlacer );
288   }
289 
290   protected void setNodePlacers( Node root, NodePlacer nodePlacer ) {
291     setNodePlacer( root, nodePlacer );
292     setNodePlacerForChildren( root, nodePlacer );
293   }
294 
295   protected void setNodePlacerForChildren( Node root, NodePlacer nodePlacer ) {
296     for ( Edge edge = root.firstOutEdge(); edge != null; edge = edge.nextOutEdge() ) {
297       Node child = edge.target();
298       setNodePlacers( child, nodePlacer );
299     }
300   }
301 
302   public final void layout( GenericTreeLayouter layouter, LayoutGraph graph ) {
303     configure( layouter, graph );
304     try {
305       layouter.doLayout( this.graph );
306     } finally {
307       cleanUp( this.graph );
308     }
309 
310     this.layouter = null;
311     this.graph = null;
312   }
313 
314   protected void prepare() {
315     nodePlacerMap = graph.createNodeMap();
316 
317     graph.addDataProvider( GenericTreeLayouter.NODE_PLACER_DPKEY, nodePlacerMap );
318     graph.addDataProvider( GenericTreeLayouter.CHILD_COMPARATOR_DPKEY, new ChildEdgeComparatorProvider() );
319   }
320 
321   /**
322    * Do not forget to clean up afterwards {@link #cleanUp(y.base.Graph)}
323    * @param layouter
324    */
325   public void configure( GenericTreeLayouter layouter, LayoutGraph graph ) {
326     this.graph = graph;
327     this.layouter = layouter;
328     prepare();
329   }
330 
331   public static void cleanUp( Graph graph ) {
332     DataProvider nodePlacerMap = graph.getDataProvider( GenericTreeLayouter.NODE_PLACER_DPKEY );
333     if ( nodePlacerMap != null && nodePlacerMap instanceof NodeMap ) {
334       graph.disposeNodeMap( ( NodeMap ) nodePlacerMap );
335     }
336     graph.removeDataProvider( GenericTreeLayouter.NODE_PLACER_DPKEY );
337     graph.removeDataProvider( GenericTreeLayouter.CHILD_COMPARATOR_DPKEY );
338   }
339 
340   public boolean canLayout( LayoutGraph graph ) {
341     return true;
342   }
343 
344   public void doLayout( LayoutGraph graph ) {
345     layout( new GenericTreeLayouter(), graph );
346   }
347 
348   class ChildEdgeComparatorProvider extends DataProviderAdapter {
349     public Object get( Object dataHolder ) {
350       NodePlacer placer = ( NodePlacer ) nodePlacerMap.get( dataHolder );
351       if ( placer instanceof AbstractRotatableNodePlacer ) {
352         return ( ( AbstractRotatableNodePlacer ) placer ).createComparator();
353       }
354       if ( placer instanceof DefaultNodePlacer ) {
355         return ( ( DefaultNodePlacer ) placer ).createComparator();
356       }
357       return null;
358     }
359   }
360 }
361