sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1766795 - in /sis/branches/JDK8/core/sis-utility/src: main/java/org/apache/sis/measure/ test/java/org/apache/sis/measure/
Date Thu, 27 Oct 2016 09:28:03 GMT
Author: desruisseaux
Date: Thu Oct 27 09:28:03 2016
New Revision: 1766795

URL: http://svn.apache.org/viewvc?rev=1766795&view=rev
Log:
Add support for parsing product of units.

Modified:
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/AbstractUnit.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/UnitFormat.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/Units.java
    sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/measure/UnitFormatTest.java
    sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/measure/UnitsTest.java

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/AbstractUnit.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/AbstractUnit.java?rev=1766795&r1=1766794&r2=1766795&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/AbstractUnit.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/AbstractUnit.java
[UTF-8] Thu Oct 27 09:28:03 2016
@@ -210,6 +210,10 @@ abstract class AbstractUnit<Q extends Qu
      */
     @Override
     public final Unit<Q> multiply(final double multiplier) {
+        final double r = inverse(multiplier);
+        if (!Double.isNaN(r)) {
+            return divide(r);
+        }
         return transform(LinearConverter.scale(multiplier, 1));
     }
 
@@ -222,10 +226,30 @@ abstract class AbstractUnit<Q extends Qu
      */
     @Override
     public final Unit<Q> divide(final double divisor) {
+        final double r = inverse(divisor);
+        if (!Double.isNaN(r)) {
+            return multiply(r);
+        }
         return transform(LinearConverter.scale(1, divisor));
     }
 
     /**
+     * If the inverse of the given multiplier is an integer, returns that inverse. Otherwise
returns NaN.
+     * This method is used for replacing e.g. {@code multiply(0.001)} calls by {@code divide(1000)}
calls.
+     * The later allows more accurate operations because of the way {@link LinearConverter}
is implemented.
+     */
+    private static double inverse(final double multiplier) {
+        if (Math.abs(multiplier) < 1) {
+            final double inverse = 1 / multiplier;
+            final double r = Math.rint(inverse);
+            if (Math.abs(inverse - r) <= Math.ulp(inverse)) {
+                return r;
+            }
+        }
+        return Double.NaN;
+    }
+
+    /**
      * Returns the inverse of this unit.
      *
      * @return 1 / {@code this}

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/UnitFormat.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/UnitFormat.java?rev=1766795&r1=1766794&r2=1766795&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/UnitFormat.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/UnitFormat.java
[UTF-8] Thu Oct 27 09:28:03 2016
@@ -393,8 +393,7 @@ public class UnitFormat extends Format i
      * Returns the mapping from long localized and unlocalized names to unit instances.
      * This mapping is somewhat the converse of {@link #symbolToName()}, but includes
      * international and American spelling of unit names in addition of localized names.
-     * The intend is to recognize "meter" as well as "metre" (together with, for example,
-     * "mètre" if and only if the locale language is French).
+     * The intend is to recognize "meter" as well as "metre".
      *
      * <p>While we said that {@code UnitFormat} is not thread safe, we make an exception
for this method
      * for allowing the singleton {@link #INSTANCE} to parse symbols in a multi-threads environment.</p>
@@ -448,7 +447,7 @@ public class UnitFormat extends Format i
      */
     private static void copy(final Locale locale, final ResourceBundle symbolToName, final
Map<String,Unit<?>> nameToUnit) {
         for (final String symbol : symbolToName.keySet()) {
-            nameToUnit.put(symbolToName.getString(symbol).toLowerCase(locale).intern(), Units.get(symbol));
+            nameToUnit.put(CharSequences.toASCII(symbolToName.getString(symbol).toLowerCase(locale)).toString().intern(),
Units.get(symbol));
         }
     }
 
@@ -723,6 +722,7 @@ public class UnitFormat extends Format i
      * @see Units#valueOf(String)
      */
     @Override
+    @SuppressWarnings("null")
     public Unit<?> parse(final CharSequence symbols) throws ParserException {
         String uom = CharSequences.trimWhitespaces(symbols).toString();
         /*
@@ -750,12 +750,60 @@ public class UnitFormat extends Format i
             }
         }
         /*
+         * Split the unit around the multiplication and division operators and parse each
term individually.
+         */
+        int offset    = 0;                  // Index of the first character of the next unit
term to parse.
+        int operation = 0;                  // 1 = multiplication,  2 = division.
+        Unit<?> unit  = null;
+        final int length = uom.length();
+        for (int i=0; i<length; i++) {
+            final char c = uom.charAt(i);   // No need to use code points because we search
characters in BMP.
+            final int next;
+            switch (c) {
+                case '.':
+                case '⋅': next = 1; break;
+                case '/':
+                case '∕': next = 2; break;
+                default:  continue;
+            }
+            final Unit<?> term = parseSymbol(CharSequences.trimWhitespaces(uom, offset,
i).toString(), symbols, offset);
+            switch (operation) {
+                case 0:  unit = term; break;
+                case 1:  unit = unit.multiply(term); break;
+                case 2:  unit = unit.divide(term); break;
+                default: throw new AssertionError(operation);
+            }
+            operation = next;
+            offset = i+1;
+        }
+        final Unit<?> term = parseSymbol(CharSequences.trimWhitespaces(uom, offset,
length).toString(), symbols, offset);
+        switch (operation) {
+            case 0:  unit = term; break;
+            case 1:  unit = unit.multiply(term); break;
+            case 2:  unit = unit.divide(term); break;
+            default: throw new AssertionError(operation);
+        }
+        return unit;
+    }
+
+    /**
+     * Parses a single unit symbol with its exponent.
+     * The given symbol shall not contain multiplication or division operator.
+     *
+     * @param  uom      the single unit symbol to parse.
+     * @param  symbols  the complete string specified by the user, used for error reporting.
+     * @param  offset   index of {@code uom} in the {@code symbols} string, used for error
reporting.
+     * @return the parsed unit symbol (never {@code null}).
+     * @throws ParserException if a problem occurred while parsing the given symbols.
+     */
+    private Unit<?> parseSymbol(final String uom, final CharSequence symbols, final
int offset) throws ParserException {
+        /*
          * Check for labels explicitly given by users. Those labels have precedence over
the Apache SIS hard-coded
          * symbols. If no explicit label was found, check for symbols and names known to
this UnitFormat instance.
          */
         Unit<?> unit = labelToUnit.get(uom);
         if (unit == null) {
-            unit = withPrefix(uom);
+            unit = getPrefixed(uom);
             if (unit == null) {
                 final int length = uom.length();
                 if (length == 0) {
@@ -787,7 +835,7 @@ public class UnitFormat extends Format i
                                 } catch (NumberFormatException e) {
                                     // Should never happen unless the number is larger than
'int' capacity.
                                     throw (ParserException) new ParserException(Errors.format(
-                                            Errors.Keys.UnknownUnit_1, uom), symbols, i).initCause(e);
+                                            Errors.Keys.UnknownUnit_1, uom), symbols, offset+i).initCause(e);
                                 }
                                 canApply = true;
                                 break;
@@ -795,8 +843,7 @@ public class UnitFormat extends Format i
                         } while (i != 0);
                     }
                     if (canApply) {
-                        uom = CharSequences.trimWhitespaces(uom.substring(0, i));
-                        unit = withPrefix(uom);
+                        unit = getPrefixed(CharSequences.trimWhitespaces(uom, 0, i).toString());
                         if (unit != null) {
                             return unit.pow(power);
                         }
@@ -827,13 +874,13 @@ public class UnitFormat extends Format i
                  * appear in numerous places.
                  */
                 String lc = uom.replace('_', ' ').toLowerCase(locale);
-                lc = CharSequences.replace(CharSequences.replace(CharSequences.replace(lc,
+                lc = CharSequences.replace(CharSequences.replace(CharSequences.replace(CharSequences.toASCII(lc),
                         "meters",  "meter"),
                         "metres",  "metre"),
                         "degrees", "degree").toString();
                 unit = nameToUnit().get(lc);
                 if (unit == null) {
-                    throw new ParserException(Errors.format(Errors.Keys.UnknownUnit_1, uom),
symbols, 0);
+                    throw new ParserException(Errors.format(Errors.Keys.UnknownUnit_1, uom),
symbols, offset);
                 }
             }
         }
@@ -845,7 +892,7 @@ public class UnitFormat extends Format i
      * This method does not perform any arithmetic operation on {@code Unit}.
      * Returns {@code null} if no unit is found.
      */
-    private static Unit<?> withPrefix(final String uom) {
+    private static Unit<?> getPrefixed(final String uom) {
         Unit<?> unit = Units.get(uom);
         if (unit == null && uom.length() >= 2) {
             int s = 1;

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/Units.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/Units.java?rev=1766795&r1=1766794&r2=1766795&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/Units.java [UTF-8]
(original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/Units.java [UTF-8]
Thu Oct 27 09:28:03 2016
@@ -776,6 +776,7 @@ public final class Units extends Static
         UnitRegistry.alias(CELSIUS,   "Cel");
         UnitRegistry.alias(GRAD,      "gon");
         UnitRegistry.alias(HECTARE,   "hm²");
+        UnitRegistry.alias(UNITY,       "1");
 
         initialized = true;
     }

Modified: sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/measure/UnitFormatTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/measure/UnitFormatTest.java?rev=1766795&r1=1766794&r2=1766795&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/measure/UnitFormatTest.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/measure/UnitFormatTest.java
[UTF-8] Thu Oct 27 09:28:03 2016
@@ -284,4 +284,19 @@ public final strictfp class UnitFormatTe
             assertTrue(message, message.contains("ka"));
         }
     }
+
+    /**
+     * Tests parsing of symbols composed of terms combined by arithmetic operations (e.g.
"m/s").
+     */
+    @Test
+    @DependsOnMethod("testPrefixParsing")
+    public void testTermsParsing() {
+        final UnitFormat f = new UnitFormat(Locale.UK);
+        assertSame(Units.SQUARE_METRE,      f.parse("m⋅m"));
+        assertSame(Units.CUBIC_METRE,       f.parse("m⋅m⋅m"));
+        assertSame(Units.CUBIC_METRE,       f.parse("m²⋅m"));
+        assertSame(Units.CUBIC_METRE,       f.parse("m2.m"));
+        assertSame(Units.METRES_PER_SECOND, f.parse("m∕s"));
+        assertSame(Units.HERTZ,             f.parse("1/s"));
+    }
 }

Modified: sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/measure/UnitsTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/measure/UnitsTest.java?rev=1766795&r1=1766794&r2=1766795&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/measure/UnitsTest.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/measure/UnitsTest.java
[UTF-8] Thu Oct 27 09:28:03 2016
@@ -35,6 +35,7 @@ import static org.apache.sis.test.Assert
  * @module
  */
 @DependsOn({
+    UnitFormatTest.class,
     SexagesimalConverterTest.class,
     org.apache.sis.internal.util.DefinitionURITest.class,
     org.apache.sis.internal.util.XPathsTest.class
@@ -293,7 +294,7 @@ public final strictfp class UnitsTest ex
         assertEquals(Integer.valueOf(9122), getEpsgCode(DEGREE,         true));
         assertEquals(Integer.valueOf(9110), getEpsgCode(DMS,            false));
         assertEquals(Integer.valueOf(9110), getEpsgCode(DMS,            true));
-        assertEquals(Integer.valueOf(9108), getEpsgCode(DMS_SCALED,     false));
+        assertEquals(Integer.valueOf(9107), getEpsgCode(DMS_SCALED,     false));
         assertEquals(Integer.valueOf(9111), getEpsgCode(DM,             false));
         assertEquals(Integer.valueOf(1029), getEpsgCode(TROPICAL_YEAR,  false));
         assertEquals(Integer.valueOf(1040), getEpsgCode(SECOND,         false));



Mime
View raw message