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