This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new eef29ecb6c Make `AbstractConverter` public and add an 
`Units.logarithm(Unit)` method. The intend is to make easier for users to 
define their own "decibel watt" unit.
eef29ecb6c is described below

commit eef29ecb6c2f36f63267d117d33bcb4740e19203
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Fri May 10 17:58:11 2024 +0200

    Make `AbstractConverter` public and add an `Units.logarithm(Unit)` method.
    The intend is to make easier for users to define their own "decibel watt" 
unit.
---
 .../org/apache/sis/measure/AbstractConverter.java  | 31 ++++++++++++++----
 .../main/org/apache/sis/measure/AbstractUnit.java  |  5 +--
 .../org/apache/sis/measure/ConventionalUnit.java   | 38 +++++++++++++++-------
 .../main/org/apache/sis/measure/PowerOf10.java     |  2 +-
 .../main/org/apache/sis/measure/SystemUnit.java    |  7 +++-
 .../main/org/apache/sis/measure/UnitFormat.java    |  6 ++--
 .../main/org/apache/sis/measure/Units.java         | 22 +++++++++++++
 .../main/org/apache/sis/util/resources/Errors.java |  7 +---
 .../apache/sis/util/resources/Errors.properties    |  1 -
 .../apache/sis/util/resources/Errors_fr.properties |  1 -
 .../apache/sis/measure/ConventionalUnitTest.java   | 17 ++++++++++
 11 files changed, 104 insertions(+), 33 deletions(-)

diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/measure/AbstractConverter.java
 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/measure/AbstractConverter.java
index 83d648276e..ec0761e16d 100644
--- 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/measure/AbstractConverter.java
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/measure/AbstractConverter.java
@@ -18,16 +18,30 @@ package org.apache.sis.measure;
 
 import java.util.List;
 import java.io.Serializable;
+import javax.measure.Unit;
 import javax.measure.UnitConverter;
 import org.apache.sis.math.DecimalFunctions;
 
 
 /**
- * Base class of unit converters.
+ * Skeletal implementation of the {@code UnitConverter} interface for reducing 
implementation effort.
+ * This class makes easier to define a non-linear conversion between two units 
of measurement.
+ * Note that for linear conversions, the standard {@link Unit#shift(Number)}, 
{@link Unit#multiply(Number)}
+ * and {@link Unit#divide(Number)} methods should be used instead.
+ *
+ * <p>After a non-linear conversion has been created, a new unit of 
measurement using that conversion
+ * can be defined by a call to {@link Unit#transform(UnitConverter)}.
+ * See {@link Units#logarithm(Unit)} for an example.</p>
  *
  * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.5
+ *
+ * @see Units#converter(Number, Number)
+ * @see Units#logarithm(Unit)
+ *
+ * @since 1.5
  */
-abstract class AbstractConverter implements UnitConverter, Serializable {
+public abstract class AbstractConverter implements UnitConverter, Serializable 
{
     /**
      * For cross-version compatibility.
      */
@@ -36,7 +50,7 @@ abstract class AbstractConverter implements UnitConverter, 
Serializable {
     /**
      * Creates a new converter.
      */
-    AbstractConverter() {
+    protected AbstractConverter() {
     }
 
     /**
@@ -135,19 +149,24 @@ abstract class AbstractConverter implements 
UnitConverter, Serializable {
      * by the specified converter (right converter), and then converting by 
this converter (left converter).
      *
      * <p>The default implementation is okay, but subclasses should override 
if they can detect optimizations.</p>
+     *
+     * @param  before  the converter to concatenate before this converter.
+     * @return a conversion which applies {@code before} first, then {@code 
this}.
      */
     @Override
-    public UnitConverter concatenate(final UnitConverter converter) {
-        if (equals(converter.inverse())) {
+    public UnitConverter concatenate(final UnitConverter before) {
+        if (equals(before.inverse())) {
             return IdentityConverter.INSTANCE;
         }
-        return new ConcatenatedConverter(converter, this);
+        return new ConcatenatedConverter(before, this);
     }
 
     /**
      * Returns the steps of fundamental converters making up this converter. 
The default implementation returns
      * only {@code this} on the assumption that this conversion is not a 
concatenation of other converters.
      * Subclasses should override if this assumption does not hold.
+     *
+     * @return list of steps in the unit conversion represented by this 
instance.
      */
     @Override
     public List<UnitConverter> getConversionSteps() {
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/measure/AbstractUnit.java
 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/measure/AbstractUnit.java
index ac3cff03f7..c6a44cd1f6 100644
--- 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/measure/AbstractUnit.java
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/measure/AbstractUnit.java
@@ -266,9 +266,10 @@ abstract class AbstractUnit<Q extends Quantity<Q>> 
implements Unit<Q>, LenientCo
      */
     final <R extends Quantity<R>> Unit<R> inferSymbol(Unit<R> result, final 
char operation, final Unit<?> other) {
         if (result instanceof ConventionalUnit<?> && result.getSymbol() == 
null) {
+            @SuppressWarnings("LocalVariableHidesMemberVariable")
             final String symbol = inferSymbol(operation, other);
             if (symbol != null) {
-                result = ((ConventionalUnit<R>) result).forSymbol(symbol);
+                result = ((ConventionalUnit<R>) result).alternate(symbol);
             }
         }
         return result;
@@ -597,7 +598,7 @@ abstract class AbstractUnit<Q extends Quantity<Q>> 
implements Unit<Q>, LenientCo
      * @see #getSymbol()
      */
     @Override
-    public final String toString() {
+    public String toString() {
         if (symbol != null) {
             return symbol;
         } else {
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/measure/ConventionalUnit.java
 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/measure/ConventionalUnit.java
index 8645d641c0..060331c964 100644
--- 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/measure/ConventionalUnit.java
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/measure/ConventionalUnit.java
@@ -358,26 +358,22 @@ final class ConventionalUnit<Q extends Quantity<Q>> 
extends AbstractUnit<Q> {
      * Returns a new unit identical to this unit except for the symbol, which 
is set to the given value.
      * This is used by {@link UnitFormat} mostly; we do not provide public API 
for setting a unit symbol
      * on a conventional unit.
-     */
-    final ConventionalUnit<Q> forSymbol(final String symbol) {
-        if (symbol.equals(getSymbol())) {
-            return this;
-        }
-        return new ConventionalUnit<>(target, toTarget, symbol, scope, epsg);
-    }
-
-    /**
-     * Unsupported operation for conventional units, as required by JSR-385 
specification.
+     *
+     * <h4>Departure from JSR-385 specification</h4>
+     * The JSR-385 specification requires that we throw a {@link 
MeasurementException} because this unit
+     * is not an unscaled standard unit. This implementation relaxes that 
restriction.
      *
      * @param  symbol  the new symbol for the alternate unit.
      * @return the alternate unit.
-     * @throws MeasurementException always thrown because this unit is not an 
unscaled standard unit.
      *
      * @see SystemUnit#alternate(String)
      */
     @Override
     public Unit<Q> alternate(final String symbol) {
-        throw new 
MeasurementException(Errors.format(Errors.Keys.NonSystemUnit_1, this));
+        if (symbol.equals(getSymbol())) {
+            return this;
+        }
+        return new ConventionalUnit<>(target, toTarget, symbol, scope, epsg);
     }
 
     /**
@@ -506,4 +502,22 @@ final class ConventionalUnit<Q extends Quantity<Q>> 
extends AbstractUnit<Q> {
     public int hashCode() {
         return super.hashCode() + 37 * (target.hashCode() + 31 * 
toTarget.hashCode());
     }
+
+    /**
+     * Returns a string representation of this unit of measurement.
+     *
+     * @return a string representation.
+     */
+    @Override
+    public String toString() {
+        if (toTarget instanceof LinearConverter || toTarget.isLinear()) {
+            return super.toString();
+        }
+        final String symbol = getSymbol();
+        if (symbol != null) {
+            return symbol;
+        } else {
+            return "f(" + target + ')';
+        }
+    }
 }
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/measure/PowerOf10.java 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/measure/PowerOf10.java
index 9fbab44a76..7fc64313fa 100644
--- 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/measure/PowerOf10.java
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/measure/PowerOf10.java
@@ -40,7 +40,7 @@ final class PowerOf10 extends AbstractConverter {
     /**
      * The singleton instance. Can be used for conversion from neper units to 
{@link Units#UNITY}.
      */
-    private static final UnitConverter INSTANCE = new PowerOf10();
+    static final UnitConverter INSTANCE = new PowerOf10();
 
     /**
      * Returns the converter from bel unit (B) to dimensionless unit. ISO 
80000-3:2006 defines
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/measure/SystemUnit.java 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/measure/SystemUnit.java
index bb7abf1421..062c74061e 100644
--- 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/measure/SystemUnit.java
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/measure/SystemUnit.java
@@ -57,6 +57,11 @@ final class SystemUnit<Q extends Quantity<Q>> extends 
AbstractUnit<Q> implements
      */
     static final String ONE = "1";
 
+    /**
+     * The symbol for unformattable units.
+     */
+    static final String UNFORMATTABLE = "?";
+
     /**
      * The type of quantity that uses this unit, or {@code null} if unknown.
      * This field should be null only when this unit is the result of an 
arithmetic
@@ -308,7 +313,7 @@ final class SystemUnit<Q extends Quantity<Q>> extends 
AbstractUnit<Q> implements
         }
         if (!dimension.equals(unit.dimension)) {
             throw new 
ClassCastException(Errors.format(Errors.Keys.IncompatibleUnitDimension_5, new 
Object[] {
-                    this, (quantity != null) ? quantity.getSimpleName() : "?", 
dimension,
+                    this, (quantity != null) ? quantity.getSimpleName() : 
UNFORMATTABLE, dimension,
                     type.getSimpleName(), unit.dimension}));
         }
         return unit;
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/measure/UnitFormat.java 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/measure/UnitFormat.java
index f24097ec71..08c892c1de 100644
--- 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/measure/UnitFormat.java
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/measure/UnitFormat.java
@@ -427,7 +427,7 @@ public class UnitFormat extends Format implements 
javax.measure.format.UnitForma
         }
         Unit<?> labeledUnit = unit;
         if (labeledUnit instanceof ConventionalUnit<?>) {
-            labeledUnit = ((ConventionalUnit<?>) labeledUnit).forSymbol(label);
+            labeledUnit = ((ConventionalUnit<?>) labeledUnit).alternate(label);
         }
         final Unit<?> unitForOldLabel = 
labelToUnit.remove(unitToLabel.put(unit, label));
         final Unit<?> oldUnitForLabel = labelToUnit.put(label, labeledUnit);
@@ -443,7 +443,7 @@ public class UnitFormat extends Format implements 
javax.measure.format.UnitForma
              * Assuming there is no bug in our algorithm, this exception 
should never happen
              * unless this UnitFormat has been modified concurrently in 
another thread.
              * We compared system units because the units may not be strictly 
equal
-             * as a result of the call to ConventionalUnit.forSymbol(label).
+             * as a result of the call to ConventionalUnit.alternate(label).
              */
             throw new CorruptedObjectException("labelToUnit");
         }
@@ -889,7 +889,7 @@ appPow: if (unit == null) {
                 return;
             }
         }
-        toAppendTo.append('?');
+        toAppendTo.append(SystemUnit.UNFORMATTABLE);
     }
 
     /**
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/measure/Units.java 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/measure/Units.java
index d965d057d9..68954b7b64 100644
--- a/endorsed/src/org.apache.sis.util/main/org/apache/sis/measure/Units.java
+++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/measure/Units.java
@@ -1621,6 +1621,28 @@ public final class Units extends Static {
         return unit.transform(LinearConverter.scale(numerator, denominator));
     }
 
+    /**
+     * Creates an unit for values computed by the logarithm in base 10 of 
values in the given unit.
+     * Conversions from the given unit to the returned unit is done by {@link 
Math#log10(double)}.
+     *
+     * <p><strong>The given unit should be dimensionless.</strong>
+     * However, this method does not enforce this constraint in order to allow 
the creation of
+     * units such as <a 
href="https://en.wikipedia.org/wiki/Decibel_watt";>decibel watt</a>.
+     * For example:</p>
+     *
+     * {@snippet lang="java" :
+     * Unit<Power> dBW = Units.logarithm(Units.WATT).divide(10);
+     * }
+     *
+     * @param  unit  the unit from which to convert.
+     * @return an unit which is the logarithm in base 10 of the given unit.
+     *
+     * @since 1.5
+     */
+    public static <Q extends Quantity<Q>> Unit<Q> logarithm(final Unit<Q> 
unit) {
+        return unit.transform(PowerOf10.INSTANCE);
+    }
+
     /**
      * Returns the factor by which to multiply the standard unit in order to 
get the given unit.
      * The "standard" unit is usually the SI unit on which the given unit is 
based, as given by
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors.java
 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors.java
index 257a3fdb13..74998ec8a3 100644
--- 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors.java
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors.java
@@ -166,7 +166,7 @@ public class Errors extends IndexedResourceBundle {
         /**
          * Cannot resolve “{0}” as an absolute path.
          */
-        public static final short CanNotResolveAsAbsolutePath_1 = 205;
+        public static final short CanNotResolveAsAbsolutePath_1 = 106;
 
         /**
          * Cannot set a value for parameter “{0}”.
@@ -724,11 +724,6 @@ public class Errors extends IndexedResourceBundle {
          */
         public static final short NonScaleUnit_1 = 105;
 
-        /**
-         * “{0}” is not a fundamental or derived unit.
-         */
-        public static final short NonSystemUnit_1 = 106;
-
         /**
          * “{0}” is not a time unit.
          */
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors.properties
 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors.properties
index a8290bea8d..cf1ed4b216 100644
--- 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors.properties
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors.properties
@@ -155,7 +155,6 @@ NonAngularUnit_1                  = \u201c{0}\u201d is not 
an angular unit.
 NonLinearUnit_1                   = \u201c{0}\u201d is not a linear unit.
 NonScaleUnit_1                    = \u201c{0}\u201d is not a scale unit.
 NonTemporalUnit_1                 = \u201c{0}\u201d is not a time unit.
-NonSystemUnit_1                   = \u201c{0}\u201d is not a fundamental or 
derived unit.
 NonRatioUnit_1                    = The scale of measurement for 
\u201c{0}\u201d unit is not a ratio scale.
 NotABackwardReference_1           = No element for the \u201c{0}\u201d 
identifier, or the identifier is a forward reference.
 NotADivisorOrMultiple_4           = Value of \u2018{0}\u2019 shall be a 
{1,choice,0#divisor|1#multiple} of {2} but the given value is {3}.
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors_fr.properties
 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors_fr.properties
index 59f896eec0..46b4e9cdfb 100644
--- 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors_fr.properties
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors_fr.properties
@@ -152,7 +152,6 @@ NonAngularUnit_1                  = 
\u00ab\u202f{0}\u202f\u00bb n\u2019est pas u
 NonLinearUnit_1                   = \u00ab\u202f{0}\u202f\u00bb n\u2019est pas 
une unit\u00e9 de longueurs.
 NonScaleUnit_1                    = \u00ab\u202f{0}\u202f\u00bb n\u2019est pas 
une unit\u00e9 d\u2019\u00e9chelles.
 NonTemporalUnit_1                 = \u00ab\u202f{0}\u202f\u00bb n\u2019est pas 
une unit\u00e9 de temps.
-NonSystemUnit_1                   = \u00ab\u202f{0}\u202f\u00bb n\u2019est pas 
une unit\u00e9 fondamentale ou d\u00e9riv\u00e9e.
 NonRatioUnit_1                    = L\u2019\u00e9chelle de mesure de 
l\u2019unit\u00e9 \u00ab\u202f{0}\u202f\u00bb n\u2019est pas une \u00e9chelle 
de rapports.
 NotABackwardReference_1           = Il n\u2019y a pas d\u2019\u00e9l\u00e9ment 
pour l\u2019identifiant \u201c{0}\u201d, ou l\u2019identifiant est une 
r\u00e9f\u00e9rence vers l\u2019avant.
 NotADivisorOrMultiple_4           = La valeur de \u2018{0}\u2019 doit 
\u00eatre un {1,choice,0#diviseur|1#multiple} de {2}, mais la valeur 
donn\u00e9e est {3}.
diff --git 
a/endorsed/src/org.apache.sis.util/test/org/apache/sis/measure/ConventionalUnitTest.java
 
b/endorsed/src/org.apache.sis.util/test/org/apache/sis/measure/ConventionalUnitTest.java
index f5abf7c68d..8283be0b94 100644
--- 
a/endorsed/src/org.apache.sis.util/test/org/apache/sis/measure/ConventionalUnitTest.java
+++ 
b/endorsed/src/org.apache.sis.util/test/org/apache/sis/measure/ConventionalUnitTest.java
@@ -19,6 +19,7 @@ package org.apache.sis.measure;
 import javax.measure.IncommensurableException;
 import javax.measure.Unit;
 import javax.measure.UnitConverter;
+import javax.measure.quantity.Power;
 import javax.measure.quantity.Volume;
 
 // Test dependencies
@@ -277,6 +278,22 @@ public final class ConventionalUnitTest extends TestCase {
         assertEquals(40, cl.getConverterTo(cm3).convert(4), "4 cL to cm³");
     }
 
+    /**
+     * Tests the creation of a unit of measurement defined by a logarithm.
+     *
+     * @see <a href="https://en.wikipedia.org/wiki/DBm";>Decibel-milliwatts on 
Wikipedia</a>
+     */
+    @Test
+    public void testDecibelWatt() {
+        final Unit<Power> dBm = 
Units.logarithm(Units.WATT.divide(1000)).divide(10);
+        final UnitConverter c = dBm.getConverterTo(Units.WATT);
+        assertEquals(100000, c.convert(80));
+        assertEquals(  1000, c.convert(60));
+        assertEquals(    10, c.convert(40));
+        assertEquals(     1, c.convert(30));
+        assertEquals(0.3162, c.convert(25), 0.0001);
+    }
+
     /**
      * Serializes some units, deserializes them and verifies that we get the 
same instance.
      */

Reply via email to