/* * This file is part of the OpenSCADA project * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com) * * OpenSCADA is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 * only, as published by the Free Software Foundation. * * OpenSCADA 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 Lesser General Public License version 3 for more details * (a copy is included in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU Lesser General Public License * version 3 along with OpenSCADA. If not, see * <http://opensource.org/licenses/lgpl-3.0.html> for a copy of the LGPLv3 License. */ package org.openscada.ui.utils.toggle.internal; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicLong; import org.eclipse.swt.widgets.Display; import org.openscada.ui.utils.toggle.ToggleCallback; import org.openscada.ui.utils.toggle.ToggleError; import org.openscada.ui.utils.toggle.ToggleService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ToggleServiceImpl implements ToggleService, Runnable { private static final Logger logger = LoggerFactory.getLogger ( ToggleServiceImpl.class ); private static final int delay = Integer.getInteger ( "org.openscada.ui.utils.toggle.delay", 100 ); private final ConcurrentMap<Integer, ToggleInfo> toggleInfos = new ConcurrentHashMap<Integer, ToggleInfo> (); private final ConcurrentMap<Integer, List<ToggleCallback>> toggleCallbacks = new ConcurrentHashMap<Integer, List<ToggleCallback>> (); private final AtomicLong counter = new AtomicLong ( 0 ); private final Object addRemoveLock = new Object (); private final Display display; private volatile boolean running; public ToggleServiceImpl ( final Display display ) { this.display = display; } public void addListener ( final int interval, final ToggleCallback bc ) throws ToggleError { final int intervalMs = interval * delay; synchronized ( this.addRemoveLock ) { if ( !this.toggleInfos.containsKey ( intervalMs ) ) { this.toggleInfos.put ( intervalMs, new ToggleInfo ( intervalMs ) ); } if ( !this.toggleCallbacks.containsKey ( intervalMs ) ) { this.toggleCallbacks.put ( intervalMs, new CopyOnWriteArrayList<ToggleCallback> () ); } final List<ToggleCallback> handlers = this.toggleCallbacks.get ( intervalMs ); handlers.add ( bc ); } } public void removeListener ( final ToggleCallback bc ) { synchronized ( this.addRemoveLock ) { for ( final List<ToggleCallback> bcs : this.toggleCallbacks.values () ) { bcs.remove ( bc ); } final List<Integer> toDelete = new ArrayList<Integer> (); for ( final Entry<Integer, List<ToggleCallback>> entry : this.toggleCallbacks.entrySet () ) { if ( entry.getValue ().isEmpty () ) { toDelete.add ( entry.getKey () ); } } for ( final Integer integer : toDelete ) { this.toggleCallbacks.remove ( integer ); this.toggleInfos.remove ( integer ); } } } public void start () { this.running = true; this.display.asyncExec ( new Runnable () { public void run () { triggerNext (); } } ); } private void triggerNext () { this.display.timerExec ( delay, this ); } public void stop () { this.running = false; synchronized ( this.addRemoveLock ) { this.toggleInfos.clear (); this.toggleCallbacks.clear (); } } public void run () { if ( !this.running ) { return; } try { final long c = this.counter.getAndAdd ( delay ); for ( final int toggle : this.toggleInfos.keySet () ) { if ( c % toggle == 0 ) { final ToggleInfo i = this.toggleInfos.get ( toggle ); final boolean isOn = i.toggle (); final Collection<ToggleCallback> callbacks = this.toggleCallbacks.get ( toggle ); if ( callbacks != null ) { for ( final ToggleCallback bc : callbacks ) { try { bc.toggle ( isOn ); } catch ( final Exception e ) { logger.warn ( "call of toggle action failed", e ); } } } } } } finally { if ( this.running ) { triggerNext (); } } } }