• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // © 2019 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 package org.unicode.icu.tool.cldrtoicu;
4 
5 import static com.google.common.base.Preconditions.checkNotNull;
6 
7 import java.util.function.Function;
8 
9 import org.unicode.cldr.api.CldrPath;
10 import org.unicode.cldr.api.CldrValue;
11 
12 import com.google.common.collect.ImmutableList;
13 
14 /**
15  * API for transforming CLDR path/value pairs. Transformed results support grouping by their key
16  * and the ability to generate default "fallback" values to account for missing values in a group.
17  *
18  * <p>To transform some set of CLDR path/values:
19  * <ol>
20  * <li>Transform all desired path/value pairs into a set of matched results, discarding duplicates
21  * (see {@link #transform(CldrValue)}.
22  * <li>Group the results by key (e.g. into a {@code ListMultimap}).
23  * <li>For each group, add any fallback values which don't yet exist for that key (see
24  * {@link #getFallbackResultsFor(RbPath, DynamicVars)} and {@link Result#isFallbackFor(Result)}).
25  * <li>Sort elements within each group and flatten result values (see {@link Result#isGrouped()}).
26  * </ol>
27  *
28  * <p>For each unique key, this should yield correctly ordered sequence of values (according to the
29  * semantics of the chosen transformer implementation).
30  */
31 public abstract class PathValueTransformer {
32     /**
33      * A result either obtained by transforming a path/value pair, or as a potential fallback for
34      * some known key (see {@link PathValueTransformer#transform(CldrValue)} and
35      * {@link PathValueTransformer#getFallbackResultsFor(RbPath, DynamicVars)}).
36      */
37     public static abstract class Result implements Comparable<Result> {
38         private final RbPath key;
39 
Result(RbPath key)40         protected Result(RbPath key) {
41             this.key = checkNotNull(key);
42         }
43 
44         /**
45          * Returns the key of this result, used to group results and determine fallback values
46          * according to the semantics of the chosen transformer.
47          */
getKey()48         public RbPath getKey() {
49             return key;
50         }
51 
52         /**
53          * Returns whether the values in this result should be grouped or not. Un-grouped values
54          * should be considered as individual values in a sequence and might be joined with values
55          * from other results in the same group. Grouped values cannot be split and must appear
56          * as a single value.
57          *
58          * <p>For example for the ordered results:
59          * <pre>
60          * Result X = { key=K, values=[ "a", "b" ], grouped=false }
61          * Result Y = { key=K, values=[ "c", "d" ], grouped=false }
62          * Result Z = { key=K, values=[ "e" ], grouped=false }
63          * </pre>
64          * the values for key {@code K} are conceptually {@code [ "a", "b", "c", "d", "e" ]}.
65          *
66          * <p>However if result {@code Y} has {@code grouped=true} then there are now 4 values
67          * {@code [ "a", "b", ["c", "d"], "e" ]}, and if {@code X} is also grouped, then it is
68          * {@code [ ["a", "b"], ["c", "d"], "e" ]}, producing only 3 top-level values.
69          */
isGrouped()70         public abstract boolean isGrouped();
71 
72         /**
73          * Returns the transformed values of this result, which may or may not be grouped
74          * according to {@link #isGrouped()}.
75          */
getValues()76         public abstract ImmutableList<String> getValues();
77 
78         /**
79          * Returns whether this result is a fallback for some existing matched result. Fallback
80          * results should only be used when it is not a fallback for any existing result.
81          */
isFallbackFor(Result r)82         public abstract boolean isFallbackFor(Result r);
83 
84         /** Debug only string representation. */
85         @Override
toString()86         public final String toString() {
87             return String.format(
88                 "Result{ key='%s', grouped=%s, values=%s }",
89                 getKey(), isGrouped(), getValues());
90         }
91     }
92 
93     /**
94      * A "typedef" for the function to do late binding of dynamic variables. This is used for edge
95      * cases where a %N variable in the rules config is bound to a CLDR path (e.g. "//foo/bar")
96      * which cannot be resolved until the rule is evaluated. Unfortunately the need to support late
97      * binding of variables incurs significant additional complexity in the code, despite being
98      * used in exactly one situation so far (the '%D' variable to represent the default numbering
99      * scheme.
100      */
101     // TODO: Figure out how to get rid of all of this mess.
102     public interface DynamicVars extends Function<CldrPath, String> {}
103 
104     /**
105      * Transforms a CLDR value into a sequence of results (empty if the value was not matched by
106      * any rule).
107      *
108      * @param cldrValue the value to transform.
109      * @return the transformed result(s).
110      */
transform(CldrValue cldrValue)111     public abstract ImmutableList<Result> transform(CldrValue cldrValue);
112 
113     /**
114      * Transforms a CLDR value into a sequence of results (empty if the value was not matched by
115      * any rule). The dynamic variable function provides any "late bound" CLDR path variables to be
116      * resolved from CLDR data during processing (e.g "%D=//ldml/numbers/defaultNumberingSystem").
117      *
118      * @param cldrValue the value to transform.
119      * @param varFn a function for resolving "late bound" variables.
120      * @return the transformed result(s).
121      */
transform(CldrValue cldrValue, DynamicVars varFn)122     public abstract ImmutableList<Result> transform(CldrValue cldrValue, DynamicVars varFn);
123 
124     /**
125      * Returns a possibly empty sequence of fallback results for a given key. A fallback result for
126      * a key should be used only if it is not a fallback for any other result with that key; see
127      * also {@link Result#isFallbackFor(Result)}.
128      */
getFallbackResultsFor(RbPath key, DynamicVars varFn)129     public abstract ImmutableList<Result> getFallbackResultsFor(RbPath key, DynamicVars varFn);
130 }
131