| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.sis.measure; |
| |
| import java.util.Locale; |
| import java.util.Map; |
| import java.util.Objects; |
| import java.util.MissingResourceException; |
| import java.io.ObjectStreamException; |
| import java.io.Serializable; |
| import javax.measure.Unit; |
| import javax.measure.Quantity; |
| import org.apache.sis.math.Fraction; |
| import org.apache.sis.util.Characters; |
| import org.apache.sis.util.ArgumentChecks; |
| import org.apache.sis.util.ComparisonMode; |
| import org.apache.sis.util.LenientComparable; |
| import org.apache.sis.util.resources.Errors; |
| import org.apache.sis.util.logging.Logging; |
| import org.apache.sis.internal.system.Loggers; |
| |
| import static java.util.logging.Logger.getLogger; |
| |
| |
| /** |
| * Base class of all unit implementations. There is conceptually 4 kinds of units, |
| * but some of them are implemented by the same class: |
| * |
| * <ul> |
| * <li><b>Base units</b> are the 6 or 7 SI units used as building blocks for all other units. |
| * The base units are metre, second, kilogram, Kelvin degrees, Ampere and Candela, |
| * sometime with the addition of mole.</li> |
| * <li><b>Derived units</b> are products of base units raised to some power. |
| * For example "m/s" is a derived units.</li> |
| * <li><b>Alternate units</b> are dimensionless units handled as if they had a dimension. |
| * An example is angular degrees.</li> |
| * <li><b>Conventional units</b> are units multiplied or shifted by a constant value compared to a base, |
| * derived or alternate unit. For example "km" is a unit equals to 1000 metres, and"°C" is a unit |
| * shifted by 237.15 degrees compared to the Kelvin unit.</li> |
| * </ul> |
| * |
| * In Apache SIS implementation, base and derived units are represented by the same class: {@link SystemUnit}. |
| * All unit instances shall be immutable and thread-safe. |
| * |
| * @author Martin Desruisseaux (MPO, Geomatys) |
| * @version 1.0 |
| * |
| * @param <Q> the kind of quantity to be measured using this units. |
| * |
| * @since 0.8 |
| * @module |
| */ |
| abstract class AbstractUnit<Q extends Quantity<Q>> implements Unit<Q>, LenientComparable, Serializable { |
| /** |
| * For cross-version compatibility. |
| */ |
| private static final long serialVersionUID = -5559950920796714303L; |
| |
| /** |
| * The multiplication and division symbols used for Unicode representation. |
| * Also used for internal representation of {@link #symbol}. |
| */ |
| static final char MULTIPLY = '⋅', DIVIDE = '∕'; |
| |
| /** |
| * Maximum number of multiplications or divisions in the symbol of the first unit |
| * for allowing the use of that symbol for inferring a new symbol. |
| * |
| * @see #isValidSymbol(String, boolean, boolean) |
| */ |
| private static final int MAX_OPERATIONS_IN_SYMBOL = 1; |
| |
| /** |
| * The unit symbol, or {@code null} if this unit has no specific symbol. If {@code null}, |
| * then the {@link #toString()} method is responsible for creating a representation on the fly. |
| * If non-null, this symbol should complies with the {@link UnitFormat.Style#SYMBOL} formatting |
| * (<strong>not</strong> the UCUM format). In particular, this symbol uses Unicode characters |
| * for arithmetic operators and superscripts, as in “m/s²”. However this symbol should never |
| * contains the unit conversion terms. For example “km” is okay, but “1000⋅m” is not. |
| * The intent of those rules is to make easier to analyze the symbol in methods like |
| * {@link ConventionalUnit#power(String)}. |
| * |
| * <p>Users can override this symbol by call to {@link UnitFormat#label(Unit, String)}, |
| * but such overriding applies only to the target {@code UnitFormat} instance.</p> |
| * |
| * <p>The value assigned to this field is also used by {@link #getName()} for fetching a localized name |
| * from the resource bundle.</p> |
| * |
| * @see #getSymbol() |
| * @see SystemUnit#alternate(String) |
| */ |
| private final String symbol; |
| |
| /** |
| * A code that identifies whether this unit is part of SI system, or outside SI but accepted for use with SI. |
| * Value can be {@link UnitRegistry#SI}, {@link UnitRegistry#ACCEPTED}, other constants or 0 if unknown. |
| * |
| * <p>This information may be approximate since we can not always guess correctly whether the result of |
| * an operation is part of SI or not. Values given to the field may be adjusted in any future version.</p> |
| * |
| * <p>This information is not serialized because {@link #readResolve()} will replace the deserialized instance |
| * by a hard-coded instance with appropriate value, if possible.</p> |
| * |
| * @see #equals(short, short) |
| */ |
| final transient byte scope; |
| |
| /** |
| * The EPSG code, or 0 if this unit has no EPSG code. |
| * |
| * <p>This information is not serialized because {@link #readResolve()} will replace the deserialized instance |
| * by a hard-coded instance with appropriate value, if possible.</p> |
| * |
| * @see #equals(short, short) |
| */ |
| final transient short epsg; |
| |
| /** |
| * Creates a new unit having the given symbol and EPSG code. |
| * |
| * @param symbol the unit symbol, or {@code null} if this unit has no specific symbol. |
| * @param scope {@link UnitRegistry#SI}, {@link UnitRegistry#ACCEPTED}, other constants or 0 if unknown. |
| * @param epsg the EPSG code, or 0 if this unit has no EPSG code. |
| */ |
| AbstractUnit(final String symbol, final byte scope, final short epsg) { |
| this.symbol = symbol; |
| this.scope = scope; |
| this.epsg = epsg; |
| } |
| |
| /** |
| * Returns {@code true} if the use of SI prefixes is allowed for the given unit. |
| */ |
| static boolean isPrefixable(final Unit<?> unit) { |
| return (unit instanceof AbstractUnit<?>) && ((AbstractUnit<?>) unit).isPrefixable(); |
| } |
| |
| /** |
| * Returns {@code true} if the use of SI prefixes is allowed for this unit. |
| */ |
| final boolean isPrefixable() { |
| return (scope & UnitRegistry.PREFIXABLE) != 0; |
| } |
| |
| /** |
| * Returns {@code true} if the given Unicode code point is a valid character for a unit symbol. |
| * Current implementation accepts letters, subscripts and the degree sign, but the set of legal |
| * characters may be expanded in any future SIS version (however it should never allow spaces). |
| * The goal is to avoid confusion with exponents and to detect where a unit symbol ends. |
| * |
| * <p>Space characters must be excluded from the set of legal characters because allowing them |
| * would make harder for {@link UnitFormat} to detect correctly where a unit symbol ends.</p> |
| * |
| * <p>Note that some units defined in the {@link Units} class break this rule. In particular, |
| * some of those units contains superscripts or division sign. But the hard-coded symbols in |
| * that class are known to be consistent with SI usage or with {@link UnitFormat} work.</p> |
| */ |
| static boolean isSymbolChar(final int c) { |
| return Character.isLetter(c) || Characters.isSubScript(c) || "°'′’\"″%‰‱-_".indexOf(c) >= 0; |
| } |
| |
| /** |
| * Returns {@code true} if the given unit symbol is valid. |
| * |
| * @param symbol the symbol to verify for validity. |
| * @param allowExponents whether to accept also exponent characters. |
| * @param allowMultiply whether to accept a few multiplications. |
| * @return {@code true} if the given symbol is valid. |
| */ |
| private static boolean isValidSymbol(final String symbol, final boolean allowExponents, final boolean allowMultiply) { |
| return invalidCharForSymbol(symbol, allowMultiply ? MAX_OPERATIONS_IN_SYMBOL : 0, allowExponents) == -1; |
| } |
| |
| /** |
| * 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 maxMultiply maximal number of multiplication symbol to accept. |
| * @param allowExponents whether to accept also exponent characters. |
| * @return Unicode code point of the invalid character, or a negative value. |
| */ |
| static int invalidCharForSymbol(final String symbol, int maxMultiply, final boolean allowExponents) { |
| if (symbol == null || symbol.isEmpty()) { |
| return -2; |
| } |
| for (int i=0; i < symbol.length();) { |
| final int c = symbol.codePointAt(i); |
| if (!isSymbolChar(c)) { |
| if (c == MULTIPLY) { |
| if (--maxMultiply < 0) return c; |
| } else if (!allowExponents || !Characters.isSuperScript(c)) { |
| return c; |
| } |
| } |
| i += Character.charCount(c); |
| } |
| return -1; |
| } |
| |
| /** |
| * Infers a symbol for a unit resulting from an arithmetic operation between two units. |
| * The left operand is {@code this} unit and the right operand is the {@code other} unit. |
| * |
| * @param operation {@link #MULTIPLY}, {@link #DIVIDE} or an exponent. |
| * @param other the left operand used in the operation, or {@code null} if {@code operation} is an exponent. |
| * @return the symbol for the operation result, or {@code null} if no suggestion. |
| */ |
| @SuppressWarnings("fallthrough") |
| final String inferSymbol(final char operation, final Unit<?> other) { |
| String ts = symbol; |
| boolean inverse = false; |
| boolean allowExponents = false; |
| switch (operation) { |
| case DIVIDE: inverse = (ts != null && ts.isEmpty()); // Fall through |
| case MULTIPLY: allowExponents = true; break; |
| } |
| if (inverse || isValidSymbol(ts, allowExponents, allowExponents)) { |
| if (other != null) { |
| final String os = other.getSymbol(); |
| if (isValidSymbol(os, allowExponents, false)) { |
| if (inverse) ts = SystemUnit.ONE; |
| return (ts + operation + os).intern(); |
| } |
| } else if (!allowExponents) { |
| assert Characters.isSuperScript(operation) : operation; |
| return (ts + operation).intern(); |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * If the {@code result} unit is a {@link ConventionalUnit} with no symbol, tries to infer a symbol for it. |
| * Otherwise returns {@code result} unchanged. |
| * |
| * @param result the result of an arithmetic operation. |
| * @param operation {@link #MULTIPLY}, {@link #DIVIDE} or an exponent. |
| * @param other the left operand used in the operation, or {@code null} if {@code operation} is an exponent. |
| * @return {@code result} or an equivalent unit augmented with a symbol. |
| */ |
| final <R extends Quantity<R>> Unit<R> inferSymbol(Unit<R> result, final char operation, final Unit<?> other) { |
| if (result instanceof ConventionalUnit<?> && result.getSymbol() == null) { |
| final String symbol = inferSymbol(operation, other); |
| if (symbol != null) { |
| result = ((ConventionalUnit<R>) result).forSymbol(symbol); |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Returns the symbol (if any) of this unit. A unit may have no symbol, in which case |
| * the {@link #toString()} method is responsible for creating a string representation. |
| * |
| * <div class="note"><b>Example:</b> |
| * {@link Units#METRE} has the {@code "m"} symbol and the same string representation. |
| * But {@link Units#METRES_PER_SECOND} has no symbol; it has only the {@code "m/s"} |
| * string representation.</div> |
| * |
| * @return the unit symbol, or {@code null} if this unit has no specific symbol. |
| * |
| * @see #toString() |
| * @see UnitFormat |
| */ |
| @Override |
| public final String getSymbol() { |
| return symbol; |
| } |
| |
| /** |
| * Returns the name (if any) of this unit. For example {@link Units#METRE} has the "m" symbol and the "metre" name. |
| * If this unit exists in the EPSG database, then this method should return the name as specified in the database. |
| * |
| * @return the unit name, or {@code null} if this unit has no specific name. |
| * |
| * @see UnitFormat#format(Unit, Appendable) |
| */ |
| @Override |
| public final String getName() { |
| if (symbol != null) try { |
| return UnitFormat.getBundle(Locale.getDefault()).getString(symbol); |
| } catch (MissingResourceException e) { |
| Logging.ignorableException(getLogger(Loggers.MEASURE), AbstractUnit.class, "getName", e); |
| // Ignore as per this method contract. |
| } |
| return null; |
| } |
| |
| /** |
| * Returns the unscaled system unit from which this unit is derived. |
| * System units are either base units, {@linkplain #alternate(String) alternate} |
| * units or product of rational powers of system units. |
| * |
| * @return the system unit this unit is derived from, or {@code this} if this unit is a system unit. |
| */ |
| @Override |
| public abstract SystemUnit<Q> getSystemUnit(); |
| |
| /** |
| * Returns the base units and their exponent whose product is the system unit, |
| * or {@code null} if the system unit is a base unit (not a product of existing units). |
| * |
| * @return the base units and their exponent making up the system unit. |
| */ |
| @Override |
| public abstract Map<SystemUnit<?>, Integer> getBaseUnits(); |
| |
| /** |
| * Returns the base units used by Apache SIS implementations. |
| * Contrarily to {@link #getBaseUnits()}, this method never returns {@code null}. |
| */ |
| abstract Map<SystemUnit<?>, Fraction> getBaseSystemUnits(); |
| |
| /** |
| * Indicates if this unit is compatible with the given unit. |
| * This implementation delegates to: |
| * |
| * {@preformat java |
| * return getDimension().equals(that.getDimension()); |
| * } |
| * |
| * @param that the other unit to compare for compatibility. |
| * @return {@code true} if the given unit is compatible with this unit. |
| * |
| * @see #getDimension() |
| */ |
| @Override |
| public final boolean isCompatible(final Unit<?> that) { |
| ArgumentChecks.ensureNonNull("that", that); |
| return getDimension().equals(that.getDimension()); |
| } |
| |
| /** |
| * Returns the error message for an incompatible unit. |
| */ |
| final String incompatible(final Unit<?> that) { |
| return Errors.format(Errors.Keys.IncompatibleUnits_2, this, that); |
| } |
| |
| /** |
| * Returns the result of setting the origin of the scale of measurement to the given value. |
| * For example {@code CELSIUS = KELVIN.shift(273.15)} returns a unit where 0°C is equal to 273.15 K. |
| * |
| * @param offset the value to add when converting from the new unit to this unit. |
| * @return this unit offset by the specified value, or {@code this} if the given offset is zero. |
| */ |
| @Override |
| public final Unit<Q> shift(double offset) { |
| if (offset == 0) return this; |
| double divisor = 1; |
| double m = 1; |
| do { |
| final double c = offset * m; |
| final double r = Math.rint(c); |
| if (Math.abs(c - r) <= Math.ulp(c)) { |
| offset = r; |
| divisor = m; |
| break; |
| } |
| } while ((m *= 10) <= 1E6); |
| return transform(LinearConverter.offset(offset, divisor)); |
| } |
| |
| /** |
| * Returns the result of multiplying this unit by the specified factor. |
| * For example {@code KILOMETRE = METRE.multiply(1000)} returns a unit where 1 km is equal to 1000 m. |
| * |
| * @param multiplier the scale factor when converting from the new unit to this unit. |
| * @return this unit scaled by the specified multiplier. |
| */ |
| @Override |
| public final Unit<Q> multiply(double multiplier) { |
| if (multiplier == 1) return this; |
| double divisor = inverse(multiplier); |
| if (divisor != 1) { |
| multiplier = 1; |
| } else { |
| double m = 1; |
| do { |
| final double c = multiplier * m; |
| final double r = Math.rint(c); |
| if (Math.abs(c - r) <= Math.ulp(c)) { |
| multiplier = r; |
| divisor = m; |
| break; |
| } |
| } while ((m *= 10) <= 1E6); |
| } |
| return transform(LinearConverter.scale(multiplier, divisor)); |
| } |
| |
| /** |
| * Returns the result of dividing this unit by an approximate divisor. |
| * For example {@code GRAM = KILOGRAM.divide(1000)} returns a unit where 1 g is equal to 0.001 kg. |
| * |
| * @param divisor the inverse of the scale factor when converting from the new unit to this unit. |
| * @return this unit divided by the specified divisor. |
| */ |
| @Override |
| public final Unit<Q> divide(double divisor) { |
| if (divisor == 1) return this; |
| final double multiplier = inverse(divisor); |
| if (multiplier != 1) divisor = 1; |
| return transform(LinearConverter.scale(multiplier, divisor)); |
| } |
| |
| /** |
| * If the inverse of the given multiplier is an integer, returns that inverse. Otherwise returns 1. |
| * This method is used for replacing e.g. {@code multiply(0.001)} calls by {@code divide(1000)} calls. |
| * The latter 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 (AbstractConverter.epsilonEquals(inverse, r)) { |
| return r; |
| } |
| } |
| return 1; |
| } |
| |
| /** |
| * Returns the inverse of this unit. |
| * |
| * @return 1 / {@code this} |
| */ |
| @Override |
| public final Unit<?> inverse() { |
| return pow(-1); |
| } |
| |
| /** |
| * Returns units for the same quantity but with scale factors that are not the SI one, or {@code null} if none. |
| * This method returns a direct reference to the internal field; caller shall not modify. |
| */ |
| ConventionalUnit<Q>[] related() { |
| return null; |
| } |
| |
| /** |
| * Compares this unit with the given object for equality. |
| * |
| * @param other the other object to compare with this unit, or {@code null}. |
| * @return {@code true} if the given object is equal to this unit. |
| */ |
| @Override |
| public final boolean equals(final Object other) { |
| return equals(other, ComparisonMode.STRICT); |
| } |
| |
| /** |
| * Compares this unit with the given object for equality, |
| * optionally ignoring metadata and rounding errors. |
| */ |
| @Override |
| public boolean equals(final Object other, final ComparisonMode mode) { |
| if (other == null || other.getClass() != getClass()) { |
| return false; |
| } |
| if (mode.isIgnoringMetadata()) { |
| return true; |
| } |
| final AbstractUnit<?> that = (AbstractUnit<?>) other; |
| return equals(epsg, that.epsg) && equals(scope, that.scope) && Objects.equals(symbol, that.symbol); |
| } |
| |
| /** |
| * Compares the given values only if both of them are non-zero. If at least one value is zero (meaning "unknown"), |
| * assume that values are equal. We do that because deserialized {@code AbstractUnit} instances have {@link #epsg} |
| * and {@link #scope} fields initialized to 0, and we need to ignore those values when comparing the deserialized |
| * instances with instances hard-coded in {@link Units} class. We should not have inconsistencies because there is |
| * no public way to set those fields; they can only be defined in the {@code Units} hard-coded constants. |
| */ |
| private static boolean equals(final short a, final short b) { |
| return (a == 0) || (b == 0) || (a == b); |
| } |
| |
| /** |
| * Returns a hash code value for this unit. |
| */ |
| @Override |
| public int hashCode() { |
| // Do not use EPSG code or scope because they are not serialized. |
| return 31 * Objects.hashCode(symbol); |
| } |
| |
| /** |
| * Returns the unlocalized string representation of this unit, either as a single symbol |
| * or a product of symbols or scale factors, eventually with an offset. |
| * |
| * @see #getSymbol() |
| */ |
| @Override |
| public final String toString() { |
| if (symbol != null) { |
| return symbol; |
| } else { |
| return UnitFormat.INSTANCE.format(this); |
| } |
| } |
| |
| /** |
| * Invoked on deserialization for returning a unique instance of {@code AbstractUnit} if possible. |
| */ |
| final Object readResolve() throws ObjectStreamException { |
| if (symbol != null && Units.initialized) { // Force Units class initialization. |
| final Object existing = UnitRegistry.putIfAbsent(symbol, this); |
| if (equals(existing)) { |
| return (Unit<?>) existing; |
| } |
| } |
| return this; |
| } |
| } |