Added persistence of ComponentPresets in the ORK file. Bumped ORK file version number to 1.5 when saving designs containing ComponentPresets. Added a digest string to ComponentPreset which is used during reading and writing ORK files to verify the correct ComponentPreset is used.

This commit is contained in:
Kevin Ruland 2012-04-11 17:36:50 +00:00
parent 68eaf21ef0
commit edcd0008b1
7 changed files with 559 additions and 365 deletions

View File

@ -38,3 +38,5 @@ The following file format versions exist:
deploymentvelocity attributes to <flightdata> element. The motor deploymentvelocity attributes to <flightdata> element. The motor
digesting algorithm was changed. Adds <separationevent> and digesting algorithm was changed. Adds <separationevent> and
<separationdelay> elements to stage components (except sustainer). <separationdelay> elements to stage components (except sustainer).
1.5: Introduced with OpenRocket 12.xx. Added ComponentPresets.

View File

@ -26,4 +26,6 @@ public interface ComponentPresetDao {
public void setFavorite( ComponentPreset preset, boolean favorite ); public void setFavorite( ComponentPreset preset, boolean favorite );
public List<ComponentPreset> find( String manufacturer, String partNo );
} }

View File

@ -129,6 +129,17 @@ public class ComponentPresetDatabase extends Database<ComponentPreset> implement
return result; return result;
} }
@Override
public List<ComponentPreset> find(String manufacturer, String partNo) {
List<ComponentPreset> presets = new ArrayList<ComponentPreset>();
for( ComponentPreset preset : list ) {
if ( preset.getManufacturer().getSimpleName().equals(manufacturer) && preset.getPartNo().equals(partNo) ) {
presets.add(preset);
}
}
return presets;
}
@Override @Override
public void setFavorite( ComponentPreset preset, boolean favorite ) { public void setFavorite( ComponentPreset preset, boolean favorite ) {
preset.setFavorite(favorite); preset.setFavorite(favorite);

View File

@ -184,6 +184,9 @@ public class OpenRocketSaver extends RocketSaver {
*/ */
private int calculateNecessaryFileVersion(OpenRocketDocument document, StorageOptions opts) { private int calculateNecessaryFileVersion(OpenRocketDocument document, StorageOptions opts) {
/* /*
* File version 1.5 is requires for:
* - saving designs using ComponentPrests
*
* File version 1.4 is required for: * File version 1.4 is required for:
* - saving simulation data * - saving simulation data
* - saving motor data * - saving motor data
@ -195,6 +198,22 @@ public class OpenRocketSaver extends RocketSaver {
* Otherwise use version 1.0. * Otherwise use version 1.0.
*/ */
// Search the rocket for any ComponentPrests
{
Rocket r = document.getRocket();
Iterator<RocketComponent> componentIterator = r.iterator();
boolean usesComponentPreset = false;
while ( !usesComponentPreset && componentIterator.hasNext() ) {
RocketComponent c = componentIterator.next();
if ( c.getPresetComponent() != null ) {
usesComponentPreset = true;
}
}
if ( usesComponentPreset ) {
return FILE_VERSION_DIVISOR + 5;
}
}
// Check if design has simulations defined (version 1.4) // Check if design has simulations defined (version 1.4)
if (document.getSimulationCount() > 0) { if (document.getSimulationCount() > 0) {
return FILE_VERSION_DIVISOR + 4; return FILE_VERSION_DIVISOR + 4;

View File

@ -9,6 +9,7 @@ import net.sf.openrocket.file.RocketSaver;
import net.sf.openrocket.material.Material; import net.sf.openrocket.material.Material;
import net.sf.openrocket.motor.Motor; import net.sf.openrocket.motor.Motor;
import net.sf.openrocket.motor.ThrustCurveMotor; import net.sf.openrocket.motor.ThrustCurveMotor;
import net.sf.openrocket.preset.ComponentPreset;
import net.sf.openrocket.rocketcomponent.ComponentAssembly; import net.sf.openrocket.rocketcomponent.ComponentAssembly;
import net.sf.openrocket.rocketcomponent.MotorMount; import net.sf.openrocket.rocketcomponent.MotorMount;
import net.sf.openrocket.rocketcomponent.Rocket; import net.sf.openrocket.rocketcomponent.Rocket;
@ -19,15 +20,22 @@ import net.sf.openrocket.util.LineStyle;
public class RocketComponentSaver { public class RocketComponentSaver {
protected RocketComponentSaver() { protected RocketComponentSaver() {
// Prevent instantiation from outside the package // Prevent instantiation from outside the package
} }
protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List<String> elements) { protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List<String> elements) {
elements.add("<name>" + RocketSaver.escapeXML(c.getName()) + "</name>"); elements.add("<name>" + RocketSaver.escapeXML(c.getName()) + "</name>");
ComponentPreset preset = c.getPresetComponent();
if ( preset != null ) {
elements.add("<preset type=\"" + preset.getType() +
"\" manufacturer=\"" + preset.getManufacturer().getSimpleName() +
"\" partno=\"" + preset.getPartNo() + "\" digest=\"" + preset.getDigest() +"\"/>");
}
// Save color and line style if significant // Save color and line style if significant
if (!(c instanceof Rocket || c instanceof ComponentAssembly)) { if (!(c instanceof Rocket || c instanceof ComponentAssembly)) {
Color color = c.getColor(); Color color = c.getColor();
@ -35,23 +43,23 @@ public class RocketComponentSaver {
elements.add("<color red=\"" + color.getRed() + "\" green=\"" + color.getGreen() elements.add("<color red=\"" + color.getRed() + "\" green=\"" + color.getGreen()
+ "\" blue=\"" + color.getBlue() + "\"/>"); + "\" blue=\"" + color.getBlue() + "\"/>");
} }
LineStyle style = c.getLineStyle(); LineStyle style = c.getLineStyle();
if (style != null) { if (style != null) {
// Type names currently equivalent to the enum names except for case. // Type names currently equivalent to the enum names except for case.
elements.add("<linestyle>" + style.name().toLowerCase(Locale.ENGLISH) + "</linestyle>"); elements.add("<linestyle>" + style.name().toLowerCase(Locale.ENGLISH) + "</linestyle>");
} }
} }
// Save position unless "AFTER" // Save position unless "AFTER"
if (c.getRelativePosition() != RocketComponent.Position.AFTER) { if (c.getRelativePosition() != RocketComponent.Position.AFTER) {
// The type names are currently equivalent to the enum names except for case. // The type names are currently equivalent to the enum names except for case.
String type = c.getRelativePosition().name().toLowerCase(Locale.ENGLISH); String type = c.getRelativePosition().name().toLowerCase(Locale.ENGLISH);
elements.add("<position type=\"" + type + "\">" + c.getPositionValue() + "</position>"); elements.add("<position type=\"" + type + "\">" + c.getPositionValue() + "</position>");
} }
// Overrides // Overrides
boolean overridden = false; boolean overridden = false;
if (c.isMassOverridden()) { if (c.isMassOverridden()) {
@ -66,26 +74,26 @@ public class RocketComponentSaver {
elements.add("<overridesubcomponents>" + c.getOverrideSubcomponents() elements.add("<overridesubcomponents>" + c.getOverrideSubcomponents()
+ "</overridesubcomponents>"); + "</overridesubcomponents>");
} }
// Comment // Comment
if (c.getComment().length() > 0) { if (c.getComment().length() > 0) {
elements.add("<comment>" + RocketSaver.escapeXML(c.getComment()) + "</comment>"); elements.add("<comment>" + RocketSaver.escapeXML(c.getComment()) + "</comment>");
} }
} }
protected final String materialParam(Material mat) { protected final String materialParam(Material mat) {
return materialParam("material", mat); return materialParam("material", mat);
} }
protected final String materialParam(String tag, Material mat) { protected final String materialParam(String tag, Material mat) {
String str = "<" + tag; String str = "<" + tag;
switch (mat.getType()) { switch (mat.getType()) {
case LINE: case LINE:
str += " type=\"line\""; str += " type=\"line\"";
@ -99,27 +107,27 @@ public class RocketComponentSaver {
default: default:
throw new BugException("Unknown material type: " + mat.getType()); throw new BugException("Unknown material type: " + mat.getType());
} }
return str + " density=\"" + mat.getDensity() + "\">" + RocketSaver.escapeXML(mat.getName()) + "</" + tag + ">"; return str + " density=\"" + mat.getDensity() + "\">" + RocketSaver.escapeXML(mat.getName()) + "</" + tag + ">";
} }
protected final List<String> motorMountParams(MotorMount mount) { protected final List<String> motorMountParams(MotorMount mount) {
if (!mount.isMotorMount()) if (!mount.isMotorMount())
return Collections.emptyList(); return Collections.emptyList();
String[] motorConfigIDs = ((RocketComponent) mount).getRocket().getMotorConfigurationIDs(); String[] motorConfigIDs = ((RocketComponent) mount).getRocket().getMotorConfigurationIDs();
List<String> elements = new ArrayList<String>(); List<String> elements = new ArrayList<String>();
elements.add("<motormount>"); elements.add("<motormount>");
for (String id : motorConfigIDs) { for (String id : motorConfigIDs) {
Motor motor = mount.getMotor(id); Motor motor = mount.getMotor(id);
// Nothing is stored if no motor loaded // Nothing is stored if no motor loaded
if (motor == null) if (motor == null)
continue; continue;
elements.add(" <motor configid=\"" + id + "\">"); elements.add(" <motor configid=\"" + id + "\">");
if (motor.getMotorType() != Motor.Type.UNKNOWN) { if (motor.getMotorType() != Motor.Type.UNKNOWN) {
elements.add(" <type>" + motor.getMotorType().name().toLowerCase(Locale.ENGLISH) + "</type>"); elements.add(" <type>" + motor.getMotorType().name().toLowerCase(Locale.ENGLISH) + "</type>");
@ -133,27 +141,27 @@ public class RocketComponentSaver {
elements.add(" <designation>" + RocketSaver.escapeXML(motor.getDesignation()) + "</designation>"); elements.add(" <designation>" + RocketSaver.escapeXML(motor.getDesignation()) + "</designation>");
elements.add(" <diameter>" + motor.getDiameter() + "</diameter>"); elements.add(" <diameter>" + motor.getDiameter() + "</diameter>");
elements.add(" <length>" + motor.getLength() + "</length>"); elements.add(" <length>" + motor.getLength() + "</length>");
// Motor delay // Motor delay
if (mount.getMotorDelay(id) == Motor.PLUGGED) { if (mount.getMotorDelay(id) == Motor.PLUGGED) {
elements.add(" <delay>none</delay>"); elements.add(" <delay>none</delay>");
} else { } else {
elements.add(" <delay>" + mount.getMotorDelay(id) + "</delay>"); elements.add(" <delay>" + mount.getMotorDelay(id) + "</delay>");
} }
elements.add(" </motor>"); elements.add(" </motor>");
} }
elements.add(" <ignitionevent>" elements.add(" <ignitionevent>"
+ mount.getIgnitionEvent().name().toLowerCase(Locale.ENGLISH).replace("_", "") + mount.getIgnitionEvent().name().toLowerCase(Locale.ENGLISH).replace("_", "")
+ "</ignitionevent>"); + "</ignitionevent>");
elements.add(" <ignitiondelay>" + mount.getIgnitionDelay() + "</ignitiondelay>"); elements.add(" <ignitiondelay>" + mount.getIgnitionDelay() + "</ignitiondelay>");
elements.add(" <overhang>" + mount.getMotorOverhang() + "</overhang>"); elements.add(" <overhang>" + mount.getMotorOverhang() + "</overhang>");
elements.add("</motormount>"); elements.add("</motormount>");
return elements; return elements;
} }
} }

View File

@ -1,6 +1,13 @@
package net.sf.openrocket.preset; package net.sf.openrocket.preset;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import net.sf.openrocket.material.Material; import net.sf.openrocket.material.Material;
@ -9,6 +16,7 @@ import net.sf.openrocket.rocketcomponent.BodyTube;
import net.sf.openrocket.rocketcomponent.ExternalComponent.Finish; import net.sf.openrocket.rocketcomponent.ExternalComponent.Finish;
import net.sf.openrocket.unit.UnitGroup; import net.sf.openrocket.unit.UnitGroup;
import net.sf.openrocket.util.BugException; import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.TextUtil;
/** /**
@ -21,37 +29,38 @@ import net.sf.openrocket.util.BugException;
*/ */
// FIXME - Implement clone. // FIXME - Implement clone.
public class ComponentPreset implements Comparable<ComponentPreset> { public class ComponentPreset implements Comparable<ComponentPreset> {
private final TypedPropertyMap properties = new TypedPropertyMap(); private final TypedPropertyMap properties = new TypedPropertyMap();
private boolean favorite = false; private boolean favorite = false;
private String digest = "";
public enum Type { public enum Type {
BODY_TUBE, BODY_TUBE,
NOSE_CONE; NOSE_CONE;
Type[] compatibleTypes; Type[] compatibleTypes;
Type () { Type () {
compatibleTypes = new Type[1]; compatibleTypes = new Type[1];
compatibleTypes[0] = this; compatibleTypes[0] = this;
} }
Type( Type ... t ) { Type( Type ... t ) {
compatibleTypes = new Type[t.length+1]; compatibleTypes = new Type[t.length+1];
compatibleTypes[0] = this; compatibleTypes[0] = this;
for( int i=0; i<t.length; i++ ) { for( int i=0; i<t.length; i++ ) {
compatibleTypes[i+1] = t[i]; compatibleTypes[i+1] = t[i];
} }
} }
public Type[] getCompatibleTypes() { public Type[] getCompatibleTypes() {
return compatibleTypes; return compatibleTypes;
} }
} }
public final static TypedKey<Manufacturer> MANUFACTURER = new TypedKey<Manufacturer>("Manufacturer", Manufacturer.class); public final static TypedKey<Manufacturer> MANUFACTURER = new TypedKey<Manufacturer>("Manufacturer", Manufacturer.class);
public final static TypedKey<String> PARTNO = new TypedKey<String>("PartNo",String.class); public final static TypedKey<String> PARTNO = new TypedKey<String>("PartNo",String.class);
public final static TypedKey<Type> TYPE = new TypedKey<Type>("Type",Type.class); public final static TypedKey<Type> TYPE = new TypedKey<Type>("Type",Type.class);
@ -63,7 +72,7 @@ public class ComponentPreset implements Comparable<ComponentPreset> {
public final static TypedKey<Double> THICKNESS = new TypedKey<Double>("Thickness", Double.class, UnitGroup.UNITS_LENGTH); public final static TypedKey<Double> THICKNESS = new TypedKey<Double>("Thickness", Double.class, UnitGroup.UNITS_LENGTH);
public final static TypedKey<Boolean> FILLED = new TypedKey<Boolean>("Filled", Boolean.class); public final static TypedKey<Boolean> FILLED = new TypedKey<Boolean>("Filled", Boolean.class);
public final static TypedKey<Double> MASS = new TypedKey<Double>("Mass", Double.class, UnitGroup.UNITS_MASS); public final static TypedKey<Double> MASS = new TypedKey<Double>("Mass", Double.class, UnitGroup.UNITS_MASS);
public final static Map<String, TypedKey<?>> keyMap = new HashMap<String, TypedKey<?>>(); public final static Map<String, TypedKey<?>> keyMap = new HashMap<String, TypedKey<?>>();
static { static {
keyMap.put(MANUFACTURER.getName(), MANUFACTURER); keyMap.put(MANUFACTURER.getName(), MANUFACTURER);
@ -78,15 +87,15 @@ public class ComponentPreset implements Comparable<ComponentPreset> {
keyMap.put(FILLED.getName(), FILLED); keyMap.put(FILLED.getName(), FILLED);
keyMap.put(MASS.getName(), MASS); keyMap.put(MASS.getName(), MASS);
} }
public static ComponentPreset create( TypedPropertyMap props ) throws InvalidComponentPresetException { public static ComponentPreset create( TypedPropertyMap props ) throws InvalidComponentPresetException {
ComponentPreset preset = new ComponentPreset(); ComponentPreset preset = new ComponentPreset();
// First do validation. // First do validation.
if ( !props.containsKey(TYPE)) { if ( !props.containsKey(TYPE)) {
throw new InvalidComponentPresetException("No Type specified " + props.toString() ); throw new InvalidComponentPresetException("No Type specified " + props.toString() );
} }
if (!props.containsKey(MANUFACTURER)) { if (!props.containsKey(MANUFACTURER)) {
throw new InvalidComponentPresetException("No Manufacturer specified " + props.toString() ); throw new InvalidComponentPresetException("No Manufacturer specified " + props.toString() );
} }
@ -96,25 +105,25 @@ public class ComponentPreset implements Comparable<ComponentPreset> {
} }
preset.properties.putAll(props); preset.properties.putAll(props);
// Should check for various bits of each of the types. // Should check for various bits of each of the types.
Type t = props.get(TYPE); Type t = props.get(TYPE);
switch ( t ) { switch ( t ) {
case BODY_TUBE: { case BODY_TUBE: {
if ( !props.containsKey(LENGTH) ) { if ( !props.containsKey(LENGTH) ) {
throw new InvalidComponentPresetException( "No Length specified for body tube preset " + props.toString()); throw new InvalidComponentPresetException( "No Length specified for body tube preset " + props.toString());
} }
BodyTube bt = new BodyTube(); BodyTube bt = new BodyTube();
bt.setLength(props.get(LENGTH)); bt.setLength(props.get(LENGTH));
// Need to verify contains 2 of OD, thickness, ID. Compute the third. // Need to verify contains 2 of OD, thickness, ID. Compute the third.
boolean hasOd = props.containsKey(OUTER_DIAMETER); boolean hasOd = props.containsKey(OUTER_DIAMETER);
boolean hasId = props.containsKey(INNER_DIAMETER); boolean hasId = props.containsKey(INNER_DIAMETER);
boolean hasThickness = props.containsKey(THICKNESS); boolean hasThickness = props.containsKey(THICKNESS);
if ( hasOd ) { if ( hasOd ) {
double outerRadius = props.get(OUTER_DIAMETER)/2.0; double outerRadius = props.get(OUTER_DIAMETER)/2.0;
double thickness = 0; double thickness = 0;
@ -140,7 +149,7 @@ public class ComponentPreset implements Comparable<ComponentPreset> {
preset.properties.put(OUTER_DIAMETER, bt.getOuterRadius() *2.0); preset.properties.put(OUTER_DIAMETER, bt.getOuterRadius() *2.0);
preset.properties.put(INNER_DIAMETER, bt.getInnerRadius() *2.0); preset.properties.put(INNER_DIAMETER, bt.getInnerRadius() *2.0);
preset.properties.put(THICKNESS, bt.getThickness()); preset.properties.put(THICKNESS, bt.getThickness());
// Need to translate Mass to Density. // Need to translate Mass to Density.
if ( props.containsKey(MASS) ) { if ( props.containsKey(MASS) ) {
String materialName = "TubeCustom"; String materialName = "TubeCustom";
@ -150,35 +159,57 @@ public class ComponentPreset implements Comparable<ComponentPreset> {
Material m = Material.newMaterial(Material.Type.BULK, materialName, props.get(MASS)/bt.getComponentVolume(), false); Material m = Material.newMaterial(Material.Type.BULK, materialName, props.get(MASS)/bt.getComponentVolume(), false);
preset.properties.put(MATERIAL, m); preset.properties.put(MATERIAL, m);
} }
break; break;
} }
case NOSE_CONE: { case NOSE_CONE: {
break; break;
} }
} }
preset.computeDigest();
return preset; return preset;
} }
// Private constructor to encourage use of factory. // Private constructor to encourage use of factory.
private ComponentPreset() { private ComponentPreset() {
} }
/**
* Convenience method to retrieve the Type of this ComponentPreset.
*
* @return
*/
public Type getType() {
return properties.get(TYPE);
}
/**
* Convenience method to retrieve the Manufacturer of this ComponentPreset.
* @return
*/
public Manufacturer getManufacturer() { public Manufacturer getManufacturer() {
return properties.get(MANUFACTURER); return properties.get(MANUFACTURER);
} }
/**
* Convenience method to retrieve the PartNo of this ComponentPreset.
* @return
*/
public String getPartNo() { public String getPartNo() {
return properties.get(PARTNO); return properties.get(PARTNO);
} }
public String getDigest() {
return digest;
}
public boolean has(Object key) { public boolean has(Object key) {
return properties.containsKey(key); return properties.containsKey(key);
} }
public <T> T get(TypedKey<T> key) { public <T> T get(TypedKey<T> key) {
T value = properties.get(key); T value = properties.get(key);
if (value == null) { if (value == null) {
@ -186,7 +217,7 @@ public class ComponentPreset implements Comparable<ComponentPreset> {
} }
return (T) value; return (T) value;
} }
public boolean isFavorite() { public boolean isFavorite() {
return favorite; return favorite;
} }
@ -200,7 +231,7 @@ public class ComponentPreset implements Comparable<ComponentPreset> {
int manuCompare = this.getManufacturer().getSimpleName().compareTo(p2.getManufacturer().getSimpleName()); int manuCompare = this.getManufacturer().getSimpleName().compareTo(p2.getManufacturer().getSimpleName());
if ( manuCompare != 0 ) if ( manuCompare != 0 )
return manuCompare; return manuCompare;
int partNoCompare = this.getPartNo().compareTo(p2.getPartNo()); int partNoCompare = this.getPartNo().compareTo(p2.getPartNo());
return partNoCompare; return partNoCompare;
} }
@ -209,9 +240,63 @@ public class ComponentPreset implements Comparable<ComponentPreset> {
public String toString() { public String toString() {
return get(MANUFACTURER).toString() + " " + get(PARTNO); return get(MANUFACTURER).toString() + " " + get(PARTNO);
} }
public String preferenceKey() { public String preferenceKey() {
return get(MANUFACTURER).toString() + "|" + get(PARTNO); return get(MANUFACTURER).toString() + "|" + get(PARTNO);
} }
private void computeDigest() {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream os = new DataOutputStream(bos);
List<TypedKey<?>> keys = new ArrayList<TypedKey<?>>( properties.keySet());
Collections.sort(keys, new Comparator<TypedKey<?>>() {
@Override
public int compare( TypedKey<?> a, TypedKey<?> b ) {
return a.getName().compareTo(b.getName());
}
});
for ( TypedKey<?> key : keys ) {
Object value = properties.get(key);
os.writeBytes(key.getName());
if ( key.getType() == Double.class ) {
Double d = (Double) value;
os.writeDouble(d);
} else if (key.getType() == String.class ) {
String s = (String) value;
os.writeBytes(s);
} else if (key.getType() == Manufacturer.class ) {
String s = ((Manufacturer)value).getSimpleName();
os.writeBytes(s);
} else if ( key.getType() == Finish.class ) {
String s = ((Finish)value).name();
os.writeBytes(s);
} else if ( key.getType() == Type.class ) {
String s = ((Type)value).name();
os.writeBytes(s);
} else if ( key.getType() == Boolean.class ) {
Boolean b = (Boolean) value;
os.writeBoolean(b);
} else if ( key.getType() == Material.class ) {
double d = ((Material)value).getDensity();
os.writeDouble(d);
}
}
MessageDigest md5 = MessageDigest.getInstance("MD5");
digest = TextUtil.hexString(md5.digest( bos.toByteArray() ));
}
catch ( Exception e ) {
throw new BugException(e);
}
}
} }