1 package net.sf.openrocket.database;
3 import java.text.Collator;
4 import java.util.Collections;
5 import java.util.Comparator;
6 import java.util.IdentityHashMap;
8 import java.util.Locale;
10 import java.util.regex.Matcher;
11 import java.util.regex.Pattern;
13 import net.sf.openrocket.motor.DesignationComparator;
14 import net.sf.openrocket.motor.Manufacturer;
15 import net.sf.openrocket.motor.Motor;
16 import net.sf.openrocket.motor.Motor.Type;
17 import net.sf.openrocket.motor.ThrustCurveMotor;
18 import net.sf.openrocket.util.ArrayList;
19 import net.sf.openrocket.util.MathUtil;
21 public class ThrustCurveMotorSet implements Comparable<ThrustCurveMotorSet> {
24 private static final Collator COLLATOR = Collator.getInstance(Locale.US);
26 COLLATOR.setStrength(Collator.PRIMARY);
28 private static final DesignationComparator DESIGNATION_COMPARATOR = new DesignationComparator();
29 private static final ThrustCurveMotorComparator comparator = new ThrustCurveMotorComparator();
33 private final ArrayList<ThrustCurveMotor> motors = new ArrayList<ThrustCurveMotor>();
34 private final Map<ThrustCurveMotor, String> digestMap =
35 new IdentityHashMap<ThrustCurveMotor, String>();
37 private final List<Double> delays = new ArrayList<Double>();
39 private Manufacturer manufacturer = null;
40 private String designation = null;
41 private String simplifiedDesignation = null;
42 private double diameter = -1;
43 private double length = -1;
44 private Motor.Type type = Motor.Type.UNKNOWN;
48 public void addMotor(ThrustCurveMotor motor) {
50 // Check for first insertion
51 if (motors.isEmpty()) {
52 manufacturer = motor.getManufacturer();
53 designation = motor.getDesignation();
54 simplifiedDesignation = simplifyDesignation(designation);
55 diameter = motor.getDiameter();
56 length = motor.getLength();
59 // Verify that the motor can be added
60 if (!matches(motor)) {
61 throw new IllegalArgumentException("Motor does not match the set:" +
62 " manufacturer=" + manufacturer +
63 " designation=" + designation +
64 " diameter=" + diameter +
66 " set_size=" + motors.size() +
70 // Update the type if now known
71 if (type == Motor.Type.UNKNOWN) {
72 type = motor.getMotorType();
73 // Add "Plugged" option if hybrid
74 if (type == Motor.Type.HYBRID) {
75 if (!delays.contains(Motor.PLUGGED)) {
76 delays.add(Motor.PLUGGED);
81 // Change the simplified designation if necessary
82 if (!designation.equalsIgnoreCase(motor.getDesignation().trim())) {
83 designation = simplifiedDesignation;
86 // Add the standard delays
87 for (double d : motor.getStandardDelays()) {
89 if (!delays.contains(d)) {
93 Collections.sort(delays);
96 // Check whether to add as new motor or overwrite existing
97 final String digest = motor.getDigest();
98 for (int index = 0; index < motors.size(); index++) {
99 Motor m = motors.get(index);
101 if (digest.equals(digestMap.get(m)) &&
102 motor.getDesignation().equals(m.getDesignation())) {
104 // Match found, check which one to keep (or both) based on comment
105 String newCmt = motor.getDescription().replaceAll("\\s+", " ").trim();
106 String oldCmt = m.getDescription().replaceAll("\\s+", " ").trim();
108 if (newCmt.length() == 0 || newCmt.equals(oldCmt)) {
109 // Do not replace and do not add
111 } else if (oldCmt.length() == 0) {
112 // Replace existing motor
113 motors.set(index, motor);
114 digestMap.put(motor, digest);
117 // else continue search and add both
122 // Motor not present, add it
124 digestMap.put(motor, digest);
125 Collections.sort(motors, comparator);
130 public boolean matches(ThrustCurveMotor m) {
131 if (motors.isEmpty())
134 if (manufacturer != m.getManufacturer())
137 if (!MathUtil.equals(diameter, m.getDiameter()))
140 if (!MathUtil.equals(length, m.getLength()))
143 if ((type != Type.UNKNOWN) && (m.getMotorType() != Type.UNKNOWN) &&
144 (type != m.getMotorType())) {
148 if (!simplifiedDesignation.equals(simplifyDesignation(m.getDesignation())))
155 public List<ThrustCurveMotor> getMotors() {
156 return motors.clone();
160 public int getMotorCount() {
161 return motors.size();
166 * Return the standard delays applicable to this motor type. This is a union of
167 * all the delays of the motors included in this set.
170 public List<Double> getDelays() {
171 return Collections.unmodifiableList(delays);
176 * Return the manufacturer of this motor type.
177 * @return the manufacturer
179 public Manufacturer getManufacturer() {
185 * Return the designation of this motor type. This is either the exact or simplified
186 * designation, depending on what motors have been added.
187 * @return the designation
189 public String getDesignation() {
195 * Return the diameter of this motor type.
196 * @return the diameter
198 public double getDiameter() {
204 * Return the length of this motor type.
207 public double getLength() {
213 * Return the type of this motor type. If any of the added motors has a type different
214 * from UNKNOWN, then that type will be returned.
217 public Motor.Type getType() {
225 public String toString() {
226 return "ThrustCurveMotorSet[" + manufacturer + " " + designation +
227 ", type=" + type + ", count=" + motors.size() + "]";
233 private static final Pattern SIMPLIFY_PATTERN = Pattern.compile("^[0-9]*[ -]*([A-Z][0-9]+).*");
236 * Simplify a motor designation, if possible. This attempts to reduce the designation
237 * into a simple letter + number notation for the impulse class and average thrust.
239 * @param str the designation to simplify
240 * @return the simplified designation, or the string itself if the format was not detected
242 public static String simplifyDesignation(String str) {
244 Matcher m = SIMPLIFY_PATTERN.matcher(str);
254 * Comparator for deciding in which order to display matching motors.
256 private static class ThrustCurveMotorComparator implements Comparator<ThrustCurveMotor> {
259 public int compare(ThrustCurveMotor o1, ThrustCurveMotor o2) {
261 if (!o1.getDesignation().equals(o2.getDesignation())) {
262 return o1.getDesignation().compareTo(o2.getDesignation());
265 // 2. Number of data points (more is better)
266 if (o1.getTimePoints().length != o2.getTimePoints().length) {
267 return o2.getTimePoints().length - o1.getTimePoints().length;
270 // 3. Comment length (longer is better)
271 return o2.getDescription().length() - o1.getDescription().length();
278 public int compareTo(ThrustCurveMotorSet other) {
283 value = COLLATOR.compare(this.manufacturer.getDisplayName(),
284 other.manufacturer.getDisplayName());
289 value = DESIGNATION_COMPARATOR.compare(this.designation, other.designation);
294 value = (int) ((this.diameter - other.diameter) * 1000000);
299 value = (int) ((this.length - other.length) * 1000000);