// © 2016 and later: Unicode, Inc. and others. // License & terms of use: http://www.unicode.org/copyright.html#License /* ******************************************************************************* * Copyright (C) 2015-2016, International Business Machines Corporation and * others. All Rights Reserved. ******************************************************************************* */ package com.ibm.icu.impl; import java.util.Collections; import java.util.EnumMap; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import com.ibm.icu.impl.locale.AsciiUtil; import com.ibm.icu.util.UResourceBundle; import com.ibm.icu.util.UResourceBundleIterator; /** * @author markdavis * */ public class ValidIdentifiers { public enum Datatype { currency, language, region, script, subdivision, unit, variant, u, t, x, illegal } public enum Datasubtype { deprecated, private_use, regular, special, unknown, macroregion, } public static class ValiditySet { public final Set regularData; public final Map> subdivisionData; public ValiditySet(Set plainData, boolean makeMap) { if (makeMap) { HashMap> _subdivisionData = new HashMap>(); for (String s : plainData) { int pos = s.indexOf('-'); // read v28 data also int pos2 = pos+1; if (pos < 0) { pos2 = pos = s.charAt(0) < 'A' ? 3 : 2; } final String key = s.substring(0, pos); final String subdivision = s.substring(pos2); Set oldSet = _subdivisionData.get(key); if (oldSet == null) { _subdivisionData.put(key, oldSet = new HashSet()); } oldSet.add(subdivision); } this.regularData = null; HashMap> _subdivisionData2 = new HashMap>(); // protect the sets for (Entry> e : _subdivisionData.entrySet()) { Set value = e.getValue(); // optimize a bit by using singleton Set set = value.size() == 1 ? Collections.singleton(value.iterator().next()) : Collections.unmodifiableSet(value); _subdivisionData2.put(e.getKey(), set); } this.subdivisionData = Collections.unmodifiableMap(_subdivisionData2); } else { this.regularData = Collections.unmodifiableSet(plainData); this.subdivisionData = null; } } public boolean contains(String code) { if (regularData != null) { return regularData.contains(code); } else { int pos = code.indexOf('-'); String key = code.substring(0,pos); final String value = code.substring(pos+1); return contains(key, value); } } public boolean contains(String key, String value) { Set oldSet = subdivisionData.get(key); return oldSet != null && oldSet.contains(value); } @Override public String toString() { if (regularData != null) { return regularData.toString(); } else { return subdivisionData.toString(); } } } private static class ValidityData { static final Map> data; static { Map> _data = new EnumMap>(Datatype.class); UResourceBundle suppData = UResourceBundle.getBundleInstance( ICUData.ICU_BASE_NAME, "supplementalData", ICUResourceBundle.ICU_DATA_CLASS_LOADER); UResourceBundle validityInfo = suppData.get("idValidity"); for(UResourceBundleIterator datatypeIterator = validityInfo.getIterator(); datatypeIterator.hasNext();) { UResourceBundle datatype = datatypeIterator.next(); String rawKey = datatype.getKey(); Datatype key = Datatype.valueOf(rawKey); Map values = new EnumMap(Datasubtype.class); for(UResourceBundleIterator datasubtypeIterator = datatype.getIterator(); datasubtypeIterator.hasNext();) { UResourceBundle datasubtype = datasubtypeIterator.next(); String rawsubkey = datasubtype.getKey(); Datasubtype subkey = Datasubtype.valueOf(rawsubkey); // handle single value specially Set subvalues = new HashSet(); if (datasubtype.getType() == UResourceBundle.STRING) { addRange(datasubtype.getString(), subvalues); } else { for (String string : datasubtype.getStringArray()) { addRange(string, subvalues); } } values.put(subkey, new ValiditySet(subvalues, key == Datatype.subdivision)); } _data.put(key, Collections.unmodifiableMap(values)); } data = Collections.unmodifiableMap(_data); } private static void addRange(String string, Set subvalues) { string = AsciiUtil.toLowerString(string); int pos = string.indexOf('~'); if (pos < 0) { subvalues.add(string); } else { StringRange.expand(string.substring(0,pos), string.substring(pos+1), false, subvalues); } } } public static Map> getData() { return ValidityData.data; } /** * Returns the Datasubtype containing the code, or null if there is none. */ public static Datasubtype isValid(Datatype datatype, Set datasubtypes, String code) { Map subtable = ValidityData.data.get(datatype); if (subtable != null) { for (Datasubtype datasubtype : datasubtypes) { ValiditySet validitySet = subtable.get(datasubtype); if (validitySet != null) { if (validitySet.contains(AsciiUtil.toLowerString(code))) { return datasubtype; } } } } return null; } public static Datasubtype isValid(Datatype datatype, Set datasubtypes, String code, String value) { Map subtable = ValidityData.data.get(datatype); if (subtable != null) { code = AsciiUtil.toLowerString(code); value = AsciiUtil.toLowerString(value); for (Datasubtype datasubtype : datasubtypes) { ValiditySet validitySet = subtable.get(datasubtype); if (validitySet != null) { if (validitySet.contains(code, value)) { return datasubtype; } } } } return null; } }