diff --git a/core/src/net/sf/openrocket/rocketcomponent/Rocket.java b/core/src/net/sf/openrocket/rocketcomponent/Rocket.java index 1f547cdb1..907f36094 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/Rocket.java +++ b/core/src/net/sf/openrocket/rocketcomponent/Rocket.java @@ -439,8 +439,10 @@ public class Rocket extends ComponentAssembly { this.configSet.reset(); this.configSet.setDefault(new FlightConfiguration(this)); for (FlightConfigurationId key : source.configSet.map.keySet()) { + FlightConfiguration srcCfg = source.configSet.get(key); FlightConfiguration newCfg = new FlightConfiguration(this, key); - newCfg.setName(source.configSet.get(key).getNameRaw()); // Copy config name + newCfg.copyStageActiveness(srcCfg); + newCfg.setName(srcCfg.getNameRaw()); // Copy config name this.configSet.set(key, newCfg); } this.selectedConfiguration = this.configSet.get(source.getSelectedConfiguration().getId()); diff --git a/swing/src/net/sf/openrocket/gui/adaptors/BooleanModel.java b/swing/src/net/sf/openrocket/gui/adaptors/BooleanModel.java index ae5ad9288..34f0d7d97 100644 --- a/swing/src/net/sf/openrocket/gui/adaptors/BooleanModel.java +++ b/swing/src/net/sf/openrocket/gui/adaptors/BooleanModel.java @@ -6,6 +6,7 @@ import java.beans.PropertyChangeListener; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.Arrays; import java.util.EventListener; import java.util.EventObject; import java.util.List; @@ -22,8 +23,6 @@ import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.util.BugException; import net.sf.openrocket.util.ChangeSource; import net.sf.openrocket.util.Invalidatable; -import net.sf.openrocket.util.Invalidator; -import net.sf.openrocket.util.MemoryManagement; import net.sf.openrocket.util.Reflection; import net.sf.openrocket.util.StateChangeListener; @@ -44,6 +43,7 @@ import net.sf.openrocket.util.StateChangeListener; * @author Sampo Niskanen */ public class BooleanModel extends AbstractAction implements StateChangeListener, ChangeSource, Invalidatable { + private final ModelInvalidator modelInvalidator; // Composite pattern because f***ing Java doesn't allow multiple inheritance... private static final long serialVersionUID = -7299680391506320196L; private static final Logger log = LoggerFactory.getLogger(BooleanModel.class); @@ -68,9 +68,7 @@ public class BooleanModel extends AbstractAction implements StateChangeListener, private boolean oldValue; private boolean oldEnabled; - - private Invalidator invalidator = new Invalidator(this); - private final ArrayList listeners = new ArrayList<>(); + /** @@ -79,6 +77,7 @@ public class BooleanModel extends AbstractAction implements StateChangeListener, * @param initialValue the initial value of the boolean */ public BooleanModel(boolean initialValue) { + this.modelInvalidator = new ModelInvalidator(null, this); this.valueName = null; this.source = null; this.getMethod = null; @@ -102,13 +101,14 @@ public class BooleanModel extends AbstractAction implements StateChangeListener, * @param valueName the name of the getter/setter method (without the get/is/set prefix) */ public BooleanModel(ChangeSource source, String valueName) { + this.modelInvalidator = new ModelInvalidator(source, this); this.source = source; this.valueName = valueName; Method getter = null, setter = null; - if(RocketComponent.class.isAssignableFrom(source.getClass())) { - ((RocketComponent)source).addChangeListener(this); + if (RocketComponent.class.isAssignableFrom(source.getClass())) { + source.addChangeListener(this); } // Try get/is and set @@ -172,7 +172,7 @@ public class BooleanModel extends AbstractAction implements StateChangeListener, } public void setValue(boolean b) { - checkState(true); + modelInvalidator.checkState(true); log.debug("Setting value of " + this + " to " + b); if (setMethod != null) { @@ -200,7 +200,7 @@ public class BooleanModel extends AbstractAction implements StateChangeListener, * @param enableState the state in which the component should be enabled. */ public void addEnableComponent(Component component, boolean enableState) { - checkState(true); + modelInvalidator.checkState(true); components.add(component); componentEnableState.add(enableState); updateEnableStatus(); @@ -211,7 +211,7 @@ public class BooleanModel extends AbstractAction implements StateChangeListener, * @param component component to remove from the list */ public void removeEnableComponent(Component component) { - checkState(true); + modelInvalidator.checkState(true); components.remove(component); } @@ -223,7 +223,7 @@ public class BooleanModel extends AbstractAction implements StateChangeListener, * @see #addEnableComponent(Component, boolean) */ public void addEnableComponent(Component component) { - checkState(true); + modelInvalidator.checkState(true); addEnableComponent(component, true); } @@ -250,10 +250,14 @@ public class BooleanModel extends AbstractAction implements StateChangeListener, throw Reflection.handleWrappedException(e); } } + + private List getListeners() { + return modelInvalidator.listeners; + } @Override public void stateChanged(EventObject event) { - checkState(true); + modelInvalidator.checkState(true); if (firing > 0) { log.debug("Ignoring stateChanged of " + this + ", currently firing events"); @@ -277,7 +281,7 @@ public class BooleanModel extends AbstractAction implements StateChangeListener, setEnabled(e); } - for (EventListener listener : listeners) { + for (EventListener listener : getListeners()) { if (listener instanceof StateChangeListener) { ((StateChangeListener) listener).stateChanged(event); } else if (listener instanceof ChangeListener) { @@ -308,7 +312,7 @@ public class BooleanModel extends AbstractAction implements StateChangeListener, firing--; } - for (EventListener listener : listeners) { + for (EventListener listener : getListeners()) { if (listener instanceof StateChangeListener) { ((StateChangeListener) listener).stateChanged(e); } else if (listener instanceof ChangeListener) { @@ -321,36 +325,18 @@ public class BooleanModel extends AbstractAction implements StateChangeListener, @Override public void addPropertyChangeListener(PropertyChangeListener listener) { - checkState(true); + modelInvalidator.checkState(true); super.addPropertyChangeListener(listener); } - /** - * Add a listener to the model. Adds the model as a listener to the value source if this - * is the first listener. - * @param listener Listener to add. - */ @Override public void addChangeListener(StateChangeListener listener) { - checkState(true); - - if (listeners.add(listener)) { - log.trace(this + " adding listener (total " + listeners.size() + "): " + listener); - } + modelInvalidator.addChangeListener(listener); } - /** - * Remove a listener from the model. Removes the model from being a listener to the Component - * if this was the last listener of the model. - * @param listener Listener to remove. - */ @Override public void removeChangeListener(StateChangeListener listener) { - checkState(false); - - if (listeners.remove(listener)) { - log.trace(this + " removing listener (total " + listeners.size() + "): " + listener); - } + modelInvalidator.removeChangeListener(listener); } @@ -361,31 +347,16 @@ public class BooleanModel extends AbstractAction implements StateChangeListener, */ @Override public void invalidate() { - invalidator.invalidate(); - PropertyChangeListener[] listeners = this.getPropertyChangeListeners(); if (listeners.length > 0) { - log.warn("Invalidating " + this + " while still having listeners " + listeners); + log.warn("Invalidating " + this + " while still having listeners " + Arrays.toString(listeners)); for (PropertyChangeListener l : listeners) { this.removePropertyChangeListener(l); } } - if (!this.listeners.isEmpty()) { - log.warn("Invalidating " + this + " while still having listeners " + this.listeners); - } - this.listeners.clear(); - if (source != null) { - source.removeChangeListener(this); - } - MemoryManagement.collectable(this); + modelInvalidator.invalidate(); } - - private void checkState(boolean error) { - invalidator.check(error); - } - - @Override public String toString() { diff --git a/swing/src/net/sf/openrocket/gui/adaptors/DoubleModel.java b/swing/src/net/sf/openrocket/gui/adaptors/DoubleModel.java index 171b23b04..4b636d041 100644 --- a/swing/src/net/sf/openrocket/gui/adaptors/DoubleModel.java +++ b/swing/src/net/sf/openrocket/gui/adaptors/DoubleModel.java @@ -8,6 +8,7 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.EventListener; import java.util.EventObject; +import java.util.List; import javax.swing.AbstractAction; import javax.swing.AbstractSpinnerModel; @@ -29,9 +30,7 @@ import net.sf.openrocket.util.ChangeSource; import net.sf.openrocket.util.ExpressionParser; import net.sf.openrocket.util.InvalidExpressionException; import net.sf.openrocket.util.Invalidatable; -import net.sf.openrocket.util.Invalidator; import net.sf.openrocket.util.MathUtil; -import net.sf.openrocket.util.MemoryManagement; import net.sf.openrocket.util.Reflection; import net.sf.openrocket.util.StateChangeListener; @@ -52,7 +51,7 @@ import net.sf.openrocket.util.StateChangeListener; public class DoubleModel implements StateChangeListener, ChangeSource, Invalidatable { private static final Logger log = LoggerFactory.getLogger(DoubleModel.class); - + private final ModelInvalidator modelInvalidator; // Composite pattern because f***ing Java doesn't allow multiple inheritance... public static final DoubleModel ZERO = new DoubleModel(0); @@ -613,8 +612,7 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat private final Method getAutoMethod; private final Method setAutoMethod; - - private final ArrayList listeners = new ArrayList(); + private UnitGroup units; private Unit currentUnit; @@ -632,8 +630,6 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat private double lastValue = 0; private boolean lastAutomatic = false; - private Invalidator invalidator = new Invalidator(this); - /** * Generate a DoubleModel that contains an internal double value. @@ -674,6 +670,7 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat * @param max maximum value. */ public DoubleModel(double value, UnitGroup unit, double min, double max) { + this.modelInvalidator = new ModelInvalidator(null, this); this.lastValue = value; this.minValue = min; this.maxValue = max; @@ -701,6 +698,7 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat */ public DoubleModel(Object source, String valueName, double multiplier, UnitGroup unit, double min, double max) { + this.modelInvalidator = new ModelInvalidator(source, this); this.source = source; this.valueName = valueName; this.multiplier = multiplier; @@ -808,7 +806,7 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat * @param v New value for parameter in SI units. */ public void setValue(double v) { - checkState(true); + modelInvalidator.checkState(true); double clampedValue = MathUtil.clamp(v, minValue, maxValue); if (clampedValue != v) { @@ -871,7 +869,7 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat * state change event if automatic setting is not available. */ public void setAutomatic(boolean auto) { - checkState(true); + modelInvalidator.checkState(true); if (setAutoMethod == null) { log.debug("Setting automatic to " + auto + " for " + this + ", automatic not available"); @@ -906,7 +904,7 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat * @param u The unit to set active. */ public void setCurrentUnit(Unit u) { - checkState(true); + modelInvalidator.checkState(true); if (currentUnit == u) return; log.debug("Setting unit for " + this + " to '" + u + "'"); @@ -930,7 +928,10 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat public UnitGroup getUnitGroup() { return units; } - + + private List getListeners() { + return modelInvalidator.listeners; + } /** @@ -950,17 +951,16 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat * @param l Listener to add. */ public void addChangeListener(EventListener l) { - checkState(true); + modelInvalidator.checkState(true); - if (listeners.isEmpty()) { + if (getListeners().isEmpty()) { if (source != null) { lastValue = getValue(); lastAutomatic = isAutomatic(); } } - - listeners.add(l); - log.trace(this + " adding listener (total " + listeners.size() + "): " + l); + + modelInvalidator.addChangeListener(l); } /** @@ -979,10 +979,7 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat * @param l Listener to remove. */ public void removeChangeListener(EventListener l) { - checkState(false); - - listeners.remove(l); - log.trace(this + " removing listener (total " + listeners.size() + "): " + l); + modelInvalidator.removeChangeListener(l); } @@ -993,31 +990,15 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat */ @Override public void invalidate() { - log.trace("Invalidating " + this); - invalidator.invalidate(); - - if (!listeners.isEmpty()) { - log.warn("Invalidating " + this + " while still having listeners " + listeners); - } - listeners.clear(); - if (source instanceof ChangeSource) { - ((ChangeSource) source).removeChangeListener(this); - } - MemoryManagement.collectable(this); + modelInvalidator.invalidate(); } - - private void checkState(boolean error) { - invalidator.check(error); - } - - + + // TODO MEDIUM: finalize is deprecated, replace with something better @Override protected void finalize() throws Throwable { super.finalize(); - if (!listeners.isEmpty()) { - log.warn(this + " being garbage-collected while having listeners " + listeners); - } + modelInvalidator.finalize(); }; @@ -1025,13 +1006,13 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat * Fire a ChangeEvent to all listeners. */ protected void fireStateChanged() { - checkState(true); + modelInvalidator.checkState(true); EventObject event = new EventObject(this); ChangeEvent cevent = new ChangeEvent(this); firing++; // Copy the list before iterating to prevent concurrent modification exceptions. - EventListener[] ls = listeners.toArray(new EventListener[0]); + EventListener[] ls = getListeners().toArray(new EventListener[0]); for (EventListener l : ls) { if (l instanceof StateChangeListener) { ((StateChangeListener) l).stateChanged(event); @@ -1048,7 +1029,7 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat */ @Override public void stateChanged(EventObject e) { - checkState(true); + modelInvalidator.checkState(true); double v = getValue(); boolean b = isAutomatic(); diff --git a/swing/src/net/sf/openrocket/gui/adaptors/EnumModel.java b/swing/src/net/sf/openrocket/gui/adaptors/EnumModel.java index 3b5fda722..b44394231 100644 --- a/swing/src/net/sf/openrocket/gui/adaptors/EnumModel.java +++ b/swing/src/net/sf/openrocket/gui/adaptors/EnumModel.java @@ -7,13 +7,15 @@ import javax.swing.AbstractListModel; import javax.swing.ComboBoxModel; import javax.swing.MutableComboBoxModel; +import net.sf.openrocket.util.Invalidatable; import net.sf.openrocket.util.Reflection; import net.sf.openrocket.util.StateChangeListener; public class EnumModel> extends AbstractListModel - implements ComboBoxModel, MutableComboBoxModel, StateChangeListener { + implements ComboBoxModel, MutableComboBoxModel, StateChangeListener, Invalidatable { private static final long serialVersionUID = 7766446027840316797L; + private final ModelInvalidator modelInvalidator; private final Object source; private final String valueName; private final String nullText; @@ -39,6 +41,7 @@ public class EnumModel> extends AbstractListModel @SuppressWarnings("unchecked") public EnumModel(Object source, String valueName, T[] values, String nullText) { Class> enumClass; + this.modelInvalidator = new ModelInvalidator(source, this); this.source = source; this.valueName = valueName; @@ -124,6 +127,8 @@ public class EnumModel> extends AbstractListModel @SuppressWarnings("unchecked") @Override public void stateChanged(EventObject e) { + modelInvalidator.checkState(true); + T value = (T) getMethod.invoke(source); if (value != currentValue) { currentValue = value; @@ -163,4 +168,8 @@ public class EnumModel> extends AbstractListModel this.displayedValues.remove( index ); } + @Override + public void invalidate() { + modelInvalidator.invalidate(); + } } diff --git a/swing/src/net/sf/openrocket/gui/adaptors/IntegerModel.java b/swing/src/net/sf/openrocket/gui/adaptors/IntegerModel.java index 6a32355f9..e26e981c3 100644 --- a/swing/src/net/sf/openrocket/gui/adaptors/IntegerModel.java +++ b/swing/src/net/sf/openrocket/gui/adaptors/IntegerModel.java @@ -2,7 +2,6 @@ package net.sf.openrocket.gui.adaptors; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.ArrayList; import java.util.EventListener; import java.util.EventObject; @@ -13,6 +12,7 @@ import javax.swing.SpinnerNumberModel; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; +import net.sf.openrocket.util.Invalidatable; import net.sf.openrocket.util.MathUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -24,10 +24,12 @@ import net.sf.openrocket.util.Reflection; import net.sf.openrocket.util.StateChangeListener; -public class IntegerModel implements StateChangeListener { +public class IntegerModel implements StateChangeListener, Invalidatable { private static final Logger log = LoggerFactory.getLogger(IntegerModel.class); - - + + private final ModelInvalidator modelInvalidator; + + //////////// JSpinner Model //////////// private class IntegerSpinnerModel extends SpinnerNumberModel { @@ -139,8 +141,7 @@ public class IntegerModel implements StateChangeListener { private final Method getMethod; private final Method setMethod; - - private final ArrayList listeners = new ArrayList(); + private final int minValue; private final int maxValue; @@ -166,6 +167,7 @@ public class IntegerModel implements StateChangeListener { * @param max Maximum value allowed (in SI units) */ public IntegerModel(ChangeSource source, String valueName, int min, int max) { + this.modelInvalidator = new ModelInvalidator(source, this); this.source = source; this.valueName = valueName; @@ -237,13 +239,7 @@ public class IntegerModel implements StateChangeListener { * @param l Listener to add. */ public void addChangeListener(EventListener l) { - if (listeners.isEmpty()) { - source.addChangeListener(this); - lastValue = getValue(); - } - - listeners.add(l); - log.trace(this + " adding listener (total " + listeners.size() + "): " + l); + modelInvalidator.addChangeListener(l); } /** @@ -252,25 +248,19 @@ public class IntegerModel implements StateChangeListener { * @param l Listener to remove. */ public void removeChangeListener(ChangeListener l) { - listeners.remove(l); - if (listeners.isEmpty()) { - source.removeChangeListener(this); - } - log.trace(this + " removing listener (total " + listeners.size() + "): " + l); + modelInvalidator.removeChangeListener(l); } @Override protected void finalize() throws Throwable { super.finalize(); - if (!listeners.isEmpty()) { - log.warn(this + " being garbage-collected while having listeners " + listeners); - } + modelInvalidator.finalize(); }; public void fireStateChanged() { - EventListener[] list = listeners.toArray(new EventListener[0] ); + EventListener[] list = modelInvalidator.listeners.toArray(new EventListener[0] ); EventObject event = new EventObject(this); ChangeEvent cevent = new ChangeEvent(this); firing++; @@ -290,6 +280,8 @@ public class IntegerModel implements StateChangeListener { */ @Override public void stateChanged(EventObject e) { + modelInvalidator.checkState(true); + int v = getValue(); if (lastValue == v) return; @@ -307,5 +299,10 @@ public class IntegerModel implements StateChangeListener { } return toString; } + + @Override + public void invalidate() { + modelInvalidator.invalidate(); + } } diff --git a/swing/src/net/sf/openrocket/gui/adaptors/MaterialModel.java b/swing/src/net/sf/openrocket/gui/adaptors/MaterialModel.java index 0ed7fe5cc..04444c01f 100644 --- a/swing/src/net/sf/openrocket/gui/adaptors/MaterialModel.java +++ b/swing/src/net/sf/openrocket/gui/adaptors/MaterialModel.java @@ -17,11 +17,13 @@ import net.sf.openrocket.rocketcomponent.ComponentChangeEvent; import net.sf.openrocket.rocketcomponent.ComponentChangeListener; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.startup.Application; +import net.sf.openrocket.util.Invalidatable; import net.sf.openrocket.util.Reflection; public class MaterialModel extends AbstractListModel implements - ComboBoxModel, ComponentChangeListener, DatabaseListener { + ComboBoxModel, ComponentChangeListener, DatabaseListener, Invalidatable { private static final long serialVersionUID = 4552478532933113655L; + private final ModelInvalidator modelInvalidator; private final Material custom; @@ -46,6 +48,7 @@ public class MaterialModel extends AbstractListModel implements public MaterialModel(Component parent, RocketComponent component, Material.Type type, String name) { + this.modelInvalidator = new ModelInvalidator(component, this); this.parentUIComponent = parent; this.rocketComponent = component; this.type = type; @@ -166,5 +169,15 @@ public class MaterialModel extends AbstractListModel implements public void elementRemoved(Material element, Database source) { this.fireContentsChanged(this, 0, database.size()); } - + + @Override + public void invalidate() { + modelInvalidator.invalidate(); + } + + @Override + protected void finalize() throws Throwable { + super.finalize(); + modelInvalidator.finalize(); + } } diff --git a/swing/src/net/sf/openrocket/gui/adaptors/ModelInvalidator.java b/swing/src/net/sf/openrocket/gui/adaptors/ModelInvalidator.java new file mode 100644 index 000000000..a67e9bd1c --- /dev/null +++ b/swing/src/net/sf/openrocket/gui/adaptors/ModelInvalidator.java @@ -0,0 +1,129 @@ +package net.sf.openrocket.gui.adaptors; + +import net.sf.openrocket.rocketcomponent.ComponentChangeListener; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.util.ChangeSource; +import net.sf.openrocket.util.Invalidatable; +import net.sf.openrocket.util.Invalidator; +import net.sf.openrocket.util.MemoryManagement; +import net.sf.openrocket.util.StateChangeListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.EventListener; +import java.util.EventObject; +import java.util.List; + +/** + * A helper model for invalidating value models. + * This class has nothing to do with the actual setting/getting of the value, it's just here to avoid duplicate code + * in invalidating value models. + * This model should probably be used in a composition pattern, where this model is instantiated in a class. + * This class should then delegate the methods of the interface + * to this class. + * Thanks, Java, for not allowing multiple inheritance. + */ +public class ModelInvalidator implements StateChangeListener, Invalidatable, ChangeSource { + private static final Logger log = LoggerFactory.getLogger(ModelInvalidator.class); + + private final Invalidator invalidator; + private final Object source; + private final EventListener model; + protected final List listeners = new ArrayList<>(); + + public ModelInvalidator(Object source, EventListener model) { + this.source = source; + this.model = model; + this.invalidator = new Invalidator(model); + } + + /** + * Add a listener to the model. Adds the model as a listener to the value source if this + * is the first listener. + * @param listener Listener to add. + */ + @Override + public void addChangeListener(StateChangeListener listener) { + addChangeListener((EventListener) listener); + } + + public void addChangeListener(EventListener listener) { + checkState(true); + + if (listeners.add(listener)) { + log.trace(this + " adding listener (total " + listeners.size() + "): " + listener); + } else { + log.warn(this + " adding listener that was already registered: " + listener); + } + } + + /** + * Remove a listener from the model. Removes the model from being a listener to the Component + * if this was the last listener of the model. + * @param listener Listener to remove. + */ + @Override + public void removeChangeListener(StateChangeListener listener) { + removeChangeListener((EventListener) listener); + } + + /** + * Remove a listener from the model. Removes the model from being a listener to the Component + * if this was the last listener of the model. + * @param l Listener to remove. + */ + public void removeChangeListener(EventListener l) { + checkState(false); + + if (listeners.remove(l)) { + log.trace(this + " removing listener (total " + listeners.size() + "): " + l); + } else { + log.warn(this + " removing listener that was not registered: " + l); + } + } + + /** + * Check the state of this model. If the model is invalid, throw an IllegalStateException if `errpr` is true. + * @param error If true, throw an IllegalStateException if the model is invalid. + */ + protected void checkState(boolean error) { + invalidator.check(error); + } + + /** + * Invalidates this model by removing all listeners and removing this from + * listening to the source. After invalidation no listeners can be added to this + * model and the value cannot be set. + */ + @Override + public void invalidate() { + log.trace("Invalidating " + this); + invalidator.invalidate(); + + if (!listeners.isEmpty()) { + log.warn("Invalidating " + this + " while still having listeners " + listeners); + } + listeners.clear(); + if (source instanceof ChangeSource && model instanceof StateChangeListener) { + ((ChangeSource) source).removeChangeListener((StateChangeListener) model); + } else if (source instanceof RocketComponent && model instanceof ComponentChangeListener) { + ((RocketComponent) source).removeComponentChangeListener((ComponentChangeListener) model); + } + MemoryManagement.collectable(model); + MemoryManagement.collectable(this); + } + + @Override + protected void finalize() throws Throwable { + super.finalize(); + if (!listeners.isEmpty()) { + log.warn(model + " being garbage-collected while having listeners " + listeners); + } + } + + @Override + public void stateChanged(EventObject e) { + // Do nothing + } +} diff --git a/swing/src/net/sf/openrocket/gui/adaptors/PresetModel.java b/swing/src/net/sf/openrocket/gui/adaptors/PresetModel.java index 2fcec306e..d93f2d510 100644 --- a/swing/src/net/sf/openrocket/gui/adaptors/PresetModel.java +++ b/swing/src/net/sf/openrocket/gui/adaptors/PresetModel.java @@ -6,6 +6,7 @@ import java.util.List; import javax.swing.AbstractListModel; import javax.swing.ComboBoxModel; +import net.sf.openrocket.util.Invalidatable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -22,10 +23,12 @@ import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.startup.Application; import net.sf.openrocket.util.BugException; -public class PresetModel extends AbstractListModel implements ComboBoxModel, ComponentChangeListener, DatabaseListener { +public class PresetModel extends AbstractListModel + implements ComboBoxModel, ComponentChangeListener, DatabaseListener, Invalidatable { private static final Logger log = LoggerFactory.getLogger(PresetModel.class); private static final Translator trans = Application.getTranslator(); + private final ModelInvalidator modelInvalidator; private static final String NONE_SELECTED = String.format("%s", trans.get("PresetModel.lbl.custompreset")); @@ -37,6 +40,7 @@ public class PresetModel extends AbstractListModel implements ComboBoxModel, Com private List presets; public PresetModel(Component parent, OpenRocketDocument document, RocketComponent component) { + this.modelInvalidator = new ModelInvalidator(component, this); this.parent = parent; this.document = document; presets = Application.getComponentPresetDao().listForType(component.getPresetType(), true); @@ -102,5 +106,15 @@ public class PresetModel extends AbstractListModel implements ComboBoxModel, Com presets = Application.getComponentPresetDao().listForType(component.getPresetType(), true); this.fireContentsChanged(this, 0, getSize()); } - + + @Override + public void invalidate() { + modelInvalidator.invalidate(); + } + + @Override + protected void finalize() throws Throwable { + super.finalize(); + modelInvalidator.finalize(); + } } diff --git a/swing/src/net/sf/openrocket/gui/adaptors/TransitionShapeModel.java b/swing/src/net/sf/openrocket/gui/adaptors/TransitionShapeModel.java index d485c1896..267cbd3e3 100644 --- a/swing/src/net/sf/openrocket/gui/adaptors/TransitionShapeModel.java +++ b/swing/src/net/sf/openrocket/gui/adaptors/TransitionShapeModel.java @@ -4,17 +4,20 @@ import net.sf.openrocket.rocketcomponent.ComponentChangeEvent; import net.sf.openrocket.rocketcomponent.ComponentChangeListener; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.rocketcomponent.Transition; +import net.sf.openrocket.util.Invalidatable; import javax.swing.AbstractListModel; import javax.swing.ComboBoxModel; public class TransitionShapeModel extends AbstractListModel - implements ComboBoxModel, ComponentChangeListener { + implements ComboBoxModel, ComponentChangeListener, Invalidatable { + private final ModelInvalidator modelInvalidator; private final RocketComponent component; private final Transition.Shape[] typeList = Transition.Shape.values(); private Transition.Shape previousType; public TransitionShapeModel(RocketComponent component) { + this.modelInvalidator = new ModelInvalidator(component, this); this.component = component; if (component instanceof Transition) { previousType = ((Transition) component).getShapeType(); @@ -61,4 +64,15 @@ public class TransitionShapeModel extends AbstractListModel fireContentsChanged(this, 0, 0); } } + + @Override + public void invalidate() { + modelInvalidator.invalidate(); + } + + @Override + protected void finalize() throws Throwable { + super.finalize(); + modelInvalidator.finalize(); + } }