moving to core/
[debian/openrocket] / core / src / net / sf / openrocket / database / ThrustCurveMotorSet.java
1 package net.sf.openrocket.database;
2
3 import java.text.Collator;
4 import java.util.Collections;
5 import java.util.Comparator;
6 import java.util.IdentityHashMap;
7 import java.util.List;
8 import java.util.Locale;
9 import java.util.Map;
10 import java.util.regex.Matcher;
11 import java.util.regex.Pattern;
12
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;
21
22 public class ThrustCurveMotorSet implements Comparable<ThrustCurveMotorSet> {
23         
24         //  Comparators:
25         private static final Collator COLLATOR = Collator.getInstance(Locale.US);
26         static {
27                 COLLATOR.setStrength(Collator.PRIMARY);
28         }
29         private static final DesignationComparator DESIGNATION_COMPARATOR = new DesignationComparator();
30         private static final ThrustCurveMotorComparator comparator = new ThrustCurveMotorComparator();
31         
32
33
34         private final ArrayList<ThrustCurveMotor> motors = new ArrayList<ThrustCurveMotor>();
35         private final Map<ThrustCurveMotor, String> digestMap =
36                         new IdentityHashMap<ThrustCurveMotor, String>();
37         
38         private final List<Double> delays = new ArrayList<Double>();
39         
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;
46         
47         
48
49         public void addMotor(ThrustCurveMotor motor) {
50                 
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();
58                 }
59                 
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 +
66                                         " length=" + length +
67                                         " set_size=" + motors.size() +
68                                         " motor=" + motor);
69                 }
70                 
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);
78                                 }
79                         }
80                 }
81                 
82                 // Change the simplified designation if necessary
83                 if (!designation.equalsIgnoreCase(motor.getDesignation().trim())) {
84                         designation = simplifiedDesignation;
85                 }
86                 
87                 // Add the standard delays
88                 for (double d : motor.getStandardDelays()) {
89                         d = Math.rint(d);
90                         if (!delays.contains(d)) {
91                                 delays.add(d);
92                         }
93                 }
94                 Collections.sort(delays);
95                 
96
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);
101                         
102                         if (digest.equals(digestMap.get(m)) &&
103                                         motor.getDesignation().equals(m.getDesignation())) {
104                                 
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();
108                                 
109                                 if (newCmt.length() == 0 || newCmt.equals(oldCmt)) {
110                                         // Do not replace and do not add
111                                         return;
112                                 } else if (oldCmt.length() == 0) {
113                                         // Replace existing motor
114                                         motors.set(index, motor);
115                                         digestMap.put(motor, digest);
116                                         return;
117                                 }
118                                 // else continue search and add both
119                                 
120                         }
121                 }
122                 
123                 // Motor not present, add it
124                 motors.add(motor);
125                 digestMap.put(motor, digest);
126                 Collections.sort(motors, comparator);
127                 
128         }
129         
130         
131         public boolean matches(ThrustCurveMotor m) {
132                 if (motors.isEmpty())
133                         return true;
134                 
135                 if (manufacturer != m.getManufacturer())
136                         return false;
137                 
138                 if (!MathUtil.equals(diameter, m.getDiameter()))
139                         return false;
140                 
141                 if (!MathUtil.equals(length, m.getLength()))
142                         return false;
143                 
144                 if ((type != Type.UNKNOWN) && (m.getMotorType() != Type.UNKNOWN) &&
145                                 (type != m.getMotorType())) {
146                         return false;
147                 }
148                 
149                 if (!simplifiedDesignation.equals(simplifyDesignation(m.getDesignation())))
150                         return false;
151                 
152                 return true;
153         }
154         
155         
156         public List<ThrustCurveMotor> getMotors() {
157                 return motors.clone();
158         }
159         
160         
161         public int getMotorCount() {
162                 return motors.size();
163         }
164         
165         
166         /**
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.
169          * @return the delays
170          */
171         public List<Double> getDelays() {
172                 return Collections.unmodifiableList(delays);
173         }
174         
175         
176         /**
177          * Return the manufacturer of this motor type.
178          * @return the manufacturer
179          */
180         public Manufacturer getManufacturer() {
181                 return manufacturer;
182         }
183         
184         
185         /**
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
189          */
190         public String getDesignation() {
191                 return designation;
192         }
193         
194         
195         /**
196          * Return the diameter of this motor type.
197          * @return the diameter
198          */
199         public double getDiameter() {
200                 return diameter;
201         }
202         
203         
204         /**
205          * Return the length of this motor type.
206          * @return the length
207          */
208         public double getLength() {
209                 return length;
210         }
211         
212         
213         /**
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.
216          * @return the type
217          */
218         public Motor.Type getType() {
219                 return type;
220         }
221         
222         
223
224
225         @Override
226         public String toString() {
227                 return "ThrustCurveMotorSet[" + manufacturer + " " + designation +
228                                 ", type=" + type + ", count=" + motors.size() + "]";
229         }
230         
231         
232
233
234         private static final Pattern SIMPLIFY_PATTERN = Pattern.compile("^[0-9]*[ -]*([A-Z][0-9]+).*");
235         
236         /**
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.
239          * 
240          * @param str   the designation to simplify
241          * @return              the simplified designation, or the string itself if the format was not detected
242          */
243         public static String simplifyDesignation(String str) {
244                 str = str.trim();
245                 Matcher m = SIMPLIFY_PATTERN.matcher(str);
246                 if (m.matches()) {
247                         return m.group(1);
248                 } else {
249                         return str;
250                 }
251         }
252         
253         
254         /**
255          * Comparator for deciding in which order to display matching motors.
256          */
257         private static class ThrustCurveMotorComparator implements Comparator<ThrustCurveMotor> {
258                 
259                 @Override
260                 public int compare(ThrustCurveMotor o1, ThrustCurveMotor o2) {
261                         // 1. Designation
262                         if (!o1.getDesignation().equals(o2.getDesignation())) {
263                                 return o1.getDesignation().compareTo(o2.getDesignation());
264                         }
265                         
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;
269                         }
270                         
271                         // 3. Comment length (longer is better)
272                         return o2.getDescription().length() - o1.getDescription().length();
273                 }
274                 
275         }
276         
277         
278         @Override
279         public int compareTo(ThrustCurveMotorSet other) {
280                 
281                 int value;
282                 
283                 // 1. Manufacturer
284                 value = COLLATOR.compare(this.manufacturer.getDisplayName(),
285                                 other.manufacturer.getDisplayName());
286                 if (value != 0)
287                         return value;
288                 
289                 // 2. Designation
290                 value = DESIGNATION_COMPARATOR.compare(this.designation, other.designation);
291                 if (value != 0)
292                         return value;
293                 
294                 // 3. Diameter
295                 value = (int) ((this.diameter - other.diameter) * 1000000);
296                 if (value != 0)
297                         return value;
298                 
299                 // 4. Length
300                 value = (int) ((this.length - other.length) * 1000000);
301                 return value;
302                 
303         }
304         
305 }