xref: /aosp_15_r20/external/cldr/tools/cldr-code/src/main/java/org/unicode/cldr/test/CheckCasing.java (revision 912701f9769bb47905792267661f0baf2b85bed5)
1 package org.unicode.cldr.test;
2 
3 import com.ibm.icu.lang.UCharacter;
4 import com.ibm.icu.text.BreakIterator;
5 import com.ibm.icu.util.ULocale;
6 import java.util.List;
7 import org.unicode.cldr.test.CheckCLDR.CheckStatus.Subtype;
8 import org.unicode.cldr.util.CLDRFile;
9 import org.unicode.cldr.util.XPathParts;
10 
11 public class CheckCasing extends CheckCLDR {
12     public enum Case {
13         mixed,
14         lowercase_words,
15         titlecase_words,
16         titlecase_firstword,
17         verbatim;
18 
forString(String input)19         public static Case forString(String input) {
20             return valueOf(input.replace('-', '_'));
21         }
22     }
23 
24     // remember to add this class to the list in CheckCLDR.getCheckAll
25     // to run just this test, on just locales starting with 'nl', use CheckCLDR with -fnl.*
26     // -t.*Currencies.*
27 
28     ULocale uLocale = null;
29     BreakIterator breaker = null;
30 
31     @Override
handleSetCldrFileToCheck( CLDRFile cldrFileToCheck, Options options, List<CheckStatus> possibleErrors)32     public CheckCLDR handleSetCldrFileToCheck(
33             CLDRFile cldrFileToCheck, Options options, List<CheckStatus> possibleErrors) {
34         if (cldrFileToCheck == null) return this;
35         super.handleSetCldrFileToCheck(cldrFileToCheck, options, possibleErrors);
36         uLocale = new ULocale(cldrFileToCheck.getLocaleID());
37         breaker = BreakIterator.getWordInstance(uLocale);
38         return this;
39     }
40 
41     // If you don't need any file initialization or postprocessing, you only need this one routine
42     @Override
handleCheck( String path, String fullPath, String value, Options options, List<CheckStatus> result)43     public CheckCLDR handleCheck(
44             String path, String fullPath, String value, Options options, List<CheckStatus> result) {
45         // it helps performance to have a quick reject of most paths
46         if (fullPath == null) return this; // skip paths that we don't have
47         if (fullPath.indexOf("casing") < 0) return this;
48 
49         if (!accept(result)) return this;
50         // pick up the casing attributes from the full path
51         XPathParts parts = XPathParts.getFrozenInstance(fullPath);
52 
53         Case caseTest = Case.mixed;
54         for (int i = 0; i < parts.size(); ++i) {
55             String casingValue = parts.getAttributeValue(i, "casing");
56             if (casingValue == null) {
57                 continue;
58             }
59             caseTest = Case.forString(casingValue);
60             if (caseTest == Case.verbatim) {
61                 return this; // we're done
62             }
63         }
64 
65         String newValue = value;
66         switch (caseTest) {
67             case lowercase_words:
68                 newValue = UCharacter.toLowerCase(uLocale, value);
69                 break;
70             case titlecase_words:
71                 newValue = UCharacter.toTitleCase(uLocale, value, null);
72                 break;
73             case titlecase_firstword:
74                 newValue = TitleCaseFirst(uLocale, value);
75                 break;
76             default:
77                 break;
78         }
79         if (!newValue.equals(value)) {
80             // the following is how you signal an error or warning (or add a demo....)
81             result.add(
82                     new CheckStatus()
83                             .setCause(this)
84                             .setMainType(CheckStatus.errorType)
85                             .setSubtype(Subtype.incorrectCasing)
86                             // typically warningType or errorType
87                             .setMessage(
88                                     "Casing incorrect: either should have casing=\"verbatim\" or be <{0}>",
89                                     new Object[] {
90                                         newValue
91                                     })); // the message; can be MessageFormat with arguments
92         }
93         return this;
94     }
95 
96     // -f(bg|cs|da|el|et|is|it|lt|ro|ru|sl|uk) -t(.*casing.*)
97 
TitleCaseFirst(ULocale locale, String value)98     private String TitleCaseFirst(ULocale locale, String value) {
99         if (value.length() == 0) {
100             return value;
101         }
102         breaker.setText(value);
103         breaker.first();
104         int endOfFirstWord = breaker.next();
105         return UCharacter.toTitleCase(uLocale, value.substring(0, endOfFirstWord), breaker)
106                 + value.substring(endOfFirstWord);
107     }
108 }
109