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.MotorDigest;
18 import net.sf.openrocket.motor.ThrustCurveMotor;
19 import net.sf.openrocket.util.ArrayList;
20 import net.sf.openrocket.util.MathUtil;
22 public class ThrustCurveMotorSet implements Comparable<ThrustCurveMotorSet> {
25 private static final Collator COLLATOR = Collator.getInstance(Locale.US);
27 COLLATOR.setStrength(Collator.PRIMARY);
29 private static final DesignationComparator DESIGNATION_COMPARATOR = new DesignationComparator();
30 private static final ThrustCurveMotorComparator comparator = new ThrustCurveMotorComparator();
34 private final ArrayList<ThrustCurveMotor> motors = new ArrayList<ThrustCurveMotor>();
35 private final Map<ThrustCurveMotor, String> digestMap =
36 new IdentityHashMap<ThrustCurveMotor, String>();
38 private final List<Double> delays = new ArrayList<Double>();
40 private Manufacturer manufacturer = null;
41 private String designation = null;
42 private String simplifiedDesignation = null;
43 private double diameter = -1;
44 private double length = -1;
45 private Motor.Type type = Motor.Type.UNKNOWN;
49 public void addMotor(ThrustCurveMotor motor) {
51 // Check for first insertion
52 if (motors.isEmpty()) {
53 manufacturer = motor.getManufacturer();
54 designation = motor.getDesignation();
55 simplifiedDesignation = simplifyDesignation(designation);
56 diameter = motor.getDiameter();
57 length = motor.getLength();
60 // Verify that the motor can be added
61 if (!matches(motor)) {
62 throw new IllegalArgumentException("Motor does not match the set:" +
63 " manufacturer=" + manufacturer +
64 " designation=" + designation +
65 " diameter=" + diameter +
67 " set_size=" + motors.size() +
71 // Update the type if now known
72 if (type == Motor.Type.UNKNOWN) {
73 type = motor.getMotorType();
74 // Add "Plugged" option if hybrid
75 if (type == Motor.Type.HYBRID) {
76 if (!delays.contains(Motor.PLUGGED)) {
77 delays.add(Motor.PLUGGED);
82 // Change the simplified designation if necessary
83 if (!designation.equalsIgnoreCase(motor.getDesignation().trim())) {
84 designation = simplifiedDesignation;
87 // Add the standard delays
88 for (double d : motor.getStandardDelays()) {
90 if (!delays.contains(d)) {
94 Collections.sort(delays);
97 // Check whether to add as new motor or overwrite existing
98 final String digest = MotorDigest.digestMotor(motor);
99 for (int index = 0; index < motors.size(); index++) {
100 Motor m = motors.get(index);
102 if (digest.equals(digestMap.get(m)) &&
103 motor.getDesignation().equals(m.getDesignation())) {
105 // Match found, check which one to keep (or both) based on comment
106 String newCmt = motor.getDescription().replaceAll("\\s+", " ").trim();
107 String oldCmt = m.getDescription().replaceAll("\\s+", " ").trim();
109 if (newCmt.length() == 0 || newCmt.equals(oldCmt)) {
110 // Do not replace and do not add
112 } else if (oldCmt.length() == 0) {
113 // Replace existing motor
114 motors.set(index, motor);
115 digestMap.put(motor, digest);
118 // else continue search and add both
123 // Motor not present, add it
125 digestMap.put(motor, digest);
126 Collections.sort(motors, comparator);
131 public boolean matches(ThrustCurveMotor m) {
132 if (motors.isEmpty())
135 if (manufacturer != m.getManufacturer())
138 if (!MathUtil.equals(diameter, m.getDiameter()))
141 if (!MathUtil.equals(length, m.getLength()))
144 if ((type != Type.UNKNOWN) && (m.getMotorType() != Type.UNKNOWN) &&
145 (type != m.getMotorType())) {
149 if (!simplifiedDesignation.equals(simplifyDesignation(m.getDesignation())))
156 public List<ThrustCurveMotor> getMotors() {
157 return motors.clone();
161 public int getMotorCount() {
162 return motors.size();
167 * Return the standard delays applicable to this motor type. This is a union of
168 * all the delays of the motors included in this set.
171 public List<Double> getDelays() {
172 return Collections.unmodifiableList(delays);
177 * Return the manufacturer of this motor type.
178 * @return the manufacturer
180 public Manufacturer getManufacturer() {
186 * Return the designation of this motor type. This is either the exact or simplified
187 * designation, depending on what motors have been added.
188 * @return the designation
190 public String getDesignation() {
196 * Return the diameter of this motor type.
197 * @return the diameter
199 public double getDiameter() {
205 * Return the length of this motor type.
208 public double getLength() {
214 * Return the type of this motor type. If any of the added motors has a type different
215 * from UNKNOWN, then that type will be returned.
218 public Motor.Type getType() {
226 public String toString() {
227 return "ThrustCurveMotorSet[" + manufacturer + " " + designation +
228 ", type=" + type + ", count=" + motors.size() + "]";
234 private static final Pattern SIMPLIFY_PATTERN = Pattern.compile("^[0-9]*[ -]*([A-Z][0-9]+).*");
237 * Simplify a motor designation, if possible. This attempts to reduce the designation
238 * into a simple letter + number notation for the impulse class and average thrust.
240 * @param str the designation to simplify
241 * @return the simplified designation, or the string itself if the format was not detected
243 public static String simplifyDesignation(String str) {
245 Matcher m = SIMPLIFY_PATTERN.matcher(str);
255 * Comparator for deciding in which order to display matching motors.
257 private static class ThrustCurveMotorComparator implements Comparator<ThrustCurveMotor> {
260 public int compare(ThrustCurveMotor o1, ThrustCurveMotor o2) {
262 if (!o1.getDesignation().equals(o2.getDesignation())) {
263 return o1.getDesignation().compareTo(o2.getDesignation());
266 // 2. Number of data points (more is better)
267 if (o1.getTimePoints().length != o2.getTimePoints().length) {
268 return o2.getTimePoints().length - o1.getTimePoints().length;
271 // 3. Comment length (longer is better)
272 return o2.getDescription().length() - o1.getDescription().length();
279 public int compareTo(ThrustCurveMotorSet other) {
284 value = COLLATOR.compare(this.manufacturer.getDisplayName(),
285 other.manufacturer.getDisplayName());
290 value = DESIGNATION_COMPARATOR.compare(this.designation, other.designation);
295 value = (int) ((this.diameter - other.diameter) * 1000000);
300 value = (int) ((this.length - other.length) * 1000000);