/*
Copyright 2008-2010 Gephi
Authors : Mathieu Bastian <mathieu.bastian@gephi.org>
Website : http://www.gephi.org
This file is part of Gephi.
Gephi is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
Gephi is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with Gephi. If not, see <http://www.gnu.org/licenses/>.
*/
package org.gephi.graph.dhns.core;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicReference;
import org.gephi.graph.api.Edge;
import org.gephi.graph.api.GraphEvent;
import org.gephi.graph.api.GraphListener;
import org.gephi.graph.api.GraphView;
import org.gephi.graph.api.Node;
import org.gephi.graph.dhns.event.AbstractEvent;
import org.gephi.graph.dhns.event.EdgeEvent;
import org.gephi.graph.dhns.event.GeneralEvent;
import org.gephi.graph.dhns.event.GraphEventDataImpl;
import org.gephi.graph.dhns.event.GraphEventImpl;
import org.gephi.graph.dhns.event.NodeEvent;
import org.gephi.graph.dhns.event.ViewEvent;
/**
*
* @author Mathieu Bastian
*/
public class EventManager implements Runnable {
//Const
private final static long DELAY = 1;
//Architecture
private final Dhns dhns;
private final List<GraphListener> listeners;
private final AtomicReference<Thread> thread = new AtomicReference<Thread>();
private final LinkedBlockingQueue<AbstractEvent> eventQueue;
private final Object lock = new Object();
//Flag
private boolean stop;
public EventManager(Dhns dhns) {
this.dhns = dhns;
this.eventQueue = new LinkedBlockingQueue<AbstractEvent>();
this.listeners = Collections.synchronizedList(new ArrayList<GraphListener>());
}
@Override
public void run() {
while (!stop) {
try {
Thread.sleep(DELAY);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
List<Object> eventCompress = null;
AbstractEvent precEvt = null;
AbstractEvent evt = null;
while ((evt = eventQueue.peek()) != null) {
if (precEvt != null) {
if ((evt instanceof NodeEvent || evt instanceof EdgeEvent) && precEvt.getEventType().equals(evt.getEventType()) && precEvt.getView() == evt.getView()) { //Same type
if (eventCompress == null) {
eventCompress = new ArrayList<Object>();
eventCompress.add(precEvt.getData());
}
eventCompress.add(evt.getData());
} else {
break;
}
}
eventQueue.poll();
precEvt = evt;
}
if (precEvt != null) {
GraphEvent event = createEvent(precEvt, eventCompress);
for (GraphListener l : listeners.toArray(new GraphListener[0])) {
l.graphChanged(event);
}
}
while (eventQueue.isEmpty()) {
try {
synchronized (lock) {
lock.wait();
}
} catch (InterruptedException e) {
}
}
}
}
private GraphEvent createEvent(AbstractEvent event, List<Object> compress) {
final GraphEventDataImpl eventData = (event instanceof GeneralEvent) ? null : new GraphEventDataImpl();
final GraphEventImpl graphEventImpl = new GraphEventImpl(event.getView(), event.getEventType(), eventData);
if (event instanceof NodeEvent) {
Node[] nodes;
if (compress != null) {
nodes = compress.toArray(new Node[0]);
} else {
nodes = new Node[]{(Node) event.getData()};
}
switch (event.getEventType()) {
case ADD_NODES:
eventData.setAddedNodes(nodes);
break;
case REMOVE_NODES:
eventData.setRemovedNodes(nodes);
break;
case EXPAND:
eventData.setExpandedNodes(nodes);
break;
case RETRACT:
eventData.setRetractedNodes(nodes);
break;
case MOVE_NODES:
eventData.setMovedNodes(nodes);
break;
}
} else if (event instanceof EdgeEvent) {
Edge[] edges;
if (compress != null) {
edges = compress.toArray(new Edge[0]);
} else {
edges = new Edge[]{(Edge) event.getData()};
}
switch (event.getEventType()) {
case ADD_EDGES:
eventData.setAddedEdges(edges);
break;
case REMOVE_EDGES:
eventData.setRemovedEdges(edges);
break;
}
} else if (event instanceof ViewEvent) {
eventData.setView((GraphView) event.getData());
}
return graphEventImpl;
}
public void stop(boolean stop) {
this.stop = stop;
}
public void fireEvent(AbstractEvent event) {
eventQueue.add(event);
synchronized (lock) {
lock.notifyAll();
}
}
public void start() {
Thread t = new Thread(this);
t.setDaemon(true);
t.setName("graph-event-bus");
if (this.thread.compareAndSet(null, t)) {
t.start();
}
}
public boolean isRunning() {
return thread.get() != null;
}
public void addGraphListener(GraphListener listener) {
if (!listeners.contains(listener)) {
listeners.add(listener);
}
}
public void removeGraphListener(GraphListener listener) {
listeners.remove(listener);
}
}