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