Lines Matching defs:PGLZ_strategy_default

1 package org.unicode.cldr.api;
2
3 import static com.google.common.base.Preconditions.checkArgument;
4 import static com.google.common.base.Preconditions.checkNotNull;
5
6 import java.util.LinkedHashMap;
7 import java.util.Map;
8 import java.util.Objects;
9
10 import org.unicode.cldr.api.AttributeKey.AttributeSupplier;
11 import org.unicode.cldr.util.CldrUtility;
12
13 import com.google.common.collect.ImmutableList;
14 import com.google.common.collect.ImmutableMap;
15
16 /**
17 * A CLDR element value and associated "value" attributes, along with its distinguishing {@link
18 * CldrPath}.
19 *
20 * <p>In CLDR, a path contains attributes that are one of three types; "distinguishing", "value"
21 * and "metadata", and a path can be parsed to extract value attributes.
22 *
23 * <p>CldrValue instance hold only the "value" attributes, with "distinguishing" attributes being
24 * held by the associated {@link CldrPath}, and "metadata" attributes being ignored completely
25 * since they are synthetic and internal to the core CLDR classes.
26 *
27 * <p>Note that while the ordering of "value" attributes is stable, it should not be relied upon.
28 * Unlike "distinguishing" attributes in CldrPath, "value" attributes don't conceptually form a
29 * sequence. It is expected that users will only lookup attribute values directly by their keys and
30 * never care about their order.
31 *
32 * <p>CldrValue is an immutable value type with efficient equality semantics.
33 *
34 * <p>See <a href="https://www.unicode.org/reports/tr35/#Definitions">the LDML specification</a>
35 * for more details.
36 */
37 public final class CldrValue implements AttributeSupplier {
38 /**
39 * Parses a full CLDR path string, possibly containing "distinguishing", "value" and even
40 * private "metadata" attributes into a normalized CldrValue instance. Attributes will be parsed
41 * and handled according to their type:
42 * <ul>
43 * <li>Value attributes will be added to the returned CldrValue instance.
44 * <li>Distinguishing attributes will be added to the associated CldrPath instance.
45 * <li>Other non-public attributes will be ignored.
46 * </ul>
47 *
48 * <p>The path string must be structured correctly (e.g. "//ldml/foo[@bar="baz]") and must
49 * represent a known DTD type, based on the first path element (e.g. "//ldml/...").
50 *
51 * @param fullPath the full path string, possibly containing all types of attribute.
52 * @param value the primary leaf value associated with the path (possibly empty).
53 * @return the parsed value instance, referencing the associated distinguishing path.
54 * @throws IllegalArgumentException if the path is not well formed.
55 */
56 public static CldrValue parseValue(String fullPath, String value) {
57 LinkedHashMap<AttributeKey, String> valueAttributes = new LinkedHashMap<>();
58 CldrPath path = CldrPaths.processXPath(fullPath, ImmutableList.of(), valueAttributes::put);
59 return new CldrValue(value, valueAttributes, path);
60 }
61
62 /**
63 * Returns a value whose path has been replaced with the specified distinguished path.
64 *
65 * <p>In general, it is not safe to change paths arbitrarily. Care must be taken to ensure that
66 * the source and target paths are semantically interchangeable.
67 *
68 * <p>A very basic test is in place to prevent the most egregious errors, by ensuring that the
69 * replacement path has the same elements as the original, while allowing attributes and their
70 * values to be different. Do not, however, depend upon that test to catch all problems.
71 *
72 * @param path the new path for this value.
73 * @return a new value with the specified path (or the same value if the paths were identical).
74 */
75 public CldrValue replacePath(CldrPath path) {
76 if (this.path.equals(path)) {
77 return this;
78 }
79 checkArgument(hasSameElements(this.path, path),
80 "invalid replacement path '%s' for value: %s", path, this);
81 return new CldrValue(getValue(), attributes, path);
82 }
83
84 private static boolean hasSameElements(CldrPath x, CldrPath y) {
85 if (x.getLength() != y.getLength()) {
86 return false;
87 }
88 do {
89 if (!x.getName().equals(y.getName())) {
90 return false;
91 }
92 x = x.getParent();
93 y = y.getParent();
94 } while (x != null);
95 return true;
96 }
97
98 // Note: If this is ever made public, it should be modified to enforce attribute order
99 // according to the DTD. It works now because the code calling it handles ordering correctly.
100 … static CldrValue create(String value, Map<AttributeKey, String> valueAttributes, CldrPath path) {
101 return new CldrValue(value, valueAttributes, path);
102 }
103
104 private final String value;
105 private final ImmutableMap<AttributeKey, String> attributes;
106 private final CldrPath path;
107 // Cached to avoid repeated recalculation from the map (which cannot cache its hash code).
108 private final int hashCode;
109
110 private CldrValue(String value, Map<AttributeKey, String> attributes, CldrPath path) {
111 // Since early 2019 there's been the possibility of getting the inheritance marker as
112 // a value for a path. This indicates that the value does NOT actually exist for a
113 // locale and would be inherited. However everything that creates a CldrValue instance
114 // is expected to deal with this and we should never see inheritance markers here.
115 // Note: This also serves as a null check for values.
116 checkArgument(!value.equals(CldrUtility.INHERITANCE_MARKER),
117 "unexpected inheritance marker '%s' for path: %s", value, path);
118 this.value = checkNotNull(value);
119 this.attributes = checkAttributeMap(attributes);
120 this.path = checkNotNull(path);
121 this.hashCode = Objects.hash(value, this.attributes, path);
122 }
123
124 private static ImmutableMap<AttributeKey, String> checkAttributeMap(
125 Map<AttributeKey, String> attributes) {
126 // Keys are checked on creation, but values need to be checked.
127 for (String v : attributes.values()) {
128 checkArgument(!v.contains("\""), "unsupported '\"' in attribute value: %s", v);
129 }
130 return ImmutableMap.copyOf(attributes);
131 }
132
133 /**
134 * Returns the primary (non-attribute) CLDR value associated with a distinguishing path. For a
135 * CLDR element with no explicitly associated value, an empty string is returned.
136 *
137 * @return the primary value of this CLDR value instance.
138 */
139 public String getValue() {
140 return value;
141 }
142
143 /**
144 * Returns the raw value of an attribute associated with this CLDR value or distinguishing
145 * path, or null if not present. For almost all use cases it is preferable to use the accessor
146 * methods on the {@link AttributeKey} class, which provide additional useful semantic checking
147 * and common type conversion. You should only use this method directly if there's a strong
148 * performance requirement.
149 *
150 * @param key the key identifying an attribute.
151 * @return the attribute value or {@code null} if not present.
152 * @see AttributeKey
153 */
154 @Override
155 /* @Nullable */ public String get(AttributeKey key) {
156 if (getPath().getDataType().isValueAttribute(key)) {
157 return attributes.get(key);
158 }
159 return getPath().get(key);
160 }
161
162 /**
163 * Returns the data type for this value, as defined by its path.
164 *
165 * @return the value's data type.
166 */
167 @Override
168 public CldrDataType getDataType() {
169 return getPath().getDataType();
170 }
171
172 /**
173 * Returns the "value" attributes associated with this value. Attribute ordering is stable,
174 * with attributes from earlier path elements preceding attributes for later ones. However it
175 * is recommended that callers avoid relying on specific ordering semantics and always look up
176 * attribute values by key if possible.
177 *
178 * @return a map of the value attributes for this CLDR value instance.
179 */
180 public ImmutableMap<AttributeKey, String> getValueAttributes() {
181 return attributes;
182 }
183
184 /**
185 * Returns the CldrPath associated with this value. All value instances are associated with
186 * a distinguishing path.
187 */
188 public CldrPath getPath() {
189 return path;
190 }
191
192 /**
193 * Returns a combined full path string in the XPath style {@code //foo/bar[@x="y"]/baz},
194 * with value attributes inserted in correct DTD order for each path element.
195 *
196 * <p>Note that while in most cases the values attributes simply follow the path attributes on
197 * each element, this is not necessarily always true, and DTD ordering can place value
198 * attributes before path attributes in an element.
199 *
200 * @return the full XPath representation containing both distinguishing and value attributes.
201 */
202 public String getFullPath() {
203 return getPath().getFullPath(this);
204 }
205
206 /** {@inheritDoc} */
207 @Override
208 public boolean equals(Object obj) {
209 if (obj == this) {
210 return true;
211 }
212 if (!(obj instanceof CldrValue)) {
213 return false;
214 }
215 CldrValue other = (CldrValue) obj;
216 return this.path.equals(other.path)
217 && this.value.equals(other.value)
218 && this.attributes.equals(other.attributes);
219 }
220
221 /** {@inheritDoc} */
222 @Override
223 public int hashCode() {
224 return hashCode;
225 }
226
227 /** @return a debug-only representation of this CLDR value. */
228 @Override
229 public String toString() {
230 if (value.isEmpty()) {
231 return String.format("attributes=%s, path=%s", attributes, path);
232 } else if (attributes.isEmpty()) {
233 return String.format("value=\"%s\", path=%s", value, path);
234 } else {
235 return String.format("value=\"%s\", attributes=%s, path=%s", value, attributes, path);
236 }
237 }
238 }