diff --git a/docs/processes/release/tasks/updating-measure-unit-old.md b/docs/processes/release/tasks/updating-measure-unit-old.md new file mode 100644 index 000000000000..f5d9219cf955 --- /dev/null +++ b/docs/processes/release/tasks/updating-measure-unit-old.md @@ -0,0 +1,150 @@ +--- +layout: default +title: Updating MeasureUnit with new CLDR data +parent: Release & Milestone Tasks +grand_parent: Contributors +nav_order: 120 +--- + + + +# 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. diff --git a/docs/processes/release/tasks/updating-measure-unit.md b/docs/processes/release/tasks/updating-measure-unit.md index f5d9219cf955..be5add902ba8 100644 --- a/docs/processes/release/tasks/updating-measure-unit.md +++ b/docs/processes/release/tasks/updating-measure-unit.md @@ -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: - -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)). +This document applies to ICU 77 and later. +For older versions see updating-measure-unit-old.md + +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. + +The code is generated by running `MeasureUnitGeneratorTest.java` unit tests, which writes +generated code to various file. + +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` + +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. + +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. + +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 + +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 + +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 + +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 + +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 + +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 +``` + +Some kind of diff tool or editor (for example `vi -d`) work nicely. + +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. + +If the generated code has no `// Start` ... `// End ...` pair then the new +code should be appended at some fixed place (details below). + +* **`MeasureUnit.java`:** replace range. +* **`MeasureUnitCompatibilityTest.java`:** append the new generated method at the end. \ + It is named something like `TestCompatible()`. \ + 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()` 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. + +## Run tests for both `icu4c` and `icu4j` + +## 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`. @@ -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()`. diff --git a/icu4j/main/common_tests/src/test/java/com/ibm/icu/dev/test/format/MeasureUnitCompatibleTest.java b/icu4j/main/common_tests/src/test/java/com/ibm/icu/dev/test/format/MeasureUnitCompatibilityTest.java similarity index 99% rename from icu4j/main/common_tests/src/test/java/com/ibm/icu/dev/test/format/MeasureUnitCompatibleTest.java rename to icu4j/main/common_tests/src/test/java/com/ibm/icu/dev/test/format/MeasureUnitCompatibilityTest.java index 9e5eceb3bf88..69f9eef13b2f 100644 --- a/icu4j/main/common_tests/src/test/java/com/ibm/icu/dev/test/format/MeasureUnitCompatibleTest.java +++ b/icu4j/main/common_tests/src/test/java/com/ibm/icu/dev/test/format/MeasureUnitCompatibilityTest.java @@ -16,11 +16,11 @@ 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 void TestCompatible74() { 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 void TestCompatible76() { 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. + */ } diff --git a/icu4j/main/common_tests/src/test/java/com/ibm/icu/dev/test/format/MeasureUnitGeneratorTest.java b/icu4j/main/common_tests/src/test/java/com/ibm/icu/dev/test/format/MeasureUnitGeneratorTest.java index 2637e67469eb..82e22c146cd1 100644 --- a/icu4j/main/common_tests/src/test/java/com/ibm/icu/dev/test/format/MeasureUnitGeneratorTest.java +++ b/icu4j/main/common_tests/src/test/java/com/ibm/icu/dev/test/format/MeasureUnitGeneratorTest.java @@ -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.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 extends Pair implements Comparable> { + private static class OrderedPair, S extends Comparable> extends Pair implements Comparable> { - OrderedPair(F first, S second) { + private OrderedPair(F first, S second) { super(first, second); } - public static OrderedPair of(F first, S second) { + private static , S extends Comparable> OrderedPair of(F first, S second) { if (first == null || second == null) { throw new IllegalArgumentException("OrderedPair.of requires non null values."); } @@ -264,7 +270,7 @@ public int compareTo(OrderedPair other) { // modify certain CLDR unit names before generating functions // that create/get the corresponding MeasureUnit objects - private static final Map CLDR_NAME_REMAP = new HashMap(); + private static final Map CLDR_NAME_REMAP = new HashMap<>(); static { TIME_CODES.add("year"); @@ -293,22 +299,40 @@ public int compareTo(OrderedPair other) { 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 + generateCXXHConstants(icuVersion); // update generated createXXX methods + generateCXXConstants(); // update generated code + generateCXXBackwardCompatibilityTest(icuVersion); // create TestCompatible + 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> getUnitsToPerParts() { + private static Map> getUnitsToPerParts() { TreeMap> allUnits = getAllUnits(); Map> unitsToPerStrings = new HashMap<>(); @@ -342,95 +366,96 @@ static Map> getUnitsToPerParts() { 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 seen = new HashMap<>(); - System.out.println("// Start generated createXXX methods"); - System.out.println(); - TreeMap> allUnits = getAllUnits(); - for (Map.Entry> 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"); - } - 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)); + private static void generateCXXHConstants(String thisVersion) throws IOException { + String fullOutputPath = "${icuroot}/icu4c/source/i18n/unicode/measunit.h"; + try (PrintStream out = createAndStartOutputFile(fullOutputPath)) { + Map seen = new HashMap<>(); + out.println("// Start generated createXXX methods"); + out.println(); + TreeMap> allUnits = getAllUnits(); + for (Map.Entry> entry : allUnits.entrySet()) { + String type = entry.getKey(); + if (type.equals("currency")) { + continue; } - 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 @@ private static void checkForDup( } } - // 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 seen = new HashMap<>(); - TreeMap> allUnits = getAllUnits(); - for (Map.Entry> 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 seen = new HashMap<>(); + TreeMap> allUnits = getAllUnits(); + for (Map.Entry> 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> getAllUnits() { + + private static TreeMap> getAllUnits() { + final Comparator measureUnitComparator = + (MeasureUnit o1, MeasureUnit o2) -> o1.getSubtype().compareTo(o2.getSubtype()); TreeMap> allUnits = new TreeMap<>(); for (String type : MeasureUnit.getAvailableTypes()) { ArrayList units = new ArrayList<>(MeasureUnit.getAvailable(type)); - Collections.sort( - units, - new Comparator() { - - @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> 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; - - 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> entry : allUnits.entrySet()) { - System.out.printf(" %d,\n", index); - if (entry.getKey() == "currency") { - currencyIndex = typeCount; - } - 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> entry : allUnits.entrySet()) { - if (!first) { - System.out.println(","); + 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> 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; + + 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> entry : allUnits.entrySet()) { + out.printf(" %d,\n", index); + if (entry.getKey() == "currency") { + currencyIndex = typeCount; + } + typeCount++; + index += entry.getValue().size(); } - 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 measureUnitToOffset = new HashMap<>(); - Map> measureUnitToTypeSubType = - new HashMap<>(); - for (Map.Entry> 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> 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; + } + 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 measureUnitToOffset = new HashMap<>(); + Map> measureUnitToTypeSubType = + new HashMap<>(); + for (Map.Entry> 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++; } - offset++; - subTypeIdx++; + 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, Pair> unitPerUnitOffsetsToTypeSubType + = new TreeMap<>(); + for (Map.Entry> entry + : getUnitsToPerParts().entrySet()) { + Pair unitPerUnit = entry.getValue(); + unitPerUnitOffsetsToTypeSubType.put( + OrderedPair.of( + measureUnitToOffset.get(unitPerUnit.first), + measureUnitToOffset.get(unitPerUnit.second)), + measureUnitToTypeSubType.get(entry.getKey())); } - 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, Pair> unitPerUnitOffsetsToTypeSubType - = new TreeMap<>(); - for (Map.Entry> entry - : getUnitsToPerParts().entrySet()) { - Pair 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(); + // 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 seen = new HashMap<>(); - for (Map.Entry> entry : allUnits.entrySet()) { + Map seen = new HashMap<>(); + for (Map.Entry> 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 typeSubType = measureUnitToTypeSubType.get(unit); - if (typeSubType == null) { - throw new IllegalStateException(); + String type = entry.getKey(); + if (type.equals("currency") || type.equals("none")) { + continue; } - 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); + for (MeasureUnit unit : entry.getValue()) { + String name = toCamelCase(unit); + Pair 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 typeSubType) { + // The tool won't create them any more + private static void addCXXForMetricTon(Pair 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 @@ private static String toCamelCase(MeasureUnit unit) { 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 seen = new HashMap<>(); - System.out.println(); - System.out.printf(" public void TestCompatible%s() {\n", version.replace(".", "_")); - System.out.println(" MeasureUnit[] units = {"); - TreeMap> allUnits = getAllUnits(); - int count = 0; - for (Map.Entry> 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 seen = new HashMap<>(); + out.println(); + out.printf(" @Test\n"); + out.printf(" public void TestCompatible%s() {\n", version.replace(".", "_")); + out.println(" MeasureUnit[] units = {"); + TreeMap> allUnits = getAllUnits(); + int count = 0; + for (Map.Entry> 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 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;"); - System.out.println(" MeasureUnit measureUnitValue;"); - TreeMap> allUnits = getAllUnits(); - for (Map.Entry> 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 seen = new HashMap<>(); + out.printf("void MeasureFormatTest::TestCompatible%s() {\n", version.replace(".", "_")); + out.println(" UErrorCode status = U_ZERO_ERROR;"); + out.println(" LocalPointer measureUnit;"); + out.println(" MeasureUnit measureUnitValue;"); + TreeMap> allUnits = getAllUnits(); + for (Map.Entry> 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 @@ static String toJAVAName(MeasureUnit unit) { 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 seen = new HashMap<>(); - TreeMap> allUnits = getAllUnits(); - for (Map.Entry> 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)); + 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 seen = new HashMap<>(); + TreeMap> allUnits = getAllUnits(); + for (Map.Entry> entry : allUnits.entrySet()) { + String type = entry.getKey(); + if (isTypeHidden(type)) { + continue; } - 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 + - "\");"); + 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(); } - System.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 @@ private static boolean isDraft(String javaName) { 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. + */ } diff --git a/icu4j/main/common_tests/src/test/java/com/ibm/icu/dev/test/format/MeasureUnitTest.java b/icu4j/main/common_tests/src/test/java/com/ibm/icu/dev/test/format/MeasureUnitTest.java index 5d61e6cfb0f0..01c9295c2235 100644 --- a/icu4j/main/common_tests/src/test/java/com/ibm/icu/dev/test/format/MeasureUnitTest.java +++ b/icu4j/main/common_tests/src/test/java/com/ibm/icu/dev/test/format/MeasureUnitTest.java @@ -955,7 +955,7 @@ public void testParseObject() { @Test public void testCLDRUnitAvailability() { Set 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 void TestParseBuiltIns() { if (unit.getType() == "currency") { continue; } - + if (unit.getIdentifier().equals("portion-per-1e9")) { logKnownIssue("ICU-22781", "Handle concentr/perbillion in ICU"); continue;