package net.sf.openrocket.material; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.startup.Application; import net.sf.openrocket.unit.Unit; import net.sf.openrocket.unit.UnitGroup; import net.sf.openrocket.util.MathUtil; /** * A class for different material types. Each material has a name and density. * The interpretation of the density depends on the material type. For * {@link Type#BULK} it is kg/m^3, for {@link Type#SURFACE} km/m^2. * <p> * Objects of this type are immutable. * * @author Sampo Niskanen <sampo.niskanen@iki.fi> */ public abstract class Material implements Comparable<Material> { private static final Translator trans = Application.getTranslator(); public enum Type { LINE("Databases.materials.types.Line", UnitGroup.UNITS_DENSITY_LINE), SURFACE("Databases.materials.types.Surface", UnitGroup.UNITS_DENSITY_SURFACE), BULK("Databases.materials.types.Bulk", UnitGroup.UNITS_DENSITY_BULK); private final String name; private final UnitGroup units; private Type(String nameKey, UnitGroup units) { this.name = trans.get(nameKey); this.units = units; } public UnitGroup getUnitGroup() { return units; } @Override public String toString() { return name; } } ///// Definitions of different material types ///// public static class Line extends Material { Line(String name, double density, boolean userDefined) { super(name, density, userDefined); } @Override public Type getType() { return Type.LINE; } } public static class Surface extends Material { Surface(String name, double density, boolean userDefined) { super(name, density, userDefined); } @Override public Type getType() { return Type.SURFACE; } @Override public String toStorableString() { return super.toStorableString(); } } public static class Bulk extends Material { Bulk(String name, double density, boolean userDefined) { super(name, density, userDefined); } @Override public Type getType() { return Type.BULK; } } private final String name; private final double density; private final boolean userDefined; /** * Constructor for materials. * * @param name ignored when defining system materials. * @param key ignored when defining user materials. * @param density * @param userDefined true if this is a user defined material, false if it is a system material. */ private Material(String name, double density, boolean userDefined) { this.name = name; this.userDefined = userDefined; this.density = density; } public double getDensity() { return density; } public String getName() { return name; } public String getName(Unit u) { return name + " (" + u.toStringUnit(density) + ")"; } public boolean isUserDefined() { return userDefined; } public abstract Type getType(); @Override public String toString() { return this.getName(this.getType().getUnitGroup().getDefaultUnit()); } /** * Compares this object to another object. Material objects are equal if and only if * their types, names and densities are identical. */ @Override public boolean equals(Object o) { if (o == null) return false; if (this.getClass() != o.getClass()) return false; Material m = (Material) o; return ((m.name.equals(this.name)) && MathUtil.equals(m.density, this.density)); } /** * A hashCode() method giving a hash code compatible with the equals() method. */ @Override public int hashCode() { return name.hashCode() + (int) (density * 1000); } /** * Order the materials according to their name, secondarily according to density. */ @Override public int compareTo(Material o) { int c = this.name.compareTo(o.name); if (c != 0) { return c; } else { return (int) ((this.density - o.density) * 1000); } } /** * Return a new material. The name is used as-is, without any translation. * * @param type the material type * @param name the material name * @param density the material density * @param userDefined whether the material is user-defined or not * @return the new material */ public static Material newMaterial(Type type, String name, double density, boolean userDefined) { switch (type) { case LINE: return new Material.Line(name, density, userDefined); case SURFACE: return new Material.Surface(name, density, userDefined); case BULK: return new Material.Bulk(name, density, userDefined); default: throw new IllegalArgumentException("Unknown material type: " + type); } } public String toStorableString() { return getType().name() + "|" + name.replace('|', ' ') + '|' + density; } /** * Return a material defined by the provided string. * * @param str the material storage string. * @param userDefined whether the created material is user-defined. * @return a new <code>Material</code> object. * @throws IllegalArgumentException if <code>str</code> is invalid or null. */ public static Material fromStorableString(String str, boolean userDefined) { if (str == null) throw new IllegalArgumentException("Material string is null"); String[] split = str.split("\\|", 3); if (split.length < 3) throw new IllegalArgumentException("Illegal material string: " + str); Type type = null; String name; double density; try { type = Type.valueOf(split[0]); } catch (Exception e) { throw new IllegalArgumentException("Illegal material string: " + str, e); } name = split[1]; try { density = Double.parseDouble(split[2]); } catch (NumberFormatException e) { throw new IllegalArgumentException("Illegal material string: " + str, e); } switch (type) { case BULK: return new Material.Bulk(name, density, userDefined); case SURFACE: return new Material.Surface(name, density, userDefined); case LINE: return new Material.Line(name, density, userDefined); default: throw new IllegalArgumentException("Illegal material string: " + str); } } }