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.module;
29  
30  import y.module.LayoutModule;
31  import y.module.YModule;
32  
33  import y.base.DataProvider;
34  import y.base.Edge;
35  import y.base.EdgeCursor;
36  import y.layout.ComponentLayouter;
37  import y.layout.CompositeLayoutStage;
38  import y.layout.LabelLayoutConstants;
39  import y.layout.LabelLayoutDataRefinement;
40  import y.layout.LabelLayoutTranslator;
41  import y.layout.LabelRanking;
42  import y.layout.OrientationLayouter;
43  import y.layout.PortConstraint;
44  import y.layout.PortConstraintKeys;
45  import y.layout.PreferredPlacementDescriptor;
46  import y.layout.grouping.FixedGroupLayoutStage;
47  import y.layout.grouping.GroupNodeHider;
48  import y.layout.hierarchic.BFSLayerer;
49  import y.layout.hierarchic.ClassicLayerSequencer;
50  import y.layout.hierarchic.HierarchicGroupLayouter;
51  import y.layout.hierarchic.HierarchicLayouter;
52  import y.layout.hierarchic.LayerSequencer;
53  import y.layout.labeling.GreedyMISLabeling;
54  import y.option.ConstraintManager;
55  import y.option.EnumOptionItem;
56  import y.option.OptionHandler;
57  import y.util.DataProviderAdapter;
58  import y.view.EdgeLabel;
59  import y.view.EdgeRealizer;
60  import y.view.Graph2D;
61  import y.view.Selections;
62  import y.view.SmartEdgeLabelModel;
63  import y.view.hierarchy.HierarchyManager;
64  
65  /**
66   * This module represents an interactive configurator and launcher for
67   * {@link y.layout.hierarchic.HierarchicLayouter}
68   * and {@link y.layout.hierarchic.HierarchicGroupLayouter}.
69   *
70   */
71  public class HierarchicLayoutModule extends LayoutModule {
72    //// Module 'Hierarchic Layout'
73    protected static final String MODULE_HIERARCHIC = "HIERARCHIC";
74    
75    //// Section 'Layout'
76    protected static final String SECTION_LAYOUT = "LAYOUT";
77    // Section 'Layout' items
78    protected static final String ITEM_MINIMAL_LAYER_DISTANCE = "MINIMAL_LAYER_DISTANCE";
79    protected static final String ITEM_MINIMAL_NODE_DISTANCE = "MINIMAL_NODE_DISTANCE";
80    protected static final String ITEM_MINIMAL_EDGE_DISTANCE = "MINIMAL_EDGE_DISTANCE";
81    protected static final String ITEM_MINIMAL_FIRST_SEGMENT_LENGTH = "MINIMAL_FIRST_SEGMENT_LENGTH";
82    protected static final String ITEM_MAXIMAL_DURATION = "MAXIMAL_DURATION";
83    protected static final String ITEM_ORIENTATION = "ORIENTATION";
84    protected static final String VALUE_TOP_TO_BOTTOM = "TOP_TO_BOTTOM";
85    protected static final String VALUE_LEFT_TO_RIGHT = "LEFT_TO_RIGHT";
86    protected static final String VALUE_BOTTOM_TO_TOP = "BOTTOM_TO_TOP";
87    protected static final String VALUE_RIGHT_TO_LEFT = "RIGHT_TO_LEFT";
88    protected static final String ITEM_NODE_PLACEMENT = "NODE_PLACEMENT";
89    protected static final String VALUE_PENDULUM = "PENDULUM";
90    protected static final String VALUE_LINEAR_SEGMENTS = "LINEAR_SEGMENTS";
91    protected static final String VALUE_POLYLINE = "POLYLINE";
92    protected static final String VALUE_TREE = "TREE";
93    protected static final String VALUE_SIMPLEX = "SIMPLEX";
94    protected static final String VALUE_MEDIAN_SIMPLEX = "MEDIAN_SIMPLEX";
95    protected static final String ITEM_EDGE_ROUTING = "EDGE_ROUTING";
96    protected static final String VALUE_POLYLINE_EDGE = "POLYLINE";
97    protected static final String VALUE_ORTHOGONAL = "ORTHOGONAL";
98    protected static final String ITEM_BACKLOOP_ROUTING = "BACKLOOP_ROUTING";
99    protected static final String ITEM_ACT_ON_SELECTION_ONLY = "ACT_ON_SELECTION_ONLY";
100   
101   //// Section 'Node Rank'
102   protected static final String SECTION_NODE_RANK = "NODE_RANK";
103   // Section 'Node Rank' items
104   protected static final String ITEM_RANKING_POLICY = "RANKING_POLICY";
105   protected static final String VALUE_NO_RERANKING = "NO_RERANKING";
106   protected static final String VALUE_DOWNSHIFT_NODES = "DOWNSHIFT_NODES";
107   protected static final String VALUE_TIGHT_TREE = "TIGHT_TREE";
108   protected static final String VALUE_SIMPLEX_RANK = "SIMPLEX";
109   protected static final String VALUE_AS_IS_RANK = "AS_IS";
110   protected static final String VALUE_BFS = "BFS";
111   
112   //// Section 'Node Order'
113   protected static final String SECTION_NODE_ORDER = "NODE_ORDER";
114   // Section 'Node Order' items
115   protected static final String ITEM_WEIGHT_HEURISTIC = "WEIGHT_HEURISTIC";
116   protected static final String VALUE_BARYCENTER = "BARYCENTER";
117   protected static final String VALUE_MEDIAN = "MEDIAN";
118   protected static final String ITEM_USE_TRANSPOSITION = "USE_TRANSPOSITION";
119   protected static final String ITEM_REMOVE_FALSE_CROSSINGS = "REMOVE_FALSE_CROSSINGS";
120   protected static final String ITEM_RANDOMIZATION_ROUNDS = "RANDOMIZATION_ROUNDS";
121   
122   //// Section 'Labeling'
123   protected static final String SECTION_LABELING = "LABELING";
124   //-
125   protected static final String ITEM_EDGE_LABELING = "EDGE_LABELING";
126   protected static final String VALUE_NONE = "NONE";
127   protected static final String VALUE_HIERARCHIC = "HIERARCHIC";
128   protected static final String VALUE_GENERIC = "GENERIC";
129   protected static final String ITEM_EDGE_LABEL_MODEL = "EDGE_LABEL_MODEL";
130   protected static final String VALUE_BEST = "BEST";
131   protected static final String VALUE_AS_IS = "AS_IS";
132   protected static final String VALUE_CENTER_SLIDER = "CENTER_SLIDER";
133   protected static final String VALUE_SIDE_SLIDER = "SIDE_SLIDER";
134   protected static final String VALUE_FREE = "FREE";
135   
136   //// Section 'Grouping'
137   protected static final String SECTION_GROUPING = "GROUPING";
138   // Section 'Grouping' items
139   protected static final String ITEM_GROUP_POLICY = "GROUP_LAYOUT_POLICY";
140   protected static final String VALUE_LAYOUT_GROUPS = "LAYOUT_GROUPS";
141   protected static final String VALUE_FIX_GROUPS = "FIX_GROUPS";
142   protected static final String VALUE_IGNORE_GROUPS = "IGNORE_GROUPS";
143   protected static final String ITEM_ENABLE_GLOBAL_SEQUENCING = "ENABLE_GLOBAL_SEQUENCING";
144   
145   /**
146    * Creates an instance of this module.
147    */
148   public HierarchicLayoutModule() {
149     super(MODULE_HIERARCHIC);
150     setPortIntersectionCalculatorEnabled(true);
151   }
152   
153   /**
154    * Creates an OptionHandler and adds the option items used by this module.
155    * @return the created <code>OptionHandler</code> providing module related options
156    */
157   protected OptionHandler createOptionHandler() {
158     final OptionHandler options = new OptionHandler(getModuleName());
159     final ConstraintManager optionConstraints = new ConstraintManager(options);
160     // Defaults provider
161     final HierarchicGroupLayouter defaults = new HierarchicGroupLayouter();
162     final ClassicLayerSequencer defaultsSequencer = (ClassicLayerSequencer) defaults.getLayerSequencer();
163 
164     //// Section 'Layout'
165     options.useSection(SECTION_LAYOUT);
166     // Populate items
167     options.addInt(ITEM_MINIMAL_LAYER_DISTANCE, (int)defaults.getMinimalLayerDistance());
168     options.addInt(ITEM_MINIMAL_NODE_DISTANCE, (int)defaults.getMinimalNodeDistance());
169     options.addInt(ITEM_MINIMAL_EDGE_DISTANCE, (int)defaults.getMinimalEdgeDistance());
170     options.addInt(ITEM_MINIMAL_FIRST_SEGMENT_LENGTH, (int) defaults.getMinimalFirstSegmentLength());
171     options.addInt(ITEM_MAXIMAL_DURATION, 5);
172     options.addEnum(ITEM_ORIENTATION, new String[]{
173         VALUE_TOP_TO_BOTTOM,
174         VALUE_LEFT_TO_RIGHT,
175         VALUE_BOTTOM_TO_TOP,
176         VALUE_RIGHT_TO_LEFT
177     }, 0);
178     options.addEnum(ITEM_NODE_PLACEMENT, new String[]{
179         VALUE_PENDULUM,
180         VALUE_LINEAR_SEGMENTS,
181         VALUE_POLYLINE,
182         VALUE_TREE,
183         VALUE_SIMPLEX,
184         VALUE_MEDIAN_SIMPLEX
185     }, defaults.getLayoutStyle());
186     options.addEnum(ITEM_EDGE_ROUTING, new String[]{
187         VALUE_POLYLINE_EDGE,
188         VALUE_ORTHOGONAL
189     }, defaults.getRoutingStyle());
190     options.addBool(ITEM_BACKLOOP_ROUTING, false);
191     options.addBool(ITEM_ACT_ON_SELECTION_ONLY, false);
192 
193     //// Section 'Node Rank'
194     options.useSection(SECTION_NODE_RANK);
195     // Populate items
196     options.addEnum(ITEM_RANKING_POLICY, new String[]{
197         VALUE_NO_RERANKING,
198         VALUE_DOWNSHIFT_NODES,
199         VALUE_TIGHT_TREE,
200         VALUE_SIMPLEX_RANK,
201         VALUE_AS_IS_RANK,
202         VALUE_BFS
203     }, 2);
204 
205     //// Section 'Node Order'
206     options.useSection(SECTION_NODE_ORDER);
207     // Populate items
208     options.addEnum(ITEM_WEIGHT_HEURISTIC, new String[]{
209         VALUE_BARYCENTER,
210         VALUE_MEDIAN
211     }, defaultsSequencer.getWeightHeuristic());
212     options.addBool(ITEM_USE_TRANSPOSITION, defaultsSequencer.getUseTransposition());
213     options.addBool(ITEM_REMOVE_FALSE_CROSSINGS, defaults.getRemoveFalseCrossings());
214     options.addInt(ITEM_RANDOMIZATION_ROUNDS, defaultsSequencer.getRandomizationRounds());
215 
216     //// Section 'Labeling'
217     options.useSection(SECTION_LABELING);
218     // Populate items
219     final EnumOptionItem itemEdgeLabeling = options.addEnum(ITEM_EDGE_LABELING, new String[]{
220         VALUE_NONE,
221         VALUE_HIERARCHIC,
222         VALUE_GENERIC
223     }, 0);
224     final EnumOptionItem itemEdgeLabelModel = options.addEnum(ITEM_EDGE_LABEL_MODEL, new String[]{
225         VALUE_BEST,
226         VALUE_AS_IS,
227         VALUE_CENTER_SLIDER,
228         VALUE_SIDE_SLIDER,
229         VALUE_FREE
230     }, 0);
231     // Enable/disable items depending on specific values
232     optionConstraints.setEnabledOnValueEquals(itemEdgeLabeling, VALUE_NONE, itemEdgeLabelModel, true);
233 
234     //// Section 'Grouping'
235     options.useSection(SECTION_GROUPING);
236     // Populate items
237     options.addEnum(ITEM_GROUP_POLICY, new String[]{
238         VALUE_LAYOUT_GROUPS,
239         VALUE_FIX_GROUPS,
240         VALUE_IGNORE_GROUPS
241     }, 0);
242     options.addBool(ITEM_ENABLE_GLOBAL_SEQUENCING, true);
243     
244     return options;
245   }
246   
247   /**
248    * Main module execution routine.
249    * Launches the module's underlying algorithm on the module's graph based on user options.
250    */
251   protected void mainrun() {
252     final HierarchicGroupLayouter hierarchic = new HierarchicGroupLayouter();
253 
254     final OptionHandler options = getOptionHandler();
255     configure(hierarchic, options);
256 
257     final Graph2D graph = getGraph2D();
258 
259     prepareGraph(graph, options);
260     try {
261       launchLayouter(hierarchic);
262     } finally {
263       restoreGraph(graph, options);
264     }
265   }
266 
267   /**
268    * Prepares a <code>graph</code> depending on the given options for the
269    * module's layout algorithm.
270    * <br>
271    * Additional resources created by this method have to be freed up by calling
272    * {@link #restoreGraph(y.view.Graph2D, y.option.OptionHandler)} after
273    * layout calculation.  
274    * @param graph the graph to be prepared
275    * @param options the options for the module's layout algorithm
276    */
277   protected void prepareGraph(final Graph2D graph, final OptionHandler options) {
278     String el = options.getString(ITEM_EDGE_LABELING);
279     if (!el.equals(VALUE_NONE)) {
280       setupEdgeLabelModel(graph, el, options.getString(ITEM_EDGE_LABEL_MODEL));
281     }
282     
283     if (options.getString(ITEM_RANKING_POLICY).equals(VALUE_BFS)) {
284       // backup existing data providers to prevent loss of user settings
285       backupDataProvider(graph, BFSLayerer.CORE_NODES);
286       graph.addDataProvider(BFSLayerer.CORE_NODES, Selections.createSelectionNodeMap(graph));
287     }
288     
289     if (options.getBool(ITEM_BACKLOOP_ROUTING)) {
290       final DataProvider oldSdp = graph.getDataProvider(PortConstraintKeys.SOURCE_PORT_CONSTRAINT_KEY);
291       final DataProvider oldTdp = graph.getDataProvider(PortConstraintKeys.TARGET_PORT_CONSTRAINT_KEY);
292       PortConstraint spc = null, tpc = null;
293       if (options.get(ITEM_ORIENTATION).equals(VALUE_TOP_TO_BOTTOM)) {
294         spc = PortConstraint.create(PortConstraint.SOUTH);
295         tpc = PortConstraint.create(PortConstraint.NORTH);
296       } else if (options.get(ITEM_ORIENTATION).equals(VALUE_LEFT_TO_RIGHT)) {
297         spc = PortConstraint.create(PortConstraint.EAST);
298         tpc = PortConstraint.create(PortConstraint.WEST);
299       } else if (options.get(ITEM_ORIENTATION).equals(VALUE_BOTTOM_TO_TOP)) {
300         spc = PortConstraint.create(PortConstraint.NORTH);
301         tpc = PortConstraint.create(PortConstraint.SOUTH);
302       } else if (options.get(ITEM_ORIENTATION).equals(VALUE_RIGHT_TO_LEFT)) {
303         spc = PortConstraint.create(PortConstraint.WEST);
304         tpc = PortConstraint.create(PortConstraint.EAST);
305       }
306       final DataProvider sdp = new BackloopConstraintDP(spc, oldSdp);
307       final DataProvider tdp = new BackloopConstraintDP(tpc, oldTdp);
308       
309       // Re-register (overwrite) the keys with the new data providers
310       // Note oldSdp, oldTdp are contained in the new dps, no information is lost
311       graph.addDataProvider(PortConstraintKeys.SOURCE_PORT_CONSTRAINT_KEY, sdp);
312       graph.addDataProvider(PortConstraintKeys.TARGET_PORT_CONSTRAINT_KEY, tdp);
313     }
314   }
315 
316   /**
317    * Restores the given <code>graph</code> by freeing up resources created by
318    * {@link #prepareGraph(y.view.Graph2D, y.option.OptionHandler)}.
319    * @param graph the graph for which <code>prepareGraph</code> has been called
320    * @param options the options for the module's layout algorithm
321    */
322   protected void restoreGraph(Graph2D graph, OptionHandler options) {
323     if (options.getBool(ITEM_BACKLOOP_ROUTING)) {
324       final BackloopConstraintDP sdp, tdp;
325       sdp = (BackloopConstraintDP) graph.getDataProvider(PortConstraintKeys.SOURCE_PORT_CONSTRAINT_KEY);
326       tdp = (BackloopConstraintDP) graph.getDataProvider(PortConstraintKeys.TARGET_PORT_CONSTRAINT_KEY);
327 
328       // If there is an old data provider re-register it
329       // else deregister data providers registered by this module
330       final DataProvider oldSdp = sdp.delegate;
331       if (oldSdp != null) {
332         graph.addDataProvider(PortConstraintKeys.SOURCE_PORT_CONSTRAINT_KEY, oldSdp);
333       } else {
334 
335         graph.removeDataProvider(PortConstraintKeys.SOURCE_PORT_CONSTRAINT_KEY);
336       }
337 
338       final DataProvider oldTdp = tdp.delegate;
339       if (oldTdp != null) {
340         graph.addDataProvider(PortConstraintKeys.TARGET_PORT_CONSTRAINT_KEY, oldTdp);
341       } else {
342         graph.removeDataProvider(PortConstraintKeys.TARGET_PORT_CONSTRAINT_KEY);
343       }
344     }
345 
346     //cleanup BFSLayerer key if present
347     if (options.getString(ITEM_RANKING_POLICY).equals(VALUE_BFS)) {
348       // remove the data providers set by this module by restoring the initial state
349       restoreDataProvider(graph, BFSLayerer.CORE_NODES);
350     }
351   }
352 
353   /**
354    * Configures the module's layout algorithm according to the given options.
355    * <p>
356    * Important: This method does also depend on the <code>Graph2D</code>
357    * of this module in addition to the method's parameters.
358    * </p>
359    * @param hierarchic the <code>HierarchicGroupLayouter</code> to be configured
360    * @param options the layout options to set
361    */
362   protected void configure(HierarchicGroupLayouter hierarchic, OptionHandler options) {
363     ((ComponentLayouter) hierarchic.getComponentLayouter()).setStyle(ComponentLayouter.STYLE_MULTI_ROWS);
364     hierarchic.setRemoveFalseCrossings(options.getBool(ITEM_REMOVE_FALSE_CROSSINGS));
365     hierarchic.setMaximalDuration(options.getInt(ITEM_MAXIMAL_DURATION) * 1000);
366     hierarchic.setMinimalNodeDistance(options.getInt(ITEM_MINIMAL_NODE_DISTANCE));
367     hierarchic.setMinimalEdgeDistance(options.getInt(ITEM_MINIMAL_EDGE_DISTANCE));
368     hierarchic.setMinimalFirstSegmentLength(options.getInt(ITEM_MINIMAL_FIRST_SEGMENT_LENGTH));
369     hierarchic.setMinimalLayerDistance(options.getInt(ITEM_MINIMAL_LAYER_DISTANCE));
370 
371     if (options.get(ITEM_ORIENTATION).equals(VALUE_TOP_TO_BOTTOM)) {
372       hierarchic.setLayoutOrientation(OrientationLayouter.TOP_TO_BOTTOM);
373     } else if (options.get(ITEM_ORIENTATION).equals(VALUE_LEFT_TO_RIGHT)) {
374       hierarchic.setLayoutOrientation(OrientationLayouter.LEFT_TO_RIGHT);
375     } else if (options.get(ITEM_ORIENTATION).equals(VALUE_BOTTOM_TO_TOP)) {
376       hierarchic.setLayoutOrientation(OrientationLayouter.BOTTOM_TO_TOP);
377     } else if (options.get(ITEM_ORIENTATION).equals(VALUE_RIGHT_TO_LEFT)) {
378       hierarchic.setLayoutOrientation(OrientationLayouter.RIGHT_TO_LEFT);
379     }
380 
381     hierarchic.setGlobalSequencingActive(options.getBool(SECTION_GROUPING, ITEM_ENABLE_GLOBAL_SEQUENCING));
382 
383     configureLabeling(hierarchic, options);
384 
385     String ls = options.getString(ITEM_NODE_PLACEMENT);
386     if (ls.equals(VALUE_PENDULUM)) {
387       hierarchic.setLayoutStyle(HierarchicLayouter.PENDULUM);
388     } else if (ls.equals(VALUE_POLYLINE)) {
389       hierarchic.setLayoutStyle(HierarchicLayouter.POLYLINE);
390     } else if (ls.equals(VALUE_LINEAR_SEGMENTS)) {
391       hierarchic.setLayoutStyle(HierarchicLayouter.LINEAR_SEGMENTS);
392     } else if (ls.equals(VALUE_TREE)) {
393       hierarchic.setLayoutStyle(HierarchicLayouter.TREE);
394     } else if (ls.equals(VALUE_SIMPLEX)) {
395       hierarchic.setLayoutStyle(HierarchicLayouter.SIMPLEX);
396     } else if (ls.equals(VALUE_MEDIAN_SIMPLEX)) {
397       hierarchic.setLayoutStyle(HierarchicLayouter.MEDIAN_SIMPLEX);
398     }
399     String rs = options.getString(ITEM_EDGE_ROUTING);
400     if (rs.equals(VALUE_POLYLINE)) {
401       hierarchic.setRoutingStyle(HierarchicLayouter.ROUTE_POLYLINE);
402     } else if (rs.equals(VALUE_ORTHOGONAL)) {
403       hierarchic.setRoutingStyle(HierarchicLayouter.ROUTE_ORTHOGONAL);
404     }
405 
406     hierarchic.setSubgraphLayouterEnabled(options.getBool(ITEM_ACT_ON_SELECTION_ONLY));
407 
408     String rp = options.getString(ITEM_RANKING_POLICY);
409     if (rp.equals(VALUE_AS_IS_RANK)) {
410       hierarchic.setLayeringStrategy(HierarchicLayouter.LAYERING_FROM_SKETCH);
411     } else if (rp.equals(VALUE_SIMPLEX_RANK)) {
412       hierarchic.setLayeringStrategy(HierarchicLayouter.LAYERING_HIERARCHICAL_OPTIMAL);
413     } else if (rp.equals(VALUE_NO_RERANKING)) {
414       hierarchic.setLayeringStrategy(HierarchicLayouter.LAYERING_HIERARCHICAL_TOPMOST);
415     } else if (rp.equals(VALUE_DOWNSHIFT_NODES)) {
416       hierarchic.setLayeringStrategy(HierarchicLayouter.LAYERING_HIERARCHICAL_DOWNSHIFT);
417     } else if (rp.equals(VALUE_TIGHT_TREE)) {
418       hierarchic.setLayeringStrategy(HierarchicLayouter.LAYERING_HIERARCHICAL_TIGHT_TREE);
419     } else if (rp.equals(VALUE_BFS)) {
420       hierarchic.setLayeringStrategy(HierarchicLayouter.LAYERING_BFS);
421     }
422 
423     String  wh = options.getString(ITEM_WEIGHT_HEURISTIC);
424 
425     LayerSequencer layerSequencer = hierarchic.getLayerSequencer();
426     if (layerSequencer instanceof ClassicLayerSequencer) {
427       ClassicLayerSequencer cls = (ClassicLayerSequencer)layerSequencer;
428       if (wh.equals(VALUE_MEDIAN)) {
429         cls.setWeightHeuristic(ClassicLayerSequencer.MEDIAN_HEURISTIC);
430       } else {
431         cls.setWeightHeuristic(ClassicLayerSequencer.BARYCENTER_HEURISTIC);
432       }
433       cls.setUseTransposition(options.getBool(ITEM_USE_TRANSPOSITION));
434       cls.setRandomizationRounds(options.getInt(SECTION_NODE_ORDER, ITEM_RANDOMIZATION_ROUNDS));
435       hierarchic.setLayerSequencer(cls);
436     }
437     
438     if (HierarchyManager.containsGroupNodes(getGraph2D())) {
439       if (options.get(ITEM_GROUP_POLICY).equals(VALUE_IGNORE_GROUPS)) {
440         hierarchic.prependStage(new GroupNodeHider());
441       } else {
442         if (options.get(ITEM_GROUP_POLICY).equals(VALUE_FIX_GROUPS)) {
443           final FixedGroupLayoutStage fixedGroupLayoutStage = new FixedGroupLayoutStage();
444           if (options.get(ITEM_EDGE_ROUTING).equals(VALUE_ORTHOGONAL)) {
445             fixedGroupLayoutStage.setInterEdgeRoutingStyle(FixedGroupLayoutStage.ROUTING_STYLE_ORTHOGONAL);
446           }
447           hierarchic.prependStage(fixedGroupLayoutStage);
448         }
449       }
450     }
451   }
452 
453   private void configureLabeling(final HierarchicGroupLayouter hierarchic, final OptionHandler options) {
454     final String el = options.getString(ITEM_EDGE_LABELING);
455     if (!el.equals(VALUE_NONE)) {
456       if (el.equals(VALUE_GENERIC)) {
457         final GreedyMISLabeling la = new GreedyMISLabeling();
458         la.setPlaceNodeLabels(false);
459         la.setPlaceEdgeLabels(true);
460         la.setAutoFlippingEnabled(true);
461         la.setProfitModel(new LabelRanking());
462         hierarchic.setLabelLayouter(la);
463         hierarchic.setLabelLayouterEnabled(true);
464       } else if (el.equals(MODULE_HIERARCHIC)) {
465         CompositeLayoutStage ll = new CompositeLayoutStage();
466         ll.appendStage(new LabelLayoutTranslator());
467         ll.appendStage(new LabelLayoutDataRefinement());
468         hierarchic.setLabelLayouter(ll);
469         hierarchic.setLabelLayouterEnabled(true);
470       }
471     } else {
472       hierarchic.setLabelLayouterEnabled(false);
473     }
474   }
475 
476   static final class BackloopConstraintDP extends DataProviderAdapter {
477     private final PortConstraint pc;
478     private final DataProvider delegate;
479     private static final PortConstraint anySide = PortConstraint.create(PortConstraint.ANY_SIDE);
480     BackloopConstraintDP(PortConstraint pc, DataProvider delegate) {
481       this.pc = pc;
482       this.delegate = delegate;
483     }
484     
485     public Object get(Object o) {
486       if (delegate != null) {
487         final Object delegateResult = delegate.get(o);
488         if (delegateResult != null) {
489           return delegateResult;
490         }
491       } 
492       final Edge e = (Edge)o;
493       if (e.isSelfLoop()) {
494         return anySide;
495       } else {
496         return pc;
497       }
498     }
499   }
500   
501   private void setupEdgeLabelModel(final Graph2D graph, final String edgeLabeling, String edgeLabelModel) {
502     if (edgeLabeling.equals(VALUE_NONE) || edgeLabelModel.equals(VALUE_AS_IS)) {
503       return; //nothing to do
504     }
505     
506     if (edgeLabelModel.equals(VALUE_BEST)) {
507       if (edgeLabeling.equals(VALUE_GENERIC)) {
508         edgeLabelModel = VALUE_SIDE_SLIDER;
509       } else if (edgeLabeling.equals(MODULE_HIERARCHIC)) {
510         edgeLabelModel = VALUE_FREE;
511       }
512     }
513     
514     byte model = EdgeLabel.SIDE_SLIDER;
515     byte preferredSide = LabelLayoutConstants.PLACE_RIGHT_OF_EDGE;
516     if (edgeLabelModel.equals(VALUE_CENTER_SLIDER)) {
517       model = EdgeLabel.CENTER_SLIDER;
518       preferredSide = LabelLayoutConstants.PLACE_ON_EDGE;
519     } else if (edgeLabelModel.equals(VALUE_FREE)) {
520       model = EdgeLabel.FREE;
521       preferredSide = LabelLayoutConstants.PLACE_ON_EDGE;
522     }
523     
524     for(EdgeCursor ec = graph.edges(); ec.ok(); ec.next()) {
525       Edge e = ec.edge();
526       EdgeRealizer er = graph.getRealizer(e);
527       for(int i = 0; i < er.labelCount(); i++) {
528         EdgeLabel el = er.getLabel(i);
529         if (EdgeLabel.FREE != model || !(el.getLabelModel() instanceof SmartEdgeLabelModel)) {
530           // SmartEdgeLabelModel is an enhanced free model,
531           // therefore it is ok to stick with it instead of
532           // replacing it with FreeEdgeLabelModel
533           el.setModel(model);
534         }
535         setPreferredSide(el, preferredSide);
536       }
537     }
538   }
539 
540   private static void setPreferredSide(final EdgeLabel el, final byte preferredSide) {
541     final PreferredPlacementDescriptor oldDescriptor =
542             el.getPreferredPlacementDescriptor();
543     if (oldDescriptor.getSideOfEdge() != preferredSide) {
544       final PreferredPlacementDescriptor newDescriptor =
545               new PreferredPlacementDescriptor(oldDescriptor);
546       newDescriptor.setSideOfEdge(preferredSide);
547       el.setPreferredPlacementDescriptor(newDescriptor);
548     }
549   }
550 }
551