You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2016/10/27 09:28:03 UTC
svn commit: r1766795 - in /sis/branches/JDK8/core/sis-utility/src:
main/java/org/apache/sis/measure/ test/java/org/apache/sis/measure/
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 '\u22c5': next = 1; break;
+ case '/':
+ case '\u2215': 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\u22c5m"));
+ assertSame(Units.CUBIC_METRE, f.parse("m\u22c5m\u22c5m"));
+ assertSame(Units.CUBIC_METRE, f.parse("m�\u22c5m"));
+ assertSame(Units.CUBIC_METRE, f.parse("m2.m"));
+ assertSame(Units.METRES_PER_SECOND, f.parse("m\u2215s"));
+ 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));