/* * Copyright (c) Thomas Parker, 2016. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This program 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 for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ package pcgen.cdom.formula; import java.util.Objects; import javax.swing.event.EventListenerList; import pcgen.base.formula.base.VariableID; import pcgen.base.solver.AggressiveSolverManager; import pcgen.facade.util.WriteableReferenceFacade; import pcgen.facade.util.event.ReferenceEvent; import pcgen.facade.util.event.ReferenceListener; /** * A VariableChannel provides a common mechanism for reading and writing to a * variable from a system external to the PCGen core. * * @param <T> * The Format of the information contained in this VariableChannel */ public final class VariableChannel<T> implements VariableListener<T>, WriteableReferenceFacade<T> { /** * The underlying AggressiveSolverManager that solves the given VariableID */ private final AggressiveSolverManager manager; /** * The VariableID indicating to which Variable this VariableChannel is * providing an interface. */ private final VariableID<T> varID; /** * The MonitorableVariableStore that the results of the calculations by the * AggressiveSolverManager are placed in. */ private final MonitorableVariableStore varStore; /** * The list of listeners that listen to this VariableChannel for * ReferenceEvents. */ private final EventListenerList listenerList = new EventListenerList(); /** * Constructs a new VariableChannel with the given AggressiveSolverManager, * MonitorableVariableStore, and VariableID indicating the contents of the * Channel. * * @param manager * The underlying AggressiveSolverManager that solves the given * VariableID * @param varStore * The MonitorableVariableStore that the results of the * calculations by the AggressiveSolverManager are placed in * @param varID * The VariableID indicating to which Variable this * VariableChannel is providing an interface */ private VariableChannel(AggressiveSolverManager manager, MonitorableVariableStore varStore, VariableID<T> varID) { this.manager = Objects.requireNonNull(manager); this.varStore = Objects.requireNonNull(varStore); this.varID = Objects.requireNonNull(varID); } @Override public void variableChanged(VariableChangeEvent<T> event) { fireReferenceChangedEvent(event.getSource(), event.getOldValue(), event.getNewValue()); } @Override public T get() { T value = varStore.get(varID); if (value == null) { return manager.getDefaultValue(varID.getVariableFormat()); } return value; } @Override public void set(T object) { varStore.put(varID, object); manager.solveChildren(varID); } /** * Disconnects the VariableChannel from the WriteableVariableStore. This is * necessary before a VariableChannel is disposed of, so that it does not * cause a memory leak. * * Note, if disconnect() is called and the VariableChannel continues to be * used, then it is effectively a WriteableVariableStore, not a * MonitorableVariableStore. It will no longer send any ReferenceEvents if * the underlying VariableID changes. */ public void disconnect() { varStore.removeVariableListener(varID, this); } @Override public void addReferenceListener(ReferenceListener<? super T> listener) { listenerList.add(ReferenceListener.class, listener); } @Override public void removeReferenceListener(ReferenceListener<? super T> listener) { listenerList.remove(ReferenceListener.class, listener); } private void fireReferenceChangedEvent(Object source, T oldValue, T newValue) { ReferenceListener[] listeners = listenerList.getListeners(ReferenceListener.class); ReferenceEvent<T> e = null; for (int i = listeners.length - 1; i >= 0; i--) { if (e == null) { e = new ReferenceEvent<>(source, oldValue, newValue); } listeners[i].referenceChanged(e); } } /** * Constructs a new VariableChannel from the given parameters and links the * VariableChannel as a listener to the given MonitorableVariableStore (for * the given VariableID). * * As a note on object cleanup: The returned VariableChannel is a listener * to the given MonitorableVariableStore. Should use of this VariableChannel * be no longer necessary, then the disconnect() method of the * VariableChannel should be called in order to disconnect the * VariableChannel from the WriteableVariableStore. * * @param manager * The underlying AggressiveSolverManager that solves the given * VariableID * @param varStore * The MonitorableVariableStore that the results of the * calculations by the AggressiveSolverManager are placed in * @param varID * The VariableID indicating to which Variable this * VariableChannel is providing an interface * @return A new VariableChannel linked as a listener to the given * MonitorableVariableStore for the given VariableID */ public static <T> VariableChannel<T> construct( AggressiveSolverManager manager, MonitorableVariableStore varStore, VariableID<T> varID) { VariableChannel<T> ref = new VariableChannel<>(manager, varStore, varID); varStore.addVariableListener(varID, ref); return ref; } }