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