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}