package org.unicode.cldr.util; import java.util.ArrayList; import java.util.Arrays; import java.util.EnumSet; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.TreeSet; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.unicode.cldr.util.PatternPlaceholders.PlaceholderInfo; import com.ibm.icu.text.MessageFormat; import com.ibm.icu.util.Output; public class PathDescription { public enum ErrorHandling { SKIP, CONTINUE } // BE sure to sync with the list in xmbSkip! public static final Set EXTRA_LANGUAGES = new TreeSet( Arrays .asList( "ach|af|ak|ak|am|ar|az|be|bem|bg|bh|bn|br|bs|ca|chr|ckb|co|crs|cs|cy|da|de|de_AT|de_CH|ee|el|en|en_AU|en_CA|en_GB|en_US|eo|es|es_419|es_ES|et|eu|fa|fi|fil|fo|fr|fr_CA|fr_CH|fy|ga|gaa|gd|gl|gn|gsw|gu|ha|haw|he|hi|hr|ht|hu|hy|ia|id|ig|io|is|it|ja|jv|ka|kg|kk|km|kn|ko|kri|ku|ky|la|lg|ln|lo|loz|lt|lua|lv|mfe|mg|mi|mk|ml|mn|mr|ms|mt|my|nb|ne|nl|nl_BE|nn|nso|ny|nyn|oc|om|or|pa|pcm|pl|ps|pt|pt_BR|pt_PT|qu|rm|rn|ro|ro|ro_MD|ru|rw|sd|si|sk|sl|sn|so|sq|sr|sr_Latn|sr_ME|st|su|sv|sw|ta|te|tg|th|ti|tk|tlh|tn|to|tr|tt|tum|ug|uk|und|ur|uz|vi|wo|xh|yi|yo|zh|zh_Hans|zh_Hant|zh_HK|zu|zxx" .split("|"))); private static final Pattern METAZONE_PATTERN = Pattern .compile("//ldml/dates/timeZoneNames/metazone\\[@type=\"([^\"]*)\"]/(.*)/(.*)"); private static final Pattern STAR_ATTRIBUTE_PATTERN = PatternCache.get("=\"([^\"]*)\""); private static final StandardCodes STANDARD_CODES = StandardCodes.make(); private static Map ZONE2COUNTRY = STANDARD_CODES.getZoneToCounty(); private static RegexLookup pathHandling = new RegexLookup().loadFromFile(PathDescription.class, "data/PathDescription.txt"); // set in construction private final CLDRFile english; private final Map extras; private final ErrorHandling errorHandling; private final Map>> starredPaths; private final Set allMetazones; // used on instance private Matcher metazoneMatcher = METAZONE_PATTERN.matcher(""); private XPathParts parts = new XPathParts(); private String starredPathOutput; private Output pathArguments = new Output(); private EnumSet status = EnumSet.noneOf(Status.class); public static final String MISSING_DESCRIPTION = "Before translating, please see http://cldr.org/translation."; public PathDescription(SupplementalDataInfo supplementalDataInfo, CLDRFile english, Map extras, Map>> starredPaths, ErrorHandling errorHandling) { this.english = english; this.extras = extras == null ? new HashMap() : extras; this.starredPaths = starredPaths == null ? new HashMap>>() : starredPaths; allMetazones = supplementalDataInfo.getAllMetazones(); this.errorHandling = errorHandling; } public String getStarredPathOutput() { return starredPathOutput; } public EnumSet getStatus() { return status; } public enum Status { SKIP, NULL_VALUE, EMPTY_CONTENT, NOT_REQUIRED } public String getRawDescription(String path, String value, Object context) { status.clear(); return pathHandling.get(path, context, pathArguments); } public String getDescription(String path, String value, Level level, Object context) { status.clear(); String description = pathHandling.get(path, context, pathArguments); if (description == null) { description = MISSING_DESCRIPTION; } else if ("SKIP".equals(description)) { status.add(Status.SKIP); if (errorHandling == ErrorHandling.SKIP) { return null; } } // String localeWhereFound = english.getSourceLocaleID(path, status); // if (!status.pathWhereFound.equals(path)) { // reasonsToPaths.put("alias", path + " " + value); // continue; // } if (value == null) { // a count item? String xpath = extras.get(path); if (xpath != null) { value = english.getStringValue(xpath); } else if (path.contains("/metazone")) { if (metazoneMatcher.reset(path).matches()) { String name = metazoneMatcher.group(1); String type = metazoneMatcher.group(3); value = name.replace('_', ' ') + (type.equals("generic") ? "" : type.equals("daylight") ? " Summer" : " Winter") + " Time"; // System.out.println("Missing: " + path + " : " + value); } } if (value == null) { status.add(Status.NULL_VALUE); if (errorHandling == ErrorHandling.SKIP) { return null; } } } if (value != null && value.length() == 0) { status.add(Status.EMPTY_CONTENT); if (errorHandling == ErrorHandling.SKIP) { return null; } } // if (GenerateXMB.contentMatcher != null && !GenerateXMB.contentMatcher.reset(value).find()) { // PathDescription.addSkipReasons(reasonsToPaths, "content-parameter", level, path, value); // return null; // } List attributes = addStarredInfo(starredPaths, path); // In special cases, only use if there is a root value (languageNames, ... if (description.startsWith("ROOT")) { int typeEnd = description.indexOf(';'); String type = description.substring(4, typeEnd).trim(); description = description.substring(typeEnd + 1).trim(); boolean isMetazone = type.equals("metazone"); String code = attributes.get(0); boolean isRootCode = isRootCode(code, allMetazones, type, isMetazone); if (!isRootCode) { status.add(Status.NOT_REQUIRED); if (errorHandling == ErrorHandling.SKIP) { return null; } } if (isMetazone) { parts.set(path); String daylightType = parts.getElement(-1); daylightType = daylightType.equals("daylight") ? "summer" : daylightType.equals("standard") ? "winter" : daylightType; String length = parts.getElement(-2); length = length.equals("long") ? "" : "abbreviated "; code = code + ", " + length + daylightType + " form"; } else if (type.equals("timezone")) { String country = (String) ZONE2COUNTRY.get(code); int lastSlash = code.lastIndexOf('/'); String codeName = lastSlash < 0 ? code : code.substring(lastSlash + 1).replace('_', ' '); boolean found = false; if ("001".equals(country)) { code = "the timezone “" + codeName + "”"; found = true; } else if (country != null) { String countryName = english.getName("territory", country); if (countryName != null) { if (!codeName.equals(countryName)) { code = "the city “" + codeName + "” (in " + countryName + ")"; } else { code = "the country “" + codeName + "”"; } found = true; } } if (!found) { System.out.println("Missing country for timezone " + code); } } description = MessageFormat.format(MessageFormat.autoQuoteApostrophe(description), new Object[] { code }); } else if (path.contains("exemplarCity")) { String regionCode = ZONE2COUNTRY.get(attributes.get(0)); String englishRegionName = english.getName(CLDRFile.TERRITORY_NAME, regionCode); description = MessageFormat.format(MessageFormat.autoQuoteApostrophe(description), new Object[] { englishRegionName }); } else if (description != MISSING_DESCRIPTION) { description = MessageFormat.format(MessageFormat.autoQuoteApostrophe(description), (Object[]) pathArguments.value); } return description; } /** * Creates an escaped HTML string of placeholder information. * * @param path * the xpath to specify placeholder information for * @return a HTML string, or an empty string if there was no placeholder information */ public String getPlaceholderDescription(String path) { Map placeholders = PatternPlaceholders.getInstance().get(path); if (placeholders != null && placeholders.size() > 0) { StringBuffer buffer = new StringBuffer(); buffer.append(""); buffer.append(""); for (Entry entry : placeholders.entrySet()) { PlaceholderInfo info = entry.getValue(); buffer.append(""); buffer.append(""); buffer.append(""); buffer.append(""); buffer.append(""); } buffer.append("
PlaceholderMeaningExample
").append(entry.getKey()).append("").append(info.name).append("").append(info.example).append("
"); return buffer.toString(); } return ""; } private static boolean isRootCode(String code, Set allMetazones, String type, boolean isMetazone) { Set codes = isMetazone ? allMetazones : type.equals("timezone") ? STANDARD_CODES.getCanonicalTimeZones() : STANDARD_CODES.getSurveyToolDisplayCodes(type); // end boolean isRootCode = codes.contains(code) || code.contains("_"); if (!isRootCode && type.equals("language") && EXTRA_LANGUAGES.contains(code)) { isRootCode = true; } return isRootCode; } private List addStarredInfo(Map>> starredPaths, String path) { Matcher starAttributeMatcher = STAR_ATTRIBUTE_PATTERN.matcher(path); StringBuilder starredPath = new StringBuilder(); List attributes = new ArrayList(); int lastEnd = 0; while (starAttributeMatcher.find()) { int start = starAttributeMatcher.start(1); int end = starAttributeMatcher.end(1); starredPath.append(path.substring(lastEnd, start)); starredPath.append(".*"); attributes.add(path.substring(start, end)); lastEnd = end; } starredPath.append(path.substring(lastEnd)); String starredPathString = starredPath.toString().intern(); starredPathOutput = starredPathString; List> attributeList = starredPaths.get(starredPathString); if (attributeList == null) { starredPaths.put(starredPathString, attributeList = new ArrayList>()); } int i = 0; for (String attribute : attributes) { if (attributeList.size() <= i) { TreeSet subset = new TreeSet(); subset.add(attribute); attributeList.add(subset); } else { Set subset = attributeList.get(i); subset.add(attribute); } ++i; } return attributes; } }