ICU-22781 Fix &Improve MeasureUnit identifier generation for constant denominators (Java)

See #3363
This commit is contained in:
Younies Mahmoud 2025-02-03 10:43:55 +00:00
parent a262e87aa6
commit bc7ccb0589
2 changed files with 71 additions and 3 deletions

View file

@ -1455,6 +1455,32 @@ public class MeasureUnitTest extends CoreTestFmwk {
}
}
@Test
public void TestGetIdentifierForConstantDenominator() {
String testCases[][] = {
{ "meter-per-1000", "meter-per-1000" },
{ "meter-per-1000-kilometer", "meter-per-1000-kilometer" },
{ "meter-per-1000000", "meter-per-1e6" },
{ "meter-per-1000000-kilometer", "meter-per-1e6-kilometer" },
{ "meter-per-1000000000", "meter-per-1e9" },
{ "meter-per-1000000000-kilometer", "meter-per-1e9-kilometer" },
{ "meter-per-1000000000000", "meter-per-1e12" },
{ "meter-per-1000000000000-kilometer", "meter-per-1e12-kilometer" },
{ "meter-per-1000000000000000", "meter-per-1e15" },
{ "meter-per-1e15-kilometer", "meter-per-1e15-kilometer" },
{ "meter-per-1000000000000000000", "meter-per-1e18" },
{ "meter-per-1e18-kilometer", "meter-per-1e18-kilometer" },
{ "meter-per-1000000000000001", "meter-per-1000000000000001" },
{ "meter-per-1000000000000001-kilometer", "meter-per-1000000000000001-kilometer" },
};
for (String[] testCase : testCases) {
MeasureUnit unit = MeasureUnit.forIdentifier(testCase[0]);
String actual = unit.getIdentifier();
assertEquals(testCase[0], testCase[1], actual);
}
}
@Test
public void TestIdentifierDetails() {
MeasureUnit joule = MeasureUnit.forIdentifier("joule");

View file

@ -256,6 +256,46 @@ public class MeasureUnitImpl {
this.constantDenominator = constantDenominator;
}
private int countCharacter(String str, char ch) {
int count = 0;
for (int i = 0; i < str.length(); i++) {
if (str.charAt(i) == ch) {
count++;
}
}
return count;
}
/**
* Internal function that returns a string of the constants in the correct
* format.
*
* Example:
* 1000 --> "-per-1000"
* 1000000 --> "-per-1e6"
*
* NOTE: this function is only used when the constant denominator is greater
* than 0.
*/
private String getConstantsString(long constantDenominator) {
assert constantDenominator > 0;
StringBuilder constantString = new StringBuilder();
constantString.append(constantDenominator);
String result = constantString.toString();
if (constantDenominator <= 1000) {
return result;
}
// Check if the constant denominator is a power of 10
int zeroCount = countCharacter(result, '0');
if (zeroCount == result.length() - 1 && result.charAt(0) == '1') {
return "1e" + zeroCount;
}
return result;
}
/**
* Normalizes the MeasureUnitImpl and generates the identifier string in place.
*/
@ -274,6 +314,7 @@ public class MeasureUnitImpl {
StringBuilder result = new StringBuilder();
boolean beforePer = true;
boolean firstTimeNegativeDimension = false;
boolean isConstantDenominatorAdded = false;
for (SingleUnitImpl singleUnit : this.getSingleUnits()) {
if (beforePer && singleUnit.getDimensionality() < 0) {
beforePer = false;
@ -284,8 +325,9 @@ public class MeasureUnitImpl {
if (firstTimeNegativeDimension && this.constantDenominator > 0) {
result.append("-per-");
result.append(this.constantDenominator);
result.append(getConstantsString(this.constantDenominator));
firstTimeNegativeDimension = false;
isConstantDenominatorAdded = true;
}
if (this.getComplexity() == MeasureUnit.Complexity.MIXED) {
@ -309,9 +351,9 @@ public class MeasureUnitImpl {
result.append(singleUnit.getNeutralIdentifier());
}
if (this.constantDenominator > 0) {
if (this.constantDenominator > 0 && !isConstantDenominatorAdded) {
result.append("-per-");
result.append(this.constantDenominator);
result.append(getConstantsString(this.constantDenominator));
}
this.identifier = result.toString();