package net.sf.openrocket.optimization.rocketoptimization.modifiers;
import java.util.ArrayList;
import java.util.EventListener;
import java.util.EventObject;
import java.util.List;
import net.sf.openrocket.document.Simulation;
import net.sf.openrocket.optimization.general.OptimizationException;
import net.sf.openrocket.optimization.rocketoptimization.SimulationModifier;
import net.sf.openrocket.unit.UnitGroup;
import net.sf.openrocket.util.MathUtil;
import net.sf.openrocket.util.StateChangeListener;
/**
* An abstract implementation of the SimulationModifier interface. An implementation
* needs only to implement the {@link #getCurrentSIValue(Simulation)} and
* {@link #modify(net.sf.openrocket.document.Simulation, double)} methods.
*
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/
public abstract class AbstractSimulationModifier implements SimulationModifier {
private final String name;
private final String description;
private final Object relatedObject;
private final UnitGroup unitGroup;
private double minValue = 0.0;
private double maxValue = 1.0;
private final List<EventListener> listeners = new ArrayList<EventListener>();
/**
* Sole constructor.
*
* @param modifierName the name of this modifier (returned by {@link #getName()})
* @param modifierDescription the description of this modifier (returned by {@link #getDescription()})
* @param relatedObject the related object (returned by {@link #getRelatedObject()})
* @param unitGroup the unit group (returned by {@link #getUnitGroup()})
*/
public AbstractSimulationModifier(String modifierName, String modifierDescription, Object relatedObject,
UnitGroup unitGroup) {
this.name = modifierName;
this.description = modifierDescription;
this.relatedObject = relatedObject;
this.unitGroup = unitGroup;
if (this.name == null || this.description == null || this.relatedObject == null || this.unitGroup == null) {
throw new IllegalArgumentException("null value provided:" +
" name=" + this.name + " description=" + description + " relatedObject=" + relatedObject +
" unitGroup=" + unitGroup);
}
}
@Override
public String getName() {
return name;
}
@Override
public String getDescription() {
return description;
}
@Override
public Object getRelatedObject() {
return relatedObject;
}
@Override
public double getCurrentScaledValue(Simulation simulation) throws OptimizationException {
double value = getCurrentSIValue(simulation);
return toScaledValue(value);
}
@Override
public void initialize(Simulation simulation) throws OptimizationException {
// Default is no-op.
}
/**
* Returns the scaled value (normally within [0...1]). If the min...max range is singular,
* this method returns 0.0, 1.0 or 0.5 depending on whether the value is less than,
* greater than or equal to the limit.
*
* @param value the value in SI units
* @return the value in scaled range (normally within [0...1])
*/
protected double toScaledValue(double value) {
if (MathUtil.equals(minValue, maxValue)) {
if (value > maxValue)
return 1.0;
if (value < minValue)
return 0.0;
return 0.5;
}
return MathUtil.map(value, minValue, maxValue, 0.0, 1.0);
}
/**
* Returns the base value (in SI units).
*
* @param value the value in scaled range (normally within [0...1])
* @return the value in SI units
*/
protected double toBaseValue(double value) {
return MathUtil.map(value, 0.0, 1.0, minValue, maxValue);
}
@Override
public double getMinValue() {
return minValue;
}
@Override
public void setMinValue(double value) {
if (MathUtil.equals(minValue, value))
return;
this.minValue = value;
if (maxValue < minValue)
maxValue = minValue;
fireChangeEvent();
}
@Override
public double getMaxValue() {
return maxValue;
}
@Override
public void setMaxValue(double value) {
if (MathUtil.equals(maxValue, value))
return;
this.maxValue = value;
if (minValue > maxValue)
minValue = maxValue;
fireChangeEvent();
}
@Override
public UnitGroup getUnitGroup() {
return unitGroup;
}
@Override
public void addChangeListener(StateChangeListener listener) {
listeners.add(listener);
}
@Override
public void removeChangeListener(StateChangeListener listener) {
listeners.remove(listener);
}
/**
* Fire a change event to the listeners.
*/
protected void fireChangeEvent() {
EventObject event = new EventObject(this);
// Copy the list before iterating to prevent concurrent modification exceptions.
EventListener[] list = listeners.toArray(new EventListener[0]);
for (EventListener l : list) {
if (l instanceof StateChangeListener) {
((StateChangeListener) l).stateChanged(event);
}
}
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
AbstractSimulationModifier other = (AbstractSimulationModifier) obj;
if (!this.description.equals(other.description))
return false;
if (!this.name.equals(other.name))
return false;
if (!this.relatedObject.equals(other.relatedObject))
return false;
if (!this.unitGroup.equals(other.unitGroup))
return false;
return true;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (description.hashCode());
result = prime * result + (name.hashCode());
result = prime * result + (relatedObject.hashCode());
result = prime * result + (unitGroup.hashCode());
return result;
}
}