View Javadoc
1   package org.djunits.unit;
2   
3   import java.io.Serializable;
4   
5   import org.djunits.unit.quantity.Quantity;
6   import org.djunits.unit.scale.OffsetLinearScale;
7   import org.djunits.unit.scale.Scale;
8   import org.djunits.unit.si.SIPrefixes;
9   import org.djunits.unit.unitsystem.UnitSystem;
10  import org.djunits.unit.util.UnitRuntimeException;
11  import org.djutils.exceptions.Throw;
12  
13  /**
14   * The AbsoluteUnit class indicates that a unit is absolute and has a "zero" point. The relative unit base will be set correctly
15   * for each derived unit, as this is one of the fields that will be cloned to create a derived unit.
16   * <p>
17   * Copyright (c) 2019-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
18   * BSD-style license. See <a href="https://djunits.org/docs/license.html">DJUNITS License</a>
19   * </p>
20   * @author <a href="https://www.tudelft.nl/averbraeck" target="_blank">Alexander Verbraeck</a>
21   * @param <AU> the specific unit
22   * @param <RU> the corresponding relative unit
23   */
24  public abstract class AbsoluteLinearUnit<AU extends AbsoluteLinearUnit<AU, RU>, RU extends Unit<RU>> extends Unit<AU>
25          implements Serializable
26  {
27      /** */
28      private static final long serialVersionUID = 20190826L;
29  
30      /** The relative unit belonging to this unit. */
31      private Quantity<RU> relativeQuantity;
32  
33      /** The corresponding relative unit with the same unit scale factor. */
34      private RU relativeUnit;
35  
36      /**
37       * Return the corresponding relative unit base.
38       * @return Unit&lt;RU&gt;; the the corresponding relative unit base
39       */
40      public Quantity<RU> getRelativeQuantity()
41      {
42          return this.relativeQuantity;
43      }
44  
45      /**
46       * Return the corresponding relative unit.
47       * @return Unit&lt;RU&gt;; the the corresponding relative unit
48       */
49      public RU getRelativeUnit()
50      {
51          return this.relativeUnit;
52      }
53  
54      /** {@inheritDoc} */
55      @SuppressWarnings("unchecked")
56      @Override
57      public AU build(final Unit.Builder<AU> builder) throws UnitRuntimeException
58      {
59          if (builder instanceof Builder)
60          {
61              this.relativeUnit = ((Builder<AU, RU>) builder).getRelativeUnit();
62          }
63          else
64          {
65              throw new UnitRuntimeException("Trying to build an absolute unit with a non-absolute builder");
66          }
67          Throw.whenNull(this.relativeUnit, "Relative unit for unit " + builder.getId() + " cannot be null");
68          this.relativeQuantity = this.relativeUnit.getQuantity();
69          AU unit = super.build(builder);
70          return unit;
71      }
72  
73      /**
74       * Create a linearly scaled version of this unit with an offset. The scale field will be filled with the correct
75       * scaleFactor. Note that the unit that is used for derivation is currently not allowed to already have a scaleFactor and an
76       * offset. <br>
77       * When this has to be implemented later, it might work as follows: when we have an original scale with offset o1 and
78       * scalefactor f1, the calculation to the unit base is valueSI = (value1 + o1) * f1. So the offset is expressed in the
79       * "unit" of the value. If we combine a second scale factor for a derived unit with offset o2 and scalefactor f2, we need to
80       * calculate the ultimate scale to the base (si) unit. The factor then becomes: <code>value1 = (value2 + o2) * f2</code>,
81       * and therefore <code>valueSI =
82       * ((value2 + o2) * f2 + o1) * f1</code>, which can also be written as
83       * <code>value2 * f1 * f2 + f1 * f2 * o2 + f1 * o1</code>.
84       * @param scaleFactor double; the linear scale factor of the unit
85       * @param offset double; the offset to use for the scale
86       * @param derivedRelativeUnit RU; the corresponding relative unit with the same scale factor
87       * @param derivedId String; the new id of the derived unit
88       * @param derivedName String; the new name of the derived unit
89       * @param derivedUnitSystem UnitSystem; the unit system of the derived unit
90       * @param derivedDefaultDisplayAbbreviation String; the default abbreviation to use in e.g, the toString() method. Can be
91       *            null.
92       * @param derivedDefaultTextualAbbreviation String; the default textual abbreviation to use in, e.g, typing. Can be null.
93       * @param derivedAbbreviations String...; String... any other valid abbreviations for the unit, e.g. {"h", "hr", "hour"}.
94       *            Can be empty.
95       * @return AU; a linearly scaled instance of this unit with new id, abbreviation, name, and unit system
96       * @throws UnitRuntimeException when cloning fails
97       */
98      @SuppressWarnings("checkstyle:parameternumber")
99      public AU deriveLinearOffset(final double scaleFactor, final double offset, final RU derivedRelativeUnit,
100             final String derivedId, final String derivedName, final UnitSystem derivedUnitSystem,
101             final String derivedDefaultDisplayAbbreviation, final String derivedDefaultTextualAbbreviation,
102             final String... derivedAbbreviations)
103     {
104         String cName = getClass().getSimpleName();
105         Throw.whenNull(derivedId, "deriving unit from %s.%s; derivedId cannot be null", cName, getId());
106         Throw.whenNull(derivedName, "deriving unit from %s.%s; derivedName cannot be null", cName, getId());
107         Throw.whenNull(derivedUnitSystem, "deriving unit from %s.%s; derivedUnitSystem cannot be null", cName, getId());
108         if (getScale().getOffsetToStandardUnit() != 0.0 || getScale().getConversionFactorToStandardUnit() != 1.0)
109         {
110             throw new UnitRuntimeException("Cannot derive from unit " + cName + "." + getId() + " with scale " + getScale()
111                     + ". Scale should have no offset, and scale factor 1.0");
112         }
113 
114         try
115         {
116             AU clone = clone();
117 
118             // make a builder and set values
119             Builder<AU, RU> builder = makeBuilder();
120             builder.setId(derivedId);
121             builder.setName(derivedName);
122             builder.setRelativeUnit(derivedRelativeUnit);
123             builder.setQuantity(getQuantity());
124             builder.setSiPrefixes(SIPrefixes.NONE, 1.0);
125             builder.setScale(new OffsetLinearScale(scaleFactor, offset));
126             builder.setUnitSystem(derivedUnitSystem);
127             builder.setDefaultDisplayAbbreviation(derivedDefaultDisplayAbbreviation);
128             builder.setDefaultTextualAbbreviation(derivedDefaultTextualAbbreviation);
129             if (derivedAbbreviations != null)
130             {
131                 builder.setAdditionalAbbreviations(derivedAbbreviations);
132             }
133 
134             return clone.build(builder);
135         }
136         catch (CloneNotSupportedException exception)
137         {
138             throw new UnitRuntimeException(exception);
139         }
140     }
141 
142     /**
143      * Create a linearly scaled version of this unit with an offset. The scale field will be filled with the correct
144      * scaleFactor. Note that the unit that is used for derivation can already have a scaleFactor.
145      * @param scaleFactor double; the linear scale factor of the unit
146      * @param offset double; the offset to use for the scale
147      * @param derivedRelativeUnit RU; the corresponding relative unit with the same scale factor
148      * @param derivedId String; the new id of the derived unit
149      * @param derivedName String; the new name of the derived unit
150      * @param derivedUnitSystem UnitSystem; the unit system of the derived unit
151      * @return AU; a linearly scaled instance of this unit with new id, abbreviation, name, and unit system
152      * @throws UnitRuntimeException when cloning fails
153      */
154     public AU deriveLinearOffset(final double scaleFactor, final double offset, final RU derivedRelativeUnit,
155             final String derivedId, final String derivedName, final UnitSystem derivedUnitSystem)
156     {
157         return deriveLinearOffset(scaleFactor, offset, derivedRelativeUnit, derivedId, derivedName, derivedUnitSystem, null,
158                 null);
159     }
160 
161     /**
162      * Create a linearly scaled version of this unit with an offset. The unitSystem will be copied. The scale field will be
163      * filled with the correct scaleFactor. Note that the unit that is used for derivation can already have a scaleFactor.
164      * @param scaleFactor double; the linear scale factor of the unit
165      * @param offset double; the offset to use for the scale
166      * @param derivedRelativeUnit RU; the corresponding relative unit with the same scale factor
167      * @param derivedId String; the new id of the derived unit
168      * @param derivedName String; the new name of the derived unit
169      * @return AU; a linearly scaled instance of this unit with new id, abbreviation, name, and unit system
170      * @throws UnitRuntimeException when cloning fails
171      */
172     public AU deriveLinearOffset(final double scaleFactor, final double offset, final RU derivedRelativeUnit,
173             final String derivedId, final String derivedName)
174     {
175         return deriveLinearOffset(scaleFactor, offset, derivedRelativeUnit, derivedId, derivedName, getUnitSystem());
176     }
177 
178     /** {@inheritDoc} */
179     @Override
180     public Builder<AU, RU> makeBuilder()
181     {
182         Builder<AU, RU> builder = new Builder<AU, RU>();
183         builder.setRelativeUnit(getRelativeUnit());
184         return builder;
185     }
186 
187     /** {@inheritDoc} */
188     @Override
189     public OffsetLinearScale getScale()
190     {
191         return (OffsetLinearScale) super.getScale();
192     }
193 
194     /**
195      * Builder for the AbsoluteUnit. This builder forces a relative unit base to be set for the absolute unit as well.
196      * Furthermore, it forces an OffsetLinearScale rather than a linear scale.
197      * <p>
198      * Copyright (c) 2019-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
199      * <br>
200      * BSD-style license. See <a href="https://djunits.org/docs/license.html">DJUNITS License</a>.
201      * <p>
202      * @author <a href="https://www.tudelft.nl/averbraeck" target="_blank">Alexander Verbraeck</a>
203      * @param <AU> the specific unit for which this is the builder
204      * @param <RU> the corresponding relative unit
205      */
206     public static class Builder<AU extends AbsoluteLinearUnit<AU, RU>, RU extends Unit<RU>> extends Unit.Builder<AU>
207     {
208         /** ... */
209         private static final long serialVersionUID = 20191018L;
210 
211         /** The relative unit belonging to this unit. */
212         private RU relativeUnit;
213 
214         /** {@inheritDoc} */
215         @Override
216         public OffsetLinearScale getScale()
217         {
218             return (OffsetLinearScale) super.getScale();
219         }
220 
221         /** {@inheritDoc} */
222         @Override
223         public Builder<AU, RU> setScale(final Scale scale)
224         {
225             Throw.when(!(scale instanceof OffsetLinearScale), UnitRuntimeException.class,
226                     "scale for an absolute unit (id=" + getId() + ") should be an offset scale");
227             super.setScale(scale);
228             return this;
229         }
230 
231         /**
232          * Retrieve the corresponding relative unit that has the same conversion factor.
233          * @return Unit&lt;?&gt;; the the corresponding relative unit with the same conversion factor
234          */
235         public RU getRelativeUnit()
236         {
237             return this.relativeUnit;
238         }
239 
240         /**
241          * Sets the corresponding relative unit that has the same conversion factor.
242          * @param newRelativeUnit RU; the the corresponding relative unit with the same conversion factor
243          * @return Builder; this builder instance that is being constructed (for method call chaining)
244          */
245         public Builder<AU, RU> setRelativeUnit(final RU newRelativeUnit)
246         {
247             Throw.whenNull(newRelativeUnit, "Relative unit for unit id " + getId() + " cannot be null");
248             this.relativeUnit = newRelativeUnit;
249             return this;
250         }
251 
252         // Other setters will have to be overridden to make them return this builder rather than the Unit.Builder.
253 
254         /** {@inheritDoc} */
255         @Override
256         public Builder<AU, RU> setSiPrefixes(final SIPrefixes siPrefixes, final double power)
257         {
258             super.setSiPrefixes(siPrefixes, power);
259             return this;
260         }
261 
262         /** {@inheritDoc} */
263         @Override
264         public Builder<AU, RU> setId(final String id)
265         {
266             super.setId(id);
267             return this;
268         }
269 
270         /** {@inheritDoc} */
271         @Override
272         public Builder<AU, RU> setAdditionalAbbreviations(final String... abbreviations)
273         {
274             super.setAdditionalAbbreviations(abbreviations);
275             return this;
276         }
277 
278         /** {@inheritDoc} */
279         @Override
280         public Builder<AU, RU> setDefaultDisplayAbbreviation(final String defaultDisplayAbbreviation)
281         {
282             super.setDefaultDisplayAbbreviation(defaultDisplayAbbreviation);
283             return this;
284         }
285 
286         /** {@inheritDoc} */
287         @Override
288         public Builder<AU, RU> setDefaultTextualAbbreviation(final String defaultTextualAbbreviation)
289         {
290             super.setDefaultTextualAbbreviation(defaultTextualAbbreviation);
291             return this;
292         }
293 
294         /** {@inheritDoc} */
295         @Override
296         public Builder<AU, RU> setName(final String name)
297         {
298             super.setName(name);
299             return this;
300         }
301 
302         /** {@inheritDoc} */
303         @Override
304         public Builder<AU, RU> setUnitSystem(final UnitSystem unitSystem)
305         {
306             super.setUnitSystem(unitSystem);
307             return this;
308         }
309 
310         /** {@inheritDoc} */
311         @Override
312         public Builder<AU, RU> setGenerated(final boolean generated)
313         {
314             super.setGenerated(generated);
315             return this;
316         }
317 
318         /** {@inheritDoc} */
319         @Override
320         public Builder<AU, RU> setQuantity(final Quantity<AU> baseUnit)
321         {
322             super.setQuantity(baseUnit);
323             return this;
324         }
325 
326         /** {@inheritDoc} */
327         @Override
328         public String toString()
329         {
330             return "Builder [relativeUnit=" + this.relativeUnit + ", getId()=" + this.getId() + ", getName()=" + this.getName()
331                     + ", getQuantity()=" + this.getQuantity() + "]";
332         }
333 
334     }
335 
336 }