Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: e97563d26244bd44b59b81febc33cdfb418729a8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
/*
 * Copyright (c) OSGi Alliance (2002, 2013). All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.osgi.util.measurement;

import java.util.Hashtable;

/**
 * A unit system for measurements.
 * 
 * This class contains definitions of the most common SI units.
 * <p>
 * 
 * <p>
 * This class only support exponents for the base SI units in the range -64 to
 * +63. Any operation which produces an exponent outside of this range will
 * result in a {@code Unit} object with undefined exponents.
 * 
 * @Immutable
 * @author $Id$
 */
/*
 * This local class maintains the information about units. It can calculate new
 * units when two values are multiplied, divided, added or subtracted. <p> The
 * unit works with the 7 basic SI types + rad + up to 2^6 custom types. For each
 * type, the unit keeps a bit mask with the exponents of the basic types. Eg.
 * m/s is m = 1, s = -1. Multiplying one unit with another means that the bit
 * masks are added, dividing means that the bit masks are subtracted. <p> This
 * class can handle any reasonable combination of SI units. However, it will
 * always try to coerce results back into the basic set. E.g. when you do V*A
 * you should get W and not m2.kg/s3 . Only when the existing types do not match
 * does the unit fallback to the expanded form. <p> This class uses offset
 * arithmetic. This means that the exponents are stored in an long. The special
 * field is used for units that should not be arithmetically divided or
 * multiplied, like longitude and lattitude. These special units can however, be
 * divided and multiplied by the basic 7 constants of the SI, e.g. deg/s.
 */
public class Unit {
	private final static long	UNITY		= createType(0, 0, 0, 0, 0, 0, 0, 0, 0);
	private final static long	ZERO		= 0x40L;
	private final static long	MASK		= 0x7fL;
	private final static int	m_SHIFT		= 0;
	private final static int	s_SHIFT		= 7;
	private final static int	kg_SHIFT	= 14;
	private final static int	K_SHIFT		= 21;
	private final static int	A_SHIFT		= 28;
	private final static int	mol_SHIFT	= 35;
	private final static int	cd_SHIFT	= 42;
	private final static int	rad_SHIFT	= 49;
	private final static int	x_SHIFT		= 56;
	private final static long	x_MASK		= MASK << x_SHIFT;
	/** No Unit (Unity) */
	public final static Unit	unity		= new Unit("", UNITY);																											// Unity
	/* SI Base Units */
	/** The length unit meter (m) */
	public final static Unit	m			= new Unit("m", createType(0, 0, 0, 0, 0, 0, 0, 0, 1));																		// Distance
	// meter
	/** The time unit second (s) */
	public final static Unit	s			= new Unit("s", createType(0, 0, 0, 0, 0, 0, 0, 1, 0));																		// Time
	// Seconds
	// s
	/** The mass unit kilogram (kg) */
	public final static Unit	kg			= new Unit("kg", createType(0, 0, 0, 0, 0, 0, 1, 0, 0));																		// Mass
	// kilogram
	// kg
	/** The temperature unit kelvin (K) */
	public final static Unit	K			= new Unit("K", createType(0, 0, 0, 0, 0, 1, 0, 0, 0));																		// Temperature
	// kelvin
	// K
	/** The electric current unit ampere (A) */
	public final static Unit	A			= new Unit("A", createType(0, 0, 0, 0, 1, 0, 0, 0, 0));																		// Current
	// ampere
	// A
	/** The amount of substance unit mole (mol) */
	public final static Unit	mol			= new Unit("mol", createType(0, 0, 0, 1, 0, 0, 0, 0, 0));																		// Substance
	// mole
	// mol
	/** The luminous intensity unit candela (cd) */
	public final static Unit	cd			= new Unit("cd", createType(0, 0, 1, 0, 0, 0, 0, 0, 0));																		// Light
	// candela
	// cd
	/* SI Derived Units */
	/** The speed unit meter per second (m/s) */
	public final static Unit	m_s			= new Unit("m/s", createType(0, 0, 0, 0, 0, 0, 0, -1, 1));																		// Speed
	// m/s
	/** The acceleration unit meter per second squared (m/s<sup>2</sup>) */
	public final static Unit	m_s2		= new Unit("m/s2", createType(0, 0, 0, 0, 0, 0, 0, -2, 1));																	// Acceleration
	// m/s^2
	/** The area unit square meter (m<sup>2</sup>) */
	public final static Unit	m2			= new Unit("m2", createType(0, 0, 0, 0, 0, 0, 0, 0, 2));																		// Surface
	// m^2
	/** The volume unit cubic meter (m<sup>3</sup>) */
	public final static Unit	m3			= new Unit("m3", createType(0, 0, 0, 0, 0, 0, 0, 0, 3));																		// Volume
	// m^3
	/**
	 * The frequency unit hertz (Hz).
	 * <p>
	 * hertz is expressed in SI units as 1/s
	 */
	public final static Unit	Hz			= new Unit("Hz", createType(0, 0, 0, 0, 0, 0, 0, -1, 0));																		// Frequency
	// 1/s
	/**
	 * The force unit newton (N).
	 * <p>
	 * N is expressed in SI units as m&#183;kg/s<sup>2</sup>
	 */
	public final static Unit	N			= new Unit("N", createType(0, 0, 0, 0, 0, 0, 1, -2, 1));																		// Force
	// newton
	// (m*kg)/s^2
	/**
	 * The pressure unit pascal (Pa).
	 * <p>
	 * Pa is equal to N/m<sup>2</sup> or is expressed in SI units as
	 * kg/m&#183;s<sup>2</sup>
	 */
	public final static Unit	Pa			= new Unit("Pa", createType(0, 0, 0, 0, 0, 0, 1, -2, -1));																		// Pressure
	// pascal
	// kg/(m*s^2)
	/**
	 * The energy unit joule (J).
	 * <p>
	 * joule is equal to N&#183;m or is expressed in SI units as
	 * m<sup>2</sup>&#183;kg/s<sup>2</sup>
	 */
	public final static Unit	J			= new Unit("J", createType(0, 0, 0, 0, 0, 0, 1, -2, 2));																		// Energy
	// joule
	// (m^2*kg)/s^2
	/**
	 * The power unit watt (W).
	 * <p>
	 * watt is equal to J/s or is expressed in SI units as
	 * m<sup>2</sup>&#183;kg/s<sup>3</sup>
	 */
	public final static Unit	W			= new Unit("W", createType(0, 0, 0, 0, 0, 0, 1, -3, 2));																		// Power
	// watt
	// (m^2*kg)/s^3
	/**
	 * The electric charge unit coulomb (C).
	 * <p>
	 * coulomb is expressed in SI units as s&#183;A
	 */
	public final static Unit	C			= new Unit("C", createType(0, 0, 0, 0, 1, 0, 0, 1, 0));																		// Charge
	// coulumb
	// s*A
	/**
	 * The electric potential difference unit volt (V).
	 * <p>
	 * volt is equal to W/A or is expressed in SI units as
	 * m<sup>2</sup>&#183;kg/s<sup>3</sup>&#183;A
	 */
	public final static Unit	V			= new Unit("V", createType(0, 0, 0, 0, -1, 0, 1, -3, 2));																		// El.
	// Potent.
	// volt
	// (m^2*kg)/(s^3*A)
	/**
	 * The capacitance unit farad (F).
	 * <p>
	 * farad is equal to C/V or is expressed in SI units as
	 * s<sup>4</sup>&#183;A<sup>2</sup>/m<sup>2</sup>&#183;kg
	 */
	public final static Unit	F			= new Unit("F", createType(0, 0, 0, 0, 2, 0, -1, 4, -2));																		// Capacitance
	// farad
	// (s^4*A^2)/(m^2*kg)
	/**
	 * The electric resistance unit ohm.
	 * <p>
	 * ohm is equal to V/A or is expressed in SI units as
	 * m<sup>2</sup>&#183;kg/s<sup>3</sup>&#183;A<sup>2</sup>
	 */
	public final static Unit	Ohm			= new Unit("Ohm", createType(0, 0, 0, 0, -2, 0, 1, -3, 2));																	// Resistance
	// ohm
	// (m^2*kg)/(s^3*A^2)
	/**
	 * The electric conductance unit siemens (S).
	 * <p>
	 * siemens is equal to A/V or is expressed in SI units as
	 * s<sup>3</sup>&#183;A<sup>2</sup>/m<sup>2</sup>&#183;kg
	 */
	public final static Unit	S			= new Unit("S", createType(0, 0, 0, 0, 2, 0, -1, 3, -2));																		// Conductance
	// siemens
	// (s^3*A^2)/(m^2*kg)
	/**
	 * The magnetic flux unit weber (Wb).
	 * <p>
	 * weber is equal to V&#183;s or is expressed in SI units as
	 * m<sup>2</sup>&#183;kg/s<sup>2</sup>&#183;A
	 */
	public final static Unit	Wb			= new Unit("Wb", createType(0, 0, 0, 0, -1, 0, 1, -2, 2));																		// Magn.
	// Flux
	// weber
	// (m^2*kg)/(s^2*A)
	/**
	 * The magnetic flux density unit tesla (T).
	 * <p>
	 * tesla is equal to Wb/m<sup>2</sup> or is expressed in SI units as
	 * kg/s<sup>2</sup>&#183;A
	 */
	public final static Unit	T			= new Unit("T", createType(0, 0, 0, 0, -1, 0, 1, -2, 0));																		// Magn.
	// Flux
	// Dens.
	// tesla
	// kg/(s^2*A)
	/**
	 * The illuminance unit lux (lx).
	 * <p>
	 * lux is expressed in SI units as cd/m<sup>2</sup>
	 */
	public final static Unit	lx			= new Unit("lx", createType(0, 0, 1, 0, 0, 0, 0, 0, -2));																		// Illuminace
	// lux
	// cd/m^2
	/**
	 * The absorbed dose unit gray (Gy).
	 * <p>
	 * Gy is equal to J/kg or is expressed in SI units as
	 * m<sup>2</sup>/s<sup>2</sup>
	 */
	public final static Unit	Gy			= new Unit("Gy", createType(0, 0, 0, 0, 0, 0, 0, -2, 2));																		// Absorbed
	// dose
	// gray
	// m^2/s^2
	/**
	 * The catalytic activity unit katal (kat).
	 * <p>
	 * katal is expressed in SI units as mol/s
	 */
	public final static Unit	kat			= new Unit("kat", createType(0, 0, 0, 1, 0, 0, 0, -1, 0));																		// Catalytic
	// Act.
	// katal
	// mol/s
	/** The angle unit radians (rad) */
	public final static Unit	rad			= new Unit("rad", createType(0, 1, 0, 0, 0, 0, 0, 0, 0));																		// Angle
	// radians
	// rad
	/**
	 * An array containing all units defined. The first seven items must be m,
	 * s, kg, K, A, mol, cd, rad in this order!
	 */
	private final static Unit[]	allUnits	= new Unit[] {m, s, kg, K, A, mol, cd, rad, m_s, m_s2, m2, m3, Hz, N, Pa, J, W, C, V, F, Ohm, S, Wb, T, lx, Gy, kat, unity};

	/* @GuardedBy("this") */
	private static Hashtable	base;
	private final String		name;
	private final long			type;

	/**
	 * Creates a new {@code Unit} instance.
	 * 
	 * @param name the name of the {@code Unit}
	 * @param type the type of the {@code Unit}
	 */
	private Unit(String name, long type) {
		if (name == null) {
			name = computeName(type);
		}
		this.name = name;
		this.type = type;
		// System.out.println( name + " " + Long.toHexString( type ) );
	}

	/**
	 * Create a type field from the base SI unit exponent values.
	 * 
	 */
	private static long createType(int _x, int _rad, int _cd, int _mol, int _A, int _K, int _kg, int _s, int _m) {
		return (((ZERO + _m) & MASK) << m_SHIFT) | (((ZERO + _s) & MASK) << s_SHIFT) | (((ZERO + _kg) & MASK) << kg_SHIFT) | (((ZERO + _K) & MASK) << K_SHIFT) | (((ZERO + _A) & MASK) << A_SHIFT)
				| (((ZERO + _mol) & MASK) << mol_SHIFT) | (((ZERO + _cd) & MASK) << cd_SHIFT) | (((ZERO + _rad) & MASK) << rad_SHIFT) | (((long) _x) << x_SHIFT);
	}

	/**
	 * Checks whether this {@code Unit} object is equal to the specified
	 * {@code Unit} object. The {@code Unit} objects are considered equal if
	 * their exponents are equal.
	 * 
	 * @param obj the {@code Unit} object that should be checked for equality
	 * 
	 * @return true if the specified {@code Unit} object is equal to this
	 *         {@code Unit} object.
	 */
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (!(obj instanceof Unit)) {
			return false;
		}
		return ((Unit) obj).type == type;
	}

	/**
	 * Returns the hash code for this object.
	 * 
	 * @return This object's hash code.
	 */
	public int hashCode() {
		return 31 * 17 + (int) (type ^ (type >>> 32));
	}

	/**
	 * Returns a new {@code Unit} that is the multiplication of this
	 * {@code Unit} and the {@code Unit} specified
	 * 
	 * @param that the {@code Unit} that will be multiplied with this
	 *        {@code Unit}
	 * 
	 * @return a new {@code Unit} that is the multiplication of this
	 *         {@code Unit} and the {@code Unit} specified
	 * 
	 * @throws RuntimeException if both {@code Unit} s are special
	 * 
	 * @see Unit#isSpecial()
	 */
	Unit mul(Unit that) {
		if (this.isSpecial() && that.isSpecial()) {
			throw new ArithmeticException("Cannot multiply " + this + " with " + that);
		}
		return find(this.type - UNITY + that.type);
	}

	/**
	 * Returns a new {@code Unit} that is the division of this {@code Unit} and
	 * the {@code Unit} specified
	 * 
	 * @param that the {@code Unit} that this {@code Unit} will be divided with
	 * @return a new {@code Unit} that is the division of this {@code Unit} and
	 *         the {@code Unit} specified
	 * 
	 * @throws RuntimeException if both {@code Unit} s are special
	 * 
	 * @see Unit#isSpecial()
	 */
	Unit div(Unit that) {
		if (this.isSpecial() && that.isSpecial()) {
			if (this.type == that.type) {
				return Unit.unity;
			}
			throw new ArithmeticException("Cannot divide " + this + " by " + that);
		}
		return find(this.type - that.type + UNITY);
	}

	/**
	 * Returns a new {@code Unit} that is the addition of this {@code Unit} and
	 * the {@code Unit} specified.
	 * 
	 * @param that the {@code Unit} that should be added to this {@code Unit}
	 * 
	 * @return a new {@code Unit} that is the addition of this {@code Unit} and
	 *         the {@code Unit} specified.
	 * 
	 * @throws RuntimeException if the two {@code Unit} s are not the same
	 */
	Unit add(Unit that) {
		if (!this.equals(that)) {
			throw new ArithmeticException("Cannot add " + this + " to " + that);
		}
		return this;
	}

	/**
	 * Returns a new {@code Unit} that is the subtraction between this
	 * {@code Unit} and the {@code Unit} specified.
	 * 
	 * @param that the {@code Unit} that will be subtracted from this
	 *        {@code Unit}
	 * @return a new {@code Unit} that is the subtraction between this
	 *         {@code Unit} and the {@code Unit} specified.
	 * 
	 * @throws RuntimeException if the {@code Unit} specified is not the same as
	 *         this {@code Unit}
	 */
	Unit sub(Unit that) {
		if (!this.equals(that)) {
			throw new ArithmeticException("Cannot subtract " + that + " from " + this);
		}
		return this;
	}

	/**
	 * Finds a {@code Unit} based on a type. If the {@code Unit} is not found,
	 * it will be created and added to the list of all units under a null name.
	 * 
	 * @param type the type of the {@code Unit} to find
	 * 
	 * @return the {@code Unit}
	 */
	static synchronized Unit find(long type) {
		if (base == null) {
			int size = allUnits.length;
			base = new Hashtable(size << 1);
			for (int i = 0; i < size; i++) {
				base.put(allUnits[i], allUnits[i]);
			}
		}
		Unit unit = new Unit(null, type);
		Unit out = (Unit) base.get(unit);
		if (out == null) {
			base.put(unit, unit);
			out = unit;
		}
		return out;
	}

	/**
	 * Returns a {@code String} object representing the {@code Unit}
	 * 
	 * @return A {@code String} object representing the {@code Unit}
	 */
	public String toString() {
		return name;
	}

	private static String computeName(long type) {
		int _m = (int) (((type >> m_SHIFT) & MASK) - ZERO);
		int _s = (int) (((type >> s_SHIFT) & MASK) - ZERO);
		int _kg = (int) (((type >> kg_SHIFT) & MASK) - ZERO);
		int _K = (int) (((type >> K_SHIFT) & MASK) - ZERO);
		int _A = (int) (((type >> A_SHIFT) & MASK) - ZERO);
		int _mol = (int) (((type >> mol_SHIFT) & MASK) - ZERO);
		int _cd = (int) (((type >> cd_SHIFT) & MASK) - ZERO);
		int _rad = (int) (((type >> rad_SHIFT) & MASK) - ZERO);
		StringBuffer numerator = new StringBuffer();
		StringBuffer denominator = new StringBuffer();
		addSIname(_m, "m", numerator, denominator);
		addSIname(_s, "s", numerator, denominator);
		addSIname(_kg, "kg", numerator, denominator);
		addSIname(_K, "K", numerator, denominator);
		addSIname(_A, "A", numerator, denominator);
		addSIname(_mol, "mol", numerator, denominator);
		addSIname(_cd, "cd", numerator, denominator);
		addSIname(_rad, "rad", numerator, denominator);
		if (denominator.length() > 0) {
			if (numerator.length() == 0) {
				numerator.append("1");
			}
			numerator.append("/");
			numerator.append(denominator.toString());
		}
		return numerator.toString();
	}

	private static void addSIname(int si, String name, StringBuffer numerator, StringBuffer denominator) {
		if (si != 0) {
			StringBuffer sb = (si > 0) ? numerator : denominator;
			if (sb.length() > 0) {
				sb.append("*");
			}
			sb.append(name);
			int power = Math.abs(si);
			if (power > 1) {
				sb.append("^");
				sb.append(power);
			}
		}
	}

	/**
	 * Checks whether the unit has a special type, i.e. not a SI unit.
	 * 
	 * @return true if the type is special, otherwise false.
	 */
	private boolean isSpecial() {
		return (type & x_MASK) != 0;
	}
}

Back to the top