View Javadoc
1   package org.djunits.unit;
2   
3   import java.io.Serializable;
4   import java.util.Arrays;
5   import java.util.LinkedHashSet;
6   import java.util.Set;
7   
8   import org.djunits.Throw;
9   import org.djunits.unit.quantity.Quantities;
10  import org.djunits.unit.quantity.Quantity;
11  import org.djunits.unit.scale.IdentityScale;
12  import org.djunits.unit.scale.LinearScale;
13  import org.djunits.unit.scale.OffsetLinearScale;
14  import org.djunits.unit.scale.Scale;
15  import org.djunits.unit.si.SIDimensions;
16  import org.djunits.unit.si.SIPrefix;
17  import org.djunits.unit.si.SIPrefixes;
18  import org.djunits.unit.unitsystem.UnitSystem;
19  import org.djunits.unit.util.UnitRuntimeException;
20  
21  /**
22   * All units are internally <i>stored</i> relative to a standard unit with conversion factor. This means that e.g., a meter is
23   * stored with conversion factor 1.0, whereas kilometer is stored with a conversion factor 1000.0. This means that if we want to
24   * express a length meter in kilometers, we have to <i>divide</i> by the conversion factor.
25   * <p>
26   * Copyright (c) 2019-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
27   * BSD-style license. See <a href="https://djunits.org/docs/license.html">DJUNITS License</a>
28   * </p>
29   * @author <a href="https://www.tudelft.nl/averbraeck" target="_blank">Alexander Verbraeck</a>
30   * @param <U> the unit to reference the actual unit in return values
31   */
32  public class Unit<U extends Unit<U>> implements Serializable, Cloneable
33  {
34      /** */
35      private static final long serialVersionUID = 20190818L;
36  
37      /** The id of the unit; has to be unique within the unit name. Used for, e.g., localization and retrieval. */
38      private String id;
39  
40      /** The abbreviations in the default locale. All abbreviations an be used in the valueOf() and of() methods. */
41      private Set<String> abbreviations;
42  
43      /** The default display abbreviation in the default locale for printing. Included in the abbreviations list. */
44      private String defaultDisplayAbbreviation;
45  
46      /** The default textual abbreviation in the default locale for data entry. Included in the abbreviations list. */
47      private String defaultTextualAbbreviation;
48  
49      /** The long name of the unit in the default locale. */
50      private String name;
51  
52      /** The scale to use to convert between this unit and the standard (e.g., SI, BASE) unit. */
53      private Scale scale;
54  
55      /** The unit system, e.g. SI or Imperial. */
56      private UnitSystem unitSystem;
57  
58      /** Has the unit been automatically generated or not. */
59      private boolean generated;
60  
61      /** Does the unit have the standard SI signature? */
62      private boolean baseSIUnit;
63  
64      /**
65       * The corresponding unit base that contains all registered units for the unit as well as SI dimension information. The base
66       * unit of a unit base is null.
67       */
68      private Quantity<U> quantity;
69  
70      // TODO create a static that loads all unit classes in the registry
71  
72      /**
73       * Initialize a blank unit that can be built through reflection with several 'setter' methods followed by calling the
74       * build(...) method.
75       */
76      protected Unit()
77      {
78          //
79      }
80  
81      /**
82       * Build the unit, using the information of the provided builder class. First check rigorously if all necessary fields in
83       * the builder have been set, and whether they are consistent and valid. The behavior is as follows: the defaultAbbreviation
84       * and defaultTextualAbbreviation are added to the abbreviations set, if they are not yet already there. When the
85       * defaultAbbreviation is set and the defaultTextualAbbreviation is not set, the defaultTextualAbbreviation gets the value
86       * of defaultAbbreviation. The reverse also holds: When the defaultTextualAbbreviation is set and the defaultAbbreviation is
87       * not set, the defaultAbbreviation gets the value of defaultTextualAbbreviation. When neither the
88       * defaultTextualAbbreviation, nor the defaultAbbreviation are set, both get the value of the unitId provided in the
89       * builder.
90       * @param builder Builder&lt;U&gt;; Builder&lt;U&gt; the object that contains the information about the construction of the
91       *            class
92       * @return U; the constructed unit
93       * @throws UnitRuntimeException when not all fields have been set
94       */
95      @SuppressWarnings("unchecked")
96      public U build(final Builder<U> builder) throws UnitRuntimeException
97      {
98          // Check the validity
99          String cName = getClass().getSimpleName();
100         Throw.whenNull(builder.getId(), "Constructing unit %s: id cannot be null", cName);
101         Throw.when(builder.getId().length() == 0, UnitRuntimeException.class, "Constructing unit %s: id.length cannot be 0",
102                 cName);
103         String unitId = builder.getId();
104         Throw.whenNull(builder.getQuantity(), "Constructing unit %s.%s: baseUnit cannot be null", cName, unitId);
105         Throw.whenNull(builder.getName(), "Constructing unit %s.%s: name cannot be null", cName, unitId);
106         Throw.when(builder.getName().length() == 0, UnitRuntimeException.class,
107                 "Constructing unit %s.%s: name.length cannot be 0", cName, unitId);
108         Throw.whenNull(builder.getScale(), "Constructing unit %s.%s: scale cannot be null", cName, unitId);
109         Throw.whenNull(builder.getUnitSystem(), "Constructing unit %s.%s: unitSystem cannot be null", cName, unitId);
110 
111         // set the key fields
112         this.id = unitId;
113         this.name = builder.getName();
114         this.quantity = builder.getQuantity();
115         this.unitSystem = builder.getUnitSystem();
116         this.scale = builder.getScale();
117         this.generated = builder.isGenerated();
118         this.baseSIUnit = this.scale.isBaseSIScale();
119 
120         // Set and check the abbreviations
121         if (builder.getDefaultDisplayAbbreviation() == null)
122         {
123             if (builder.getDefaultTextualAbbreviation() == null)
124             {
125                 this.defaultDisplayAbbreviation = unitId;
126             }
127             else
128             {
129                 this.defaultDisplayAbbreviation = builder.getDefaultTextualAbbreviation();
130             }
131         }
132         else
133         {
134             this.defaultDisplayAbbreviation = builder.getDefaultDisplayAbbreviation();
135         }
136         if (builder.getDefaultTextualAbbreviation() == null)
137         {
138             this.defaultTextualAbbreviation = this.defaultDisplayAbbreviation;
139         }
140         else
141         {
142             this.defaultTextualAbbreviation = builder.getDefaultTextualAbbreviation();
143         }
144         this.abbreviations = new LinkedHashSet<>();
145         this.abbreviations.add(this.defaultDisplayAbbreviation);
146         this.abbreviations.add(this.defaultTextualAbbreviation);
147         this.abbreviations.addAll(builder.getAdditionalAbbreviations());
148 
149         // See what SI prefixes have to be registered. If not specified: NONE.
150         SIPrefixes siPrefixes = builder.getSiPrefixes() == null ? SIPrefixes.NONE : builder.getSiPrefixes();
151 
152         // Register the unit, possibly including all SI prefixes
153         this.quantity.registerUnit((U) this, siPrefixes, builder.getSiPrefixPowerFactor());
154         return (U) this;
155     }
156 
157     /**
158      * Create a scaled version of this unit with the same unit system but another SI prefix and scale.
159      * @param siPrefix SIPrefix; the prefix for which to scale the unit
160      * @param siPrefixPower double; the power factor of the SI prefixes, e.g. 2.0 for square meters and 3.0 for cubic meters.
161      * @param automaticallyGenerated boolean; indicate whether the unit has been automatically generated
162      * @return U; a scaled instance of this unit
163      * @throws UnitRuntimeException when cloning fails
164      */
165     public U deriveSI(final SIPrefix siPrefix, final double siPrefixPower, final boolean automaticallyGenerated)
166     {
167         Throw.whenNull(siPrefix, "siPrefix cannot be null");
168         try
169         {
170             U clone = clone();
171 
172             // Get values: combine all prefixes with all names / abbreviations
173             String cloneId = siPrefix.getDefaultTextualPrefix() + clone.getId();
174             String cloneName = clone.getName();
175             if (cloneName.startsWith("square "))
176             {
177                 cloneName = "square " + siPrefix.getPrefixName() + cloneName.substring(6);
178             }
179             else if (clone.getName().startsWith("cubic "))
180             {
181                 cloneName = "cubic " + siPrefix.getPrefixName() + cloneName.substring(5);
182             }
183             else
184             {
185                 cloneName = siPrefix.getPrefixName() + cloneName;
186             }
187             Set<String> cloneAbbreviations = new LinkedHashSet<>();
188             for (String abbreviation : clone.getAbbreviations())
189             {
190                 cloneAbbreviations.add(siPrefix.getDefaultTextualPrefix() + abbreviation);
191             }
192             String cloneDefaultAbbreviation = siPrefix.getDefaultDisplayPrefix() + clone.getDefaultDisplayAbbreviation();
193             String cloneDefaultTextualAbbreviation = siPrefix.getDefaultTextualPrefix() + clone.getDefaultTextualAbbreviation();
194 
195             // Make a builder and set values
196             Builder<U> builder = makeBuilder();
197             builder.setId(cloneId);
198             builder.setName(cloneName);
199             builder.setQuantity(this.quantity);
200             builder.setSiPrefixes(SIPrefixes.NONE, 1.0);
201             builder.setDefaultDisplayAbbreviation(cloneDefaultAbbreviation);
202             builder.setDefaultTextualAbbreviation(cloneDefaultTextualAbbreviation);
203             builder.setAdditionalAbbreviations(cloneAbbreviations.toArray(new String[cloneAbbreviations.size()]));
204             if (getScale() instanceof OffsetLinearScale)
205             {
206                 builder.setScale(new OffsetLinearScale(
207                         (siPrefixPower == 1.0 ? siPrefix.getFactor() : Math.pow(siPrefix.getFactor(), siPrefixPower))
208                                 * ((LinearScale) getScale()).getConversionFactorToStandardUnit(),
209                         0.0));
210             }
211             else
212             {
213                 builder.setScale(new LinearScale(
214                         (siPrefixPower == 1.0 ? siPrefix.getFactor() : Math.pow(siPrefix.getFactor(), siPrefixPower))
215                                 * ((LinearScale) getScale()).getConversionFactorToStandardUnit()));
216             }
217             builder.setUnitSystem(this.unitSystem); // SI_BASE stays SI_BASE with prefix
218             builder.setGenerated(automaticallyGenerated);
219 
220             return clone.build(builder);
221         }
222         catch (CloneNotSupportedException exception)
223         {
224             throw new UnitRuntimeException(exception);
225         }
226     }
227 
228     /**
229      * Create a scaled version of this unit with the same unit system but another SI prefix and scale. This method is used for a
230      * unit that is explicitly scaled with an SI prefix.
231      * @param siPrefix SIPrefix; the prefix for which to scale the unit
232      * @param siPrefixPower double; the power factor of the SI prefixes, e.g. 2.0 for square meters and 3.0 for cubic meters.
233      * @return a scaled instance of this unit
234      * @throws UnitRuntimeException when cloning fails
235      */
236     public U deriveSI(final SIPrefix siPrefix, final double siPrefixPower)
237     {
238         return deriveSI(siPrefix, siPrefixPower, false);
239     }
240 
241     /**
242      * Create a scaled version of this unit with the same unit system but another SI prefix and scale, where the "k" and "kilo"
243      * abbreviations at the start will be replaced by the new information from the SIPrefix.
244      * @param siPrefix SIPrefix; the prefix for which to scale the unit
245      * @param siPrefixPower double; the power factor of the SI prefixes, e.g. 2.0 for square meters and 3.0 for cubic meters.
246      * @param automaticallyGenerated boolean; indicate whether the unit has been automatically generated
247      * @return U; a scaled instance of this unit
248      * @throws UnitRuntimeException when cloning fails
249      */
250     public U deriveSIKilo(final SIPrefix siPrefix, final double siPrefixPower, final boolean automaticallyGenerated)
251     {
252         Throw.whenNull(siPrefix, "siPrefix cannot be null");
253         Throw.when(!this.id.startsWith("k"), UnitRuntimeException.class, "deriving from a kilo-unit: id should start with 'k'");
254         Throw.when(!this.defaultTextualAbbreviation.startsWith("k"), UnitRuntimeException.class,
255                 "deriving from a kilo-unit: defaultTextualAbbreviation should start with 'k'");
256         Throw.when(!this.defaultDisplayAbbreviation.startsWith("k"), UnitRuntimeException.class,
257                 "deriving from a kilo-unit: defaultDisplayAbbreviation should start with 'k'");
258         Throw.when(!this.name.startsWith("kilo"), UnitRuntimeException.class,
259                 "deriving from a kilo-unit: name should start with 'kilo'");
260         for (String abbreviation : getAbbreviations())
261         {
262             Throw.when(!abbreviation.startsWith("k"), UnitRuntimeException.class,
263                     "deriving from a kilo-unit: each abbreviation should start with 'k'");
264         }
265 
266         try
267         {
268             U clone = clone();
269 
270             // get values: combine all prefixes with all names / abbreviations
271             String cloneId = siPrefix.getDefaultTextualPrefix() + clone.getId().substring(1);
272             String cloneName = siPrefix.getPrefixName() + clone.getName().substring(4);
273             Set<String> cloneAbbreviations = new LinkedHashSet<>();
274             for (String abbreviation : clone.getAbbreviations())
275             {
276                 cloneAbbreviations.add(siPrefix.getDefaultTextualPrefix() + abbreviation.substring(1));
277             }
278             String cloneDefaultAbbreviation =
279                     siPrefix.getDefaultDisplayPrefix() + clone.getDefaultDisplayAbbreviation().substring(1);
280             String cloneDefaultTextualAbbreviation =
281                     siPrefix.getDefaultTextualPrefix() + clone.getDefaultTextualAbbreviation().substring(1);
282 
283             // make a builder and set values
284             Builder<U> builder = makeBuilder();
285             builder.setId(cloneId);
286             builder.setName(cloneName);
287             builder.setQuantity(this.quantity);
288             builder.setSiPrefixes(SIPrefixes.NONE, 1.0);
289             builder.setDefaultDisplayAbbreviation(cloneDefaultAbbreviation);
290             builder.setDefaultTextualAbbreviation(cloneDefaultTextualAbbreviation);
291             builder.setAdditionalAbbreviations(cloneAbbreviations.toArray(new String[cloneAbbreviations.size()]));
292             if (getScale() instanceof OffsetLinearScale)
293             {
294                 builder.setScale(new OffsetLinearScale(
295                         (siPrefixPower == 1.0 ? siPrefix.getFactor() : Math.pow(siPrefix.getFactor(), siPrefixPower))
296                                 * ((LinearScale) getScale()).getConversionFactorToStandardUnit(),
297                         0.0));
298             }
299             else
300             {
301                 builder.setScale(new LinearScale(
302                         (siPrefixPower == 1.0 ? siPrefix.getFactor() : Math.pow(siPrefix.getFactor(), siPrefixPower))
303                                 * ((LinearScale) getScale()).getConversionFactorToStandardUnit()));
304             }
305 
306             builder.setUnitSystem(this.unitSystem);
307             builder.setGenerated(automaticallyGenerated);
308 
309             return clone.build(builder);
310         }
311         catch (CloneNotSupportedException exception)
312         {
313             throw new UnitRuntimeException(exception);
314         }
315     }
316 
317     /**
318      * Create a scaled version of this unit with the same unit system but another SI prefix and scale. The "per" units scale in
319      * the opposite direction as the normally scaled units. It will yield units like "/ms", "/mus", "/ns", etc.
320      * @param siPrefix SIPrefix; the prefix for which to scale the unit
321      * @param siPrefixPower double; the power factor of the SI prefixes, e.g. 2.0 for square meters and 3.0 for cubic meters.
322      * @param automaticallyGenerated boolean; indicate whether the unit has been automatically generated
323      * @return U; a scaled instance of this unit
324      * @throws UnitRuntimeException when cloning fails
325      */
326     public U derivePerSI(final SIPrefix siPrefix, final double siPrefixPower, final boolean automaticallyGenerated)
327     {
328         Throw.whenNull(siPrefix, "siPrefix cannot be null");
329         try
330         {
331             U clone = clone();
332 
333             // Get values: combine all prefixes with all names / abbreviations
334             String cloneId = siPrefix.getDefaultTextualPrefix() + clone.getId().replace("1/", "").replace("/", "").trim();
335             String cloneName =
336                     siPrefix.getPrefixName() + clone.getName().replace("per", "").replace("1/", "").replace("/", "").trim();
337             Set<String> cloneAbbreviations = new LinkedHashSet<>();
338             for (String abbreviation : clone.getAbbreviations())
339             {
340                 cloneAbbreviations
341                         .add(siPrefix.getDefaultTextualPrefix() + abbreviation.replace("1/", "").replace("/", "").trim());
342                 cloneAbbreviations
343                         .add("1" + siPrefix.getDefaultTextualPrefix() + abbreviation.replace("1/", "").replace("/", "").trim());
344             }
345             String cloneDefaultAbbreviation = siPrefix.getDefaultDisplayPrefix()
346                     + clone.getDefaultDisplayAbbreviation().replace("1/", "").replace("/", "").trim();
347             String cloneDefaultTextualAbbreviation = siPrefix.getDefaultTextualPrefix()
348                     + clone.getDefaultTextualAbbreviation().replace("1/", "").replace("/", "").trim();
349 
350             // Make a builder and set values
351             Builder<U> builder = makeBuilder();
352             builder.setId(cloneId);
353             builder.setName(cloneName);
354             builder.setQuantity(this.quantity);
355             builder.setSiPrefixes(SIPrefixes.NONE, 1.0);
356             builder.setDefaultDisplayAbbreviation(cloneDefaultAbbreviation);
357             builder.setDefaultTextualAbbreviation(cloneDefaultTextualAbbreviation);
358             builder.setAdditionalAbbreviations(cloneAbbreviations.toArray(new String[cloneAbbreviations.size()]));
359             if (getScale() instanceof OffsetLinearScale)
360             {
361                 builder.setScale(new OffsetLinearScale(
362                         (siPrefixPower == 1.0 ? siPrefix.getFactor() : Math.pow(siPrefix.getFactor(), siPrefixPower))
363                                 * ((LinearScale) getScale()).getConversionFactorToStandardUnit(),
364                         0.0));
365             }
366             else
367             {
368                 builder.setScale(new LinearScale(
369                         (siPrefixPower == 1.0 ? siPrefix.getFactor() : Math.pow(siPrefix.getFactor(), siPrefixPower))
370                                 * ((LinearScale) getScale()).getConversionFactorToStandardUnit()));
371             }
372             builder.setUnitSystem(this.unitSystem); // SI_BASE stays SI_BASE with prefix
373             builder.setGenerated(automaticallyGenerated);
374 
375             return clone.build(builder);
376         }
377         catch (CloneNotSupportedException exception)
378         {
379             throw new UnitRuntimeException(exception);
380         }
381     }
382 
383     /**
384      * Create a linearly scaled version of this unit. The scale field will be filled with the correct scaleFactor. Note that the
385      * unit that is used for derivation can already have a scaleFactor.
386      * @param scaleFactor double; the linear scale factor of the unit
387      * @param derivedId String; the new id of the derived unit
388      * @param derivedName String; the new name of the derived unit
389      * @param derivedUnitSystem UnitSystem; the unit system of the derived unit
390      * @param derivedDefaultDisplayAbbreviation String; the default abbreviation to use in e.g, the toString() method. Can be
391      *            null.
392      * @param derivedDefaultTextualAbbreviation String; the default textual abbreviation to use in, e.g, typing. Can be null.
393      * @param derivedAbbreviations String...; the other valid abbreviations for the unit, e.g. {"h", "hr", "hour"}. Can be left
394      *            out.
395      * @return U; a linearly scaled instance of this unit with new id, abbreviation, name, and unit system
396      * @throws UnitRuntimeException when cloning fails
397      */
398     public U deriveLinear(final double scaleFactor, final String derivedId, final String derivedName,
399             final UnitSystem derivedUnitSystem, final String derivedDefaultDisplayAbbreviation,
400             final String derivedDefaultTextualAbbreviation, final String... derivedAbbreviations)
401     {
402         String cName = getClass().getSimpleName();
403         Throw.whenNull(derivedId, "deriving unit from %s.%s; derivedId cannot be null", cName, this.id);
404         Throw.whenNull(derivedName, "deriving unit from %s.%s; derivedName cannot be null", cName, this.id);
405         Throw.whenNull(derivedUnitSystem, "deriving unit from %s.%s; derivedUnitSystem cannot be null", cName, this.id);
406         if (!getScale().getClass().equals(LinearScale.class) && !getScale().getClass().equals(IdentityScale.class))
407         {
408             throw new UnitRuntimeException("Cannot derive from unit " + cName + "." + getId() + " with scale "
409                     + getScale().getClass().getSimpleName() + ". Scale should be Linear");
410         }
411 
412         try
413         {
414             U clone = clone();
415 
416             // make a builder and set values
417             Builder<U> builder = makeBuilder();
418             builder.setId(derivedId);
419             builder.setName(derivedName);
420             builder.setQuantity(this.quantity);
421             builder.setSiPrefixes(SIPrefixes.NONE, 1.0);
422             builder.setScale(new LinearScaleg/djunits/unit/scale/LinearScale.html#LinearScale">LinearScale(scaleFactor * ((LinearScale) getScale()).getConversionFactorToStandardUnit()));
423             builder.setUnitSystem(derivedUnitSystem);
424             builder.setDefaultDisplayAbbreviation(derivedDefaultDisplayAbbreviation);
425             builder.setDefaultTextualAbbreviation(derivedDefaultTextualAbbreviation);
426             if (derivedAbbreviations != null)
427             {
428                 builder.setAdditionalAbbreviations(derivedAbbreviations);
429             }
430 
431             return clone.build(builder);
432         }
433         catch (CloneNotSupportedException exception)
434         {
435             throw new UnitRuntimeException(exception);
436         }
437     }
438 
439     /**
440      * Create a linearly scaled version of this unit. The scale field will be filled with the correct scaleFactor. Note that the
441      * unit that is used for derivation can already have a scaleFactor.
442      * @param scaleFactor double; the linear scale factor of the unit
443      * @param derivedId String; the new id of the derived unit
444      * @param derivedName String; the new name of the derived unit
445      * @param derivedUnitSystem UnitSystem; the unit system of the derived unit
446      * @return U; a linearly scaled instance of this unit with new id, abbreviation, name, and unit system
447      * @throws UnitRuntimeException when cloning fails
448      */
449     public U deriveLinear(final double scaleFactor, final String derivedId, final String derivedName,
450             final UnitSystem derivedUnitSystem)
451     {
452         return deriveLinear(scaleFactor, derivedId, derivedName, derivedUnitSystem, null, null);
453     }
454 
455     /**
456      * Create a linearly scaled version of this unit. The unitSystem will be copied. The scale field will be filled with the
457      * correct scaleFactor. Note that the unit that is used for derivation can already have a scaleFactor.
458      * @param scaleFactor double; the linear scale factor of the unit
459      * @param derivedId String; the new id of the derived unit
460      * @param derivedName String; the new name of the derived unit
461      * @return U; a linearly scaled instance of this unit with new id, abbreviation, name, and unit system
462      * @throws UnitRuntimeException when cloning fails
463      */
464     public U deriveLinear(final double scaleFactor, final String derivedId, final String derivedName)
465     {
466         return deriveLinear(scaleFactor, derivedId, derivedName, getUnitSystem());
467     }
468 
469     /**
470      * Create a Builder. Override at subclasses to create extended builder.
471      * @return an instance of a builder.
472      */
473     public Builder<U> makeBuilder()
474     {
475         return new Builder<U>();
476     }
477 
478     /**
479      * Create or lookup a unit based on given SI dimensions. E.g., a unit with dimensions 1/s^2 or kg.m/s^2.
480      * @param siDimensions SIDimensions; the vector with the dimensionality of the unit
481      * @return SIUnit; an SIUnit object with the right dimensions
482      */
483     @SuppressWarnings("unchecked")
484     public static SIUnit lookupOrCreateUnitWithSIDimensions(final SIDimensions siDimensions)
485     {
486         Throw.whenNull(siDimensions, "siDimensions cannot be null");
487 
488         Quantity<SIUnit> quantity = null;
489         SIUnit unit = null;
490 
491         Set<Quantity<?>> baseUnitSet = Quantities.INSTANCE.getQuantities(siDimensions);
492         for (Quantity<?> bu : baseUnitSet)
493         {
494             if (bu.getStandardUnit().getClass().equals(Unit.class))
495             {
496                 quantity = (Quantity<SIUnit>) bu;
497             }
498         }
499 
500         if (quantity == null)
501         {
502             quantity = new Quantity<SIUnit>(siDimensions.toString(), siDimensions);
503             Builder<SIUnit> builder = new Builder<>();
504             builder.setId(siDimensions.toString(true, true));
505             builder.setName(siDimensions.toString(true, true));
506             builder.setQuantity(quantity);
507             builder.setScale(IdentityScale.SCALE);
508             builder.setGenerated(true);
509             builder.setUnitSystem(UnitSystem.SI_DERIVED);
510             builder.setSiPrefixes(SIPrefixes.NONE, 1.0);
511             unit = new SIUnit();
512             unit.build(builder); // it will be registered at the BaseUnit
513         }
514         else
515         {
516             unit = quantity.getStandardUnit();
517         }
518 
519         return unit;
520     }
521 
522     /**
523      * Retrieve the unit id.
524      * @return String; the unit id
525      */
526     public String getId()
527     {
528         return this.id;
529     }
530 
531     /**
532      * Retrieve a safe copy of the unit abbreviations.
533      * @return Set&lt;String&gt;; the unit abbreviations
534      */
535     public Set<String> getAbbreviations()
536     {
537         return new LinkedHashSet<>(this.abbreviations);
538     }
539 
540     /**
541      * Retrieve the default abbreviation.
542      * @return String; the default abbreviation
543      */
544     public String getDefaultDisplayAbbreviation()
545     {
546         return this.defaultDisplayAbbreviation;
547     }
548 
549     /**
550      * Retrieve the default textual abbreviation.
551      * @return String; the default textual abbreviation
552      */
553     public String getDefaultTextualAbbreviation()
554     {
555         return this.defaultTextualAbbreviation;
556     }
557 
558     /**
559      * Retrieve the name of this unit.
560      * @return String; the name of this unit
561      */
562     public String getName()
563     {
564         return this.name;
565     }
566 
567     /**
568      * Retrieve the scale of this unit.
569      * @return Scale; the scale of this unit
570      */
571     public Scale getScale()
572     {
573         return this.scale;
574     }
575 
576     /**
577      * Retrieve the unit system of this unit.
578      * @return unitSystem the unit system of this unit
579      */
580     public UnitSystem getUnitSystem()
581     {
582         return this.unitSystem;
583     }
584 
585     /**
586      * Retrieve the unit base of this unit.
587      * @return BaseUnit&lt;U&gt;; the unit base of this unit. if this unit is itself a unit base; the returned value is
588      *         <code>null</code>
589      */
590     public Quantity<U> getQuantity()
591     {
592         return this.quantity;
593     }
594 
595     /**
596      * Indicate whether is unit was automatically generated.
597      * @return boolean; true if this unit has been automatically generate; false if it was not automatically generated
598      */
599     public boolean isGenerated()
600     {
601         return this.generated;
602     }
603 
604     /**
605      * Indicate whether this unit has the standard SI signature.
606      * @return boolean; true if this unit has the standard SI signature; false if this unit does not have the standard SI
607      *         signature
608      */
609     public boolean isBaseSIUnit()
610     {
611         return this.baseSIUnit;
612     }
613 
614     /**
615      * Retrieve the standard unit (SI Unit) belonging to this unit.
616      * @return U; the standard unit (SI unit) belonging to this unit
617      */
618     public U getStandardUnit()
619     {
620         return getQuantity().getStandardUnit();
621     }
622 
623     /** {@inheritDoc} */
624     @SuppressWarnings("unchecked")
625     @Override
626     public U clone() throws CloneNotSupportedException
627     {
628         return (U) super.clone();
629     }
630 
631     /** {@inheritDoc} */
632     @Override
633     public int hashCode()
634     {
635         final int prime = 31;
636         int result = 1;
637         result = prime * result + ((this.abbreviations == null) ? 0 : this.abbreviations.hashCode());
638         result = prime * result + ((this.quantity == null) ? 0 : this.quantity.hashCode());
639         result = prime * result + ((this.defaultDisplayAbbreviation == null) ? 0 : this.defaultDisplayAbbreviation.hashCode());
640         result = prime * result + ((this.defaultTextualAbbreviation == null) ? 0 : this.defaultTextualAbbreviation.hashCode());
641         result = prime * result + (this.generated ? 1231 : 1237);
642         result = prime * result + ((this.id == null) ? 0 : this.id.hashCode());
643         result = prime * result + ((this.name == null) ? 0 : this.name.hashCode());
644         result = prime * result + ((this.scale == null) ? 0 : this.scale.hashCode());
645         result = prime * result + ((this.unitSystem == null) ? 0 : this.unitSystem.hashCode());
646         return result;
647     }
648 
649     /** {@inheritDoc} */
650     @Override
651     @SuppressWarnings("checkstyle:needbraces")
652     public boolean equals(final Object obj)
653     {
654         if (this == obj)
655             return true;
656         if (obj == null)
657             return false;
658         if (getClass() != obj.getClass())
659             return false;
660         Unit<?> other = (Unit<?>) obj;
661         if (this.abbreviations == null)
662         {
663             if (other.abbreviations != null)
664                 return false;
665         }
666         else if (!this.abbreviations.equals(other.abbreviations))
667             return false;
668         if (this.quantity == null)
669         {
670             if (other.quantity != null)
671                 return false;
672         }
673         else if (!this.quantity.equals(other.quantity))
674             return false;
675         if (this.defaultDisplayAbbreviation == null)
676         {
677             if (other.defaultDisplayAbbreviation != null)
678                 return false;
679         }
680         else if (!this.defaultDisplayAbbreviation.equals(other.defaultDisplayAbbreviation))
681             return false;
682         if (this.defaultTextualAbbreviation == null)
683         {
684             if (other.defaultTextualAbbreviation != null)
685                 return false;
686         }
687         else if (!this.defaultTextualAbbreviation.equals(other.defaultTextualAbbreviation))
688             return false;
689         if (this.generated != other.generated)
690             return false;
691         if (this.id == null)
692         {
693             if (other.id != null)
694                 return false;
695         }
696         else if (!this.id.equals(other.id))
697             return false;
698         if (this.name == null)
699         {
700             if (other.name != null)
701                 return false;
702         }
703         else if (!this.name.equals(other.name))
704             return false;
705         if (this.scale == null)
706         {
707             if (other.scale != null)
708                 return false;
709         }
710         else if (!this.scale.equals(other.scale))
711             return false;
712         if (this.unitSystem == null)
713         {
714             if (other.unitSystem != null)
715                 return false;
716         }
717         else if (!this.unitSystem.equals(other.unitSystem))
718             return false;
719         return true;
720     }
721 
722     /** {@inheritDoc} */
723     @Override
724     public String toString()
725     {
726         return this.defaultDisplayAbbreviation;
727     }
728 
729     /**
730      * The class that contains the information to build a unit.
731      * <p>
732      * Copyright (c) 2019-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
733      * <br>
734      * BSD-style license. See <a href="https://djunits.org/docs/license.html">DJUNITS License</a>.
735      * <p>
736      * @author <a href="https://www.tudelft.nl/averbraeck" target="_blank">Alexander Verbraeck</a>
737      * @param <U> the unit for which the builder contains the information.
738      */
739     public static class Builder<U extends Unit<U>> implements Serializable
740     {
741         /** ... */
742         private static final long serialVersionUID = 20191018L;
743 
744         /** The id of the unit; has to be unique within the unit name. Used for, e.g., localization and retrieval. */
745         private String id;
746 
747         /** The abbreviations in the default locale. All abbreviations an be used in the valueOf() and of() methods. */
748         private Set<String> additionalAbbreviations = new LinkedHashSet<>();
749 
750         /** The default abbreviation in the default locale for printing. Included in the abbreviations list. */
751         private String defaultDisplayAbbreviation;
752 
753         /** The default textual abbreviation in the default locale for data entry. Included in the abbreviations list. */
754         private String defaultTextualAbbreviation;
755 
756         /** The full name of the unit in the default locale. */
757         private String name;
758 
759         /** The scale to use to convert between this unit and the standard (e.g., SI, BASE) unit. */
760         private Scale scale;
761 
762         /** The unit system, e.g. SI or Imperial. */
763         private UnitSystem unitSystem;
764 
765         /** Whether or not the unit supports SI prefixes from "yotta" (y) to "yocto" (Y). */
766         private SIPrefixes siPrefixes;
767 
768         /** The power factor of the SI prefixes, e.g. 2.0 for square meters and 3.0 for cubic meters. */
769         private double siPrefixPower = 1.0;
770 
771         /** Whether the unit has been automatically generated or not. */
772         private boolean generated = false;
773 
774         /**
775          * The corresponding unit base that contains all registered units for the unit as well as SI dimension information. The
776          * unit base should never be null.
777          */
778         private Quantity<U> quantity;
779 
780         /**
781          * Empty constructor. Content is generated through chaining: new
782          * Unit.Builder&lt;TypeUnit&gt;().setId("id").setName("name");
783          */
784         public Builder()
785         {
786             // Empty constructor. Content is generated through (chaining of) method calls
787         }
788 
789         /**
790          * Return whether SI prefixes, ranging from yotta (y) to yocto (Y), will be generated.
791          * @return siPrefixes, NONE (e.g., for inch), ALL (e.g., for meter) or KILO (e.g., for kilometer)
792          */
793         public SIPrefixes getSiPrefixes()
794         {
795             return this.siPrefixes;
796         }
797 
798         /**
799          * Return the power factor of the SI prefixes, e.g. 2.0 for square meters and 3.0 for cubic meters.
800          * @return siPrefixPower double; power factor of the SI prefixes, e.g. 2.0 for square meters and 3.0 for cubic meters
801          */
802         public double getSiPrefixPowerFactor()
803         {
804             return this.siPrefixPower;
805         }
806 
807         /**
808          * Set whether SI prefixes, ranging from yotta (y) to yocto (Y), are allowed. If not set; this property defaults to
809          * <code>false</code>.
810          * @param newSiPrefixes SIPrefixes; SIPrefixes set siPrefixes, NONE (e.g., for inch), ALL (e.g., for meter) or KILO
811          *            (e.g., for kilometer)
812          * @param power double; power factor of the SI prefixes, e.g. 2.0 for square meters and 3.0 for cubic meters
813          * @return Builder; this builder instance that is being constructed (for method call chaining)
814          */
815         public Builder<U> setSiPrefixes(final SIPrefixes newSiPrefixes, final double power)
816         {
817             this.siPrefixes = newSiPrefixes;
818             this.siPrefixPower = power;
819             return this;
820         }
821 
822         /**
823          * Retrieve the id of the unit that this builder builds.
824          * @return String; the id of the unit that this builder builds
825          */
826         public String getId()
827         {
828             return this.id;
829         }
830 
831         /**
832          * Set the id of the unit that this builder builds.
833          * @param newId String; set the id of the unit that this builder builds (must be set; the default is <code>null</code>
834          *            which is invalid)
835          * @return Builder; this builder instance that is being constructed (for method call chaining)
836          */
837         public Builder<U> setId(final String newId)
838         {
839             this.id = newId;
840             return this;
841         }
842 
843         /**
844          * Retrieve the additional abbreviations.
845          * @return Set&lt;String&gt;; the additional abbreviations
846          */
847         public Set<String> getAdditionalAbbreviations()
848         {
849             return this.additionalAbbreviations;
850         }
851 
852         /**
853          * Set the additional abbreviations.
854          * @param newAdditionalAbbreviations String...; the additional abbreviations
855          * @return Builder; this builder instance that is being constructed (for method call chaining)
856          */
857         public Builder<U> setAdditionalAbbreviations(final String... newAdditionalAbbreviations)
858         {
859             this.additionalAbbreviations = new LinkedHashSet<>(Arrays.asList(newAdditionalAbbreviations)); // safe copy
860             return this;
861         }
862 
863         /**
864          * Retrieve the default display abbreviation.
865          * @return String; the default display abbreviation
866          */
867         public String getDefaultDisplayAbbreviation()
868         {
869             return this.defaultDisplayAbbreviation;
870         }
871 
872         /**
873          * Set the default display abbreviation.
874          * @param newDefaultDisplayAbbreviation String; the default display abbreviation
875          * @return Builder; this builder instance that is being constructed (for method call chaining)
876          */
877         public Builder<U> setDefaultDisplayAbbreviation(final String newDefaultDisplayAbbreviation)
878         {
879             this.defaultDisplayAbbreviation = newDefaultDisplayAbbreviation;
880             return this;
881         }
882 
883         /**
884          * Retrieve the default textual abbreviation.
885          * @return String; the default textual abbreviation
886          */
887         public String getDefaultTextualAbbreviation()
888         {
889             return this.defaultTextualAbbreviation;
890         }
891 
892         /**
893          * Set the default textual abbreviation.
894          * @param newDefaultTextualAbbreviation String; the default textual abbreviation
895          * @return Builder; this builder instance that is being constructed (for method call chaining)
896          */
897         public Builder<U> setDefaultTextualAbbreviation(final String newDefaultTextualAbbreviation)
898         {
899             this.defaultTextualAbbreviation = newDefaultTextualAbbreviation;
900             return this;
901         }
902 
903         /**
904          * Return the name.
905          * @return String; the name
906          */
907         public String getName()
908         {
909             return this.name;
910         }
911 
912         /**
913          * Set the name.
914          * @param newName String; the name
915          * @return Builder; this builder instance that is being constructed (for method call chaining)
916          */
917         public Builder<U> setName(final String newName)
918         {
919             this.name = newName;
920             return this;
921         }
922 
923         /**
924          * Retrieve the scale.
925          * @return Scale; the scale
926          */
927         public Scale getScale()
928         {
929             return this.scale;
930         }
931 
932         /**
933          * Set the scale.
934          * @param newScale Scale; set the scale
935          * @return Builder; this builder instance that is being constructed (for method call chaining)
936          */
937         public Builder<U> setScale(final Scale newScale)
938         {
939             this.scale = newScale;
940             return this;
941         }
942 
943         /**
944          * Retrieve the unit system.
945          * @return unitSystem UnitSystem; the unit system
946          */
947         public UnitSystem getUnitSystem()
948         {
949             return this.unitSystem;
950         }
951 
952         /**
953          * Set the unit system.
954          * @param newUnitSystem UnitSystem; the unit system
955          * @return Builder; this builder instance that is being constructed (for method call chaining)
956          */
957         public Builder<U> setUnitSystem(final UnitSystem newUnitSystem)
958         {
959             this.unitSystem = newUnitSystem;
960             return this;
961         }
962 
963         /**
964          * Retrieve the generated flag.
965          * @return generated Boolean; the generated flag
966          */
967         public boolean isGenerated()
968         {
969             return this.generated;
970         }
971 
972         /**
973          * Set the generated flag. Defaults to false. Should be set for units that are automatically generated.
974          * @param newGenerated boolean; the value for the generated flag
975          * @return Builder; this builder instance that is being constructed (for method call chaining)
976          */
977         public Builder<U> setGenerated(final boolean newGenerated)
978         {
979             this.generated = newGenerated;
980             return this;
981         }
982 
983         /**
984          * Retrieve the unit base.
985          * @return baseUnit BaseUnit&lt;U&gt;; the unit base
986          */
987         public Quantity<U> getQuantity()
988         {
989             return this.quantity;
990         }
991 
992         /**
993          * Set the unit base. Can never be null and has to be filled.
994          * @param newQuantity Quantity&lt;U&gt;; the unit base
995          * @return Builder; this builder instance that is being constructed (for method call chaining)
996          */
997         public Builder<U> setQuantity(final Quantity<U> newQuantity)
998         {
999             this.quantity = newQuantity;
1000             return this;
1001         }
1002 
1003         /** {@inheritDoc} */
1004         @Override
1005         public String toString()
1006         {
1007             return "Builder [id=" + this.id + ", name=" + this.name + ", scale=" + this.scale + "]";
1008         }
1009 
1010     }
1011 
1012     /**
1013      * Find or create a unit for the given SI dimensions.
1014      * @param unitString String; the textual representation of the unit
1015      * @return SIUnit; the unit
1016      * @throws IllegalArgumentException when the unit cannot be parsed or is incorrect
1017      * @throws NullPointerException when the unitString argument is null
1018      */
1019     public static SIUnit getUnit(final String unitString)
1020     {
1021         Throw.whenNull(unitString, "Error parsing SIVector: unitString is null");
1022         Throw.when(unitString.length() == 0, IllegalArgumentException.class, "Error parsing SIVector: empty unitString");
1023         try
1024         {
1025             SIUnit unit = Unit.lookupOrCreateUnitWithSIDimensions(SIDimensions.of(unitString));
1026             if (unit != null)
1027             {
1028                 return unit;
1029             }
1030         }
1031         catch (Exception exception)
1032         {
1033             throw new IllegalArgumentException("Error parsing SIUnit from " + unitString, exception);
1034         }
1035         throw new IllegalArgumentException("Error parsing SIVector with unit " + unitString);
1036     }
1037 
1038 }