package net.sf.openrocket.optimization.rocketoptimization.modifiers;
import java.util.Locale;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.sf.openrocket.document.Simulation;
import net.sf.openrocket.optimization.general.OptimizationException;
import net.sf.openrocket.unit.UnitGroup;
import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.MathUtil;
import net.sf.openrocket.util.Reflection.Method;
/**
* A generic SimulationModifier that uses reflection to get and set a double value.
* Implementations need to implement the {@link #getModifiedObject(Simulation)} method
* to return which object is modified.
*
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/
public abstract class GenericModifier<T> extends AbstractSimulationModifier {
private static final Logger log = LoggerFactory.getLogger(GenericModifier.class);
private final double multiplier;
private final Class<? extends T> modifiedClass;
private final String methodName;
private final Method getter;
private final Method setter;
/**
* 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()})
* @param multiplier the multiplier by which the value returned by the getter is multiplied
* to obtain the desired value
* @param modifiedClass the class type that {@link #getModifiedObject(Simulation)} returns
* @param methodName the base name of the getter/setter methods (without "get"/"set")
*/
public GenericModifier(String modifierName, String modifierDescription, Object relatedObject, UnitGroup unitGroup,
double multiplier, Class<? extends T> modifiedClass, String methodName) {
super(modifierName, modifierDescription, relatedObject, unitGroup);
this.multiplier = multiplier;
this.modifiedClass = modifiedClass;
this.methodName = methodName;
if (MathUtil.equals(multiplier, 0)) {
throw new IllegalArgumentException("multiplier is zero");
}
try {
methodName = methodName.substring(0, 1).toUpperCase(Locale.ENGLISH) + methodName.substring(1);
getter = new Method(modifiedClass.getMethod("get" + methodName));
setter = new Method(modifiedClass.getMethod("set" + methodName, double.class));
} catch (SecurityException e) {
throw new BugException("Trying to find method get/set" + methodName + " in class " + modifiedClass, e);
} catch (NoSuchMethodException e) {
throw new BugException("Trying to find method get/set" + methodName + " in class " + modifiedClass, e);
}
}
@Override
public double getCurrentSIValue(Simulation simulation) throws OptimizationException {
T modifiable = getModifiedObject(simulation);
if (modifiable == null) {
throw new OptimizationException("BUG: getModifiedObject() returned null");
}
return ((Double) getter.invoke(modifiable)) * multiplier;
}
@Override
public void modify(Simulation simulation, double scaledValue) throws OptimizationException {
T modifiable = getModifiedObject(simulation);
if (modifiable == null) {
throw new OptimizationException("BUG: getModifiedObject() returned null");
}
double siValue = toBaseValue(scaledValue) / multiplier;
log.trace("Setting setter=" + setter + " modifiable=" + modifiable + " siValue=" + siValue + "scaledValue=" + scaledValue);
setter.invoke(modifiable, siValue);
}
/**
* Return the object from the simulation that will be modified.
* @param simulation the simulation
* @return the object to modify
*
* @throws OptimizationException if the object cannot be found
*/
protected abstract T getModifiedObject(Simulation simulation) throws OptimizationException;
@Override
public String toString() {
return "GenericModifier[modifiedClass=" + modifiedClass.getCanonicalName() + ", methodName=" + methodName + ", multiplier=" + multiplier + "]";
}
}