create changelog entry
[debian/openrocket] / core / 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         @Override
74         public String toString() {
75                 return unit;
76         }
77         
78         // TODO: Should this use grouping separator ("#,##0.##")?
79         
80         private static final DecimalFormat intFormat = new DecimalFormat("#");
81         private static final DecimalFormat decFormat = new DecimalFormat("0.##");
82         private static final DecimalFormat smallFormat = new DecimalFormat("0.###");
83         private static final DecimalFormat expFormat = new DecimalFormat("0.00E0");
84         
85         /**
86          * Format the given value (in SI units) to a string representation of the value in this
87          * units.  An suitable amount of decimals for the unit are used in the representation.
88          * The unit is not appended to the numerical value.
89          *  
90          * @param value  Value in SI units.
91          * @return       A string representation of the number in these units.
92          */
93         public String toString(double value) {
94                 double val = toUnit(value);
95                 
96                 if (Math.abs(val) > 1E6) {
97                         return expFormat.format(val);
98                 }
99                 if (Math.abs(val) >= 100) {
100                         return intFormat.format(val);
101                 }
102                 if (Math.abs(val) <= 0.0005) {
103                         return "0";
104                 }
105                 if ( Math.abs(val) < 0.095) {
106                         return smallFormat.format(val);
107                 }
108                 
109                 double sign = Math.signum(val);
110                 val = Math.abs(val);
111                 double mul = 1.0;
112                 while (val < 100) {
113                         mul *= 10;
114                         val *= 10;
115                 }
116                 val = Math.rint(val) / mul * sign;
117                 return decFormat.format(val);
118         }
119         
120         
121         /**
122          * Return a string with the specified value and unit.  The value is converted into
123          * this unit.  If <code>value</code> is NaN, returns "N/A" (not applicable).
124          * 
125          * @param value         the value to print in SI units.
126          * @return                      the value and unit, or "N/A".
127          */
128         public String toStringUnit(double value) {
129                 if (Double.isNaN(value))
130                         return "N/A";
131                 
132                 String s = toString(value);
133                 if (hasSpace())
134                         s += " ";
135                 s += unit;
136                 return s;
137         }
138         
139         
140
141         /**
142          * Creates a new Value object with the specified value and this unit.
143          * 
144          * @param value the value to set.
145          * @return              a new Value object.
146          */
147         public Value toValue(double value) {
148                 return new Value(value, this);
149         }
150         
151         
152
153         /**
154          * Round the value (in the current units) to a precision suitable for rough valuing
155          * (approximately 2 significant numbers).
156          * 
157          * @param value  Value in current units
158          * @return       Rounded value.
159          */
160         public abstract double round(double value);
161         
162         /**
163          * Return the next rounded value after the given value.
164          * @param value  Value in these units.
165          * @return       The next suitable rounded value.
166          */
167         public abstract double getNextValue(double value);
168         
169         /**
170          * Return the previous rounded value before the given value.
171          * @param value  Value in these units.
172          * @return       The previous suitable rounded value.
173          */
174         public abstract double getPreviousValue(double value);
175         
176         //public abstract ArrayList<Tick> getTicks(double start, double end, double scale);
177         
178         /**
179          * Return ticks in the range start - end (in current units).  minor is the minimum
180          * distance between minor, non-notable ticks and major the minimum distance between
181          * major non-notable ticks.  The values are in current units, i.e. no conversion is
182          * performed.
183          */
184         public abstract Tick[] getTicks(double start, double end, double minor, double major);
185         
186         /**
187          * Compares whether the two units are equal.  Equality requires the unit classes,
188          * multiplier values and units to be equal.
189          */
190         @Override
191         public boolean equals(Object other) {
192                 if (other == null)
193                         return false;
194                 if (this.getClass() != other.getClass())
195                         return false;
196                 return ((this.multiplier == ((Unit) other).multiplier) && this.unit.equals(((Unit) other).unit));
197         }
198         
199         @Override
200         public int hashCode() {
201                 return this.getClass().hashCode() + this.unit.hashCode();
202         }
203         
204 }