language selector, bug fixed
[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         // TODO: Should this use grouping separator ("#,##0.##")?
115         
116         private static final DecimalFormat intFormat = new DecimalFormat("#");
117         private static final DecimalFormat decFormat = new DecimalFormat("0.##");
118         private static final DecimalFormat expFormat = new DecimalFormat("0.00E0");
119         
120         /**
121          * Format the given value (in SI units) to a string representation of the value in this
122          * units.  An suitable amount of decimals for the unit are used in the representation.
123          * The unit is not appended to the numerical value.
124          *  
125          * @param value  Value in SI units.
126          * @return       A string representation of the number in these units.
127          */
128         public String toString(double value) {
129                 double val = toUnit(value);
130                 
131                 if (Math.abs(val) > 1E6) {
132                         return expFormat.format(val);
133                 }
134                 if (Math.abs(val) >= 100) {
135                         return intFormat.format(val);
136                 }
137                 if (Math.abs(val) <= 0.005) {
138                         return "0";
139                 }
140                 
141                 double sign = Math.signum(val);
142                 val = Math.abs(val);
143                 double mul = 1.0;
144                 while (val < 100) {
145                         mul *= 10;
146                         val *= 10;
147                 }
148                 val = Math.rint(val) / mul * sign;
149                 
150                 return decFormat.format(val);
151         }
152         
153         
154         /**
155          * Return a string with the specified value and unit.  The value is converted into
156          * this unit.  If <code>value</code> is NaN, returns "N/A" (not applicable).
157          * 
158          * @param value         the value to print in SI units.
159          * @return                      the value and unit, or "N/A".
160          */
161         public String toStringUnit(double value) {
162                 if (Double.isNaN(value))
163                         return "N/A";
164                 
165                 String s = toString(value);
166                 if (hasSpace())
167                         s += " ";
168                 s += unit;
169                 return s;
170         }
171         
172         
173
174         /**
175          * Creates a new Value object with the specified value and this unit.
176          * 
177          * @param value the value to set.
178          * @return              a new Value object.
179          */
180         public Value toValue(double value) {
181                 return new Value(value, this);
182         }
183         
184         
185
186         /**
187          * Round the value (in the current units) to a precision suitable for rough valuing
188          * (approximately 2 significant numbers).
189          * 
190          * @param value  Value in current units
191          * @return       Rounded value.
192          */
193         public abstract double round(double value);
194         
195         /**
196          * Return the next rounded value after the given value.
197          * @param value  Value in these units.
198          * @return       The next suitable rounded value.
199          */
200         public abstract double getNextValue(double value);
201         
202         /**
203          * Return the previous rounded value before the given value.
204          * @param value  Value in these units.
205          * @return       The previous suitable rounded value.
206          */
207         public abstract double getPreviousValue(double value);
208         
209         //public abstract ArrayList<Tick> getTicks(double start, double end, double scale);
210         
211         /**
212          * Return ticks in the range start - end (in current units).  minor is the minimum
213          * distance between minor, non-notable ticks and major the minimum distance between
214          * major non-notable ticks.  The values are in current units, i.e. no conversion is
215          * performed.
216          */
217         public abstract Tick[] getTicks(double start, double end, double minor, double major);
218         
219         /**
220          * Compares whether the two units are equal.  Equality requires the unit classes,
221          * multiplier values and units to be equal.
222          */
223         @Override
224         public boolean equals(Object other) {
225                 if (other == null)
226                         return false;
227                 if (this.getClass() != other.getClass())
228                         return false;
229                 return ((this.multiplier == ((Unit) other).multiplier) && this.unit.equals(((Unit) other).unit));
230         }
231         
232         @Override
233         public int hashCode() {
234                 return this.getClass().hashCode() + this.unit.hashCode();
235         }
236         
237 }