Fix correctness and threading issue in new Manufacturer lookup mechanism. It was...
[debian/openrocket] / core / src / net / sf / openrocket / motor / Manufacturer.java
1 package net.sf.openrocket.motor;
2
3 import java.util.ArrayList;
4 import java.util.Collections;
5 import java.util.HashSet;
6 import java.util.List;
7 import java.util.Locale;
8 import java.util.Set;
9 import java.util.concurrent.ConcurrentHashMap;
10
11 /**
12  * Class containing information about motor manufacturers.
13  * 
14  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
15  */
16 public class Manufacturer {
17
18         private static class ManufacturerList extends ConcurrentHashMap<String,Manufacturer> {
19                 
20                 void add( Manufacturer m ) {
21                         for( String s : m.getSearchNames() ) {
22                                 Manufacturer previousRegistered;
23                                 if ( (previousRegistered = putIfAbsent( s, m )) != null ) {
24                                         throw new IllegalStateException("Manufacturer name clash between " +
25                                                         "manufacturers " + previousRegistered + " and " + m + " name " + s);
26                                 }
27                         }
28                 }
29                 
30         }
31         private static ManufacturerList manufacturers = new ManufacturerList();
32
33         static {
34                 
35                 // AeroTech has many name combinations...
36                 List<String> names = new ArrayList<String>();
37                 for (String s : new String[] { "A", "AT", "AERO", "AEROT", "AEROTECH" }) {
38                         names.add(s);
39                         names.add(s + "-RMS");
40                         names.add(s + "-RCS");
41                         names.add("RCS-" + s);
42                         names.add(s + "-APOGEE");
43                 }
44                 names.add("ISP");
45                 
46                 // Aerotech has single-use, reload and hybrid motors
47                 manufacturers.add(new Manufacturer("AeroTech", "AeroTech", Motor.Type.UNKNOWN,
48                                 names.toArray(new String[0])));
49                 
50                 manufacturers.add(new Manufacturer("Alpha Hybrid Rocketry LLC", "Alpha Hybrid Rocketry", Motor.Type.HYBRID,
51                                 "AHR", "ALPHA", "ALPHA HYBRID", "ALPHA HYBRIDS", "ALPHA HYBRIDS ROCKETRY"));
52                 
53                 // TODO: HIGH: AMW/ProX - how to classify?
54                 
55                 manufacturers.add(new Manufacturer("Animal Motor Works", "Animal Motor Works", Motor.Type.RELOAD,
56                                 "AMW", "AW", "ANIMAL"));
57                 
58                 manufacturers.add(new Manufacturer("Apogee", "Apogee", Motor.Type.SINGLE,
59                                 "AP", "APOG", "P"));
60                 
61                 manufacturers.add(new Manufacturer("Cesaroni Technology Inc.", "Cesaroni Technology", Motor.Type.RELOAD,
62                                 "CES", "CESARONI", "CESARONI TECHNOLOGY INCORPORATED", "CTI",
63                                 "CS", "CSR", "PRO38", "ABC"));
64                 
65                 manufacturers.add(new Manufacturer("Contrail Rockets", "Contrail Rockets", Motor.Type.HYBRID,
66                                 "CR", "CONTR", "CONTRAIL", "CONTRAIL ROCKET"));
67                 
68                 manufacturers.add(new Manufacturer("Estes", "Estes", Motor.Type.SINGLE,
69                                 "E", "ES"));
70                 
71                 // Ellis Mountain has both single-use and reload motors
72                 manufacturers.add(new Manufacturer("Ellis Mountain", "Ellis Mountain", Motor.Type.UNKNOWN,
73                                 "EM", "ELLIS", "ELLIS MOUNTAIN ROCKET", "ELLIS MOUNTAIN ROCKETS"));
74                 
75                 manufacturers.add(new Manufacturer("Gorilla Rocket Motors", "Gorilla Rocket Motors", Motor.Type.RELOAD,
76                                 "GR", "GORILLA", "GORILLA ROCKET", "GORILLA ROCKETS", "GORILLA MOTOR",
77                                 "GORILLA MOTORS", "GORILLA ROCKET MOTOR"));
78                 
79                 manufacturers.add(new Manufacturer("HyperTEK", "HyperTEK", Motor.Type.HYBRID,
80                                 "H", "HT", "HYPER"));
81                 
82                 manufacturers.add(new Manufacturer("Kosdon by AeroTech", "Kosdon by AeroTech", Motor.Type.RELOAD,
83                                 "K", "KBA", "K-AT", "KOS", "KOSDON", "KOSDON/AT", "KOSDON/AEROTECH"));
84                 
85                 manufacturers.add(new Manufacturer("Loki Research", "Loki Research", Motor.Type.RELOAD,
86                                 "LOKI", "LR"));
87                 
88                 manufacturers.add(new Manufacturer("Public Missiles, Ltd.", "Public Missiles", Motor.Type.SINGLE,
89                                 "PM", "PML", "PUBLIC MISSILES LIMITED"));
90                 
91                 manufacturers.add(new Manufacturer("Propulsion Polymers", "Propulsion Polymers", Motor.Type.HYBRID,
92                                 "PP", "PROP", "PROPULSION"));
93                 
94                 manufacturers.add(new Manufacturer("Quest", "Quest", Motor.Type.SINGLE,
95                                 "Q", "QU"));
96                 
97                 manufacturers.add(new Manufacturer("RATT Works", "RATT Works", Motor.Type.HYBRID,
98                                 "RATT", "RT", "RTW"));
99                 
100                 manufacturers.add(new Manufacturer("Roadrunner Rocketry", "Roadrunner Rocketry", Motor.Type.SINGLE,
101                                 "RR", "ROADRUNNER"));
102                 
103                 manufacturers.add(new Manufacturer("Rocketvision", "Rocketvision", Motor.Type.SINGLE,
104                                 "RV", "ROCKET VISION"));
105                 
106                 manufacturers.add(new Manufacturer("Sky Ripper Systems", "Sky Ripper Systems", Motor.Type.HYBRID,
107                                 "SR", "SRS", "SKYR", "SKYRIPPER", "SKY RIPPER", "SKYRIPPER SYSTEMS"));
108                 
109                 manufacturers.add(new Manufacturer("West Coast Hybrids", "West Coast Hybrids", Motor.Type.HYBRID,
110                                 "WCH", "WCR", "WEST COAST", "WEST COAST HYBRID"));
111                 
112                 // German WECO Feuerwerk, previously Sachsen Feuerwerk
113                 manufacturers.add(new Manufacturer("WECO Feuerwerk", "WECO Feuerwerk", Motor.Type.SINGLE,
114                                 "WECO", "WECO FEUERWERKS", "SF", "SACHSEN", "SACHSEN FEUERWERK",
115                                 "SACHSEN FEUERWERKS"));
116                 
117         }
118         
119         
120         
121         private final String displayName;
122         private final String simpleName;
123         private final Set<String> allNames;
124         private final Set<String> searchNames;
125         private final Motor.Type motorType;
126         
127         
128         private Manufacturer(String displayName, String simpleName, Motor.Type motorType, String... alternateNames) {
129                 this.displayName = displayName;
130                 this.simpleName = simpleName;
131                 this.motorType = motorType;
132                 if (motorType == null) {
133                         throw new IllegalArgumentException("motorType cannot be null");
134                 }
135                 
136                 Set<String> all = new HashSet<String>();
137                 Set<String> search = new HashSet<String>();
138                 
139                 all.add(displayName);
140                 all.add(simpleName);
141                 search.add(generateSearchString(displayName));
142                 search.add(generateSearchString(simpleName));
143                 
144                 for (String name : alternateNames) {
145                         all.add(name);
146                         search.add(generateSearchString(name));
147                 }
148                 
149                 this.allNames = Collections.unmodifiableSet(all);
150                 this.searchNames = Collections.unmodifiableSet(search);
151         }
152         
153         
154         /**
155          * Returns the display name of the manufacturer.  This is the value that
156          * should be presented in the UI to the user.
157          * 
158          * @return      the display name
159          */
160         public String getDisplayName() {
161                 return displayName;
162         }
163         
164         
165         /**
166          * Returns the simple name of the manufacturer.  This should be used for example
167          * when saving the manufacturer for compatibility.
168          * 
169          * @return      the simple name.
170          */
171         public String getSimpleName() {
172                 return simpleName;
173         }
174         
175         
176         /**
177          * Return all names of the manufacturer.  This includes all kinds of
178          * codes that correspond to the manufacturer (for example "A" for AeroTech).
179          * 
180          * @return      an unmodifiable set of the alternative names.
181          */
182         public Set<String> getAllNames() {
183                 return allNames;
184         }
185         
186         Set<String> getSearchNames() {
187                 return searchNames;
188         }
189         
190         /**
191          * Return the motor type that this manufacturer produces if it produces only one motor type.
192          * If the manufacturer manufactures multiple motor types or the type is unknown,
193          * type <code>UNKNOWN</code> is returned. 
194          * 
195          * @return      the manufactured motor type, or <code>UNKNOWN</code>.
196          */
197         public Motor.Type getMotorType() {
198                 return motorType;
199         }
200         
201         
202         /**
203          * Check whether the display, simple or any of the alternative names matches the
204          * specified name.  Matching is performed case insensitively and ignoring any
205          * non-letter and non-number characters.
206          * 
207          * @param name  the name to search for.
208          * @return              whether this manufacturer matches the request.
209          */
210         public boolean matches(String name) {
211                 if (name == null)
212                         return false;
213                 return this.searchNames.contains(generateSearchString(name));
214         }
215         
216         
217         /**
218          * Return the display name of the manufacturer.
219          */
220         @Override
221         public String toString() {
222                 return displayName;
223         }
224         
225         
226         /**
227          * Returns a manufacturer for the given name.  The manufacturer is searched for
228          * within the manufacturers and if a match is found the corresponding
229          * object is returned.  If not, a new manufacturer is returned with display and
230          * simple name the name specified.  Subsequent requests for the same (or corresponding)
231          * manufacturer name will return the same object.
232          * 
233          * @param name  the manufacturer name to search for.
234          * @return              the Manufacturer object corresponding the name.
235          */
236         public static Manufacturer getManufacturer(String name) {
237                 String searchString = generateSearchString(name);
238                 Manufacturer m = manufacturers.get(searchString);
239                 if ( m != null ) {
240                         return m;
241                 }
242
243                 m = new Manufacturer(name.trim(), name.trim(), Motor.Type.UNKNOWN);
244
245                 // We need some additional external synchronization here so we lock on the manufacturers.
246                 synchronized( manufacturers ) {
247                         Manufacturer retest = manufacturers.get(searchString);
248                         if ( retest != null ) {
249                                 // it exists now.
250                                 return retest;
251                         }
252                         manufacturers.add(m);
253                 }
254                 return m;
255         }
256         
257         private static String generateSearchString(String str) {
258                 return str.toLowerCase(Locale.getDefault()).replaceAll("[^a-zA-Z0-9]+", " ").trim();
259         }
260         
261 }