create changelog entry
[debian/openrocket] / core / src / net / sf / openrocket / database / ThrustCurveMotorSetDatabase.java
1 package net.sf.openrocket.database;
2
3 import java.util.ArrayList;
4 import java.util.Collections;
5 import java.util.List;
6
7 import net.sf.openrocket.logging.LogHelper;
8 import net.sf.openrocket.motor.Motor;
9 import net.sf.openrocket.motor.ThrustCurveMotor;
10 import net.sf.openrocket.startup.Application;
11
12 /**
13  * A database containing ThrustCurveMotorSet objects and allowing adding a motor
14  * to the database.
15  * 
16  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
17  */
18 public abstract class ThrustCurveMotorSetDatabase implements MotorDatabase {
19         
20         private static final LogHelper logger = Application.getLogger();
21         
22         protected List<ThrustCurveMotorSet> motorSets;
23         
24         private volatile boolean startedLoading = false;
25         private volatile boolean endedLoading = false;
26         private final boolean asynchronous;
27         
28         /** Set to true the first time {@link #blockUntilLoaded()} is called. */
29         protected volatile boolean inUse = false;
30         
31         /**
32          * Sole constructor.
33          * 
34          * @param asynchronous  whether to load motors asynchronously in a background thread.
35          */
36         public ThrustCurveMotorSetDatabase(boolean asynchronous) {
37                 this.asynchronous = asynchronous;
38         }
39         
40         
41         /* (non-Javadoc)
42          * @see net.sf.openrocket.database.ThrustCurveMotorSetDatabaseI#getMotorSets()
43          */
44         public List<ThrustCurveMotorSet> getMotorSets() {
45                 blockUntilLoaded();
46                 return motorSets;
47         }
48         
49         
50
51         /* (non-Javadoc)
52          * @see net.sf.openrocket.database.ThrustCurveMotorSetDatabaseI#findMotors(net.sf.openrocket.motor.Motor.Type, java.lang.String, java.lang.String, double, double)
53          */
54         @Override
55         public List<ThrustCurveMotor> findMotors(Motor.Type type, String manufacturer, String designation,
56                         double diameter, double length) {
57                 blockUntilLoaded();
58                 ArrayList<ThrustCurveMotor> results = new ArrayList<ThrustCurveMotor>();
59                 
60                 for (ThrustCurveMotorSet set : motorSets) {
61                         for (ThrustCurveMotor m : set.getMotors()) {
62                                 boolean match = true;
63                                 if (type != null && type != set.getType())
64                                         match = false;
65                                 else if (manufacturer != null && !m.getManufacturer().matches(manufacturer))
66                                         match = false;
67                                 else if (designation != null && !designation.equalsIgnoreCase(m.getDesignation()))
68                                         match = false;
69                                 else if (!Double.isNaN(diameter) && (Math.abs(diameter - m.getDiameter()) > 0.0015))
70                                         match = false;
71                                 else if (!Double.isNaN(length) && (Math.abs(length - m.getLength()) > 0.0015))
72                                         match = false;
73                                 
74                                 if (match)
75                                         results.add(m);
76                         }
77                 }
78                 
79                 return results;
80         }
81         
82         
83         /**
84          * Add a motor to the database.  If a matching ThrustCurveMototSet is found, 
85          * the motor is added to that set, otherwise a new set is created and added to the
86          * database.
87          * 
88          * @param motor         the motor to add
89          */
90         protected void addMotor(ThrustCurveMotor motor) {
91                 // Iterate from last to first, as this is most likely to hit early when loading files
92                 for (int i = motorSets.size() - 1; i >= 0; i--) {
93                         ThrustCurveMotorSet set = motorSets.get(i);
94                         if (set.matches(motor)) {
95                                 set.addMotor(motor);
96                                 return;
97                         }
98                 }
99                 
100                 ThrustCurveMotorSet newSet = new ThrustCurveMotorSet();
101                 newSet.addMotor(motor);
102                 motorSets.add(newSet);
103         }
104         
105         
106
107
108
109         /**
110          * Start loading the motors.  If asynchronous 
111          * 
112          * @throws  IllegalStateException       if this method has already been called.
113          */
114         public void startLoading() {
115                 if (startedLoading) {
116                         throw new IllegalStateException("Already called startLoading");
117                 }
118                 startedLoading = true;
119                 if (asynchronous) {
120                         new LoadingThread().start();
121                 } else {
122                         performMotorLoading();
123                 }
124         }
125         
126         
127         /**
128          * Return whether loading the database has ended.
129          * 
130          * @return      whether background loading has ended.
131          */
132         public boolean isLoaded() {
133                 return endedLoading;
134         }
135         
136         
137         /**
138          * Mark that this database is in use or a place is waiting for the database to 
139          * become loaded.  This can be used in conjunction with {@link #isLoaded()} to load
140          * the database without blocking.
141          */
142         public void setInUse() {
143                 inUse = true;
144         }
145         
146         
147         /**
148          * Block the current thread until loading of the motors has been completed.
149          * 
150          * @throws IllegalStateException        if startLoading() has not been called.
151          */
152         public void blockUntilLoaded() {
153                 inUse = true;
154                 if (!startedLoading) {
155                         throw new IllegalStateException("startLoading() has not been called");
156                 }
157                 if (!endedLoading) {
158                         synchronized (this) {
159                                 while (!endedLoading) {
160                                         try {
161                                                 this.wait();
162                                         } catch (InterruptedException e) {
163                                                 logger.warn("InterruptedException occurred, ignoring", e);
164                                         }
165                                 }
166                         }
167                 }
168         }
169         
170         
171         /**
172          * Used for loading the motor database.  This method will be called in a background
173          * thread to load the motors asynchronously.  This method should call 
174          * {@link #addMotor(ThrustCurveMotor)} to add the motors to the database.
175          */
176         protected abstract void loadMotors();
177         
178         
179
180         /**
181          * Creates the motor list, calls {@link #loadMotors()}, sorts the list and marks
182          * the motors as loaded.  This method is called either synchronously or from the
183          * background thread.
184          */
185         private void performMotorLoading() {
186                 motorSets = new ArrayList<ThrustCurveMotorSet>();
187                 try {
188                         loadMotors();
189                 } catch (Exception e) {
190                         logger.error("Loading motors failed", e);
191                 }
192                 Collections.sort(motorSets);
193                 motorSets = Collections.unmodifiableList(motorSets);
194                 synchronized (ThrustCurveMotorSetDatabase.this) {
195                         endedLoading = true;
196                         ThrustCurveMotorSetDatabase.this.notifyAll();
197                 }
198         }
199         
200         
201         /**
202          * Background thread for loading the motors.  This creates the motor list,
203          * calls loadMotors(), sorts the database, makes it unmodifiable, and finally
204          * marks the database as loaded and notifies any blocked threads.
205          */
206         private class LoadingThread extends Thread {
207                 private LoadingThread() {
208                         this.setName("MotorLoadingThread");
209                         this.setPriority(MIN_PRIORITY);
210                 }
211                 @Override
212                 public void run() {
213                         performMotorLoading();
214                 }
215         }
216         
217 }