From 47d26811268cfd639461aac84f658a2710b0b96b Mon Sep 17 00:00:00 2001
From: Werner Keil
Date: Wed, 7 Jun 2017 23:39:34 +0200
Subject: [PATCH] 62: Behavior of equals() for same Quantity value
Task-Url: https://github.com/unitsofmeasurement/unit-ri/issues/issues/62
---
src/main/java/tec/units/ri/AbstractUnit.java | 21 +-
.../tec/units/ri/format/SimpleUnitFormat.java | 213 +++++++++---------
.../tec/units/ri/unit/TransformedUnit.java | 70 +++---
.../units/ri/quantity/NumberQuantityTest.java | 2 +-
.../quantity/QuantityFactoryProviderTest.java | 4 +-
.../ri/quantity/QuantityFactoryTest.java | 3 +-
.../units/ri/quantity/QuantityPrefixTest.java | 4 +-
.../java/tec/units/ri/unit/PrefixTest.java | 4 +-
.../java/tec/units/ri/unit/UnitsTest.java | 8 +-
9 files changed, 173 insertions(+), 156 deletions(-)
diff --git a/src/main/java/tec/units/ri/AbstractUnit.java b/src/main/java/tec/units/ri/AbstractUnit.java
index 173b88d..846ba21 100644
--- a/src/main/java/tec/units/ri/AbstractUnit.java
+++ b/src/main/java/tec/units/ri/AbstractUnit.java
@@ -64,7 +64,7 @@
*
* @author Jean-Marie Dautelle
* @author Werner Keil
- * @version 1.0.3, March 17, 2017
+ * @version 1.0.4, June 7, 2017
* @since 1.0
*/
public abstract class AbstractUnit> implements Unit, Comparable> {
@@ -333,12 +333,19 @@ public final Unit alternate(String symbol) {
}
@Override
- public final AbstractUnit transform(UnitConverter operation) {
- AbstractUnit systemUnit = this.getSystemUnit();
- UnitConverter cvtr = this.getSystemConverter().concatenate(operation);
- if (cvtr.equals(AbstractConverter.IDENTITY))
+ public final Unit transform(UnitConverter operation) {
+ Unit systemUnit = this.getSystemUnit();
+ UnitConverter cvtr;
+ if (this.isSystemUnit()) {
+ cvtr = this.getSystemConverter().concatenate(operation);
+ } else {
+ cvtr = operation;
+ }
+ if (cvtr.equals(AbstractConverter.IDENTITY)) {
return systemUnit;
- return new TransformedUnit(systemUnit, cvtr);
+ } else {
+ return new TransformedUnit<>(null, this, systemUnit, cvtr);
+ }
}
@Override
@@ -417,7 +424,7 @@ public final Unit> inverse() {
* the divisor value.
* @return this unit divided by the specified divisor.
*/
- public final AbstractUnit divide(double divisor) {
+ public final Unit divide(double divisor) {
if (divisor == 1)
return this;
if (isLongValue(divisor))
diff --git a/src/main/java/tec/units/ri/format/SimpleUnitFormat.java b/src/main/java/tec/units/ri/format/SimpleUnitFormat.java
index d3d358a..342e44c 100644
--- a/src/main/java/tec/units/ri/format/SimpleUnitFormat.java
+++ b/src/main/java/tec/units/ri/format/SimpleUnitFormat.java
@@ -62,33 +62,33 @@
*
*
*
- * For SI units, the 20 SI prefixes used to form decimal multiples and sub-multiples of SI units are recognized. {@link Units} are directly
+ * For all SI units, the 20 SI prefixes used to form decimal multiples and sub-multiples of SI units are recognized. {@link Units} are directly
* recognized. For example:
- *
- * AbstractUnit.parse("m°C").equals(MetricPrefix.MILLI(Units.CELSIUS))
- * AbstractUnit.parse("kW").equals(MetricPrefix.KILO(Units.WATT))
+ *
+ * AbstractUnit.parse("m°C").equals(MetricPrefix.MILLI(Units.CELSIUS))
+ * AbstractUnit.parse("kW").equals(MetricPrefix.KILO(Units.WATT))
* AbstractUnit.parse("ft").equals(Units.METRE.multiply(0.3048))
*
*
* @author Jean-Marie Dautelle
* @author Werner Keil
* @author Eric Russell
- * @version 1.0.2, October 6, 2016
+ * @version 1.0.3, June 7, 2017
* @since 1.0
*/
public abstract class SimpleUnitFormat extends AbstractUnitFormat {
- /**
- *
- */
+ /**
+ *
+ */
// private static final long serialVersionUID = 4149424034841739785L;
/**
* Flavor of this format
- *
+ *
* @author Werner
*
*/
- public static enum Flavor {
+ public enum Flavor {
Default, ASCII
}
@@ -103,10 +103,10 @@ public static enum Flavor {
private static final ASCIIFormat ASCII = new ASCIIFormat();
/**
- * Returns the default unit format (format used by {@link AbstractUnit#parse(CharSequence) AbstractUnit.parse(CharSequence)} and
+ * Returns the unit format for the default locale (format used by {@link AbstractUnit#parse(CharSequence) AbstractUnit.parse(CharSequence)} and
* {@link Unit#toString() Unit.toString()}).
- *
- * @return the default unit format.
+ *
+ * @return the default unit format (locale sensitive).
*/
public static SimpleUnitFormat getInstance() {
return getInstance(Flavor.Default);
@@ -114,7 +114,7 @@ public static SimpleUnitFormat getInstance() {
/**
* Returns the {@link SimpleUnitFormat} in the desired {@link Flavor}
- *
+ *
* @return the instance for the given {@link Flavor}.
*/
public static SimpleUnitFormat getInstance(Flavor flavor) {
@@ -173,12 +173,10 @@ protected SimpleUnitFormat() {
public abstract Unit extends Quantity> parseSingleUnit(CharSequence csq, ParsePosition pos) throws ParserException;
/**
- * Attaches a system-wide label to the specified unit. For example:
- *
- * SimpleUnitFormat.getInstance().label(DAY.multiply(365), "year");
- * SimpleUnitFormat.getInstance().label(METRE.multiply(0.3048), "ft");
- * If the specified label is already associated to an unit the previous association is discarded or ignored.
- *
+ * Attaches a system-wide label to the specified unit. For example: SimpleUnitFormat.getInstance().label(DAY.multiply(365), "year");
+ * SimpleUnitFormat.getInstance().label(METER.multiply(0.3048), "ft"); If the specified label is already associated to an unit the previous
+ * association is discarded or ignored.
+ *
* @param unit
* the unit being labeled.
* @param label
@@ -188,11 +186,15 @@ protected SimpleUnitFormat() {
*/
public abstract void label(Unit> unit, String label);
+ public boolean isLocaleSensitive() {
+ return false;
+ }
+
/**
* Attaches a system-wide alias to this unit. Multiple aliases may be attached to the same unit. Aliases are used during parsing to recognize
- * different variants of the same unit. For example: [code] SimpleUnitformat.getInstance().alias(METER.multiply(0.3048), "foot");
+ * different variants of the same unit. For example: SimpleUnitFormat.getInstance().alias(METER.multiply(0.3048), "foot");
* SimpleUnitFormat.getInstance().alias(METER.multiply(0.3048), "feet"); SimpleUnitFormat.getInstance().alias(METER, "meter");
- * SimpleUnitFormat.getInstance().alias(METER, "metre"); [/code] If the specified label is already associated to an unit the previous association is
+ * SimpleUnitFormat.getInstance().alias(METER, "metre"); If the specified label is already associated to an unit the previous association is
* discarded or ignored.
*
* @param unit
@@ -214,7 +216,7 @@ protected SimpleUnitFormat() {
public abstract boolean isValidIdentifier(String name);
/**
- * Formats an unit and appends the resulting text to a given string buffer.
+ * Formats an unit and appends the resulting text to a given string buffer (implements java.text.Format).
*
* @param unit
* the unit to format.
@@ -255,8 +257,8 @@ public Appendable append(CharSequence arg0, int arg1, int arg2) throws IOExcepti
}
/**
- * Parses the text from a string to produce an object.
- *
+ * Parses the text from a string to produce an object (implements java.text.Format).
+ *
* @param source
* the string source, part of which should be parsed.
* @param pos
@@ -266,6 +268,10 @@ public Appendable append(CharSequence arg0, int arg1, int arg2) throws IOExcepti
public final Unit> parseObject(String source, ParsePosition pos) throws ParserException {
// int start = pos.getIndex();
return parseProductUnit(source, pos);
+ /*
+ * } catch (ParserException e) { pos.setIndex(start);
+ * pos.setErrorIndex(e.getPosition()); return null; }
+ */
}
/**
@@ -289,12 +295,12 @@ protected static class DefaultFormat extends SimpleUnitFormat {
/**
* Holds the name to unit mapping.
*/
- final HashMap> _nameToUnit = new HashMap>();
+ final HashMap> _nameToUnit = new HashMap<>();
/**
* Holds the unit to name mapping.
*/
- final HashMap, String> _unitToName = new HashMap, String>();
+ final HashMap, String> _unitToName = new HashMap<>();
@Override
public void label(Unit> unit, String label) {
@@ -323,35 +329,17 @@ public boolean isValidIdentifier(String name) {
* for (int i = 0; i < name.length(); i++) { if
* (!isUnitIdentifierPart(name.charAt(i))) return false; }
*/
- if (!isUnitIdentifierPart(name.charAt(0))) // label must not begin
- // with a digit or
- // mathematical operator
- return false;
- return true;
- }
-
- static boolean isLetter(char c) {
- return (c > 64 && c < 91) || (c > 96 && c < 123);
- }
-
- // Not necessary but included anyways
- static boolean isUpperCase(char c) {
- return c > 64 && c < 91;
- }
-
- static boolean isSpace(char c) {
- // Accounts for spaces and other "space-like" characters
- return c == 32 || c == 12 || c == 13 || c == 14;
+ return isUnitIdentifierPart(name.charAt(0));
}
static boolean isUnitIdentifierPart(char ch) {
- return isLetter(ch)
+ return Character.isLetter(ch)
|| (!Character.isWhitespace(ch) && !Character.isDigit(ch) && (ch != '\u00b7') && (ch != '*') && (ch != '/') && (ch != '(') && (ch != ')')
&& (ch != '[') && (ch != ']') && (ch != '\u00b9') && (ch != '\u00b2') && (ch != '\u00b3') && (ch != '^') && (ch != '+') && (ch != '-'));
}
// Returns the name for the specified unit or null if product unit.
- public String nameFor(Unit> unit) {
+ protected String nameFor(Unit> unit) {
// Searches label database.
String label = _unitToName.get(unit);
if (label != null)
@@ -362,10 +350,11 @@ public String nameFor(Unit> unit) {
return ((AlternateUnit>) unit).getSymbol();
if (unit instanceof TransformedUnit) {
TransformedUnit> tfmUnit = (TransformedUnit>) unit;
- Unit> baseUnits = tfmUnit.toSystemUnit();
- UnitConverter cvtr = tfmUnit.getSystemConverter();
- StringBuffer result = new StringBuffer();
- String baseUnitName = baseUnits.toString();
+ Unit> baseUnit = tfmUnit.getParentUnit();
+ UnitConverter cvtr = tfmUnit.getConverter(); // tfmUnit.getSystemConverter();
+ StringBuilder result = new StringBuilder();
+ String baseUnitName = baseUnit.toString();
+ String prefix = prefixFor(cvtr);
if ((baseUnitName.indexOf('\u00b7') >= 0) || (baseUnitName.indexOf('*') >= 0) || (baseUnitName.indexOf('/') >= 0)) {
// We could use parentheses whenever baseUnits is an
// instanceof ProductUnit, but most ProductUnits have
@@ -377,26 +366,29 @@ public String nameFor(Unit> unit) {
} else {
result.append(baseUnitName);
}
- if (cvtr instanceof AddConverter) {
- result.append('+');
- result.append(((AddConverter) cvtr).getOffset());
- } else if (cvtr instanceof RationalConverter) {
- double dividend = ((RationalConverter) cvtr).getDividend();
- if (dividend != 1) {
+ if (prefix != null) {
+ result.insert(0, prefix);
+ } else {
+ if (cvtr instanceof AddConverter) {
+ result.append('+');
+ result.append(((AddConverter) cvtr).getOffset());
+ } else if (cvtr instanceof RationalConverter) {
+ double dividend = ((RationalConverter) cvtr).getDividend();
+ if (dividend != 1) {
+ result.append('*');
+ result.append(dividend);
+ }
+ double divisor = ((RationalConverter) cvtr).getDivisor();
+ if (divisor != 1) {
+ result.append('/');
+ result.append(divisor);
+ }
+ } else if (cvtr instanceof MultiplyConverter) {
result.append('*');
- result.append(dividend);
+ result.append(((MultiplyConverter) cvtr).getFactor());
+ } else { // Other converters.
+ return "[" + baseUnit + "?]";
}
- double divisor = ((RationalConverter) cvtr).getDivisor();
- if (divisor != 1) {
- result.append('/');
- result.append(divisor);
- }
- ;
- } else if (cvtr instanceof MultiplyConverter) {
- result.append('*');
- result.append(((MultiplyConverter) cvtr).getFactor());
- } else { // Other converters.
- return "[" + baseUnits + "?]";
}
return result.toString();
}
@@ -409,8 +401,18 @@ public String nameFor(Unit> unit) {
return null; // Product unit.
}
+ // Returns the prefix for the specified unit converter.
+ protected String prefixFor(UnitConverter converter) {
+ for (int i = 0; i < CONVERTERS.length; i++) {
+ if (CONVERTERS[i].equals(converter)) {
+ return PREFIXES[i];
+ }
+ }
+ return null; // TODO or return blank?
+ }
+
// Returns the unit for the specified name.
- public Unit> unitFor(String name) {
+ protected Unit> unitFor(String name) {
Unit> unit = _nameToUnit.get(name);
if (unit != null)
return unit;
@@ -420,20 +422,19 @@ public Unit> unitFor(String name) {
// //////////////////////////
// Parsing.
-
- @SuppressWarnings("rawtypes")
+ @SuppressWarnings({ "rawtypes", "unchecked" })
public Unit extends Quantity> parseSingleUnit(CharSequence csq, ParsePosition pos) throws ParserException {
int startIndex = pos.getIndex();
String name = readIdentifier(csq, pos);
- Unit> unit = unitFor(name);
+ Unit unit = unitFor(name);
check(unit != null, name + " not recognized", csq, startIndex);
return unit;
}
- @SuppressWarnings("rawtypes")
+ @SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public Unit extends Quantity> parseProductUnit(CharSequence csq, ParsePosition pos) throws ParserException {
- Unit> result = AbstractUnit.ONE;
+ Unit result = AbstractUnit.ONE;
int token = nextToken(csq, pos);
switch (token) {
case IDENTIFIER:
@@ -654,7 +655,6 @@ private long readLong(CharSequence csq, ParsePosition pos) {
return isNegative ? -result : result;
}
- // TODO change ParsePos to int arguments
private double readDouble(CharSequence csq, ParsePosition pos) {
final int length = csq.length();
int start = pos.getIndex();
@@ -685,10 +685,12 @@ private String readIdentifier(CharSequence csq, ParsePosition pos) {
@Override
public Appendable format(Unit> unit, Appendable appendable) throws IOException {
String name = nameFor(unit);
- if (name != null)
+ if (name != null) {
return appendable.append(name);
- if (!(unit instanceof ProductUnit))
+ }
+ if (!(unit instanceof ProductUnit)) {
throw new IllegalArgumentException("Cannot format given Object as a Unit");
+ }
// Product unit.
ProductUnit> productUnit = (ProductUnit>) unit;
@@ -762,6 +764,7 @@ private void append(Appendable appendable, CharSequence symbol, int pow, int roo
// private static final long serialVersionUID = 1L;
+ @Override
public Unit> parse(CharSequence csq) throws ParserException {
return parse(csq, 0);
}
@@ -775,10 +778,10 @@ protected Unit> parse(CharSequence csq, int index) throws ParserException {
/**
* This class represents the ASCII format.
*/
- protected static final class ASCIIFormat extends DefaultFormat {
+ protected final static class ASCIIFormat extends DefaultFormat {
@Override
- public String nameFor(Unit> unit) {
+ protected String nameFor(Unit> unit) {
// First search if specific ASCII name should be used.
String name = _unitToName.get(unit);
if (name != null)
@@ -788,7 +791,7 @@ public String nameFor(Unit> unit) {
}
@Override
- public Unit> unitFor(String name) {
+ protected Unit> unitFor(String name) {
// First search if specific ASCII name.
Unit> unit = _nameToUnit.get(name);
if (unit != null)
@@ -831,43 +834,19 @@ public Appendable format(Unit> unit, Appendable appendable) throws IOException
public boolean isValidIdentifier(String name) {
if ((name == null) || (name.length() == 0))
return false;
- if (!isUnitIdentifierPart(name.charAt(0))) // label must not begin
- // with a digit or
- // mathematical operator
- return false;
- return isAllASCII(name);
+ // label must not begin with a digit or mathematical operator
+ return isUnitIdentifierPart(name.charAt(0)) && isAllASCII(name);
/*
* for (int i = 0; i < name.length(); i++) { if
* (!isAsciiCharacter(name.charAt(i))) return false; } return true;
*/
}
-
- // private static final long serialVersionUID = 1L;
- }
-
- /*
- * // return if the given character is within the ASCII charset private
- * static boolean isAsciiCharacter(char c) { // return (c > 64 && c < 91) ||
- * (c > 96 && c < 123); return (int) c < 128; }
- */
- // to check if a string only contains US-ASCII characters
- //
- protected static boolean isAllASCII(String input) {
- boolean isASCII = true;
- for (int i = 0; i < input.length(); i++) {
- int c = input.charAt(i);
- if (c > 0x7F) {
- isASCII = false;
- break;
- }
- }
- return isASCII;
}
/**
* Holds the unique symbols collection (base units or alternate units).
*/
- private static final Map> SYMBOL_TO_UNIT = new HashMap>();
+ private static final Map> SYMBOL_TO_UNIT = new HashMap<>();
// //////////////////////////////////////////////////////////////////////////
// Initializes the standard unit database for SI units.
@@ -881,7 +860,7 @@ protected static boolean isAllASCII(String input) {
MILLI.getSymbol(), MICRO.getSymbol(), NANO.getSymbol(), PICO.getSymbol(), FEMTO.getSymbol(), ATTO.getSymbol(), ZEPTO.getSymbol(),
YOCTO.getSymbol() };
- // XXX we could try retrieving this dynamically in a static {} method from
+ // TODO we could try retrieving this dynamically in a static {} method from
// MetricPrefix if symbols above are also aligned
private static final UnitConverter[] CONVERTERS = { YOTTA.getConverter(), ZETTA.getConverter(), EXA.getConverter(), PETA.getConverter(),
TERA.getConverter(), GIGA.getConverter(), MEGA.getConverter(), KILO.getConverter(), HECTO.getConverter(), DEKA.getConverter(),
@@ -892,6 +871,20 @@ private static String asciiPrefix(String prefix) {
return prefix == "µ" ? "micro" : prefix;
}
+ // to check if a string only contains US-ASCII characters
+ //
+ protected static boolean isAllASCII(String input) {
+ boolean isASCII = true;
+ for (int i = 0; i < input.length(); i++) {
+ int c = input.charAt(i);
+ if (c > 0x7F) {
+ isASCII = false;
+ break;
+ }
+ }
+ return isASCII;
+ }
+
static {
for (int i = 0; i < SI_UNITS.length; i++) {
Unit> si = SI_UNITS[i];
diff --git a/src/main/java/tec/units/ri/unit/TransformedUnit.java b/src/main/java/tec/units/ri/unit/TransformedUnit.java
index 2cb36de..fc0767c 100644
--- a/src/main/java/tec/units/ri/unit/TransformedUnit.java
+++ b/src/main/java/tec/units/ri/unit/TransformedUnit.java
@@ -66,7 +66,7 @@
*
* @author Jean-Marie Dautelle
* @author Werner Keil
- * @version 1.0.1, April 24, 2017
+ * @version 1.0.2, June 7, 2017
* @since 1.0
*/
public final class TransformedUnit> extends AbstractUnit implements UnitConverterSupplier {
@@ -77,10 +77,15 @@ public final class TransformedUnit> extends AbstractUnit parentUnit;
+ /**
+ * Holds the system unit.
+ */
+ private final Unit systemUnit;
+
/**
* Holds the converter to the parent unit.
*/
@@ -92,47 +97,58 @@ public final class TransformedUnit> extends AbstractUnit parentUnit, Unit sysUnit, UnitConverter unitConverter) {
+ if (parentUnit instanceof AbstractUnit) {
+ final AbstractUnit abParent = (AbstractUnit) parentUnit;
+
+ this.systemUnit = sysUnit;
+ // if (!abParent.isSystemUnit()) {
+ // throw new IllegalArgumentException("The parent unit: " + abParent
+ // + " is not a system unit");
+ // }
+ this.parentUnit = abParent;
+ this.converter = unitConverter;
+ this.symbol = symbol;
+ // this.symbol = symbol; //TODO see
+ // https://github.com/unitsofmeasurement/uom-se/issues/54
+ } else {
+ throw new IllegalArgumentException("The parent unit: " + parentUnit + " is not an abstract unit.");
+ }
+ }
+
+ /**
+ * Creates a transformed unit from the specified parent unit.
*
* @param parentUnit
* the system unit from which this unit is derived.
* @param unitConverter
* the converter to the parent units.
- * @throws IllegalArgumentException
- * if the specified parent unit is not an {@link AbstractUnit#isSystemUnit() system unit}
*/
public TransformedUnit(AbstractUnit parentUnit, UnitConverter unitConverter) {
- if (!parentUnit.isSystemUnit())
- throw new IllegalArgumentException("The parent unit: " + parentUnit + " is not a system unit");
- this.parentUnit = parentUnit;
- this.converter = unitConverter;
- // this.symbol = parentUnit.getSymbol();
+ this(null, parentUnit, unitConverter);
}
/**
- * Creates a transformed unit from the specified system unit.
+ * Creates a transformed unit from the specified parent unit.
*
- * @parem symbol the symbol to use with this transformed unit.
+ * @param symbol
+ * the symbol to use with this transformed unit.
* @param parentUnit
- * the system unit from which this unit is derived.
+ * the parent unit from which this unit is derived.
* @param unitConverter
* the converter to the parent units.
- * @throws IllegalArgumentException
- * if the specified parent unit is not an {@link AbstractUnit#isSystemUnit() system unit}
*/
public TransformedUnit(String symbol, Unit parentUnit, UnitConverter unitConverter) {
- if (parentUnit instanceof AbstractUnit) {
- final AbstractUnit abParent = (AbstractUnit) parentUnit;
- if (!abParent.isSystemUnit()) {
- throw new IllegalArgumentException("The parent unit: " + abParent + " is not a system unit");
- }
- this.parentUnit = abParent;
- this.converter = unitConverter;
- this.symbol = symbol; // TODO see
- // https://github.com/unitsofmeasurement/uom-se/issues/54
- } else {
- throw new IllegalArgumentException("The parent unit: " + parentUnit + " is not an abstract unit.");
- }
+ this(null, parentUnit, parentUnit.getSystemUnit(), unitConverter);
}
/**
diff --git a/src/test/java/tec/units/ri/quantity/NumberQuantityTest.java b/src/test/java/tec/units/ri/quantity/NumberQuantityTest.java
index 7f9dcce..a70b59c 100644
--- a/src/test/java/tec/units/ri/quantity/NumberQuantityTest.java
+++ b/src/test/java/tec/units/ri/quantity/NumberQuantityTest.java
@@ -29,7 +29,7 @@
*/
package tec.units.ri.quantity;
-import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
diff --git a/src/test/java/tec/units/ri/quantity/QuantityFactoryProviderTest.java b/src/test/java/tec/units/ri/quantity/QuantityFactoryProviderTest.java
index 0c239fc..f05fd65 100644
--- a/src/test/java/tec/units/ri/quantity/QuantityFactoryProviderTest.java
+++ b/src/test/java/tec/units/ri/quantity/QuantityFactoryProviderTest.java
@@ -31,6 +31,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static tec.units.ri.unit.Units.KILOGRAM;
import static tec.units.ri.unit.Units.METRE;
import static tec.units.ri.unit.Units.MINUTE;
@@ -92,9 +93,8 @@ public void testTimeDerived() {
Quantity