1 package org.djunits.unit.si;
2
3 import java.io.Serializable;
4 import java.util.Arrays;
5
6 import org.djunits.Throw;
7 import org.djunits.unit.util.UnitException;
8
9
10
11
12
13
14
15
16
17
18 public class SIDimensions implements Serializable
19 {
20
21 private static final long serialVersionUID = 20190818L;
22
23
24 public static final int NUMBER_DIMENSIONS = 9;
25
26
27 private static final byte[] UNIT_DENOMINATOR = new byte[] {1, 1, 1, 1, 1, 1, 1, 1, 1};
28
29
30 public static final String[] SI_ABBREVIATIONS = new String[] {"rad", "sr", "kg", "m", "s", "A", "K", "mol", "cd"};
31
32
33 private static final int[] PARSE_ORDER = new int[] {0, 1, 2, 7, 3, 4, 5, 6, 8};
34
35
36
37
38
39
40 private final byte[] dimensions;
41
42
43 private final byte[] denominator;
44
45
46 private final boolean fractional;
47
48
49
50
51
52
53
54
55 public SIDimensions(final byte[] dimensions)
56 {
57 Throw.whenNull(dimensions, "dimensions cannot be null");
58 Throw.when(dimensions.length != NUMBER_DIMENSIONS, SIRuntimeException.class, "SIDimensions wrong dimensionality");
59 this.dimensions = dimensions.clone();
60 this.denominator = UNIT_DENOMINATOR;
61 this.fractional = false;
62 }
63
64
65
66
67
68
69
70
71
72
73
74 protected SIDimensions(final byte[] numerator, final byte[] denominator)
75 {
76
77 Throw.whenNull(numerator, "numerator cannot be null");
78 Throw.whenNull(denominator, "denominator cannot be null");
79 Throw.when(numerator.length != NUMBER_DIMENSIONS, SIRuntimeException.class, "numerator has wrong dimensionality");
80 Throw.when(denominator.length != NUMBER_DIMENSIONS, SIRuntimeException.class, "denominator has wrong dimensionality");
81 this.dimensions = numerator.clone();
82 this.denominator = denominator.clone();
83 this.fractional = !Arrays.equals(denominator, UNIT_DENOMINATOR);
84 }
85
86
87
88
89
90
91
92
93
94
95
96
97
98 @SuppressWarnings("checkstyle:parameternumber")
99 public SIDimensions(final int angle, final int solidAngle, final int mass, final int length, final int time,
100 final int current, final int temperature, final int amountOfSubstance, final int luminousIntensity)
101 {
102 this.dimensions = new byte[NUMBER_DIMENSIONS];
103 this.dimensions[0] = (byte) angle;
104 this.dimensions[1] = (byte) solidAngle;
105 this.dimensions[2] = (byte) mass;
106 this.dimensions[3] = (byte) length;
107 this.dimensions[4] = (byte) time;
108 this.dimensions[5] = (byte) current;
109 this.dimensions[6] = (byte) temperature;
110 this.dimensions[7] = (byte) amountOfSubstance;
111 this.dimensions[8] = (byte) luminousIntensity;
112 this.denominator = UNIT_DENOMINATOR;
113 this.fractional = false;
114 }
115
116
117
118
119
120
121
122
123
124
125
126
127 public static SIDimensions of(final String siString) throws UnitException
128 {
129 Throw.whenNull(siString, "siString cannot be null");
130 String dimString = siString.replaceAll("[ .^]", "");
131
132 if (dimString.contains("/"))
133 {
134 String[] parts = dimString.split("\\/");
135 if (parts.length != 2)
136 {
137 throw new UnitException("SI String " + dimString + " contains more than one division sign");
138 }
139 byte[] numerator = parse(parts[0]);
140 byte[] denominator = parse(parts[1]);
141 for (int i = 0; i < NUMBER_DIMENSIONS; i++)
142 {
143 numerator[i] -= denominator[i];
144 }
145 return new SIDimensions(numerator);
146 }
147 return new SIDimensions(parse(dimString));
148 }
149
150
151
152
153
154
155
156
157
158
159 private static byte[] parse(final String siString) throws UnitException
160 {
161 Throw.whenNull(siString, "siString cannot be null");
162 byte[] result = new byte[NUMBER_DIMENSIONS];
163 if (siString.equals("1") || siString.length() == 0)
164 {
165 return result;
166 }
167 String copy = siString;
168 int copyLength = copy.length();
169 while (copyLength > 0)
170 {
171
172 for (int j = 0; j < SI_ABBREVIATIONS.length; j++)
173 {
174 int i = PARSE_ORDER[j];
175 String si = SI_ABBREVIATIONS[i];
176 if (copy.startsWith(si))
177 {
178 if (result[i] != 0)
179 {
180 throw new UnitException("SI string " + siString + " has a double entry for unit " + si);
181 }
182 copy = copy.substring(si.length());
183 if (copy.length() == 0)
184 {
185 result[i] = 1;
186 break;
187 }
188 else if (copy.startsWith("-"))
189 {
190 if (copy.length() == 1)
191 {
192 throw new UnitException("SI string " + siString + " ends with a minus sign");
193 }
194 if (Character.isDigit(copy.charAt(1)))
195 {
196 result[i] = (byte) (-1 * (copy.charAt(1) - '0'));
197 copy = copy.substring(2);
198 break;
199 }
200 throw new UnitException(
201 "SI string " + siString + " has a minus sign for unit " + si + " but no dimension");
202 }
203 else if (Character.isDigit(copy.charAt(0)))
204 {
205 result[i] = (byte) (copy.charAt(0) - '0');
206 copy = copy.substring(1);
207 break;
208 }
209 else
210 {
211 result[i] = 1;
212 break;
213 }
214 }
215 }
216 if (copy.length() == copyLength)
217 {
218
219 break;
220 }
221 copyLength = copy.length();
222 }
223 if (copy.length() != 0)
224 {
225 throw new UnitException("Trailing information in SI string " + siString);
226 }
227 return result;
228 }
229
230
231
232
233
234
235
236 public SIDimensions.html#SIDimensions">SIDimensions plus(final SIDimensions other)
237 {
238 byte[] result = new byte[NUMBER_DIMENSIONS];
239 for (int i = 0; i < NUMBER_DIMENSIONS; i++)
240 {
241 result[i] = (byte) (this.dimensions[i] + other.dimensions[i]);
242 }
243 return new SIDimensions(result);
244 }
245
246
247
248
249
250
251
252 public SIDimensionshtml#SIDimensions">SIDimensions minus(final SIDimensions other)
253 {
254 byte[] result = new byte[NUMBER_DIMENSIONS];
255 for (int i = 0; i < NUMBER_DIMENSIONS; i++)
256 {
257 result[i] = (byte) (this.dimensions[i] - other.dimensions[i]);
258 }
259 return new SIDimensions(result);
260 }
261
262
263
264
265
266
267 public SIDimensions invert()
268 {
269 byte[] result = new byte[NUMBER_DIMENSIONS];
270 for (int i = 0; i < NUMBER_DIMENSIONS; i++)
271 {
272 result[i] = (byte) (-this.dimensions[i]);
273 }
274 return new SIDimensions(result);
275 }
276
277
278
279
280
281
282
283
284 public static SIDimensionshtml#SIDimensions">SIDimensionss.html#SIDimensions">SIDimensions add(final SIDimensionshtml#SIDimensions">SIDimensions dim1, final SIDimensions dim2)
285 {
286 byte[] dim = new byte[NUMBER_DIMENSIONS];
287 for (int i = 0; i < NUMBER_DIMENSIONS; i++)
288 {
289 dim[i] = (byte) (dim1.dimensions[i] + dim2.dimensions[i]);
290 }
291 return new SIDimensions(dim);
292 }
293
294
295
296
297
298
299
300
301 public static SIDimensionshtml#SIDimensions">SIDimensionsl#SIDimensions">SIDimensions subtract(final SIDimensionshtml#SIDimensions">SIDimensions dim1, final SIDimensions dim2)
302 {
303 byte[] dim = new byte[NUMBER_DIMENSIONS];
304 for (int i = 0; i < NUMBER_DIMENSIONS; i++)
305 {
306 dim[i] = (byte) (dim1.dimensions[i] - dim2.dimensions[i]);
307 }
308 return new SIDimensions(dim);
309 }
310
311
312
313
314
315 public boolean isFractional()
316 {
317 return this.fractional;
318 }
319
320
321 @Override
322 public int hashCode()
323 {
324 final int prime = 31;
325 int result = 1;
326 result = prime * result + Arrays.hashCode(this.denominator);
327 result = prime * result + Arrays.hashCode(this.dimensions);
328 return result;
329 }
330
331
332 @Override
333 @SuppressWarnings("checkstyle:needbraces")
334 public boolean equals(final Object obj)
335 {
336 if (this == obj)
337 return true;
338 if (obj == null)
339 return false;
340 if (getClass() != obj.getClass())
341 return false;
342 SIDimensions/../../org/djunits/unit/si/SIDimensions.html#SIDimensions">SIDimensions other = (SIDimensions) obj;
343 if (!Arrays.equals(this.denominator, other.denominator))
344 return false;
345 if (!Arrays.equals(this.dimensions, other.dimensions))
346 return false;
347 return true;
348 }
349
350
351
352
353
354
355
356
357
358 public String toString(final boolean divided, final String separator, final String powerPrefix, final String powerPostfix)
359 {
360 StringBuffer s = new StringBuffer();
361 boolean first = true;
362 boolean negative = false;
363 for (int i = 0; i < NUMBER_DIMENSIONS; i++)
364 {
365 if (this.dimensions[i] < 0)
366 {
367 negative = true;
368 }
369 if ((!divided && this.dimensions[i] != 0) || (divided && this.dimensions[i] > 0))
370 {
371 if (!first)
372 {
373 s.append(separator);
374 }
375 else
376 {
377 first = false;
378 }
379 s.append(SI_ABBREVIATIONS[i]);
380 if (this.dimensions[i] != 1)
381 {
382 s.append(powerPrefix);
383 s.append(this.dimensions[i]);
384 s.append(powerPostfix);
385 }
386 }
387 }
388 if (s.length() == 0)
389 {
390 s.append("1");
391 }
392 if (divided && negative)
393 {
394 s.append("/");
395 }
396 if (divided)
397 {
398 first = true;
399 for (int i = 0; i < NUMBER_DIMENSIONS; i++)
400 {
401 if (this.dimensions[i] < 0)
402 {
403 if (!first)
404 {
405 s.append(separator);
406 }
407 else
408 {
409 first = false;
410 }
411 s.append(SI_ABBREVIATIONS[i]);
412 if (this.dimensions[i] < -1)
413 {
414 s.append(powerPrefix);
415 s.append(-this.dimensions[i]);
416 s.append(powerPostfix);
417 }
418 }
419 }
420 }
421 return s.toString();
422 }
423
424
425
426
427
428
429
430 public String toString(final boolean divided, final boolean separator)
431 {
432 return toString(divided, separator ? "." : "", "", "");
433 }
434
435
436
437
438
439
440
441
442 public String toString(final boolean divided, final boolean separator, final boolean power)
443 {
444 return toString(divided, separator ? "." : "", power ? "^" : "", "");
445 }
446
447
448
449
450
451
452
453 public String toHTMLString(final boolean divided, final boolean separator)
454 {
455 return toString(divided, separator ? "." : "", "<sup>", "</sup>");
456 }
457
458
459 @Override
460 public String toString()
461 {
462 if (this.fractional)
463 {
464 StringBuffer sb = new StringBuffer();
465 sb.append("[");
466 for (int i = 0; i < NUMBER_DIMENSIONS; i++)
467 {
468 if (i > 0)
469 {
470 sb.append(", ");
471 }
472 if (this.denominator[i] != 1 && this.dimensions[i] != 0)
473 {
474 sb.append(this.dimensions[i] + "/" + this.denominator[i]);
475 }
476 else
477 {
478 sb.append(this.dimensions[i]);
479 }
480 }
481 sb.append("]");
482 return sb.toString();
483 }
484 else
485 {
486 return Arrays.toString(this.dimensions);
487 }
488 }
489
490 }