Initial commit of Matt's measurement add-in for OpenOffice.org Calc.
[measurements/measurements.git] / src / net / mattmccutchen / measurements / Unit.java
1 package net.mattmccutchen.measurements;
2
3 import java.util.*;
4
5 public class Unit {
6         public static final Unit SECOND  = new Unit("s"   , 1.0, 1, 0, 0, 0, 0);
7         public static final Unit METER   = new Unit("m"   , 1.0, 0, 1, 0, 0, 0);
8         public static final Unit GRAM    = new Unit("g"   , 1.0, 0, 0, 1, 0, 0);
9         public static final Unit COULOMB = new Unit("coul", 1.0, 0, 0, 0, 1, 0);
10         public static final Unit KELVIN  = new Unit("K"   , 1.0, 0, 0, 0, 0, 1);
11         
12         public static final Unit[] basicUnits = new Unit[] {
13                 SECOND, METER, GRAM, COULOMB, KELVIN,
14         }; // Don't mutate
15         
16         public static final Unit LITER = new Unit("L", 1e-3, 0, 3, 0, 0, 0);
17         public static final Unit PERCENT = new Unit("%", 1e-2, 0, 0, 0, 0, 0);
18         
19         public static final Unit[] allUnits = new Unit[] {
20                 PERCENT, SECOND, METER, GRAM, COULOMB, KELVIN, LITER,
21         }; // Don't mutate
22         
23         public final String symbol;
24         public final double factor;
25         public final int[] powers; // Don't mutate
26         
27         private Unit(String symbol, double factor, int... powers) {
28                 this.symbol = symbol;
29                 this.factor = factor;
30                 this.powers = powers;
31         }
32         
33         private static final Map<String, Unit> unitsBySymbol; // Don't mutate
34         private static final Map<Character, Double> prefixes; // Don't mutate
35         
36         public static Unit lookupBySymbol(String symbol) {
37                 Unit u = unitsBySymbol.get(symbol);
38                 if (u != null)
39                         return u;
40                 char pfx = symbol.charAt(0);
41                 symbol = symbol.substring(1);
42                 u = unitsBySymbol.get(symbol);
43                 Double mult1 = prefixes.get(pfx);
44                 if (u != null && mult1 != null)
45                         return new Unit(symbol, u.factor * mult1, u.powers);
46                 return null;
47         }
48         
49         static {
50                 unitsBySymbol = new LinkedHashMap<String, Unit>();
51                 for (Unit u : allUnits)
52                         unitsBySymbol.put(u.symbol, u);
53                 
54                 prefixes = new LinkedHashMap<Character, Double>();
55                 prefixes.put('k', 1e+03);
56                 prefixes.put('M', 1e+06);
57                 prefixes.put('G', 1e+09);
58                 prefixes.put('T', 1e+12);
59                 prefixes.put('P', 1e+15);
60                 prefixes.put('E', 1e+18);
61                 prefixes.put('Z', 1e+21);
62                 prefixes.put('Y', 1e+24);
63                 prefixes.put('m', 1e-03);
64                 prefixes.put('u', 1e-06); // micro -> u: oh well
65                 prefixes.put('n', 1e-09);
66                 prefixes.put('p', 1e-12);
67                 prefixes.put('f', 1e-15);
68                 prefixes.put('a', 1e-18);
69                 prefixes.put('z', 1e-21);
70                 prefixes.put('y', 1e-24);
71         }
72         
73         public static Unit parseUnitString(String ustr) {
74                 double fctr = 1;
75                 int[] up = new int[Unit.basicUnits.length];
76                 StringTokenizer unitT = new StringTokenizer(ustr, " */", true);
77                 boolean invert = false;
78                 while (unitT.hasMoreTokens()) {
79                         String tok = unitT.nextToken();
80                         if (tok.equals("/")) {
81                                 invert = !invert;
82                                 continue;
83                         } else if (tok.equals(" ") || tok.equals("*"))
84                                 continue;
85                         int caret = tok.indexOf('^');
86                         String symbol;
87                         int power;
88                         if (caret == -1) {
89                                 power = 1;
90                                 symbol = tok;
91                         } else {
92                                 power = Integer.parseInt(tok.substring(caret + 1));
93                                 symbol = tok.substring(0, caret);
94                         }
95                         Unit unit = Unit.lookupBySymbol(symbol);
96                         if (unit == null)
97                                 throw new NullPointerException();
98                         if (invert)
99                                 power = -power;
100                         fctr *= Math.pow(unit.factor, power);
101                         for (int i = 0; i < Unit.basicUnits.length; i++)
102                                 up[i] += power * unit.powers[i];
103                 }
104                 return new Unit(ustr, fctr, up);
105         }
106 }