mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-07 06:25:30 +00:00
ICU-22917 Output to files, cleanup, update docs
This commit is contained in:
parent
8dc2ec0bb0
commit
fbfbe6c7aa
5 changed files with 621 additions and 444 deletions
150
docs/processes/release/tasks/updating-measure-unit-old.md
Normal file
150
docs/processes/release/tasks/updating-measure-unit-old.md
Normal file
|
@ -0,0 +1,150 @@
|
|||
---
|
||||
layout: default
|
||||
title: Updating MeasureUnit with new CLDR data
|
||||
parent: Release & Milestone Tasks
|
||||
grand_parent: Contributors
|
||||
nav_order: 120
|
||||
---
|
||||
|
||||
<!--
|
||||
© 2020 and later: Unicode, Inc. and others.
|
||||
License & terms of use: http://www.unicode.org/copyright.html
|
||||
-->
|
||||
|
||||
# Updating MeasureUnit with new CLDR data
|
||||
{: .no_toc }
|
||||
|
||||
## Contents
|
||||
{: .no_toc .text-delta }
|
||||
|
||||
1. TOC
|
||||
{:toc}
|
||||
|
||||
---
|
||||
|
||||
This document explains how to update the C++ and Java version of the MeasureUnit
|
||||
class with new CLDR data.
|
||||
|
||||
Code is generated by running MeasureUnitTest.java unit tests, which writes
|
||||
generated code to System.out. Two ways to access this:
|
||||
|
||||
1. Within **eclipse**:
|
||||
- Open MeasureUnitTest.java, run it by clicking on the green play button on
|
||||
menu bar.
|
||||
- Copy the generated code from the eclipse console to the clipboard.
|
||||
|
||||
2. With **ant**:
|
||||
- Run: `ant checkTest
|
||||
-Dtestclass='com.ibm.icu.dev.test.format.MeasureUnitTest'`
|
||||
- Open the checkTest output: `out/junit-results/checkTest/html/index.html`
|
||||
- Navigate to "System.out" at the bottom of the MeasureUnitTest page to find
|
||||
the generated code, and copy to the clipboard.
|
||||
|
||||
After syncing CLDR data with ICU do the following. This documentation assumes
|
||||
that you are updating the MeasureUnit clases for ICU 68.
|
||||
|
||||
* Check out
|
||||
$GIT_ROOT/icu4j/main/common_tests/src/test/java/com/ibm/icu/dev/test/format/MeasureUnitTest.java
|
||||
* Open MeasureUnitTest.java.
|
||||
* Find the `testZZZ` test, its code should all be commented out. This test will
|
||||
execute last and will run the desired code.
|
||||
|
||||
Make sure DRAFT_VERSIONS at top of MeasureUnitTest.java is set correctly.
|
||||
These are the ICU versions that have draft methods.
|
||||
|
||||
## Update MeasureUnit.java
|
||||
|
||||
* Change `testZZZ` to run `generateConstants(“68”); // ICU 68.`
|
||||
* Run MeasureUnitTest.java, copy the generated code (see instructions above).
|
||||
* Open MeasureUnit.java:
|
||||
$GIT_ROOT/icu4j/main/core/src/main/java/com/ibm/icu/util/MeasureUnit.java
|
||||
* Look for line containing:
|
||||
|
||||
`// Start generated MeasureUnit constants`
|
||||
* Look for line containing:
|
||||
|
||||
`// End generated MeasureUnit constants`
|
||||
* Replace all the generated code in between with the contents of the clipboard
|
||||
* Run the MeasureUnitTest.java to ensure that the new code is backward
|
||||
compatible. These compatibility tests are called something like
|
||||
`TestCompatible65`, which tests backward compatibility with ICU 65.
|
||||
* Create a compatibility test for ICU 68. Change `testZZZ` to run
|
||||
`generateBackwardCompatibilityTest(“68”)`
|
||||
* Run tests.
|
||||
* Copy generated test (see instructions above) into MeasureUnitTest.java
|
||||
* Run tests again to ensure that new code is backward compatible with itself
|
||||
|
||||
## Update ICU4C
|
||||
|
||||
* checkout ICU4C
|
||||
|
||||
### Update measunit.h
|
||||
|
||||
* Change testZZZ to run `generateCXXHConstants(“68”); // ICU 68`.
|
||||
* Run MeasureUnitTest.java, copy the generated code (see instructions above).
|
||||
* Open $GIT_ROOT/icu4c/source/i18n/unicode/measunit.h. Look for line containing:
|
||||
|
||||
`// Start generated createXXX methods`
|
||||
* Look for line:
|
||||
|
||||
`// End generated createXXX methods`
|
||||
* Replace all the generated code in between with the contents of the clipboard
|
||||
|
||||
### Update measunit.cpp
|
||||
|
||||
* Change testZZZ to run generateCXXConstants();
|
||||
* Run MeasureUnitTest.java, copy the generated code (see instructions above).
|
||||
* Open $GIT_ROOT/icu4c/source/i18n/measunit.cpp. Look for line containing:
|
||||
|
||||
`// Start generated code for measunit.cpp`
|
||||
* Look for lines
|
||||
|
||||
`// End generated code for measunit.cpp`
|
||||
* Replace all the generated code in between with the contents of the clipboard
|
||||
|
||||
### Run C++ tests
|
||||
|
||||
* Run `./intltest format/MeasureFormatTest` from `test/intltest` to ensure new
|
||||
code is backward compatible.
|
||||
* Create a compatibility test for ICU 68. Change `testZZZ` in eclipse to run
|
||||
`generateCXXBackwardCompatibilityTest(“68”)`
|
||||
* Run tests.
|
||||
* Copy generated test (see instructions above) into
|
||||
$GIT_ROOT/icu4c/source/test/intltest/measfmttest.cpp. Make other necessary
|
||||
changes to make test compile. You can find these changes by searching for
|
||||
`TestCompatible65()`
|
||||
* Run tests again to ensure that new code is backward compatible with itself
|
||||
|
||||
## Finishing changes
|
||||
|
||||
These last changes are necessary to permanently record the ICU version number of
|
||||
any new measure units. Without these changes any new functions for this release
|
||||
will be considered new for the next release too.
|
||||
|
||||
* Change `testZZZ` to run `updateJAVAVersions(“68”);`
|
||||
* Run MeasureUnitTest.java, copy the generated code (see instructions above).
|
||||
* Append the clipboard contents to the values of the JAVA_VERSIONS variable
|
||||
near the top of MeasureUnitTest.java.
|
||||
|
||||
**Important:** what you are copying are just the new functions for the current
|
||||
ICU version, in this case 68. Therefore append, do not replace.
|
||||
|
||||
## Updating units.txt and unitConstants
|
||||
|
||||
The standard ldml2icu process is used to update ICU's resource files (see
|
||||
[cldr-icu-readme.txt](https://github.com/unicode-org/icu/blob/main/icu4c/source/data/cldr-icu-readme.txt)).
|
||||
CLDR's units.xml defines conversion rates in terms of some constants defined in
|
||||
`unitConstants`.
|
||||
|
||||
For efficiency and simplicity, ICU does not read `unitConstants` from the
|
||||
resource file. If any new constants are added, some code changes would be
|
||||
needed. This would be caught by `testUnitConstantFreshness` unit test in
|
||||
`units_test.cpp`.
|
||||
|
||||
They are hard-coded:
|
||||
* Java: `UnitConverter.java` has the constant names in
|
||||
`UnitConverter.Factor.addEntity()` and constant values in
|
||||
`UnitConverter.Factor.getConversionRate()`.
|
||||
* C++: `units_converter.cpp` has the constant names in
|
||||
`addSingleFactorConstant()`, with the constant values in `double
|
||||
constantsValues[]` in the `units_converter.h` header file.
|
|
@ -12,9 +12,11 @@ License & terms of use: http://www.unicode.org/copyright.html
|
|||
-->
|
||||
|
||||
# Updating MeasureUnit with new CLDR data
|
||||
|
||||
{: .no_toc }
|
||||
|
||||
## Contents
|
||||
|
||||
{: .no_toc .text-delta }
|
||||
|
||||
1. TOC
|
||||
|
@ -22,117 +24,94 @@ License & terms of use: http://www.unicode.org/copyright.html
|
|||
|
||||
---
|
||||
|
||||
This document explains how to update the C++ and Java version of the MeasureUnit
|
||||
This document explains how to update the C++ and Java version of the `MeasureUnit`
|
||||
class with new CLDR data.
|
||||
|
||||
Code is generated by running MeasureUnitTest.java unit tests, which writes
|
||||
generated code to System.out. Two ways to access this:
|
||||
This document applies to ICU 77 and later.
|
||||
For older versions see updating-measure-unit-old.md
|
||||
|
||||
1. Within **eclipse**:
|
||||
- Open MeasureUnitTest.java, run it by clicking on the green play button on
|
||||
menu bar.
|
||||
- Copy the generated code from the eclipse console to the clipboard.
|
||||
Make sure `DRAFT_VERSION_SET` at top of
|
||||
`./icu4j/main/common_tests/src/test/java/com/ibm/icu/dev/test/format/MeasureUnitGeneratorTest.java`
|
||||
is set correctly. \
|
||||
These are the ICU versions that have draft methods.
|
||||
|
||||
2. With **ant**:
|
||||
- Run: `ant checkTest
|
||||
-Dtestclass='com.ibm.icu.dev.test.format.MeasureUnitTest'`
|
||||
- Open the checkTest output: `out/junit-results/checkTest/html/index.html`
|
||||
- Navigate to "System.out" at the bottom of the MeasureUnitTest page to find
|
||||
the generated code, and copy to the clipboard.
|
||||
The code is generated by running `MeasureUnitGeneratorTest.java` unit tests, which writes
|
||||
generated code to various file.
|
||||
|
||||
After syncing CLDR data with ICU do the following. This documentation assumes
|
||||
that you are updating the MeasureUnit clases for ICU 68.
|
||||
1. With **maven** (command line):
|
||||
- Change folder to `{icuRoot}/icu4j`
|
||||
- run `mvn install -DskipTests -DskipITs`
|
||||
- run `mvn install -q -Dtest=MeasureUnitGeneratorTest -DgenerateMeasureUnitUpdate -f main/common_tests`
|
||||
|
||||
* Check out
|
||||
$GIT_ROOT/icu4j/main/common_tests/src/test/java/com/ibm/icu/dev/test/format/MeasureUnitTest.java
|
||||
* Open MeasureUnitTest.java.
|
||||
* Find the `testZZZ` test, its code should all be commented out. This test will
|
||||
execute last and will run the desired code.
|
||||
2. Within **Eclipse**:
|
||||
- Open `MeasureUnitGeneratorTest.java`, find the `generateUnitTestsUpdate` methods
|
||||
and run it by clicking on the green play button on menu bar. \
|
||||
Choose "JUnit Test" if asked. \
|
||||
This will not generate the update, but it will run the test and create a "Run Configuration". \
|
||||
Open it (Main menu -- "Run" -- "Run Configurations"), select the one named
|
||||
`MeasureUnitGeneratorTest.generateUnitTestsUpdate`, go to the "Arguments" tab and add
|
||||
`-DgenerateMeasureUnitUpdate` to the "VM Arguments" text area.
|
||||
|
||||
Make sure DRAFT_VERSIONS at top of MeasureUnitTest.java is set correctly.
|
||||
These are the ICU versions that have draft methods.
|
||||
Both methods will generate files with in `icu4j/main/common_tests/target/` folder. \
|
||||
The file names and the logging to the standard output will guide you.
|
||||
|
||||
## Update MeasureUnit.java
|
||||
It currently looks something like this:
|
||||
```
|
||||
Copy the generated code fragments from / to
|
||||
/some/absolute/path/icu4j/main/common_tests/target/MeasureUnit.java \
|
||||
/some/absolute/path/icu4j/main/core/src/main/java/com/ibm/icu/util/MeasureUnit.java
|
||||
|
||||
* Change `testZZZ` to run `generateConstants(“68”); // ICU 68.`
|
||||
* Run MeasureUnitTest.java, copy the generated code (see instructions above).
|
||||
* Open MeasureUnit.java:
|
||||
$GIT_ROOT/icu4j/main/core/src/main/java/com/ibm/icu/util/MeasureUnit.java
|
||||
* Look for line containing:
|
||||
Copy the generated code fragments from / to
|
||||
/some/absolute/path/icu4j/main/common_tests/target/MeasureUnitCompatibilityTest.java \
|
||||
/some/absolute/path/icu4j/main/common_tests/src/test/java/com/ibm/icu/dev/test/format/MeasureUnitCompatibilityTest.java
|
||||
|
||||
`// Start generated MeasureUnit constants`
|
||||
* Look for line containing:
|
||||
Copy the generated code fragments from / to
|
||||
/some/absolute/path/icu4j/main/common_tests/target/measunit.h \
|
||||
/some/absolute/path/icu4c/source/i18n/unicode/measunit.h
|
||||
|
||||
`// End generated MeasureUnit constants`
|
||||
* Replace all the generated code in between with the contents of the clipboard
|
||||
* Run the MeasureUnitTest.java to ensure that the new code is backward
|
||||
compatible. These compatibility tests are called something like
|
||||
`TestCompatible65`, which tests backward compatibility with ICU 65.
|
||||
* Create a compatibility test for ICU 68. Change `testZZZ` to run
|
||||
`generateBackwardCompatibilityTest(“68”)`
|
||||
* Run tests.
|
||||
* Copy generated test (see instructions above) into MeasureUnitTest.java
|
||||
* Run tests again to ensure that new code is backward compatible with itself
|
||||
Copy the generated code fragments from / to
|
||||
/some/absolute/path/icu4j/main/common_tests/target/measunit.cpp \
|
||||
/some/absolute/path/icu4c/source/i18n/measunit.cpp
|
||||
|
||||
## Update ICU4C
|
||||
Copy the generated code fragments from / to
|
||||
/some/absolute/path/icu4j/main/common_tests/target/measfmttest.cpp \
|
||||
/some/absolute/path/icu4c/source/test/intltest/measfmttest.cpp
|
||||
|
||||
* checkout ICU4C
|
||||
Copy the generated code fragments from / to
|
||||
/some/absolute/path/icu4j/main/common_tests/target/MeasureUnitGeneratorTest.java \
|
||||
/some/absolute/path/icu4j/main/common_tests/src/test/java/com/ibm/icu/dev/test/format/MeasureUnitGeneratorTest.java
|
||||
```
|
||||
|
||||
### Update measunit.h
|
||||
Some kind of diff tool or editor (for example `vi -d`) work nicely.
|
||||
|
||||
* Change testZZZ to run `generateCXXHConstants(“68”); // ICU 68`.
|
||||
* Run MeasureUnitTest.java, copy the generated code (see instructions above).
|
||||
* Open $GIT_ROOT/icu4c/source/i18n/unicode/measunit.h. Look for line containing:
|
||||
Look for line containing `// Start generated ...` and `// End generated ...`
|
||||
These lines exist in both the original files, and the generated one. \
|
||||
Replace all the generated code in between with the contents of the clipboard.
|
||||
|
||||
`// Start generated createXXX methods`
|
||||
* Look for line:
|
||||
If the generated code has no `// Start` ... `// End ...` pair then the new
|
||||
code should be appended at some fixed place (details below).
|
||||
|
||||
`// End generated createXXX methods`
|
||||
* Replace all the generated code in between with the contents of the clipboard
|
||||
* **`MeasureUnit.java`:** replace range.
|
||||
* **`MeasureUnitCompatibilityTest.java`:** append the new generated method at the end. \
|
||||
It is named something like `TestCompatible<version>()`. \
|
||||
Don't add it if it already exists.
|
||||
* **`measunit.h`:** replace range.
|
||||
* **`measunit.cpp`:** replace range.
|
||||
* **`measfmttest.cpp`:** append the new generated method after the last
|
||||
`MeasureFormatTest::TestCompatible<version>()` method. \
|
||||
Don't add it if it already exists. \
|
||||
WARNING: here you should add the method in two places. The method proper, with code,
|
||||
as generated, and the declaration in the class definition.
|
||||
* **`MeasureUnitGeneratorTest.java`:** append the new pairs of measure + version at
|
||||
the end of the `JAVA_VERSIONS` structure. \
|
||||
Don't add them if they already exist.
|
||||
|
||||
### Update measunit.cpp
|
||||
## Run tests for both `icu4c` and `icu4j`
|
||||
|
||||
* Change testZZZ to run generateCXXConstants();
|
||||
* Run MeasureUnitTest.java, copy the generated code (see instructions above).
|
||||
* Open $GIT_ROOT/icu4c/source/i18n/measunit.cpp. Look for line containing:
|
||||
## Updating `units.txt` and `unitConstants`
|
||||
|
||||
`// Start generated code for measunit.cpp`
|
||||
* Look for lines
|
||||
|
||||
`// End generated code for measunit.cpp`
|
||||
* Replace all the generated code in between with the contents of the clipboard
|
||||
|
||||
### Run C++ tests
|
||||
|
||||
* Run `./intltest format/MeasureFormatTest` from `test/intltest` to ensure new
|
||||
code is backward compatible.
|
||||
* Create a compatibility test for ICU 68. Change `testZZZ` in eclipse to run
|
||||
`generateCXXBackwardCompatibilityTest(“68”)`
|
||||
* Run tests.
|
||||
* Copy generated test (see instructions above) into
|
||||
$GIT_ROOT/icu4c/source/test/intltest/measfmttest.cpp. Make other necessary
|
||||
changes to make test compile. You can find these changes by searching for
|
||||
`TestCompatible65()`
|
||||
* Run tests again to ensure that new code is backward compatible with itself
|
||||
|
||||
## Finishing changes
|
||||
|
||||
These last changes are necessary to permanently record the ICU version number of
|
||||
any new measure units. Without these changes any new functions for this release
|
||||
will be considered new for the next release too.
|
||||
|
||||
* Change `testZZZ` to run `updateJAVAVersions(“68”);`
|
||||
* Run MeasureUnitTest.java, copy the generated code (see instructions above).
|
||||
* Append the clipboard contents to the values of the JAVA_VERSIONS variable
|
||||
near the top of MeasureUnitTest.java.
|
||||
|
||||
**Important:** what you are copying are just the new functions for the current
|
||||
ICU version, in this case 68. Therefore append, do not replace.
|
||||
|
||||
## Updating units.txt and unitConstants
|
||||
|
||||
The standard ldml2icu process is used to update ICU's resource files (see
|
||||
[cldr-icu-readme.txt](https://github.com/unicode-org/icu/blob/main/icu4c/source/data/cldr-icu-readme.txt)).
|
||||
The standard `ldml2icu` process is used to update ICU's resource files (see
|
||||
[`cldr-icu-readme.txt`](https://github.com/unicode-org/icu/blob/main/icu4c/source/data/cldr-icu-readme.txt)).
|
||||
CLDR's units.xml defines conversion rates in terms of some constants defined in
|
||||
`unitConstants`.
|
||||
|
||||
|
@ -142,6 +121,7 @@ needed. This would be caught by `testUnitConstantFreshness` unit test in
|
|||
`units_test.cpp`.
|
||||
|
||||
They are hard-coded:
|
||||
|
||||
* Java: `UnitConverter.java` has the constant names in
|
||||
`UnitConverter.Factor.addEntity()` and constant values in
|
||||
`UnitConverter.Factor.getConversionRate()`.
|
||||
|
|
|
@ -16,11 +16,11 @@ import com.ibm.icu.dev.test.CoreTestFmwk;
|
|||
import com.ibm.icu.util.MeasureUnit;
|
||||
|
||||
/**
|
||||
* These class contains only compatibility tests,
|
||||
* generated by MeasureUnitGeneratorTest.
|
||||
* This class contains only compatibility tests generated by MeasureUnitGeneratorTest.
|
||||
* Do not add any other tests here.
|
||||
*/
|
||||
@RunWith(JUnit4.class)
|
||||
public class MeasureUnitCompatibleTest extends CoreTestFmwk {
|
||||
public class MeasureUnitCompatibilityTest extends CoreTestFmwk {
|
||||
|
||||
@Test
|
||||
public void TestCompatible53() {
|
||||
|
@ -2560,7 +2560,8 @@ public class MeasureUnitCompatibleTest extends CoreTestFmwk {
|
|||
assertEquals("", 190, units.length);
|
||||
}
|
||||
|
||||
public void TestCompatible76() {
|
||||
@Test
|
||||
public void TestCompatible76() {
|
||||
MeasureUnit[] units = {
|
||||
MeasureUnit.G_FORCE,
|
||||
MeasureUnit.METER_PER_SECOND_SQUARED,
|
||||
|
@ -2755,6 +2756,11 @@ public class MeasureUnitCompatibleTest extends CoreTestFmwk {
|
|||
MeasureUnit.TABLESPOON,
|
||||
MeasureUnit.TEASPOON,
|
||||
};
|
||||
assertEquals("", 193, units.length);
|
||||
assertEquals("", 192, units.length);
|
||||
}
|
||||
|
||||
/*
|
||||
* This class contains only compatibility tests generated by MeasureUnitGeneratorTest.
|
||||
* Do not add any other tests here.
|
||||
*/
|
||||
}
|
|
@ -8,6 +8,10 @@
|
|||
*/
|
||||
package com.ibm.icu.dev.test.format;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
@ -27,25 +31,27 @@ import com.ibm.icu.dev.test.CoreTestFmwk;
|
|||
import com.ibm.icu.impl.Pair;
|
||||
import com.ibm.icu.util.MeasureUnit;
|
||||
import com.ibm.icu.util.NoUnit;
|
||||
import com.ibm.icu.util.VersionInfo;
|
||||
|
||||
/**
|
||||
* This is not a real test class. It is only used to
|
||||
* generate updated unit tests code based on new CLDR data.
|
||||
* Do not add any other tests here.
|
||||
*
|
||||
* See https://sites.google.com/site/icusite/processes/release/tasks/standards?pli=1
|
||||
* See https://unicode-org.github.io/icu/processes/release/tasks/updating-measure-unit.html
|
||||
* for information on how to update with each new release.
|
||||
* @author markdavis
|
||||
*/
|
||||
@RunWith(JUnit4.class)
|
||||
public class MeasureUnitGeneratorTest extends CoreTestFmwk {
|
||||
|
||||
static class OrderedPair<F extends Comparable, S extends Comparable> extends Pair<F, S> implements Comparable<OrderedPair<F, S>> {
|
||||
private static class OrderedPair<F extends Comparable<F>, S extends Comparable<S>> extends Pair<F, S> implements Comparable<OrderedPair<F, S>> {
|
||||
|
||||
OrderedPair(F first, S second) {
|
||||
private OrderedPair(F first, S second) {
|
||||
super(first, second);
|
||||
}
|
||||
|
||||
public static <F extends Comparable, S extends Comparable> OrderedPair<F, S> of(F first, S second) {
|
||||
private static <F extends Comparable<F>, S extends Comparable<S>> OrderedPair<F, S> of(F first, S second) {
|
||||
if (first == null || second == null) {
|
||||
throw new IllegalArgumentException("OrderedPair.of requires non null values.");
|
||||
}
|
||||
|
@ -264,7 +270,7 @@ public class MeasureUnitGeneratorTest extends CoreTestFmwk {
|
|||
|
||||
// modify certain CLDR unit names before generating functions
|
||||
// that create/get the corresponding MeasureUnit objects
|
||||
private static final Map<String,String> CLDR_NAME_REMAP = new HashMap();
|
||||
private static final Map<String,String> CLDR_NAME_REMAP = new HashMap<>();
|
||||
|
||||
static {
|
||||
TIME_CODES.add("year");
|
||||
|
@ -293,22 +299,40 @@ public class MeasureUnitGeneratorTest extends CoreTestFmwk {
|
|||
CLDR_NAME_REMAP.put("pound-force-foot", "pound-foot");
|
||||
}
|
||||
|
||||
private static final String ICU_ROOT = findIcuRoot();
|
||||
|
||||
private static String findIcuRoot() {
|
||||
URL x = MeasureUnitGeneratorTest.class.getResource(".");
|
||||
String classFile = x.getFile();
|
||||
int idx = classFile.indexOf("/icu4j/main/common_tests/target/");
|
||||
if (idx != -1) {
|
||||
return classFile.substring(0, idx);
|
||||
} else {
|
||||
return "${icuroot}";
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testZZZ() {
|
||||
public void generateUnitTestsUpdate() throws IOException {
|
||||
// various generateXXX calls go here, see
|
||||
// docs/processes/release/tasks/updating-measure-unit.md
|
||||
// use this test to run each of the following in succession
|
||||
generateConstants("76"); // for MeasureUnit.java, update generated MeasureUnit constants
|
||||
generateBackwardCompatibilityTest("76"); // for MeasureUnitTest.java, create TestCompatible74
|
||||
generateCXXHConstants("76"); // for measunit.h, update generated createXXX methods
|
||||
generateCXXConstants(); // for measunit.cpp, update generated code
|
||||
generateCXXBackwardCompatibilityTest("74"); // for measfmttest.cpp, create TestCompatible74
|
||||
updateJAVAVersions("74"); // for MeasureUnitTest.java, JAVA_VERSIONS
|
||||
if (System.getProperty("generateMeasureUnitUpdate") != null) {
|
||||
final String icuVersion = Integer.toString(VersionInfo.ICU_VERSION.getMajor());
|
||||
System.out.println();
|
||||
System.out.println("WARNING: open the pairs of files listed below and copy code fragments, not full files!");
|
||||
System.out.println("Some kind of diff tool / editor would work best.");
|
||||
|
||||
generateConstants(icuVersion); // update generated MeasureUnit constants
|
||||
generateBackwardCompatibilityTest(icuVersion); // create TestCompatible<icu_ver>
|
||||
generateCXXHConstants(icuVersion); // update generated createXXX methods
|
||||
generateCXXConstants(); // update generated code
|
||||
generateCXXBackwardCompatibilityTest(icuVersion); // create TestCompatible<icu_ver>
|
||||
updateJAVAVersions(icuVersion); // JAVA_VERSIONS
|
||||
}
|
||||
}
|
||||
|
||||
// DO NOT DELETE THIS FUNCTION! It may appear as dead code, but we use this to generate code
|
||||
// for MeasureFormat during the release process.
|
||||
static Map<MeasureUnit, Pair<MeasureUnit, MeasureUnit>> getUnitsToPerParts() {
|
||||
private static Map<MeasureUnit, Pair<MeasureUnit, MeasureUnit>> getUnitsToPerParts() {
|
||||
TreeMap<String, List<MeasureUnit>> allUnits = getAllUnits();
|
||||
Map<MeasureUnit, Pair<String, String>> unitsToPerStrings =
|
||||
new HashMap<>();
|
||||
|
@ -342,95 +366,96 @@ public class MeasureUnitGeneratorTest extends CoreTestFmwk {
|
|||
return unitsToPerUnits;
|
||||
}
|
||||
|
||||
// DO NOT DELETE THIS FUNCTION! It may appear as dead code, but we use this to generate code
|
||||
// for MeasureFormat during the release process.
|
||||
static void generateCXXHConstants(String thisVersion) {
|
||||
Map<String, MeasureUnit> seen = new HashMap<>();
|
||||
System.out.println("// Start generated createXXX methods");
|
||||
System.out.println();
|
||||
TreeMap<String, List<MeasureUnit>> allUnits = getAllUnits();
|
||||
for (Map.Entry<String, List<MeasureUnit>> entry : allUnits.entrySet()) {
|
||||
String type = entry.getKey();
|
||||
if (type.equals("currency")) {
|
||||
continue;
|
||||
}
|
||||
for (MeasureUnit unit : entry.getValue()) {
|
||||
String code = unit.getSubtype();
|
||||
String name = toCamelCase(unit);
|
||||
String javaName = toJAVAName(unit);
|
||||
checkForDup(seen, name, unit);
|
||||
if (isDraft(javaName)) {
|
||||
System.out.println("#ifndef U_HIDE_DRAFT_API");
|
||||
private static void generateCXXHConstants(String thisVersion) throws IOException {
|
||||
String fullOutputPath = "${icuroot}/icu4c/source/i18n/unicode/measunit.h";
|
||||
try (PrintStream out = createAndStartOutputFile(fullOutputPath)) {
|
||||
Map<String, MeasureUnit> seen = new HashMap<>();
|
||||
out.println("// Start generated createXXX methods");
|
||||
out.println();
|
||||
TreeMap<String, List<MeasureUnit>> allUnits = getAllUnits();
|
||||
for (Map.Entry<String, List<MeasureUnit>> entry : allUnits.entrySet()) {
|
||||
String type = entry.getKey();
|
||||
if (type.equals("currency")) {
|
||||
continue;
|
||||
}
|
||||
System.out.println(" /**");
|
||||
System.out.println(" * Returns by pointer, unit of " + type + ": " + code + ".");
|
||||
System.out.println(" * Caller owns returned value and must free it.");
|
||||
System.out.printf(" * Also see {@link #get%s()}.\n", name);
|
||||
System.out.println(" * @param status ICU error code.");
|
||||
if (isDraft(javaName)) {
|
||||
System.out.println(" * @draft ICU " + getVersion(javaName, thisVersion));
|
||||
} else {
|
||||
System.out.println(" * @stable ICU " + getVersion(javaName, thisVersion));
|
||||
}
|
||||
System.out.println(" */");
|
||||
System.out.printf(" static MeasureUnit *create%s(UErrorCode &status);\n", name);
|
||||
System.out.println();
|
||||
System.out.println(" /**");
|
||||
System.out.println(" * Returns by value, unit of " + type + ": " + code + ".");
|
||||
System.out.printf(" * Also see {@link #create%s()}.\n", name);
|
||||
String getterVersion = getVersion(javaName, thisVersion);
|
||||
if (Integer.parseInt(getterVersion) < 64) {
|
||||
getterVersion = "64";
|
||||
}
|
||||
if (isDraft(javaName)) {
|
||||
System.out.println(" * @draft ICU " + getterVersion);
|
||||
} else {
|
||||
System.out.println(" * @stable ICU " + getterVersion);
|
||||
}
|
||||
System.out.println(" */");
|
||||
System.out.printf(" static MeasureUnit get%s();\n", name);
|
||||
if (isDraft(javaName)) {
|
||||
System.out.println("#endif /* U_HIDE_DRAFT_API */");
|
||||
}
|
||||
System.out.println("");
|
||||
// Hack: METRIC-TON unit changed its name from "metric-ton" to "tonne"
|
||||
// In order to preserve the existing APIs for "metric-ton" we need to
|
||||
// add those APIs manually
|
||||
if (name.equals("Tonne")) {
|
||||
addCXXHForMetricTon();
|
||||
for (MeasureUnit unit : entry.getValue()) {
|
||||
String code = unit.getSubtype();
|
||||
String name = toCamelCase(unit);
|
||||
String javaName = toJAVAName(unit);
|
||||
checkForDup(seen, name, unit);
|
||||
if (isDraft(javaName)) {
|
||||
out.println("#ifndef U_HIDE_DRAFT_API");
|
||||
}
|
||||
out.println(" /**");
|
||||
out.println(" * Returns by pointer, unit of " + type + ": " + code + ".");
|
||||
out.println(" * Caller owns returned value and must free it.");
|
||||
out.printf(" * Also see {@link #get%s()}.\n", name);
|
||||
out.println(" * @param status ICU error code.");
|
||||
if (isDraft(javaName)) {
|
||||
out.println(" * @draft ICU " + getVersion(javaName, thisVersion));
|
||||
} else {
|
||||
out.println(" * @stable ICU " + getVersion(javaName, thisVersion));
|
||||
}
|
||||
out.println(" */");
|
||||
out.printf(" static MeasureUnit *create%s(UErrorCode &status);\n", name);
|
||||
out.println();
|
||||
out.println(" /**");
|
||||
out.println(" * Returns by value, unit of " + type + ": " + code + ".");
|
||||
out.printf(" * Also see {@link #create%s()}.\n", name);
|
||||
String getterVersion = getVersion(javaName, thisVersion);
|
||||
if (Integer.parseInt(getterVersion) < 64) {
|
||||
getterVersion = "64";
|
||||
}
|
||||
if (isDraft(javaName)) {
|
||||
out.println(" * @draft ICU " + getterVersion);
|
||||
} else {
|
||||
out.println(" * @stable ICU " + getterVersion);
|
||||
}
|
||||
out.println(" */");
|
||||
out.printf(" static MeasureUnit get%s();\n", name);
|
||||
if (isDraft(javaName)) {
|
||||
out.println("#endif /* U_HIDE_DRAFT_API */");
|
||||
}
|
||||
out.println("");
|
||||
// Hack: METRIC-TON unit changed its name from "metric-ton" to "tonne"
|
||||
// In order to preserve the existing APIs for "metric-ton" we need to
|
||||
// add those APIs manually
|
||||
if (name.equals("Tonne")) {
|
||||
addCXXHForMetricTon(out);
|
||||
}
|
||||
}
|
||||
}
|
||||
out.println("// End generated createXXX methods");
|
||||
}
|
||||
System.out.println("// End generated createXXX methods");
|
||||
}
|
||||
|
||||
// Add the headers for "metric-ton"
|
||||
// The tool won't create them any more
|
||||
private static void addCXXHForMetricTon() {
|
||||
System.out.println(" /**");
|
||||
System.out.println(" * Returns by pointer, unit of mass: metric-ton");
|
||||
System.out.println(" * (renamed to tonne in CLDR 42 / ICU 72).");
|
||||
System.out.println(" * Caller owns returned value and must free it.");
|
||||
System.out.println(" * Note: In ICU 74 this will be deprecated in favor of");
|
||||
System.out.println(" * createTonne(), which is currently draft but will");
|
||||
System.out.println(" * become stable in ICU 74, and which uses the preferred naming.");
|
||||
System.out.println(" * Also see {@link #getMetricTon()} and {@link #createTonne()}.");
|
||||
System.out.println(" * @param status ICU error code.");
|
||||
System.out.println(" * @stable ICU 54");
|
||||
System.out.println(" */");
|
||||
System.out.println(" static MeasureUnit *createMetricTon(UErrorCode &status);");
|
||||
System.out.println("");
|
||||
System.out.println(" /**");
|
||||
System.out.println(" * Returns by value, unit of mass: metric-ton");
|
||||
System.out.println(" * (renamed to tonne in CLDR 42 / ICU 72).");
|
||||
System.out.println(" * Note: In ICU 74 this will be deprecated in favor of");
|
||||
System.out.println(" * getTonne(), which is currently draft but will");
|
||||
System.out.println(" * become stable in ICU 74, and which uses the preferred naming.");
|
||||
System.out.println(" * Also see {@link #createMetricTon()} and {@link #getTonne()}.");
|
||||
System.out.println(" * @stable ICU 64");
|
||||
System.out.println(" */");
|
||||
System.out.println(" static MeasureUnit getMetricTon();");
|
||||
System.out.println("");
|
||||
// The tool won't create them any more
|
||||
private static void addCXXHForMetricTon(PrintStream out) {
|
||||
out.println(" /**");
|
||||
out.println(" * Returns by pointer, unit of mass: metric-ton");
|
||||
out.println(" * (renamed to tonne in CLDR 42 / ICU 72).");
|
||||
out.println(" * Caller owns returned value and must free it.");
|
||||
out.println(" * Note: In ICU 74 this will be deprecated in favor of");
|
||||
out.println(" * createTonne(), which is currently draft but will");
|
||||
out.println(" * become stable in ICU 74, and which uses the preferred naming.");
|
||||
out.println(" * Also see {@link #getMetricTon()} and {@link #createTonne()}.");
|
||||
out.println(" * @param status ICU error code.");
|
||||
out.println(" * @stable ICU 54");
|
||||
out.println(" */");
|
||||
out.println(" static MeasureUnit *createMetricTon(UErrorCode &status);");
|
||||
out.println("");
|
||||
out.println(" /**");
|
||||
out.println(" * Returns by value, unit of mass: metric-ton");
|
||||
out.println(" * (renamed to tonne in CLDR 42 / ICU 72).");
|
||||
out.println(" * Note: In ICU 74 this will be deprecated in favor of");
|
||||
out.println(" * getTonne(), which is currently draft but will");
|
||||
out.println(" * become stable in ICU 74, and which uses the preferred naming.");
|
||||
out.println(" * Also see {@link #createMetricTon()} and {@link #getTonne()}.");
|
||||
out.println(" * @stable ICU 64");
|
||||
out.println(" */");
|
||||
out.println(" static MeasureUnit getMetricTon();");
|
||||
out.println("");
|
||||
}
|
||||
|
||||
private static void checkForDup(
|
||||
|
@ -442,196 +467,192 @@ public class MeasureUnitGeneratorTest extends CoreTestFmwk {
|
|||
}
|
||||
}
|
||||
|
||||
// DO NOT DELETE THIS FUNCTION! It may appear as dead code, but we use this to generate code
|
||||
// for MeasureFormat during the release process.
|
||||
static void updateJAVAVersions(String thisVersion) {
|
||||
System.out.println();
|
||||
Map<String, MeasureUnit> seen = new HashMap<>();
|
||||
TreeMap<String, List<MeasureUnit>> allUnits = getAllUnits();
|
||||
for (Map.Entry<String, List<MeasureUnit>> entry : allUnits.entrySet()) {
|
||||
String type = entry.getKey();
|
||||
if (type.equals("currency")) {
|
||||
continue;
|
||||
}
|
||||
for (MeasureUnit unit : entry.getValue()) {
|
||||
String javaName = toJAVAName(unit);
|
||||
checkForDup(seen, javaName, unit);
|
||||
if (!JAVA_VERSION_MAP.containsKey(javaName)) {
|
||||
System.out.printf(" {\"%s\", \"%s\"},\n", javaName, thisVersion);
|
||||
private static void updateJAVAVersions(String thisVersion) throws IOException {
|
||||
String fullOutputPath = "${icuroot}/icu4j/main/common_tests/src/test/java/com/ibm/icu/dev/test/format/MeasureUnitGeneratorTest.java";
|
||||
try (PrintStream out = createAndStartOutputFile(fullOutputPath)) {
|
||||
out.println();
|
||||
Map<String, MeasureUnit> seen = new HashMap<>();
|
||||
TreeMap<String, List<MeasureUnit>> allUnits = getAllUnits();
|
||||
for (Map.Entry<String, List<MeasureUnit>> entry : allUnits.entrySet()) {
|
||||
String type = entry.getKey();
|
||||
if (type.equals("currency")) {
|
||||
continue;
|
||||
}
|
||||
for (MeasureUnit unit : entry.getValue()) {
|
||||
String javaName = toJAVAName(unit);
|
||||
checkForDup(seen, javaName, unit);
|
||||
if (!JAVA_VERSION_MAP.containsKey(javaName)) {
|
||||
out.printf(" {\"%s\", \"%s\"},\n", javaName, thisVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static TreeMap<String, List<MeasureUnit>> getAllUnits() {
|
||||
|
||||
private static TreeMap<String, List<MeasureUnit>> getAllUnits() {
|
||||
final Comparator<MeasureUnit> measureUnitComparator =
|
||||
(MeasureUnit o1, MeasureUnit o2) -> o1.getSubtype().compareTo(o2.getSubtype());
|
||||
TreeMap<String, List<MeasureUnit>> allUnits = new TreeMap<>();
|
||||
for (String type : MeasureUnit.getAvailableTypes()) {
|
||||
ArrayList<MeasureUnit> units = new ArrayList<>(MeasureUnit.getAvailable(type));
|
||||
Collections.sort(
|
||||
units,
|
||||
new Comparator<MeasureUnit>() {
|
||||
|
||||
@Override
|
||||
public int compare(MeasureUnit o1, MeasureUnit o2) {
|
||||
return o1.getSubtype().compareTo(o2.getSubtype());
|
||||
}
|
||||
|
||||
});
|
||||
Collections.sort(units, measureUnitComparator);
|
||||
allUnits.put(type, units);
|
||||
}
|
||||
return allUnits;
|
||||
}
|
||||
|
||||
// DO NOT DELETE THIS FUNCTION! It may appear as dead code, but we use this to generate code
|
||||
// for MeasureFormat during the release process.
|
||||
static void generateCXXConstants() {
|
||||
System.out.println("// Start generated code for measunit.cpp");
|
||||
System.out.println("");
|
||||
TreeMap<String, List<MeasureUnit>> allUnits = getAllUnits();
|
||||
private static void generateCXXConstants() throws IOException {
|
||||
String fullOutputPath = "${icuroot}/icu4c/source/i18n/measunit.cpp";
|
||||
try (PrintStream out = createAndStartOutputFile(fullOutputPath)) {
|
||||
out.println("// Start generated code for measunit.cpp");
|
||||
out.println("");
|
||||
TreeMap<String, List<MeasureUnit>> allUnits = getAllUnits();
|
||||
|
||||
// Hack: for C++, add base unit here, but ignore them when printing the create methods.
|
||||
// Also keep track of the base unit offset to make the C++ default constructor faster.
|
||||
allUnits.put("none", Arrays.asList(new MeasureUnit[] {NoUnit.BASE}));
|
||||
int baseTypeIdx = -1;
|
||||
int baseSubTypeIdx = -1;
|
||||
// Hack: for C++, add base unit here, but ignore them when printing the create methods.
|
||||
// Also keep track of the base unit offset to make the C++ default constructor faster.
|
||||
allUnits.put("none", Arrays.asList(new MeasureUnit[] {NoUnit.BASE}));
|
||||
int baseTypeIdx = -1;
|
||||
int baseSubTypeIdx = -1;
|
||||
|
||||
System.out.println("// Maps from Type ID to offset in gSubTypes.");
|
||||
System.out.println("static const int32_t gOffsets[] = {");
|
||||
int index = 0;
|
||||
int typeCount = 0;
|
||||
int currencyIndex = -1;
|
||||
for (Map.Entry<String, List<MeasureUnit>> entry : allUnits.entrySet()) {
|
||||
System.out.printf(" %d,\n", index);
|
||||
if (entry.getKey() == "currency") {
|
||||
currencyIndex = typeCount;
|
||||
out.println("// Maps from Type ID to offset in gSubTypes.");
|
||||
out.println("static const int32_t gOffsets[] = {");
|
||||
int index = 0;
|
||||
int typeCount = 0;
|
||||
int currencyIndex = -1;
|
||||
for (Map.Entry<String, List<MeasureUnit>> entry : allUnits.entrySet()) {
|
||||
out.printf(" %d,\n", index);
|
||||
if (entry.getKey() == "currency") {
|
||||
currencyIndex = typeCount;
|
||||
}
|
||||
typeCount++;
|
||||
index += entry.getValue().size();
|
||||
}
|
||||
typeCount++;
|
||||
index += entry.getValue().size();
|
||||
}
|
||||
assertTrue("currency present", currencyIndex >= 0);
|
||||
System.out.printf(" %d\n", index);
|
||||
System.out.println("};");
|
||||
System.out.println();
|
||||
System.out.println("static const int32_t kCurrencyOffset = " + currencyIndex + ";");
|
||||
System.out.println();
|
||||
System.out.println("// Must be sorted alphabetically.");
|
||||
System.out.println("static const char * const gTypes[] = {");
|
||||
boolean first = true;
|
||||
for (Map.Entry<String, List<MeasureUnit>> entry : allUnits.entrySet()) {
|
||||
if (!first) {
|
||||
System.out.println(",");
|
||||
}
|
||||
System.out.print(" \"" + entry.getKey() + "\"");
|
||||
first = false;
|
||||
}
|
||||
System.out.println();
|
||||
System.out.println("};");
|
||||
System.out.println();
|
||||
System.out.println("// Must be grouped by type and sorted alphabetically within each type.");
|
||||
System.out.println("static const char * const gSubTypes[] = {");
|
||||
first = true;
|
||||
int offset = 0;
|
||||
int typeIdx = 0;
|
||||
Map<MeasureUnit, Integer> measureUnitToOffset = new HashMap<>();
|
||||
Map<MeasureUnit, Pair<Integer, Integer>> measureUnitToTypeSubType =
|
||||
new HashMap<>();
|
||||
for (Map.Entry<String, List<MeasureUnit>> entry : allUnits.entrySet()) {
|
||||
int subTypeIdx = 0;
|
||||
for (MeasureUnit unit : entry.getValue()) {
|
||||
assertTrue("currency present", currencyIndex >= 0);
|
||||
out.printf(" %d\n", index);
|
||||
out.println("};");
|
||||
out.println();
|
||||
out.println("static const int32_t kCurrencyOffset = " + currencyIndex + ";");
|
||||
out.println();
|
||||
out.println("// Must be sorted alphabetically.");
|
||||
out.println("static const char * const gTypes[] = {");
|
||||
boolean first = true;
|
||||
for (Map.Entry<String, List<MeasureUnit>> entry : allUnits.entrySet()) {
|
||||
if (!first) {
|
||||
System.out.println(",");
|
||||
}
|
||||
if (unit != null) {
|
||||
System.out.print(" \"" + unit.getSubtype() + "\"");
|
||||
} else {
|
||||
assertEquals("unit only null for \"none\" type", "none", entry.getKey());
|
||||
System.out.print(" \"\"");
|
||||
out.println(",");
|
||||
}
|
||||
out.print(" \"" + entry.getKey() + "\"");
|
||||
first = false;
|
||||
measureUnitToOffset.put(unit, offset);
|
||||
measureUnitToTypeSubType.put(unit, Pair.of(typeIdx, subTypeIdx));
|
||||
if (unit == NoUnit.BASE) {
|
||||
baseTypeIdx = typeIdx;
|
||||
baseSubTypeIdx = subTypeIdx;
|
||||
}
|
||||
offset++;
|
||||
subTypeIdx++;
|
||||
}
|
||||
typeIdx++;
|
||||
}
|
||||
System.out.println();
|
||||
System.out.println("};");
|
||||
System.out.println();
|
||||
|
||||
// Build unit per unit offsets to corresponding type sub types sorted by
|
||||
// unit first and then per unit.
|
||||
TreeMap<OrderedPair<Integer, Integer>, Pair<Integer, Integer>> unitPerUnitOffsetsToTypeSubType
|
||||
= new TreeMap<>();
|
||||
for (Map.Entry<MeasureUnit, Pair<MeasureUnit, MeasureUnit>> entry
|
||||
: getUnitsToPerParts().entrySet()) {
|
||||
Pair<MeasureUnit, MeasureUnit> unitPerUnit = entry.getValue();
|
||||
unitPerUnitOffsetsToTypeSubType.put(
|
||||
OrderedPair.of(
|
||||
measureUnitToOffset.get(unitPerUnit.first),
|
||||
measureUnitToOffset.get(unitPerUnit.second)),
|
||||
measureUnitToTypeSubType.get(entry.getKey()));
|
||||
}
|
||||
|
||||
// Print out the fast-path for the default constructor
|
||||
System.out.println("// Shortcuts to the base unit in order to make the default constructor fast");
|
||||
System.out.println("static const int32_t kBaseTypeIdx = " + baseTypeIdx + ";");
|
||||
System.out.println("static const int32_t kBaseSubTypeIdx = " + baseSubTypeIdx + ";");
|
||||
System.out.println();
|
||||
|
||||
Map<String, MeasureUnit> seen = new HashMap<>();
|
||||
for (Map.Entry<String, List<MeasureUnit>> entry : allUnits.entrySet()) {
|
||||
|
||||
String type = entry.getKey();
|
||||
if (type.equals("currency") || type.equals("none")) {
|
||||
continue;
|
||||
}
|
||||
for (MeasureUnit unit : entry.getValue()) {
|
||||
String name = toCamelCase(unit);
|
||||
Pair<Integer, Integer> typeSubType = measureUnitToTypeSubType.get(unit);
|
||||
if (typeSubType == null) {
|
||||
throw new IllegalStateException();
|
||||
out.println();
|
||||
out.println("};");
|
||||
out.println();
|
||||
out.println("// Must be grouped by type and sorted alphabetically within each type.");
|
||||
out.println("static const char * const gSubTypes[] = {");
|
||||
first = true;
|
||||
int offset = 0;
|
||||
int typeIdx = 0;
|
||||
Map<MeasureUnit, Integer> measureUnitToOffset = new HashMap<>();
|
||||
Map<MeasureUnit, Pair<Integer, Integer>> measureUnitToTypeSubType =
|
||||
new HashMap<>();
|
||||
for (Map.Entry<String, List<MeasureUnit>> entry : allUnits.entrySet()) {
|
||||
int subTypeIdx = 0;
|
||||
for (MeasureUnit unit : entry.getValue()) {
|
||||
if (!first) {
|
||||
out.println(",");
|
||||
}
|
||||
if (unit != null) {
|
||||
out.print(" \"" + unit.getSubtype() + "\"");
|
||||
} else {
|
||||
assertEquals("unit only null for \"none\" type", "none", entry.getKey());
|
||||
out.print(" \"\"");
|
||||
}
|
||||
first = false;
|
||||
measureUnitToOffset.put(unit, offset);
|
||||
measureUnitToTypeSubType.put(unit, Pair.of(typeIdx, subTypeIdx));
|
||||
if (unit == NoUnit.BASE) {
|
||||
baseTypeIdx = typeIdx;
|
||||
baseSubTypeIdx = subTypeIdx;
|
||||
}
|
||||
offset++;
|
||||
subTypeIdx++;
|
||||
}
|
||||
checkForDup(seen, name, unit);
|
||||
System.out.printf("MeasureUnit *MeasureUnit::create%s(UErrorCode &status) {\n", name);
|
||||
System.out.printf(" return MeasureUnit::create(%d, %d, status);\n",
|
||||
typeSubType.first, typeSubType.second);
|
||||
System.out.println("}");
|
||||
System.out.println();
|
||||
System.out.printf("MeasureUnit MeasureUnit::get%s() {\n", name);
|
||||
System.out.printf(" return MeasureUnit(%d, %d);\n",
|
||||
typeSubType.first, typeSubType.second);
|
||||
System.out.println("}");
|
||||
System.out.println();
|
||||
// Hack: METRIC-TON unit changed its name from "metric-ton" to "tonne"
|
||||
// In order to preserve the existing APIs for "metric-ton" we need to
|
||||
// add those APIs manually
|
||||
if (name.equals("Tonne")) {
|
||||
addCXXForMetricTon(typeSubType);
|
||||
typeIdx++;
|
||||
}
|
||||
out.println();
|
||||
out.println("};");
|
||||
out.println();
|
||||
|
||||
// Build unit per unit offsets to corresponding type sub types sorted by
|
||||
// unit first and then per unit.
|
||||
TreeMap<OrderedPair<Integer, Integer>, Pair<Integer, Integer>> unitPerUnitOffsetsToTypeSubType
|
||||
= new TreeMap<>();
|
||||
for (Map.Entry<MeasureUnit, Pair<MeasureUnit, MeasureUnit>> entry
|
||||
: getUnitsToPerParts().entrySet()) {
|
||||
Pair<MeasureUnit, MeasureUnit> unitPerUnit = entry.getValue();
|
||||
unitPerUnitOffsetsToTypeSubType.put(
|
||||
OrderedPair.of(
|
||||
measureUnitToOffset.get(unitPerUnit.first),
|
||||
measureUnitToOffset.get(unitPerUnit.second)),
|
||||
measureUnitToTypeSubType.get(entry.getKey()));
|
||||
}
|
||||
|
||||
// Print out the fast-path for the default constructor
|
||||
out.println("// Shortcuts to the base unit in order to make the default constructor fast");
|
||||
out.println("static const int32_t kBaseTypeIdx = " + baseTypeIdx + ";");
|
||||
out.println("static const int32_t kBaseSubTypeIdx = " + baseSubTypeIdx + ";");
|
||||
out.println();
|
||||
|
||||
Map<String, MeasureUnit> seen = new HashMap<>();
|
||||
for (Map.Entry<String, List<MeasureUnit>> entry : allUnits.entrySet()) {
|
||||
|
||||
String type = entry.getKey();
|
||||
if (type.equals("currency") || type.equals("none")) {
|
||||
continue;
|
||||
}
|
||||
for (MeasureUnit unit : entry.getValue()) {
|
||||
String name = toCamelCase(unit);
|
||||
Pair<Integer, Integer> typeSubType = measureUnitToTypeSubType.get(unit);
|
||||
if (typeSubType == null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
checkForDup(seen, name, unit);
|
||||
out.printf("MeasureUnit *MeasureUnit::create%s(UErrorCode &status) {\n", name);
|
||||
out.printf(" return MeasureUnit::create(%d, %d, status);\n",
|
||||
typeSubType.first, typeSubType.second);
|
||||
out.println("}");
|
||||
out.println();
|
||||
out.printf("MeasureUnit MeasureUnit::get%s() {\n", name);
|
||||
out.printf(" return MeasureUnit(%d, %d);\n",
|
||||
typeSubType.first, typeSubType.second);
|
||||
out.println("}");
|
||||
out.println();
|
||||
// Hack: METRIC-TON unit changed its name from "metric-ton" to "tonne"
|
||||
// In order to preserve the existing APIs for "metric-ton" we need to
|
||||
// add those APIs manually
|
||||
if (name.equals("Tonne")) {
|
||||
addCXXForMetricTon(typeSubType, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
out.println("// End generated code for measunit.cpp");
|
||||
}
|
||||
System.out.println("// End generated code for measunit.cpp");
|
||||
}
|
||||
|
||||
// Add the API skeletons for "metric-ton"
|
||||
// The tool won't create them any more
|
||||
private static void addCXXForMetricTon(Pair<Integer, Integer> typeSubType) {
|
||||
// The tool won't create them any more
|
||||
private static void addCXXForMetricTon(Pair<Integer, Integer> typeSubType, PrintStream out) {
|
||||
String name = "MetricTon";
|
||||
System.out.printf("MeasureUnit *MeasureUnit::create%s(UErrorCode &status) {\n", name);
|
||||
System.out.printf(" return MeasureUnit::create(%d, %d, status);\n",
|
||||
out.printf("MeasureUnit *MeasureUnit::create%s(UErrorCode &status) {\n", name);
|
||||
out.printf(" return MeasureUnit::create(%d, %d, status);\n",
|
||||
typeSubType.first, typeSubType.second);
|
||||
System.out.println("}");
|
||||
System.out.println();
|
||||
System.out.printf("MeasureUnit MeasureUnit::get%s() {\n", name);
|
||||
System.out.printf(" return MeasureUnit(%d, %d);\n",
|
||||
out.println("}");
|
||||
out.println();
|
||||
out.printf("MeasureUnit MeasureUnit::get%s() {\n", name);
|
||||
out.printf(" return MeasureUnit(%d, %d);\n",
|
||||
typeSubType.first, typeSubType.second);
|
||||
System.out.println("}");
|
||||
System.out.println();
|
||||
out.println("}");
|
||||
out.println();
|
||||
}
|
||||
|
||||
private static String toCamelCase(MeasureUnit unit) {
|
||||
|
@ -662,61 +683,64 @@ public class MeasureUnitGeneratorTest extends CoreTestFmwk {
|
|||
return result.toString();
|
||||
}
|
||||
|
||||
static boolean isTypeHidden(String type) {
|
||||
private static boolean isTypeHidden(String type) {
|
||||
return "currency".equals(type);
|
||||
}
|
||||
|
||||
// DO NOT DELETE THIS FUNCTION! It may appear as dead code, but we use this to generate code
|
||||
// for MeasureFormat during the release process.
|
||||
static void generateBackwardCompatibilityTest(String version) {
|
||||
Map<String, MeasureUnit> seen = new HashMap<>();
|
||||
System.out.println();
|
||||
System.out.printf(" public void TestCompatible%s() {\n", version.replace(".", "_"));
|
||||
System.out.println(" MeasureUnit[] units = {");
|
||||
TreeMap<String, List<MeasureUnit>> allUnits = getAllUnits();
|
||||
int count = 0;
|
||||
for (Map.Entry<String, List<MeasureUnit>> entry : allUnits.entrySet()) {
|
||||
if (isTypeHidden(entry.getKey())) {
|
||||
continue;
|
||||
}
|
||||
for (MeasureUnit unit : entry.getValue()) {
|
||||
String javaName = toJAVAName(unit);
|
||||
checkForDup(seen, javaName, unit);
|
||||
System.out.printf(" MeasureUnit.%s,\n", javaName);
|
||||
count++;
|
||||
private static void generateBackwardCompatibilityTest(String version) throws IOException {
|
||||
String fullOutputPath = "${icuroot}/icu4j/main/common_tests/src/test/java/com/ibm/icu/dev/test/format/MeasureUnitCompatibilityTest.java";
|
||||
try (PrintStream out = createAndStartOutputFile(fullOutputPath)) {
|
||||
Map<String, MeasureUnit> seen = new HashMap<>();
|
||||
out.println();
|
||||
out.printf(" @Test\n");
|
||||
out.printf(" public void TestCompatible%s() {\n", version.replace(".", "_"));
|
||||
out.println(" MeasureUnit[] units = {");
|
||||
TreeMap<String, List<MeasureUnit>> allUnits = getAllUnits();
|
||||
int count = 0;
|
||||
for (Map.Entry<String, List<MeasureUnit>> entry : allUnits.entrySet()) {
|
||||
if (isTypeHidden(entry.getKey())) {
|
||||
continue;
|
||||
}
|
||||
for (MeasureUnit unit : entry.getValue()) {
|
||||
String javaName = toJAVAName(unit);
|
||||
checkForDup(seen, javaName, unit);
|
||||
out.printf(" MeasureUnit.%s,\n", javaName);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
out.println(" };");
|
||||
out.printf(" assertEquals(\"\", %d, units.length);\n", count);
|
||||
out.println(" }");
|
||||
}
|
||||
System.out.println(" };");
|
||||
System.out.printf(" assertEquals(\"\", %d, units.length);\n", count);
|
||||
System.out.println(" }");
|
||||
}
|
||||
|
||||
// DO NOT DELETE THIS FUNCTION! It may appear as dead code, but we use this to generate code
|
||||
// for MeasureFormat during the release process.
|
||||
static void generateCXXBackwardCompatibilityTest(String version) {
|
||||
System.out.println();
|
||||
Map<String, MeasureUnit> seen = new HashMap<>();
|
||||
System.out.printf("void MeasureFormatTest::TestCompatible%s() {\n", version.replace(".", "_"));
|
||||
System.out.println(" UErrorCode status = U_ZERO_ERROR;");
|
||||
System.out.println(" LocalPointer<MeasureUnit> measureUnit;");
|
||||
System.out.println(" MeasureUnit measureUnitValue;");
|
||||
TreeMap<String, List<MeasureUnit>> allUnits = getAllUnits();
|
||||
for (Map.Entry<String, List<MeasureUnit>> entry : allUnits.entrySet()) {
|
||||
if (isTypeHidden(entry.getKey())) {
|
||||
continue;
|
||||
}
|
||||
for (MeasureUnit unit : entry.getValue()) {
|
||||
String camelCase = toCamelCase(unit);
|
||||
checkForDup(seen, camelCase, unit);
|
||||
System.out.printf(" measureUnit.adoptInstead(MeasureUnit::create%s(status));\n", camelCase);
|
||||
System.out.printf(" measureUnitValue = MeasureUnit::get%s();\n", camelCase);
|
||||
private static void generateCXXBackwardCompatibilityTest(String version) throws IOException {
|
||||
String fullOutputPath = "${icuroot}/icu4c/source/test/intltest/measfmttest.cpp";
|
||||
try (PrintStream out = createAndStartOutputFile(fullOutputPath)) {
|
||||
out.println();
|
||||
Map<String, MeasureUnit> seen = new HashMap<>();
|
||||
out.printf("void MeasureFormatTest::TestCompatible%s() {\n", version.replace(".", "_"));
|
||||
out.println(" UErrorCode status = U_ZERO_ERROR;");
|
||||
out.println(" LocalPointer<MeasureUnit> measureUnit;");
|
||||
out.println(" MeasureUnit measureUnitValue;");
|
||||
TreeMap<String, List<MeasureUnit>> allUnits = getAllUnits();
|
||||
for (Map.Entry<String, List<MeasureUnit>> entry : allUnits.entrySet()) {
|
||||
if (isTypeHidden(entry.getKey())) {
|
||||
continue;
|
||||
}
|
||||
for (MeasureUnit unit : entry.getValue()) {
|
||||
String camelCase = toCamelCase(unit);
|
||||
checkForDup(seen, camelCase, unit);
|
||||
out.printf(" measureUnit.adoptInstead(MeasureUnit::create%s(status));\n", camelCase);
|
||||
out.printf(" measureUnitValue = MeasureUnit::get%s();\n", camelCase);
|
||||
}
|
||||
}
|
||||
out.println(" assertSuccess(\"\", status);");
|
||||
out.println("}");
|
||||
}
|
||||
System.out.println(" assertSuccess(\"\", status);");
|
||||
System.out.println("}");
|
||||
}
|
||||
|
||||
static String toJAVAName(MeasureUnit unit) {
|
||||
private static String toJAVAName(MeasureUnit unit) {
|
||||
String code = unit.getSubtype();
|
||||
String type = unit.getType();
|
||||
|
||||
|
@ -734,53 +758,54 @@ public class MeasureUnitGeneratorTest extends CoreTestFmwk {
|
|||
return name;
|
||||
}
|
||||
|
||||
// DO NOT DELETE THIS FUNCTION! It may appear as dead code, but we use this to generate code
|
||||
// for MeasureFormat during the release process.
|
||||
static void generateConstants(String thisVersion) {
|
||||
System.out.println(" // Start generated MeasureUnit constants");
|
||||
System.out.println();
|
||||
Map<String, MeasureUnit> seen = new HashMap<>();
|
||||
TreeMap<String, List<MeasureUnit>> allUnits = getAllUnits();
|
||||
for (Map.Entry<String, List<MeasureUnit>> entry : allUnits.entrySet()) {
|
||||
String type = entry.getKey();
|
||||
if (isTypeHidden(type)) {
|
||||
continue;
|
||||
}
|
||||
for (MeasureUnit unit : entry.getValue()) {
|
||||
String name = toJAVAName(unit);
|
||||
String code = unit.getSubtype();
|
||||
checkForDup(seen, name, unit);
|
||||
System.out.println(" /**");
|
||||
System.out.println(" * Constant for unit of " + type +
|
||||
": " +
|
||||
code);
|
||||
// Special case JAVA had old constants for time from before.
|
||||
if ("duration".equals(type) && TIME_CODES.contains(code)) {
|
||||
System.out.println(" * @stable ICU 4.0");
|
||||
}
|
||||
else if (isDraft(name)) {
|
||||
System.out.println(" * @draft ICU " + getVersion(name, thisVersion));
|
||||
} else {
|
||||
System.out.println(" * @stable ICU " + getVersion(name, thisVersion));
|
||||
}
|
||||
System.out.println(" */");
|
||||
if ("duration".equals(type) && TIME_CODES.contains(code)) {
|
||||
System.out.println(" public static final TimeUnit " + name + " = (TimeUnit) MeasureUnit.internalGetInstance(\"" +
|
||||
type +
|
||||
"\", \"" +
|
||||
code +
|
||||
"\");");
|
||||
} else {
|
||||
System.out.println(" public static final MeasureUnit " + name + " = MeasureUnit.internalGetInstance(\"" +
|
||||
type +
|
||||
"\", \"" +
|
||||
code +
|
||||
"\");");
|
||||
}
|
||||
System.out.println();
|
||||
private static void generateConstants(String thisVersion) throws IOException {
|
||||
String fullOutputPath = "${icuroot}/icu4j/main/core/src/main/java/com/ibm/icu/util/MeasureUnit.java";
|
||||
try (PrintStream out = createAndStartOutputFile(fullOutputPath)) {
|
||||
out.println(" // Start generated MeasureUnit constants");
|
||||
out.println();
|
||||
Map<String, MeasureUnit> seen = new HashMap<>();
|
||||
TreeMap<String, List<MeasureUnit>> allUnits = getAllUnits();
|
||||
for (Map.Entry<String, List<MeasureUnit>> entry : allUnits.entrySet()) {
|
||||
String type = entry.getKey();
|
||||
if (isTypeHidden(type)) {
|
||||
continue;
|
||||
}
|
||||
for (MeasureUnit unit : entry.getValue()) {
|
||||
String name = toJAVAName(unit);
|
||||
String code = unit.getSubtype();
|
||||
checkForDup(seen, name, unit);
|
||||
out.println(" /**");
|
||||
out.println(" * Constant for unit of " + type +
|
||||
": " +
|
||||
code);
|
||||
// Special case JAVA had old constants for time from before.
|
||||
if ("duration".equals(type) && TIME_CODES.contains(code)) {
|
||||
out.println(" * @stable ICU 4.0");
|
||||
}
|
||||
else if (isDraft(name)) {
|
||||
out.println(" * @draft ICU " + getVersion(name, thisVersion));
|
||||
} else {
|
||||
out.println(" * @stable ICU " + getVersion(name, thisVersion));
|
||||
}
|
||||
out.println(" */");
|
||||
if ("duration".equals(type) && TIME_CODES.contains(code)) {
|
||||
out.println(" public static final TimeUnit " + name + " = (TimeUnit) MeasureUnit.internalGetInstance(\"" +
|
||||
type +
|
||||
"\", \"" +
|
||||
code +
|
||||
"\");");
|
||||
} else {
|
||||
out.println(" public static final MeasureUnit " + name + " = MeasureUnit.internalGetInstance(\"" +
|
||||
type +
|
||||
"\", \"" +
|
||||
code +
|
||||
"\");");
|
||||
}
|
||||
out.println();
|
||||
}
|
||||
}
|
||||
out.println(" // End generated MeasureUnit constants");
|
||||
}
|
||||
System.out.println(" // End generated MeasureUnit constants");
|
||||
}
|
||||
|
||||
private static String getVersion(String javaName, String thisVersion) {
|
||||
|
@ -799,4 +824,20 @@ public class MeasureUnitGeneratorTest extends CoreTestFmwk {
|
|||
return DRAFT_VERSION_SET.contains(version);
|
||||
}
|
||||
|
||||
private static PrintStream createAndStartOutputFile(String fullOutputFileName) throws IOException {
|
||||
if (fullOutputFileName.startsWith("${icuroot}")) {
|
||||
fullOutputFileName = fullOutputFileName.replace("${icuroot}", ICU_ROOT);
|
||||
}
|
||||
File outputFile = new File("target", new File(fullOutputFileName).getName());
|
||||
System.out.printf("%nCopy the generated code fragments from / to\n %s \\\n %s%n",
|
||||
outputFile.getAbsoluteFile(), fullOutputFileName);
|
||||
|
||||
return new PrintStream(outputFile, "utf-8");
|
||||
}
|
||||
|
||||
/*
|
||||
* This is not a real test class. It is only used to
|
||||
*generate updated unit tests code based on new CLDR data.
|
||||
* Do not add any other tests here.
|
||||
*/
|
||||
}
|
||||
|
|
|
@ -955,7 +955,7 @@ public class MeasureUnitTest extends CoreTestFmwk {
|
|||
@Test
|
||||
public void testCLDRUnitAvailability() {
|
||||
Set<MeasureUnit> knownUnits = new HashSet<>();
|
||||
Class cMeasureUnit, cTimeUnit;
|
||||
Class<?> cMeasureUnit, cTimeUnit;
|
||||
try {
|
||||
cMeasureUnit = Class.forName("com.ibm.icu.util.MeasureUnit");
|
||||
cTimeUnit = Class.forName("com.ibm.icu.util.TimeUnit");
|
||||
|
@ -1439,7 +1439,7 @@ public class MeasureUnitTest extends CoreTestFmwk {
|
|||
if (unit.getType() == "currency") {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (unit.getIdentifier().equals("portion-per-1e9")) {
|
||||
logKnownIssue("ICU-22781", "Handle concentr/perbillion in ICU");
|
||||
continue;
|
||||
|
|
Loading…
Add table
Reference in a new issue