Add cubic inch and cubic millimeter as volume units
[sw/motorsim] / src / com / billkuker / rocketry / motorsim / RocketScience.java
index 32343777e4873c41a1611cd7a1225dc77f9e3cb9..f552b99a10de207956acedca3daeffa030a0c1d3 100644 (file)
@@ -1,8 +1,12 @@
 package com.billkuker.rocketry.motorsim;\r
 \r
+import java.lang.ref.WeakReference;\r
+import java.lang.reflect.Field;\r
+import java.lang.reflect.Modifier;\r
 import java.text.DecimalFormat;\r
 import java.text.NumberFormat;\r
 import java.util.HashSet;\r
+import java.util.Iterator;\r
 import java.util.Set;\r
 import java.util.prefs.Preferences;\r
 \r
@@ -14,11 +18,15 @@ import javax.measure.unit.SI;
 import javax.measure.unit.Unit;\r
 import javax.measure.unit.UnitFormat;\r
 \r
+import org.apache.log4j.Logger;\r
 import org.jscience.physics.amount.Amount;\r
 \r
 public class RocketScience {\r
+       private static final Logger log = Logger.getLogger(RocketScience.class);\r
+       \r
        public static Unit<Pressure> PSI = new ProductUnit<Pressure>(NonSI.POUND_FORCE.divide(NonSI.INCH.pow(2)));\r
        public static Unit<Impulse> NEWTON_SECOND = new ProductUnit<Impulse>(SI.NEWTON.times(SI.SECOND));\r
+       public static Unit<Impulse> POUND_SECOND = new ProductUnit<Impulse>(NonSI.POUND_FORCE.times(SI.SECOND));\r
        static{\r
                UnitFormat.getInstance().label(PSI, "psi");\r
                UnitFormat.getInstance().label(NEWTON_SECOND, "Ns");\r
@@ -32,21 +40,37 @@ public class RocketScience {
        public interface Impulse extends Quantity {\r
                public static Unit<Impulse> UNIT = NEWTON_SECOND;\r
        }\r
+       \r
+       private static HashSet<WeakReference<UnitPreferenceListener>> prefListeners = new HashSet<WeakReference<UnitPreferenceListener>>();\r
+       public static interface UnitPreferenceListener{\r
+               public void preferredUnitsChanged();\r
+       }\r
+       public static void addUnitPreferenceListener(UnitPreferenceListener l){\r
+               prefListeners.add(new WeakReference<RocketScience.UnitPreferenceListener>(l));\r
+       }\r
 \r
        public static enum UnitPreference{\r
                SI(new Unit[]{\r
+                               javax.measure.unit.SI.METERS_PER_SECOND,\r
+                               javax.measure.unit.SI.MILLIMETER.pow(3),\r
+                               javax.measure.unit.SI.MILLIMETER.pow(2),\r
                                javax.measure.unit.SI.MILLIMETER,\r
                                javax.measure.unit.SI.MILLIMETER.divide(javax.measure.unit.SI.SECOND),\r
                                javax.measure.unit.SI.NEWTON,\r
+                               javax.measure.unit.SI.GRAM,\r
                                javax.measure.unit.SI.MEGA(javax.measure.unit.SI.PASCAL),\r
                                NEWTON_SECOND\r
                }),\r
                NONSI(new Unit[]{\r
+                               javax.measure.unit.NonSI.MILES_PER_HOUR,\r
+                               javax.measure.unit.NonSI.INCH.pow(3),\r
+                               javax.measure.unit.NonSI.INCH.pow(2),\r
                                javax.measure.unit.NonSI.INCH,\r
                                javax.measure.unit.NonSI.POUND_FORCE,\r
+                               javax.measure.unit.NonSI.OUNCE,\r
                                javax.measure.unit.NonSI.INCH.divide(javax.measure.unit.SI.SECOND),\r
                                PSI,\r
-                               NEWTON_SECOND\r
+                               POUND_SECOND\r
                });\r
                \r
                private static UnitPreference preference = SI;\r
@@ -62,9 +86,22 @@ public class RocketScience {
                }\r
                \r
                public static void setUnitPreference( final UnitPreference up ){\r
+                       if ( preference == up )\r
+                               return;\r
                        preference = up;\r
                        Preferences prefs = Preferences.userNodeForPackage(RocketScience.class);\r
                        prefs.put("PreferedUnits", up.toString());\r
+                       Iterator<WeakReference<UnitPreferenceListener>> weakIter = prefListeners.iterator();\r
+                       while (weakIter.hasNext()) {\r
+                               WeakReference<UnitPreferenceListener> weak = weakIter.next();\r
+                               UnitPreferenceListener l = weak.get();\r
+                               if (l != null) {\r
+                                       l.preferredUnitsChanged();\r
+                               } else {\r
+                                       log.debug("Weak reference to UPE is null");\r
+                                       weakIter.remove();\r
+                               }\r
+                       }\r
                }\r
                \r
                protected Set<Unit<?>> units = new HashSet<Unit<?>>();\r
@@ -85,21 +122,60 @@ public class RocketScience {
                        }\r
                        return u;\r
                }\r
+               \r
+               @SuppressWarnings("unchecked")\r
+               public <T extends Quantity> Unit<T> getPreferredUnit(Class<T> q){\r
+                       for( Unit<?> u : units ){\r
+                               try {\r
+                                       return u.asType(q); \r
+                               } catch ( ClassCastException e ) {\r
+                                       //Not compatible\r
+                               }\r
+                       }\r
+                       try {\r
+                               Field f = q.getDeclaredField("UNIT");\r
+                               if ( Modifier.isStatic(f.getModifiers()) ){\r
+                                       if ( Unit.class.isAssignableFrom(f.getType())){\r
+                                               return (Unit<T>)f.get(null);\r
+                                       }\r
+                               }\r
+                       } catch (SecurityException e) {\r
+                               e.printStackTrace();\r
+                       } catch (NoSuchFieldException e) {\r
+                               e.printStackTrace();\r
+                       } catch (IllegalArgumentException e) {\r
+                               log.error(e);\r
+                       } catch (IllegalAccessException e) {\r
+                               log.error(e);\r
+                       }\r
+                       return null;\r
+               }\r
        }\r
 \r
        public static <T extends Quantity> String ammountToString(Amount<T> a) {\r
+               if ( a == null )\r
+                       return "Null";\r
                final NumberFormat nf = new DecimalFormat("##########.###");\r
                return nf.format(a.doubleValue(a.getUnit())) + " " + a.getUnit();\r
        }\r
        \r
+       @SuppressWarnings("unchecked")\r
        public static <T extends Quantity> String ammountToRoundedString(Amount<T> a) {\r
                if (a == null)\r
-                       return "NULL";\r
+                       return "Null";\r
                Unit<T> u = RocketScience.UnitPreference.preference.getPreferredUnit(a\r
                                .getUnit());\r
                double d = a.doubleValue(u);\r
 \r
                DecimalFormat df;\r
+               \r
+               if (u == SI.MILLIMETER && d > 1000.0) {\r
+                       u = (Unit<T>) SI.METER;\r
+                       d = d / 1000.0;\r
+               } else if (u == NonSI.INCH && d > 12.0) {\r
+                       u = (Unit<T>) NonSI.FOOT;\r
+                       d = d / 12.0;\r
+               }\r
 \r
                if (Math.abs(d) < 10.0) {\r
                        df = new DecimalFormat("#.##");\r