package net.sf.openrocket.motor;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* Class containing information about motor manufacturers.
*
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/
public class Manufacturer implements Serializable {
/**
*
*/
private static final long serialVersionUID = 8124172026153369292L;
private static class ManufacturerList extends ConcurrentHashMap<String,Manufacturer> {
void add( Manufacturer m ) {
for( String s : m.getSearchNames() ) {
Manufacturer previousRegistered;
if ( (previousRegistered = putIfAbsent( s, m )) != null ) {
throw new IllegalStateException("Manufacturer name clash between " +
"manufacturers " + previousRegistered + " and " + m + " name " + s);
}
}
}
}
private static ManufacturerList manufacturers = new ManufacturerList();
static {
// AeroTech has many name combinations...
List<String> names = new ArrayList<String>();
for (String s : new String[] { "A", "AT", "AERO", "AEROT", "AEROTECH" }) {
names.add(s);
names.add(s + "-RMS");
names.add(s + "-RCS");
names.add("RCS-" + s);
names.add(s + "-APOGEE");
}
names.add("ISP");
// Aerotech has single-use, reload and hybrid motors
manufacturers.add(new Manufacturer("AeroTech", "AeroTech", Motor.Type.UNKNOWN,
names.toArray(new String[0])));
manufacturers.add(new Manufacturer("Alpha Hybrid Rocketry LLC", "Alpha Hybrid Rocketry", Motor.Type.HYBRID,
"AHR", "ALPHA", "ALPHA HYBRID", "ALPHA HYBRIDS", "ALPHA HYBRIDS ROCKETRY"));
// TODO: HIGH: AMW/ProX - how to classify?
manufacturers.add(new Manufacturer("Animal Motor Works", "Animal Motor Works", Motor.Type.RELOAD,
"AMW", "AW", "ANIMAL"));
manufacturers.add(new Manufacturer("Apogee", "Apogee", Motor.Type.SINGLE,
"AP", "APOG", "P"));
manufacturers.add(new Manufacturer("Cesaroni Technology Inc.", "Cesaroni Technology", Motor.Type.RELOAD,
"CES", "CESARONI", "CESARONI TECHNOLOGY INCORPORATED", "CTI",
"CS", "CSR", "PRO38", "ABC"));
manufacturers.add(new Manufacturer("Contrail Rockets", "Contrail Rockets", Motor.Type.HYBRID,
"CR", "CONTR", "CONTRAIL", "CONTRAIL ROCKET"));
manufacturers.add(new Manufacturer("Estes", "Estes", Motor.Type.SINGLE,
"E", "ES"));
// Ellis Mountain has both single-use and reload motors
manufacturers.add(new Manufacturer("Ellis Mountain", "Ellis Mountain", Motor.Type.UNKNOWN,
"EM", "ELLIS", "ELLIS MOUNTAIN ROCKET", "ELLIS MOUNTAIN ROCKETS"));
manufacturers.add(new Manufacturer("Gorilla Rocket Motors", "Gorilla Rocket Motors", Motor.Type.RELOAD,
"GR", "GORILLA", "GORILLA ROCKET", "GORILLA ROCKETS", "GORILLA MOTOR",
"GORILLA MOTORS", "GORILLA ROCKET MOTOR"));
manufacturers.add(new Manufacturer("HyperTEK", "HyperTEK", Motor.Type.HYBRID,
"H", "HT", "HYPER"));
manufacturers.add(new Manufacturer("Kosdon by AeroTech", "Kosdon by AeroTech", Motor.Type.RELOAD,
"K", "KBA", "K-AT", "KOS", "KOSDON", "KOSDON/AT", "KOSDON/AEROTECH"));
manufacturers.add(new Manufacturer("Loki Research", "Loki Research", Motor.Type.RELOAD,
"LOKI", "LR"));
manufacturers.add(new Manufacturer("Public Missiles, Ltd.", "Public Missiles", Motor.Type.SINGLE,
"PM", "PML", "PUBLIC MISSILES LIMITED"));
manufacturers.add(new Manufacturer("Propulsion Polymers", "Propulsion Polymers", Motor.Type.HYBRID,
"PP", "PROP", "PROPULSION"));
manufacturers.add(new Manufacturer("Quest", "Quest", Motor.Type.SINGLE,
"Q", "QU"));
manufacturers.add(new Manufacturer("RATT Works", "RATT Works", Motor.Type.HYBRID,
"RATT", "RT", "RTW"));
manufacturers.add(new Manufacturer("Roadrunner Rocketry", "Roadrunner Rocketry", Motor.Type.SINGLE,
"RR", "ROADRUNNER"));
manufacturers.add(new Manufacturer("Rocketvision", "Rocketvision", Motor.Type.SINGLE,
"RV", "ROCKET VISION"));
manufacturers.add(new Manufacturer("Sky Ripper Systems", "Sky Ripper Systems", Motor.Type.HYBRID,
"SR", "SRS", "SKYR", "SKYRIPPER", "SKY RIPPER", "SKYRIPPER SYSTEMS"));
manufacturers.add(new Manufacturer("West Coast Hybrids", "West Coast Hybrids", Motor.Type.HYBRID,
"WCH", "WCR", "WEST COAST", "WEST COAST HYBRID"));
// German WECO Feuerwerk, previously Sachsen Feuerwerk
manufacturers.add(new Manufacturer("WECO Feuerwerk", "WECO Feuerwerk", Motor.Type.SINGLE,
"WECO", "WECO FEUERWERKS", "SF", "SACHSEN", "SACHSEN FEUERWERK",
"SACHSEN FEUERWERKS"));
}
private final String displayName;
private final String simpleName;
private final Set<String> allNames;
private final Set<String> searchNames;
private final Motor.Type motorType;
private Manufacturer(String displayName, String simpleName, Motor.Type motorType, String... alternateNames) {
this.displayName = displayName;
this.simpleName = simpleName;
this.motorType = motorType;
if (motorType == null) {
throw new IllegalArgumentException("motorType cannot be null");
}
Set<String> all = new HashSet<String>();
Set<String> search = new HashSet<String>();
all.add(displayName);
all.add(simpleName);
search.add(generateSearchString(displayName));
search.add(generateSearchString(simpleName));
for (String name : alternateNames) {
all.add(name);
search.add(generateSearchString(name));
}
this.allNames = Collections.unmodifiableSet(all);
this.searchNames = Collections.unmodifiableSet(search);
}
/**
* Returns the display name of the manufacturer. This is the value that
* should be presented in the UI to the user.
*
* @return the display name
*/
public String getDisplayName() {
return displayName;
}
/**
* Returns the simple name of the manufacturer. This should be used for example
* when saving the manufacturer for compatibility.
*
* @return the simple name.
*/
public String getSimpleName() {
return simpleName;
}
/**
* Return all names of the manufacturer. This includes all kinds of
* codes that correspond to the manufacturer (for example "A" for AeroTech).
*
* @return an unmodifiable set of the alternative names.
*/
public Set<String> getAllNames() {
return allNames;
}
Set<String> getSearchNames() {
return searchNames;
}
/**
* Return the motor type that this manufacturer produces if it produces only one motor type.
* If the manufacturer manufactures multiple motor types or the type is unknown,
* type <code>UNKNOWN</code> is returned.
*
* @return the manufactured motor type, or <code>UNKNOWN</code>.
*/
public Motor.Type getMotorType() {
return motorType;
}
/**
* Check whether the display, simple or any of the alternative names matches the
* specified name. Matching is performed case insensitively and ignoring any
* non-letter and non-number characters.
*
* @param name the name to search for.
* @return whether this manufacturer matches the request.
*/
public boolean matches(String name) {
if (name == null)
return false;
return this.searchNames.contains(generateSearchString(name));
}
/**
* Return the display name of the manufacturer.
*/
@Override
public String toString() {
return displayName;
}
/**
* Returns a manufacturer for the given name. The manufacturer is searched for
* within the manufacturers and if a match is found the corresponding
* object is returned. If not, a new manufacturer is returned with display and
* simple name the name specified. Subsequent requests for the same (or corresponding)
* manufacturer name will return the same object.
*
* @param name the manufacturer name to search for.
* @return the Manufacturer object corresponding the name.
*/
public static Manufacturer getManufacturer(String name) {
String searchString = generateSearchString(name);
Manufacturer m = manufacturers.get(searchString);
if ( m != null ) {
return m;
}
m = new Manufacturer(name.trim(), name.trim(), Motor.Type.UNKNOWN);
// We need some additional external synchronization here so we lock on the manufacturers.
synchronized( manufacturers ) {
Manufacturer retest = manufacturers.get(searchString);
if ( retest != null ) {
// it exists now.
return retest;
}
manufacturers.add(m);
}
return m;
}
private static String generateSearchString(String str) {
return str.toLowerCase(Locale.getDefault()).replaceAll("[^a-zA-Z0-9]+", " ").trim();
}
private Object writeReplace() {
return new ManufacturerSerializationProxy(this.displayName);
}
private static class ManufacturerSerializationProxy implements Serializable {
/**
*
*/
private static final long serialVersionUID = 8311677686087002569L;
String displayName;
ManufacturerSerializationProxy( String displayName ) {
this.displayName = displayName;
}
private Object readResolve() throws ObjectStreamException {
return Manufacturer.getManufacturer(this.displayName);
}
}
}