/******************************************************************************* * Copyright 2013 Geoscience Australia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package au.gov.ga.earthsci.core.temporal; import java.util.LinkedHashSet; import java.util.concurrent.locks.ReadWriteLock; import javax.inject.Singleton; import au.gov.ga.earthsci.common.util.AbstractPropertyChangeBean; import au.gov.ga.earthsci.common.util.Validate; import au.gov.ga.earthsci.worldwind.common.util.LenientReadWriteLock; /** * The central time keeper/server in the EarthSci platform. * <p/> * The current application time can be set and accessed through this class. * Listeners can also be attached using the standard property change mechanism * to listen for changes to the {@value #CURRENT_TIME_PROPERTY_NAME} property. * <p/> * This class also maintains a collection of {@link ITemporal} objects and * notifies them of changes to the current application time. {@link ITemporal} * objects will be notified of changes to current time <em>before</em> other * listeners that have been registered via the property change mechanism. * <p/> * This class is intended to be used as singleton via the DI mechanism - there * should only exist one instance per application. All public methods are * threadsafe and can be executed concurrently with well defined behaviour. * * @author James Navin (james.navin@ga.gov.au) */ @Singleton public class Chronos extends AbstractPropertyChangeBean { /** The name of the property event issued when the current time is changed */ public static final String CURRENT_TIME_PROPERTY_NAME = "currentTime"; //$NON-NLS-1$ private LinkedHashSet<ITemporal> temporalObjects = new LinkedHashSet<ITemporal>(); private ReadWriteLock temporalObjectsLock = new LenientReadWriteLock(); private BigTime currentTime = BigTime.now(); /** * Add the provided temporal object to the central time server. The object * will receive future notifications about changes to the current time. * * @param t * The temporal object to add */ public void addTemporal(ITemporal t) { if (t == null) { return; } try { temporalObjectsLock.writeLock().lock(); temporalObjects.add(t); } finally { temporalObjectsLock.writeLock().unlock(); } } /** * Remove the provided temporal object from the central time server, if it * exists. The object will receive no further notifications about changes to * current time. * * @param t * The temporal object to remove */ public void removeTemporal(ITemporal t) { try { temporalObjectsLock.writeLock().lock(); temporalObjects.remove(t); } finally { temporalObjectsLock.writeLock().unlock(); } } /** * Set the current application time. All registered temporal objects and * listeners will be notified of the change in time. * * @param currentTime * the currentTime to set */ public synchronized void setCurrentTime(BigTime currentTime) { Validate.notNull(currentTime, "A time instant is required"); //$NON-NLS-1$ BigTime oldTime = currentTime; this.currentTime = currentTime; if (!oldTime.equals(currentTime)) { notifyTemporalObjectsOfChangedTime(); firePropertyChange(CURRENT_TIME_PROPERTY_NAME, oldTime, currentTime); } } /** * @return the current application time */ public synchronized BigTime getCurrentTime() { return currentTime; } private void notifyTemporalObjectsOfChangedTime() { try { temporalObjectsLock.readLock().lock(); for (ITemporal t : temporalObjects) { t.apply(currentTime); } } finally { temporalObjectsLock.readLock().unlock(); } } }