• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // © 2020 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 
4 package com.ibm.icu.impl.units;
5 
6 import com.ibm.icu.util.MeasureUnit;
7 
8 // TODO: revisit documentation in this file. E.g. we don't do dimensionless
9 // units in Java? We use null instead.
10 
11 /**
12  * A class representing a single unit (optional SI or binary prefix, and dimensionality).
13  */
14 public class SingleUnitImpl {
15     /**
16      * Simple unit index, unique for every simple unit, -1 for the dimensionless
17      * unit. This is an index into a string list in unit.txt {ConversionUnits}.
18      * <p>
19      * The default value is -1, meaning the dimensionless unit:
20      * isDimensionless() will return true, until index is changed.
21      */
22     private int index = -1;
23     /**
24      * SimpleUnit is the simplest form of a Unit. For example, for "square-millimeter", the simple unit would be "meter"Ò
25      * <p>
26      * The default value is "", meaning the dimensionless unit:
27      * isDimensionless() will return true, until index is changed.
28      */
29     private String simpleUnitID = "";
30     /**
31      * Determine the power of the `SingleUnit`. For example, for "square-meter", the dimensionality will be `2`.
32      * <p>
33      * NOTE:
34      * Default dimensionality is 1.
35      */
36     private int dimensionality = 1;
37     /**
38      * SI or binary prefix.
39      */
40     private MeasureUnit.MeasurePrefix unitPrefix = MeasureUnit.MeasurePrefix.ONE;
41 
copy()42     public SingleUnitImpl copy() {
43         SingleUnitImpl result = new SingleUnitImpl();
44         result.index = this.index;
45         result.dimensionality = this.dimensionality;
46         result.simpleUnitID = this.simpleUnitID;
47         result.unitPrefix = this.unitPrefix;
48 
49         return result;
50     }
51 
build()52     public MeasureUnit build() {
53         MeasureUnitImpl measureUnit = new MeasureUnitImpl(this);
54         return measureUnit.build();
55     }
56 
57     /**
58      * Generates a neutral identifier string for a single unit which means we do not include the dimension signal.
59      */
getNeutralIdentifier()60     public String getNeutralIdentifier() {
61         StringBuilder result = new StringBuilder();
62         int absPower = Math.abs(this.getDimensionality());
63 
64         assert absPower > 0 : "this function does not support the dimensionless single units";
65 
66         if (absPower == 1) {
67             // no-op
68         } else if (absPower == 2) {
69             result.append("square-");
70         } else if (absPower == 3) {
71             result.append("cubic-");
72         } else if (absPower <= 15) {
73             result.append("pow");
74             result.append(absPower);
75             result.append('-');
76         } else {
77             throw new IllegalArgumentException("Unit Identifier Syntax Error");
78         }
79 
80         result.append(this.getPrefix().getIdentifier());
81         result.append(this.getSimpleUnitID());
82 
83         return result.toString();
84     }
85 
86     /**
87      * Compare this SingleUnitImpl to another SingleUnitImpl for the sake of
88      * sorting and coalescing.
89      * <p>
90      * Sort order of units is specified by UTS #35
91      * (https://unicode.org/reports/tr35/tr35-info.html#Unit_Identifier_Normalization).
92      * <p>
93      * Takes the sign of dimensionality into account, but not the absolute
94      * value: per-meter is not considered the same as meter, but meter is
95      * considered the same as square-meter.
96      * <p>
97      * The dimensionless unit generally does not get compared, but if it did, it
98      * would sort before other units by virtue of index being < 0 and
99      * dimensionality not being negative.
100      */
compareTo(SingleUnitImpl other)101     int compareTo(SingleUnitImpl other) {
102         if (dimensionality < 0 && other.dimensionality > 0) {
103             // Positive dimensions first
104             return 1;
105         }
106         if (dimensionality > 0 && other.dimensionality < 0) {
107             return -1;
108         }
109         // Sort by official quantity order
110         int thisCategoryIndex = UnitsData.getCategoryIndexOfSimpleUnit(index);
111         int otherCategoryIndex = UnitsData.getCategoryIndexOfSimpleUnit(other.index);
112         if (thisCategoryIndex < otherCategoryIndex) {
113             return -1;
114         }
115         if (thisCategoryIndex > otherCategoryIndex) {
116             return 1;
117         }
118         // If quantity order didn't help, then we go by index.
119         if (index < other.index) {
120             return -1;
121         }
122         if (index > other.index) {
123             return 1;
124         }
125         // TODO: revisit if the spec dictates prefix sort order - it doesn't
126         // currently. For now we're sorting binary prefixes before SI prefixes,
127         // as per ICU4C's enum values order.
128         if (this.getPrefix().getBase() < other.getPrefix().getBase()) {
129             return 1;
130         }
131         if (this.getPrefix().getBase() > other.getPrefix().getBase()) {
132             return -1;
133         }
134         if (this.getPrefix().getPower() < other.getPrefix().getPower()) {
135             return -1;
136         }
137         if (this.getPrefix().getPower() > other.getPrefix().getPower()) {
138             return 1;
139         }
140         return 0;
141     }
142 
143     /**
144      * Checks whether this SingleUnitImpl is compatible with another for the purpose of coalescing.
145      * <p>
146      * Units with the same base unit and SI or binary prefix should match, except that they must also
147      * have the same dimensionality sign, such that we don't merge numerator and denominator.
148      */
isCompatibleWith(SingleUnitImpl other)149     boolean isCompatibleWith(SingleUnitImpl other) {
150         return (compareTo(other) == 0);
151     }
152 
getSimpleUnitID()153     public String getSimpleUnitID() {
154         return simpleUnitID;
155     }
156 
setSimpleUnit(int simpleUnitIndex, String[] simpleUnits)157     public void setSimpleUnit(int simpleUnitIndex, String[] simpleUnits) {
158         this.index = simpleUnitIndex;
159         this.simpleUnitID = simpleUnits[simpleUnitIndex];
160     }
161 
getDimensionality()162     public int getDimensionality() {
163         return dimensionality;
164     }
165 
setDimensionality(int dimensionality)166     public void setDimensionality(int dimensionality) {
167         this.dimensionality = dimensionality;
168     }
169 
getPrefix()170     public MeasureUnit.MeasurePrefix getPrefix() {
171         return unitPrefix;
172     }
173 
setPrefix(MeasureUnit.MeasurePrefix unitPrefix)174     public void setPrefix(MeasureUnit.MeasurePrefix unitPrefix) {
175         this.unitPrefix = unitPrefix;
176     }
177 
178     // TODO: unused? Delete?
getIndex()179     public int getIndex() {
180         return index;
181     }
182 
183 }
184