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