Add cubic inch and cubic millimeter as volume units
[sw/motorsim] / src / com / billkuker / rocketry / motorsim / RocketScience.java
1 package com.billkuker.rocketry.motorsim;\r
2 \r
3 import java.lang.ref.WeakReference;\r
4 import java.lang.reflect.Field;\r
5 import java.lang.reflect.Modifier;\r
6 import java.text.DecimalFormat;\r
7 import java.text.NumberFormat;\r
8 import java.util.HashSet;\r
9 import java.util.Iterator;\r
10 import java.util.Set;\r
11 import java.util.prefs.Preferences;\r
12 \r
13 import javax.measure.quantity.Pressure;\r
14 import javax.measure.quantity.Quantity;\r
15 import javax.measure.unit.NonSI;\r
16 import javax.measure.unit.ProductUnit;\r
17 import javax.measure.unit.SI;\r
18 import javax.measure.unit.Unit;\r
19 import javax.measure.unit.UnitFormat;\r
20 \r
21 import org.apache.log4j.Logger;\r
22 import org.jscience.physics.amount.Amount;\r
23 \r
24 public class RocketScience {\r
25         private static final Logger log = Logger.getLogger(RocketScience.class);\r
26         \r
27         public static Unit<Pressure> PSI = new ProductUnit<Pressure>(NonSI.POUND_FORCE.divide(NonSI.INCH.pow(2)));\r
28         public static Unit<Impulse> NEWTON_SECOND = new ProductUnit<Impulse>(SI.NEWTON.times(SI.SECOND));\r
29         public static Unit<Impulse> POUND_SECOND = new ProductUnit<Impulse>(NonSI.POUND_FORCE.times(SI.SECOND));\r
30         static{\r
31                 UnitFormat.getInstance().label(PSI, "psi");\r
32                 UnitFormat.getInstance().label(NEWTON_SECOND, "Ns");\r
33         }\r
34 \r
35         public interface MolarWeight extends Quantity {\r
36                 public static final Unit<MolarWeight> UNIT = new ProductUnit<MolarWeight>(\r
37                                 SI.KILOGRAM.divide(SI.MOLE));\r
38         }\r
39         \r
40         public interface Impulse extends Quantity {\r
41                 public static Unit<Impulse> UNIT = NEWTON_SECOND;\r
42         }\r
43         \r
44         private static HashSet<WeakReference<UnitPreferenceListener>> prefListeners = new HashSet<WeakReference<UnitPreferenceListener>>();\r
45         public static interface UnitPreferenceListener{\r
46                 public void preferredUnitsChanged();\r
47         }\r
48         public static void addUnitPreferenceListener(UnitPreferenceListener l){\r
49                 prefListeners.add(new WeakReference<RocketScience.UnitPreferenceListener>(l));\r
50         }\r
51 \r
52         public static enum UnitPreference{\r
53                 SI(new Unit[]{\r
54                                 javax.measure.unit.SI.METERS_PER_SECOND,\r
55                                 javax.measure.unit.SI.MILLIMETER.pow(3),\r
56                                 javax.measure.unit.SI.MILLIMETER.pow(2),\r
57                                 javax.measure.unit.SI.MILLIMETER,\r
58                                 javax.measure.unit.SI.MILLIMETER.divide(javax.measure.unit.SI.SECOND),\r
59                                 javax.measure.unit.SI.NEWTON,\r
60                                 javax.measure.unit.SI.GRAM,\r
61                                 javax.measure.unit.SI.MEGA(javax.measure.unit.SI.PASCAL),\r
62                                 NEWTON_SECOND\r
63                 }),\r
64                 NONSI(new Unit[]{\r
65                                 javax.measure.unit.NonSI.MILES_PER_HOUR,\r
66                                 javax.measure.unit.NonSI.INCH.pow(3),\r
67                                 javax.measure.unit.NonSI.INCH.pow(2),\r
68                                 javax.measure.unit.NonSI.INCH,\r
69                                 javax.measure.unit.NonSI.POUND_FORCE,\r
70                                 javax.measure.unit.NonSI.OUNCE,\r
71                                 javax.measure.unit.NonSI.INCH.divide(javax.measure.unit.SI.SECOND),\r
72                                 PSI,\r
73                                 POUND_SECOND\r
74                 });\r
75                 \r
76                 private static UnitPreference preference = SI;\r
77 \r
78                 static {\r
79                         Preferences prefs = Preferences.userNodeForPackage(RocketScience.class);\r
80                         String p = prefs.get("PreferedUnits", "SI");\r
81                         preference = UnitPreference.valueOf(p);\r
82                 }\r
83                 \r
84                 public static UnitPreference getUnitPreference(){\r
85                         return preference;\r
86                 }\r
87                 \r
88                 public static void setUnitPreference( final UnitPreference up ){\r
89                         if ( preference == up )\r
90                                 return;\r
91                         preference = up;\r
92                         Preferences prefs = Preferences.userNodeForPackage(RocketScience.class);\r
93                         prefs.put("PreferedUnits", up.toString());\r
94                         Iterator<WeakReference<UnitPreferenceListener>> weakIter = prefListeners.iterator();\r
95                         while (weakIter.hasNext()) {\r
96                                 WeakReference<UnitPreferenceListener> weak = weakIter.next();\r
97                                 UnitPreferenceListener l = weak.get();\r
98                                 if (l != null) {\r
99                                         l.preferredUnitsChanged();\r
100                                 } else {\r
101                                         log.debug("Weak reference to UPE is null");\r
102                                         weakIter.remove();\r
103                                 }\r
104                         }\r
105                 }\r
106                 \r
107                 protected Set<Unit<?>> units = new HashSet<Unit<?>>();\r
108                 \r
109                 UnitPreference( Unit<?> u[] ){\r
110                         for ( Unit<?> uu : u )\r
111                                 units.add(uu);\r
112                 }\r
113                 \r
114                 @SuppressWarnings("unchecked")\r
115                 public <T extends Quantity> Unit<T> getPreferredUnit(Unit<T> u){\r
116                         if ( units.contains(u) )\r
117                                 return u;\r
118                         for( Unit<?> ret : units ){\r
119                                 if ( ret.isCompatible(u) ){\r
120                                         return (Unit<T>) ret;\r
121                                 }\r
122                         }\r
123                         return u;\r
124                 }\r
125                 \r
126                 @SuppressWarnings("unchecked")\r
127                 public <T extends Quantity> Unit<T> getPreferredUnit(Class<T> q){\r
128                         for( Unit<?> u : units ){\r
129                                 try {\r
130                                         return u.asType(q); \r
131                                 } catch ( ClassCastException e ) {\r
132                                         //Not compatible\r
133                                 }\r
134                         }\r
135                         try {\r
136                                 Field f = q.getDeclaredField("UNIT");\r
137                                 if ( Modifier.isStatic(f.getModifiers()) ){\r
138                                         if ( Unit.class.isAssignableFrom(f.getType())){\r
139                                                 return (Unit<T>)f.get(null);\r
140                                         }\r
141                                 }\r
142                         } catch (SecurityException e) {\r
143                                 e.printStackTrace();\r
144                         } catch (NoSuchFieldException e) {\r
145                                 e.printStackTrace();\r
146                         } catch (IllegalArgumentException e) {\r
147                                 log.error(e);\r
148                         } catch (IllegalAccessException e) {\r
149                                 log.error(e);\r
150                         }\r
151                         return null;\r
152                 }\r
153         }\r
154 \r
155         public static <T extends Quantity> String ammountToString(Amount<T> a) {\r
156                 if ( a == null )\r
157                         return "Null";\r
158                 final NumberFormat nf = new DecimalFormat("##########.###");\r
159                 return nf.format(a.doubleValue(a.getUnit())) + " " + a.getUnit();\r
160         }\r
161         \r
162         @SuppressWarnings("unchecked")\r
163         public static <T extends Quantity> String ammountToRoundedString(Amount<T> a) {\r
164                 if (a == null)\r
165                         return "Null";\r
166                 Unit<T> u = RocketScience.UnitPreference.preference.getPreferredUnit(a\r
167                                 .getUnit());\r
168                 double d = a.doubleValue(u);\r
169 \r
170                 DecimalFormat df;\r
171                 \r
172                 if (u == SI.MILLIMETER && d > 1000.0) {\r
173                         u = (Unit<T>) SI.METER;\r
174                         d = d / 1000.0;\r
175                 } else if (u == NonSI.INCH && d > 12.0) {\r
176                         u = (Unit<T>) NonSI.FOOT;\r
177                         d = d / 12.0;\r
178                 }\r
179 \r
180                 if (Math.abs(d) < 10.0) {\r
181                         df = new DecimalFormat("#.##");\r
182                 } else if (Math.abs(d) < 100.0) {\r
183                         df = new DecimalFormat("#.#");\r
184                 } else {\r
185                         df = new DecimalFormat("#");\r
186                 }\r
187 \r
188                 return df.format(d) + " " + u.toString();\r
189         }\r
190 \r
191 }\r