001/* 002 * Copyright 2006 - 2013 003 * Stefan Balev <stefan.balev@graphstream-project.org> 004 * Julien Baudry <julien.baudry@graphstream-project.org> 005 * Antoine Dutot <antoine.dutot@graphstream-project.org> 006 * Yoann Pigné <yoann.pigne@graphstream-project.org> 007 * Guilhelm Savin <guilhelm.savin@graphstream-project.org> 008 * 009 * This file is part of GraphStream <http://graphstream-project.org>. 010 * 011 * GraphStream is a library whose purpose is to handle static or dynamic 012 * graph, create them from scratch, file or any source and display them. 013 * 014 * This program is free software distributed under the terms of two licenses, the 015 * CeCILL-C license that fits European law, and the GNU Lesser General Public 016 * License. You can use, modify and/ or redistribute the software under the terms 017 * of the CeCILL-C license as circulated by CEA, CNRS and INRIA at the following 018 * URL <http://www.cecill.info> or under the terms of the GNU LGPL as published by 019 * the Free Software Foundation, either version 3 of the License, or (at your 020 * option) any later version. 021 * 022 * This program is distributed in the hope that it will be useful, but WITHOUT ANY 023 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 024 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 025 * 026 * You should have received a copy of the GNU Lesser General Public License 027 * along with this program. If not, see <http://www.gnu.org/licenses/>. 028 * 029 * The fact that you are presently reading this means that you have had 030 * knowledge of the CeCILL-C and LGPL licenses and that you accept their terms. 031 */ 032package org.graphstream.ui.layout; 033 034import org.graphstream.graph.Graph; 035import org.graphstream.stream.ProxyPipe; 036import org.graphstream.stream.Source; 037import org.graphstream.stream.thread.ThreadProxyPipe; 038 039/** 040 * Allows to run a layout in a distinct thread. 041 * 042 * <p> 043 * A layout runner will run in its own thread and periodically activate a layout 044 * algorithm on a graph event stream (you do not need a graph). This 045 * implementation is mainly used by the graph viewer but could be used by any 046 * program that needs a layout algorithm that run continuously on a dynamic 047 * graph (adapting the layout as the graph changes). 048 * </p> 049 * 050 * <p> 051 * The layout algorithms in GraphStream are iterative versions that can be 052 * called repeatedly to take graph dynamics into account and may produce a 053 * result only after several invocations. This is why the layout runner invokes 054 * the layout on a regular basis. The runner is temporized, it will not run in a 055 * loop as fast as possible, instead it will wait a little between each layout 056 * invocation. When the last layout invocation indicated the layout was good, it 057 * will wait longer that when the last invocation indicated the layout was not 058 * good (stabilized). These two times can be configured using 059 * {@link #setNaps(long, long)}. 060 * </p> 061 * 062 * <p> 063 * Once you finished using the runner, you must call {@link #release()} to break 064 * the link with the event source and stop the thread. The runner cannot be used 065 * after. 066 * </p> 067 */ 068public class LayoutRunner extends Thread { 069 /** 070 * The layout algorithm. 071 */ 072 protected Layout layout = null; 073 074 /** 075 * The proxy on the source of graph events. 076 */ 077 protected ThreadProxyPipe pumpPipe = null; 078 079 /** 080 * The meaning of life. 081 */ 082 protected boolean loop = true; 083 084 /** 085 * The time to wait between each layout invocation, when the layout 086 * stabilized. 087 */ 088 protected long longNap = 80; 089 090 /** 091 * The time to wait between each layout invocation, when the layout is not 092 * yet stabilized. 093 */ 094 protected long shortNap = 10; 095 096 /** 097 * New layout runner that listens at the given source and compute a layout 098 * on its graph structure in a distinct thread. 099 * 100 * @param source 101 * The source of graph events. 102 * @param layout 103 * The layout algorithm to use. 104 */ 105 public LayoutRunner(Source source, Layout layout) { 106 this(source, layout, true); 107 } 108 109 /** 110 * New layout runner that listen at the given source and compute a layout on 111 * its graph structure in a distinct thread. 112 * 113 * @param source 114 * The source of graph events. 115 * @param layout 116 * The layout algorithm to use. 117 * @param start 118 * Start the layout thread immediately ? Else the start() method 119 * must be called later. 120 */ 121 public LayoutRunner(Source source, Layout layout, boolean start) { 122 this.layout = layout; 123 this.pumpPipe = new ThreadProxyPipe(); 124 this.pumpPipe.addSink(layout); 125 126 if (start) 127 start(); 128 129 this.pumpPipe.init(source); 130 } 131 132 /** 133 * New layout runner that listen at the given graph and compute a layout on 134 * its graph structure in a distinct thread. A pipe is still created to 135 * listen at the graph. This means that the graph is never directly used. 136 * 137 * @param graph 138 * The source of graph events. 139 * @param layout 140 * The layout algorithm to use. 141 * @param start 142 * Start the layout thread immediately ? Else the start() method 143 * must be called later. 144 * @param replay 145 * If the graph already contains some data, replay events to 146 * create the data, this is mostly always needed. 147 */ 148 public LayoutRunner(Graph graph, Layout layout, boolean start, 149 boolean replay) { 150 this.layout = layout; 151 this.pumpPipe = new ThreadProxyPipe(); 152 this.pumpPipe.addSink(layout); 153 154 if (start) 155 start(); 156 157 this.pumpPipe.init(graph, replay); 158 } 159 160 /** 161 * Pipe out whose input is connected to the layout algorithm. You can safely 162 * connect as a sink to it to receive events of the layout from a distinct 163 * thread. 164 */ 165 public ProxyPipe newLayoutPipe() { 166 ThreadProxyPipe tpp = new ThreadProxyPipe(); 167 tpp.init(layout); 168 169 return tpp; 170 } 171 172 @Override 173 public void run() { 174 String layoutName = layout.getLayoutAlgorithmName(); 175 176 while (loop) { 177 double limit = layout.getStabilizationLimit(); 178 179 pumpPipe.pump(); 180 if (limit > 0) { 181 if (layout.getStabilization() > limit) { 182 nap(longNap); 183 } else { 184 layout.compute(); 185 nap(shortNap); 186 } 187 } else { 188 layout.compute(); 189 nap(shortNap); 190 } 191 } 192 193 System.out.printf("Layout '%s' process stopped.%n", layoutName); 194 System.out.flush(); 195 } 196 197 /** 198 * Release any link to the source of events and stop the layout proces. The 199 * thread will end after this method has been called. 200 */ 201 public void release() { 202 pumpPipe.unregisterFromSource(); 203 pumpPipe.removeSink(layout); 204 pumpPipe = null; 205 loop = false; 206 207 if (Thread.currentThread() != this) { 208 try { 209 this.join(); 210 } catch (InterruptedException e) { 211 e.printStackTrace(); 212 System.err.printf("Layout can not stop ...\n"); 213 } 214 } 215 216 layout = null; 217 } 218 219 /** 220 * Sleep for the given period of time in milliseconds. 221 * 222 * @param ms 223 * The number of milliseconds to wait. 224 */ 225 protected void nap(long ms) { 226 try { 227 Thread.sleep(ms); 228 } catch (Exception e) { 229 } 230 } 231 232 /** 233 * Configure the time to wait between each layout invocation. The long nap 234 * configures the time to wait when the last layout invocation indicated the 235 * layout was stabilized, the short nap is used in the other case. 236 * 237 * @param longNap 238 * The time to wait between stabilized layout invocations, by 239 * default 80. 240 * @param shortNap 241 * The time to wait between non stabilized layout invocations, by 242 * default 10. 243 */ 244 public void setNaps(long longNap, long shortNap) { 245 this.longNap = longNap; 246 this.shortNap = shortNap; 247 } 248}