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.router;
29  
30  import demo.view.DemoBase;
31  import demo.view.DemoDefaults;
32  import y.base.DataProvider;
33  import y.base.Edge;
34  import y.base.EdgeCursor;
35  import y.base.EdgeMap;
36  import y.base.Node;
37  import y.base.NodeList;
38  import y.io.IOHandler;
39  import y.layout.Layouter;
40  import y.layout.PortConstraintConfigurator;
41  import y.layout.PortConstraintKeys;
42  import y.layout.router.ChannelEdgeRouter;
43  import y.layout.router.OrthogonalEdgeRouter;
44  import y.layout.router.polyline.EdgeRouter;
45  import demo.layout.module.ChannelEdgeRouterModule;
46  import demo.layout.module.OrthogonalEdgeRouterModule;
47  import demo.layout.module.PolylineEdgeRouterModule;
48  import y.module.YModule;
49  import y.option.OptionHandler;
50  import y.util.DataProviderAdapter;
51  import y.view.Bend;
52  import y.view.BendCursor;
53  import y.view.CreateEdgeMode;
54  import y.view.DefaultGraph2DRenderer;
55  import y.view.Drawable;
56  import y.view.EdgeRealizer;
57  import y.view.EditMode;
58  import y.view.Graph2D;
59  import y.view.Graph2DView;
60  import y.view.HotSpotMode;
61  import y.view.MovePortMode;
62  import y.view.MoveSelectionMode;
63  import y.view.Port;
64  import y.view.Selections;
65  
66  import javax.swing.AbstractAction;
67  import javax.swing.JComboBox;
68  import javax.swing.JToolBar;
69  import java.awt.EventQueue;
70  import java.awt.Graphics2D;
71  import java.awt.Rectangle;
72  import java.awt.event.ActionEvent;
73  import java.awt.event.ActionListener;
74  import java.io.IOException;
75  import java.util.HashSet;
76  import java.util.Locale;
77  import java.util.Set;
78  
79  /**
80   * A demo that shows how Orthogonal Edge Router and Channel Edge Router can be used to find routes through a maze.
81   * Not only will it find a way but also one with fewest possible changes in direction.
82   * <br>
83   * The following aspects of using the edge routers are demonstrated.
84   * <ol>
85   * <li>How to use OrthogonalEdgeRouterModule, ChannelEdgeRouterModules or PolylineEdgeRouterModule, respectively as
86   *     a convenient means to launch and configure the edge routers.</li>
87   * <li>How to modify the yFiles EditMode in order to trigger the
88   *     orthogonal edge router whenever
89   *     <ul>
90   *     <li>new edges get created</li>
91   *     <li>nodes get resized</li>
92   *     <li>selected nodes will be moved</li>
93   *     </ul></li>
94   * </ol>
95   * Additionally this demo shows how non-editable background-layer graphs can be displayed inside
96   * the graph view.
97   * <br/>
98   * Usage: Create nodes and edges. The edges will be routed immediately. To reroute all edges use
99   * the toolbar button "Route Edges".
100  *
101  * @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
102  * @see <a href="http://docs.yworks.com/yfiles/doc/api/index.html#/dguide/orthogonal_edge_router" target="_blank">Section Orthogonal Edge Routing</a> in the yFiles for Java Developer's Guide
103  * @see <a href="http://docs.yworks.com/yfiles/doc/api/index.html#/dguide/channel_edge_router" target="_blank">Section Channel Edge Routing</a> in the yFiles for Java Developer's Guide
104  */
105 public class MazeRouterDemo extends DemoBase {
106   private RouterStrategy strategy;
107   private Graph2D mazeG;
108   private Drawable mazeD;
109   private NodeList mazeNodes;
110   private OrthogonalEdgeRouterStrategy orthogonalEdgeRouterStrategy;
111   private ChannelEdgeRouterStrategy channelEdgeRouterStrategy;
112   private PolylineEdgeRouterStrategy polylineEdgeRouterStrategy;
113 
114   public MazeRouterDemo() {
115     initializeMaze();
116 
117     initializeGraph();
118   }
119 
120   protected void initialize() {
121     channelEdgeRouterStrategy = new ChannelEdgeRouterStrategy();
122     orthogonalEdgeRouterStrategy = new OrthogonalEdgeRouterStrategy();
123     polylineEdgeRouterStrategy = new PolylineEdgeRouterStrategy();
124     view.setContentPolicy(Graph2DView.CONTENT_POLICY_BACKGROUND_DRAWABLES);
125   }
126 
127   protected void configureDefaultRealizers() {
128     super.configureDefaultRealizers();
129     view.getGraph2D().getDefaultNodeRealizer().setSize(30, 30);
130   }
131 
132   /**
133    * Returns ViewActionDemo toolbar plus actions to trigger some layout algorithms
134    */
135   protected JToolBar createToolBar() {
136     final JComboBox comboBox = new JComboBox(new Object[]{"Polyline Edge Router", "Orthogonal Edge Router", "Channel Edge Router"});
137     comboBox.setMaximumSize(comboBox.getPreferredSize());
138     comboBox.addActionListener(new ActionListener() {
139       public void actionPerformed(ActionEvent e) {
140         switch (comboBox.getSelectedIndex()) {
141           case 0:
142             strategy = polylineEdgeRouterStrategy;
143             break;
144           case 1:
145             strategy = orthogonalEdgeRouterStrategy;
146             break;
147           case 2:
148             strategy = channelEdgeRouterStrategy;
149         }
150         doLayout();
151       }
152     });
153     strategy = polylineEdgeRouterStrategy;
154 
155     JToolBar toolBar = super.createToolBar();
156     toolBar.addSeparator();
157     toolBar.add(createActionControl(new LayoutAction()));
158     toolBar.addSeparator(TOOLBAR_SMALL_SEPARATOR);
159     toolBar.add(comboBox);
160     toolBar.addSeparator(TOOLBAR_SMALL_SEPARATOR);
161     toolBar.add(createActionControl(new OptionAction()));
162 
163     return toolBar;
164   }
165 
166   /**
167    * Provides configuration options for the edge router.
168    */
169   class OptionAction extends AbstractAction {
170     OptionAction() {
171       super("Settings...", getIconResource("resource/properties.png"));
172     }
173 
174     public void actionPerformed(ActionEvent e) {
175       final ActionListener listener = new ActionListener() {
176         public void actionPerformed(ActionEvent e) {
177           doLayout();
178         }
179       };
180       OptionSupport.showDialog(strategy.getModule().getOptionHandler(), listener, false, view.getFrame());
181     }
182   }
183 
184   /**
185    * Launches the Orthogonal Edge Router.
186    */
187   class LayoutAction extends AbstractAction {
188     LayoutAction() {
189       super("Route Edges", SHARED_LAYOUT_ICON);
190     }
191 
192     public void actionPerformed(ActionEvent e) {
193       doLayout();
194     }
195   }
196 
197   /**
198    * Modified action to fit the content nicely inside the view.
199    */
200   class FitContent extends AbstractAction {
201     FitContent() {
202       super("Fit Content");
203     }
204 
205     public void actionPerformed(ActionEvent e) {
206       Graph2D graph = view.getGraph2D();
207 
208       Rectangle r = graph.getBoundingBox();
209       r.add(mazeD.getBounds());
210       view.fitRectangle(r);
211       graph.updateViews();
212     }
213   }
214 
215   void doLayout() {
216     Graph2D graph = view.getGraph2D();
217     graph.firePreEvent();
218     try {
219       addMazeGraph();
220       // Start the module.
221       strategy.getModule().start(graph);
222       subtractMazeGraph();
223     } finally {
224       graph.firePostEvent();
225     }
226   }
227 
228   /**
229    * Adds a specially configured EditMode that will automatically route all
230    * newly created edges orthogonally. The orthogonal edge router will also
231    * be activated on some edges, when nodes get resized or a node selection gets
232    * moved.
233    */
234   protected void registerViewModes() {
235     EditMode mode = new EditMode();
236     view.addViewMode(mode);
237 
238     mode.setMoveSelectionMode(new MyMoveSelectionMode());
239     mode.setCreateEdgeMode(new MyCreateEdgeMode());
240     mode.setHotSpotMode(new MyHotSpotMode());
241     mode.setMovePortMode(new MyMovePortMode());
242   }
243 
244   /**
245    * A special mode for creating edges.
246    */
247   class MyCreateEdgeMode extends CreateEdgeMode {
248     private Node source;
249 
250     protected boolean acceptSourceNode(Node s, double x, double y) {
251       source = s;
252       return true;
253     }
254 
255     protected boolean acceptTargetNode(Node t, double x, double y) {
256       return (source != t);
257     }
258 
259     protected Edge createEdge(Graph2D graph, Node startNode, Node targetNode, EdgeRealizer realizer) {
260       graph.firePreEvent();
261       return super.createEdge(graph, startNode, targetNode, realizer);
262     }
263 
264     protected void edgeCreated(final Edge e) {
265       routeEdge(e);
266       getGraph2D().firePostEvent();
267     }
268   }
269 
270   void routeEdge(Edge e) {
271     final Graph2D graph = view.getGraph2D();
272     addMazeGraph();
273     strategy.routeEdge(e);
274     subtractMazeGraph();
275     graph.updateViews();
276   }
277 
278   /**
279    * A special mode for resizing nodes.
280    */
281   class MyHotSpotMode extends HotSpotMode {
282     public void mouseReleasedLeft(double x, double y) {
283       super.mouseReleasedLeft(x, y);
284 
285       final Graph2D graph = view.getGraph2D();
286 
287       DataProvider selectedNodes = Selections.createSelectionDataProvider(graph);
288       addMazeGraph();
289       strategy.rerouteAdjacentEdges(selectedNodes, graph);
290       subtractMazeGraph();
291       graph.updateViews();
292     }
293   }
294 
295   /**
296    * A special mode for moving a selection of the graph.
297    */
298   class MyMoveSelectionMode extends MoveSelectionMode {
299     private static final boolean ROUTE_EDGES_ON_MOVE = false;
300 
301     protected void selectionOnMove(double dx, double dy, double x, double y) {
302       if (ROUTE_EDGES_ON_MOVE) {
303         routeEdgesToSelection(false);
304       }
305     }
306 
307     protected void selectionMovedAction(double dx, double dy, double x, double y) {
308       routeEdgesToSelection(true);
309     }
310 
311     void routeEdgesToSelection(boolean includeBends) {
312       final Graph2D graph = view.getGraph2D();
313       graph.firePreEvent();
314       graph.backupRealizers();
315       try {
316         if (graph.selectedNodes().ok() || (includeBends && graph.selectedBends().ok())) {
317           addMazeGraph();
318           strategy.routeEdgesToSelection(graph);
319           subtractMazeGraph();
320           graph.updateViews();
321         }
322       } finally {
323         graph.firePostEvent();
324       }
325     }
326   }
327 
328   class MyMovePortMode extends MovePortMode {
329     MyMovePortMode() {
330       setChangeEdgeEnabled(true);
331       setIndicatingTargetNode(true);
332     }
333 
334     protected void portMoved(Port port, double x, double y) {
335       super.portMoved(port, x, y);
336       final Edge edge = port.getOwner().getEdge();
337       strategy.routeEdge(edge);
338     }
339   }
340 
341   /**
342    * Adds the maze to the user-given graph, so that the edge router can lay
343    * Ariadne's thread...
344    */
345   private void addMazeGraph() {
346     mazeNodes = new NodeList(mazeG.nodes());
347     mazeG.moveSubGraph(mazeNodes, view.getGraph2D());
348   }
349 
350   /**
351    * The maze gets removed from the user-given graph again.
352    **/
353   private void subtractMazeGraph() {
354     view.getGraph2D().moveSubGraph(mazeNodes, mazeG);
355   }
356 
357   /**
358    * Initializes the maze the first time.
359    */
360   private void initializeMaze() {
361     mazeG = new Graph2D();
362     try {
363       IOHandler ioHandler = createGraphMLIOHandler();
364       ioHandler.read(mazeG, getResource("resource/maze.graphml"));
365       DemoDefaults.applyFillColor(mazeG, DemoDefaults.DEFAULT_CONTRAST_COLOR);
366       DemoDefaults.applyLineColor(mazeG, DemoDefaults.DEFAULT_CONTRAST_COLOR);
367       
368     } catch (IOException e) {
369       System.out.println("Could not initialize maze!");
370       e.printStackTrace();
371       System.exit(-1);
372     }
373     // Create a drawable and add it to the graph as a visual representation
374     // of the maze. This way it is not possible to move the maze's walls.
375     mazeD = new MazeDrawable(mazeG);
376     view.addBackgroundDrawable(mazeD);
377     view.fitRectangle(mazeD.getBounds());
378   }
379 
380   /**
381    * Creates an initial graph and initially routes the contained edge through the maze
382    */
383   private void initializeGraph() {
384     loadGraph("resource/mazeRouterDemo.graphml");
385   }
386   /**
387    * Launches this demo.
388    */
389   public static void main(String[] args) {
390     EventQueue.invokeLater(new Runnable() {
391       public void run() {
392         Locale.setDefault(Locale.ENGLISH);
393         initLnF();
394         (new MazeRouterDemo()).start();
395       }
396     });
397   }
398 
399   /**
400    * To transform the whole maze graph into a maze drawable.
401    */
402   static class MazeDrawable implements Drawable {
403     private Graph2D mazeG;
404     private DefaultGraph2DRenderer render;
405 
406     public MazeDrawable(Graph2D g) {
407       mazeG = g;
408       render = new DefaultGraph2DRenderer();
409     }
410 
411     public Rectangle getBounds() {
412       return mazeG.getBoundingBox();
413     }
414 
415     public void paint(Graphics2D gfx) {
416       render.paint(gfx, mazeG);
417     }
418   }
419 
420   abstract static class RouterStrategy {
421     abstract YModule getModule();
422 
423     abstract void routeEdge(Edge e);
424 
425     abstract void rerouteAdjacentEdges(final DataProvider selectedNodes, final Graph2D graph);
426 
427     abstract void routeEdgesToSelection(final Graph2D graph);
428 
429     abstract void route(final Graph2D graph);
430 
431     protected void routeEdge(final Edge e, final Graph2D graph) {
432       EdgeMap spc = (EdgeMap) graph.getDataProvider(PortConstraintKeys.SOURCE_PORT_CONSTRAINT_KEY);
433       EdgeMap tpc = (EdgeMap) graph.getDataProvider(PortConstraintKeys.TARGET_PORT_CONSTRAINT_KEY);
434 
435       PortConstraintConfigurator pcc = new PortConstraintConfigurator();
436       if (spc != null && tpc != null) {
437         spc.set(e, pcc.createPortConstraintFromSketch(graph, e, true, false));
438         tpc.set(e, pcc.createPortConstraintFromSketch(graph, e, false, false));
439         route(graph);
440         spc.set(e, null);
441         tpc.set(e, null);
442       } else {
443         route(graph);
444       }
445     }
446 
447     protected void routeEdgesToSelection(final Graph2D graph, Object affectedEdgesKey) {
448       final Set selectedEdges = new HashSet();
449       for (EdgeCursor ec = graph.edges(); ec.ok(); ec.next()) {
450         final Edge edge = ec.edge();
451         if (graph.isSelected(edge.source()) ^ graph.isSelected(edge.target())) {
452           selectedEdges.add(edge);
453           continue;
454         }
455         for (BendCursor bc = graph.selectedBends(); bc.ok(); bc.next()) {
456           final Bend bend = (Bend) bc.current();
457           if (bend.getEdge() == edge) {
458             selectedEdges.add(edge);
459             break;
460           }
461         }
462       }
463       graph.addDataProvider(affectedEdgesKey, new DataProviderAdapter() {
464         public boolean getBool(Object dataHolder) {
465           return selectedEdges.contains(dataHolder);
466         }
467       });
468       route(graph);
469       graph.removeDataProvider(affectedEdgesKey);
470     }
471 
472     protected void routeEdge(final Edge e, Graph2D graph, Object selectedEdgesKey) {
473       graph.addDataProvider(selectedEdgesKey, new DataProviderAdapter() {
474         public boolean getBool(Object o) {
475           return e == o;
476         }
477       });
478       routeEdge(e, graph);
479       graph.removeDataProvider(selectedEdgesKey);
480     }
481   }
482 
483   static class OrthogonalEdgeRouterStrategy extends RouterStrategy {
484     private final MyOrthogonalEdgeRouterModule module;
485 
486     OrthogonalEdgeRouterStrategy() {
487       module = new MyOrthogonalEdgeRouterModule();
488     }
489 
490     public YModule getModule() {
491       return module;
492     }
493 
494     public void routeEdge(final Edge e) {
495       final Graph2D graph = (Graph2D) e.getGraph();
496       module.setSphereOfAction(OrthogonalEdgeRouter.ROUTE_SELECTED_EDGES);
497       routeEdge(e, graph, Layouter.SELECTED_EDGES);
498       module.resetSphereOfAction();
499     }
500 
501     public void rerouteAdjacentEdges(final DataProvider selectedNodes, final Graph2D graph) {
502       module.setSphereOfAction(OrthogonalEdgeRouter.ROUTE_EDGES_AT_SELECTED_NODES);
503       graph.addDataProvider(Layouter.SELECTED_NODES, selectedNodes);
504       this.route(graph);
505       graph.removeDataProvider(Layouter.SELECTED_NODES);
506       module.resetSphereOfAction();
507     }
508 
509     public void routeEdgesToSelection(final Graph2D graph) {
510       module.setSphereOfAction(OrthogonalEdgeRouter.ROUTE_SELECTED_EDGES);
511       routeEdgesToSelection(graph, Layouter.SELECTED_EDGES);
512       module.resetSphereOfAction();
513     }
514 
515     void route(final Graph2D graph) {
516       module.start(graph);
517     }
518   }
519 
520   /**
521    *  Overwrites (if necessary) the scope (or sphere of action) of the routing
522    *  algorithm.
523    */
524   private static class MyOrthogonalEdgeRouterModule extends OrthogonalEdgeRouterModule {
525     private final byte UNDEFINED = -1;
526     private byte sphereOfAction = UNDEFINED;
527 
528     protected void configure(final OrthogonalEdgeRouter orthogonal, final OptionHandler options) {
529       super.configure(orthogonal, options);
530 
531       if (sphereOfAction != UNDEFINED) {
532         orthogonal.setSphereOfAction(sphereOfAction);
533       }
534     }
535 
536     public void setSphereOfAction(final byte sphereOfAction) {
537       this.sphereOfAction = sphereOfAction;
538     }
539 
540     public void resetSphereOfAction() {
541       sphereOfAction = UNDEFINED;
542     }
543   }
544 
545   static class ChannelEdgeRouterStrategy extends RouterStrategy {
546     private final MyChannelEdgeRouterModule module;
547 
548     public ChannelEdgeRouterStrategy() {
549       module = new MyChannelEdgeRouterModule();
550     }
551 
552     public YModule getModule() {
553       return module;
554     }
555 
556     public void routeEdge(final Edge e) {
557       final Graph2D graph = (Graph2D) e.getGraph();
558       routeEdge(e, graph, ChannelEdgeRouter.AFFECTED_EDGES);
559     }
560 
561     public void rerouteAdjacentEdges(final DataProvider selectedNodes, final Graph2D graph) {
562       graph.addDataProvider(ChannelEdgeRouter.AFFECTED_EDGES, new DataProviderAdapter() {
563         public boolean getBool(Object dataHolder) {
564           return selectedNodes.getBool((((Edge) dataHolder).source())) || selectedNodes.getBool(
565               ((Edge) dataHolder).target());
566         }
567       });
568       this.route(graph);
569       graph.removeDataProvider(ChannelEdgeRouter.AFFECTED_EDGES);
570     }
571 
572     public void routeEdgesToSelection(final Graph2D graph) {
573       routeEdgesToSelection(graph, ChannelEdgeRouter.AFFECTED_EDGES);
574     }
575 
576     void route(final Graph2D graph) {
577       module.start(graph);
578     }
579   }
580 
581   /**
582    * Sets custom initial option values for the routing algorithm.
583    */
584   private static class MyChannelEdgeRouterModule extends ChannelEdgeRouterModule {
585     protected OptionHandler createOptionHandler() {
586       final OptionHandler options = super.createOptionHandler();
587       options.set(ITEM_PATHFINDER, VALUE_ORTHOGONAL_SHORTESTPATH_PATH_FINDER);
588       return options;
589     }
590   }
591 
592   static class PolylineEdgeRouterStrategy extends RouterStrategy {
593     private final MyPolylineEdgeRouterModule module;
594 
595     PolylineEdgeRouterStrategy() {
596       module = new MyPolylineEdgeRouterModule();
597     }
598 
599     YModule getModule() {
600       return module;
601     }
602 
603     void routeEdge(final Edge e) {
604       final Graph2D graph = (Graph2D) e.getGraph();
605       module.setSphereOfAction(EdgeRouter.ROUTE_SELECTED_EDGES);
606       routeEdge(e, graph, Layouter.SELECTED_EDGES);
607       module.resetSphereOfAction();
608     }
609 
610     void rerouteAdjacentEdges(final DataProvider selectedNodes, final Graph2D graph) {
611       module.setSphereOfAction(EdgeRouter.ROUTE_SELECTED_EDGES);
612       graph.addDataProvider(Layouter.SELECTED_EDGES, new DataProviderAdapter() {
613         public boolean getBool(Object dataHolder) {
614           return selectedNodes.getBool(((Edge) dataHolder).source())
615               || selectedNodes.getBool(((Edge) dataHolder).target());
616         }
617       });
618       this.route(graph);
619       graph.removeDataProvider(Layouter.SELECTED_EDGES);
620       module.resetSphereOfAction();
621     }
622 
623     void routeEdgesToSelection(final Graph2D graph) {
624       module.setSphereOfAction(EdgeRouter.ROUTE_SELECTED_EDGES);
625       routeEdgesToSelection(graph, Layouter.SELECTED_EDGES);
626       module.resetSphereOfAction();
627     }
628 
629     void route(final Graph2D graph) {
630       module.start(graph);
631     }
632   }
633 
634   /**
635    * Sets custom initial option values and (if necessary) overwrites the scope
636    * (or sphere of action) for the routing algorithm.
637    */
638   private static class MyPolylineEdgeRouterModule extends PolylineEdgeRouterModule {
639     private final byte UNDEFINED = -1;
640     private byte sphereOfAction = UNDEFINED;
641 
642     protected OptionHandler createOptionHandler() {
643       final OptionHandler options = super.createOptionHandler();
644       options.set(ITEM_ENABLE_POLYLINE_ROUTING, Boolean.TRUE);
645       return options;
646     }
647 
648     protected void configure(final EdgeRouter router, final OptionHandler options) {
649       super.configure(router, options);
650 
651       if (sphereOfAction != UNDEFINED) {
652         router.setSphereOfAction(sphereOfAction);
653       }
654     }
655 
656     public void setSphereOfAction(final byte sphereOfAction) {
657       this.sphereOfAction = sphereOfAction;
658     }
659 
660     public void resetSphereOfAction() {
661       sphereOfAction = UNDEFINED;
662     }
663   }
664 }
665