+package net.sf.openrocket.startup;\r
+\r
+import java.util.HashMap;\r
+import java.util.Map;\r
+\r
+import net.sf.openrocket.database.Databases;\r
+import net.sf.openrocket.l10n.Translator;\r
+import net.sf.openrocket.material.Material;\r
+import net.sf.openrocket.rocketcomponent.MassObject;\r
+import net.sf.openrocket.rocketcomponent.RocketComponent;\r
+import net.sf.openrocket.util.BugException;\r
+import net.sf.openrocket.util.BuildProperties;\r
+import net.sf.openrocket.util.Color;\r
+import net.sf.openrocket.util.LineStyle;\r
+import net.sf.openrocket.util.MathUtil;\r
+import net.sf.openrocket.util.UniqueID;\r
+\r
+public abstract class Preferences {\r
+\r
+ /*\r
+ * Well known string keys to preferences.\r
+ * There are other strings out there in the source as well.\r
+ */\r
+ public static final String BODY_COMPONENT_INSERT_POSITION_KEY = "BodyComponentInsertPosition";\r
+ public static final String USER_THRUST_CURVES_KEY = "UserThrustCurves";\r
+ public static final String CONFIRM_DELETE_SIMULATION = "ConfirmDeleteSimulation";\r
+ // Preferences related to data export\r
+ public static final String EXPORT_FIELD_SEPARATOR = "ExportFieldSeparator";\r
+ public static final String EXPORT_SIMULATION_COMMENT = "ExportSimulationComment";\r
+ public static final String EXPORT_FIELD_NAME_COMMENT = "ExportFieldDescriptionComment";\r
+ public static final String EXPORT_EVENT_COMMENTS = "ExportEventComments";\r
+ public static final String EXPORT_COMMENT_CHARACTER = "ExportCommentCharacter";\r
+ public static final String USER_LOCAL = "locale";\r
+ \r
+ public static final String PLOT_SHOW_POINTS = "ShowPlotPoints";\r
+ \r
+ private static final String CHECK_UPDATES = "CheckUpdates";\r
+ public static final String LAST_UPDATE = "LastUpdateVersion";\r
+ \r
+ public static final String MOTOR_DIAMETER_FILTER = "MotorDiameterMatch";\r
+ public static final String MOTOR_HIDE_SIMILAR = "MotorHideSimilar";\r
+\r
+ // Node names\r
+ public static final String PREFERRED_THRUST_CURVE_MOTOR_NODE = "preferredThrustCurveMotors";\r
+\r
+ /*\r
+ * ******************************************************************************************\r
+ * \r
+ * Abstract methods which must be implemented by any derived class.\r
+ */\r
+ public abstract boolean getBoolean( String key, boolean defaultValue );\r
+ public abstract void putBoolean( String key, boolean value );\r
+\r
+ public abstract int getInt( String key, int defaultValue);\r
+ public abstract void putInt( String key, int value );\r
+\r
+ public abstract double getDouble( String key, double defaultValue );\r
+ public abstract void putDouble( String key, double value );\r
+\r
+ public abstract String getString( String key, String defaultValue );\r
+ public abstract void putString( String key, String value );\r
+ \r
+ /**\r
+ * Directory represents a way to collect multiple keys together. Implementors may\r
+ * choose to concatenate the directory with the key using some special character.\r
+ * @param directory\r
+ * @param key\r
+ * @param defaultValue\r
+ * @return\r
+ */\r
+ public abstract String getString( String directory, String key, String defaultValue);\r
+\r
+ public abstract void putString( String directory, String key, String value );\r
+\r
+ /*\r
+ * ******************************************************************************************\r
+ */\r
+ public final boolean getCheckUpdates() {\r
+ return this.getBoolean(CHECK_UPDATES, BuildProperties.getDefaultCheckUpdates());\r
+ }\r
+ \r
+ public final void setCheckUpdates(boolean check) {\r
+ this.putBoolean(CHECK_UPDATES, check);\r
+ }\r
+ \r
+ public final double getDefaultMach() {\r
+ // TODO: HIGH: implement custom default mach number\r
+ return 0.3;\r
+ }\r
+ \r
+ /**\r
+ * Return the OpenRocket unique ID.\r
+ * \r
+ * @return a random ID string that stays constant between OpenRocket executions\r
+ */\r
+ public final String getUniqueID() {\r
+ String id = this.getString("id", null);\r
+ if (id == null) {\r
+ id = UniqueID.uuid();\r
+ this.putString("id", id);\r
+ }\r
+ return id;\r
+ }\r
+ \r
+ /**\r
+ * Returns a limited-range integer value from the preferences. If the value \r
+ * in the preferences is negative or greater than max, then the default value \r
+ * is returned.\r
+ * \r
+ * @param key The preference to retrieve.\r
+ * @param max Maximum allowed value for the choice.\r
+ * @param def Default value.\r
+ * @return The preference value.\r
+ */\r
+ public final int getChoice(String key, int max, int def) {\r
+ int v = this.getInt(key, def);\r
+ if ((v < 0) || (v > max))\r
+ return def;\r
+ return v;\r
+ }\r
+ \r
+ /**\r
+ * Helper method that puts an integer choice value into the preferences.\r
+ * \r
+ * @param key the preference key.\r
+ * @param value the value to store.\r
+ */\r
+ public final void putChoice(String key, int value) {\r
+ this.putInt(key, value);\r
+ }\r
+ \r
+ /**\r
+ * Retrieve an enum value from the user preferences.\r
+ * \r
+ * @param <T> the enum type\r
+ * @param key the key\r
+ * @param def the default value, cannot be null\r
+ * @return the value in the preferences, or the default value\r
+ */\r
+ public final <T extends Enum<T>> T getEnum(String key, T def) {\r
+ if (def == null) {\r
+ throw new BugException("Default value cannot be null");\r
+ }\r
+ \r
+ String value = getString(key, null);\r
+ if (value == null) {\r
+ return def;\r
+ }\r
+ \r
+ try {\r
+ return Enum.valueOf(def.getDeclaringClass(), value);\r
+ } catch (IllegalArgumentException e) {\r
+ return def;\r
+ }\r
+ }\r
+ \r
+ /**\r
+ * Store an enum value to the user preferences.\r
+ * \r
+ * @param key the key\r
+ * @param value the value to store, or null to remove the value\r
+ */\r
+ public final void putEnum(String key, Enum<?> value) {\r
+ if (value == null) {\r
+ putString(key, null);\r
+ } else {\r
+ putString(key, value.name());\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Retrieve a Line style for the given component.\r
+ * @param c\r
+ * @return\r
+ */\r
+ public final LineStyle getDefaultLineStyle(Class<? extends RocketComponent> c) {\r
+ String value = get("componentStyle", c, DEFAULT_LINE_STYLES);\r
+ try {\r
+ return LineStyle.valueOf(value);\r
+ } catch (Exception e) {\r
+ return LineStyle.SOLID;\r
+ }\r
+ }\r
+ \r
+ /**\r
+ * Set a default line style for the given component.\r
+ * @param c\r
+ * @param style\r
+ */\r
+ public final void setDefaultLineStyle(Class<? extends RocketComponent> c,\r
+ LineStyle style) {\r
+ if (style == null)\r
+ return;\r
+ putString("componentStyle", c.getSimpleName(), style.name());\r
+ }\r
+ \r
+ /**\r
+ * Get the default material type for the given component.\r
+ * @param componentClass\r
+ * @param type the Material.Type to return.\r
+ * @return\r
+ */\r
+ public Material getDefaultComponentMaterial(\r
+ Class<? extends RocketComponent> componentClass,\r
+ Material.Type type) {\r
+ \r
+ String material = get("componentMaterials", componentClass, null);\r
+ if (material != null) {\r
+ try {\r
+ Material m = Material.fromStorableString(material, false);\r
+ if (m.getType() == type)\r
+ return m;\r
+ } catch (IllegalArgumentException ignore) {\r
+ }\r
+ }\r
+ \r
+ switch (type) {\r
+ case LINE:\r
+ return DefaultMaterialHolder.DEFAULT_LINE_MATERIAL;\r
+ case SURFACE:\r
+ return DefaultMaterialHolder.DEFAULT_SURFACE_MATERIAL;\r
+ case BULK:\r
+ return DefaultMaterialHolder.DEFAULT_BULK_MATERIAL;\r
+ }\r
+ throw new IllegalArgumentException("Unknown material type: " + type);\r
+ }\r
+ \r
+ /**\r
+ * Set the default material for a component type.\r
+ * @param componentClass\r
+ * @param material\r
+ */\r
+ public void setDefaultComponentMaterial(\r
+ Class<? extends RocketComponent> componentClass, Material material) {\r
+ \r
+ putString("componentMaterials", componentClass.getSimpleName(),\r
+ material == null ? null : material.toStorableString());\r
+ }\r
+\r
+ /**\r
+ * get a net.sf.openrocket.util.Color object for the given key.\r
+ * @param key\r
+ * @param defaultValue\r
+ * @return\r
+ */\r
+ public final Color getColor( String key, Color defaultValue ) {\r
+ Color c = parseColor( getString(key,null) );\r
+ if ( c == null ) {\r
+ return defaultValue;\r
+ }\r
+ return c;\r
+ }\r
+ \r
+ /**\r
+ * set a net.sf.openrocket.util.Color preference value for the given key.\r
+ * @param key\r
+ * @param value\r
+ */\r
+ public final void putColor( String key, Color value ) {\r
+ putString( key, stringifyColor(value) );\r
+ }\r
+\r
+ /**\r
+ * Helper function to convert a string representation into a net.sf.openrocket.util.Color object.\r
+ * @param color\r
+ * @return\r
+ */\r
+ protected static Color parseColor(String color) {\r
+ if (color == null) {\r
+ return null;\r
+ }\r
+ \r
+ String[] rgb = color.split(",");\r
+ if (rgb.length == 3) {\r
+ try {\r
+ int red = MathUtil.clamp(Integer.parseInt(rgb[0]), 0, 255);\r
+ int green = MathUtil.clamp(Integer.parseInt(rgb[1]), 0, 255);\r
+ int blue = MathUtil.clamp(Integer.parseInt(rgb[2]), 0, 255);\r
+ return new Color(red, green, blue);\r
+ } catch (NumberFormatException ignore) {\r
+ }\r
+ }\r
+ return null;\r
+ }\r
+ \r
+ /**\r
+ * Helper function to convert a net.sf.openrocket.util.Color object into a\r
+ * String before storing in a preference.\r
+ * @param color\r
+ * @return\r
+ */\r
+ protected static String stringifyColor(Color color) {\r
+ String string = color.getRed() + "," + color.getGreen() + "," + color.getBlue();\r
+ return string;\r
+ }\r
+\r
+ /**\r
+ * Special helper function which allows for a map of default values.\r
+ * \r
+ * First getString(directory,componentClass.getSimpleName(), null) is invoked,\r
+ * if the returned value is null, the defaultMap is consulted for a value.\r
+ * \r
+ * @param directory\r
+ * @param componentClass\r
+ * @param defaultMap\r
+ * @return\r
+ */\r
+ protected String get(String directory,\r
+ Class<? extends RocketComponent> componentClass,\r
+ Map<Class<?>, String> defaultMap) {\r
+ \r
+ // Search preferences\r
+ Class<?> c = componentClass;\r
+ while (c != null && RocketComponent.class.isAssignableFrom(c)) {\r
+ String value = this.getString(directory, c.getSimpleName(), null);\r
+ if (value != null)\r
+ return value;\r
+ c = c.getSuperclass();\r
+ }\r
+ \r
+ if (defaultMap == null)\r
+ return null;\r
+ \r
+ // Search defaults\r
+ c = componentClass;\r
+ while (RocketComponent.class.isAssignableFrom(c)) {\r
+ String value = defaultMap.get(c);\r
+ if (value != null)\r
+ return value;\r
+ c = c.getSuperclass();\r
+ }\r
+ \r
+ return null;\r
+ }\r
+\r
+ /*\r
+ * Map of default line styles\r
+ */\r
+ private static final HashMap<Class<?>, String> DEFAULT_LINE_STYLES =\r
+ new HashMap<Class<?>, String>();\r
+ static {\r
+ DEFAULT_LINE_STYLES.put(RocketComponent.class, LineStyle.SOLID.name());\r
+ DEFAULT_LINE_STYLES.put(MassObject.class, LineStyle.DASHED.name());\r
+ }\r
+ \r
+ /*\r
+ * Within a holder class so they will load only when needed.\r
+ */\r
+ private static class DefaultMaterialHolder {\r
+ private static final Translator trans = Application.getTranslator();\r
+ \r
+ //// Elastic cord (round 2mm, 1/16 in)\r
+ private static final Material DEFAULT_LINE_MATERIAL =\r
+ Databases.findMaterial(Material.Type.LINE, trans.get("Databases.materials.Elasticcordround2mm"),\r
+ 0.0018, false);\r
+ //// Ripstop nylon\r
+ private static final Material DEFAULT_SURFACE_MATERIAL =\r
+ Databases.findMaterial(Material.Type.SURFACE, trans.get("Databases.materials.Ripstopnylon"), 0.067, false);\r
+ //// Cardboard\r
+ private static final Material DEFAULT_BULK_MATERIAL =\r
+ Databases.findMaterial(Material.Type.BULK, trans.get("Databases.materials.Cardboard"), 680, false);\r
+ }\r
+ \r
+\r
+\r
+}\r