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