| // © 2020 and later: Unicode, Inc. and others. |
| // License & terms of use: http://www.unicode.org/copyright.html |
| |
| package com.ibm.icu.impl.units; |
| |
| import com.ibm.icu.util.MeasureUnit; |
| |
| // TODO: revisit documentation in this file. E.g. we don't do dimensionless |
| // units in Java? We use null instead. |
| |
| /** |
| * A class representing a single unit (optional SI or binary prefix, and dimensionality). |
| */ |
| public class SingleUnitImpl { |
| /** |
| * Simple unit index, unique for every simple unit, -1 for the dimensionless |
| * unit. This is an index into a string list in unit.txt {ConversionUnits}. |
| * <p> |
| * The default value is -1, meaning the dimensionless unit: |
| * isDimensionless() will return true, until index is changed. |
| */ |
| private int index = -1; |
| /** |
| * SimpleUnit is the simplest form of a Unit. For example, for "square-millimeter", the simple unit would be "meter"Ò |
| * <p> |
| * The default value is "", meaning the dimensionless unit: |
| * isDimensionless() will return true, until index is changed. |
| */ |
| private String simpleUnitID = ""; |
| /** |
| * Determine the power of the `SingleUnit`. For example, for "square-meter", the dimensionality will be `2`. |
| * <p> |
| * NOTE: |
| * Default dimensionality is 1. |
| */ |
| private int dimensionality = 1; |
| /** |
| * SI or binary prefix. |
| */ |
| private MeasureUnit.MeasurePrefix unitPrefix = MeasureUnit.MeasurePrefix.ONE; |
| |
| public SingleUnitImpl copy() { |
| SingleUnitImpl result = new SingleUnitImpl(); |
| result.index = this.index; |
| result.dimensionality = this.dimensionality; |
| result.simpleUnitID = this.simpleUnitID; |
| result.unitPrefix = this.unitPrefix; |
| |
| return result; |
| } |
| |
| public MeasureUnit build() { |
| MeasureUnitImpl measureUnit = new MeasureUnitImpl(this); |
| return measureUnit.build(); |
| } |
| |
| /** |
| * Generates a neutral identifier string for a single unit which means we do not include the dimension signal. |
| */ |
| public String getNeutralIdentifier() { |
| StringBuilder result = new StringBuilder(); |
| int absPower = Math.abs(this.getDimensionality()); |
| |
| assert absPower > 0 : "this function does not support the dimensionless single units"; |
| |
| if (absPower == 1) { |
| // no-op |
| } else if (absPower == 2) { |
| result.append("square-"); |
| } else if (absPower == 3) { |
| result.append("cubic-"); |
| } else if (absPower <= 15) { |
| result.append("pow"); |
| result.append(absPower); |
| result.append('-'); |
| } else { |
| throw new IllegalArgumentException("Unit Identifier Syntax Error"); |
| } |
| |
| result.append(this.getPrefix().getIdentifier()); |
| result.append(this.getSimpleUnitID()); |
| |
| return result.toString(); |
| } |
| |
| /** |
| * Compare this SingleUnitImpl to another SingleUnitImpl for the sake of |
| * sorting and coalescing. |
| * <p> |
| * Sort order of units is specified by UTS #35 |
| * (https://unicode.org/reports/tr35/tr35-info.html#Unit_Identifier_Normalization). |
| * <p> |
| * Takes the sign of dimensionality into account, but not the absolute |
| * value: per-meter is not considered the same as meter, but meter is |
| * considered the same as square-meter. |
| * <p> |
| * The dimensionless unit generally does not get compared, but if it did, it |
| * would sort before other units by virtue of index being < 0 and |
| * dimensionality not being negative. |
| */ |
| int compareTo(SingleUnitImpl other) { |
| if (dimensionality < 0 && other.dimensionality > 0) { |
| // Positive dimensions first |
| return 1; |
| } |
| if (dimensionality > 0 && other.dimensionality < 0) { |
| return -1; |
| } |
| // Sort by official quantity order |
| int thisCategoryIndex = UnitsData.getCategoryIndexOfSimpleUnit(index); |
| int otherCategoryIndex = UnitsData.getCategoryIndexOfSimpleUnit(other.index); |
| if (thisCategoryIndex < otherCategoryIndex) { |
| return -1; |
| } |
| if (thisCategoryIndex > otherCategoryIndex) { |
| return 1; |
| } |
| // If quantity order didn't help, then we go by index. |
| if (index < other.index) { |
| return -1; |
| } |
| if (index > other.index) { |
| return 1; |
| } |
| // TODO: revisit if the spec dictates prefix sort order - it doesn't |
| // currently. For now we're sorting binary prefixes before SI prefixes, |
| // as per ICU4C's enum values order. |
| if (this.getPrefix().getBase() < other.getPrefix().getBase()) { |
| return 1; |
| } |
| if (this.getPrefix().getBase() > other.getPrefix().getBase()) { |
| return -1; |
| } |
| if (this.getPrefix().getPower() < other.getPrefix().getPower()) { |
| return -1; |
| } |
| if (this.getPrefix().getPower() > other.getPrefix().getPower()) { |
| return 1; |
| } |
| return 0; |
| } |
| |
| /** |
| * Checks whether this SingleUnitImpl is compatible with another for the purpose of coalescing. |
| * <p> |
| * Units with the same base unit and SI or binary prefix should match, except that they must also |
| * have the same dimensionality sign, such that we don't merge numerator and denominator. |
| */ |
| boolean isCompatibleWith(SingleUnitImpl other) { |
| return (compareTo(other) == 0); |
| } |
| |
| public String getSimpleUnitID() { |
| return simpleUnitID; |
| } |
| |
| public void setSimpleUnit(int simpleUnitIndex, String[] simpleUnits) { |
| this.index = simpleUnitIndex; |
| this.simpleUnitID = simpleUnits[simpleUnitIndex]; |
| } |
| |
| public int getDimensionality() { |
| return dimensionality; |
| } |
| |
| public void setDimensionality(int dimensionality) { |
| this.dimensionality = dimensionality; |
| } |
| |
| public MeasureUnit.MeasurePrefix getPrefix() { |
| return unitPrefix; |
| } |
| |
| public void setPrefix(MeasureUnit.MeasurePrefix unitPrefix) { |
| this.unitPrefix = unitPrefix; |
| } |
| |
| // TODO: unused? Delete? |
| public int getIndex() { |
| return index; |
| } |
| |
| } |