package org.simantics.diagram.profile; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import org.simantics.diagram.elements.DiagramNodeUtil; import org.simantics.g2d.canvas.ICanvasContext; import org.simantics.scenegraph.INode; import org.simantics.scenegraph.g2d.G2DNode; import org.simantics.utils.threads.ThreadUtils; class Updater implements Runnable { private static Updater INSTANCE; Map requesters = new HashMap(); AtomicBoolean state = new AtomicBoolean(false); private static long time = System.nanoTime(); private Updater() { } @Override public void run() { // Stop this if nothing is to be done, need synchonization since this is not AWT synchronized(requesters) { if(requesters.isEmpty()) { state.set(false); throw new RuntimeException(); } } // TODO: in unit tests this should not be AWT ThreadUtils.AWT_EDT.execute(new Runnable() { @Override public void run() { time = System.nanoTime(); synchronized(requesters) { HashSet ctxSet = new HashSet(); for(ICanvasContext ctx : requesters.values()) { if(ctx != null) { if(ctxSet.add(ctx)) ctx.getContentContext().setDirty(); } } } } }); } public void register(INode node) { // We use the size of this map to determine whether updates are needed, this is done in AWT thread synchronized(requesters) { if(requesters.size() == 0) { if(state.compareAndSet(false, true)) { ThreadUtils.getNonBlockingWorkExecutor().scheduleWithFixedDelay(this, 0, 500, TimeUnit.MILLISECONDS); } } ICanvasContext context = DiagramNodeUtil.getPossibleCanvasContext((G2DNode)node); requesters.put(node, context); } } public void unregister(INode node) { synchronized(requesters) { requesters.remove(node); } } public long getTime() { return time; } public static Updater getInstance() { if(INSTANCE == null) { INSTANCE = new Updater(); } return INSTANCE; } }