1 package org.djunits.quantity;
2
3 import java.io.Serializable;
4 import java.util.LinkedHashMap;
5 import java.util.LinkedHashSet;
6 import java.util.Locale;
7 import java.util.Map;
8 import java.util.Set;
9
10 import org.djunits.locale.UnitLocale;
11 import org.djunits.unit.Unit;
12 import org.djunits.unit.si.SIDimensions;
13 import org.djunits.unit.si.SIPrefix;
14 import org.djunits.unit.si.SIPrefixes;
15 import org.djunits.unit.util.UnitException;
16 import org.djunits.unit.util.UnitRuntimeException;
17 import org.djutils.exceptions.Throw;
18
19
20
21
22
23
24
25
26
27
28
29 public class Quantity<U extends Unit<U>> implements Serializable
30 {
31
32 private static final long serialVersionUID = 20190818L;
33
34
35
36
37
38 private final SIDimensions siDimensions;
39
40
41 private final String name;
42
43
44 private final Map<String, U> unitsById = new LinkedHashMap<String, U>();
45
46
47 private final Map<String, U> unitsByAbbreviation = new LinkedHashMap<String, U>();
48
49
50 private U standardUnit = null;
51
52
53 private final Map<String, U> unitsByLocalizedAbbreviation = new LinkedHashMap<String, U>();
54
55
56 private static Locale currentLocale = null;
57
58
59 private static UnitLocale localization = new UnitLocale("unit");
60
61
62
63
64
65
66
67 public Quantity(final String name, final SIDimensions siDimensions)
68 {
69 Throw.whenNull(name, "name cannot be null");
70 Throw.when(name.length() == 0, UnitRuntimeException.class, "name of unit cannot be empty");
71 Throw.whenNull(siDimensions, "siDimensions cannot be null");
72 this.name = name;
73 this.siDimensions = siDimensions;
74 }
75
76
77
78
79
80
81
82
83 public Quantity(final String name, final String siString) throws UnitRuntimeException
84 {
85 Throw.whenNull(name, "name cannot be null");
86 Throw.when(name.length() == 1, UnitRuntimeException.class, "name of unit cannot be empty");
87 Throw.whenNull(siString, "siString cannot be null");
88 this.name = name;
89 try
90 {
91 this.siDimensions = SIDimensions.of(siString);
92 }
93 catch (UnitException exception)
94 {
95 throw new UnitRuntimeException(exception);
96 }
97 }
98
99
100
101
102
103
104
105 public Quantity(final String name, final byte[] siSignature)
106 {
107 this(name, new SIDimensions(siSignature));
108 }
109
110
111
112
113
114
115
116
117
118
119
120
121 public void registerUnit(final U unit, final SIPrefixes siPrefixes, final double siPrefixPower)
122 {
123 Throw.whenNull(unit, "unit cannot be null");
124 if (this.standardUnit == null)
125 {
126 this.standardUnit = unit;
127 Quantities.INSTANCE.register(this);
128 }
129 if (siPrefixes.equals(SIPrefixes.UNIT))
130 {
131 for (SIPrefix siPrefix : SIPrefixes.UNIT_PREFIXES.values())
132 {
133 unit.deriveSI(siPrefix, siPrefixPower, true);
134
135 }
136 }
137 else if (siPrefixes.equals(SIPrefixes.UNIT_POS))
138 {
139 for (SIPrefix siPrefix : SIPrefixes.UNIT_POS_PREFIXES.values())
140 {
141 unit.deriveSI(siPrefix, siPrefixPower, true);
142 }
143 }
144 else if (siPrefixes.equals(SIPrefixes.KILO))
145 {
146 for (SIPrefix siPrefix : SIPrefixes.KILO_PREFIXES.values())
147 {
148 unit.deriveSIKilo(siPrefix, siPrefixPower, true);
149 }
150 }
151 else if (siPrefixes.equals(SIPrefixes.PER_UNIT))
152 {
153 for (SIPrefix siPrefix : SIPrefixes.PER_UNIT_PREFIXES.values())
154 {
155 unit.derivePerSI(siPrefix, siPrefixPower, true);
156 }
157 }
158
159
160 if (this.unitsById.containsKey(unit.getId()))
161 {
162
163 if (this.unitsById.get(unit.getId()).isGenerated() == unit.isGenerated())
164 {
165 throw new UnitRuntimeException("A unit with id " + unit.getId() + " has already been registered for unit type "
166 + unit.getClass().getSimpleName());
167 }
168 else
169 {
170 if (!unit.isGenerated())
171 {
172
173 this.unitsById.put(unit.getId(), unit);
174 }
175
176 }
177 }
178 else
179 {
180
181 this.unitsById.put(unit.getId(), unit);
182 }
183
184
185 for (String abbreviation : unit.getDefaultAbbreviations())
186 {
187 if (this.unitsByAbbreviation.containsKey(abbreviation))
188 {
189
190 if (this.unitsByAbbreviation.get(abbreviation).isGenerated() == unit.isGenerated())
191 {
192 throw new UnitRuntimeException("A unit with abbreviation " + abbreviation
193 + " has already been registered for unit type " + unit.getClass().getSimpleName());
194 }
195 else
196 {
197 if (!unit.isGenerated())
198 {
199
200 this.unitsByAbbreviation.put(abbreviation, unit);
201 }
202
203 }
204 }
205 else
206 {
207
208 this.unitsByAbbreviation.put(abbreviation, unit);
209 }
210 }
211 }
212
213
214
215
216
217 public void unregister(final U unit)
218 {
219 Throw.whenNull(unit, "null unit cannot be removed from the unit registry");
220 if (this.unitsById.containsValue(unit))
221 {
222 this.unitsById.remove(unit.getId(), unit);
223 }
224 for (String abbreviation : unit.getDefaultAbbreviations())
225 {
226 if (this.unitsByAbbreviation.containsKey(abbreviation))
227 {
228 if (unit.equals(this.unitsByAbbreviation.get(abbreviation)))
229 {
230 this.unitsByAbbreviation.remove(abbreviation, unit);
231 }
232 }
233 }
234 }
235
236
237
238
239
240 public final String getName()
241 {
242 return this.name;
243 }
244
245
246
247
248 public final SIDimensions getSiDimensions()
249 {
250 return this.siDimensions;
251 }
252
253
254
255
256
257
258 public U getUnitById(final String id)
259 {
260 return this.unitsById.get(id);
261 }
262
263
264
265
266 protected void checkLocale()
267 {
268 if (currentLocale == null || !currentLocale.equals(Locale.getDefault(Locale.Category.DISPLAY)))
269 {
270 localization.checkReload();
271 this.unitsByLocalizedAbbreviation.clear();
272 for (String id : this.unitsById.keySet())
273 {
274 String[] abbreviationArray = localization.getString(getName() + "." + id).split("\\|");
275 for (String abb : abbreviationArray)
276 {
277 this.unitsByLocalizedAbbreviation.put(abb.strip(), this.unitsById.get(id));
278 }
279 }
280 currentLocale = Locale.getDefault(Locale.Category.DISPLAY);
281 }
282 }
283
284
285
286
287
288
289
290
291 public U getUnitByAbbreviation(final String abbreviation)
292 {
293 checkLocale();
294 U unit = this.unitsByLocalizedAbbreviation.get(abbreviation);
295 if (unit == null)
296 {
297 unit = this.unitsByLocalizedAbbreviation.get(abbreviation.replaceAll("[ .^]", ""));
298 }
299 if (unit == null)
300 {
301 unit = this.unitsByAbbreviation.get(abbreviation);
302 }
303 if (unit == null)
304 {
305 unit = this.unitsByAbbreviation.get(abbreviation.replaceAll("[ .^]", ""));
306 }
307 if (unit == null)
308 {
309 try
310 {
311 SIDimensions dim = SIDimensions.of(abbreviation);
312 if (dim != null && dim.equals(this.siDimensions))
313 {
314 unit = this.standardUnit;
315 }
316 }
317 catch (UnitException exception)
318 {
319 unit = null;
320 }
321 }
322 return unit;
323 }
324
325
326
327
328
329
330
331
332 public U of(final String abbreviation)
333 {
334 return this.getUnitByAbbreviation(abbreviation);
335 }
336
337
338
339
340
341 public Map<String, U> getUnitsById()
342 {
343 return new LinkedHashMap<>(this.unitsById);
344 }
345
346
347
348
349
350 public Map<String, U> getUnitsByAbbreviation()
351 {
352 return new LinkedHashMap<>(this.unitsByAbbreviation);
353 }
354
355
356
357
358
359 public Map<String, U> getUnitsByLocalizedAbbreviation()
360 {
361 return new LinkedHashMap<>(this.unitsByLocalizedAbbreviation);
362 }
363
364
365
366
367
368
369 public Set<String> getLocalizedAbbreviations(final U unit)
370 {
371 String[] abbreviationArray = localization.getString(getName() + "." + unit.getId()).split("\\|");
372 Set<String> set = new LinkedHashSet<>();
373 for (String abb : abbreviationArray)
374 {
375 set.add(abb.strip());
376 }
377 return set;
378 }
379
380
381
382
383
384
385 public String getLocalizedDisplayAbbreviation(final U unit)
386 {
387 String[] abbreviationArray = localization.getString(getName() + "." + unit.getId()).split("\\|");
388 return abbreviationArray[0].strip();
389 }
390
391
392
393
394
395
396 public String getLocalizedTextualAbbreviation(final U unit)
397 {
398 String[] abbreviationArray = localization.getString(getName() + "." + unit.getId()).split("\\|");
399 return (abbreviationArray.length > 1) ? abbreviationArray[1].strip() : abbreviationArray[0].strip();
400 }
401
402
403
404
405
406 public String getLocalizedName()
407 {
408 return localization.getString(getName());
409 }
410
411
412
413
414
415 public U getStandardUnit()
416 {
417 return this.standardUnit;
418 }
419
420 @Override
421 public int hashCode()
422 {
423
424
425
426 final int prime = 31;
427 int result = 1;
428 result = prime * result + ((this.siDimensions == null) ? 0 : this.siDimensions.hashCode());
429 result = prime * result + ((this.standardUnit == null) ? 0 : this.standardUnit.getId().hashCode());
430 result = prime * result + ((this.unitsByAbbreviation == null) ? 0 : this.unitsByAbbreviation.keySet().hashCode());
431 result = prime * result + ((this.unitsById == null) ? 0 : this.unitsById.keySet().hashCode());
432 return result;
433 }
434
435 @Override
436 @SuppressWarnings("checkstyle:needbraces")
437 public boolean equals(final Object obj)
438 {
439 if (this == obj)
440 return true;
441 if (obj == null)
442 return false;
443 if (getClass() != obj.getClass())
444 return false;
445 Quantity<?> other = (Quantity<?>) obj;
446 if (this.siDimensions == null)
447 {
448 if (other.siDimensions != null)
449 return false;
450 }
451 else if (!this.siDimensions.equals(other.siDimensions))
452 return false;
453 if (this.standardUnit == null)
454 {
455 if (other.standardUnit != null)
456 return false;
457 }
458
459 else if (!this.standardUnit.getId().equals(other.standardUnit.getId()))
460 return false;
461 if (this.unitsByAbbreviation == null)
462 {
463 if (other.unitsByAbbreviation != null)
464 return false;
465 }
466
467 else if (!this.unitsByAbbreviation.keySet().equals(other.unitsByAbbreviation.keySet()))
468 return false;
469 if (this.unitsById == null)
470 {
471 if (other.unitsById != null)
472 return false;
473 }
474
475 else if (!this.unitsById.keySet().equals(other.unitsById.keySet()))
476 return false;
477 return true;
478 }
479
480 @Override
481 public String toString()
482 {
483 return "Quantity [standardUnit=" + this.standardUnit + ", name=" + this.name + ", siDimensions=" + this.siDimensions
484 + "]";
485 }
486
487 }