3686bedc38ca2a0bcee2e3a575944f5afe5916a7
[debian/openrocket] / src / net / sf / openrocket / unit / Unit.java
1 package net.sf.openrocket.unit;
2
3 import java.text.DecimalFormat;
4
5 public abstract class Unit {
6         
7         /** No unit with 2 digit precision */
8         public static final Unit NOUNIT2 = new GeneralUnit(1,"\u200b", 2);  // zero-width space
9
10         protected final double multiplier;   // meters = units * multiplier
11         protected final String unit;
12
13         /**
14          * Creates a new Unit with a given multiplier and unit name.
15          * 
16          * Multiplier e.g. 1 in = 0.0254 meter
17          * 
18          * @param multiplier  The multiplier to use on the value, 1 this unit == multiplier SI units
19          * @param unit        The unit's short form.
20          */
21         public Unit(double multiplier, String unit) {
22                 if (multiplier == 0)
23                         throw new IllegalArgumentException("Unit has multiplier=0");
24                 this.multiplier = multiplier;
25                 this.unit = unit;
26         }
27
28         /**
29          * Converts from SI units to this unit.  The default implementation simply divides by the
30          * multiplier.
31          * 
32          * @param value  Value in SI unit
33          * @return       Value in these units
34          */
35         public double toUnit(double value) {
36                 return value/multiplier;
37         }
38
39         /**
40          * Convert from this type of units to SI units.  The default implementation simply 
41          * multiplies by the multiplier.
42          * 
43          * @param value  Value in these units
44          * @return       Value in SI units
45          */
46         public double fromUnit(double value) {
47                 return value*multiplier;
48         }
49
50         
51         /**
52          * Return the unit name.
53          * 
54          * @return      the unit.
55          */
56         public String getUnit() {
57                 return unit;
58         }
59         
60         /**
61          * Whether the value and unit should be separated by a whitespace.  This method 
62          * returns true as most units have a space between the value and unit, but may be 
63          * overridden.
64          * 
65          * @return  true if the value and unit should be separated
66          */
67         public boolean hasSpace() {
68                 return true;
69         }
70         
71         
72         // Testcases for toString(double)
73         public static void main(String arg[]) {
74                 System.out.println(NOUNIT2.toString(0.0049));
75                 System.out.println(NOUNIT2.toString(0.0050));
76                 System.out.println(NOUNIT2.toString(0.0051));
77                 System.out.println(NOUNIT2.toString(0.00123));
78                 System.out.println(NOUNIT2.toString(0.0123));
79                 System.out.println(NOUNIT2.toString(0.1234));
80                 System.out.println(NOUNIT2.toString(1.2345));
81                 System.out.println(NOUNIT2.toString(12.345));
82                 System.out.println(NOUNIT2.toString(123.456));
83                 System.out.println(NOUNIT2.toString(1234.5678));
84                 System.out.println(NOUNIT2.toString(12345.6789));
85                 System.out.println(NOUNIT2.toString(123456.789));
86                 System.out.println(NOUNIT2.toString(1234567.89));
87                 System.out.println(NOUNIT2.toString(12345678.9));
88                 
89                 System.out.println(NOUNIT2.toString(-0.0049));
90                 System.out.println(NOUNIT2.toString(-0.0050));
91                 System.out.println(NOUNIT2.toString(-0.0051));
92                 System.out.println(NOUNIT2.toString(-0.00123));
93                 System.out.println(NOUNIT2.toString(-0.0123));
94                 System.out.println(NOUNIT2.toString(-0.1234));
95                 System.out.println(NOUNIT2.toString(-1.2345));
96                 System.out.println(NOUNIT2.toString(-12.345));
97                 System.out.println(NOUNIT2.toString(-123.456));
98                 System.out.println(NOUNIT2.toString(-1234.5678));
99                 System.out.println(NOUNIT2.toString(-12345.6789));
100                 System.out.println(NOUNIT2.toString(-123456.789));
101                 System.out.println(NOUNIT2.toString(-1234567.89));
102                 System.out.println(NOUNIT2.toString(-12345678.9));
103                 
104         }
105         
106         
107         @Override
108         public String toString() {
109                 return unit;
110         }
111         
112         private static final DecimalFormat intFormat = new DecimalFormat("#");
113         private static final DecimalFormat decFormat = new DecimalFormat("0.##");
114         private static final DecimalFormat expFormat = new DecimalFormat("0.00E0");
115
116         /**
117          * Format the given value (in SI units) to a string representation of the value in this
118          * units.  An suitable amount of decimals for the unit are used in the representation.
119          * The unit is not appended to the numerical value.
120          *  
121          * @param value  Value in SI units.
122          * @return       A string representation of the number in these units.
123          */
124         public String toString(double value) {
125                 double val = toUnit(value);
126
127                 if (Math.abs(val) > 1E6) {
128                         return expFormat.format(val);
129                 }
130                 if (Math.abs(val) >= 100) {
131                         return intFormat.format(val);
132                 }
133                 if (Math.abs(val) <= 0.005) {
134                         return "0";
135                 }
136
137                 double sign = Math.signum(val);
138                 val = Math.abs(val);
139                 double mul = 1.0;
140                 while (val < 100) {
141                         mul *= 10;
142                         val *= 10;
143                 }
144                 val = Math.rint(val)/mul * sign;
145                 
146                 return decFormat.format(val);
147         }
148         
149         
150         /**
151          * Return a string with the specified value and unit.  The value is converted into
152          * this unit.  If <code>value</code> is NaN, returns "N/A" (not applicable).
153          * 
154          * @param value         the value to print in SI units.
155          * @return                      the value and unit, or "N/A".
156          */
157         public String toStringUnit(double value) {
158                 if (Double.isNaN(value))
159                         return "N/A";
160                 
161                 String s = toString(value);
162                 if (hasSpace())
163                         s += " ";
164                 s += unit;
165                 return s;
166         }
167         
168         
169         
170
171         /**
172          * Round the value (in the current units) to a precision suitable for rough valuing
173          * (approximately 2 significant numbers).
174          * 
175          * @param value  Value in current units
176          * @return       Rounded value.
177          */
178         public abstract double round(double value);
179
180         /**
181          * Return the next rounded value after the given value.
182          * @param value  Value in these units.
183          * @return       The next suitable rounded value.
184          */
185         public abstract double getNextValue(double value);
186         
187         /**
188          * Return the previous rounded value before the given value.
189          * @param value  Value in these units.
190          * @return       The previous suitable rounded value.
191          */
192         public abstract double getPreviousValue(double value);
193         
194         //public abstract ArrayList<Tick> getTicks(double start, double end, double scale);
195         
196         /**
197          * Return ticks in the range start - end (in current units).  minor is the minimum
198          * distance between minor, non-notable ticks and major the minimum distance between
199          * major non-notable ticks.  The values are in current units, i.e. no conversion is
200          * performed.
201          */
202         public abstract Tick[] getTicks(double start, double end, double minor, double major);
203         
204         /**
205          * Compares whether the two units are equal.  Equality requires the unit classes,
206          * multiplier values and units to be equal.
207          */
208         @Override
209         public boolean equals(Object other) {
210                 if (other == null)
211                         return false;
212                 if (this.getClass() != other.getClass())
213                         return false;
214                 return ((this.multiplier == ((Unit)other).multiplier) && 
215                                 this.unit.equals(((Unit)other).unit));
216         }
217         
218         @Override
219         public int hashCode() {
220                 return this.getClass().hashCode() + this.unit.hashCode();
221         }
222
223 }