sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] branch geoapi-4.0 updated: Preserve better the unit symbol after some parsings.
Date Sun, 05 Aug 2018 21:40:56 GMT
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 cb95be3  Preserve better the unit symbol after some parsings.
cb95be3 is described below

commit cb95be32da75f9cd5e152b8edf667da71c6ef4de
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Sun Aug 5 23:40:28 2018 +0200

    Preserve better the unit symbol after some parsings.
---
 .../java/org/apache/sis/measure/SystemUnit.java    | 92 +++++++++++++++++-----
 .../java/org/apache/sis/measure/UnitDimension.java | 14 ++++
 .../org/apache/sis/measure/UnitFormatTest.java     | 26 +++---
 .../java/org/apache/sis/measure/UnitsTest.java     |  4 +-
 4 files changed, 101 insertions(+), 35 deletions(-)

diff --git a/core/sis-utility/src/main/java/org/apache/sis/measure/SystemUnit.java b/core/sis-utility/src/main/java/org/apache/sis/measure/SystemUnit.java
index 1b92853..24d7827 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/measure/SystemUnit.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/measure/SystemUnit.java
@@ -114,41 +114,84 @@ final class SystemUnit<Q extends Quantity<Q>> extends AbstractUnit<Q>
implements
      *                    Ignored if the condition documented in javadoc does not hold.
      */
     private SystemUnit<?> create(final UnitDimension newDimension, final char operation,
final Unit<?> other) {
-        if (newDimension == dimension) {
-            return this;
+        /*
+         * Check if the SystemUnit to create is known to Units, provided that no dimensionless
units
+         * is involved. If a dimensionless unit is involved, we will try to build a symbol
before to
+         * check if the unit is known to Units. The reason is that there is many dimensionless
units
+         * with different symbols (rad, sr, psu, …).
+         */
+        final boolean deferred = newDimension.isDimensionless() || dimension.isDimensionless()
||
+                (other != null && UnitDimension.isDimensionless(other.getDimension()));
+        if (!deferred) {
+            final SystemUnit<?> result = Units.get(newDimension);
+            if (result != null) return result;
         }
-        SystemUnit<?> result = Units.get(newDimension);
-        if (result == null) {
-            String symbol = null;
-            if (operation != 0) {
-                final String ts = getSymbol();
-                if (invalidCharForSymbol(ts) == -1) {
-                    if (other == null) {
-                        symbol = (ts + operation).intern();
-                    } else {
-                        final String os = other.getSymbol();
-                        if (invalidCharForSymbol(os) == -1) {
-                            symbol = (ts + operation + os).intern();
-                        }
+        String symbol = null;
+        if (operation != 0) {
+            final String ts = getSymbol();
+            final boolean exponents = (operation == MULTIPLY || operation == DIVIDE);
+            if (invalidCharForSymbol(ts, exponents ? 1 : 0, exponents) == -1) {
+                if (other == null) {
+                    symbol = (ts + operation).intern();
+                } else {
+                    final String os = other.getSymbol();
+                    if (invalidCharForSymbol(os, 0, exponents) == -1) {
+                        symbol = (ts + operation + os).intern();
                     }
                 }
             }
-            result = new SystemUnit<>(null, newDimension, symbol, (byte) 0, (short)
0, null);
         }
-        return result;
+        /*
+         * The check that we did not performed at the beginning of this method
+         * if any component were unitless.
+         */
+        if (deferred) {
+            final SystemUnit<?> result = Units.get(newDimension);
+            if (result != null && result.sameSymbol(symbol)) {
+                return result;
+            }
+        }
+        if (newDimension == dimension && sameSymbol(symbol)) {
+            return this;
+        }
+        return new SystemUnit<>(null, newDimension, symbol, (byte) 0, (short) 0, null);
+    }
+
+    /**
+     * Returns {@code true} if the given symbol is null or equals to the symbol of this unit.
+     */
+    private boolean sameSymbol(final String symbol) {
+        return (symbol == null) || symbol.equals(getSymbol());
     }
 
     /**
      * If the given symbol contains an invalid character for a unit symbol, returns the character
code point.
      * Otherwise if the given symbol is null or empty, returns -2. Otherwise (the symbol
is valid) returns -1.
+     *
+     * <p>The check for valid symbols can be relaxed, for example when building a new
symbol from existing units.
+     * For example we may want to accept "W" and "m²" as valid symbols for deriving "W∕m²"
without being rejected
+     * because of the "²" in "m²". We do not want to relax too much however, because a
long sequence of arithmetic
+     * operations would result in a long and maybe meaningless unit symbol, while declaring
"no symbol" would allow
+     * {@link UnitFormat} to create a new one from the base units. The criterion for accepting
a symbol or not (for
+     * example how many multiplications) is arbitrary.</p>
+     *
+     * @param  symbol     the symbol to verify for invalid characters.
+     * @param  multiply   maximal number of multiplication symbol to accept.
+     * @param  exponents  whether to accept also exponent characters.
      */
-    private static int invalidCharForSymbol(final String symbol) {
+    private static int invalidCharForSymbol(final String symbol, int multiply, final boolean
exponents) {
         if (symbol == null || symbol.isEmpty()) {
             return -2;
         }
         for (int i=0; i < symbol.length();) {
             final int c = symbol.codePointAt(i);
-            if (!isSymbolChar(c)) return c;
+            if (!isSymbolChar(c)) {
+                if (c == MULTIPLY) {
+                    if (--multiply < 0) return c;
+                } else if (!exponents || !Characters.isSuperScript(c)) {
+                    return c;
+                }
+            }
             i += Character.charCount(c);
         }
         return -1;
@@ -397,7 +440,7 @@ final class SystemUnit<Q extends Quantity<Q>> extends AbstractUnit<Q>
implements
     @SuppressWarnings("unchecked")
     public Unit<Q> alternate(final String symbol) {
         ArgumentChecks.ensureNonEmpty("symbol", symbol);
-        final int c = invalidCharForSymbol(symbol);
+        final int c = invalidCharForSymbol(symbol, 0, false);
         if (c >= 0) {
             throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalCharacter_2,
                     "symbol", String.valueOf(Character.toChars(c))));
@@ -494,7 +537,14 @@ final class SystemUnit<Q extends Quantity<Q>> extends AbstractUnit<Q>
implements
      */
     @Override
     public Unit<?> pow(final int n) {
-        return create(dimension.pow(n), (n >= 0 && n <= 9) ? Characters.toSuperScript((char)
('0' + n)) : 0, null);
+        switch (n) {
+            case 0: return Units.UNITY;
+            case 1: return this;
+            default: {
+                final char p = (n >= 0 && n <= 9) ? Characters.toSuperScript((char)
('0' + n)) : 0;
+                return create(dimension.pow(n), p, null);
+            }
+        }
     }
 
     /**
diff --git a/core/sis-utility/src/main/java/org/apache/sis/measure/UnitDimension.java b/core/sis-utility/src/main/java/org/apache/sis/measure/UnitDimension.java
index 85f0218..93fc821 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/measure/UnitDimension.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/measure/UnitDimension.java
@@ -173,6 +173,20 @@ final class UnitDimension implements Dimension, Serializable {
     }
 
     /**
+     * Returns {@code true} if the given dimension has no components.
+     */
+    static boolean isDimensionless(final Dimension dim) {
+        if (dim instanceof UnitDimension) {
+            return ((UnitDimension) dim).isDimensionless();
+        } else if (dim != null) {
+            // Fallback for foreigner implementations.
+            final Map<? extends Dimension, Integer> bases = dim.getBaseDimensions();
+            if (bases != null) return bases.isEmpty();
+        }
+        return false;       // Unit is a base unit (not a product of existing units).
+    }
+
+    /**
      * Returns {@code true} if the numerator is the dimension identified by the given symbol.
      * This method returns {@code true} only if the numerator is not be raised to any exponent
      * other than 1 and there is no other numerator. All denominator terms are ignored.
diff --git a/core/sis-utility/src/test/java/org/apache/sis/measure/UnitFormatTest.java b/core/sis-utility/src/test/java/org/apache/sis/measure/UnitFormatTest.java
index b03d581..ae55366 100644
--- a/core/sis-utility/src/test/java/org/apache/sis/measure/UnitFormatTest.java
+++ b/core/sis-utility/src/test/java/org/apache/sis/measure/UnitFormatTest.java
@@ -575,10 +575,12 @@ public final strictfp class UnitFormatTest extends TestCase {
     public void testParseAndFormat() {
         final UnitFormat f = new UnitFormat(Locale.UK);
         roundtrip(f, "K.m2.kg-1.s-1",    "K⋅m²∕(kg⋅s)");
+        roundtrip(f, "m.m6.m-3",         "m⋅m⁶∕m³");
         roundtrip(f, "Pa.s-1",           "Pa∕s");
         roundtrip(f, "S.m-1",            "S∕m");
         roundtrip(f, "m2/3.s-1",         "m^⅔∕s");
         roundtrip(f, "J.kg-1",           "J∕kg");
+        roundtrip(f, "mol.mol-1",        "mol∕mol");
         roundtrip(f, "mol.s-1",          "mol∕s");
         roundtrip(f, "K.s-1",            "K∕s");
         roundtrip(f, "m.s-1",            "m∕s");
@@ -586,7 +588,9 @@ public final strictfp class UnitFormatTest extends TestCase {
         roundtrip(f, "Pa.m",             "Pa⋅m");
         roundtrip(f, "m3.s-1",           "m³∕s");
         roundtrip(f, "kg.m-2.s-1",       "kg∕(m²⋅s)");
+        roundtrip(f, "μg.m-2",           "µg∕m²");
         roundtrip(f, "K.m-1",            "K∕m");
+        roundtrip(f, "W.m-2",            "W∕m²");
         roundtrip(f, "N.m-2",            "Pa");
         roundtrip(f, "kg.m-2",           "kg∕m²");
         roundtrip(f, "kg.m-3",           "kg∕m³");
@@ -596,19 +600,26 @@ public final strictfp class UnitFormatTest extends TestCase {
         roundtrip(f, "kg/kg*Pa/s",       "Pa∕s");
         roundtrip(f, "kg/kg*m/s",        "m∕s");
         roundtrip(f, "day",              "d");
+        roundtrip(f, "µg.m-3",           "µg∕m³");
         roundtrip(f, "Pa*Pa",            "Pa²");
         roundtrip(f, "N.m-1",            "N∕m");
         roundtrip(f, "m-2.s-1",          "1∕(m²⋅s)");
         roundtrip(f, "°",                "°");
+        roundtrip(f, "K*Pa/s",           "K⋅Pa∕s");
+        roundtrip(f, "kg.kg-1",          "kg∕kg");
+        roundtrip(f, "m3.m-3",           "m³∕m³");
         roundtrip(f, "s.m-1",            "s∕m");
         roundtrip(f, "V.m-1",            "V∕m");
         roundtrip(f, "m2.s-2",           "m²∕s²");
         roundtrip(f, "m2.s-1",           "m²∕s");
         roundtrip(f, "mol.m-3",          "mol∕m³");
+        roundtrip(f, "J.m-2",            "J∕m²");
         roundtrip(f, "psu",              "psu");
         roundtrip(f, "kg-2.s-1",         "1∕(kg²⋅s)");
         roundtrip(f, "K*K",              "K²");
         roundtrip(f, "kg.m-3.s-1",       "kg∕(m³⋅s)");
+        roundtrip(f, "m.rad-1",          "m∕rad");
+        roundtrip(f, "rad.s-1",          "rad∕s");
     }
 
     /**
@@ -623,25 +634,16 @@ public final strictfp class UnitFormatTest extends TestCase {
     @Test
     public void needForImprovements() {
         final UnitFormat f = new UnitFormat(Locale.UK);
-        roundtrip(f, "m.m6.m-3",         "m⁴");
         roundtrip(f, "kg.kg-1.m.s-1",    "m∕s");
-        roundtrip(f, "mol.mol-1",        "");
-        roundtrip(f, "μg.m-3",           "10⁻⁹⋅kg∕m³");
-        roundtrip(f, "W.m-2",            "kg∕s³");
-        roundtrip(f, "(m2.s.sr)-1",      "-1⋅m²⋅s");
+        roundtrip(f, "(m2.s.sr)-1",      "-1⋅m²⋅s");                    // TODO: this
one is a bug.
         roundtrip(f, "cm/day",           "1.1574074074074074E-7⋅m∕s");
         roundtrip(f, "m-2.s.rad-1",      "s∕m²");
         roundtrip(f, "kg.kg-1.s-1",      "Hz");
-        roundtrip(f, "kg/kg*kg/kg",      "");
-        roundtrip(f, "µg.m-3",           "10⁻⁹⋅kg∕m³");
+        roundtrip(f, "kg/kg*kg/kg",      "kg∕kg");
         roundtrip(f, "W.m-2.Hz-1",       "kg∕s²");
         roundtrip(f, "W.sr-1.m-2",       "kg∕s³");
         roundtrip(f, "W.m-1.sr-1",       "W∕m");
-        roundtrip(f, "kg.kg-1",          "");
-        roundtrip(f, "W.m-3.sr-1",       "kg∕(m⋅s³)");
-        roundtrip(f, "K*Pa/s",           "K⋅kg∕(m⋅s³)");
-        roundtrip(f, "m3.m-3",           "");
-        roundtrip(f, "J.m-2",            "kg∕s²");
+        roundtrip(f, "W.m-3.sr-1",       "W∕m³");
         roundtrip(f, "m3.s-1.m-1",       "m²∕s");
         roundtrip(f, "(kg.m-3)*(m.s-1)", "kg∕(m²⋅s)");
         roundtrip(f, "W.m-2.nm-1",       "10⁹⋅kg∕(m⋅s³)");
diff --git a/core/sis-utility/src/test/java/org/apache/sis/measure/UnitsTest.java b/core/sis-utility/src/test/java/org/apache/sis/measure/UnitsTest.java
index fe396d1..c774c18 100644
--- a/core/sis-utility/src/test/java/org/apache/sis/measure/UnitsTest.java
+++ b/core/sis-utility/src/test/java/org/apache/sis/measure/UnitsTest.java
@@ -304,8 +304,8 @@ public final strictfp class UnitsTest extends TestCase {
         assertSame  (HERTZ,                         valueOf("1/s"));
         assertSame  (HERTZ,                         valueOf("s-1"));
         assertSame  (PERCENT,                       valueOf("%"));
-        assertSame  (UNITY,                         valueOf("kg/kg"));
-        assertSame  (UNITY,                         valueOf("kg.kg-1"));
+        assertEquals(KILOGRAM.divide(KILOGRAM),     valueOf("kg/kg"));
+        assertEquals(KILOGRAM.divide(KILOGRAM),     valueOf("kg.kg-1"));
         assertSame  (PPM,                           valueOf("ppm"));            // Parts
per million
         assertSame  (PSU,                           valueOf("psu"));            // Pratical
Salinity Unit
         assertSame  (SIGMA,                         valueOf("sigma"));


Mime
View raw message