1 package org.unicode.cldr.util; 2 3 import com.ibm.icu.util.Output; 4 import java.util.Set; 5 6 /** 7 * Automatically construct language names (glossonyms) 8 * 9 * <p>Example: in German (de), for the path 10 * 11 * <p>//ldml/localeDisplayNames/languages/language[@type="ro_MD"] 12 * 13 * <p>the value "Rumänisch (Republik Moldau)" is automatically constructed based on the code 14 * "ro_MD". 15 * 16 * <p>The constructed value is a default if no preferable value is submitted or inherited. A 17 * different (non-constructed) value, such as "Moldauisch", may become the winning value instead of 18 * the constructed value. 19 */ 20 public class GlossonymConstructor { 21 22 /** Some paths with this prefix can get automatically constructed values */ 23 public static final String PATH_PREFIX = 24 "//ldml/localeDisplayNames/languages/language[@type=\""; 25 26 /** 27 * The code such as "ro_MD" must contain an underscore, otherwise there is no constructed value. 28 * Underscore also serves as a clue for recognizing a value that is from code-fallback; for 29 * example, when the value "ro_MD" is inherited, the underscore implies it's a raw code ("bogus 30 * value") and should be replaced by a constructed value like "Rumänisch (Republik Moldau)"; but 31 * when the value "Moldauisch" (without underscore) is inherited, it should not be replaced by a 32 * constructed value 33 */ 34 private static final String CODE_SEPARATOR = "_"; 35 36 /** 37 * For "pathWhereFound" when the value is constructed. It is non-null to satisfy 38 * TestPathHeadersAndValues. It should not be treated as an actual path; for example, the Survey 39 * Tool Info Panel should not show a broken "Jump to original" link. 40 */ 41 public static final String PSEUDO_PATH = "constructed"; 42 43 /** 44 * Is the given path eligible for getting a constructed value? 45 * 46 * @param xpath the given path 47 * @return true if eligible 48 */ pathIsEligible(String xpath)49 public static boolean pathIsEligible(String xpath) { 50 return xpath.startsWith(PATH_PREFIX) && xpath.contains(CODE_SEPARATOR); 51 } 52 53 /** 54 * Is the given value bogus, and therefore eligible for getting replaced by a constructed value? 55 * 56 * @param value the given value 57 * @return true if bogus 58 */ valueIsBogus(String value)59 public static boolean valueIsBogus(String value) { 60 return (value == null 61 || value.contains(CODE_SEPARATOR) 62 || RegexUtilities.PATTERN_3_OR_4_DIGITS.matcher(value).find()); 63 } 64 65 private final CLDRFile cldrFile; 66 GlossonymConstructor(CLDRFile cldrFile)67 public GlossonymConstructor(CLDRFile cldrFile) { 68 this.cldrFile = cldrFile; 69 if (!cldrFile.isResolved()) { 70 throw new IllegalArgumentException( 71 "Unresolved CLDRFile in GlossonymConstructor constructor"); 72 } 73 } 74 75 /** 76 * Get the constructed value and fill in tracking information about where it was found 77 * 78 * @param xpath the path 79 * @param pathWhereFound if not null, to be filled in 80 * @param localeWhereFound if not null, to be filled in 81 * @return the constructed value, or null 82 */ getValueAndTrack( String xpath, Output<String> pathWhereFound, Output<String> localeWhereFound)83 public String getValueAndTrack( 84 String xpath, Output<String> pathWhereFound, Output<String> localeWhereFound) { 85 final String constructedValue = getValue(xpath); 86 if (constructedValue != null) { 87 if (localeWhereFound != null) { 88 localeWhereFound.value = cldrFile.getLocaleID(); 89 } 90 if (pathWhereFound != null) { 91 pathWhereFound.value = PSEUDO_PATH; 92 } 93 return constructedValue; 94 } 95 return null; 96 } 97 98 /** 99 * Get the constructed value for the given path 100 * 101 * @param xpath the given path 102 * @return the constructed value, or null 103 */ getValue(String xpath)104 public String getValue(String xpath) { 105 if (pathIsEligible(xpath)) { 106 return reallyGetValue(xpath); 107 } 108 return null; 109 } 110 reallyGetValue(String xpath)111 private synchronized String reallyGetValue(String xpath) { 112 final XPathParts parts = XPathParts.getFrozenInstance(xpath); 113 final String type = parts.getAttributeValue(-1, "type"); 114 if (type.contains(CODE_SEPARATOR)) { 115 final String alt = parts.getAttributeValue(-1, "alt"); 116 final CLDRFile.SimpleAltPicker altPicker = 117 (alt == null) ? null : new CLDRFile.SimpleAltPicker(alt); 118 final String value = cldrFile.getName(type, true, altPicker); 119 if (!valueIsBogus(value)) { 120 return value; 121 } 122 } 123 return null; 124 } 125 getPathsWhereFound(String xpath, Set<String> paths)126 public Set<String> getPathsWhereFound(String xpath, Set<String> paths) { 127 final XPathParts parts = XPathParts.getFrozenInstance(xpath); 128 final String type = parts.getAttributeValue(-1, "type"); 129 if (type.contains(CODE_SEPARATOR)) { 130 final String alt = parts.getAttributeValue(-1, "alt"); 131 final CLDRFile.SimpleAltPicker altPicker = 132 (alt == null) ? null : new CLDRFile.SimpleAltPicker(alt); 133 final String value = cldrFile.getName(type, true, altPicker, paths); 134 if (!valueIsBogus(value)) { 135 return paths; 136 } 137 } 138 return null; 139 } 140 } 141